index.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  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 purchaseReturnDetail from '@/views/bpm/handleTask/components/purchaseOrder/returnGoods/detailDialog.vue';
  324. import purchaseReturnSubmit from '@/views/bpm/handleTask/components/purchaseOrder/returnGoods/submit.vue';
  325. import {
  326. delegateTask,
  327. getReturnList,
  328. getTaskListByProcessInstanceId,
  329. returnTask,
  330. updateTaskAssignee
  331. } from '@/api/bpm/task';
  332. import { getProcessDefinitionBpmnXML } from '@/api/bpm/definition';
  333. import { getActivityList } from '@/api/bpm/activity';
  334. // import { getDate } from '@/utils/dateUtils';
  335. // 已有审批组件的流程未配路由时,使用的本项目约定路径(按流程标识匹配)
  336. const FORM_FALLBACK_MAP = {
  337. contact_approve: {
  338. pcView: '/bpm/handleTask/components/contact/contactDetailDialog.vue',
  339. pcHandle: '/bpm/handleTask/components/contact/submit.vue'
  340. },
  341. hr_recruit_hiring_approval: {
  342. pcView:
  343. '/bpm/handleTask/components/hiringApproval/hiringApprovalDetail.vue',
  344. pcHandle: '/bpm/handleTask/components/hiringApproval/submit.vue'
  345. },
  346. hr_contract_approval: {
  347. pcView:
  348. '/bpm/handleTask/components/hrContractManagement/hrContractDetail.vue',
  349. pcHandle: '/bpm/handleTask/components/hrContractManagement/submit.vue'
  350. },
  351. hr_contract_termination_approval: {
  352. pcView:
  353. '/bpm/handleTask/components/hrRemoveContractManagement/hrRemoveContractDetail.vue',
  354. pcHandle:
  355. '/bpm/handleTask/components/hrRemoveContractManagement/submit.vue'
  356. },
  357. hr_contract_change_approval: {
  358. pcView:
  359. '/bpm/handleTask/components/hrChangeContractManagement/hrChangeContractDetail.vue',
  360. pcHandle:
  361. '/bpm/handleTask/components/hrChangeContractManagement/submit.vue'
  362. },
  363. // 考勤申请:补卡 / 请假 / 外出 / 加班 / 出差
  364. hr_attendance_card_repair_apply: {
  365. pcView:
  366. '/bpm/handleTask/components/hrCardRepairManagement/hrCardRepairDetail.vue',
  367. pcHandle: '/bpm/handleTask/components/hrCardRepairManagement/submit.vue'
  368. },
  369. hr_attendance_leave_apply: {
  370. pcView: '/bpm/handleTask/components/hrLeaveManagement/hrLeaveDetail.vue',
  371. pcHandle: '/bpm/handleTask/components/hrLeaveManagement/submit.vue'
  372. },
  373. hr_attendance_outing_apply: {
  374. pcView:
  375. '/bpm/handleTask/components/hrOutingManagement/hrOutingDetail.vue',
  376. pcHandle: '/bpm/handleTask/components/hrOutingManagement/submit.vue'
  377. },
  378. hr_attendance_overtime_apply: {
  379. pcView:
  380. '/bpm/handleTask/components/hrOvertimeManagement/hrOvertimeDetail.vue',
  381. pcHandle: '/bpm/handleTask/components/hrOvertimeManagement/submit.vue'
  382. },
  383. hr_attendance_trip_apply: {
  384. pcView: '/bpm/handleTask/components/hrTripManagement/hrTripDetail.vue',
  385. pcHandle: '/bpm/handleTask/components/hrTripManagement/submit.vue'
  386. }
  387. };
  388. // 流程实例的详情页,可用于审批
  389. export default {
  390. name: 'ProcessInstanceDetail',
  391. components: {},
  392. data() {
  393. return {
  394. fullscreen: false,
  395. // 遮罩层
  396. processInstanceLoading: true,
  397. dialogVisible: false,
  398. // 流程实例
  399. listData: {
  400. id: undefined, // 流程实例的编号
  401. pcHandleRouter: '',
  402. pcViewRouter: '',
  403. businessId: '', // 业务id
  404. businessCode: '', //业务编码
  405. taskDefinitionKey: '',
  406. businessType: '',
  407. taskId: '' //任务编码
  408. },
  409. processInstance: {},
  410. formLoading: false,
  411. // 流程表单详情
  412. detailForm: {
  413. fields: []
  414. },
  415. //回退列表数据
  416. returnList: [],
  417. formData: {
  418. id: '',
  419. targetDefinitionKey: undefined,
  420. reason: ''
  421. },
  422. formRules: {
  423. targetDefinitionKey: [
  424. { required: true, message: '必须选择回退节点', trigger: 'change' }
  425. ],
  426. reason: [
  427. { required: true, message: '回退理由不能为空', trigger: 'blur' }
  428. ]
  429. },
  430. returnOpen: false,
  431. activityList: [],
  432. tasks: [],
  433. // 审批表单
  434. runningTasks: [],
  435. auditForms: [],
  436. auditRule: {
  437. reason: [
  438. { required: true, message: '审批建议不能为空', trigger: 'blur' }
  439. ]
  440. },
  441. // 转派审批人
  442. userOptions: [],
  443. updateAssignee: {
  444. open: false,
  445. form: {
  446. assigneeUserId: undefined
  447. },
  448. rules: {
  449. assigneeUserId: [
  450. { required: true, message: '新审批人不能为空', trigger: 'change' }
  451. ]
  452. }
  453. },
  454. updateDelegate: {
  455. open: false,
  456. form: {
  457. delegateUserId: undefined,
  458. reason: ''
  459. },
  460. rules: {
  461. delegateUserId: [
  462. { required: true, message: '新审批人不能为空', trigger: 'change' }
  463. ],
  464. reason: [
  465. { required: true, message: '委派理由不能为空', trigger: 'blur' }
  466. ]
  467. }
  468. }
  469. };
  470. },
  471. created() {
  472. // this.id = this.$route.query.id;
  473. // if (!this.id) {
  474. // this.$message.error('未传递 id 参数,无法查看流程信息');
  475. // return;
  476. // }
  477. // this.getDetail();
  478. // 获得用户列表
  479. this.userOptions = [];
  480. listAllUserBind().then((data) => {
  481. this.userOptions.push(...data);
  482. });
  483. },
  484. methods: {
  485. isStockBatchChange() {
  486. return (
  487. String(this.listData.businessCode || '').startsWith('PCBG') ||
  488. String(this.listData.pcViewRouter || '').includes('stockBatchChange') ||
  489. String(this.listData.pcViewRouter || '').includes(
  490. 'warehouseManagement/stockLedger'
  491. )
  492. );
  493. },
  494. /** 解析流程模型里配置的表单路由 JSON:{ pcView, pcHandle, ... } */
  495. parseFormRouteConfig(raw) {
  496. if (!raw) return null;
  497. if (typeof raw === 'object') return raw;
  498. const text = String(raw).trim();
  499. if (!text || text[0] !== '{') return null;
  500. try {
  501. return JSON.parse(text);
  502. } catch (e) {
  503. return null;
  504. }
  505. },
  506. /** 规范化为 /bpm/.../*.vue,便于 require(`@/views${path}`) */
  507. normalizeViewPath(path) {
  508. let text = String(path || '').trim();
  509. if (!text) return '';
  510. text = text.replace(/\\/g, '/');
  511. text = text.replace(/^@\/views/, '');
  512. text = text.replace(/^src\/views/, '');
  513. if (!text.startsWith('/')) text = `/${text}`;
  514. if (text === '/') return '';
  515. return text;
  516. },
  517. /**
  518. * 解析 PC 详情/处理组件路径
  519. * 优先待办带回的 pcViewRouter/pcHandleRouter,
  520. * 其次流程定义 formCustomCreatePath / formKey(pcView、pcHandle)
  521. */
  522. resolveFormRouters() {
  523. const fromTodo = {
  524. view: this.normalizeViewPath(this.listData.pcViewRouter),
  525. handle: this.normalizeViewPath(this.listData.pcHandleRouter)
  526. };
  527. const def = this.processInstance?.processDefinition || {};
  528. const fromDef =
  529. this.parseFormRouteConfig(def.formCustomCreatePath) ||
  530. this.parseFormRouteConfig(def.formKey) ||
  531. this.parseFormRouteConfig(this.listData.formKey) ||
  532. this.parseFormRouteConfig(this.listData.formCustomCreatePath) ||
  533. {};
  534. // 已有审批组件的流程未配路由时使用本项目约定路径
  535. const businessKey = String(
  536. def.key ||
  537. this.listData.businessKey ||
  538. this.listData.processDefinitionKey ||
  539. ''
  540. );
  541. const formFallback = FORM_FALLBACK_MAP[businessKey] || {};
  542. const resolved = {
  543. view:
  544. fromTodo.view ||
  545. this.normalizeViewPath(fromDef.pcView || fromDef.pcViewRouter) ||
  546. this.normalizeViewPath(formFallback.pcView),
  547. handle:
  548. fromTodo.handle ||
  549. this.normalizeViewPath(
  550. fromDef.pcHandle || fromDef.pcHandleRouter
  551. ) ||
  552. this.normalizeViewPath(formFallback.pcHandle)
  553. };
  554. // 排查路由串台:打印三级来源,确认最终组件路径是谁给的
  555. if (process.env.NODE_ENV !== 'production') {
  556. // eslint-disable-next-line no-console
  557. console.log('[流程表单路由解析]', {
  558. businessKey,
  559. '①待办数据': fromTodo,
  560. '②流程定义原始': {
  561. key: def.key,
  562. formCustomCreatePath: def.formCustomCreatePath,
  563. formKey: def.formKey
  564. },
  565. '②流程定义解析': fromDef,
  566. '③前端兜底': formFallback,
  567. 最终: resolved
  568. });
  569. }
  570. return resolved;
  571. },
  572. getBusinessComponentRouter(type) {
  573. const routers = this.resolveFormRouters();
  574. const router = type === 'view' ? routers.view : routers.handle;
  575. if (this.isStockBatchChange()) {
  576. const invalidViewRouter =
  577. type === 'view' &&
  578. String(router || '').includes('warehouseManagement/stockLedger');
  579. if (!router || invalidViewRouter) {
  580. return `/bpm/handleTask/components/stockBatchChange/${
  581. type === 'view' ? 'detailDialog' : 'submit'
  582. }.vue`;
  583. }
  584. }
  585. return router || '';
  586. },
  587. registerAsyncBizComponents() {
  588. const viewRouter = this.getBusinessComponentRouter('view');
  589. const handleRouter = this.getBusinessComponentRouter('handle');
  590. if (!viewRouter || !handleRouter) {
  591. console.error('流程表单路由未配置', {
  592. pcViewRouter: this.listData.pcViewRouter,
  593. pcHandleRouter: this.listData.pcHandleRouter,
  594. formCustomCreatePath:
  595. this.processInstance?.processDefinition?.formCustomCreatePath,
  596. formKey: this.processInstance?.processDefinition?.formKey,
  597. resolved: { viewRouter, handleRouter },
  598. businessCode: this.listData.businessCode,
  599. businessType: this.listData.businessType
  600. });
  601. this.$message.error(
  602. '流程未配置 PC 表单路由。请在流程模型「业务表单」或节点 formKey 中配置:{"pcView":"/bpm/handleTask/components/.../detail.vue","pcHandle":"/bpm/handleTask/components/.../submit.vue"}'
  603. );
  604. return false;
  605. }
  606. // 回填到 listData,便于后续使用
  607. this.listData.pcViewRouter = viewRouter;
  608. this.listData.pcHandleRouter = handleRouter;
  609. console.log('加载业务表单组件', { viewRouter, handleRouter });
  610. const staticComponents = {
  611. '/bpm/handleTask/components/purchaseOrder/returnGoods/detailDialog.vue':
  612. purchaseReturnDetail,
  613. '/bpm/handleTask/components/purchaseOrder/returnGoods/detailDialog':
  614. purchaseReturnDetail,
  615. '/bpm/handleTask/components/purchaseOrder/returnGoods/submit.vue':
  616. purchaseReturnSubmit,
  617. '/bpm/handleTask/components/purchaseOrder/returnGoods/submit':
  618. purchaseReturnSubmit
  619. };
  620. const viewComponent = staticComponents[viewRouter];
  621. const handleComponent = staticComponents[handleRouter];
  622. Vue.component(
  623. 'async-biz-form-component',
  624. viewComponent || ((resolve) => {
  625. require([`@/views${viewRouter}`], resolve);
  626. })
  627. );
  628. Vue.component(
  629. 'async-sub-form-component',
  630. handleComponent || ((resolve) => {
  631. require([`@/views${handleRouter}`], resolve);
  632. })
  633. );
  634. return true;
  635. },
  636. submit(data) {
  637. this.$refs.bziRef.save(data);
  638. },
  639. open(data) {
  640. // 先清空上一条流程,让业务表单组件在新数据渲染前卸载,
  641. // 避免旧流程的组件带着新流程的 businessId 重新取数(串 id 请求)
  642. this.processInstance = {};
  643. this.listData = data;
  644. console.log('实例流程~~~~~~', data);
  645. this.dialogVisible = true;
  646. this.getDetail();
  647. // this.getTasksRecordDetail();
  648. },
  649. activeCompChange(activeComp) {
  650. this.$refs.subForm[0].activeCompChange(activeComp);
  651. },
  652. /** 获得流程实例 */
  653. getDetail() {
  654. // 获得流程实例相关
  655. this.processInstanceLoading = true;
  656. getProcessInstance(this.listData.id).then((response) => {
  657. if (!response) {
  658. this.$message.error('查询不到流程信息!');
  659. return;
  660. }
  661. // 设置流程信息
  662. this.processInstance = response;
  663. // //将业务表单,注册为动态组件
  664. // let formCustomCreatePath = JSON.parse(
  665. // this.processInstance.processDefinition.formCustomCreatePath
  666. // );
  667. // const bizpath = formCustomCreatePath.pcView;
  668. // const subpath = formCustomCreatePath.pcHandle;
  669. this.registerAsyncBizComponents();
  670. // // 设置表单信息
  671. // if (this.processInstance.processDefinition.formType === 10) {
  672. // this.detailForm = {
  673. // ...JSON.parse(this.processInstance.processDefinition.formConf),
  674. // disabled: true, // 表单禁用
  675. // formBtns: false, // 按钮隐藏
  676. // fields: decodeFields(this.processInstance.processDefinition.formFields)
  677. // }
  678. // // 设置表单的值
  679. // this.detailForm.fields.forEach(item => {
  680. // const val = this.processInstance.formVariables[item.__vModel__]
  681. // if (val) {
  682. // item.__config__.defaultValue = val
  683. // }
  684. // });
  685. // }
  686. // 取消加载中
  687. this.processInstanceLoading = false;
  688. });
  689. this.runningTasks = [];
  690. this.auditForms = [];
  691. getTaskListByProcessInstanceId(this.listData.id).then((response) => {
  692. console.log(response, 'response');
  693. // 审批记录
  694. this.tasks = [];
  695. // 移除已取消的审批
  696. response.forEach((task) => {
  697. if (task.result !== 4) {
  698. this.tasks.push(task);
  699. }
  700. });
  701. // 排序,将未完成的排在前面,已完成的排在后面;
  702. this.tasks.sort((a, b) => {
  703. // 有已完成的情况,按照完成时间倒序
  704. if (a.endTime && b.endTime) {
  705. return b.endTime - a.endTime;
  706. } else if (a.endTime) {
  707. return 1;
  708. } else if (b.endTime) {
  709. return -1;
  710. // 都是未完成,按照创建时间倒序
  711. } else {
  712. return b.createTime - a.createTime;
  713. }
  714. });
  715. // 需要审核的记录
  716. const userId = store.getters.user.info.userId;
  717. if (this.tasks.some((item) => item.result == 7)) {
  718. let taskItem = this.tasks.find((item) => item.result == 7);
  719. const stack = [];
  720. let task = [];
  721. stack.push(taskItem.children);
  722. while (stack.length > 0) {
  723. const node = stack.pop();
  724. console.log(node, '-----');
  725. node.forEach((n) => {
  726. if (n.children?.length === 0) {
  727. task.push(n);
  728. } else {
  729. task = [];
  730. stack.push(n.children);
  731. }
  732. });
  733. }
  734. task.forEach((task) => {
  735. if (task.result !== 1 && task.result !== 6) {
  736. // 只有待处理才需要
  737. return;
  738. }
  739. if (!task.assigneeUser || task.assigneeUser.id !== userId) {
  740. // 自己不是处理人
  741. return;
  742. }
  743. if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
  744. // 不是当前流程的
  745. return;
  746. }
  747. this.runningTasks.push({ ...task });
  748. this.auditForms.push({
  749. reason: ''
  750. });
  751. });
  752. } else {
  753. this.tasks.forEach((task) => {
  754. if (task.result !== 1 && task.result !== 6) {
  755. // 只有待处理才需要
  756. return;
  757. }
  758. if (!task.assigneeUser || task.assigneeUser.id !== userId) {
  759. // 自己不是处理人
  760. return;
  761. }
  762. if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
  763. // 不是当前流程的
  764. return;
  765. }
  766. this.runningTasks.push({ ...task });
  767. this.auditForms.push({
  768. reason: ''
  769. });
  770. });
  771. }
  772. this.normalizeRunningTasks();
  773. });
  774. },
  775. normalizeRunningTasks() {
  776. const currentTaskIndex = this.runningTasks.findIndex(
  777. (task) => `${task.id}` === `${this.listData.taskId}`
  778. );
  779. const task =
  780. this.runningTasks[currentTaskIndex > -1 ? currentTaskIndex : 0];
  781. this.runningTasks = task ? [task] : [];
  782. this.auditForms = this.runningTasks.map(() => ({
  783. reason: ''
  784. }));
  785. },
  786. getTimelineItemIcon(item) {
  787. if (item.result === 1) {
  788. return 'el-icon-time';
  789. }
  790. if (item.result === 2) {
  791. return 'el-icon-check';
  792. }
  793. if (item.result === 3) {
  794. return 'el-icon-close';
  795. }
  796. if (item.result === 4) {
  797. return 'el-icon-remove-outline';
  798. }
  799. if (item.result === 5) {
  800. return 'el-icon-back';
  801. }
  802. return '';
  803. },
  804. getTimelineItemType(item) {
  805. if (item.result === 1) {
  806. return 'primary';
  807. }
  808. if (item.result === 2) {
  809. return 'success';
  810. }
  811. if (item.result === 3) {
  812. return 'danger';
  813. }
  814. if (item.result === 4) {
  815. return 'info';
  816. }
  817. if (item.result === 5) {
  818. return 'warning';
  819. }
  820. if (item.result === 6) {
  821. return 'default';
  822. }
  823. return '';
  824. },
  825. /** 处理审批通过和不通过的操作 */
  826. handleAudit(data) {
  827. let text = data.status === 1 ? '通过' : '不通过';
  828. this.$message.success(`审批${data.title || text}成功!`);
  829. this.handleClose(); // 获得最新详情
  830. // const index = this.runningTasks.indexOf(task);
  831. // this.$refs['form' + index][0].validate((valid) => {
  832. // if (!valid) {
  833. // return;
  834. // }
  835. // const data = {
  836. // id: task.id,
  837. // reason: this.auditForms[index].reason
  838. // };
  839. // if (pass) {
  840. // approveTask(data).then((response) => {
  841. // this.$message.success('审批通过成功!');
  842. // this.handleClose(); // 获得最新详情
  843. // });
  844. // } else {
  845. // rejectTask(data).then((response) => {
  846. // this.$message.success('审批不通过成功!');
  847. // this.handleClose(); // 获得最新详情
  848. // });
  849. // }
  850. // });
  851. },
  852. /** 处理转办审批人 */
  853. handleUpdateAssignee(task) {
  854. // 设置表单
  855. this.resetUpdateAssigneeForm();
  856. this.updateAssignee.form.id = task.id;
  857. // 设置为打开
  858. this.updateAssignee.open = true;
  859. },
  860. /** 提交转办审批人 */
  861. submitUpdateAssigneeForm() {
  862. this.$refs['updateAssigneeForm'].validate((valid) => {
  863. if (!valid) {
  864. return;
  865. }
  866. if (this.$refs.subForm[0].formUpdate) {
  867. this.$refs.subForm[0].formUpdate();
  868. }
  869. updateTaskAssignee(this.updateAssignee.form).then((response) => {
  870. this.$message.success('转办任务成功!');
  871. this.updateAssignee.open = false;
  872. this.handleClose(); // 获得最新详情
  873. });
  874. });
  875. },
  876. /** 取消转办审批人 */
  877. cancelUpdateAssigneeForm() {
  878. this.updateAssignee.open = false;
  879. this.resetUpdateAssigneeForm();
  880. },
  881. /** 重置转办审批人 */
  882. resetUpdateAssigneeForm() {
  883. this.updateAssignee.form = {
  884. id: undefined,
  885. assigneeUserId: undefined
  886. };
  887. this.resetForm('updateAssigneeForm');
  888. },
  889. /** 处理审批委派的操作 */
  890. handleDelegate(task) {
  891. //this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!");
  892. this.resetUpdateDelegateForm();
  893. this.updateDelegate.form.id = task.id;
  894. // 设置为打开
  895. this.updateDelegate.open = true;
  896. },
  897. /** 提交委派审批人 */
  898. submitUpdateDelegateForm() {
  899. this.$refs['updateDelegateForm'].validate((valid) => {
  900. if (!valid) {
  901. return;
  902. }
  903. delegateTask(this.updateDelegate.form).then((response) => {
  904. this.$message.success('委派任务成功!');
  905. this.updateDelegate.open = false;
  906. this.handleClose(); // 获得最新详情
  907. });
  908. });
  909. },
  910. /** 取消委派审批人 */
  911. cancelUpdateDelegateForm() {
  912. this.updateDelegate.open = false;
  913. this.resetUpdateDelegateForm();
  914. },
  915. /** 重置委派审批人 */
  916. resetUpdateDelegateForm() {
  917. this.updateDelegate.form = {
  918. id: undefined,
  919. delegateUserId: undefined
  920. };
  921. this.resetForm('updateDelegateForm');
  922. },
  923. /** 处理审批退回的操作 */
  924. /** 返回退回节点列表 */
  925. handleBackList(task) {
  926. // 可参考 http://blog.wya1.com/article/636697030/details/7296
  927. getReturnList(task.id).then((response) => {
  928. this.returnList = response.data.data;
  929. if (this.returnList.length == 0) {
  930. this.$message.error('当前没有可回退的节点!');
  931. return;
  932. }
  933. this.formData.id = task.id;
  934. this.returnOpen = true;
  935. });
  936. },
  937. /** 提交退回任务 */
  938. submitReturn() {
  939. if (!this.formData.targetDefinitionKey) {
  940. this.$message.error('请选择退回节点!');
  941. }
  942. this.$refs['formRef'].validate((valid) => {
  943. if (!valid) {
  944. return;
  945. }
  946. returnTask(this.formData).then((res) => {
  947. if (res.data) {
  948. this.$message.success('回退成功!');
  949. this.returnOpen = false;
  950. this.handleClose(); // 获得最新详情
  951. }
  952. });
  953. });
  954. },
  955. handleClose() {
  956. this.$emit('reload');
  957. this.dialogVisible = false;
  958. },
  959. getTableValue(fn) {
  960. // 第二个参数告诉审批组件:用户在表格里是否手动改过单价(用于决定是否需要落库)
  961. fn(
  962. this.$refs.bziRef.getTableValue(),
  963. this.$refs.bziRef.isSinglePriceChanged?.()
  964. );
  965. }
  966. }
  967. };
  968. </script>
  969. <style lang="scss">
  970. .my-process-designer {
  971. height: calc(100vh - 200px);
  972. }
  973. .box-card {
  974. width: 100%;
  975. margin-bottom: 20px;
  976. }
  977. </style>