@robot-admin/naive-ui-components 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. package/README.md +257 -0
  2. package/dist/C_ActionBar-DWN-woTc.css.map +1 -0
  3. package/dist/C_ActionBar.cjs +5 -0
  4. package/dist/C_ActionBar.d.cts +2 -0
  5. package/dist/C_ActionBar.d.ts +2 -0
  6. package/dist/C_ActionBar.js +4 -0
  7. package/dist/C_ActionBar2.js +196 -0
  8. package/dist/C_ActionBar2.js.map +1 -0
  9. package/dist/C_AntV-AFKyK6hH.css.map +1 -0
  10. package/dist/C_AntV.cjs +8 -0
  11. package/dist/C_AntV.d.cts +2 -0
  12. package/dist/C_AntV.d.ts +2 -0
  13. package/dist/C_AntV.js +4 -0
  14. package/dist/C_AntV2.js +3150 -0
  15. package/dist/C_AntV2.js.map +1 -0
  16. package/dist/C_Barcode-P_EFj8dC.css.map +1 -0
  17. package/dist/C_Barcode.cjs +4 -0
  18. package/dist/C_Barcode.d.cts +2 -0
  19. package/dist/C_Barcode.d.ts +2 -0
  20. package/dist/C_Barcode.js +3 -0
  21. package/dist/C_Barcode2.js +68 -0
  22. package/dist/C_Barcode2.js.map +1 -0
  23. package/dist/C_Captcha-C-ef41xw.css.map +1 -0
  24. package/dist/C_Captcha.cjs +4 -0
  25. package/dist/C_Captcha.d.cts +2 -0
  26. package/dist/C_Captcha.d.ts +2 -0
  27. package/dist/C_Captcha.js +3 -0
  28. package/dist/C_Captcha2.js +155 -0
  29. package/dist/C_Captcha2.js.map +1 -0
  30. package/dist/C_Cascade-D9kNsjsV.css.map +1 -0
  31. package/dist/C_Cascade.cjs +4 -0
  32. package/dist/C_Cascade.d.cts +2 -0
  33. package/dist/C_Cascade.d.ts +2 -0
  34. package/dist/C_Cascade.js +3 -0
  35. package/dist/C_Cascade2.js +103 -0
  36. package/dist/C_Cascade2.js.map +1 -0
  37. package/dist/C_City-BCQ4ipiK.css.map +1 -0
  38. package/dist/C_City.cjs +4 -0
  39. package/dist/C_City.d.cts +2 -0
  40. package/dist/C_City.d.ts +2 -0
  41. package/dist/C_City.js +3 -0
  42. package/dist/C_City2.js +841 -0
  43. package/dist/C_City2.js.map +1 -0
  44. package/dist/C_Code-C9kvvEmO.css.map +1 -0
  45. package/dist/C_Code.cjs +5 -0
  46. package/dist/C_Code.d.cts +2 -0
  47. package/dist/C_Code.d.ts +2 -0
  48. package/dist/C_Code.js +4 -0
  49. package/dist/C_Code2.js +346 -0
  50. package/dist/C_Code2.js.map +1 -0
  51. package/dist/C_CollapsePanel-BUJHuYcU.css.map +1 -0
  52. package/dist/C_CollapsePanel.cjs +6 -0
  53. package/dist/C_CollapsePanel.d.cts +2 -0
  54. package/dist/C_CollapsePanel.d.ts +2 -0
  55. package/dist/C_CollapsePanel.js +4 -0
  56. package/dist/C_CollapsePanel2.js +319 -0
  57. package/dist/C_CollapsePanel2.js.map +1 -0
  58. package/dist/C_Cron-yx2Ob4Jl.css.map +1 -0
  59. package/dist/C_Cron.cjs +15 -0
  60. package/dist/C_Cron.d.cts +2 -0
  61. package/dist/C_Cron.d.ts +2 -0
  62. package/dist/C_Cron.js +4 -0
  63. package/dist/C_Cron2.js +1209 -0
  64. package/dist/C_Cron2.js.map +1 -0
  65. package/dist/C_Date.cjs +4 -0
  66. package/dist/C_Date.d.cts +2 -0
  67. package/dist/C_Date.d.ts +2 -0
  68. package/dist/C_Date.js +3 -0
  69. package/dist/C_Date2.js +219 -0
  70. package/dist/C_Date2.js.map +1 -0
  71. package/dist/C_Draggable-C483syRC.css.map +1 -0
  72. package/dist/C_Draggable.cjs +5 -0
  73. package/dist/C_Draggable.d.cts +2 -0
  74. package/dist/C_Draggable.d.ts +2 -0
  75. package/dist/C_Draggable.js +3 -0
  76. package/dist/C_Draggable2.js +295 -0
  77. package/dist/C_Draggable2.js.map +1 -0
  78. package/dist/C_Editor-Bp0SyIEw.css.map +1 -0
  79. package/dist/C_Editor.cjs +4 -0
  80. package/dist/C_Editor.d.cts +2 -0
  81. package/dist/C_Editor.d.ts +2 -0
  82. package/dist/C_Editor.js +3 -0
  83. package/dist/C_Editor2.js +160 -0
  84. package/dist/C_Editor2.js.map +1 -0
  85. package/dist/C_FilePreview-CPqvhoCy.css.map +1 -0
  86. package/dist/C_FilePreview.cjs +6 -0
  87. package/dist/C_FilePreview.d.cts +2 -0
  88. package/dist/C_FilePreview.d.ts +2 -0
  89. package/dist/C_FilePreview.js +3 -0
  90. package/dist/C_FilePreview2.js +1031 -0
  91. package/dist/C_FilePreview2.js.map +1 -0
  92. package/dist/C_Form-Jx7PY3sT.css.map +1 -0
  93. package/dist/C_Form.cjs +15 -0
  94. package/dist/C_Form.d.cts +2 -0
  95. package/dist/C_Form.d.ts +2 -0
  96. package/dist/C_Form.js +4 -0
  97. package/dist/C_Form2.js +2510 -0
  98. package/dist/C_Form2.js.map +1 -0
  99. package/dist/C_FormSearch-DvRgxlRn.css.map +1 -0
  100. package/dist/C_FormSearch.cjs +6 -0
  101. package/dist/C_FormSearch.d.cts +2 -0
  102. package/dist/C_FormSearch.d.ts +2 -0
  103. package/dist/C_FormSearch.js +3 -0
  104. package/dist/C_FormSearch2.js +356 -0
  105. package/dist/C_FormSearch2.js.map +1 -0
  106. package/dist/C_FormulaEditor-DtGkt4T_.css.map +1 -0
  107. package/dist/C_FormulaEditor.cjs +13 -0
  108. package/dist/C_FormulaEditor.d.cts +2 -0
  109. package/dist/C_FormulaEditor.d.ts +2 -0
  110. package/dist/C_FormulaEditor.js +4 -0
  111. package/dist/C_FormulaEditor2.js +1433 -0
  112. package/dist/C_FormulaEditor2.js.map +1 -0
  113. package/dist/C_FullCalendar-BF7H0YIx.css.map +1 -0
  114. package/dist/C_FullCalendar.cjs +9 -0
  115. package/dist/C_FullCalendar.d.cts +2 -0
  116. package/dist/C_FullCalendar.d.ts +2 -0
  117. package/dist/C_FullCalendar.js +3 -0
  118. package/dist/C_FullCalendar2.js +377 -0
  119. package/dist/C_FullCalendar2.js.map +1 -0
  120. package/dist/C_Guide.cjs +4 -0
  121. package/dist/C_Guide.d.cts +2 -0
  122. package/dist/C_Guide.d.ts +2 -0
  123. package/dist/C_Guide.js +3 -0
  124. package/dist/C_Guide2.js +58 -0
  125. package/dist/C_Guide2.js.map +1 -0
  126. package/dist/C_Icon.cjs +4 -0
  127. package/dist/C_Icon.d.cts +2 -0
  128. package/dist/C_Icon.d.ts +2 -0
  129. package/dist/C_Icon.js +3 -0
  130. package/dist/C_Icon2.js +286 -0
  131. package/dist/C_Icon2.js.map +1 -0
  132. package/dist/C_ImageCropper-BVJfUufl.css.map +1 -0
  133. package/dist/C_ImageCropper.cjs +6 -0
  134. package/dist/C_ImageCropper.d.cts +2 -0
  135. package/dist/C_ImageCropper.d.ts +2 -0
  136. package/dist/C_ImageCropper.js +4 -0
  137. package/dist/C_ImageCropper2.js +723 -0
  138. package/dist/C_ImageCropper2.js.map +1 -0
  139. package/dist/C_Language.cjs +4 -0
  140. package/dist/C_Language.d.cts +2 -0
  141. package/dist/C_Language.d.ts +2 -0
  142. package/dist/C_Language.js +3 -0
  143. package/dist/C_Language2.js +72 -0
  144. package/dist/C_Language2.js.map +1 -0
  145. package/dist/C_Map-DpzeuWdX.css.map +1 -0
  146. package/dist/C_Map.cjs +7 -0
  147. package/dist/C_Map.d.cts +2 -0
  148. package/dist/C_Map.d.ts +2 -0
  149. package/dist/C_Map.js +3 -0
  150. package/dist/C_Map2.js +199 -0
  151. package/dist/C_Map2.js.map +1 -0
  152. package/dist/C_Markdown-BEjxknqd.css.map +1 -0
  153. package/dist/C_Markdown.cjs +4 -0
  154. package/dist/C_Markdown.d.cts +2 -0
  155. package/dist/C_Markdown.d.ts +2 -0
  156. package/dist/C_Markdown.js +3 -0
  157. package/dist/C_Markdown2.js +186 -0
  158. package/dist/C_Markdown2.js.map +1 -0
  159. package/dist/C_NotificationCenter-0l3TY2Gn.css.map +1 -0
  160. package/dist/C_NotificationCenter.cjs +20 -0
  161. package/dist/C_NotificationCenter.d.cts +2 -0
  162. package/dist/C_NotificationCenter.d.ts +2 -0
  163. package/dist/C_NotificationCenter.js +4 -0
  164. package/dist/C_NotificationCenter2.js +1383 -0
  165. package/dist/C_NotificationCenter2.js.map +1 -0
  166. package/dist/C_Progress.cjs +4 -0
  167. package/dist/C_Progress.d.cts +2 -0
  168. package/dist/C_Progress.d.ts +2 -0
  169. package/dist/C_Progress.js +3 -0
  170. package/dist/C_Progress2.js +103 -0
  171. package/dist/C_Progress2.js.map +1 -0
  172. package/dist/C_QRCode-DbdiAIPg.css.map +1 -0
  173. package/dist/C_QRCode.cjs +5 -0
  174. package/dist/C_QRCode.d.cts +2 -0
  175. package/dist/C_QRCode.d.ts +2 -0
  176. package/dist/C_QRCode.js +3 -0
  177. package/dist/C_QRCode2.js +218 -0
  178. package/dist/C_QRCode2.js.map +1 -0
  179. package/dist/C_Signature-zhHCbra9.css.map +1 -0
  180. package/dist/C_Signature.cjs +8 -0
  181. package/dist/C_Signature.d.cts +2 -0
  182. package/dist/C_Signature.d.ts +2 -0
  183. package/dist/C_Signature.js +4 -0
  184. package/dist/C_Signature2.js +618 -0
  185. package/dist/C_Signature2.js.map +1 -0
  186. package/dist/C_SplitPane-C6sBsfKY.css.map +1 -0
  187. package/dist/C_SplitPane.cjs +6 -0
  188. package/dist/C_SplitPane.d.cts +2 -0
  189. package/dist/C_SplitPane.d.ts +2 -0
  190. package/dist/C_SplitPane.js +4 -0
  191. package/dist/C_SplitPane2.js +356 -0
  192. package/dist/C_SplitPane2.js.map +1 -0
  193. package/dist/C_Steps-CODHN5Hs.css.map +1 -0
  194. package/dist/C_Steps.cjs +4 -0
  195. package/dist/C_Steps.d.cts +2 -0
  196. package/dist/C_Steps.d.ts +2 -0
  197. package/dist/C_Steps.js +3 -0
  198. package/dist/C_Steps2.js +82 -0
  199. package/dist/C_Steps2.js.map +1 -0
  200. package/dist/C_Table-DSNsntmT.css.map +1 -0
  201. package/dist/C_Table.cjs +19 -0
  202. package/dist/C_Table.d.cts +2 -0
  203. package/dist/C_Table.d.ts +2 -0
  204. package/dist/C_Table.js +5 -0
  205. package/dist/C_Table2.js +3009 -0
  206. package/dist/C_Table2.js.map +1 -0
  207. package/dist/C_Theme.cjs +4 -0
  208. package/dist/C_Theme.d.cts +2 -0
  209. package/dist/C_Theme.d.ts +2 -0
  210. package/dist/C_Theme.js +3 -0
  211. package/dist/C_Theme2.js +60 -0
  212. package/dist/C_Theme2.js.map +1 -0
  213. package/dist/C_Time-BvZLYraL.css.map +1 -0
  214. package/dist/C_Time.cjs +5 -0
  215. package/dist/C_Time.d.cts +2 -0
  216. package/dist/C_Time.d.ts +2 -0
  217. package/dist/C_Time.js +3 -0
  218. package/dist/C_Time2.js +199 -0
  219. package/dist/C_Time2.js.map +1 -0
  220. package/dist/C_Tree-0GDv--jX.css.map +1 -0
  221. package/dist/C_Tree.cjs +7 -0
  222. package/dist/C_Tree.d.cts +2 -0
  223. package/dist/C_Tree.d.ts +2 -0
  224. package/dist/C_Tree.js +4 -0
  225. package/dist/C_Tree2.js +441 -0
  226. package/dist/C_Tree2.js.map +1 -0
  227. package/dist/C_Upload-BXd3YYLx.css.map +1 -0
  228. package/dist/C_Upload.cjs +12 -0
  229. package/dist/C_Upload.d.cts +2 -0
  230. package/dist/C_Upload.d.ts +2 -0
  231. package/dist/C_Upload.js +4 -0
  232. package/dist/C_Upload2.js +1388 -0
  233. package/dist/C_Upload2.js.map +1 -0
  234. package/dist/C_VideoPlayer-DYG3RL0Q.css.map +1 -0
  235. package/dist/C_VideoPlayer.cjs +23 -0
  236. package/dist/C_VideoPlayer.d.cts +2 -0
  237. package/dist/C_VideoPlayer.d.ts +2 -0
  238. package/dist/C_VideoPlayer.js +3 -0
  239. package/dist/C_VideoPlayer2.js +1932 -0
  240. package/dist/C_VideoPlayer2.js.map +1 -0
  241. package/dist/C_VtableGantt-fhItIiHE.css.map +1 -0
  242. package/dist/C_VtableGantt.cjs +6 -0
  243. package/dist/C_VtableGantt.d.cts +2 -0
  244. package/dist/C_VtableGantt.d.ts +2 -0
  245. package/dist/C_VtableGantt.js +4 -0
  246. package/dist/C_VtableGantt2.js +873 -0
  247. package/dist/C_VtableGantt2.js.map +1 -0
  248. package/dist/C_WaterFall-8sQDFXKg.css.map +1 -0
  249. package/dist/C_WaterFall.cjs +13 -0
  250. package/dist/C_WaterFall.d.cts +2 -0
  251. package/dist/C_WaterFall.d.ts +2 -0
  252. package/dist/C_WaterFall.js +3 -0
  253. package/dist/C_WaterFall2.js +365 -0
  254. package/dist/C_WaterFall2.js.map +1 -0
  255. package/dist/C_WorkFlow-J-dyIuh9.css.map +1 -0
  256. package/dist/C_WorkFlow.cjs +8 -0
  257. package/dist/C_WorkFlow.d.cts +2 -0
  258. package/dist/C_WorkFlow.d.ts +2 -0
  259. package/dist/C_WorkFlow.js +4 -0
  260. package/dist/C_WorkFlow2.js +1984 -0
  261. package/dist/C_WorkFlow2.js.map +1 -0
  262. package/dist/chunk.js +22 -0
  263. package/dist/city.js +4817 -0
  264. package/dist/city.js.map +1 -0
  265. package/dist/constants.d.ts +273 -0
  266. package/dist/constants.d.ts.map +1 -0
  267. package/dist/constants2.d.ts +178 -0
  268. package/dist/constants2.d.ts.map +1 -0
  269. package/dist/constants3.d.ts +475 -0
  270. package/dist/constants3.d.ts.map +1 -0
  271. package/dist/constants4.d.ts +430 -0
  272. package/dist/constants4.d.ts.map +1 -0
  273. package/dist/constants5.d.ts +4283 -0
  274. package/dist/constants5.d.ts.map +1 -0
  275. package/dist/data.d.ts +67 -0
  276. package/dist/data.d.ts.map +1 -0
  277. package/dist/export-helper.js +9 -0
  278. package/dist/index.cjs +409 -0
  279. package/dist/index.d.cts +96 -0
  280. package/dist/index.d.cts.map +1 -0
  281. package/dist/index.d.ts +103 -0
  282. package/dist/index.d.ts.map +1 -0
  283. package/dist/index.js +230 -0
  284. package/dist/index.js.map +1 -0
  285. package/dist/index.vue.d.ts +80 -0
  286. package/dist/index.vue.d.ts.map +1 -0
  287. package/dist/index10.vue.d.ts +72 -0
  288. package/dist/index10.vue.d.ts.map +1 -0
  289. package/dist/index11.vue.d.ts +26 -0
  290. package/dist/index11.vue.d.ts.map +1 -0
  291. package/dist/index12.vue.d.ts +81 -0
  292. package/dist/index12.vue.d.ts.map +1 -0
  293. package/dist/index13.vue.d.ts +55 -0
  294. package/dist/index13.vue.d.ts.map +1 -0
  295. package/dist/index14.vue.d.ts +33 -0
  296. package/dist/index14.vue.d.ts.map +1 -0
  297. package/dist/index15.vue.d.ts +18 -0
  298. package/dist/index15.vue.d.ts.map +1 -0
  299. package/dist/index16.vue.d.ts +662 -0
  300. package/dist/index16.vue.d.ts.map +1 -0
  301. package/dist/index2.vue.d.ts +38 -0
  302. package/dist/index2.vue.d.ts.map +1 -0
  303. package/dist/index3.vue.d.ts +45 -0
  304. package/dist/index3.vue.d.ts.map +1 -0
  305. package/dist/index4.vue.d.ts +31 -0
  306. package/dist/index4.vue.d.ts.map +1 -0
  307. package/dist/index5.vue.d.ts +35 -0
  308. package/dist/index5.vue.d.ts.map +1 -0
  309. package/dist/index6.vue.d.ts +48 -0
  310. package/dist/index6.vue.d.ts.map +1 -0
  311. package/dist/index7.vue.d.ts +56 -0
  312. package/dist/index7.vue.d.ts.map +1 -0
  313. package/dist/index8.vue.d.ts +41 -0
  314. package/dist/index8.vue.d.ts.map +1 -0
  315. package/dist/index9.vue.d.ts +30 -0
  316. package/dist/index9.vue.d.ts.map +1 -0
  317. package/dist/storage.js +31 -0
  318. package/dist/storage.js.map +1 -0
  319. package/dist/style.css +7725 -0
  320. package/dist/useCalendarEvents.d.ts +148 -0
  321. package/dist/useCalendarEvents.d.ts.map +1 -0
  322. package/dist/useCollapsePanel.d.ts +132 -0
  323. package/dist/useCollapsePanel.d.ts.map +1 -0
  324. package/dist/useCropperCore.d.ts +102 -0
  325. package/dist/useCropperCore.d.ts.map +1 -0
  326. package/dist/useDraggableLayout.d.ts +194 -0
  327. package/dist/useDraggableLayout.d.ts.map +1 -0
  328. package/dist/useDynamicFormState.d.ts +4248 -0
  329. package/dist/useDynamicFormState.d.ts.map +1 -0
  330. package/dist/useEdgeInteraction.d.ts +7614 -0
  331. package/dist/useEdgeInteraction.d.ts.map +1 -0
  332. package/dist/useFullscreen.d.ts +166 -0
  333. package/dist/useFullscreen.d.ts.map +1 -0
  334. package/dist/useInfiniteScroll.d.ts +169 -0
  335. package/dist/useInfiniteScroll.d.ts.map +1 -0
  336. package/dist/useModalEdit.d.ts +960 -0
  337. package/dist/useModalEdit.d.ts.map +1 -0
  338. package/dist/useQRCode.d.ts +87 -0
  339. package/dist/useQRCode.d.ts.map +1 -0
  340. package/dist/useSearchState.d.ts +180 -0
  341. package/dist/useSearchState.d.ts.map +1 -0
  342. package/dist/useSignatureHistory.d.ts +189 -0
  343. package/dist/useSignatureHistory.d.ts.map +1 -0
  344. package/dist/useSplitResize.d.ts +158 -0
  345. package/dist/useSplitResize.d.ts.map +1 -0
  346. package/dist/useTimeSelection.d.ts +105 -0
  347. package/dist/useTimeSelection.d.ts.map +1 -0
  348. package/dist/useTreeOperations.d.ts +183 -0
  349. package/dist/useTreeOperations.d.ts.map +1 -0
  350. package/dist/useWorkflowValidation.d.ts +1052 -0
  351. package/dist/useWorkflowValidation.d.ts.map +1 -0
  352. package/package.json +342 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_AntV2.js","names":["show","$emit","showToolbar","show","showToolbar","show","showToolbar"],"sources":["../src/components/C_AntV/composables/useGraphBase.ts","../src/components/C_AntV/utils/exportUtils.ts","../src/components/C_AntV/composables/useGraphExport.ts","../src/components/C_AntV/layout/ER/data.ts","../src/components/C_AntV/layout/ER/components/ERTableEditor.vue","../src/components/C_AntV/layout/ER/components/ERTableEditor.vue","../src/components/C_AntV/layout/ER/components/ERTableEditor.vue","../src/components/C_AntV/layout/ER/index.vue","../src/components/C_AntV/layout/ER/index.vue","../src/components/C_AntV/layout/ER/index.vue","../src/components/C_AntV/composables/useEdgeInteraction.ts","../src/components/C_AntV/layout/BPMN/data.ts","../src/components/C_AntV/layout/BPMN/components/BPMNPropertyEditor.vue","../src/components/C_AntV/layout/BPMN/components/BPMNPropertyEditor.vue","../src/components/C_AntV/layout/BPMN/components/BPMNPropertyEditor.vue","../src/components/C_AntV/layout/BPMN/index.vue","../src/components/C_AntV/layout/BPMN/index.vue","../src/components/C_AntV/layout/BPMN/index.vue","../src/components/C_AntV/layout/UML/components/UMLClassEditor.vue","../src/components/C_AntV/layout/UML/components/UMLClassEditor.vue","../src/components/C_AntV/layout/UML/components/UMLClassEditor.vue","../src/components/C_AntV/layout/UML/data.ts","../src/components/C_AntV/layout/UML/index.vue","../src/components/C_AntV/layout/UML/index.vue","../src/components/C_AntV/layout/UML/index.vue","../src/components/C_AntV/index.vue","../src/components/C_AntV/index.vue","../src/components/C_AntV/index.vue"],"sourcesContent":["import { Graph } from \"@antv/x6\";\r\nimport { ref, watch, nextTick, onUnmounted } from \"vue\";\r\nimport type { ComputedRef, Ref } from \"vue\";\r\n\r\n/**\r\n * 获取主题相关的颜色配置\r\n */\r\nfunction getThemeColors(isDark: boolean) {\r\n return {\r\n background: isDark ? \"#18181c\" : \"#f5f5f5\",\r\n gridPrimary: isDark ? \"rgba(255, 255, 255, 0.08)\" : \"#eee\",\r\n gridSecondary: isDark ? \"rgba(255, 255, 255, 0.04)\" : \"#ddd\",\r\n };\r\n}\r\n\r\n/**\r\n * 默认的图表配置选项\r\n */\r\nfunction getDefaultOptions(isDark: boolean = false) {\r\n const colors = getThemeColors(isDark);\r\n\r\n return {\r\n background: { color: colors.background },\r\n grid: {\r\n visible: true,\r\n type: \"doubleMesh\",\r\n args: [\r\n { color: colors.gridPrimary, thickness: 1 },\r\n { color: colors.gridSecondary, thickness: 1, factor: 4 },\r\n ],\r\n },\r\n mousewheel: {\r\n enabled: true,\r\n zoomAtMousePosition: true,\r\n modifiers: \"ctrl\",\r\n minScale: 0.5,\r\n maxScale: 2,\r\n },\r\n selecting: {\r\n enabled: true,\r\n rubberband: true,\r\n movable: true,\r\n showNodeSelectionBox: true,\r\n },\r\n resizing: true,\r\n rotating: true,\r\n snapline: true,\r\n keyboard: true,\r\n clipboard: true,\r\n };\r\n}\r\n\r\n/**\r\n * 获取容器的尺寸\r\n */\r\nfunction getContainerSize(container: HTMLElement | undefined) {\r\n if (!container) return { width: 0, height: 0 };\r\n return {\r\n width: container.clientWidth || container.offsetWidth || 800,\r\n height: container.clientHeight || container.offsetHeight || 600,\r\n };\r\n}\r\n\r\n/**\r\n * 创建和管理 AntV X6 图表实例的组合式 API\r\n */\r\nexport function useGraphBase(\r\n containerRef: Ref<HTMLElement | undefined>,\r\n isDarkRef?: Ref<boolean> | ComputedRef<boolean>,\r\n) {\r\n const MAX_RETRY = 10;\r\n let retryCount = 0;\r\n\r\n const graph: Ref<any> = ref(null);\r\n const loading = ref(false);\r\n\r\n /**\r\n * 初始化图表实例\r\n */\r\n const initGraph = async (options: any = {}) => {\r\n await nextTick();\r\n loading.value = true;\r\n\r\n try {\r\n if (!containerRef.value) return;\r\n\r\n // 销毁旧实例\r\n if (graph.value) {\r\n graph.value.dispose();\r\n graph.value = null;\r\n }\r\n\r\n const { width, height } = getContainerSize(containerRef.value);\r\n\r\n // 容器尺寸为0,延迟重试(最多 10 次)\r\n if (width === 0 || height === 0) {\r\n retryCount++;\r\n if (retryCount < MAX_RETRY) {\r\n setTimeout(() => initGraph(options), 100);\r\n }\r\n return;\r\n }\r\n retryCount = 0;\r\n\r\n const isDark = isDarkRef?.value ?? false;\r\n const defaultOptions = getDefaultOptions(isDark);\r\n\r\n const finalOptions = {\r\n container: containerRef.value,\r\n width,\r\n height,\r\n ...defaultOptions,\r\n ...options,\r\n };\r\n\r\n graph.value = new Graph(finalOptions);\r\n } catch {\r\n // 初始化失败静默处理\r\n } finally {\r\n loading.value = false;\r\n }\r\n };\r\n\r\n /**\r\n * 更新主题\r\n */\r\n function updateTheme(isDark: boolean) {\r\n if (!graph.value) return;\r\n\r\n const colors = getThemeColors(isDark);\r\n\r\n // 更新背景色\r\n graph.value.drawBackground({ color: colors.background });\r\n\r\n // 更新网格颜色\r\n graph.value.drawGrid({\r\n type: \"doubleMesh\",\r\n args: [\r\n { color: colors.gridPrimary, thickness: 1 },\r\n { color: colors.gridSecondary, thickness: 1, factor: 4 },\r\n ],\r\n });\r\n }\r\n\r\n // 监听主题变化\r\n if (isDarkRef) {\r\n watch(isDarkRef, (isDark) => {\r\n updateTheme(isDark);\r\n });\r\n }\r\n\r\n /**\r\n * 销毁当前图表实例\r\n */\r\n function destroyGraph() {\r\n if (graph.value) {\r\n graph.value.dispose();\r\n graph.value = null;\r\n }\r\n }\r\n\r\n /**\r\n * 将图表内容居中显示\r\n */\r\n function centerContent() {\r\n graph.value?.centerContent();\r\n }\r\n\r\n /**\r\n * 自适应缩放图表以适应容器\r\n */\r\n function zoomToFit() {\r\n graph.value?.zoomToFit({ padding: 20, maxScale: 1 });\r\n }\r\n\r\n /**\r\n * 按指定因子缩放图表\r\n */\r\n function zoom(factor: number) {\r\n graph.value?.zoom(factor);\r\n }\r\n\r\n /**\r\n * 调整图表尺寸以适应容器变化\r\n */\r\n function resizeGraph() {\r\n if (graph.value && containerRef.value) {\r\n const { width, height } = getContainerSize(containerRef.value);\r\n graph.value.resize(width, height);\r\n }\r\n }\r\n\r\n // 组件卸载时自动销毁图表\r\n onUnmounted(destroyGraph);\r\n\r\n return {\r\n graph,\r\n loading,\r\n initGraph,\r\n destroyGraph,\r\n centerContent,\r\n zoomToFit,\r\n zoom,\r\n resizeGraph,\r\n updateTheme,\r\n };\r\n}\r\n","import type { Graph } from \"@antv/x6\";\r\nimport html2canvas from \"html2canvas\";\r\n\r\n/**\r\n * 下载 Blob 文件到本地\r\n * @param blob - Blob 对象\r\n * @param filename - 文件名\r\n */\r\nfunction downloadBlob(blob: Blob, filename: string): void {\r\n const url = URL.createObjectURL(blob);\r\n const link = document.createElement(\"a\");\r\n link.href = url;\r\n link.download = filename;\r\n link.style.display = \"none\";\r\n document.body.appendChild(link);\r\n link.click();\r\n document.body.removeChild(link);\r\n URL.revokeObjectURL(url);\r\n}\r\n\r\n/**\r\n * 导出数据为 JSON 文件\r\n * @param data - 要导出的数据\r\n * @param filename - 文件名,默认 'diagram.json'\r\n */\r\nexport function exportJSON(data: any, filename = \"diagram.json\"): void {\r\n const jsonString = JSON.stringify(data, null, 2);\r\n const blob = new Blob([jsonString], { type: \"application/json\" });\r\n downloadBlob(blob, filename);\r\n}\r\n\r\n/**\r\n * 导出图表为 PNG 图片(基于 html2canvas 截图方案)\r\n * @param graph - AntV X6 图表实例\r\n * @param filename - 文件名,默认 'diagram.png'\r\n * @param options - 导出选项\r\n */\r\nexport async function exportPNG(\r\n graph: Graph,\r\n filename = \"diagram.png\",\r\n options: { backgroundColor?: string; scale?: number } = {},\r\n): Promise<void> {\r\n const { backgroundColor = \"#ffffff\", scale = 2 } = options;\r\n const { container } = graph;\r\n\r\n const canvas = await html2canvas(container, {\r\n backgroundColor,\r\n scale,\r\n useCORS: true,\r\n logging: false,\r\n });\r\n\r\n const blob = await canvasToBlob(canvas, \"image/png\");\r\n downloadBlob(blob, filename);\r\n}\r\n\r\n/**\r\n * 导出图表为 SVG 矢量图(从容器内 SVG 元素序列化)\r\n * @param graph - AntV X6 图表实例\r\n * @param filename - 文件名,默认 'diagram.svg'\r\n */\r\nexport function exportSVG(graph: Graph, filename = \"diagram.svg\"): void {\r\n const svgElement = graph.container.querySelector(\"svg\");\r\n if (!svgElement) {\r\n throw new Error(\"SVG导出失败: 未找到图表 SVG 元素\");\r\n }\r\n\r\n // 克隆以避免修改原始 DOM\r\n const cloned = svgElement.cloneNode(true) as SVGSVGElement;\r\n\r\n // 确保有 xmlns 属性\r\n if (!cloned.getAttribute(\"xmlns\")) {\r\n cloned.setAttribute(\"xmlns\", \"http://www.w3.org/2000/svg\");\r\n }\r\n if (!cloned.getAttribute(\"xmlns:xlink\")) {\r\n cloned.setAttribute(\"xmlns:xlink\", \"http://www.w3.org/1999/xlink\");\r\n }\r\n\r\n const serializer = new XMLSerializer();\r\n const svgString = serializer.serializeToString(cloned);\r\n const blob = new Blob([svgString], { type: \"image/svg+xml;charset=utf-8\" });\r\n downloadBlob(blob, filename);\r\n}\r\n\r\n/**\r\n * 将 Canvas 转换为 Blob\r\n */\r\nfunction canvasToBlob(\r\n canvas: HTMLCanvasElement,\r\n mimeType: string,\r\n quality = 1,\r\n): Promise<Blob> {\r\n return new Promise((resolve, reject) => {\r\n canvas.toBlob(\r\n (blob) => {\r\n if (blob) {\r\n resolve(blob);\r\n } else {\r\n reject(new Error(\"Canvas 转 Blob 失败\"));\r\n }\r\n },\r\n mimeType,\r\n quality,\r\n );\r\n });\r\n}\r\n\r\n/**\r\n * 复制内容到剪贴板\r\n * @param content - 要复制的内容\r\n * @returns 是否复制成功\r\n */\r\nexport async function copyToClipboard(content: string): Promise<boolean> {\r\n try {\r\n if (!navigator.clipboard) {\r\n return fallbackCopyToClipboard(content);\r\n }\r\n\r\n await navigator.clipboard.writeText(content);\r\n return true;\r\n } catch {\r\n return fallbackCopyToClipboard(content);\r\n }\r\n}\r\n\r\n/**\r\n * 备用的复制到剪贴板方法(适用于不支持 Clipboard API 的浏览器)\r\n */\r\nfunction fallbackCopyToClipboard(content: string): boolean {\r\n try {\r\n const textArea = document.createElement(\"textarea\");\r\n textArea.value = content;\r\n textArea.style.position = \"fixed\";\r\n textArea.style.left = \"-999999px\";\r\n textArea.style.top = \"-999999px\";\r\n\r\n document.body.appendChild(textArea);\r\n textArea.focus();\r\n textArea.select();\r\n\r\n const result = document.execCommand(\"copy\");\r\n document.body.removeChild(textArea);\r\n return result;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","import type { Ref } from 'vue'\r\nimport type { Graph } from '@antv/x6'\r\nimport { useMessage } from 'naive-ui'\r\nimport { exportJSON, exportPNG, exportSVG } from '../utils/exportUtils'\r\n\r\n/** 导出下拉菜单选项(三个布局共享) */\r\nexport const EXPORT_OPTIONS = [\r\n { label: '导出PNG', key: 'png' },\r\n { label: '导出SVG', key: 'svg' },\r\n { label: '导出JSON', key: 'json' },\r\n] as const\r\n\r\n/**\r\n * 图表导出 composable — 统一 PNG / SVG / JSON 导出逻辑\r\n * @param graph - X6 Graph 实例引用\r\n * @param filenamePrefix - 导出文件名前缀,默认 'diagram'\r\n */\r\nexport function useGraphExport(\r\n graph: Ref<Graph | null>,\r\n filenamePrefix = 'diagram'\r\n) {\r\n const message = useMessage()\r\n /**\r\n * 处理导出操作\r\n * @param key - 导出格式 'png' | 'svg' | 'json'\r\n * @param getData - 获取当前图表数据的函数(用于 JSON 导出)\r\n */\r\n const handleExport = async (key: string, getData?: () => any) => {\r\n if (!graph.value) return\r\n\r\n try {\r\n switch (key) {\r\n case 'png':\r\n await exportPNG(graph.value, `${filenamePrefix}.png`)\r\n break\r\n case 'svg':\r\n exportSVG(graph.value, `${filenamePrefix}.svg`)\r\n break\r\n case 'json':\r\n if (getData) {\r\n exportJSON(getData(), `${filenamePrefix}.json`)\r\n }\r\n break\r\n }\r\n } catch (error) {\r\n console.error(`[useGraphExport] 导出失败 (${key}):`, error)\r\n message.error(`导出${key.toUpperCase()}失败,请重试`)\r\n }\r\n }\r\n\r\n return { exportOptions: EXPORT_OPTIONS, handleExport }\r\n}\r\n","/* 原有的常量数据 */\r\nexport const fieldTypes = [\r\n { label: \"BIGINT\", value: \"BIGINT\" },\r\n { label: \"INT\", value: \"INT\" },\r\n { label: \"SMALLINT\", value: \"SMALLINT\" },\r\n { label: \"TINYINT\", value: \"TINYINT\" },\r\n { label: \"VARCHAR(50)\", value: \"VARCHAR(50)\" },\r\n { label: \"VARCHAR(100)\", value: \"VARCHAR(100)\" },\r\n { label: \"VARCHAR(255)\", value: \"VARCHAR(255)\" },\r\n { label: \"CHAR(10)\", value: \"CHAR(10)\" },\r\n { label: \"TEXT\", value: \"TEXT\" },\r\n { label: \"LONGTEXT\", value: \"LONGTEXT\" },\r\n { label: \"DATETIME\", value: \"DATETIME\" },\r\n { label: \"TIMESTAMP\", value: \"TIMESTAMP\" },\r\n { label: \"DATE\", value: \"DATE\" },\r\n { label: \"TIME\", value: \"TIME\" },\r\n { label: \"DECIMAL(10,2)\", value: \"DECIMAL(10,2)\" },\r\n { label: \"FLOAT\", value: \"FLOAT\" },\r\n { label: \"DOUBLE\", value: \"DOUBLE\" },\r\n { label: \"BOOLEAN\", value: \"BOOLEAN\" },\r\n { label: \"JSON\", value: \"JSON\" },\r\n];\r\n","<!--\r\n ERTableEditor — ER 表编辑器抽屉\r\n 负责:表名/注释编辑 + 字段 CRUD + 主键处理\r\n-->\r\n<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"600\"\r\n placement=\"right\"\r\n @update:show=\"$emit('update:show', $event)\"\r\n >\r\n <NDrawerContent :title=\"`编辑表: ${editingTable?.name || '新表'}`\" closable>\r\n <div class=\"table-editor\" v-if=\"editingTable\">\r\n <NForm :model=\"editingTable\" label-placement=\"top\">\r\n <NFormItem label=\"表名\">\r\n <NInput\r\n v-model:value=\"editingTable.name\"\r\n placeholder=\"请输入表名\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"表注释\">\r\n <NInput v-model:value=\"editingTable.comment\" placeholder=\"表注释\" />\r\n </NFormItem>\r\n\r\n <NDivider>字段列表</NDivider>\r\n\r\n <div class=\"fields-container\">\r\n <NCard\r\n v-for=\"(field, index) in editingTable.fields\"\r\n :key=\"index\"\r\n size=\"small\"\r\n class=\"field-card\"\r\n >\r\n <template #header>\r\n <div class=\"field-header\">\r\n <span>#{{ index + 1 }} {{ field.name || \"新字段\" }}</span>\r\n <NButton\r\n @click=\"removeField(index)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n :disabled=\"editingTable.fields.length <= 1\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n\r\n <NGrid :cols=\"2\" :x-gap=\"12\">\r\n <NGi>\r\n <NFormItem label=\"字段名\" size=\"small\">\r\n <NInput\r\n v-model:value=\"field.name\"\r\n placeholder=\"字段名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NGi>\r\n <NGi>\r\n <NFormItem label=\"类型\" size=\"small\">\r\n <NSelect\r\n v-model:value=\"field.type\"\r\n :options=\"fieldTypes\"\r\n size=\"small\"\r\n filterable\r\n placeholder=\"选择类型\"\r\n />\r\n </NFormItem>\r\n </NGi>\r\n </NGrid>\r\n\r\n <NSpace style=\"margin-top: 12px\">\r\n <NCheckbox\r\n v-model:checked=\"field.isPrimaryKey\"\r\n @update:checked=\"handlePrimaryKey(field, $event)\"\r\n >\r\n 主键\r\n </NCheckbox>\r\n <NCheckbox v-model:checked=\"field.isRequired\">必填</NCheckbox>\r\n <NCheckbox v-model:checked=\"field.isForeignKey\">外键</NCheckbox>\r\n </NSpace>\r\n\r\n <NFormItem label=\"注释\" size=\"small\" style=\"margin-top: 12px\">\r\n <NInput\r\n v-model:value=\"field.comment\"\r\n placeholder=\"字段注释\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NCard>\r\n\r\n <NButton\r\n @click=\"addField\"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n style=\"margin-top: 16px\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加字段\r\n </NButton>\r\n </div>\r\n </NForm>\r\n </div>\r\n\r\n <template #footer>\r\n <NSpace justify=\"end\">\r\n <NButton @click=\"$emit('update:show', false)\">取消</NButton>\r\n <NButton @click=\"$emit('save')\" type=\"primary\">保存</NButton>\r\n </NSpace>\r\n </template>\r\n </NDrawerContent>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n NDrawer,\r\n NDrawerContent,\r\n NForm,\r\n NFormItem,\r\n NInput,\r\n NSelect,\r\n NCard,\r\n NGrid,\r\n NGi,\r\n NSpace,\r\n NCheckbox,\r\n NButton,\r\n NDivider,\r\n} from \"naive-ui\";\r\nimport type { ERTable, ERField } from \"../../types\";\r\nimport { fieldTypes } from \"../data\";\r\nimport C_Icon from \"../../../../C_Icon/index.vue\";\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingTable = defineModel<ERTable>(\"editingTable\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n \"add-field\": [];\r\n \"remove-field\": [index: number];\r\n \"handle-primary-key\": [field: ERField, isPrimaryKey: boolean];\r\n}>();\r\n\r\nconst handlePrimaryKey = (field: ERField, isPrimaryKey: boolean) =>\r\n emit(\"handle-primary-key\", field, isPrimaryKey);\r\n\r\nconst addField = () => emit(\"add-field\");\r\n\r\nconst removeField = (index: number) => emit(\"remove-field\", index);\r\n</script>\r\n","<!--\r\n ERTableEditor — ER 表编辑器抽屉\r\n 负责:表名/注释编辑 + 字段 CRUD + 主键处理\r\n-->\r\n<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"600\"\r\n placement=\"right\"\r\n @update:show=\"$emit('update:show', $event)\"\r\n >\r\n <NDrawerContent :title=\"`编辑表: ${editingTable?.name || '新表'}`\" closable>\r\n <div class=\"table-editor\" v-if=\"editingTable\">\r\n <NForm :model=\"editingTable\" label-placement=\"top\">\r\n <NFormItem label=\"表名\">\r\n <NInput\r\n v-model:value=\"editingTable.name\"\r\n placeholder=\"请输入表名\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"表注释\">\r\n <NInput v-model:value=\"editingTable.comment\" placeholder=\"表注释\" />\r\n </NFormItem>\r\n\r\n <NDivider>字段列表</NDivider>\r\n\r\n <div class=\"fields-container\">\r\n <NCard\r\n v-for=\"(field, index) in editingTable.fields\"\r\n :key=\"index\"\r\n size=\"small\"\r\n class=\"field-card\"\r\n >\r\n <template #header>\r\n <div class=\"field-header\">\r\n <span>#{{ index + 1 }} {{ field.name || \"新字段\" }}</span>\r\n <NButton\r\n @click=\"removeField(index)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n :disabled=\"editingTable.fields.length <= 1\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n\r\n <NGrid :cols=\"2\" :x-gap=\"12\">\r\n <NGi>\r\n <NFormItem label=\"字段名\" size=\"small\">\r\n <NInput\r\n v-model:value=\"field.name\"\r\n placeholder=\"字段名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NGi>\r\n <NGi>\r\n <NFormItem label=\"类型\" size=\"small\">\r\n <NSelect\r\n v-model:value=\"field.type\"\r\n :options=\"fieldTypes\"\r\n size=\"small\"\r\n filterable\r\n placeholder=\"选择类型\"\r\n />\r\n </NFormItem>\r\n </NGi>\r\n </NGrid>\r\n\r\n <NSpace style=\"margin-top: 12px\">\r\n <NCheckbox\r\n v-model:checked=\"field.isPrimaryKey\"\r\n @update:checked=\"handlePrimaryKey(field, $event)\"\r\n >\r\n 主键\r\n </NCheckbox>\r\n <NCheckbox v-model:checked=\"field.isRequired\">必填</NCheckbox>\r\n <NCheckbox v-model:checked=\"field.isForeignKey\">外键</NCheckbox>\r\n </NSpace>\r\n\r\n <NFormItem label=\"注释\" size=\"small\" style=\"margin-top: 12px\">\r\n <NInput\r\n v-model:value=\"field.comment\"\r\n placeholder=\"字段注释\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NCard>\r\n\r\n <NButton\r\n @click=\"addField\"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n style=\"margin-top: 16px\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加字段\r\n </NButton>\r\n </div>\r\n </NForm>\r\n </div>\r\n\r\n <template #footer>\r\n <NSpace justify=\"end\">\r\n <NButton @click=\"$emit('update:show', false)\">取消</NButton>\r\n <NButton @click=\"$emit('save')\" type=\"primary\">保存</NButton>\r\n </NSpace>\r\n </template>\r\n </NDrawerContent>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n NDrawer,\r\n NDrawerContent,\r\n NForm,\r\n NFormItem,\r\n NInput,\r\n NSelect,\r\n NCard,\r\n NGrid,\r\n NGi,\r\n NSpace,\r\n NCheckbox,\r\n NButton,\r\n NDivider,\r\n} from \"naive-ui\";\r\nimport type { ERTable, ERField } from \"../../types\";\r\nimport { fieldTypes } from \"../data\";\r\nimport C_Icon from \"../../../../C_Icon/index.vue\";\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingTable = defineModel<ERTable>(\"editingTable\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n \"add-field\": [];\r\n \"remove-field\": [index: number];\r\n \"handle-primary-key\": [field: ERField, isPrimaryKey: boolean];\r\n}>();\r\n\r\nconst handlePrimaryKey = (field: ERField, isPrimaryKey: boolean) =>\r\n emit(\"handle-primary-key\", field, isPrimaryKey);\r\n\r\nconst addField = () => emit(\"add-field\");\r\n\r\nconst removeField = (index: number) => emit(\"remove-field\", index);\r\n</script>\r\n","<!--\r\n ERTableEditor — ER 表编辑器抽屉\r\n 负责:表名/注释编辑 + 字段 CRUD + 主键处理\r\n-->\r\n<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"600\"\r\n placement=\"right\"\r\n @update:show=\"$emit('update:show', $event)\"\r\n >\r\n <NDrawerContent :title=\"`编辑表: ${editingTable?.name || '新表'}`\" closable>\r\n <div class=\"table-editor\" v-if=\"editingTable\">\r\n <NForm :model=\"editingTable\" label-placement=\"top\">\r\n <NFormItem label=\"表名\">\r\n <NInput\r\n v-model:value=\"editingTable.name\"\r\n placeholder=\"请输入表名\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"表注释\">\r\n <NInput v-model:value=\"editingTable.comment\" placeholder=\"表注释\" />\r\n </NFormItem>\r\n\r\n <NDivider>字段列表</NDivider>\r\n\r\n <div class=\"fields-container\">\r\n <NCard\r\n v-for=\"(field, index) in editingTable.fields\"\r\n :key=\"index\"\r\n size=\"small\"\r\n class=\"field-card\"\r\n >\r\n <template #header>\r\n <div class=\"field-header\">\r\n <span>#{{ index + 1 }} {{ field.name || \"新字段\" }}</span>\r\n <NButton\r\n @click=\"removeField(index)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n :disabled=\"editingTable.fields.length <= 1\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n\r\n <NGrid :cols=\"2\" :x-gap=\"12\">\r\n <NGi>\r\n <NFormItem label=\"字段名\" size=\"small\">\r\n <NInput\r\n v-model:value=\"field.name\"\r\n placeholder=\"字段名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NGi>\r\n <NGi>\r\n <NFormItem label=\"类型\" size=\"small\">\r\n <NSelect\r\n v-model:value=\"field.type\"\r\n :options=\"fieldTypes\"\r\n size=\"small\"\r\n filterable\r\n placeholder=\"选择类型\"\r\n />\r\n </NFormItem>\r\n </NGi>\r\n </NGrid>\r\n\r\n <NSpace style=\"margin-top: 12px\">\r\n <NCheckbox\r\n v-model:checked=\"field.isPrimaryKey\"\r\n @update:checked=\"handlePrimaryKey(field, $event)\"\r\n >\r\n 主键\r\n </NCheckbox>\r\n <NCheckbox v-model:checked=\"field.isRequired\">必填</NCheckbox>\r\n <NCheckbox v-model:checked=\"field.isForeignKey\">外键</NCheckbox>\r\n </NSpace>\r\n\r\n <NFormItem label=\"注释\" size=\"small\" style=\"margin-top: 12px\">\r\n <NInput\r\n v-model:value=\"field.comment\"\r\n placeholder=\"字段注释\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NCard>\r\n\r\n <NButton\r\n @click=\"addField\"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n style=\"margin-top: 16px\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加字段\r\n </NButton>\r\n </div>\r\n </NForm>\r\n </div>\r\n\r\n <template #footer>\r\n <NSpace justify=\"end\">\r\n <NButton @click=\"$emit('update:show', false)\">取消</NButton>\r\n <NButton @click=\"$emit('save')\" type=\"primary\">保存</NButton>\r\n </NSpace>\r\n </template>\r\n </NDrawerContent>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n NDrawer,\r\n NDrawerContent,\r\n NForm,\r\n NFormItem,\r\n NInput,\r\n NSelect,\r\n NCard,\r\n NGrid,\r\n NGi,\r\n NSpace,\r\n NCheckbox,\r\n NButton,\r\n NDivider,\r\n} from \"naive-ui\";\r\nimport type { ERTable, ERField } from \"../../types\";\r\nimport { fieldTypes } from \"../data\";\r\nimport C_Icon from \"../../../../C_Icon/index.vue\";\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingTable = defineModel<ERTable>(\"editingTable\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n \"add-field\": [];\r\n \"remove-field\": [index: number];\r\n \"handle-primary-key\": [field: ERField, isPrimaryKey: boolean];\r\n}>();\r\n\r\nconst handlePrimaryKey = (field: ERField, isPrimaryKey: boolean) =>\r\n emit(\"handle-primary-key\", field, isPrimaryKey);\r\n\r\nconst addField = () => emit(\"add-field\");\r\n\r\nconst removeField = (index: number) => emit(\"remove-field\", index);\r\n</script>\r\n","<template>\r\n <div class=\"er-layout\">\r\n <!-- 工具栏 -->\r\n <div class=\"toolbar\" v-if=\"showToolbar\">\r\n <NSpace>\r\n <NButton @click=\"addTable\" type=\"primary\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:table-plus\" :size=\"16\" />\r\n </template>\r\n 添加表\r\n </NButton>\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n <NButton\r\n @click=\"toggleDeleteMode\"\r\n :type=\"deleteMode ? 'error' : 'default'\"\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n {{ deleteMode ? \"退出删除\" : \"删除连线\" }}\r\n </NButton>\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </NSpace>\r\n\r\n <div v-if=\"deleteMode\" style=\"margin-top: 8px\">\r\n <NAlert type=\"info\" size=\"small\" :show-icon=\"false\">\r\n 删除模式:点击连接线即可删除\r\n </NAlert>\r\n </div>\r\n </div>\r\n\r\n <!-- 图表容器 -->\r\n <div ref=\"containerRef\" class=\"graph-container\"></div>\r\n\r\n <!-- 表编辑器 -->\r\n <ERTableEditor\r\n :show=\"showEditor\"\r\n v-model:editing-table=\"editingTable\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveTable\"\r\n @add-field=\"addField\"\r\n @remove-field=\"removeField\"\r\n @handle-primary-key=\"handlePrimaryKey\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, nextTick, onMounted, onUnmounted } from \"vue\";\r\nimport { NSpace, NButton, NDropdown, NAlert } from \"naive-ui\";\r\nimport { Node, Graph, Cell, Edge } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport type { ERTable, ERField, ERDiagramData, ERRelation } from \"../../types\";\r\nimport ERTableEditor from \"./components/ERTableEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\n\r\ninterface Props {\r\n data?: ERDiagramData;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n (e: \"ready\", graph: Graph): void;\r\n (e: \"data-change\", data: ERDiagramData): void;\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"er-diagram\");\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingTable = ref<ERTable>();\r\nconst deleteMode = ref(false);\r\n\r\n// ==================== 工具函数 ====================\r\nconst truncateText = (text: string, maxLength: number) =>\r\n text.length > maxLength ? text.substring(0, maxLength - 1) + \"..\" : text;\r\n\r\nconst createPortConfig = (table: ERTable) =>\r\n table.fields?.map((field) => {\r\n const displayName = field.isPrimaryKey\r\n ? `🔑 ${field.name}`\r\n : field.isRequired\r\n ? `* ${field.name}`\r\n : field.name;\r\n return {\r\n id: `${table.id}_${field.name}`,\r\n group: \"list\",\r\n attrs: {\r\n portNameLabel: {\r\n text: truncateText(displayName, 12),\r\n title: displayName,\r\n },\r\n portTypeLabel: {\r\n text: truncateText(field.type, 10),\r\n title: field.type,\r\n },\r\n portBody: { fill: field.isPrimaryKey ? \"#FFF7E6\" : \"#EFF4FF\" },\r\n },\r\n };\r\n }) || [];\r\n\r\nconst createNodeConfig = (table: ERTable) => ({\r\n id: table.id,\r\n shape: \"er-rect\",\r\n x: table.position.x,\r\n y: table.position.y,\r\n width: 200,\r\n height: 24 + (table.fields?.length || 0) * 24,\r\n data: table,\r\n attrs: {\r\n label: {\r\n text: truncateText(table.name, 20),\r\n refX: 0.5,\r\n refY: 10,\r\n textAnchor: \"middle\",\r\n title: table.name,\r\n },\r\n },\r\n ports: createPortConfig(table),\r\n});\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n if (!graph.value) return;\r\n\r\n Graph.registerPortLayout(\r\n \"erPortPosition\",\r\n (portsPositionArgs) =>\r\n portsPositionArgs.map((_, index) => ({\r\n position: { x: 0, y: (index + 1) * 24 },\r\n angle: 0,\r\n })),\r\n true,\r\n );\r\n\r\n Graph.registerNode(\r\n \"er-rect\",\r\n {\r\n inherit: \"rect\",\r\n markup: [\r\n { tagName: \"rect\", selector: \"body\" },\r\n { tagName: \"text\", selector: \"label\" },\r\n ],\r\n attrs: {\r\n rect: { strokeWidth: 1, stroke: \"#5F95FF\", fill: \"#5F95FF\" },\r\n label: { fontWeight: \"bold\", fill: \"#ffffff\", fontSize: 12 },\r\n },\r\n ports: {\r\n groups: {\r\n list: {\r\n markup: [\r\n { tagName: \"rect\", selector: \"portBody\" },\r\n { tagName: \"text\", selector: \"portNameLabel\" },\r\n { tagName: \"text\", selector: \"portTypeLabel\" },\r\n ],\r\n attrs: {\r\n portBody: {\r\n width: 200,\r\n height: 24,\r\n strokeWidth: 1,\r\n stroke: \"#5F95FF\",\r\n fill: \"#EFF4FF\",\r\n magnet: true,\r\n },\r\n portNameLabel: {\r\n ref: \"portBody\",\r\n refX: 6,\r\n refY: 6,\r\n fontSize: 9,\r\n textAnchor: \"start\",\r\n textOverflow: \"ellipsis\",\r\n },\r\n portTypeLabel: {\r\n ref: \"portBody\",\r\n refX: 120,\r\n refY: 6,\r\n fontSize: 9,\r\n textAnchor: \"start\",\r\n fill: \"#666\",\r\n },\r\n },\r\n position: \"erPortPosition\",\r\n },\r\n },\r\n },\r\n },\r\n true,\r\n );\r\n};\r\n\r\n// ==================== 删除模式 ====================\r\nconst toggleDeleteMode = () => {\r\n deleteMode.value = !deleteMode.value;\r\n if (!deleteMode.value) resetEdgeStyles();\r\n};\r\n\r\nconst resetEdgeStyles = () => {\r\n graph.value?.getEdges().forEach((edge) => {\r\n edge.attr(\"line/stroke\", \"#A2B1C3\");\r\n edge.attr(\"line/strokeWidth\", 2);\r\n });\r\n};\r\n\r\n// ==================== 表 CRUD ====================\r\nconst createTableNode = (table: ERTable) => {\r\n if (!graph.value) return;\r\n const node = graph.value.createNode(createNodeConfig(table));\r\n graph.value.resetCells([node, ...graph.value.getCells()]);\r\n return node;\r\n};\r\n\r\nconst findPosition = () => {\r\n const nodes = graph.value?.getNodes() || [];\r\n const spacing = 250;\r\n for (let row = 0; row < 10; row++) {\r\n for (let col = 0; col < 3; col++) {\r\n const pos = { x: col * spacing + 50, y: row * spacing + 50 };\r\n const hasOverlap = nodes.some((node) => {\r\n const nodePos = node.getPosition();\r\n return (\r\n Math.abs(nodePos.x - pos.x) < spacing * 0.8 &&\r\n Math.abs(nodePos.y - pos.y) < spacing * 0.8\r\n );\r\n });\r\n if (!hasOverlap) return pos;\r\n }\r\n }\r\n return { x: 50, y: 50 };\r\n};\r\n\r\nconst addTable = () => {\r\n const newTable: ERTable = {\r\n id: `table_${Date.now()}`,\r\n name: \"新表\",\r\n comment: \"\",\r\n fields: [\r\n {\r\n name: \"id\",\r\n type: \"BIGINT\",\r\n isPrimaryKey: true,\r\n isRequired: true,\r\n isForeignKey: false,\r\n comment: \"主键\",\r\n },\r\n {\r\n name: \"name\",\r\n type: \"VARCHAR(100)\",\r\n isPrimaryKey: false,\r\n isRequired: true,\r\n isForeignKey: false,\r\n comment: \"名称\",\r\n },\r\n ],\r\n position: findPosition(),\r\n };\r\n createTableNode(newTable);\r\n editTable(newTable);\r\n emitDataChange();\r\n};\r\n\r\nconst editTable = (table: ERTable) => {\r\n editingTable.value = {\r\n ...table,\r\n fields: table.fields?.map((field) => ({ ...field })) || [],\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveTable = () => {\r\n if (!graph.value || !editingTable.value) return;\r\n const node = graph.value.getCellById(editingTable.value.id) as Node;\r\n if (node) {\r\n node.setData(editingTable.value);\r\n node.prop({\r\n size: {\r\n width: 200,\r\n height: 24 + editingTable.value.fields.length * 24,\r\n },\r\n attrs: {\r\n label: {\r\n text: truncateText(editingTable.value.name, 20),\r\n title: editingTable.value.name,\r\n },\r\n },\r\n ports: createPortConfig(editingTable.value),\r\n });\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 字段操作(由子组件事件触发) ====================\r\nconst handlePrimaryKey = (field: ERField, isPrimaryKey: boolean) => {\r\n if (!isPrimaryKey) return;\r\n field.isRequired = true;\r\n editingTable.value?.fields.forEach((f) => {\r\n if (f !== field) f.isPrimaryKey = false;\r\n });\r\n};\r\n\r\nconst addField = () => {\r\n editingTable.value?.fields.push({\r\n name: `field_${(editingTable.value?.fields.length || 0) + 1}`,\r\n type: \"VARCHAR(100)\",\r\n isPrimaryKey: false,\r\n isRequired: false,\r\n isForeignKey: false,\r\n comment: \"\",\r\n });\r\n};\r\n\r\nconst removeField = (index: number) => {\r\n if (editingTable.value && editingTable.value.fields.length > 1)\r\n editingTable.value.fields.splice(index, 1);\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): ERDiagramData => {\r\n if (!graph.value) return { tables: [], relations: [] };\r\n\r\n const tables = graph.value.getNodes().map((node: any) => ({\r\n ...node.getData(),\r\n position: node.getPosition(),\r\n }));\r\n\r\n const relations: ERRelation[] = [];\r\n graph.value.getEdges().forEach((edge: any) => {\r\n const source = edge.getSourceNode();\r\n const target = edge.getTargetNode();\r\n const sourcePort = edge.getSourcePortId();\r\n const targetPort = edge.getTargetPortId();\r\n if (source && target && sourcePort && targetPort) {\r\n relations.push({\r\n id: edge.id,\r\n type: \"foreign-key\",\r\n sourceTable: source.id,\r\n sourceField: sourcePort.split(\"_\").slice(1).join(\"_\"),\r\n targetTable: target.id,\r\n targetField: targetPort.split(\"_\").slice(1).join(\"_\"),\r\n name: `${source.getData()?.name || source.id} -> ${target.getData()?.name || target.id}`,\r\n });\r\n }\r\n });\r\n return { tables, relations };\r\n};\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n registerNodes();\r\n newGraph.on(\"node:dblclick\", ({ node }) => {\r\n if (!props.readonly) editTable(node.getData() as ERTable);\r\n });\r\n newGraph.on(\"edge:connected\", emitDataChange);\r\n newGraph.on(\"edge:removed\", emitDataChange);\r\n\r\n let selectedEdge: Edge | null = null;\r\n\r\n newGraph.on(\"edge:click\", ({ edge }) => {\r\n if (deleteMode.value) {\r\n edge.remove();\r\n emitDataChange();\r\n } else {\r\n resetEdgeStyles();\r\n edge.attr(\"line/stroke\", \"#ff4d4f\");\r\n edge.attr(\"line/strokeWidth\", 3);\r\n selectedEdge = edge;\r\n }\r\n });\r\n newGraph.on(\"edge:dblclick\", ({ edge }) => {\r\n edge.remove();\r\n emitDataChange();\r\n });\r\n newGraph.on(\"blank:click\", () => {\r\n selectedEdge = null;\r\n if (!deleteMode.value) resetEdgeStyles();\r\n });\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n // 忽略来自 input/textarea/contenteditable 元素的按键\r\n const tag = (e.target as HTMLElement)?.tagName;\r\n if (\r\n tag === \"INPUT\" ||\r\n tag === \"TEXTAREA\" ||\r\n (e.target as HTMLElement)?.isContentEditable\r\n )\r\n return;\r\n\r\n if ((e.key === \"Delete\" || e.key === \"Backspace\") && selectedEdge) {\r\n selectedEdge.remove();\r\n emitDataChange();\r\n selectedEdge = null;\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleKeyDown);\r\n onUnmounted(() => document.removeEventListener(\"keydown\", handleKeyDown));\r\n\r\n emit(\"ready\", newGraph);\r\n\r\n nextTick(() => {\r\n if (props.data?.tables) {\r\n const cells: Cell[] = props.data.tables.map((table) =>\r\n newGraph.createNode(createNodeConfig(table)),\r\n );\r\n\r\n if (props.data.relations?.length) {\r\n props.data.relations.forEach((relation) => {\r\n cells.push(\r\n newGraph.createEdge({\r\n source: {\r\n cell: relation.sourceTable,\r\n port: `${relation.sourceTable}_${relation.sourceField}`,\r\n },\r\n target: {\r\n cell: relation.targetTable,\r\n port: `${relation.targetTable}_${relation.targetField}`,\r\n },\r\n attrs: { line: { stroke: \"#A2B1C3\", strokeWidth: 2 } },\r\n }),\r\n );\r\n });\r\n }\r\n\r\n newGraph.resetCells(cells);\r\n setTimeout(() => newGraph.zoomToFit({ padding: 20, maxScale: 1 }), 300);\r\n }\r\n });\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (graph.value && newData?.tables) {\r\n const cells: Cell[] = newData.tables.map((table) =>\r\n graph.value!.createNode(createNodeConfig(table)),\r\n );\r\n\r\n // 加载关系连线\r\n if (newData.relations?.length) {\r\n newData.relations.forEach((relation) => {\r\n cells.push(\r\n graph.value!.createEdge({\r\n source: {\r\n cell: relation.sourceTable,\r\n port: `${relation.sourceTable}_${relation.sourceField}`,\r\n },\r\n target: {\r\n cell: relation.targetTable,\r\n port: `${relation.targetTable}_${relation.targetField}`,\r\n },\r\n attrs: { line: { stroke: \"#A2B1C3\", strokeWidth: 2 } },\r\n }),\r\n );\r\n });\r\n }\r\n\r\n graph.value.resetCells(cells);\r\n }\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(() => initGraph());\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value ?? undefined,\r\n getData: getCurrentData,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <div class=\"er-layout\">\r\n <!-- 工具栏 -->\r\n <div class=\"toolbar\" v-if=\"showToolbar\">\r\n <NSpace>\r\n <NButton @click=\"addTable\" type=\"primary\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:table-plus\" :size=\"16\" />\r\n </template>\r\n 添加表\r\n </NButton>\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n <NButton\r\n @click=\"toggleDeleteMode\"\r\n :type=\"deleteMode ? 'error' : 'default'\"\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n {{ deleteMode ? \"退出删除\" : \"删除连线\" }}\r\n </NButton>\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </NSpace>\r\n\r\n <div v-if=\"deleteMode\" style=\"margin-top: 8px\">\r\n <NAlert type=\"info\" size=\"small\" :show-icon=\"false\">\r\n 删除模式:点击连接线即可删除\r\n </NAlert>\r\n </div>\r\n </div>\r\n\r\n <!-- 图表容器 -->\r\n <div ref=\"containerRef\" class=\"graph-container\"></div>\r\n\r\n <!-- 表编辑器 -->\r\n <ERTableEditor\r\n :show=\"showEditor\"\r\n v-model:editing-table=\"editingTable\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveTable\"\r\n @add-field=\"addField\"\r\n @remove-field=\"removeField\"\r\n @handle-primary-key=\"handlePrimaryKey\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, nextTick, onMounted, onUnmounted } from \"vue\";\r\nimport { NSpace, NButton, NDropdown, NAlert } from \"naive-ui\";\r\nimport { Node, Graph, Cell, Edge } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport type { ERTable, ERField, ERDiagramData, ERRelation } from \"../../types\";\r\nimport ERTableEditor from \"./components/ERTableEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\n\r\ninterface Props {\r\n data?: ERDiagramData;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n (e: \"ready\", graph: Graph): void;\r\n (e: \"data-change\", data: ERDiagramData): void;\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"er-diagram\");\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingTable = ref<ERTable>();\r\nconst deleteMode = ref(false);\r\n\r\n// ==================== 工具函数 ====================\r\nconst truncateText = (text: string, maxLength: number) =>\r\n text.length > maxLength ? text.substring(0, maxLength - 1) + \"..\" : text;\r\n\r\nconst createPortConfig = (table: ERTable) =>\r\n table.fields?.map((field) => {\r\n const displayName = field.isPrimaryKey\r\n ? `🔑 ${field.name}`\r\n : field.isRequired\r\n ? `* ${field.name}`\r\n : field.name;\r\n return {\r\n id: `${table.id}_${field.name}`,\r\n group: \"list\",\r\n attrs: {\r\n portNameLabel: {\r\n text: truncateText(displayName, 12),\r\n title: displayName,\r\n },\r\n portTypeLabel: {\r\n text: truncateText(field.type, 10),\r\n title: field.type,\r\n },\r\n portBody: { fill: field.isPrimaryKey ? \"#FFF7E6\" : \"#EFF4FF\" },\r\n },\r\n };\r\n }) || [];\r\n\r\nconst createNodeConfig = (table: ERTable) => ({\r\n id: table.id,\r\n shape: \"er-rect\",\r\n x: table.position.x,\r\n y: table.position.y,\r\n width: 200,\r\n height: 24 + (table.fields?.length || 0) * 24,\r\n data: table,\r\n attrs: {\r\n label: {\r\n text: truncateText(table.name, 20),\r\n refX: 0.5,\r\n refY: 10,\r\n textAnchor: \"middle\",\r\n title: table.name,\r\n },\r\n },\r\n ports: createPortConfig(table),\r\n});\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n if (!graph.value) return;\r\n\r\n Graph.registerPortLayout(\r\n \"erPortPosition\",\r\n (portsPositionArgs) =>\r\n portsPositionArgs.map((_, index) => ({\r\n position: { x: 0, y: (index + 1) * 24 },\r\n angle: 0,\r\n })),\r\n true,\r\n );\r\n\r\n Graph.registerNode(\r\n \"er-rect\",\r\n {\r\n inherit: \"rect\",\r\n markup: [\r\n { tagName: \"rect\", selector: \"body\" },\r\n { tagName: \"text\", selector: \"label\" },\r\n ],\r\n attrs: {\r\n rect: { strokeWidth: 1, stroke: \"#5F95FF\", fill: \"#5F95FF\" },\r\n label: { fontWeight: \"bold\", fill: \"#ffffff\", fontSize: 12 },\r\n },\r\n ports: {\r\n groups: {\r\n list: {\r\n markup: [\r\n { tagName: \"rect\", selector: \"portBody\" },\r\n { tagName: \"text\", selector: \"portNameLabel\" },\r\n { tagName: \"text\", selector: \"portTypeLabel\" },\r\n ],\r\n attrs: {\r\n portBody: {\r\n width: 200,\r\n height: 24,\r\n strokeWidth: 1,\r\n stroke: \"#5F95FF\",\r\n fill: \"#EFF4FF\",\r\n magnet: true,\r\n },\r\n portNameLabel: {\r\n ref: \"portBody\",\r\n refX: 6,\r\n refY: 6,\r\n fontSize: 9,\r\n textAnchor: \"start\",\r\n textOverflow: \"ellipsis\",\r\n },\r\n portTypeLabel: {\r\n ref: \"portBody\",\r\n refX: 120,\r\n refY: 6,\r\n fontSize: 9,\r\n textAnchor: \"start\",\r\n fill: \"#666\",\r\n },\r\n },\r\n position: \"erPortPosition\",\r\n },\r\n },\r\n },\r\n },\r\n true,\r\n );\r\n};\r\n\r\n// ==================== 删除模式 ====================\r\nconst toggleDeleteMode = () => {\r\n deleteMode.value = !deleteMode.value;\r\n if (!deleteMode.value) resetEdgeStyles();\r\n};\r\n\r\nconst resetEdgeStyles = () => {\r\n graph.value?.getEdges().forEach((edge) => {\r\n edge.attr(\"line/stroke\", \"#A2B1C3\");\r\n edge.attr(\"line/strokeWidth\", 2);\r\n });\r\n};\r\n\r\n// ==================== 表 CRUD ====================\r\nconst createTableNode = (table: ERTable) => {\r\n if (!graph.value) return;\r\n const node = graph.value.createNode(createNodeConfig(table));\r\n graph.value.resetCells([node, ...graph.value.getCells()]);\r\n return node;\r\n};\r\n\r\nconst findPosition = () => {\r\n const nodes = graph.value?.getNodes() || [];\r\n const spacing = 250;\r\n for (let row = 0; row < 10; row++) {\r\n for (let col = 0; col < 3; col++) {\r\n const pos = { x: col * spacing + 50, y: row * spacing + 50 };\r\n const hasOverlap = nodes.some((node) => {\r\n const nodePos = node.getPosition();\r\n return (\r\n Math.abs(nodePos.x - pos.x) < spacing * 0.8 &&\r\n Math.abs(nodePos.y - pos.y) < spacing * 0.8\r\n );\r\n });\r\n if (!hasOverlap) return pos;\r\n }\r\n }\r\n return { x: 50, y: 50 };\r\n};\r\n\r\nconst addTable = () => {\r\n const newTable: ERTable = {\r\n id: `table_${Date.now()}`,\r\n name: \"新表\",\r\n comment: \"\",\r\n fields: [\r\n {\r\n name: \"id\",\r\n type: \"BIGINT\",\r\n isPrimaryKey: true,\r\n isRequired: true,\r\n isForeignKey: false,\r\n comment: \"主键\",\r\n },\r\n {\r\n name: \"name\",\r\n type: \"VARCHAR(100)\",\r\n isPrimaryKey: false,\r\n isRequired: true,\r\n isForeignKey: false,\r\n comment: \"名称\",\r\n },\r\n ],\r\n position: findPosition(),\r\n };\r\n createTableNode(newTable);\r\n editTable(newTable);\r\n emitDataChange();\r\n};\r\n\r\nconst editTable = (table: ERTable) => {\r\n editingTable.value = {\r\n ...table,\r\n fields: table.fields?.map((field) => ({ ...field })) || [],\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveTable = () => {\r\n if (!graph.value || !editingTable.value) return;\r\n const node = graph.value.getCellById(editingTable.value.id) as Node;\r\n if (node) {\r\n node.setData(editingTable.value);\r\n node.prop({\r\n size: {\r\n width: 200,\r\n height: 24 + editingTable.value.fields.length * 24,\r\n },\r\n attrs: {\r\n label: {\r\n text: truncateText(editingTable.value.name, 20),\r\n title: editingTable.value.name,\r\n },\r\n },\r\n ports: createPortConfig(editingTable.value),\r\n });\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 字段操作(由子组件事件触发) ====================\r\nconst handlePrimaryKey = (field: ERField, isPrimaryKey: boolean) => {\r\n if (!isPrimaryKey) return;\r\n field.isRequired = true;\r\n editingTable.value?.fields.forEach((f) => {\r\n if (f !== field) f.isPrimaryKey = false;\r\n });\r\n};\r\n\r\nconst addField = () => {\r\n editingTable.value?.fields.push({\r\n name: `field_${(editingTable.value?.fields.length || 0) + 1}`,\r\n type: \"VARCHAR(100)\",\r\n isPrimaryKey: false,\r\n isRequired: false,\r\n isForeignKey: false,\r\n comment: \"\",\r\n });\r\n};\r\n\r\nconst removeField = (index: number) => {\r\n if (editingTable.value && editingTable.value.fields.length > 1)\r\n editingTable.value.fields.splice(index, 1);\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): ERDiagramData => {\r\n if (!graph.value) return { tables: [], relations: [] };\r\n\r\n const tables = graph.value.getNodes().map((node: any) => ({\r\n ...node.getData(),\r\n position: node.getPosition(),\r\n }));\r\n\r\n const relations: ERRelation[] = [];\r\n graph.value.getEdges().forEach((edge: any) => {\r\n const source = edge.getSourceNode();\r\n const target = edge.getTargetNode();\r\n const sourcePort = edge.getSourcePortId();\r\n const targetPort = edge.getTargetPortId();\r\n if (source && target && sourcePort && targetPort) {\r\n relations.push({\r\n id: edge.id,\r\n type: \"foreign-key\",\r\n sourceTable: source.id,\r\n sourceField: sourcePort.split(\"_\").slice(1).join(\"_\"),\r\n targetTable: target.id,\r\n targetField: targetPort.split(\"_\").slice(1).join(\"_\"),\r\n name: `${source.getData()?.name || source.id} -> ${target.getData()?.name || target.id}`,\r\n });\r\n }\r\n });\r\n return { tables, relations };\r\n};\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n registerNodes();\r\n newGraph.on(\"node:dblclick\", ({ node }) => {\r\n if (!props.readonly) editTable(node.getData() as ERTable);\r\n });\r\n newGraph.on(\"edge:connected\", emitDataChange);\r\n newGraph.on(\"edge:removed\", emitDataChange);\r\n\r\n let selectedEdge: Edge | null = null;\r\n\r\n newGraph.on(\"edge:click\", ({ edge }) => {\r\n if (deleteMode.value) {\r\n edge.remove();\r\n emitDataChange();\r\n } else {\r\n resetEdgeStyles();\r\n edge.attr(\"line/stroke\", \"#ff4d4f\");\r\n edge.attr(\"line/strokeWidth\", 3);\r\n selectedEdge = edge;\r\n }\r\n });\r\n newGraph.on(\"edge:dblclick\", ({ edge }) => {\r\n edge.remove();\r\n emitDataChange();\r\n });\r\n newGraph.on(\"blank:click\", () => {\r\n selectedEdge = null;\r\n if (!deleteMode.value) resetEdgeStyles();\r\n });\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n // 忽略来自 input/textarea/contenteditable 元素的按键\r\n const tag = (e.target as HTMLElement)?.tagName;\r\n if (\r\n tag === \"INPUT\" ||\r\n tag === \"TEXTAREA\" ||\r\n (e.target as HTMLElement)?.isContentEditable\r\n )\r\n return;\r\n\r\n if ((e.key === \"Delete\" || e.key === \"Backspace\") && selectedEdge) {\r\n selectedEdge.remove();\r\n emitDataChange();\r\n selectedEdge = null;\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleKeyDown);\r\n onUnmounted(() => document.removeEventListener(\"keydown\", handleKeyDown));\r\n\r\n emit(\"ready\", newGraph);\r\n\r\n nextTick(() => {\r\n if (props.data?.tables) {\r\n const cells: Cell[] = props.data.tables.map((table) =>\r\n newGraph.createNode(createNodeConfig(table)),\r\n );\r\n\r\n if (props.data.relations?.length) {\r\n props.data.relations.forEach((relation) => {\r\n cells.push(\r\n newGraph.createEdge({\r\n source: {\r\n cell: relation.sourceTable,\r\n port: `${relation.sourceTable}_${relation.sourceField}`,\r\n },\r\n target: {\r\n cell: relation.targetTable,\r\n port: `${relation.targetTable}_${relation.targetField}`,\r\n },\r\n attrs: { line: { stroke: \"#A2B1C3\", strokeWidth: 2 } },\r\n }),\r\n );\r\n });\r\n }\r\n\r\n newGraph.resetCells(cells);\r\n setTimeout(() => newGraph.zoomToFit({ padding: 20, maxScale: 1 }), 300);\r\n }\r\n });\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (graph.value && newData?.tables) {\r\n const cells: Cell[] = newData.tables.map((table) =>\r\n graph.value!.createNode(createNodeConfig(table)),\r\n );\r\n\r\n // 加载关系连线\r\n if (newData.relations?.length) {\r\n newData.relations.forEach((relation) => {\r\n cells.push(\r\n graph.value!.createEdge({\r\n source: {\r\n cell: relation.sourceTable,\r\n port: `${relation.sourceTable}_${relation.sourceField}`,\r\n },\r\n target: {\r\n cell: relation.targetTable,\r\n port: `${relation.targetTable}_${relation.targetField}`,\r\n },\r\n attrs: { line: { stroke: \"#A2B1C3\", strokeWidth: 2 } },\r\n }),\r\n );\r\n });\r\n }\r\n\r\n graph.value.resetCells(cells);\r\n }\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(() => initGraph());\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value ?? undefined,\r\n getData: getCurrentData,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <div class=\"er-layout\">\r\n <!-- 工具栏 -->\r\n <div class=\"toolbar\" v-if=\"showToolbar\">\r\n <NSpace>\r\n <NButton @click=\"addTable\" type=\"primary\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:table-plus\" :size=\"16\" />\r\n </template>\r\n 添加表\r\n </NButton>\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n <NButton\r\n @click=\"toggleDeleteMode\"\r\n :type=\"deleteMode ? 'error' : 'default'\"\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n {{ deleteMode ? \"退出删除\" : \"删除连线\" }}\r\n </NButton>\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </NSpace>\r\n\r\n <div v-if=\"deleteMode\" style=\"margin-top: 8px\">\r\n <NAlert type=\"info\" size=\"small\" :show-icon=\"false\">\r\n 删除模式:点击连接线即可删除\r\n </NAlert>\r\n </div>\r\n </div>\r\n\r\n <!-- 图表容器 -->\r\n <div ref=\"containerRef\" class=\"graph-container\"></div>\r\n\r\n <!-- 表编辑器 -->\r\n <ERTableEditor\r\n :show=\"showEditor\"\r\n v-model:editing-table=\"editingTable\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveTable\"\r\n @add-field=\"addField\"\r\n @remove-field=\"removeField\"\r\n @handle-primary-key=\"handlePrimaryKey\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, nextTick, onMounted, onUnmounted } from \"vue\";\r\nimport { NSpace, NButton, NDropdown, NAlert } from \"naive-ui\";\r\nimport { Node, Graph, Cell, Edge } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport type { ERTable, ERField, ERDiagramData, ERRelation } from \"../../types\";\r\nimport ERTableEditor from \"./components/ERTableEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\n\r\ninterface Props {\r\n data?: ERDiagramData;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n (e: \"ready\", graph: Graph): void;\r\n (e: \"data-change\", data: ERDiagramData): void;\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"er-diagram\");\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingTable = ref<ERTable>();\r\nconst deleteMode = ref(false);\r\n\r\n// ==================== 工具函数 ====================\r\nconst truncateText = (text: string, maxLength: number) =>\r\n text.length > maxLength ? text.substring(0, maxLength - 1) + \"..\" : text;\r\n\r\nconst createPortConfig = (table: ERTable) =>\r\n table.fields?.map((field) => {\r\n const displayName = field.isPrimaryKey\r\n ? `🔑 ${field.name}`\r\n : field.isRequired\r\n ? `* ${field.name}`\r\n : field.name;\r\n return {\r\n id: `${table.id}_${field.name}`,\r\n group: \"list\",\r\n attrs: {\r\n portNameLabel: {\r\n text: truncateText(displayName, 12),\r\n title: displayName,\r\n },\r\n portTypeLabel: {\r\n text: truncateText(field.type, 10),\r\n title: field.type,\r\n },\r\n portBody: { fill: field.isPrimaryKey ? \"#FFF7E6\" : \"#EFF4FF\" },\r\n },\r\n };\r\n }) || [];\r\n\r\nconst createNodeConfig = (table: ERTable) => ({\r\n id: table.id,\r\n shape: \"er-rect\",\r\n x: table.position.x,\r\n y: table.position.y,\r\n width: 200,\r\n height: 24 + (table.fields?.length || 0) * 24,\r\n data: table,\r\n attrs: {\r\n label: {\r\n text: truncateText(table.name, 20),\r\n refX: 0.5,\r\n refY: 10,\r\n textAnchor: \"middle\",\r\n title: table.name,\r\n },\r\n },\r\n ports: createPortConfig(table),\r\n});\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n if (!graph.value) return;\r\n\r\n Graph.registerPortLayout(\r\n \"erPortPosition\",\r\n (portsPositionArgs) =>\r\n portsPositionArgs.map((_, index) => ({\r\n position: { x: 0, y: (index + 1) * 24 },\r\n angle: 0,\r\n })),\r\n true,\r\n );\r\n\r\n Graph.registerNode(\r\n \"er-rect\",\r\n {\r\n inherit: \"rect\",\r\n markup: [\r\n { tagName: \"rect\", selector: \"body\" },\r\n { tagName: \"text\", selector: \"label\" },\r\n ],\r\n attrs: {\r\n rect: { strokeWidth: 1, stroke: \"#5F95FF\", fill: \"#5F95FF\" },\r\n label: { fontWeight: \"bold\", fill: \"#ffffff\", fontSize: 12 },\r\n },\r\n ports: {\r\n groups: {\r\n list: {\r\n markup: [\r\n { tagName: \"rect\", selector: \"portBody\" },\r\n { tagName: \"text\", selector: \"portNameLabel\" },\r\n { tagName: \"text\", selector: \"portTypeLabel\" },\r\n ],\r\n attrs: {\r\n portBody: {\r\n width: 200,\r\n height: 24,\r\n strokeWidth: 1,\r\n stroke: \"#5F95FF\",\r\n fill: \"#EFF4FF\",\r\n magnet: true,\r\n },\r\n portNameLabel: {\r\n ref: \"portBody\",\r\n refX: 6,\r\n refY: 6,\r\n fontSize: 9,\r\n textAnchor: \"start\",\r\n textOverflow: \"ellipsis\",\r\n },\r\n portTypeLabel: {\r\n ref: \"portBody\",\r\n refX: 120,\r\n refY: 6,\r\n fontSize: 9,\r\n textAnchor: \"start\",\r\n fill: \"#666\",\r\n },\r\n },\r\n position: \"erPortPosition\",\r\n },\r\n },\r\n },\r\n },\r\n true,\r\n );\r\n};\r\n\r\n// ==================== 删除模式 ====================\r\nconst toggleDeleteMode = () => {\r\n deleteMode.value = !deleteMode.value;\r\n if (!deleteMode.value) resetEdgeStyles();\r\n};\r\n\r\nconst resetEdgeStyles = () => {\r\n graph.value?.getEdges().forEach((edge) => {\r\n edge.attr(\"line/stroke\", \"#A2B1C3\");\r\n edge.attr(\"line/strokeWidth\", 2);\r\n });\r\n};\r\n\r\n// ==================== 表 CRUD ====================\r\nconst createTableNode = (table: ERTable) => {\r\n if (!graph.value) return;\r\n const node = graph.value.createNode(createNodeConfig(table));\r\n graph.value.resetCells([node, ...graph.value.getCells()]);\r\n return node;\r\n};\r\n\r\nconst findPosition = () => {\r\n const nodes = graph.value?.getNodes() || [];\r\n const spacing = 250;\r\n for (let row = 0; row < 10; row++) {\r\n for (let col = 0; col < 3; col++) {\r\n const pos = { x: col * spacing + 50, y: row * spacing + 50 };\r\n const hasOverlap = nodes.some((node) => {\r\n const nodePos = node.getPosition();\r\n return (\r\n Math.abs(nodePos.x - pos.x) < spacing * 0.8 &&\r\n Math.abs(nodePos.y - pos.y) < spacing * 0.8\r\n );\r\n });\r\n if (!hasOverlap) return pos;\r\n }\r\n }\r\n return { x: 50, y: 50 };\r\n};\r\n\r\nconst addTable = () => {\r\n const newTable: ERTable = {\r\n id: `table_${Date.now()}`,\r\n name: \"新表\",\r\n comment: \"\",\r\n fields: [\r\n {\r\n name: \"id\",\r\n type: \"BIGINT\",\r\n isPrimaryKey: true,\r\n isRequired: true,\r\n isForeignKey: false,\r\n comment: \"主键\",\r\n },\r\n {\r\n name: \"name\",\r\n type: \"VARCHAR(100)\",\r\n isPrimaryKey: false,\r\n isRequired: true,\r\n isForeignKey: false,\r\n comment: \"名称\",\r\n },\r\n ],\r\n position: findPosition(),\r\n };\r\n createTableNode(newTable);\r\n editTable(newTable);\r\n emitDataChange();\r\n};\r\n\r\nconst editTable = (table: ERTable) => {\r\n editingTable.value = {\r\n ...table,\r\n fields: table.fields?.map((field) => ({ ...field })) || [],\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveTable = () => {\r\n if (!graph.value || !editingTable.value) return;\r\n const node = graph.value.getCellById(editingTable.value.id) as Node;\r\n if (node) {\r\n node.setData(editingTable.value);\r\n node.prop({\r\n size: {\r\n width: 200,\r\n height: 24 + editingTable.value.fields.length * 24,\r\n },\r\n attrs: {\r\n label: {\r\n text: truncateText(editingTable.value.name, 20),\r\n title: editingTable.value.name,\r\n },\r\n },\r\n ports: createPortConfig(editingTable.value),\r\n });\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 字段操作(由子组件事件触发) ====================\r\nconst handlePrimaryKey = (field: ERField, isPrimaryKey: boolean) => {\r\n if (!isPrimaryKey) return;\r\n field.isRequired = true;\r\n editingTable.value?.fields.forEach((f) => {\r\n if (f !== field) f.isPrimaryKey = false;\r\n });\r\n};\r\n\r\nconst addField = () => {\r\n editingTable.value?.fields.push({\r\n name: `field_${(editingTable.value?.fields.length || 0) + 1}`,\r\n type: \"VARCHAR(100)\",\r\n isPrimaryKey: false,\r\n isRequired: false,\r\n isForeignKey: false,\r\n comment: \"\",\r\n });\r\n};\r\n\r\nconst removeField = (index: number) => {\r\n if (editingTable.value && editingTable.value.fields.length > 1)\r\n editingTable.value.fields.splice(index, 1);\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): ERDiagramData => {\r\n if (!graph.value) return { tables: [], relations: [] };\r\n\r\n const tables = graph.value.getNodes().map((node: any) => ({\r\n ...node.getData(),\r\n position: node.getPosition(),\r\n }));\r\n\r\n const relations: ERRelation[] = [];\r\n graph.value.getEdges().forEach((edge: any) => {\r\n const source = edge.getSourceNode();\r\n const target = edge.getTargetNode();\r\n const sourcePort = edge.getSourcePortId();\r\n const targetPort = edge.getTargetPortId();\r\n if (source && target && sourcePort && targetPort) {\r\n relations.push({\r\n id: edge.id,\r\n type: \"foreign-key\",\r\n sourceTable: source.id,\r\n sourceField: sourcePort.split(\"_\").slice(1).join(\"_\"),\r\n targetTable: target.id,\r\n targetField: targetPort.split(\"_\").slice(1).join(\"_\"),\r\n name: `${source.getData()?.name || source.id} -> ${target.getData()?.name || target.id}`,\r\n });\r\n }\r\n });\r\n return { tables, relations };\r\n};\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n registerNodes();\r\n newGraph.on(\"node:dblclick\", ({ node }) => {\r\n if (!props.readonly) editTable(node.getData() as ERTable);\r\n });\r\n newGraph.on(\"edge:connected\", emitDataChange);\r\n newGraph.on(\"edge:removed\", emitDataChange);\r\n\r\n let selectedEdge: Edge | null = null;\r\n\r\n newGraph.on(\"edge:click\", ({ edge }) => {\r\n if (deleteMode.value) {\r\n edge.remove();\r\n emitDataChange();\r\n } else {\r\n resetEdgeStyles();\r\n edge.attr(\"line/stroke\", \"#ff4d4f\");\r\n edge.attr(\"line/strokeWidth\", 3);\r\n selectedEdge = edge;\r\n }\r\n });\r\n newGraph.on(\"edge:dblclick\", ({ edge }) => {\r\n edge.remove();\r\n emitDataChange();\r\n });\r\n newGraph.on(\"blank:click\", () => {\r\n selectedEdge = null;\r\n if (!deleteMode.value) resetEdgeStyles();\r\n });\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n // 忽略来自 input/textarea/contenteditable 元素的按键\r\n const tag = (e.target as HTMLElement)?.tagName;\r\n if (\r\n tag === \"INPUT\" ||\r\n tag === \"TEXTAREA\" ||\r\n (e.target as HTMLElement)?.isContentEditable\r\n )\r\n return;\r\n\r\n if ((e.key === \"Delete\" || e.key === \"Backspace\") && selectedEdge) {\r\n selectedEdge.remove();\r\n emitDataChange();\r\n selectedEdge = null;\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleKeyDown);\r\n onUnmounted(() => document.removeEventListener(\"keydown\", handleKeyDown));\r\n\r\n emit(\"ready\", newGraph);\r\n\r\n nextTick(() => {\r\n if (props.data?.tables) {\r\n const cells: Cell[] = props.data.tables.map((table) =>\r\n newGraph.createNode(createNodeConfig(table)),\r\n );\r\n\r\n if (props.data.relations?.length) {\r\n props.data.relations.forEach((relation) => {\r\n cells.push(\r\n newGraph.createEdge({\r\n source: {\r\n cell: relation.sourceTable,\r\n port: `${relation.sourceTable}_${relation.sourceField}`,\r\n },\r\n target: {\r\n cell: relation.targetTable,\r\n port: `${relation.targetTable}_${relation.targetField}`,\r\n },\r\n attrs: { line: { stroke: \"#A2B1C3\", strokeWidth: 2 } },\r\n }),\r\n );\r\n });\r\n }\r\n\r\n newGraph.resetCells(cells);\r\n setTimeout(() => newGraph.zoomToFit({ padding: 20, maxScale: 1 }), 300);\r\n }\r\n });\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (graph.value && newData?.tables) {\r\n const cells: Cell[] = newData.tables.map((table) =>\r\n graph.value!.createNode(createNodeConfig(table)),\r\n );\r\n\r\n // 加载关系连线\r\n if (newData.relations?.length) {\r\n newData.relations.forEach((relation) => {\r\n cells.push(\r\n graph.value!.createEdge({\r\n source: {\r\n cell: relation.sourceTable,\r\n port: `${relation.sourceTable}_${relation.sourceField}`,\r\n },\r\n target: {\r\n cell: relation.targetTable,\r\n port: `${relation.targetTable}_${relation.targetField}`,\r\n },\r\n attrs: { line: { stroke: \"#A2B1C3\", strokeWidth: 2 } },\r\n }),\r\n );\r\n });\r\n }\r\n\r\n graph.value.resetCells(cells);\r\n }\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(() => initGraph());\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value ?? undefined,\r\n getData: getCurrentData,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","import type { Ref } from 'vue'\r\nimport type { Graph, Node } from '@antv/x6'\r\n\r\nexport interface EdgeInteractionOptions {\r\n /** 连线默认颜色 */\r\n defaultColor?: string\r\n /** 连线高亮颜色 */\r\n highlightColor?: string\r\n /** 默认线宽 */\r\n strokeWidth?: number\r\n /** 高亮线宽 */\r\n highlightStrokeWidth?: number\r\n /** 端口位置名称列表(用于控制 mouseenter/leave 显隐) */\r\n portPositions?: string[]\r\n /** 数据变更回调 */\r\n onDataChange?: () => void\r\n}\r\n\r\nconst DEFAULT_OPTIONS: Required<EdgeInteractionOptions> = {\r\n defaultColor: '#A2B1C3',\r\n highlightColor: '#ff4d4f',\r\n strokeWidth: 2,\r\n highlightStrokeWidth: 3,\r\n portPositions: [],\r\n onDataChange: () => {},\r\n}\r\n\r\n/**\r\n * 图表交互 composable — 统一管理连线点击/高亮/删除 + 端口显隐\r\n * @param graph - X6 Graph 实例引用\r\n * @param options - 交互配置\r\n */\r\nexport function useEdgeInteraction(\r\n graph: Ref<Graph | null>,\r\n options: EdgeInteractionOptions = {}\r\n) {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n /** 重置所有连线颜色 */\r\n const resetEdgeStyles = () => {\r\n graph.value?.getEdges().forEach(edge => {\r\n edge.attr('line/stroke', config.defaultColor)\r\n edge.attr('line/strokeWidth', config.strokeWidth)\r\n })\r\n }\r\n\r\n /** 高亮指定连线 */\r\n const highlightEdge = (edge: any) => {\r\n resetEdgeStyles()\r\n edge.attr('line/stroke', config.highlightColor)\r\n edge.attr('line/strokeWidth', config.highlightStrokeWidth)\r\n }\r\n\r\n /** 切换端口可见性 */\r\n const togglePorts = (node: Node, opacity: number) => {\r\n config.portPositions.forEach(pos =>\r\n node.attr(`port-${pos}/style/opacity`, opacity)\r\n )\r\n }\r\n\r\n /**\r\n * 绑定标准交互事件到 graph 实例\r\n * - edge:click → 高亮\r\n * - edge:dblclick → 删除\r\n * - blank:click / node:click → 重置颜色\r\n * - node:mouseenter / mouseleave → 端口显隐(如配置了 portPositions)\r\n */\r\n const bindInteractions = () => {\r\n const g = graph.value\r\n if (!g) return\r\n\r\n // 连线点击高亮\r\n g.on('edge:click', ({ edge }) => highlightEdge(edge))\r\n\r\n // 连线双击删除\r\n g.on('edge:dblclick', ({ edge }) => {\r\n edge.remove()\r\n config.onDataChange()\r\n })\r\n\r\n // 点击空白 / 节点时重置颜色\r\n g.on('blank:click', resetEdgeStyles)\r\n g.on('node:click', resetEdgeStyles)\r\n\r\n // 连线创建时通知数据变更\r\n g.on('edge:connected', config.onDataChange)\r\n\r\n // 端口显隐(如果配置了 portPositions)\r\n if (config.portPositions.length > 0) {\r\n g.on('node:mouseenter', ({ node }) => togglePorts(node, 1))\r\n g.on('node:mouseleave', ({ node }) => togglePorts(node, 0))\r\n }\r\n }\r\n\r\n return { resetEdgeStyles, highlightEdge, togglePorts, bindInteractions }\r\n}\r\n","export const elementTypes = {\r\n \"start-event\": {\r\n name: \"开始\",\r\n title: \"开始事件\",\r\n iconClass: \"circle start-event\",\r\n },\r\n activity: { name: \"任务\", title: \"任务活动\", iconClass: \"rect activity\" },\r\n gateway: { name: \"网关\", title: \"排他网关\", iconClass: \"diamond gateway\" },\r\n \"end-event\": {\r\n name: \"结束\",\r\n title: \"结束事件\",\r\n iconClass: \"circle end-event\",\r\n },\r\n};\r\n\r\nexport const elementTypeNames = {\r\n event: \"事件\",\r\n activity: \"活动\",\r\n gateway: \"网关\",\r\n \"bpmn-edge\": \"连接线\",\r\n};\r\n\r\n/* 端口位置配置 */\r\nexport const portPositions = [\"top\", \"right\", \"bottom\", \"left\"];\r\n\r\n/* 端口属性配置函数 */\r\nexport const createPortAttrs = (\r\n positions: string[],\r\n nodeType: \"circle\" | \"rect\" | \"polygon\" = \"rect\",\r\n) =>\r\n positions.reduce((acc, pos) => {\r\n const isCircle = nodeType === \"circle\";\r\n const refConfig =\r\n pos === \"top\"\r\n ? isCircle\r\n ? { refCx: 0.5, refCy: 0 }\r\n : { refX: 0.5, refY: 0 }\r\n : pos === \"right\"\r\n ? isCircle\r\n ? { refCx: 1, refCy: 0.5 }\r\n : { refX: 1, refY: 0.5 }\r\n : pos === \"bottom\"\r\n ? isCircle\r\n ? { refCx: 0.5, refCy: 1 }\r\n : { refX: 0.5, refY: 1 }\r\n : isCircle\r\n ? { refCx: 0, refCy: 0.5 }\r\n : { refX: 0, refY: 0.5 };\r\n\r\n acc[`port-${pos}`] = {\r\n ref: \"body\",\r\n ...refConfig,\r\n r: 4,\r\n fill: \"#31d0c6\",\r\n stroke: \"#ffffff\",\r\n strokeWidth: 2,\r\n magnet: true,\r\n style: { cursor: \"crosshair\", opacity: 0, transition: \"opacity 0.2s\" },\r\n };\r\n return acc;\r\n }, {} as any);\r\n\r\n/* 节点配置 */\r\nexport const nodeConfigs = {\r\n event: {\r\n inherit: \"circle\",\r\n attrs: {\r\n body: {\r\n strokeWidth: 2,\r\n stroke: \"#5F95FF\",\r\n fill: \"#FFF\",\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n text: {\r\n fontSize: 12,\r\n fill: \"#262626\",\r\n textAnchor: \"middle\",\r\n textVerticalAnchor: \"middle\",\r\n pointerEvents: \"none\",\r\n },\r\n },\r\n },\r\n activity: {\r\n inherit: \"rect\",\r\n attrs: {\r\n body: {\r\n rx: 6,\r\n ry: 6,\r\n stroke: \"#5F95FF\",\r\n fill: \"#EFF4FF\",\r\n strokeWidth: 1,\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n text: {\r\n fontSize: 12,\r\n fill: \"#262626\",\r\n textAnchor: \"middle\",\r\n textVerticalAnchor: \"middle\",\r\n pointerEvents: \"none\",\r\n },\r\n },\r\n },\r\n gateway: {\r\n inherit: \"polygon\",\r\n attrs: {\r\n body: {\r\n refPoints: \"0,10 10,0 20,10 10,20\",\r\n strokeWidth: 2,\r\n stroke: \"#5F95FF\",\r\n fill: \"#EFF4FF\",\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n text: {\r\n fontSize: 20,\r\n fill: \"#5F95FF\",\r\n textAnchor: \"middle\",\r\n textVerticalAnchor: \"middle\",\r\n pointerEvents: \"none\",\r\n },\r\n },\r\n },\r\n};\r\n\r\n/* 边配置 */\r\nexport const edgeConfig = {\r\n inherit: \"edge\",\r\n attrs: {\r\n line: {\r\n stroke: \"#A2B1C3\",\r\n strokeWidth: 2,\r\n targetMarker: {\r\n name: \"block\",\r\n width: 8,\r\n height: 6,\r\n fill: \"#A2B1C3\",\r\n },\r\n cursor: \"pointer\",\r\n },\r\n },\r\n};\r\n\r\n/**\r\n * 获取图形配置(支持主题切换)\r\n * @param isDark 是否为暗色主题\r\n * @returns Graph配置对象\r\n */\r\nexport function getGraphConfig(isDark = false) {\r\n const themeColors = {\r\n background: isDark ? \"#18181c\" : \"#ffffff\",\r\n gridPrimary: isDark ? \"rgba(255, 255, 255, 0.08)\" : \"#eee\",\r\n gridSecondary: isDark ? \"rgba(255, 255, 255, 0.04)\" : \"#ddd\",\r\n };\r\n\r\n return {\r\n background: { color: themeColors.background },\r\n grid: {\r\n visible: true,\r\n type: \"doubleMesh\",\r\n args: [\r\n { color: themeColors.gridPrimary, thickness: 1 },\r\n { color: themeColors.gridSecondary, thickness: 1, factor: 4 },\r\n ],\r\n },\r\n mousewheel: {\r\n enabled: true,\r\n zoomAtMousePosition: true,\r\n modifiers: \"ctrl\",\r\n minScale: 0.5,\r\n maxScale: 3,\r\n },\r\n connecting: {\r\n router: \"manhattan\",\r\n connector: { name: \"rounded\", args: { radius: 8 } },\r\n allowBlank: false,\r\n allowLoop: false,\r\n allowNode: false,\r\n snap: true,\r\n allowEdge: false,\r\n },\r\n highlighting: {\r\n magnetAvailable: {\r\n name: \"stroke\",\r\n args: { attrs: { fill: \"#31d0c6\", stroke: \"#31d0c6\", opacity: 1 } },\r\n },\r\n magnetAdsorbed: {\r\n name: \"stroke\",\r\n args: { attrs: { fill: \"#5F95FF\", stroke: \"#5F95FF\", opacity: 1 } },\r\n },\r\n },\r\n selecting: {\r\n enabled: true,\r\n rubberband: true,\r\n showNodeSelectionBox: true,\r\n },\r\n resizing: true,\r\n rotating: false,\r\n snapline: true,\r\n keyboard: true,\r\n clipboard: true,\r\n };\r\n}\r\n\r\n/* 保持向后兼容 */\r\nexport const graphConfig = getGraphConfig();\r\n\r\n/* 添加元素配置 */\r\nexport const addElementConfigs = {\r\n \"start-event\": {\r\n shape: \"event\",\r\n width: 50,\r\n height: 50,\r\n label: \"开始事件\",\r\n data: { type: \"start\" },\r\n },\r\n \"end-event\": {\r\n shape: \"event\",\r\n width: 50,\r\n height: 50,\r\n label: \"结束事件\",\r\n data: { type: \"end\" },\r\n },\r\n activity: {\r\n shape: \"activity\",\r\n width: 120,\r\n height: 60,\r\n label: \"新活动\",\r\n data: { description: \"\", assignee: \"\" },\r\n },\r\n gateway: {\r\n shape: \"gateway\",\r\n width: 40,\r\n height: 40,\r\n label: \"+\",\r\n data: { type: \"exclusive\" },\r\n },\r\n};\r\n\r\n/* 示例数据 */\r\nexport const sampleData = [\r\n {\r\n id: \"start-1\",\r\n shape: \"event\",\r\n x: 100,\r\n y: 200,\r\n width: 50,\r\n height: 50,\r\n label: \"流程开始\",\r\n data: { type: \"start\" },\r\n },\r\n {\r\n id: \"activity-1\",\r\n shape: \"activity\",\r\n x: 200,\r\n y: 180,\r\n width: 120,\r\n height: 60,\r\n label: \"用户申请\",\r\n data: { description: \"用户提交申请表单\", assignee: \"申请人\" },\r\n },\r\n {\r\n id: \"activity-2\",\r\n shape: \"activity\",\r\n x: 380,\r\n y: 180,\r\n width: 120,\r\n height: 60,\r\n label: \"初审\",\r\n data: { description: \"部门进行初步审核\", assignee: \"部门主管\" },\r\n },\r\n {\r\n id: \"gateway-1\",\r\n shape: \"gateway\",\r\n x: 550,\r\n y: 200,\r\n width: 40,\r\n height: 40,\r\n label: \"+\",\r\n data: { type: \"exclusive\" },\r\n },\r\n {\r\n id: \"activity-3\",\r\n shape: \"activity\",\r\n x: 650,\r\n y: 120,\r\n width: 120,\r\n height: 60,\r\n label: \"终审\",\r\n data: { description: \"高级主管最终审核\", assignee: \"总监\" },\r\n },\r\n {\r\n id: \"activity-4\",\r\n shape: \"activity\",\r\n x: 650,\r\n y: 260,\r\n width: 120,\r\n height: 60,\r\n label: \"驳回处理\",\r\n data: { description: \"处理驳回流程\", assignee: \"专员\" },\r\n },\r\n {\r\n id: \"end-1\",\r\n shape: \"event\",\r\n x: 820,\r\n y: 140,\r\n width: 50,\r\n height: 50,\r\n label: \"审批通过\",\r\n data: { type: \"end\" },\r\n },\r\n {\r\n id: \"end-2\",\r\n shape: \"event\",\r\n x: 820,\r\n y: 280,\r\n width: 50,\r\n height: 50,\r\n label: \"审批驳回\",\r\n data: { type: \"end\" },\r\n },\r\n {\r\n id: \"edge-1\",\r\n shape: \"bpmn-edge\",\r\n source: \"start-1\",\r\n target: \"activity-1\",\r\n x: 0,\r\n y: 0,\r\n },\r\n {\r\n id: \"edge-2\",\r\n shape: \"bpmn-edge\",\r\n source: \"activity-1\",\r\n target: \"activity-2\",\r\n x: 0,\r\n y: 0,\r\n },\r\n {\r\n id: \"edge-3\",\r\n shape: \"bpmn-edge\",\r\n source: \"activity-2\",\r\n target: \"gateway-1\",\r\n x: 0,\r\n y: 0,\r\n },\r\n {\r\n id: \"edge-4\",\r\n shape: \"bpmn-edge\",\r\n source: \"gateway-1\",\r\n target: \"activity-3\",\r\n label: \"通过\",\r\n x: 0,\r\n y: 0,\r\n },\r\n {\r\n id: \"edge-5\",\r\n shape: \"bpmn-edge\",\r\n source: \"gateway-1\",\r\n target: \"activity-4\",\r\n label: \"驳回\",\r\n x: 0,\r\n y: 0,\r\n },\r\n {\r\n id: \"edge-6\",\r\n shape: \"bpmn-edge\",\r\n source: \"activity-3\",\r\n target: \"end-1\",\r\n x: 0,\r\n y: 0,\r\n },\r\n {\r\n id: \"edge-7\",\r\n shape: \"bpmn-edge\",\r\n source: \"activity-4\",\r\n target: \"end-2\",\r\n x: 0,\r\n y: 0,\r\n },\r\n];\r\n","<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"300\"\r\n title=\"属性设置\"\r\n @update:show=\"emit('update:show', $event)\"\r\n >\r\n <div v-if=\"editingElement\" class=\"property-panel\">\r\n <div class=\"property-item\">\r\n <div class=\"property-label\">名称</div>\r\n <NInput\r\n v-model:value=\"editingElement.label\"\r\n placeholder=\"请输入名称\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"property-item\">\r\n <div class=\"property-label\">类型</div>\r\n <NInput :value=\"typeName\" readonly size=\"small\" />\r\n </div>\r\n <div class=\"property-item\" v-if=\"editingElement.shape === 'activity'\">\r\n <div class=\"property-label\">描述</div>\r\n <NInput\r\n v-model:value=\"editingElement.description\"\r\n type=\"textarea\"\r\n :rows=\"3\"\r\n placeholder=\"请输入活动描述\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"property-item\" v-if=\"editingElement.shape === 'activity'\">\r\n <div class=\"property-label\">执行人</div>\r\n <NInput\r\n v-model:value=\"editingElement.assignee\"\r\n placeholder=\"请输入执行人\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"form-actions\">\r\n <NSpace>\r\n <NButton @click=\"emit('save')\" type=\"primary\" size=\"small\">\r\n 保存\r\n </NButton>\r\n <NButton @click=\"emit('update:show', false)\" size=\"small\">\r\n 取消\r\n </NButton>\r\n <NButton @click=\"emit('delete')\" type=\"error\" size=\"small\">\r\n 删除\r\n </NButton>\r\n </NSpace>\r\n </div>\r\n </div>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport { NDrawer, NInput, NSpace, NButton } from \"naive-ui\";\r\nimport { elementTypeNames } from \"../data\";\r\n\r\ninterface BPMNElement {\r\n id: string;\r\n shape: string;\r\n label?: string;\r\n description?: string;\r\n assignee?: string;\r\n [key: string]: any;\r\n}\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingElement = defineModel<BPMNElement>(\"editingElement\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n delete: [];\r\n}>();\r\n\r\nconst typeName = computed(\r\n () =>\r\n elementTypeNames[\r\n editingElement.value?.shape as keyof typeof elementTypeNames\r\n ] ||\r\n editingElement.value?.shape ||\r\n \"\",\r\n);\r\n</script>\r\n","<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"300\"\r\n title=\"属性设置\"\r\n @update:show=\"emit('update:show', $event)\"\r\n >\r\n <div v-if=\"editingElement\" class=\"property-panel\">\r\n <div class=\"property-item\">\r\n <div class=\"property-label\">名称</div>\r\n <NInput\r\n v-model:value=\"editingElement.label\"\r\n placeholder=\"请输入名称\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"property-item\">\r\n <div class=\"property-label\">类型</div>\r\n <NInput :value=\"typeName\" readonly size=\"small\" />\r\n </div>\r\n <div class=\"property-item\" v-if=\"editingElement.shape === 'activity'\">\r\n <div class=\"property-label\">描述</div>\r\n <NInput\r\n v-model:value=\"editingElement.description\"\r\n type=\"textarea\"\r\n :rows=\"3\"\r\n placeholder=\"请输入活动描述\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"property-item\" v-if=\"editingElement.shape === 'activity'\">\r\n <div class=\"property-label\">执行人</div>\r\n <NInput\r\n v-model:value=\"editingElement.assignee\"\r\n placeholder=\"请输入执行人\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"form-actions\">\r\n <NSpace>\r\n <NButton @click=\"emit('save')\" type=\"primary\" size=\"small\">\r\n 保存\r\n </NButton>\r\n <NButton @click=\"emit('update:show', false)\" size=\"small\">\r\n 取消\r\n </NButton>\r\n <NButton @click=\"emit('delete')\" type=\"error\" size=\"small\">\r\n 删除\r\n </NButton>\r\n </NSpace>\r\n </div>\r\n </div>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport { NDrawer, NInput, NSpace, NButton } from \"naive-ui\";\r\nimport { elementTypeNames } from \"../data\";\r\n\r\ninterface BPMNElement {\r\n id: string;\r\n shape: string;\r\n label?: string;\r\n description?: string;\r\n assignee?: string;\r\n [key: string]: any;\r\n}\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingElement = defineModel<BPMNElement>(\"editingElement\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n delete: [];\r\n}>();\r\n\r\nconst typeName = computed(\r\n () =>\r\n elementTypeNames[\r\n editingElement.value?.shape as keyof typeof elementTypeNames\r\n ] ||\r\n editingElement.value?.shape ||\r\n \"\",\r\n);\r\n</script>\r\n","<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"300\"\r\n title=\"属性设置\"\r\n @update:show=\"emit('update:show', $event)\"\r\n >\r\n <div v-if=\"editingElement\" class=\"property-panel\">\r\n <div class=\"property-item\">\r\n <div class=\"property-label\">名称</div>\r\n <NInput\r\n v-model:value=\"editingElement.label\"\r\n placeholder=\"请输入名称\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"property-item\">\r\n <div class=\"property-label\">类型</div>\r\n <NInput :value=\"typeName\" readonly size=\"small\" />\r\n </div>\r\n <div class=\"property-item\" v-if=\"editingElement.shape === 'activity'\">\r\n <div class=\"property-label\">描述</div>\r\n <NInput\r\n v-model:value=\"editingElement.description\"\r\n type=\"textarea\"\r\n :rows=\"3\"\r\n placeholder=\"请输入活动描述\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"property-item\" v-if=\"editingElement.shape === 'activity'\">\r\n <div class=\"property-label\">执行人</div>\r\n <NInput\r\n v-model:value=\"editingElement.assignee\"\r\n placeholder=\"请输入执行人\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"form-actions\">\r\n <NSpace>\r\n <NButton @click=\"emit('save')\" type=\"primary\" size=\"small\">\r\n 保存\r\n </NButton>\r\n <NButton @click=\"emit('update:show', false)\" size=\"small\">\r\n 取消\r\n </NButton>\r\n <NButton @click=\"emit('delete')\" type=\"error\" size=\"small\">\r\n 删除\r\n </NButton>\r\n </NSpace>\r\n </div>\r\n </div>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport { NDrawer, NInput, NSpace, NButton } from \"naive-ui\";\r\nimport { elementTypeNames } from \"../data\";\r\n\r\ninterface BPMNElement {\r\n id: string;\r\n shape: string;\r\n label?: string;\r\n description?: string;\r\n assignee?: string;\r\n [key: string]: any;\r\n}\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingElement = defineModel<BPMNElement>(\"editingElement\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n delete: [];\r\n}>();\r\n\r\nconst typeName = computed(\r\n () =>\r\n elementTypeNames[\r\n editingElement.value?.shape as keyof typeof elementTypeNames\r\n ] ||\r\n editingElement.value?.shape ||\r\n \"\",\r\n);\r\n</script>\r\n","<template>\r\n <div class=\"bpmn-layout\">\r\n <div class=\"top-toolbar\" v-if=\"showToolbar\">\r\n <div class=\"toolbar-section\">\r\n <div class=\"toolbar-group\">\r\n <NButton @click=\"clearAll\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete-outline\" :size=\"16\" />\r\n </template>\r\n 清空\r\n </NButton>\r\n </div>\r\n <div class=\"toolbar-group\">\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n </div>\r\n <div class=\"toolbar-group\">\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"main-content\">\r\n <div class=\"left-panel\" v-if=\"showToolbar\">\r\n <div class=\"panel-title\">组件</div>\r\n <div class=\"element-grid\">\r\n <div\r\n v-for=\"(item, key) in elementTypes\"\r\n :key=\"key\"\r\n class=\"element-item\"\r\n @click=\"addElement(key)\"\r\n :title=\"item.title\"\r\n >\r\n <div :class=\"['element-icon', item.iconClass]\"></div>\r\n <span>{{ item.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"graph-wrapper\">\r\n <div ref=\"containerRef\" class=\"graph-container\"></div>\r\n </div>\r\n </div>\r\n\r\n <BPMNPropertyEditor\r\n :show=\"showEditor\"\r\n v-model:editing-element=\"editingElement\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveElement\"\r\n @delete=\"deleteElement\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, onMounted } from \"vue\";\r\nimport { NButton, NDropdown } from \"naive-ui\";\r\nimport { Graph } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport { useEdgeInteraction } from \"../../composables/useEdgeInteraction\";\r\nimport BPMNPropertyEditor from \"./components/BPMNPropertyEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\nimport {\r\n elementTypes,\r\n portPositions,\r\n createPortAttrs,\r\n nodeConfigs,\r\n edgeConfig,\r\n getGraphConfig,\r\n addElementConfigs,\r\n sampleData,\r\n} from \"./data\";\r\n\r\ninterface BPMNElement {\r\n id: string;\r\n shape: string;\r\n x: number;\r\n y: number;\r\n width?: number;\r\n height?: number;\r\n label?: string;\r\n source?: string;\r\n target?: string;\r\n description?: string;\r\n assignee?: string;\r\n data?: any;\r\n [key: string]: any;\r\n}\r\n\r\ninterface Props {\r\n data?: BPMNElement[] | Record<string, BPMNElement[]>;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n width?: number | string;\r\n height?: number | string;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n width: \"100%\",\r\n height: \"600px\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n ready: [graph: Graph];\r\n \"data-change\": [data: BPMNElement[]];\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"bpmn-diagram\");\r\nconst { bindInteractions } = useEdgeInteraction(graph, {\r\n portPositions,\r\n onDataChange: () => emitDataChange(),\r\n});\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingElement = ref<BPMNElement>();\r\n\r\n// ==================== 工具函数 ====================\r\nconst getLabel = (cell: any): string =>\r\n String(cell.attr(\"text/text\") || cell.attr(\"label/text\") || \"\");\r\n\r\nconst normalizeData = (data: any): any[] => {\r\n if (!data) return [];\r\n if (Array.isArray(data)) return data;\r\n const result: any[] = [];\r\n Object.values(data).forEach((arr: any) => {\r\n if (Array.isArray(arr)) result.push(...arr);\r\n });\r\n return result;\r\n};\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n const portMarkup = portPositions.map((pos) => ({\r\n tagName: \"circle\",\r\n selector: `port-${pos}`,\r\n }));\r\n\r\n const nodeTypes = [\"event\", \"activity\", \"gateway\"] as const;\r\n const bodyTags = { event: \"circle\", activity: \"rect\", gateway: \"polygon\" };\r\n\r\n nodeTypes.forEach((type) => {\r\n Graph.registerNode(\r\n type,\r\n {\r\n ...nodeConfigs[type],\r\n markup: [\r\n { tagName: bodyTags[type], selector: \"body\" },\r\n { tagName: \"text\", selector: \"text\" },\r\n ...portMarkup,\r\n ],\r\n attrs: {\r\n ...nodeConfigs[type].attrs,\r\n ...createPortAttrs(portPositions, bodyTags[type]),\r\n },\r\n },\r\n true,\r\n );\r\n });\r\n\r\n Graph.registerEdge(\"bpmn-edge\", edgeConfig, true);\r\n};\r\n\r\n// ==================== 数据操作 ====================\r\nconst loadData = (data: any[]) => {\r\n if (!graph.value || !data.length) return;\r\n const cells = data.map((item) => {\r\n const { data: nodeData, ...cellProps } = item;\r\n const cell =\r\n item.shape === \"bpmn-edge\"\r\n ? graph.value!.createEdge(cellProps)\r\n : graph.value!.createNode(cellProps);\r\n if (nodeData) cell.setData(nodeData);\r\n return cell;\r\n });\r\n graph.value.resetCells(cells);\r\n setTimeout(() => graph.value!.zoomToFit({ padding: 50, maxScale: 1 }), 200);\r\n};\r\n\r\nconst addElement = (type: string) => {\r\n if (!graph.value) return;\r\n const config = addElementConfigs[type as keyof typeof addElementConfigs];\r\n if (!config) return;\r\n\r\n const centerX = (graph.value.options.width as number) / 2;\r\n const centerY = (graph.value.options.height as number) / 2;\r\n const { data: nodeData, ...nodeProps } = {\r\n id: `${type}-${Date.now()}`,\r\n x: centerX + Math.random() * 100 - 50,\r\n y: centerY + Math.random() * 100 - 50,\r\n ...config,\r\n };\r\n const node = graph.value.createNode(nodeProps);\r\n if (nodeData) node.setData(nodeData);\r\n graph.value.addCell(node);\r\n emitDataChange();\r\n};\r\n\r\nconst clearAll = () => {\r\n graph.value?.clearCells();\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 元素编辑 ====================\r\nconst editElement = (cell: any) => {\r\n const cellData = cell.getData() || {};\r\n editingElement.value = {\r\n id: cell.id,\r\n shape: cell.shape,\r\n label: getLabel(cell),\r\n description: cellData.description || \"\",\r\n assignee: cellData.assignee || \"\",\r\n x: 0,\r\n y: 0,\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveElement = () => {\r\n if (!graph.value || !editingElement.value) return;\r\n const cell = graph.value.getCellById(editingElement.value.id);\r\n if (cell) {\r\n cell.setData({\r\n description: editingElement.value.description,\r\n assignee: editingElement.value.assignee,\r\n });\r\n cell.attr(\"text/text\", editingElement.value.label || \"\");\r\n cell.attr(\"label/text\", editingElement.value.label || \"\");\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\nconst deleteElement = () => {\r\n if (!graph.value || !editingElement.value) return;\r\n graph.value.getCellById(editingElement.value.id)?.remove();\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): BPMNElement[] => {\r\n if (!graph.value) return [];\r\n return [\r\n ...graph.value.getNodes().map((node) => ({\r\n id: node.id,\r\n shape: node.shape,\r\n x: node.getPosition().x,\r\n y: node.getPosition().y,\r\n width: node.getSize().width,\r\n height: node.getSize().height,\r\n label: getLabel(node),\r\n data: node.getData() || {},\r\n })),\r\n ...graph.value.getEdges().map((edge) => ({\r\n id: edge.id,\r\n shape: \"bpmn-edge\",\r\n source: edge.getSourceCellId() || \"\",\r\n target: edge.getTargetCellId() || \"\",\r\n label: getLabel(edge),\r\n x: 0,\r\n y: 0,\r\n })),\r\n ] as BPMNElement[];\r\n};\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n if (!props.readonly) {\r\n bindInteractions();\r\n newGraph.on(\"node:dblclick\", ({ node }) => editElement(node));\r\n newGraph.on(\"node:moved\", emitDataChange);\r\n }\r\n\r\n emit(\"ready\", newGraph);\r\n loadData(sampleData);\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (!newData || !graph.value) return;\r\n const normalized = normalizeData(newData);\r\n if (normalized.length > 0) loadData(normalized);\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(async () => {\r\n registerNodes();\r\n const bpmnConfig = getGraphConfig();\r\n await initGraph({\r\n ...bpmnConfig,\r\n connecting: {\r\n ...bpmnConfig.connecting,\r\n createEdge: () => graph.value!.createEdge({ shape: \"bpmn-edge\" }),\r\n validateConnection: ({\r\n sourceView,\r\n targetView,\r\n sourceMagnet,\r\n targetMagnet,\r\n }: any) =>\r\n sourceView !== targetView &&\r\n !!sourceMagnet &&\r\n !!targetMagnet &&\r\n sourceMagnet.getAttribute(\"magnet\") === \"true\" &&\r\n targetMagnet.getAttribute(\"magnet\") === \"true\",\r\n },\r\n });\r\n});\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value,\r\n getData: getCurrentData,\r\n loadData,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <div class=\"bpmn-layout\">\r\n <div class=\"top-toolbar\" v-if=\"showToolbar\">\r\n <div class=\"toolbar-section\">\r\n <div class=\"toolbar-group\">\r\n <NButton @click=\"clearAll\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete-outline\" :size=\"16\" />\r\n </template>\r\n 清空\r\n </NButton>\r\n </div>\r\n <div class=\"toolbar-group\">\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n </div>\r\n <div class=\"toolbar-group\">\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"main-content\">\r\n <div class=\"left-panel\" v-if=\"showToolbar\">\r\n <div class=\"panel-title\">组件</div>\r\n <div class=\"element-grid\">\r\n <div\r\n v-for=\"(item, key) in elementTypes\"\r\n :key=\"key\"\r\n class=\"element-item\"\r\n @click=\"addElement(key)\"\r\n :title=\"item.title\"\r\n >\r\n <div :class=\"['element-icon', item.iconClass]\"></div>\r\n <span>{{ item.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"graph-wrapper\">\r\n <div ref=\"containerRef\" class=\"graph-container\"></div>\r\n </div>\r\n </div>\r\n\r\n <BPMNPropertyEditor\r\n :show=\"showEditor\"\r\n v-model:editing-element=\"editingElement\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveElement\"\r\n @delete=\"deleteElement\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, onMounted } from \"vue\";\r\nimport { NButton, NDropdown } from \"naive-ui\";\r\nimport { Graph } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport { useEdgeInteraction } from \"../../composables/useEdgeInteraction\";\r\nimport BPMNPropertyEditor from \"./components/BPMNPropertyEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\nimport {\r\n elementTypes,\r\n portPositions,\r\n createPortAttrs,\r\n nodeConfigs,\r\n edgeConfig,\r\n getGraphConfig,\r\n addElementConfigs,\r\n sampleData,\r\n} from \"./data\";\r\n\r\ninterface BPMNElement {\r\n id: string;\r\n shape: string;\r\n x: number;\r\n y: number;\r\n width?: number;\r\n height?: number;\r\n label?: string;\r\n source?: string;\r\n target?: string;\r\n description?: string;\r\n assignee?: string;\r\n data?: any;\r\n [key: string]: any;\r\n}\r\n\r\ninterface Props {\r\n data?: BPMNElement[] | Record<string, BPMNElement[]>;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n width?: number | string;\r\n height?: number | string;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n width: \"100%\",\r\n height: \"600px\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n ready: [graph: Graph];\r\n \"data-change\": [data: BPMNElement[]];\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"bpmn-diagram\");\r\nconst { bindInteractions } = useEdgeInteraction(graph, {\r\n portPositions,\r\n onDataChange: () => emitDataChange(),\r\n});\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingElement = ref<BPMNElement>();\r\n\r\n// ==================== 工具函数 ====================\r\nconst getLabel = (cell: any): string =>\r\n String(cell.attr(\"text/text\") || cell.attr(\"label/text\") || \"\");\r\n\r\nconst normalizeData = (data: any): any[] => {\r\n if (!data) return [];\r\n if (Array.isArray(data)) return data;\r\n const result: any[] = [];\r\n Object.values(data).forEach((arr: any) => {\r\n if (Array.isArray(arr)) result.push(...arr);\r\n });\r\n return result;\r\n};\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n const portMarkup = portPositions.map((pos) => ({\r\n tagName: \"circle\",\r\n selector: `port-${pos}`,\r\n }));\r\n\r\n const nodeTypes = [\"event\", \"activity\", \"gateway\"] as const;\r\n const bodyTags = { event: \"circle\", activity: \"rect\", gateway: \"polygon\" };\r\n\r\n nodeTypes.forEach((type) => {\r\n Graph.registerNode(\r\n type,\r\n {\r\n ...nodeConfigs[type],\r\n markup: [\r\n { tagName: bodyTags[type], selector: \"body\" },\r\n { tagName: \"text\", selector: \"text\" },\r\n ...portMarkup,\r\n ],\r\n attrs: {\r\n ...nodeConfigs[type].attrs,\r\n ...createPortAttrs(portPositions, bodyTags[type]),\r\n },\r\n },\r\n true,\r\n );\r\n });\r\n\r\n Graph.registerEdge(\"bpmn-edge\", edgeConfig, true);\r\n};\r\n\r\n// ==================== 数据操作 ====================\r\nconst loadData = (data: any[]) => {\r\n if (!graph.value || !data.length) return;\r\n const cells = data.map((item) => {\r\n const { data: nodeData, ...cellProps } = item;\r\n const cell =\r\n item.shape === \"bpmn-edge\"\r\n ? graph.value!.createEdge(cellProps)\r\n : graph.value!.createNode(cellProps);\r\n if (nodeData) cell.setData(nodeData);\r\n return cell;\r\n });\r\n graph.value.resetCells(cells);\r\n setTimeout(() => graph.value!.zoomToFit({ padding: 50, maxScale: 1 }), 200);\r\n};\r\n\r\nconst addElement = (type: string) => {\r\n if (!graph.value) return;\r\n const config = addElementConfigs[type as keyof typeof addElementConfigs];\r\n if (!config) return;\r\n\r\n const centerX = (graph.value.options.width as number) / 2;\r\n const centerY = (graph.value.options.height as number) / 2;\r\n const { data: nodeData, ...nodeProps } = {\r\n id: `${type}-${Date.now()}`,\r\n x: centerX + Math.random() * 100 - 50,\r\n y: centerY + Math.random() * 100 - 50,\r\n ...config,\r\n };\r\n const node = graph.value.createNode(nodeProps);\r\n if (nodeData) node.setData(nodeData);\r\n graph.value.addCell(node);\r\n emitDataChange();\r\n};\r\n\r\nconst clearAll = () => {\r\n graph.value?.clearCells();\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 元素编辑 ====================\r\nconst editElement = (cell: any) => {\r\n const cellData = cell.getData() || {};\r\n editingElement.value = {\r\n id: cell.id,\r\n shape: cell.shape,\r\n label: getLabel(cell),\r\n description: cellData.description || \"\",\r\n assignee: cellData.assignee || \"\",\r\n x: 0,\r\n y: 0,\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveElement = () => {\r\n if (!graph.value || !editingElement.value) return;\r\n const cell = graph.value.getCellById(editingElement.value.id);\r\n if (cell) {\r\n cell.setData({\r\n description: editingElement.value.description,\r\n assignee: editingElement.value.assignee,\r\n });\r\n cell.attr(\"text/text\", editingElement.value.label || \"\");\r\n cell.attr(\"label/text\", editingElement.value.label || \"\");\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\nconst deleteElement = () => {\r\n if (!graph.value || !editingElement.value) return;\r\n graph.value.getCellById(editingElement.value.id)?.remove();\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): BPMNElement[] => {\r\n if (!graph.value) return [];\r\n return [\r\n ...graph.value.getNodes().map((node) => ({\r\n id: node.id,\r\n shape: node.shape,\r\n x: node.getPosition().x,\r\n y: node.getPosition().y,\r\n width: node.getSize().width,\r\n height: node.getSize().height,\r\n label: getLabel(node),\r\n data: node.getData() || {},\r\n })),\r\n ...graph.value.getEdges().map((edge) => ({\r\n id: edge.id,\r\n shape: \"bpmn-edge\",\r\n source: edge.getSourceCellId() || \"\",\r\n target: edge.getTargetCellId() || \"\",\r\n label: getLabel(edge),\r\n x: 0,\r\n y: 0,\r\n })),\r\n ] as BPMNElement[];\r\n};\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n if (!props.readonly) {\r\n bindInteractions();\r\n newGraph.on(\"node:dblclick\", ({ node }) => editElement(node));\r\n newGraph.on(\"node:moved\", emitDataChange);\r\n }\r\n\r\n emit(\"ready\", newGraph);\r\n loadData(sampleData);\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (!newData || !graph.value) return;\r\n const normalized = normalizeData(newData);\r\n if (normalized.length > 0) loadData(normalized);\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(async () => {\r\n registerNodes();\r\n const bpmnConfig = getGraphConfig();\r\n await initGraph({\r\n ...bpmnConfig,\r\n connecting: {\r\n ...bpmnConfig.connecting,\r\n createEdge: () => graph.value!.createEdge({ shape: \"bpmn-edge\" }),\r\n validateConnection: ({\r\n sourceView,\r\n targetView,\r\n sourceMagnet,\r\n targetMagnet,\r\n }: any) =>\r\n sourceView !== targetView &&\r\n !!sourceMagnet &&\r\n !!targetMagnet &&\r\n sourceMagnet.getAttribute(\"magnet\") === \"true\" &&\r\n targetMagnet.getAttribute(\"magnet\") === \"true\",\r\n },\r\n });\r\n});\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value,\r\n getData: getCurrentData,\r\n loadData,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <div class=\"bpmn-layout\">\r\n <div class=\"top-toolbar\" v-if=\"showToolbar\">\r\n <div class=\"toolbar-section\">\r\n <div class=\"toolbar-group\">\r\n <NButton @click=\"clearAll\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete-outline\" :size=\"16\" />\r\n </template>\r\n 清空\r\n </NButton>\r\n </div>\r\n <div class=\"toolbar-group\">\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n </div>\r\n <div class=\"toolbar-group\">\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"main-content\">\r\n <div class=\"left-panel\" v-if=\"showToolbar\">\r\n <div class=\"panel-title\">组件</div>\r\n <div class=\"element-grid\">\r\n <div\r\n v-for=\"(item, key) in elementTypes\"\r\n :key=\"key\"\r\n class=\"element-item\"\r\n @click=\"addElement(key)\"\r\n :title=\"item.title\"\r\n >\r\n <div :class=\"['element-icon', item.iconClass]\"></div>\r\n <span>{{ item.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"graph-wrapper\">\r\n <div ref=\"containerRef\" class=\"graph-container\"></div>\r\n </div>\r\n </div>\r\n\r\n <BPMNPropertyEditor\r\n :show=\"showEditor\"\r\n v-model:editing-element=\"editingElement\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveElement\"\r\n @delete=\"deleteElement\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, onMounted } from \"vue\";\r\nimport { NButton, NDropdown } from \"naive-ui\";\r\nimport { Graph } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport { useEdgeInteraction } from \"../../composables/useEdgeInteraction\";\r\nimport BPMNPropertyEditor from \"./components/BPMNPropertyEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\nimport {\r\n elementTypes,\r\n portPositions,\r\n createPortAttrs,\r\n nodeConfigs,\r\n edgeConfig,\r\n getGraphConfig,\r\n addElementConfigs,\r\n sampleData,\r\n} from \"./data\";\r\n\r\ninterface BPMNElement {\r\n id: string;\r\n shape: string;\r\n x: number;\r\n y: number;\r\n width?: number;\r\n height?: number;\r\n label?: string;\r\n source?: string;\r\n target?: string;\r\n description?: string;\r\n assignee?: string;\r\n data?: any;\r\n [key: string]: any;\r\n}\r\n\r\ninterface Props {\r\n data?: BPMNElement[] | Record<string, BPMNElement[]>;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n width?: number | string;\r\n height?: number | string;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n width: \"100%\",\r\n height: \"600px\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n ready: [graph: Graph];\r\n \"data-change\": [data: BPMNElement[]];\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"bpmn-diagram\");\r\nconst { bindInteractions } = useEdgeInteraction(graph, {\r\n portPositions,\r\n onDataChange: () => emitDataChange(),\r\n});\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingElement = ref<BPMNElement>();\r\n\r\n// ==================== 工具函数 ====================\r\nconst getLabel = (cell: any): string =>\r\n String(cell.attr(\"text/text\") || cell.attr(\"label/text\") || \"\");\r\n\r\nconst normalizeData = (data: any): any[] => {\r\n if (!data) return [];\r\n if (Array.isArray(data)) return data;\r\n const result: any[] = [];\r\n Object.values(data).forEach((arr: any) => {\r\n if (Array.isArray(arr)) result.push(...arr);\r\n });\r\n return result;\r\n};\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n const portMarkup = portPositions.map((pos) => ({\r\n tagName: \"circle\",\r\n selector: `port-${pos}`,\r\n }));\r\n\r\n const nodeTypes = [\"event\", \"activity\", \"gateway\"] as const;\r\n const bodyTags = { event: \"circle\", activity: \"rect\", gateway: \"polygon\" };\r\n\r\n nodeTypes.forEach((type) => {\r\n Graph.registerNode(\r\n type,\r\n {\r\n ...nodeConfigs[type],\r\n markup: [\r\n { tagName: bodyTags[type], selector: \"body\" },\r\n { tagName: \"text\", selector: \"text\" },\r\n ...portMarkup,\r\n ],\r\n attrs: {\r\n ...nodeConfigs[type].attrs,\r\n ...createPortAttrs(portPositions, bodyTags[type]),\r\n },\r\n },\r\n true,\r\n );\r\n });\r\n\r\n Graph.registerEdge(\"bpmn-edge\", edgeConfig, true);\r\n};\r\n\r\n// ==================== 数据操作 ====================\r\nconst loadData = (data: any[]) => {\r\n if (!graph.value || !data.length) return;\r\n const cells = data.map((item) => {\r\n const { data: nodeData, ...cellProps } = item;\r\n const cell =\r\n item.shape === \"bpmn-edge\"\r\n ? graph.value!.createEdge(cellProps)\r\n : graph.value!.createNode(cellProps);\r\n if (nodeData) cell.setData(nodeData);\r\n return cell;\r\n });\r\n graph.value.resetCells(cells);\r\n setTimeout(() => graph.value!.zoomToFit({ padding: 50, maxScale: 1 }), 200);\r\n};\r\n\r\nconst addElement = (type: string) => {\r\n if (!graph.value) return;\r\n const config = addElementConfigs[type as keyof typeof addElementConfigs];\r\n if (!config) return;\r\n\r\n const centerX = (graph.value.options.width as number) / 2;\r\n const centerY = (graph.value.options.height as number) / 2;\r\n const { data: nodeData, ...nodeProps } = {\r\n id: `${type}-${Date.now()}`,\r\n x: centerX + Math.random() * 100 - 50,\r\n y: centerY + Math.random() * 100 - 50,\r\n ...config,\r\n };\r\n const node = graph.value.createNode(nodeProps);\r\n if (nodeData) node.setData(nodeData);\r\n graph.value.addCell(node);\r\n emitDataChange();\r\n};\r\n\r\nconst clearAll = () => {\r\n graph.value?.clearCells();\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 元素编辑 ====================\r\nconst editElement = (cell: any) => {\r\n const cellData = cell.getData() || {};\r\n editingElement.value = {\r\n id: cell.id,\r\n shape: cell.shape,\r\n label: getLabel(cell),\r\n description: cellData.description || \"\",\r\n assignee: cellData.assignee || \"\",\r\n x: 0,\r\n y: 0,\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveElement = () => {\r\n if (!graph.value || !editingElement.value) return;\r\n const cell = graph.value.getCellById(editingElement.value.id);\r\n if (cell) {\r\n cell.setData({\r\n description: editingElement.value.description,\r\n assignee: editingElement.value.assignee,\r\n });\r\n cell.attr(\"text/text\", editingElement.value.label || \"\");\r\n cell.attr(\"label/text\", editingElement.value.label || \"\");\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\nconst deleteElement = () => {\r\n if (!graph.value || !editingElement.value) return;\r\n graph.value.getCellById(editingElement.value.id)?.remove();\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): BPMNElement[] => {\r\n if (!graph.value) return [];\r\n return [\r\n ...graph.value.getNodes().map((node) => ({\r\n id: node.id,\r\n shape: node.shape,\r\n x: node.getPosition().x,\r\n y: node.getPosition().y,\r\n width: node.getSize().width,\r\n height: node.getSize().height,\r\n label: getLabel(node),\r\n data: node.getData() || {},\r\n })),\r\n ...graph.value.getEdges().map((edge) => ({\r\n id: edge.id,\r\n shape: \"bpmn-edge\",\r\n source: edge.getSourceCellId() || \"\",\r\n target: edge.getTargetCellId() || \"\",\r\n label: getLabel(edge),\r\n x: 0,\r\n y: 0,\r\n })),\r\n ] as BPMNElement[];\r\n};\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n if (!props.readonly) {\r\n bindInteractions();\r\n newGraph.on(\"node:dblclick\", ({ node }) => editElement(node));\r\n newGraph.on(\"node:moved\", emitDataChange);\r\n }\r\n\r\n emit(\"ready\", newGraph);\r\n loadData(sampleData);\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (!newData || !graph.value) return;\r\n const normalized = normalizeData(newData);\r\n if (normalized.length > 0) loadData(normalized);\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(async () => {\r\n registerNodes();\r\n const bpmnConfig = getGraphConfig();\r\n await initGraph({\r\n ...bpmnConfig,\r\n connecting: {\r\n ...bpmnConfig.connecting,\r\n createEdge: () => graph.value!.createEdge({ shape: \"bpmn-edge\" }),\r\n validateConnection: ({\r\n sourceView,\r\n targetView,\r\n sourceMagnet,\r\n targetMagnet,\r\n }: any) =>\r\n sourceView !== targetView &&\r\n !!sourceMagnet &&\r\n !!targetMagnet &&\r\n sourceMagnet.getAttribute(\"magnet\") === \"true\" &&\r\n targetMagnet.getAttribute(\"magnet\") === \"true\",\r\n },\r\n });\r\n});\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value,\r\n getData: getCurrentData,\r\n loadData,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"450\"\r\n title=\"编辑类\"\r\n @update:show=\"emit('update:show', $event)\"\r\n >\r\n <div v-if=\"editingClass\" class=\"class-editor\">\r\n <NForm label-placement=\"top\">\r\n <NFormItem label=\"类名\">\r\n <NInput v-model:value=\"editingClass.name\" placeholder=\"请输入类名\" />\r\n </NFormItem>\r\n\r\n <NDivider style=\"margin: 20px 0\">属性</NDivider>\r\n <div class=\"section\">\r\n <div\r\n v-for=\"(attr, index) in editingClass.attributes\"\r\n :key=\"index\"\r\n class=\"item\"\r\n >\r\n <NCard size=\"small\" style=\"margin-bottom: 12px\">\r\n <template #header>\r\n <div class=\"section-header\">\r\n <span style=\"font-size: 13px\"\r\n >#{{ index + 1 }} {{ attr.name || \"新属性\" }}</span\r\n >\r\n <NButton\r\n @click=\"editingClass!.attributes.splice(index, 1)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n style=\"margin-left: auto\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n <NSpace vertical size=\"small\">\r\n <NSpace>\r\n <NFormItem label=\"属性名\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"attr.name\"\r\n placeholder=\"属性名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"类型\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"attr.type\"\r\n placeholder=\"类型\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NSpace>\r\n <NFormItem label=\"可见性\" style=\"margin: 0\">\r\n <NRadioGroup v-model:value=\"attr.visibility\" size=\"small\">\r\n <NRadio value=\"public\">+ public</NRadio>\r\n <NRadio value=\"private\">- private</NRadio>\r\n <NRadio value=\"protected\"># protected</NRadio>\r\n </NRadioGroup>\r\n </NFormItem>\r\n </NSpace>\r\n </NCard>\r\n </div>\r\n <NButton\r\n @click=\"\r\n editingClass!.attributes.push({\r\n name: 'newAttribute',\r\n type: 'string',\r\n visibility: 'private',\r\n })\r\n \"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加属性\r\n </NButton>\r\n </div>\r\n\r\n <NDivider style=\"margin: 20px 0\">方法</NDivider>\r\n <div class=\"section\">\r\n <div\r\n v-for=\"(method, index) in editingClass.methods\"\r\n :key=\"index\"\r\n class=\"item\"\r\n >\r\n <NCard size=\"small\" style=\"margin-bottom: 12px\">\r\n <template #header>\r\n <div class=\"section-header\">\r\n <span style=\"font-size: 13px\"\r\n >#{{ index + 1 }} {{ method.name || \"新方法\" }}</span\r\n >\r\n <NButton\r\n @click=\"editingClass!.methods.splice(index, 1)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n style=\"margin-left: auto\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n <NSpace vertical size=\"small\">\r\n <NSpace>\r\n <NFormItem label=\"方法名\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"method.name\"\r\n placeholder=\"方法名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"返回类型\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"method.returnType\"\r\n placeholder=\"返回类型\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NSpace>\r\n <NFormItem label=\"可见性\" style=\"margin: 0\">\r\n <NRadioGroup v-model:value=\"method.visibility\" size=\"small\">\r\n <NRadio value=\"public\">+ public</NRadio>\r\n <NRadio value=\"private\">- private</NRadio>\r\n <NRadio value=\"protected\"># protected</NRadio>\r\n </NRadioGroup>\r\n </NFormItem>\r\n </NSpace>\r\n </NCard>\r\n </div>\r\n <NButton\r\n @click=\"\r\n editingClass!.methods.push({\r\n name: 'newMethod',\r\n returnType: 'void',\r\n visibility: 'public',\r\n })\r\n \"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加方法\r\n </NButton>\r\n </div>\r\n\r\n <NDivider style=\"margin: 20px 0\"></NDivider>\r\n <NSpace justify=\"space-between\">\r\n <NButton @click=\"emit('delete')\" type=\"error\" ghost>\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n 删除类\r\n </NButton>\r\n <NSpace>\r\n <NButton @click=\"emit('update:show', false)\">取消</NButton>\r\n <NButton @click=\"emit('save')\" type=\"primary\"> 保存 </NButton>\r\n </NSpace>\r\n </NSpace>\r\n </NForm>\r\n </div>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n NDrawer,\r\n NForm,\r\n NFormItem,\r\n NInput,\r\n NCard,\r\n NSpace,\r\n NButton,\r\n NDivider,\r\n NRadioGroup,\r\n NRadio,\r\n} from \"naive-ui\";\r\nimport type { UMLClass } from \"../../types\";\r\nimport C_Icon from \"../../../../C_Icon/index.vue\";\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingClass = defineModel<UMLClass>(\"editingClass\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n delete: [];\r\n}>();\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.class-editor {\r\n padding: 20px;\r\n height: calc(100vh - 60px);\r\n overflow-y: auto;\r\n}\r\n\r\n.section {\r\n max-height: 300px;\r\n overflow-y: auto;\r\n padding-right: 4px;\r\n\r\n &::-webkit-scrollbar {\r\n width: 6px;\r\n }\r\n\r\n &::-webkit-scrollbar-thumb {\r\n background: #d4d4d4;\r\n border-radius: 3px;\r\n }\r\n}\r\n\r\n.section-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n}\r\n\r\n.item {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.section .n-card {\r\n border: 1px solid #e8e8e8;\r\n}\r\n\r\n.section .n-card-header {\r\n padding: 8px 12px;\r\n background: #fafafa;\r\n border-bottom: 1px solid #e8e8e8;\r\n}\r\n\r\n.section .n-card__content {\r\n padding: 12px;\r\n}\r\n\r\n.section .n-form-item {\r\n margin-bottom: 0;\r\n}\r\n\r\n.section .n-form-item-label {\r\n font-size: 12px;\r\n margin-bottom: 4px;\r\n color: #666;\r\n}\r\n</style>\r\n","<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"450\"\r\n title=\"编辑类\"\r\n @update:show=\"emit('update:show', $event)\"\r\n >\r\n <div v-if=\"editingClass\" class=\"class-editor\">\r\n <NForm label-placement=\"top\">\r\n <NFormItem label=\"类名\">\r\n <NInput v-model:value=\"editingClass.name\" placeholder=\"请输入类名\" />\r\n </NFormItem>\r\n\r\n <NDivider style=\"margin: 20px 0\">属性</NDivider>\r\n <div class=\"section\">\r\n <div\r\n v-for=\"(attr, index) in editingClass.attributes\"\r\n :key=\"index\"\r\n class=\"item\"\r\n >\r\n <NCard size=\"small\" style=\"margin-bottom: 12px\">\r\n <template #header>\r\n <div class=\"section-header\">\r\n <span style=\"font-size: 13px\"\r\n >#{{ index + 1 }} {{ attr.name || \"新属性\" }}</span\r\n >\r\n <NButton\r\n @click=\"editingClass!.attributes.splice(index, 1)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n style=\"margin-left: auto\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n <NSpace vertical size=\"small\">\r\n <NSpace>\r\n <NFormItem label=\"属性名\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"attr.name\"\r\n placeholder=\"属性名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"类型\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"attr.type\"\r\n placeholder=\"类型\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NSpace>\r\n <NFormItem label=\"可见性\" style=\"margin: 0\">\r\n <NRadioGroup v-model:value=\"attr.visibility\" size=\"small\">\r\n <NRadio value=\"public\">+ public</NRadio>\r\n <NRadio value=\"private\">- private</NRadio>\r\n <NRadio value=\"protected\"># protected</NRadio>\r\n </NRadioGroup>\r\n </NFormItem>\r\n </NSpace>\r\n </NCard>\r\n </div>\r\n <NButton\r\n @click=\"\r\n editingClass!.attributes.push({\r\n name: 'newAttribute',\r\n type: 'string',\r\n visibility: 'private',\r\n })\r\n \"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加属性\r\n </NButton>\r\n </div>\r\n\r\n <NDivider style=\"margin: 20px 0\">方法</NDivider>\r\n <div class=\"section\">\r\n <div\r\n v-for=\"(method, index) in editingClass.methods\"\r\n :key=\"index\"\r\n class=\"item\"\r\n >\r\n <NCard size=\"small\" style=\"margin-bottom: 12px\">\r\n <template #header>\r\n <div class=\"section-header\">\r\n <span style=\"font-size: 13px\"\r\n >#{{ index + 1 }} {{ method.name || \"新方法\" }}</span\r\n >\r\n <NButton\r\n @click=\"editingClass!.methods.splice(index, 1)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n style=\"margin-left: auto\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n <NSpace vertical size=\"small\">\r\n <NSpace>\r\n <NFormItem label=\"方法名\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"method.name\"\r\n placeholder=\"方法名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"返回类型\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"method.returnType\"\r\n placeholder=\"返回类型\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NSpace>\r\n <NFormItem label=\"可见性\" style=\"margin: 0\">\r\n <NRadioGroup v-model:value=\"method.visibility\" size=\"small\">\r\n <NRadio value=\"public\">+ public</NRadio>\r\n <NRadio value=\"private\">- private</NRadio>\r\n <NRadio value=\"protected\"># protected</NRadio>\r\n </NRadioGroup>\r\n </NFormItem>\r\n </NSpace>\r\n </NCard>\r\n </div>\r\n <NButton\r\n @click=\"\r\n editingClass!.methods.push({\r\n name: 'newMethod',\r\n returnType: 'void',\r\n visibility: 'public',\r\n })\r\n \"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加方法\r\n </NButton>\r\n </div>\r\n\r\n <NDivider style=\"margin: 20px 0\"></NDivider>\r\n <NSpace justify=\"space-between\">\r\n <NButton @click=\"emit('delete')\" type=\"error\" ghost>\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n 删除类\r\n </NButton>\r\n <NSpace>\r\n <NButton @click=\"emit('update:show', false)\">取消</NButton>\r\n <NButton @click=\"emit('save')\" type=\"primary\"> 保存 </NButton>\r\n </NSpace>\r\n </NSpace>\r\n </NForm>\r\n </div>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n NDrawer,\r\n NForm,\r\n NFormItem,\r\n NInput,\r\n NCard,\r\n NSpace,\r\n NButton,\r\n NDivider,\r\n NRadioGroup,\r\n NRadio,\r\n} from \"naive-ui\";\r\nimport type { UMLClass } from \"../../types\";\r\nimport C_Icon from \"../../../../C_Icon/index.vue\";\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingClass = defineModel<UMLClass>(\"editingClass\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n delete: [];\r\n}>();\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.class-editor {\r\n padding: 20px;\r\n height: calc(100vh - 60px);\r\n overflow-y: auto;\r\n}\r\n\r\n.section {\r\n max-height: 300px;\r\n overflow-y: auto;\r\n padding-right: 4px;\r\n\r\n &::-webkit-scrollbar {\r\n width: 6px;\r\n }\r\n\r\n &::-webkit-scrollbar-thumb {\r\n background: #d4d4d4;\r\n border-radius: 3px;\r\n }\r\n}\r\n\r\n.section-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n}\r\n\r\n.item {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.section .n-card {\r\n border: 1px solid #e8e8e8;\r\n}\r\n\r\n.section .n-card-header {\r\n padding: 8px 12px;\r\n background: #fafafa;\r\n border-bottom: 1px solid #e8e8e8;\r\n}\r\n\r\n.section .n-card__content {\r\n padding: 12px;\r\n}\r\n\r\n.section .n-form-item {\r\n margin-bottom: 0;\r\n}\r\n\r\n.section .n-form-item-label {\r\n font-size: 12px;\r\n margin-bottom: 4px;\r\n color: #666;\r\n}\r\n</style>\r\n","<template>\r\n <NDrawer\r\n :show=\"show\"\r\n width=\"450\"\r\n title=\"编辑类\"\r\n @update:show=\"emit('update:show', $event)\"\r\n >\r\n <div v-if=\"editingClass\" class=\"class-editor\">\r\n <NForm label-placement=\"top\">\r\n <NFormItem label=\"类名\">\r\n <NInput v-model:value=\"editingClass.name\" placeholder=\"请输入类名\" />\r\n </NFormItem>\r\n\r\n <NDivider style=\"margin: 20px 0\">属性</NDivider>\r\n <div class=\"section\">\r\n <div\r\n v-for=\"(attr, index) in editingClass.attributes\"\r\n :key=\"index\"\r\n class=\"item\"\r\n >\r\n <NCard size=\"small\" style=\"margin-bottom: 12px\">\r\n <template #header>\r\n <div class=\"section-header\">\r\n <span style=\"font-size: 13px\"\r\n >#{{ index + 1 }} {{ attr.name || \"新属性\" }}</span\r\n >\r\n <NButton\r\n @click=\"editingClass!.attributes.splice(index, 1)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n style=\"margin-left: auto\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n <NSpace vertical size=\"small\">\r\n <NSpace>\r\n <NFormItem label=\"属性名\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"attr.name\"\r\n placeholder=\"属性名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"类型\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"attr.type\"\r\n placeholder=\"类型\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NSpace>\r\n <NFormItem label=\"可见性\" style=\"margin: 0\">\r\n <NRadioGroup v-model:value=\"attr.visibility\" size=\"small\">\r\n <NRadio value=\"public\">+ public</NRadio>\r\n <NRadio value=\"private\">- private</NRadio>\r\n <NRadio value=\"protected\"># protected</NRadio>\r\n </NRadioGroup>\r\n </NFormItem>\r\n </NSpace>\r\n </NCard>\r\n </div>\r\n <NButton\r\n @click=\"\r\n editingClass!.attributes.push({\r\n name: 'newAttribute',\r\n type: 'string',\r\n visibility: 'private',\r\n })\r\n \"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加属性\r\n </NButton>\r\n </div>\r\n\r\n <NDivider style=\"margin: 20px 0\">方法</NDivider>\r\n <div class=\"section\">\r\n <div\r\n v-for=\"(method, index) in editingClass.methods\"\r\n :key=\"index\"\r\n class=\"item\"\r\n >\r\n <NCard size=\"small\" style=\"margin-bottom: 12px\">\r\n <template #header>\r\n <div class=\"section-header\">\r\n <span style=\"font-size: 13px\"\r\n >#{{ index + 1 }} {{ method.name || \"新方法\" }}</span\r\n >\r\n <NButton\r\n @click=\"editingClass!.methods.splice(index, 1)\"\r\n size=\"tiny\"\r\n type=\"error\"\r\n quaternary\r\n style=\"margin-left: auto\"\r\n >\r\n 删除\r\n </NButton>\r\n </div>\r\n </template>\r\n <NSpace vertical size=\"small\">\r\n <NSpace>\r\n <NFormItem label=\"方法名\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"method.name\"\r\n placeholder=\"方法名\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n <NFormItem label=\"返回类型\" style=\"margin: 0; flex: 1\">\r\n <NInput\r\n v-model:value=\"method.returnType\"\r\n placeholder=\"返回类型\"\r\n size=\"small\"\r\n />\r\n </NFormItem>\r\n </NSpace>\r\n <NFormItem label=\"可见性\" style=\"margin: 0\">\r\n <NRadioGroup v-model:value=\"method.visibility\" size=\"small\">\r\n <NRadio value=\"public\">+ public</NRadio>\r\n <NRadio value=\"private\">- private</NRadio>\r\n <NRadio value=\"protected\"># protected</NRadio>\r\n </NRadioGroup>\r\n </NFormItem>\r\n </NSpace>\r\n </NCard>\r\n </div>\r\n <NButton\r\n @click=\"\r\n editingClass!.methods.push({\r\n name: 'newMethod',\r\n returnType: 'void',\r\n visibility: 'public',\r\n })\r\n \"\r\n dashed\r\n block\r\n type=\"primary\"\r\n ghost\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </template>\r\n 添加方法\r\n </NButton>\r\n </div>\r\n\r\n <NDivider style=\"margin: 20px 0\"></NDivider>\r\n <NSpace justify=\"space-between\">\r\n <NButton @click=\"emit('delete')\" type=\"error\" ghost>\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n 删除类\r\n </NButton>\r\n <NSpace>\r\n <NButton @click=\"emit('update:show', false)\">取消</NButton>\r\n <NButton @click=\"emit('save')\" type=\"primary\"> 保存 </NButton>\r\n </NSpace>\r\n </NSpace>\r\n </NForm>\r\n </div>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n NDrawer,\r\n NForm,\r\n NFormItem,\r\n NInput,\r\n NCard,\r\n NSpace,\r\n NButton,\r\n NDivider,\r\n NRadioGroup,\r\n NRadio,\r\n} from \"naive-ui\";\r\nimport type { UMLClass } from \"../../types\";\r\nimport C_Icon from \"../../../../C_Icon/index.vue\";\r\n\r\ndefineProps<{\r\n show: boolean;\r\n}>();\r\n\r\nconst editingClass = defineModel<UMLClass>(\"editingClass\");\r\n\r\nconst emit = defineEmits<{\r\n \"update:show\": [value: boolean];\r\n save: [];\r\n delete: [];\r\n}>();\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.class-editor {\r\n padding: 20px;\r\n height: calc(100vh - 60px);\r\n overflow-y: auto;\r\n}\r\n\r\n.section {\r\n max-height: 300px;\r\n overflow-y: auto;\r\n padding-right: 4px;\r\n\r\n &::-webkit-scrollbar {\r\n width: 6px;\r\n }\r\n\r\n &::-webkit-scrollbar-thumb {\r\n background: #d4d4d4;\r\n border-radius: 3px;\r\n }\r\n}\r\n\r\n.section-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n}\r\n\r\n.item {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.section .n-card {\r\n border: 1px solid #e8e8e8;\r\n}\r\n\r\n.section .n-card-header {\r\n padding: 8px 12px;\r\n background: #fafafa;\r\n border-bottom: 1px solid #e8e8e8;\r\n}\r\n\r\n.section .n-card__content {\r\n padding: 12px;\r\n}\r\n\r\n.section .n-form-item {\r\n margin-bottom: 0;\r\n}\r\n\r\n.section .n-form-item-label {\r\n font-size: 12px;\r\n margin-bottom: 4px;\r\n color: #666;\r\n}\r\n</style>\r\n","import type { UMLAttribute, UMLMethod } from \"../../types\";\r\n\r\nexport const visibilityMap = {\r\n public: \"+\",\r\n private: \"-\",\r\n protected: \"#\",\r\n};\r\n\r\n/* 连线类型配置 */\r\nexport const edgeTypes = {\r\n extends: {\r\n strokeDasharray: 0,\r\n targetMarker: {\r\n name: \"block\",\r\n width: 10,\r\n height: 8,\r\n fill: \"white\",\r\n stroke: \"#722ed1\",\r\n strokeWidth: 2,\r\n },\r\n },\r\n implement: {\r\n strokeDasharray: \"8,4\",\r\n targetMarker: {\r\n name: \"block\",\r\n width: 10,\r\n height: 8,\r\n fill: \"white\",\r\n stroke: \"#722ed1\",\r\n strokeWidth: 2,\r\n },\r\n },\r\n composition: {\r\n strokeDasharray: 0,\r\n sourceMarker: {\r\n name: \"diamond\",\r\n width: 12,\r\n height: 8,\r\n fill: \"#722ed1\",\r\n stroke: \"#722ed1\",\r\n },\r\n },\r\n aggregation: {\r\n strokeDasharray: 0,\r\n sourceMarker: {\r\n name: \"diamond\",\r\n width: 12,\r\n height: 8,\r\n fill: \"white\",\r\n stroke: \"#722ed1\",\r\n strokeWidth: 2,\r\n },\r\n },\r\n association: {\r\n strokeDasharray: 0,\r\n targetMarker: { name: \"classic\", size: 8, fill: \"#722ed1\" },\r\n },\r\n};\r\n\r\n/* 端口配置 */\r\nexport const portConfig = [\"top\", \"right\", \"bottom\", \"left\"].reduce(\r\n (acc, pos) => {\r\n const refConfig =\r\n pos === \"top\"\r\n ? { refX: 0.5, refY: 0 }\r\n : pos === \"right\"\r\n ? { refX: 1, refY: 0.5 }\r\n : pos === \"bottom\"\r\n ? { refX: 0.5, refY: 1 }\r\n : { refX: 0, refY: 0.5 };\r\n acc[`port-${pos}`] = {\r\n ref: \"body\",\r\n ...refConfig,\r\n r: 4,\r\n fill: \"#31d0c6\",\r\n stroke: \"#ffffff\",\r\n strokeWidth: 2,\r\n magnet: true,\r\n style: {\r\n cursor: \"crosshair\",\r\n opacity: 0,\r\n transition: \"opacity 0.2s\",\r\n },\r\n };\r\n return acc;\r\n },\r\n {} as any,\r\n);\r\n\r\n/* UML类节点配置 */\r\nexport const classNodeConfig = {\r\n inherit: \"rect\",\r\n markup: [\r\n { tagName: \"rect\", selector: \"body\" },\r\n { tagName: \"rect\", selector: \"name-rect\" },\r\n { tagName: \"rect\", selector: \"attrs-rect\" },\r\n { tagName: \"rect\", selector: \"methods-rect\" },\r\n { tagName: \"text\", selector: \"name-text\" },\r\n { tagName: \"text\", selector: \"attrs-text\" },\r\n { tagName: \"text\", selector: \"methods-text\" },\r\n { tagName: \"circle\", selector: \"port-top\" },\r\n { tagName: \"circle\", selector: \"port-right\" },\r\n { tagName: \"circle\", selector: \"port-bottom\" },\r\n { tagName: \"circle\", selector: \"port-left\" },\r\n ],\r\n attrs: {\r\n rect: { width: 160 },\r\n body: {\r\n stroke: \"#5f95ff\",\r\n strokeWidth: 1,\r\n fill: \"#fff\",\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n \"name-rect\": {\r\n fill: \"#5f95ff\",\r\n stroke: \"#fff\",\r\n strokeWidth: 0.5,\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n \"attrs-rect\": {\r\n fill: \"#eff4ff\",\r\n stroke: \"#fff\",\r\n strokeWidth: 0.5,\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n \"methods-rect\": {\r\n fill: \"#eff4ff\",\r\n stroke: \"#fff\",\r\n strokeWidth: 0.5,\r\n magnet: false,\r\n style: { cursor: \"move\" },\r\n },\r\n \"name-text\": {\r\n ref: \"name-rect\",\r\n refY: 0.5,\r\n refX: 0.5,\r\n textAnchor: \"middle\",\r\n fontWeight: \"bold\",\r\n fill: \"#fff\",\r\n fontSize: 12,\r\n pointerEvents: \"none\",\r\n },\r\n \"attrs-text\": {\r\n ref: \"attrs-rect\",\r\n refY: 0.5,\r\n refX: 5,\r\n textAnchor: \"left\",\r\n fill: \"black\",\r\n fontSize: 10,\r\n pointerEvents: \"none\",\r\n },\r\n \"methods-text\": {\r\n ref: \"methods-rect\",\r\n refY: 0.5,\r\n refX: 5,\r\n textAnchor: \"left\",\r\n fill: \"black\",\r\n fontSize: 10,\r\n pointerEvents: \"none\",\r\n },\r\n ...portConfig,\r\n },\r\n};\r\n\r\n/* 图形配置 */\r\nexport const graphConfig = {\r\n grid: true,\r\n panning: true,\r\n mousewheel: {\r\n enabled: true,\r\n zoomAtMousePosition: true,\r\n modifiers: \"ctrl\",\r\n minScale: 0.5,\r\n maxScale: 3,\r\n },\r\n};\r\n\r\n/* 连接配置 */\r\nexport const connectingConfig = {\r\n router: \"manhattan\",\r\n connector: { name: \"rounded\", args: { radius: 8 } },\r\n allowBlank: false,\r\n allowLoop: false,\r\n allowNode: false,\r\n snap: true,\r\n allowEdge: false,\r\n};\r\n\r\n/* 高亮配置 */\r\nexport const highlightingConfig = {\r\n magnetAvailable: {\r\n name: \"stroke\",\r\n args: { attrs: { fill: \"#31d0c6\", stroke: \"#31d0c6\", opacity: 1 } },\r\n },\r\n magnetAdsorbed: {\r\n name: \"stroke\",\r\n args: { attrs: { fill: \"#5F95FF\", stroke: \"#5F95FF\", opacity: 1 } },\r\n },\r\n};\r\n\r\n/* 默认边配置 */\r\nexport const defaultEdgeConfig = {\r\n shape: \"extends\",\r\n attrs: {\r\n line: {\r\n stroke: \"#722ed1\",\r\n strokeWidth: 2,\r\n targetMarker: {\r\n name: \"block\",\r\n width: 8,\r\n height: 6,\r\n fill: \"#722ed1\",\r\n },\r\n },\r\n },\r\n};\r\n\r\n/* 示例类数据 */\r\nexport const sampleClasses = [\r\n {\r\n id: \"animal\",\r\n name: \"<<Abstract>>\\n动物\",\r\n x: 342,\r\n y: 98,\r\n attributes: [{ name: \"名字\", type: \"string\", visibility: \"public\" }],\r\n methods: [\r\n { name: \"新陈代谢\", returnType: \"void\", visibility: \"public\" },\r\n { name: \"繁殖\", returnType: \"void\", visibility: \"public\" },\r\n ],\r\n },\r\n {\r\n id: \"bird\",\r\n name: \"鸟\",\r\n x: 575,\r\n y: 280,\r\n attributes: [\r\n { name: \"羽毛\", type: \"string\", visibility: \"public\" },\r\n { name: \"下蛋\", type: \"boolean\", visibility: \"public\" },\r\n ],\r\n methods: [],\r\n },\r\n {\r\n id: \"mammal\",\r\n name: \"哺乳动物\",\r\n x: 844,\r\n y: 319,\r\n attributes: [{ name: \"翅膀\", type: \"boolean\", visibility: \"public\" }],\r\n methods: [],\r\n },\r\n {\r\n id: \"dog\",\r\n name: \"狗\",\r\n x: 656,\r\n y: 456,\r\n attributes: [{ name: \"下蛋\", type: \"boolean\", visibility: \"public\" }],\r\n methods: [],\r\n },\r\n {\r\n id: \"enterprise\",\r\n name: \"企鹅\",\r\n x: 984,\r\n y: 456,\r\n attributes: [{ name: \"下蛋\", type: \"boolean\", visibility: \"public\" }],\r\n methods: [],\r\n },\r\n {\r\n id: \"sparrow\",\r\n name: \"麻雀\",\r\n x: 531,\r\n y: 608,\r\n attributes: [{ name: \"学飞\", type: \"boolean\", visibility: \"public\" }],\r\n methods: [],\r\n },\r\n {\r\n id: \"interface-flying\",\r\n name: \"<<interface>>\\n飞翔\",\r\n x: 762,\r\n y: 608,\r\n attributes: [{ name: \"下蛋\", type: \"boolean\", visibility: \"public\" }],\r\n methods: [],\r\n },\r\n { id: \"qemu\", name: \"气球\", x: 981, y: 608, attributes: [], methods: [] },\r\n];\r\n\r\n/* 示例连接关系 */\r\nexport const sampleConnections = [\r\n { id: \"edge-1\", shape: \"extends\", source: \"bird\", target: \"animal\" },\r\n { id: \"edge-2\", shape: \"extends\", source: \"mammal\", target: \"animal\" },\r\n { id: \"edge-3\", shape: \"extends\", source: \"dog\", target: \"mammal\" },\r\n {\r\n id: \"edge-4\",\r\n shape: \"extends\",\r\n source: \"enterprise\",\r\n target: \"mammal\",\r\n },\r\n { id: \"edge-5\", shape: \"extends\", source: \"sparrow\", target: \"bird\" },\r\n {\r\n id: \"edge-6\",\r\n shape: \"implement\",\r\n source: \"sparrow\",\r\n target: \"interface-flying\",\r\n },\r\n {\r\n id: \"edge-7\",\r\n shape: \"implement\",\r\n source: \"enterprise\",\r\n target: \"interface-flying\",\r\n },\r\n {\r\n id: \"edge-8\",\r\n shape: \"association\",\r\n source: \"bird\",\r\n target: \"mammal\",\r\n label: \"1:2\",\r\n },\r\n];\r\n\r\n/* 工具函数 */\r\nexport const getVisibilitySymbol = (visibility: string) =>\r\n visibilityMap[visibility as keyof typeof visibilityMap] || \"+\";\r\n\r\n/* 新类模板 */\r\nexport const newClassTemplate = {\r\n name: \"新类\",\r\n attributes: [\r\n { name: \"attribute\", type: \"string\", visibility: \"private\" },\r\n ] as UMLAttribute[],\r\n methods: [\r\n { name: \"method\", returnType: \"void\", visibility: \"public\" },\r\n ] as UMLMethod[],\r\n position: { x: 100, y: 100 },\r\n};\r\n","<template>\r\n <div class=\"uml-layout\">\r\n <div class=\"toolbar\" v-if=\"showToolbar\">\r\n <NSpace>\r\n <NButton @click=\"addClass\" type=\"primary\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus-box\" :size=\"16\" />\r\n </template>\r\n 添加类\r\n </NButton>\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </NSpace>\r\n </div>\r\n\r\n <div\r\n ref=\"containerRef\"\r\n class=\"graph-container\"\r\n :style=\"{ width: props.width, height: props.height }\"\r\n ></div>\r\n\r\n <UMLClassEditor\r\n :show=\"showEditor\"\r\n v-model:editing-class=\"editingClass\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveClass\"\r\n @delete=\"deleteClass\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, onMounted } from \"vue\";\r\nimport { NSpace, NButton, NDropdown } from \"naive-ui\";\r\nimport { Graph, ObjectExt, type Node, type Cell } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport { useEdgeInteraction } from \"../../composables/useEdgeInteraction\";\r\nimport type { UMLClass, UMLDiagramData } from \"../../types\";\r\nimport UMLClassEditor from \"./components/UMLClassEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\nimport {\r\n edgeTypes,\r\n classNodeConfig,\r\n connectingConfig,\r\n highlightingConfig,\r\n defaultEdgeConfig,\r\n sampleClasses,\r\n sampleConnections,\r\n getVisibilitySymbol,\r\n newClassTemplate,\r\n} from \"./data\";\r\n\r\ninterface Props {\r\n data?: UMLDiagramData;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n width?: number | string;\r\n height?: number | string;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n width: \"100%\",\r\n height: \"600px\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n ready: [graph: any];\r\n \"data-change\": [data: UMLDiagramData];\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"uml-diagram\");\r\nconst { bindInteractions } = useEdgeInteraction(graph, {\r\n defaultColor: \"#722ed1\",\r\n portPositions: [\"top\", \"right\", \"bottom\", \"left\"],\r\n onDataChange: () => emitDataChange(),\r\n});\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingClass = ref<UMLClass>();\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n Graph.registerNode(\r\n \"class\",\r\n {\r\n ...classNodeConfig,\r\n /** @description 节点属性钩子,用于计算 UML 类图节点布局 */\r\n propHooks(meta: any) {\r\n const { name, attributes, methods, ...others } = meta;\r\n if (!(name && attributes && methods)) return meta;\r\n\r\n let offsetY = 0;\r\n [\r\n { type: \"name\", text: name },\r\n { type: \"attrs\", text: attributes },\r\n { type: \"methods\", text: methods },\r\n ].forEach((rect) => {\r\n const height = Array.isArray(rect.text)\r\n ? rect.text.length * 12 + 16\r\n : 32;\r\n ObjectExt.setByPath(\r\n others,\r\n `attrs/${rect.type}-text/text`,\r\n Array.isArray(rect.text) ? rect.text.join(\"\\n\") : rect.text,\r\n );\r\n ObjectExt.setByPath(others, `attrs/${rect.type}-rect/height`, height);\r\n ObjectExt.setByPath(\r\n others,\r\n `attrs/${rect.type}-rect/transform`,\r\n `translate(0,${offsetY})`,\r\n );\r\n offsetY += height;\r\n });\r\n others.size = { width: 160, height: offsetY };\r\n return others;\r\n },\r\n },\r\n true,\r\n );\r\n\r\n Object.entries(edgeTypes).forEach(([type, config]) => {\r\n Graph.registerEdge(\r\n type,\r\n {\r\n inherit: \"edge\",\r\n attrs: { line: { strokeWidth: 2, stroke: \"#722ed1\", ...config } },\r\n },\r\n true,\r\n );\r\n });\r\n};\r\n\r\n// ==================== 数据加载 ====================\r\nconst loadData = (data: any[]) => {\r\n if (!graph.value || !data.length) return;\r\n const edgeShapes = [\r\n \"extends\",\r\n \"composition\",\r\n \"implement\",\r\n \"aggregation\",\r\n \"association\",\r\n ];\r\n const cells: Cell[] = [];\r\n\r\n data.forEach((item) => {\r\n if (edgeShapes.includes(item.shape)) {\r\n cells.push(graph.value!.createEdge(item));\r\n } else {\r\n const { data: nodeData, ...displayProps } = item;\r\n const node = graph.value!.createNode(displayProps);\r\n if (nodeData) node.setData(nodeData);\r\n cells.push(node);\r\n }\r\n });\r\n\r\n graph.value.resetCells(cells);\r\n setTimeout(() => graph.value!.zoomToFit({ padding: 10, maxScale: 1 }), 200);\r\n};\r\n\r\nconst formatClassDisplay = (cls: {\r\n name: string;\r\n attributes: any[];\r\n methods: any[];\r\n}) => ({\r\n name: cls.name,\r\n attributes: cls.attributes.map(\r\n (attr) =>\r\n `${getVisibilitySymbol(attr.visibility)} ${attr.name}: ${attr.type}`,\r\n ),\r\n methods: cls.methods.map(\r\n (method) =>\r\n `${getVisibilitySymbol(method.visibility)} ${method.name}(): ${method.returnType}`,\r\n ),\r\n});\r\n\r\n// ==================== 类 CRUD ====================\r\nconst addClass = () => {\r\n if (!graph.value) return;\r\n const newClass: UMLClass = {\r\n id: `class_${Date.now()}`,\r\n ...newClassTemplate,\r\n };\r\n const nodeData = {\r\n id: newClass.id,\r\n shape: \"class\",\r\n x: newClass.position.x,\r\n y: newClass.position.y,\r\n ...formatClassDisplay(newClass),\r\n data: newClass,\r\n };\r\n graph.value.addCell(graph.value.createNode(nodeData));\r\n emitDataChange();\r\n};\r\n\r\nconst editClass = (umlClass: UMLClass) => {\r\n editingClass.value = {\r\n ...umlClass,\r\n attributes: [...(umlClass.attributes || [])],\r\n methods: [...(umlClass.methods || [])],\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveClass = () => {\r\n if (!graph.value || !editingClass.value) return;\r\n const node = graph.value.getCellById(editingClass.value.id) as Node;\r\n if (node) {\r\n node.setData(editingClass.value);\r\n node.prop(formatClassDisplay(editingClass.value));\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\nconst deleteClass = () => {\r\n if (!graph.value || !editingClass.value) return;\r\n graph.value.getCellById(editingClass.value.id)?.remove();\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): UMLDiagramData => ({\r\n classes:\r\n graph.value?.getNodes().map((node: Node) => ({\r\n ...node.getData(),\r\n position: node.getPosition(),\r\n })) || [],\r\n relations:\r\n graph.value?.getEdges().map((edge: any) => ({\r\n id: edge.id,\r\n type: edge.shape as string,\r\n source: edge.getSourceCellId() || \"\",\r\n target: edge.getTargetCellId() || \"\",\r\n })) || [],\r\n});\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n registerNodes();\r\n bindInteractions();\r\n\r\n newGraph.on(\"node:dblclick\", ({ node }: { node: Node }) => {\r\n if (!props.readonly && node.getData()) editClass(node.getData());\r\n });\r\n\r\n emit(\"ready\", newGraph);\r\n\r\n const defaultCells = [\r\n ...sampleClasses.map((cls) => ({\r\n id: cls.id,\r\n shape: \"class\",\r\n x: cls.x,\r\n y: cls.y,\r\n ...formatClassDisplay(cls),\r\n data: {\r\n id: cls.id,\r\n name: cls.name,\r\n attributes: cls.attributes,\r\n methods: cls.methods,\r\n position: { x: cls.x, y: cls.y },\r\n },\r\n })),\r\n ...sampleConnections,\r\n ];\r\n loadData(defaultCells);\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (graph.value && newData?.classes) {\r\n const edgeShapes = [\r\n \"extends\",\r\n \"composition\",\r\n \"implement\",\r\n \"aggregation\",\r\n \"association\",\r\n ];\r\n const cells: any[] = [\r\n ...newData.classes.map((cls) => ({\r\n id: cls.id,\r\n shape: \"class\",\r\n x: cls.position?.x ?? 50,\r\n y: cls.position?.y ?? 50,\r\n ...formatClassDisplay(cls),\r\n data: cls,\r\n })),\r\n ...(newData.relations || [])\r\n .filter((r) => edgeShapes.includes(r.type))\r\n .map((r) => ({\r\n shape: r.type,\r\n source: r.source,\r\n target: r.target,\r\n })),\r\n ];\r\n loadData(cells);\r\n }\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n initGraph({\r\n interacting: true,\r\n connecting: {\r\n ...connectingConfig,\r\n createEdge: () => graph.value!.createEdge(defaultEdgeConfig),\r\n validateConnection: ({\r\n sourceView,\r\n targetView,\r\n sourceMagnet,\r\n targetMagnet,\r\n }: any) =>\r\n sourceView !== targetView &&\r\n !!sourceMagnet &&\r\n !!targetMagnet &&\r\n sourceMagnet.getAttribute(\"magnet\") === \"true\" &&\r\n targetMagnet.getAttribute(\"magnet\") === \"true\",\r\n },\r\n highlighting: highlightingConfig,\r\n });\r\n});\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value,\r\n getData: getCurrentData,\r\n addClass,\r\n centerContent,\r\n zoomToFit,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <div class=\"uml-layout\">\r\n <div class=\"toolbar\" v-if=\"showToolbar\">\r\n <NSpace>\r\n <NButton @click=\"addClass\" type=\"primary\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus-box\" :size=\"16\" />\r\n </template>\r\n 添加类\r\n </NButton>\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </NSpace>\r\n </div>\r\n\r\n <div\r\n ref=\"containerRef\"\r\n class=\"graph-container\"\r\n :style=\"{ width: props.width, height: props.height }\"\r\n ></div>\r\n\r\n <UMLClassEditor\r\n :show=\"showEditor\"\r\n v-model:editing-class=\"editingClass\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveClass\"\r\n @delete=\"deleteClass\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, onMounted } from \"vue\";\r\nimport { NSpace, NButton, NDropdown } from \"naive-ui\";\r\nimport { Graph, ObjectExt, type Node, type Cell } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport { useEdgeInteraction } from \"../../composables/useEdgeInteraction\";\r\nimport type { UMLClass, UMLDiagramData } from \"../../types\";\r\nimport UMLClassEditor from \"./components/UMLClassEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\nimport {\r\n edgeTypes,\r\n classNodeConfig,\r\n connectingConfig,\r\n highlightingConfig,\r\n defaultEdgeConfig,\r\n sampleClasses,\r\n sampleConnections,\r\n getVisibilitySymbol,\r\n newClassTemplate,\r\n} from \"./data\";\r\n\r\ninterface Props {\r\n data?: UMLDiagramData;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n width?: number | string;\r\n height?: number | string;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n width: \"100%\",\r\n height: \"600px\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n ready: [graph: any];\r\n \"data-change\": [data: UMLDiagramData];\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"uml-diagram\");\r\nconst { bindInteractions } = useEdgeInteraction(graph, {\r\n defaultColor: \"#722ed1\",\r\n portPositions: [\"top\", \"right\", \"bottom\", \"left\"],\r\n onDataChange: () => emitDataChange(),\r\n});\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingClass = ref<UMLClass>();\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n Graph.registerNode(\r\n \"class\",\r\n {\r\n ...classNodeConfig,\r\n /** @description 节点属性钩子,用于计算 UML 类图节点布局 */\r\n propHooks(meta: any) {\r\n const { name, attributes, methods, ...others } = meta;\r\n if (!(name && attributes && methods)) return meta;\r\n\r\n let offsetY = 0;\r\n [\r\n { type: \"name\", text: name },\r\n { type: \"attrs\", text: attributes },\r\n { type: \"methods\", text: methods },\r\n ].forEach((rect) => {\r\n const height = Array.isArray(rect.text)\r\n ? rect.text.length * 12 + 16\r\n : 32;\r\n ObjectExt.setByPath(\r\n others,\r\n `attrs/${rect.type}-text/text`,\r\n Array.isArray(rect.text) ? rect.text.join(\"\\n\") : rect.text,\r\n );\r\n ObjectExt.setByPath(others, `attrs/${rect.type}-rect/height`, height);\r\n ObjectExt.setByPath(\r\n others,\r\n `attrs/${rect.type}-rect/transform`,\r\n `translate(0,${offsetY})`,\r\n );\r\n offsetY += height;\r\n });\r\n others.size = { width: 160, height: offsetY };\r\n return others;\r\n },\r\n },\r\n true,\r\n );\r\n\r\n Object.entries(edgeTypes).forEach(([type, config]) => {\r\n Graph.registerEdge(\r\n type,\r\n {\r\n inherit: \"edge\",\r\n attrs: { line: { strokeWidth: 2, stroke: \"#722ed1\", ...config } },\r\n },\r\n true,\r\n );\r\n });\r\n};\r\n\r\n// ==================== 数据加载 ====================\r\nconst loadData = (data: any[]) => {\r\n if (!graph.value || !data.length) return;\r\n const edgeShapes = [\r\n \"extends\",\r\n \"composition\",\r\n \"implement\",\r\n \"aggregation\",\r\n \"association\",\r\n ];\r\n const cells: Cell[] = [];\r\n\r\n data.forEach((item) => {\r\n if (edgeShapes.includes(item.shape)) {\r\n cells.push(graph.value!.createEdge(item));\r\n } else {\r\n const { data: nodeData, ...displayProps } = item;\r\n const node = graph.value!.createNode(displayProps);\r\n if (nodeData) node.setData(nodeData);\r\n cells.push(node);\r\n }\r\n });\r\n\r\n graph.value.resetCells(cells);\r\n setTimeout(() => graph.value!.zoomToFit({ padding: 10, maxScale: 1 }), 200);\r\n};\r\n\r\nconst formatClassDisplay = (cls: {\r\n name: string;\r\n attributes: any[];\r\n methods: any[];\r\n}) => ({\r\n name: cls.name,\r\n attributes: cls.attributes.map(\r\n (attr) =>\r\n `${getVisibilitySymbol(attr.visibility)} ${attr.name}: ${attr.type}`,\r\n ),\r\n methods: cls.methods.map(\r\n (method) =>\r\n `${getVisibilitySymbol(method.visibility)} ${method.name}(): ${method.returnType}`,\r\n ),\r\n});\r\n\r\n// ==================== 类 CRUD ====================\r\nconst addClass = () => {\r\n if (!graph.value) return;\r\n const newClass: UMLClass = {\r\n id: `class_${Date.now()}`,\r\n ...newClassTemplate,\r\n };\r\n const nodeData = {\r\n id: newClass.id,\r\n shape: \"class\",\r\n x: newClass.position.x,\r\n y: newClass.position.y,\r\n ...formatClassDisplay(newClass),\r\n data: newClass,\r\n };\r\n graph.value.addCell(graph.value.createNode(nodeData));\r\n emitDataChange();\r\n};\r\n\r\nconst editClass = (umlClass: UMLClass) => {\r\n editingClass.value = {\r\n ...umlClass,\r\n attributes: [...(umlClass.attributes || [])],\r\n methods: [...(umlClass.methods || [])],\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveClass = () => {\r\n if (!graph.value || !editingClass.value) return;\r\n const node = graph.value.getCellById(editingClass.value.id) as Node;\r\n if (node) {\r\n node.setData(editingClass.value);\r\n node.prop(formatClassDisplay(editingClass.value));\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\nconst deleteClass = () => {\r\n if (!graph.value || !editingClass.value) return;\r\n graph.value.getCellById(editingClass.value.id)?.remove();\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): UMLDiagramData => ({\r\n classes:\r\n graph.value?.getNodes().map((node: Node) => ({\r\n ...node.getData(),\r\n position: node.getPosition(),\r\n })) || [],\r\n relations:\r\n graph.value?.getEdges().map((edge: any) => ({\r\n id: edge.id,\r\n type: edge.shape as string,\r\n source: edge.getSourceCellId() || \"\",\r\n target: edge.getTargetCellId() || \"\",\r\n })) || [],\r\n});\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n registerNodes();\r\n bindInteractions();\r\n\r\n newGraph.on(\"node:dblclick\", ({ node }: { node: Node }) => {\r\n if (!props.readonly && node.getData()) editClass(node.getData());\r\n });\r\n\r\n emit(\"ready\", newGraph);\r\n\r\n const defaultCells = [\r\n ...sampleClasses.map((cls) => ({\r\n id: cls.id,\r\n shape: \"class\",\r\n x: cls.x,\r\n y: cls.y,\r\n ...formatClassDisplay(cls),\r\n data: {\r\n id: cls.id,\r\n name: cls.name,\r\n attributes: cls.attributes,\r\n methods: cls.methods,\r\n position: { x: cls.x, y: cls.y },\r\n },\r\n })),\r\n ...sampleConnections,\r\n ];\r\n loadData(defaultCells);\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (graph.value && newData?.classes) {\r\n const edgeShapes = [\r\n \"extends\",\r\n \"composition\",\r\n \"implement\",\r\n \"aggregation\",\r\n \"association\",\r\n ];\r\n const cells: any[] = [\r\n ...newData.classes.map((cls) => ({\r\n id: cls.id,\r\n shape: \"class\",\r\n x: cls.position?.x ?? 50,\r\n y: cls.position?.y ?? 50,\r\n ...formatClassDisplay(cls),\r\n data: cls,\r\n })),\r\n ...(newData.relations || [])\r\n .filter((r) => edgeShapes.includes(r.type))\r\n .map((r) => ({\r\n shape: r.type,\r\n source: r.source,\r\n target: r.target,\r\n })),\r\n ];\r\n loadData(cells);\r\n }\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n initGraph({\r\n interacting: true,\r\n connecting: {\r\n ...connectingConfig,\r\n createEdge: () => graph.value!.createEdge(defaultEdgeConfig),\r\n validateConnection: ({\r\n sourceView,\r\n targetView,\r\n sourceMagnet,\r\n targetMagnet,\r\n }: any) =>\r\n sourceView !== targetView &&\r\n !!sourceMagnet &&\r\n !!targetMagnet &&\r\n sourceMagnet.getAttribute(\"magnet\") === \"true\" &&\r\n targetMagnet.getAttribute(\"magnet\") === \"true\",\r\n },\r\n highlighting: highlightingConfig,\r\n });\r\n});\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value,\r\n getData: getCurrentData,\r\n addClass,\r\n centerContent,\r\n zoomToFit,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<template>\r\n <div class=\"uml-layout\">\r\n <div class=\"toolbar\" v-if=\"showToolbar\">\r\n <NSpace>\r\n <NButton @click=\"addClass\" type=\"primary\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:plus-box\" :size=\"16\" />\r\n </template>\r\n 添加类\r\n </NButton>\r\n <NButton @click=\"centerContent\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:image-filter-center-focus\" :size=\"16\" />\r\n </template>\r\n 居中\r\n </NButton>\r\n <NButton @click=\"zoomToFit\" size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:fit-to-screen\" :size=\"16\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n <NDropdown\r\n :options=\"exportOptions\"\r\n @select=\"(key: string) => handleExport(key, getCurrentData)\"\r\n >\r\n <NButton size=\"small\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:export\" :size=\"16\" />\r\n </template>\r\n 导出\r\n </NButton>\r\n </NDropdown>\r\n </NSpace>\r\n </div>\r\n\r\n <div\r\n ref=\"containerRef\"\r\n class=\"graph-container\"\r\n :style=\"{ width: props.width, height: props.height }\"\r\n ></div>\r\n\r\n <UMLClassEditor\r\n :show=\"showEditor\"\r\n v-model:editing-class=\"editingClass\"\r\n @update:show=\"showEditor = $event\"\r\n @save=\"saveClass\"\r\n @delete=\"deleteClass\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch, onMounted } from \"vue\";\r\nimport { NSpace, NButton, NDropdown } from \"naive-ui\";\r\nimport { Graph, ObjectExt, type Node, type Cell } from \"@antv/x6\";\r\nimport { useGraphBase } from \"../../composables/useGraphBase\";\r\nimport { useGraphExport } from \"../../composables/useGraphExport\";\r\nimport { useEdgeInteraction } from \"../../composables/useEdgeInteraction\";\r\nimport type { UMLClass, UMLDiagramData } from \"../../types\";\r\nimport UMLClassEditor from \"./components/UMLClassEditor.vue\";\r\nimport C_Icon from \"../../../C_Icon/index.vue\";\r\nimport {\r\n edgeTypes,\r\n classNodeConfig,\r\n connectingConfig,\r\n highlightingConfig,\r\n defaultEdgeConfig,\r\n sampleClasses,\r\n sampleConnections,\r\n getVisibilitySymbol,\r\n newClassTemplate,\r\n} from \"./data\";\r\n\r\ninterface Props {\r\n data?: UMLDiagramData;\r\n showToolbar?: boolean;\r\n readonly?: boolean;\r\n width?: number | string;\r\n height?: number | string;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n showToolbar: true,\r\n readonly: false,\r\n width: \"100%\",\r\n height: \"600px\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n ready: [graph: any];\r\n \"data-change\": [data: UMLDiagramData];\r\n}>();\r\n\r\n// ==================== Composables ====================\r\nconst containerRef = ref<HTMLDivElement>();\r\nconst isDark = computed(() => props.theme === \"dark\");\r\nconst { graph, initGraph, centerContent, zoomToFit } = useGraphBase(\r\n containerRef,\r\n isDark,\r\n);\r\nconst { exportOptions, handleExport } = useGraphExport(graph, \"uml-diagram\");\r\nconst { bindInteractions } = useEdgeInteraction(graph, {\r\n defaultColor: \"#722ed1\",\r\n portPositions: [\"top\", \"right\", \"bottom\", \"left\"],\r\n onDataChange: () => emitDataChange(),\r\n});\r\n\r\n// ==================== 编辑器状态 ====================\r\nconst showEditor = ref(false);\r\nconst editingClass = ref<UMLClass>();\r\n\r\n// ==================== 节点注册 ====================\r\nconst registerNodes = () => {\r\n Graph.registerNode(\r\n \"class\",\r\n {\r\n ...classNodeConfig,\r\n /** @description 节点属性钩子,用于计算 UML 类图节点布局 */\r\n propHooks(meta: any) {\r\n const { name, attributes, methods, ...others } = meta;\r\n if (!(name && attributes && methods)) return meta;\r\n\r\n let offsetY = 0;\r\n [\r\n { type: \"name\", text: name },\r\n { type: \"attrs\", text: attributes },\r\n { type: \"methods\", text: methods },\r\n ].forEach((rect) => {\r\n const height = Array.isArray(rect.text)\r\n ? rect.text.length * 12 + 16\r\n : 32;\r\n ObjectExt.setByPath(\r\n others,\r\n `attrs/${rect.type}-text/text`,\r\n Array.isArray(rect.text) ? rect.text.join(\"\\n\") : rect.text,\r\n );\r\n ObjectExt.setByPath(others, `attrs/${rect.type}-rect/height`, height);\r\n ObjectExt.setByPath(\r\n others,\r\n `attrs/${rect.type}-rect/transform`,\r\n `translate(0,${offsetY})`,\r\n );\r\n offsetY += height;\r\n });\r\n others.size = { width: 160, height: offsetY };\r\n return others;\r\n },\r\n },\r\n true,\r\n );\r\n\r\n Object.entries(edgeTypes).forEach(([type, config]) => {\r\n Graph.registerEdge(\r\n type,\r\n {\r\n inherit: \"edge\",\r\n attrs: { line: { strokeWidth: 2, stroke: \"#722ed1\", ...config } },\r\n },\r\n true,\r\n );\r\n });\r\n};\r\n\r\n// ==================== 数据加载 ====================\r\nconst loadData = (data: any[]) => {\r\n if (!graph.value || !data.length) return;\r\n const edgeShapes = [\r\n \"extends\",\r\n \"composition\",\r\n \"implement\",\r\n \"aggregation\",\r\n \"association\",\r\n ];\r\n const cells: Cell[] = [];\r\n\r\n data.forEach((item) => {\r\n if (edgeShapes.includes(item.shape)) {\r\n cells.push(graph.value!.createEdge(item));\r\n } else {\r\n const { data: nodeData, ...displayProps } = item;\r\n const node = graph.value!.createNode(displayProps);\r\n if (nodeData) node.setData(nodeData);\r\n cells.push(node);\r\n }\r\n });\r\n\r\n graph.value.resetCells(cells);\r\n setTimeout(() => graph.value!.zoomToFit({ padding: 10, maxScale: 1 }), 200);\r\n};\r\n\r\nconst formatClassDisplay = (cls: {\r\n name: string;\r\n attributes: any[];\r\n methods: any[];\r\n}) => ({\r\n name: cls.name,\r\n attributes: cls.attributes.map(\r\n (attr) =>\r\n `${getVisibilitySymbol(attr.visibility)} ${attr.name}: ${attr.type}`,\r\n ),\r\n methods: cls.methods.map(\r\n (method) =>\r\n `${getVisibilitySymbol(method.visibility)} ${method.name}(): ${method.returnType}`,\r\n ),\r\n});\r\n\r\n// ==================== 类 CRUD ====================\r\nconst addClass = () => {\r\n if (!graph.value) return;\r\n const newClass: UMLClass = {\r\n id: `class_${Date.now()}`,\r\n ...newClassTemplate,\r\n };\r\n const nodeData = {\r\n id: newClass.id,\r\n shape: \"class\",\r\n x: newClass.position.x,\r\n y: newClass.position.y,\r\n ...formatClassDisplay(newClass),\r\n data: newClass,\r\n };\r\n graph.value.addCell(graph.value.createNode(nodeData));\r\n emitDataChange();\r\n};\r\n\r\nconst editClass = (umlClass: UMLClass) => {\r\n editingClass.value = {\r\n ...umlClass,\r\n attributes: [...(umlClass.attributes || [])],\r\n methods: [...(umlClass.methods || [])],\r\n };\r\n showEditor.value = true;\r\n};\r\n\r\nconst saveClass = () => {\r\n if (!graph.value || !editingClass.value) return;\r\n const node = graph.value.getCellById(editingClass.value.id) as Node;\r\n if (node) {\r\n node.setData(editingClass.value);\r\n node.prop(formatClassDisplay(editingClass.value));\r\n }\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\nconst deleteClass = () => {\r\n if (!graph.value || !editingClass.value) return;\r\n graph.value.getCellById(editingClass.value.id)?.remove();\r\n showEditor.value = false;\r\n emitDataChange();\r\n};\r\n\r\n// ==================== 数据获取 ====================\r\nconst getCurrentData = (): UMLDiagramData => ({\r\n classes:\r\n graph.value?.getNodes().map((node: Node) => ({\r\n ...node.getData(),\r\n position: node.getPosition(),\r\n })) || [],\r\n relations:\r\n graph.value?.getEdges().map((edge: any) => ({\r\n id: edge.id,\r\n type: edge.shape as string,\r\n source: edge.getSourceCellId() || \"\",\r\n target: edge.getTargetCellId() || \"\",\r\n })) || [],\r\n});\r\n\r\nconst emitDataChange = () => emit(\"data-change\", getCurrentData());\r\n\r\n// ==================== Graph 事件绑定 ====================\r\nwatch(\r\n graph,\r\n (newGraph) => {\r\n if (!(newGraph instanceof Graph)) return;\r\n\r\n registerNodes();\r\n bindInteractions();\r\n\r\n newGraph.on(\"node:dblclick\", ({ node }: { node: Node }) => {\r\n if (!props.readonly && node.getData()) editClass(node.getData());\r\n });\r\n\r\n emit(\"ready\", newGraph);\r\n\r\n const defaultCells = [\r\n ...sampleClasses.map((cls) => ({\r\n id: cls.id,\r\n shape: \"class\",\r\n x: cls.x,\r\n y: cls.y,\r\n ...formatClassDisplay(cls),\r\n data: {\r\n id: cls.id,\r\n name: cls.name,\r\n attributes: cls.attributes,\r\n methods: cls.methods,\r\n position: { x: cls.x, y: cls.y },\r\n },\r\n })),\r\n ...sampleConnections,\r\n ];\r\n loadData(defaultCells);\r\n },\r\n { immediate: true },\r\n);\r\n\r\nwatch(\r\n () => props.data,\r\n (newData) => {\r\n if (graph.value && newData?.classes) {\r\n const edgeShapes = [\r\n \"extends\",\r\n \"composition\",\r\n \"implement\",\r\n \"aggregation\",\r\n \"association\",\r\n ];\r\n const cells: any[] = [\r\n ...newData.classes.map((cls) => ({\r\n id: cls.id,\r\n shape: \"class\",\r\n x: cls.position?.x ?? 50,\r\n y: cls.position?.y ?? 50,\r\n ...formatClassDisplay(cls),\r\n data: cls,\r\n })),\r\n ...(newData.relations || [])\r\n .filter((r) => edgeShapes.includes(r.type))\r\n .map((r) => ({\r\n shape: r.type,\r\n source: r.source,\r\n target: r.target,\r\n })),\r\n ];\r\n loadData(cells);\r\n }\r\n },\r\n { deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n initGraph({\r\n interacting: true,\r\n connecting: {\r\n ...connectingConfig,\r\n createEdge: () => graph.value!.createEdge(defaultEdgeConfig),\r\n validateConnection: ({\r\n sourceView,\r\n targetView,\r\n sourceMagnet,\r\n targetMagnet,\r\n }: any) =>\r\n sourceView !== targetView &&\r\n !!sourceMagnet &&\r\n !!targetMagnet &&\r\n sourceMagnet.getAttribute(\"magnet\") === \"true\" &&\r\n targetMagnet.getAttribute(\"magnet\") === \"true\",\r\n },\r\n highlighting: highlightingConfig,\r\n });\r\n});\r\n\r\ndefineExpose({\r\n getGraph: () => graph.value,\r\n getData: getCurrentData,\r\n addClass,\r\n centerContent,\r\n zoomToFit,\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-08-01\r\n * @Description: AntV X6 图形编辑器组件(ER/BPMN/UML)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-antv-container\">\r\n <component\r\n :is=\"currentComponent\"\r\n v-bind=\"componentProps\"\r\n @ready=\"handleReady\"\r\n @data-change=\"handleDataChange\"\r\n ref=\"layoutRef\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport ERLayout from \"./layout/ER/index.vue\";\r\nimport BPMNLayout from \"./layout/BPMN/index.vue\";\r\nimport UMLLayout from \"./layout/UML/index.vue\";\r\nimport type { DiagramConfig, DiagramData } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_AntV\" });\r\n\r\ninterface Props extends DiagramConfig {\r\n type: \"er\" | \"bpmn\" | \"uml\";\r\n data?: DiagramData;\r\n width?: string | number;\r\n height?: string | number;\r\n readonly?: boolean;\r\n showToolbar?: boolean;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n width: \"100%\",\r\n height: \"600px\",\r\n readonly: false,\r\n showToolbar: true,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n (e: \"ready\", graph: any): void;\r\n (e: \"data-change\", data: DiagramData): void;\r\n}>();\r\n\r\nconst layoutRef = ref();\r\n\r\nconst components = {\r\n er: ERLayout,\r\n bpmn: BPMNLayout,\r\n uml: UMLLayout,\r\n} as const;\r\n\r\nconst currentComponent = computed(() => components[props.type]);\r\n\r\nconst componentProps = computed(() => {\r\n const baseProps = {\r\n width: props.width,\r\n height: props.height,\r\n readonly: props.readonly,\r\n showToolbar: props.showToolbar,\r\n theme: props.theme,\r\n };\r\n\r\n // 处理 BPMN 数据格式转换\r\n let adaptedData: any = props.data;\r\n\r\n if (props.type === \"bpmn\" && props.data) {\r\n // 检查是否为 BPMNDiagramData 格式\r\n const bpmnData = props.data as any;\r\n if (\r\n bpmnData &&\r\n typeof bpmnData === \"object\" &&\r\n \"nodes\" in bpmnData &&\r\n \"edges\" in bpmnData\r\n ) {\r\n // 转换为 BPMNLayout 期望的 BPMNElement[] 格式\r\n adaptedData = [\r\n ...(bpmnData.nodes || []).map((node: any) => ({\r\n ...node,\r\n shape: node.type || node.shape || \"rect\",\r\n x: node.x ?? 0,\r\n y: node.y ?? 0,\r\n })),\r\n ...(bpmnData.edges || []).map((edge: any) => ({\r\n ...edge,\r\n shape: \"edge\",\r\n x: 0,\r\n y: 0,\r\n })),\r\n ];\r\n }\r\n }\r\n\r\n return {\r\n ...baseProps,\r\n data: adaptedData,\r\n } as any;\r\n});\r\n\r\nconst handleReady = (graph: any) => {\r\n emit(\"ready\", graph);\r\n};\r\n\r\nconst handleDataChange = (data: any) => {\r\n let convertedData: DiagramData;\r\n\r\n if (props.type === \"bpmn\" && Array.isArray(data)) {\r\n // 将 BPMNElement[] 转换回 BPMNDiagramData 格式,包含必需的 flows 属性\r\n const nodes = data\r\n .filter((item: any) => item.shape !== \"edge\")\r\n .map((item: any) => {\r\n const { shape, x, y, ...node } = item;\r\n return {\r\n ...node,\r\n type: shape === \"rect\" ? \"task\" : shape,\r\n x: x || 0,\r\n y: y || 0,\r\n };\r\n });\r\n\r\n const edges = data\r\n .filter((item: any) => item.shape === \"edge\")\r\n .map((item: any) => {\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const { shape, x, y, ...edge } = item;\r\n return edge;\r\n });\r\n\r\n // 创建完整的 BPMNDiagramData 对象,包含必需的 flows 属性\r\n convertedData = {\r\n nodes,\r\n edges,\r\n flows: [], // 添加必需的 flows 属性,根据实际需求填充\r\n } as unknown as DiagramData; // 使用双重断言确保类型转换\r\n } else {\r\n convertedData = data as DiagramData;\r\n }\r\n\r\n emit(\"data-change\", convertedData);\r\n};\r\n\r\ndefineExpose({\r\n getGraph: () => layoutRef.value?.getGraph?.(),\r\n getData: () => {\r\n const rawData = layoutRef.value?.getData?.();\r\n\r\n // 根据组件类型转换回标准格式\r\n if (props.type === \"bpmn\" && Array.isArray(rawData)) {\r\n const nodes = rawData.filter((item: any) => item.shape !== \"edge\");\r\n const edges = rawData.filter((item: any) => item.shape === \"edge\");\r\n\r\n return {\r\n nodes,\r\n edges,\r\n flows: [], // 确保包含 flows 属性\r\n } as unknown as DiagramData;\r\n }\r\n\r\n return rawData;\r\n },\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.c-antv-container {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-08-01\r\n * @Description: AntV X6 图形编辑器组件(ER/BPMN/UML)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-antv-container\">\r\n <component\r\n :is=\"currentComponent\"\r\n v-bind=\"componentProps\"\r\n @ready=\"handleReady\"\r\n @data-change=\"handleDataChange\"\r\n ref=\"layoutRef\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport ERLayout from \"./layout/ER/index.vue\";\r\nimport BPMNLayout from \"./layout/BPMN/index.vue\";\r\nimport UMLLayout from \"./layout/UML/index.vue\";\r\nimport type { DiagramConfig, DiagramData } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_AntV\" });\r\n\r\ninterface Props extends DiagramConfig {\r\n type: \"er\" | \"bpmn\" | \"uml\";\r\n data?: DiagramData;\r\n width?: string | number;\r\n height?: string | number;\r\n readonly?: boolean;\r\n showToolbar?: boolean;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n width: \"100%\",\r\n height: \"600px\",\r\n readonly: false,\r\n showToolbar: true,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n (e: \"ready\", graph: any): void;\r\n (e: \"data-change\", data: DiagramData): void;\r\n}>();\r\n\r\nconst layoutRef = ref();\r\n\r\nconst components = {\r\n er: ERLayout,\r\n bpmn: BPMNLayout,\r\n uml: UMLLayout,\r\n} as const;\r\n\r\nconst currentComponent = computed(() => components[props.type]);\r\n\r\nconst componentProps = computed(() => {\r\n const baseProps = {\r\n width: props.width,\r\n height: props.height,\r\n readonly: props.readonly,\r\n showToolbar: props.showToolbar,\r\n theme: props.theme,\r\n };\r\n\r\n // 处理 BPMN 数据格式转换\r\n let adaptedData: any = props.data;\r\n\r\n if (props.type === \"bpmn\" && props.data) {\r\n // 检查是否为 BPMNDiagramData 格式\r\n const bpmnData = props.data as any;\r\n if (\r\n bpmnData &&\r\n typeof bpmnData === \"object\" &&\r\n \"nodes\" in bpmnData &&\r\n \"edges\" in bpmnData\r\n ) {\r\n // 转换为 BPMNLayout 期望的 BPMNElement[] 格式\r\n adaptedData = [\r\n ...(bpmnData.nodes || []).map((node: any) => ({\r\n ...node,\r\n shape: node.type || node.shape || \"rect\",\r\n x: node.x ?? 0,\r\n y: node.y ?? 0,\r\n })),\r\n ...(bpmnData.edges || []).map((edge: any) => ({\r\n ...edge,\r\n shape: \"edge\",\r\n x: 0,\r\n y: 0,\r\n })),\r\n ];\r\n }\r\n }\r\n\r\n return {\r\n ...baseProps,\r\n data: adaptedData,\r\n } as any;\r\n});\r\n\r\nconst handleReady = (graph: any) => {\r\n emit(\"ready\", graph);\r\n};\r\n\r\nconst handleDataChange = (data: any) => {\r\n let convertedData: DiagramData;\r\n\r\n if (props.type === \"bpmn\" && Array.isArray(data)) {\r\n // 将 BPMNElement[] 转换回 BPMNDiagramData 格式,包含必需的 flows 属性\r\n const nodes = data\r\n .filter((item: any) => item.shape !== \"edge\")\r\n .map((item: any) => {\r\n const { shape, x, y, ...node } = item;\r\n return {\r\n ...node,\r\n type: shape === \"rect\" ? \"task\" : shape,\r\n x: x || 0,\r\n y: y || 0,\r\n };\r\n });\r\n\r\n const edges = data\r\n .filter((item: any) => item.shape === \"edge\")\r\n .map((item: any) => {\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const { shape, x, y, ...edge } = item;\r\n return edge;\r\n });\r\n\r\n // 创建完整的 BPMNDiagramData 对象,包含必需的 flows 属性\r\n convertedData = {\r\n nodes,\r\n edges,\r\n flows: [], // 添加必需的 flows 属性,根据实际需求填充\r\n } as unknown as DiagramData; // 使用双重断言确保类型转换\r\n } else {\r\n convertedData = data as DiagramData;\r\n }\r\n\r\n emit(\"data-change\", convertedData);\r\n};\r\n\r\ndefineExpose({\r\n getGraph: () => layoutRef.value?.getGraph?.(),\r\n getData: () => {\r\n const rawData = layoutRef.value?.getData?.();\r\n\r\n // 根据组件类型转换回标准格式\r\n if (props.type === \"bpmn\" && Array.isArray(rawData)) {\r\n const nodes = rawData.filter((item: any) => item.shape !== \"edge\");\r\n const edges = rawData.filter((item: any) => item.shape === \"edge\");\r\n\r\n return {\r\n nodes,\r\n edges,\r\n flows: [], // 确保包含 flows 属性\r\n } as unknown as DiagramData;\r\n }\r\n\r\n return rawData;\r\n },\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.c-antv-container {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-08-01\r\n * @Description: AntV X6 图形编辑器组件(ER/BPMN/UML)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-antv-container\">\r\n <component\r\n :is=\"currentComponent\"\r\n v-bind=\"componentProps\"\r\n @ready=\"handleReady\"\r\n @data-change=\"handleDataChange\"\r\n ref=\"layoutRef\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport ERLayout from \"./layout/ER/index.vue\";\r\nimport BPMNLayout from \"./layout/BPMN/index.vue\";\r\nimport UMLLayout from \"./layout/UML/index.vue\";\r\nimport type { DiagramConfig, DiagramData } from \"./types\";\r\n\r\ndefineOptions({ name: \"C_AntV\" });\r\n\r\ninterface Props extends DiagramConfig {\r\n type: \"er\" | \"bpmn\" | \"uml\";\r\n data?: DiagramData;\r\n width?: string | number;\r\n height?: string | number;\r\n readonly?: boolean;\r\n showToolbar?: boolean;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n width: \"100%\",\r\n height: \"600px\",\r\n readonly: false,\r\n showToolbar: true,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n (e: \"ready\", graph: any): void;\r\n (e: \"data-change\", data: DiagramData): void;\r\n}>();\r\n\r\nconst layoutRef = ref();\r\n\r\nconst components = {\r\n er: ERLayout,\r\n bpmn: BPMNLayout,\r\n uml: UMLLayout,\r\n} as const;\r\n\r\nconst currentComponent = computed(() => components[props.type]);\r\n\r\nconst componentProps = computed(() => {\r\n const baseProps = {\r\n width: props.width,\r\n height: props.height,\r\n readonly: props.readonly,\r\n showToolbar: props.showToolbar,\r\n theme: props.theme,\r\n };\r\n\r\n // 处理 BPMN 数据格式转换\r\n let adaptedData: any = props.data;\r\n\r\n if (props.type === \"bpmn\" && props.data) {\r\n // 检查是否为 BPMNDiagramData 格式\r\n const bpmnData = props.data as any;\r\n if (\r\n bpmnData &&\r\n typeof bpmnData === \"object\" &&\r\n \"nodes\" in bpmnData &&\r\n \"edges\" in bpmnData\r\n ) {\r\n // 转换为 BPMNLayout 期望的 BPMNElement[] 格式\r\n adaptedData = [\r\n ...(bpmnData.nodes || []).map((node: any) => ({\r\n ...node,\r\n shape: node.type || node.shape || \"rect\",\r\n x: node.x ?? 0,\r\n y: node.y ?? 0,\r\n })),\r\n ...(bpmnData.edges || []).map((edge: any) => ({\r\n ...edge,\r\n shape: \"edge\",\r\n x: 0,\r\n y: 0,\r\n })),\r\n ];\r\n }\r\n }\r\n\r\n return {\r\n ...baseProps,\r\n data: adaptedData,\r\n } as any;\r\n});\r\n\r\nconst handleReady = (graph: any) => {\r\n emit(\"ready\", graph);\r\n};\r\n\r\nconst handleDataChange = (data: any) => {\r\n let convertedData: DiagramData;\r\n\r\n if (props.type === \"bpmn\" && Array.isArray(data)) {\r\n // 将 BPMNElement[] 转换回 BPMNDiagramData 格式,包含必需的 flows 属性\r\n const nodes = data\r\n .filter((item: any) => item.shape !== \"edge\")\r\n .map((item: any) => {\r\n const { shape, x, y, ...node } = item;\r\n return {\r\n ...node,\r\n type: shape === \"rect\" ? \"task\" : shape,\r\n x: x || 0,\r\n y: y || 0,\r\n };\r\n });\r\n\r\n const edges = data\r\n .filter((item: any) => item.shape === \"edge\")\r\n .map((item: any) => {\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const { shape, x, y, ...edge } = item;\r\n return edge;\r\n });\r\n\r\n // 创建完整的 BPMNDiagramData 对象,包含必需的 flows 属性\r\n convertedData = {\r\n nodes,\r\n edges,\r\n flows: [], // 添加必需的 flows 属性,根据实际需求填充\r\n } as unknown as DiagramData; // 使用双重断言确保类型转换\r\n } else {\r\n convertedData = data as DiagramData;\r\n }\r\n\r\n emit(\"data-change\", convertedData);\r\n};\r\n\r\ndefineExpose({\r\n getGraph: () => layoutRef.value?.getGraph?.(),\r\n getData: () => {\r\n const rawData = layoutRef.value?.getData?.();\r\n\r\n // 根据组件类型转换回标准格式\r\n if (props.type === \"bpmn\" && Array.isArray(rawData)) {\r\n const nodes = rawData.filter((item: any) => item.shape !== \"edge\");\r\n const edges = rawData.filter((item: any) => item.shape === \"edge\");\r\n\r\n return {\r\n nodes,\r\n edges,\r\n flows: [], // 确保包含 flows 属性\r\n } as unknown as DiagramData;\r\n }\r\n\r\n return rawData;\r\n },\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.c-antv-container {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n</style>\r\n"],"mappings":"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"}