@robot-admin/naive-ui-components 0.10.3 → 0.11.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 (658) hide show
  1. package/CHANGELOG.md +111 -100
  2. package/LICENSE +21 -21
  3. package/README.md +463 -318
  4. package/README_EN.md +404 -0
  5. package/SECURITY.md +23 -0
  6. package/dist/C_ActionBar.cjs +1 -1
  7. package/dist/C_ActionBar.css +46 -48
  8. package/dist/C_ActionBar.d.cts +1 -1
  9. package/dist/C_ActionBar.js +1 -1
  10. package/dist/C_ActionBar2.cjs +1 -0
  11. package/dist/C_ActionBar2.js +1 -2
  12. package/dist/C_AntV.cjs +1 -1
  13. package/dist/C_AntV.css +3 -3
  14. package/dist/C_AntV.d.cts +1 -1
  15. package/dist/C_AntV.js +1 -1
  16. package/dist/C_AntV2.cjs +4 -0
  17. package/dist/C_AntV2.js +3 -4
  18. package/dist/C_AudioPlayer.cjs +1 -1
  19. package/dist/C_AudioPlayer.css +15 -7
  20. package/dist/C_AudioPlayer.d.cts +1 -1
  21. package/dist/C_AudioPlayer.js +1 -1
  22. package/dist/C_AudioPlayer2.cjs +1 -0
  23. package/dist/C_AudioPlayer2.js +1 -2
  24. package/dist/C_AvatarGroup.cjs +1 -1
  25. package/dist/C_AvatarGroup.css +15 -7
  26. package/dist/C_AvatarGroup.d.cts +1 -1
  27. package/dist/C_AvatarGroup2.cjs +1 -0
  28. package/dist/C_AvatarGroup2.js +1 -2
  29. package/dist/C_Barcode.cjs +1 -1
  30. package/dist/C_Barcode.css +0 -2
  31. package/dist/C_Barcode.d.cts +1 -1
  32. package/dist/C_Barcode2.cjs +1 -0
  33. package/dist/C_Barcode2.js +1 -2
  34. package/dist/C_Breadcrumb.cjs +1 -1
  35. package/dist/C_Breadcrumb.css +0 -2
  36. package/dist/C_Breadcrumb.d.cts +2 -2
  37. package/dist/C_Breadcrumb.d.ts +1 -2
  38. package/dist/C_Breadcrumb.js +1 -1
  39. package/dist/C_Breadcrumb2.cjs +1 -0
  40. package/dist/C_Breadcrumb2.js +1 -2
  41. package/dist/C_Captcha.cjs +1 -1
  42. package/dist/C_Captcha.css +46 -21
  43. package/dist/C_Captcha.d.cts +3 -2
  44. package/dist/C_Captcha.d.ts +3 -2
  45. package/dist/C_Captcha.js +1 -1
  46. package/dist/C_Captcha2.cjs +1 -0
  47. package/dist/C_Captcha2.js +1 -2
  48. package/dist/C_Cascade.cjs +1 -1
  49. package/dist/C_Cascade.css +0 -2
  50. package/dist/C_Cascade.d.cts +1 -1
  51. package/dist/C_Cascade.d.ts +1 -1
  52. package/dist/C_Cascade2.cjs +1 -0
  53. package/dist/C_Cascade2.js +1 -2
  54. package/dist/C_Chat.cjs +1 -1
  55. package/dist/C_Chat.css +0 -2
  56. package/dist/C_Chat.d.cts +1 -1
  57. package/dist/C_Chat.d.ts +1 -1
  58. package/dist/C_Chat.js +1 -1
  59. package/dist/C_Chat2.cjs +1 -0
  60. package/dist/C_Chat2.js +1 -2
  61. package/dist/C_City.cjs +1 -1
  62. package/dist/C_City.css +28 -30
  63. package/dist/C_City.d.cts +1 -1
  64. package/dist/C_City.d.ts +1 -1
  65. package/dist/C_City2.cjs +1 -0
  66. package/dist/C_City2.js +1 -2
  67. package/dist/C_Code.cjs +1 -1
  68. package/dist/C_Code.css +241 -4
  69. package/dist/C_Code.d.cts +1 -1
  70. package/dist/C_Code.d.ts +1 -1
  71. package/dist/C_Code.js +1 -1
  72. package/dist/C_Code2.cjs +1 -0
  73. package/dist/C_Code2.js +1 -2
  74. package/dist/C_CollapsePanel.cjs +1 -1
  75. package/dist/C_CollapsePanel.css +12 -6
  76. package/dist/C_CollapsePanel.d.cts +1 -1
  77. package/dist/C_CollapsePanel.js +1 -1
  78. package/dist/C_CollapsePanel2.cjs +1 -0
  79. package/dist/C_CollapsePanel2.js +1 -2
  80. package/dist/C_ContextMenu.cjs +1 -1
  81. package/dist/C_ContextMenu.css +16 -18
  82. package/dist/C_ContextMenu.d.cts +1 -1
  83. package/dist/C_ContextMenu.d.ts +1 -1
  84. package/dist/C_ContextMenu.js +1 -1
  85. package/dist/C_ContextMenu2.cjs +1 -0
  86. package/dist/C_ContextMenu2.js +1 -2
  87. package/dist/C_Cron.cjs +1 -1
  88. package/dist/C_Cron.css +18 -8
  89. package/dist/C_Cron.d.cts +1 -1
  90. package/dist/C_Cron.js +1 -1
  91. package/dist/C_Cron2.cjs +1 -0
  92. package/dist/C_Cron2.js +1 -2
  93. package/dist/C_Date.cjs +1 -1
  94. package/dist/C_Date.css +1 -0
  95. package/dist/C_Date.d.cts +2 -2
  96. package/dist/C_Date.d.ts +2 -2
  97. package/dist/C_Date2.cjs +1 -0
  98. package/dist/C_Date2.js +1 -2
  99. package/dist/C_Draggable.cjs +1 -1
  100. package/dist/C_Draggable.css +9 -5
  101. package/dist/C_Draggable.d.cts +1 -1
  102. package/dist/C_Draggable2.cjs +1 -0
  103. package/dist/C_Draggable2.js +1 -2
  104. package/dist/C_Editor.cjs +1 -1
  105. package/dist/C_Editor.css +80 -67
  106. package/dist/C_Editor.d.cts +1 -1
  107. package/dist/C_Editor.d.ts +1 -1
  108. package/dist/C_Editor2.cjs +1 -0
  109. package/dist/C_Editor2.js +1 -2
  110. package/dist/C_FilePreview.cjs +1 -1
  111. package/dist/C_FilePreview.css +0 -2
  112. package/dist/C_FilePreview.d.cts +1 -1
  113. package/dist/C_FilePreview.js +1 -1
  114. package/dist/C_FilePreview2.cjs +1 -0
  115. package/dist/C_FilePreview2.js +1 -2
  116. package/dist/C_Form.base.css +980 -0
  117. package/dist/C_Form.cjs +1 -1
  118. package/dist/C_Form.css +256 -155
  119. package/dist/C_Form.d.cts +2 -2
  120. package/dist/C_Form.d.ts +2 -2
  121. package/dist/C_Form.js +1 -1
  122. package/dist/C_Form2.cjs +1 -0
  123. package/dist/C_Form2.js +1 -2
  124. package/dist/C_FormSearch.cjs +1 -1
  125. package/dist/C_FormSearch.css +47 -47
  126. package/dist/C_FormSearch.d.cts +2 -2
  127. package/dist/C_FormSearch.d.ts +2 -2
  128. package/dist/C_FormSearch2.cjs +1 -0
  129. package/dist/C_FormSearch2.js +1 -2
  130. package/dist/C_FormulaEditor.cjs +1 -1
  131. package/dist/C_FormulaEditor.css +64 -38
  132. package/dist/C_FormulaEditor.d.cts +1 -1
  133. package/dist/C_FormulaEditor.js +1 -1
  134. package/dist/C_FormulaEditor2.cjs +2 -0
  135. package/dist/C_FormulaEditor2.js +2 -2
  136. package/dist/C_FullCalendar.cjs +1 -1
  137. package/dist/C_FullCalendar.css +0 -2
  138. package/dist/C_FullCalendar.d.cts +1 -1
  139. package/dist/C_FullCalendar2.cjs +1 -0
  140. package/dist/C_FullCalendar2.js +1 -2
  141. package/dist/C_GlobalSearch.cjs +1 -1
  142. package/dist/C_GlobalSearch.css +111 -113
  143. package/dist/C_GlobalSearch.d.cts +1 -1
  144. package/dist/C_GlobalSearch2.cjs +1 -0
  145. package/dist/C_GlobalSearch2.js +1 -2
  146. package/dist/C_Guide.cjs +1 -1
  147. package/dist/C_Guide.css +1 -0
  148. package/dist/C_Guide.d.cts +1 -1
  149. package/dist/C_Guide.d.ts +1 -1
  150. package/dist/C_Guide.js +1 -1
  151. package/dist/C_Guide2.cjs +1 -0
  152. package/dist/C_Guide2.js +1 -2
  153. package/dist/C_Icon.cjs +1 -1
  154. package/dist/C_Icon.css +58 -0
  155. package/dist/C_Icon.d.cts +1 -1
  156. package/dist/C_Icon.d.ts +1 -1
  157. package/dist/C_Icon2.cjs +1 -0
  158. package/dist/C_Icon2.js +1 -2
  159. package/dist/C_ImageCropper.cjs +1 -1
  160. package/dist/C_ImageCropper.css +0 -2
  161. package/dist/C_ImageCropper.d.cts +1 -1
  162. package/dist/C_ImageCropper.js +1 -1
  163. package/dist/C_ImageCropper2.cjs +1 -0
  164. package/dist/C_ImageCropper2.js +1 -2
  165. package/dist/C_Language.cjs +1 -1
  166. package/dist/C_Language.css +1 -0
  167. package/dist/C_Language.d.cts +1 -1
  168. package/dist/C_Language.d.ts +1 -1
  169. package/dist/C_Language.js +1 -1
  170. package/dist/C_Language2.cjs +1 -0
  171. package/dist/C_Language2.js +1 -2
  172. package/dist/C_Login.cjs +1 -1
  173. package/dist/C_Login.css +92 -76
  174. package/dist/C_Login.d.cts +1 -1
  175. package/dist/C_Login.d.ts +1 -1
  176. package/dist/C_Login.js +1 -1
  177. package/dist/C_Login2.cjs +1 -0
  178. package/dist/C_Login2.js +1 -2
  179. package/dist/C_Map.cjs +1 -1
  180. package/dist/C_Map.css +665 -5
  181. package/dist/C_Map.d.cts +1 -1
  182. package/dist/C_Map2.cjs +1 -0
  183. package/dist/C_Map2.js +1 -2
  184. package/dist/C_Markdown.cjs +1 -1
  185. package/dist/C_Markdown.css +940 -35
  186. package/dist/C_Markdown.d.cts +1 -1
  187. package/dist/C_Markdown.d.ts +1 -1
  188. package/dist/C_Markdown2.cjs +1 -0
  189. package/dist/C_Markdown2.js +1 -2
  190. package/dist/C_Menu.cjs +1 -1
  191. package/dist/C_Menu.css +1 -0
  192. package/dist/C_Menu.d.cts +3 -3
  193. package/dist/C_Menu.d.ts +3 -4
  194. package/dist/C_Menu.js +1 -1
  195. package/dist/C_Menu2.cjs +1 -0
  196. package/dist/C_Menu2.js +1 -2
  197. package/dist/C_NotificationCenter.cjs +1 -1
  198. package/dist/C_NotificationCenter.css +95 -47
  199. package/dist/C_NotificationCenter.d.cts +1 -1
  200. package/dist/C_NotificationCenter.js +1 -1
  201. package/dist/C_NotificationCenter2.cjs +1 -0
  202. package/dist/C_NotificationCenter2.js +1 -2
  203. package/dist/C_OrgChart.cjs +1 -1
  204. package/dist/C_OrgChart.css +6 -4
  205. package/dist/C_OrgChart.d.cts +1 -1
  206. package/dist/C_OrgChart.d.ts +1 -1
  207. package/dist/C_OrgChart2.cjs +1 -0
  208. package/dist/C_OrgChart2.js +1 -2
  209. package/dist/C_Progress.cjs +1 -1
  210. package/dist/C_Progress.css +1 -0
  211. package/dist/C_Progress.d.cts +1 -1
  212. package/dist/C_Progress.d.ts +1 -1
  213. package/dist/C_Progress2.cjs +1 -0
  214. package/dist/C_Progress2.js +1 -2
  215. package/dist/C_QRCode.cjs +1 -1
  216. package/dist/C_QRCode.css +8 -10
  217. package/dist/C_QRCode.d.cts +1 -1
  218. package/dist/C_QRCode2.cjs +1 -0
  219. package/dist/C_QRCode2.js +1 -2
  220. package/dist/C_Signature.cjs +1 -1
  221. package/dist/C_Signature.css +0 -2
  222. package/dist/C_Signature.d.cts +1 -1
  223. package/dist/C_Signature.js +1 -1
  224. package/dist/C_Signature2.cjs +1 -0
  225. package/dist/C_Signature2.js +1 -2
  226. package/dist/C_Skeleton.cjs +1 -1
  227. package/dist/C_Skeleton.css +0 -2
  228. package/dist/C_Skeleton.d.cts +1 -1
  229. package/dist/C_Skeleton.d.ts +1 -1
  230. package/dist/C_Skeleton2.cjs +1 -0
  231. package/dist/C_Skeleton2.js +1 -2
  232. package/dist/C_SplitPane.cjs +1 -1
  233. package/dist/C_SplitPane.css +27 -11
  234. package/dist/C_SplitPane.d.cts +1 -1
  235. package/dist/C_SplitPane.js +1 -1
  236. package/dist/C_SplitPane2.cjs +1 -0
  237. package/dist/C_SplitPane2.js +1 -2
  238. package/dist/C_Steps.cjs +1 -1
  239. package/dist/C_Steps.css +37 -39
  240. package/dist/C_Steps.d.cts +1 -1
  241. package/dist/C_Steps.d.ts +1 -1
  242. package/dist/C_Steps2.cjs +1 -0
  243. package/dist/C_Steps2.js +1 -2
  244. package/dist/C_Table.base.css +1588 -0
  245. package/dist/C_Table.cjs +1 -1
  246. package/dist/C_Table.css +1202 -112
  247. package/dist/C_Table.d.cts +2 -2
  248. package/dist/C_Table.d.ts +11 -2
  249. package/dist/C_Table.js +1 -1
  250. package/dist/C_TagsView.cjs +1 -1
  251. package/dist/C_TagsView.css +44 -40
  252. package/dist/C_TagsView.d.cts +2 -2
  253. package/dist/C_TagsView.d.ts +2 -3
  254. package/dist/C_TagsView.js +1 -1
  255. package/dist/C_TagsView2.cjs +1 -0
  256. package/dist/C_TagsView2.js +1 -2
  257. package/dist/C_Theme.cjs +1 -1
  258. package/dist/C_Theme.css +1 -0
  259. package/dist/C_Theme.d.cts +1 -1
  260. package/dist/C_Theme.d.ts +1 -1
  261. package/dist/C_Theme.js +1 -1
  262. package/dist/C_Theme2.cjs +1 -0
  263. package/dist/C_Theme2.js +1 -2
  264. package/dist/C_Time.cjs +1 -1
  265. package/dist/C_Time.css +0 -2
  266. package/dist/C_Time.d.cts +2 -2
  267. package/dist/C_Time.d.ts +2 -2
  268. package/dist/C_Time2.cjs +1 -0
  269. package/dist/C_Time2.js +1 -2
  270. package/dist/C_Timeline.cjs +1 -1
  271. package/dist/C_Timeline.css +44 -46
  272. package/dist/C_Timeline.d.cts +1 -1
  273. package/dist/C_Timeline.js +1 -1
  274. package/dist/C_Timeline2.cjs +1 -0
  275. package/dist/C_Timeline2.js +1 -2
  276. package/dist/C_Transfer.cjs +1 -1
  277. package/dist/C_Transfer.css +21 -9
  278. package/dist/C_Transfer.d.cts +1 -1
  279. package/dist/C_Transfer.js +1 -1
  280. package/dist/C_Transfer2.cjs +1 -0
  281. package/dist/C_Transfer2.js +1 -2
  282. package/dist/C_Tree.cjs +1 -1
  283. package/dist/C_Tree.css +0 -2
  284. package/dist/C_Tree.d.cts +1 -1
  285. package/dist/C_Tree.js +1 -1
  286. package/dist/C_Tree2.cjs +1 -0
  287. package/dist/C_Tree2.js +1 -2
  288. package/dist/C_Upload.cjs +1 -1
  289. package/dist/C_Upload.css +17 -17
  290. package/dist/C_Upload.d.cts +1 -1
  291. package/dist/C_Upload.js +1 -1
  292. package/dist/C_Upload2.cjs +1 -0
  293. package/dist/C_Upload2.js +1 -25
  294. package/dist/C_VideoPlayer.cjs +1 -1
  295. package/dist/C_VideoPlayer.css +35 -24
  296. package/dist/C_VideoPlayer.d.cts +2 -2
  297. package/dist/C_VideoPlayer.d.ts +2 -2
  298. package/dist/C_VideoPlayer.js +1 -1
  299. package/dist/C_VideoPlayer2.cjs +3 -0
  300. package/dist/C_VideoPlayer2.js +3 -4
  301. package/dist/C_VtableGantt.cjs +1 -1
  302. package/dist/C_VtableGantt.css +15 -7
  303. package/dist/C_VtableGantt.d.cts +1 -1
  304. package/dist/C_VtableGantt.d.ts +1 -1
  305. package/dist/C_VtableGantt.js +1 -1
  306. package/dist/C_VtableGantt2.cjs +1 -0
  307. package/dist/C_VtableGantt2.js +1 -2
  308. package/dist/C_WaterFall.cjs +1 -1
  309. package/dist/C_WaterFall.css +15 -17
  310. package/dist/C_WaterFall.d.cts +1 -1
  311. package/dist/C_WaterFall2.cjs +1 -0
  312. package/dist/C_WaterFall2.js +1 -2
  313. package/dist/C_WorkFlow.cjs +1 -1
  314. package/dist/C_WorkFlow.css +257 -197
  315. package/dist/C_WorkFlow.d.cts +1 -1
  316. package/dist/C_WorkFlow.js +1 -1
  317. package/dist/C_WorkFlow2.cjs +1 -0
  318. package/dist/C_WorkFlow2.js +1 -2
  319. package/dist/city.cjs +1 -0
  320. package/dist/city.js +1 -2
  321. package/dist/config.cjs +1 -0
  322. package/dist/config.d.cts +33 -0
  323. package/dist/config.d.ts +33 -0
  324. package/dist/config.js +1 -0
  325. package/dist/constants.d.cts +272 -0
  326. package/dist/constants.d.ts +6 -7
  327. package/dist/constants2.d.cts +177 -0
  328. package/dist/constants2.d.ts +5 -6
  329. package/dist/constants3.d.cts +479 -0
  330. package/dist/constants3.d.ts +17 -13
  331. package/dist/constants4.d.cts +420 -0
  332. package/dist/constants4.d.ts +30 -40
  333. package/dist/constants5.d.cts +491 -0
  334. package/dist/constants5.d.ts +22 -6
  335. package/dist/data.cjs +1 -0
  336. package/dist/data.d.cts +67 -0
  337. package/dist/data.d.ts +13 -13
  338. package/dist/data.js +1 -0
  339. package/dist/export-helper.cjs +1 -0
  340. package/dist/helpers.cjs +1 -0
  341. package/dist/helpers.js +1 -0
  342. package/dist/html.cjs +1 -0
  343. package/dist/html.js +1 -0
  344. package/dist/images/marker-icon-2x.png +0 -0
  345. package/dist/images/marker-icon.png +0 -0
  346. package/dist/images/marker-shadow.png +0 -0
  347. package/dist/index.cjs +1 -2
  348. package/dist/index.d.cts +60 -99
  349. package/dist/index.d.ts +41 -83
  350. package/dist/index.js +1 -2
  351. package/dist/index.vue.d.cts +79 -0
  352. package/dist/index.vue.d.ts +3 -4
  353. package/dist/index10.vue.d.cts +47 -0
  354. package/dist/index10.vue.d.ts +80 -40
  355. package/dist/index11.vue.d.cts +88 -0
  356. package/dist/index11.vue.d.ts +68 -78
  357. package/dist/index12.vue.d.cts +79 -0
  358. package/dist/index12.vue.d.ts +49 -45
  359. package/dist/index13.vue.d.cts +64 -0
  360. package/dist/index13.vue.d.ts +99 -34
  361. package/dist/index14.vue.d.cts +106 -0
  362. package/dist/index14.vue.d.ts +82 -99
  363. package/dist/index15.vue.d.cts +91 -0
  364. package/dist/index15.vue.d.ts +25 -83
  365. package/dist/index16.vue.d.cts +34 -0
  366. package/dist/index16.vue.d.ts +113 -24
  367. package/dist/index17.vue.d.cts +124 -0
  368. package/dist/index17.vue.d.ts +76 -110
  369. package/dist/index18.vue.d.cts +83 -0
  370. package/dist/index18.vue.d.ts +67 -73
  371. package/dist/index19.vue.d.cts +101 -0
  372. package/dist/index19.vue.d.ts +93 -59
  373. package/dist/index2.vue.d.cts +82 -0
  374. package/dist/index2.vue.d.ts +1 -2
  375. package/dist/index20.vue.d.cts +54 -0
  376. package/dist/index20.vue.d.ts +40 -88
  377. package/dist/index21.vue.d.cts +103 -0
  378. package/dist/index21.vue.d.ts +89 -41
  379. package/dist/index22.vue.d.cts +45 -0
  380. package/dist/index22.vue.d.ts +37 -96
  381. package/dist/index23.vue.d.cts +19 -0
  382. package/dist/index23.vue.d.ts +8 -35
  383. package/dist/{index28.vue.d.ts → index24.vue.d.cts} +5 -6
  384. package/dist/index24.vue.d.ts +650 -86
  385. package/dist/index25.vue.d.cts +96 -0
  386. package/dist/index25.vue.d.ts +86 -10
  387. package/dist/index26.vue.d.cts +111 -0
  388. package/dist/index26.vue.d.ts +3 -4
  389. package/dist/index27.vue.d.cts +80 -0
  390. package/dist/index27.vue.d.ts +3 -4
  391. package/dist/index3.vue.d.cts +87 -0
  392. package/dist/index3.vue.d.ts +2 -3
  393. package/dist/index4.vue.d.cts +37 -0
  394. package/dist/index4.vue.d.ts +5 -6
  395. package/dist/index5.vue.d.cts +133 -0
  396. package/dist/index5.vue.d.ts +2 -3
  397. package/dist/index6.vue.d.cts +29 -0
  398. package/dist/index6.vue.d.ts +25 -38
  399. package/dist/index7.vue.d.cts +32 -0
  400. package/dist/index7.vue.d.ts +117 -23
  401. package/dist/index8.vue.d.cts +127 -0
  402. package/dist/index8.vue.d.ts +17 -111
  403. package/dist/index9.vue.d.cts +34 -0
  404. package/dist/index9.vue.d.ts +38 -26
  405. package/dist/{menuAdapter.d.ts → public.d.ts} +2 -3
  406. package/dist/resolver.cjs +1 -2
  407. package/dist/resolver.d.cts +6 -4
  408. package/dist/resolver.d.ts +6 -4
  409. package/dist/resolver.js +1 -2
  410. package/dist/storage.cjs +1 -0
  411. package/dist/storage.js +1 -2
  412. package/dist/style.css +9864 -7702
  413. package/dist/types.d.cts +107 -0
  414. package/dist/types.d.ts +58 -0
  415. package/dist/types2.d.cts +58 -0
  416. package/dist/useCalendarEvents.d.cts +150 -0
  417. package/dist/useCalendarEvents.d.ts +3 -4
  418. package/dist/useCollapsePanel.d.cts +133 -0
  419. package/dist/useCollapsePanel.d.ts +1 -2
  420. package/dist/useCropperCore.d.cts +104 -0
  421. package/dist/useCropperCore.d.ts +12 -10
  422. package/dist/useDraggableLayout.d.cts +196 -0
  423. package/dist/useDraggableLayout.d.ts +18 -16
  424. package/dist/useDynamicFormState.d.cts +749 -0
  425. package/dist/useDynamicFormState.d.ts +234 -112
  426. package/dist/useEdgeInteraction.d.cts +324 -0
  427. package/dist/useEdgeInteraction.d.ts +6 -7
  428. package/dist/useFullscreen.d.cts +165 -0
  429. package/dist/useFullscreen.d.ts +1 -2
  430. package/dist/useGlobalSearch.d.cts +110 -0
  431. package/dist/useGlobalSearch.d.ts +17 -9
  432. package/dist/useInfiniteScroll.d.cts +177 -0
  433. package/dist/useInfiniteScroll.d.ts +11 -3
  434. package/dist/useQRCode.d.cts +89 -0
  435. package/dist/useQRCode.d.ts +7 -5
  436. package/dist/useSearchState.d.cts +871 -0
  437. package/dist/useSearchState.d.ts +828 -28
  438. package/dist/useSignatureHistory.d.cts +197 -0
  439. package/dist/useSignatureHistory.d.ts +9 -4
  440. package/dist/useSplitResize.d.cts +157 -0
  441. package/dist/useSplitResize.d.ts +3 -4
  442. package/dist/useTableQuery.cjs +3 -0
  443. package/dist/{useModalEdit.d.ts → useTableQuery.d.cts} +624 -211
  444. package/dist/useTableQuery.d.ts +1721 -0
  445. package/dist/useTableQuery.js +3 -0
  446. package/dist/useTimeSelection.d.cts +116 -0
  447. package/dist/useTimeSelection.d.ts +19 -8
  448. package/dist/useTreeOperations.d.cts +185 -0
  449. package/dist/useTreeOperations.d.ts +14 -12
  450. package/dist/useWorkflowValidation.d.cts +1051 -0
  451. package/dist/useWorkflowValidation.d.ts +66 -67
  452. package/dist/verification.d.cts +58 -0
  453. package/dist/verification.d.ts +58 -0
  454. package/package.json +101 -69
  455. package/dist/C_ActionBar-BAcfOfRw.css.map +0 -1
  456. package/dist/C_ActionBar-DG2HW7t5.css.map +0 -1
  457. package/dist/C_ActionBar.js.map +0 -1
  458. package/dist/C_ActionBar2.js.map +0 -1
  459. package/dist/C_AntV-D_jTK8oZ.css.map +0 -1
  460. package/dist/C_AntV-DanTAq22.css.map +0 -1
  461. package/dist/C_AntV.js.map +0 -1
  462. package/dist/C_AntV2.js.map +0 -1
  463. package/dist/C_AudioPlayer-BnYIJXPD.css.map +0 -1
  464. package/dist/C_AudioPlayer-ldTy9Y34.css.map +0 -1
  465. package/dist/C_AudioPlayer.js.map +0 -1
  466. package/dist/C_AudioPlayer2.js.map +0 -1
  467. package/dist/C_AvatarGroup-B9d-GJ9c.css.map +0 -1
  468. package/dist/C_AvatarGroup-SIzxuuZr.css.map +0 -1
  469. package/dist/C_AvatarGroup.js.map +0 -1
  470. package/dist/C_AvatarGroup2.js.map +0 -1
  471. package/dist/C_Barcode-CAU2mFcx.css.map +0 -1
  472. package/dist/C_Barcode-D2oJaEg3.css.map +0 -1
  473. package/dist/C_Barcode.js.map +0 -1
  474. package/dist/C_Barcode2.js.map +0 -1
  475. package/dist/C_Breadcrumb-BiH0d9AI.css.map +0 -1
  476. package/dist/C_Breadcrumb-Bqqrl8nX.css.map +0 -1
  477. package/dist/C_Breadcrumb.js.map +0 -1
  478. package/dist/C_Breadcrumb2.js.map +0 -1
  479. package/dist/C_Captcha-B3yrtG7t.css.map +0 -1
  480. package/dist/C_Captcha-D4hDdWDu.css.map +0 -1
  481. package/dist/C_Captcha.js.map +0 -1
  482. package/dist/C_Captcha2.js.map +0 -1
  483. package/dist/C_Cascade-CmCrLVft.css.map +0 -1
  484. package/dist/C_Cascade-U6A5AJJb.css.map +0 -1
  485. package/dist/C_Cascade.js.map +0 -1
  486. package/dist/C_Cascade2.js.map +0 -1
  487. package/dist/C_Chat-CF1SU7mc.css.map +0 -1
  488. package/dist/C_Chat-DeUv6uuI.css.map +0 -1
  489. package/dist/C_Chat.js.map +0 -1
  490. package/dist/C_Chat2.js.map +0 -1
  491. package/dist/C_City-DdkX6u64.css.map +0 -1
  492. package/dist/C_City-iP-dIvui.css.map +0 -1
  493. package/dist/C_City.js.map +0 -1
  494. package/dist/C_City2.js.map +0 -1
  495. package/dist/C_Code-BWmJMNS7.css.map +0 -1
  496. package/dist/C_Code-k14vjgVl.css.map +0 -1
  497. package/dist/C_Code.js.map +0 -1
  498. package/dist/C_Code2.js.map +0 -1
  499. package/dist/C_CollapsePanel-BSKcWSmY.css.map +0 -1
  500. package/dist/C_CollapsePanel-DRvRq8tB.css.map +0 -1
  501. package/dist/C_CollapsePanel.js.map +0 -1
  502. package/dist/C_CollapsePanel2.js.map +0 -1
  503. package/dist/C_ContextMenu-Bn_uhJSo.css.map +0 -1
  504. package/dist/C_ContextMenu-C9iuxG2n.css.map +0 -1
  505. package/dist/C_ContextMenu.js.map +0 -1
  506. package/dist/C_ContextMenu2.js.map +0 -1
  507. package/dist/C_Cron-BIQ2By9v.css.map +0 -1
  508. package/dist/C_Cron-BNSAGD6A.css.map +0 -1
  509. package/dist/C_Cron.js.map +0 -1
  510. package/dist/C_Cron2.js.map +0 -1
  511. package/dist/C_Date.js.map +0 -1
  512. package/dist/C_Date2.js.map +0 -1
  513. package/dist/C_Draggable-BDAbk4s5.css.map +0 -1
  514. package/dist/C_Draggable-UhfDLXop.css.map +0 -1
  515. package/dist/C_Draggable.js.map +0 -1
  516. package/dist/C_Draggable2.js.map +0 -1
  517. package/dist/C_Editor-CZOaChru.css.map +0 -1
  518. package/dist/C_Editor-kJAQq5z6.css.map +0 -1
  519. package/dist/C_Editor.js.map +0 -1
  520. package/dist/C_Editor2.js.map +0 -1
  521. package/dist/C_FilePreview-Bs_omgJS.css.map +0 -1
  522. package/dist/C_FilePreview-DbwoyF2j.css.map +0 -1
  523. package/dist/C_FilePreview.js.map +0 -1
  524. package/dist/C_FilePreview2.js.map +0 -1
  525. package/dist/C_Form-B_2ql62V.css.map +0 -1
  526. package/dist/C_Form-CX-7xsz3.css.map +0 -1
  527. package/dist/C_Form.js.map +0 -1
  528. package/dist/C_Form2.js.map +0 -1
  529. package/dist/C_FormSearch-C3F8peAY.css.map +0 -1
  530. package/dist/C_FormSearch-qW56bJPo.css.map +0 -1
  531. package/dist/C_FormSearch.js.map +0 -1
  532. package/dist/C_FormSearch2.js.map +0 -1
  533. package/dist/C_FormulaEditor-ChOvDbsZ.css.map +0 -1
  534. package/dist/C_FormulaEditor-mfiIUDY4.css.map +0 -1
  535. package/dist/C_FormulaEditor.js.map +0 -1
  536. package/dist/C_FormulaEditor2.js.map +0 -1
  537. package/dist/C_FullCalendar-C2nsqrgS.css.map +0 -1
  538. package/dist/C_FullCalendar-DdFtXVhy.css.map +0 -1
  539. package/dist/C_FullCalendar.js.map +0 -1
  540. package/dist/C_FullCalendar2.js.map +0 -1
  541. package/dist/C_GlobalSearch-Dq4Wo01K.css.map +0 -1
  542. package/dist/C_GlobalSearch-RB7J43gu.css.map +0 -1
  543. package/dist/C_GlobalSearch.js.map +0 -1
  544. package/dist/C_GlobalSearch2.js.map +0 -1
  545. package/dist/C_Guide.js.map +0 -1
  546. package/dist/C_Guide2.js.map +0 -1
  547. package/dist/C_Icon.js.map +0 -1
  548. package/dist/C_Icon2.js.map +0 -1
  549. package/dist/C_ImageCropper-CdTX2nxU.css.map +0 -1
  550. package/dist/C_ImageCropper-ilOqPklJ.css.map +0 -1
  551. package/dist/C_ImageCropper.js.map +0 -1
  552. package/dist/C_ImageCropper2.js.map +0 -1
  553. package/dist/C_Language.js.map +0 -1
  554. package/dist/C_Language2.js.map +0 -1
  555. package/dist/C_Login-B3qYkLrN.css.map +0 -1
  556. package/dist/C_Login-xMI1_ojE.css.map +0 -1
  557. package/dist/C_Login.js.map +0 -1
  558. package/dist/C_Login2.js.map +0 -1
  559. package/dist/C_Map-CWMMN58_.css.map +0 -1
  560. package/dist/C_Map-xSR2H-9o.css.map +0 -1
  561. package/dist/C_Map.js.map +0 -1
  562. package/dist/C_Map2.js.map +0 -1
  563. package/dist/C_Markdown-C7lTVJ1S.css.map +0 -1
  564. package/dist/C_Markdown-CZDm_baQ.css.map +0 -1
  565. package/dist/C_Markdown.js.map +0 -1
  566. package/dist/C_Markdown2.js.map +0 -1
  567. package/dist/C_Menu.js.map +0 -1
  568. package/dist/C_Menu2.js.map +0 -1
  569. package/dist/C_NotificationCenter-D7GGGWg7.css.map +0 -1
  570. package/dist/C_NotificationCenter-SSmeHA45.css.map +0 -1
  571. package/dist/C_NotificationCenter.js.map +0 -1
  572. package/dist/C_NotificationCenter2.js.map +0 -1
  573. package/dist/C_OrgChart-ChZu346B.css.map +0 -1
  574. package/dist/C_OrgChart-DqBufOv5.css.map +0 -1
  575. package/dist/C_OrgChart.js.map +0 -1
  576. package/dist/C_OrgChart2.js.map +0 -1
  577. package/dist/C_Progress.js.map +0 -1
  578. package/dist/C_Progress2.js.map +0 -1
  579. package/dist/C_QRCode-B_1lHUdx.css.map +0 -1
  580. package/dist/C_QRCode-CuQRYu0s.css.map +0 -1
  581. package/dist/C_QRCode.js.map +0 -1
  582. package/dist/C_QRCode2.js.map +0 -1
  583. package/dist/C_Signature-Bp-RBpAl.css.map +0 -1
  584. package/dist/C_Signature-Cb2VW1ok.css.map +0 -1
  585. package/dist/C_Signature.js.map +0 -1
  586. package/dist/C_Signature2.js.map +0 -1
  587. package/dist/C_Skeleton-BNPbaut7.css.map +0 -1
  588. package/dist/C_Skeleton-Djsjw8XY.css.map +0 -1
  589. package/dist/C_Skeleton.js.map +0 -1
  590. package/dist/C_Skeleton2.js.map +0 -1
  591. package/dist/C_SplitPane-CgGWYsRE.css.map +0 -1
  592. package/dist/C_SplitPane-DGf6WEeR.css.map +0 -1
  593. package/dist/C_SplitPane.js.map +0 -1
  594. package/dist/C_SplitPane2.js.map +0 -1
  595. package/dist/C_Steps-27Exh7PM.css.map +0 -1
  596. package/dist/C_Steps-DU9ZsFrs.css.map +0 -1
  597. package/dist/C_Steps.js.map +0 -1
  598. package/dist/C_Steps2.js.map +0 -1
  599. package/dist/C_Table--kbQaaBv.css.map +0 -1
  600. package/dist/C_Table-D631kdXC.css.map +0 -1
  601. package/dist/C_Table.js.map +0 -1
  602. package/dist/C_Table2.js +0 -4
  603. package/dist/C_Table2.js.map +0 -1
  604. package/dist/C_TagsView-BN8vMRsY.css.map +0 -1
  605. package/dist/C_TagsView-s1dnSJAV.css.map +0 -1
  606. package/dist/C_TagsView.js.map +0 -1
  607. package/dist/C_TagsView2.js.map +0 -1
  608. package/dist/C_Theme.js.map +0 -1
  609. package/dist/C_Theme2.js.map +0 -1
  610. package/dist/C_Time-BQ9PGy8B.css.map +0 -1
  611. package/dist/C_Time-CO73krC6.css.map +0 -1
  612. package/dist/C_Time.js.map +0 -1
  613. package/dist/C_Time2.js.map +0 -1
  614. package/dist/C_Timeline-4rk0Dzgn.css.map +0 -1
  615. package/dist/C_Timeline-DdESmqwL.css.map +0 -1
  616. package/dist/C_Timeline.js.map +0 -1
  617. package/dist/C_Timeline2.js.map +0 -1
  618. package/dist/C_Transfer-B-vjvhjI.css.map +0 -1
  619. package/dist/C_Transfer-EzdHzZNv.css.map +0 -1
  620. package/dist/C_Transfer.js.map +0 -1
  621. package/dist/C_Transfer2.js.map +0 -1
  622. package/dist/C_Tree-CsXgGfhn.css.map +0 -1
  623. package/dist/C_Tree-P7eRpnOa.css.map +0 -1
  624. package/dist/C_Tree.js.map +0 -1
  625. package/dist/C_Tree2.js.map +0 -1
  626. package/dist/C_Upload-CHjiVVh7.css.map +0 -1
  627. package/dist/C_Upload-DE3OLsZD.css.map +0 -1
  628. package/dist/C_Upload.js.map +0 -1
  629. package/dist/C_Upload2.js.map +0 -1
  630. package/dist/C_VideoPlayer-CnaNOFtW.css.map +0 -1
  631. package/dist/C_VideoPlayer-cnP88zNV.css.map +0 -1
  632. package/dist/C_VideoPlayer.js.map +0 -1
  633. package/dist/C_VideoPlayer2.js.map +0 -1
  634. package/dist/C_VtableGantt-CZlJgvpC.css.map +0 -1
  635. package/dist/C_VtableGantt-DKfQkql0.css.map +0 -1
  636. package/dist/C_VtableGantt.js.map +0 -1
  637. package/dist/C_VtableGantt2.js.map +0 -1
  638. package/dist/C_WaterFall-BJ_39ext.css.map +0 -1
  639. package/dist/C_WaterFall-cYc1vntE.css.map +0 -1
  640. package/dist/C_WaterFall.js.map +0 -1
  641. package/dist/C_WaterFall2.js.map +0 -1
  642. package/dist/C_WorkFlow-BFzRGGhJ.css.map +0 -1
  643. package/dist/C_WorkFlow-DSyCOvRp.css.map +0 -1
  644. package/dist/C_WorkFlow.js.map +0 -1
  645. package/dist/C_WorkFlow2.js.map +0 -1
  646. package/dist/_shared.cjs +0 -1
  647. package/dist/_shared.d.cts +0 -2
  648. package/dist/_shared.d.ts +0 -2
  649. package/dist/_shared.js +0 -1
  650. package/dist/city.js.map +0 -1
  651. package/dist/index.cjs.map +0 -1
  652. package/dist/index.js.map +0 -1
  653. package/dist/menuAdapter.js +0 -2
  654. package/dist/menuAdapter.js.map +0 -1
  655. package/dist/resolver.cjs.map +0 -1
  656. package/dist/resolver.js.map +0 -1
  657. package/dist/storage.js.map +0 -1
  658. /package/dist/{chunk.js → chunk.cjs} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_WorkFlow2.js","names":["data","data","data","data","StartNode","ApprovalNode","CopyNode","ConditionNode","users","departments"],"sources":["../src/components/C_WorkFlow/data.ts","../src/components/C_WorkFlow/NodeConfigModal.vue","../src/components/C_WorkFlow/NodeConfigModal.vue","../src/components/C_WorkFlow/nodes/StartNode.vue","../src/components/C_WorkFlow/nodes/StartNode.vue","../src/components/C_WorkFlow/nodes/ApprovalNode.vue","../src/components/C_WorkFlow/nodes/ApprovalNode.vue","../src/components/C_WorkFlow/nodes/CopyNode.vue","../src/components/C_WorkFlow/nodes/CopyNode.vue","../src/components/C_WorkFlow/nodes/ConditionNode.vue","../src/components/C_WorkFlow/nodes/ConditionNode.vue","../src/components/C_WorkFlow/composables/useWorkflowNodes.ts","../src/components/C_WorkFlow/composables/useWorkflowValidation.ts","../src/components/C_WorkFlow/composables/useWorkflowPreview.ts","../src/components/C_WorkFlow/index.vue","../src/components/C_WorkFlow/index.vue"],"sourcesContent":["/*\r\n * @Description: 工作(审批流)流组件 - 数据层\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n */\r\nimport type { NodeType, WorkflowNode } from \"./types\";\r\n\r\n/* 节点类型选项 */\r\nexport const NODE_TYPE_OPTIONS = [\r\n {\r\n type: \"approval\" as NodeType,\r\n label: \"审批人\",\r\n icon: \"mdi:account-check\",\r\n iconClass: \"approval-icon\",\r\n },\r\n {\r\n type: \"copy\" as NodeType,\r\n label: \"抄送人\",\r\n icon: \"mdi:email-outline\",\r\n iconClass: \"copy-icon\",\r\n },\r\n {\r\n type: \"condition\" as NodeType,\r\n label: \"条件分支\",\r\n icon: \"mdi:source-branch\",\r\n iconClass: \"condition-icon\",\r\n },\r\n];\r\n\r\n/* 审批模式选项 */\r\nexport const APPROVAL_MODES = [\r\n { value: \"any\", label: \"或签\", desc: \"任意一人同意即可通过\" },\r\n { value: \"all\", label: \"会签\", desc: \"所有人都同意才能通过\" },\r\n { value: \"sequence\", label: \"顺序审批\", desc: \"按选择顺序依次审批\" },\r\n];\r\n\r\n/* 字段选项 */\r\nexport const FIELD_OPTIONS = [\r\n { label: \"申请金额\", value: \"amount\" },\r\n { label: \"申请人部门\", value: \"department\" },\r\n { label: \"申请类型\", value: \"type\" },\r\n { label: \"紧急程度\", value: \"priority\" },\r\n];\r\n\r\n/* 操作符选项 */\r\nexport const OPERATOR_OPTIONS = [\r\n { label: \"等于\", value: \"equals\" },\r\n { label: \"大于\", value: \"greater_than\" },\r\n { label: \"小于\", value: \"less_than\" },\r\n { label: \"包含\", value: \"contains\" },\r\n { label: \"不等于\", value: \"not_equals\" },\r\n];\r\n\r\n/* 节点标题映射 */\r\nexport const NODE_TITLES: Record<NodeType, string> = {\r\n start: \"发起人\",\r\n approval: \"审批人\",\r\n copy: \"抄送人\",\r\n condition: \"条件分支\",\r\n};\r\n\r\n/* 配置标题映射 */\r\nexport const CONFIG_TITLES = {\r\n approval: \"审批人设置\",\r\n copy: \"抄送人设置\",\r\n condition: \"条件分支设置\",\r\n};\r\n\r\n/* 字段显示名称映射 */\r\nexport const FIELD_DISPLAY_NAMES: Record<string, string> = {\r\n approvers: \"审批人\",\r\n conditions: \"条件配置\",\r\n connection: \"节点连接\",\r\n copyUsers: \"抄送人\",\r\n};\r\n\r\n/* 错误类型文本映射 */\r\nexport const ERROR_TYPE_TEXTS: Record<string, string> = {\r\n required: \"必填\",\r\n incomplete: \"不完整\",\r\n warning: \"警告\",\r\n error: \"错误\",\r\n};\r\n\r\n/** 节点之间的 Y 轴间距(px) */\r\nexport const NODE_Y_GAP = 180;\r\n\r\n/* 初始节点数据 */\r\nexport const INITIAL_NODE: WorkflowNode = {\r\n id: \"start-1\",\r\n type: \"start\",\r\n position: { x: 150, y: 100 },\r\n data: { title: \"发起人\", status: \"active\", initiators: [] },\r\n};\r\n\r\n/* 默认头像生成函数 */\r\nexport const getDefaultAvatar = (name: string): string =>\r\n `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=random`;\r\n\r\n/* 生成条件ID的函数 */\r\nexport const generateConditionId = (): string => `condition-${Date.now()}`;\r\n\r\n/* 生成边ID的函数 */\r\nexport const generateEdgeId = (sourceId: string, targetId: string): string =>\r\n `edge-${sourceId}-${targetId}`;\r\n\r\n/* 默认条件对象工厂函数 */\r\nexport const createDefaultCondition = () => ({\r\n id: generateConditionId(),\r\n name: \"\",\r\n field: \"\",\r\n operator: \"equals\" as const,\r\n value: \"\",\r\n});\r\n","<!--\r\n * @Description: 节点配置弹窗组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <NModal\r\n v-model:show=\"visible\"\r\n style=\"width: 900px\"\r\n :mask-closable=\"false\"\r\n preset=\"dialog\"\r\n :title=\"configTitle\"\r\n positive-text=\"确定\"\r\n negative-text=\"取消\"\r\n :loading=\"configLoading\"\r\n @positive-click=\"saveNodeConfig\"\r\n @negative-click=\"handleCancel\"\r\n >\r\n <!-- 公共选择面板 -->\r\n <template v-if=\"isUserSelectNode\">\r\n <div class=\"max-h-60vh overflow-y-auto\">\r\n <div class=\"config-section\">\r\n <h4\r\n class=\"flex items-center gap-2 mb-4 text-base font-semibold\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n <C_Icon\r\n :name=\"userSelectConfig.icon\"\r\n :size=\"16\"\r\n v-if=\"userSelectConfig.icon\"\r\n />\r\n {{ userSelectConfig.title }}\r\n </h4>\r\n\r\n <NInput\r\n v-model:value=\"searchKeyword\"\r\n placeholder=\"搜索用户姓名或部门\"\r\n clearable\r\n class=\"mb-4\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\r\n </template>\r\n </NInput>\r\n\r\n <div\r\n class=\"border rounded-lg p-3 mb-4 max-h-50 overflow-y-auto\"\r\n :style=\"{\r\n borderColor: 'var(--n-border-color)',\r\n background: 'var(--n-color-embedded)',\r\n }\"\r\n >\r\n <NTree\r\n :data=\"departmentUserTree\"\r\n :checked-keys=\"userSelectConfig.checkedKeys\"\r\n :selectable=\"false\"\r\n checkable\r\n cascade\r\n :virtual-scroll=\"true\"\r\n style=\"max-height: 300px\"\r\n @update:checked-keys=\"userSelectConfig.onSelect\"\r\n />\r\n </div>\r\n\r\n <div\r\n v-if=\"userSelectConfig.selectedUsers.length > 0\"\r\n class=\"rounded-lg p-4 mb-4 border\"\r\n :style=\"{\r\n borderColor: 'var(--n-border-color)',\r\n background: 'var(--n-color-embedded)',\r\n }\"\r\n >\r\n <h5\r\n class=\"mb-2 text-sm font-medium\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n {{ userSelectConfig.selectedLabel }}\r\n ({{ userSelectConfig.selectedUsers.length }})\r\n </h5>\r\n <div class=\"flex flex-wrap gap-3\">\r\n <NTag\r\n v-for=\"user in userSelectConfig.selectedUsers\"\r\n :key=\"user.id\"\r\n closable\r\n :type=\"userSelectConfig.tagType\"\r\n @close=\"userSelectConfig.onRemove(user.id)\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <NAvatar\r\n :src=\"user.avatar\"\r\n :fallback-src=\"getDefaultAvatar(user.name)\"\r\n size=\"small\"\r\n />\r\n <span class=\"font-medium text-sm\">{{ user.name }}</span>\r\n <span\r\n class=\"text-xs px-1.5 py-0.5 rounded\"\r\n :style=\"{\r\n color: 'var(--n-text-color-3)',\r\n background: 'var(--n-color-embedded)',\r\n }\"\r\n >{{ user.department }}</span\r\n >\r\n </div>\r\n </NTag>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- 审批节点时附带审批模式 -->\r\n <template v-if=\"props.currentNode?.type === 'approval'\">\r\n <div class=\"mt-4\">\r\n <h5\r\n class=\"mb-3 text-sm font-medium\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n 审批模式\r\n </h5>\r\n <NRadioGroup v-model:value=\"approvalMode\">\r\n <NSpace vertical>\r\n <NRadio\r\n v-for=\"mode in APPROVAL_MODES\"\r\n :key=\"mode.value\"\r\n :value=\"mode.value\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <strong class=\"text-sm\">{{ mode.label }}</strong>\r\n <span\r\n class=\"text-xs\"\r\n :style=\"{ color: 'var(--n-text-color-3)' }\"\r\n >{{ mode.desc }}</span\r\n >\r\n </div>\r\n </NRadio>\r\n </NSpace>\r\n </NRadioGroup>\r\n </div>\r\n </template>\r\n </div>\r\n </template>\r\n\r\n <!-- 条件节点配置 -->\r\n <div\r\n v-else-if=\"props.currentNode?.type === 'condition'\"\r\n class=\"max-h-60vh overflow-y-auto\"\r\n >\r\n <div class=\"config-section\">\r\n <h4\r\n class=\"flex items-center gap-2 mb-4 text-base font-semibold\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n <C_Icon name=\"mdi:source-branch\" :size=\"16\" />\r\n 条件分支设置\r\n </h4>\r\n <div class=\"space-y-3\">\r\n <div\r\n v-for=\"(condition, index) in conditions\"\r\n :key=\"condition.id\"\r\n class=\"condition-item\"\r\n >\r\n <NCard\r\n size=\"small\"\r\n class=\"border border-gray-200 hover:border-gray-300 transition-colors\"\r\n >\r\n <div class=\"flex items-center gap-3 flex-nowrap min-h-10 p-2\">\r\n <NInput\r\n v-model:value=\"condition.name\"\r\n placeholder=\"分支名称\"\r\n style=\"width: 150px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NSelect\r\n v-model:value=\"condition.field\"\r\n placeholder=\"选择字段\"\r\n :options=\"FIELD_OPTIONS\"\r\n style=\"width: 120px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NSelect\r\n v-model:value=\"condition.operator\"\r\n placeholder=\"操作符\"\r\n :options=\"OPERATOR_OPTIONS\"\r\n style=\"width: 100px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NInput\r\n v-model:value=\"condition.value\"\r\n placeholder=\"值\"\r\n style=\"width: 120px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NButton\r\n quaternary\r\n type=\"error\"\r\n @click=\"removeCondition(index)\"\r\n class=\"flex-shrink-0 ml-auto\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </NCard>\r\n </div>\r\n <NButton dashed block @click=\"addCondition\" class=\"w-full\">\r\n <template #icon><C_Icon name=\"mdi:plus\" :size=\"16\" /></template>\r\n 添加条件\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n </NModal>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from \"vue\";\r\nimport {\r\n NModal,\r\n NInput,\r\n NTree,\r\n NTag,\r\n NAvatar,\r\n NRadioGroup,\r\n NRadio,\r\n NSpace,\r\n NCard,\r\n NSelect,\r\n NButton,\r\n useMessage,\r\n} from \"naive-ui\";\r\nimport { C_Icon } from \"../C_Icon\";\r\nimport type { WorkflowNode, User, Department, Condition } from \"./types\";\r\nimport {\r\n APPROVAL_MODES,\r\n FIELD_OPTIONS,\r\n OPERATOR_OPTIONS,\r\n CONFIG_TITLES,\r\n getDefaultAvatar,\r\n createDefaultCondition,\r\n} from \"./data\";\r\n\r\ninterface Props {\r\n show: boolean;\r\n currentNode: WorkflowNode | null;\r\n users: User[];\r\n departments: Department[];\r\n}\r\nconst props = withDefaults(defineProps<Props>(), {\r\n show: false,\r\n currentNode: null,\r\n users: () => [],\r\n departments: () => [],\r\n});\r\nconst emit = defineEmits([\"update:show\", \"save\", \"cancel\"]);\r\nconst message = useMessage();\r\n\r\nconst searchKeyword = ref(\"\");\r\nconst selectedUsers = ref<string[]>([]);\r\nconst selectedCopyUsers = ref<string[]>([]);\r\nconst approvalMode = ref<\"any\" | \"all\" | \"sequence\">(\"any\");\r\nconst configLoading = ref(false);\r\nconst conditions = ref<Condition[]>([]);\r\n\r\nconst visible = computed({\r\n get: () => props.show,\r\n set: (value: boolean) => emit(\"update:show\", value),\r\n});\r\nconst configTitle = computed(() => {\r\n if (props.currentNode?.type === \"start\") return \"发起人设置\";\r\n const type = props.currentNode?.type as keyof typeof CONFIG_TITLES;\r\n return CONFIG_TITLES[type] || \"节点设置\";\r\n});\r\n\r\n/* 用户树 */\r\nconst departmentUserTree = computed(() => {\r\n const tree: any[] = [];\r\n const deptMap = new Map();\r\n props.departments?.forEach((dept) => {\r\n if (!deptMap.has(dept.id)) {\r\n deptMap.set(dept.id, {\r\n key: `dept-${dept.id}`,\r\n label: `${dept.name} ${dept.manager ? `(负责人: ${dept.manager})` : \"\"}`,\r\n children: [],\r\n isLeaf: false,\r\n disabled: true,\r\n });\r\n }\r\n });\r\n const filteredUsers =\r\n props.users?.filter(\r\n (user) =>\r\n !searchKeyword.value ||\r\n user.name.includes(searchKeyword.value) ||\r\n user.department.includes(searchKeyword.value),\r\n ) || [];\r\n\r\n filteredUsers.forEach((user) => {\r\n const dept = props.departments?.find((d) => d.name === user.department);\r\n if (dept && deptMap.has(dept.id)) {\r\n deptMap.get(dept.id).children.push({\r\n key: user.id,\r\n label: `${user.name}${user.role ? `(${user.role})` : \"\"}`,\r\n isLeaf: true,\r\n user,\r\n });\r\n }\r\n });\r\n\r\n deptMap.forEach((dept) => {\r\n if (dept.children.length > 0) {\r\n tree.push(dept);\r\n }\r\n });\r\n\r\n return tree;\r\n});\r\n\r\nconst selectedApprovers = computed(\r\n () => props.users?.filter((u) => selectedUsers.value.includes(u.id)) || [],\r\n);\r\nconst selectedCopyUserList = computed(\r\n () =>\r\n props.users?.filter((u) => selectedCopyUsers.value.includes(u.id)) || [],\r\n);\r\nconst selectedInitiators = computed(\r\n () => props.users?.filter((u) => selectedUsers.value.includes(u.id)) || [],\r\n);\r\n\r\nconst handleUserSelect = (keys: string[]) => {\r\n selectedUsers.value = keys.filter((key) => !key.startsWith(\"dept-\"));\r\n};\r\nconst handleCopyUserSelect = (keys: string[]) => {\r\n selectedCopyUsers.value = keys.filter((key) => !key.startsWith(\"dept-\"));\r\n};\r\nconst removeApprover = (userId: string) => {\r\n selectedUsers.value = selectedUsers.value.filter((id) => id !== userId);\r\n};\r\nconst removeCopyUser = (userId: string) => {\r\n selectedCopyUsers.value = selectedCopyUsers.value.filter(\r\n (id) => id !== userId,\r\n );\r\n};\r\nconst removeInitiator = (userId: string) => {\r\n selectedUsers.value = selectedUsers.value.filter((id) => id !== userId);\r\n};\r\n\r\nconst addCondition = () => conditions.value.push(createDefaultCondition());\r\nconst removeCondition = (index: number) => conditions.value.splice(index, 1);\r\n\r\nconst configureStartNode = (node: WorkflowNode) => {\r\n const { initiators } = node.data as any;\r\n selectedUsers.value = initiators ? initiators.map((u: User) => u.id) : [];\r\n};\r\nconst configureApprovalNode = (node: WorkflowNode) => {\r\n const approvers = (node.data as any).approvers || [];\r\n selectedUsers.value = approvers.map((u: User) => u.id);\r\n approvalMode.value = (node.data as any).approvalMode || \"any\";\r\n};\r\nconst configureCopyNode = (node: WorkflowNode) => {\r\n const copyUsers = (node.data as any).copyUsers || [];\r\n selectedCopyUsers.value = copyUsers.map((u: User) => u.id);\r\n};\r\nconst configureConditionNode = (node: WorkflowNode) => {\r\n conditions.value = (node.data as any).conditions || [];\r\n};\r\n\r\nconst saveStartNodeConfig = async (): Promise<boolean> => {\r\n if (selectedUsers.value.length === 0) {\r\n message.error(\"请选择发起人\");\r\n return false;\r\n }\r\n const selectedUserObjs = selectedInitiators.value;\r\n emit(\"save\", { initiators: selectedUserObjs });\r\n message.success(`已设置 ${selectedUserObjs.length} 个发起人`);\r\n return true;\r\n};\r\nconst saveApprovalNodeConfig = async (): Promise<boolean> => {\r\n if (selectedUsers.value.length === 0) {\r\n message.error(\"请至少选择一个审批人\");\r\n return false;\r\n }\r\n const selectedUserObjs = selectedApprovers.value;\r\n emit(\"save\", {\r\n approvers: selectedUserObjs,\r\n approvalMode: approvalMode.value,\r\n });\r\n message.success(`已设置 ${selectedUserObjs.length} 个审批人`);\r\n return true;\r\n};\r\nconst saveCopyNodeConfig = async (): Promise<boolean> => {\r\n const selectedUserObjs = selectedCopyUserList.value;\r\n emit(\"save\", { copyUsers: selectedUserObjs });\r\n message.success(`已设置 ${selectedUserObjs.length} 个抄送人`);\r\n return true;\r\n};\r\nconst saveConditionNodeConfig = async (): Promise<boolean> => {\r\n if (conditions.value.length === 0) {\r\n message.error(\"请至少添加一个条件分支\");\r\n return false;\r\n }\r\n const validConditions = conditions.value.filter(\r\n (c) => c.name && c.field && c.operator && c.value,\r\n );\r\n if (validConditions.length === 0) {\r\n message.error(\"请完善条件配置\");\r\n return false;\r\n }\r\n emit(\"save\", { conditions: validConditions });\r\n message.success(`已设置 ${validConditions.length} 个条件分支`);\r\n return true;\r\n};\r\nconst saveNodeConfig = async (): Promise<boolean> => {\r\n if (!props.currentNode) return false;\r\n configLoading.value = true;\r\n try {\r\n const nodeSavers = {\r\n start: saveStartNodeConfig,\r\n approval: saveApprovalNodeConfig,\r\n copy: saveCopyNodeConfig,\r\n condition: saveConditionNodeConfig,\r\n };\r\n const saver = nodeSavers[props.currentNode.type as keyof typeof nodeSavers];\r\n const success = saver ? await saver() : false;\r\n return success;\r\n } catch (error) {\r\n message.error(\"保存配置失败\");\r\n console.error(\"Save node config error:\", error);\r\n return false;\r\n } finally {\r\n configLoading.value = false;\r\n }\r\n};\r\nconst handleCancel = () => {\r\n emit(\"cancel\");\r\n};\r\nwatch(\r\n () => props.currentNode,\r\n (newNode) => {\r\n if (newNode) {\r\n searchKeyword.value = \"\";\r\n const nodeConfigurators = {\r\n start: configureStartNode,\r\n approval: configureApprovalNode,\r\n copy: configureCopyNode,\r\n condition: configureConditionNode,\r\n };\r\n const configurator =\r\n nodeConfigurators[newNode.type as keyof typeof nodeConfigurators];\r\n if (configurator) {\r\n configurator(newNode);\r\n }\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\nconst isUserSelectNode = computed(() =>\r\n [\"start\", \"approval\", \"copy\"].includes(props.currentNode?.type || \"\"),\r\n);\r\n\r\nconst userSelectConfig = computed(() => {\r\n const type = props.currentNode?.type;\r\n if (type === \"start\") {\r\n return {\r\n icon: \"mdi:account-star\",\r\n title: \"选择发起人\",\r\n checkedKeys: selectedUsers.value,\r\n onSelect: handleUserSelect,\r\n selectedUsers: selectedInitiators.value,\r\n selectedLabel: \"已选择发起人\",\r\n tagType: \"primary\" as const,\r\n onRemove: removeInitiator,\r\n };\r\n } else if (type === \"approval\") {\r\n return {\r\n icon: \"mdi:account-check\",\r\n title: \"选择审批人\",\r\n checkedKeys: selectedUsers.value,\r\n onSelect: handleUserSelect,\r\n selectedUsers: selectedApprovers.value,\r\n selectedLabel: \"已选择审批人\",\r\n tagType: \"info\" as const,\r\n onRemove: removeApprover,\r\n };\r\n } else if (type === \"copy\") {\r\n return {\r\n icon: \"mdi:email-outline\",\r\n title: \"选择抄送人\",\r\n checkedKeys: selectedCopyUsers.value,\r\n onSelect: handleCopyUserSelect,\r\n selectedUsers: selectedCopyUserList.value,\r\n selectedLabel: \"已选择抄送人\",\r\n tagType: \"success\" as const,\r\n onRemove: removeCopyUser,\r\n };\r\n }\r\n /* 安全兜底 */\r\n return {\r\n icon: \"\",\r\n title: \"\",\r\n checkedKeys: [] as string[],\r\n onSelect: (_keys: string[]) => {},\r\n selectedUsers: [] as User[],\r\n selectedLabel: \"\",\r\n tagType: \"default\" as const,\r\n onRemove: (_id: string) => {},\r\n };\r\n});\r\n</script>\r\n","<!--\r\n * @Description: 节点配置弹窗组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <NModal\r\n v-model:show=\"visible\"\r\n style=\"width: 900px\"\r\n :mask-closable=\"false\"\r\n preset=\"dialog\"\r\n :title=\"configTitle\"\r\n positive-text=\"确定\"\r\n negative-text=\"取消\"\r\n :loading=\"configLoading\"\r\n @positive-click=\"saveNodeConfig\"\r\n @negative-click=\"handleCancel\"\r\n >\r\n <!-- 公共选择面板 -->\r\n <template v-if=\"isUserSelectNode\">\r\n <div class=\"max-h-60vh overflow-y-auto\">\r\n <div class=\"config-section\">\r\n <h4\r\n class=\"flex items-center gap-2 mb-4 text-base font-semibold\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n <C_Icon\r\n :name=\"userSelectConfig.icon\"\r\n :size=\"16\"\r\n v-if=\"userSelectConfig.icon\"\r\n />\r\n {{ userSelectConfig.title }}\r\n </h4>\r\n\r\n <NInput\r\n v-model:value=\"searchKeyword\"\r\n placeholder=\"搜索用户姓名或部门\"\r\n clearable\r\n class=\"mb-4\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\r\n </template>\r\n </NInput>\r\n\r\n <div\r\n class=\"border rounded-lg p-3 mb-4 max-h-50 overflow-y-auto\"\r\n :style=\"{\r\n borderColor: 'var(--n-border-color)',\r\n background: 'var(--n-color-embedded)',\r\n }\"\r\n >\r\n <NTree\r\n :data=\"departmentUserTree\"\r\n :checked-keys=\"userSelectConfig.checkedKeys\"\r\n :selectable=\"false\"\r\n checkable\r\n cascade\r\n :virtual-scroll=\"true\"\r\n style=\"max-height: 300px\"\r\n @update:checked-keys=\"userSelectConfig.onSelect\"\r\n />\r\n </div>\r\n\r\n <div\r\n v-if=\"userSelectConfig.selectedUsers.length > 0\"\r\n class=\"rounded-lg p-4 mb-4 border\"\r\n :style=\"{\r\n borderColor: 'var(--n-border-color)',\r\n background: 'var(--n-color-embedded)',\r\n }\"\r\n >\r\n <h5\r\n class=\"mb-2 text-sm font-medium\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n {{ userSelectConfig.selectedLabel }}\r\n ({{ userSelectConfig.selectedUsers.length }})\r\n </h5>\r\n <div class=\"flex flex-wrap gap-3\">\r\n <NTag\r\n v-for=\"user in userSelectConfig.selectedUsers\"\r\n :key=\"user.id\"\r\n closable\r\n :type=\"userSelectConfig.tagType\"\r\n @close=\"userSelectConfig.onRemove(user.id)\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <NAvatar\r\n :src=\"user.avatar\"\r\n :fallback-src=\"getDefaultAvatar(user.name)\"\r\n size=\"small\"\r\n />\r\n <span class=\"font-medium text-sm\">{{ user.name }}</span>\r\n <span\r\n class=\"text-xs px-1.5 py-0.5 rounded\"\r\n :style=\"{\r\n color: 'var(--n-text-color-3)',\r\n background: 'var(--n-color-embedded)',\r\n }\"\r\n >{{ user.department }}</span\r\n >\r\n </div>\r\n </NTag>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- 审批节点时附带审批模式 -->\r\n <template v-if=\"props.currentNode?.type === 'approval'\">\r\n <div class=\"mt-4\">\r\n <h5\r\n class=\"mb-3 text-sm font-medium\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n 审批模式\r\n </h5>\r\n <NRadioGroup v-model:value=\"approvalMode\">\r\n <NSpace vertical>\r\n <NRadio\r\n v-for=\"mode in APPROVAL_MODES\"\r\n :key=\"mode.value\"\r\n :value=\"mode.value\"\r\n >\r\n <div class=\"flex flex-col gap-1\">\r\n <strong class=\"text-sm\">{{ mode.label }}</strong>\r\n <span\r\n class=\"text-xs\"\r\n :style=\"{ color: 'var(--n-text-color-3)' }\"\r\n >{{ mode.desc }}</span\r\n >\r\n </div>\r\n </NRadio>\r\n </NSpace>\r\n </NRadioGroup>\r\n </div>\r\n </template>\r\n </div>\r\n </template>\r\n\r\n <!-- 条件节点配置 -->\r\n <div\r\n v-else-if=\"props.currentNode?.type === 'condition'\"\r\n class=\"max-h-60vh overflow-y-auto\"\r\n >\r\n <div class=\"config-section\">\r\n <h4\r\n class=\"flex items-center gap-2 mb-4 text-base font-semibold\"\r\n :style=\"{ color: 'var(--n-text-color-1)' }\"\r\n >\r\n <C_Icon name=\"mdi:source-branch\" :size=\"16\" />\r\n 条件分支设置\r\n </h4>\r\n <div class=\"space-y-3\">\r\n <div\r\n v-for=\"(condition, index) in conditions\"\r\n :key=\"condition.id\"\r\n class=\"condition-item\"\r\n >\r\n <NCard\r\n size=\"small\"\r\n class=\"border border-gray-200 hover:border-gray-300 transition-colors\"\r\n >\r\n <div class=\"flex items-center gap-3 flex-nowrap min-h-10 p-2\">\r\n <NInput\r\n v-model:value=\"condition.name\"\r\n placeholder=\"分支名称\"\r\n style=\"width: 150px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NSelect\r\n v-model:value=\"condition.field\"\r\n placeholder=\"选择字段\"\r\n :options=\"FIELD_OPTIONS\"\r\n style=\"width: 120px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NSelect\r\n v-model:value=\"condition.operator\"\r\n placeholder=\"操作符\"\r\n :options=\"OPERATOR_OPTIONS\"\r\n style=\"width: 100px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NInput\r\n v-model:value=\"condition.value\"\r\n placeholder=\"值\"\r\n style=\"width: 120px\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <NButton\r\n quaternary\r\n type=\"error\"\r\n @click=\"removeCondition(index)\"\r\n class=\"flex-shrink-0 ml-auto\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete\" :size=\"16\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </NCard>\r\n </div>\r\n <NButton dashed block @click=\"addCondition\" class=\"w-full\">\r\n <template #icon><C_Icon name=\"mdi:plus\" :size=\"16\" /></template>\r\n 添加条件\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n </NModal>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from \"vue\";\r\nimport {\r\n NModal,\r\n NInput,\r\n NTree,\r\n NTag,\r\n NAvatar,\r\n NRadioGroup,\r\n NRadio,\r\n NSpace,\r\n NCard,\r\n NSelect,\r\n NButton,\r\n useMessage,\r\n} from \"naive-ui\";\r\nimport { C_Icon } from \"../C_Icon\";\r\nimport type { WorkflowNode, User, Department, Condition } from \"./types\";\r\nimport {\r\n APPROVAL_MODES,\r\n FIELD_OPTIONS,\r\n OPERATOR_OPTIONS,\r\n CONFIG_TITLES,\r\n getDefaultAvatar,\r\n createDefaultCondition,\r\n} from \"./data\";\r\n\r\ninterface Props {\r\n show: boolean;\r\n currentNode: WorkflowNode | null;\r\n users: User[];\r\n departments: Department[];\r\n}\r\nconst props = withDefaults(defineProps<Props>(), {\r\n show: false,\r\n currentNode: null,\r\n users: () => [],\r\n departments: () => [],\r\n});\r\nconst emit = defineEmits([\"update:show\", \"save\", \"cancel\"]);\r\nconst message = useMessage();\r\n\r\nconst searchKeyword = ref(\"\");\r\nconst selectedUsers = ref<string[]>([]);\r\nconst selectedCopyUsers = ref<string[]>([]);\r\nconst approvalMode = ref<\"any\" | \"all\" | \"sequence\">(\"any\");\r\nconst configLoading = ref(false);\r\nconst conditions = ref<Condition[]>([]);\r\n\r\nconst visible = computed({\r\n get: () => props.show,\r\n set: (value: boolean) => emit(\"update:show\", value),\r\n});\r\nconst configTitle = computed(() => {\r\n if (props.currentNode?.type === \"start\") return \"发起人设置\";\r\n const type = props.currentNode?.type as keyof typeof CONFIG_TITLES;\r\n return CONFIG_TITLES[type] || \"节点设置\";\r\n});\r\n\r\n/* 用户树 */\r\nconst departmentUserTree = computed(() => {\r\n const tree: any[] = [];\r\n const deptMap = new Map();\r\n props.departments?.forEach((dept) => {\r\n if (!deptMap.has(dept.id)) {\r\n deptMap.set(dept.id, {\r\n key: `dept-${dept.id}`,\r\n label: `${dept.name} ${dept.manager ? `(负责人: ${dept.manager})` : \"\"}`,\r\n children: [],\r\n isLeaf: false,\r\n disabled: true,\r\n });\r\n }\r\n });\r\n const filteredUsers =\r\n props.users?.filter(\r\n (user) =>\r\n !searchKeyword.value ||\r\n user.name.includes(searchKeyword.value) ||\r\n user.department.includes(searchKeyword.value),\r\n ) || [];\r\n\r\n filteredUsers.forEach((user) => {\r\n const dept = props.departments?.find((d) => d.name === user.department);\r\n if (dept && deptMap.has(dept.id)) {\r\n deptMap.get(dept.id).children.push({\r\n key: user.id,\r\n label: `${user.name}${user.role ? `(${user.role})` : \"\"}`,\r\n isLeaf: true,\r\n user,\r\n });\r\n }\r\n });\r\n\r\n deptMap.forEach((dept) => {\r\n if (dept.children.length > 0) {\r\n tree.push(dept);\r\n }\r\n });\r\n\r\n return tree;\r\n});\r\n\r\nconst selectedApprovers = computed(\r\n () => props.users?.filter((u) => selectedUsers.value.includes(u.id)) || [],\r\n);\r\nconst selectedCopyUserList = computed(\r\n () =>\r\n props.users?.filter((u) => selectedCopyUsers.value.includes(u.id)) || [],\r\n);\r\nconst selectedInitiators = computed(\r\n () => props.users?.filter((u) => selectedUsers.value.includes(u.id)) || [],\r\n);\r\n\r\nconst handleUserSelect = (keys: string[]) => {\r\n selectedUsers.value = keys.filter((key) => !key.startsWith(\"dept-\"));\r\n};\r\nconst handleCopyUserSelect = (keys: string[]) => {\r\n selectedCopyUsers.value = keys.filter((key) => !key.startsWith(\"dept-\"));\r\n};\r\nconst removeApprover = (userId: string) => {\r\n selectedUsers.value = selectedUsers.value.filter((id) => id !== userId);\r\n};\r\nconst removeCopyUser = (userId: string) => {\r\n selectedCopyUsers.value = selectedCopyUsers.value.filter(\r\n (id) => id !== userId,\r\n );\r\n};\r\nconst removeInitiator = (userId: string) => {\r\n selectedUsers.value = selectedUsers.value.filter((id) => id !== userId);\r\n};\r\n\r\nconst addCondition = () => conditions.value.push(createDefaultCondition());\r\nconst removeCondition = (index: number) => conditions.value.splice(index, 1);\r\n\r\nconst configureStartNode = (node: WorkflowNode) => {\r\n const { initiators } = node.data as any;\r\n selectedUsers.value = initiators ? initiators.map((u: User) => u.id) : [];\r\n};\r\nconst configureApprovalNode = (node: WorkflowNode) => {\r\n const approvers = (node.data as any).approvers || [];\r\n selectedUsers.value = approvers.map((u: User) => u.id);\r\n approvalMode.value = (node.data as any).approvalMode || \"any\";\r\n};\r\nconst configureCopyNode = (node: WorkflowNode) => {\r\n const copyUsers = (node.data as any).copyUsers || [];\r\n selectedCopyUsers.value = copyUsers.map((u: User) => u.id);\r\n};\r\nconst configureConditionNode = (node: WorkflowNode) => {\r\n conditions.value = (node.data as any).conditions || [];\r\n};\r\n\r\nconst saveStartNodeConfig = async (): Promise<boolean> => {\r\n if (selectedUsers.value.length === 0) {\r\n message.error(\"请选择发起人\");\r\n return false;\r\n }\r\n const selectedUserObjs = selectedInitiators.value;\r\n emit(\"save\", { initiators: selectedUserObjs });\r\n message.success(`已设置 ${selectedUserObjs.length} 个发起人`);\r\n return true;\r\n};\r\nconst saveApprovalNodeConfig = async (): Promise<boolean> => {\r\n if (selectedUsers.value.length === 0) {\r\n message.error(\"请至少选择一个审批人\");\r\n return false;\r\n }\r\n const selectedUserObjs = selectedApprovers.value;\r\n emit(\"save\", {\r\n approvers: selectedUserObjs,\r\n approvalMode: approvalMode.value,\r\n });\r\n message.success(`已设置 ${selectedUserObjs.length} 个审批人`);\r\n return true;\r\n};\r\nconst saveCopyNodeConfig = async (): Promise<boolean> => {\r\n const selectedUserObjs = selectedCopyUserList.value;\r\n emit(\"save\", { copyUsers: selectedUserObjs });\r\n message.success(`已设置 ${selectedUserObjs.length} 个抄送人`);\r\n return true;\r\n};\r\nconst saveConditionNodeConfig = async (): Promise<boolean> => {\r\n if (conditions.value.length === 0) {\r\n message.error(\"请至少添加一个条件分支\");\r\n return false;\r\n }\r\n const validConditions = conditions.value.filter(\r\n (c) => c.name && c.field && c.operator && c.value,\r\n );\r\n if (validConditions.length === 0) {\r\n message.error(\"请完善条件配置\");\r\n return false;\r\n }\r\n emit(\"save\", { conditions: validConditions });\r\n message.success(`已设置 ${validConditions.length} 个条件分支`);\r\n return true;\r\n};\r\nconst saveNodeConfig = async (): Promise<boolean> => {\r\n if (!props.currentNode) return false;\r\n configLoading.value = true;\r\n try {\r\n const nodeSavers = {\r\n start: saveStartNodeConfig,\r\n approval: saveApprovalNodeConfig,\r\n copy: saveCopyNodeConfig,\r\n condition: saveConditionNodeConfig,\r\n };\r\n const saver = nodeSavers[props.currentNode.type as keyof typeof nodeSavers];\r\n const success = saver ? await saver() : false;\r\n return success;\r\n } catch (error) {\r\n message.error(\"保存配置失败\");\r\n console.error(\"Save node config error:\", error);\r\n return false;\r\n } finally {\r\n configLoading.value = false;\r\n }\r\n};\r\nconst handleCancel = () => {\r\n emit(\"cancel\");\r\n};\r\nwatch(\r\n () => props.currentNode,\r\n (newNode) => {\r\n if (newNode) {\r\n searchKeyword.value = \"\";\r\n const nodeConfigurators = {\r\n start: configureStartNode,\r\n approval: configureApprovalNode,\r\n copy: configureCopyNode,\r\n condition: configureConditionNode,\r\n };\r\n const configurator =\r\n nodeConfigurators[newNode.type as keyof typeof nodeConfigurators];\r\n if (configurator) {\r\n configurator(newNode);\r\n }\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\nconst isUserSelectNode = computed(() =>\r\n [\"start\", \"approval\", \"copy\"].includes(props.currentNode?.type || \"\"),\r\n);\r\n\r\nconst userSelectConfig = computed(() => {\r\n const type = props.currentNode?.type;\r\n if (type === \"start\") {\r\n return {\r\n icon: \"mdi:account-star\",\r\n title: \"选择发起人\",\r\n checkedKeys: selectedUsers.value,\r\n onSelect: handleUserSelect,\r\n selectedUsers: selectedInitiators.value,\r\n selectedLabel: \"已选择发起人\",\r\n tagType: \"primary\" as const,\r\n onRemove: removeInitiator,\r\n };\r\n } else if (type === \"approval\") {\r\n return {\r\n icon: \"mdi:account-check\",\r\n title: \"选择审批人\",\r\n checkedKeys: selectedUsers.value,\r\n onSelect: handleUserSelect,\r\n selectedUsers: selectedApprovers.value,\r\n selectedLabel: \"已选择审批人\",\r\n tagType: \"info\" as const,\r\n onRemove: removeApprover,\r\n };\r\n } else if (type === \"copy\") {\r\n return {\r\n icon: \"mdi:email-outline\",\r\n title: \"选择抄送人\",\r\n checkedKeys: selectedCopyUsers.value,\r\n onSelect: handleCopyUserSelect,\r\n selectedUsers: selectedCopyUserList.value,\r\n selectedLabel: \"已选择抄送人\",\r\n tagType: \"success\" as const,\r\n onRemove: removeCopyUser,\r\n };\r\n }\r\n /* 安全兜底 */\r\n return {\r\n icon: \"\",\r\n title: \"\",\r\n checkedKeys: [] as string[],\r\n onSelect: (_keys: string[]) => {},\r\n selectedUsers: [] as User[],\r\n selectedLabel: \"\",\r\n tagType: \"default\" as const,\r\n onRemove: (_id: string) => {},\r\n };\r\n});\r\n</script>\r\n","<!--\r\n * @Description: 开始节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"start-node\">\r\n <!-- 主要内容区域 -->\r\n <div class=\"node-content\">\r\n <div class=\"node-icon\">🚀</div>\r\n <div class=\"node-info\">\r\n <div class=\"node-text\">{{ data.title }}</div>\r\n <div v-if=\"initiatorNames\" class=\"initiator-name\">\r\n {{ initiatorNames }}\r\n </div>\r\n <div v-else class=\"placeholder-text\">点击设置发起人</div>\r\n </div>\r\n </div>\r\n\r\n <!-- 添加节点按钮 -->\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { inject, computed } from \"vue\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\ninterface Initiator {\r\n id: string;\r\n name: string;\r\n department: string;\r\n role: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n status?: string;\r\n initiators?: Initiator[];\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\n/* 只注入需要的方法 */\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\n/* 计算发起人名称 */\r\nconst initiatorNames = computed(() => {\r\n const { initiators } = props.data;\r\n if (!initiators || !Array.isArray(initiators) || initiators.length === 0) {\r\n return \"\";\r\n }\r\n return initiators.map((user) => user?.name || \"未知用户\").join(\"、\");\r\n});\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.start-node {\r\n position: relative;\r\n}\r\n\r\n.node-content {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 16px 20px;\r\n border-radius: 12px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 12px;\r\n min-width: 160px;\r\n box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(102, 126, 234, 0.4);\r\n }\r\n}\r\n\r\n.node-icon {\r\n font-size: 16px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.node-info {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n text-align: center;\r\n}\r\n\r\n.node-text {\r\n font-weight: 600;\r\n font-size: 14px;\r\n}\r\n\r\n.initiator-name {\r\n font-size: 12px;\r\n opacity: 0.9;\r\n background: rgba(255, 255, 255, 0.2);\r\n padding: 2px 8px;\r\n border-radius: 8px;\r\n}\r\n\r\n.placeholder-text {\r\n font-size: 11px;\r\n opacity: 0.7;\r\n font-style: italic;\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #1890ff, #722ed1);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n font-size: 16px;\r\n font-weight: bold;\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 开始节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"start-node\">\r\n <!-- 主要内容区域 -->\r\n <div class=\"node-content\">\r\n <div class=\"node-icon\">🚀</div>\r\n <div class=\"node-info\">\r\n <div class=\"node-text\">{{ data.title }}</div>\r\n <div v-if=\"initiatorNames\" class=\"initiator-name\">\r\n {{ initiatorNames }}\r\n </div>\r\n <div v-else class=\"placeholder-text\">点击设置发起人</div>\r\n </div>\r\n </div>\r\n\r\n <!-- 添加节点按钮 -->\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { inject, computed } from \"vue\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\ninterface Initiator {\r\n id: string;\r\n name: string;\r\n department: string;\r\n role: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n status?: string;\r\n initiators?: Initiator[];\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\n/* 只注入需要的方法 */\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\n/* 计算发起人名称 */\r\nconst initiatorNames = computed(() => {\r\n const { initiators } = props.data;\r\n if (!initiators || !Array.isArray(initiators) || initiators.length === 0) {\r\n return \"\";\r\n }\r\n return initiators.map((user) => user?.name || \"未知用户\").join(\"、\");\r\n});\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.start-node {\r\n position: relative;\r\n}\r\n\r\n.node-content {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 16px 20px;\r\n border-radius: 12px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 12px;\r\n min-width: 160px;\r\n box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(102, 126, 234, 0.4);\r\n }\r\n}\r\n\r\n.node-icon {\r\n font-size: 16px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.node-info {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n text-align: center;\r\n}\r\n\r\n.node-text {\r\n font-weight: 600;\r\n font-size: 14px;\r\n}\r\n\r\n.initiator-name {\r\n font-size: 12px;\r\n opacity: 0.9;\r\n background: rgba(255, 255, 255, 0.2);\r\n padding: 2px 8px;\r\n border-radius: 8px;\r\n}\r\n\r\n.placeholder-text {\r\n font-size: 11px;\r\n opacity: 0.7;\r\n font-style: italic;\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #1890ff, #722ed1);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n font-size: 16px;\r\n font-weight: bold;\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 审批节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"approval-node\">\r\n <div class=\"status-indicator\" :class=\"data.status\"></div>\r\n\r\n <!-- 删除按钮 -->\r\n <div class=\"delete-btn\" @click=\"handleDelete\" title=\"删除节点\">\r\n <C_Icon name=\"mdi:close\" :size=\"12\" />\r\n </div>\r\n\r\n <div class=\"node-card\" @click=\"handleNodeClick\">\r\n <div class=\"node-header\">\r\n <div class=\"node-icon\">\r\n <C_Icon name=\"mdi:account\" :size=\"12\" color=\"white\" />\r\n </div>\r\n <span class=\"node-title\">{{ data.title }}</span>\r\n <div class=\"node-badge\" v-if=\"approverCount > 0\">\r\n {{ approverCount }}\r\n </div>\r\n <div class=\"approval-mode-badge\" v-if=\"data.approvalMode\">\r\n {{ getApprovalModeText(data.approvalMode) }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <div v-if=\"approverCount > 0\" class=\"approvers-list\">\r\n <div\r\n v-for=\"approver in displayApprovers\"\r\n :key=\"approver.id\"\r\n class=\"approver-tag\"\r\n >\r\n <div class=\"approver-info\">\r\n <div class=\"approver-avatar\">\r\n <NAvatar\r\n :src=\"approver.avatar\"\r\n :fallback-src=\"getDefaultAvatar(approver.name)\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"approver-details\">\r\n <span class=\"approver-name\">{{ approver.name }}</span>\r\n <span class=\"approver-dept\">{{ approver.department }}</span>\r\n <span class=\"approver-role\">{{ approver.role }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div v-if=\"moreCount > 0\" class=\"more-count\">+{{ moreCount }}</div>\r\n </div>\r\n <div v-else class=\"placeholder\">\r\n <C_Icon name=\"mdi:account-plus\" :size=\"24\" color=\"#9ca3af\" />\r\n <span>请设置审批人</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, inject } from \"vue\";\r\nimport { NAvatar } from \"naive-ui\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\n/* 定义审批人类型 */\r\ninterface Approver {\r\n id: string;\r\n name: string;\r\n avatar?: string;\r\n department: string;\r\n role: string;\r\n email?: string;\r\n phone?: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n approvers?: Approver[];\r\n approvalMode?: \"any\" | \"all\" | \"sequence\";\r\n status?: string;\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\nconst deleteNodeFn = inject(\"deleteNode\") as\r\n | ((nodeId: string) => void)\r\n | undefined;\r\n\r\n/* 使用安全的访问方式和默认值 */\r\nconst approvers = computed(() => props.data.approvers ?? []);\r\nconst approverCount = computed(() => approvers.value.length);\r\nconst displayApprovers = computed(() => approvers.value.slice(0, 3));\r\nconst moreCount = computed(() => Math.max(0, approverCount.value - 3));\r\n\r\nconst getApprovalModeText = (mode?: string) => {\r\n const modeMap = {\r\n any: \"或签\",\r\n all: \"会签\",\r\n sequence: \"顺序\",\r\n };\r\n return modeMap[mode as keyof typeof modeMap] || \"\";\r\n};\r\n\r\nconst getDefaultAvatar = (name: string) => {\r\n return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=random`;\r\n};\r\n\r\nconst handleNodeClick = () => {\r\n /* 不阻止事件冒泡,让 VueFlow 的 node-click 事件自然触发 */\r\n};\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n\r\nconst handleDelete = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n if (deleteNodeFn) {\r\n deleteNodeFn(props.id);\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.approval-node {\r\n position: relative;\r\n}\r\n\r\n.status-indicator {\r\n position: absolute;\r\n top: -4px;\r\n right: -4px;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n z-index: 2;\r\n background: #faad14;\r\n\r\n &.approved {\r\n background: #52c41a;\r\n }\r\n &.rejected {\r\n background: #ff4d4f;\r\n }\r\n &.pending {\r\n background: #1890ff;\r\n }\r\n}\r\n\r\n.delete-btn {\r\n position: absolute;\r\n top: -10px;\r\n right: -10px;\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #ff4d4f;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 100; /* 确保在所有元素之上 */\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition: all 0.2s ease;\r\n border: 2px solid white;\r\n box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);\r\n\r\n &:hover {\r\n transform: scale(1);\r\n background: #ff7875;\r\n box-shadow: 0 4px 12px rgba(255, 77, 79, 0.5);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n}\r\n\r\n.approval-node:hover .delete-btn {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.node-card {\r\n background: white;\r\n border-radius: 12px;\r\n min-width: 240px;\r\n max-width: 300px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\r\n border: 2px solid transparent;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\r\n border-color: #1890ff;\r\n }\r\n}\r\n\r\n.node-header {\r\n padding: 12px 16px;\r\n background: linear-gradient(135deg, #e6f7ff, #f0f9ff);\r\n border-bottom: 1px solid #91d5ff;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.node-icon {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #1890ff;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.node-title {\r\n font-weight: 600;\r\n color: var(--n-text-color-1);\r\n font-size: 14px;\r\n flex: 1;\r\n}\r\n\r\n.node-badge {\r\n background: #1890ff;\r\n color: white;\r\n padding: 2px 6px;\r\n border-radius: 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n min-width: 16px;\r\n text-align: center;\r\n}\r\n\r\n.approval-mode-badge {\r\n background: #f0f0f0;\r\n color: #666;\r\n padding: 2px 6px;\r\n border-radius: 8px;\r\n font-size: 10px;\r\n font-weight: 500;\r\n}\r\n\r\n.node-content {\r\n padding: 16px;\r\n}\r\n\r\n.approvers-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.approver-tag {\r\n background: linear-gradient(135deg, #e6f7ff, #f0f9ff);\r\n border: 1px solid #91d5ff;\r\n border-radius: 8px;\r\n padding: 8px 12px;\r\n transition: all 0.2s ease;\r\n\r\n &:hover {\r\n transform: translateX(2px);\r\n box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);\r\n }\r\n\r\n .approver-info {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n\r\n .approver-avatar {\r\n flex-shrink: 0;\r\n }\r\n\r\n .approver-details {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n min-width: 0;\r\n\r\n .approver-name {\r\n font-weight: 600;\r\n color: #1890ff;\r\n font-size: 13px;\r\n }\r\n\r\n .approver-dept {\r\n font-size: 11px;\r\n color: #666;\r\n background: rgba(0, 0, 0, 0.05);\r\n padding: 1px 4px;\r\n border-radius: 4px;\r\n display: inline-block;\r\n }\r\n\r\n .approver-role {\r\n font-size: 10px;\r\n color: #999;\r\n }\r\n }\r\n }\r\n}\r\n\r\n.more-count {\r\n background: #f0f0f0;\r\n color: #8c8c8c;\r\n padding: 6px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n text-align: center;\r\n border: 1px dashed #d9d9d9;\r\n}\r\n\r\n.placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 8px;\r\n color: #8c8c8c;\r\n font-size: 12px;\r\n padding: 20px 0;\r\n border: 1px dashed #d9d9d9;\r\n border-radius: 8px;\r\n background: #fafafa;\r\n\r\n span {\r\n font-weight: 500;\r\n }\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #1890ff, #722ed1);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 审批节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"approval-node\">\r\n <div class=\"status-indicator\" :class=\"data.status\"></div>\r\n\r\n <!-- 删除按钮 -->\r\n <div class=\"delete-btn\" @click=\"handleDelete\" title=\"删除节点\">\r\n <C_Icon name=\"mdi:close\" :size=\"12\" />\r\n </div>\r\n\r\n <div class=\"node-card\" @click=\"handleNodeClick\">\r\n <div class=\"node-header\">\r\n <div class=\"node-icon\">\r\n <C_Icon name=\"mdi:account\" :size=\"12\" color=\"white\" />\r\n </div>\r\n <span class=\"node-title\">{{ data.title }}</span>\r\n <div class=\"node-badge\" v-if=\"approverCount > 0\">\r\n {{ approverCount }}\r\n </div>\r\n <div class=\"approval-mode-badge\" v-if=\"data.approvalMode\">\r\n {{ getApprovalModeText(data.approvalMode) }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <div v-if=\"approverCount > 0\" class=\"approvers-list\">\r\n <div\r\n v-for=\"approver in displayApprovers\"\r\n :key=\"approver.id\"\r\n class=\"approver-tag\"\r\n >\r\n <div class=\"approver-info\">\r\n <div class=\"approver-avatar\">\r\n <NAvatar\r\n :src=\"approver.avatar\"\r\n :fallback-src=\"getDefaultAvatar(approver.name)\"\r\n size=\"small\"\r\n />\r\n </div>\r\n <div class=\"approver-details\">\r\n <span class=\"approver-name\">{{ approver.name }}</span>\r\n <span class=\"approver-dept\">{{ approver.department }}</span>\r\n <span class=\"approver-role\">{{ approver.role }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div v-if=\"moreCount > 0\" class=\"more-count\">+{{ moreCount }}</div>\r\n </div>\r\n <div v-else class=\"placeholder\">\r\n <C_Icon name=\"mdi:account-plus\" :size=\"24\" color=\"#9ca3af\" />\r\n <span>请设置审批人</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, inject } from \"vue\";\r\nimport { NAvatar } from \"naive-ui\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\n/* 定义审批人类型 */\r\ninterface Approver {\r\n id: string;\r\n name: string;\r\n avatar?: string;\r\n department: string;\r\n role: string;\r\n email?: string;\r\n phone?: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n approvers?: Approver[];\r\n approvalMode?: \"any\" | \"all\" | \"sequence\";\r\n status?: string;\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\nconst deleteNodeFn = inject(\"deleteNode\") as\r\n | ((nodeId: string) => void)\r\n | undefined;\r\n\r\n/* 使用安全的访问方式和默认值 */\r\nconst approvers = computed(() => props.data.approvers ?? []);\r\nconst approverCount = computed(() => approvers.value.length);\r\nconst displayApprovers = computed(() => approvers.value.slice(0, 3));\r\nconst moreCount = computed(() => Math.max(0, approverCount.value - 3));\r\n\r\nconst getApprovalModeText = (mode?: string) => {\r\n const modeMap = {\r\n any: \"或签\",\r\n all: \"会签\",\r\n sequence: \"顺序\",\r\n };\r\n return modeMap[mode as keyof typeof modeMap] || \"\";\r\n};\r\n\r\nconst getDefaultAvatar = (name: string) => {\r\n return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=random`;\r\n};\r\n\r\nconst handleNodeClick = () => {\r\n /* 不阻止事件冒泡,让 VueFlow 的 node-click 事件自然触发 */\r\n};\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n\r\nconst handleDelete = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n if (deleteNodeFn) {\r\n deleteNodeFn(props.id);\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.approval-node {\r\n position: relative;\r\n}\r\n\r\n.status-indicator {\r\n position: absolute;\r\n top: -4px;\r\n right: -4px;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n z-index: 2;\r\n background: #faad14;\r\n\r\n &.approved {\r\n background: #52c41a;\r\n }\r\n &.rejected {\r\n background: #ff4d4f;\r\n }\r\n &.pending {\r\n background: #1890ff;\r\n }\r\n}\r\n\r\n.delete-btn {\r\n position: absolute;\r\n top: -10px;\r\n right: -10px;\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #ff4d4f;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 100; /* 确保在所有元素之上 */\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition: all 0.2s ease;\r\n border: 2px solid white;\r\n box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);\r\n\r\n &:hover {\r\n transform: scale(1);\r\n background: #ff7875;\r\n box-shadow: 0 4px 12px rgba(255, 77, 79, 0.5);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n}\r\n\r\n.approval-node:hover .delete-btn {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.node-card {\r\n background: white;\r\n border-radius: 12px;\r\n min-width: 240px;\r\n max-width: 300px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\r\n border: 2px solid transparent;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\r\n border-color: #1890ff;\r\n }\r\n}\r\n\r\n.node-header {\r\n padding: 12px 16px;\r\n background: linear-gradient(135deg, #e6f7ff, #f0f9ff);\r\n border-bottom: 1px solid #91d5ff;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.node-icon {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #1890ff;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.node-title {\r\n font-weight: 600;\r\n color: var(--n-text-color-1);\r\n font-size: 14px;\r\n flex: 1;\r\n}\r\n\r\n.node-badge {\r\n background: #1890ff;\r\n color: white;\r\n padding: 2px 6px;\r\n border-radius: 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n min-width: 16px;\r\n text-align: center;\r\n}\r\n\r\n.approval-mode-badge {\r\n background: #f0f0f0;\r\n color: #666;\r\n padding: 2px 6px;\r\n border-radius: 8px;\r\n font-size: 10px;\r\n font-weight: 500;\r\n}\r\n\r\n.node-content {\r\n padding: 16px;\r\n}\r\n\r\n.approvers-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.approver-tag {\r\n background: linear-gradient(135deg, #e6f7ff, #f0f9ff);\r\n border: 1px solid #91d5ff;\r\n border-radius: 8px;\r\n padding: 8px 12px;\r\n transition: all 0.2s ease;\r\n\r\n &:hover {\r\n transform: translateX(2px);\r\n box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);\r\n }\r\n\r\n .approver-info {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n\r\n .approver-avatar {\r\n flex-shrink: 0;\r\n }\r\n\r\n .approver-details {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n min-width: 0;\r\n\r\n .approver-name {\r\n font-weight: 600;\r\n color: #1890ff;\r\n font-size: 13px;\r\n }\r\n\r\n .approver-dept {\r\n font-size: 11px;\r\n color: #666;\r\n background: rgba(0, 0, 0, 0.05);\r\n padding: 1px 4px;\r\n border-radius: 4px;\r\n display: inline-block;\r\n }\r\n\r\n .approver-role {\r\n font-size: 10px;\r\n color: #999;\r\n }\r\n }\r\n }\r\n}\r\n\r\n.more-count {\r\n background: #f0f0f0;\r\n color: #8c8c8c;\r\n padding: 6px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n text-align: center;\r\n border: 1px dashed #d9d9d9;\r\n}\r\n\r\n.placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 8px;\r\n color: #8c8c8c;\r\n font-size: 12px;\r\n padding: 20px 0;\r\n border: 1px dashed #d9d9d9;\r\n border-radius: 8px;\r\n background: #fafafa;\r\n\r\n span {\r\n font-weight: 500;\r\n }\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #1890ff, #722ed1);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 抄送节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"copy-node\">\r\n <div class=\"status-indicator\" :class=\"data.status\"></div>\r\n\r\n <!-- 删除按钮 -->\r\n <div class=\"delete-btn\" @click=\"handleDelete\" title=\"删除节点\">\r\n <C_Icon name=\"mdi:close\" :size=\"12\" />\r\n </div>\r\n\r\n <div class=\"node-card\" @click=\"handleNodeClick\">\r\n <div class=\"node-header\">\r\n <div class=\"node-icon\">📋</div>\r\n <span class=\"node-title\">{{ data.title }}</span>\r\n <div class=\"node-badge\" v-if=\"copyCount > 0\">{{ copyCount }}</div>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <div v-if=\"copyCount > 0\" class=\"copy-users-list\">\r\n <div\r\n v-for=\"user in displayCopyUsers\"\r\n :key=\"user.id\"\r\n class=\"copy-user-tag\"\r\n >\r\n {{ user.name }}\r\n </div>\r\n <div v-if=\"moreCount > 0\" class=\"more-count\">+{{ moreCount }}</div>\r\n </div>\r\n <div v-else class=\"placeholder\">请设置抄送人</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, inject } from \"vue\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\ninterface User {\r\n id: string;\r\n name: string;\r\n department: string;\r\n role: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n copyUsers?: User[];\r\n status?: string;\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\nconst deleteNodeFn = inject(\"deleteNode\") as\r\n | ((nodeId: string) => void)\r\n | undefined;\r\n\r\n/* 统一使用安全的访问方式 */\r\nconst copyUsers = computed(() => props.data.copyUsers ?? []);\r\nconst copyCount = computed(() => copyUsers.value.length);\r\nconst displayCopyUsers = computed(() => copyUsers.value.slice(0, 3));\r\nconst moreCount = computed(() => Math.max(0, copyCount.value - 3));\r\n\r\nconst handleNodeClick = () => {\r\n /* 不阻止事件冒泡,让 VueFlow 的 node-click 事件自然触发 */\r\n};\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n\r\nconst handleDelete = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n if (deleteNodeFn) {\r\n deleteNodeFn(props.id);\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.copy-node {\r\n position: relative;\r\n}\r\n\r\n.status-indicator {\r\n position: absolute;\r\n top: -4px;\r\n right: -4px;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n z-index: 2;\r\n background: #faad14;\r\n\r\n &.completed {\r\n background: #52c41a;\r\n }\r\n &.active {\r\n background: #1890ff;\r\n }\r\n}\r\n\r\n.delete-btn {\r\n position: absolute;\r\n top: -10px;\r\n right: -10px;\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #ff4d4f;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 100;\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition: all 0.2s ease;\r\n border: 2px solid white;\r\n box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);\r\n\r\n &:hover {\r\n transform: scale(1);\r\n background: #ff7875;\r\n box-shadow: 0 4px 12px rgba(255, 77, 79, 0.5);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n}\r\n\r\n.copy-node:hover .delete-btn {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.node-card {\r\n background: white;\r\n border-radius: 12px;\r\n min-width: 200px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\r\n border: 2px solid transparent;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\r\n border-color: #52c41a;\r\n }\r\n}\r\n\r\n.node-header {\r\n padding: 12px 16px;\r\n background: #f6ffed;\r\n border-bottom: 1px solid #d9f7be;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.node-icon {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #52c41a;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 12px;\r\n}\r\n\r\n.node-title {\r\n font-weight: 600;\r\n color: #262626;\r\n font-size: 14px;\r\n flex: 1;\r\n}\r\n\r\n.node-badge {\r\n background: #52c41a;\r\n color: white;\r\n padding: 2px 6px;\r\n border-radius: 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n min-width: 16px;\r\n text-align: center;\r\n}\r\n\r\n.node-content {\r\n padding: 16px;\r\n}\r\n\r\n.copy-users-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 6px;\r\n align-items: center;\r\n}\r\n\r\n.copy-user-tag {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n background: #f6ffed;\r\n color: #52c41a;\r\n padding: 4px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n border: 1px solid #b7eb8f;\r\n}\r\n\r\n.more-count {\r\n background: #f0f0f0;\r\n color: #8c8c8c;\r\n padding: 4px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n}\r\n\r\n.placeholder {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n color: #8c8c8c;\r\n font-size: 12px;\r\n padding: 8px 0;\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #52c41a, #389e0d);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(82, 196, 26, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n font-size: 16px;\r\n font-weight: bold;\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 抄送节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"copy-node\">\r\n <div class=\"status-indicator\" :class=\"data.status\"></div>\r\n\r\n <!-- 删除按钮 -->\r\n <div class=\"delete-btn\" @click=\"handleDelete\" title=\"删除节点\">\r\n <C_Icon name=\"mdi:close\" :size=\"12\" />\r\n </div>\r\n\r\n <div class=\"node-card\" @click=\"handleNodeClick\">\r\n <div class=\"node-header\">\r\n <div class=\"node-icon\">📋</div>\r\n <span class=\"node-title\">{{ data.title }}</span>\r\n <div class=\"node-badge\" v-if=\"copyCount > 0\">{{ copyCount }}</div>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <div v-if=\"copyCount > 0\" class=\"copy-users-list\">\r\n <div\r\n v-for=\"user in displayCopyUsers\"\r\n :key=\"user.id\"\r\n class=\"copy-user-tag\"\r\n >\r\n {{ user.name }}\r\n </div>\r\n <div v-if=\"moreCount > 0\" class=\"more-count\">+{{ moreCount }}</div>\r\n </div>\r\n <div v-else class=\"placeholder\">请设置抄送人</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, inject } from \"vue\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\ninterface User {\r\n id: string;\r\n name: string;\r\n department: string;\r\n role: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n copyUsers?: User[];\r\n status?: string;\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\nconst deleteNodeFn = inject(\"deleteNode\") as\r\n | ((nodeId: string) => void)\r\n | undefined;\r\n\r\n/* 统一使用安全的访问方式 */\r\nconst copyUsers = computed(() => props.data.copyUsers ?? []);\r\nconst copyCount = computed(() => copyUsers.value.length);\r\nconst displayCopyUsers = computed(() => copyUsers.value.slice(0, 3));\r\nconst moreCount = computed(() => Math.max(0, copyCount.value - 3));\r\n\r\nconst handleNodeClick = () => {\r\n /* 不阻止事件冒泡,让 VueFlow 的 node-click 事件自然触发 */\r\n};\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n\r\nconst handleDelete = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n if (deleteNodeFn) {\r\n deleteNodeFn(props.id);\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.copy-node {\r\n position: relative;\r\n}\r\n\r\n.status-indicator {\r\n position: absolute;\r\n top: -4px;\r\n right: -4px;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n z-index: 2;\r\n background: #faad14;\r\n\r\n &.completed {\r\n background: #52c41a;\r\n }\r\n &.active {\r\n background: #1890ff;\r\n }\r\n}\r\n\r\n.delete-btn {\r\n position: absolute;\r\n top: -10px;\r\n right: -10px;\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #ff4d4f;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 100;\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition: all 0.2s ease;\r\n border: 2px solid white;\r\n box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);\r\n\r\n &:hover {\r\n transform: scale(1);\r\n background: #ff7875;\r\n box-shadow: 0 4px 12px rgba(255, 77, 79, 0.5);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n}\r\n\r\n.copy-node:hover .delete-btn {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.node-card {\r\n background: white;\r\n border-radius: 12px;\r\n min-width: 200px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\r\n border: 2px solid transparent;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\r\n border-color: #52c41a;\r\n }\r\n}\r\n\r\n.node-header {\r\n padding: 12px 16px;\r\n background: #f6ffed;\r\n border-bottom: 1px solid #d9f7be;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.node-icon {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #52c41a;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 12px;\r\n}\r\n\r\n.node-title {\r\n font-weight: 600;\r\n color: #262626;\r\n font-size: 14px;\r\n flex: 1;\r\n}\r\n\r\n.node-badge {\r\n background: #52c41a;\r\n color: white;\r\n padding: 2px 6px;\r\n border-radius: 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n min-width: 16px;\r\n text-align: center;\r\n}\r\n\r\n.node-content {\r\n padding: 16px;\r\n}\r\n\r\n.copy-users-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 6px;\r\n align-items: center;\r\n}\r\n\r\n.copy-user-tag {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n background: #f6ffed;\r\n color: #52c41a;\r\n padding: 4px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n border: 1px solid #b7eb8f;\r\n}\r\n\r\n.more-count {\r\n background: #f0f0f0;\r\n color: #8c8c8c;\r\n padding: 4px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n}\r\n\r\n.placeholder {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n color: #8c8c8c;\r\n font-size: 12px;\r\n padding: 8px 0;\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #52c41a, #389e0d);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(82, 196, 26, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n font-size: 16px;\r\n font-weight: bold;\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 条件节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"condition-node\">\r\n <div class=\"status-indicator\" :class=\"data.status\"></div>\r\n\r\n <!-- 删除按钮 -->\r\n <div class=\"delete-btn\" @click=\"handleDelete\" title=\"删除节点\">\r\n <C_Icon name=\"mdi:close\" :size=\"12\" />\r\n </div>\r\n\r\n <div class=\"node-card\">\r\n <div class=\"node-header\">\r\n <div class=\"node-icon\">🔀</div>\r\n <span class=\"node-title\">{{ data.title }}</span>\r\n <div class=\"node-badge\" v-if=\"conditionCount > 0\">\r\n {{ conditionCount }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <div v-if=\"conditionCount > 0\" class=\"conditions-list\">\r\n <div\r\n v-for=\"condition in displayConditions\"\r\n :key=\"condition.id\"\r\n class=\"condition-tag\"\r\n >\r\n <span class=\"condition-icon\">→</span>\r\n <span>{{\r\n condition.name ||\r\n \"条件\" + (displayConditions.indexOf(condition) + 1)\r\n }}</span>\r\n </div>\r\n <div v-if=\"moreCount > 0\" class=\"more-count\">+{{ moreCount }}</div>\r\n </div>\r\n <div v-else class=\"placeholder\">请设置分支条件</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, inject } from \"vue\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\n/* 定义条件类型 */\r\ninterface Condition {\r\n id: string;\r\n name: string;\r\n operator: \"equals\" | \"not_equals\" | \"greater_than\" | \"less_than\" | \"contains\";\r\n value: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n conditions?: Condition[];\r\n status?: string;\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\nconst deleteNodeFn = inject(\"deleteNode\") as\r\n | ((nodeId: string) => void)\r\n | undefined;\r\n\r\n/* 使用安全的访问方式和默认值 */\r\nconst conditions = computed(() => props.data.conditions ?? []);\r\nconst conditionCount = computed(() => conditions.value.length);\r\nconst displayConditions = computed(() => conditions.value.slice(0, 2));\r\nconst moreCount = computed(() => Math.max(0, conditionCount.value - 2));\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n\r\nconst handleDelete = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n if (deleteNodeFn) {\r\n deleteNodeFn(props.id);\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.condition-node {\r\n position: relative;\r\n}\r\n\r\n.status-indicator {\r\n position: absolute;\r\n top: -4px;\r\n right: -4px;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n z-index: 2;\r\n background: #faad14;\r\n\r\n &.completed {\r\n background: #52c41a;\r\n }\r\n &.active {\r\n background: #fa8c16;\r\n }\r\n}\r\n\r\n.delete-btn {\r\n position: absolute;\r\n top: -10px;\r\n right: -10px;\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #ff4d4f;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 100;\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition: all 0.2s ease;\r\n border: 2px solid white;\r\n box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);\r\n\r\n &:hover {\r\n transform: scale(1);\r\n background: #ff7875;\r\n box-shadow: 0 4px 12px rgba(255, 77, 79, 0.5);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n}\r\n\r\n.condition-node:hover .delete-btn {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.node-card {\r\n background: white;\r\n border-radius: 12px;\r\n min-width: 220px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\r\n border: 2px solid transparent;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\r\n border-color: #fa8c16;\r\n }\r\n}\r\n\r\n.node-header {\r\n padding: 12px 16px;\r\n background: #fff7e6;\r\n border-bottom: 1px solid #ffe7ba;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.node-icon {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #fa8c16;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 12px;\r\n}\r\n\r\n.node-title {\r\n font-weight: 600;\r\n color: #262626;\r\n font-size: 14px;\r\n flex: 1;\r\n}\r\n\r\n.node-badge {\r\n background: #fa8c16;\r\n color: white;\r\n padding: 2px 6px;\r\n border-radius: 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n min-width: 16px;\r\n text-align: center;\r\n}\r\n\r\n.node-content {\r\n padding: 16px;\r\n}\r\n\r\n.conditions-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n\r\n.condition-tag {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n background: #fff7e6;\r\n color: #fa8c16;\r\n padding: 6px 10px;\r\n border-radius: 8px;\r\n font-size: 12px;\r\n border: 1px solid #ffd591;\r\n transition: all 0.2s ease;\r\n\r\n &:hover {\r\n background: #ffe7ba;\r\n transform: translateX(2px);\r\n }\r\n}\r\n\r\n.condition-icon {\r\n font-weight: bold;\r\n color: #d48806;\r\n}\r\n\r\n.more-count {\r\n background: #f0f0f0;\r\n color: #8c8c8c;\r\n padding: 4px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n text-align: center;\r\n margin-top: 4px;\r\n}\r\n\r\n.placeholder {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n color: #8c8c8c;\r\n font-size: 12px;\r\n padding: 8px 0;\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #fa8c16, #d48806);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(250, 140, 22, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n font-size: 16px;\r\n font-weight: bold;\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(250, 140, 22, 0.4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Description: 条件节点组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"condition-node\">\r\n <div class=\"status-indicator\" :class=\"data.status\"></div>\r\n\r\n <!-- 删除按钮 -->\r\n <div class=\"delete-btn\" @click=\"handleDelete\" title=\"删除节点\">\r\n <C_Icon name=\"mdi:close\" :size=\"12\" />\r\n </div>\r\n\r\n <div class=\"node-card\">\r\n <div class=\"node-header\">\r\n <div class=\"node-icon\">🔀</div>\r\n <span class=\"node-title\">{{ data.title }}</span>\r\n <div class=\"node-badge\" v-if=\"conditionCount > 0\">\r\n {{ conditionCount }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <div v-if=\"conditionCount > 0\" class=\"conditions-list\">\r\n <div\r\n v-for=\"condition in displayConditions\"\r\n :key=\"condition.id\"\r\n class=\"condition-tag\"\r\n >\r\n <span class=\"condition-icon\">→</span>\r\n <span>{{\r\n condition.name ||\r\n \"条件\" + (displayConditions.indexOf(condition) + 1)\r\n }}</span>\r\n </div>\r\n <div v-if=\"moreCount > 0\" class=\"more-count\">+{{ moreCount }}</div>\r\n </div>\r\n <div v-else class=\"placeholder\">请设置分支条件</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"add-node-btn\" @click=\"showAddMenu\" title=\"添加下一个节点\">\r\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, inject } from \"vue\";\r\nimport { C_Icon } from \"../../C_Icon\";\r\n\r\n/* 定义条件类型 */\r\ninterface Condition {\r\n id: string;\r\n name: string;\r\n operator: \"equals\" | \"not_equals\" | \"greater_than\" | \"less_than\" | \"contains\";\r\n value: string;\r\n}\r\n\r\ninterface Props {\r\n id: string;\r\n data: {\r\n title: string;\r\n conditions?: Condition[];\r\n status?: string;\r\n };\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\nconst showAddMenuFn = inject(\"showAddMenu\") as\r\n | ((position: { x: number; y: number }, nodeId?: string) => void)\r\n | undefined;\r\n\r\nconst deleteNodeFn = inject(\"deleteNode\") as\r\n | ((nodeId: string) => void)\r\n | undefined;\r\n\r\n/* 使用安全的访问方式和默认值 */\r\nconst conditions = computed(() => props.data.conditions ?? []);\r\nconst conditionCount = computed(() => conditions.value.length);\r\nconst displayConditions = computed(() => conditions.value.slice(0, 2));\r\nconst moreCount = computed(() => Math.max(0, conditionCount.value - 2));\r\n\r\nconst showAddMenu = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\r\n\r\n if (showAddMenuFn) {\r\n showAddMenuFn(\r\n {\r\n x: rect.left + rect.width / 2,\r\n y: rect.bottom + 10,\r\n },\r\n props.id,\r\n );\r\n }\r\n};\r\n\r\nconst handleDelete = (event: MouseEvent) => {\r\n event.stopPropagation();\r\n if (deleteNodeFn) {\r\n deleteNodeFn(props.id);\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.condition-node {\r\n position: relative;\r\n}\r\n\r\n.status-indicator {\r\n position: absolute;\r\n top: -4px;\r\n right: -4px;\r\n width: 12px;\r\n height: 12px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n z-index: 2;\r\n background: #faad14;\r\n\r\n &.completed {\r\n background: #52c41a;\r\n }\r\n &.active {\r\n background: #fa8c16;\r\n }\r\n}\r\n\r\n.delete-btn {\r\n position: absolute;\r\n top: -10px;\r\n right: -10px;\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #ff4d4f;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 100;\r\n opacity: 0;\r\n transform: scale(0.8);\r\n transition: all 0.2s ease;\r\n border: 2px solid white;\r\n box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);\r\n\r\n &:hover {\r\n transform: scale(1);\r\n background: #ff7875;\r\n box-shadow: 0 4px 12px rgba(255, 77, 79, 0.5);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n}\r\n\r\n.condition-node:hover .delete-btn {\r\n opacity: 1;\r\n transform: scale(1);\r\n}\r\n\r\n.node-card {\r\n background: white;\r\n border-radius: 12px;\r\n min-width: 220px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\r\n border: 2px solid transparent;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\r\n border-color: #fa8c16;\r\n }\r\n}\r\n\r\n.node-header {\r\n padding: 12px 16px;\r\n background: #fff7e6;\r\n border-bottom: 1px solid #ffe7ba;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.node-icon {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 50%;\r\n background: #fa8c16;\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 12px;\r\n}\r\n\r\n.node-title {\r\n font-weight: 600;\r\n color: #262626;\r\n font-size: 14px;\r\n flex: 1;\r\n}\r\n\r\n.node-badge {\r\n background: #fa8c16;\r\n color: white;\r\n padding: 2px 6px;\r\n border-radius: 10px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n min-width: 16px;\r\n text-align: center;\r\n}\r\n\r\n.node-content {\r\n padding: 16px;\r\n}\r\n\r\n.conditions-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n\r\n.condition-tag {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n background: #fff7e6;\r\n color: #fa8c16;\r\n padding: 6px 10px;\r\n border-radius: 8px;\r\n font-size: 12px;\r\n border: 1px solid #ffd591;\r\n transition: all 0.2s ease;\r\n\r\n &:hover {\r\n background: #ffe7ba;\r\n transform: translateX(2px);\r\n }\r\n}\r\n\r\n.condition-icon {\r\n font-weight: bold;\r\n color: #d48806;\r\n}\r\n\r\n.more-count {\r\n background: #f0f0f0;\r\n color: #8c8c8c;\r\n padding: 4px 8px;\r\n border-radius: 12px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n text-align: center;\r\n margin-top: 4px;\r\n}\r\n\r\n.placeholder {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n color: #8c8c8c;\r\n font-size: 12px;\r\n padding: 8px 0;\r\n}\r\n\r\n.add-node-btn {\r\n position: absolute;\r\n left: 50%;\r\n bottom: -20px;\r\n transform: translateX(-50%);\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #fa8c16, #d48806);\r\n color: white;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n box-shadow: 0 4px 12px rgba(250, 140, 22, 0.3);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n font-size: 16px;\r\n font-weight: bold;\r\n z-index: 10;\r\n\r\n &:hover {\r\n transform: translateX(-50%) scale(1.1);\r\n box-shadow: 0 6px 20px rgba(250, 140, 22, 0.4);\r\n }\r\n}\r\n</style>\r\n","/**\r\n * 工作流节点管理 Composable\r\n * 封装节点 CRUD、provide/inject、边重连、画布操作等逻辑\r\n */\r\n\r\nimport {\r\n ref,\r\n computed,\r\n provide,\r\n watch,\r\n onMounted,\r\n nextTick,\r\n markRaw,\r\n} from \"vue\";\r\nimport type { Component, Ref } from \"vue\";\r\nimport { useMessage } from \"naive-ui\";\r\nimport type {\r\n WorkflowNode,\r\n WorkflowEdge,\r\n WorkflowData,\r\n WorkflowProps,\r\n NodeType,\r\n MenuPosition,\r\n} from \"../types\";\r\nimport { NODE_TITLES, INITIAL_NODE, NODE_Y_GAP, generateEdgeId } from \"../data\";\r\n\r\nimport StartNode from \"../nodes/StartNode.vue\";\r\nimport ApprovalNode from \"../nodes/ApprovalNode.vue\";\r\nimport CopyNode from \"../nodes/CopyNode.vue\";\r\nimport ConditionNode from \"../nodes/ConditionNode.vue\";\r\n\r\n/** 节点组件映射(模块级常量,仅初始化一次) */\r\nconst NODE_COMPONENT_MAP: Record<string, Component> = {\r\n start: markRaw(StartNode),\r\n approval: markRaw(ApprovalNode),\r\n copy: markRaw(CopyNode),\r\n condition: markRaw(ConditionNode),\r\n};\r\n\r\ntype EmitFn = (event: string, ...args: any[]) => void;\r\n\r\n/** 工作流节点管理 —— 封装节点 CRUD、provide/inject、边重连、画布操作等逻辑 */\r\nexport function useWorkflowNodes(\r\n props: WorkflowProps,\r\n emit: EmitFn,\r\n vueFlowRef: Ref,\r\n) {\r\n const message = useMessage();\r\n\r\n /* ─── 响应式状态 ────────────────────────────────────────── */\r\n const nodes = ref<WorkflowNode[]>([{ ...INITIAL_NODE }]);\r\n const edges = ref<WorkflowEdge[]>([]);\r\n const showAddMenu = ref(false);\r\n const menuPosition = ref<MenuPosition>({ x: 0, y: 0 });\r\n const showNodeConfig = ref(false);\r\n const currentNode = ref<WorkflowNode | null>(null);\r\n const currentAddNodeId = ref<string | null>(null);\r\n\r\n /* ─── 计算属性 ──────────────────────────────────────────── */\r\n const nodeTypes = computed(() => NODE_COMPONENT_MAP);\r\n\r\n const workflowStats = computed(() => {\r\n const stats = {\r\n totalNodes: nodes.value.length,\r\n approvalNodes: 0,\r\n copyNodes: 0,\r\n conditionNodes: 0,\r\n };\r\n nodes.value.forEach((n) => {\r\n if (n.type === \"approval\") stats.approvalNodes++;\r\n else if (n.type === \"copy\") stats.copyNodes++;\r\n else if (n.type === \"condition\") stats.conditionNodes++;\r\n });\r\n return stats;\r\n });\r\n\r\n /* ─── 数据操作 ──────────────────────────────────────────── */\r\n /** 获取当前工作流完整数据 */\r\n const getCurrentWorkflowData = (): WorkflowData => ({\r\n nodes: nodes.value,\r\n edges: edges.value,\r\n config: { version: \"1.0\", createdAt: new Date().toISOString() },\r\n });\r\n\r\n /** 触发数据变更事件 */\r\n const emitChange = (): void => {\r\n const data = getCurrentWorkflowData();\r\n emit(\"update:modelValue\", data);\r\n emit(\"change\", data);\r\n };\r\n\r\n /** 延迟适应画布视图 */\r\n const deferFitView = (padding = 60, duration = 400): void => {\r\n nextTick(() => {\r\n setTimeout(() => {\r\n vueFlowRef.value?.fitView?.({ padding, duration });\r\n }, 100);\r\n });\r\n };\r\n\r\n /* ─── 添加菜单 ──────────────────────────────────────────── */\r\n const handleShowAddMenu = (position: MenuPosition, nodeId?: string): void => {\r\n menuPosition.value = {\r\n x: typeof position.x === \"number\" ? position.x : 0,\r\n y: typeof position.y === \"number\" ? position.y : 0,\r\n };\r\n currentAddNodeId.value = nodeId || null;\r\n showAddMenu.value = true;\r\n };\r\n\r\n const closeAddMenu = (): void => {\r\n showAddMenu.value = false;\r\n };\r\n\r\n /* ─── 节点删除 ──────────────────────────────────────────── */\r\n const deleteNode = (nodeId: string): void => {\r\n if (nodeId === \"start-1\") {\r\n message.warning(\"不能删除开始节点\");\r\n return;\r\n }\r\n\r\n const nodeIndex = nodes.value.findIndex((n) => n.id === nodeId);\r\n if (nodeIndex === -1) return;\r\n\r\n /* 收集上下游边,用于重连 */\r\n const incomingEdges = edges.value.filter((e) => e.target === nodeId);\r\n const outgoingEdges = edges.value.filter((e) => e.source === nodeId);\r\n\r\n /* 移除节点和关联边 */\r\n nodes.value.splice(nodeIndex, 1);\r\n edges.value = edges.value.filter(\r\n (e) => e.source !== nodeId && e.target !== nodeId,\r\n );\r\n\r\n /* 重连上下游 */\r\n incomingEdges.forEach((inEdge) => {\r\n outgoingEdges.forEach((outEdge) => {\r\n edges.value.push({\r\n id: generateEdgeId(inEdge.source, outEdge.target),\r\n source: inEdge.source,\r\n target: outEdge.target,\r\n animated: true,\r\n type: \"default\",\r\n });\r\n });\r\n });\r\n\r\n /* 下移后续节点位置 */\r\n nodes.value.forEach((node, i) => {\r\n if (i >= nodeIndex) node.position.y -= NODE_Y_GAP;\r\n });\r\n\r\n emitChange();\r\n message.success(\"节点已删除\");\r\n deferFitView();\r\n };\r\n\r\n /* ─── provide 注入给子节点组件 ───────────────────────────── */\r\n provide(\"showAddMenu\", handleShowAddMenu);\r\n provide(\"deleteNode\", deleteNode);\r\n\r\n /* ─── 节点添加(内部辅助) ───────────────────────────────── */\r\n const getTargetNodeInfo = () => {\r\n let targetNodeIndex = nodes.value.length - 1;\r\n let targetNode = nodes.value[targetNodeIndex];\r\n\r\n if (currentAddNodeId.value) {\r\n const foundIndex = nodes.value.findIndex(\r\n (n) => n.id === currentAddNodeId.value,\r\n );\r\n if (foundIndex !== -1) {\r\n targetNodeIndex = foundIndex;\r\n targetNode = nodes.value[targetNodeIndex];\r\n }\r\n }\r\n return { targetNodeIndex, targetNode };\r\n };\r\n\r\n const createNewNode = (\r\n type: NodeType,\r\n targetNode: WorkflowNode | null,\r\n ): WorkflowNode => ({\r\n id: `${type}-${Date.now()}`,\r\n type,\r\n position: {\r\n x: targetNode?.position.x || 150,\r\n y: (targetNode?.position.y || 130) + NODE_Y_GAP,\r\n },\r\n data: {\r\n title: NODE_TITLES[type],\r\n status: \"pending\",\r\n ...(type === \"approval\" && { approvers: [], approvalMode: \"any\" }),\r\n ...(type === \"copy\" && { copyUsers: [] }),\r\n ...(type === \"condition\" && { conditions: [] }),\r\n },\r\n });\r\n\r\n /** 将新节点插入到 targetNode 之后,重连所有边 */\r\n const reconnectEdges = (\r\n targetNode: WorkflowNode,\r\n newNode: WorkflowNode,\r\n ): void => {\r\n const outgoing = edges.value.filter((e) => e.source === targetNode.id);\r\n edges.value = edges.value.filter((e) => e.source !== targetNode.id);\r\n\r\n /* 原节点 → 新节点 */\r\n edges.value.push({\r\n id: generateEdgeId(targetNode.id, newNode.id),\r\n source: targetNode.id,\r\n target: newNode.id,\r\n animated: true,\r\n type: \"default\",\r\n });\r\n\r\n /* 新节点 → 原下游 */\r\n outgoing.forEach((edge) => {\r\n edges.value.push({\r\n id: generateEdgeId(newNode.id, edge.target),\r\n source: newNode.id,\r\n target: edge.target,\r\n animated: true,\r\n type: \"default\",\r\n });\r\n });\r\n };\r\n\r\n /* ─── 节点添加(公开) ───────────────────────────────────── */\r\n const addNode = (type: NodeType): void => {\r\n try {\r\n const { targetNodeIndex, targetNode } = getTargetNodeInfo();\r\n const newNode = createNewNode(type, targetNode);\r\n\r\n nodes.value.splice(targetNodeIndex + 1, 0, newNode);\r\n\r\n /* 下推后续节点 */\r\n for (let i = targetNodeIndex + 2; i < nodes.value.length; i++) {\r\n nodes.value[i].position.y += NODE_Y_GAP;\r\n }\r\n\r\n if (targetNode) reconnectEdges(targetNode, newNode);\r\n\r\n showAddMenu.value = false;\r\n currentAddNodeId.value = null;\r\n emitChange();\r\n deferFitView();\r\n } catch (error) {\r\n console.error(\"Error adding node:\", error);\r\n message.error(\"添加节点失败,请重试\");\r\n }\r\n };\r\n\r\n /* ─── 节点交互 ──────────────────────────────────────────── */\r\n const onNodeClick = (event: { node: WorkflowNode }): void => {\r\n const { node } = event;\r\n currentNode.value = node;\r\n showNodeConfig.value = true;\r\n emit(\"node-click\", node);\r\n };\r\n\r\n const handleConfigSave = (configData: Record<string, unknown>): void => {\r\n if (!currentNode.value) return;\r\n\r\n const nodeIndex = nodes.value.findIndex(\r\n (n) => n.id === currentNode.value!.id,\r\n );\r\n if (nodeIndex !== -1) {\r\n const updatedNode = {\r\n ...nodes.value[nodeIndex],\r\n data: { ...nodes.value[nodeIndex].data, ...configData },\r\n };\r\n nodes.value.splice(nodeIndex, 1, updatedNode);\r\n currentNode.value = updatedNode;\r\n }\r\n\r\n emitChange();\r\n showNodeConfig.value = false;\r\n message.success(\"节点配置已保存\");\r\n };\r\n\r\n /* ─── 画布操作 ──────────────────────────────────────────── */\r\n const fitView = (): void => {\r\n if (!vueFlowRef.value?.fitView) {\r\n message.warning(\"画布未准备就绪,请稍后重试\");\r\n return;\r\n }\r\n nextTick(() => {\r\n vueFlowRef.value.fitView({\r\n padding: 50,\r\n includeHiddenNodes: false,\r\n minZoom: 0.5,\r\n maxZoom: 1.5,\r\n duration: 800,\r\n });\r\n });\r\n message.success(\"已适应窗口大小\");\r\n };\r\n\r\n /** 重置节点和边(不涉及验证状态) */\r\n const resetNodes = (): void => {\r\n nodes.value = [{ ...INITIAL_NODE }];\r\n edges.value = [];\r\n emitChange();\r\n deferFitView(80, 600);\r\n message.success(\"画布已清空\");\r\n };\r\n\r\n /* ─── 生命周期 ──────────────────────────────────────────── */\r\n watch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (newValue && newValue !== getCurrentWorkflowData()) {\r\n nodes.value = newValue.nodes || [];\r\n edges.value = newValue.edges || [];\r\n }\r\n },\r\n { deep: true },\r\n );\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n setTimeout(() => {\r\n vueFlowRef.value?.fitView?.({\r\n padding: 80,\r\n includeHiddenNodes: false,\r\n minZoom: 0.8,\r\n maxZoom: 1.2,\r\n duration: 600,\r\n });\r\n }, 300);\r\n });\r\n });\r\n\r\n return {\r\n /* 状态 */\r\n nodes,\r\n edges,\r\n showAddMenu,\r\n menuPosition,\r\n showNodeConfig,\r\n currentNode,\r\n /* 计算属性 */\r\n nodeTypes,\r\n workflowStats,\r\n /* 方法 */\r\n addNode,\r\n onNodeClick,\r\n closeAddMenu,\r\n handleConfigSave,\r\n resetNodes,\r\n getCurrentWorkflowData,\r\n fitView,\r\n deleteNode,\r\n };\r\n}\r\n","/**\r\n * 工作流验证 Composable\r\n * 封装流程校验逻辑、错误展示和节点定位\r\n */\r\n\r\nimport { ref } from \"vue\";\r\nimport type { Ref } from \"vue\";\r\nimport { useMessage } from \"naive-ui\";\r\nimport type { WorkflowNode, WorkflowEdge, ValidationError } from \"../types\";\r\nimport { FIELD_DISPLAY_NAMES, ERROR_TYPE_TEXTS } from \"../data\";\r\n\r\nexport interface WorkflowValidationOptions {\r\n /** 定位错误节点后打开配置弹窗的回调 */\r\n onShowNodeConfig?: (node: WorkflowNode) => void;\r\n /** 验证失败时的外部回调(通常用于 emit) */\r\n onValidateError?: (errors: ValidationError[]) => void;\r\n}\r\n\r\n/** 工作流验证 —— 封装流程校验逻辑、错误展示和节点定位 */\r\nexport function useWorkflowValidation(\r\n nodes: Ref<WorkflowNode[]>,\r\n edges: Ref<WorkflowEdge[]>,\r\n vueFlowRef: Ref,\r\n options?: WorkflowValidationOptions,\r\n) {\r\n const message = useMessage();\r\n\r\n /* ─── 响应式状态 ────────────────────────────────────────── */\r\n const validationErrors = ref<ValidationError[]>([]);\r\n const showValidationErrors = ref(false);\r\n\r\n /* ─── 核心验证 ──────────────────────────────────────────── */\r\n /** 执行工作流验证,返回错误列表(纯逻辑,不操作 UI) */\r\n const validateWorkflow = (): ValidationError[] => {\r\n const errors: ValidationError[] = [];\r\n\r\n nodes.value.forEach((node) => {\r\n /* 审批节点:必须有审批人 */\r\n if (node.type === \"approval\") {\r\n const approvers = (node.data as any).approvers || [];\r\n if (approvers.length === 0) {\r\n errors.push({\r\n nodeId: node.id,\r\n nodeName: node.data.title,\r\n field: \"approvers\",\r\n message: \"审批节点必须设置至少一个审批人,否则流程无法正常运行\",\r\n type: \"required\",\r\n });\r\n }\r\n }\r\n\r\n /* 条件节点:必须有分支且配置完整 */\r\n if (node.type === \"condition\") {\r\n const conditions = (node.data as any).conditions || [];\r\n if (conditions.length === 0) {\r\n errors.push({\r\n nodeId: node.id,\r\n nodeName: node.data.title,\r\n field: \"conditions\",\r\n message:\r\n \"条件分支节点必须配置至少一个分支条件,否则无法进行条件判断\",\r\n type: \"required\",\r\n });\r\n } else {\r\n const incomplete = conditions.filter(\r\n (c: any) => !c.name || !c.field || !c.operator || !c.value,\r\n );\r\n if (incomplete.length > 0) {\r\n errors.push({\r\n nodeId: node.id,\r\n nodeName: node.data.title,\r\n field: \"conditions\",\r\n message: `有 ${incomplete.length} 个条件分支配置不完整,请完善所有必填字段`,\r\n type: \"incomplete\",\r\n });\r\n }\r\n }\r\n }\r\n });\r\n\r\n /* 检查断连节点 */\r\n const connectedNodes = new Set<string>();\r\n edges.value.forEach((edge) => {\r\n connectedNodes.add(edge.source);\r\n connectedNodes.add(edge.target);\r\n });\r\n\r\n nodes.value.forEach((node) => {\r\n if (node.type !== \"start\" && !connectedNodes.has(node.id)) {\r\n errors.push({\r\n nodeId: node.id,\r\n nodeName: node.data.title,\r\n field: \"connection\",\r\n message: \"此节点未与其他节点连接,可能导致流程中断\",\r\n type: \"warning\",\r\n });\r\n }\r\n });\r\n\r\n return errors;\r\n };\r\n\r\n /* ─── UI 交互 ───────────────────────────────────────────── */\r\n /** 执行验证并更新 UI 状态(消息提示 + 抽屉) */\r\n const validateCurrentWorkflow = (): void => {\r\n const errors = validateWorkflow();\r\n validationErrors.value = errors;\r\n\r\n if (errors.length === 0) {\r\n message.success(\"工作流验证通过!所有节点配置正确\");\r\n showValidationErrors.value = false;\r\n } else {\r\n message.error(`发现 ${errors.length} 个问题,请查看详细错误`);\r\n showValidationErrors.value = true;\r\n options?.onValidateError?.(errors);\r\n }\r\n };\r\n\r\n /** 定位到错误节点并打开配置弹窗 */\r\n const jumpToErrorNode = (nodeId: string): void => {\r\n const node = nodes.value.find((n) => n.id === nodeId);\r\n if (!node || !vueFlowRef.value) return;\r\n\r\n vueFlowRef.value.setCenter(node.position.x, node.position.y, {\r\n zoom: 1.2,\r\n duration: 800,\r\n });\r\n\r\n setTimeout(() => {\r\n options?.onShowNodeConfig?.(node);\r\n showValidationErrors.value = false;\r\n }, 900);\r\n\r\n message.info(`已定位到节点:${node.data.title}`);\r\n };\r\n\r\n /* ─── 辅助方法 ──────────────────────────────────────────── */\r\n const getFieldDisplayName = (field: string): string =>\r\n FIELD_DISPLAY_NAMES[field] || field;\r\n\r\n const getErrorTypeText = (type: string): string =>\r\n ERROR_TYPE_TEXTS[type] || type;\r\n\r\n /** 重置验证状态 */\r\n const resetValidation = (): void => {\r\n validationErrors.value = [];\r\n showValidationErrors.value = false;\r\n };\r\n\r\n return {\r\n validationErrors,\r\n showValidationErrors,\r\n validateWorkflow,\r\n validateCurrentWorkflow,\r\n jumpToErrorNode,\r\n getFieldDisplayName,\r\n getErrorTypeText,\r\n resetValidation,\r\n };\r\n}\r\n","/**\r\n * 工作流预览 Composable\r\n * 封装流程预览逻辑、节点排序和步骤展示\r\n */\r\n\r\nimport { ref } from \"vue\";\r\nimport type { Ref } from \"vue\";\r\nimport type { WorkflowNode, WorkflowEdge } from \"../types\";\r\n\r\n/** 详情项类型 */\r\nexport type DetailItemType = \"text\" | \"mode\" | \"users\" | \"warning\";\r\n\r\n/** 结构化详情项 */\r\nexport interface DetailItem {\r\n type: DetailItemType;\r\n label: string;\r\n value?: string;\r\n users?: { name: string; department?: string }[];\r\n modeKey?: string;\r\n}\r\n\r\n/** 流程步骤(已排序) */\r\nexport interface FlowStep {\r\n order: number;\r\n node: WorkflowNode;\r\n nodeTypeLabel: string;\r\n icon: string;\r\n colorClass: string;\r\n details: DetailItem[];\r\n children?: FlowStep[];\r\n}\r\n\r\n/** 预览统计信息 */\r\nexport interface PreviewStats {\r\n totalNodes: number;\r\n approvalNodes: number;\r\n copyNodes: number;\r\n conditionNodes: number;\r\n totalEdges: number;\r\n}\r\n\r\nconst NODE_TYPE_META: Record<\r\n string,\r\n { label: string; icon: string; colorClass: string }\r\n> = {\r\n start: { label: \"发起人\", icon: \"mdi:play-circle\", colorClass: \"start\" },\r\n approval: {\r\n label: \"审批节点\",\r\n icon: \"mdi:account-check\",\r\n colorClass: \"approval\",\r\n },\r\n copy: { label: \"抄送节点\", icon: \"mdi:email-outline\", colorClass: \"copy\" },\r\n condition: {\r\n label: \"条件分支\",\r\n icon: \"mdi:source-branch\",\r\n colorClass: \"condition\",\r\n },\r\n};\r\n\r\nconst APPROVAL_MODE_LABELS: Record<string, string> = {\r\n any: \"或签(任一审批人通过即可)\",\r\n all: \"会签(所有审批人必须通过)\",\r\n sequence: \"顺序审批(按顺序依次审批)\",\r\n};\r\n\r\nconst OPERATOR_LABELS: Record<string, string> = {\r\n equals: \"等于\",\r\n not_equals: \"不等于\",\r\n greater_than: \"大于\",\r\n less_than: \"小于\",\r\n contains: \"包含\",\r\n};\r\n\r\n/** 工作流预览 —— 封装流程预览逻辑、节点排序和步骤展示 */\r\nexport function useWorkflowPreview(\r\n nodes: Ref<WorkflowNode[]>,\r\n edges: Ref<WorkflowEdge[]>,\r\n) {\r\n /* ─── 响应式状态 ────────────────────────────────────────── */\r\n const showPreview = ref(false);\r\n const previewSteps = ref<FlowStep[]>([]);\r\n const previewStats = ref<PreviewStats>({\r\n totalNodes: 0,\r\n approvalNodes: 0,\r\n copyNodes: 0,\r\n conditionNodes: 0,\r\n totalEdges: 0,\r\n });\r\n\r\n /* ─── 节点详情提取 ──────────────────────────────────────── */\r\n /** 提取开始节点详情 */\r\n const extractStartDetails = (data: any): DetailItem[] => {\r\n const initiators = data.initiators || [];\r\n return initiators.length > 0\r\n ? [{ type: \"users\", label: \"发起人\", users: initiators }]\r\n : [{ type: \"text\", label: \"发起人\", value: \"所有人\" }];\r\n };\r\n\r\n /** 提取审批节点详情 */\r\n const extractApprovalDetails = (data: any): DetailItem[] => {\r\n const mode = data.approvalMode || \"any\";\r\n const approvers = data.approvers || [];\r\n const items: DetailItem[] = [\r\n {\r\n type: \"mode\",\r\n label: \"审批方式\",\r\n value: APPROVAL_MODE_LABELS[mode] || mode,\r\n modeKey: mode,\r\n },\r\n ];\r\n if (approvers.length > 0) {\r\n items.push({ type: \"users\", label: \"审批人\", users: approvers });\r\n } else {\r\n items.push({ type: \"warning\", label: \"审批人\", value: \"未设置\" });\r\n }\r\n return items;\r\n };\r\n\r\n /** 提取抄送节点详情 */\r\n const extractCopyDetails = (data: any): DetailItem[] => {\r\n const copyUsers = data.copyUsers || [];\r\n return copyUsers.length > 0\r\n ? [{ type: \"users\", label: \"抄送人\", users: copyUsers }]\r\n : [{ type: \"text\", label: \"抄送人\", value: \"未设置\" }];\r\n };\r\n\r\n /** 提取条件节点详情 */\r\n const extractConditionDetails = (data: any): DetailItem[] => {\r\n const conditions = data.conditions || [];\r\n if (conditions.length === 0) {\r\n return [{ type: \"warning\", label: \"条件分支\", value: \"未配置\" }];\r\n }\r\n return conditions.map((cond: any, i: number) => {\r\n const op = OPERATOR_LABELS[cond.operator] || cond.operator;\r\n return {\r\n type: \"text\" as const,\r\n label: `分支 ${i + 1}`,\r\n value: `${cond.name || \"未命名\"} — ${cond.field || \"?\"} ${op} ${cond.value || \"?\"}`,\r\n };\r\n });\r\n };\r\n\r\n /** 节点详情提取策略 */\r\n const detailExtractors: Record<string, (data: any) => DetailItem[]> = {\r\n start: extractStartDetails,\r\n approval: extractApprovalDetails,\r\n copy: extractCopyDetails,\r\n condition: extractConditionDetails,\r\n };\r\n\r\n /** 从节点数据中提取可读的配置详情 */\r\n const extractNodeDetails = (node: WorkflowNode): DetailItem[] => {\r\n const extractor = detailExtractors[node.type || \"start\"];\r\n return extractor ? extractor(node.data) : [];\r\n };\r\n\r\n /* ─── 拓扑排序 ──────────────────────────────────────────── */\r\n /** 基于 BFS 对流程节点进行拓扑排序 */\r\n const sortNodesByFlow = (): WorkflowNode[] => {\r\n const nodeMap = new Map<string, WorkflowNode>();\r\n nodes.value.forEach((n) => nodeMap.set(n.id, n));\r\n\r\n const adjacency = new Map<string, string[]>();\r\n const inDegree = new Map<string, number>();\r\n nodes.value.forEach((n) => {\r\n adjacency.set(n.id, []);\r\n inDegree.set(n.id, 0);\r\n });\r\n\r\n edges.value.forEach((e) => {\r\n adjacency.get(e.source)?.push(e.target);\r\n inDegree.set(e.target, (inDegree.get(e.target) || 0) + 1);\r\n });\r\n\r\n /* 从入度为 0 的节点(start)开始 BFS */\r\n const queue: string[] = [];\r\n inDegree.forEach((deg, id) => {\r\n if (deg === 0) queue.push(id);\r\n });\r\n\r\n const sorted: WorkflowNode[] = [];\r\n const visited = new Set<string>();\r\n\r\n while (queue.length > 0) {\r\n const id = queue.shift()!;\r\n if (visited.has(id)) continue;\r\n visited.add(id);\r\n\r\n const node = nodeMap.get(id);\r\n if (node) sorted.push(node);\r\n\r\n const neighbors = adjacency.get(id) || [];\r\n for (const next of neighbors) {\r\n const newDeg = (inDegree.get(next) || 1) - 1;\r\n inDegree.set(next, newDeg);\r\n if (newDeg <= 0 && !visited.has(next)) {\r\n queue.push(next);\r\n }\r\n }\r\n }\r\n\r\n /* 追加未连接的孤立节点 */\r\n nodes.value.forEach((n) => {\r\n if (!visited.has(n.id)) sorted.push(n);\r\n });\r\n\r\n return sorted;\r\n };\r\n\r\n /* ─── 生成流程步骤 ──────────────────────────────────────── */\r\n /** 构建预览步骤列表 */\r\n const buildFlowSteps = (): FlowStep[] => {\r\n const sorted = sortNodesByFlow();\r\n return sorted.map((node, index) => {\r\n const meta = NODE_TYPE_META[node.type || \"start\"] || NODE_TYPE_META.start;\r\n return {\r\n order: index + 1,\r\n node,\r\n nodeTypeLabel: meta.label,\r\n icon: meta.icon,\r\n colorClass: meta.colorClass,\r\n details: extractNodeDetails(node),\r\n };\r\n });\r\n };\r\n\r\n /* ─── 统计信息 ──────────────────────────────────────────── */\r\n const computeStats = (): PreviewStats => {\r\n const stats: PreviewStats = {\r\n totalNodes: nodes.value.length,\r\n approvalNodes: 0,\r\n copyNodes: 0,\r\n conditionNodes: 0,\r\n totalEdges: edges.value.length,\r\n };\r\n nodes.value.forEach((n) => {\r\n if (n.type === \"approval\") stats.approvalNodes++;\r\n else if (n.type === \"copy\") stats.copyNodes++;\r\n else if (n.type === \"condition\") stats.conditionNodes++;\r\n });\r\n return stats;\r\n };\r\n\r\n /* ─── 公开方法 ──────────────────────────────────────────── */\r\n /** 打开预览面板 */\r\n const openPreview = (): void => {\r\n previewSteps.value = buildFlowSteps();\r\n previewStats.value = computeStats();\r\n showPreview.value = true;\r\n };\r\n\r\n /** 关闭预览面板 */\r\n const closePreview = (): void => {\r\n showPreview.value = false;\r\n };\r\n\r\n return {\r\n showPreview,\r\n previewSteps,\r\n previewStats,\r\n openPreview,\r\n closePreview,\r\n };\r\n}\r\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-08-01\n * @Description: 工作流/审批流组件(基于 Vue Flow)\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n\n<template>\n <div class=\"approval-workflow-container\">\n <!-- 浮动工具栏 -->\n <div class=\"floating-toolbar\">\n <NButton\n size=\"small\"\n type=\"primary\"\n @click=\"saveWorkflow\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:content-save\"\n :size=\"16\"\n />\n </template>\n 保存\n </NButton>\n <NButton\n size=\"small\"\n @click=\"previewWorkflow\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:eye\"\n :size=\"16\"\n />\n </template>\n 预览\n </NButton>\n <NButton\n size=\"small\"\n @click=\"validateCurrentWorkflow\"\n title=\"检查工作流配置是否正确\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:check-circle\"\n :size=\"16\"\n />\n </template>\n 验证流程\n </NButton>\n <div class=\"toolbar-divider\"></div>\n <NButton\n size=\"small\"\n @click=\"fitView\"\n title=\"适应窗口\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:fit-to-screen\"\n :size=\"16\"\n />\n </template>\n </NButton>\n <NButton\n size=\"small\"\n type=\"error\"\n @click=\"clearWorkflow\"\n title=\"清空画布\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:delete-sweep\"\n :size=\"16\"\n />\n </template>\n </NButton>\n </div>\n\n <!-- Vue Flow 画布 -->\n <VueFlow\n ref=\"vueFlowRef\"\n v-model:nodes=\"nodes\"\n v-model:edges=\"edges\"\n :node-types=\"nodeTypes\"\n class=\"workflow-canvas\"\n :default-viewport=\"{ zoom: 1, x: 0, y: 0 }\"\n :min-zoom=\"0.5\"\n :max-zoom=\"2\"\n :fit-view-on-init=\"true\"\n :nodes-draggable=\"true\"\n :elements-selectable=\"true\"\n @node-click=\"onNodeClick as any\"\n @pane-click=\"closeAddMenu\"\n />\n\n <!-- 节点添加菜单 -->\n <Teleport to=\"body\">\n <div\n v-show=\"showAddMenu\"\n class=\"add-node-menu\"\n :style=\"{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }\"\n >\n <div class=\"add-menu-content\">\n <div\n v-for=\"nodeType in NODE_TYPE_OPTIONS\"\n :key=\"nodeType.type\"\n class=\"add-menu-item\"\n @click=\"addNode(nodeType.type)\"\n >\n <div\n class=\"menu-icon\"\n :class=\"nodeType.iconClass\"\n >\n <C_Icon\n :name=\"nodeType.icon\"\n :size=\"16\"\n />\n </div>\n <span class=\"menu-text\">{{ nodeType.label }}</span>\n </div>\n </div>\n </div>\n </Teleport>\n\n <!-- 节点配置弹窗 - 拆分到独立组件 -->\n <NodeConfigModal\n v-model:show=\"showNodeConfig\"\n :current-node=\"currentNode\"\n :users=\"users\"\n :departments=\"departments\"\n @save=\"handleConfigSave\"\n @cancel=\"showNodeConfig = false\"\n />\n\n <!-- 验证错误日志抽屉 -->\n <NDrawer\n v-model:show=\"showValidationErrors\"\n :width=\"450\"\n placement=\"right\"\n >\n <NDrawerContent\n title=\"流程验证结果\"\n closable\n >\n <div\n v-if=\"validationErrors.length === 0\"\n class=\"validation-success\"\n >\n <div class=\"success-icon\">\n <C_Icon\n name=\"mdi:check-circle\"\n :size=\"32\"\n color=\"#52c41a\"\n />\n </div>\n <h3>验证通过</h3>\n <p>工作流配置正确,所有节点都已正确设置!</p>\n </div>\n\n <div\n v-else\n class=\"validation-errors\"\n >\n <div class=\"error-summary\">\n <div class=\"error-icon\">\n <C_Icon\n name=\"mdi:alert-circle\"\n :size=\"24\"\n color=\"#ff4d4f\"\n />\n </div>\n <h3>发现 {{ validationErrors.length }} 个问题</h3>\n <p>请修复以下问题后重新验证:</p>\n </div>\n\n <div class=\"error-list\">\n <div\n v-for=\"(error, index) in validationErrors\"\n :key=\"error.nodeId\"\n class=\"error-item\"\n >\n <div class=\"error-header\">\n <span class=\"error-number\">{{ index + 1 }}</span>\n <div class=\"error-info\">\n <strong class=\"error-node\">{{ error.nodeName }}</strong>\n <span class=\"error-field\">{{\n getFieldDisplayName(error.field)\n }}</span>\n </div>\n <div\n class=\"error-type\"\n :class=\"error.type\"\n >\n {{ getErrorTypeText(error.type) }}\n </div>\n </div>\n <div class=\"error-message\">{{ error.message }}</div>\n <div class=\"error-actions\">\n <NButton\n size=\"small\"\n type=\"primary\"\n @click=\"jumpToErrorNode(error.nodeId)\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:target\"\n :size=\"16\"\n />\n </template>\n 定位节点\n </NButton>\n </div>\n </div>\n </div>\n\n <div class=\"validation-tips\">\n <h4>💡 常见问题解决方案:</h4>\n <ul>\n <li>\n <strong>审批人未设置:</strong\n >点击审批节点,在弹窗中选择审批人员\n </li>\n <li>\n <strong>条件分支未配置:</strong\n >点击条件节点,添加至少一个条件分支\n </li>\n <li>\n <strong>节点连接断开:</strong> 检查节点之间的连线是否正确\n </li>\n </ul>\n </div>\n </div>\n\n <template #footer>\n <div class=\"validation-footer\">\n <NButton @click=\"showValidationErrors = false\">关闭</NButton>\n <NButton\n type=\"primary\"\n @click=\"validateCurrentWorkflow\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:refresh\"\n :size=\"16\"\n />\n </template>\n 重新验证\n </NButton>\n </div>\n </template>\n </NDrawerContent>\n </NDrawer>\n\n <!-- 流程预览抽屉 -->\n <NDrawer\n v-model:show=\"showPreview\"\n :width=\"520\"\n placement=\"right\"\n >\n <NDrawerContent\n title=\"流程预览\"\n closable\n >\n <!-- 统计概览 -->\n <div class=\"preview-stats\">\n <div class=\"stat-item\">\n <span class=\"stat-value\">{{ previewStats.totalNodes }}</span>\n <span class=\"stat-label\">总节点</span>\n </div>\n <div class=\"stat-item approval\">\n <span class=\"stat-value\">{{ previewStats.approvalNodes }}</span>\n <span class=\"stat-label\">审批</span>\n </div>\n <div class=\"stat-item copy\">\n <span class=\"stat-value\">{{ previewStats.copyNodes }}</span>\n <span class=\"stat-label\">抄送</span>\n </div>\n <div class=\"stat-item condition\">\n <span class=\"stat-value\">{{ previewStats.conditionNodes }}</span>\n <span class=\"stat-label\">条件</span>\n </div>\n <div class=\"stat-item edge\">\n <span class=\"stat-value\">{{ previewStats.totalEdges }}</span>\n <span class=\"stat-label\">连线</span>\n </div>\n </div>\n\n <!-- 流程步骤时间线 -->\n <div class=\"preview-timeline\">\n <div\n v-for=\"(step, index) in previewSteps\"\n :key=\"step.node.id\"\n class=\"preview-step\"\n :class=\"step.colorClass\"\n >\n <!-- 时间线连接线 -->\n <div class=\"step-connector\">\n <div class=\"step-dot\">\n <C_Icon\n :name=\"step.icon\"\n :size=\"16\"\n />\n </div>\n <div\n v-if=\"index < previewSteps.length - 1\"\n class=\"step-line\"\n ></div>\n </div>\n\n <!-- 步骤内容 -->\n <div class=\"step-content\">\n <div class=\"step-header\">\n <span class=\"step-order\">步骤 {{ step.order }}</span>\n <span class=\"step-type-badge\">{{ step.nodeTypeLabel }}</span>\n </div>\n <div class=\"step-title\">{{ step.node.data.title }}</div>\n <div\n v-if=\"step.details.length > 0\"\n class=\"step-details\"\n >\n <div\n v-for=\"(detail, dIdx) in step.details\"\n :key=\"dIdx\"\n class=\"step-detail-item\"\n :class=\"{ 'is-warning': detail.type === 'warning' }\"\n >\n <span class=\"detail-label\">{{ detail.label }}:</span>\n\n <!-- 审批方式 badge -->\n <span\n v-if=\"detail.type === 'mode'\"\n class=\"mode-badge\"\n :class=\"detail.modeKey\"\n >\n {{ detail.value }}\n </span>\n\n <!-- 人员 tags -->\n <template v-else-if=\"detail.type === 'users'\">\n <NTag\n v-for=\"user in detail.users\"\n :key=\"user.name\"\n size=\"small\"\n :bordered=\"false\"\n round\n class=\"user-tag\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:account\"\n :size=\"12\"\n />\n </template>\n {{ user.name }}\n </NTag>\n </template>\n\n <!-- 警告文案 -->\n <span\n v-else-if=\"detail.type === 'warning'\"\n class=\"warning-text\"\n >\n ⚠️ {{ detail.value }}\n </span>\n\n <!-- 普通文本 -->\n <span v-else>{{ detail.value }}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 空状态 -->\n <div\n v-if=\"previewSteps.length === 0\"\n class=\"preview-empty\"\n >\n <C_Icon\n name=\"mdi:file-document-outline\"\n :size=\"48\"\n color=\"#d1d5db\"\n />\n <p>暂无流程节点</p>\n </div>\n\n <template #footer>\n <div class=\"preview-footer\">\n <NButton @click=\"closePreview\">关闭</NButton>\n <NButton\n type=\"primary\"\n @click=\"confirmAndSave\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:content-save\"\n :size=\"16\"\n />\n </template>\n 确认并保存\n </NButton>\n </div>\n </template>\n </NDrawerContent>\n </NDrawer>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { VueFlow } from '@vue-flow/core'\n import { NButton, NDrawer, NDrawerContent, NTag, useMessage } from 'naive-ui'\n import { C_Icon } from '../C_Icon'\n import type { WorkflowProps, WorkflowEmits } from './types'\n import { NODE_TYPE_OPTIONS } from './data'\n import NodeConfigModal from './NodeConfigModal.vue'\n import { useWorkflowNodes } from './composables/useWorkflowNodes'\n import { useWorkflowValidation } from './composables/useWorkflowValidation'\n import { useWorkflowPreview } from './composables/useWorkflowPreview'\n\n defineOptions({ name: 'C_WorkFlow' })\n\n /* Props & Emits */\n const props = withDefaults(defineProps<WorkflowProps>(), {\n users: () => [],\n roles: () => [],\n departments: () => [],\n readonly: false,\n theme: 'light',\n })\n\n const emit = defineEmits<WorkflowEmits>()\n const message = useMessage()\n const vueFlowRef = ref()\n\n /* ─── 节点管理 ──────────────────────────────────────────── */\n const {\n nodes,\n edges,\n showAddMenu,\n menuPosition,\n showNodeConfig,\n currentNode,\n nodeTypes,\n workflowStats,\n addNode,\n onNodeClick,\n closeAddMenu,\n handleConfigSave,\n resetNodes,\n getCurrentWorkflowData,\n fitView,\n deleteNode,\n } = useWorkflowNodes(props, emit as any, vueFlowRef)\n\n /* ─── 流程验证 ──────────────────────────────────────────── */\n const {\n validationErrors,\n showValidationErrors,\n validateWorkflow,\n validateCurrentWorkflow,\n jumpToErrorNode,\n getFieldDisplayName,\n getErrorTypeText,\n resetValidation,\n } = useWorkflowValidation(nodes, edges, vueFlowRef, {\n onShowNodeConfig: node => {\n currentNode.value = node\n showNodeConfig.value = true\n },\n onValidateError: errors => emit('validate-error', errors),\n })\n\n /* ─── 流程预览 ──────────────────────────────────────────── */\n const { showPreview, previewSteps, previewStats, openPreview, closePreview } =\n useWorkflowPreview(nodes, edges)\n\n /* ─── 编排方法(跨 composable 协作) ────────────────────── */\n const saveWorkflow = (): void => {\n const errors = validateWorkflow()\n if (errors.length > 0) {\n message.error(`工作流验证失败: ${errors[0].message}`)\n showValidationErrors.value = true\n return\n }\n const data = getCurrentWorkflowData()\n emit('save', data)\n message.success('工作流保存成功')\n }\n\n const previewWorkflow = (): void => {\n openPreview()\n }\n\n const confirmAndSave = (): void => {\n closePreview()\n saveWorkflow()\n }\n\n const clearWorkflow = (): void => {\n resetNodes()\n resetValidation()\n }\n\n /* ─── 暴露方法 ──────────────────────────────────────────── */\n defineExpose({\n validateWorkflow,\n getCurrentWorkflowData,\n saveWorkflow,\n previewWorkflow,\n deleteNode,\n stats: workflowStats,\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-08-01\n * @Description: 工作流/审批流组件(基于 Vue Flow)\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n\n<template>\n <div class=\"approval-workflow-container\">\n <!-- 浮动工具栏 -->\n <div class=\"floating-toolbar\">\n <NButton\n size=\"small\"\n type=\"primary\"\n @click=\"saveWorkflow\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:content-save\"\n :size=\"16\"\n />\n </template>\n 保存\n </NButton>\n <NButton\n size=\"small\"\n @click=\"previewWorkflow\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:eye\"\n :size=\"16\"\n />\n </template>\n 预览\n </NButton>\n <NButton\n size=\"small\"\n @click=\"validateCurrentWorkflow\"\n title=\"检查工作流配置是否正确\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:check-circle\"\n :size=\"16\"\n />\n </template>\n 验证流程\n </NButton>\n <div class=\"toolbar-divider\"></div>\n <NButton\n size=\"small\"\n @click=\"fitView\"\n title=\"适应窗口\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:fit-to-screen\"\n :size=\"16\"\n />\n </template>\n </NButton>\n <NButton\n size=\"small\"\n type=\"error\"\n @click=\"clearWorkflow\"\n title=\"清空画布\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:delete-sweep\"\n :size=\"16\"\n />\n </template>\n </NButton>\n </div>\n\n <!-- Vue Flow 画布 -->\n <VueFlow\n ref=\"vueFlowRef\"\n v-model:nodes=\"nodes\"\n v-model:edges=\"edges\"\n :node-types=\"nodeTypes\"\n class=\"workflow-canvas\"\n :default-viewport=\"{ zoom: 1, x: 0, y: 0 }\"\n :min-zoom=\"0.5\"\n :max-zoom=\"2\"\n :fit-view-on-init=\"true\"\n :nodes-draggable=\"true\"\n :elements-selectable=\"true\"\n @node-click=\"onNodeClick as any\"\n @pane-click=\"closeAddMenu\"\n />\n\n <!-- 节点添加菜单 -->\n <Teleport to=\"body\">\n <div\n v-show=\"showAddMenu\"\n class=\"add-node-menu\"\n :style=\"{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }\"\n >\n <div class=\"add-menu-content\">\n <div\n v-for=\"nodeType in NODE_TYPE_OPTIONS\"\n :key=\"nodeType.type\"\n class=\"add-menu-item\"\n @click=\"addNode(nodeType.type)\"\n >\n <div\n class=\"menu-icon\"\n :class=\"nodeType.iconClass\"\n >\n <C_Icon\n :name=\"nodeType.icon\"\n :size=\"16\"\n />\n </div>\n <span class=\"menu-text\">{{ nodeType.label }}</span>\n </div>\n </div>\n </div>\n </Teleport>\n\n <!-- 节点配置弹窗 - 拆分到独立组件 -->\n <NodeConfigModal\n v-model:show=\"showNodeConfig\"\n :current-node=\"currentNode\"\n :users=\"users\"\n :departments=\"departments\"\n @save=\"handleConfigSave\"\n @cancel=\"showNodeConfig = false\"\n />\n\n <!-- 验证错误日志抽屉 -->\n <NDrawer\n v-model:show=\"showValidationErrors\"\n :width=\"450\"\n placement=\"right\"\n >\n <NDrawerContent\n title=\"流程验证结果\"\n closable\n >\n <div\n v-if=\"validationErrors.length === 0\"\n class=\"validation-success\"\n >\n <div class=\"success-icon\">\n <C_Icon\n name=\"mdi:check-circle\"\n :size=\"32\"\n color=\"#52c41a\"\n />\n </div>\n <h3>验证通过</h3>\n <p>工作流配置正确,所有节点都已正确设置!</p>\n </div>\n\n <div\n v-else\n class=\"validation-errors\"\n >\n <div class=\"error-summary\">\n <div class=\"error-icon\">\n <C_Icon\n name=\"mdi:alert-circle\"\n :size=\"24\"\n color=\"#ff4d4f\"\n />\n </div>\n <h3>发现 {{ validationErrors.length }} 个问题</h3>\n <p>请修复以下问题后重新验证:</p>\n </div>\n\n <div class=\"error-list\">\n <div\n v-for=\"(error, index) in validationErrors\"\n :key=\"error.nodeId\"\n class=\"error-item\"\n >\n <div class=\"error-header\">\n <span class=\"error-number\">{{ index + 1 }}</span>\n <div class=\"error-info\">\n <strong class=\"error-node\">{{ error.nodeName }}</strong>\n <span class=\"error-field\">{{\n getFieldDisplayName(error.field)\n }}</span>\n </div>\n <div\n class=\"error-type\"\n :class=\"error.type\"\n >\n {{ getErrorTypeText(error.type) }}\n </div>\n </div>\n <div class=\"error-message\">{{ error.message }}</div>\n <div class=\"error-actions\">\n <NButton\n size=\"small\"\n type=\"primary\"\n @click=\"jumpToErrorNode(error.nodeId)\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:target\"\n :size=\"16\"\n />\n </template>\n 定位节点\n </NButton>\n </div>\n </div>\n </div>\n\n <div class=\"validation-tips\">\n <h4>💡 常见问题解决方案:</h4>\n <ul>\n <li>\n <strong>审批人未设置:</strong\n >点击审批节点,在弹窗中选择审批人员\n </li>\n <li>\n <strong>条件分支未配置:</strong\n >点击条件节点,添加至少一个条件分支\n </li>\n <li>\n <strong>节点连接断开:</strong> 检查节点之间的连线是否正确\n </li>\n </ul>\n </div>\n </div>\n\n <template #footer>\n <div class=\"validation-footer\">\n <NButton @click=\"showValidationErrors = false\">关闭</NButton>\n <NButton\n type=\"primary\"\n @click=\"validateCurrentWorkflow\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:refresh\"\n :size=\"16\"\n />\n </template>\n 重新验证\n </NButton>\n </div>\n </template>\n </NDrawerContent>\n </NDrawer>\n\n <!-- 流程预览抽屉 -->\n <NDrawer\n v-model:show=\"showPreview\"\n :width=\"520\"\n placement=\"right\"\n >\n <NDrawerContent\n title=\"流程预览\"\n closable\n >\n <!-- 统计概览 -->\n <div class=\"preview-stats\">\n <div class=\"stat-item\">\n <span class=\"stat-value\">{{ previewStats.totalNodes }}</span>\n <span class=\"stat-label\">总节点</span>\n </div>\n <div class=\"stat-item approval\">\n <span class=\"stat-value\">{{ previewStats.approvalNodes }}</span>\n <span class=\"stat-label\">审批</span>\n </div>\n <div class=\"stat-item copy\">\n <span class=\"stat-value\">{{ previewStats.copyNodes }}</span>\n <span class=\"stat-label\">抄送</span>\n </div>\n <div class=\"stat-item condition\">\n <span class=\"stat-value\">{{ previewStats.conditionNodes }}</span>\n <span class=\"stat-label\">条件</span>\n </div>\n <div class=\"stat-item edge\">\n <span class=\"stat-value\">{{ previewStats.totalEdges }}</span>\n <span class=\"stat-label\">连线</span>\n </div>\n </div>\n\n <!-- 流程步骤时间线 -->\n <div class=\"preview-timeline\">\n <div\n v-for=\"(step, index) in previewSteps\"\n :key=\"step.node.id\"\n class=\"preview-step\"\n :class=\"step.colorClass\"\n >\n <!-- 时间线连接线 -->\n <div class=\"step-connector\">\n <div class=\"step-dot\">\n <C_Icon\n :name=\"step.icon\"\n :size=\"16\"\n />\n </div>\n <div\n v-if=\"index < previewSteps.length - 1\"\n class=\"step-line\"\n ></div>\n </div>\n\n <!-- 步骤内容 -->\n <div class=\"step-content\">\n <div class=\"step-header\">\n <span class=\"step-order\">步骤 {{ step.order }}</span>\n <span class=\"step-type-badge\">{{ step.nodeTypeLabel }}</span>\n </div>\n <div class=\"step-title\">{{ step.node.data.title }}</div>\n <div\n v-if=\"step.details.length > 0\"\n class=\"step-details\"\n >\n <div\n v-for=\"(detail, dIdx) in step.details\"\n :key=\"dIdx\"\n class=\"step-detail-item\"\n :class=\"{ 'is-warning': detail.type === 'warning' }\"\n >\n <span class=\"detail-label\">{{ detail.label }}:</span>\n\n <!-- 审批方式 badge -->\n <span\n v-if=\"detail.type === 'mode'\"\n class=\"mode-badge\"\n :class=\"detail.modeKey\"\n >\n {{ detail.value }}\n </span>\n\n <!-- 人员 tags -->\n <template v-else-if=\"detail.type === 'users'\">\n <NTag\n v-for=\"user in detail.users\"\n :key=\"user.name\"\n size=\"small\"\n :bordered=\"false\"\n round\n class=\"user-tag\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:account\"\n :size=\"12\"\n />\n </template>\n {{ user.name }}\n </NTag>\n </template>\n\n <!-- 警告文案 -->\n <span\n v-else-if=\"detail.type === 'warning'\"\n class=\"warning-text\"\n >\n ⚠️ {{ detail.value }}\n </span>\n\n <!-- 普通文本 -->\n <span v-else>{{ detail.value }}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 空状态 -->\n <div\n v-if=\"previewSteps.length === 0\"\n class=\"preview-empty\"\n >\n <C_Icon\n name=\"mdi:file-document-outline\"\n :size=\"48\"\n color=\"#d1d5db\"\n />\n <p>暂无流程节点</p>\n </div>\n\n <template #footer>\n <div class=\"preview-footer\">\n <NButton @click=\"closePreview\">关闭</NButton>\n <NButton\n type=\"primary\"\n @click=\"confirmAndSave\"\n >\n <template #icon>\n <C_Icon\n name=\"mdi:content-save\"\n :size=\"16\"\n />\n </template>\n 确认并保存\n </NButton>\n </div>\n </template>\n </NDrawerContent>\n </NDrawer>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { VueFlow } from '@vue-flow/core'\n import { NButton, NDrawer, NDrawerContent, NTag, useMessage } from 'naive-ui'\n import { C_Icon } from '../C_Icon'\n import type { WorkflowProps, WorkflowEmits } from './types'\n import { NODE_TYPE_OPTIONS } from './data'\n import NodeConfigModal from './NodeConfigModal.vue'\n import { useWorkflowNodes } from './composables/useWorkflowNodes'\n import { useWorkflowValidation } from './composables/useWorkflowValidation'\n import { useWorkflowPreview } from './composables/useWorkflowPreview'\n\n defineOptions({ name: 'C_WorkFlow' })\n\n /* Props & Emits */\n const props = withDefaults(defineProps<WorkflowProps>(), {\n users: () => [],\n roles: () => [],\n departments: () => [],\n readonly: false,\n theme: 'light',\n })\n\n const emit = defineEmits<WorkflowEmits>()\n const message = useMessage()\n const vueFlowRef = ref()\n\n /* ─── 节点管理 ──────────────────────────────────────────── */\n const {\n nodes,\n edges,\n showAddMenu,\n menuPosition,\n showNodeConfig,\n currentNode,\n nodeTypes,\n workflowStats,\n addNode,\n onNodeClick,\n closeAddMenu,\n handleConfigSave,\n resetNodes,\n getCurrentWorkflowData,\n fitView,\n deleteNode,\n } = useWorkflowNodes(props, emit as any, vueFlowRef)\n\n /* ─── 流程验证 ──────────────────────────────────────────── */\n const {\n validationErrors,\n showValidationErrors,\n validateWorkflow,\n validateCurrentWorkflow,\n jumpToErrorNode,\n getFieldDisplayName,\n getErrorTypeText,\n resetValidation,\n } = useWorkflowValidation(nodes, edges, vueFlowRef, {\n onShowNodeConfig: node => {\n currentNode.value = node\n showNodeConfig.value = true\n },\n onValidateError: errors => emit('validate-error', errors),\n })\n\n /* ─── 流程预览 ──────────────────────────────────────────── */\n const { showPreview, previewSteps, previewStats, openPreview, closePreview } =\n useWorkflowPreview(nodes, edges)\n\n /* ─── 编排方法(跨 composable 协作) ────────────────────── */\n const saveWorkflow = (): void => {\n const errors = validateWorkflow()\n if (errors.length > 0) {\n message.error(`工作流验证失败: ${errors[0].message}`)\n showValidationErrors.value = true\n return\n }\n const data = getCurrentWorkflowData()\n emit('save', data)\n message.success('工作流保存成功')\n }\n\n const previewWorkflow = (): void => {\n openPreview()\n }\n\n const confirmAndSave = (): void => {\n closePreview()\n saveWorkflow()\n }\n\n const clearWorkflow = (): void => {\n resetNodes()\n resetValidation()\n }\n\n /* ─── 暴露方法 ──────────────────────────────────────────── */\n defineExpose({\n validateWorkflow,\n getCurrentWorkflowData,\n saveWorkflow,\n previewWorkflow,\n deleteNode,\n stats: workflowStats,\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
