vue2-client 1.15.84 → 1.15.86

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 (377) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.editorconfig +9 -9
  3. package/.env +20 -20
  4. package/.env.development +1 -1
  5. package/.env.iot +19 -19
  6. package/.env.message +19 -19
  7. package/.env.revenue +19 -19
  8. package/.env.runtime +19 -19
  9. package/.eslintrc.json +5 -5
  10. package/CLAUDE.md +89 -89
  11. package/README.md +65 -65
  12. package/babel.config.js +21 -21
  13. package/docs/Logic/345/207/275/346/225/260/344/275/277/347/224/250/347/233/270/345/205/263.md +46 -46
  14. package/docs/notice.md +22 -22
  15. package/docs//345/207/275/346/225/260/344/275/277/347/224/250/347/233/270/345/205/263.md +179 -179
  16. package/jest.config.js +22 -22
  17. package/package.json +111 -111
  18. package/src/App.vue +196 -196
  19. package/src/ReportView.js +13 -13
  20. package/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox.vue +532 -532
  21. package/src/base-client/components/common/AddressSearchCombobox/index.js +3 -3
  22. package/src/base-client/components/common/AmapMarker/index.js +3 -3
  23. package/src/base-client/components/common/CitySelect/CitySelect.vue +376 -376
  24. package/src/base-client/components/common/CitySelect/demo.vue +43 -43
  25. package/src/base-client/components/common/ColorPickerCombobox/ColorPickerCombobox.vue +99 -99
  26. package/src/base-client/components/common/ColorPickerCombobox/demo.vue +34 -34
  27. package/src/base-client/components/common/FormGroupEdit/FormGroupEdit.vue +146 -146
  28. package/src/base-client/components/common/JSONToTree/index.js +3 -3
  29. package/src/base-client/components/common/Upload/Upload.vue +323 -323
  30. package/src/base-client/components/common/XAddForm/index.js +3 -3
  31. package/src/base-client/components/common/XAddForm/index.md +61 -61
  32. package/src/base-client/components/common/XAddNativeForm/XAddNativeForm.vue +1169 -1169
  33. package/src/base-client/components/common/XAddNativeForm/demo.vue +54 -40
  34. package/src/base-client/components/common/XAddNativeForm/index.js +3 -3
  35. package/src/base-client/components/common/XAddReport/XAddReport.vue +212 -212
  36. package/src/base-client/components/common/XBadge/index.js +3 -3
  37. package/src/base-client/components/common/XBadge/index.md +39 -39
  38. package/src/base-client/components/common/XButtons/XButtons.vue +284 -284
  39. package/src/base-client/components/common/XCalendar/XCalendar.vue +369 -369
  40. package/src/base-client/components/common/XCalendar/index.md +284 -284
  41. package/src/base-client/components/common/XCard/index.js +3 -3
  42. package/src/base-client/components/common/XCard/index.md +43 -43
  43. package/src/base-client/components/common/XCardSet/XCardSet.vue +300 -300
  44. package/src/base-client/components/common/XCollapse/XCollapse.vue +354 -354
  45. package/src/base-client/components/common/XConversation/XConversation.vue +576 -576
  46. package/src/base-client/components/common/XConversation/XConversationDemo.vue +28 -28
  47. package/src/base-client/components/common/XDataCard/XDataCard.vue +629 -629
  48. package/src/base-client/components/common/XDatePicker/index.vue +276 -276
  49. package/src/base-client/components/common/XDescriptions/XDescriptions.vue +174 -174
  50. package/src/base-client/components/common/XDescriptions/XDescriptionsGroup.vue +314 -314
  51. package/src/base-client/components/common/XDescriptions/demo.vue +51 -51
  52. package/src/base-client/components/common/XForm/XForm.vue +420 -420
  53. package/src/base-client/components/common/XForm/XFormItem.vue +1474 -1390
  54. package/src/base-client/components/common/XForm/XTreeSelect.vue +276 -276
  55. package/src/base-client/components/common/XForm/demo.vue +105 -105
  56. package/src/base-client/components/common/XForm/index.js +3 -3
  57. package/src/base-client/components/common/XFormCol/XFormCol.vue +157 -157
  58. package/src/base-client/components/common/XFormCol/index.js +3 -3
  59. package/src/base-client/components/common/XFormCol/index.md +35 -35
  60. package/src/base-client/components/common/XFormGroup/XFormGroup.vue +301 -301
  61. package/src/base-client/components/common/XFormGroup/demo.vue +41 -41
  62. package/src/base-client/components/common/XFormTable/XFormTable.vue +938 -938
  63. package/src/base-client/components/common/XFormTable/demo.vue +87 -87
  64. package/src/base-client/components/common/XFormTable/index.js +3 -3
  65. package/src/base-client/components/common/XImportExcel/XImportExcel.vue +174 -174
  66. package/src/base-client/components/common/XImportExcel/index.js +3 -3
  67. package/src/base-client/components/common/XImportExcel/index.md +38 -38
  68. package/src/base-client/components/common/XInput/XInput.vue +128 -128
  69. package/src/base-client/components/common/XInput/index.js +3 -3
  70. package/src/base-client/components/common/XInput/index.md +97 -97
  71. package/src/base-client/components/common/XIntervalPicker/XIntervalPicker.vue +121 -121
  72. package/src/base-client/components/common/XLicensePlate/index.js +3 -3
  73. package/src/base-client/components/common/XLicensePlate/index.md +38 -38
  74. package/src/base-client/components/common/XPrint/Demo.vue +41 -41
  75. package/src/base-client/components/common/XPrint/PrintBill.vue +308 -308
  76. package/src/base-client/components/common/XRate/demo.vue +102 -102
  77. package/src/base-client/components/common/XRate/index.vue +149 -149
  78. package/src/base-client/components/common/XReport/XReport.vue +963 -963
  79. package/src/base-client/components/common/XReport/XReportDemo.vue +70 -70
  80. package/src/base-client/components/common/XReport/XReportTrGroup.vue +1005 -1005
  81. package/src/base-client/components/common/XReport/index.md +103 -103
  82. package/src/base-client/components/common/XReportDrawer/XReportDrawer.vue +201 -201
  83. package/src/base-client/components/common/XReportGrid/XReport.vue +1075 -1075
  84. package/src/base-client/components/common/XReportGrid/XReportDemo.vue +44 -44
  85. package/src/base-client/components/common/XReportGrid/XReportDesign.vue +620 -620
  86. package/src/base-client/components/common/XReportGrid/XReportTrGroup.vue +723 -723
  87. package/src/base-client/components/common/XReportGrid/print.js +184 -184
  88. package/src/base-client/components/common/XTab/XTab.vue +299 -299
  89. package/src/base-client/components/common/XTable/ExportExcel.vue +283 -283
  90. package/src/base-client/components/common/XTable/XTable.vue +1599 -1599
  91. package/src/base-client/components/common/XTable/XTableWrapper.vue +597 -597
  92. package/src/base-client/components/common/XTable/index.js +3 -3
  93. package/src/base-client/components/common/XTimeline/XTimeline.vue +358 -358
  94. package/src/base-client/components/common/XTimeline/index.md +191 -191
  95. package/src/base-client/components/common/XTree/XTreePro.vue +452 -452
  96. package/src/base-client/components/common/XTreeOne/index.js +3 -3
  97. package/src/base-client/components/common/XUploadFilesView/index.vue +485 -485
  98. package/src/base-client/components/his/XCharge/XCharge.vue +238 -238
  99. package/src/base-client/components/his/XCheckbox/XCheckbox.vue +105 -105
  100. package/src/base-client/components/his/XCheckbox/index.md +253 -253
  101. package/src/base-client/components/his/XHDescriptions/XHDescriptions.vue +430 -430
  102. package/src/base-client/components/his/XHDescriptions/index.md +217 -217
  103. package/src/base-client/components/his/XHisEditor/XHisEditor.vue +629 -629
  104. package/src/base-client/components/his/XHisEditor/diagnosisAutocomplete.js +263 -263
  105. package/src/base-client/components/his/XList/XList.vue +495 -495
  106. package/src/base-client/components/his/XQuestionnaire/XQuestionnaire.json +3 -3
  107. package/src/base-client/components/his/XQuestionnaire/XQuestionnaire.vue +106 -106
  108. package/src/base-client/components/his/XQuestionnaire/XQuestionnaireDemo.vue +51 -51
  109. package/src/base-client/components/his/XQuestionnaire/XQuestionnaireItem.vue +269 -269
  110. package/src/base-client/components/his/XRadio/XRadio.vue +125 -125
  111. package/src/base-client/components/his/XRadio/index.md +234 -234
  112. package/src/base-client/components/his/XSelect/XSelect.vue +72 -72
  113. package/src/base-client/components/his/XShiftSchedule/XShiftSchedule.vue +234 -234
  114. package/src/base-client/components/his/XShiftSchedule/dome.vue +29 -29
  115. package/src/base-client/components/his/XSidebar/XSidebar.vue +240 -240
  116. package/src/base-client/components/his/XTextCard/XTextCard.vue +207 -207
  117. package/src/base-client/components/his/XTimeSelect/XTimeSelect.vue +162 -162
  118. package/src/base-client/components/his/XTimeSelect/XTimeSelectDemo.vue +23 -23
  119. package/src/base-client/components/his/XTitle/README.md +113 -113
  120. package/src/base-client/components/his/XTitle/XTitle.vue +123 -123
  121. package/src/base-client/components/his/XTreeRows/TreeNode.vue +107 -107
  122. package/src/base-client/components/his/XTreeRows/XTreeRows.vue +307 -307
  123. package/src/base-client/components/his/threeTestOrders/dome.vue +68 -68
  124. package/src/base-client/components/his/threeTestOrders/editor.vue +111 -111
  125. package/src/base-client/components/his/threeTestOrders/textBox.vue +457 -457
  126. package/src/base-client/components/his/threeTestOrders/threeTestOrders.vue +475 -475
  127. package/src/base-client/components/layout/XPageView/RenderRow.vue +88 -88
  128. package/src/base-client/components/layout/XPageView/XErrorView.vue +22 -22
  129. package/src/base-client/components/layout/XPageView/XPageRowTemplate.vue +37 -37
  130. package/src/base-client/components/layout/XPageView/XPageView.vue +223 -223
  131. package/src/base-client/components/layout/XPageView/XTab/XTab.vue +96 -96
  132. package/src/base-client/components/layout/XPageView/XTab/index.js +3 -3
  133. package/src/base-client/components/layout/XPageView/componentTypes.js +22 -22
  134. package/src/base-client/components/layout/XPageView/index.js +2 -2
  135. package/src/base-client/components/layout/XPageView/index.md +96 -96
  136. package/src/base-client/components/system/DictionaryDetailsView/index.js +3 -3
  137. package/src/base-client/components/system/DictionaryDetailsView/index.md +41 -41
  138. package/src/base-client/components/system/LogDetailsView/LogDetailsView.vue +376 -376
  139. package/src/base-client/components/system/LogDetailsView/index.js +3 -3
  140. package/src/base-client/components/system/LogDetailsView/index.md +41 -41
  141. package/src/base-client/components/system/QueryParamsDetailsView/index.js +3 -3
  142. package/src/base-client/components/ticket/TicketDetailsView/TicketDetailsView.vue +807 -807
  143. package/src/base-client/components/ticket/TicketDetailsView/index.js +3 -3
  144. package/src/base-client/components/ticket/TicketDetailsView/index.md +29 -29
  145. package/src/base-client/components/ticket/TicketDetailsView/part/TicketDetailsFlow.vue +260 -260
  146. package/src/base-client/components/ticket/TicketDetailsView/part/index.js +3 -3
  147. package/src/base-client/components/ticket/TicketSubmitSuccessView/TicketSubmitSuccessView.vue +532 -532
  148. package/src/base-client/components/ticket/TicketSubmitSuccessView/index.js +3 -3
  149. package/src/base-client/components/ticket/TicketSubmitSuccessView/index.md +29 -29
  150. package/src/base-client/plugins/AppData.js +126 -126
  151. package/src/base-client/plugins/PagedList.js +177 -177
  152. package/src/base-client/plugins/__tests__/selectValueTypeHelper.test.js +154 -0
  153. package/src/base-client/plugins/authority-plugin.js +167 -167
  154. package/src/base-client/plugins/compatible/LoginServiceOA.js +20 -20
  155. package/src/base-client/plugins/i18n-extend.js +32 -32
  156. package/src/base-client/plugins/moment.js +8 -8
  157. package/src/base-client/plugins/selectValueTypeHelper.js +281 -0
  158. package/src/bootstrap.js +51 -51
  159. package/src/components/Ellipsis/Ellipsis.vue +65 -65
  160. package/src/components/Ellipsis/index.js +3 -3
  161. package/src/components/Ellipsis/index.md +38 -38
  162. package/src/components/FileImageItem/FileItem.vue +320 -320
  163. package/src/components/FileImageItem/FileItemGroup.vue +197 -197
  164. package/src/components/FileImageItem/ImageItem.vue +107 -107
  165. package/src/components/FileImageItem/index.js +4 -4
  166. package/src/components/FilePreview/FilePreview.vue +181 -181
  167. package/src/components/FilePreview/FilePreviewDemo.vue +30 -30
  168. package/src/components/FilePreview/index.js +3 -3
  169. package/src/components/HeightScanner/index.vue +615 -615
  170. package/src/components/STable/README.md +341 -341
  171. package/src/components/STable/index.js +558 -558
  172. package/src/components/TableSetting/TableSetting.vue +143 -143
  173. package/src/components/TableSetting/index.js +3 -3
  174. package/src/components/Trend/Trend.vue +41 -41
  175. package/src/components/Trend/index.js +3 -3
  176. package/src/components/Trend/index.less +41 -41
  177. package/src/components/Trend/index.md +45 -45
  178. package/src/components/_util/util.js +46 -46
  179. package/src/components/cache/AKeepAlive.js +179 -179
  180. package/src/components/exception/typeConfig.js +19 -19
  181. package/src/components/form/FormRow.vue +52 -52
  182. package/src/components/index.less +5 -5
  183. package/src/components/menu/Contextmenu.vue +84 -84
  184. package/src/components/menu/index.less +38 -38
  185. package/src/components/page/header/PageHeader.vue +64 -64
  186. package/src/components/page/header/index.less +40 -40
  187. package/src/components/result/Result.vue +77 -77
  188. package/src/components/setting/SettingItem.vue +26 -26
  189. package/src/components/setting/i18n.js +117 -117
  190. package/src/components/table/StandardTable.vue +141 -141
  191. package/src/components/table/advance/ActionColumns.vue +158 -158
  192. package/src/components/table/advance/ActionSize.vue +45 -45
  193. package/src/components/table/advance/AdvanceTable.vue +275 -275
  194. package/src/components/table/advance/SearchArea.vue +355 -355
  195. package/src/components/table/advance/index.js +2 -2
  196. package/src/components/table/api/ApiTable.vue +50 -50
  197. package/src/components/task/TaskGroup.vue +80 -80
  198. package/src/components/task/TaskItem.vue +26 -26
  199. package/src/components/tool/AvatarList.vue +68 -68
  200. package/src/components/tool/DetailList.vue +157 -157
  201. package/src/components/tool/Drawer.vue +142 -142
  202. package/src/components/tool/FooterToolBar.vue +30 -30
  203. package/src/components/tool/HeadInfo.vue +35 -35
  204. package/src/components/tool/TagSelect.vue +83 -83
  205. package/src/components/tool/TagSelectOption.vue +33 -33
  206. package/src/components/transition/PageToggleTransition.vue +97 -97
  207. package/src/config/default/admin.config.js +18 -18
  208. package/src/config/default/animate.config.js +21 -21
  209. package/src/config/default/index.js +6 -6
  210. package/src/config/index.js +3 -3
  211. package/src/config/replacer/index.js +10 -10
  212. package/src/config/replacer/resolve.config.js +67 -67
  213. package/src/expression/ExpressionRunner.js +26 -26
  214. package/src/expression/TestExpression.js +509 -509
  215. package/src/expression/core/Delegate.js +115 -115
  216. package/src/expression/core/Expression.js +1358 -1358
  217. package/src/expression/core/Program.js +932 -932
  218. package/src/expression/core/Token.js +27 -27
  219. package/src/expression/enums/ExpressionType.js +81 -81
  220. package/src/expression/enums/TokenType.js +11 -11
  221. package/src/expression/exception/BreakWayException.js +2 -2
  222. package/src/expression/exception/ContinueWayException.js +2 -2
  223. package/src/expression/exception/ExpressionException.js +28 -28
  224. package/src/expression/exception/ReturnWayException.js +14 -14
  225. package/src/expression/exception/ServiceException.js +22 -22
  226. package/src/expression/instances/LogicConsole.js +44 -44
  227. package/src/expression/ts/ExpressionRunner.ts +28 -28
  228. package/src/expression/ts/TestExpression.ts +509 -509
  229. package/src/expression/ts/core/Delegate.ts +114 -114
  230. package/src/expression/ts/core/Expression.ts +1309 -1309
  231. package/src/expression/ts/core/Program.ts +950 -950
  232. package/src/expression/ts/core/Token.ts +29 -29
  233. package/src/expression/ts/enums/ExpressionType.ts +81 -81
  234. package/src/expression/ts/enums/TokenType.ts +13 -13
  235. package/src/expression/ts/exception/BreakWayException.ts +2 -2
  236. package/src/expression/ts/exception/ContinueWayException.ts +2 -2
  237. package/src/expression/ts/exception/ExpressionException.ts +28 -28
  238. package/src/expression/ts/exception/ReturnWayException.ts +14 -14
  239. package/src/expression/ts/exception/ServiceException.ts +22 -22
  240. package/src/expression/ts/instances/JSONArray.ts +48 -48
  241. package/src/expression/ts/instances/JSONObject.ts +109 -109
  242. package/src/expression/ts/instances/LogicConsole.ts +32 -32
  243. package/src/layouts/AdminLayout.vue +176 -176
  244. package/src/layouts/BlankView.vue +79 -79
  245. package/src/layouts/ComponentLayoutOne.vue +47 -47
  246. package/src/layouts/GridView.vue +43 -43
  247. package/src/layouts/PageView.vue +55 -55
  248. package/src/layouts/footer/PageFooter.vue +49 -49
  249. package/src/layouts/header/HeaderAvatar.vue +64 -64
  250. package/src/layouts/header/HeaderSearch.vue +67 -67
  251. package/src/layouts/header/index.less +92 -92
  252. package/src/layouts/tabs/TabsView.vue +383 -383
  253. package/src/layouts/tabs/i18n.js +25 -25
  254. package/src/layouts/tabs/index.js +2 -2
  255. package/src/logic/LogicRunner.js +62 -62
  256. package/src/logic/TestLogic.js +13 -13
  257. package/src/logic/plugins/common/DateTools.js +35 -35
  258. package/src/logic/plugins/common/VueTools.js +30 -30
  259. package/src/logic/plugins/index.js +7 -7
  260. package/src/logic/ts/LogicRunner.ts +67 -67
  261. package/src/logic/ts/TestLogic.ts +13 -13
  262. package/src/main.js +33 -33
  263. package/src/mixins/formValidationMixin.js +46 -46
  264. package/src/mock/common/activityData.js +32 -32
  265. package/src/mock/common/index.js +89 -89
  266. package/src/mock/common/reportData.js +20 -20
  267. package/src/mock/common/tableData.js +118 -118
  268. package/src/mock/index.js +12 -12
  269. package/src/mock/project/index.js +17 -17
  270. package/src/mock/user/current.js +13 -13
  271. package/src/mock/user/login.js +39 -39
  272. package/src/mock/user/routes.js +61 -61
  273. package/src/mock/workplace/index.js +15 -15
  274. package/src/pages/LogicCallExample/index.vue +46 -46
  275. package/src/pages/ReportGrid/index.vue +76 -76
  276. package/src/pages/ReportView.vue +50 -50
  277. package/src/pages/WorkflowDetail/WorkFlowDemo.vue +47 -47
  278. package/src/pages/WorkflowDetail/WorkFlowDemo2.vue +204 -204
  279. package/src/pages/WorkflowDetail/WorkFlowDemo3.vue +203 -203
  280. package/src/pages/WorkflowDetail/WorkflowDetail.vue +391 -391
  281. package/src/pages/WorkflowDetail/WorkflowPageDetail/LeaveMessage.vue +388 -388
  282. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowBaseInformation.vue +415 -415
  283. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue +1766 -1765
  284. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandleReso.vue +975 -975
  285. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowPreview.vue +109 -109
  286. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowTimeline.vue +929 -929
  287. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkOrderParentDetails.vue +222 -222
  288. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkflowDetailResso.vue +243 -243
  289. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkflowLog.vue +188 -188
  290. package/src/pages/WorkflowDetail/WorkflowPageDetail/components/WorkflowPersonSelector.vue +109 -109
  291. package/src/pages/WorkflowDetail/WorkflowPageDetail/worklog.vue +97 -97
  292. package/src/pages/XPageViewExample/index.vue +149 -149
  293. package/src/pages/addressSelect/addressDemo.vue +24 -24
  294. package/src/pages/addressSelect/index.vue +270 -270
  295. package/src/pages/dashboard/workplace/i18n.js +40 -40
  296. package/src/pages/dashboard/workplace/index.js +2 -2
  297. package/src/pages/dashboard/workplace/index.less +59 -59
  298. package/src/pages/exception/403.vue +21 -21
  299. package/src/pages/exception/404.vue +24 -24
  300. package/src/pages/exception/500.vue +21 -21
  301. package/src/pages/login/index.js +2 -2
  302. package/src/pages/report/ReportTableHome.vue +28 -28
  303. package/src/pages/resourceManage/depListManage.vue +23 -23
  304. package/src/pages/resourceManage/funListManage.vue +23 -23
  305. package/src/pages/resourceManage/index.js +15 -15
  306. package/src/pages/resourceManage/resourceManageMain.vue +57 -57
  307. package/src/pages/resourceManage/roleListManage.vue +23 -23
  308. package/src/pages/resourceManage/staffListManage.vue +23 -23
  309. package/src/pages/system/file/Info.vue +56 -56
  310. package/src/pages/system/file/index.vue +317 -317
  311. package/src/pages/system/settings/index.vue +126 -126
  312. package/src/pages/userInfoDetailManage/FillCardRecordQuery/index.vue +77 -77
  313. package/src/pages/userInfoDetailManage/FillGasRecordQuery/index.vue +75 -75
  314. package/src/pages/userInfoDetailManage/InsuranceDetailQuery/index.vue +64 -64
  315. package/src/pages/userInfoDetailManage/MachineRecordQuery/index.vue +75 -75
  316. package/src/pages/userInfoDetailManage/OtherChargeRecordQuery/index.vue +75 -75
  317. package/src/pages/userInfoDetailManage/PriceAdjustments/index.vue +64 -64
  318. package/src/pages/userInfoDetailManage/UserChargeRecordQuery/index.vue +94 -94
  319. package/src/pages/userInfoDetailManage/UserException/index.vue +64 -64
  320. package/src/pages/userInfoDetailManage/UserHandRecordQuery/index.vue +87 -87
  321. package/src/pages/userInfoDetailManage/UserRecordQuery/index.vue +74 -74
  322. package/src/pages/userInfoDetailManage/index.vue +290 -290
  323. package/src/pages/userInfoDetailManage/uploadFilesHistory/ImagePreview.vue +101 -101
  324. package/src/pages/userInfoDetailManage/uploadFilesHistory/index.vue +129 -129
  325. package/src/pages/userInfoDetailManage/userInfoDetailQueryTabs.vue +144 -144
  326. package/src/plugins/HiPrintPlugin.js +164 -164
  327. package/src/router/async/router.map.js +126 -126
  328. package/src/router/guards.js +262 -262
  329. package/src/router/i18n.js +57 -57
  330. package/src/router.js +17 -17
  331. package/src/services/api/DictionaryDetailsViewApi.js +6 -6
  332. package/src/services/api/LogDetailsViewApi.js +10 -10
  333. package/src/services/api/QueryParamsDetailsViewApi.js +6 -6
  334. package/src/services/api/logininfor/index.js +6 -6
  335. package/src/services/api/manage.js +8 -8
  336. package/src/services/api/restTools.js +215 -215
  337. package/src/services/api/workFlow.js +57 -57
  338. package/src/services/apiService.js +16 -16
  339. package/src/services/dataSource.js +12 -12
  340. package/src/services/index.js +7 -7
  341. package/src/services/user.js +92 -92
  342. package/src/services/v3Api.js +116 -116
  343. package/src/store/index.js +5 -5
  344. package/src/store/mutation-types.js +4 -4
  345. package/src/theme/antd/ant-menu.less +2 -2
  346. package/src/theme/antd/ant-message.less +3 -3
  347. package/src/theme/antd/ant-table.less +22 -22
  348. package/src/theme/antd/ant-time-picker.less +3 -3
  349. package/src/theme/antd/index.less +3 -3
  350. package/src/theme/default/color.less +43 -43
  351. package/src/theme/default/index.less +3 -3
  352. package/src/theme/default/nprogress.less +76 -76
  353. package/src/theme/global.less +279 -279
  354. package/src/theme/index.less +5 -5
  355. package/src/theme/reportTable.less +58 -58
  356. package/src/theme/theme.less +1 -1
  357. package/src/utils/EncryptUtil.js +162 -162
  358. package/src/utils/Objects.js +25 -25
  359. package/src/utils/axios-interceptors.js +100 -100
  360. package/src/utils/colors.js +107 -107
  361. package/src/utils/common.js +10 -10
  362. package/src/utils/excel/Blob.js +180 -180
  363. package/src/utils/excel/Export2Excel.js +141 -141
  364. package/src/utils/filter.js +21 -21
  365. package/src/utils/i18n.js +80 -80
  366. package/src/utils/indexedDB.js +549 -549
  367. package/src/utils/microAppUtils.js +49 -49
  368. package/src/utils/request.js +395 -395
  369. package/src/utils/routerUtil.js +553 -553
  370. package/src/utils/themeUtil.js +100 -100
  371. package/test/Tree.spec.js +168 -168
  372. package/test/myDialog.spec.js +47 -47
  373. package/test/request.test.js +17 -17
  374. package/test/util.test.js +53 -53
  375. package/test/v3Api.test.js +1984 -1984
  376. package/tests/unit/ReportTable.spec.js +16 -16
  377. package/vue.config.js +222 -222
