TableNode.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import React, { useEffect, useMemo, useRef } from "react";
  2. import { register } from "@antv/x6-react-shape";
  3. import { Edge, Graph, Node } from "@antv/x6";
  4. import type { ColumnItem, TableItemType } from "@/type";
  5. import { DATA_TYPE_OPTIONS } from "@/constants";
  6. import { uuid } from "@/utils";
  7. function TableNode({ node, graph }: { node: Node; graph: Graph }) {
  8. const { table, tableColumnList } = node.getData<TableItemType>();
  9. const containerRef = useRef<HTMLDivElement>(null);
  10. useEffect(() => {
  11. const container = containerRef.current;
  12. if (container?.clientHeight) {
  13. node.setSize(container.clientWidth, container.clientHeight);
  14. }
  15. }, [tableColumnList.length]);
  16. const hasListener = useRef(false);
  17. const relationSource = useRef<any>();
  18. useEffect(() => {
  19. if (!hasListener.current) {
  20. hasListener.current = true;
  21. // 参考线添加完成
  22. graph.on("edge:added", (args) => {
  23. if (args.edge.data?.type === "refer") {
  24. relationSource.current = args.edge.source;
  25. }
  26. });
  27. // 参考线移除
  28. graph.on("edge:removed", (args) => {
  29. if (args.edge.data?.type === "refer") {
  30. relationSource.current = undefined;
  31. }
  32. });
  33. graph.on("edge:connected", (args) => {
  34. // 连接完成后删除refer类型的连线
  35. if (args.edge.data?.type === "refer") {
  36. graph.removeEdge(args.edge);
  37. const target: any = args.edge.target;
  38. const source: any = args.edge.source;
  39. if (target?.cell === node.id && target?.port?.includes("port1")) {
  40. node.prop("add:relation", {
  41. source: {
  42. tableId: source?.cell,
  43. columnId: source?.port?.slice(
  44. 0,
  45. source?.port?.indexOf("_port")
  46. ),
  47. },
  48. target: {
  49. tableId: node.id,
  50. columnId: target.port.slice(0, target.port.indexOf("_port")),
  51. },
  52. });
  53. }
  54. }
  55. });
  56. }
  57. }, []);
  58. // 连接桩,1用于开始连接拖拽,2用于关系连线连接
  59. useEffect(() => {
  60. const ports = node.getPorts();
  61. ports?.forEach((item) => {
  62. if (!tableColumnList.find((column) => item.id?.includes(column.id))) {
  63. node.removePort(item);
  64. }
  65. });
  66. tableColumnList.forEach((item, index) => {
  67. if (!ports.find((port) => port.id?.includes(item.id))) {
  68. node.addPort({
  69. id: item.id + "_port1",
  70. group: "columnPort",
  71. args: {
  72. y: 42 + 16 + index * 27,
  73. },
  74. attrs: {
  75. circle: {
  76. r: 4,
  77. magnet: true,
  78. stroke: "#31d0c6",
  79. strokeWidth: 2,
  80. fill: "#fff",
  81. },
  82. },
  83. zIndex: 5,
  84. });
  85. node.addPort({
  86. id: item.id + "_port2",
  87. group: "columnPort",
  88. args: {
  89. x: 0,
  90. y: 42 + index * 27,
  91. },
  92. attrs: {
  93. rect: {
  94. magnet: true,
  95. fill: "rgba(255,255,255,0)",
  96. strokeWidth: 0,
  97. width: node.size().width,
  98. height: 27,
  99. },
  100. },
  101. zIndex: 0,
  102. });
  103. }
  104. });
  105. }, [tableColumnList]);
  106. // 触发添加关系操作
  107. // 字段位置鼠标抬起,判断有没有开始连线操作
  108. const handleColumnMouseUp = (record: ColumnItem) => {
  109. if (relationSource.current) {
  110. node.prop("add:relation", {
  111. source: {
  112. tableId: relationSource.current?.cell,
  113. columnId: relationSource.current?.port.slice(
  114. 0,
  115. relationSource.current.port.indexOf("_port")
  116. ),
  117. },
  118. target: {
  119. tableId: node.id,
  120. columnId: record.id,
  121. },
  122. });
  123. }
  124. };
  125. const ColumnItem = ({ record }: { record: ColumnItem }) => {
  126. const type = DATA_TYPE_OPTIONS.find((item) => item.value === record.type);
  127. return (
  128. <div
  129. className="w-full flex py-4px px-8px"
  130. onMouseUp={() => handleColumnMouseUp(record)}
  131. >
  132. <span className="flex-1 truncate flex items-center justify-between">
  133. <span className="flex items-center">
  134. <span className=" w-6px h-6px rounded-full mr-4px bg-#5684bb inline-block cursor-pointer" />
  135. {record.schemaName}
  136. {record.cn_name ? `(${record.cn_name})` : ""}
  137. </span>
  138. <span>
  139. {record.isUnique ? (
  140. <span className="m-r-4px">
  141. <i className="iconfont icon-key-fill color-#efc553 m-r--4px" />
  142. <span className="text-12px color-#f15359">P</span>
  143. <span className="text-12px color-#8dafd1">F</span>
  144. </span>
  145. ) : (
  146. <span>
  147. <i className="iconfont icon-24_beizhu color-#efc553 m-r-4px text-12px" />
  148. </span>
  149. )}
  150. <span>{type?.label}</span>
  151. </span>
  152. </span>
  153. </div>
  154. );
  155. };
  156. return (
  157. <div
  158. ref={containerRef}
  159. className="w-full border border-1px border-solid border-#333 flex flex-col overflow-hidden rounded-8px"
  160. >
  161. <div
  162. className="w-full h-10px bg-#eee"
  163. style={{ background: table.style?.color || "#eee" }}
  164. ></div>
  165. <div
  166. className="
  167. header
  168. border-b-solid
  169. border-b-1px
  170. border-b-#333
  171. truncate
  172. bg-#e4e4e7
  173. py-4px
  174. font-bold
  175. text-14px
  176. min-h-32px
  177. px-8px
  178. "
  179. >
  180. {table.schemaName}({table.cn_name})
  181. </div>
  182. <div className="bg-#fafafa flex-1">
  183. <div className="field-info">
  184. {tableColumnList.map((item) => {
  185. return <ColumnItem key={item.id} record={item} />;
  186. })}
  187. </div>
  188. </div>
  189. </div>
  190. );
  191. }
  192. register({
  193. shape: "table-node",
  194. component: TableNode,
  195. width: 300,
  196. height: 220,
  197. effect: ["data"],
  198. });