submitVerify.vue 17 KB

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