Browse Source

血缘图优化

master
xueyinfei 2 weeks ago
parent
commit
75573ff65a
  1. 27
      vue-fastapi-backend/module_admin/service/meta_service.py
  2. 7
      vue-fastapi-frontend/src/assets/aichat/101.svg
  3. 9
      vue-fastapi-frontend/src/assets/aichat/101n.svg
  4. 6
      vue-fastapi-frontend/src/assets/aichat/11.svg
  5. 8
      vue-fastapi-frontend/src/assets/aichat/11n.svg
  6. 144
      vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelation.vue
  7. 145
      vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelationSql.vue
  8. 356
      vue-fastapi-frontend/src/views/meta/metaInfo/businssRelation.vue

27
vue-fastapi-backend/module_admin/service/meta_service.py

@ -271,6 +271,8 @@ class MetaService:
"tab_eng_name": currentNode['a_tab_eng_name'], "tab_eng_name": currentNode['a_tab_eng_name'],
"fld_eng_name": currentNode['a_fld_eng_name']}, "fld_eng_name": currentNode['a_fld_eng_name']},
"endArrow": True} "endArrow": True}
if meta_query.type == 'er':
relation['relation_type'] = currentNode['relation_type']
relationList.append(relation) relationList.append(relation)
if meta_query.type == 'er': if meta_query.type == 'er':
if currentNode['a_tab_eng_name'] == meta_query.tab_eng_name: if currentNode['a_tab_eng_name'] == meta_query.tab_eng_name:
@ -292,7 +294,9 @@ class MetaService:
'mdl_name': nextNode['b_mdl_name'], 'mdl_name': nextNode['b_mdl_name'],
'tab_eng_name': nextNode['b_tab_eng_name'], 'tab_eng_name': nextNode['b_tab_eng_name'],
'fld_eng_name': nextNode['b_fld_eng_name']}, 'fld_eng_name': nextNode['b_fld_eng_name']},
"endArrow": True} if nextNode['father'] == 'A' else \ "endArrow": True,
"relation_type": nextNode['relation_type']
} if nextNode['father'] == 'A' else \
{"source": {'ssys_id': nextNode['b_ssys_id'], {"source": {'ssys_id': nextNode['b_ssys_id'],
'mdl_name': nextNode['b_mdl_name'], 'mdl_name': nextNode['b_mdl_name'],
'tab_eng_name': nextNode['b_tab_eng_name'], 'tab_eng_name': nextNode['b_tab_eng_name'],
@ -301,7 +305,8 @@ class MetaService:
"mdl_name": nextNode['a_mdl_name'], "mdl_name": nextNode['a_mdl_name'],
"tab_eng_name": nextNode['a_tab_eng_name'], "tab_eng_name": nextNode['a_tab_eng_name'],
"fld_eng_name": nextNode['a_fld_eng_name']}, "fld_eng_name": nextNode['a_fld_eng_name']},
"endArrow": True "endArrow": True,
"relation_type": nextNode['relation_type']
} }
relationList.append(relation) relationList.append(relation)
if currentNode['father'] == 'B': if currentNode['father'] == 'B':
@ -322,7 +327,8 @@ class MetaService:
'mdl_name': preNode['b_mdl_name'], 'mdl_name': preNode['b_mdl_name'],
'tab_eng_name': preNode['b_tab_eng_name'], 'tab_eng_name': preNode['b_tab_eng_name'],
'fld_eng_name': preNode['b_fld_eng_name']}, 'fld_eng_name': preNode['b_fld_eng_name']},
"endArrow": True} if preNode['father'] == 'A' else \ "endArrow": True,
"relation_type": preNode['relation_type']} if preNode['father'] == 'A' else \
{"source": {'ssys_id': preNode['b_ssys_id'], {"source": {'ssys_id': preNode['b_ssys_id'],
'mdl_name': preNode['b_mdl_name'], 'mdl_name': preNode['b_mdl_name'],
'tab_eng_name': preNode['b_tab_eng_name'], 'tab_eng_name': preNode['b_tab_eng_name'],
@ -330,7 +336,8 @@ class MetaService:
"target": {"ssys_id": preNode['a_ssys_id'], "mdl_name": preNode['a_mdl_name'], "target": {"ssys_id": preNode['a_ssys_id'], "mdl_name": preNode['a_mdl_name'],
"tab_eng_name": preNode['a_tab_eng_name'], "tab_eng_name": preNode['a_tab_eng_name'],
"fld_eng_name": preNode['a_fld_eng_name']}, "fld_eng_name": preNode['a_fld_eng_name']},
"endArrow": True} "endArrow": True,
"relation_type": preNode['relation_type']}
relationList.append(relation) relationList.append(relation)
if currentNode['b_tab_eng_name'] == meta_query.tab_eng_name: if currentNode['b_tab_eng_name'] == meta_query.tab_eng_name:
if currentNode['father'] == 'A': if currentNode['father'] == 'A':
@ -351,7 +358,8 @@ class MetaService:
'mdl_name': preNode['b_mdl_name'], 'mdl_name': preNode['b_mdl_name'],
'tab_eng_name': preNode['b_tab_eng_name'], 'tab_eng_name': preNode['b_tab_eng_name'],
'fld_eng_name': preNode['b_fld_eng_name']}, 'fld_eng_name': preNode['b_fld_eng_name']},
"endArrow": True} if \ "endArrow": True,
"relation_type": preNode['relation_type']} if \
preNode['father'] == 'A' else { preNode['father'] == 'A' else {
"source": {'ssys_id': preNode['b_ssys_id'], "source": {'ssys_id': preNode['b_ssys_id'],
'mdl_name': preNode['b_mdl_name'], 'mdl_name': preNode['b_mdl_name'],
@ -360,7 +368,8 @@ class MetaService:
"target": {"ssys_id": preNode['a_ssys_id'], "mdl_name": preNode['a_mdl_name'], "target": {"ssys_id": preNode['a_ssys_id'], "mdl_name": preNode['a_mdl_name'],
"tab_eng_name": preNode['a_tab_eng_name'], "tab_eng_name": preNode['a_tab_eng_name'],
"fld_eng_name": preNode['a_fld_eng_name']}, "fld_eng_name": preNode['a_fld_eng_name']},
"endArrow": True} "endArrow": True,
"relation_type": preNode['relation_type']}
relationList.append(relation) relationList.append(relation)
if currentNode['father'] == 'B': if currentNode['father'] == 'B':
# a 为下游,取a字段的下下游 # a 为下游,取a字段的下下游
@ -379,7 +388,8 @@ class MetaService:
'mdl_name': nextNode['b_mdl_name'], 'mdl_name': nextNode['b_mdl_name'],
'tab_eng_name': nextNode['b_tab_eng_name'], 'tab_eng_name': nextNode['b_tab_eng_name'],
'fld_eng_name': nextNode['b_fld_eng_name']}, 'fld_eng_name': nextNode['b_fld_eng_name']},
"endArrow": True} if \ "endArrow": True,
"relation_type": nextNode['relation_type']} if \
nextNode['father'] == 'A' else { nextNode['father'] == 'A' else {
"source": {'ssys_id': nextNode['b_ssys_id'], "source": {'ssys_id': nextNode['b_ssys_id'],
'mdl_name': nextNode['b_mdl_name'], 'mdl_name': nextNode['b_mdl_name'],
@ -388,7 +398,8 @@ class MetaService:
"target": {"ssys_id": nextNode['a_ssys_id'], "mdl_name": nextNode['a_mdl_name'], "target": {"ssys_id": nextNode['a_ssys_id'], "mdl_name": nextNode['a_mdl_name'],
"tab_eng_name": nextNode['a_tab_eng_name'], "tab_eng_name": nextNode['a_tab_eng_name'],
"fld_eng_name": nextNode['a_fld_eng_name']}, "fld_eng_name": nextNode['a_fld_eng_name']},
"endArrow": True} "endArrow": True,
"relation_type": nextNode['relation_type']}
relationList.append(relation) relationList.append(relation)
tableList = [] tableList = []
if len(relationList) > 0: if len(relationList) > 0:

7
vue-fastapi-frontend/src/assets/aichat/101.svg

@ -0,0 +1,7 @@
<svg viewBox="0 0 200 400.002" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.000000" height="400.002289" fill="none" customFrame="#000000">
<g id="101">
<circle id="椭圆 1" cx="100" cy="100" r="90" stroke="rgb(0,0,0)" stroke-width="20" />
<line id="直线 1" x1="0" x2="200" y1="200" y2="200" stroke="rgb(0,0,0)" stroke-width="20" />
<line id="直线 2" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(0.000229315,1,-1,0.000229315,100,200)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 547 B

9
vue-fastapi-frontend/src/assets/aichat/101n.svg

@ -0,0 +1,9 @@
<svg viewBox="0 0 215.817 400.002" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="215.816711" height="400.002289" fill="none" customFrame="#000000">
<g id="101n">
<circle id="椭圆 1" cx="107.762756" cy="100" r="90" stroke="rgb(0,0,0)" stroke-width="20" />
<line id="直线 1" x1="7.76275635" x2="207.762756" y1="200" y2="200" stroke="rgb(0,0,0)" stroke-width="20" />
<line id="直线 2" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(0.000229315,1,-1,0.000229315,107.763,200)" />
<line id="直线 4" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(-0.5,0.866025,-0.866025,-0.5,108.66,200.247)" />
<line id="直线 3" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(0.5,0.866025,-0.866025,0.5,107.156,200.347)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 892 B

6
vue-fastapi-frontend/src/assets/aichat/11.svg

@ -0,0 +1,6 @@
<svg viewBox="0 0 200 210.002" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.000000" height="210.002289" fill="none" customFrame="#000000">
<g id="11">
<line id="直线 1" x1="0" x2="200" y1="10" y2="10" stroke="rgb(0,0,0)" stroke-width="20" />
<line id="直线 2" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(0.000229315,1,-1,0.000229315,100,10)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 453 B

8
vue-fastapi-frontend/src/assets/aichat/11n.svg

@ -0,0 +1,8 @@
<svg viewBox="0 0 215.817 210.002" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="215.816727" height="210.002289" fill="none" customFrame="#000000">
<g id="11n">
<line id="直线 1" x1="7.76277161" x2="207.762772" y1="10" y2="10" stroke="rgb(0,0,0)" stroke-width="20" />
<line id="直线 2" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(0.000229315,1,-1,0.000229315,107.763,10)" />
<line id="直线 4" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(-0.5,0.866025,-0.866025,-0.5,108.66,10.2473)" />
<line id="直线 3" x1="0" x2="200" y1="0" y2="0" stroke="rgb(0,0,0)" stroke-width="20" transform="matrix(0.5,0.866025,-0.866025,0.5,107.156,10.3474)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 791 B

144
vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelation.vue

@ -55,53 +55,7 @@ function initG6() {
"node:mouseup": "mouseup" "node:mouseup": "mouseup"
}; };
}, },
scorll(e) { scorll(e) {},
// e.preventDefault();
const {
graph
} = this;
const nodes = graph.getNodes().filter((n) => {
const bbox = n.getBBox();
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
});
const x = e.deltaX || e.movementX;
let y = e.deltaY || e.movementY;
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
if (nodes) {
const edgesToUpdate = new Set();
nodes.forEach((node) => {
return;
const model = node.getModel();
if (model.attrs.length < 2) {
return;
}
const idx = model.startIndex || 0;
let startX = model.startX || 0.5;
let startIndex = idx + y * 0.02;
startX -= x;
if (startIndex < 0) {
startIndex = 0;
}
if (startX > 0) {
startX = 0;
}
if (startIndex > model.attrs.length - 1) {
startIndex = model.attrs.length - 1;
}
graph.updateItem(node, {
startIndex,
startX,
});
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
});
// G6 update the related edges when graph.updateItem with a node according to the new properties
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
edgesToUpdate.forEach(edge => edge.refresh())
}
},
click(e) { click(e) {
const { const {
graph graph
@ -160,27 +114,9 @@ function initG6() {
}) })
} }
}, },
mousedown(e) { mousedown(e) {},
this.isMousedown = true; mouseup(e) {},
}, move(e) {},
mouseup(e) {
this.isMousedown = false;
},
move(e) {
if (this.isMousedown) return;
// const name = e.shape.get("name");
// const item = e.item;
//
// if (name && name.startsWith("item")) {
// this.graph.updateItem(item, {
// selectedIndex: Number(name.split("-")[1]),
// });
// } else {
// this.graph.updateItem(item, {
// selectedIndex: NaN,
// });
// }
},
}); });
registerEdge("dice-er-edge", { registerEdge("dice-er-edge", {
@ -433,45 +369,6 @@ function initG6() {
draggable: true, draggable: true,
}); });
if (list.length + 1 > itemCount) {
const barStyle = {
width: 4,
padding: 0,
boxStyle: {
stroke: "#00000022",
},
innerStyle: {
fill: "#00000022",
},
};
//
listContainer.addShape("rect", {
attrs: {
y: 30,
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: (list.length + 1) * itemHeight - 30,
...barStyle.boxStyle,
},
});
const indexHeight =
afterList.length > itemCount ?
(afterList.length / list.length + 1) * (list.length + 1) * itemHeight :
10;
//
listContainer.addShape("rect", {
attrs: {
y: 30 +
barStyle.padding +
(startIndex / list.length + 1) * ((list.length + 1) * itemHeight - 30),
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: Math.min((list.length + 1) * itemHeight, indexHeight),
...barStyle.innerStyle,
},
});
}
if (afterList) { if (afterList) {
afterList.forEach((e, i) => { afterList.forEach((e, i) => {
const isSelected = const isSelected =
@ -498,35 +395,18 @@ function initG6() {
name: `item-${Math.floor(startIndex) + i}-content`, name: `item-${Math.floor(startIndex) + i}-content`,
draggable: true, draggable: true,
}); });
listContainer.addShape("line", {
if (!cfg.hideDot) {
//
listContainer.addShape("circle", {
attrs: { attrs: {
x: 0, x1: 0,
y: i * itemHeight + offsetY, y1: i * itemHeight - itemHeight / 2 + offsetY,
r: 3, x2: width,
stroke: boxStyle.stroke, y2: i * itemHeight - itemHeight / 2 + offsetY,
fill: "white", stroke: 'black',
radius: 2, lineWidth: 0.1,
lineWidth: 1,
cursor: "pointer", cursor: "pointer",
}, },
name: `item-${Math.floor(startIndex) + i}-top-border`,
}); });
//
listContainer.addShape("circle", {
attrs: {
x: width,
y: i * itemHeight + offsetY,
r: 3,
stroke: boxStyle.stroke,
fill: "white",
radius: 2,
lineWidth: 1,
cursor: "pointer",
},
});
}
// //
listContainer.addShape("text", { listContainer.addShape("text", {
attrs: { attrs: {

145
vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelationSql.vue

@ -55,53 +55,7 @@ function initG6() {
"node:mouseup": "mouseup" "node:mouseup": "mouseup"
}; };
}, },
scorll(e) { scorll(e) {},
// e.preventDefault();
const {
graph
} = this;
const nodes = graph.getNodes().filter((n) => {
const bbox = n.getBBox();
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
});
const x = e.deltaX || e.movementX;
let y = e.deltaY || e.movementY;
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
if (nodes) {
const edgesToUpdate = new Set();
nodes.forEach((node) => {
return;
const model = node.getModel();
if (model.attrs.length < 2) {
return;
}
const idx = model.startIndex || 0;
let startX = model.startX || 0.5;
let startIndex = idx + y * 0.02;
startX -= x;
if (startIndex < 0) {
startIndex = 0;
}
if (startX > 0) {
startX = 0;
}
if (startIndex > model.attrs.length - 1) {
startIndex = model.attrs.length - 1;
}
graph.updateItem(node, {
startIndex,
startX,
});
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
});
// G6 update the related edges when graph.updateItem with a node according to the new properties
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
edgesToUpdate.forEach(edge => edge.refresh())
}
},
click(e) { click(e) {
const { const {
graph graph
@ -166,27 +120,9 @@ else if (shape.get("name") && shape.get("name").startsWith("item")) {
}) })
} }
}, },
mousedown(e) { mousedown(e) {},
this.isMousedown = true; mouseup(e) {},
}, move(e) {},
mouseup(e) {
this.isMousedown = false;
},
move(e) {
if (this.isMousedown) return;
// const name = e.shape.get("name");
// const item = e.item;
//
// if (name && name.startsWith("item")) {
// this.graph.updateItem(item, {
// selectedIndex: Number(name.split("-")[1]),
// });
// } else {
// this.graph.updateItem(item, {
// selectedIndex: NaN,
// });
// }
},
}); });
registerEdge("dice-er-edge", { registerEdge("dice-er-edge", {
@ -438,46 +374,6 @@ else if (shape.get("name") && shape.get("name").startsWith("item")) {
}, },
draggable: true, draggable: true,
}); });
if (list.length + 1 > itemCount) {
const barStyle = {
width: 4,
padding: 0,
boxStyle: {
stroke: "#00000022",
},
innerStyle: {
fill: "#00000022",
},
};
//
listContainer.addShape("rect", {
attrs: {
y: 30,
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: (list.length + 1) * itemHeight - 30,
...barStyle.boxStyle,
},
});
const indexHeight =
afterList.length > itemCount ?
(afterList.length / list.length + 1) * (list.length + 1) * itemHeight :
10;
//
listContainer.addShape("rect", {
attrs: {
y: 30 +
barStyle.padding +
(startIndex / list.length + 1) * ((list.length + 1) * itemHeight - 30),
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: Math.min((list.length + 1) * itemHeight, indexHeight),
...barStyle.innerStyle,
},
});
}
if (afterList) { if (afterList) {
afterList.forEach((e, i) => { afterList.forEach((e, i) => {
const isSelected = const isSelected =
@ -504,35 +400,18 @@ else if (shape.get("name") && shape.get("name").startsWith("item")) {
name: `item-${Math.floor(startIndex) + i}-content`, name: `item-${Math.floor(startIndex) + i}-content`,
draggable: true, draggable: true,
}); });
listContainer.addShape("line", {
if (!cfg.hideDot) {
//
listContainer.addShape("circle", {
attrs: { attrs: {
x: 0, x1: 0,
y: i * itemHeight + offsetY, y1: i * itemHeight - itemHeight / 2 + offsetY,
r: 3, x2: width,
stroke: boxStyle.stroke, y2: i * itemHeight - itemHeight / 2 + offsetY,
fill: "white", stroke: 'black',
radius: 2, lineWidth: 0.1,
lineWidth: 1,
cursor: "pointer", cursor: "pointer",
}, },
name: `item-${Math.floor(startIndex) + i}-top-border`,
}); });
//
listContainer.addShape("circle", {
attrs: {
x: width,
y: i * itemHeight + offsetY,
r: 3,
stroke: boxStyle.stroke,
fill: "white",
radius: 2,
lineWidth: 1,
cursor: "pointer",
},
});
}
// //
listContainer.addShape("text", { listContainer.addShape("text", {
attrs: { attrs: {

356
vue-fastapi-frontend/src/views/meta/metaInfo/businssRelation.vue

@ -4,7 +4,11 @@
</template> </template>
<script setup> <script setup>
import G6 from '@antv/g6'; import G6 from '@antv/g6';
import {watch} from "vue"; import {onMounted, watch} from "vue";
import svgUrl11 from '@/assets/aichat/11.svg?url'
import svgUrl11n from '@/assets/aichat/11n.svg?url'
import svgUrl101 from '@/assets/aichat/101.svg?url'
import svgUrl101n from '@/assets/aichat/101n.svg?url'
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
@ -20,13 +24,102 @@ const props = defineProps({
} }
}) })
const g6data = ref([]) const g6data = ref([])
async function initSvg(svgUrl) {
// 1. SVG
const response = await fetch(svgUrl)
let svgContent = await response.text()
// 2. SVG
// A fill/stroke
svgContent = svgContent.replace(/fill=["'][^"']*["']/gi, 'fill="white"')
svgContent = svgContent.replace(/stroke=["'][^"']*["']/gi, 'stroke="#1890ff"')
// Bfillfill
if (!svgContent.includes('fill=')) {
const svgStart = svgContent.indexOf('<svg')
const svgTagEnd = svgContent.indexOf('>', svgStart)
svgContent = svgContent.slice(0, svgTagEnd) + ' fill="white"' + svgContent.slice(svgTagEnd)
}
// 3. base64
return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgContent)))}`
}
const svg11 = ref(null)
const svg11n =ref(null)
const svg101 =ref(null)
const svg101n =ref(null)
const getArrowSvgByType = (type) => {
switch(type) {
case '1': return svg11.value
case '2': return svg101.value
case '3': return svg11n.value
case '4': return svg101n.value
default: return svg11.value // 使svg11
}
}
function calculateArrowPosition(startPoint, endPoint, arrowHeight) {
const t = 1.0; //
// 线
const p0 = startPoint;
const p1 = {
x: endPoint.x / 3 + (2 / 3) * startPoint.x,
y: startPoint.y
};
const p2 = {
x: endPoint.x / 3 + (2 / 3) * startPoint.x,
y: endPoint.y
};
const p3 = endPoint;
// 线线
const dx = 3 * Math.pow(1 - t, 2) * (p1.x - p0.x) +
6 * (1 - t) * t * (p2.x - p1.x) +
3 * Math.pow(t, 2) * (p3.x - p2.x);
const dy = 3 * Math.pow(1 - t, 2) * (p1.y - p0.y) +
6 * (1 - t) * t * (p2.y - p1.y) +
3 * Math.pow(t, 2) * (p3.y - p2.y);
// 线
const tangentAngle = Math.atan2(dy, dx);
// 使沿线
//
const rotateAngle = tangentAngle - Math.PI / 2;
//
const arrowBottomMidX = endPoint.x;
const arrowBottomMidY = endPoint.y;
function initG6() { //
// arrowHeight/2
const centerX = arrowBottomMidX - (arrowHeight / 2) * Math.sin(tangentAngle);
const centerY = arrowBottomMidY + (arrowHeight / 2) * Math.cos(tangentAngle);
//
const arrowTopMidX = arrowBottomMidX - arrowHeight * Math.sin(tangentAngle);
const arrowTopMidY = arrowBottomMidY + arrowHeight * Math.cos(tangentAngle);
//
return {
x: centerX - arrowHeight / 2, // x
y: centerY - arrowHeight / 2, // y
rotate: rotateAngle, //
centerX, // x
centerY, // y
bottomMidX: arrowBottomMidX, // x
bottomMidY: arrowBottomMidY, // y
topMidX: arrowTopMidX, // x
topMidY: arrowTopMidY, // y
tangentAngle, // 线
arrowHeight, //
endPoint //
};
}
async function initG6() {
const { const {
Util, Util,
registerBehavior, registerBehavior,
registerEdge, registerEdge,
registerNode registerNode,
} = G6; } = G6;
const isInBBox = (point, bbox) => { const isInBBox = (point, bbox) => {
const { const {
@ -42,6 +135,7 @@ function initG6() {
return x < maxX && x > minX && y > minY && y < maxY; return x < maxX && x > minX && y > minY && y < maxY;
}; };
const itemHeight = 20; const itemHeight = 20;
registerBehavior("dice-er-scroll", { registerBehavior("dice-er-scroll", {
getDefaultCfg() { getDefaultCfg() {
@ -59,53 +153,7 @@ function initG6() {
"node:mouseup": "mouseup" "node:mouseup": "mouseup"
}; };
}, },
scorll(e) { scorll(e) {},
// e.preventDefault();
const {
graph
} = this;
const nodes = graph.getNodes().filter((n) => {
const bbox = n.getBBox();
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
});
const x = e.deltaX || e.movementX;
let y = e.deltaY || e.movementY;
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
if (nodes) {
const edgesToUpdate = new Set();
nodes.forEach((node) => {
return;
const model = node.getModel();
if (model.attrs.length < 2) {
return;
}
const idx = model.startIndex || 0;
let startX = model.startX || 0.5;
let startIndex = idx + y * 0.02;
startX -= x;
if (startIndex < 0) {
startIndex = 0;
}
if (startX > 0) {
startX = 0;
}
if (startIndex > model.attrs.length - 1) {
startIndex = model.attrs.length - 1;
}
graph.updateItem(node, {
startIndex,
startX,
});
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
});
// G6 update the related edges when graph.updateItem with a node according to the new properties
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
edgesToUpdate.forEach(edge => edge.refresh())
}
},
click(e) { click(e) {
const { const {
graph graph
@ -164,27 +212,9 @@ function initG6() {
}) })
} }
}, },
mousedown(e) { mousedown(e) {},
this.isMousedown = true; mouseup(e) {},
}, move(e) {},
mouseup(e) {
this.isMousedown = false;
},
move(e) {
if (this.isMousedown) return;
// const name = e.shape.get("name");
// const item = e.item;
//
// if (name && name.startsWith("item")) {
// this.graph.updateItem(item, {
// selectedIndex: Number(name.split("-")[1]),
// });
// } else {
// this.graph.updateItem(item, {
// selectedIndex: NaN,
// });
// }
},
}); });
registerEdge("dice-er-edge", { registerEdge("dice-er-edge", {
@ -192,43 +222,35 @@ function initG6() {
const edge = group.cfg.item; const edge = group.cfg.item;
const sourceNode = edge.getSource().getModel(); const sourceNode = edge.getSource().getModel();
const targetNode = edge.getTarget().getModel(); const targetNode = edge.getTarget().getModel();
const sourceIndex = sourceNode.attrs.findIndex( const sourceIndex = sourceNode.attrs.findIndex(
(e) => e.key === cfg.sourceKey (e) => e.key === cfg.sourceKey
); );
const sourceStartIndex = sourceNode.startIndex || 0; const sourceStartIndex = sourceNode.startIndex || 0;
let sourceY = 15; let sourceY = 15;
if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) { if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) {
sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * itemHeight; sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * itemHeight;
} }
const targetIndex = targetNode.attrs.findIndex( const targetIndex = targetNode.attrs.findIndex(
(e) => e.key === cfg.targetKey (e) => e.key === cfg.targetKey
); );
const targetStartIndex = targetNode.startIndex || 0; const targetStartIndex = targetNode.startIndex || 0;
let targetY = 15; let targetY = 15;
if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) { if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) {
targetY = (targetIndex - targetStartIndex + 0.5) * itemHeight + 30; targetY = (targetIndex - targetStartIndex + 0.5) * itemHeight + 30;
} }
const startPoint = { const startPoint = {
...cfg.startPoint ...cfg.startPoint
}; };
const endPoint = { const endPoint = {
...cfg.endPoint ...cfg.endPoint
}; };
startPoint.y = startPoint.y + sourceY; startPoint.y = startPoint.y + sourceY;
endPoint.y = endPoint.y + targetY; endPoint.y = endPoint.y + targetY;
let shape; let shape;
let arrowConfig = null;
if (sourceNode.id !== targetNode.id) { if (sourceNode.id !== targetNode.id) {
// SVG
if (cfg.relationType) {
shape = group.addShape("path", { shape = group.addShape("path", {
attrs: { attrs: {
stroke: "#5B8FF9", stroke: "#5B8FF9",
@ -240,15 +262,54 @@ function initG6() {
startPoint.y, startPoint.y,
endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.x / 3 + (2 / 3) * startPoint.x,
endPoint.y, endPoint.y,
endPoint.x - ((cfg.relationType === '2' || cfg.relationType === '4')? 16:8),
endPoint.y,
endPoint.x, endPoint.x,
endPoint.y, endPoint.y,
], ],
], ],
endArrow: props.type === 'er'? {path:G6.Arrow.triangle(10,-10,6),fill:'#5B8FF9'} : false//cfg.endArrow,
}, },
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: "path-shape", name: "path-shape",
}); });
const arrowSvg = getArrowSvgByType(cfg.relationType)
// 线线
//
arrowConfig = calculateArrowPosition(startPoint, endPoint, (cfg.relationType === '2' || cfg.relationType === '4')?16:8);
// SVG
const arrowShape = group.addShape('image', {
attrs: {
x: arrowConfig.x-((cfg.relationType === '2' || cfg.relationType === '4')?8:4),
y: arrowConfig.y+4,
width: 8,
height: (cfg.relationType === '2' || cfg.relationType === '4')?16:8,
'img':arrowSvg,
cursor: 'pointer',
},
name: 'custom-arrow',
})
arrowShape.rotateAtStart(arrowConfig.rotate);
}else{
shape = group.addShape("path", {
attrs: {
stroke: "#5B8FF9",
path: [
["M", startPoint.x, startPoint.y],
[
"C",
endPoint.x / 3 + (2 / 3) * startPoint.x,
startPoint.y,
endPoint.x / 3 + (2 / 3) * startPoint.x,
endPoint.y,
endPoint.x,
endPoint.y,
],
],
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: "path-shape",
});
}
} else { } else {
let gap = Math.abs((startPoint.y - endPoint.y) / 3); let gap = Math.abs((startPoint.y - endPoint.y) / 3);
if (startPoint["index"] === 1) { if (startPoint["index"] === 1) {
@ -269,11 +330,33 @@ function initG6() {
endPoint.y, endPoint.y,
], ],
], ],
endArrow: props.type === 'er'? {path: G6.Arrow.triangle(10,-10,6),fill:'#5B8FF9'} : false//cfg.endArrow,
}, },
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: "path-shape", name: "path-shape",
}); });
//
if (cfg.relationType) {
const arrowSvg = getArrowSvgByType(cfg.relationType)
//
const angle = -Math.PI / 2 // 90
const degree = angle * (180 / Math.PI)
//
const arrowSize = 8
const arrowX = startPoint.x - arrowSize / 2
const arrowY = endPoint.y - arrowSize
group.addShape('image', {
attrs: {
x: arrowX,
y: arrowY,
width: arrowSize,
height: arrowSize,
img: arrowSvg,
rotate: degree,
cursor: 'pointer',
},
name: 'custom-arrow',
})
}
} }
return shape; return shape;
}, },
@ -436,46 +519,6 @@ function initG6() {
}, },
draggable: true, draggable: true,
}); });
if (list.length + 1 > itemCount) {
const barStyle = {
width: 4,
padding: 0,
boxStyle: {
stroke: "#00000022",
},
innerStyle: {
fill: "#00000022",
},
};
//
listContainer.addShape("rect", {
attrs: {
y: 30,
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: (list.length + 1) * itemHeight - 30,
...barStyle.boxStyle,
},
});
const indexHeight =
afterList.length > itemCount ?
(afterList.length / list.length + 1) * (list.length + 1) * itemHeight :
10;
//
listContainer.addShape("rect", {
attrs: {
y: 30 +
barStyle.padding +
(startIndex / list.length + 1) * ((list.length + 1) * itemHeight - 30),
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: Math.min((list.length + 1) * itemHeight, indexHeight),
...barStyle.innerStyle,
},
});
}
if (afterList) { if (afterList) {
afterList.forEach((e, i) => { afterList.forEach((e, i) => {
const isSelected = const isSelected =
@ -502,35 +545,18 @@ function initG6() {
name: `item-${Math.floor(startIndex) + i}-content`, name: `item-${Math.floor(startIndex) + i}-content`,
draggable: true, draggable: true,
}); });
listContainer.addShape("line", {
if (!cfg.hideDot) {
//
listContainer.addShape("circle", {
attrs: {
x: 0,
y: i * itemHeight + offsetY,
r: 3,
stroke: boxStyle.stroke,
fill: "white",
radius: 2,
lineWidth: 1,
cursor: "pointer",
},
});
//
listContainer.addShape("circle", {
attrs: { attrs: {
x: width, x1: 0,
y: i * itemHeight + offsetY, y1: i * itemHeight - itemHeight / 2 + offsetY,
r: 3, x2: width,
stroke: boxStyle.stroke, y2: i * itemHeight - itemHeight / 2 + offsetY,
fill: "white", stroke: 'black',
radius: 2, lineWidth: 0.1,
lineWidth: 1,
cursor: "pointer", cursor: "pointer",
}, },
name: `item-${Math.floor(startIndex) + i}-top-border`,
}); });
}
// //
listContainer.addShape("text", { listContainer.addShape("text", {
attrs: { attrs: {
@ -577,6 +603,7 @@ function initG6() {
target: relation.nodeId, target: relation.nodeId,
sourceKey: attr.key, sourceKey: attr.key,
endArrow: relation.endArrow, endArrow: relation.endArrow,
relationType: relation.relationType,
targetKey: relation.key, targetKey: relation.key,
label: relation.label, label: relation.label,
}); });
@ -627,7 +654,7 @@ function initG6() {
style: { style: {
stroke: '#e2e2e2', stroke: '#e2e2e2',
lineWidth: 4, lineWidth: 4,
endArrow: {path: G6.Arrow.triangle(10,-10,6),fill:'#5B8FF9'}, // endArrow: {path: G6.Arrow.triangle(10, -10, 6), fill: '#5B8FF9'},
}, },
}, },
modes: { modes: {
@ -651,7 +678,7 @@ function initG6() {
watch( watch(
() => props.data, () => props.data,
(val) => { async (val) => {
g6data.value = [] g6data.value = []
if (props.data.tableList && props.data.tableList.length > 0) { if (props.data.tableList && props.data.tableList.length > 0) {
for (let i = 0; i < props.data.tableList.length; i++) { for (let i = 0; i < props.data.tableList.length; i++) {
@ -677,6 +704,7 @@ watch(
let tableKey = relation.source.ssys_cd + "-" + relation.source.mdl_name + "-" + relation.source.tab_eng_name let tableKey = relation.source.ssys_cd + "-" + relation.source.mdl_name + "-" + relation.source.tab_eng_name
let nodeId = relation.target.ssys_cd + "-" + relation.target.mdl_name + "-" + relation.target.tab_eng_name let nodeId = relation.target.ssys_cd + "-" + relation.target.mdl_name + "-" + relation.target.tab_eng_name
let endArrow = relation.endArrow; let endArrow = relation.endArrow;
let relationType = relation.relation_type;
if (g6data.value.length > 0) { if (g6data.value.length > 0) {
for (let j = 0; j < g6data.value.length; j++) { for (let j = 0; j < g6data.value.length; j++) {
if (g6data.value[j].id === tableKey) { if (g6data.value[j].id === tableKey) {
@ -690,18 +718,26 @@ watch(
} }
} }
if (!hasRelation) { if (!hasRelation) {
g6data.value[j].attrs[k].relation.push({ let obj = {
key: key, key: key,
nodeId: nodeId, nodeId: nodeId,
endArrow: endArrow endArrow: endArrow
}) }
if (relationType && relationType.length>0){
obj.relationType = relationType
}
g6data.value[j].attrs[k].relation.push(obj)
} }
} else { } else {
g6data.value[j].attrs[k].relation = [{ let obj = {
key: key, key: key,
nodeId: nodeId, nodeId: nodeId,
endArrow: endArrow endArrow: endArrow
}] }
if (relationType && relationType.length>0){
obj.relationType = relationType
}
g6data.value[j].attrs[k].relation = [obj]
} }
} }
} }
@ -721,11 +757,17 @@ watch(
initG6() initG6()
} }
} }
}, },
{ deep: true,immediate: true } { deep: true,immediate: true }
) )
onMounted(async () => {
[svg11.value, svg11n.value, svg101.value, svg101n.value] = await Promise.all([
initSvg(svgUrl11),
initSvg(svgUrl11n),
initSvg(svgUrl101),
initSvg(svgUrl101n)
])
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

Loading…
Cancel
Save