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. 152
      vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelation.vue
  7. 153
      vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelationSql.vue
  8. 456
      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

152
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) { attrs: {
// x1: 0,
listContainer.addShape("circle", { y1: i * itemHeight - itemHeight / 2 + offsetY,
attrs: { x2: width,
x: 0, y2: i * itemHeight - itemHeight / 2 + offsetY,
y: i * itemHeight + offsetY, stroke: 'black',
r: 3, lineWidth: 0.1,
stroke: boxStyle.stroke, cursor: "pointer",
fill: "white", },
radius: 2, name: `item-${Math.floor(startIndex) + i}-top-border`,
lineWidth: 1, });
cursor: "pointer",
},
});
//
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: {

153
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) { attrs: {
// x1: 0,
listContainer.addShape("circle", { y1: i * itemHeight - itemHeight / 2 + offsetY,
attrs: { x2: width,
x: 0, y2: i * itemHeight - itemHeight / 2 + offsetY,
y: i * itemHeight + offsetY, stroke: 'black',
r: 3, lineWidth: 0.1,
stroke: boxStyle.stroke, cursor: "pointer",
fill: "white", },
radius: 2, name: `item-${Math.floor(startIndex) + i}-top-border`,
lineWidth: 1, });
cursor: "pointer",
},
});
//
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: {

456
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) {
function initG6() { // 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;
//
// 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
@ -138,9 +186,9 @@ function initG6() {
edges.forEach(edg => { edges.forEach(edg => {
if (edg.getModel().sourceKey === columnName && edg.getModel().source === item.getModel().id) { if (edg.getModel().sourceKey === columnName && edg.getModel().source === item.getModel().id) {
edg.show() edg.show()
} else if(edg.getModel().targetKey === columnName && edg.getModel().target === item.getModel().id){ } else if (edg.getModel().targetKey === columnName && edg.getModel().target === item.getModel().id) {
edg.show() edg.show()
} else{ } else {
edg.hide() edg.hide()
} }
}) })
@ -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,63 +222,94 @@ 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) {
shape = group.addShape("path", { // SVG
attrs: { if (cfg.relationType) {
stroke: "#5B8FF9", shape = group.addShape("path", {
path: [ attrs: {
["M", startPoint.x, startPoint.y], stroke: "#5B8FF9",
[ path: [
"C", ["M", startPoint.x, startPoint.y],
endPoint.x / 3 + (2 / 3) * startPoint.x, [
startPoint.y, "C",
endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.x / 3 + (2 / 3) * startPoint.x,
endPoint.y, startPoint.y,
endPoint.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.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
}, name: "path-shape",
// 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", 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;
}, },
@ -315,15 +398,15 @@ function initG6() {
icon, icon,
} = cfg; } = cfg;
let currentTableLabel = props.currentTable.tabEngName let currentTableLabel = props.currentTable.tabEngName
if (props.currentTable.tabCnName && props.currentTable.tabCnName.length>0){ if (props.currentTable.tabCnName && props.currentTable.tabCnName.length > 0) {
currentTableLabel += "("+props.currentTable.tabCnName+")" currentTableLabel += "(" + props.currentTable.tabCnName + ")"
}else if (props.currentTable.tabCrrctName && props.currentTable.tabCrrctName.length>0){ } else if (props.currentTable.tabCrrctName && props.currentTable.tabCrrctName.length > 0) {
currentTableLabel += "("+props.currentTable.tabCrrctName+")" currentTableLabel += "(" + props.currentTable.tabCrrctName + ")"
} }
const list = attrs; const list = attrs;
const itemCount = list.length; const itemCount = list.length;
const boxStyle = { const boxStyle = {
stroke: currentTableLabel === cfg.label?"#67C23A":"#096DD9", stroke: currentTableLabel === cfg.label ? "#67C23A" : "#096DD9",
radius: 4, radius: 4,
}; };
const afterList = list.slice( const afterList = list.slice(
@ -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) { attrs: {
// x1: 0,
listContainer.addShape("circle", { y1: i * itemHeight - itemHeight / 2 + offsetY,
attrs: { x2: width,
x: 0, y2: i * itemHeight - itemHeight / 2 + offsetY,
y: i * itemHeight + offsetY, stroke: 'black',
r: 3, lineWidth: 0.1,
stroke: boxStyle.stroke, cursor: "pointer",
fill: "white", },
radius: 2, name: `item-${Math.floor(startIndex) + i}-top-border`,
lineWidth: 1, });
cursor: "pointer",
},
});
//
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: {
@ -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,17 +678,17 @@ 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++) {
let table = props.data.tableList[i] let table = props.data.tableList[i]
let g6Tab = { let g6Tab = {
id: table.ssys_cd+"-"+table.mdl_name+"-"+table.tab_eng_name, id: table.ssys_cd + "-" + table.mdl_name + "-" + table.tab_eng_name,
label: table.tab_eng_name + ((table.tab_cn_name && table.tab_cn_name.length>0)?"("+table.tab_cn_name+")":""), label: table.tab_eng_name + ((table.tab_cn_name && table.tab_cn_name.length > 0) ? "(" + table.tab_cn_name + ")" : ""),
attrs:[], attrs: [],
collapsed:true collapsed: true
} }
for (let j = 0; j < table.column.length; j++) { for (let j = 0; j < table.column.length; j++) {
g6Tab.attrs.push({ g6Tab.attrs.push({
key: table.column[j].fldEngName, key: table.column[j].fldEngName,
@ -670,38 +697,47 @@ watch(
} }
g6data.value.push(g6Tab) g6data.value.push(g6Tab)
} }
if (props.data.relation && props.data.relation.length>0){ if (props.data.relation && props.data.relation.length > 0) {
for (let i = 0; i < props.data.relation.length; i++) { for (let i = 0; i < props.data.relation.length; i++) {
let relation = props.data.relation[i] let relation = props.data.relation[i]
let key = relation.target.fld_eng_name let key = relation.target.fld_eng_name
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;
if (g6data.value.length > 0){ let relationType = relation.relation_type;
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) {
for (let k = 0; k < g6data.value[j].attrs.length; k++) { for (let k = 0; k < g6data.value[j].attrs.length; k++) {
if (g6data.value[j].attrs[k].key === relation.source.fld_eng_name){ if (g6data.value[j].attrs[k].key === relation.source.fld_eng_name) {
if (g6data.value[j].attrs[k].relation && g6data.value[j].attrs[k].relation.length>0){ if (g6data.value[j].attrs[k].relation && g6data.value[j].attrs[k].relation.length > 0) {
let hasRelation = false let hasRelation = false
for (let l = 0; l < g6data.value[j].attrs[k].relation.length; l++) { for (let l = 0; l < g6data.value[j].attrs[k].relation.length; l++) {
if (g6data.value[j].attrs[k].relation[l].key === key && g6data.value[j].attrs[k].relation[l].nodeId === nodeId){ if (g6data.value[j].attrs[k].relation[l].key === key && g6data.value[j].attrs[k].relation[l].nodeId === nodeId) {
hasRelation = true hasRelation = true
} }
} }
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]
} }
} }
} }
@ -713,19 +749,25 @@ watch(
let g6 = document.getElementById("businessRelationG6") let g6 = document.getElementById("businessRelationG6")
const mini_container = document.getElementById("g6mini-container"); const mini_container = document.getElementById("g6mini-container");
if (mini_container){ if (mini_container) {
mini_container.innerHTML='' mini_container.innerHTML = ''
} }
if (g6){ if (g6) {
g6.innerHTML='' g6.innerHTML = ''
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