|
@@ -1,17 +1,26 @@
|
|
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
-import { Graph, Shape } from "@antv/x6";
|
|
|
+import { EventArgs, Graph, Shape } from "@antv/x6";
|
|
|
import { History } from "@antv/x6-plugin-history";
|
|
|
import { Transform } from "@antv/x6-plugin-transform";
|
|
|
import { Scroller } from "@antv/x6-plugin-scroller";
|
|
|
-import { Snapline } from '@antv/x6-plugin-snapline'
|
|
|
+import { Snapline } from "@antv/x6-plugin-snapline";
|
|
|
import { GetAllDesignTables } from "@/api";
|
|
|
import { useRequest } from "umi";
|
|
|
-import type { ColumnRelation, ProjectInfo, RemarkInfo, TableItemType, TopicAreaInfo } from "@/type";
|
|
|
+import type {
|
|
|
+ ColumnItem,
|
|
|
+ ColumnRelation,
|
|
|
+ ProjectInfo,
|
|
|
+ RemarkInfo,
|
|
|
+ TableItemType,
|
|
|
+ TopicAreaInfo,
|
|
|
+} from "@/type";
|
|
|
import { uuid } from "@/utils";
|
|
|
+import { RelationType } from "@/enum";
|
|
|
|
|
|
import "@/components/TableNode";
|
|
|
import "@/components/TopicNode";
|
|
|
import "@/components/NoticeNode";
|
|
|
+import { message } from "antd";
|
|
|
|
|
|
export default function erModel() {
|
|
|
const graphRef = useRef<Graph>();
|
|
@@ -56,12 +65,12 @@ export default function erModel() {
|
|
|
},
|
|
|
highlighting: {
|
|
|
nodeAvailable: {
|
|
|
- name: 'stroke',
|
|
|
+ name: "stroke",
|
|
|
args: {
|
|
|
padding: 4,
|
|
|
attrs: {
|
|
|
- 'stroke-width': 2,
|
|
|
- stroke: 'red',
|
|
|
+ "stroke-width": 2,
|
|
|
+ stroke: "red",
|
|
|
},
|
|
|
},
|
|
|
},
|
|
@@ -71,26 +80,22 @@ export default function erModel() {
|
|
|
allowEdge: false,
|
|
|
allowLoop: false,
|
|
|
router: {
|
|
|
- name: 'normal',
|
|
|
+ name: "normal",
|
|
|
},
|
|
|
createEdge() {
|
|
|
return new Shape.Edge({
|
|
|
attrs: {
|
|
|
line: {
|
|
|
- stroke: '#ff0000',
|
|
|
+ stroke: "#ff0000",
|
|
|
strokeWidth: 1,
|
|
|
strokeDasharray: 5,
|
|
|
- targetMarker: null
|
|
|
+ targetMarker: null,
|
|
|
},
|
|
|
},
|
|
|
data: {
|
|
|
- type: 'refference'
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- validateMagnet(args) {
|
|
|
- console.log('validateMagnet', args);
|
|
|
- return true;
|
|
|
+ type: "refer",
|
|
|
+ },
|
|
|
+ });
|
|
|
},
|
|
|
},
|
|
|
grid: {
|
|
@@ -120,7 +125,6 @@ export default function erModel() {
|
|
|
resizing: {
|
|
|
enabled: true,
|
|
|
},
|
|
|
-
|
|
|
})
|
|
|
);
|
|
|
instance.use(new Scroller());
|
|
@@ -128,26 +132,18 @@ export default function erModel() {
|
|
|
setGraph(instance);
|
|
|
graphRef.current = instance;
|
|
|
|
|
|
- instance.on("node:port:click", (args) => {
|
|
|
- console.log('port click', args)
|
|
|
- })
|
|
|
-
|
|
|
- instance.on("edge:added", (args) => {
|
|
|
- console.log('edge:added', args)
|
|
|
- })
|
|
|
-
|
|
|
- instance.on("edge:connected", (args) => {
|
|
|
- console.log('edge:connected', args)
|
|
|
- })
|
|
|
+ instance.on(
|
|
|
+ "node:change:add:relation",
|
|
|
+ (args: EventArgs["cell:change:*"]) => {
|
|
|
+ console.log("node:change:add:relation", args.current);
|
|
|
+ const { source, target } = args.current;
|
|
|
+ if (source && target) {
|
|
|
+ addRelation(source, target);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
};
|
|
|
|
|
|
- const { data, run, loading } = useRequest(
|
|
|
- () => GetAllDesignTables({ groupType: "" }),
|
|
|
- {
|
|
|
- manual: true,
|
|
|
- }
|
|
|
- );
|
|
|
-
|
|
|
/**
|
|
|
* 添加表
|
|
|
*/
|
|
@@ -232,32 +228,34 @@ export default function erModel() {
|
|
|
zIndex: 3,
|
|
|
ports: {
|
|
|
groups: {
|
|
|
- in: {
|
|
|
+ // 字段名前连接桩
|
|
|
+ columnPort: {
|
|
|
+ markup: [
|
|
|
+ {
|
|
|
+ tagName: "rect",
|
|
|
+ selector: "rect",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ tagName: "circle",
|
|
|
+ selector: "circle",
|
|
|
+ },
|
|
|
+ ],
|
|
|
position: {
|
|
|
- name: 'absolute',
|
|
|
+ name: "absolute",
|
|
|
args: {
|
|
|
x: 12,
|
|
|
- y: 42
|
|
|
- }
|
|
|
- },
|
|
|
- attrs: {
|
|
|
- circle: {
|
|
|
- r: 4,
|
|
|
- magnet: true,
|
|
|
- stroke: '#31d0c6',
|
|
|
- strokeWidth: 2,
|
|
|
- fill: '#fff',
|
|
|
+ y: 42,
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
- }
|
|
|
- }
|
|
|
+ },
|
|
|
+ },
|
|
|
});
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 更新表
|
|
|
- * @param table
|
|
|
+ * @param table
|
|
|
*/
|
|
|
const updateTable = (table: TableItemType) => {
|
|
|
setProject({
|
|
@@ -271,19 +269,27 @@ export default function erModel() {
|
|
|
return item;
|
|
|
}),
|
|
|
});
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
/**
|
|
|
* 删除表
|
|
|
- * @param tableId
|
|
|
+ * @param tableId
|
|
|
*/
|
|
|
const deleteTable = (tableId: string) => {
|
|
|
setProject({
|
|
|
...project,
|
|
|
tables: project.tables.filter((item) => item.table.id !== tableId),
|
|
|
+ relations: project.relations.filter(
|
|
|
+ (item) => item.primaryTable !== tableId && item.foreignTable !== tableId
|
|
|
+ ),
|
|
|
});
|
|
|
graphRef.current?.removeCell(tableId);
|
|
|
- // todo删除关联关系
|
|
|
+ // 删除关联关系
|
|
|
+ project.relations.forEach((item) => {
|
|
|
+ if (item.primaryTable === tableId || item.foreignTable === tableId) {
|
|
|
+ graphRef.current?.removeCell(item.id);
|
|
|
+ }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -296,7 +302,7 @@ export default function erModel() {
|
|
|
name: "主题域_" + (project.topicAreas.length + 1),
|
|
|
style: {
|
|
|
background: "#175e7a",
|
|
|
- }
|
|
|
+ },
|
|
|
};
|
|
|
setProject({
|
|
|
...project,
|
|
@@ -316,8 +322,8 @@ export default function erModel() {
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * 修改主题域
|
|
|
- */
|
|
|
+ * 修改主题域
|
|
|
+ */
|
|
|
const updateTopicArea = (topicArea: TopicAreaInfo) => {
|
|
|
setProject({
|
|
|
...project,
|
|
@@ -328,13 +334,13 @@ export default function erModel() {
|
|
|
return topicArea;
|
|
|
}
|
|
|
return item;
|
|
|
- })
|
|
|
- })
|
|
|
- }
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
/**
|
|
|
* 删除主题域
|
|
|
- * @param topicAreaId
|
|
|
+ * @param topicAreaId
|
|
|
*/
|
|
|
const deleteTopicArea = (topicAreaId: string) => {
|
|
|
setProject({
|
|
@@ -359,7 +365,7 @@ export default function erModel() {
|
|
|
width: 200,
|
|
|
height: 100,
|
|
|
background: "#fcf7ac",
|
|
|
- }
|
|
|
+ },
|
|
|
};
|
|
|
setProject({
|
|
|
...project,
|
|
@@ -377,7 +383,7 @@ export default function erModel() {
|
|
|
zIndex: 1,
|
|
|
});
|
|
|
|
|
|
- notice?.on("change:data", function(args) {
|
|
|
+ notice?.on("change:data", function (args) {
|
|
|
updateRemark(args.current);
|
|
|
});
|
|
|
};
|
|
@@ -387,7 +393,7 @@ export default function erModel() {
|
|
|
*/
|
|
|
const updateRemark = (remark: RemarkInfo) => {
|
|
|
setProject((state) => ({
|
|
|
- ...state || {},
|
|
|
+ ...(state || {}),
|
|
|
remarks: state.remarks.map((item) => {
|
|
|
if (item.id === remark.id) {
|
|
|
const remarkNode = graphRef.current?.getCellById(remark.id);
|
|
@@ -395,13 +401,13 @@ export default function erModel() {
|
|
|
return remark;
|
|
|
}
|
|
|
return item;
|
|
|
- })
|
|
|
+ }),
|
|
|
}));
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 删除备注
|
|
|
- * @param remarkId
|
|
|
+ * @param remarkId
|
|
|
*/
|
|
|
const deleteRemark = (remarkId: string) => {
|
|
|
setProject({
|
|
@@ -411,30 +417,166 @@ export default function erModel() {
|
|
|
graphRef.current?.removeCell(remarkId);
|
|
|
};
|
|
|
|
|
|
- type RelationType = {
|
|
|
+ const getRelations = (project: ProjectInfo, newRelation: ColumnRelation) => {
|
|
|
+ let sourceColumn: ColumnItem | undefined;
|
|
|
+ let targetColumn: ColumnItem | undefined;
|
|
|
+ let sourceTable: TableItemType | undefined;
|
|
|
+ let targetTable: TableItemType | undefined;
|
|
|
+ project.tables.forEach((table) => {
|
|
|
+ if (table.table.id === newRelation.primaryTable) {
|
|
|
+ sourceTable = table;
|
|
|
+ sourceColumn = table.tableColumnList.find(
|
|
|
+ (item) => item.id === newRelation.primaryKey
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (table.table.id === newRelation.foreignTable) {
|
|
|
+ targetTable = table;
|
|
|
+ targetColumn = table.tableColumnList.find(
|
|
|
+ (item) => item.id === newRelation.foreignKey
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ console.log(sourceColumn, targetColumn, newRelation);
|
|
|
+ if (!sourceColumn || !targetColumn) {
|
|
|
+ return {
|
|
|
+ relations: project.relations,
|
|
|
+ canAdd: false,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ if (sourceColumn.type !== targetColumn.type) {
|
|
|
+ message.warning("数据类型不一致");
|
|
|
+ return {
|
|
|
+ relations: project.relations,
|
|
|
+ canAdd: false,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ relations: [
|
|
|
+ ...project.relations, {
|
|
|
+ ...newRelation,
|
|
|
+ name: `${sourceTable?.table.schemaName}_${targetTable?.table.schemaName}_${sourceColumn.schemaName}`,
|
|
|
+ }],
|
|
|
+ canAdd: true,
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ const addRelationEdge = (
|
|
|
+ id: string,
|
|
|
+ source: RelationItem,
|
|
|
+ target: RelationItem
|
|
|
+ ) => {
|
|
|
+ // 添加关系连线
|
|
|
+ const relationEdge = graphRef.current?.addEdge({
|
|
|
+ id,
|
|
|
+ router: {
|
|
|
+ name: "manhattan",
|
|
|
+ args: {
|
|
|
+ direction: "H",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ attrs: {
|
|
|
+ line: {
|
|
|
+ stroke: "#333",
|
|
|
+ strokeWidth: 1,
|
|
|
+ targetMarker: null,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ source: {
|
|
|
+ cell: source.tableId,
|
|
|
+ port: source.columnId + "_port2",
|
|
|
+ anchor: "left",
|
|
|
+ },
|
|
|
+ target: {
|
|
|
+ cell: target.tableId,
|
|
|
+ port: target.columnId + "_port2",
|
|
|
+ anchor: "left",
|
|
|
+ },
|
|
|
+ data: {
|
|
|
+ type: "relation",
|
|
|
+ label: uuid(),
|
|
|
+ },
|
|
|
+ defaultLabel: {
|
|
|
+ markup: [
|
|
|
+ {
|
|
|
+ tagName: "circle",
|
|
|
+ selector: "bg",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ tagName: "text",
|
|
|
+ selector: "txt",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ attrs: {
|
|
|
+ txt: {
|
|
|
+ fill: "#fff",
|
|
|
+ textAnchor: "middle",
|
|
|
+ textVerticalAnchor: "middle",
|
|
|
+ },
|
|
|
+ bg: {
|
|
|
+ ref: "txt",
|
|
|
+ fill: "#333",
|
|
|
+ r: 10,
|
|
|
+ strokeWidth: 0,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ });
|
|
|
+ relationEdge?.appendLabel({
|
|
|
+ attrs: {
|
|
|
+ txt: {
|
|
|
+ text: 1,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ position: {
|
|
|
+ distance: 25,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ relationEdge?.appendLabel({
|
|
|
+ attrs: {
|
|
|
+ txt: {
|
|
|
+ text: 1,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ position: {
|
|
|
+ distance: -25,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ type RelationItem = {
|
|
|
tableId: string;
|
|
|
columnId: string;
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
/**
|
|
|
* 添加关系
|
|
|
*/
|
|
|
- const addRelation = (source: RelationType, target: RelationType) => {
|
|
|
+ const addRelation = (source: RelationItem, target: RelationItem) => {
|
|
|
const newRelation: ColumnRelation = {
|
|
|
id: uuid(),
|
|
|
- name: '',
|
|
|
+ name: "",
|
|
|
primaryKey: source.columnId,
|
|
|
primaryTable: source.tableId,
|
|
|
foreignKey: target.columnId,
|
|
|
- foreignTable: target.columnId,
|
|
|
+ foreignTable: target.tableId,
|
|
|
relationType: 1,
|
|
|
- style: {}
|
|
|
+ style: {},
|
|
|
};
|
|
|
- setProject({
|
|
|
- ...project,
|
|
|
- relations: [...project.relations, newRelation],
|
|
|
+ setProject((state) => {
|
|
|
+ const obj = getRelations(state, newRelation);
|
|
|
+ if (obj.canAdd) {
|
|
|
+ // 添加连线
|
|
|
+ addRelationEdge(newRelation.id, source, target);
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ relations: obj.relations,
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ return state;
|
|
|
+ }
|
|
|
});
|
|
|
- // todo 添加连线
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -452,7 +594,41 @@ export default function erModel() {
|
|
|
}),
|
|
|
};
|
|
|
});
|
|
|
- // todo 更新连线
|
|
|
+ // 更新连线
|
|
|
+ const relationEdge = graphRef.current?.getCellById(relation.id);
|
|
|
+ if (relationEdge?.isEdge()) {
|
|
|
+ relationEdge.setSource({
|
|
|
+ cell: relation.primaryTable,
|
|
|
+ port: relation.primaryKey + "_port2",
|
|
|
+ anchor: "left",
|
|
|
+ });
|
|
|
+ relationEdge.setTarget({
|
|
|
+ cell: relation.foreignTable,
|
|
|
+ port: relation.foreignKey + "_port2",
|
|
|
+ anchor: "left",
|
|
|
+ });
|
|
|
+ // 更新标签
|
|
|
+ relationEdge.setLabelAt(0, {
|
|
|
+ attrs: {
|
|
|
+ txt: {
|
|
|
+ text: relation.relationType === RelationType.OneToOne || relation.relationType === RelationType.OneToMany ? '1' : 'n',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ position: {
|
|
|
+ distance: 25,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ relationEdge.setLabelAt(1, {
|
|
|
+ attrs: {
|
|
|
+ txt: {
|
|
|
+ text: relation.relationType === RelationType.OneToMany || relation.relationType === RelationType.ManyToMany ? 'n' : '1',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ position: {
|
|
|
+ distance: -25,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -463,14 +639,14 @@ export default function erModel() {
|
|
|
...project,
|
|
|
relations: project.relations.filter((item) => item.id !== relationId),
|
|
|
});
|
|
|
- // todo 删除连线
|
|
|
+ // 删除连线
|
|
|
+ graphRef.current?.removeCell(relationId);
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
initGraph,
|
|
|
graph,
|
|
|
graphRef,
|
|
|
- loading,
|
|
|
project,
|
|
|
setProject,
|
|
|
addTable,
|