submitVerify.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. <template>
  2. <el-dialog v-model="dialog.visible" :title="dialog.title" width="50%" draggable :before-close="cancel" center :close-on-click-modal="false">
  3. <el-form v-loading="loading" :model="form" label-width="120px">
  4. <el-form-item label="消息提醒">
  5. <el-checkbox-group v-model="form.messageType">
  6. <el-checkbox value="1" name="type" disabled>站内信</el-checkbox>
  7. <el-checkbox value="2" name="type">邮件</el-checkbox>
  8. <el-checkbox value="3" name="type">短信</el-checkbox>
  9. </el-checkbox-group>
  10. </el-form-item>
  11. <el-form-item label="附件" v-if="buttonObj.file">
  12. <fileUpload v-model="form.fileId" :file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'txt', 'pdf']" :file-size="20" />
  13. </el-form-item>
  14. <el-form-item label="抄送" v-if="buttonObj.copy">
  15. <el-button type="primary" icon="Plus" circle @click="openUserSelectCopy" />
  16. <el-tag v-for="user in selectCopyUserList" :key="user.userId" closable style="margin: 2px" @close="handleCopyCloseTag(user)">
  17. {{ user.nickName }}
  18. </el-tag>
  19. </el-form-item>
  20. <el-form-item v-if="buttonObj.pop && nestNodeList && nestNodeList.length > 0" label="下一步审批人" prop="assigneeMap">
  21. <div v-for="(item, index) in nestNodeList" :key="index" style="margin-bottom: 5px; width: 500px">
  22. <span>【{{ item.nodeName }}】:</span>
  23. <el-input v-if="false" v-model="form.assigneeMap[item.nodeCode]" />
  24. <el-input placeholder="请选择审批人" readonly v-model="nickName[item.nodeCode]">
  25. <template v-slot:append>
  26. <el-button @click="choosePeople(item)" icon="search">选择</el-button>
  27. </template>
  28. </el-input>
  29. </div>
  30. </el-form-item>
  31. <el-form-item v-if="task.flowStatus === 'waiting'" label="审批意见">
  32. <el-input v-model="form.message" type="textarea" resize="none" />
  33. </el-form-item>
  34. </el-form>
  35. <template #footer>
  36. <span class="dialog-footer">
  37. <el-button :disabled="buttonDisabled" type="primary" @click="handleCompleteTask"> 提交 </el-button>
  38. <el-button v-if="task.flowStatus === 'waiting' && buttonObj.trust" :disabled="buttonDisabled" type="primary" @click="openDelegateTask">
  39. 委托
  40. </el-button>
  41. <el-button v-if="task.flowStatus === 'waiting' && buttonObj.transfer" :disabled="buttonDisabled" type="primary" @click="openTransferTask">
  42. 转办
  43. </el-button>
  44. <el-button
  45. v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0 && buttonObj.addSign"
  46. :disabled="buttonDisabled"
  47. type="primary"
  48. @click="openMultiInstanceUser"
  49. >
  50. 加签
  51. </el-button>
  52. <el-button
  53. v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0 && buttonObj.subSign"
  54. :disabled="buttonDisabled"
  55. type="primary"
  56. @click="handleTaskUser"
  57. >
  58. 减签
  59. </el-button>
  60. <el-button
  61. v-if="task.flowStatus === 'waiting' && buttonObj.termination"
  62. :disabled="buttonDisabled"
  63. type="danger"
  64. @click="handleTerminationTask"
  65. >
  66. 终止
  67. </el-button>
  68. <el-button v-if="task.flowStatus === 'waiting' && buttonObj.back" :disabled="buttonDisabled" type="danger" @click="handleBackProcessOpen">
  69. 退回
  70. </el-button>
  71. <el-button :disabled="buttonDisabled" @click="cancel">取消</el-button>
  72. </span>
  73. </template>
  74. <!-- 抄送 -->
  75. <UserSelect ref="userSelectCopyRef" :multiple="true" :data="selectCopyUserIds" @confirm-call-back="userSelectCopyCallBack"></UserSelect>
  76. <!-- 转办 -->
  77. <UserSelect ref="transferTaskRef" :multiple="false" @confirm-call-back="handleTransferTask"></UserSelect>
  78. <!-- 委托 -->
  79. <UserSelect ref="delegateTaskRef" :multiple="false" @confirm-call-back="handleDelegateTask"></UserSelect>
  80. <!-- 加签组件 -->
  81. <UserSelect ref="multiInstanceUserRef" :multiple="true" @confirm-call-back="addMultiInstanceUser"></UserSelect>
  82. <!-- 弹窗选人 -->
  83. <UserSelect
  84. ref="porUserRef"
  85. :data="form.assigneeMap[nodeCode]"
  86. :multiple="true"
  87. :userIds="popUserIds"
  88. @confirm-call-back="handlePopUser"
  89. ></UserSelect>
  90. <!-- 驳回开始 -->
  91. <el-dialog v-model="backVisible" draggable title="驳回" width="40%" :close-on-click-modal="false">
  92. <el-form v-if="task.flowStatus === 'waiting'" v-loading="backLoading" :model="backForm" label-width="120px">
  93. <el-form-item label="驳回节点">
  94. <el-select v-model="backForm.nodeCode" clearable placeholder="请选择" style="width: 300px">
  95. <el-option v-for="item in taskNodeList" :key="item.nodeCode" :label="item.nodeName" :value="item.nodeCode" />
  96. </el-select>
  97. </el-form-item>
  98. <el-form-item label="消息提醒">
  99. <el-checkbox-group v-model="backForm.messageType">
  100. <el-checkbox label="1" name="type" disabled>站内信</el-checkbox>
  101. <el-checkbox label="2" name="type">邮件</el-checkbox>
  102. <el-checkbox label="3" name="type">短信</el-checkbox>
  103. </el-checkbox-group>
  104. </el-form-item>
  105. <el-form-item v-if="task.flowStatus === 'waiting'" label="附件">
  106. <fileUpload
  107. v-model="backForm.fileId"
  108. :file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'txt', 'pdf']"
  109. :file-size="20"
  110. />
  111. </el-form-item>
  112. <el-form-item label="审批意见">
  113. <el-input v-model="backForm.message" type="textarea" resize="none" />
  114. </el-form-item>
  115. </el-form>
  116. <template #footer>
  117. <div class="dialog-footer" style="float: right; padding-bottom: 20px">
  118. <el-button :disabled="backButtonDisabled" type="primary" @click="handleBackProcess">确认</el-button>
  119. <el-button :disabled="backButtonDisabled" @click="backVisible = false">取消</el-button>
  120. </div>
  121. </template>
  122. </el-dialog>
  123. <!-- 驳回结束 -->
  124. <el-dialog v-model="deleteSignatureVisible" draggable title="减签人员" width="700px" height="400px" append-to-body :close-on-click-modal="false">
  125. <div>
  126. <el-table :data="deleteUserList" border>
  127. <el-table-column prop="nodeName" label="任务名称" />
  128. <el-table-column prop="nickName" label="办理人" />
  129. <el-table-column label="操作" align="center" width="160">
  130. <template #default="scope">
  131. <el-button type="danger" size="small" icon="Delete" @click="deleteMultiInstanceUser(scope.row)">删除 </el-button>
  132. </template>
  133. </el-table-column>
  134. </el-table>
  135. </div>
  136. </el-dialog>
  137. </el-dialog>
  138. </template>
  139. <script setup lang="ts">
  140. import { ref } from 'vue';
  141. import { ComponentInternalInstance } from 'vue';
  142. import { ElForm } from 'element-plus';
  143. import {
  144. completeTask,
  145. backProcess,
  146. getTask,
  147. taskOperation,
  148. terminationTask,
  149. getBackTaskNode,
  150. currentTaskAllUser,
  151. getNextNodeList
  152. } from '@/api/workflow/task';
  153. import UserSelect from '@/components/UserSelect';
  154. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  155. import { FlowCopyVo, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
  156. const userSelectCopyRef = ref<InstanceType<typeof UserSelect>>();
  157. const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
  158. const delegateTaskRef = ref<InstanceType<typeof UserSelect>>();
  159. const multiInstanceUserRef = ref<InstanceType<typeof UserSelect>>();
  160. const porUserRef = ref<InstanceType<typeof UserSelect>>();
  161. const props = defineProps({
  162. taskVariables: {
  163. type: Object as () => Record<string, any>,
  164. default: () => {}
  165. }
  166. });
  167. //遮罩层
  168. const loading = ref(true);
  169. //按钮
  170. const buttonDisabled = ref(true);
  171. //任务id
  172. const taskId = ref<string>('');
  173. //抄送人
  174. const selectCopyUserList = ref<FlowCopyVo[]>([]);
  175. //抄送人id
  176. const selectCopyUserIds = ref<string>(undefined);
  177. //自定义节点变量
  178. const varNodeList = ref<Map<string, string>>(undefined);
  179. //可减签的人员
  180. const deleteUserList = ref<any>([]);
  181. //弹窗可选择的人员id
  182. const popUserIds = ref<any>([]);
  183. //驳回是否显示
  184. const backVisible = ref(false);
  185. const backLoading = ref(true);
  186. const backButtonDisabled = ref(true);
  187. // 可驳回得任务节点
  188. const taskNodeList = ref([]);
  189. const nickName = ref({});
  190. //节点编码
  191. const nodeCode = ref<string>('');
  192. const buttonObj = ref<any>({
  193. pop: false,
  194. trust: false,
  195. transfer: false,
  196. addSign: false,
  197. subSign: false,
  198. termination: false,
  199. back: false
  200. });
  201. //下一节点列表
  202. const nestNodeList = ref([]);
  203. //任务
  204. const task = ref<FlowTaskVO>({
  205. id: undefined,
  206. createTime: undefined,
  207. updateTime: undefined,
  208. tenantId: undefined,
  209. definitionId: undefined,
  210. instanceId: undefined,
  211. flowName: undefined,
  212. businessId: undefined,
  213. nodeCode: undefined,
  214. nodeName: undefined,
  215. flowCode: undefined,
  216. flowStatus: undefined,
  217. formCustom: undefined,
  218. formPath: undefined,
  219. nodeType: undefined,
  220. nodeRatio: undefined,
  221. applyNode: false,
  222. buttonList: [],
  223. copyList: [],
  224. varList: undefined,
  225. businessCode: undefined,
  226. businessTitle: undefined
  227. });
  228. const dialog = reactive<DialogOption>({
  229. visible: false,
  230. title: '提示'
  231. });
  232. //减签弹窗
  233. const deleteSignatureVisible = ref(false);
  234. const form = ref<Record<string, any>>({
  235. taskId: undefined,
  236. message: undefined,
  237. assigneeMap: {},
  238. variables: {},
  239. messageType: ['1'],
  240. flowCopyList: []
  241. });
  242. const backForm = ref<Record<string, any>>({
  243. taskId: undefined,
  244. nodeCode: undefined,
  245. message: undefined,
  246. variables: {},
  247. messageType: ['1']
  248. });
  249. //打开弹窗
  250. const openDialog = async (id?: string) => {
  251. selectCopyUserIds.value = undefined;
  252. selectCopyUserList.value = [];
  253. form.value.fileId = undefined;
  254. taskId.value = id;
  255. form.value.message = undefined;
  256. dialog.visible = true;
  257. loading.value = true;
  258. buttonDisabled.value = true;
  259. const response = await getTask(taskId.value);
  260. task.value = response.data;
  261. buttonObj.value = {};
  262. task.value.buttonList?.forEach((e) => {
  263. buttonObj.value[e.code] = e.show;
  264. });
  265. selectCopyUserList.value = task.value.copyList;
  266. selectCopyUserIds.value = task.value.copyList.map((e) => e.userId).join(',');
  267. varNodeList.value = task.value.varList;
  268. console.log('varNodeList', varNodeList.value);
  269. buttonDisabled.value = false;
  270. try {
  271. const data = {
  272. taskId: taskId.value,
  273. variables: props.taskVariables
  274. };
  275. const nextData = await getNextNodeList(data);
  276. nestNodeList.value = nextData.data;
  277. } finally {
  278. loading.value = false;
  279. }
  280. };
  281. onMounted(() => {});
  282. const emits = defineEmits(['submitCallback', 'cancelCallback']);
  283. /** 办理流程 */
  284. const handleCompleteTask = async () => {
  285. form.value.taskId = taskId.value;
  286. form.value.variables = props.taskVariables;
  287. let verify = false;
  288. if (buttonObj.value.pop && nestNodeList.value && nestNodeList.value.length > 0) {
  289. nestNodeList.value.forEach((e) => {
  290. if (
  291. Object.keys(form.value.assigneeMap).length === 0 ||
  292. form.value.assigneeMap[e.nodeCode] === '' ||
  293. form.value.assigneeMap[e.nodeCode] === null ||
  294. form.value.assigneeMap[e.nodeCode] === undefined
  295. ) {
  296. verify = true;
  297. }
  298. });
  299. if (verify) {
  300. proxy?.$modal.msgWarning('请选择审批人!');
  301. return false;
  302. }
  303. } else {
  304. form.value.assigneeMap = {};
  305. }
  306. if (selectCopyUserList.value && selectCopyUserList.value.length > 0) {
  307. const flowCopyList = [];
  308. selectCopyUserList.value.forEach((e) => {
  309. const copyUser = {
  310. userId: e.userId,
  311. nickName: e.nickName
  312. };
  313. flowCopyList.push(copyUser);
  314. });
  315. form.value.flowCopyList = flowCopyList;
  316. }
  317. await proxy?.$modal.confirm('是否确认提交?');
  318. loading.value = true;
  319. buttonDisabled.value = true;
  320. try {
  321. await completeTask(form.value);
  322. dialog.visible = false;
  323. emits('submitCallback');
  324. proxy?.$modal.msgSuccess('操作成功');
  325. } finally {
  326. loading.value = false;
  327. buttonDisabled.value = false;
  328. }
  329. };
  330. /** 驳回弹窗打开 */
  331. const handleBackProcessOpen = async () => {
  332. backForm.value = {};
  333. backForm.value.messageType = ['1'];
  334. backVisible.value = true;
  335. backLoading.value = true;
  336. backButtonDisabled.value = true;
  337. const data = await getBackTaskNode(task.value.id, task.value.nodeCode);
  338. taskNodeList.value = data.data;
  339. backLoading.value = false;
  340. backButtonDisabled.value = false;
  341. backForm.value.nodeCode = taskNodeList.value[0].nodeCode;
  342. };
  343. /** 驳回流程 */
  344. const handleBackProcess = async () => {
  345. backForm.value.taskId = taskId.value;
  346. await proxy?.$modal.confirm('是否确认驳回到申请人?');
  347. loading.value = true;
  348. backLoading.value = true;
  349. backButtonDisabled.value = true;
  350. await backProcess(backForm.value).finally(() => {
  351. loading.value = false;
  352. buttonDisabled.value = false;
  353. });
  354. dialog.visible = false;
  355. backLoading.value = false;
  356. backButtonDisabled.value = false;
  357. emits('submitCallback');
  358. proxy?.$modal.msgSuccess('操作成功');
  359. };
  360. //取消
  361. const cancel = async () => {
  362. dialog.visible = false;
  363. buttonDisabled.value = false;
  364. nickName.value = {};
  365. form.value.assigneeMap = {};
  366. emits('cancelCallback');
  367. };
  368. //打开抄送人员
  369. const openUserSelectCopy = () => {
  370. userSelectCopyRef.value.open();
  371. };
  372. //确认抄送人员
  373. const userSelectCopyCallBack = (data: FlowCopyVo[]) => {
  374. if (data && data.length > 0) {
  375. selectCopyUserList.value = data;
  376. selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
  377. }
  378. };
  379. //删除抄送人员
  380. const handleCopyCloseTag = (user: FlowCopyVo) => {
  381. const userId = user.userId;
  382. // 使用split删除用户
  383. const index = selectCopyUserList.value.findIndex((item) => item.userId === userId);
  384. selectCopyUserList.value.splice(index, 1);
  385. selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
  386. };
  387. //加签
  388. const openMultiInstanceUser = async () => {
  389. multiInstanceUserRef.value.open();
  390. };
  391. //加签
  392. const addMultiInstanceUser = async (data) => {
  393. if (data && data.length > 0) {
  394. const taskOperationBo = reactive<TaskOperationBo>({
  395. userIds: data.map((e) => e.userId),
  396. taskId: taskId.value,
  397. message: form.value.message,
  398. messageType: ['1']
  399. });
  400. await proxy?.$modal.confirm('是否确认提交?');
  401. loading.value = true;
  402. buttonDisabled.value = true;
  403. await taskOperation(taskOperationBo, 'addSignature').finally(() => {
  404. loading.value = false;
  405. buttonDisabled.value = false;
  406. });
  407. dialog.visible = false;
  408. emits('submitCallback');
  409. proxy?.$modal.msgSuccess('操作成功');
  410. } else {
  411. proxy?.$modal.msgWarning('请选择用户!');
  412. }
  413. };
  414. //减签
  415. const deleteMultiInstanceUser = async (row) => {
  416. await proxy?.$modal.confirm('是否确认提交?');
  417. loading.value = true;
  418. buttonDisabled.value = true;
  419. const taskOperationBo = reactive<TaskOperationBo>({
  420. userIds: [row.userId],
  421. taskId: taskId.value,
  422. message: form.value.message,
  423. messageType: ['1']
  424. });
  425. await taskOperation(taskOperationBo, 'reductionSignature').finally(() => {
  426. loading.value = false;
  427. buttonDisabled.value = false;
  428. });
  429. dialog.visible = false;
  430. emits('submitCallback');
  431. proxy?.$modal.msgSuccess('操作成功');
  432. };
  433. //打开转办
  434. const openTransferTask = () => {
  435. transferTaskRef.value.open();
  436. };
  437. //转办
  438. const handleTransferTask = async (data) => {
  439. if (data && data.length > 0) {
  440. const taskOperationBo = reactive<TaskOperationBo>({
  441. userId: data[0].userId,
  442. taskId: taskId.value,
  443. message: form.value.message,
  444. messageType: ['1']
  445. });
  446. await proxy?.$modal.confirm('是否确认提交?');
  447. loading.value = true;
  448. buttonDisabled.value = true;
  449. await taskOperation(taskOperationBo, 'transferTask').finally(() => {
  450. loading.value = false;
  451. buttonDisabled.value = false;
  452. });
  453. dialog.visible = false;
  454. emits('submitCallback');
  455. proxy?.$modal.msgSuccess('操作成功');
  456. } else {
  457. proxy?.$modal.msgWarning('请选择用户!');
  458. }
  459. };
  460. //打开委托
  461. const openDelegateTask = () => {
  462. delegateTaskRef.value.open();
  463. };
  464. //委托
  465. const handleDelegateTask = async (data) => {
  466. if (data && data.length > 0) {
  467. const taskOperationBo = reactive<TaskOperationBo>({
  468. userId: data[0].userId,
  469. taskId: taskId.value,
  470. message: form.value.message,
  471. messageType: ['1']
  472. });
  473. await proxy?.$modal.confirm('是否确认提交?');
  474. loading.value = true;
  475. buttonDisabled.value = true;
  476. await taskOperation(taskOperationBo, 'delegateTask').finally(() => {
  477. loading.value = false;
  478. buttonDisabled.value = false;
  479. });
  480. dialog.visible = false;
  481. emits('submitCallback');
  482. proxy?.$modal.msgSuccess('操作成功');
  483. } else {
  484. proxy?.$modal.msgWarning('请选择用户!');
  485. }
  486. };
  487. //终止任务
  488. const handleTerminationTask = async () => {
  489. const params = {
  490. taskId: taskId.value,
  491. comment: form.value.message
  492. };
  493. await proxy?.$modal.confirm('是否确认终止?');
  494. loading.value = true;
  495. buttonDisabled.value = true;
  496. await terminationTask(params).finally(() => {
  497. loading.value = false;
  498. buttonDisabled.value = false;
  499. });
  500. dialog.visible = false;
  501. emits('submitCallback');
  502. proxy?.$modal.msgSuccess('操作成功');
  503. };
  504. const handleTaskUser = async () => {
  505. const data = await currentTaskAllUser(taskId.value);
  506. deleteUserList.value = data.data;
  507. if (deleteUserList.value && deleteUserList.value.length > 0) {
  508. deleteUserList.value.forEach((e) => {
  509. e.nodeName = task.value.nodeName;
  510. });
  511. }
  512. deleteSignatureVisible.value = true;
  513. };
  514. // 选择人员
  515. const choosePeople = async (data) => {
  516. if (!data.permissionFlag) {
  517. proxy?.$modal.msgError('没有可选择的人员,请联系管理员!');
  518. }
  519. popUserIds.value = data.permissionFlag;
  520. nodeCode.value = data.nodeCode;
  521. porUserRef.value.open();
  522. };
  523. //确认选择
  524. const handlePopUser = async (userList) => {
  525. const userIds = userList.map((item) => {
  526. return item.userId;
  527. });
  528. const nickNames = userList.map((item) => {
  529. return item.nickName;
  530. });
  531. form.value.assigneeMap[nodeCode.value] = userIds.join(',');
  532. nickName.value[nodeCode.value] = nickNames.join(',');
  533. };
  534. /**
  535. * 对外暴露子组件方法
  536. */
  537. defineExpose({
  538. openDialog
  539. });
  540. </script>