ProcessInstanceOperationButton.vue 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175
  1. <template>
  2. <div
  3. class="h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
  4. >
  5. <!-- 【通过】按钮 -->
  6. <el-popover
  7. :visible="popOverVisible.approve"
  8. placement="top-end"
  9. :width="420"
  10. trigger="click"
  11. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.APPROVE)"
  12. >
  13. <template #reference>
  14. <el-button plain type="success" @click="openPopover('approve')">
  15. <Icon icon="ep:select" />&nbsp; {{ getButtonDisplayName(OperationButtonType.APPROVE) }}
  16. </el-button>
  17. </template>
  18. <!-- 审批表单 -->
  19. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  20. <el-form
  21. label-position="top"
  22. class="mb-auto"
  23. ref="approveFormRef"
  24. :model="approveReasonForm"
  25. :rules="approveReasonRule"
  26. label-width="100px"
  27. >
  28. <el-card v-if="runningTask?.formId > 0" class="mb-15px !-mt-10px">
  29. <template #header>
  30. <span class="el-icon-picture-outline"> 填写表单【{{ runningTask?.formName }}】 </span>
  31. </template>
  32. <form-create
  33. v-model="approveForm.value"
  34. v-model:api="approveFormFApi"
  35. :option="approveForm.option"
  36. :rule="approveForm.rule"
  37. />
  38. </el-card>
  39. <el-form-item :label="`${nodeTypeName}意见`" prop="reason">
  40. <el-input
  41. v-model="approveReasonForm.reason"
  42. :placeholder="`请输入${nodeTypeName}意见`"
  43. type="textarea"
  44. :rows="4"
  45. />
  46. </el-form-item>
  47. <el-form-item
  48. label="下一个节点的审批人"
  49. prop="nextAssignees"
  50. v-if="nextAssigneesActivityNode.length > 0"
  51. >
  52. <div class="ml-10px -mt-15px -mb-35px">
  53. <ProcessInstanceTimeline
  54. :activity-nodes="nextAssigneesActivityNode"
  55. :show-status-icon="false"
  56. @select-user-confirm="selectNextAssigneesConfirm"
  57. />
  58. </div>
  59. </el-form-item>
  60. <el-form-item
  61. v-if="runningTask.signEnable"
  62. label="签名"
  63. prop="signPicUrl"
  64. ref="approveSignFormRef"
  65. >
  66. <el-button @click="signRef.open()">点击签名</el-button>
  67. <el-image
  68. class="w-90px h-40px ml-5px"
  69. v-if="approveReasonForm.signPicUrl"
  70. :src="approveReasonForm.signPicUrl"
  71. :preview-src-list="[approveReasonForm.signPicUrl]"
  72. />
  73. </el-form-item>
  74. <el-form-item>
  75. <el-button
  76. :disabled="formLoading"
  77. type="success"
  78. @click="handleAudit(true, approveFormRef)"
  79. >
  80. {{ getButtonDisplayName(OperationButtonType.APPROVE) }}
  81. </el-button>
  82. <el-button @click="closePopover('approve', approveFormRef)"> 取消 </el-button>
  83. </el-form-item>
  84. </el-form>
  85. </div>
  86. </el-popover>
  87. <!-- 【拒绝】按钮 -->
  88. <el-popover
  89. :visible="popOverVisible.reject"
  90. placement="top-end"
  91. :width="420"
  92. trigger="click"
  93. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.REJECT)"
  94. >
  95. <template #reference>
  96. <el-button class="mr-20px" plain type="danger" @click="openPopover('reject')">
  97. <Icon icon="ep:close" />&nbsp; {{ getButtonDisplayName(OperationButtonType.REJECT) }}
  98. </el-button>
  99. </template>
  100. <!-- 审批表单 -->
  101. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  102. <el-form
  103. label-position="top"
  104. class="mb-auto"
  105. ref="rejectFormRef"
  106. :model="rejectReasonForm"
  107. :rules="rejectReasonRule"
  108. label-width="100px"
  109. >
  110. <el-form-item label="审批意见" prop="reason">
  111. <el-input
  112. v-model="rejectReasonForm.reason"
  113. placeholder="请输入审批意见"
  114. type="textarea"
  115. :rows="4"
  116. />
  117. </el-form-item>
  118. <el-form-item>
  119. <el-button
  120. :disabled="formLoading"
  121. type="danger"
  122. @click="handleAudit(false, rejectFormRef)"
  123. >
  124. {{ getButtonDisplayName(OperationButtonType.REJECT) }}
  125. </el-button>
  126. <el-button @click="closePopover('reject', rejectFormRef)"> 取消 </el-button>
  127. </el-form-item>
  128. </el-form>
  129. </div>
  130. </el-popover>
  131. <!-- 【抄送】按钮 -->
  132. <el-popover
  133. :visible="popOverVisible.copy"
  134. placement="top-start"
  135. :width="420"
  136. trigger="click"
  137. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.COPY)"
  138. >
  139. <template #reference>
  140. <div @click="openPopover('copy')" class="hover-bg-gray-100 rounded-xl p-6px">
  141. <Icon :size="14" icon="svg-icon:send" />&nbsp;
  142. {{ getButtonDisplayName(OperationButtonType.COPY) }}
  143. </div>
  144. </template>
  145. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  146. <el-form
  147. label-position="top"
  148. class="mb-auto"
  149. ref="copyFormRef"
  150. :model="copyForm"
  151. :rules="copyFormRule"
  152. label-width="100px"
  153. >
  154. <el-form-item label="抄送人" prop="copyUserIds">
  155. <el-select
  156. v-model="copyForm.copyUserIds"
  157. clearable
  158. style="width: 100%"
  159. multiple
  160. placeholder="请选择抄送人"
  161. >
  162. <el-option
  163. v-for="item in userOptions"
  164. :key="item.id"
  165. :label="item.nickname"
  166. :value="item.id"
  167. />
  168. </el-select>
  169. </el-form-item>
  170. <el-form-item label="抄送意见" prop="copyReason">
  171. <el-input
  172. v-model="copyForm.copyReason"
  173. clearable
  174. placeholder="请输入抄送意见"
  175. type="textarea"
  176. :rows="3"
  177. />
  178. </el-form-item>
  179. <el-form-item>
  180. <el-button :disabled="formLoading" type="primary" @click="handleCopy">
  181. {{ getButtonDisplayName(OperationButtonType.COPY) }}
  182. </el-button>
  183. <el-button @click="closePopover('copy', copyFormRef)"> 取消 </el-button>
  184. </el-form-item>
  185. </el-form>
  186. </div>
  187. </el-popover>
  188. <!-- 【转办】按钮 -->
  189. <el-popover
  190. :visible="popOverVisible.transfer"
  191. placement="top-start"
  192. :width="420"
  193. trigger="click"
  194. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.TRANSFER)"
  195. >
  196. <template #reference>
  197. <div @click="openPopover('transfer')" class="hover-bg-gray-100 rounded-xl p-6px">
  198. <Icon :size="14" icon="fa:share-square-o" />&nbsp;
  199. {{ getButtonDisplayName(OperationButtonType.TRANSFER) }}
  200. </div>
  201. </template>
  202. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  203. <el-form
  204. label-position="top"
  205. class="mb-auto"
  206. ref="transferFormRef"
  207. :model="transferForm"
  208. :rules="processInstance.name==='故障上报流程'?transferFormRuleType:transferFormRule"
  209. label-width="100px"
  210. >
  211. <!-- <el-form-item label="新审批人" prop="assigneeUserId">-->
  212. <el-form-item label="新审批人" prop="assigneeUserId" v-if="processInstance.name!='故障上报流程'">
  213. <el-select v-model="transferForm.assigneeUserId" filterable clearable style="width: 100%">
  214. <el-option
  215. v-for="item in userOptions"
  216. :key="item.id"
  217. :label="item.nickname"
  218. :value="item.id"
  219. />
  220. </el-select>
  221. </el-form-item>
  222. <el-form-item label="负责人" prop="assigneeUserId" v-if="processInstance.name==='故障上报流程'">
  223. <el-select v-model="transferForm.assigneeUserId" filterable clearable style="width: 100%">
  224. <el-option
  225. v-for="item in deptUsers"
  226. :key="item.id"
  227. :label="item.nickname"
  228. :value="item.id"
  229. />
  230. </el-select>
  231. </el-form-item>
  232. <el-form-item label="维修类型" v-if="processInstance.name==='故障上报流程'" prop="type">
  233. <el-select v-model="transferForm.type">
  234. <el-option
  235. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_TYPE)"
  236. :key="dict.label"
  237. :label="dict.label"
  238. :value="dict.value"
  239. />
  240. </el-select>
  241. </el-form-item>
  242. <el-form-item label="审批意见" prop="reason">
  243. <el-input
  244. v-model="transferForm.reason"
  245. clearable
  246. placeholder="请输入审批意见"
  247. type="textarea"
  248. :rows="3"
  249. />
  250. </el-form-item>
  251. <el-form-item>
  252. <el-button :disabled="formLoading" type="primary" @click="processInstance.name==='故障上报流程'?handleTransferType():handleTransfer()">
  253. {{ getButtonDisplayName(OperationButtonType.TRANSFER) }}
  254. </el-button>
  255. <el-button @click="closePopover('transfer', transferFormRef)"> 取消 </el-button>
  256. </el-form-item>
  257. </el-form>
  258. </div>
  259. </el-popover>
  260. <!-- 【委派】按钮 -->
  261. <el-popover
  262. :visible="popOverVisible.delegate"
  263. placement="top-start"
  264. :width="420"
  265. trigger="click"
  266. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.DELEGATE)"
  267. >
  268. <template #reference>
  269. <div @click="openPopover('delegate')" class="hover-bg-gray-100 rounded-xl p-6px">
  270. <Icon :size="14" icon="ep:position" />&nbsp;
  271. {{ getButtonDisplayName(OperationButtonType.DELEGATE) }}
  272. </div>
  273. </template>
  274. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  275. <el-form
  276. label-position="top"
  277. class="mb-auto"
  278. ref="delegateFormRef"
  279. :model="delegateForm"
  280. :rules="delegateFormRule"
  281. label-width="100px"
  282. >
  283. <el-form-item label="接收人" prop="delegateUserId">
  284. <el-select v-model="delegateForm.delegateUserId" clearable style="width: 100%">
  285. <el-option
  286. v-for="item in userOptions"
  287. :key="item.id"
  288. :label="item.nickname"
  289. :value="item.id"
  290. />
  291. </el-select>
  292. </el-form-item>
  293. <el-form-item label="审批意见" prop="reason">
  294. <el-input
  295. v-model="delegateForm.reason"
  296. clearable
  297. placeholder="请输入审批意见"
  298. type="textarea"
  299. :rows="3"
  300. />
  301. </el-form-item>
  302. <el-form-item>
  303. <el-button :disabled="formLoading" type="primary" @click="handleDelegate()">
  304. {{ getButtonDisplayName(OperationButtonType.DELEGATE) }}
  305. </el-button>
  306. <el-button @click="closePopover('delegate', delegateFormRef)"> 取消 </el-button>
  307. </el-form-item>
  308. </el-form>
  309. </div>
  310. </el-popover>
  311. <!-- 【加签】按钮 当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
  312. <el-popover
  313. :visible="popOverVisible.addSign"
  314. placement="top-start"
  315. :width="420"
  316. trigger="click"
  317. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.ADD_SIGN)"
  318. >
  319. <template #reference>
  320. <div @click="openPopover('addSign')" class="hover-bg-gray-100 rounded-xl p-6px">
  321. <Icon :size="14" icon="ep:plus" />&nbsp;
  322. {{ getButtonDisplayName(OperationButtonType.ADD_SIGN) }}
  323. </div>
  324. </template>
  325. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  326. <el-form
  327. label-position="top"
  328. class="mb-auto"
  329. ref="addSignFormRef"
  330. :model="addSignForm"
  331. :rules="addSignFormRule"
  332. label-width="100px"
  333. >
  334. <el-form-item label="加签处理人" prop="addSignUserIds">
  335. <el-select v-model="addSignForm.addSignUserIds" multiple clearable style="width: 100%">
  336. <el-option
  337. v-for="item in userOptions"
  338. :key="item.id"
  339. :label="item.nickname"
  340. :value="item.id"
  341. />
  342. </el-select>
  343. </el-form-item>
  344. <el-form-item label="审批意见" prop="reason">
  345. <el-input
  346. v-model="addSignForm.reason"
  347. clearable
  348. placeholder="请输入审批意见"
  349. type="textarea"
  350. :rows="3"
  351. />
  352. </el-form-item>
  353. <el-form-item>
  354. <el-button :disabled="formLoading" type="primary" @click="handlerAddSign('before')">
  355. 向前{{ getButtonDisplayName(OperationButtonType.ADD_SIGN) }}
  356. </el-button>
  357. <el-button :disabled="formLoading" type="primary" @click="handlerAddSign('after')">
  358. 向后{{ getButtonDisplayName(OperationButtonType.ADD_SIGN) }}
  359. </el-button>
  360. <el-button @click="closePopover('addSign', addSignFormRef)"> 取消 </el-button>
  361. </el-form-item>
  362. </el-form>
  363. </div>
  364. </el-popover>
  365. <!-- 【减签】按钮 -->
  366. <el-popover
  367. :visible="popOverVisible.deleteSign"
  368. placement="top-start"
  369. :width="420"
  370. trigger="click"
  371. v-if="runningTask?.children.length > 0"
  372. >
  373. <template #reference>
  374. <div @click="openPopover('deleteSign')" class="hover-bg-gray-100 rounded-xl p-6px">
  375. <Icon :size="14" icon="ep:semi-select" />&nbsp; 减签
  376. </div>
  377. </template>
  378. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  379. <el-form
  380. label-position="top"
  381. class="mb-auto"
  382. ref="deleteSignFormRef"
  383. :model="deleteSignForm"
  384. :rules="deleteSignFormRule"
  385. label-width="100px"
  386. >
  387. <el-form-item label="减签人员" prop="deleteSignTaskId">
  388. <el-select v-model="deleteSignForm.deleteSignTaskId" clearable style="width: 100%">
  389. <el-option
  390. v-for="item in runningTask.children"
  391. :key="item.id"
  392. :label="getDeleteSignUserLabel(item)"
  393. :value="item.id"
  394. />
  395. </el-select>
  396. </el-form-item>
  397. <el-form-item label="审批意见" prop="reason">
  398. <el-input
  399. v-model="deleteSignForm.reason"
  400. clearable
  401. placeholder="请输入审批意见"
  402. type="textarea"
  403. :rows="3"
  404. />
  405. </el-form-item>
  406. <el-form-item>
  407. <el-button :disabled="formLoading" type="primary" @click="handlerDeleteSign()">
  408. 减签
  409. </el-button>
  410. <el-button @click="closePopover('deleteSign', deleteSignFormRef)"> 取消 </el-button>
  411. </el-form-item>
  412. </el-form>
  413. </div>
  414. </el-popover>
  415. <!-- 【退回】按钮 -->
  416. <el-popover
  417. :visible="popOverVisible.return"
  418. placement="top-start"
  419. :width="420"
  420. trigger="click"
  421. v-if="runningTask && isHandleTaskStatus() && isShowButton(OperationButtonType.RETURN)"
  422. >
  423. <template #reference>
  424. <div @click="openPopover('return')" class="hover-bg-gray-100 rounded-xl p-6px">
  425. <Icon :size="14" icon="ep:back" />&nbsp;
  426. {{ getButtonDisplayName(OperationButtonType.RETURN) }}
  427. </div>
  428. </template>
  429. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  430. <el-form
  431. label-position="top"
  432. class="mb-auto"
  433. ref="returnFormRef"
  434. :model="returnForm"
  435. :rules="returnFormRule"
  436. label-width="100px"
  437. >
  438. <el-form-item label="退回节点" prop="targetTaskDefinitionKey">
  439. <el-select v-model="returnForm.targetTaskDefinitionKey" clearable style="width: 100%">
  440. <el-option
  441. v-for="item in returnList"
  442. :key="item.taskDefinitionKey"
  443. :label="item.name"
  444. :value="item.taskDefinitionKey"
  445. />
  446. </el-select>
  447. </el-form-item>
  448. <el-form-item label="退回理由" prop="returnReason">
  449. <el-input
  450. v-model="returnForm.returnReason"
  451. clearable
  452. placeholder="请输入退回理由"
  453. type="textarea"
  454. :rows="3"
  455. />
  456. </el-form-item>
  457. <el-form-item>
  458. <el-button :disabled="formLoading" type="primary" @click="handleReturn()">
  459. {{ getButtonDisplayName(OperationButtonType.RETURN) }}
  460. </el-button>
  461. <el-button @click="closePopover('return', returnFormRef)"> 取消 </el-button>
  462. </el-form-item>
  463. </el-form>
  464. </div>
  465. </el-popover>
  466. <!--【取消】按钮 这个对应发起人的取消, 只有发起人可以取消 -->
  467. <el-popover
  468. :visible="popOverVisible.cancel"
  469. placement="top-start"
  470. :width="420"
  471. trigger="click"
  472. v-if="
  473. userId === processInstance?.startUser?.id && !isEndProcessStatus(processInstance?.status)
  474. "
  475. >
  476. <template #reference>
  477. <div @click="openPopover('cancel')" class="hover-bg-gray-100 rounded-xl p-6px">
  478. <Icon :size="14" icon="fa:mail-reply" />&nbsp; 取消
  479. </div>
  480. </template>
  481. <div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
  482. <el-form
  483. label-position="top"
  484. class="mb-auto"
  485. ref="cancelFormRef"
  486. :model="cancelForm"
  487. :rules="cancelFormRule"
  488. label-width="100px"
  489. >
  490. <el-form-item label="取消理由" prop="cancelReason">
  491. <span class="text-#878c93 text-12px">&nbsp; 取消后,该审批流程将自动结束</span>
  492. <el-input
  493. v-model="cancelForm.cancelReason"
  494. clearable
  495. placeholder="请输入取消理由"
  496. type="textarea"
  497. :rows="3"
  498. />
  499. </el-form-item>
  500. <el-form-item>
  501. <el-button :disabled="formLoading" type="primary" @click="handleCancel()">
  502. 确认
  503. </el-button>
  504. <el-button @click="closePopover('cancel', cancelFormRef)"> 取消 </el-button>
  505. </el-form-item>
  506. </el-form>
  507. </div>
  508. </el-popover>
  509. <!-- 【再次提交】 按钮-->
  510. <div
  511. @click="handleReCreate()"
  512. class="hover-bg-gray-100 rounded-xl p-6px"
  513. v-if="
  514. userId === processInstance?.startUser?.id &&
  515. isEndProcessStatus(processInstance?.status) &&
  516. processDefinition?.formType === 10
  517. "
  518. >
  519. <Icon :size="14" icon="ep:refresh" />&nbsp; 再次提交
  520. </div>
  521. </div>
  522. <!-- 签名弹窗 -->
  523. <SignDialog ref="signRef" @success="handleSignFinish" />
  524. </template>
  525. <script lang="ts" setup>
  526. import { useUserStoreWithOut } from '@/store/modules/user'
  527. import { setConfAndFields2 } from '@/utils/formCreate'
  528. import * as TaskApi from '@/api/bpm/task'
  529. import * as ProcessInstanceApi from '@/api/bpm/processInstance'
  530. import * as UserApi from '@/api/system/user'
  531. import {
  532. NodeType,
  533. OPERATION_BUTTON_NAME,
  534. OperationButtonType,
  535. CandidateStrategy
  536. } from '@/components/SimpleProcessDesignerV2/src/consts'
  537. import { BpmModelFormType, BpmProcessInstanceStatus } from '@/utils/constants'
  538. import type { FormInstance, FormRules } from 'element-plus'
  539. import SignDialog from './SignDialog.vue'
  540. import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue'
  541. import { isEmpty } from '@/utils/is'
  542. import {DICT_TYPE, getBoolDictOptions, getStrDictOptions} from "@/utils/dict";
  543. import {IotMaintainApi} from "@/api/pms/maintain";
  544. import {IotFailureReportApi} from "@/api/pms/failure";
  545. defineOptions({ name: 'ProcessInstanceBtnContainer' })
  546. const router = useRouter() // 路由
  547. const message = useMessage() // 消息弹窗
  548. const userId = useUserStoreWithOut().getUser.id // 当前登录的编号
  549. const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  550. const props = defineProps<{
  551. processInstance: any // 流程实例信息
  552. processDefinition: any // 流程定义信息
  553. userOptions: UserApi.UserVO[]
  554. deptUsers: UserApi.UserVO[]
  555. normalForm: any // 流程表单 formCreate
  556. normalFormApi: any // 流程表单 formCreate Api
  557. writableFields: string[] // 流程表单可以编辑的字段
  558. }>()
  559. const formLoading = ref(false) // 表单加载中
  560. const popOverVisible = ref({
  561. approve: false,
  562. reject: false,
  563. transfer: false,
  564. delegate: false,
  565. addSign: false,
  566. return: false,
  567. copy: false,
  568. cancel: false,
  569. deleteSign: false
  570. }) // 气泡卡是否展示
  571. const returnList = ref([] as any) // 退回节点
  572. // ========== 审批信息 ==========
  573. const runningTask = ref<any>() // 运行中的任务
  574. const approveForm = ref<any>({}) // 审批通过时,额外的补充信息
  575. const approveFormFApi = ref<any>({}) // approveForms 的 fAPi
  576. const nodeTypeName = ref('审批') // 节点类型名称
  577. // 审批通过意见表单
  578. const reasonRequire = ref()
  579. const approveFormRef = ref<FormInstance>()
  580. const signRef = ref()
  581. const approveSignFormRef = ref()
  582. const nextAssigneesActivityNode = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 下一个审批节点信息
  583. const approveReasonForm = reactive({
  584. reason: '',
  585. signPicUrl: '',
  586. nextAssignees: {}
  587. })
  588. const approveReasonRule = computed(() => {
  589. return {
  590. reason: [
  591. { required: reasonRequire.value, message: nodeTypeName + '意见不能为空', trigger: 'blur' }
  592. ],
  593. signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }],
  594. nextAssignees: [{ required: true, message: '审批人不能为空', trigger: 'blur' }]
  595. }
  596. })
  597. // 拒绝表单
  598. const rejectFormRef = ref<FormInstance>()
  599. const rejectReasonForm = reactive({
  600. reason: ''
  601. })
  602. const rejectReasonRule = computed(() => {
  603. return {
  604. reason: [{ required: reasonRequire.value, message: '审批意见不能为空', trigger: 'blur' }]
  605. }
  606. })
  607. // 抄送表单
  608. const copyFormRef = ref<FormInstance>()
  609. const copyForm = reactive({
  610. copyUserIds: [],
  611. copyReason: ''
  612. })
  613. const copyFormRule = reactive<FormRules<typeof copyForm>>({
  614. copyUserIds: [{ required: true, message: '抄送人不能为空', trigger: 'change' }]
  615. })
  616. // 转办表单
  617. const transferFormRef = ref<FormInstance>()
  618. const transferForm = reactive({
  619. assigneeUserId: undefined,
  620. reason: '',
  621. type:''
  622. })
  623. const transferFormRule = reactive<FormRules<typeof transferForm>>({
  624. assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
  625. reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
  626. })
  627. const transferFormRuleType = reactive<FormRules<typeof transferForm>>({
  628. assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
  629. type: [{ required: true, message: '维修类型不能为空', trigger: 'change' }],
  630. reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
  631. })
  632. // 委派表单
  633. const delegateFormRef = ref<FormInstance>()
  634. const delegateForm = reactive({
  635. delegateUserId: undefined,
  636. reason: ''
  637. })
  638. const delegateFormRule = reactive<FormRules<typeof delegateForm>>({
  639. delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
  640. reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
  641. })
  642. // 加签表单
  643. const addSignFormRef = ref<FormInstance>()
  644. const addSignForm = reactive({
  645. addSignUserIds: undefined,
  646. reason: ''
  647. })
  648. const addSignFormRule = reactive<FormRules<typeof addSignForm>>({
  649. addSignUserIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
  650. reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
  651. })
  652. // 减签表单
  653. const deleteSignFormRef = ref<FormInstance>()
  654. const deleteSignForm = reactive({
  655. deleteSignTaskId: undefined,
  656. reason: ''
  657. })
  658. const deleteSignFormRule = reactive<FormRules<typeof deleteSignForm>>({
  659. deleteSignTaskId: [{ required: true, message: '减签人员不能为空', trigger: 'change' }],
  660. reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
  661. })
  662. // 退回表单
  663. const returnFormRef = ref<FormInstance>()
  664. const returnForm = reactive({
  665. targetTaskDefinitionKey: undefined,
  666. returnReason: ''
  667. })
  668. const returnFormRule = reactive<FormRules<typeof returnForm>>({
  669. targetTaskDefinitionKey: [{ required: true, message: '退回节点不能为空', trigger: 'change' }],
  670. returnReason: [{ required: true, message: '退回理由不能为空', trigger: 'blur' }]
  671. })
  672. // 取消表单
  673. const cancelFormRef = ref<FormInstance>()
  674. const cancelForm = reactive({
  675. cancelReason: ''
  676. })
  677. const cancelFormRule = reactive<FormRules<typeof cancelForm>>({
  678. cancelReason: [{ required: true, message: '取消理由不能为空', trigger: 'blur' }]
  679. })
  680. /** 监听 approveFormFApis,实现它对应的 form-create 初始化后,隐藏掉对应的表单提交按钮 */
  681. watch(
  682. () => approveFormFApi.value,
  683. (val) => {
  684. val?.btn?.show(false)
  685. val?.resetBtn?.show(false)
  686. },
  687. {
  688. deep: true
  689. }
  690. )
  691. /** 弹出气泡卡 */
  692. const openPopover = async (type: string) => {
  693. if (type === 'approve') {
  694. // 校验流程表单
  695. const valid = await validateNormalForm()
  696. if (!valid) {
  697. message.warning('表单校验不通过,请先完善表单!!')
  698. return
  699. }
  700. initNextAssigneesFormField()
  701. }
  702. if (type === 'return') {
  703. // 获取退回节点
  704. returnList.value = await TaskApi.getTaskListByReturn(runningTask.value.id)
  705. if (returnList.value.length === 0) {
  706. message.warning('当前没有可退回的节点')
  707. return
  708. }
  709. }
  710. Object.keys(popOverVisible.value).forEach((item) => {
  711. popOverVisible.value[item] = item === type
  712. })
  713. // await nextTick()
  714. // formRef.value.resetFields()
  715. }
  716. /** 关闭气泡卡 */
  717. const closePopover = (type: string, formRef: FormInstance | undefined) => {
  718. if (formRef) {
  719. formRef.resetFields()
  720. }
  721. popOverVisible.value[type] = false
  722. nextAssigneesActivityNode.value = []
  723. }
  724. /** 流程通过时,根据表单变量查询新的流程节点,判断下一个节点类型是否为自选审批人 */
  725. const initNextAssigneesFormField = async () => {
  726. // 获取修改的流程变量, 暂时只支持流程表单
  727. const variables = getUpdatedProcessInstanceVariables()
  728. const data = await ProcessInstanceApi.getNextApprovalNodes({
  729. processInstanceId: props.processInstance.id,
  730. taskId: runningTask.value.id,
  731. processVariablesStr: JSON.stringify(variables)
  732. })
  733. if (data && data.length > 0) {
  734. data.forEach((node: any) => {
  735. if (
  736. // 情况一:当前节点没有审批人,并且是发起人自选
  737. (isEmpty(node.tasks) &&
  738. isEmpty(node.candidateUsers) &&
  739. CandidateStrategy.START_USER_SELECT === node.candidateStrategy) ||
  740. // 情况二:当前节点是审批人自选
  741. CandidateStrategy.APPROVE_USER_SELECT === node.candidateStrategy
  742. ) {
  743. nextAssigneesActivityNode.value.push(node)
  744. }
  745. })
  746. }
  747. }
  748. /** 选择下一个节点的审批人 */
  749. const selectNextAssigneesConfirm = (id: string, userList: any[]) => {
  750. approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id)
  751. }
  752. /** 审批通过时,校验每个自选审批人的节点是否都已配置了审批人 */
  753. const validateNextAssignees = () => {
  754. if (Object.keys(nextAssigneesActivityNode.value).length === 0) {
  755. return true
  756. }
  757. // 如果需要自选审批人,则校验每个节点是否都已配置审批人
  758. for (const item of nextAssigneesActivityNode.value) {
  759. if (isEmpty(approveReasonForm.nextAssignees[item.id])) {
  760. message.warning('下一个节点的审批人不能为空!')
  761. return false
  762. }
  763. }
  764. return true
  765. }
  766. /** 处理审批通过和不通过的操作 */
  767. const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => {
  768. formLoading.value = true
  769. try {
  770. // 校验表单
  771. if (!formRef) return
  772. await formRef.validate()
  773. // 校验流程表单必填字段
  774. const valid = await validateNormalForm()
  775. if (!valid) {
  776. message.warning('表单校验不通过,请先完善表单!!')
  777. return
  778. }
  779. if (pass) {
  780. const nextAssigneesValid = validateNextAssignees()
  781. if (!nextAssigneesValid) return
  782. const variables = getUpdatedProcessInstanceVariables()
  783. // 审批通过数据
  784. const data = {
  785. id: runningTask.value.id,
  786. reason: approveReasonForm.reason,
  787. variables, // 审批通过, 把修改的字段值赋于流程实例变量
  788. nextAssignees: approveReasonForm.nextAssignees // 下个自选节点选择的审批人信息
  789. } as any
  790. // 签名
  791. if (runningTask.value.signEnable) {
  792. data.signPicUrl = approveReasonForm.signPicUrl
  793. }
  794. // 多表单处理,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
  795. // TODO 芋艿 任务有多表单这里要如何处理,会和可编辑的字段冲突
  796. const formCreateApi = approveFormFApi.value
  797. if (Object.keys(formCreateApi)?.length > 0) {
  798. await formCreateApi.validate()
  799. // @ts-ignore
  800. data.variables = approveForm.value.value
  801. }
  802. await TaskApi.approveTask(data)
  803. popOverVisible.value.approve = false
  804. nextAssigneesActivityNode.value = []
  805. message.success('审批通过成功')
  806. } else {
  807. // 审批不通过数据
  808. const data = {
  809. id: runningTask.value.id,
  810. reason: rejectReasonForm.reason
  811. }
  812. await TaskApi.rejectTask(data)
  813. popOverVisible.value.reject = false
  814. message.success('审批不通过成功')
  815. }
  816. // 重置表单
  817. formRef.resetFields()
  818. // 加载最新数据
  819. reload()
  820. } finally {
  821. formLoading.value = false
  822. }
  823. }
  824. /** 处理抄送 */
  825. const handleCopy = async () => {
  826. formLoading.value = true
  827. try {
  828. // 1. 校验表单
  829. if (!copyFormRef.value) return
  830. await copyFormRef.value.validate()
  831. // 2. 提交抄送
  832. const data = {
  833. id: runningTask.value.id,
  834. reason: copyForm.copyReason,
  835. copyUserIds: copyForm.copyUserIds
  836. }
  837. await TaskApi.copyTask(data)
  838. copyFormRef.value.resetFields()
  839. popOverVisible.value.copy = false
  840. message.success('操作成功')
  841. } finally {
  842. formLoading.value = false
  843. }
  844. }
  845. /** 处理转交 */
  846. const handleTransfer = async () => {
  847. debugger
  848. formLoading.value = true
  849. try {
  850. // 1.1 校验表单
  851. if (!transferFormRef.value) return
  852. await transferFormRef.value.validate()
  853. // 1.2 提交转交
  854. const data = {
  855. id: runningTask.value.id,
  856. reason: transferForm.reason,
  857. assigneeUserId: transferForm.assigneeUserId
  858. }
  859. await TaskApi.transferTask(data)
  860. transferFormRef.value.resetFields()
  861. popOverVisible.value.transfer = false
  862. message.success('操作成功')
  863. // 2. 加载最新数据
  864. reload()
  865. } finally {
  866. formLoading.value = false
  867. }
  868. }
  869. const handleTransferType = async () => {
  870. formLoading.value = true
  871. try {
  872. // 1.1 校验表单
  873. if (!transferFormRef.value) return
  874. await transferFormRef.value.validate()
  875. // 1.2 提交转交
  876. await IotFailureReportApi.updateIotMaintainProcess(props.processInstance.businessKey, transferForm.type, transferForm.assigneeUserId);
  877. const data = {
  878. id: runningTask.value.id,
  879. reason: transferForm.reason,
  880. type: transferForm.type,
  881. assigneeUserId: transferForm.assigneeUserId
  882. }
  883. await TaskApi.transferTask(data)
  884. transferFormRef.value.resetFields()
  885. popOverVisible.value.transfer = false
  886. message.success('操作成功')
  887. // 2. 加载最新数据
  888. reload()
  889. } finally {
  890. formLoading.value = false
  891. }
  892. }
  893. /** 处理委派 */
  894. const handleDelegate = async () => {
  895. formLoading.value = true
  896. try {
  897. // 1.1 校验表单
  898. if (!delegateFormRef.value) return
  899. await delegateFormRef.value.validate()
  900. // 1.2 处理委派
  901. const data = {
  902. id: runningTask.value.id,
  903. reason: delegateForm.reason,
  904. delegateUserId: delegateForm.delegateUserId
  905. }
  906. await TaskApi.delegateTask(data)
  907. popOverVisible.value.delegate = false
  908. delegateFormRef.value.resetFields()
  909. message.success('操作成功')
  910. // 2. 加载最新数据
  911. reload()
  912. } finally {
  913. formLoading.value = false
  914. }
  915. }
  916. /** 处理加签 */
  917. const handlerAddSign = async (type: string) => {
  918. formLoading.value = true
  919. try {
  920. // 1.1 校验表单
  921. if (!addSignFormRef.value) return
  922. await addSignFormRef.value.validate()
  923. // 1.2 提交加签
  924. const data = {
  925. id: runningTask.value.id,
  926. type,
  927. reason: addSignForm.reason,
  928. userIds: addSignForm.addSignUserIds
  929. }
  930. await TaskApi.signCreateTask(data)
  931. message.success('操作成功')
  932. addSignFormRef.value.resetFields()
  933. popOverVisible.value.addSign = false
  934. // 2 加载最新数据
  935. reload()
  936. } finally {
  937. formLoading.value = false
  938. }
  939. }
  940. /** 处理退回 */
  941. const handleReturn = async () => {
  942. formLoading.value = true
  943. try {
  944. // 1.1 校验表单
  945. if (!returnFormRef.value) return
  946. await returnFormRef.value.validate()
  947. // 1.2 提交退回
  948. const data = {
  949. id: runningTask.value.id,
  950. reason: returnForm.returnReason,
  951. targetTaskDefinitionKey: returnForm.targetTaskDefinitionKey
  952. }
  953. await TaskApi.returnTask(data)
  954. popOverVisible.value.return = false
  955. returnFormRef.value.resetFields()
  956. message.success('操作成功')
  957. // 2 重新加载数据
  958. reload()
  959. } finally {
  960. formLoading.value = false
  961. }
  962. }
  963. /** 处理取消 */
  964. const handleCancel = async () => {
  965. formLoading.value = true
  966. try {
  967. // 1.1 校验表单
  968. if (!cancelFormRef.value) return
  969. await cancelFormRef.value.validate()
  970. // 1.2 提交取消
  971. await ProcessInstanceApi.cancelProcessInstanceByStartUser(
  972. props.processInstance.id,
  973. cancelForm.cancelReason
  974. )
  975. popOverVisible.value.return = false
  976. message.success('操作成功')
  977. cancelFormRef.value.resetFields()
  978. // 2 重新加载数据
  979. reload()
  980. } finally {
  981. formLoading.value = false
  982. }
  983. }
  984. /** 处理再次提交 */
  985. const handleReCreate = async () => {
  986. // 跳转发起流程界面
  987. await router.push({
  988. name: 'BpmProcessInstanceCreate',
  989. query: { processInstanceId: props.processInstance?.id }
  990. })
  991. }
  992. /** 获取减签人员标签 */
  993. const getDeleteSignUserLabel = (task: any): string => {
  994. const deptName = task?.assigneeUser?.deptName || task?.ownerUser?.deptName
  995. const nickname = task?.assigneeUser?.nickname || task?.ownerUser?.nickname
  996. return `${nickname} ( 所属部门:${deptName} )`
  997. }
  998. /** 处理减签 */
  999. const handlerDeleteSign = async () => {
  1000. formLoading.value = true
  1001. try {
  1002. // 1.1 校验表单
  1003. if (!deleteSignFormRef.value) return
  1004. await deleteSignFormRef.value.validate()
  1005. // 1.2 提交减签
  1006. const data = {
  1007. id: deleteSignForm.deleteSignTaskId,
  1008. reason: deleteSignForm.reason
  1009. }
  1010. await TaskApi.signDeleteTask(data)
  1011. message.success('减签成功')
  1012. deleteSignFormRef.value.resetFields()
  1013. popOverVisible.value.deleteSign = false
  1014. // 2 加载最新数据
  1015. reload()
  1016. } finally {
  1017. formLoading.value = false
  1018. }
  1019. }
  1020. /** 重新加载数据 */
  1021. const reload = () => {
  1022. emit('success')
  1023. }
  1024. /** 任务是否为处理中状态 */
  1025. const isHandleTaskStatus = () => {
  1026. let canHandle = false
  1027. if (TaskApi.TaskStatusEnum.RUNNING === runningTask.value?.status) {
  1028. canHandle = true
  1029. }
  1030. return canHandle
  1031. }
  1032. /** 流程状态是否为结束状态 */
  1033. const isEndProcessStatus = (status: number) => {
  1034. let isEndStatus = false
  1035. if (
  1036. BpmProcessInstanceStatus.APPROVE === status ||
  1037. BpmProcessInstanceStatus.REJECT === status ||
  1038. BpmProcessInstanceStatus.CANCEL === status
  1039. ) {
  1040. isEndStatus = true
  1041. }
  1042. return isEndStatus
  1043. }
  1044. /** 是否显示按钮 */
  1045. const isShowButton = (btnType: OperationButtonType): boolean => {
  1046. let isShow = true
  1047. if (runningTask.value?.buttonsSetting && runningTask.value?.buttonsSetting[btnType]) {
  1048. isShow = runningTask.value.buttonsSetting[btnType].enable
  1049. }
  1050. return isShow
  1051. }
  1052. /** 获取按钮的显示名称 */
  1053. const getButtonDisplayName = (btnType: OperationButtonType) => {
  1054. let displayName = OPERATION_BUTTON_NAME.get(btnType)
  1055. if (runningTask.value?.buttonsSetting && runningTask.value?.buttonsSetting[btnType]) {
  1056. displayName = runningTask.value.buttonsSetting[btnType].displayName
  1057. }
  1058. return displayName
  1059. }
  1060. const loadTodoTask = (task: any) => {
  1061. approveForm.value = {}
  1062. runningTask.value = task
  1063. approveFormFApi.value = {}
  1064. reasonRequire.value = task?.reasonRequire ?? false
  1065. nodeTypeName.value = task?.nodeType === NodeType.TRANSACTOR_NODE ? '办理' : '审批'
  1066. // 处理 approve 表单.
  1067. if (task && task.formId && task.formConf) {
  1068. const tempApproveForm = {}
  1069. setConfAndFields2(tempApproveForm, task.formConf, task.formFields, task.formVariables)
  1070. approveForm.value = tempApproveForm
  1071. } else {
  1072. approveForm.value = {} // 占位,避免为空
  1073. }
  1074. }
  1075. /** 校验流程表单 */
  1076. const validateNormalForm = async () => {
  1077. if (props.processDefinition?.formType === BpmModelFormType.NORMAL) {
  1078. let valid = true
  1079. try {
  1080. await props.normalFormApi?.validate()
  1081. } catch {
  1082. valid = false
  1083. }
  1084. return valid
  1085. } else {
  1086. return true
  1087. }
  1088. }
  1089. /** 从可以编辑的流程表单字段,获取需要修改的流程实例的变量 */
  1090. const getUpdatedProcessInstanceVariables = () => {
  1091. const variables = {}
  1092. props.writableFields.forEach((field) => {
  1093. variables[field] = props.normalFormApi.getValue(field)
  1094. })
  1095. return variables
  1096. }
  1097. /** 处理签名完成 */
  1098. const handleSignFinish = (url: string) => {
  1099. approveReasonForm.signPicUrl = url
  1100. approveSignFormRef.value.validate('change')
  1101. }
  1102. defineExpose({ loadTodoTask })
  1103. </script>
  1104. <style lang="scss" scoped>
  1105. :deep(.el-affix--fixed) {
  1106. background-color: var(--el-bg-color);
  1107. }
  1108. .btn-container {
  1109. > div {
  1110. display: flex;
  1111. margin: 0 8px;
  1112. cursor: pointer;
  1113. align-items: center;
  1114. &:hover {
  1115. color: #6db5ff;
  1116. }
  1117. }
  1118. }
  1119. </style>