index.vue 19 KB

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