vue2-client 1.17.38 → 1.17.40

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 (344) hide show
  1. package/.claude/settings.local.json +20 -20
  2. package/.cursorrules +19 -19
  3. package/.env +20 -20
  4. package/.env.apply +19 -19
  5. package/.env.gaslink +19 -19
  6. package/.env.his +19 -19
  7. package/.env.liuli +20 -20
  8. package/.env.scada +19 -19
  9. package/.eslintrc.js +90 -90
  10. package/.serena/memories/code_style_conventions.md +217 -217
  11. package/.serena/memories/project_overview.md +54 -54
  12. package/.serena/memories/project_structure.md +329 -329
  13. package/.serena/memories/suggested_commands.md +127 -127
  14. package/.serena/memories/task_completion_checklist.md +183 -183
  15. package/.serena/memories/tech_stack.md +94 -94
  16. package/CHANGELOG.md +830 -830
  17. package/CLAUDE.md +97 -97
  18. package/Components.md +60 -60
  19. package/docs/LowCode/lowcode.md +155 -155
  20. package/docs/LowCode/lowcodeForDeveloper.md +230 -230
  21. package/docs/index.md +30 -30
  22. package/index.js +31 -31
  23. package/jest-transform-stub.js +8 -8
  24. package/jest.setup.js +7 -7
  25. package/jsconfig.json +19 -19
  26. package/package.json +1 -1
  27. package/public/his/editor/editor.html +51 -51
  28. package/public/his/editor/mock/bind_data.html +779 -779
  29. package/public/his/editor/mock/data_table.html +40 -40
  30. package/public/his/editor/mock/sign.html +75 -75
  31. package/public/his/editor/vender/JsBarcode.all.js +3669 -3669
  32. package/public/his/editor/vender/date97/My97DatePicker.htm +65 -65
  33. package/public/his/editor/vender/date97/WdatePicker.js +677 -677
  34. package/public/his/editor/vender/date97/calendar.js +4 -4
  35. package/public/his/editor/vender/date97/lang/en.js +13 -13
  36. package/public/his/editor/vender/date97/lang/zh-cn.js +13 -13
  37. package/public/his/editor/vender/date97/lang/zh-tw.js +13 -13
  38. package/public/his/editor/vender/date97/skin/WdatePicker.css +10 -10
  39. package/public/his/editor/vender/date97/skin/default/datepicker.css +328 -328
  40. package/public/his/editor/vender/date97/skin/ext/datepicker.css +308 -308
  41. package/public/his/editor/vender/date97/skin/whyGreen/datepicker.css +255 -255
  42. package/public/his/editor/vender/diff.js +1627 -1627
  43. package/public/his/editor/vender/editor.js +1 -1
  44. package/public/his/editor/vender/fabric.js +31187 -31187
  45. package/public/his/editor/vender/jquery/jquery.base64.js +190 -190
  46. package/public/his/editor/vender/jquery/jquery.js +10872 -10872
  47. package/public/his/editor/vender/jquery/jquery.print.js +255 -255
  48. package/public/his/editor/vender/jquery/zTreeStyle/zTreeStyle.css +96 -96
  49. package/public/his/editor/vender/mui/mui.min.css +4 -4
  50. package/public/his/editor/vender/mui/mui.min.js +5 -5
  51. package/public/his/editor/vender/mui/mui.picker.min.css +6 -6
  52. package/public/his/editor/vender/mui/mui.picker.min.js +6 -6
  53. package/public/his/editor/vender/qrcode.js +7 -7
  54. package/public/his/editor/vender/requirejs/require.js +2145 -2145
  55. package/public/his/editor/vender/signature/jSignature.CompressorSVG.js +518 -518
  56. package/public/his/editor/vender/signature/jSignature.UndoButton.js +164 -164
  57. package/public/his/editor/vender/signature/jSignature.js +1486 -1486
  58. package/public/his/editor/vender/validator.js +5094 -5094
  59. package/public/his/editor/vender/weui/weui.css +5659 -5659
  60. package/public/his/editor/vender/weui/weui.min.css +4 -4
  61. package/public/his/editor/vender/weui/weui.min.js +11 -11
  62. package/src/assets/img/paymentMethod/package.info +1 -1
  63. package/src/assets/img/querySlotDemo.svg +15 -15
  64. package/src/assets/svg/badtwo.svg +1 -1
  65. package/src/assets/svg/goodtwo.svg +1 -1
  66. package/src/base-client/components/AI/AskAiBtn.vue +136 -136
  67. package/src/base-client/components/AI/demo.vue +31 -31
  68. package/src/base-client/components/common/AddressSearchCombobox/IcMapIcon.vue +16 -16
  69. package/src/base-client/components/common/AddressSearchCombobox/demo.vue +36 -36
  70. package/src/base-client/components/common/AddressSearchCombobox/ic_map.svg +6 -6
  71. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  72. package/src/base-client/components/common/CitySelect/index.js +3 -3
  73. package/src/base-client/components/common/CitySelect/index.md +109 -109
  74. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  75. package/src/base-client/components/common/CreateQuery/CreateQueryItem.vue +1014 -1014
  76. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  77. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  78. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQuery.vue +452 -452
  79. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQueryItem.vue +511 -511
  80. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  81. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  82. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  83. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  84. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  85. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  86. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  87. package/src/base-client/components/common/HIS/demo.vue +61 -61
  88. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  89. package/src/base-client/components/common/LowCodeComponent/LowCodeEditorModal.vue +108 -108
  90. package/src/base-client/components/common/LowCodeComponent/LowCodeEditorPanel.vue +413 -413
  91. package/src/base-client/components/common/LowCodeComponent/LowCodePageOrganization.vue +502 -502
  92. package/src/base-client/components/common/LowCodeComponent/LowCodeRender.vue +728 -728
  93. package/src/base-client/components/common/LowCodeComponent/LowCodeRenderEnter.vue +29 -29
  94. package/src/base-client/components/common/LowCodeComponent/LowCodeUIStore.vue +219 -219
  95. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeAddPageModal.vue +117 -117
  96. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeCustomJSModal.vue +80 -80
  97. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeEventEditorModal.vue +398 -398
  98. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLifeCycleModal.vue +65 -65
  99. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLogicCallbackModal.vue +64 -64
  100. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLogicParamModal.vue +73 -73
  101. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeRunFunctionParamModal.vue +76 -76
  102. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  103. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  104. package/src/base-client/components/common/Recording/Recording.vue +243 -243
  105. package/src/base-client/components/common/Recording/index.js +3 -3
  106. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  107. package/src/base-client/components/common/Tree/index.js +2 -2
  108. package/src/base-client/components/common/Upload/index.js +3 -3
  109. package/src/base-client/components/common/XAddForm/XAddForm.vue +113 -113
  110. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  111. package/src/base-client/components/common/XAddNativeFormOA/XAddNativeFormOA.vue +304 -304
  112. package/src/base-client/components/common/XAddNativeFormOA/index.js +3 -3
  113. package/src/base-client/components/common/XAddNativeFormOA/index.md +146 -146
  114. package/src/base-client/components/common/XAddReport/index.js +3 -3
  115. package/src/base-client/components/common/XAddReport/index.md +56 -56
  116. package/src/base-client/components/common/XBadge/XBadge.vue +94 -94
  117. package/src/base-client/components/common/XButtons/XButtonDemo.vue +28 -28
  118. package/src/base-client/components/common/XButtons/index.js +3 -3
  119. package/src/base-client/components/common/XButtons/index.md +61 -61
  120. package/src/base-client/components/common/XCalendar/XCalendar.vue +4 -4
  121. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  122. package/src/base-client/components/common/XCheckList/XCheckList.vue +106 -106
  123. package/src/base-client/components/common/XCheckList/XCheckListDemo.vue +41 -41
  124. package/src/base-client/components/common/XCollapse/XCollapse.vue +20 -23
  125. package/src/base-client/components/common/XDataCard/index.js +3 -3
  126. package/src/base-client/components/common/XDataCard/index.md +1 -1
  127. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  128. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  129. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  130. package/src/base-client/components/common/XDatePicker/demo.vue +153 -153
  131. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  132. package/src/base-client/components/common/XDescriptions/index.md +83 -83
  133. package/src/base-client/components/common/XDetailsView/XDetailsView.vue +238 -238
  134. package/src/base-client/components/common/XDetailsView/index.js +3 -3
  135. package/src/base-client/components/common/XForm/XFormItem.vue +12 -4
  136. package/src/base-client/components/common/XForm/XStatusButton.vue +54 -54
  137. package/src/base-client/components/common/XForm/XTreeSelect.vue +276 -276
  138. package/src/base-client/components/common/XForm/index.md +178 -178
  139. package/src/base-client/components/common/XForm/itemComponent/XClickChangeBtn/index.vue +49 -49
  140. package/src/base-client/components/common/XFormGroup/index.js +3 -3
  141. package/src/base-client/components/common/XFormGroup/index.md +38 -38
  142. package/src/base-client/components/common/XFormGroupDetails/index.js +3 -3
  143. package/src/base-client/components/common/XFormTable/XFormTable.vue +1093 -980
  144. package/src/base-client/components/common/XFormTable/demo.vue +34 -10
  145. package/src/base-client/components/common/XFormTable/index.md +92 -92
  146. package/src/base-client/components/common/XLabelSelect/XLabelSelect.vue +110 -110
  147. package/src/base-client/components/common/XLabelSelect/XLabelSelectDemo.vue +35 -35
  148. package/src/base-client/components/common/XLicensePlate/XLicensePlate.vue +193 -193
  149. package/src/base-client/components/common/XLicensePlate/XLicensePlateDemo.vue +48 -48
  150. package/src/base-client/components/common/XPrint/OpenInvoice.vue +21 -21
  151. package/src/base-client/components/common/XPrint/PrintHtml.js +98 -98
  152. package/src/base-client/components/common/XPrint/css/hiPrintCss.js +359 -359
  153. package/src/base-client/components/common/XPrint/css/lodopCss.js +26 -26
  154. package/src/base-client/components/common/XPrint/css/print-lock.css +351 -351
  155. package/src/base-client/components/common/XPrint/index.vue +97 -97
  156. package/src/base-client/components/common/XReport/XReportDesign.vue +463 -463
  157. package/src/base-client/components/common/XReport/XReportJsonRender.vue +381 -381
  158. package/src/base-client/components/common/XReport/index.js +3 -3
  159. package/src/base-client/components/common/XReport/print.js +186 -186
  160. package/src/base-client/components/common/XReportDrawer/index.js +3 -3
  161. package/src/base-client/components/common/XReportGrid/index.js +3 -3
  162. package/src/base-client/components/common/XReportGrid/index.md +44 -44
  163. package/src/base-client/components/common/XReportSlot/XReportSlot.vue +110 -110
  164. package/src/base-client/components/common/XReportSlot/index.js +3 -3
  165. package/src/base-client/components/common/XReportSlot/index.md +48 -48
  166. package/src/base-client/components/common/XSimpleDescriptions/XSimpleDescriptions.vue +166 -166
  167. package/src/base-client/components/common/XSimpleDescriptions/index.js +3 -3
  168. package/src/base-client/components/common/XSimpleDescriptions/index.md +7 -7
  169. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  170. package/src/base-client/components/common/XStepView/index.js +3 -3
  171. package/src/base-client/components/common/XStepView/index.md +31 -31
  172. package/src/base-client/components/common/XTab/XTabDemo.vue +22 -22
  173. package/src/base-client/components/common/XTab/index.js +3 -3
  174. package/src/base-client/components/common/XTable/CustomFuncCel.vue +51 -51
  175. package/src/base-client/components/common/XTable/TableCellRenderer.vue +161 -161
  176. package/src/base-client/components/common/XTable/XTable.vue +46 -0
  177. package/src/base-client/components/common/XTable/XTableWrapper.vue +706 -674
  178. package/src/base-client/components/common/XTable/index.md +255 -255
  179. package/src/base-client/components/common/XTagGroup/index.vue +52 -52
  180. package/src/base-client/components/common/XTree/XTree.vue +424 -424
  181. package/src/base-client/components/common/XTree/index.js +3 -3
  182. package/src/base-client/components/common/XTree/index.md +36 -36
  183. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +113 -113
  184. package/src/base-client/components/common/XTreeOne/XTreeOnePro.vue +128 -128
  185. package/src/base-client/components/common/richTextModal/index.vue +56 -56
  186. package/src/base-client/components/common/richTextModal/richDemo.vue +48 -48
  187. package/src/base-client/components/his/XCharge/XChargeDemo.vue +145 -145
  188. package/src/base-client/components/his/XHDescriptions/XHDescriptions.vue +41 -2
  189. package/src/base-client/components/his/XHisEditor/index.js +3 -3
  190. package/src/base-client/components/index.js +51 -51
  191. package/src/base-client/components/layout/XTreeView/XTreeView.vue +130 -130
  192. package/src/base-client/components/layout/XTreeView/index.js +3 -3
  193. package/src/base-client/components/layout/XTreeView/index.md +46 -46
  194. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  195. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +281 -281
  196. package/src/base-client/plugins/Config.js +19 -19
  197. package/src/base-client/plugins/GetLoginInfoService.js +183 -183
  198. package/src/base-client/plugins/Recording.js +258 -258
  199. package/src/base-client/plugins/index.js +23 -23
  200. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  201. package/src/components/Charts/Bar.vue +62 -62
  202. package/src/components/Charts/ChartCard.vue +134 -134
  203. package/src/components/Charts/Liquid.vue +67 -67
  204. package/src/components/Charts/MiniArea.vue +39 -39
  205. package/src/components/Charts/MiniBar.vue +39 -39
  206. package/src/components/Charts/MiniProgress.vue +75 -75
  207. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  208. package/src/components/Charts/Radar.vue +68 -68
  209. package/src/components/Charts/RankList.vue +77 -77
  210. package/src/components/Charts/TagCloud.vue +113 -113
  211. package/src/components/Charts/TransferBar.vue +64 -64
  212. package/src/components/Charts/Trend.vue +82 -82
  213. package/src/components/Charts/chart.less +12 -12
  214. package/src/components/Charts/smooth.area.less +13 -13
  215. package/src/components/CodeMirror/inedx.vue +118 -118
  216. package/src/components/CodeMirror/setting.js +40 -40
  217. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  218. package/src/components/NumberInfo/index.js +3 -3
  219. package/src/components/NumberInfo/index.less +54 -54
  220. package/src/components/NumberInfo/index.md +43 -43
  221. package/src/components/STable/index.js +771 -732
  222. package/src/components/card/ChartCard.vue +79 -79
  223. package/src/components/chart/Bar.vue +60 -60
  224. package/src/components/chart/MiniArea.vue +67 -67
  225. package/src/components/chart/MiniBar.vue +59 -59
  226. package/src/components/chart/MiniProgress.vue +57 -57
  227. package/src/components/chart/Radar.vue +80 -80
  228. package/src/components/chart/RankingList.vue +60 -60
  229. package/src/components/chart/Trend.vue +79 -79
  230. package/src/components/chart/index.less +9 -9
  231. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  232. package/src/components/checkbox/ImgCheckbox.vue +117 -117
  233. package/src/components/checkbox/ImgCheckboxGroup.vue +76 -76
  234. package/src/components/checkbox/index.js +9 -9
  235. package/src/components/exception/ExceptionPage.vue +70 -70
  236. package/src/components/g2Charts/constants.js +202 -202
  237. package/src/components/g2Charts/demo.vue +808 -808
  238. package/src/components/g2Charts/designer.vue +228 -228
  239. package/src/components/g2Charts/designerBaseConfig.vue +61 -61
  240. package/src/components/g2Charts/designerDataConfig.vue +259 -259
  241. package/src/components/g2Charts/designerStyleConfig.vue +16 -16
  242. package/src/components/g2Charts/index.vue +397 -397
  243. package/src/components/index.js +36 -36
  244. package/src/components/input/IInput.vue +66 -66
  245. package/src/components/menu/SideMenu.vue +75 -75
  246. package/src/components/menu/menu.js +273 -273
  247. package/src/components/setting/Setting.vue +234 -234
  248. package/src/components/tool/AStepItem.vue +60 -60
  249. package/src/config/CreateQueryConfig.js +325 -325
  250. package/src/config/default/antd.config.js +89 -89
  251. package/src/config/default/setting.config.js +55 -55
  252. package/src/font-style/font.css +60 -60
  253. package/src/layouts/CommonLayout.vue +56 -56
  254. package/src/layouts/PageLayout.vue +151 -151
  255. package/src/layouts/SinglePageView.vue +136 -136
  256. package/src/layouts/header/AdminHeader.vue +132 -132
  257. package/src/layouts/header/HeaderNotice.vue +177 -177
  258. package/src/layouts/header/InstitutionDetail.vue +181 -181
  259. package/src/layouts/tabs/TabsHead.vue +189 -189
  260. package/src/lib.js +1 -1
  261. package/src/mock/extend/index.js +84 -84
  262. package/src/mock/goods/index.js +108 -108
  263. package/src/pages/DefaultExample/index.vue +77 -77
  264. package/src/pages/DynamicStatistics/ChartSelector.vue +331 -331
  265. package/src/pages/DynamicStatistics/DataTabs.vue +83 -83
  266. package/src/pages/DynamicStatistics/DynamicTable.vue +128 -128
  267. package/src/pages/DynamicStatistics/EvaluationArea.vue +69 -69
  268. package/src/pages/DynamicStatistics/FavoriteList.vue +50 -50
  269. package/src/pages/DynamicStatistics/QuestionHistoryAndFavorites.vue +591 -591
  270. package/src/pages/DynamicStatistics/SearchBar.vue +192 -192
  271. package/src/pages/DynamicStatistics/index.vue +282 -282
  272. package/src/pages/Example/childIndex.vue +15 -15
  273. package/src/pages/Example/index.vue +30 -30
  274. package/src/pages/NewDynamicStatistics/ChartSelector.vue +331 -331
  275. package/src/pages/NewDynamicStatistics/DataTabs.vue +122 -122
  276. package/src/pages/NewDynamicStatistics/DynamicTable.vue +128 -128
  277. package/src/pages/NewDynamicStatistics/EvaluationArea.vue +69 -69
  278. package/src/pages/NewDynamicStatistics/FavoriteList.vue +50 -50
  279. package/src/pages/NewDynamicStatistics/QuestionHistoryAndFavorites.vue +289 -289
  280. package/src/pages/NewDynamicStatistics/SearchBar.vue +193 -193
  281. package/src/pages/NewDynamicStatistics/index.vue +258 -258
  282. package/src/pages/Recording/index.vue +77 -77
  283. package/src/pages/ServiceReview/index.vue +284 -284
  284. package/src/pages/SubExample/index.vue +26 -26
  285. package/src/pages/WorkflowDetail/WorkflowPageDetail/TrimTextTail.vue +23 -23
  286. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowBaseInformation.vue +417 -417
  287. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue +1815 -1815
  288. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowTimeline.vue +1014 -1014
  289. package/src/pages/XReportView/index.vue +64 -64
  290. package/src/pages/XTreeOneProExample/index.vue +67 -67
  291. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  292. package/src/pages/login/Login.vue +379 -379
  293. package/src/pages/login/LoginV3.vue +389 -389
  294. package/src/pages/lowCode/lowCodeEditor.vue +1219 -1219
  295. package/src/pages/lowCode/lowCodeRenderPage.vue +43 -43
  296. package/src/pages/report/ReportTable.js +124 -124
  297. package/src/pages/resourceManage/orgListManage.vue +98 -98
  298. package/src/pages/system/dictionary/index.vue +44 -44
  299. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  300. package/src/pages/system/monitor/operLog/index.vue +37 -37
  301. package/src/pages/system/settings/modifyPassword.vue +117 -117
  302. package/src/pages/system/ticket/index.vue +480 -480
  303. package/src/pages/system/ticket/submitTicketSuccess.vue +484 -484
  304. package/src/pages/userInfoDetailManage/ChangeMeterRecordQuery/index.vue +64 -64
  305. package/src/pages/userInfoDetailManage/InfoChangeRecordQuery/index.vue +64 -64
  306. package/src/pages/userInfoDetailManage/InstructRecordQuery/index.vue +64 -64
  307. package/src/pages/userInfoDetailManage/MeterParamRecordQuery/index.vue +64 -64
  308. package/src/pages/userInfoDetailManage/TransferRecordQuery/index.vue +66 -66
  309. package/src/pages/userInfoDetailManage/WatchCollectionRecordQuery/index.vue +64 -64
  310. package/src/plugins/EventLogPlugin.js +33 -33
  311. package/src/plugins/FindParentsData.js +17 -17
  312. package/src/router/async/router.map.js +2 -2
  313. package/src/services/DataModel.js +30 -30
  314. package/src/services/LodopFuncs.js +137 -137
  315. package/src/services/api/TicketDetailsViewApi.js +46 -46
  316. package/src/services/api/cas.js +79 -79
  317. package/src/services/api/entity.js +18 -18
  318. package/src/services/api/index.js +17 -17
  319. package/src/services/api/restTools.js +215 -215
  320. package/src/store/modules/account.js +121 -121
  321. package/src/store/modules/index.js +5 -5
  322. package/src/store/modules/lowCode.js +33 -33
  323. package/src/store/modules/setting.js +119 -119
  324. package/src/theme/default/style.less +58 -58
  325. package/src/theme/global.less +311 -279
  326. package/src/utils/authority-utils.js +85 -85
  327. package/src/utils/errorCode.js +6 -6
  328. package/src/utils/formatter.js +74 -74
  329. package/src/utils/htmlToPDF.js +108 -108
  330. package/src/utils/htmlToPDFApi.js +5 -5
  331. package/src/utils/login.js +188 -188
  332. package/src/utils/lowcode/lowcodeComponentMixin.js +120 -120
  333. package/src/utils/lowcode/lowcodeLog.js +29 -29
  334. package/src/utils/lowcode/lowcodeUtils.js +373 -373
  335. package/src/utils/lowcode/registerComponentForEditor.js +1 -1
  336. package/src/utils/lowcode/registerComponentForRender.js +11 -11
  337. package/src/utils/map-utils.js +47 -47
  338. package/src/utils/reg.js +95 -95
  339. package/src/utils/runEvalFunction.js +14 -14
  340. package/src/utils/theme-color-replacer-extend.js +92 -92
  341. package/src/utils/util.js +329 -329
  342. package/src/utils/waterMark.js +31 -31
  343. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
  344. package/src/pages/WorkflowDetail/WorkFlowDemo4.vue +0 -127
