index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. <template>
  2. <el-dialog
  3. title="处理"
  4. :before-close="handleClose"
  5. :visible.sync="dialogVisible"
  6. :close-on-click-modal="false"
  7. :append-to-body="true"
  8. width="80%"
  9. >
  10. <div class="app-container">
  11. <!-- 申请信息 -->
  12. <el-card class="box-card" v-loading="processInstanceLoading">
  13. <div slot="header" class="clearfix">
  14. <span class="el-icon-document"
  15. >申请信息【{{ processInstance.name }}】 申请人:【{{
  16. processInstance.startUser?.nickname
  17. }}】</span
  18. >
  19. </div>
  20. <div v-if="processInstance.processDefinition">
  21. <async-biz-form-component
  22. :taskId="listData?.taskId"
  23. :businessId="listData?.businessId"
  24. :id="listData?.id"
  25. :taskDefinitionKey="listData?.taskDefinitionKey"
  26. @activeCompChange="activeCompChange"
  27. @handleClose="handleClose"
  28. ref="bziRef"
  29. ></async-biz-form-component>
  30. </div>
  31. </el-card>
  32. <!-- 审批信息 -->
  33. <el-card
  34. class="box-card"
  35. v-loading="processInstanceLoading"
  36. v-for="(item, index) in runningTasks"
  37. :key="index"
  38. >
  39. <div slot="header" class="clearfix">
  40. <span class="el-icon-picture-outline">审批任务</span>
  41. </div>
  42. <div v-if="processInstance.processDefinition">
  43. <async-sub-form-component
  44. :taskId="listData?.taskId"
  45. :businessId="listData?.businessId"
  46. :id="listData?.id"
  47. :taskDefinitionKey="listData?.taskDefinitionKey"
  48. @handleAudit="handleAudit"
  49. @getTableValue="getTableValue"
  50. @handleUpdateAssignee="handleUpdateAssignee(item)"
  51. @handleBackList="handleBackList(item)"
  52. @submit="submit"
  53. @handleClose="handleClose"
  54. ref="subForm"
  55. ></async-sub-form-component>
  56. </div>
  57. <!-- <el-col :span="16" :offset="6">
  58. <el-form
  59. :ref="'form' + index"
  60. :model="auditForms[index]"
  61. :rules="auditRule"
  62. label-width="100px"
  63. >
  64. <el-form-item
  65. label="审批建议"
  66. prop="reason"
  67. style="margin-bottom: 20px"
  68. >
  69. <el-input
  70. type="textarea"
  71. v-model="auditForms[index].reason"
  72. placeholder="请输入审批建议"
  73. />
  74. </el-form-item>
  75. </el-form>
  76. <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
  77. <el-button
  78. icon="el-icon-edit-outline"
  79. type="success"
  80. size="mini"
  81. @click="handleAudit(item, true)"
  82. >通过
  83. </el-button>
  84. <el-button
  85. icon="el-icon-circle-close"
  86. type="danger"
  87. size="mini"
  88. @click="handleAudit(item, false)"
  89. >不通过
  90. </el-button>
  91. <el-button
  92. icon="el-icon-edit-outline"
  93. type="primary"
  94. size="mini"
  95. @click="handleUpdateAssignee(item)"
  96. >转办
  97. </el-button>
  98. <el-button
  99. icon="el-icon-edit-outline"
  100. type="primary"
  101. size="mini"
  102. @click="handleDelegate(item)"
  103. >委派
  104. </el-button>
  105. <el-button
  106. icon="el-icon-refresh-left"
  107. type="warning"
  108. size="mini"
  109. @click="handleBackList(item)"
  110. >退回
  111. </el-button>
  112. </div>
  113. </el-col> -->
  114. </el-card>
  115. <!-- 对话框(转办审批人) -->
  116. <el-dialog
  117. title="转办审批人"
  118. :visible.sync="updateAssignee.open"
  119. width="500px"
  120. append-to-body
  121. >
  122. <el-form
  123. ref="updateAssigneeForm"
  124. :model="updateAssignee.form"
  125. :rules="updateAssignee.rules"
  126. label-width="110px"
  127. >
  128. <el-form-item label="新审批人" prop="assigneeUserId">
  129. <el-select
  130. v-model="updateAssignee.form.assigneeUserId"
  131. clearable
  132. style="width: 100%"
  133. :filterable="true"
  134. >
  135. <el-option
  136. v-for="item in userOptions"
  137. :key="item.id"
  138. :label="item.name"
  139. :value="item.id"
  140. />
  141. </el-select>
  142. </el-form-item>
  143. </el-form>
  144. <div slot="footer" class="dialog-footer">
  145. <el-button type="primary" @click="submitUpdateAssigneeForm"
  146. >确 定
  147. </el-button>
  148. <el-button @click="cancelUpdateAssigneeForm">取 消</el-button>
  149. </div>
  150. </el-dialog>
  151. <!-- 对话框(委派审批人) -->
  152. <el-dialog
  153. title="委派审批人"
  154. :visible.sync="updateDelegate.open"
  155. width="500px"
  156. append-to-body
  157. >
  158. <el-form
  159. ref="updateDelegateForm"
  160. :model="updateDelegate.form"
  161. :rules="updateDelegate.rules"
  162. label-width="110px"
  163. >
  164. <el-form-item label="新审批人" prop="assigneeUserId">
  165. <el-select
  166. v-model="updateDelegate.form.delegateUserId"
  167. clearable
  168. style="width: 100%"
  169. :filterable="true"
  170. >
  171. <el-option
  172. v-for="item in userOptions"
  173. :key="item.id"
  174. :label="item.name"
  175. :value="item.id"
  176. />
  177. </el-select>
  178. </el-form-item>
  179. <el-form-item label="委派理由" prop="reason">
  180. <el-input
  181. v-model="updateDelegate.form.reason"
  182. clearable
  183. placeholder="请输入委派理由"
  184. />
  185. </el-form-item>
  186. </el-form>
  187. <div slot="footer" class="dialog-footer">
  188. <el-button type="primary" @click="submitUpdateDelegateForm"
  189. >确 定
  190. </el-button>
  191. <el-button @click="cancelUpdateDelegateForm">取 消</el-button>
  192. </div>
  193. </el-dialog>
  194. <!--退回流程-->
  195. <el-dialog
  196. title="退回流程"
  197. :visible.sync="returnOpen"
  198. width="40%"
  199. append-to-body
  200. >
  201. <el-form
  202. ref="formRef"
  203. v-loading="formLoading"
  204. :model="formData"
  205. :rules="formRules"
  206. label-width="110px"
  207. >
  208. <el-form-item label="退回节点" prop="targetDefinitionKey">
  209. <el-select
  210. v-model="formData.targetDefinitionKey"
  211. clearable
  212. style="width: 100%"
  213. >
  214. <el-option
  215. v-for="item in returnList"
  216. :key="item.definitionKey"
  217. :label="item.name"
  218. :value="item.definitionKey"
  219. />
  220. </el-select>
  221. </el-form-item>
  222. <el-form-item label="回退理由" prop="reason">
  223. <el-input
  224. v-model="formData.reason"
  225. clearable
  226. placeholder="请输入回退理由"
  227. />
  228. </el-form-item>
  229. </el-form>
  230. <span slot="footer" class="dialog-footer">
  231. <el-button @click="returnOpen = false">取 消</el-button>
  232. <el-button
  233. :disabled="formLoading"
  234. type="primary"
  235. @click="submitReturn"
  236. >确 定</el-button
  237. >
  238. </span>
  239. </el-dialog>
  240. </div>
  241. </el-dialog>
  242. </template>
  243. <script>
  244. // import { getProcessDefinitionBpmnXML } from '@/api/bpm/definition';
  245. import store from '@/store';
  246. // import {decodeFields} from "@/utils/formGenerator";
  247. // import Parser from '@/components/parser/Parser'
  248. import { getProcessInstance } from '@/api/bpm/processInstance';
  249. import { listAllUserBind } from '@/api/system/organization';
  250. import Vue from 'vue';
  251. import {
  252. approveTask,
  253. delegateTask,
  254. getReturnList,
  255. getTaskListByProcessInstanceId,
  256. rejectTask,
  257. returnTask,
  258. updateTaskAssignee
  259. } from '@/api/bpm/task';
  260. // import { getDate } from '@/utils/dateUtils';
  261. // 流程实例的详情页,可用于审批
  262. export default {
  263. name: 'ProcessInstanceDetail',
  264. components: {
  265. // Parser
  266. },
  267. data() {
  268. return {
  269. // 遮罩层
  270. processInstanceLoading: true,
  271. dialogVisible: false,
  272. // 流程实例
  273. listData: {
  274. id: undefined, // 流程实例的编号
  275. pcHandleRouter: '',
  276. pcViewRouter: '',
  277. businessId: '', // 业务id
  278. taskDefinitionKey: '',
  279. taskId: '' //任务编码
  280. },
  281. processInstance: {},
  282. formLoading: false,
  283. // 流程表单详情
  284. detailForm: {
  285. fields: []
  286. },
  287. //回退列表数据
  288. returnList: [],
  289. formData: {
  290. id: '',
  291. targetDefinitionKey: undefined,
  292. reason: ''
  293. },
  294. formRules: {
  295. targetDefinitionKey: [
  296. { required: true, message: '必须选择回退节点', trigger: 'change' }
  297. ],
  298. reason: [
  299. { required: true, message: '回退理由不能为空', trigger: 'blur' }
  300. ]
  301. },
  302. returnOpen: false,
  303. activityList: [],
  304. tasks: [],
  305. // 审批表单
  306. runningTasks: [],
  307. auditForms: [],
  308. auditRule: {
  309. reason: [
  310. { required: true, message: '审批建议不能为空', trigger: 'blur' }
  311. ]
  312. },
  313. // 转派审批人
  314. userOptions: [],
  315. updateAssignee: {
  316. open: false,
  317. form: {
  318. assigneeUserId: undefined
  319. },
  320. rules: {
  321. assigneeUserId: [
  322. { required: true, message: '新审批人不能为空', trigger: 'change' }
  323. ]
  324. }
  325. },
  326. updateDelegate: {
  327. open: false,
  328. form: {
  329. delegateUserId: undefined,
  330. reason: ''
  331. },
  332. rules: {
  333. delegateUserId: [
  334. { required: true, message: '新审批人不能为空', trigger: 'change' }
  335. ],
  336. reason: [
  337. { required: true, message: '委派理由不能为空', trigger: 'blur' }
  338. ]
  339. }
  340. }
  341. };
  342. },
  343. created() {
  344. // this.id = this.$route.query.id;
  345. // if (!this.id) {
  346. // this.$message.error('未传递 id 参数,无法查看流程信息');
  347. // return;
  348. // }
  349. // this.getDetail();
  350. // 获得用户列表
  351. this.userOptions = [];
  352. listAllUserBind().then((data) => {
  353. this.userOptions.push(...data);
  354. });
  355. },
  356. methods: {
  357. submit(data) {
  358. this.$refs.bziRef.save(data);
  359. },
  360. open(data) {
  361. this.listData = data;
  362. console.log('实例流程~~~~~~', data);
  363. this.dialogVisible = true;
  364. this.getDetail();
  365. },
  366. activeCompChange(activeComp) {
  367. console.log(activeComp);
  368. this.$refs.subForm[0].activeCompChange(activeComp);
  369. },
  370. /** 获得流程实例 */
  371. getDetail() {
  372. // 获得流程实例相关
  373. this.processInstanceLoading = true;
  374. getProcessInstance(this.listData.id).then((response) => {
  375. if (!response) {
  376. this.$message.error('查询不到流程信息!');
  377. return;
  378. }
  379. // 设置流程信息
  380. console.log('==', response);
  381. console.log('==', this.listData);
  382. this.processInstance = response;
  383. // //将业务表单,注册为动态组件
  384. // let formCustomCreatePath = JSON.parse(
  385. // this.processInstance.processDefinition.formCustomCreatePath
  386. // );
  387. // const bizpath = formCustomCreatePath.pcView;
  388. // const subpath = formCustomCreatePath.pcHandle;
  389. Vue.component('async-biz-form-component', (resolve) => {
  390. require([`@/views${this.listData.pcViewRouter}`], resolve);
  391. });
  392. Vue.component('async-sub-form-component', (resolve) => {
  393. require([`@/views${this.listData.pcHandleRouter}`], resolve);
  394. });
  395. // // 设置表单信息
  396. // if (this.processInstance.processDefinition.formType === 10) {
  397. // this.detailForm = {
  398. // ...JSON.parse(this.processInstance.processDefinition.formConf),
  399. // disabled: true, // 表单禁用
  400. // formBtns: false, // 按钮隐藏
  401. // fields: decodeFields(this.processInstance.processDefinition.formFields)
  402. // }
  403. // // 设置表单的值
  404. // this.detailForm.fields.forEach(item => {
  405. // const val = this.processInstance.formVariables[item.__vModel__]
  406. // if (val) {
  407. // item.__config__.defaultValue = val
  408. // }
  409. // });
  410. // }
  411. // 取消加载中
  412. this.processInstanceLoading = false;
  413. });
  414. this.runningTasks = [];
  415. this.auditForms = [];
  416. getTaskListByProcessInstanceId(this.listData.id).then((response) => {
  417. console.log(response, 'response');
  418. // 审批记录
  419. this.tasks = [];
  420. // 移除已取消的审批
  421. response.forEach((task) => {
  422. if (task.result !== 4) {
  423. this.tasks.push(task);
  424. }
  425. });
  426. // 排序,将未完成的排在前面,已完成的排在后面;
  427. this.tasks.sort((a, b) => {
  428. // 有已完成的情况,按照完成时间倒序
  429. if (a.endTime && b.endTime) {
  430. return b.endTime - a.endTime;
  431. } else if (a.endTime) {
  432. return 1;
  433. } else if (b.endTime) {
  434. return -1;
  435. // 都是未完成,按照创建时间倒序
  436. } else {
  437. return b.createTime - a.createTime;
  438. }
  439. });
  440. // 需要审核的记录
  441. const userId = store.getters.user.info.userId;
  442. this.tasks.forEach((task) => {
  443. if (task.result !== 1 && task.result !== 6) {
  444. // 只有待处理才需要
  445. return;
  446. }
  447. if (!task.assigneeUser || task.assigneeUser.id !== userId) {
  448. // 自己不是处理人
  449. return;
  450. }
  451. if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
  452. // 不是当前流程的
  453. return;
  454. }
  455. this.runningTasks.push({ ...task });
  456. console.log(this.runningTasks, ' this.runningTasks');
  457. this.auditForms.push({
  458. reason: ''
  459. });
  460. });
  461. });
  462. },
  463. /** 处理审批通过和不通过的操作 */
  464. handleAudit(data) {
  465. let text = data.status === 1 ? '通过' : '不通过';
  466. this.$message.success(`审批${data.title || text}成功!`);
  467. this.handleClose(); // 获得最新详情
  468. // const index = this.runningTasks.indexOf(task);
  469. // this.$refs['form' + index][0].validate((valid) => {
  470. // if (!valid) {
  471. // return;
  472. // }
  473. // const data = {
  474. // id: task.id,
  475. // reason: this.auditForms[index].reason
  476. // };
  477. // if (pass) {
  478. // approveTask(data).then((response) => {
  479. // this.$message.success('审批通过成功!');
  480. // this.handleClose(); // 获得最新详情
  481. // });
  482. // } else {
  483. // rejectTask(data).then((response) => {
  484. // this.$message.success('审批不通过成功!');
  485. // this.handleClose(); // 获得最新详情
  486. // });
  487. // }
  488. // });
  489. },
  490. /** 处理转办审批人 */
  491. handleUpdateAssignee(task) {
  492. // 设置表单
  493. this.resetUpdateAssigneeForm();
  494. this.updateAssignee.form.id = task.id;
  495. // 设置为打开
  496. this.updateAssignee.open = true;
  497. },
  498. /** 提交转办审批人 */
  499. submitUpdateAssigneeForm() {
  500. this.$refs['updateAssigneeForm'].validate((valid) => {
  501. if (!valid) {
  502. return;
  503. }
  504. updateTaskAssignee(this.updateAssignee.form).then((response) => {
  505. this.$message.success('转办任务成功!');
  506. this.updateAssignee.open = false;
  507. this.handleClose(); // 获得最新详情
  508. });
  509. });
  510. },
  511. /** 取消转办审批人 */
  512. cancelUpdateAssigneeForm() {
  513. this.updateAssignee.open = false;
  514. this.resetUpdateAssigneeForm();
  515. },
  516. /** 重置转办审批人 */
  517. resetUpdateAssigneeForm() {
  518. this.updateAssignee.form = {
  519. id: undefined,
  520. assigneeUserId: undefined
  521. };
  522. this.resetForm('updateAssigneeForm');
  523. },
  524. /** 处理审批委派的操作 */
  525. handleDelegate(task) {
  526. //this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!");
  527. this.resetUpdateDelegateForm();
  528. this.updateDelegate.form.id = task.id;
  529. // 设置为打开
  530. this.updateDelegate.open = true;
  531. },
  532. /** 提交委派审批人 */
  533. submitUpdateDelegateForm() {
  534. this.$refs['updateDelegateForm'].validate((valid) => {
  535. if (!valid) {
  536. return;
  537. }
  538. delegateTask(this.updateDelegate.form).then((response) => {
  539. this.$message.success('委派任务成功!');
  540. this.updateDelegate.open = false;
  541. this.handleClose(); // 获得最新详情
  542. });
  543. });
  544. },
  545. /** 取消委派审批人 */
  546. cancelUpdateDelegateForm() {
  547. this.updateDelegate.open = false;
  548. this.resetUpdateDelegateForm();
  549. },
  550. /** 重置委派审批人 */
  551. resetUpdateDelegateForm() {
  552. this.updateDelegate.form = {
  553. id: undefined,
  554. delegateUserId: undefined
  555. };
  556. this.resetForm('updateDelegateForm');
  557. },
  558. /** 处理审批退回的操作 */
  559. /** 返回退回节点列表 */
  560. handleBackList(task) {
  561. // 可参考 http://blog.wya1.com/article/636697030/details/7296
  562. getReturnList(task.id).then((response) => {
  563. this.returnList = response.data.data;
  564. if (this.returnList.length == 0) {
  565. this.$message.error('当前没有可回退的节点!');
  566. return;
  567. }
  568. this.formData.id = task.id;
  569. this.returnOpen = true;
  570. });
  571. },
  572. /** 提交退回任务 */
  573. submitReturn() {
  574. if (!this.formData.targetDefinitionKey) {
  575. this.$message.error('请选择退回节点!');
  576. }
  577. this.$refs['formRef'].validate((valid) => {
  578. if (!valid) {
  579. return;
  580. }
  581. returnTask(this.formData).then((res) => {
  582. if (res.data) {
  583. this.$message.success('回退成功!');
  584. this.returnOpen = false;
  585. this.handleClose(); // 获得最新详情
  586. }
  587. });
  588. });
  589. },
  590. handleClose() {
  591. this.$emit('reload');
  592. this.dialogVisible = false;
  593. },
  594. getTableValue(fn) {
  595. fn(this.$refs.bziRef.getTableValue());
  596. }
  597. }
  598. };
  599. </script>
  600. <style lang="scss">
  601. .my-process-designer {
  602. height: calc(100vh - 200px);
  603. }
  604. .box-card {
  605. width: 100%;
  606. margin-bottom: 20px;
  607. }
  608. </style>