package/dist/_shared.cjs DELETED
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./menuAdapter.js`);exports.createMenuOptions=e.t,exports.flattenRoutes=e.n;
@@ -1,2 +0,0 @@
1
- import { a as RouteItem, i as BreadcrumbItem, n as createMenuOptions, o as RouteMeta, r as flattenRoutes, s as TagItem, t as MenuAdapterConfig } from "./menuAdapter.js";
2
- export { type BreadcrumbItem, type MenuAdapterConfig, type RouteItem, type RouteMeta, type TagItem, createMenuOptions, flattenRoutes };
package/dist/_shared.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { a as RouteItem, i as BreadcrumbItem, n as createMenuOptions, o as RouteMeta, r as flattenRoutes, s as TagItem, t as MenuAdapterConfig } from "./menuAdapter.js";
2
- export { type BreadcrumbItem, type MenuAdapterConfig, type RouteItem, type RouteMeta, type TagItem, createMenuOptions, flattenRoutes };
package/dist/_shared.js DELETED
@@ -1 +0,0 @@
1
- import{n as e,t}from"./menuAdapter.js";export{t as createMenuOptions,e as flattenRoutes};
package/dist/city.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"city.js","names":[],"sources":["../src/components/C_City/city.json"],"sourcesContent":[""],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["hljs","javascript","typescript","json","xml","css","bash","yaml","markdown","java","csharp","go","python","C_Code","C_Icon","C_Upload","C_Barcode","C_Captcha","C_Cascade","C_Guide","C_Progress","C_Steps","C_ActionBar","C_Theme","C_Language","C_Date","C_Editor","C_Markdown","C_Map","C_City","C_VtableGantt","C_Signature","C_SplitPane","C_CollapsePanel","C_Time","C_FormSearch","C_Tree","C_QRCode","C_ImageCropper","C_Draggable","C_WaterFall","C_FullCalendar","C_NotificationCenter","C_Cron","C_FormulaEditor","C_AntV","C_VideoPlayer","C_FilePreview","C_WorkFlow","C_Form","C_Table","C_GlobalSearch","C_Menu","C_Breadcrumb","C_TagsView","C_AudioPlayer","C_AvatarGroup","C_Chat","C_ContextMenu","C_Login","C_Timeline","C_Transfer","C_Skeleton","C_OrgChart"],"sources":["../src/plugins/highlight.ts","../src/index.ts"],"sourcesContent":["\n\nimport type { App } from 'vue'\nimport type { HLJSApi, LanguageFn } from 'highlight.js'\nimport hljs from 'highlight.js/lib/core'\n\n// 导入默认主题样式\nimport 'highlight.js/styles/github.css'\n\n// 默认支持的语言包(常用语言预加载)\nimport javascript from 'highlight.js/lib/languages/javascript'\nimport typescript from 'highlight.js/lib/languages/typescript'\nimport json from 'highlight.js/lib/languages/json'\nimport xml from 'highlight.js/lib/languages/xml' // HTML\nimport css from 'highlight.js/lib/languages/css'\nimport bash from 'highlight.js/lib/languages/bash'\nimport yaml from 'highlight.js/lib/languages/yaml'\nimport markdown from 'highlight.js/lib/languages/markdown'\nimport java from 'highlight.js/lib/languages/java'\nimport csharp from 'highlight.js/lib/languages/csharp'\nimport go from 'highlight.js/lib/languages/go'\nimport python from 'highlight.js/lib/languages/python'\n\n// 可选语言包映射(懒加载)\nconst OPTIONAL_LANGUAGES: Record<\n string,\n () => Promise<{ default: LanguageFn }>\n> = {\n cpp: () => import('highlight.js/lib/languages/cpp'),\n c: () => import('highlight.js/lib/languages/c'),\n php: () => import('highlight.js/lib/languages/php'),\n ruby: () => import('highlight.js/lib/languages/ruby'),\n rust: () => import('highlight.js/lib/languages/rust'),\n swift: () => import('highlight.js/lib/languages/swift'),\n kotlin: () => import('highlight.js/lib/languages/kotlin'),\n scss: () => import('highlight.js/lib/languages/scss'),\n less: () => import('highlight.js/lib/languages/less'),\n sql: () => import('highlight.js/lib/languages/sql'),\n dockerfile: () => import('highlight.js/lib/languages/dockerfile'),\n powershell: () => import('highlight.js/lib/languages/powershell'),\n}\n\n// 插件配置选项\nexport interface HighlightPluginOptions {\n autoDetect?: boolean\n extraLanguages?: string[]\n}\n\n// 状态管理\nconst loadedLanguages = new Set<string>()\nconst hlJsInstance: HLJSApi = hljs\n\n/**\n * * @description 注册语言包到 highlight.js 实例\n * ? @param name - 语言名称\n * ? @param languageFn - 语言定义函数\n * ! @return void\n */\nfunction registerLanguage(name: string, languageFn: LanguageFn): void {\n hlJsInstance.registerLanguage(name, languageFn)\n loadedLanguages.add(name)\n}\n\n/**\n * * @description 初始化核心配置,注册默认语言包\n * ? @param options - 插件配置选项\n * ! @return void\n */\nfunction initializeCore(options: HighlightPluginOptions): void {\n // 注册默认语言包\n const defaultLanguages = [\n ['javascript', javascript],\n ['typescript', typescript],\n ['json', json],\n ['html', xml],\n ['xml', xml],\n ['css', css],\n ['bash', bash],\n ['shell', bash],\n ['yaml', yaml],\n ['yml', yaml],\n ['markdown', markdown],\n ['java', java],\n ['csharp', csharp],\n ['go', go],\n ['python', python],\n ] as const\n\n defaultLanguages.forEach(([name, langFn]) => {\n registerLanguage(name, langFn)\n })\n\n // 配置 highlight.js\n hlJsInstance.configure({\n ignoreUnescapedHTML: true,\n languages: options.autoDetect ? undefined : [],\n })\n\n // 预加载额外语言\n if (options.extraLanguages?.length) {\n loadLanguages(options.extraLanguages)\n }\n}\n\n/**\n * * @description 批量加载多个语言包\n * ? @param languages - 要加载的语言名称数组\n * ! @return Promise<string[]> 成功加载的语言名称数组\n */\nasync function loadLanguages(languages: string[]): Promise<string[]> {\n const loadPromises = languages.map(lang => loadLanguage(lang))\n const results = await Promise.allSettled(loadPromises)\n\n return results\n .map((result, index) =>\n result.status === 'fulfilled' ? languages[index] : null\n )\n .filter(Boolean) as string[]\n}\n\n/**\n * * @description 动态加载单个语言包\n * ? @param language - 要加载的语言名称\n * ! @return Promise<boolean> 是否加载成功\n */\nasync function loadLanguage(language: string): Promise<boolean> {\n if (loadedLanguages.has(language)) {\n return true\n }\n\n const loader = OPTIONAL_LANGUAGES[language.toLowerCase()]\n if (!loader) {\n console.warn(`[HighlightPlugin] Language '${language}' not supported`)\n return false\n }\n\n try {\n const languageModule = await loader()\n registerLanguage(language, languageModule.default)\n console.log(`[HighlightPlugin] Language '${language}' loaded successfully`)\n return true\n } catch (error) {\n console.error(\n `[HighlightPlugin] Failed to load language '${language}':`,\n error\n )\n return false\n }\n}\n\n/**\n * * @description 获取 highlight 功能的 API 接口\n * ! @return highlight API 对象,包含所有可用方法\n */\nexport const useHighlight = () => {\n return {\n // 核心方法\n getHljs: () => hlJsInstance,\n\n // 语言管理\n loadLanguage: (language: string) => loadLanguage(language),\n loadLanguages: (languages: string[]) => loadLanguages(languages),\n getLoadedLanguages: () => Array.from(loadedLanguages),\n }\n}\n\n/**\n * * @description 安装 highlight.js 插件到 Vue 应用\n * ? @param app - Vue 应用实例\n * ? @param options - 插件配置选项\n * ! @return void\n */\nexport function setupHighlight(app: App, options: HighlightPluginOptions = {}) {\n const pluginOptions: HighlightPluginOptions = {\n autoDetect: false,\n extraLanguages: [],\n ...options,\n }\n\n // 初始化核心配置\n initializeCore(pluginOptions)\n\n // 挂载到全局\n if (typeof window !== 'undefined') {\n window.hljs = hlJsInstance\n }\n\n // 提供给 Vue 应用\n const highlightAPI = useHighlight()\n app.provide('highlightManager', highlightAPI)\n app.config.globalProperties.$highlight = highlightAPI\n\n console.log('[HighlightPlugin] 🎨 highlight.js plugin installed successfully')\n}\n\n// 导出默认配置\nexport const defaultHighlightOptions: HighlightPluginOptions = {\n autoDetect: false,\n extraLanguages: [],\n}\n\n// 类型声明\ndeclare global {\n interface Window {\n hljs: HLJSApi\n }\n}\n","import type { App, Component } from 'vue'\n\n// ====== 全局样式由 sass CLI 编译到 dist/style.css,不再通过 JS 导入 ======\n\n// ====== 组件导入(仅用于 install 全量注册) ======\nimport { C_Code } from './components/C_Code'\nimport { C_Icon } from './components/C_Icon'\nimport { C_Upload } from './components/C_Upload'\nimport { C_Barcode } from './components/C_Barcode'\nimport { C_Captcha } from './components/C_Captcha'\nimport { C_Cascade } from './components/C_Cascade'\nimport { C_Guide } from './components/C_Guide'\nimport { C_Progress } from './components/C_Progress'\nimport { C_Steps } from './components/C_Steps'\nimport { C_ActionBar } from './components/C_ActionBar'\nimport { C_Theme } from './components/C_Theme'\nimport { C_Language } from './components/C_Language'\nimport { C_Date } from './components/C_Date'\nimport { C_Editor } from './components/C_Editor'\nimport { C_Markdown } from './components/C_Markdown'\nimport { C_Map } from './components/C_Map'\nimport { C_City } from './components/C_City'\nimport { C_VtableGantt } from './components/C_VtableGantt'\nimport { C_Signature } from './components/C_Signature'\nimport { C_SplitPane } from './components/C_SplitPane'\nimport { C_CollapsePanel } from './components/C_CollapsePanel'\nimport { C_Time } from './components/C_Time'\nimport { C_FormSearch } from './components/C_FormSearch'\nimport { C_Tree } from './components/C_Tree'\nimport { C_QRCode } from './components/C_QRCode'\nimport { C_ImageCropper } from './components/C_ImageCropper'\nimport { C_Draggable } from './components/C_Draggable'\nimport { C_WaterFall } from './components/C_WaterFall'\nimport { C_FullCalendar } from './components/C_FullCalendar'\nimport { C_NotificationCenter } from './components/C_NotificationCenter'\nimport { C_Cron } from './components/C_Cron'\nimport { C_FormulaEditor } from './components/C_FormulaEditor'\nimport { C_AntV } from './components/C_AntV'\nimport { C_VideoPlayer } from './components/C_VideoPlayer'\nimport { C_FilePreview } from './components/C_FilePreview'\nimport { C_WorkFlow } from './components/C_WorkFlow'\nimport { C_Form } from './components/C_Form'\nimport { C_Table } from './components/C_Table'\nimport { C_GlobalSearch } from './components/C_GlobalSearch'\nimport { C_Menu } from './components/C_Menu'\nimport { C_Breadcrumb } from './components/C_Breadcrumb'\nimport { C_TagsView } from './components/C_TagsView'\nimport { C_AudioPlayer } from './components/C_AudioPlayer'\nimport { C_AvatarGroup } from './components/C_AvatarGroup'\nimport { C_Chat } from './components/C_Chat'\nimport { C_ContextMenu } from './components/C_ContextMenu'\nimport { C_Login } from './components/C_Login'\nimport { C_Timeline } from './components/C_Timeline'\nimport { C_Transfer } from './components/C_Transfer'\nimport { C_Skeleton } from './components/C_Skeleton'\nimport { C_OrgChart } from './components/C_OrgChart'\n\n// ====== 插件 ======\nimport { setupHighlight, useHighlight } from './plugins/highlight'\n\n// ====== 安装选项 ======\nexport interface ComponentLibOptions {\n highlight?: import('./plugins/highlight').HighlightPluginOptions\n}\n\n// ====== 组件列表(全量注册使用) ======\nconst components: Component[] = [\n C_Code,\n C_Icon,\n C_Upload,\n C_Barcode,\n C_Captcha,\n C_Cascade,\n C_Guide,\n C_Progress,\n C_Steps,\n C_ActionBar,\n C_Theme,\n C_Language,\n C_Date,\n C_Editor,\n C_Markdown,\n C_Map,\n C_City,\n C_VtableGantt,\n C_Signature,\n C_SplitPane,\n C_CollapsePanel,\n C_Time,\n C_FormSearch,\n C_Tree,\n C_QRCode,\n C_ImageCropper,\n C_Draggable,\n C_WaterFall,\n C_FullCalendar,\n C_NotificationCenter,\n C_Cron,\n C_FormulaEditor,\n C_AntV,\n C_VideoPlayer,\n C_FilePreview,\n C_WorkFlow,\n C_Form,\n C_Table,\n C_GlobalSearch,\n C_Menu,\n C_Breadcrumb,\n C_TagsView,\n C_AudioPlayer,\n C_AvatarGroup,\n C_Chat,\n C_ContextMenu,\n C_Login,\n C_Timeline,\n C_Transfer,\n C_Skeleton,\n C_OrgChart,\n]\n\n// ====== Barrel Re-exports:所有组件 + composables + types + constants ======\nexport * from './components/C_Code'\nexport * from './components/C_Icon'\nexport * from './components/C_Upload'\nexport * from './components/C_Barcode'\nexport * from './components/C_Captcha'\nexport * from './components/C_Cascade'\nexport * from './components/C_Guide'\nexport * from './components/C_Progress'\nexport * from './components/C_Steps'\nexport * from './components/C_ActionBar'\nexport * from './components/C_Theme'\nexport * from './components/C_Language'\nexport * from './components/C_Date'\nexport * from './components/C_Editor'\nexport * from './components/C_Markdown'\nexport * from './components/C_Map'\nexport * from './components/C_City'\nexport * from './components/C_VtableGantt'\nexport * from './components/C_Signature'\nexport * from './components/C_SplitPane'\nexport * from './components/C_CollapsePanel'\nexport * from './components/C_Time'\nexport * from './components/C_FormSearch'\nexport * from './components/C_Tree'\nexport * from './components/C_QRCode'\nexport * from './components/C_ImageCropper'\nexport * from './components/C_Draggable'\nexport * from './components/C_WaterFall'\nexport * from './components/C_FullCalendar'\nexport * from './components/C_NotificationCenter'\nexport * from './components/C_Cron'\nexport * from './components/C_FormulaEditor'\nexport * from './components/C_AntV'\nexport * from './components/C_VideoPlayer'\nexport * from './components/C_FilePreview'\nexport * from './components/C_WorkFlow'\nexport * from './components/C_Form'\nexport * from './components/C_Table'\nexport * from './components/C_GlobalSearch'\nexport * from './components/C_Menu'\nexport * from './components/C_Breadcrumb'\nexport * from './components/C_TagsView'\nexport * from './components/C_AudioPlayer'\nexport * from './components/C_AvatarGroup'\nexport * from './components/C_Chat'\nexport * from './components/C_ContextMenu'\nexport * from './components/C_Login'\nexport * from './components/C_Timeline'\nexport * from './components/C_Transfer'\nexport * from './components/C_Skeleton'\nexport * from './components/C_OrgChart'\n\n// ====== 工具函数(不属于任何组件的公共工具) ======\nexport { setItem, getItem, removeItem, removeAllItem } from './utils/storage'\n\n// ====== 插件导出 ======\nexport { setupHighlight, useHighlight }\nexport type { HighlightPluginOptions } from './plugins/highlight'\n\n// ====== 全量安装 ======\nconst install = (app: App, options: ComponentLibOptions = {}) => {\n components.forEach(component => {\n const name =\n (component as any).__name || (component as any).name || 'UnknownComponent'\n app.component(name, component)\n })\n\n if (options.highlight !== undefined) {\n setupHighlight(app, options.highlight)\n }\n}\n\nexport default {\n install,\n} satisfies { install: (app: App, options?: ComponentLibOptions) => void }\n"],"mappings":"g6EAwBA,MAAM,GAGF,CACF,QAAW,OAAO,kCAClB,MAAS,OAAO,gCAChB,QAAW,OAAO,kCAClB,SAAY,OAAO,mCACnB,SAAY,OAAO,mCACnB,UAAa,OAAO,oCACpB,WAAc,OAAO,qCACrB,SAAY,OAAO,mCACnB,SAAY,OAAO,mCACnB,QAAW,OAAO,kCAClB,eAAkB,OAAO,yCACzB,eAAkB,OAAO,yCAC1B,CASK,EAAkB,IAAI,IACtB,EAAwBA,EAAAA,QAQ9B,SAAS,EAAiB,EAAc,EAA8B,CACpE,EAAa,iBAAiB,EAAM,EAAW,CAC/C,EAAgB,IAAI,EAAK,CAQ3B,SAAS,GAAe,EAAuC,CAEpC,CACvB,CAAC,aAAcC,EAAAA,QAAW,CAC1B,CAAC,aAAcC,EAAAA,QAAW,CAC1B,CAAC,OAAQC,EAAAA,QAAK,CACd,CAAC,OAAQC,EAAAA,QAAI,CACb,CAAC,MAAOA,EAAAA,QAAI,CACZ,CAAC,MAAOC,EAAAA,QAAI,CACZ,CAAC,OAAQC,EAAAA,QAAK,CACd,CAAC,QAASA,EAAAA,QAAK,CACf,CAAC,OAAQC,EAAAA,QAAK,CACd,CAAC,MAAOA,EAAAA,QAAK,CACb,CAAC,WAAYC,EAAAA,QAAS,CACtB,CAAC,OAAQC,EAAAA,QAAK,CACd,CAAC,SAAUC,EAAAA,QAAO,CAClB,CAAC,KAAMC,EAAAA,QAAG,CACV,CAAC,SAAUC,EAAAA,QAAO,CACnB,CAEgB,SAAS,CAAC,EAAM,KAAY,CAC3C,EAAiB,EAAM,EAAO,EAC9B,CAGF,EAAa,UAAU,CACrB,oBAAqB,GACrB,UAAW,EAAQ,WAAa,IAAA,GAAY,EAAE,CAC/C,CAAC,CAGE,EAAQ,gBAAgB,QAC1B,GAAc,EAAQ,eAAe,CASzC,eAAe,GAAc,EAAwC,CACnE,IAAM,EAAe,EAAU,IAAI,GAAQ,GAAa,EAAK,CAAC,CAG9D,OAFgB,MAAM,QAAQ,WAAW,EAAa,EAGnD,KAAK,EAAQ,IACZ,EAAO,SAAW,YAAc,EAAU,GAAS,KACpD,CACA,OAAO,QAAQ,CAQpB,eAAe,GAAa,EAAoC,CAC9D,GAAI,EAAgB,IAAI,EAAS,CAC/B,MAAO,GAGT,IAAM,EAAS,GAAmB,EAAS,aAAa,EACxD,GAAI,CAAC,EAEH,OADA,QAAQ,KAAK,+BAA+B,EAAS,iBAAiB,CAC/D,GAGT,GAAI,CAIF,OAFA,EAAiB,GADM,MAAM,GAAQ,EACK,QAAQ,CAClD,QAAQ,IAAI,+BAA+B,EAAS,uBAAuB,CACpE,SACA,EAAO,CAKd,OAJA,QAAQ,MACN,8CAA8C,EAAS,IACvD,EACD,CACM,IAQX,MAAa,QACJ,CAEL,YAAe,EAGf,aAAe,GAAqB,GAAa,EAAS,CAC1D,cAAgB,GAAwB,GAAc,EAAU,CAChE,uBAA0B,MAAM,KAAK,EAAgB,CACtD,EASH,SAAgB,GAAe,EAAU,EAAkC,EAAE,CAAE,CAQ7E,GAP8C,CAC5C,WAAY,GACZ,eAAgB,EAAE,CAClB,GAAG,EACJ,CAG4B,CAGzB,OAAO,OAAW,MACpB,OAAO,KAAO,GAIhB,IAAM,EAAe,IAAc,CACnC,EAAI,QAAQ,mBAAoB,EAAa,CAC7C,EAAI,OAAO,iBAAiB,WAAa,EAEzC,QAAQ,IAAI,kEAAkE,CC9HhF,MAAM,GAA0B,CAC9BC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,EAAAA,EACAC,GAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,GAAAA,EACAC,GAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACAC,EAAAA,EACD,CA2ED,IAAA,GAAe,CACb,SAbe,EAAU,EAA+B,EAAE,GAAK,CAC/D,GAAW,QAAQ,GAAa,CAC9B,IAAM,EACH,EAAkB,QAAW,EAAkB,MAAQ,mBAC1D,EAAI,UAAU,EAAM,EAAU,EAC9B,CAEE,EAAQ,YAAc,IAAA,IACxB,GAAe,EAAK,EAAQ,UAAU,EAMzC"}