@@ -1,1014 +1,1014 @@
1
- <template>
2
- <div class="timeline-wrapper" ref="timelineContent" style="white-space: nowrap; overflow-x: auto">
3
- <!-- 动态渲染时间轴段落 -->
4
- <template v-for="(segment, segmentIndex) in getTimelineSegments()">
5
-
6
- <!-- 主时间轴段落 -->
7
- <div v-if="segment.type === 'main'" :key="'main-' + segmentIndex" class="timeline-section main-timeline">
8
- <a-steps
9
- :current="getCurrentStepInSection(segment.steps)"
10
- :initial="1">
11
- <template #progressDot="{index}">
12
- <span v-if="!changeAble" class="step-label in-steps"><i :class="['step-icon', getStepIconColor(index, segment.steps)]"></i></span>
13
- <span v-else :key="displayStepId" :class="['step-icon-filled digital', getStepIconColor(index, segment.steps)]">
14
- <a-icon v-if="isStepCompleted(segment.steps[index - 1])" type="check-circle" theme="filled" :class="['step-icon-filled icon', getStepIconColor(index, segment.steps)]" />
15
- <span v-else :class="['step-icon-filled digital', getStepIconColor(index, segment.steps)]">{{ getStepNumber(segmentIndex, index) }}</span>
16
- </span>
17
- </template>
18
-
19
- <a-step
20
- v-for="item in segment.steps"
21
- :key="item.id"
22
- class="step-item"
23
- @click.stop="onStepClick(item.id)"
24
- @contextmenu.native="handleStepContextMenu($event, item)"
25
- >
26
- <template #title>
27
- <div class="step-title-container">
28
- <h3 v-if="item.name">{{ item.name }}</h3>
29
- <!-- 判断节点特殊标识 -->
30
- <div v-if="item.type === 'condition'" class="condition-indicators">
31
- <a-tag color="purple" size="small" class="condition-tag">
32
- <a-icon type="branches" /> 条件节点
33
- </a-tag>
34
- <a-tooltip title="此节点根据条件判断后续流程走向">
35
- <a-icon type="question-circle" class="condition-help" />
36
- </a-tooltip>
37
- </div>
38
- </div>
39
- </template>
40
- <template #description>
41
- <div>
42
- <p v-if="item.handler">负责人:
43
- <trim-text-tail :text="item.handler" :length="14"/>
44
- </p>
45
- <p v-if="item.date">填报时间:{{ formatDate(item.date, 'yyyy-MM-dd hh:mm') }}</p>
46
- <p v-if="item.deadline" :class="{ 'text-red': item.id === currentStepId && isOverdue }">截止时间:{{ formatDate(item.deadline, 'yyyy-MM-dd hh:mm') }}</p>
47
- <p v-if="item.note">备注:
48
- <trim-text-tail :text="item.note" :length="15"/>
49
- </p>
50
- </div>
51
- </template>
52
- </a-step>
53
- </a-steps>
54
- </div>
55
-
56
- <!-- 并行分支段落 -->
57
- <div v-else-if="segment.type === 'parallel'" :key="'parallel-' + segmentIndex" class="parallel-section">
58
- <!-- 左侧连接线 -->
59
- <div class="connection-line left-line"></div>
60
-
61
- <!-- 并行分支容器 -->
62
- <div class="parallel-branches-container">
63
- <!-- 并行节点标题 -->
64
- <a-tag color="cyan" size="small" class="parallel-node-title">
65
- <a-icon type="apartment" /> 并行节点
66
- </a-tag>
67
- <!-- 动态渲染并行分支 -->
68
- <div
69
- v-for="(branch, branchIndex) in segment.branches"
70
- :key="'segment-' + segmentIndex + '-branch-' + branchIndex"
71
- class="branch-row">
72
- <!-- 分支时间轴 -->
73
- <div :class="['branch-timeline', getBranchTimelineClass(branch)]">
74
- <a-steps size="small" class="branch-steps">
75
- <a-step
76
- v-for="step in branch"
77
- :key="'segment-' + segmentIndex + '-branch-' + branchIndex + '-step-' + step.id"
78
- :class="['branch-step', getBranchStepClass(step)]"
79
- @click.stop="onStepClick(step.id)"
80
- @contextmenu.native="handleStepContextMenu($event, step)">
81
- <template #title>
82
- <a-popover placement="top" trigger="hover" :mouseEnterDelay="0.3">
83
- <template #content>
84
- <div class="branch-step-popover">
85
- <div class="popover-item" v-if="step.handler">
86
- <span class="popover-label">负责人:</span>
87
- <span class="popover-value">{{ step.handler }}</span>
88
- </div>
89
- <div class="popover-item" v-if="step.date">
90
- <span class="popover-label">填报时间:</span>
91
- <span class="popover-value">{{ formatDate(step.date, 'yyyy-MM-dd hh:mm') }}</span>
92
- </div>
93
- <div class="popover-item" v-if="step.deadline">
94
- <span class="popover-label">截止时间:</span>
95
- <span class="popover-value">{{ formatDate(step.deadline, 'yyyy-MM-dd hh:mm') }}</span>
96
- </div>
97
- <div class="popover-item" v-if="step.note">
98
- <span class="popover-label">备注:</span>
99
- <span class="popover-value">{{ step.note }}</span>
100
- </div>
101
- <div class="popover-item no-data" v-if="!step.handler && !step.date && !step.deadline && !step.note">
102
- <span class="popover-value">暂无详细信息</span>
103
- </div>
104
- </div>
105
- </template>
106
- <template #title>
107
- <div class="popover-title">
108
- <a-icon type="info-circle" />
109
- {{ step.name }} - 详细信息
110
- </div>
111
- </template>
112
- <span class="branch-step-title">{{ step.name }}</span>
113
- </a-popover>
114
- </template>
115
- <template #description>
116
- <div class="branch-step-desc">
117
- <p v-if="step.handler">{{ step.handler }}</p>
118
- <p v-if="step.date">{{ formatDate(step.date, 'MM-dd hh:mm') }}</p>
119
- </div>
120
- </template>
121
- </a-step>
122
- </a-steps>
123
- </div>
124
- </div>
125
- </div>
126
-
127
- <!-- 右侧连接线 -->
128
- <div class="connection-line right-line"></div>
129
- </div>
130
-
131
- </template>
132
-
133
- <!-- 当步骤被截断时显示省略状态 -->
134
- <div class="more-steps-section" v-if="hasMoreSteps">
135
- <a-steps :current="1" :initial="1">
136
- <a-step
137
- key="more-steps"
138
- class="step-item more-steps-item">
139
- <template #title>
140
- <div class="more-steps-container">
141
- <h3>后续步骤</h3>
142
- </div>
143
- </template>
144
- <template #description>
145
- <div class="more-steps-desc">
146
- <p>根据上述条件判断结果,将继续执行相应的后续流程</p>
147
- <p class="steps-count">剩余 {{ remainingStepsCount }} 个步骤</p>
148
- </div>
149
- </template>
150
- <template #icon>
151
- <a-icon type="more" style="color: #d9d9d9;" />
152
- </template>
153
- </a-step>
154
- </a-steps>
155
- </div>
156
-
157
- <!-- 右键菜单 -->
158
- <a-dropdown
159
- :visible="contextMenuVisible"
160
- :trigger="[]"
161
- @visibleChange="val => !val && closeContextMenu()"
162
- >
163
- <div
164
- v-show="contextMenuVisible"
165
- :style="{
166
- position: 'fixed',
167
- left: contextMenuX + 'px',
168
- top: contextMenuY + 'px',
169
- width: '1px',
170
- height: '1px'
171
- }"
172
- ></div>
173
- <a-menu slot="overlay" @click="closeContextMenu">
174
- <a-menu-item @click="reloadFromStep">
175
- <a-icon type="reload" />
176
- 从此节点重新加载
177
- </a-menu-item>
178
- </a-menu>
179
- </a-dropdown>
180
- </div>
181
- </template>
182
-
183
- <script>
184
- import { mapGetters } from 'vuex'
185
- import { formatDate } from '@vue2-client/utils/util'
186
- import TrimTextTail from '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/TrimTextTail'
187
-
188
- export default {
189
- components: { TrimTextTail },
190
- name: 'WorkFlowTimeline',
191
- props: {
192
- workflowId: {
193
- type: [String, Number],
194
- required: true
195
- },
196
- currentStepId: {
197
- type: Number,
198
- required: false,
199
- default: 1
200
- },
201
- activeStepId: {
202
- type: Number,
203
- required: false,
204
- default: 1
205
- },
206
- state: {
207
- type: [Boolean, Number],
208
- required: false,
209
- default: false
210
- },
211
- steps: {
212
- type: Array,
213
- required: true
214
- },
215
- changeAble: {
216
- type: Boolean,
217
- required: false,
218
- default: false
219
- }
220
- },
221
- data () {
222
- return {
223
- // 当前显示的步骤 id
224
- displayStepId: 1,
225
- // 当前任务是否逾期
226
- isOverdue: false,
227
- // 是否显示所有步骤(包括条件后的步骤)
228
- showAllSteps: false,
229
- // 右键菜单相关状态
230
- contextMenuVisible: false,
231
- contextMenuX: 0,
232
- contextMenuY: 0,
233
- selectedStepForMenu: null
234
- }
235
- },
236
- computed: {
237
- ...mapGetters('account', ['isDebugUser']),
238
- // 控制显示的步骤:当判断节点未执行时只显示到判断节点,执行后显示包括并行分支的所有步骤
239
- displaySteps () {
240
- const steps = this.steps || []
241
-
242
- // 如果用户选择显示所有步骤,直接返回
243
- if (this.showAllSteps) {
244
- return steps
245
- }
246
-
247
- // 找到第一个判断节点
248
- const firstConditionStepIndex = steps.findIndex(step => step.type === 'condition')
249
-
250
- // 如果没有找到判断节点,显示所有步骤
251
- if (firstConditionStepIndex === -1) {
252
- return steps
253
- }
254
-
255
- const firstConditionStep = steps[firstConditionStepIndex]
256
-
257
- // 如果当前步骤在第一个判断节点之前或等于判断节点,只显示到判断节点
258
- if (this.currentStepId <= firstConditionStep.id) {
259
- return steps.slice(0, firstConditionStepIndex + 1)
260
- }
261
-
262
- // 如果判断节点已经执行,显示所有步骤(包括并行分支)
263
- return steps
264
- },
265
-
266
- // 是否有更多步骤(被条件截断)
267
- hasMoreSteps () {
268
- // 如果已经显示所有步骤,则不显示"更多步骤"
269
- if (this.showAllSteps) return false
270
-
271
- const steps = this.steps || []
272
- const firstConditionStepIndex = steps.findIndex(step => step.type === 'condition')
273
-
274
- if (firstConditionStepIndex === -1) return false
275
-
276
- const firstConditionStep = steps[firstConditionStepIndex]
277
- return this.currentStepId <= firstConditionStep.id && firstConditionStepIndex < steps.length - 1
278
- },
279
-
280
- // 剩余步骤数量
281
- remainingStepsCount () {
282
- const steps = this.steps || []
283
- const firstConditionStepIndex = steps.findIndex(step => step.type === 'condition')
284
-
285
- if (firstConditionStepIndex === -1) return 0
286
-
287
- return steps.length - (firstConditionStepIndex + 1)
288
- }
289
- },
290
- mounted () {
291
- this.init()
292
- const timelineContent = this.$refs.timelineContent
293
- timelineContent.onmousewheel = function (e) {
294
- timelineContent.scrollLeft -= e.wheelDelta
295
- e.preventDefault()
296
- }
297
- // 添加全局点击事件,关闭右键菜单
298
- document.addEventListener('click', this.closeContextMenu)
299
- },
300
- beforeDestroy () {
301
- this.$refs.timelineContent.onmousewheel = null
302
- // 移除全局点击事件
303
- document.removeEventListener('click', this.closeContextMenu)
304
- },
305
- methods: {
306
- init () {
307
- this.displayStepId = this.activeStepId
308
- const currentStep = this.steps.find(step => step.id === this.currentStepId)
309
- if (currentStep && currentStep.deadline) {
310
- this.isOverdue = !this.state && this.formatDate(new Date(), 'yyyy-MM-dd hh:mm') > currentStep.deadline
311
- }
312
- this.$emit('activeStep', this.displayStepId)
313
- },
314
-
315
- // 获取时间轴段落数据
316
- getTimelineSegments () {
317
- const steps = this.displaySteps
318
- const segments = []
319
- let currentIndex = 0
320
-
321
- while (currentIndex < steps.length) {
322
- // 收集连续的主流程步骤(包括并行分支入口节点)
323
- const mainSteps = []
324
- while (currentIndex < steps.length && !this.isParallelStep(steps[currentIndex])) {
325
- mainSteps.push(steps[currentIndex])
326
-
327
- // 如果当前步骤是并行分支入口,处理完后跳出循环准备处理并行分支
328
- if (this.isParallelBranchEntry(steps[currentIndex])) {
329
- currentIndex++
330
- break
331
- }
332
- currentIndex++
333
- }
334
-
335
- if (mainSteps.length > 0) {
336
- segments.push({
337
- type: 'main',
338
- steps: mainSteps
339
- })
340
- }
341
-
342
- // 检查是否有并行分支需要处理
343
- if (currentIndex > 0 && this.isParallelBranchEntry(steps[currentIndex - 1])) {
344
- // 获取这个并行分支的所有步骤
345
- const branchIndex = steps[currentIndex - 1].properties.branchIndex
346
- const parallelSteps = this.getParallelStepsByBranchIndex(steps, branchIndex)
347
-
348
- if (parallelSteps.length > 0) {
349
- // 按branchPath分组
350
- const branches = this.groupParallelSteps(parallelSteps)
351
- segments.push({
352
- type: 'parallel',
353
- branches: branches
354
- })
355
-
356
- // 跳过已处理的并行步骤
357
- currentIndex = this.skipProcessedParallelSteps(steps, currentIndex, parallelSteps)
358
- }
359
- }
360
- }
361
-
362
- return segments
363
- },
364
-
365
- // 根据branchIndex获取并行步骤
366
- getParallelStepsByBranchIndex (steps, branchIndex) {
367
- return steps.filter(step =>
368
- step.properties &&
369
- step.properties.branchPath &&
370
- step.properties.branchPath.includes(`parallel_${branchIndex}_`)
371
- )
372
- },
373
-
374
- // 将并行步骤按branchPath分组
375
- groupParallelSteps (parallelSteps) {
376
- const branches = {}
377
- parallelSteps.forEach(step => {
378
- const branchPath = step.properties.branchPath
379
- if (!branches[branchPath]) {
380
- branches[branchPath] = []
381
- }
382
- branches[branchPath].push(step)
383
- })
384
-
385
- // 对每个分支内的步骤按id排序
386
- Object.keys(branches).forEach(branchPath => {
387
- branches[branchPath].sort((a, b) => a.id - b.id)
388
- })
389
-
390
- return Object.values(branches)
391
- },
392
-
393
- // 跳过已处理的并行步骤
394
- skipProcessedParallelSteps (steps, currentIndex, processedSteps) {
395
- const processedIds = new Set(processedSteps.map(step => step.id))
396
- while (currentIndex < steps.length && processedIds.has(steps[currentIndex].id)) {
397
- currentIndex++
398
- }
399
- return currentIndex
400
- },
401
-
402
- // 判断是否为并行分支入口
403
- isParallelBranchEntry (step) {
404
- return step.properties &&
405
- step.properties.branchType === 'parallelBranch'
406
- },
407
-
408
- // 判断是否为分支汇合节点
409
- isBranchExit (step) {
410
- return step.properties &&
411
- step.properties.flowRole === 'branchExit'
412
- },
413
-
414
- // 获取在某个section中的当前步骤索引
415
- getCurrentStepInSection (sectionSteps) {
416
- if (!sectionSteps || sectionSteps.length === 0) return 0
417
-
418
- const currentStepIndex = sectionSteps.findIndex(step => step.id === this.currentStepId)
419
- return currentStepIndex >= 0 ? currentStepIndex + 1 : 0
420
- },
421
-
422
- // 获取步骤编号
423
- getStepNumber (segmentIndex, stepIndex) {
424
- const segments = this.getTimelineSegments()
425
- let stepNumber = 0
426
-
427
- // 计算前面所有段落的步骤数
428
- for (let i = 0; i < segmentIndex; i++) {
429
- const segment = segments[i]
430
- if (segment.type === 'main') {
431
- stepNumber += segment.steps.length
432
- } else if (segment.type === 'parallel') {
433
- // 并行分支计算总步骤数
434
- stepNumber += segment.branches.reduce((total, branch) => total + branch.length, 0)
435
- }
436
- }
437
-
438
- return stepNumber + stepIndex
439
- },
440
-
441
- // 判断步骤是否已完成
442
- isStepCompleted (step) {
443
- if (!step) return false
444
- return this.isStepGreater(this.currentStepId, step.id) || this.state
445
- },
446
-
447
- // 判断第一环节是否再第二个环节之后
448
- isStepGreater (firstId, secondId) {
449
- const first = this.steps.find(item => item.id === firstId)
450
- const second = this.steps.find(item => item.id === secondId)
451
- // 优先使用 sequence,没有则用 id
452
- const firstValue = first?.sequence ?? first?.id
453
- const secondValue = second?.sequence ?? second?.id
454
- if (firstValue == null || secondValue == null) {
455
- console.warn('步骤不存在或无效:', { firstId, secondId })
456
- return false
457
- }
458
- return firstValue > secondValue
459
- },
460
-
461
- // 判断id是否为流程中最后一个
462
- isLastStep (stepId) {
463
- return stepId >= this.steps.length
464
- },
465
-
466
- // 判断是否为并行步骤
467
- isParallelStep (step) {
468
- return step.properties &&
469
- step.properties.branchPath &&
470
- step.properties.branchPath.includes('parallel_')
471
- },
472
-
473
- // 动态展示时间线节点颜色
474
- getStepIconColor (index, sectionSteps) {
475
- if (!sectionSteps || !sectionSteps[index - 1]) return 'gray'
476
-
477
- const stepId = sectionSteps[index - 1].id
478
-
479
- if (this.changeAble && stepId === this.displayStepId) {
480
- return 'yellow'
481
- } else if (!this.changeAble && stepId === this.currentStepId) {
482
- return this.state ? 'blue' : 'green'
483
- }
484
- if (this.isStepGreater(this.currentStepId, stepId) || this.state) {
485
- return 'blue'
486
- }
487
- if (stepId === this.currentStepId) {
488
- return this.isOverdue ? 'red' : 'blue'
489
- }
490
- return 'gray'
491
- },
492
-
493
- // 获取分支步骤的CSS类
494
- getBranchStepClass (step) {
495
- if (!step) return 'branch-step-gray'
496
-
497
- // 如果在可编辑状态下且是当前显示的步骤
498
- if (this.changeAble && step.id === this.displayStepId) {
499
- return 'branch-step-yellow'
500
- }
501
-
502
- // 根据 status 判断颜色
503
- switch (step.status) {
504
- case 0: // 未处理
505
- return 'branch-step-gray'
506
- case 1: // 正在处理
507
- // 检查是否逾期
508
- if (step.deadline && !this.state && this.formatDate(new Date(), 'yyyy-MM-dd hh:mm') > step.deadline) {
509
- return 'branch-step-red'
510
- }
511
- return 'branch-step-blue'
512
- case 2: // 已处理
513
- return 'branch-step-green'
514
- default:
515
- return 'branch-step-gray'
516
- }
517
- },
518
-
519
- // 获取分支时间轴的CSS类(根据分支中的步骤状态)
520
- getBranchTimelineClass (branch) {
521
- if (!branch || branch.length === 0) return 'branch-timeline-gray'
522
-
523
- // 检查分支中所有步骤的状态
524
- const hasCompleted = branch.some(step => step.status === 2) // 有已完成的
525
- const hasProcessing = branch.some(step => step.status === 1) // 有正在处理的
526
- const allPending = branch.every(step => step.status === 0 || step.status === undefined) // 全部未处理
527
-
528
- // 优先级:正在处理 > 已完成 > 未处理
529
- if (hasProcessing) {
530
- return 'branch-timeline-blue' // 正在进行
531
- } else if (hasCompleted) {
532
- return 'branch-timeline-green' // 已完成
533
- } else if (allPending) {
534
- return 'branch-timeline-gray' // 未抵达
535
- } else {
536
- return 'branch-timeline-gray' // 默认
537
- }
538
- },
539
-
540
- onStepClick (stepId) {
541
- if (!this.changeAble || stepId === this.displayStepId) {
542
- return
543
- }
544
- const curStep = this.steps.find(item => item.id === stepId)
545
- if (this.isStepGreater(stepId, this.currentStepId) && curStep.status !== 1 && curStep.status !== 2) {
546
- return this.$message.warn('请先完成当前步骤')
547
- }
548
- this.$emit('activeStep', stepId)
549
- this.displayStepId = stepId
550
- },
551
-
552
- // 处理节点右键点击
553
- handleStepContextMenu (e, step) {
554
- // 只有调试用户才能看到右键菜单
555
- if (!this.isDebugUser) {
556
- return
557
- }
558
- // 只有已完成的节点或当前节点才能右键
559
- if (!this.isStepCompleted(step) && step.id !== this.currentStepId) {
560
- return
561
- }
562
-
563
- e.preventDefault()
564
- e.stopPropagation()
565
-
566
- this.selectedStepForMenu = step
567
- this.contextMenuX = e.clientX
568
- this.contextMenuY = e.clientY
569
- this.contextMenuVisible = true
570
- },
571
-
572
- // 关闭右键菜单
573
- closeContextMenu () {
574
- this.contextMenuVisible = false
575
- this.selectedStepForMenu = null
576
- },
577
-
578
- // 从此节点重新加载
579
- reloadFromStep () {
580
- if (!this.selectedStepForMenu) return
581
-
582
- this.$emit('reloadFromStep', this.selectedStepForMenu.id)
583
- this.closeContextMenu()
584
- },
585
-
586
- formatDate,
587
- },
588
- watch: {
589
- activeStepId: function (newVal) {
590
- this.displayStepId = newVal
591
- }
592
- }
593
- }
594
- </script>
595
-
596
- <style lang="less" scoped>
597
- .timeline-wrapper {
598
- display: flex;
599
- align-items: flex-start;
600
- gap: 0;
601
-
602
- .timeline-section {
603
- flex-shrink: 0;
604
-
605
- // 主时间轴自动填充剩余宽度
606
- &.main-timeline {
607
- flex: 1;
608
- }
609
-
610
- /deep/ .ant-steps-dot {
611
- margin-bottom: 6px;
612
- .ant-steps-item-tail {
613
- top: 7px;
614
- margin-left: 106px;
615
- width: calc(100% - 23px);
616
- }
617
- .ant-steps-item-icon {
618
- margin-left: 90px;
619
- width: 8px;
620
- height: 8px;
621
- line-height: 8px;
622
- }
623
- .ant-steps-item-content {
624
- min-width: 195px;
625
- margin-top: 24px;
626
- .ant-steps-item-description {
627
- text-align: left;
628
- background: #fff;
629
- border-radius: 4px;
630
- padding: 12px;
631
- position: relative;
632
-
633
- p {
634
- position: relative;
635
- padding: 8px 0;
636
- margin: 0;
637
- font-size: 13px;
638
- color: #595959;
639
- line-height: 1.5;
640
- white-space: nowrap;
641
- overflow: visible;
642
-
643
- &:first-child {
644
- border-top: 1px dotted #e8e8e8;
645
- }
646
-
647
- &:not(:last-child) {
648
- border-bottom: 1px dashed #f0f0f0;
649
- }
650
-
651
- &:last-child {
652
- padding-bottom: 0;
653
- }
654
- }
655
- }
656
- }
657
- &.ant-steps {
658
- padding-top: 8px;
659
- }
660
- }
661
-
662
- /deep/ .ant-steps-item-title {
663
- font-size: 14px;
664
- h3 {
665
- font-size: 14px;
666
- margin-bottom: 0;
667
- }
668
- }
669
- }
670
-
671
- .parallel-section {
672
- display: flex;
673
- align-items: flex-start;
674
- position: relative;
675
-
676
- .connection-line {
677
- width: 2rem;
678
- height: 0;
679
- flex-shrink: 0;
680
- margin-top: 1rem;
681
-
682
- &.left-line {
683
- margin-right: 0;
684
- position: relative;
685
-
686
- &::before {
687
- content: '';
688
- position: absolute;
689
- left: -4.8rem;
690
- top: -1px;
691
- width: 7rem;
692
- height: 3px;
693
- background: #f0f0f0;
694
- }
695
- }
696
-
697
- &.right-line {
698
- margin-left: 0;
699
- position: relative;
700
-
701
- &::before {
702
- content: '';
703
- position: absolute;
704
- right: -4.6rem;
705
- top: -1px;
706
- width: 6.8rem;
707
- height: 3px;
708
- background: #f0f0f0;
709
- }
710
- }
711
- }
712
-
713
- .parallel-branches-container {
714
- display: flex;
715
- flex-direction: column; /* 竖直排列分支节点 */
716
- gap: 6px; /* 缩小间距 */
717
- align-items: center;
718
- position: relative;
719
- border: 2px dotted #d9d9d9;
720
- border-radius: 8px;
721
- margin: 0 12px;
722
- background: #fff;
723
- padding-bottom: 14px;
724
- width: max-content;
725
-
726
- /* 为并行节点容器添加小标题 */
727
- .parallel-node-title {
728
- position: absolute;
729
- bottom: -10px; /* 调整标题的上下位置 */
730
- left: 50%; /* 水平居中 */
731
- transform: translateX(-50%); /* 精确水平居中 */
732
- background: #fff; /* 背景色与容器一致 */
733
- font-size: 12px; /* 标题的字体大小 */
734
- border-radius: 4px;
735
- }
736
- }
737
-
738
- .branch-row {
739
- display: flex;
740
- flex-direction: column;
741
- align-items: center;
742
- position: relative;
743
- gap: 16px;
744
-
745
- :deep(.ant-steps-item-content) {
746
- text-align: left;
747
- }
748
-
749
- .branch-timeline {
750
- padding: 12px 16px;
751
- position: relative;
752
- min-width: 80px;
753
- text-align: center;
754
- transition: all 0.3s ease; /* 平滑过渡 */
755
- }
756
-
757
- .branch-timeline:hover {
758
- transform: scale(1.01); /* 鼠标悬停时放大 */
759
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
760
- }
761
-
762
- .branch-step-title {
763
- font-size: 14px;
764
- font-weight: 500;
765
- color: #262626;
766
- cursor: pointer;
767
- }
768
-
769
- .branch-step-desc {
770
- p {
771
- margin: 0;
772
- padding: 4px 0;
773
- font-size: 12px;
774
- color: #666;
775
- }
776
- }
777
-
778
- /deep/ .ant-steps-icon {
779
- color: #fefefe;
780
- }
781
-
782
- // 分支步骤节点样式
783
- /deep/ .ant-steps-item-icon {
784
- width: 20px;
785
- height: 20px;
786
- line-height: 20px;
787
- border-radius: 20px;
788
- }
789
-
790
- /deep/ .ant-steps-item-title {
791
- line-height: 20px;
792
- }
793
-
794
- // 分支步骤状态颜色
795
- /deep/ .branch-step-gray .ant-steps-item-icon {
796
- background-color: #bfbfbf !important;
797
- border-color: #bfbfbf !important;
798
- }
799
-
800
- /deep/ .branch-step-blue .ant-steps-item-icon {
801
- background-color: #1890ff !important;
802
- border-color: #1890ff !important;
803
- }
804
-
805
- /deep/ .branch-step-green .ant-steps-item-icon {
806
- background-color: #52c41a !important;
807
- border-color: #52c41a !important;
808
- }
809
-
810
- /deep/ .branch-step-red .ant-steps-item-icon {
811
- background-color: #ff4d4f !important;
812
- border-color: #ff4d4f !important;
813
- }
814
-
815
- /deep/ .branch-step-yellow .ant-steps-item-icon {
816
- background-color: #faad14 !important;
817
- border-color: #faad14 !important;
818
- }
819
- }
820
- }
821
-
822
- @red: rgb(255, 77, 79);
823
-
824
- .step-icon-filled {
825
- position: absolute;
826
- top: -2px;
827
- left: -6px;
828
- @blue: rgb(24, 144, 255);
829
- @yellow: rgb(255, 164, 39);
830
- @gray: rgb(191, 191, 191);
831
- &.digital {
832
- width: 28px;
833
- line-height: 28px;
834
- border-radius: 50%;
835
- color: #ffffff;
836
- &.blue {
837
- background: @blue;
838
- }
839
- &.yellow {
840
- background: @yellow;
841
- }
842
- &.gray {
843
- background: @gray;
844
- }
845
- &.red {
846
- background: @red;
847
- }
848
- }
849
- &.icon {
850
- font-size: 28px;
851
- &.blue {
852
- color: @blue;
853
- }
854
- &.yellow {
855
- color: @yellow;
856
- }
857
- &.gray {
858
- color: @gray;
859
- }
860
- }
861
- }
862
-
863
- .step-item {
864
- cursor: pointer;
865
- &:hover {
866
- .ant-steps-item-description {
867
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
868
- transform: translateY(-1px);
869
- transition: all 0.3s ease;
870
- }
871
- p {
872
- color: #262626;
873
- }
874
- }
875
- .text-red {
876
- color: @red !important;
877
- }
878
- }
879
-
880
- .next-step-overdue {
881
- /deep/ .ant-steps-item-tail::after {
882
- background: @red;
883
- }
884
- }
885
-
886
- // 新增样式:步骤标题容器
887
- .step-title-container {
888
- .condition-indicators {
889
- margin-top: 8px;
890
- display: flex;
891
- align-items: center;
892
- gap: 8px;
893
-
894
- .condition-tag {
895
- margin: 0;
896
- }
897
-
898
- .condition-help {
899
- color: #999;
900
- cursor: pointer;
901
- &:hover {
902
- color: #1890ff;
903
- }
904
- }
905
- }
906
- }
907
-
908
- // 分支信息样式
909
- .branch-info {
910
- margin-top: 8px;
911
- padding: 8px;
912
- background: #f9f9f9;
913
- border-radius: 4px;
914
- border-left: 3px solid #722ed1;
915
-
916
- .branch-title {
917
- margin: 0 0 6px 0 !important;
918
- font-weight: bold;
919
- color: #722ed1;
920
- font-size: 12px;
921
- padding: 0 !important;
922
- border: none !important;
923
- }
924
-
925
- .branch-options {
926
- display: flex;
927
- flex-wrap: wrap;
928
- gap: 4px;
929
-
930
- .branch-option-tag {
931
- margin: 0;
932
- font-size: 11px;
933
- }
934
- }
935
- }
936
-
937
- // 省略步骤样式
938
- .more-steps-section {
939
- flex-shrink: 0;
940
- // margin-top: 24px;
941
-
942
- .more-steps-item {
943
- opacity: 0.7;
944
-
945
- .more-steps-container {
946
- .pending-tag {
947
- margin-top: 8px;
948
- margin-left: 0;
949
- }
950
- }
951
-
952
- .more-steps-desc {
953
- p {
954
- // font-style: italic;
955
- color: #999 !important;
956
-
957
- &.steps-count {
958
- font-weight: bold;
959
- color: #666 !important;
960
- }
961
- }
962
- }
963
- }
964
- }
965
- }
966
-
967
- // 分支步骤 Popover 样式
968
- /deep/ .ant-popover {
969
- .popover-title {
970
- display: flex;
971
- align-items: center;
972
- gap: 6px;
973
- font-weight: 600;
974
- color: #1890ff;
975
-
976
- .anticon {
977
- font-size: 14px;
978
- }
979
- }
980
-
981
- .branch-step-popover {
982
- min-width: 200px;
983
-
984
- .popover-item {
985
- display: flex;
986
- align-items: flex-start;
987
- margin-bottom: 8px;
988
-
989
- &:last-child {
990
- margin-bottom: 0;
991
- }
992
-
993
- &.no-data {
994
- justify-content: center;
995
- font-style: italic;
996
- color: #999;
997
- }
998
-
999
- .popover-label {
1000
- font-weight: 600;
1001
- color: #262626;
1002
- min-width: 70px;
1003
- flex-shrink: 0;
1004
- }
1005
-
1006
- .popover-value {
1007
- color: #595959;
1008
- word-break: break-all;
1009
- flex: 1;
1010
- }
1011
- }
1012
- }
1013
- }
1014
- </style>
1
+ <template>
2
+ <div class="timeline-wrapper" ref="timelineContent" style="white-space: nowrap; overflow-x: auto">
3
+ <!-- 动态渲染时间轴段落 -->
4
+ <template v-for="(segment, segmentIndex) in getTimelineSegments()">
5
+
6
+ <!-- 主时间轴段落 -->
7
+ <div v-if="segment.type === 'main'" :key="'main-' + segmentIndex" class="timeline-section main-timeline">
8
+ <a-steps
9
+ :current="getCurrentStepInSection(segment.steps)"
10
+ :initial="1">
11
+ <template #progressDot="{index}">
12
+ <span v-if="!changeAble" class="step-label in-steps"><i :class="['step-icon', getStepIconColor(index, segment.steps)]"></i></span>
13
+ <span v-else :key="displayStepId" :class="['step-icon-filled digital', getStepIconColor(index, segment.steps)]">
14
+ <a-icon v-if="isStepCompleted(segment.steps[index - 1])" type="check-circle" theme="filled" :class="['step-icon-filled icon', getStepIconColor(index, segment.steps)]" />
15
+ <span v-else :class="['step-icon-filled digital', getStepIconColor(index, segment.steps)]">{{ getStepNumber(segmentIndex, index) }}</span>
16
+ </span>
17
+ </template>
18
+
19
+ <a-step
20
+ v-for="item in segment.steps"
21
+ :key="item.id"
22
+ class="step-item"
23
+ @click.stop="onStepClick(item.id)"
24
+ @contextmenu.native="handleStepContextMenu($event, item)"
25
+ >
26
+ <template #title>
27
+ <div class="step-title-container">
28
+ <h3 v-if="item.name">{{ item.name }}</h3>
29
+ <!-- 判断节点特殊标识 -->
30
+ <div v-if="item.type === 'condition'" class="condition-indicators">
31
+ <a-tag color="purple" size="small" class="condition-tag">
32
+ <a-icon type="branches" /> 条件节点
33
+ </a-tag>
34
+ <a-tooltip title="此节点根据条件判断后续流程走向">
35
+ <a-icon type="question-circle" class="condition-help" />
36
+ </a-tooltip>
37
+ </div>
38
+ </div>
39
+ </template>
40
+ <template #description>
41
+ <div>
42
+ <p v-if="item.handler">负责人:
43
+ <trim-text-tail :text="item.handler" :length="14"/>
44
+ </p>
45
+ <p v-if="item.date">填报时间:{{ formatDate(item.date, 'yyyy-MM-dd hh:mm') }}</p>
46
+ <p v-if="item.deadline" :class="{ 'text-red': item.id === currentStepId && isOverdue }">截止时间:{{ formatDate(item.deadline, 'yyyy-MM-dd hh:mm') }}</p>
47
+ <p v-if="item.note">备注:
48
+ <trim-text-tail :text="item.note" :length="15"/>
49
+ </p>
50
+ </div>
51
+ </template>
52
+ </a-step>
53
+ </a-steps>
54
+ </div>
55
+
56
+ <!-- 并行分支段落 -->
57
+ <div v-else-if="segment.type === 'parallel'" :key="'parallel-' + segmentIndex" class="parallel-section">
58
+ <!-- 左侧连接线 -->
59
+ <div class="connection-line left-line"></div>
60
+
61
+ <!-- 并行分支容器 -->
62
+ <div class="parallel-branches-container">
63
+ <!-- 并行节点标题 -->
64
+ <a-tag color="cyan" size="small" class="parallel-node-title">
65
+ <a-icon type="apartment" /> 并行节点
66
+ </a-tag>
67
+ <!-- 动态渲染并行分支 -->
68
+ <div
69
+ v-for="(branch, branchIndex) in segment.branches"
70
+ :key="'segment-' + segmentIndex + '-branch-' + branchIndex"
71
+ class="branch-row">
72
+ <!-- 分支时间轴 -->
73
+ <div :class="['branch-timeline', getBranchTimelineClass(branch)]">
74
+ <a-steps size="small" class="branch-steps">
75
+ <a-step
76
+ v-for="step in branch"
77
+ :key="'segment-' + segmentIndex + '-branch-' + branchIndex + '-step-' + step.id"
78
+ :class="['branch-step', getBranchStepClass(step)]"
79
+ @click.stop="onStepClick(step.id)"
80
+ @contextmenu.native="handleStepContextMenu($event, step)">
81
+ <template #title>
82
+ <a-popover placement="top" trigger="hover" :mouseEnterDelay="0.3">
83
+ <template #content>
84
+ <div class="branch-step-popover">
85
+ <div class="popover-item" v-if="step.handler">
86
+ <span class="popover-label">负责人:</span>
87
+ <span class="popover-value">{{ step.handler }}</span>
88
+ </div>
89
+ <div class="popover-item" v-if="step.date">
90
+ <span class="popover-label">填报时间:</span>
91
+ <span class="popover-value">{{ formatDate(step.date, 'yyyy-MM-dd hh:mm') }}</span>
92
+ </div>
93
+ <div class="popover-item" v-if="step.deadline">
94
+ <span class="popover-label">截止时间:</span>
95
+ <span class="popover-value">{{ formatDate(step.deadline, 'yyyy-MM-dd hh:mm') }}</span>
96
+ </div>
97
+ <div class="popover-item" v-if="step.note">
98
+ <span class="popover-label">备注:</span>
99
+ <span class="popover-value">{{ step.note }}</span>
100
+ </div>
101
+ <div class="popover-item no-data" v-if="!step.handler && !step.date && !step.deadline && !step.note">
102
+ <span class="popover-value">暂无详细信息</span>
103
+ </div>
104
+ </div>
105
+ </template>
106
+ <template #title>
107
+ <div class="popover-title">
108
+ <a-icon type="info-circle" />
109
+ {{ step.name }} - 详细信息
110
+ </div>
111
+ </template>
112
+ <span class="branch-step-title">{{ step.name }}</span>
113
+ </a-popover>
114
+ </template>
115
+ <template #description>
116
+ <div class="branch-step-desc">
117
+ <p v-if="step.handler">{{ step.handler }}</p>
118
+ <p v-if="step.date">{{ formatDate(step.date, 'MM-dd hh:mm') }}</p>
119
+ </div>
120
+ </template>
121
+ </a-step>
122
+ </a-steps>
123
+ </div>
124
+ </div>
125
+ </div>
126
+
127
+ <!-- 右侧连接线 -->
128
+ <div class="connection-line right-line"></div>
129
+ </div>
130
+
131
+ </template>
132
+
133
+ <!-- 当步骤被截断时显示省略状态 -->
134
+ <div class="more-steps-section" v-if="hasMoreSteps">
135
+ <a-steps :current="1" :initial="1">
136
+ <a-step
137
+ key="more-steps"
138
+ class="step-item more-steps-item">
139
+ <template #title>
140
+ <div class="more-steps-container">
141
+ <h3>后续步骤</h3>
142
+ </div>
143
+ </template>
144
+ <template #description>
145
+ <div class="more-steps-desc">
146
+ <p>根据上述条件判断结果,将继续执行相应的后续流程</p>
147
+ <p class="steps-count">剩余 {{ remainingStepsCount }} 个步骤</p>
148
+ </div>
149
+ </template>
150
+ <template #icon>
151
+ <a-icon type="more" style="color: #d9d9d9;" />
152
+ </template>
153
+ </a-step>
154
+ </a-steps>
155
+ </div>
156
+
157
+ <!-- 右键菜单 -->
158
+ <a-dropdown
159
+ :visible="contextMenuVisible"
160
+ :trigger="[]"
161
+ @visibleChange="val => !val && closeContextMenu()"
162
+ >
163
+ <div
164
+ v-show="contextMenuVisible"
165
+ :style="{
166
+ position: 'fixed',
167
+ left: contextMenuX + 'px',
168
+ top: contextMenuY + 'px',
169
+ width: '1px',
170
+ height: '1px'
171
+ }"
172
+ ></div>
173
+ <a-menu slot="overlay" @click="closeContextMenu">
174
+ <a-menu-item @click="reloadFromStep">
175
+ <a-icon type="reload" />
176
+ 从此节点重新加载
177
+ </a-menu-item>
178
+ </a-menu>
179
+ </a-dropdown>
180
+ </div>
181
+ </template>
182
+
183
+ <script>
184
+ import { mapGetters } from 'vuex'
185
+ import { formatDate } from '@vue2-client/utils/util'
186
+ import TrimTextTail from '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/TrimTextTail'
187
+
188
+ export default {
189
+ components: { TrimTextTail },
190
+ name: 'WorkFlowTimeline',
191
+ props: {
192
+ workflowId: {
193
+ type: [String, Number],
194
+ required: true
195
+ },
196
+ currentStepId: {
197
+ type: Number,
198
+ required: false,
199
+ default: 1
200
+ },
201
+ activeStepId: {
202
+ type: Number,
203
+ required: false,
204
+ default: 1
205
+ },
206
+ state: {
207
+ type: [Boolean, Number],
208
+ required: false,
209
+ default: false
210
+ },
211
+ steps: {
212
+ type: Array,
213
+ required: true
214
+ },
215
+ changeAble: {
216
+ type: Boolean,
217
+ required: false,
218
+ default: false
219
+ }
220
+ },
221
+ data () {
222
+ return {
223
+ // 当前显示的步骤 id
224
+ displayStepId: 1,
225
+ // 当前任务是否逾期
226
+ isOverdue: false,
227
+ // 是否显示所有步骤(包括条件后的步骤)
228
+ showAllSteps: false,
229
+ // 右键菜单相关状态
230
+ contextMenuVisible: false,
231
+ contextMenuX: 0,
232
+ contextMenuY: 0,
233
+ selectedStepForMenu: null
234
+ }
235
+ },
236
+ computed: {
237
+ ...mapGetters('account', ['isDebugUser']),
238
+ // 控制显示的步骤:当判断节点未执行时只显示到判断节点,执行后显示包括并行分支的所有步骤
239
+ displaySteps () {
240
+ const steps = this.steps || []
241
+
242
+ // 如果用户选择显示所有步骤,直接返回
243
+ if (this.showAllSteps) {
244
+ return steps
245
+ }
246
+
247
+ // 找到第一个判断节点
248
+ const firstConditionStepIndex = steps.findIndex(step => step.type === 'condition')
249
+
250
+ // 如果没有找到判断节点,显示所有步骤
251
+ if (firstConditionStepIndex === -1) {
252
+ return steps
253
+ }
254
+
255
+ const firstConditionStep = steps[firstConditionStepIndex]
256
+
257
+ // 如果当前步骤在第一个判断节点之前或等于判断节点,只显示到判断节点
258
+ if (this.currentStepId <= firstConditionStep.id) {
259
+ return steps.slice(0, firstConditionStepIndex + 1)
260
+ }
261
+
262
+ // 如果判断节点已经执行,显示所有步骤(包括并行分支)
263
+ return steps
264
+ },
265
+
266
+ // 是否有更多步骤(被条件截断)
267
+ hasMoreSteps () {
268
+ // 如果已经显示所有步骤,则不显示"更多步骤"
269
+ if (this.showAllSteps) return false
270
+
271
+ const steps = this.steps || []
272
+ const firstConditionStepIndex = steps.findIndex(step => step.type === 'condition')
273
+
274
+ if (firstConditionStepIndex === -1) return false
275
+
276
+ const firstConditionStep = steps[firstConditionStepIndex]
277
+ return this.currentStepId <= firstConditionStep.id && firstConditionStepIndex < steps.length - 1
278
+ },
279
+
280
+ // 剩余步骤数量
281
+ remainingStepsCount () {
282
+ const steps = this.steps || []
283
+ const firstConditionStepIndex = steps.findIndex(step => step.type === 'condition')
284
+
285
+ if (firstConditionStepIndex === -1) return 0
286
+
287
+ return steps.length - (firstConditionStepIndex + 1)
288
+ }
289
+ },
290
+ mounted () {
291
+ this.init()
292
+ const timelineContent = this.$refs.timelineContent
293
+ timelineContent.onmousewheel = function (e) {
294
+ timelineContent.scrollLeft -= e.wheelDelta
295
+ e.preventDefault()
296
+ }
297
+ // 添加全局点击事件,关闭右键菜单
298
+ document.addEventListener('click', this.closeContextMenu)
299
+ },
300
+ beforeDestroy () {
301
+ this.$refs.timelineContent.onmousewheel = null
302
+ // 移除全局点击事件
303
+ document.removeEventListener('click', this.closeContextMenu)
304
+ },
305
+ methods: {
306
+ init () {
307
+ this.displayStepId = this.activeStepId
308
+ const currentStep = this.steps.find(step => step.id === this.currentStepId)
309
+ if (currentStep && currentStep.deadline) {
310
+ this.isOverdue = !this.state && this.formatDate(new Date(), 'yyyy-MM-dd hh:mm') > currentStep.deadline
311
+ }
312
+ this.$emit('activeStep', this.displayStepId)
313
+ },
314
+
315
+ // 获取时间轴段落数据
316
+ getTimelineSegments () {
317
+ const steps = this.displaySteps
318
+ const segments = []
319
+ let currentIndex = 0
320
+
321
+ while (currentIndex < steps.length) {
322
+ // 收集连续的主流程步骤(包括并行分支入口节点)
323
+ const mainSteps = []
324
+ while (currentIndex < steps.length && !this.isParallelStep(steps[currentIndex])) {
325
+ mainSteps.push(steps[currentIndex])
326
+
327
+ // 如果当前步骤是并行分支入口,处理完后跳出循环准备处理并行分支
328
+ if (this.isParallelBranchEntry(steps[currentIndex])) {
329
+ currentIndex++
330
+ break
331
+ }
332
+ currentIndex++
333
+ }
334
+
335
+ if (mainSteps.length > 0) {
336
+ segments.push({
337
+ type: 'main',
338
+ steps: mainSteps
339
+ })
340
+ }
341
+
342
+ // 检查是否有并行分支需要处理
343
+ if (currentIndex > 0 && this.isParallelBranchEntry(steps[currentIndex - 1])) {
344
+ // 获取这个并行分支的所有步骤
345
+ const branchIndex = steps[currentIndex - 1].properties.branchIndex
346
+ const parallelSteps = this.getParallelStepsByBranchIndex(steps, branchIndex)
347
+
348
+ if (parallelSteps.length > 0) {
349
+ // 按branchPath分组
350
+ const branches = this.groupParallelSteps(parallelSteps)
351
+ segments.push({
352
+ type: 'parallel',
353
+ branches: branches
354
+ })
355
+
356
+ // 跳过已处理的并行步骤
357
+ currentIndex = this.skipProcessedParallelSteps(steps, currentIndex, parallelSteps)
358
+ }
359
+ }
360
+ }
361
+
362
+ return segments
363
+ },
364
+
365
+ // 根据branchIndex获取并行步骤
366
+ getParallelStepsByBranchIndex (steps, branchIndex) {
367
+ return steps.filter(step =>
368
+ step.properties &&
369
+ step.properties.branchPath &&
370
+ step.properties.branchPath.includes(`parallel_${branchIndex}_`)
371
+ )
372
+ },
373
+
374
+ // 将并行步骤按branchPath分组
375
+ groupParallelSteps (parallelSteps) {
376
+ const branches = {}
377
+ parallelSteps.forEach(step => {
378
+ const branchPath = step.properties.branchPath
379
+ if (!branches[branchPath]) {
380
+ branches[branchPath] = []
381
+ }
382
+ branches[branchPath].push(step)
383
+ })
384
+
385
+ // 对每个分支内的步骤按id排序
386
+ Object.keys(branches).forEach(branchPath => {
387
+ branches[branchPath].sort((a, b) => a.id - b.id)
388
+ })
389
+
390
+ return Object.values(branches)
391
+ },
392
+
393
+ // 跳过已处理的并行步骤
394
+ skipProcessedParallelSteps (steps, currentIndex, processedSteps) {
395
+ const processedIds = new Set(processedSteps.map(step => step.id))
396
+ while (currentIndex < steps.length && processedIds.has(steps[currentIndex].id)) {
397
+ currentIndex++
398
+ }
399
+ return currentIndex
400
+ },
401
+
402
+ // 判断是否为并行分支入口
403
+ isParallelBranchEntry (step) {
404
+ return step.properties &&
405
+ step.properties.branchType === 'parallelBranch'
406
+ },
407
+
408
+ // 判断是否为分支汇合节点
409
+ isBranchExit (step) {
410
+ return step.properties &&
411
+ step.properties.flowRole === 'branchExit'
412
+ },
413
+
414
+ // 获取在某个section中的当前步骤索引
415
+ getCurrentStepInSection (sectionSteps) {
416
+ if (!sectionSteps || sectionSteps.length === 0) return 0
417
+
418
+ const currentStepIndex = sectionSteps.findIndex(step => step.id === this.currentStepId)
419
+ return currentStepIndex >= 0 ? currentStepIndex + 1 : 0
420
+ },
421
+
422
+ // 获取步骤编号
423
+ getStepNumber (segmentIndex, stepIndex) {
424
+ const segments = this.getTimelineSegments()
425
+ let stepNumber = 0
426
+
427
+ // 计算前面所有段落的步骤数
428
+ for (let i = 0; i < segmentIndex; i++) {
429
+ const segment = segments[i]
430
+ if (segment.type === 'main') {
431
+ stepNumber += segment.steps.length
432
+ } else if (segment.type === 'parallel') {
433
+ // 并行分支计算总步骤数
434
+ stepNumber += segment.branches.reduce((total, branch) => total + branch.length, 0)
435
+ }
436
+ }
437
+
438
+ return stepNumber + stepIndex
439
+ },
440
+
441
+ // 判断步骤是否已完成
442
+ isStepCompleted (step) {
443
+ if (!step) return false
444
+ return this.isStepGreater(this.currentStepId, step.id) || this.state
445
+ },
446
+
447
+ // 判断第一环节是否再第二个环节之后
448
+ isStepGreater (firstId, secondId) {
449
+ const first = this.steps.find(item => item.id === firstId)
450
+ const second = this.steps.find(item => item.id === secondId)
451
+ // 优先使用 sequence,没有则用 id
452
+ const firstValue = first?.sequence ?? first?.id
453
+ const secondValue = second?.sequence ?? second?.id
454
+ if (firstValue == null || secondValue == null) {
455
+ console.warn('步骤不存在或无效:', { firstId, secondId })
456
+ return false
457
+ }
458
+ return firstValue > secondValue
459
+ },
460
+
461
+ // 判断id是否为流程中最后一个
462
+ isLastStep (stepId) {
463
+ return stepId >= this.steps.length
464
+ },
465
+
466
+ // 判断是否为并行步骤
467
+ isParallelStep (step) {
468
+ return step.properties &&
469
+ step.properties.branchPath &&
470
+ step.properties.branchPath.includes('parallel_')
471
+ },
472
+
473
+ // 动态展示时间线节点颜色
474
+ getStepIconColor (index, sectionSteps) {
475
+ if (!sectionSteps || !sectionSteps[index - 1]) return 'gray'
476
+
477
+ const stepId = sectionSteps[index - 1].id
478
+
479
+ if (this.changeAble && stepId === this.displayStepId) {
480
+ return 'yellow'
481
+ } else if (!this.changeAble && stepId === this.currentStepId) {
482
+ return this.state ? 'blue' : 'green'
483
+ }
484
+ if (this.isStepGreater(this.currentStepId, stepId) || this.state) {
485
+ return 'blue'
486
+ }
487
+ if (stepId === this.currentStepId) {
488
+ return this.isOverdue ? 'red' : 'blue'
489
+ }
490
+ return 'gray'
491
+ },
492
+
493
+ // 获取分支步骤的CSS类
494
+ getBranchStepClass (step) {
495
+ if (!step) return 'branch-step-gray'
496
+
497
+ // 如果在可编辑状态下且是当前显示的步骤
498
+ if (this.changeAble && step.id === this.displayStepId) {
499
+ return 'branch-step-yellow'
500
+ }
501
+
502
+ // 根据 status 判断颜色
503
+ switch (step.status) {
504
+ case 0: // 未处理
505
+ return 'branch-step-gray'
506
+ case 1: // 正在处理
507
+ // 检查是否逾期
508
+ if (step.deadline && !this.state && this.formatDate(new Date(), 'yyyy-MM-dd hh:mm') > step.deadline) {
509
+ return 'branch-step-red'
510
+ }
511
+ return 'branch-step-blue'
512
+ case 2: // 已处理
513
+ return 'branch-step-green'
514
+ default:
515
+ return 'branch-step-gray'
516
+ }
517
+ },
518
+
519
+ // 获取分支时间轴的CSS类(根据分支中的步骤状态)
520
+ getBranchTimelineClass (branch) {
521
+ if (!branch || branch.length === 0) return 'branch-timeline-gray'
522
+
523
+ // 检查分支中所有步骤的状态
524
+ const hasCompleted = branch.some(step => step.status === 2) // 有已完成的
525
+ const hasProcessing = branch.some(step => step.status === 1) // 有正在处理的
526
+ const allPending = branch.every(step => step.status === 0 || step.status === undefined) // 全部未处理
527
+
528
+ // 优先级:正在处理 > 已完成 > 未处理
529
+ if (hasProcessing) {
530
+ return 'branch-timeline-blue' // 正在进行
531
+ } else if (hasCompleted) {
532
+ return 'branch-timeline-green' // 已完成
533
+ } else if (allPending) {
534
+ return 'branch-timeline-gray' // 未抵达
535
+ } else {
536
+ return 'branch-timeline-gray' // 默认
537
+ }
538
+ },
539
+
540
+ onStepClick (stepId) {
541
+ if (!this.changeAble || stepId === this.displayStepId) {
542
+ return
543
+ }
544
+ const curStep = this.steps.find(item => item.id === stepId)
545
+ if (this.isStepGreater(stepId, this.currentStepId) && curStep.status !== 1 && curStep.status !== 2) {
546
+ return this.$message.warn('请先完成当前步骤')
547
+ }
548
+ this.$emit('activeStep', stepId)
549
+ this.displayStepId = stepId
550
+ },
551
+
552
+ // 处理节点右键点击
553
+ handleStepContextMenu (e, step) {
554
+ // 只有调试用户才能看到右键菜单
555
+ if (!this.isDebugUser) {
556
+ return
557
+ }
558
+ // 只有已完成的节点或当前节点才能右键
559
+ if (!this.isStepCompleted(step) && step.id !== this.currentStepId) {
560
+ return
561
+ }
562
+
563
+ e.preventDefault()
564
+ e.stopPropagation()
565
+
566
+ this.selectedStepForMenu = step
567
+ this.contextMenuX = e.clientX
568
+ this.contextMenuY = e.clientY
569
+ this.contextMenuVisible = true
570
+ },
571
+
572
+ // 关闭右键菜单
573
+ closeContextMenu () {
574
+ this.contextMenuVisible = false
575
+ this.selectedStepForMenu = null
576
+ },
577
+
578
+ // 从此节点重新加载
579
+ reloadFromStep () {
580
+ if (!this.selectedStepForMenu) return
581
+
582
+ this.$emit('reloadFromStep', this.selectedStepForMenu.id)
583
+ this.closeContextMenu()
584
+ },
585
+
586
+ formatDate,
587
+ },
588
+ watch: {
589
+ activeStepId: function (newVal) {
590
+ this.displayStepId = newVal
591
+ }
592
+ }
593
+ }
594
+ </script>
595
+
596
+ <style lang="less" scoped>
597
+ .timeline-wrapper {
598
+ display: flex;
599
+ align-items: flex-start;
600
+ gap: 0;
601
+
602
+ .timeline-section {
603
+ flex-shrink: 0;
604
+
605
+ // 主时间轴自动填充剩余宽度
606
+ &.main-timeline {
607
+ flex: 1;
608
+ }
609
+
610
+ /deep/ .ant-steps-dot {
611
+ margin-bottom: 6px;
612
+ .ant-steps-item-tail {
613
+ top: 7px;
614
+ margin-left: 106px;
615
+ width: calc(100% - 23px);
616
+ }
617
+ .ant-steps-item-icon {
618
+ margin-left: 90px;
619
+ width: 8px;
620
+ height: 8px;
621
+ line-height: 8px;
622
+ }
623
+ .ant-steps-item-content {
624
+ min-width: 195px;
625
+ margin-top: 24px;
626
+ .ant-steps-item-description {
627
+ text-align: left;
628
+ background: #fff;
629
+ border-radius: 4px;
630
+ padding: 12px;
631
+ position: relative;
632
+
633
+ p {
634
+ position: relative;
635
+ padding: 8px 0;
636
+ margin: 0;
637
+ font-size: 13px;
638
+ color: #595959;
639
+ line-height: 1.5;
640
+ white-space: nowrap;
641
+ overflow: visible;
642
+
643
+ &:first-child {
644
+ border-top: 1px dotted #e8e8e8;
645
+ }
646
+
647
+ &:not(:last-child) {
648
+ border-bottom: 1px dashed #f0f0f0;
649
+ }
650
+
651
+ &:last-child {
652
+ padding-bottom: 0;
653
+ }
654
+ }
655
+ }
656
+ }
657
+ &.ant-steps {
658
+ padding-top: 8px;
659
+ }
660
+ }
661
+
662
+ /deep/ .ant-steps-item-title {
663
+ font-size: 14px;
664
+ h3 {
665
+ font-size: 14px;
666
+ margin-bottom: 0;
667
+ }
668
+ }
669
+ }
670
+
671
+ .parallel-section {
672
+ display: flex;
673
+ align-items: flex-start;
674
+ position: relative;
675
+
676
+ .connection-line {
677
+ width: 2rem;
678
+ height: 0;
679
+ flex-shrink: 0;
680
+ margin-top: 1rem;
681
+
682
+ &.left-line {
683
+ margin-right: 0;
684
+ position: relative;
685
+
686
+ &::before {
687
+ content: '';
688
+ position: absolute;
689
+ left: -4.8rem;
690
+ top: -1px;
691
+ width: 7rem;
692
+ height: 3px;
693
+ background: #f0f0f0;
694
+ }
695
+ }
696
+
697
+ &.right-line {
698
+ margin-left: 0;
699
+ position: relative;
700
+
701
+ &::before {
702
+ content: '';
703
+ position: absolute;
704
+ right: -4.6rem;
705
+ top: -1px;
706
+ width: 6.8rem;
707
+ height: 3px;
708
+ background: #f0f0f0;
709
+ }
710
+ }
711
+ }
712
+
713
+ .parallel-branches-container {
714
+ display: flex;
715
+ flex-direction: column; /* 竖直排列分支节点 */
716
+ gap: 6px; /* 缩小间距 */
717
+ align-items: center;
718
+ position: relative;
719
+ border: 2px dotted #d9d9d9;
720
+ border-radius: 8px;
721
+ margin: 0 12px;
722
+ background: #fff;
723
+ padding-bottom: 14px;
724
+ width: max-content;
725
+
726
+ /* 为并行节点容器添加小标题 */
727
+ .parallel-node-title {
728
+ position: absolute;
729
+ bottom: -10px; /* 调整标题的上下位置 */
730
+ left: 50%; /* 水平居中 */
731
+ transform: translateX(-50%); /* 精确水平居中 */
732
+ background: #fff; /* 背景色与容器一致 */
733
+ font-size: 12px; /* 标题的字体大小 */
734
+ border-radius: 4px;
735
+ }
736
+ }
737
+
738
+ .branch-row {
739
+ display: flex;
740
+ flex-direction: column;
741
+ align-items: center;
742
+ position: relative;
743
+ gap: 16px;
744
+
745
+ :deep(.ant-steps-item-content) {
746
+ text-align: left;
747
+ }
748
+
749
+ .branch-timeline {
750
+ padding: 12px 16px;
751
+ position: relative;
752
+ min-width: 80px;
753
+ text-align: center;
754
+ transition: all 0.3s ease; /* 平滑过渡 */
755
+ }
756
+
757
+ .branch-timeline:hover {
758
+ transform: scale(1.01); /* 鼠标悬停时放大 */
759
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
760
+ }
761
+
762
+ .branch-step-title {
763
+ font-size: 14px;
764
+ font-weight: 500;
765
+ color: #262626;
766
+ cursor: pointer;
767
+ }
768
+
769
+ .branch-step-desc {
770
+ p {
771
+ margin: 0;
772
+ padding: 4px 0;
773
+ font-size: 12px;
774
+ color: #666;
775
+ }
776
+ }
777
+
778
+ /deep/ .ant-steps-icon {
779
+ color: #fefefe;
780
+ }
781
+
782
+ // 分支步骤节点样式
783
+ /deep/ .ant-steps-item-icon {
784
+ width: 20px;
785
+ height: 20px;
786
+ line-height: 20px;
787
+ border-radius: 20px;
788
+ }
789
+
790
+ /deep/ .ant-steps-item-title {
791
+ line-height: 20px;
792
+ }
793
+
794
+ // 分支步骤状态颜色
795
+ /deep/ .branch-step-gray .ant-steps-item-icon {
796
+ background-color: #bfbfbf !important;
797
+ border-color: #bfbfbf !important;
798
+ }
799
+
800
+ /deep/ .branch-step-blue .ant-steps-item-icon {
801
+ background-color: #1890ff !important;
802
+ border-color: #1890ff !important;
803
+ }
804
+
805
+ /deep/ .branch-step-green .ant-steps-item-icon {
806
+ background-color: #52c41a !important;
807
+ border-color: #52c41a !important;
808
+ }
809
+
810
+ /deep/ .branch-step-red .ant-steps-item-icon {
811
+ background-color: #ff4d4f !important;
812
+ border-color: #ff4d4f !important;
813
+ }
814
+
815
+ /deep/ .branch-step-yellow .ant-steps-item-icon {
816
+ background-color: #faad14 !important;
817
+ border-color: #faad14 !important;
818
+ }
819
+ }
820
+ }
821
+
822
+ @red: rgb(255, 77, 79);
823
+
824
+ .step-icon-filled {
825
+ position: absolute;
826
+ top: -2px;
827
+ left: -6px;
828
+ @blue: rgb(24, 144, 255);
829
+ @yellow: rgb(255, 164, 39);
830
+ @gray: rgb(191, 191, 191);
831
+ &.digital {
832
+ width: 28px;
833
+ line-height: 28px;
834
+ border-radius: 50%;
835
+ color: #ffffff;
836
+ &.blue {
837
+ background: @blue;
838
+ }
839
+ &.yellow {
840
+ background: @yellow;
841
+ }
842
+ &.gray {
843
+ background: @gray;
844
+ }
845
+ &.red {
846
+ background: @red;
847
+ }
848
+ }
849
+ &.icon {
850
+ font-size: 28px;
851
+ &.blue {
852
+ color: @blue;
853
+ }
854
+ &.yellow {
855
+ color: @yellow;
856
+ }
857
+ &.gray {
858
+ color: @gray;
859
+ }
860
+ }
861
+ }
862
+
863
+ .step-item {
864
+ cursor: pointer;
865
+ &:hover {
866
+ .ant-steps-item-description {
867
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
868
+ transform: translateY(-1px);
869
+ transition: all 0.3s ease;
870
+ }
871
+ p {
872
+ color: #262626;
873
+ }
874
+ }
875
+ .text-red {
876
+ color: @red !important;
877
+ }
878
+ }
879
+
880
+ .next-step-overdue {
881
+ /deep/ .ant-steps-item-tail::after {
882
+ background: @red;
883
+ }
884
+ }
885
+
886
+ // 新增样式:步骤标题容器
887
+ .step-title-container {
888
+ .condition-indicators {
889
+ margin-top: 8px;
890
+ display: flex;
891
+ align-items: center;
892
+ gap: 8px;
893
+
894
+ .condition-tag {
895
+ margin: 0;
896
+ }
897
+
898
+ .condition-help {
899
+ color: #999;
900
+ cursor: pointer;
901
+ &:hover {
902
+ color: #1890ff;
903
+ }
904
+ }
905
+ }
906
+ }
907
+
908
+ // 分支信息样式
909
+ .branch-info {
910
+ margin-top: 8px;
911
+ padding: 8px;
912
+ background: #f9f9f9;
913
+ border-radius: 4px;
914
+ border-left: 3px solid #722ed1;
915
+
916
+ .branch-title {
917
+ margin: 0 0 6px 0 !important;
918
+ font-weight: bold;
919
+ color: #722ed1;
920
+ font-size: 12px;
921
+ padding: 0 !important;
922
+ border: none !important;
923
+ }
924
+
925
+ .branch-options {
926
+ display: flex;
927
+ flex-wrap: wrap;
928
+ gap: 4px;
929
+
930
+ .branch-option-tag {
931
+ margin: 0;
932
+ font-size: 11px;
933
+ }
934
+ }
935
+ }
936
+
937
+ // 省略步骤样式
938
+ .more-steps-section {
939
+ flex-shrink: 0;
940
+ // margin-top: 24px;
941
+
942
+ .more-steps-item {
943
+ opacity: 0.7;
944
+
945
+ .more-steps-container {
946
+ .pending-tag {
947
+ margin-top: 8px;
948
+ margin-left: 0;
949
+ }
950
+ }
951
+
952
+ .more-steps-desc {
953
+ p {
954
+ // font-style: italic;
955
+ color: #999 !important;
956
+
957
+ &.steps-count {
958
+ font-weight: bold;
959
+ color: #666 !important;
960
+ }
961
+ }
962
+ }
963
+ }
964
+ }
965
+ }
966
+
967
+ // 分支步骤 Popover 样式
968
+ /deep/ .ant-popover {
969
+ .popover-title {
970
+ display: flex;
971
+ align-items: center;
972
+ gap: 6px;
973
+ font-weight: 600;
974
+ color: #1890ff;
975
+
976
+ .anticon {
977
+ font-size: 14px;
978
+ }
979
+ }
980
+
981
+ .branch-step-popover {
982
+ min-width: 200px;
983
+
984
+ .popover-item {
985
+ display: flex;
986
+ align-items: flex-start;
987
+ margin-bottom: 8px;
988
+
989
+ &:last-child {
990
+ margin-bottom: 0;
991
+ }
992
+
993
+ &.no-data {
994
+ justify-content: center;
995
+ font-style: italic;
996
+ color: #999;
997
+ }
998
+
999
+ .popover-label {
1000
+ font-weight: 600;
1001
+ color: #262626;
1002
+ min-width: 70px;
1003
+ flex-shrink: 0;
1004
+ }
1005
+
1006
+ .popover-value {
1007
+ color: #595959;
1008
+ word-break: break-all;
1009
+ flex: 1;
1010
+ }
1011
+ }
1012
+ }
1013
+ }
1014
+ </style>