index.vue 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. <template>
  2. <ele-modal
  3. :before-close="handleClose"
  4. :visible.sync="dialogVisible"
  5. :close-on-click-modal="false"
  6. :append-to-body="true"
  7. width="80%"
  8. title="处理"
  9. :maxable="true"
  10. >
  11. <div class="app-container">
  12. <!-- 申请信息 :tasks="tasks" -->
  13. <el-card class="box-card" v-loading="processInstanceLoading">
  14. <div slot="header" class="clearfix">
  15. <span class="el-icon-document"
  16. >申请信息【{{ processInstance.name }}】 申请人:【{{
  17. processInstance.startUser?.nickname
  18. }}】</span
  19. >
  20. </div>
  21. <div v-if="processInstance.processDefinition">
  22. <async-biz-form-component
  23. :taskId="listData?.taskId"
  24. :businessId="listData?.businessId"
  25. :businessCode="listData?.businessCode"
  26. :id="listData?.id"
  27. :businessType="listData?.businessType"
  28. :processDefinitionId="processInstance.processDefinition.id"
  29. :taskDefinitionKey="listData?.taskDefinitionKey"
  30. :fromUser="processInstance.startUser?.nickname"
  31. :deptName="processInstance.startUser?.deptName"
  32. :deptId="processInstance.startUser?.deptId"
  33. :createTime="processInstance.createTime"
  34. @activeCompChange="activeCompChange"
  35. @handleClose="handleClose"
  36. ref="bziRef"
  37. ></async-biz-form-component>
  38. </div>
  39. </el-card>
  40. <div style="display: flex">
  41. <!-- 审批信息 -->
  42. <el-card
  43. style="min-width: 500px"
  44. class="box-card"
  45. v-loading="processInstanceLoading"
  46. v-for="(item, index) in runningTasks"
  47. :key="item.id || index"
  48. >
  49. <div slot="header" class="clearfix">
  50. <span class="el-icon-picture-outline">审批任务</span>
  51. </div>
  52. <div v-if="processInstance.processDefinition">
  53. <async-sub-form-component
  54. :taskId="item.id || listData?.taskId"
  55. :businessId="listData?.businessId"
  56. :businessCode="listData?.businessCode"
  57. :id="listData?.id"
  58. :taskDefinitionKey="listData?.taskDefinitionKey"
  59. :fromUser="processInstance.startUser?.nickname"
  60. :businessType="listData?.businessType"
  61. @handleAudit="handleAudit"
  62. @getTableValue="getTableValue"
  63. @handleUpdateAssignee="handleUpdateAssignee(item)"
  64. @handleBackList="handleBackList(item)"
  65. @submit="submit"
  66. @handleClose="handleClose"
  67. ref="subForm"
  68. ></async-sub-form-component>
  69. </div>
  70. <!-- <div> </div> -->
  71. <!-- <el-col :span="16" :offset="6">
  72. <el-form
  73. :ref="'form' + index"
  74. :model="auditForms[index]"
  75. :rules="auditRule"
  76. label-width="100px"
  77. >
  78. <el-form-item
  79. label="审批建议"
  80. prop="reason"
  81. style="margin-bottom: 20px"
  82. >
  83. <el-input
  84. type="textarea"
  85. v-model="auditForms[index].reason"
  86. placeholder="请输入审批建议"
  87. />
  88. </el-form-item>
  89. </el-form>
  90. <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
  91. <el-button
  92. icon="el-icon-edit-outline"
  93. type="success"
  94. size="mini"
  95. @click="handleAudit(item, true)"
  96. >通过
  97. </el-button>
  98. <el-button
  99. icon="el-icon-circle-close"
  100. type="danger"
  101. size="mini"
  102. @click="handleAudit(item, false)"
  103. >不通过
  104. </el-button>
  105. <el-button
  106. icon="el-icon-edit-outline"
  107. type="primary"
  108. size="mini"
  109. @click="handleUpdateAssignee(item)"
  110. >转办
  111. </el-button>
  112. <el-button
  113. icon="el-icon-edit-outline"
  114. type="primary"
  115. size="mini"
  116. @click="handleDelegate(item)"
  117. >委派
  118. </el-button>
  119. <el-button
  120. icon="el-icon-refresh-left"
  121. type="warning"
  122. size="mini"
  123. @click="handleBackList(item)"
  124. >退回
  125. </el-button>
  126. </div>
  127. </el-col> -->
  128. </el-card>
  129. <!-- 审批记录 -->
  130. <el-card
  131. class="box-card"
  132. style="flex: 1; min-width: 540px; margin-top: 0; margin-left: 10px"
  133. v-loading="processInstanceLoading"
  134. >
  135. <div slot="header" class="clearfix">
  136. <span class="el-icon-picture-outline">审批记录</span>
  137. </div>
  138. <div style="overflow-y: auto; height: 120px">
  139. <el-timeline>
  140. <el-timeline-item
  141. v-for="(item, index) in tasks"
  142. :key="index"
  143. :icon="getTimelineItemIcon(item)"
  144. :type="getTimelineItemType(item)"
  145. >
  146. <p style="font-weight: 500; margin-top: 5px"
  147. >任务:{{ item.name }}</p
  148. >
  149. <label
  150. v-if="item.assigneeUser"
  151. style="font-weight: normal; margin-right: 30px"
  152. >
  153. {{ index == 0 ? ' 审批人' : '发起人' }}:{{
  154. item.assigneeUser.nickname
  155. }}
  156. <el-tag type="info" size="mini">{{
  157. item.assigneeUser.deptName
  158. }}</el-tag>
  159. </label>
  160. <label
  161. v-if="item.endTime"
  162. style="margin-left: 10px; font-weight: normal"
  163. >审批时间:</label
  164. >
  165. <label
  166. v-if="item.endTime"
  167. style="color: #8a909c; font-weight: normal"
  168. >
  169. {{ item.endTime }}</label
  170. >
  171. <div>
  172. <label v-if="item.reason" style="font-weight: normal"
  173. >审批意见:</label
  174. >
  175. <label
  176. v-if="item.reason"
  177. style="color: #8a909c; font-weight: normal"
  178. >
  179. {{ item.reason }}</label
  180. >
  181. </div>
  182. </el-timeline-item>
  183. </el-timeline>
  184. </div>
  185. </el-card>
  186. </div>
  187. <!-- 对话框(转办审批人) -->
  188. <el-dialog
  189. title="转办审批人"
  190. :visible.sync="updateAssignee.open"
  191. width="500px"
  192. append-to-body
  193. >
  194. <el-form
  195. ref="updateAssigneeForm"
  196. :model="updateAssignee.form"
  197. :rules="updateAssignee.rules"
  198. label-width="110px"
  199. >
  200. <el-form-item label="新审批人" prop="assigneeUserId">
  201. <el-select
  202. v-model="updateAssignee.form.assigneeUserId"
  203. clearable
  204. style="width: 100%"
  205. :filterable="true"
  206. >
  207. <el-option
  208. v-for="item in userOptions"
  209. :key="item.id"
  210. :label="item.name"
  211. :value="item.id"
  212. />
  213. </el-select>
  214. </el-form-item>
  215. </el-form>
  216. <div slot="footer" class="dialog-footer">
  217. <el-button type="primary" @click="submitUpdateAssigneeForm"
  218. >确 定
  219. </el-button>
  220. <el-button @click="cancelUpdateAssigneeForm">取 消</el-button>
  221. </div>
  222. </el-dialog>
  223. <!-- 对话框(委派审批人) -->
  224. <el-dialog
  225. title="委派审批人"
  226. :visible.sync="updateDelegate.open"
  227. width="500px"
  228. append-to-body
  229. >
  230. <el-form
  231. ref="updateDelegateForm"
  232. :model="updateDelegate.form"
  233. :rules="updateDelegate.rules"
  234. label-width="110px"
  235. >
  236. <el-form-item label="新审批人" prop="assigneeUserId">
  237. <el-select
  238. v-model="updateDelegate.form.delegateUserId"
  239. clearable
  240. style="width: 100%"
  241. :filterable="true"
  242. >
  243. <el-option
  244. v-for="item in userOptions"
  245. :key="item.id"
  246. :label="item.name"
  247. :value="item.id"
  248. />
  249. </el-select>
  250. </el-form-item>
  251. <el-form-item label="委派理由" prop="reason">
  252. <el-input
  253. v-model="updateDelegate.form.reason"
  254. clearable
  255. placeholder="请输入委派理由"
  256. />
  257. </el-form-item>
  258. </el-form>
  259. <div slot="footer" class="dialog-footer">
  260. <el-button type="primary" @click="submitUpdateDelegateForm"
  261. >确 定
  262. </el-button>
  263. <el-button @click="cancelUpdateDelegateForm">取 消</el-button>
  264. </div>
  265. </el-dialog>
  266. <!--退回流程-->
  267. <el-dialog
  268. title="退回流程"
  269. :visible.sync="returnOpen"
  270. width="40%"
  271. append-to-body
  272. >
  273. <el-form
  274. ref="formRef"
  275. v-loading="formLoading"
  276. :model="formData"
  277. :rules="formRules"
  278. label-width="110px"
  279. >
  280. <el-form-item label="退回节点" prop="targetDefinitionKey">
  281. <el-select
  282. v-model="formData.targetDefinitionKey"
  283. clearable
  284. style="width: 100%"
  285. >
  286. <el-option
  287. v-for="item in returnList"
  288. :key="item.definitionKey"
  289. :label="item.name"
  290. :value="item.definitionKey"
  291. />
  292. </el-select>
  293. </el-form-item>
  294. <el-form-item label="回退理由" prop="reason">
  295. <el-input
  296. v-model="formData.reason"
  297. clearable
  298. placeholder="请输入回退理由"
  299. />
  300. </el-form-item>
  301. </el-form>
  302. <span slot="footer" class="dialog-footer">
  303. <el-button @click="returnOpen = false">取 消</el-button>
  304. <el-button
  305. :disabled="formLoading"
  306. type="primary"
  307. @click="submitReturn"
  308. >确 定</el-button
  309. >
  310. </span>
  311. </el-dialog>
  312. </div>
  313. </ele-modal>
  314. </template>
  315. <script>
  316. // import { getProcessDefinitionBpmnXML } from '@/api/bpm/definition';
  317. import store from '@/store';
  318. // import {decodeFields} from "@/utils/formGenerator";
  319. // import Parser from '@/components/parser/Parser'
  320. import { getProcessInstance } from '@/api/bpm/processInstance';
  321. import { listAllUserBind } from '@/api/system/organization';
  322. import Vue from 'vue';
  323. import {
  324. delegateTask,
  325. getReturnList,
  326. getTaskListByProcessInstanceId,
  327. returnTask,
  328. updateTaskAssignee
  329. } from '@/api/bpm/task';
  330. import { getProcessDefinitionBpmnXML } from '@/api/bpm/definition';
  331. import { getActivityList } from '@/api/bpm/activity';
  332. // import { getDate } from '@/utils/dateUtils';
  333. // 流程实例的详情页,可用于审批
  334. export default {
  335. name: 'ProcessInstanceDetail',
  336. components: {},
  337. data() {
  338. return {
  339. fullscreen: false,
  340. // 遮罩层
  341. processInstanceLoading: true,
  342. dialogVisible: false,
  343. // 流程实例
  344. listData: {
  345. id: undefined, // 流程实例的编号
  346. pcHandleRouter: '',
  347. pcViewRouter: '',
  348. businessId: '', // 业务id
  349. businessCode: '', //业务编码
  350. taskDefinitionKey: '',
  351. businessType: '',
  352. taskId: '' //任务编码
  353. },
  354. processInstance: {},
  355. formLoading: false,
  356. // 流程表单详情
  357. detailForm: {
  358. fields: []
  359. },
  360. //回退列表数据
  361. returnList: [],
  362. formData: {
  363. id: '',
  364. targetDefinitionKey: undefined,
  365. reason: ''
  366. },
  367. formRules: {
  368. targetDefinitionKey: [
  369. { required: true, message: '必须选择回退节点', trigger: 'change' }
  370. ],
  371. reason: [
  372. { required: true, message: '回退理由不能为空', trigger: 'blur' }
  373. ]
  374. },
  375. returnOpen: false,
  376. activityList: [],
  377. tasks: [],
  378. // 审批表单
  379. runningTasks: [],
  380. auditForms: [],
  381. auditRule: {
  382. reason: [
  383. { required: true, message: '审批建议不能为空', trigger: 'blur' }
  384. ]
  385. },
  386. // 转派审批人
  387. userOptions: [],
  388. updateAssignee: {
  389. open: false,
  390. form: {
  391. assigneeUserId: undefined
  392. },
  393. rules: {
  394. assigneeUserId: [
  395. { required: true, message: '新审批人不能为空', trigger: 'change' }
  396. ]
  397. }
  398. },
  399. updateDelegate: {
  400. open: false,
  401. form: {
  402. delegateUserId: undefined,
  403. reason: ''
  404. },
  405. rules: {
  406. delegateUserId: [
  407. { required: true, message: '新审批人不能为空', trigger: 'change' }
  408. ],
  409. reason: [
  410. { required: true, message: '委派理由不能为空', trigger: 'blur' }
  411. ]
  412. }
  413. }
  414. };
  415. },
  416. created() {
  417. // this.id = this.$route.query.id;
  418. // if (!this.id) {
  419. // this.$message.error('未传递 id 参数,无法查看流程信息');
  420. // return;
  421. // }
  422. // this.getDetail();
  423. // 获得用户列表
  424. this.userOptions = [];
  425. listAllUserBind().then((data) => {
  426. this.userOptions.push(...data);
  427. });
  428. },
  429. methods: {
  430. isStockBatchChange() {
  431. return (
  432. String(this.listData.businessCode || '').startsWith('PCBG') ||
  433. String(this.listData.pcViewRouter || '').includes('stockBatchChange') ||
  434. String(this.listData.pcViewRouter || '').includes(
  435. 'warehouseManagement/stockLedger'
  436. )
  437. );
  438. },
  439. /** 解析流程模型里配置的表单路由 JSON:{ pcView, pcHandle, ... } */
  440. parseFormRouteConfig(raw) {
  441. if (!raw) return null;
  442. if (typeof raw === 'object') return raw;
  443. const text = String(raw).trim();
  444. if (!text || text[0] !== '{') return null;
  445. try {
  446. return JSON.parse(text);
  447. } catch (e) {
  448. return null;
  449. }
  450. },
  451. /** 规范化为 /bpm/.../*.vue,便于 require(`@/views${path}`) */
  452. normalizeViewPath(path) {
  453. let text = String(path || '').trim();
  454. if (!text) return '';
  455. text = text.replace(/\\/g, '/');
  456. text = text.replace(/^@\/views/, '');
  457. text = text.replace(/^src\/views/, '');
  458. if (!text.startsWith('/')) text = `/${text}`;
  459. if (text === '/') return '';
  460. return text;
  461. },
  462. /**
  463. * 解析 PC 详情/处理组件路径
  464. * 优先待办带回的 pcViewRouter/pcHandleRouter,
  465. * 其次流程定义 formCustomCreatePath / formKey(pcView、pcHandle)
  466. */
  467. resolveFormRouters() {
  468. const fromTodo = {
  469. view: this.normalizeViewPath(this.listData.pcViewRouter),
  470. handle: this.normalizeViewPath(this.listData.pcHandleRouter)
  471. };
  472. const def = this.processInstance?.processDefinition || {};
  473. const fromDef =
  474. this.parseFormRouteConfig(def.formCustomCreatePath) ||
  475. this.parseFormRouteConfig(def.formKey) ||
  476. this.parseFormRouteConfig(this.listData.formKey) ||
  477. this.parseFormRouteConfig(this.listData.formCustomCreatePath) ||
  478. {};
  479. // 已有审批组件的流程未配路由时使用本项目约定路径
  480. const businessKey = String(
  481. def.key ||
  482. this.listData.businessKey ||
  483. this.listData.processDefinitionKey ||
  484. ''
  485. );
  486. const formFallback =
  487. businessKey === 'contact_approve'
  488. ? {
  489. pcView:
  490. '/bpm/handleTask/components/contact/contactDetailDialog.vue',
  491. pcHandle: '/bpm/handleTask/components/contact/submit.vue'
  492. }
  493. : businessKey === 'hr_recruit_hiring_approval'
  494. ? {
  495. pcView:
  496. '/bpm/handleTask/components/hiringApproval/hiringApprovalDetail.vue',
  497. pcHandle:
  498. '/bpm/handleTask/components/hiringApproval/submit.vue'
  499. }
  500. : businessKey === 'hr_contract_approval'
  501. ? {
  502. pcView:
  503. '/bpm/handleTask/components/hrContractManagement/hrContractDetail.vue',
  504. pcHandle:
  505. '/bpm/handleTask/components/hrContractManagement/submit.vue'
  506. }
  507. : {};
  508. return {
  509. view:
  510. fromTodo.view ||
  511. this.normalizeViewPath(fromDef.pcView || fromDef.pcViewRouter) ||
  512. this.normalizeViewPath(formFallback.pcView),
  513. handle:
  514. fromTodo.handle ||
  515. this.normalizeViewPath(
  516. fromDef.pcHandle || fromDef.pcHandleRouter
  517. ) ||
  518. this.normalizeViewPath(formFallback.pcHandle)
  519. };
  520. },
  521. getBusinessComponentRouter(type) {
  522. const routers = this.resolveFormRouters();
  523. const router = type === 'view' ? routers.view : routers.handle;
  524. if (this.isStockBatchChange()) {
  525. const invalidViewRouter =
  526. type === 'view' &&
  527. String(router || '').includes('warehouseManagement/stockLedger');
  528. if (!router || invalidViewRouter) {
  529. return `/bpm/handleTask/components/stockBatchChange/${
  530. type === 'view' ? 'detailDialog' : 'submit'
  531. }.vue`;
  532. }
  533. }
  534. return router || '';
  535. },
  536. registerAsyncBizComponents() {
  537. const viewRouter = this.getBusinessComponentRouter('view');
  538. const handleRouter = this.getBusinessComponentRouter('handle');
  539. if (!viewRouter || !handleRouter) {
  540. console.error('流程表单路由未配置', {
  541. pcViewRouter: this.listData.pcViewRouter,
  542. pcHandleRouter: this.listData.pcHandleRouter,
  543. formCustomCreatePath:
  544. this.processInstance?.processDefinition?.formCustomCreatePath,
  545. formKey: this.processInstance?.processDefinition?.formKey,
  546. resolved: { viewRouter, handleRouter },
  547. businessCode: this.listData.businessCode,
  548. businessType: this.listData.businessType
  549. });
  550. this.$message.error(
  551. '流程未配置 PC 表单路由。请在流程模型「业务表单」或节点 formKey 中配置:{"pcView":"/bpm/handleTask/components/.../detail.vue","pcHandle":"/bpm/handleTask/components/.../submit.vue"}'
  552. );
  553. return false;
  554. }
  555. // 回填到 listData,便于后续使用
  556. this.listData.pcViewRouter = viewRouter;
  557. this.listData.pcHandleRouter = handleRouter;
  558. console.log('加载业务表单组件', { viewRouter, handleRouter });
  559. Vue.component('async-biz-form-component', (resolve) => {
  560. require([`@/views${viewRouter}`], resolve);
  561. });
  562. Vue.component('async-sub-form-component', (resolve) => {
  563. require([`@/views${handleRouter}`], resolve);
  564. });
  565. return true;
  566. },
  567. submit(data) {
  568. this.$refs.bziRef.save(data);
  569. },
  570. open(data) {
  571. // 先清空上一条流程,让业务表单组件在新数据渲染前卸载,
  572. // 避免旧流程的组件带着新流程的 businessId 重新取数(串 id 请求)
  573. this.processInstance = {};
  574. this.listData = data;
  575. console.log('实例流程~~~~~~', data);
  576. this.dialogVisible = true;
  577. this.getDetail();
  578. // this.getTasksRecordDetail();
  579. },
  580. activeCompChange(activeComp) {
  581. this.$refs.subForm[0].activeCompChange(activeComp);
  582. },
  583. /** 获得流程实例 */
  584. getDetail() {
  585. // 获得流程实例相关
  586. this.processInstanceLoading = true;
  587. getProcessInstance(this.listData.id).then((response) => {
  588. if (!response) {
  589. this.$message.error('查询不到流程信息!');
  590. return;
  591. }
  592. // 设置流程信息
  593. this.processInstance = response;
  594. // //将业务表单,注册为动态组件
  595. // let formCustomCreatePath = JSON.parse(
  596. // this.processInstance.processDefinition.formCustomCreatePath
  597. // );
  598. // const bizpath = formCustomCreatePath.pcView;
  599. // const subpath = formCustomCreatePath.pcHandle;
  600. this.registerAsyncBizComponents();
  601. // // 设置表单信息
  602. // if (this.processInstance.processDefinition.formType === 10) {
  603. // this.detailForm = {
  604. // ...JSON.parse(this.processInstance.processDefinition.formConf),
  605. // disabled: true, // 表单禁用
  606. // formBtns: false, // 按钮隐藏
  607. // fields: decodeFields(this.processInstance.processDefinition.formFields)
  608. // }
  609. // // 设置表单的值
  610. // this.detailForm.fields.forEach(item => {
  611. // const val = this.processInstance.formVariables[item.__vModel__]
  612. // if (val) {
  613. // item.__config__.defaultValue = val
  614. // }
  615. // });
  616. // }
  617. // 取消加载中
  618. this.processInstanceLoading = false;
  619. });
  620. this.runningTasks = [];
  621. this.auditForms = [];
  622. getTaskListByProcessInstanceId(this.listData.id).then((response) => {
  623. console.log(response, 'response');
  624. // 审批记录
  625. this.tasks = [];
  626. // 移除已取消的审批
  627. response.forEach((task) => {
  628. if (task.result !== 4) {
  629. this.tasks.push(task);
  630. }
  631. });
  632. // 排序,将未完成的排在前面,已完成的排在后面;
  633. this.tasks.sort((a, b) => {
  634. // 有已完成的情况,按照完成时间倒序
  635. if (a.endTime && b.endTime) {
  636. return b.endTime - a.endTime;
  637. } else if (a.endTime) {
  638. return 1;
  639. } else if (b.endTime) {
  640. return -1;
  641. // 都是未完成,按照创建时间倒序
  642. } else {
  643. return b.createTime - a.createTime;
  644. }
  645. });
  646. // 需要审核的记录
  647. const userId = store.getters.user.info.userId;
  648. if (this.tasks.some((item) => item.result == 7)) {
  649. let taskItem = this.tasks.find((item) => item.result == 7);
  650. const stack = [];
  651. let task = [];
  652. stack.push(taskItem.children);
  653. while (stack.length > 0) {
  654. const node = stack.pop();
  655. console.log(node, '-----');
  656. node.forEach((n) => {
  657. if (n.children?.length === 0) {
  658. task.push(n);
  659. } else {
  660. task = [];
  661. stack.push(n.children);
  662. }
  663. });
  664. }
  665. task.forEach((task) => {
  666. if (task.result !== 1 && task.result !== 6) {
  667. // 只有待处理才需要
  668. return;
  669. }
  670. if (!task.assigneeUser || task.assigneeUser.id !== userId) {
  671. // 自己不是处理人
  672. return;
  673. }
  674. if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
  675. // 不是当前流程的
  676. return;
  677. }
  678. this.runningTasks.push({ ...task });
  679. this.auditForms.push({
  680. reason: ''
  681. });
  682. });
  683. } else {
  684. this.tasks.forEach((task) => {
  685. if (task.result !== 1 && task.result !== 6) {
  686. // 只有待处理才需要
  687. return;
  688. }
  689. if (!task.assigneeUser || task.assigneeUser.id !== userId) {
  690. // 自己不是处理人
  691. return;
  692. }
  693. if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
  694. // 不是当前流程的
  695. return;
  696. }
  697. this.runningTasks.push({ ...task });
  698. this.auditForms.push({
  699. reason: ''
  700. });
  701. });
  702. }
  703. this.normalizeRunningTasks();
  704. });
  705. },
  706. normalizeRunningTasks() {
  707. const currentTaskIndex = this.runningTasks.findIndex(
  708. (task) => `${task.id}` === `${this.listData.taskId}`
  709. );
  710. const task =
  711. this.runningTasks[currentTaskIndex > -1 ? currentTaskIndex : 0];
  712. this.runningTasks = task ? [task] : [];
  713. this.auditForms = this.runningTasks.map(() => ({
  714. reason: ''
  715. }));
  716. },
  717. getTimelineItemIcon(item) {
  718. if (item.result === 1) {
  719. return 'el-icon-time';
  720. }
  721. if (item.result === 2) {
  722. return 'el-icon-check';
  723. }
  724. if (item.result === 3) {
  725. return 'el-icon-close';
  726. }
  727. if (item.result === 4) {
  728. return 'el-icon-remove-outline';
  729. }
  730. if (item.result === 5) {
  731. return 'el-icon-back';
  732. }
  733. return '';
  734. },
  735. getTimelineItemType(item) {
  736. if (item.result === 1) {
  737. return 'primary';
  738. }
  739. if (item.result === 2) {
  740. return 'success';
  741. }
  742. if (item.result === 3) {
  743. return 'danger';
  744. }
  745. if (item.result === 4) {
  746. return 'info';
  747. }
  748. if (item.result === 5) {
  749. return 'warning';
  750. }
  751. if (item.result === 6) {
  752. return 'default';
  753. }
  754. return '';
  755. },
  756. /** 处理审批通过和不通过的操作 */
  757. handleAudit(data) {
  758. let text = data.status === 1 ? '通过' : '不通过';
  759. this.$message.success(`审批${data.title || text}成功!`);
  760. this.handleClose(); // 获得最新详情
  761. // const index = this.runningTasks.indexOf(task);
  762. // this.$refs['form' + index][0].validate((valid) => {
  763. // if (!valid) {
  764. // return;
  765. // }
  766. // const data = {
  767. // id: task.id,
  768. // reason: this.auditForms[index].reason
  769. // };
  770. // if (pass) {
  771. // approveTask(data).then((response) => {
  772. // this.$message.success('审批通过成功!');
  773. // this.handleClose(); // 获得最新详情
  774. // });
  775. // } else {
  776. // rejectTask(data).then((response) => {
  777. // this.$message.success('审批不通过成功!');
  778. // this.handleClose(); // 获得最新详情
  779. // });
  780. // }
  781. // });
  782. },
  783. /** 处理转办审批人 */
  784. handleUpdateAssignee(task) {
  785. // 设置表单
  786. this.resetUpdateAssigneeForm();
  787. this.updateAssignee.form.id = task.id;
  788. // 设置为打开
  789. this.updateAssignee.open = true;
  790. },
  791. /** 提交转办审批人 */
  792. submitUpdateAssigneeForm() {
  793. this.$refs['updateAssigneeForm'].validate((valid) => {
  794. if (!valid) {
  795. return;
  796. }
  797. if (this.$refs.subForm[0].formUpdate) {
  798. this.$refs.subForm[0].formUpdate();
  799. }
  800. updateTaskAssignee(this.updateAssignee.form).then((response) => {
  801. this.$message.success('转办任务成功!');
  802. this.updateAssignee.open = false;
  803. this.handleClose(); // 获得最新详情
  804. });
  805. });
  806. },
  807. /** 取消转办审批人 */
  808. cancelUpdateAssigneeForm() {
  809. this.updateAssignee.open = false;
  810. this.resetUpdateAssigneeForm();
  811. },
  812. /** 重置转办审批人 */
  813. resetUpdateAssigneeForm() {
  814. this.updateAssignee.form = {
  815. id: undefined,
  816. assigneeUserId: undefined
  817. };
  818. this.resetForm('updateAssigneeForm');
  819. },
  820. /** 处理审批委派的操作 */
  821. handleDelegate(task) {
  822. //this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!");
  823. this.resetUpdateDelegateForm();
  824. this.updateDelegate.form.id = task.id;
  825. // 设置为打开
  826. this.updateDelegate.open = true;
  827. },
  828. /** 提交委派审批人 */
  829. submitUpdateDelegateForm() {
  830. this.$refs['updateDelegateForm'].validate((valid) => {
  831. if (!valid) {
  832. return;
  833. }
  834. delegateTask(this.updateDelegate.form).then((response) => {
  835. this.$message.success('委派任务成功!');
  836. this.updateDelegate.open = false;
  837. this.handleClose(); // 获得最新详情
  838. });
  839. });
  840. },
  841. /** 取消委派审批人 */
  842. cancelUpdateDelegateForm() {
  843. this.updateDelegate.open = false;
  844. this.resetUpdateDelegateForm();
  845. },
  846. /** 重置委派审批人 */
  847. resetUpdateDelegateForm() {
  848. this.updateDelegate.form = {
  849. id: undefined,
  850. delegateUserId: undefined
  851. };
  852. this.resetForm('updateDelegateForm');
  853. },
  854. /** 处理审批退回的操作 */
  855. /** 返回退回节点列表 */
  856. handleBackList(task) {
  857. // 可参考 http://blog.wya1.com/article/636697030/details/7296
  858. getReturnList(task.id).then((response) => {
  859. this.returnList = response.data.data;
  860. if (this.returnList.length == 0) {
  861. this.$message.error('当前没有可回退的节点!');
  862. return;
  863. }
  864. this.formData.id = task.id;
  865. this.returnOpen = true;
  866. });
  867. },
  868. /** 提交退回任务 */
  869. submitReturn() {
  870. if (!this.formData.targetDefinitionKey) {
  871. this.$message.error('请选择退回节点!');
  872. }
  873. this.$refs['formRef'].validate((valid) => {
  874. if (!valid) {
  875. return;
  876. }
  877. returnTask(this.formData).then((res) => {
  878. if (res.data) {
  879. this.$message.success('回退成功!');
  880. this.returnOpen = false;
  881. this.handleClose(); // 获得最新详情
  882. }
  883. });
  884. });
  885. },
  886. handleClose() {
  887. this.$emit('reload');
  888. this.dialogVisible = false;
  889. },
  890. getTableValue(fn) {
  891. // 第二个参数告诉审批组件:用户在表格里是否手动改过单价(用于决定是否需要落库)
  892. fn(
  893. this.$refs.bziRef.getTableValue(),
  894. this.$refs.bziRef.isSinglePriceChanged?.()
  895. );
  896. }
  897. }
  898. };
  899. </script>
  900. <style lang="scss">
  901. .my-process-designer {
  902. height: calc(100vh - 200px);
  903. }
  904. .box-card {
  905. width: 100%;
  906. margin-bottom: 20px;
  907. }
  908. </style>