@@ -1,1765 +1,1766 @@
1
- <template>
2
- <div>
3
- <!-- 上方流程显示 -->
4
- <a-card :bordered="false" :loading="loading">
5
- <!-- 项目进度流程 -->
6
- <work-flow-timeline
7
- :current-step-id="currentStepId"
8
- :active-step-id="activeStepId"
9
- :workflow-id="workflowId"
10
- :steps="stepsForChild"
11
- :state="workflowState"
12
- change-able
13
- @activeStep="activeStep"/>
14
- </a-card>
15
- <!-- 无权访问的提示 -->
16
- <a-result v-show="!canSubmit" status="403" title="无权操作" sub-title="您没有访问该步骤的权限.">
17
- </a-result>
18
- <div v-show="canSubmit">
19
- <!-- 流程被退回的提示 -->
20
- <a-alert v-if="currentStep && currentStep.back && !(beforeStepActive || workflowState)" type="info" show-icon style="margin-bottom: 14px">
21
- <div slot="message">
22
- <span style="font-weight: bold">流程被退回</span>
23
- <span style="margin-left: 14px; font-size: 14px; color: rgba(0, 0, 0, 0.65)">请重新填写信息发起提交</span>
24
- </div>
25
- <div slot="description">
26
- <div>操作人:{{
27
- currentStep.back.f_operator
28
- }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;操作时间:{{ currentStep.back.f_date }}
29
- </div>
30
- <div>原因:{{ currentStep.back.f_notes }}</div>
31
- </div>
32
- </a-alert>
33
- <!-- 步骤内容主体 -->
34
- <a-tabs default-active-key="1" @change="note = ''" type="card">
35
- <a-tab-pane key="0" tab="步骤详情" v-if="beforeStepActive || workflowState">
36
- <a-card :bordered="false" :loading="loadingHistory" :body-style="{ paddingTop: 0 }">
37
- <!-- 当前步骤历史记录 -->
38
- <template v-if="showTab">
39
- <x-tab
40
- :compProp="{ buttonState: { add: false, edit: false, delete: false, import: false, extra: false }, disableAction: true }"
41
- :local-config="tabDesigner"
42
- :body-style="{ padding: 0 }"
43
- :tabBarGutter="24"
44
- :extra-data="{ workflowId: workflowId }"
45
- default-active-key="workFlowTab">
46
- <a-tab-pane
47
- :forceRender="true"
48
- v-if="formCompletedDataPreview"
49
- slot="extraBeforeTabs"
50
- key="workFlowTab"
51
- tab="表单"
52
- >
53
- <work-flow-preview :form-completed-data-preview="formCompletedDataPreview"/>
54
- </a-tab-pane>
55
- </x-tab>
56
- </template>
57
- <template v-else-if="formCompletedDataPreview">
58
- <work-flow-preview :form-completed-data-preview="formCompletedDataPreview"/>
59
- </template>
60
- <template v-else>
61
- <a-result status="404" title="暂无数据" sub-title="该步骤暂无数据。">
62
- </a-result>
63
- </template>
64
- </a-card>
65
- </a-tab-pane>
66
- <a-tab-pane key="1" tab="业务操作" v-else v-show="renderCurrentNode">
67
- <a-card :bordered="false" :loading="loadingHistory" :body-style="{ paddingTop: 0 }">
68
- <x-tab
69
- v-if="showTab"
70
- :compProp="{ buttonState: { extra: true }, disableAction: false }"
71
- :local-config="tabDesigner"
72
- :extra-data="{ workflowId:workflowId }"
73
- :body-style="{ padding: 0 }"
74
- :tabBarGutter="24"
75
- default-active-key="workFlowTab">
76
- <a-tab-pane
77
- :forceRender="true"
78
- v-if="showForm"
79
- slot="extraBeforeTabs"
80
- key="workFlowTab"
81
- tab="表单"
82
- >
83
- <x-add-native-form
84
- ref="xAddForm"
85
- @onSubmit="submitForm"/>
86
- </a-tab-pane>
87
- </x-tab>
88
- <x-add-native-form
89
- v-else
90
- ref="xAddForm"
91
- @x-form-item-emit-func="formItemEmitFunc"
92
- @onSubmit="submitForm"/>
93
- <a-divider/>
94
-
95
- <!-- 分支节点状态提示 -->
96
- <a-alert
97
- v-if="isBranchLastNode.isBranchNode && !isBranchLastNode.isLastBranch"
98
- type="info"
99
- show-icon
100
- style="margin-bottom: 16px"
101
- >
102
- <template slot="message">
103
- <span style="font-weight: bold;">分支节点等待中</span>
104
- </template>
105
- <template slot="description">
106
- <div>当前为分支节点,其他分支仍在进行中。完成当前分支后将等待其他分支完成,然后自动汇合到下一环节。</div>
107
- <div style="margin-top: 8px; color: #666; font-size: 12px;">
108
- <a-icon type="info-circle" style="margin-right: 4px;" />
109
- 提示:此操作无需选择下一环节负责人,系统将自动处理流程流转。
110
- </div>
111
- </template>
112
- </a-alert>
113
-
114
- <a-alert
115
- v-if="isBranchLastNode.isBranchNode && isBranchLastNode.isLastBranch"
116
- type="success"
117
- show-icon
118
- style="margin-bottom: 16px"
119
- >
120
- <template slot="message">
121
- <span style="font-weight: bold;">分支汇合节点</span>
122
- </template>
123
- <template slot="description">
124
- <div>您正在处理最后一个未完成的分支节点,完成后将触发分支汇合并进入下一环节。</div>
125
- </template>
126
- </a-alert>
127
-
128
- <a-form v-show="!lastStep" label-align="left" :label-col="{ span: 3 }" :wrapper-col="{ span: 13 }">
129
- <a-form-item v-if="showStepNextBtn" label="操作类型" required>
130
- <a-radio-group v-model="operationType" @change="handleOperationTypeChange">
131
- <a-radio value="submit">到下一步</a-radio>
132
- <a-radio value="skip">{{ stepNextBtnTitle }}</a-radio>
133
- </a-radio-group>
134
- </a-form-item>
135
-
136
- <!-- 智能分支人员选择组件 - 只有非分支节点或最后一个分支才显示 -->
137
- <WorkflowPersonSelector
138
- v-if="!isBranchLastNode.isBranchNode || isBranchLastNode.isLastBranch"
139
- ref="personSelector"/>
140
-
141
- <a-form-item
142
- v-if="!isBranchLastNode.isBranchNode || isBranchLastNode.isLastBranch"
143
- label="下一环节截止时间"
144
- :validate-status="deadlineValidateStatus"
145
- :help="deadlineHelp"
146
- required>
147
- <a-date-picker
148
- :getCalendarContainer="(triggerNode) => triggerNode.parentNode"
149
- v-model="deadline"
150
- value-format="YYYY-MM-DD HH:mm"
151
- format="YYYY-MM-DD HH:mm"
152
- :allow-clear="false"
153
- :show-today="false"
154
- show-time
155
- placeholder="请选择"/>
156
- </a-form-item>
157
- </a-form>
158
- <a-divider/>
159
- <!-- 备注信息 -->
160
- <a-textarea
161
- v-model="note"
162
- :auto-size="{ minRows: 3, maxRows: 5 }"
163
- placeholder="填写本环节备注事项"
164
- />
165
- <!-- 当前步骤完成后,控制流程按钮 -->
166
- <div style="text-align: center">
167
- <a-radio-group style="margin-top: 20px">
168
- <a-space>
169
- <!-- 动态按钮渲染 -->
170
- <template v-if="buttonGroup && buttonGroup.actionArr">
171
- <a-button
172
- v-for="(button, index) in buttonGroup.actionArr"
173
- :key="index"
174
- v-show="checkButtonVisible(button)"
175
- :type="button.type ? button.type : 'default'"
176
- @click="handleCustomButtonClick(button)"
177
- >
178
- {{ button.text }}
179
- </a-button>
180
- </template>
181
- <!-- 原有的按钮逻辑 -->
182
- <template>
183
- <a-popover v-if="operationType === 'submit'">
184
- <template slot="content">
185
- <p v-if="isBranchLastNode.isBranchNode && !isBranchLastNode.isLastBranch">
186
- 完成当前分支,等待其他分支完成后自动汇合
187
- </p>
188
- <p v-else-if="isBranchLastNode.isBranchNode && isBranchLastNode.isLastBranch">
189
- 完成最后一个分支并触发汇合到下一环节
190
- </p>
191
- <p v-else>{{ nextBtnTitle }}</p>
192
- </template>
193
- <a-button
194
- v-if="!lastStep"
195
- :disabled="!showNextBtn && !stepDone"
196
- type="primary"
197
- @click="nextClick"
198
- >
199
- 完成提交
200
- <a-icon type="right"/>
201
- </a-button>
202
- </a-popover>
203
- <a-popover v-else :title="stepNextBtnTitle">
204
- <template slot="content">
205
- <p v-for="(item,index) in stepNextBtnText" :key="index">{{ item }}</p>
206
- </template>
207
- <a-button
208
- v-if="showStepNextBtn && !lastStep"
209
- type="primary"
210
- @click="stepNextClick"
211
- >
212
- 跳过
213
- </a-button>
214
- </a-popover>
215
- <a-button
216
- v-if="lastStep"
217
- type="primary"
218
- @click="lastStepNextClick"
219
- >
220
- 确认完成
221
- </a-button>
222
- </template>
223
- </a-space>
224
- </a-radio-group>
225
- </div>
226
- </a-card>
227
- </a-tab-pane>
228
- <!-- 退回 -->
229
- <a-tab-pane v-if="canSubmit && !beforeStepActive && showPrevBtn && !workflowState" key="2" tab="退回">
230
- <a-form layout="vertical">
231
- <a-form-item label="退回原因" :wrapper-col="{ span: 24 }" required>
232
- <a-textarea
233
- v-model="backNote"
234
- :auto-size="{ minRows: 4, maxRows: 10 }"
235
- placeholder="请填写退回原因 / 备注"
236
- />
237
- </a-form-item>
238
- <a-form-item :wrapper-col="{ offset: 11 }">
239
- <a-popover :title="preBtnTitle">
240
- <template slot="content">
241
- <p v-for="(item,index) in preBtnText" :key="index">{{ item }}</p>
242
- </template>
243
- <a-button
244
- type="danger"
245
- @click="preClick"
246
- >
247
- <a-icon type="left"/>
248
- 退回
249
- </a-button>
250
- </a-popover>
251
- </a-form-item>
252
- </a-form>
253
- </a-tab-pane>
254
- <!-- <a-tab-pane v-if="canSubmit && !beforeStepActive && showPrevBtn && !workflowState" key="3" tab="工单拆分">
255
- &lt;!&ndash; 分配工单 &ndash;&gt;
256
- <workflow-list-resolution :workflow-project-id="workflowId" :details="details"></workflow-list-resolution>
257
- </a-tab-pane>-->
258
- </a-tabs>
259
- </div>
260
- </div>
261
- </template>
262
-
263
- <script>
264
- import XAddNativeForm from '@vue2-client/base-client/components/common/XAddNativeForm'
265
- import XFormTable from '@vue2-client/base-client/components/common/XFormTable/XFormTable'
266
- import XAddForm from '@vue2-client/base-client/components/common/XAddForm/XAddForm'
267
- import { postByServiceName } from '@vue2-client/services/api/restTools'
268
- import { workFlowViewApi } from '@vue2-client/services/api/workFlow'
269
- import { commonApi } from '@vue2-client/services/api'
270
- import { formatDate } from '@vue2-client/utils/util'
271
- import { mapState } from 'vuex'
272
- import moment from 'moment'
273
- import FilePreview from '@vue2-client/components/FilePreview'
274
- import WorkFlowTimeline from './WorkFlowTimeline.vue'
275
- import { FileItem, ImageItem } from '@vue2-client/components/FileImageItem'
276
- import WorkflowListResolution from './WorkflowListResolution'
277
- import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
278
- import * as util from '@vue2-client/utils/util'
279
- import { getConfigByNameAsync, runLogic } from '@vue2-client/services/api/common'
280
- import LogicRunner from '@vue2-client/logic/LogicRunner'
281
- import XTab from '@vue2-client/base-client/components/common/XTab/XTab.vue'
282
- import WorkFlowPreview from './WorkFlowPreview'
283
- import WorkflowPersonSelector from './components/WorkflowPersonSelector.vue'
284
-
285
- export default {
286
- name: 'WorkFlowHandle',
287
- provide () {
288
- return {
289
- formDataChange: this.handleFormDataChange,
290
- workflowHandleWrap: this.workflowHandleWrap
291
- }
292
- },
293
- components: {
294
- XTab,
295
- WorkflowListResolution,
296
- XAddNativeForm,
297
- WorkFlowTimeline,
298
- XFormTable,
299
- XAddForm,
300
- FilePreview,
301
- FileItem,
302
- ImageItem,
303
- WorkFlowPreview,
304
- WorkflowPersonSelector
305
- },
306
- computed: {
307
- workflowHandleWrap () {
308
- return this
309
- },
310
- ...mapState('account', { currUser: 'user' }),
311
- // 判断当前节点的分支状态
312
- isBranchLastNode () {
313
- // 默认返回值
314
- const defaultResult = {
315
- isBranchNode: false, // 是否为分支流程中的节点
316
- isLastBranch: false // 是否为最后一个未完成的分支(需要选择下一环节人员)
317
- }
318
-
319
- // 如果是最后一步或没有下一步或者不能提交,则不是分支节点
320
- if (this.lastStep || !this.nextBtnTo || !this.canSubmit) {
321
- return defaultResult
322
- }
323
-
324
- // 检查下一个节点是否为分支退出节点
325
- const nextStep = this.stepsDefine.find(step => step.id === this.nextBtnTo)
326
-
327
- // 如果下一个节点的flowRole不是branchExit,说明不是分支的最后一个节点
328
- if (!nextStep || nextStep.properties?.flowRole !== 'branchExit') {
329
- return defaultResult
330
- }
331
-
332
- // 获取分支退出节点等待的所有分支步骤ID
333
- const waitStepIds = nextStep.properties.waitStepIds || []
334
- if (waitStepIds.length === 0) {
335
- return {
336
- isBranchNode: true,
337
- isLastBranch: true // 如果没有等待步骤配置,默认认为是最后一个
338
- }
339
- }
340
-
341
- // 当前步骤必须在等待列表中才算是分支节点
342
- if (!waitStepIds.includes(this.activeStepId)) {
343
- return defaultResult
344
- }
345
-
346
- // 检查除当前步骤外的其他分支步骤状态
347
- const otherBranchSteps = waitStepIds.filter(stepId => stepId !== this.activeStepId)
348
-
349
- // 判断其他所有分支步骤是否都已完成(状态为2)
350
- const allOtherBranchesCompleted = otherBranchSteps.every(stepId => {
351
- const step = this.stepsForChild.find(s => s.id === stepId)
352
- return step && step.status === 2 // 已处理
353
- })
354
-
355
- return {
356
- isBranchNode: true, // 当前节点是分支流程中的节点
357
- isLastBranch: allOtherBranchesCompleted // 只有当其他所有分支都已完成时,当前分支才是最后一个
358
- }
359
- },
360
- canSubmit () {
361
- // 对于超级管理员直接认为可以提交
362
- if (this.currUser.rolesnames.indexOf('超级管理员') > -1) {
363
- return true
364
- }
365
- // currentStepId可能还没初始化,此处拿不到先返回false
366
- if (!this.currentStepId) {
367
- return false
368
- }
369
- const activeStep = this.stepsForChild.find(item => item.id === this.activeStepId)
370
- // 如果当前选中节点不是正在进行的节点,则判断viewers权限
371
- if (this.activeStepId !== this.currentStepId && activeStep != null && activeStep.status !== 1) {
372
- // 历史节点为当前登录人操作则不做查看数据权限校验
373
- if (activeStep.handler === this.currUser.name) {
374
- return true
375
- }
376
- const viewers = activeStep?.properties?.otherProperty?.viewers ?? []
377
- if (viewers.length > 0) {
378
- // 使用some方法判断当前人员是否满足任一条件
379
- return viewers.some(item => {
380
- if (item.type === 'role') {
381
- // 检查rolestr是否存在并包含指定角色
382
- return this.currUser.rolestr && this.currUser.rolestr.split(',').includes(item.name)
383
- }
384
- if (item.type === 'department') {
385
- // 检查depname是否存在并包含指定部门
386
- return this.currUser.deps && this.currUser.deps.includes(item.name)
387
- }
388
- return false
389
- })
390
- } else {
391
- return true
392
- }
393
- } else {
394
- // 当前进行节点的数据
395
- const step = this.stepsForChild.find(item => item.id === this.activeStepId)
396
- // 检查角色和部门权限
397
- if (step && step.properties && step.properties.chargePerson) {
398
- // 如果当前节点的负责人选项中有设置选择人员 当前节点就只能由上一步设置的负责人操作
399
- if (step.properties.chargePerson.needSelectPerson) {
400
- return step.handler === this.currUser.name
401
- }
402
- if (step.properties.chargePerson.personList && step.properties.chargePerson.personList.length > 0) {
403
- // 使用some方法判断当前人员是否满足任一条件
404
- return step.properties.chargePerson.personList.some(item => {
405
- if (item.type === 'role') {
406
- // 检查rolestr是否存在并包含指定角色
407
- return this.currUser.rolestr && this.currUser.rolestr.split(',').includes(item.name)
408
- }
409
- if (item.type === 'department') {
410
- // 检查parentname是否存在并包含指定部门
411
- return this.currUser.parentname && this.currUser.parentname.includes(item.name)
412
- }
413
- return false
414
- })
415
- }
416
- }
417
-
418
- // 检查handler是否包含当前用户
419
- if (step && step.handler) {
420
- return step.handler.includes(this.currUser.name)
421
- }
422
- }
423
- return false
424
- },
425
- formPreviewNoFileData () {
426
- return this.formCompletedDataPreview.data.filter(item => item.label !== '附件上传')
427
- }
428
- },
429
- data () {
430
- return {
431
- // 显示供用户填写的当前步骤表单
432
- showForm: false,
433
- // 显示步骤的tab页
434
- showTab: false,
435
- // tab页的配置
436
- tabDesigner: undefined,
437
- // 当前步骤表单标题
438
- formTitle: '',
439
- // 当前步骤表单定义Json
440
- stepDefine: [],
441
- // 当前步骤
442
- currentStep: undefined,
443
- // 当前步骤完成状态
444
- stepDone: false,
445
- // 所有节点连通数据
446
- directions: [],
447
- // 所有流程定义数据
448
- stepsDefine: [],
449
- // 当前步骤能通向的节点
450
- currentDirections: [],
451
- // 当前节点id
452
- currentStepId: undefined,
453
- // 当前活动节点 id
454
- activeStepId: 1,
455
- // 下一步按钮显示
456
- showNextBtn: false,
457
- // 回退按钮显示
458
- showPrevBtn: false,
459
- // 跳过按钮显示
460
- showStepNextBtn: false,
461
- // 定义三个按钮气泡提示内容
462
- preBtnTitle: '',
463
- preBtnText: [],
464
- nextBtnTitle: '',
465
- stepNextBtnTitle: '',
466
- stepNextBtnText: [],
467
- // 控制三个按钮跳转目标
468
- preBtnTo: undefined,
469
- nextBtnTo: undefined,
470
- stepNextBtnTo: undefined,
471
- // 控制当前标签页显示
472
- activeKey: '1',
473
- // 之前的节点是否激活
474
- beforeStepActive: false,
475
- // 之前节点中的数据
476
- beforeStepData: [],
477
- // 用于将已有数据,填回x-add表单中
478
- formCompletedData: {},
479
- // 用于展示提交的数据
480
- formCompletedDataPreview: null,
481
- // 切换至目标流程时,目标流程定义
482
- targetStepDefine: [],
483
- // 控制历史记录加载
484
- loadingHistory: true,
485
- // 控制表单校验
486
- formValid: false,
487
- // 备忘
488
- note: '',
489
- backNote: '', // 新增退回原因专用变量
490
- // 是否是最后一步
491
- lastStep: false,
492
- // 存储每一步骤填表人和时间数据
493
- stepsExtraInfo: [],
494
- // 控制基础信息页流程展示加载
495
- loading: true,
496
- createQueryVisible: false,
497
- // 激活的步骤名
498
- activeStepName: '',
499
- stepsParse: undefined,
500
- // 下一步处理人选择框数据
501
- chargePersonOptions: [],
502
- // 已选择下一步处理人
503
- checkedChargePerson: undefined,
504
- // 下一步截止时间
505
- deadline: this.getDefaultDeadline(),
506
- deadlineValidateStatus: '',
507
- deadlineHelp: '',
508
- // 当前任务是否延期
509
- // taskIsOverdue: moment().isAfter(this.completeTime, 'day'),
510
- // 操作类型,提交到下一步或跳过
511
- operationType: 'submit',
512
- // 获取微信推送是否成功信息
513
- information: [],
514
- // 是否需要选择人员
515
- needSelectPerson: true,
516
- // 下一环节人员信息
517
- chargePerson: {},
518
- // 动态按钮配置
519
- buttonGroup: null,
520
- // 分支节点相关数据
521
- branchNodes: [], // 需要选择人员的分支节点列表
522
- branchChargePersons: {}, // 分支节点人员选择 格式:{stepId: personId}
523
- needMultipleBranchSelection: false, // 是否需要为多个分支节点预先选择人员(包含WF_RESULT的条件分支或并行分支)
524
- calculatedTargetNode: null, // 前台计算出的目标节点
525
- currentBranchActions: null // 缓存当前的分支动作,避免重复调用
526
- }
527
- },
528
- async mounted () {
529
- await this.init()
530
- // this.checkDeadline()
531
- },
532
- props: {
533
- workflowId: {
534
- type: [String, Number],
535
- required: true
536
- },
537
- visible: {
538
- type: Boolean,
539
- default: false
540
- },
541
- stepsForChild: {
542
- type: Array,
543
- required: true
544
- },
545
- workflowState: {
546
- type: [Boolean, Number],
547
- required: true
548
- },
549
- completeTime: {
550
- type: String,
551
- required: true
552
- },
553
- taskName: {
554
- type: String,
555
- required: true
556
- },
557
- details: {
558
- type: Object,
559
- required: true
560
- },
561
- // 展开详情初始化的节点
562
- // 适用于不让第一个节点默认展示的情况
563
- initStepId: {
564
- type: [String, Number],
565
- required: false,
566
- default: null
567
- },
568
- renderCurrentNode: {
569
- type: Boolean,
570
- default: true
571
- }
572
- },
573
- methods: {
574
- init () {
575
- this.getCurrentStep()
576
- },
577
- onClose () {
578
- this.activeStepId = this.currentStepId
579
- this.checkedChargePerson = undefined
580
- this.loadingHistory = true
581
- this.currentStepId = undefined
582
- this.directions = []
583
- this.currentDirections = []
584
- this.preBtnText = []
585
- this.stepNextBtnText = []
586
- this.stepsExtraInfo = []
587
- this.loading = true
588
- this.showNextBtn = false
589
- this.showStepNextBtn = false
590
- this.showPrevBtn = false
591
- this.stepDone = false
592
- this.formCompletedData = {}
593
- this.formCompletedDataPreview = null
594
- this.operationType = 'submit'
595
-
596
- // 清理分支节点相关数据
597
- this.branchNodes = []
598
- this.branchChargePersons = {}
599
- this.needMultipleBranchSelection = false
600
- this.calculatedTargetNode = null
601
- this.currentBranchActions = null
602
- },
603
- async showQueryFormItemFunc () {
604
- if (this.attr.showQueryFormItemFunc) {
605
- const obj = executeStrFunctionByContext(this, this.attr.showQueryFormItemFunc, [this.form, this.setForm, this.attr, util, this.mode])
606
- // 判断是 bool 还是 obj 兼容
607
- if (typeof obj === 'boolean') {
608
- this.show = obj
609
- } else if (obj && typeof obj === 'object') {
610
- // obj 是一个对象,并且不是数组
611
- this.show = obj?.show
612
- this.readOnly = obj?.readOnly
613
- }
614
- } else {
615
- this.show = true
616
- }
617
- },
618
- // 获取单个步骤的定义
619
- getSingleStepDefine (name) {
620
- for (const step of this.stepsDefine) {
621
- if (name === step.name) {
622
- return step.properties.form
623
- }
624
- }
625
- },
626
- // 日期格式化
627
- format (date, format) {
628
- return formatDate(date, format)
629
- },
630
- // 获取当前步骤
631
- getCurrentStep () {
632
- return postByServiceName(workFlowViewApi.getWorkFlowCurrentSubState, {
633
- workflowId: this.workflowId
634
- })
635
- .then(res => {
636
- // 优先级:主动传入的 > 获取到的
637
- this.currentStepId = this.initStepId || res.id
638
-
639
- // 如果指定了具体节点,同时设置activeStepId
640
- if (this.initStepId) {
641
- this.activeStepId = this.initStepId
642
- }
643
-
644
- const currentStep = this.stepsForChild.find(item => item.id === this.currentStepId)
645
- res.state = currentStep.name
646
- // 获取到当前步骤后复制下一步时间
647
- this.deadline = this.getDefaultDeadline(currentStep.properties?.otherProperty?.nextNodeInterval)
648
- this.currentStep = res
649
- this.getDirection()
650
- }, err => {
651
- console.log(err)
652
- })
653
- },
654
- // 完工按钮
655
- async lastStepNextClick () {
656
- this.$refs.xAddForm.asyncSubmit().then((res) => {
657
- this.submitForm(res).then(_ => {
658
- postByServiceName(workFlowViewApi.afterWorkFlowFinalStepSubmit, {
659
- workflowId: this.workflowId,
660
- stepId: this.currentStepId,
661
- submitUser: this.currUser.name,
662
- submitUserId: this.currUser.id
663
- }).then(_res => {
664
- this.saveWorkflowLog('确认完工', '最后一步: ' + this.getStepNameByStepId(this.currentStepId), { notes: this.note.trim() })
665
- this.$message.success('已完工!')
666
- this.loading = true
667
- this.loadingHistory = true
668
- this.onClose()
669
- this.$emit('success', { note: this.note.trim(), form: res.realForm, workflowId: this.workflowId })
670
- },
671
- () => {
672
- this.$message.error('提交失败!')
673
- }
674
- )
675
- })
676
- }).catch(err => {
677
- if (err && err.message === 'Form validation failed') {
678
- this.$message.error('请检查表单必填项!')
679
- } else {
680
- // 对于其他错误,继续向上抛出
681
- throw err
682
- }
683
- })
684
- },
685
- // 三个按钮点击后逻辑
686
- async nextClick () {
687
- // 检查是否为非最后分支节点
688
- const branchStatus = this.isBranchLastNode
689
- const isWaitingBranch = branchStatus.isBranchNode && !branchStatus.isLastBranch
690
-
691
- const extraData = this.getApplyStepExtraData(this.nextBtnTo)
692
- if (!extraData) {
693
- return
694
- }
695
-
696
- // 确认对话框
697
- const confirmContent = isWaitingBranch
698
- ? '确定完成当前分支么?完成后将等待其他分支,不会立即流转到下一环节。'
699
- : '确定提交么?提交之后数据不可更改!'
700
-
701
- await new Promise(resolve => {
702
- this.$confirm({
703
- title: '提交确认',
704
- content: confirmContent,
705
- onOk () {
706
- resolve()
707
- return Promise.resolve()
708
- }
709
- })
710
- })
711
-
712
- this.$refs.xAddForm.asyncSubmit().then(res => {
713
- this.submitForm(res).then(_ => {
714
- extraData.form = res.realForm
715
- postByServiceName(workFlowViewApi.submitToNextStep, extraData)
716
- .then(
717
- () => {
718
- let successMessage = '提交成功'
719
-
720
- // 只有非等待分支节点才保存工作流日志
721
- if (!isWaitingBranch) {
722
- const branchStatus = this.isBranchLastNode
723
- const operation = branchStatus.isBranchNode && branchStatus.isLastBranch ? '分支汇合' : '提交'
724
-
725
- const extra = {
726
- setHandler: this.getStepHandler(),
727
- setDeadline: this.deadline,
728
- notes: this.note.trim()
729
- }
730
- this.saveWorkflowLog(operation, this.generateBranchStepChangeText(this.currentStepId), extra)
731
- } else {
732
- successMessage = '分支完成,等待其他分支完成后自动汇合'
733
- }
734
-
735
- this.$message.success(successMessage)
736
- this.loading = true
737
- this.loadingHistory = true
738
- this.$emit('nextClick', {
739
- note: this.note.trim(),
740
- form: res.realForm,
741
- workflowId: this.workflowId,
742
- isBranchWaiting: isWaitingBranch, // 标记是否为分支等待状态
743
- ...this.generateStepChange(this.currentStepId, this.nextBtnTo)
744
- })
745
- this.$emit('refresh')
746
- this.onClose()
747
- this.init()
748
- },
749
- err => {
750
- this.$message.error('提交失败!')
751
- console.log(err)
752
- }
753
- )
754
- })
755
- }).catch((err) => {
756
- if (err && err.message === 'Form validation failed') {
757
- this.$message.error('请检查表单必填项!')
758
- } else {
759
- // 对于其他错误,继续向上抛出
760
- throw err
761
- }
762
- })
763
- },
764
- async preClick () {
765
- const notes = this.backNote.trim()
766
- if (!notes) {
767
- this.$message.error('退回请在备注中填写理由')
768
- return
769
- }
770
- return postByServiceName(workFlowViewApi.updateWorkFlowState, {
771
- stepId: this.preBtnTo,
772
- workflowId: this.workflowId,
773
- type: 'back'
774
- })
775
- .then(
776
- res => {
777
- this.saveWorkflowLog('退回', this.generateStepChangeText(this.currentStepId, this.preBtnTo), { notes })
778
- this.$message.success('退回成功')
779
- this.backNote = '' // 退回成功后清空退回原因
780
- this.loading = true
781
- this.loadingHistory = true
782
- this.currentStepId = this.preBtnTo
783
- this.activeStepId = this.preBtnTo
784
-
785
- // 新增:重置按钮状态
786
- this.stepDone = false
787
- this.beforeStepActive = false
788
- this.operationType = 'submit'
789
-
790
- this.$emit('refresh')
791
- this.onClose()
792
- this.init()
793
- },
794
- err => {
795
- this.$message.error('退回失败!')
796
- console.log(err)
797
- }
798
- )
799
- },
800
- async stepNextClick () {
801
- const extraData = this.getApplyStepExtraData(this.stepNextBtnTo)
802
- if (!extraData) {
803
- return
804
- }
805
- this.$refs.xAddForm.asyncSubmit().then(res => {
806
- this.submitForm(res).then(_ => {
807
- extraData.form = res.realForm
808
- postByServiceName(workFlowViewApi.submitToNextStep, extraData)
809
- .then(
810
- () => {
811
- const extra = {
812
- setHandler: this.getStepHandler(),
813
- setDeadline: this.deadline,
814
- notes: this.note.trim()
815
- }
816
- this.saveWorkflowLog('跳过', this.generateBranchStepChangeText(this.currentStepId), extra)
817
- this.$message.success('提交成功')
818
- this.$emit('nextClick', {
819
- note: this.note.trim(),
820
- form: res.realForm,
821
- workflowId: this.workflowId,
822
- ...this.generateStepChange(this.currentStepId, this.stepNextBtnTo)
823
- })
824
- this.loading = true
825
- this.loadingHistory = true
826
- this.$emit('refresh')
827
- this.onClose()
828
- this.init()
829
- },
830
- err => {
831
- this.$message.error('提交失败!')
832
- console.log(err)
833
- }
834
- )
835
- })
836
- }).catch(err => {
837
- if (err && err.message === 'Form validation failed') {
838
- this.$message.error('请检查表单必填项!')
839
- } else {
840
- // 对于其他错误,继续向上抛出
841
- throw err
842
- }
843
- })
844
- },
845
- // 获取当前步骤节点,连通的节点
846
- async getDirection () {
847
- // 获取流程定义
848
- return postByServiceName(workFlowViewApi.getWorkFlowDefine, {
849
- id: this.workflowId
850
- })
851
- .then(async res => {
852
- this.directions = []
853
- this.stepsDefine = res.steps
854
- await this.resolveDirections()
855
- this.resolveStep()
856
- }, err => {
857
- console.log(err)
858
- })
859
- },
860
- // 分析当前节点,能通向的节点
861
- async resolveDirections () {
862
- const currentStep = this.stepsForChild.find(item => item.id === this.activeStepId)
863
- if (currentStep?.properties?.actions) {
864
- this.currentDirections = currentStep.properties.actions
865
- }
866
- // 判断是否是最后ige节点
867
- this.lastStep = (currentStep?.properties?.actions || []).filter(item => {
868
- return item.type !== 'back'
869
- }).length === 0
870
-
871
- // 处理跳转按钮
872
- this.workflowControl()
873
-
874
- if (!this.lastStep) {
875
- // 分析分支节点并设置智能人员选择
876
- await this.analyzeBranchNodes()
877
- }
878
- },
879
- // 根据步骤id获取步骤名称
880
- getStepNameByStepId (stepId) {
881
- return this.stepsDefine.find(item => item.id === stepId)?.name
882
- },
883
- // 根据步骤id获取步骤状态
884
- getStepStatusByStepId (stepId) {
885
- return this.stepsForChild.find(item => item.id === stepId)?.status
886
- },
887
- // 表单提交的回调
888
- submitForm (obj) {
889
- this.formValid = true
890
- const formData = obj.realForm
891
- const time = this.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
892
- return postByServiceName(workFlowViewApi.saveWorkFlowStepFormData, {
893
- workflowId: this.workflowId,
894
- stepId: this.activeStepId,
895
- form: formData,
896
- data: time,
897
- handler: this.currUser.name,
898
- note: this.note.trim()
899
- })
900
- .then(
901
- res => {
902
- console.log('表单提交成功')
903
- this.note = ''
904
- this.showForm = false
905
- this.stepDone = true
906
- },
907
- err => {
908
- this.$message.error('保存失败,请检查后重试')
909
- console.log(err)
910
- }
911
- )
912
- },
913
- // 获取当前步骤定义内容,构建组件
914
- async buildComp (stepId) {
915
- const properties = this.stepsDefine.find(item => item.id === stepId).properties
916
- // 表单的渲染
917
- if (properties.form && properties.form.formJson) {
918
- this.stepDefine = properties.form.formJson
919
- this.showForm = true
920
- this.$nextTick(() => {
921
- this.$refs.xAddForm && this.$refs.xAddForm.init({
922
- ...properties.form,
923
- businessType: '修改',
924
- formItems: this.stepDefine,
925
- layout: properties.form.xAddFormLayout,
926
- showSubmitBtn: false
927
- })
928
- // 初始化的数据渲染(流程第一步)
929
- console.log('导入的原始数据-json', this.stepsForChild[0].f_data)
930
- console.log('转换后的数据-对象', JSON.parse(this.stepsForChild[0].f_data))
931
- if (stepId === 1) {
932
- this.$refs.xAddForm.setForm(JSON.parse(this.stepsForChild[0].f_data))
933
- }
934
- })
935
- } else {
936
- this.showForm = false
937
- }
938
- // Tab的渲染
939
- this.tabDesigner = properties.tabDesigner
940
- this.showTab = !!this.tabDesigner
941
- // 以当前激活节点分析节点
942
- await this.resolveDirections()
943
- this.resolveStep()
944
- },
945
- // 根据当前节点,判断之后流程,以及按钮的显示
946
- workflowControl () {
947
- // 重置所有控制数据为默认值,避免之前状态的影响
948
- this.showNextBtn = false
949
- this.nextBtnTo = undefined
950
- this.nextBtnTitle = ''
951
- this.showStepNextBtn = false
952
- this.stepNextBtnTo = undefined
953
- this.stepNextBtnTitle = ''
954
- this.stepNextBtnText = []
955
- this.showPrevBtn = false
956
- this.preBtnTo = undefined
957
- this.preBtnTitle = ''
958
- this.preBtnText = []
959
-
960
- // 分类收集不同类型的actions
961
- const submitActions = this.currentDirections.filter(item => item.type === 'submit')
962
- const conditionalActions = this.currentDirections.filter(item => item.type === 'conditionalBranch')
963
- const parallelActions = this.currentDirections.filter(item => item.type === 'parallelBranch')
964
- const skipActions = this.currentDirections.filter(item => item.type === 'skip')
965
- const backActions = this.currentDirections.filter(item => item.type === 'back')
966
-
967
- // 处理提交按钮 - 优先级:submit > conditionalBranch > parallelBranch
968
- if (submitActions.length > 0) {
969
- this.showNextBtn = true
970
- this.nextBtnTo = submitActions[0].to // 如果有多个submit,取第一个作为主要跳转目标
971
- if (submitActions.length === 1) {
972
- this.nextBtnTitle = '进行下一环节:' + this.getStepNameByStepId(submitActions[0].to)
973
- } else {
974
- this.nextBtnTitle = '将进入以下环节:' + submitActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
975
- }
976
- } else if (conditionalActions.length > 0) {
977
- this.showNextBtn = true
978
- this.nextBtnTo = conditionalActions[0].to // 条件分支的第一个作为主要跳转目标
979
- const targetSteps = conditionalActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
980
- this.nextBtnTitle = '后台判断后跳转到:' + targetSteps
981
- } else if (parallelActions.length > 0) {
982
- this.showNextBtn = true
983
- this.nextBtnTo = parallelActions[0].to // 并行分支的第一个作为主要跳转目标
984
- const targetSteps = parallelActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
985
- this.nextBtnTitle = '将并行进入以下分支:' + targetSteps
986
- }
987
-
988
- // 处理跳过按钮
989
- if (skipActions.length > 0) {
990
- this.showStepNextBtn = true
991
- this.stepNextBtnTo = skipActions[0].to
992
- if (skipActions.length === 1) {
993
- this.stepNextBtnTitle = '跳至:' + this.getStepNameByStepId(skipActions[0].to) + '环节'
994
- this.stepNextBtnText.push('将跳过以下环节:')
995
- for (let i = this.currentStepId; i < skipActions[0].to - 1; i++) {
996
- this.stepNextBtnText.push(this.stepsDefine[i].name)
997
- }
998
- } else {
999
- const targetSteps = skipActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
1000
- this.stepNextBtnTitle = '跳至:' + targetSteps
1001
- }
1002
- }
1003
-
1004
- // 处理退回按钮
1005
- if (backActions.length > 0) {
1006
- const backAction = backActions[0] // 通常只有一个back action
1007
- for (let i = this.currentStepId - 1; i > 0; i--) {
1008
- if (backAction.to >= i && this.stepsForChild.find(item => item.id === i)?.handler) {
1009
- this.preBtnTo = i
1010
- this.preBtnTitle = '回退至:' + this.getStepNameByStepId(i) + '环节'
1011
- this.showPrevBtn = true
1012
- break
1013
- } else {
1014
- this.preBtnText.push(this.getStepNameByStepId(i))
1015
- }
1016
- }
1017
- if (this.preBtnText.length) {
1018
- this.preBtnText.unshift('将跳过以下环节:')
1019
- }
1020
- }
1021
- },
1022
- // 获取表单字段实际值
1023
- getRealKey (key, isHandleFormKey) {
1024
- if (key === 'selected_id') return key
1025
- if (isHandleFormKey) {
1026
- return key.substring(key.indexOf('_') + 1)
1027
- } else {
1028
- return key
1029
- }
1030
- },
1031
- // 加载完成
1032
- resolveStep () {
1033
- // 获取当前步骤的按钮组配置
1034
- const currentStep = this.stepsDefine.find(item => item.id === this.activeStepId)
1035
- if (currentStep && currentStep.properties && currentStep.properties.buttonGroup) {
1036
- this.buttonGroup = currentStep.properties.buttonGroup
1037
- } else {
1038
- this.buttonGroup = null
1039
- }
1040
- this.loading = false
1041
- },
1042
- // 流程图组件给当前组件传值用,stepNo为用户点击的非当前步骤
1043
- async activeStep (stepId) {
1044
- // 开启加载
1045
- this.loadingHistory = true
1046
- this.activeStepId = stepId
1047
- // 获取激活节点的步骤名
1048
- this.activeStepName = this.getStepNameByStepId(stepId)
1049
- const status = this.getStepStatusByStepId(stepId)
1050
- // 清空回显数据
1051
- this.formCompletedData = {}
1052
- let formCompletedDataPreview = null
1053
- this.getStepDefine(stepId)
1054
- // 判断激活的节点,是不是待完成节点 (当前节点或者 当前节点状态是1的)
1055
- if ((this.activeStepName !== this.currentStep.state && status !== 1) || this.workflowState) {
1056
- // 获取激活节点历史数据,和字段定义
1057
- await this.getCompletedFormData(stepId)
1058
- // 将回显数据拷贝,避免引用传递
1059
- formCompletedDataPreview = JSON.parse(JSON.stringify(this.formCompletedData))
1060
- // 使用字段定义中内容,将回显数据的列名,替换为定义的中文名
1061
- // 调整逻辑:formData中只显示流程配置表单中定义过的显示列,多余冗余的存储列不进行页面展示
1062
- const formDataArr = []
1063
- const dataObj = formCompletedDataPreview.data || {}
1064
- const currentStepDefine = this.stepsDefine.find(item => item.id === stepId)
1065
- const isKeyHandle = currentStepDefine?.properties?.form?.isKeyHandle || false
1066
- for (let i = 0; i < this.targetStepDefine.length; i++) {
1067
- const stepDefine = this.targetStepDefine[i]
1068
- const key = this.getRealKey(stepDefine.model, isKeyHandle)
1069
- if (Object.prototype.hasOwnProperty.call(dataObj, key)) {
1070
- // 历史表单内容根据流程中配置的表单项顺序进行渲染,需要兼容表单项展示函数
1071
- if (stepDefine.showFormItemFunc && !(await this.showFormItemFunc(stepDefine.showFormItemFunc, dataObj))) {
1072
- continue
1073
- }
1074
- let value = dataObj[key]
1075
- // 字典值处理
1076
- if (stepDefine.formType === 'select' && stepDefine.selectType === 'key') {
1077
- const dictList = this.$appdata.getDictionaryList(stepDefine.selectKey)
1078
- const dictItem = dictList.find(item => item.value === value)
1079
- value = dictItem ? dictItem.label : value
1080
- }
1081
- formDataArr.push({ label: stepDefine.name, value })
1082
- }
1083
- }
1084
- // 如果一个都没匹配上,兼容旧逻辑:把原对象转成数组
1085
- if (formDataArr.length === 0 && dataObj && typeof dataObj === 'object') {
1086
- for (const key in dataObj) {
1087
- if (Object.prototype.hasOwnProperty.call(dataObj, key)) {
1088
- formDataArr.push({ label: key, value: dataObj[key] })
1089
- }
1090
- }
1091
- }
1092
- formCompletedDataPreview.data = formDataArr.length > 0 ? formDataArr : null
1093
- if (!formCompletedDataPreview.data) {
1094
- formCompletedDataPreview.data = null
1095
- }
1096
- // 备注
1097
- formCompletedDataPreview.note = this.stepsForChild.find(item => item.id === stepId)?.note
1098
- if (!formCompletedDataPreview.data && !formCompletedDataPreview.files.length && !formCompletedDataPreview.images.length && !formCompletedDataPreview.note) {
1099
- formCompletedDataPreview = {}
1100
- }
1101
- this.formCompletedDataPreview = formCompletedDataPreview
1102
- // 渲染已完成步骤的Tab
1103
- const properties = this.stepsDefine.find(item => item.id === stepId).properties
1104
- this.tabDesigner = properties.tabDesigner
1105
- this.showTab = !!this.tabDesigner
1106
- // 完成
1107
- this.loadingHistory = false
1108
- this.beforeStepActive = this.activeStepName !== this.currentStep.state
1109
- } else {
1110
- this.loadingHistory = false
1111
- this.beforeStepActive = false
1112
- if (!this.renderCurrentNode) {
1113
- this.formCompletedDataPreview = null
1114
- this.showTab = false
1115
- this.beforeStepActive = true
1116
- return
1117
- }
1118
- await this.buildComp(this.activeStepId)
1119
- }
1120
- },
1121
- // 获取已经完成步骤的数据
1122
- getCompletedFormData (stepId) {
1123
- return postByServiceName(workFlowViewApi.getWorkFlowCompletedStepData, {
1124
- workflowId: this.workflowId,
1125
- stepId: stepId
1126
- })
1127
- .then(
1128
- res => {
1129
- this.formCompletedData = res
1130
- },
1131
- err => {
1132
- console.log(err)
1133
- }
1134
- )
1135
- },
1136
- // 获取步骤定义表中,当前步骤定义
1137
- getStepDefine (stepId) {
1138
- const stepName = this.getStepNameByStepId(stepId)
1139
- this.targetStepDefine = this.getSingleStepDefine(stepName).formJson
1140
- },
1141
- // 获取提交步骤的额外数据
1142
- getApplyStepExtraData (stepId) {
1143
- // 验证人员选择 - 只有在需要显示人员选择器的情况下才验证
1144
- // 判断条件与模板中的 WorkflowPersonSelector 显示条件保持一致
1145
- const shouldShowPersonSelector = !this.isBranchLastNode.isBranchNode || this.isBranchLastNode.isLastBranch
1146
-
1147
- if (shouldShowPersonSelector && this.needSelectPerson) {
1148
- if (this.needMultipleBranchSelection) {
1149
- // 多分支情况:检查是否所有需要的分支都选择了人员
1150
- const missingSelections = this.branchNodes.filter(node => !this.branchChargePersons[node.stepId])
1151
- if (missingSelections.length > 0) {
1152
- const missingNames = missingSelections.map(node => node.stepName).join('、')
1153
- this.$message.error(`请设置以下节点的处理人:${missingNames}`)
1154
- return false
1155
- }
1156
- } else {
1157
- // 单分支情况:检查是否选择了处理人
1158
- if (!this.checkedChargePerson) {
1159
- this.$message.error('请设置下一环节处理人')
1160
- return false
1161
- }
1162
- }
1163
- }
1164
-
1165
- // 统一获取目标步骤IDs
1166
- let stepIds = []
1167
- if (this.needMultipleBranchSelection) {
1168
- // 多分支选择:获取所有分支的目标stepId(包括不需要选择人员的)
1169
- if (!this.currentBranchActions) {
1170
- this.currentBranchActions = this.getBranchActions()
1171
- }
1172
- stepIds = this.currentBranchActions.allBranchActions.map(action => action.to)
1173
- } else if (this.calculatedTargetNode) {
1174
- // 条件判断节点:使用计算出的目标节点
1175
- stepIds = [this.calculatedTargetNode]
1176
- } else {
1177
- // 普通节点:使用传入的stepId
1178
- stepIds = [stepId]
1179
- }
1180
- // 处理bug 有的时候没带到 branchChargePersons 先循环在同一地方处理
1181
- stepIds.forEach(stepItemId => {
1182
- if (!this.branchChargePersons[stepItemId]) {
1183
- const chargePerson = this.stepsForChild.find(item => item.id === stepItemId)?.properties?.chargePerson
1184
- this.branchChargePersons[stepItemId] = this.normalizeChargePersonFormat(chargePerson)
1185
- }
1186
- })
1187
-
1188
- return {
1189
- workflowId: this.workflowId,
1190
- activeStepId: this.activeStepId,
1191
- stepId, // 当前步骤ID
1192
- stepIds, // 目标步骤IDs数组(统一格式)
1193
- name: this.getStepNameByStepId(stepId),
1194
- handler: this.getStepHandler(),
1195
- handlerId: this.checkedChargePerson,
1196
- needSelectPerson: this.needSelectPerson,
1197
- personList: this.chargePerson.personList,
1198
- deadline: this.deadline,
1199
- submitUser: this.currUser.name,
1200
- submitUserId: this.currUser.id,
1201
- branchChargePersons: this.branchChargePersons
1202
- }
1203
- },
1204
-
1205
- // 获取当前选择的负责人
1206
- getStepHandler () {
1207
- let stepHandler
1208
-
1209
- if (this.needMultipleBranchSelection) {
1210
- // 多分支情况:返回所有分支的人员信息
1211
- const selectedHandlers = []
1212
- for (const node of this.branchNodes) {
1213
- const branchData = this.branchChargePersons[node.stepId]
1214
-
1215
- if (node.needSelectPerson && branchData) {
1216
- // 需要选择人员的分支:显示选中的人员
1217
- const personName = this.getBranchPersonName(node.stepId)
1218
- selectedHandlers.push(`${node.stepName}:${personName}`)
1219
- } else if (!node.needSelectPerson && branchData) {
1220
- // 不需要选择人员的分支:显示角色/部门配置
1221
- if (branchData.personList && branchData.personList.length > 0) {
1222
- const personNames = branchData.personList.map(item => item.name).join(',')
1223
- selectedHandlers.push(`${node.stepName}:${personNames}`)
1224
- }
1225
- }
1226
- }
1227
- stepHandler = selectedHandlers.join(';')
1228
- } else {
1229
- // 单分支情况:原有逻辑
1230
- if (this.checkedChargePerson) {
1231
- // 使用 value 找到对应的 label
1232
- stepHandler = this.chargePersonOptions.find(item => item.value === this.checkedChargePerson)?.label
1233
- } else if (this.chargePerson.personList && this.chargePerson.personList.length > 0) {
1234
- stepHandler = this.chargePerson.personList.map(item => item.name).join(',')
1235
- }
1236
- }
1237
-
1238
- return stepHandler
1239
- },
1240
-
1241
- // 获取分支节点选择的人员姓名
1242
- getBranchPersonName (stepId) {
1243
- const personId = this.branchChargePersons[stepId]
1244
- if (!personId) return ''
1245
-
1246
- const node = this.branchNodes.find(n => n.stepId === stepId)
1247
- if (!node) return ''
1248
-
1249
- const person = node.chargePersonOptions.find(p => p.value === personId)
1250
- return person ? person.label : ''
1251
- },
1252
- // 生成工作流日志步骤变化描述
1253
- generateStepChangeText (fromStepId, toStepId) {
1254
- // 如果是多分支场景,toStepId可能是数组或字符串
1255
- if (this.needMultipleBranchSelection && Array.isArray(toStepId)) {
1256
- const targetSteps = toStepId.map(id => `第${id}步: ${this.getStepNameByStepId(id)}`).join('、')
1257
- return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> ${targetSteps}`
1258
- } else if (this.needMultipleBranchSelection && typeof toStepId === 'string') {
1259
- // 如果传入的是目标步骤名称字符串
1260
- return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> ${toStepId}`
1261
- } else {
1262
- // 单步骤场景
1263
- return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> 第${toStepId}步: ${this.getStepNameByStepId(toStepId)}`
1264
- }
1265
- },
1266
-
1267
- // 生成多分支场景的步骤变化描述
1268
- generateBranchStepChangeText (fromStepId) {
1269
- if (!this.needMultipleBranchSelection) {
1270
- return this.generateStepChangeText(fromStepId, this.nextBtnTo)
1271
- }
1272
-
1273
- // 使用所有分支节点生成描述
1274
- const targetSteps = this.branchNodes.map(node => `第${node.stepId}步: ${node.stepName}`).join('、')
1275
- return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> ${targetSteps}`
1276
- },
1277
-
1278
- generateStepChange (fromStepId, toStepId) {
1279
- // 获取目标步骤IDs数组
1280
- let stepIds = []
1281
-
1282
- if (this.needMultipleBranchSelection) {
1283
- // 多分支选择:获取所有分支的目标stepId(包括不需要选择人员的)
1284
- if (!this.currentBranchActions) {
1285
- this.currentBranchActions = this.getBranchActions()
1286
- }
1287
- stepIds = this.currentBranchActions.allBranchActions.map(action => action.to)
1288
- } else if (this.calculatedTargetNode) {
1289
- // 条件判断节点:使用计算出的目标节点
1290
- stepIds = [this.calculatedTargetNode]
1291
- } else {
1292
- // 普通节点:使用传入的stepId
1293
- stepIds = [toStepId]
1294
- }
1295
-
1296
- return {
1297
- formStepId: fromStepId,
1298
- fromStepId,
1299
- stepId: toStepId,
1300
- stepIds: stepIds,
1301
- formStep: this.getStepNameByStepId(fromStepId),
1302
- fromStep: this.getStepNameByStepId(fromStepId),
1303
- toStep: this.getStepNameByStepId(stepIds[0]),
1304
- successStepId: this.stepsDefine[this.stepsDefine.length - 1].id,
1305
- successStep: this.stepsDefine[this.stepsDefine.length - 1].name
1306
- }
1307
- },
1308
- // 保存工作流日志
1309
- saveWorkflowLog (operation, desc, extra) {
1310
- postByServiceName(workFlowViewApi.saveWorkFlowLog, {
1311
- workflowId: this.workflowId,
1312
- operation,
1313
- desc,
1314
- operator: this.currUser.name,
1315
- notes: '',
1316
- setHandler: '',
1317
- setDeadline: '',
1318
- ...extra
1319
- })
1320
- },
1321
- // 获取默认截止时间
1322
- getDefaultDeadline (day = 1) {
1323
- const date = new Date()
1324
- date.setDate(date.getDate() + day)
1325
- date.setHours(date.getHours() + 2)
1326
- return formatDate(date, 'yyyy-MM-dd hh:mm')
1327
- },
1328
- // 验证截止时间
1329
- checkDeadline (alert) {
1330
- const deadline = moment(this.deadline)
1331
- if (deadline.isAfter(this.completeTime, 'day')) {
1332
- this.deadlineValidateStatus = 'error'
1333
- this.deadlineHelp = '不能超过任务限定完成时间'
1334
- if (alert) {
1335
- this.$message.error('下一环节截止时间不能超过任务限定完成时间,如需设置,请联系发起人修改任务完成时间', 5)
1336
- }
1337
- return false
1338
- }
1339
- if (deadline.diff(moment(), 'hours') < 1) {
1340
- this.deadlineValidateStatus = 'error'
1341
- this.deadlineHelp = '下一环节截止时间只能在1小时之后'
1342
- return false
1343
- }
1344
- this.deadlineValidateStatus = 'success'
1345
- this.deadlineHelp = ''
1346
- return true
1347
- },
1348
- // 操作类型单选按钮改变时重新设置负责人选择框
1349
- async handleOperationTypeChange (event) {
1350
- this.checkedChargePerson = undefined
1351
- this.branchChargePersons = {}
1352
- // 重新分析分支节点
1353
- await this.analyzeBranchNodes()
1354
- },
1355
-
1356
- // 处理动态按钮点击
1357
- handleCustomButtonClick (button) {
1358
- try {
1359
- // 执行自定义函数
1360
- if (button.func) {
1361
- const result = executeStrFunctionByContext(this, button.func, [this.details, this.$refs.xAddForm.form, util, runLogic, getConfigByNameAsync])
1362
- if (result) {
1363
- // 如果返回true,执行对应的操作
1364
- if (button.text === '提交') {
1365
- this.nextClick()
1366
- } else if (button.text === '取消') {
1367
- this.$emit('cancel')
1368
- }
1369
- }
1370
- }
1371
- } catch (error) {
1372
- console.error('执行自定义按钮函数失败:', error)
1373
- this.$message.error('执行操作失败')
1374
- }
1375
- },
1376
- // 表单项展示函数--用来渲染历史节点数据时和填写表单显示内容同步
1377
- async showFormItemFunc (func, form) {
1378
- try {
1379
- if (func) {
1380
- // 一般展示项函数中不应该存在setForm函数 先屏蔽this.setForm和this.attr的传递还有在this上下文指向不同可能存在异常 流程表单的展示项函数尽量功能单一。有异常默认进行展示(先兼容到这后续有问题进行调整)
1381
- const obj = executeStrFunctionByContext(this, func, [form, null, null, util, '新增/修改'])
1382
- // 判断是 bool 还是 obj 兼容
1383
- if (typeof obj === 'boolean') {
1384
- return obj
1385
- } else if (obj && typeof obj === 'object') {
1386
- // obj 是一个对象,并且不是数组
1387
- return obj?.show
1388
- }
1389
- } else {
1390
- return true
1391
- }
1392
- } catch (e) {
1393
- return true
1394
- }
1395
- },
1396
- // 检查按钮是否显示
1397
- checkButtonVisible (button) {
1398
- try {
1399
- if (button.customFunction) {
1400
- return executeStrFunctionByContext(this, button.customFunction, [this.details, this.$refs.xAddForm.form, util, runLogic, getConfigByNameAsync])
1401
- }
1402
- return true
1403
- } catch (error) {
1404
- console.error('执行按钮显示函数失败:', error)
1405
- return false
1406
- }
1407
- },
1408
- formItemEmitFunc (func, data, value) {
1409
- this.$emit('x-form-item-emit-func', func, data, value)
1410
- },
1411
- // 检查条件表达式是否包含WF_RESULT
1412
- checkIfContainsWfResult () {
1413
- const conditionalActions = this.currentDirections.filter(action => action.type === 'conditionalBranch')
1414
- if (conditionalActions.length === 0) return false
1415
-
1416
- return conditionalActions.some(action => {
1417
- return action.expression && action.expression.includes('WF_RESULT')
1418
- })
1419
- },
1420
-
1421
- // 分析分支节点并设置选择框
1422
- async analyzeBranchNodes () {
1423
- try {
1424
- // 初始化状态
1425
- this.resetBranchSelection()
1426
-
1427
- // 获取所有类型的分支动作并缓存
1428
- this.currentBranchActions = this.getBranchActions()
1429
-
1430
- // 如果没有任何分支,处理普通流程
1431
- if (!this.currentBranchActions.hasAnyBranch) {
1432
- await this.handleNormalFlow()
1433
- return
1434
- }
1435
-
1436
- // 判断是否需要多分支选择
1437
- this.needMultipleBranchSelection = this.shouldUseMultipleBranchSelection(this.currentBranchActions)
1438
-
1439
- if (this.needMultipleBranchSelection) {
1440
- // 需要为多个分支预先选择人员
1441
- await this.setupMultipleBranchSelection(this.currentBranchActions.allBranchActions)
1442
- } else {
1443
- // 可以前台实时计算的条件分支
1444
- await this.calculateTargetNodeFromForm(this.currentBranchActions.conditionalActions)
1445
- }
1446
- } catch (error) {
1447
- console.error('分析分支节点失败:', error)
1448
- this.$message.error('工作流分支配置异常,请联系管理员')
1449
- // 降级处理:使用普通流程
1450
- await this.handleNormalFlow()
1451
- }
1452
- },
1453
-
1454
- // 重置分支选择状态
1455
- resetBranchSelection () {
1456
- this.branchNodes = []
1457
- this.branchChargePersons = {}
1458
- this.needMultipleBranchSelection = false
1459
- this.calculatedTargetNode = null
1460
- this.currentBranchActions = null
1461
- },
1462
-
1463
- // 获取分支动作分类
1464
- getBranchActions () {
1465
- const conditionalActions = this.currentDirections.filter(action => action.type === 'conditionalBranch')
1466
- const parallelActions = this.currentDirections.filter(action => action.type === 'parallelBranch')
1467
- const allBranchActions = [...conditionalActions, ...parallelActions]
1468
-
1469
- return {
1470
- conditionalActions,
1471
- parallelActions,
1472
- allBranchActions,
1473
- hasAnyBranch: allBranchActions.length > 0
1474
- }
1475
- },
1476
-
1477
- // 判断是否应该使用多分支选择模式
1478
- shouldUseMultipleBranchSelection (branchActions) {
1479
- // 情况1: 有并行分支
1480
- if (branchActions.parallelActions.length > 0) {
1481
- return true
1482
- }
1483
-
1484
- // 情况2: 条件分支包含WF_RESULT(后台结果决定)
1485
- if (branchActions.conditionalActions.length > 0) {
1486
- return this.checkIfContainsWfResult()
1487
- }
1488
-
1489
- return false
1490
- },
1491
-
1492
- // 根据表单数据计算目标节点
1493
- async calculateTargetNodeFromForm (conditionalActions = null) {
1494
- if (!conditionalActions) {
1495
- conditionalActions = this.currentDirections.filter(action => action.type === 'conditionalBranch')
1496
- }
1497
-
1498
- if (conditionalActions.length === 0) {
1499
- // 如果当前节点不是在分支流程中,才清空人员选择
1500
- // 避免在并行分支节点中误清除人员选择框
1501
- const currentStep = this.stepsForChild.find(item => item.id === this.activeStepId)
1502
- const isInBranch = currentStep?.properties?.branchPath
1503
-
1504
- if (!isInBranch) {
1505
- this.clearPersonSelection()
1506
- }
1507
- return
1508
- }
1509
-
1510
- try {
1511
- const formData = this.$refs.xAddForm?.form || {}
1512
-
1513
- // 遍历条件找到匹配的
1514
- for (const action of conditionalActions) {
1515
- if (action.expression) {
1516
- const result = await this.evaluateExpression(action.expression, formData)
1517
- if (result) {
1518
- this.calculatedTargetNode = action.to
1519
- await this.setupSingleTargetPersonSelection(action.to)
1520
- return
1521
- }
1522
- }
1523
- }
1524
- console.log('this.calculatedTargetNode', this.calculatedTargetNode)
1525
- // 如果没有匹配的条件,清空选择
1526
- this.clearPersonSelection()
1527
- } catch (error) {
1528
- console.warn('计算目标节点失败:', error)
1529
- this.clearPersonSelection()
1530
- }
1531
- },
1532
-
1533
- // 清空人员选择
1534
- clearPersonSelection () {
1535
- this.calculatedTargetNode = null
1536
- this.needSelectPerson = false
1537
- this.chargePersonOptions = []
1538
- this.checkedChargePerson = undefined
1539
- },
1540
-
1541
- // 设置单个目标节点的人员选择
1542
- async setupSingleTargetPersonSelection (stepId) {
1543
- const stepDefine = this.stepsDefine.find(step => step.id === stepId)
1544
-
1545
- if (!stepDefine?.properties?.chargePerson?.needSelectPerson) {
1546
- this.needSelectPerson = false
1547
- this.chargePersonOptions = []
1548
- return
1549
- }
1550
-
1551
- // 设置人员选择
1552
- this.chargePerson = stepDefine.properties.chargePerson
1553
- this.checkedChargePerson = undefined
1554
- this.chargePersonOptions = await this.getChargePersonOptionsForStep(stepId)
1555
-
1556
- // 如果只有一个选项,自动选中
1557
- if (this.chargePersonOptions.length === 1) {
1558
- this.checkedChargePerson = this.chargePersonOptions[0].value
1559
- }
1560
-
1561
- this.needSelectPerson = true
1562
- },
1563
-
1564
- // 表单数据变化处理函数(将通过provide传递给表单组件)
1565
- async handleFormDataChange (formData) {
1566
- // 只有在不是多分支选择模式且当前活动步骤有条件分支动作时才重新计算
1567
- if (!this.needMultipleBranchSelection && this.currentDirections.some(action => action.type === 'conditionalBranch')) {
1568
- await this.calculateTargetNodeFromForm()
1569
- }
1570
- },
1571
-
1572
- // 表达式评估 - 使用 LogicRunner
1573
- async evaluateExpression (expression, formData) {
1574
- try {
1575
- // 构建参数对象,传递当前表单数据
1576
- const params = {
1577
- WF_FORM: { ...formData }
1578
- }
1579
-
1580
- // 使用 LogicRunner 执行表达式
1581
- const result = await LogicRunner.runExpression(expression, params)
1582
- return result
1583
- } catch (error) {
1584
- console.warn('表达式评估失败:', expression, error)
1585
- return false
1586
- }
1587
- },
1588
-
1589
- // 获取指定步骤的人员选项
1590
- async getChargePersonOptionsForStep (stepId) {
1591
- const define = this.stepsDefine.find(item => item.id === stepId)
1592
- if (!define?.properties?.chargePerson) return []
1593
-
1594
- const chargePerson = define.properties.chargePerson
1595
- this.chargePerson = this.normalizeChargePersonFormat(chargePerson)
1596
-
1597
- if (!chargePerson.needSelectPerson || !chargePerson.personList) return []
1598
-
1599
- // 获取所有用户信息
1600
- const allUser = await postByServiceName(commonApi.getAllUserOptionList, {})
1601
-
1602
- // 根据配置筛选用户
1603
- const options = this.filterUsersByPersonConfig(chargePerson.personList, allUser)
1604
-
1605
- // 去重处理
1606
- return Array.from(new Map(options.map(item => [item.value, item])).values())
1607
- },
1608
-
1609
- // 标准化人员配置格式(兼容旧格式)
1610
- normalizeChargePersonFormat (chargePerson) {
1611
- if (chargePerson.role || chargePerson.department) {
1612
- chargePerson.needSelectPerson = true
1613
- chargePerson.personList = [{
1614
- type: chargePerson.role ? 'role' : 'department',
1615
- name: chargePerson.role || chargePerson.department
1616
- }]
1617
- }
1618
- return chargePerson
1619
- },
1620
-
1621
- // 根据人员配置筛选用户
1622
- filterUsersByPersonConfig (personList, allUsers) {
1623
- return personList.reduce((acc, personItem) => {
1624
- let filteredUsers = []
1625
-
1626
- if (personItem.type === 'role') {
1627
- filteredUsers = allUsers.filter(user =>
1628
- user.rolestr && user.rolestr.split(',').includes(personItem.name)
1629
- ).map(user => ({
1630
- label: user.label,
1631
- value: user.value
1632
- }))
1633
- } else if (personItem.type === 'department') {
1634
- filteredUsers = allUsers.filter(user =>
1635
- user.depname === personItem.name
1636
- ).map(user => ({
1637
- label: user.label,
1638
- value: user.value
1639
- }))
1640
- }
1641
-
1642
- return [...acc, ...filteredUsers]
1643
- }, [])
1644
- },
1645
-
1646
- // 设置多分支人员选择
1647
- async setupMultipleBranchSelection (branchActions) {
1648
- this.branchNodes = []
1649
- this.branchChargePersons = {}
1650
- let hasPersonSelection = false
1651
-
1652
- for (const action of branchActions) {
1653
- const stepDefine = this.stepsDefine.find(step => step.id === action.to)
1654
- const chargePerson = stepDefine?.properties?.chargePerson
1655
-
1656
- // 所有分支节点都加入branchNodes,保持数据结构完整
1657
- const nodeData = {
1658
- stepId: action.to,
1659
- stepName: this.getStepNameByStepId(action.to),
1660
- chargePerson: chargePerson,
1661
- needSelectPerson: chargePerson?.needSelectPerson || false,
1662
- chargePersonOptions: []
1663
- }
1664
-
1665
- if (chargePerson?.needSelectPerson) {
1666
- // 需要选择人员的分支
1667
- const chargePersonOptions = await this.getChargePersonOptionsForStep(action.to)
1668
- nodeData.chargePersonOptions = chargePersonOptions
1669
-
1670
- // 初始化选择值
1671
- this.branchChargePersons[action.to] = undefined
1672
- hasPersonSelection = true
1673
- } else {
1674
- // 不需要选择人员的分支,直接存储配置
1675
- this.branchChargePersons[action.to] = chargePerson
1676
- }
1677
-
1678
- this.branchNodes.push(nodeData)
1679
- }
1680
-
1681
- // 只有当存在需要选择人员的分支时才显示选择区域
1682
- this.needSelectPerson = hasPersonSelection
1683
- },
1684
-
1685
- // 处理普通单线流程(没有分支的情况)
1686
- async handleNormalFlow () {
1687
- const targetStepId = this.operationType === 'skip' ? this.stepNextBtnTo : this.nextBtnTo
1688
-
1689
- this.calculatedTargetNode = targetStepId
1690
- const stepDefine = this.stepsDefine.find(step => step.id === targetStepId)
1691
-
1692
- if (!stepDefine) {
1693
- console.warn('未找到目标步骤定义:', targetStepId)
1694
- this.clearPersonSelection()
1695
- return
1696
- }
1697
-
1698
- const defineProperties = stepDefine.properties
1699
- this.chargePerson = defineProperties?.chargePerson
1700
-
1701
- // 兼容旧格式
1702
- if (defineProperties.chargePerson) {
1703
- defineProperties.chargePerson = this.normalizeChargePersonFormat(defineProperties.chargePerson)
1704
- }
1705
-
1706
- if (defineProperties?.chargePerson?.needSelectPerson) {
1707
- await this.setupSingleTargetPersonSelection(targetStepId)
1708
- // 确保 branchChargePersons 中有该节点的数据结构
1709
- this.branchChargePersons[targetStepId] = {
1710
- handler: '',
1711
- handlerId: undefined,
1712
- ...defineProperties.chargePerson
1713
- }
1714
- } else {
1715
- // 不需要选择人员的情况,直接存储配置
1716
- this.branchChargePersons[targetStepId] = defineProperties?.chargePerson || {}
1717
- this.needSelectPerson = false
1718
- this.chargePersonOptions = []
1719
- }
1720
- }
1721
- },
1722
- watch: {}
1723
- }
1724
- </script>
1725
- <style lang="less" scoped>
1726
- .complete-data-title {
1727
- margin-top: 8px;
1728
- margin-bottom: 8px;
1729
- }
1730
-
1731
- :deep(.ant-result) {
1732
- padding: 0;
1733
- }
1734
-
1735
- .descriptionPreviewItem {
1736
- margin-bottom: 22px;
1737
-
1738
- :deep(.ant-descriptions-view) {
1739
- overflow: visible;
1740
- }
1741
- }
1742
-
1743
- .allWidth {
1744
- :deep(.ant-descriptions-item-content) {
1745
- width: 100%;
1746
- }
1747
- }
1748
-
1749
- .descriptionTitle {
1750
- position: relative;
1751
- padding-left: 12px;
1752
-
1753
- &::before {
1754
- content: '';
1755
- position: absolute;
1756
- left: 0;
1757
- top: 50%;
1758
- transform: translateY(-50%);
1759
- width: 4px;
1760
- height: 16px;
1761
- background-color: @primary-color;
1762
- border-radius: 2px;
1763
- }
1764
- }
1765
- </style>
1
+ <template>
2
+ <div>
3
+ <!-- 上方流程显示 -->
4
+ <a-card :bordered="false" :loading="loading">
5
+ <!-- 项目进度流程 -->
6
+ <work-flow-timeline
7
+ :current-step-id="currentStepId"
8
+ :active-step-id="activeStepId"
9
+ :workflow-id="workflowId"
10
+ :steps="stepsForChild"
11
+ :state="workflowState"
12
+ change-able
13
+ @activeStep="activeStep"/>
14
+ </a-card>
15
+ <!-- 无权访问的提示 -->
16
+ <a-result v-show="!canSubmit" status="403" title="无权操作" sub-title="您没有访问该步骤的权限.">
17
+ </a-result>
18
+ <div v-show="canSubmit">
19
+ <!-- 流程被退回的提示 -->
20
+ <a-alert v-if="currentStep && currentStep.back && !(beforeStepActive || workflowState)" type="info" show-icon style="margin-bottom: 14px">
21
+ <div slot="message">
22
+ <span style="font-weight: bold">流程被退回</span>
23
+ <span style="margin-left: 14px; font-size: 14px; color: rgba(0, 0, 0, 0.65)">请重新填写信息发起提交</span>
24
+ </div>
25
+ <div slot="description">
26
+ <div>操作人:{{
27
+ currentStep.back.f_operator
28
+ }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;操作时间:{{ currentStep.back.f_date }}
29
+ </div>
30
+ <div>原因:{{ currentStep.back.f_notes }}</div>
31
+ </div>
32
+ </a-alert>
33
+ <!-- 步骤内容主体 -->
34
+ <a-tabs default-active-key="1" @change="note = ''" type="card">
35
+ <a-tab-pane key="0" tab="步骤详情" v-if="beforeStepActive || workflowState">
36
+ <a-card :bordered="false" :loading="loadingHistory" :body-style="{ paddingTop: 0 }">
37
+ <!-- 当前步骤历史记录 -->
38
+ <template v-if="showTab">
39
+ <x-tab
40
+ :compProp="{ buttonState: { add: false, edit: false, delete: false, import: false, extra: false }, disableAction: true }"
41
+ :local-config="tabDesigner"
42
+ :body-style="{ padding: 0 }"
43
+ :tabBarGutter="24"
44
+ :extra-data="{ workflowId: workflowId }"
45
+ default-active-key="workFlowTab">
46
+ <a-tab-pane
47
+ :forceRender="true"
48
+ v-if="formCompletedDataPreview"
49
+ slot="extraBeforeTabs"
50
+ key="workFlowTab"
51
+ tab="表单"
52
+ >
53
+ <work-flow-preview :form-completed-data-preview="formCompletedDataPreview"/>
54
+ </a-tab-pane>
55
+ </x-tab>
56
+ </template>
57
+ <template v-else-if="formCompletedDataPreview">
58
+ <work-flow-preview :form-completed-data-preview="formCompletedDataPreview"/>
59
+ </template>
60
+ <template v-else>
61
+ <a-result status="404" title="暂无数据" sub-title="该步骤暂无数据。">
62
+ </a-result>
63
+ </template>
64
+ </a-card>
65
+ </a-tab-pane>
66
+ <a-tab-pane key="1" tab="业务操作" v-else v-show="renderCurrentNode">
67
+ <a-card :bordered="false" :loading="loadingHistory" :body-style="{ paddingTop: 0 }">
68
+ <x-tab
69
+ v-if="showTab"
70
+ :compProp="{ buttonState: { extra: true }, disableAction: false }"
71
+ :local-config="tabDesigner"
72
+ :extra-data="{ workflowId:workflowId }"
73
+ :body-style="{ padding: 0 }"
74
+ :tabBarGutter="24"
75
+ default-active-key="workFlowTab">
76
+ <a-tab-pane
77
+ :forceRender="true"
78
+ v-if="showForm"
79
+ slot="extraBeforeTabs"
80
+ key="workFlowTab"
81
+ tab="表单"
82
+ >
83
+ <x-add-native-form
84
+ ref="xAddForm"
85
+ @x-form-item-emit-func="formItemEmitFunc"
86
+ @onSubmit="submitForm"/>
87
+ </a-tab-pane>
88
+ </x-tab>
89
+ <x-add-native-form
90
+ v-else
91
+ ref="xAddForm"
92
+ @x-form-item-emit-func="formItemEmitFunc"
93
+ @onSubmit="submitForm"/>
94
+ <a-divider/>
95
+
96
+ <!-- 分支节点状态提示 -->
97
+ <a-alert
98
+ v-if="isBranchLastNode.isBranchNode && !isBranchLastNode.isLastBranch"
99
+ type="info"
100
+ show-icon
101
+ style="margin-bottom: 16px"
102
+ >
103
+ <template slot="message">
104
+ <span style="font-weight: bold;">分支节点等待中</span>
105
+ </template>
106
+ <template slot="description">
107
+ <div>当前为分支节点,其他分支仍在进行中。完成当前分支后将等待其他分支完成,然后自动汇合到下一环节。</div>
108
+ <div style="margin-top: 8px; color: #666; font-size: 12px;">
109
+ <a-icon type="info-circle" style="margin-right: 4px;" />
110
+ 提示:此操作无需选择下一环节负责人,系统将自动处理流程流转。
111
+ </div>
112
+ </template>
113
+ </a-alert>
114
+
115
+ <a-alert
116
+ v-if="isBranchLastNode.isBranchNode && isBranchLastNode.isLastBranch"
117
+ type="success"
118
+ show-icon
119
+ style="margin-bottom: 16px"
120
+ >
121
+ <template slot="message">
122
+ <span style="font-weight: bold;">分支汇合节点</span>
123
+ </template>
124
+ <template slot="description">
125
+ <div>您正在处理最后一个未完成的分支节点,完成后将触发分支汇合并进入下一环节。</div>
126
+ </template>
127
+ </a-alert>
128
+
129
+ <a-form v-show="!lastStep" label-align="left" :label-col="{ span: 3 }" :wrapper-col="{ span: 13 }">
130
+ <a-form-item v-if="showStepNextBtn" label="操作类型" required>
131
+ <a-radio-group v-model="operationType" @change="handleOperationTypeChange">
132
+ <a-radio value="submit">到下一步</a-radio>
133
+ <a-radio value="skip">{{ stepNextBtnTitle }}</a-radio>
134
+ </a-radio-group>
135
+ </a-form-item>
136
+
137
+ <!-- 智能分支人员选择组件 - 只有非分支节点或最后一个分支才显示 -->
138
+ <WorkflowPersonSelector
139
+ v-if="!isBranchLastNode.isBranchNode || isBranchLastNode.isLastBranch"
140
+ ref="personSelector"/>
141
+
142
+ <a-form-item
143
+ v-if="!isBranchLastNode.isBranchNode || isBranchLastNode.isLastBranch"
144
+ label="下一环节截止时间"
145
+ :validate-status="deadlineValidateStatus"
146
+ :help="deadlineHelp"
147
+ required>
148
+ <a-date-picker
149
+ :getCalendarContainer="(triggerNode) => triggerNode.parentNode"
150
+ v-model="deadline"
151
+ value-format="YYYY-MM-DD HH:mm"
152
+ format="YYYY-MM-DD HH:mm"
153
+ :allow-clear="false"
154
+ :show-today="false"
155
+ show-time
156
+ placeholder="请选择"/>
157
+ </a-form-item>
158
+ </a-form>
159
+ <a-divider/>
160
+ <!-- 备注信息 -->
161
+ <a-textarea
162
+ v-model="note"
163
+ :auto-size="{ minRows: 3, maxRows: 5 }"
164
+ placeholder="填写本环节备注事项"
165
+ />
166
+ <!-- 当前步骤完成后,控制流程按钮 -->
167
+ <div style="text-align: center">
168
+ <a-radio-group style="margin-top: 20px">
169
+ <a-space>
170
+ <!-- 动态按钮渲染 -->
171
+ <template v-if="buttonGroup && buttonGroup.actionArr">
172
+ <a-button
173
+ v-for="(button, index) in buttonGroup.actionArr"
174
+ :key="index"
175
+ v-show="checkButtonVisible(button)"
176
+ :type="button.type ? button.type : 'default'"
177
+ @click="handleCustomButtonClick(button)"
178
+ >
179
+ {{ button.text }}
180
+ </a-button>
181
+ </template>
182
+ <!-- 原有的按钮逻辑 -->
183
+ <template>
184
+ <a-popover v-if="operationType === 'submit'">
185
+ <template slot="content">
186
+ <p v-if="isBranchLastNode.isBranchNode && !isBranchLastNode.isLastBranch">
187
+ 完成当前分支,等待其他分支完成后自动汇合
188
+ </p>
189
+ <p v-else-if="isBranchLastNode.isBranchNode && isBranchLastNode.isLastBranch">
190
+ 完成最后一个分支并触发汇合到下一环节
191
+ </p>
192
+ <p v-else>{{ nextBtnTitle }}</p>
193
+ </template>
194
+ <a-button
195
+ v-if="!lastStep"
196
+ :disabled="!showNextBtn && !stepDone"
197
+ type="primary"
198
+ @click="nextClick"
199
+ >
200
+ 完成提交
201
+ <a-icon type="right"/>
202
+ </a-button>
203
+ </a-popover>
204
+ <a-popover v-else :title="stepNextBtnTitle">
205
+ <template slot="content">
206
+ <p v-for="(item,index) in stepNextBtnText" :key="index">{{ item }}</p>
207
+ </template>
208
+ <a-button
209
+ v-if="showStepNextBtn && !lastStep"
210
+ type="primary"
211
+ @click="stepNextClick"
212
+ >
213
+ 跳过
214
+ </a-button>
215
+ </a-popover>
216
+ <a-button
217
+ v-if="lastStep"
218
+ type="primary"
219
+ @click="lastStepNextClick"
220
+ >
221
+ 确认完成
222
+ </a-button>
223
+ </template>
224
+ </a-space>
225
+ </a-radio-group>
226
+ </div>
227
+ </a-card>
228
+ </a-tab-pane>
229
+ <!-- 退回 -->
230
+ <a-tab-pane v-if="canSubmit && !beforeStepActive && showPrevBtn && !workflowState" key="2" tab="退回">
231
+ <a-form layout="vertical">
232
+ <a-form-item label="退回原因" :wrapper-col="{ span: 24 }" required>
233
+ <a-textarea
234
+ v-model="backNote"
235
+ :auto-size="{ minRows: 4, maxRows: 10 }"
236
+ placeholder="请填写退回原因 / 备注"
237
+ />
238
+ </a-form-item>
239
+ <a-form-item :wrapper-col="{ offset: 11 }">
240
+ <a-popover :title="preBtnTitle">
241
+ <template slot="content">
242
+ <p v-for="(item,index) in preBtnText" :key="index">{{ item }}</p>
243
+ </template>
244
+ <a-button
245
+ type="danger"
246
+ @click="preClick"
247
+ >
248
+ <a-icon type="left"/>
249
+ 退回
250
+ </a-button>
251
+ </a-popover>
252
+ </a-form-item>
253
+ </a-form>
254
+ </a-tab-pane>
255
+ <!-- <a-tab-pane v-if="canSubmit && !beforeStepActive && showPrevBtn && !workflowState" key="3" tab="工单拆分">
256
+ &lt;!&ndash; 分配工单 &ndash;&gt;
257
+ <workflow-list-resolution :workflow-project-id="workflowId" :details="details"></workflow-list-resolution>
258
+ </a-tab-pane>-->
259
+ </a-tabs>
260
+ </div>
261
+ </div>
262
+ </template>
263
+
264
+ <script>
265
+ import XAddNativeForm from '@vue2-client/base-client/components/common/XAddNativeForm'
266
+ import XFormTable from '@vue2-client/base-client/components/common/XFormTable/XFormTable'
267
+ import XAddForm from '@vue2-client/base-client/components/common/XAddForm/XAddForm'
268
+ import { postByServiceName } from '@vue2-client/services/api/restTools'
269
+ import { workFlowViewApi } from '@vue2-client/services/api/workFlow'
270
+ import { commonApi } from '@vue2-client/services/api'
271
+ import { formatDate } from '@vue2-client/utils/util'
272
+ import { mapState } from 'vuex'
273
+ import moment from 'moment'
274
+ import FilePreview from '@vue2-client/components/FilePreview'
275
+ import WorkFlowTimeline from './WorkFlowTimeline.vue'
276
+ import { FileItem, ImageItem } from '@vue2-client/components/FileImageItem'
277
+ import WorkflowListResolution from './WorkflowListResolution'
278
+ import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
279
+ import * as util from '@vue2-client/utils/util'
280
+ import { getConfigByNameAsync, runLogic } from '@vue2-client/services/api/common'
281
+ import LogicRunner from '@vue2-client/logic/LogicRunner'
282
+ import XTab from '@vue2-client/base-client/components/common/XTab/XTab.vue'
283
+ import WorkFlowPreview from './WorkFlowPreview'
284
+ import WorkflowPersonSelector from './components/WorkflowPersonSelector.vue'
285
+
286
+ export default {
287
+ name: 'WorkFlowHandle',
288
+ provide () {
289
+ return {
290
+ formDataChange: this.handleFormDataChange,
291
+ workflowHandleWrap: this.workflowHandleWrap
292
+ }
293
+ },
294
+ components: {
295
+ XTab,
296
+ WorkflowListResolution,
297
+ XAddNativeForm,
298
+ WorkFlowTimeline,
299
+ XFormTable,
300
+ XAddForm,
301
+ FilePreview,
302
+ FileItem,
303
+ ImageItem,
304
+ WorkFlowPreview,
305
+ WorkflowPersonSelector
306
+ },
307
+ computed: {
308
+ workflowHandleWrap () {
309
+ return this
310
+ },
311
+ ...mapState('account', { currUser: 'user' }),
312
+ // 判断当前节点的分支状态
313
+ isBranchLastNode () {
314
+ // 默认返回值
315
+ const defaultResult = {
316
+ isBranchNode: false, // 是否为分支流程中的节点
317
+ isLastBranch: false // 是否为最后一个未完成的分支(需要选择下一环节人员)
318
+ }
319
+
320
+ // 如果是最后一步或没有下一步或者不能提交,则不是分支节点
321
+ if (this.lastStep || !this.nextBtnTo || !this.canSubmit) {
322
+ return defaultResult
323
+ }
324
+
325
+ // 检查下一个节点是否为分支退出节点
326
+ const nextStep = this.stepsDefine.find(step => step.id === this.nextBtnTo)
327
+
328
+ // 如果下一个节点的flowRole不是branchExit,说明不是分支的最后一个节点
329
+ if (!nextStep || nextStep.properties?.flowRole !== 'branchExit') {
330
+ return defaultResult
331
+ }
332
+
333
+ // 获取分支退出节点等待的所有分支步骤ID
334
+ const waitStepIds = nextStep.properties.waitStepIds || []
335
+ if (waitStepIds.length === 0) {
336
+ return {
337
+ isBranchNode: true,
338
+ isLastBranch: true // 如果没有等待步骤配置,默认认为是最后一个
339
+ }
340
+ }
341
+
342
+ // 当前步骤必须在等待列表中才算是分支节点
343
+ if (!waitStepIds.includes(this.activeStepId)) {
344
+ return defaultResult
345
+ }
346
+
347
+ // 检查除当前步骤外的其他分支步骤状态
348
+ const otherBranchSteps = waitStepIds.filter(stepId => stepId !== this.activeStepId)
349
+
350
+ // 判断其他所有分支步骤是否都已完成(状态为2)
351
+ const allOtherBranchesCompleted = otherBranchSteps.every(stepId => {
352
+ const step = this.stepsForChild.find(s => s.id === stepId)
353
+ return step && step.status === 2 // 已处理
354
+ })
355
+
356
+ return {
357
+ isBranchNode: true, // 当前节点是分支流程中的节点
358
+ isLastBranch: allOtherBranchesCompleted // 只有当其他所有分支都已完成时,当前分支才是最后一个
359
+ }
360
+ },
361
+ canSubmit () {
362
+ // 对于超级管理员直接认为可以提交
363
+ if (this.currUser.rolesnames.indexOf('超级管理员') > -1) {
364
+ return true
365
+ }
366
+ // currentStepId可能还没初始化,此处拿不到先返回false
367
+ if (!this.currentStepId) {
368
+ return false
369
+ }
370
+ const activeStep = this.stepsForChild.find(item => item.id === this.activeStepId)
371
+ // 如果当前选中节点不是正在进行的节点,则判断viewers权限
372
+ if (this.activeStepId !== this.currentStepId && activeStep != null && activeStep.status !== 1) {
373
+ // 历史节点为当前登录人操作则不做查看数据权限校验
374
+ if (activeStep.handler === this.currUser.name) {
375
+ return true
376
+ }
377
+ const viewers = activeStep?.properties?.otherProperty?.viewers ?? []
378
+ if (viewers.length > 0) {
379
+ // 使用some方法判断当前人员是否满足任一条件
380
+ return viewers.some(item => {
381
+ if (item.type === 'role') {
382
+ // 检查rolestr是否存在并包含指定角色
383
+ return this.currUser.rolestr && this.currUser.rolestr.split(',').includes(item.name)
384
+ }
385
+ if (item.type === 'department') {
386
+ // 检查depname是否存在并包含指定部门
387
+ return this.currUser.deps && this.currUser.deps.includes(item.name)
388
+ }
389
+ return false
390
+ })
391
+ } else {
392
+ return true
393
+ }
394
+ } else {
395
+ // 当前进行节点的数据
396
+ const step = this.stepsForChild.find(item => item.id === this.activeStepId)
397
+ // 检查角色和部门权限
398
+ if (step && step.properties && step.properties.chargePerson) {
399
+ // 如果当前节点的负责人选项中有设置选择人员 当前节点就只能由上一步设置的负责人操作
400
+ if (step.properties.chargePerson.needSelectPerson) {
401
+ return step.handler === this.currUser.name
402
+ }
403
+ if (step.properties.chargePerson.personList && step.properties.chargePerson.personList.length > 0) {
404
+ // 使用some方法判断当前人员是否满足任一条件
405
+ return step.properties.chargePerson.personList.some(item => {
406
+ if (item.type === 'role') {
407
+ // 检查rolestr是否存在并包含指定角色
408
+ return this.currUser.rolestr && this.currUser.rolestr.split(',').includes(item.name)
409
+ }
410
+ if (item.type === 'department') {
411
+ // 检查parentname是否存在并包含指定部门
412
+ return this.currUser.parentname && this.currUser.parentname.includes(item.name)
413
+ }
414
+ return false
415
+ })
416
+ }
417
+ }
418
+
419
+ // 检查handler是否包含当前用户
420
+ if (step && step.handler) {
421
+ return step.handler.includes(this.currUser.name)
422
+ }
423
+ }
424
+ return false
425
+ },
426
+ formPreviewNoFileData () {
427
+ return this.formCompletedDataPreview.data.filter(item => item.label !== '附件上传')
428
+ }
429
+ },
430
+ data () {
431
+ return {
432
+ // 显示供用户填写的当前步骤表单
433
+ showForm: false,
434
+ // 显示步骤的tab页
435
+ showTab: false,
436
+ // tab页的配置
437
+ tabDesigner: undefined,
438
+ // 当前步骤表单标题
439
+ formTitle: '',
440
+ // 当前步骤表单定义Json
441
+ stepDefine: [],
442
+ // 当前步骤
443
+ currentStep: undefined,
444
+ // 当前步骤完成状态
445
+ stepDone: false,
446
+ // 所有节点连通数据
447
+ directions: [],
448
+ // 所有流程定义数据
449
+ stepsDefine: [],
450
+ // 当前步骤能通向的节点
451
+ currentDirections: [],
452
+ // 当前节点id
453
+ currentStepId: undefined,
454
+ // 当前活动节点 id
455
+ activeStepId: 1,
456
+ // 下一步按钮显示
457
+ showNextBtn: false,
458
+ // 回退按钮显示
459
+ showPrevBtn: false,
460
+ // 跳过按钮显示
461
+ showStepNextBtn: false,
462
+ // 定义三个按钮气泡提示内容
463
+ preBtnTitle: '',
464
+ preBtnText: [],
465
+ nextBtnTitle: '',
466
+ stepNextBtnTitle: '',
467
+ stepNextBtnText: [],
468
+ // 控制三个按钮跳转目标
469
+ preBtnTo: undefined,
470
+ nextBtnTo: undefined,
471
+ stepNextBtnTo: undefined,
472
+ // 控制当前标签页显示
473
+ activeKey: '1',
474
+ // 之前的节点是否激活
475
+ beforeStepActive: false,
476
+ // 之前节点中的数据
477
+ beforeStepData: [],
478
+ // 用于将已有数据,填回x-add表单中
479
+ formCompletedData: {},
480
+ // 用于展示提交的数据
481
+ formCompletedDataPreview: null,
482
+ // 切换至目标流程时,目标流程定义
483
+ targetStepDefine: [],
484
+ // 控制历史记录加载
485
+ loadingHistory: true,
486
+ // 控制表单校验
487
+ formValid: false,
488
+ // 备忘
489
+ note: '',
490
+ backNote: '', // 新增退回原因专用变量
491
+ // 是否是最后一步
492
+ lastStep: false,
493
+ // 存储每一步骤填表人和时间数据
494
+ stepsExtraInfo: [],
495
+ // 控制基础信息页流程展示加载
496
+ loading: true,
497
+ createQueryVisible: false,
498
+ // 激活的步骤名
499
+ activeStepName: '',
500
+ stepsParse: undefined,
501
+ // 下一步处理人选择框数据
502
+ chargePersonOptions: [],
503
+ // 已选择下一步处理人
504
+ checkedChargePerson: undefined,
505
+ // 下一步截止时间
506
+ deadline: this.getDefaultDeadline(),
507
+ deadlineValidateStatus: '',
508
+ deadlineHelp: '',
509
+ // 当前任务是否延期
510
+ // taskIsOverdue: moment().isAfter(this.completeTime, 'day'),
511
+ // 操作类型,提交到下一步或跳过
512
+ operationType: 'submit',
513
+ // 获取微信推送是否成功信息
514
+ information: [],
515
+ // 是否需要选择人员
516
+ needSelectPerson: true,
517
+ // 下一环节人员信息
518
+ chargePerson: {},
519
+ // 动态按钮配置
520
+ buttonGroup: null,
521
+ // 分支节点相关数据
522
+ branchNodes: [], // 需要选择人员的分支节点列表
523
+ branchChargePersons: {}, // 分支节点人员选择 格式:{stepId: personId}
524
+ needMultipleBranchSelection: false, // 是否需要为多个分支节点预先选择人员(包含WF_RESULT的条件分支或并行分支)
525
+ calculatedTargetNode: null, // 前台计算出的目标节点
526
+ currentBranchActions: null // 缓存当前的分支动作,避免重复调用
527
+ }
528
+ },
529
+ async mounted () {
530
+ await this.init()
531
+ // this.checkDeadline()
532
+ },
533
+ props: {
534
+ workflowId: {
535
+ type: [String, Number],
536
+ required: true
537
+ },
538
+ visible: {
539
+ type: Boolean,
540
+ default: false
541
+ },
542
+ stepsForChild: {
543
+ type: Array,
544
+ required: true
545
+ },
546
+ workflowState: {
547
+ type: [Boolean, Number],
548
+ required: true
549
+ },
550
+ completeTime: {
551
+ type: String,
552
+ required: true
553
+ },
554
+ taskName: {
555
+ type: String,
556
+ required: true
557
+ },
558
+ details: {
559
+ type: Object,
560
+ required: true
561
+ },
562
+ // 展开详情初始化的节点
563
+ // 适用于不让第一个节点默认展示的情况
564
+ initStepId: {
565
+ type: [String, Number],
566
+ required: false,
567
+ default: null
568
+ },
569
+ renderCurrentNode: {
570
+ type: Boolean,
571
+ default: true
572
+ }
573
+ },
574
+ methods: {
575
+ init () {
576
+ this.getCurrentStep()
577
+ },
578
+ onClose () {
579
+ this.activeStepId = this.currentStepId
580
+ this.checkedChargePerson = undefined
581
+ this.loadingHistory = true
582
+ this.currentStepId = undefined
583
+ this.directions = []
584
+ this.currentDirections = []
585
+ this.preBtnText = []
586
+ this.stepNextBtnText = []
587
+ this.stepsExtraInfo = []
588
+ this.loading = true
589
+ this.showNextBtn = false
590
+ this.showStepNextBtn = false
591
+ this.showPrevBtn = false
592
+ this.stepDone = false
593
+ this.formCompletedData = {}
594
+ this.formCompletedDataPreview = null
595
+ this.operationType = 'submit'
596
+
597
+ // 清理分支节点相关数据
598
+ this.branchNodes = []
599
+ this.branchChargePersons = {}
600
+ this.needMultipleBranchSelection = false
601
+ this.calculatedTargetNode = null
602
+ this.currentBranchActions = null
603
+ },
604
+ async showQueryFormItemFunc () {
605
+ if (this.attr.showQueryFormItemFunc) {
606
+ const obj = executeStrFunctionByContext(this, this.attr.showQueryFormItemFunc, [this.form, this.setForm, this.attr, util, this.mode])
607
+ // 判断是 bool 还是 obj 兼容
608
+ if (typeof obj === 'boolean') {
609
+ this.show = obj
610
+ } else if (obj && typeof obj === 'object') {
611
+ // obj 是一个对象,并且不是数组
612
+ this.show = obj?.show
613
+ this.readOnly = obj?.readOnly
614
+ }
615
+ } else {
616
+ this.show = true
617
+ }
618
+ },
619
+ // 获取单个步骤的定义
620
+ getSingleStepDefine (name) {
621
+ for (const step of this.stepsDefine) {
622
+ if (name === step.name) {
623
+ return step.properties.form
624
+ }
625
+ }
626
+ },
627
+ // 日期格式化
628
+ format (date, format) {
629
+ return formatDate(date, format)
630
+ },
631
+ // 获取当前步骤
632
+ getCurrentStep () {
633
+ return postByServiceName(workFlowViewApi.getWorkFlowCurrentSubState, {
634
+ workflowId: this.workflowId
635
+ })
636
+ .then(res => {
637
+ // 优先级:主动传入的 > 获取到的
638
+ this.currentStepId = this.initStepId || res.id
639
+
640
+ // 如果指定了具体节点,同时设置activeStepId
641
+ if (this.initStepId) {
642
+ this.activeStepId = this.initStepId
643
+ }
644
+
645
+ const currentStep = this.stepsForChild.find(item => item.id === this.currentStepId)
646
+ res.state = currentStep.name
647
+ // 获取到当前步骤后复制下一步时间
648
+ this.deadline = this.getDefaultDeadline(currentStep.properties?.otherProperty?.nextNodeInterval)
649
+ this.currentStep = res
650
+ this.getDirection()
651
+ }, err => {
652
+ console.log(err)
653
+ })
654
+ },
655
+ // 完工按钮
656
+ async lastStepNextClick () {
657
+ this.$refs.xAddForm.asyncSubmit().then((res) => {
658
+ this.submitForm(res).then(_ => {
659
+ postByServiceName(workFlowViewApi.afterWorkFlowFinalStepSubmit, {
660
+ workflowId: this.workflowId,
661
+ stepId: this.currentStepId,
662
+ submitUser: this.currUser.name,
663
+ submitUserId: this.currUser.id
664
+ }).then(_res => {
665
+ this.saveWorkflowLog('确认完工', '最后一步: ' + this.getStepNameByStepId(this.currentStepId), { notes: this.note.trim() })
666
+ this.$message.success('已完工!')
667
+ this.loading = true
668
+ this.loadingHistory = true
669
+ this.onClose()
670
+ this.$emit('success', { note: this.note.trim(), form: res.realForm, workflowId: this.workflowId })
671
+ },
672
+ () => {
673
+ this.$message.error('提交失败!')
674
+ }
675
+ )
676
+ })
677
+ }).catch(err => {
678
+ if (err && err.message === 'Form validation failed') {
679
+ this.$message.error('请检查表单必填项!')
680
+ } else {
681
+ // 对于其他错误,继续向上抛出
682
+ throw err
683
+ }
684
+ })
685
+ },
686
+ // 三个按钮点击后逻辑
687
+ async nextClick () {
688
+ // 检查是否为非最后分支节点
689
+ const branchStatus = this.isBranchLastNode
690
+ const isWaitingBranch = branchStatus.isBranchNode && !branchStatus.isLastBranch
691
+
692
+ const extraData = this.getApplyStepExtraData(this.nextBtnTo)
693
+ if (!extraData) {
694
+ return
695
+ }
696
+
697
+ // 确认对话框
698
+ const confirmContent = isWaitingBranch
699
+ ? '确定完成当前分支么?完成后将等待其他分支,不会立即流转到下一环节。'
700
+ : '确定提交么?提交之后数据不可更改!'
701
+
702
+ await new Promise(resolve => {
703
+ this.$confirm({
704
+ title: '提交确认',
705
+ content: confirmContent,
706
+ onOk () {
707
+ resolve()
708
+ return Promise.resolve()
709
+ }
710
+ })
711
+ })
712
+
713
+ this.$refs.xAddForm.asyncSubmit().then(res => {
714
+ this.submitForm(res).then(_ => {
715
+ extraData.form = res.realForm
716
+ postByServiceName(workFlowViewApi.submitToNextStep, extraData)
717
+ .then(
718
+ () => {
719
+ let successMessage = '提交成功'
720
+
721
+ // 只有非等待分支节点才保存工作流日志
722
+ if (!isWaitingBranch) {
723
+ const branchStatus = this.isBranchLastNode
724
+ const operation = branchStatus.isBranchNode && branchStatus.isLastBranch ? '分支汇合' : '提交'
725
+
726
+ const extra = {
727
+ setHandler: this.getStepHandler(),
728
+ setDeadline: this.deadline,
729
+ notes: this.note.trim()
730
+ }
731
+ this.saveWorkflowLog(operation, this.generateBranchStepChangeText(this.currentStepId), extra)
732
+ } else {
733
+ successMessage = '分支完成,等待其他分支完成后自动汇合'
734
+ }
735
+
736
+ this.$message.success(successMessage)
737
+ this.loading = true
738
+ this.loadingHistory = true
739
+ this.$emit('nextClick', {
740
+ note: this.note.trim(),
741
+ form: res.realForm,
742
+ workflowId: this.workflowId,
743
+ isBranchWaiting: isWaitingBranch, // 标记是否为分支等待状态
744
+ ...this.generateStepChange(this.currentStepId, this.nextBtnTo)
745
+ })
746
+ this.$emit('refresh')
747
+ this.onClose()
748
+ this.init()
749
+ },
750
+ err => {
751
+ this.$message.error('提交失败!')
752
+ console.log(err)
753
+ }
754
+ )
755
+ })
756
+ }).catch((err) => {
757
+ if (err && err.message === 'Form validation failed') {
758
+ this.$message.error('请检查表单必填项!')
759
+ } else {
760
+ // 对于其他错误,继续向上抛出
761
+ throw err
762
+ }
763
+ })
764
+ },
765
+ async preClick () {
766
+ const notes = this.backNote.trim()
767
+ if (!notes) {
768
+ this.$message.error('退回请在备注中填写理由')
769
+ return
770
+ }
771
+ return postByServiceName(workFlowViewApi.updateWorkFlowState, {
772
+ stepId: this.preBtnTo,
773
+ workflowId: this.workflowId,
774
+ type: 'back'
775
+ })
776
+ .then(
777
+ res => {
778
+ this.saveWorkflowLog('退回', this.generateStepChangeText(this.currentStepId, this.preBtnTo), { notes })
779
+ this.$message.success('退回成功')
780
+ this.backNote = '' // 退回成功后清空退回原因
781
+ this.loading = true
782
+ this.loadingHistory = true
783
+ this.currentStepId = this.preBtnTo
784
+ this.activeStepId = this.preBtnTo
785
+
786
+ // 新增:重置按钮状态
787
+ this.stepDone = false
788
+ this.beforeStepActive = false
789
+ this.operationType = 'submit'
790
+
791
+ this.$emit('refresh')
792
+ this.onClose()
793
+ this.init()
794
+ },
795
+ err => {
796
+ this.$message.error('退回失败!')
797
+ console.log(err)
798
+ }
799
+ )
800
+ },
801
+ async stepNextClick () {
802
+ const extraData = this.getApplyStepExtraData(this.stepNextBtnTo)
803
+ if (!extraData) {
804
+ return
805
+ }
806
+ this.$refs.xAddForm.asyncSubmit().then(res => {
807
+ this.submitForm(res).then(_ => {
808
+ extraData.form = res.realForm
809
+ postByServiceName(workFlowViewApi.submitToNextStep, extraData)
810
+ .then(
811
+ () => {
812
+ const extra = {
813
+ setHandler: this.getStepHandler(),
814
+ setDeadline: this.deadline,
815
+ notes: this.note.trim()
816
+ }
817
+ this.saveWorkflowLog('跳过', this.generateBranchStepChangeText(this.currentStepId), extra)
818
+ this.$message.success('提交成功')
819
+ this.$emit('nextClick', {
820
+ note: this.note.trim(),
821
+ form: res.realForm,
822
+ workflowId: this.workflowId,
823
+ ...this.generateStepChange(this.currentStepId, this.stepNextBtnTo)
824
+ })
825
+ this.loading = true
826
+ this.loadingHistory = true
827
+ this.$emit('refresh')
828
+ this.onClose()
829
+ this.init()
830
+ },
831
+ err => {
832
+ this.$message.error('提交失败!')
833
+ console.log(err)
834
+ }
835
+ )
836
+ })
837
+ }).catch(err => {
838
+ if (err && err.message === 'Form validation failed') {
839
+ this.$message.error('请检查表单必填项!')
840
+ } else {
841
+ // 对于其他错误,继续向上抛出
842
+ throw err
843
+ }
844
+ })
845
+ },
846
+ // 获取当前步骤节点,连通的节点
847
+ async getDirection () {
848
+ // 获取流程定义
849
+ return postByServiceName(workFlowViewApi.getWorkFlowDefine, {
850
+ id: this.workflowId
851
+ })
852
+ .then(async res => {
853
+ this.directions = []
854
+ this.stepsDefine = res.steps
855
+ await this.resolveDirections()
856
+ this.resolveStep()
857
+ }, err => {
858
+ console.log(err)
859
+ })
860
+ },
861
+ // 分析当前节点,能通向的节点
862
+ async resolveDirections () {
863
+ const currentStep = this.stepsForChild.find(item => item.id === this.activeStepId)
864
+ if (currentStep?.properties?.actions) {
865
+ this.currentDirections = currentStep.properties.actions
866
+ }
867
+ // 判断是否是最后ige节点
868
+ this.lastStep = (currentStep?.properties?.actions || []).filter(item => {
869
+ return item.type !== 'back'
870
+ }).length === 0
871
+
872
+ // 处理跳转按钮
873
+ this.workflowControl()
874
+
875
+ if (!this.lastStep) {
876
+ // 分析分支节点并设置智能人员选择
877
+ await this.analyzeBranchNodes()
878
+ }
879
+ },
880
+ // 根据步骤id获取步骤名称
881
+ getStepNameByStepId (stepId) {
882
+ return this.stepsDefine.find(item => item.id === stepId)?.name
883
+ },
884
+ // 根据步骤id获取步骤状态
885
+ getStepStatusByStepId (stepId) {
886
+ return this.stepsForChild.find(item => item.id === stepId)?.status
887
+ },
888
+ // 表单提交的回调
889
+ submitForm (obj) {
890
+ this.formValid = true
891
+ const formData = obj.realForm
892
+ const time = this.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
893
+ return postByServiceName(workFlowViewApi.saveWorkFlowStepFormData, {
894
+ workflowId: this.workflowId,
895
+ stepId: this.activeStepId,
896
+ form: formData,
897
+ data: time,
898
+ handler: this.currUser.name,
899
+ note: this.note.trim()
900
+ })
901
+ .then(
902
+ res => {
903
+ console.log('表单提交成功')
904
+ this.note = ''
905
+ this.showForm = false
906
+ this.stepDone = true
907
+ },
908
+ err => {
909
+ this.$message.error('保存失败,请检查后重试')
910
+ console.log(err)
911
+ }
912
+ )
913
+ },
914
+ // 获取当前步骤定义内容,构建组件
915
+ async buildComp (stepId) {
916
+ const properties = this.stepsDefine.find(item => item.id === stepId).properties
917
+ // 表单的渲染
918
+ if (properties.form && properties.form.formJson) {
919
+ this.stepDefine = properties.form.formJson
920
+ this.showForm = true
921
+ this.$nextTick(() => {
922
+ this.$refs.xAddForm && this.$refs.xAddForm.init({
923
+ ...properties.form,
924
+ businessType: '修改',
925
+ formItems: this.stepDefine,
926
+ layout: properties.form.xAddFormLayout,
927
+ showSubmitBtn: false
928
+ })
929
+ // 初始化的数据渲染(流程第一步)
930
+ console.log('导入的原始数据-json', this.stepsForChild[0].f_data)
931
+ console.log('转换后的数据-对象', JSON.parse(this.stepsForChild[0].f_data))
932
+ if (stepId === 1) {
933
+ this.$refs.xAddForm.setForm(JSON.parse(this.stepsForChild[0].f_data))
934
+ }
935
+ })
936
+ } else {
937
+ this.showForm = false
938
+ }
939
+ // Tab的渲染
940
+ this.tabDesigner = properties.tabDesigner
941
+ this.showTab = !!this.tabDesigner
942
+ // 以当前激活节点分析节点
943
+ await this.resolveDirections()
944
+ this.resolveStep()
945
+ },
946
+ // 根据当前节点,判断之后流程,以及按钮的显示
947
+ workflowControl () {
948
+ // 重置所有控制数据为默认值,避免之前状态的影响
949
+ this.showNextBtn = false
950
+ this.nextBtnTo = undefined
951
+ this.nextBtnTitle = ''
952
+ this.showStepNextBtn = false
953
+ this.stepNextBtnTo = undefined
954
+ this.stepNextBtnTitle = ''
955
+ this.stepNextBtnText = []
956
+ this.showPrevBtn = false
957
+ this.preBtnTo = undefined
958
+ this.preBtnTitle = ''
959
+ this.preBtnText = []
960
+
961
+ // 分类收集不同类型的actions
962
+ const submitActions = this.currentDirections.filter(item => item.type === 'submit')
963
+ const conditionalActions = this.currentDirections.filter(item => item.type === 'conditionalBranch')
964
+ const parallelActions = this.currentDirections.filter(item => item.type === 'parallelBranch')
965
+ const skipActions = this.currentDirections.filter(item => item.type === 'skip')
966
+ const backActions = this.currentDirections.filter(item => item.type === 'back')
967
+
968
+ // 处理提交按钮 - 优先级:submit > conditionalBranch > parallelBranch
969
+ if (submitActions.length > 0) {
970
+ this.showNextBtn = true
971
+ this.nextBtnTo = submitActions[0].to // 如果有多个submit,取第一个作为主要跳转目标
972
+ if (submitActions.length === 1) {
973
+ this.nextBtnTitle = '进行下一环节:' + this.getStepNameByStepId(submitActions[0].to)
974
+ } else {
975
+ this.nextBtnTitle = '将进入以下环节:' + submitActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
976
+ }
977
+ } else if (conditionalActions.length > 0) {
978
+ this.showNextBtn = true
979
+ this.nextBtnTo = conditionalActions[0].to // 条件分支的第一个作为主要跳转目标
980
+ const targetSteps = conditionalActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
981
+ this.nextBtnTitle = '后台判断后跳转到:' + targetSteps
982
+ } else if (parallelActions.length > 0) {
983
+ this.showNextBtn = true
984
+ this.nextBtnTo = parallelActions[0].to // 并行分支的第一个作为主要跳转目标
985
+ const targetSteps = parallelActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
986
+ this.nextBtnTitle = '将并行进入以下分支:' + targetSteps
987
+ }
988
+
989
+ // 处理跳过按钮
990
+ if (skipActions.length > 0) {
991
+ this.showStepNextBtn = true
992
+ this.stepNextBtnTo = skipActions[0].to
993
+ if (skipActions.length === 1) {
994
+ this.stepNextBtnTitle = '跳至:' + this.getStepNameByStepId(skipActions[0].to) + '环节'
995
+ this.stepNextBtnText.push('将跳过以下环节:')
996
+ for (let i = this.currentStepId; i < skipActions[0].to - 1; i++) {
997
+ this.stepNextBtnText.push(this.stepsDefine[i].name)
998
+ }
999
+ } else {
1000
+ const targetSteps = skipActions.map(item => this.getStepNameByStepId(item.to)).join(' , ')
1001
+ this.stepNextBtnTitle = '跳至:' + targetSteps
1002
+ }
1003
+ }
1004
+
1005
+ // 处理退回按钮
1006
+ if (backActions.length > 0) {
1007
+ const backAction = backActions[0] // 通常只有一个back action
1008
+ for (let i = this.currentStepId - 1; i > 0; i--) {
1009
+ if (backAction.to >= i && this.stepsForChild.find(item => item.id === i)?.handler) {
1010
+ this.preBtnTo = i
1011
+ this.preBtnTitle = '回退至:' + this.getStepNameByStepId(i) + '环节'
1012
+ this.showPrevBtn = true
1013
+ break
1014
+ } else {
1015
+ this.preBtnText.push(this.getStepNameByStepId(i))
1016
+ }
1017
+ }
1018
+ if (this.preBtnText.length) {
1019
+ this.preBtnText.unshift('将跳过以下环节:')
1020
+ }
1021
+ }
1022
+ },
1023
+ // 获取表单字段实际值
1024
+ getRealKey (key, isHandleFormKey) {
1025
+ if (key === 'selected_id') return key
1026
+ if (isHandleFormKey) {
1027
+ return key.substring(key.indexOf('_') + 1)
1028
+ } else {
1029
+ return key
1030
+ }
1031
+ },
1032
+ // 加载完成
1033
+ resolveStep () {
1034
+ // 获取当前步骤的按钮组配置
1035
+ const currentStep = this.stepsDefine.find(item => item.id === this.activeStepId)
1036
+ if (currentStep && currentStep.properties && currentStep.properties.buttonGroup) {
1037
+ this.buttonGroup = currentStep.properties.buttonGroup
1038
+ } else {
1039
+ this.buttonGroup = null
1040
+ }
1041
+ this.loading = false
1042
+ },
1043
+ // 流程图组件给当前组件传值用,stepNo为用户点击的非当前步骤
1044
+ async activeStep (stepId) {
1045
+ // 开启加载
1046
+ this.loadingHistory = true
1047
+ this.activeStepId = stepId
1048
+ // 获取激活节点的步骤名
1049
+ this.activeStepName = this.getStepNameByStepId(stepId)
1050
+ const status = this.getStepStatusByStepId(stepId)
1051
+ // 清空回显数据
1052
+ this.formCompletedData = {}
1053
+ let formCompletedDataPreview = null
1054
+ this.getStepDefine(stepId)
1055
+ // 判断激活的节点,是不是待完成节点 (当前节点或者 当前节点状态是1)
1056
+ if ((this.activeStepName !== this.currentStep.state && status !== 1) || this.workflowState) {
1057
+ // 获取激活节点历史数据,和字段定义
1058
+ await this.getCompletedFormData(stepId)
1059
+ // 将回显数据拷贝,避免引用传递
1060
+ formCompletedDataPreview = JSON.parse(JSON.stringify(this.formCompletedData))
1061
+ // 使用字段定义中内容,将回显数据的列名,替换为定义的中文名
1062
+ // 调整逻辑:formData中只显示流程配置表单中定义过的显示列,多余冗余的存储列不进行页面展示
1063
+ const formDataArr = []
1064
+ const dataObj = formCompletedDataPreview.data || {}
1065
+ const currentStepDefine = this.stepsDefine.find(item => item.id === stepId)
1066
+ const isKeyHandle = currentStepDefine?.properties?.form?.isKeyHandle || false
1067
+ for (let i = 0; i < this.targetStepDefine.length; i++) {
1068
+ const stepDefine = this.targetStepDefine[i]
1069
+ const key = this.getRealKey(stepDefine.model, isKeyHandle)
1070
+ if (Object.prototype.hasOwnProperty.call(dataObj, key)) {
1071
+ // 历史表单内容根据流程中配置的表单项顺序进行渲染,需要兼容表单项展示函数
1072
+ if (stepDefine.showFormItemFunc && !(await this.showFormItemFunc(stepDefine.showFormItemFunc, dataObj))) {
1073
+ continue
1074
+ }
1075
+ let value = dataObj[key]
1076
+ // 字典值处理
1077
+ if (stepDefine.formType === 'select' && stepDefine.selectType === 'key') {
1078
+ const dictList = this.$appdata.getDictionaryList(stepDefine.selectKey)
1079
+ const dictItem = dictList.find(item => item.value === value)
1080
+ value = dictItem ? dictItem.label : value
1081
+ }
1082
+ formDataArr.push({ label: stepDefine.name, value })
1083
+ }
1084
+ }
1085
+ // 如果一个都没匹配上,兼容旧逻辑:把原对象转成数组
1086
+ if (formDataArr.length === 0 && dataObj && typeof dataObj === 'object') {
1087
+ for (const key in dataObj) {
1088
+ if (Object.prototype.hasOwnProperty.call(dataObj, key)) {
1089
+ formDataArr.push({ label: key, value: dataObj[key] })
1090
+ }
1091
+ }
1092
+ }
1093
+ formCompletedDataPreview.data = formDataArr.length > 0 ? formDataArr : null
1094
+ if (!formCompletedDataPreview.data) {
1095
+ formCompletedDataPreview.data = null
1096
+ }
1097
+ // 备注
1098
+ formCompletedDataPreview.note = this.stepsForChild.find(item => item.id === stepId)?.note
1099
+ if (!formCompletedDataPreview.data && !formCompletedDataPreview.files.length && !formCompletedDataPreview.images.length && !formCompletedDataPreview.note) {
1100
+ formCompletedDataPreview = {}
1101
+ }
1102
+ this.formCompletedDataPreview = formCompletedDataPreview
1103
+ // 渲染已完成步骤的Tab
1104
+ const properties = this.stepsDefine.find(item => item.id === stepId).properties
1105
+ this.tabDesigner = properties.tabDesigner
1106
+ this.showTab = !!this.tabDesigner
1107
+ // 完成
1108
+ this.loadingHistory = false
1109
+ this.beforeStepActive = this.activeStepName !== this.currentStep.state
1110
+ } else {
1111
+ this.loadingHistory = false
1112
+ this.beforeStepActive = false
1113
+ if (!this.renderCurrentNode) {
1114
+ this.formCompletedDataPreview = null
1115
+ this.showTab = false
1116
+ this.beforeStepActive = true
1117
+ return
1118
+ }
1119
+ await this.buildComp(this.activeStepId)
1120
+ }
1121
+ },
1122
+ // 获取已经完成步骤的数据
1123
+ getCompletedFormData (stepId) {
1124
+ return postByServiceName(workFlowViewApi.getWorkFlowCompletedStepData, {
1125
+ workflowId: this.workflowId,
1126
+ stepId: stepId
1127
+ })
1128
+ .then(
1129
+ res => {
1130
+ this.formCompletedData = res
1131
+ },
1132
+ err => {
1133
+ console.log(err)
1134
+ }
1135
+ )
1136
+ },
1137
+ // 获取步骤定义表中,当前步骤定义
1138
+ getStepDefine (stepId) {
1139
+ const stepName = this.getStepNameByStepId(stepId)
1140
+ this.targetStepDefine = this.getSingleStepDefine(stepName).formJson
1141
+ },
1142
+ // 获取提交步骤的额外数据
1143
+ getApplyStepExtraData (stepId) {
1144
+ // 验证人员选择 - 只有在需要显示人员选择器的情况下才验证
1145
+ // 判断条件与模板中的 WorkflowPersonSelector 显示条件保持一致
1146
+ const shouldShowPersonSelector = !this.isBranchLastNode.isBranchNode || this.isBranchLastNode.isLastBranch
1147
+
1148
+ if (shouldShowPersonSelector && this.needSelectPerson) {
1149
+ if (this.needMultipleBranchSelection) {
1150
+ // 多分支情况:检查是否所有需要的分支都选择了人员
1151
+ const missingSelections = this.branchNodes.filter(node => !this.branchChargePersons[node.stepId])
1152
+ if (missingSelections.length > 0) {
1153
+ const missingNames = missingSelections.map(node => node.stepName).join('、')
1154
+ this.$message.error(`请设置以下节点的处理人:${missingNames}`)
1155
+ return false
1156
+ }
1157
+ } else {
1158
+ // 单分支情况:检查是否选择了处理人
1159
+ if (!this.checkedChargePerson) {
1160
+ this.$message.error('请设置下一环节处理人')
1161
+ return false
1162
+ }
1163
+ }
1164
+ }
1165
+
1166
+ // 统一获取目标步骤IDs
1167
+ let stepIds = []
1168
+ if (this.needMultipleBranchSelection) {
1169
+ // 多分支选择:获取所有分支的目标stepId(包括不需要选择人员的)
1170
+ if (!this.currentBranchActions) {
1171
+ this.currentBranchActions = this.getBranchActions()
1172
+ }
1173
+ stepIds = this.currentBranchActions.allBranchActions.map(action => action.to)
1174
+ } else if (this.calculatedTargetNode) {
1175
+ // 条件判断节点:使用计算出的目标节点
1176
+ stepIds = [this.calculatedTargetNode]
1177
+ } else {
1178
+ // 普通节点:使用传入的stepId
1179
+ stepIds = [stepId]
1180
+ }
1181
+ // 处理bug 有的时候没带到 branchChargePersons 先循环在同一地方处理
1182
+ stepIds.forEach(stepItemId => {
1183
+ if (!this.branchChargePersons[stepItemId]) {
1184
+ const chargePerson = this.stepsForChild.find(item => item.id === stepItemId)?.properties?.chargePerson
1185
+ this.branchChargePersons[stepItemId] = this.normalizeChargePersonFormat(chargePerson)
1186
+ }
1187
+ })
1188
+
1189
+ return {
1190
+ workflowId: this.workflowId,
1191
+ activeStepId: this.activeStepId,
1192
+ stepId, // 当前步骤ID
1193
+ stepIds, // 目标步骤IDs数组(统一格式)
1194
+ name: this.getStepNameByStepId(stepId),
1195
+ handler: this.getStepHandler(),
1196
+ handlerId: this.checkedChargePerson,
1197
+ needSelectPerson: this.needSelectPerson,
1198
+ personList: this.chargePerson.personList,
1199
+ deadline: this.deadline,
1200
+ submitUser: this.currUser.name,
1201
+ submitUserId: this.currUser.id,
1202
+ branchChargePersons: this.branchChargePersons
1203
+ }
1204
+ },
1205
+
1206
+ // 获取当前选择的负责人
1207
+ getStepHandler () {
1208
+ let stepHandler
1209
+
1210
+ if (this.needMultipleBranchSelection) {
1211
+ // 多分支情况:返回所有分支的人员信息
1212
+ const selectedHandlers = []
1213
+ for (const node of this.branchNodes) {
1214
+ const branchData = this.branchChargePersons[node.stepId]
1215
+
1216
+ if (node.needSelectPerson && branchData) {
1217
+ // 需要选择人员的分支:显示选中的人员
1218
+ const personName = this.getBranchPersonName(node.stepId)
1219
+ selectedHandlers.push(`${node.stepName}:${personName}`)
1220
+ } else if (!node.needSelectPerson && branchData) {
1221
+ // 不需要选择人员的分支:显示角色/部门配置
1222
+ if (branchData.personList && branchData.personList.length > 0) {
1223
+ const personNames = branchData.personList.map(item => item.name).join(',')
1224
+ selectedHandlers.push(`${node.stepName}:${personNames}`)
1225
+ }
1226
+ }
1227
+ }
1228
+ stepHandler = selectedHandlers.join(';')
1229
+ } else {
1230
+ // 单分支情况:原有逻辑
1231
+ if (this.checkedChargePerson) {
1232
+ // 使用 value 找到对应的 label
1233
+ stepHandler = this.chargePersonOptions.find(item => item.value === this.checkedChargePerson)?.label
1234
+ } else if (this.chargePerson.personList && this.chargePerson.personList.length > 0) {
1235
+ stepHandler = this.chargePerson.personList.map(item => item.name).join(',')
1236
+ }
1237
+ }
1238
+
1239
+ return stepHandler
1240
+ },
1241
+
1242
+ // 获取分支节点选择的人员姓名
1243
+ getBranchPersonName (stepId) {
1244
+ const personId = this.branchChargePersons[stepId]
1245
+ if (!personId) return ''
1246
+
1247
+ const node = this.branchNodes.find(n => n.stepId === stepId)
1248
+ if (!node) return ''
1249
+
1250
+ const person = node.chargePersonOptions.find(p => p.value === personId)
1251
+ return person ? person.label : ''
1252
+ },
1253
+ // 生成工作流日志步骤变化描述
1254
+ generateStepChangeText (fromStepId, toStepId) {
1255
+ // 如果是多分支场景,toStepId可能是数组或字符串
1256
+ if (this.needMultipleBranchSelection && Array.isArray(toStepId)) {
1257
+ const targetSteps = toStepId.map(id => `第${id}步: ${this.getStepNameByStepId(id)}`).join('、')
1258
+ return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> ${targetSteps}`
1259
+ } else if (this.needMultipleBranchSelection && typeof toStepId === 'string') {
1260
+ // 如果传入的是目标步骤名称字符串
1261
+ return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> ${toStepId}`
1262
+ } else {
1263
+ // 单步骤场景
1264
+ return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> 第${toStepId}步: ${this.getStepNameByStepId(toStepId)}`
1265
+ }
1266
+ },
1267
+
1268
+ // 生成多分支场景的步骤变化描述
1269
+ generateBranchStepChangeText (fromStepId) {
1270
+ if (!this.needMultipleBranchSelection) {
1271
+ return this.generateStepChangeText(fromStepId, this.nextBtnTo)
1272
+ }
1273
+
1274
+ // 使用所有分支节点生成描述
1275
+ const targetSteps = this.branchNodes.map(node => `第${node.stepId}步: ${node.stepName}`).join('、')
1276
+ return `第${fromStepId}步: ${this.getStepNameByStepId(fromStepId)} --> ${targetSteps}`
1277
+ },
1278
+
1279
+ generateStepChange (fromStepId, toStepId) {
1280
+ // 获取目标步骤IDs数组
1281
+ let stepIds = []
1282
+
1283
+ if (this.needMultipleBranchSelection) {
1284
+ // 多分支选择:获取所有分支的目标stepId(包括不需要选择人员的)
1285
+ if (!this.currentBranchActions) {
1286
+ this.currentBranchActions = this.getBranchActions()
1287
+ }
1288
+ stepIds = this.currentBranchActions.allBranchActions.map(action => action.to)
1289
+ } else if (this.calculatedTargetNode) {
1290
+ // 条件判断节点:使用计算出的目标节点
1291
+ stepIds = [this.calculatedTargetNode]
1292
+ } else {
1293
+ // 普通节点:使用传入的stepId
1294
+ stepIds = [toStepId]
1295
+ }
1296
+
1297
+ return {
1298
+ formStepId: fromStepId,
1299
+ fromStepId,
1300
+ stepId: toStepId,
1301
+ stepIds: stepIds,
1302
+ formStep: this.getStepNameByStepId(fromStepId),
1303
+ fromStep: this.getStepNameByStepId(fromStepId),
1304
+ toStep: this.getStepNameByStepId(stepIds[0]),
1305
+ successStepId: this.stepsDefine[this.stepsDefine.length - 1].id,
1306
+ successStep: this.stepsDefine[this.stepsDefine.length - 1].name
1307
+ }
1308
+ },
1309
+ // 保存工作流日志
1310
+ saveWorkflowLog (operation, desc, extra) {
1311
+ postByServiceName(workFlowViewApi.saveWorkFlowLog, {
1312
+ workflowId: this.workflowId,
1313
+ operation,
1314
+ desc,
1315
+ operator: this.currUser.name,
1316
+ notes: '',
1317
+ setHandler: '',
1318
+ setDeadline: '',
1319
+ ...extra
1320
+ })
1321
+ },
1322
+ // 获取默认截止时间
1323
+ getDefaultDeadline (day = 1) {
1324
+ const date = new Date()
1325
+ date.setDate(date.getDate() + day)
1326
+ date.setHours(date.getHours() + 2)
1327
+ return formatDate(date, 'yyyy-MM-dd hh:mm')
1328
+ },
1329
+ // 验证截止时间
1330
+ checkDeadline (alert) {
1331
+ const deadline = moment(this.deadline)
1332
+ if (deadline.isAfter(this.completeTime, 'day')) {
1333
+ this.deadlineValidateStatus = 'error'
1334
+ this.deadlineHelp = '不能超过任务限定完成时间'
1335
+ if (alert) {
1336
+ this.$message.error('下一环节截止时间不能超过任务限定完成时间,如需设置,请联系发起人修改任务完成时间', 5)
1337
+ }
1338
+ return false
1339
+ }
1340
+ if (deadline.diff(moment(), 'hours') < 1) {
1341
+ this.deadlineValidateStatus = 'error'
1342
+ this.deadlineHelp = '下一环节截止时间只能在1小时之后'
1343
+ return false
1344
+ }
1345
+ this.deadlineValidateStatus = 'success'
1346
+ this.deadlineHelp = ''
1347
+ return true
1348
+ },
1349
+ // 操作类型单选按钮改变时重新设置负责人选择框
1350
+ async handleOperationTypeChange (event) {
1351
+ this.checkedChargePerson = undefined
1352
+ this.branchChargePersons = {}
1353
+ // 重新分析分支节点
1354
+ await this.analyzeBranchNodes()
1355
+ },
1356
+
1357
+ // 处理动态按钮点击
1358
+ handleCustomButtonClick (button) {
1359
+ try {
1360
+ // 执行自定义函数
1361
+ if (button.func) {
1362
+ const result = executeStrFunctionByContext(this, button.func, [this.details, this.$refs.xAddForm.form, util, runLogic, getConfigByNameAsync])
1363
+ if (result) {
1364
+ // 如果返回true,执行对应的操作
1365
+ if (button.text === '提交') {
1366
+ this.nextClick()
1367
+ } else if (button.text === '取消') {
1368
+ this.$emit('cancel')
1369
+ }
1370
+ }
1371
+ }
1372
+ } catch (error) {
1373
+ console.error('执行自定义按钮函数失败:', error)
1374
+ this.$message.error('执行操作失败')
1375
+ }
1376
+ },
1377
+ // 表单项展示函数--用来渲染历史节点数据时和填写表单显示内容同步
1378
+ async showFormItemFunc (func, form) {
1379
+ try {
1380
+ if (func) {
1381
+ // 一般展示项函数中不应该存在setForm函数 先屏蔽this.setForm和this.attr的传递还有在this上下文指向不同可能存在异常 流程表单的展示项函数尽量功能单一。有异常默认进行展示(先兼容到这后续有问题进行调整)
1382
+ const obj = executeStrFunctionByContext(this, func, [form, null, null, util, '新增/修改'])
1383
+ // 判断是 bool 还是 obj 兼容
1384
+ if (typeof obj === 'boolean') {
1385
+ return obj
1386
+ } else if (obj && typeof obj === 'object') {
1387
+ // obj 是一个对象,并且不是数组
1388
+ return obj?.show
1389
+ }
1390
+ } else {
1391
+ return true
1392
+ }
1393
+ } catch (e) {
1394
+ return true
1395
+ }
1396
+ },
1397
+ // 检查按钮是否显示
1398
+ checkButtonVisible (button) {
1399
+ try {
1400
+ if (button.customFunction) {
1401
+ return executeStrFunctionByContext(this, button.customFunction, [this.details, this.$refs.xAddForm.form, util, runLogic, getConfigByNameAsync])
1402
+ }
1403
+ return true
1404
+ } catch (error) {
1405
+ console.error('执行按钮显示函数失败:', error)
1406
+ return false
1407
+ }
1408
+ },
1409
+ formItemEmitFunc (func, data, value) {
1410
+ this.$emit('x-form-item-emit-func', func, data, value)
1411
+ },
1412
+ // 检查条件表达式是否包含WF_RESULT
1413
+ checkIfContainsWfResult () {
1414
+ const conditionalActions = this.currentDirections.filter(action => action.type === 'conditionalBranch')
1415
+ if (conditionalActions.length === 0) return false
1416
+
1417
+ return conditionalActions.some(action => {
1418
+ return action.expression && action.expression.includes('WF_RESULT')
1419
+ })
1420
+ },
1421
+
1422
+ // 分析分支节点并设置选择框
1423
+ async analyzeBranchNodes () {
1424
+ try {
1425
+ // 初始化状态
1426
+ this.resetBranchSelection()
1427
+
1428
+ // 获取所有类型的分支动作并缓存
1429
+ this.currentBranchActions = this.getBranchActions()
1430
+
1431
+ // 如果没有任何分支,处理普通流程
1432
+ if (!this.currentBranchActions.hasAnyBranch) {
1433
+ await this.handleNormalFlow()
1434
+ return
1435
+ }
1436
+
1437
+ // 判断是否需要多分支选择
1438
+ this.needMultipleBranchSelection = this.shouldUseMultipleBranchSelection(this.currentBranchActions)
1439
+
1440
+ if (this.needMultipleBranchSelection) {
1441
+ // 需要为多个分支预先选择人员
1442
+ await this.setupMultipleBranchSelection(this.currentBranchActions.allBranchActions)
1443
+ } else {
1444
+ // 可以前台实时计算的条件分支
1445
+ await this.calculateTargetNodeFromForm(this.currentBranchActions.conditionalActions)
1446
+ }
1447
+ } catch (error) {
1448
+ console.error('分析分支节点失败:', error)
1449
+ this.$message.error('工作流分支配置异常,请联系管理员')
1450
+ // 降级处理:使用普通流程
1451
+ await this.handleNormalFlow()
1452
+ }
1453
+ },
1454
+
1455
+ // 重置分支选择状态
1456
+ resetBranchSelection () {
1457
+ this.branchNodes = []
1458
+ this.branchChargePersons = {}
1459
+ this.needMultipleBranchSelection = false
1460
+ this.calculatedTargetNode = null
1461
+ this.currentBranchActions = null
1462
+ },
1463
+
1464
+ // 获取分支动作分类
1465
+ getBranchActions () {
1466
+ const conditionalActions = this.currentDirections.filter(action => action.type === 'conditionalBranch')
1467
+ const parallelActions = this.currentDirections.filter(action => action.type === 'parallelBranch')
1468
+ const allBranchActions = [...conditionalActions, ...parallelActions]
1469
+
1470
+ return {
1471
+ conditionalActions,
1472
+ parallelActions,
1473
+ allBranchActions,
1474
+ hasAnyBranch: allBranchActions.length > 0
1475
+ }
1476
+ },
1477
+
1478
+ // 判断是否应该使用多分支选择模式
1479
+ shouldUseMultipleBranchSelection (branchActions) {
1480
+ // 情况1: 有并行分支
1481
+ if (branchActions.parallelActions.length > 0) {
1482
+ return true
1483
+ }
1484
+
1485
+ // 情况2: 条件分支包含WF_RESULT(后台结果决定)
1486
+ if (branchActions.conditionalActions.length > 0) {
1487
+ return this.checkIfContainsWfResult()
1488
+ }
1489
+
1490
+ return false
1491
+ },
1492
+
1493
+ // 根据表单数据计算目标节点
1494
+ async calculateTargetNodeFromForm (conditionalActions = null) {
1495
+ if (!conditionalActions) {
1496
+ conditionalActions = this.currentDirections.filter(action => action.type === 'conditionalBranch')
1497
+ }
1498
+
1499
+ if (conditionalActions.length === 0) {
1500
+ // 如果当前节点不是在分支流程中,才清空人员选择
1501
+ // 避免在并行分支节点中误清除人员选择框
1502
+ const currentStep = this.stepsForChild.find(item => item.id === this.activeStepId)
1503
+ const isInBranch = currentStep?.properties?.branchPath
1504
+
1505
+ if (!isInBranch) {
1506
+ this.clearPersonSelection()
1507
+ }
1508
+ return
1509
+ }
1510
+
1511
+ try {
1512
+ const formData = this.$refs.xAddForm?.form || {}
1513
+
1514
+ // 遍历条件找到匹配的
1515
+ for (const action of conditionalActions) {
1516
+ if (action.expression) {
1517
+ const result = await this.evaluateExpression(action.expression, formData)
1518
+ if (result) {
1519
+ this.calculatedTargetNode = action.to
1520
+ await this.setupSingleTargetPersonSelection(action.to)
1521
+ return
1522
+ }
1523
+ }
1524
+ }
1525
+ console.log('this.calculatedTargetNode', this.calculatedTargetNode)
1526
+ // 如果没有匹配的条件,清空选择
1527
+ this.clearPersonSelection()
1528
+ } catch (error) {
1529
+ console.warn('计算目标节点失败:', error)
1530
+ this.clearPersonSelection()
1531
+ }
1532
+ },
1533
+
1534
+ // 清空人员选择
1535
+ clearPersonSelection () {
1536
+ this.calculatedTargetNode = null
1537
+ this.needSelectPerson = false
1538
+ this.chargePersonOptions = []
1539
+ this.checkedChargePerson = undefined
1540
+ },
1541
+
1542
+ // 设置单个目标节点的人员选择
1543
+ async setupSingleTargetPersonSelection (stepId) {
1544
+ const stepDefine = this.stepsDefine.find(step => step.id === stepId)
1545
+
1546
+ if (!stepDefine?.properties?.chargePerson?.needSelectPerson) {
1547
+ this.needSelectPerson = false
1548
+ this.chargePersonOptions = []
1549
+ return
1550
+ }
1551
+
1552
+ // 设置人员选择
1553
+ this.chargePerson = stepDefine.properties.chargePerson
1554
+ this.checkedChargePerson = undefined
1555
+ this.chargePersonOptions = await this.getChargePersonOptionsForStep(stepId)
1556
+
1557
+ // 如果只有一个选项,自动选中
1558
+ if (this.chargePersonOptions.length === 1) {
1559
+ this.checkedChargePerson = this.chargePersonOptions[0].value
1560
+ }
1561
+
1562
+ this.needSelectPerson = true
1563
+ },
1564
+
1565
+ // 表单数据变化处理函数(将通过provide传递给表单组件)
1566
+ async handleFormDataChange (formData) {
1567
+ // 只有在不是多分支选择模式且当前活动步骤有条件分支动作时才重新计算
1568
+ if (!this.needMultipleBranchSelection && this.currentDirections.some(action => action.type === 'conditionalBranch')) {
1569
+ await this.calculateTargetNodeFromForm()
1570
+ }
1571
+ },
1572
+
1573
+ // 表达式评估 - 使用 LogicRunner
1574
+ async evaluateExpression (expression, formData) {
1575
+ try {
1576
+ // 构建参数对象,传递当前表单数据
1577
+ const params = {
1578
+ WF_FORM: { ...formData }
1579
+ }
1580
+
1581
+ // 使用 LogicRunner 执行表达式
1582
+ const result = await LogicRunner.runExpression(expression, params)
1583
+ return result
1584
+ } catch (error) {
1585
+ console.warn('表达式评估失败:', expression, error)
1586
+ return false
1587
+ }
1588
+ },
1589
+
1590
+ // 获取指定步骤的人员选项
1591
+ async getChargePersonOptionsForStep (stepId) {
1592
+ const define = this.stepsDefine.find(item => item.id === stepId)
1593
+ if (!define?.properties?.chargePerson) return []
1594
+
1595
+ const chargePerson = define.properties.chargePerson
1596
+ this.chargePerson = this.normalizeChargePersonFormat(chargePerson)
1597
+
1598
+ if (!chargePerson.needSelectPerson || !chargePerson.personList) return []
1599
+
1600
+ // 获取所有用户信息
1601
+ const allUser = await postByServiceName(commonApi.getAllUserOptionList, {})
1602
+
1603
+ // 根据配置筛选用户
1604
+ const options = this.filterUsersByPersonConfig(chargePerson.personList, allUser)
1605
+
1606
+ // 去重处理
1607
+ return Array.from(new Map(options.map(item => [item.value, item])).values())
1608
+ },
1609
+
1610
+ // 标准化人员配置格式(兼容旧格式)
1611
+ normalizeChargePersonFormat (chargePerson) {
1612
+ if (chargePerson.role || chargePerson.department) {
1613
+ chargePerson.needSelectPerson = true
1614
+ chargePerson.personList = [{
1615
+ type: chargePerson.role ? 'role' : 'department',
1616
+ name: chargePerson.role || chargePerson.department
1617
+ }]
1618
+ }
1619
+ return chargePerson
1620
+ },
1621
+
1622
+ // 根据人员配置筛选用户
1623
+ filterUsersByPersonConfig (personList, allUsers) {
1624
+ return personList.reduce((acc, personItem) => {
1625
+ let filteredUsers = []
1626
+
1627
+ if (personItem.type === 'role') {
1628
+ filteredUsers = allUsers.filter(user =>
1629
+ user.rolestr && user.rolestr.split(',').includes(personItem.name)
1630
+ ).map(user => ({
1631
+ label: user.label,
1632
+ value: user.value
1633
+ }))
1634
+ } else if (personItem.type === 'department') {
1635
+ filteredUsers = allUsers.filter(user =>
1636
+ user.depname === personItem.name
1637
+ ).map(user => ({
1638
+ label: user.label,
1639
+ value: user.value
1640
+ }))
1641
+ }
1642
+
1643
+ return [...acc, ...filteredUsers]
1644
+ }, [])
1645
+ },
1646
+
1647
+ // 设置多分支人员选择
1648
+ async setupMultipleBranchSelection (branchActions) {
1649
+ this.branchNodes = []
1650
+ this.branchChargePersons = {}
1651
+ let hasPersonSelection = false
1652
+
1653
+ for (const action of branchActions) {
1654
+ const stepDefine = this.stepsDefine.find(step => step.id === action.to)
1655
+ const chargePerson = stepDefine?.properties?.chargePerson
1656
+
1657
+ // 所有分支节点都加入branchNodes,保持数据结构完整
1658
+ const nodeData = {
1659
+ stepId: action.to,
1660
+ stepName: this.getStepNameByStepId(action.to),
1661
+ chargePerson: chargePerson,
1662
+ needSelectPerson: chargePerson?.needSelectPerson || false,
1663
+ chargePersonOptions: []
1664
+ }
1665
+
1666
+ if (chargePerson?.needSelectPerson) {
1667
+ // 需要选择人员的分支
1668
+ const chargePersonOptions = await this.getChargePersonOptionsForStep(action.to)
1669
+ nodeData.chargePersonOptions = chargePersonOptions
1670
+
1671
+ // 初始化选择值
1672
+ this.branchChargePersons[action.to] = undefined
1673
+ hasPersonSelection = true
1674
+ } else {
1675
+ // 不需要选择人员的分支,直接存储配置
1676
+ this.branchChargePersons[action.to] = chargePerson
1677
+ }
1678
+
1679
+ this.branchNodes.push(nodeData)
1680
+ }
1681
+
1682
+ // 只有当存在需要选择人员的分支时才显示选择区域
1683
+ this.needSelectPerson = hasPersonSelection
1684
+ },
1685
+
1686
+ // 处理普通单线流程(没有分支的情况)
1687
+ async handleNormalFlow () {
1688
+ const targetStepId = this.operationType === 'skip' ? this.stepNextBtnTo : this.nextBtnTo
1689
+
1690
+ this.calculatedTargetNode = targetStepId
1691
+ const stepDefine = this.stepsDefine.find(step => step.id === targetStepId)
1692
+
1693
+ if (!stepDefine) {
1694
+ console.warn('未找到目标步骤定义:', targetStepId)
1695
+ this.clearPersonSelection()
1696
+ return
1697
+ }
1698
+
1699
+ const defineProperties = stepDefine.properties
1700
+ this.chargePerson = defineProperties?.chargePerson
1701
+
1702
+ // 兼容旧格式
1703
+ if (defineProperties.chargePerson) {
1704
+ defineProperties.chargePerson = this.normalizeChargePersonFormat(defineProperties.chargePerson)
1705
+ }
1706
+
1707
+ if (defineProperties?.chargePerson?.needSelectPerson) {
1708
+ await this.setupSingleTargetPersonSelection(targetStepId)
1709
+ // 确保 branchChargePersons 中有该节点的数据结构
1710
+ this.branchChargePersons[targetStepId] = {
1711
+ handler: '',
1712
+ handlerId: undefined,
1713
+ ...defineProperties.chargePerson
1714
+ }
1715
+ } else {
1716
+ // 不需要选择人员的情况,直接存储配置
1717
+ this.branchChargePersons[targetStepId] = defineProperties?.chargePerson || {}
1718
+ this.needSelectPerson = false
1719
+ this.chargePersonOptions = []
1720
+ }
1721
+ }
1722
+ },
1723
+ watch: {}
1724
+ }
1725
+ </script>
1726
+ <style lang="less" scoped>
1727
+ .complete-data-title {
1728
+ margin-top: 8px;
1729
+ margin-bottom: 8px;
1730
+ }
1731
+
1732
+ :deep(.ant-result) {
1733
+ padding: 0;
1734
+ }
1735
+
1736
+ .descriptionPreviewItem {
1737
+ margin-bottom: 22px;
1738
+
1739
+ :deep(.ant-descriptions-view) {
1740
+ overflow: visible;
1741
+ }
1742
+ }
1743
+
1744
+ .allWidth {
1745
+ :deep(.ant-descriptions-item-content) {
1746
+ width: 100%;
1747
+ }
1748
+ }
1749
+
1750
+ .descriptionTitle {
1751
+ position: relative;
1752
+ padding-left: 12px;
1753
+
1754
+ &::before {
1755
+ content: '';
1756
+ position: absolute;
1757
+ left: 0;
1758
+ top: 50%;
1759
+ transform: translateY(-50%);
1760
+ width: 4px;
1761
+ height: 16px;
1762
+ background-color: @primary-color;
1763
+ border-radius: 2px;
1764
+ }
1765
+ }
1766
+ </style>