vue2-client 1.16.27 → 1.16.28

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 (324) hide show
  1. package/.cursorrules +19 -19
  2. package/.env.apply +19 -19
  3. package/.env.gaslink +19 -19
  4. package/.env.his +19 -19
  5. package/.env.liuli +20 -20
  6. package/.env.scada +19 -19
  7. package/.eslintrc.js +90 -90
  8. package/CHANGELOG.md +824 -824
  9. package/Components.md +60 -60
  10. package/docs/LowCode/lowcode.md +155 -155
  11. package/docs/LowCode/lowcodeForDeveloper.md +230 -230
  12. package/docs/index.md +30 -30
  13. package/index.js +31 -31
  14. package/jest-transform-stub.js +8 -8
  15. package/jest.setup.js +7 -7
  16. package/jsconfig.json +19 -19
  17. package/package.json +112 -112
  18. package/public/his/editor/editor.html +51 -51
  19. package/public/his/editor/mock/bind_data.html +779 -779
  20. package/public/his/editor/mock/data_table.html +40 -40
  21. package/public/his/editor/mock/sign.html +75 -75
  22. package/public/his/editor/vender/JsBarcode.all.js +3669 -3669
  23. package/public/his/editor/vender/date97/My97DatePicker.htm +65 -65
  24. package/public/his/editor/vender/date97/WdatePicker.js +677 -677
  25. package/public/his/editor/vender/date97/calendar.js +4 -4
  26. package/public/his/editor/vender/date97/lang/en.js +13 -13
  27. package/public/his/editor/vender/date97/lang/zh-cn.js +13 -13
  28. package/public/his/editor/vender/date97/lang/zh-tw.js +13 -13
  29. package/public/his/editor/vender/date97/skin/WdatePicker.css +10 -10
  30. package/public/his/editor/vender/date97/skin/default/datepicker.css +328 -328
  31. package/public/his/editor/vender/date97/skin/ext/datepicker.css +308 -308
  32. package/public/his/editor/vender/date97/skin/whyGreen/datepicker.css +255 -255
  33. package/public/his/editor/vender/diff.js +1627 -1627
  34. package/public/his/editor/vender/editor.js +1 -1
  35. package/public/his/editor/vender/fabric.js +31187 -31187
  36. package/public/his/editor/vender/jquery/jquery.base64.js +190 -190
  37. package/public/his/editor/vender/jquery/jquery.js +10872 -10872
  38. package/public/his/editor/vender/jquery/jquery.print.js +255 -255
  39. package/public/his/editor/vender/jquery/zTreeStyle/zTreeStyle.css +96 -96
  40. package/public/his/editor/vender/mui/mui.min.css +4 -4
  41. package/public/his/editor/vender/mui/mui.min.js +5 -5
  42. package/public/his/editor/vender/mui/mui.picker.min.css +6 -6
  43. package/public/his/editor/vender/mui/mui.picker.min.js +6 -6
  44. package/public/his/editor/vender/qrcode.js +7 -7
  45. package/public/his/editor/vender/requirejs/require.js +2145 -2145
  46. package/public/his/editor/vender/signature/jSignature.CompressorSVG.js +518 -518
  47. package/public/his/editor/vender/signature/jSignature.UndoButton.js +164 -164
  48. package/public/his/editor/vender/signature/jSignature.js +1486 -1486
  49. package/public/his/editor/vender/validator.js +5094 -5094
  50. package/public/his/editor/vender/weui/weui.css +5659 -5659
  51. package/public/his/editor/vender/weui/weui.min.css +4 -4
  52. package/public/his/editor/vender/weui/weui.min.js +11 -11
  53. package/src/assets/img/querySlotDemo.svg +15 -15
  54. package/src/assets/svg/badtwo.svg +1 -1
  55. package/src/assets/svg/goodtwo.svg +1 -1
  56. package/src/base-client/components/AI/AskAiBtn.vue +136 -136
  57. package/src/base-client/components/AI/demo.vue +31 -31
  58. package/src/base-client/components/common/AddressSearchCombobox/IcMapIcon.vue +16 -16
  59. package/src/base-client/components/common/AddressSearchCombobox/demo.vue +36 -36
  60. package/src/base-client/components/common/AddressSearchCombobox/ic_map.svg +6 -6
  61. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  62. package/src/base-client/components/common/CitySelect/index.js +3 -3
  63. package/src/base-client/components/common/CitySelect/index.md +109 -109
  64. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  65. package/src/base-client/components/common/CreateQuery/CreateQueryItem.vue +1014 -1014
  66. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  67. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  68. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQuery.vue +452 -452
  69. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQueryItem.vue +511 -511
  70. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  71. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  72. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  73. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  74. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  75. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  76. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  77. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  78. package/src/base-client/components/common/LowCodeComponent/LowCodeEditorModal.vue +108 -108
  79. package/src/base-client/components/common/LowCodeComponent/LowCodeEditorPanel.vue +413 -413
  80. package/src/base-client/components/common/LowCodeComponent/LowCodePageOrganization.vue +502 -502
  81. package/src/base-client/components/common/LowCodeComponent/LowCodeRender.vue +728 -728
  82. package/src/base-client/components/common/LowCodeComponent/LowCodeRenderEnter.vue +29 -29
  83. package/src/base-client/components/common/LowCodeComponent/LowCodeUIStore.vue +219 -219
  84. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeAddPageModal.vue +117 -117
  85. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeCustomJSModal.vue +80 -80
  86. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeEventEditorModal.vue +398 -398
  87. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLifeCycleModal.vue +65 -65
  88. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLogicCallbackModal.vue +64 -64
  89. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLogicParamModal.vue +73 -73
  90. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeRunFunctionParamModal.vue +76 -76
  91. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  92. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  93. package/src/base-client/components/common/Recording/Recording.vue +243 -243
  94. package/src/base-client/components/common/Recording/index.js +3 -3
  95. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  96. package/src/base-client/components/common/Tree/index.js +2 -2
  97. package/src/base-client/components/common/Upload/Upload.vue +333 -333
  98. package/src/base-client/components/common/Upload/index.js +3 -3
  99. package/src/base-client/components/common/XAddForm/XAddForm.vue +113 -113
  100. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  101. package/src/base-client/components/common/XAddNativeFormOA/XAddNativeFormOA.vue +303 -303
  102. package/src/base-client/components/common/XAddNativeFormOA/index.js +3 -3
  103. package/src/base-client/components/common/XAddNativeFormOA/index.md +146 -146
  104. package/src/base-client/components/common/XAddReport/index.js +3 -3
  105. package/src/base-client/components/common/XAddReport/index.md +56 -56
  106. package/src/base-client/components/common/XBadge/XBadge.vue +94 -94
  107. package/src/base-client/components/common/XButtons/XButtonDemo.vue +28 -28
  108. package/src/base-client/components/common/XButtons/index.js +3 -3
  109. package/src/base-client/components/common/XButtons/index.md +61 -61
  110. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  111. package/src/base-client/components/common/XCheckList/XCheckList.vue +106 -106
  112. package/src/base-client/components/common/XCheckList/XCheckListDemo.vue +41 -41
  113. package/src/base-client/components/common/XDataCard/index.js +3 -3
  114. package/src/base-client/components/common/XDataCard/index.md +1 -1
  115. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  116. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  117. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  118. package/src/base-client/components/common/XDatePicker/demo.vue +153 -153
  119. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  120. package/src/base-client/components/common/XDescriptions/index.md +83 -83
  121. package/src/base-client/components/common/XDetailsView/XDetailsView.vue +238 -238
  122. package/src/base-client/components/common/XDetailsView/index.js +3 -3
  123. package/src/base-client/components/common/XForm/XFormItem.vue +1504 -1504
  124. package/src/base-client/components/common/XForm/XStatusButton.vue +54 -54
  125. package/src/base-client/components/common/XForm/index.md +178 -178
  126. package/src/base-client/components/common/XForm/itemComponent/XClickChangeBtn/index.vue +49 -49
  127. package/src/base-client/components/common/XFormGroup/index.js +3 -3
  128. package/src/base-client/components/common/XFormGroup/index.md +38 -38
  129. package/src/base-client/components/common/XFormGroupDetails/XFormGroupDetails.vue +72 -72
  130. package/src/base-client/components/common/XFormGroupDetails/index.js +3 -3
  131. package/src/base-client/components/common/XFormTable/index.md +92 -92
  132. package/src/base-client/components/common/XLabelSelect/XLabelSelect.vue +110 -110
  133. package/src/base-client/components/common/XLabelSelect/XLabelSelectDemo.vue +35 -35
  134. package/src/base-client/components/common/XLicensePlate/XLicensePlate.vue +193 -193
  135. package/src/base-client/components/common/XLicensePlate/XLicensePlateDemo.vue +48 -48
  136. package/src/base-client/components/common/XPrint/OpenInvoice.vue +21 -21
  137. package/src/base-client/components/common/XPrint/PrintHtml.js +98 -98
  138. package/src/base-client/components/common/XPrint/css/hiPrintCss.js +359 -359
  139. package/src/base-client/components/common/XPrint/css/lodopCss.js +26 -26
  140. package/src/base-client/components/common/XPrint/css/print-lock.css +351 -351
  141. package/src/base-client/components/common/XPrint/index.vue +97 -97
  142. package/src/base-client/components/common/XReport/XReportDesign.vue +463 -463
  143. package/src/base-client/components/common/XReport/XReportJsonRender.vue +381 -381
  144. package/src/base-client/components/common/XReport/index.js +3 -3
  145. package/src/base-client/components/common/XReport/print.js +186 -186
  146. package/src/base-client/components/common/XReportDrawer/index.js +3 -3
  147. package/src/base-client/components/common/XReportGrid/index.js +3 -3
  148. package/src/base-client/components/common/XReportGrid/index.md +44 -44
  149. package/src/base-client/components/common/XReportSlot/XReportSlot.vue +110 -110
  150. package/src/base-client/components/common/XReportSlot/index.js +3 -3
  151. package/src/base-client/components/common/XReportSlot/index.md +48 -48
  152. package/src/base-client/components/common/XSimpleDescriptions/XSimpleDescriptions.vue +166 -166
  153. package/src/base-client/components/common/XSimpleDescriptions/index.js +3 -3
  154. package/src/base-client/components/common/XSimpleDescriptions/index.md +7 -7
  155. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  156. package/src/base-client/components/common/XStepView/index.js +3 -3
  157. package/src/base-client/components/common/XStepView/index.md +31 -31
  158. package/src/base-client/components/common/XTab/XTabDemo.vue +22 -22
  159. package/src/base-client/components/common/XTab/index.js +3 -3
  160. package/src/base-client/components/common/XTable/CustomFuncCel.vue +51 -51
  161. package/src/base-client/components/common/XTable/TableCellRenderer.vue +161 -161
  162. package/src/base-client/components/common/XTable/index.md +255 -255
  163. package/src/base-client/components/common/XTagGroup/index.vue +52 -52
  164. package/src/base-client/components/common/XTree/XTree.vue +424 -424
  165. package/src/base-client/components/common/XTree/index.js +3 -3
  166. package/src/base-client/components/common/XTree/index.md +36 -36
  167. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +113 -113
  168. package/src/base-client/components/common/XTreeOne/XTreeOnePro.vue +128 -128
  169. package/src/base-client/components/common/richTextModal/index.vue +56 -56
  170. package/src/base-client/components/common/richTextModal/richDemo.vue +48 -48
  171. package/src/base-client/components/his/XHisEditor/index.js +3 -3
  172. package/src/base-client/components/index.js +51 -51
  173. package/src/base-client/components/layout/XTreeView/XTreeView.vue +130 -130
  174. package/src/base-client/components/layout/XTreeView/index.js +3 -3
  175. package/src/base-client/components/layout/XTreeView/index.md +46 -46
  176. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  177. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +281 -281
  178. package/src/base-client/plugins/Config.js +19 -19
  179. package/src/base-client/plugins/GetLoginInfoService.js +183 -183
  180. package/src/base-client/plugins/Recording.js +258 -258
  181. package/src/base-client/plugins/index.js +23 -23
  182. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  183. package/src/components/Charts/Bar.vue +62 -62
  184. package/src/components/Charts/ChartCard.vue +134 -134
  185. package/src/components/Charts/Liquid.vue +67 -67
  186. package/src/components/Charts/MiniArea.vue +39 -39
  187. package/src/components/Charts/MiniBar.vue +39 -39
  188. package/src/components/Charts/MiniProgress.vue +75 -75
  189. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  190. package/src/components/Charts/Radar.vue +68 -68
  191. package/src/components/Charts/RankList.vue +77 -77
  192. package/src/components/Charts/TagCloud.vue +113 -113
  193. package/src/components/Charts/TransferBar.vue +64 -64
  194. package/src/components/Charts/Trend.vue +82 -82
  195. package/src/components/Charts/chart.less +12 -12
  196. package/src/components/Charts/smooth.area.less +13 -13
  197. package/src/components/CodeMirror/inedx.vue +118 -118
  198. package/src/components/CodeMirror/setting.js +40 -40
  199. package/src/components/FileImageItem/FileItem.vue +320 -320
  200. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  201. package/src/components/NumberInfo/index.js +3 -3
  202. package/src/components/NumberInfo/index.less +54 -54
  203. package/src/components/NumberInfo/index.md +43 -43
  204. package/src/components/card/ChartCard.vue +79 -79
  205. package/src/components/chart/Bar.vue +60 -60
  206. package/src/components/chart/MiniArea.vue +67 -67
  207. package/src/components/chart/MiniBar.vue +59 -59
  208. package/src/components/chart/MiniProgress.vue +57 -57
  209. package/src/components/chart/Radar.vue +80 -80
  210. package/src/components/chart/RankingList.vue +60 -60
  211. package/src/components/chart/Trend.vue +79 -79
  212. package/src/components/chart/index.less +9 -9
  213. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  214. package/src/components/checkbox/ImgCheckbox.vue +117 -117
  215. package/src/components/checkbox/ImgCheckboxGroup.vue +76 -76
  216. package/src/components/checkbox/index.js +9 -9
  217. package/src/components/exception/ExceptionPage.vue +70 -70
  218. package/src/components/g2Charts/constants.js +202 -202
  219. package/src/components/g2Charts/demo.vue +808 -808
  220. package/src/components/g2Charts/designer.vue +228 -228
  221. package/src/components/g2Charts/designerBaseConfig.vue +61 -61
  222. package/src/components/g2Charts/designerDataConfig.vue +259 -259
  223. package/src/components/g2Charts/designerStyleConfig.vue +16 -16
  224. package/src/components/g2Charts/index.vue +397 -397
  225. package/src/components/index.js +36 -36
  226. package/src/components/input/IInput.vue +66 -66
  227. package/src/components/menu/SideMenu.vue +75 -75
  228. package/src/components/menu/menu.js +273 -273
  229. package/src/components/setting/Setting.vue +234 -234
  230. package/src/components/tool/AStepItem.vue +60 -60
  231. package/src/config/CreateQueryConfig.js +325 -325
  232. package/src/config/default/antd.config.js +89 -89
  233. package/src/config/default/setting.config.js +55 -55
  234. package/src/font-style/font.css +60 -60
  235. package/src/layouts/CommonLayout.vue +56 -56
  236. package/src/layouts/PageLayout.vue +151 -151
  237. package/src/layouts/SinglePageView.vue +136 -136
  238. package/src/layouts/header/AdminHeader.vue +132 -132
  239. package/src/layouts/header/HeaderNotice.vue +177 -177
  240. package/src/layouts/header/InstitutionDetail.vue +181 -181
  241. package/src/layouts/tabs/TabsHead.vue +189 -189
  242. package/src/lib.js +1 -1
  243. package/src/mock/extend/index.js +84 -84
  244. package/src/mock/goods/index.js +108 -108
  245. package/src/pages/DefaultExample/index.vue +77 -77
  246. package/src/pages/DynamicStatistics/ChartSelector.vue +331 -331
  247. package/src/pages/DynamicStatistics/DataTabs.vue +83 -83
  248. package/src/pages/DynamicStatistics/DynamicTable.vue +128 -128
  249. package/src/pages/DynamicStatistics/EvaluationArea.vue +69 -69
  250. package/src/pages/DynamicStatistics/FavoriteList.vue +50 -50
  251. package/src/pages/DynamicStatistics/QuestionHistoryAndFavorites.vue +591 -591
  252. package/src/pages/DynamicStatistics/SearchBar.vue +192 -192
  253. package/src/pages/DynamicStatistics/index.vue +282 -282
  254. package/src/pages/Example/childIndex.vue +15 -15
  255. package/src/pages/Example/index.vue +30 -30
  256. package/src/pages/NewDynamicStatistics/ChartSelector.vue +331 -331
  257. package/src/pages/NewDynamicStatistics/DataTabs.vue +122 -122
  258. package/src/pages/NewDynamicStatistics/DynamicTable.vue +128 -128
  259. package/src/pages/NewDynamicStatistics/EvaluationArea.vue +69 -69
  260. package/src/pages/NewDynamicStatistics/FavoriteList.vue +50 -50
  261. package/src/pages/NewDynamicStatistics/QuestionHistoryAndFavorites.vue +289 -289
  262. package/src/pages/NewDynamicStatistics/SearchBar.vue +193 -193
  263. package/src/pages/NewDynamicStatistics/index.vue +258 -258
  264. package/src/pages/Recording/index.vue +77 -77
  265. package/src/pages/ServiceReview/index.vue +284 -284
  266. package/src/pages/SubExample/index.vue +26 -26
  267. package/src/pages/WorkflowDetail/WorkflowPageDetail/TrimTextTail.vue +23 -23
  268. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue +1766 -1766
  269. package/src/pages/XReportView/index.vue +64 -64
  270. package/src/pages/XTreeOneProExample/index.vue +67 -67
  271. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  272. package/src/pages/login/Login.vue +379 -379
  273. package/src/pages/login/LoginV3.vue +389 -389
  274. package/src/pages/lowCode/lowCodeEditor.vue +1219 -1219
  275. package/src/pages/lowCode/lowCodeRenderPage.vue +43 -43
  276. package/src/pages/report/ReportTable.js +124 -124
  277. package/src/pages/resourceManage/orgListManage.vue +98 -98
  278. package/src/pages/system/dictionary/index.vue +44 -44
  279. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  280. package/src/pages/system/monitor/operLog/index.vue +37 -37
  281. package/src/pages/system/settings/modifyPassword.vue +117 -117
  282. package/src/pages/system/ticket/index.vue +480 -480
  283. package/src/pages/system/ticket/submitTicketSuccess.vue +484 -484
  284. package/src/pages/userInfoDetailManage/ChangeMeterRecordQuery/index.vue +64 -64
  285. package/src/pages/userInfoDetailManage/InfoChangeRecordQuery/index.vue +64 -64
  286. package/src/pages/userInfoDetailManage/InstructRecordQuery/index.vue +64 -64
  287. package/src/pages/userInfoDetailManage/MeterParamRecordQuery/index.vue +64 -64
  288. package/src/pages/userInfoDetailManage/TransferRecordQuery/index.vue +66 -66
  289. package/src/pages/userInfoDetailManage/WatchCollectionRecordQuery/index.vue +64 -64
  290. package/src/plugins/EventLogPlugin.js +33 -33
  291. package/src/plugins/FindParentsData.js +17 -17
  292. package/src/router/async/config.async.js +35 -35
  293. package/src/router/guards.js +7 -2
  294. package/src/router/index.js +27 -27
  295. package/src/services/DataModel.js +30 -30
  296. package/src/services/LodopFuncs.js +137 -137
  297. package/src/services/api/TicketDetailsViewApi.js +46 -46
  298. package/src/services/api/cas.js +79 -79
  299. package/src/services/api/common.js +346 -346
  300. package/src/services/api/entity.js +18 -18
  301. package/src/services/api/index.js +17 -17
  302. package/src/store/modules/account.js +115 -115
  303. package/src/store/modules/index.js +5 -5
  304. package/src/store/modules/lowCode.js +33 -33
  305. package/src/store/modules/setting.js +119 -119
  306. package/src/theme/default/style.less +58 -58
  307. package/src/utils/authority-utils.js +85 -85
  308. package/src/utils/errorCode.js +6 -6
  309. package/src/utils/formatter.js +74 -74
  310. package/src/utils/htmlToPDF.js +108 -108
  311. package/src/utils/htmlToPDFApi.js +5 -5
  312. package/src/utils/login.js +188 -188
  313. package/src/utils/lowcode/lowcodeComponentMixin.js +120 -120
  314. package/src/utils/lowcode/lowcodeLog.js +29 -29
  315. package/src/utils/lowcode/lowcodeUtils.js +373 -373
  316. package/src/utils/lowcode/registerComponentForEditor.js +1 -1
  317. package/src/utils/lowcode/registerComponentForRender.js +11 -11
  318. package/src/utils/map-utils.js +47 -47
  319. package/src/utils/reg.js +95 -95
  320. package/src/utils/runEvalFunction.js +14 -14
  321. package/src/utils/theme-color-replacer-extend.js +92 -92
  322. package/src/utils/util.js +329 -329
  323. package/src/utils/waterMark.js +31 -31
  324. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
@@ -1,1766 +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
- @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 '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowTimeline.vue'
276
- import { FileItem, ImageItem } from '@vue2-client/components/FileImageItem'
277
- import WorkflowListResolution from '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/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 '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowPreview'
284
- import WorkflowPersonSelector from '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/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 === res.id)
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>
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 '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowTimeline.vue'
276
+ import { FileItem, ImageItem } from '@vue2-client/components/FileImageItem'
277
+ import WorkflowListResolution from '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/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 '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowPreview'
284
+ import WorkflowPersonSelector from '@vue2-client/pages/WorkflowDetail/WorkflowPageDetail/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 === res.id)
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>