submitVerify.vue 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397
  1. <template>
  2. <view>
  3. <u-popup
  4. :show="visible"
  5. mode="center"
  6. width="100%"
  7. height="90%"
  8. border-radius="12"
  9. @close="cancel"
  10. :custom-style="{ width: '90vw', maxHeight: '90vh', overflow: 'auto' }"
  11. >
  12. <view class="submit-verify-container">
  13. <view class="header">
  14. <text class="title">{{ dialog.title }}</text>
  15. <u-icon name="close" size="24" @click="cancel" />
  16. </view>
  17. <scroll-view scroll-y class="content" :show-scrollbar="false">
  18. <!-- 消息提醒 -->
  19. <!-- <view class="form-item">
  20. <text class="label">消息提醒</text>
  21. <u-checkbox-group
  22. v-model="form.messageType"
  23. @change="handleMessageTypeChange"
  24. >
  25. <view class="checkbox-row">
  26. <u-checkbox
  27. name="1"
  28. :checked="true"
  29. :disabled="true"
  30. activeColor="#2979ff"
  31. >
  32. 站内信
  33. </u-checkbox>
  34. <u-checkbox name="2" activeColor="#2979ff">邮件</u-checkbox>
  35. <u-checkbox name="3" activeColor="#2979ff">短信</u-checkbox>
  36. </view>
  37. </u-checkbox-group>
  38. </view> -->
  39. <!-- 附件上传 -->
  40. <view class="form-item">
  41. <text class="label">附件</text>
  42. <FileUpload
  43. v-model="form.fileId"
  44. :file-type="[
  45. 'png',
  46. 'jpg',
  47. 'jpeg',
  48. 'doc',
  49. 'docx',
  50. 'xlsx',
  51. 'xls',
  52. 'ppt',
  53. 'txt',
  54. 'pdf',
  55. ]"
  56. :file-size="20"
  57. />
  58. </view>
  59. <!-- 抄送 -->
  60. <view v-if="buttonObj.copy" class="form-item">
  61. <text class="label">抄送</text>
  62. <view class="copy-section">
  63. <u-button
  64. type="primary"
  65. plain
  66. size="mini"
  67. icon="plus"
  68. @click="openUserSelectCopy"
  69. class="add-copy-btn"
  70. >
  71. 添加抄送人
  72. </u-button>
  73. <view v-if="selectCopyUserList.length > 0" class="copy-users">
  74. <u-tag
  75. v-for="user in selectCopyUserList"
  76. :key="user.userId"
  77. :text="user.nickName"
  78. :type="getCopyUserType()"
  79. size="mini"
  80. closable
  81. @close="handleCopyCloseTag(user)"
  82. class="copy-tag"
  83. />
  84. </view>
  85. </view>
  86. </view>
  87. <!-- 下一步审批人 -->
  88. <view
  89. v-if="buttonObj.pop && nestNodeList && nestNodeList.length > 0"
  90. class="form-item"
  91. >
  92. <text class="label">下一步审批人</text>
  93. <view class="next-approver-section">
  94. <view
  95. v-for="(item, index) in nestNodeList"
  96. :key="index"
  97. class="approver-item"
  98. >
  99. <text class="node-name">【{{ item.nodeName }}】</text>
  100. <view class="approver-select">
  101. <u-input
  102. v-model="nickName[item.nodeCode]"
  103. placeholder="请选择审批人"
  104. readonly
  105. suffix-icon="arrow-right"
  106. @click="choosePeople(item)"
  107. />
  108. </view>
  109. </view>
  110. </view>
  111. </view>
  112. <!-- 审批意见 -->
  113. <view v-if="task.flowStatus === 'waiting'" class="form-item">
  114. <text class="label">审批意见</text>
  115. <u-textarea
  116. v-model="form.message"
  117. placeholder="请输入审批意见"
  118. :maxlength="500"
  119. height="120"
  120. count
  121. />
  122. </view>
  123. </scroll-view>
  124. <!-- 底部操作按钮 -->
  125. <view class="footer">
  126. <view class="buttons-grid">
  127. <u-button
  128. type="primary"
  129. plain
  130. :loading="buttonDisabled"
  131. @click="handleCompleteTask"
  132. class="action-btn"
  133. >
  134. 提交
  135. </u-button>
  136. <!-- <u-button
  137. v-if="task.flowStatus === 'waiting' && buttonObj.trust"
  138. type="warning"
  139. :loading="buttonDisabled"
  140. @click="openDelegateTask"
  141. class="action-btn"
  142. >
  143. 委托
  144. </u-button>
  145. <u-button
  146. v-if="task.flowStatus === 'waiting' && buttonObj.transfer"
  147. type="warning"
  148. :loading="buttonDisabled"
  149. @click="openTransferTask"
  150. class="action-btn"
  151. >
  152. 转办
  153. </u-button>
  154. <u-button
  155. v-if="
  156. task.flowStatus === 'waiting' &&
  157. Number(task.nodeRatio) > 0 &&
  158. buttonObj.addSign
  159. "
  160. type="warning"
  161. :loading="buttonDisabled"
  162. @click="openMultiInstanceUser"
  163. class="action-btn"
  164. >
  165. 加签
  166. </u-button>
  167. <u-button
  168. v-if="
  169. task.flowStatus === 'waiting' &&
  170. Number(task.nodeRatio) > 0 &&
  171. buttonObj.subSign
  172. "
  173. type="warning"
  174. :loading="buttonDisabled"
  175. @click="handleTaskUser"
  176. class="action-btn"
  177. >
  178. 减签
  179. </u-button> -->
  180. <u-button
  181. v-if="task.flowStatus === 'waiting' && buttonObj.termination"
  182. type="error"
  183. plain
  184. :loading="buttonDisabled"
  185. @click="handleTerminationTask"
  186. class="action-btn"
  187. >
  188. 终止
  189. </u-button>
  190. <u-button
  191. v-if="task.flowStatus === 'waiting' && buttonObj.back"
  192. type="warning"
  193. shape="round"
  194. plain
  195. :loading="buttonDisabled"
  196. @click="handleBackProcessOpen"
  197. class="action-btn"
  198. >
  199. 退回
  200. </u-button>
  201. <!-- <u-button type="info" @click="cancel" class="action-btn">取消</u-button> -->
  202. </view>
  203. </view>
  204. </view>
  205. <!-- 抄送用户选择 -->
  206. <UserSelect
  207. ref="userSelectCopyRef"
  208. :multiple="true"
  209. :data="selectCopyUserIds"
  210. @confirmCallBack="userSelectCopyCallBack"
  211. />
  212. <!-- 转办用户选择 -->
  213. <UserSelect
  214. ref="transferTaskRef"
  215. :multiple="false"
  216. @confirmCallBack="handleTransferTask"
  217. />
  218. <!-- 委托用户选择 -->
  219. <UserSelect
  220. ref="delegateTaskRef"
  221. :multiple="false"
  222. @confirmCallBack="handleDelegateTask"
  223. />
  224. <!-- 加签用户选择 -->
  225. <UserSelect
  226. ref="multiInstanceUserRef"
  227. :multiple="true"
  228. @confirmCallBack="addMultiInstanceUser"
  229. />
  230. <!-- 下一步审批人选择 -->
  231. <UserSelect
  232. ref="porUserRef"
  233. :multiple="true"
  234. :userIds="popUserIds"
  235. @confirmCallBack="handlePopUser"
  236. />
  237. </u-popup>
  238. <!-- 退回弹窗 -->
  239. <u-popup
  240. :show="backVisible"
  241. mode="center"
  242. width="80%"
  243. height="70%"
  244. border-radius="12"
  245. z-index="20080"
  246. @close="closeBackDialog"
  247. :custom-style="{ width: '90vw' }"
  248. >
  249. <view class="back-container">
  250. <view class="header">
  251. <text class="title">驳回</text>
  252. <u-icon name="close" size="24" @click="closeBackDialog" />
  253. </view>
  254. <scroll-view scroll-y class="content" :show-scrollbar="false">
  255. <!-- 驳回节点 -->
  256. <view class="form-item horizontal-item">
  257. <text class="label horizontal-label">驳回节点</text>
  258. <view class="horizontal-control">
  259. <u-picker
  260. :columns="[taskNodeList]"
  261. keyName="nodeName"
  262. :defaultIndex="[selectedBackNodeIndex]"
  263. :show="showBackNodePicker"
  264. :closeOnClickOverlay="true"
  265. @confirm="onBackNodeConfirm"
  266. @cancel="onBackNodeClose"
  267. ></u-picker>
  268. <view class="picker-input" @click="showBackNodePicker = true">
  269. <text class="picker-text">
  270. {{ selectedBackNodeName || '请选择驳回节点' }}
  271. </text>
  272. <u-icon name="arrow-down" size="16" color="#c0c4cc"></u-icon>
  273. </view>
  274. </view>
  275. </view>
  276. <!-- 消息提醒 -->
  277. <!-- <view class="form-item">
  278. <text class="label">消息提醒</text>
  279. <u-checkbox-group v-model="backForm.messageType">
  280. <view class="checkbox-row">
  281. <u-checkbox
  282. name="1"
  283. :checked="true"
  284. :disabled="true"
  285. activeColor="#2979ff"
  286. >
  287. 站内信
  288. </u-checkbox>
  289. <u-checkbox name="2" activeColor="#2979ff">邮件</u-checkbox>
  290. <u-checkbox name="3" activeColor="#2979ff">短信</u-checkbox>
  291. </view>
  292. </u-checkbox-group>
  293. </view> -->
  294. <!-- 附件 -->
  295. <view class="form-item">
  296. <text class="label">附件</text>
  297. <FileUpload
  298. v-model="backForm.fileId"
  299. :file-type="[
  300. 'png',
  301. 'jpg',
  302. 'jpeg',
  303. 'doc',
  304. 'docx',
  305. 'xlsx',
  306. 'xls',
  307. 'ppt',
  308. 'txt',
  309. 'pdf',
  310. ]"
  311. :file-size="20"
  312. />
  313. </view>
  314. <!-- 审批意见 -->
  315. <view class="form-item">
  316. <text class="label">审批意见</text>
  317. <u-textarea
  318. v-model="backForm.message"
  319. placeholder="请输入审批意见"
  320. :maxlength="500"
  321. height="120"
  322. count
  323. />
  324. </view>
  325. </scroll-view>
  326. <view class="footer">
  327. <view class="buttons-grid">
  328. <u-button
  329. type="primary"
  330. plain
  331. :loading="backButtonDisabled"
  332. @click="handleBackProcess"
  333. class="action-btn"
  334. >
  335. 确认
  336. </u-button>
  337. <u-button
  338. type="info"
  339. plain
  340. @click="closeBackDialog"
  341. class="action-btn cancel-btn"
  342. >
  343. 取消
  344. </u-button>
  345. </view>
  346. </view>
  347. </view>
  348. </u-popup>
  349. <!-- 减签弹窗 -->
  350. <u-popup
  351. :show="deleteSignatureVisible"
  352. mode="center"
  353. width="90%"
  354. height="60%"
  355. border-radius="12"
  356. z-index="20080"
  357. @close="deleteSignatureVisible = false"
  358. >
  359. <view class="delete-signature-container">
  360. <view class="header">
  361. <text class="title">减签人员</text>
  362. <u-icon name="close" size="24" @click="deleteSignatureVisible = false" />
  363. </view>
  364. <scroll-view scroll-y class="content" :show-scrollbar="false">
  365. <view v-if="deleteUserList.length === 0" class="empty-data">
  366. <u-empty text="暂无可减签人员" mode="data" />
  367. </view>
  368. <view v-else class="user-list">
  369. <view v-for="user in deleteUserList" :key="user.userId" class="user-item">
  370. <view class="user-info">
  371. <text class="task-name">{{ user.nodeName }}</text>
  372. <text class="user-name">{{ user.nickName }}</text>
  373. </view>
  374. <u-button
  375. type="error"
  376. plain
  377. size="mini"
  378. @click="deleteMultiInstanceUser(user)"
  379. class="delete-btn"
  380. >
  381. 删除
  382. </u-button>
  383. </view>
  384. </view>
  385. </scroll-view>
  386. </view>
  387. </u-popup>
  388. </view>
  389. </template>
  390. <script>
  391. import { request } from '@/common/request.js';
  392. import UserSelect from '@/components/UserSelect/index.vue';
  393. import FileUpload from '@/components/FileUpload/index.vue';
  394. export default {
  395. name: 'SubmitVerify',
  396. components: {
  397. UserSelect,
  398. FileUpload,
  399. },
  400. props: {
  401. taskVariables: {
  402. type: Object,
  403. default: () => ({}),
  404. },
  405. },
  406. data() {
  407. return {
  408. visible: false,
  409. loading: true,
  410. buttonDisabled: true,
  411. taskId: '',
  412. selectCopyUserList: [],
  413. selectCopyUserIds: undefined,
  414. deleteUserList: [],
  415. popUserIds: [],
  416. backVisible: false,
  417. backLoading: true,
  418. backButtonDisabled: true,
  419. taskNodeList: [],
  420. selectedBackNodeName: '',
  421. selectedBackNodeIndex: 0,
  422. showBackNodePicker: false,
  423. nickName: {},
  424. nodeCode: '',
  425. buttonObj: {
  426. pop: false,
  427. trust: false,
  428. transfer: false,
  429. addSign: false,
  430. subSign: false,
  431. termination: false,
  432. back: false,
  433. copy: false,
  434. },
  435. nestNodeList: [],
  436. task: {
  437. id: undefined,
  438. createTime: undefined,
  439. updateTime: undefined,
  440. tenantId: undefined,
  441. definitionId: undefined,
  442. instanceId: undefined,
  443. flowName: undefined,
  444. businessId: undefined,
  445. nodeCode: undefined,
  446. nodeName: undefined,
  447. flowCode: undefined,
  448. flowStatus: undefined,
  449. formCustom: undefined,
  450. formPath: undefined,
  451. nodeType: undefined,
  452. nodeRatio: undefined,
  453. applyNode: false,
  454. buttonList: [],
  455. },
  456. dialog: {
  457. visible: false,
  458. title: '提示',
  459. },
  460. deleteSignatureVisible: false,
  461. form: {
  462. taskId: undefined,
  463. message: undefined,
  464. assigneeMap: {},
  465. variables: {},
  466. messageType: ['1'],
  467. flowCopyList: [],
  468. fileId: undefined,
  469. },
  470. backForm: {
  471. taskId: undefined,
  472. nodeCode: undefined,
  473. message: undefined,
  474. variables: {},
  475. messageType: ['1'],
  476. fileId: undefined,
  477. },
  478. };
  479. },
  480. methods: {
  481. // 打开弹窗
  482. async openDialog(id) {
  483. this.selectCopyUserIds = undefined;
  484. this.selectCopyUserList = [];
  485. this.form.fileId = undefined;
  486. this.taskId = id;
  487. this.form.message = undefined;
  488. this.visible = true;
  489. this.loading = true;
  490. this.buttonDisabled = true;
  491. try {
  492. const response = await request({
  493. url: `/workflow/task/getTask/${this.taskId}`,
  494. method: 'GET',
  495. });
  496. if (response.code === 200) {
  497. this.task = response.data;
  498. this.buttonObj = {};
  499. this.task.buttonList.forEach((e) => {
  500. this.buttonObj[e.code] = e.show;
  501. });
  502. this.buttonDisabled = false;
  503. const data = {
  504. taskId: this.taskId,
  505. variables: this.taskVariables,
  506. };
  507. const nextData = await request({
  508. url: '/workflow/task/getNextNodeList',
  509. method: 'POST',
  510. data: data,
  511. });
  512. if (nextData.code === 200) {
  513. this.nestNodeList = nextData.data;
  514. }
  515. }
  516. } catch (error) {
  517. console.error('获取任务信息失败:', error);
  518. uni.showToast({
  519. title: '获取任务信息失败',
  520. icon: 'none',
  521. });
  522. } finally {
  523. this.loading = false;
  524. }
  525. },
  526. // 关闭弹窗
  527. cancel() {
  528. this.visible = false;
  529. this.buttonDisabled = false;
  530. this.nickName = {};
  531. this.form.assigneeMap = {};
  532. this.$emit('cancelCallback');
  533. },
  534. // 消息类型变化
  535. handleMessageTypeChange(values) {
  536. // 确保站内信始终选中
  537. if (!values.includes('1')) {
  538. values.push('1');
  539. }
  540. this.form.messageType = values;
  541. },
  542. // 办理流程
  543. async handleCompleteTask() {
  544. this.form.taskId = this.taskId;
  545. this.form.taskVariables = this.taskVariables;
  546. let verify = false;
  547. if (this.buttonObj.pop && this.nestNodeList && this.nestNodeList.length > 0) {
  548. this.nestNodeList.forEach((e) => {
  549. if (
  550. Object.keys(this.form.assigneeMap).length === 0 ||
  551. this.form.assigneeMap[e.nodeCode] === '' ||
  552. this.form.assigneeMap[e.nodeCode] === null ||
  553. this.form.assigneeMap[e.nodeCode] === undefined
  554. ) {
  555. verify = true;
  556. }
  557. });
  558. if (verify) {
  559. uni.showToast({
  560. title: '请选择审批人!',
  561. icon: 'none',
  562. });
  563. return false;
  564. }
  565. } else {
  566. this.form.assigneeMap = {};
  567. }
  568. if (this.selectCopyUserList && this.selectCopyUserList.length > 0) {
  569. const flowCopyList = [];
  570. this.selectCopyUserList.forEach((e) => {
  571. const copyUser = {
  572. userId: e.userId,
  573. userName: e.nickName,
  574. };
  575. flowCopyList.push(copyUser);
  576. });
  577. this.form.flowCopyList = flowCopyList;
  578. }
  579. try {
  580. const result = await this.showConfirm('是否确认提交?');
  581. if (result.confirm) {
  582. this.loading = true;
  583. this.buttonDisabled = true;
  584. console.log('longwu---form', this.form);
  585. const response = await request({
  586. url: '/workflow/task/completeTask',
  587. method: 'POST',
  588. data: this.form,
  589. });
  590. if (response.code === 200) {
  591. this.visible = false;
  592. this.$emit('submit-callback');
  593. uni.showToast({
  594. title: '操作成功',
  595. icon: 'success',
  596. });
  597. } else {
  598. uni.showToast({
  599. title: response.msg || '操作失败',
  600. icon: 'none',
  601. });
  602. }
  603. }
  604. } catch (error) {
  605. console.error('提交失败:', error);
  606. uni.showToast({
  607. title: '操作失败',
  608. icon: 'none',
  609. });
  610. } finally {
  611. this.loading = false;
  612. this.buttonDisabled = false;
  613. }
  614. },
  615. // 退回弹窗打开
  616. async handleBackProcessOpen() {
  617. this.backForm = {};
  618. this.backForm.messageType = ['1'];
  619. this.backVisible = true;
  620. this.backLoading = true;
  621. this.backButtonDisabled = true;
  622. try {
  623. const response = await request({
  624. url: `/workflow/task/getBackTaskNode/${this.task.definitionId}/${this.task.nodeCode}`,
  625. method: 'GET',
  626. });
  627. if (response.code === 200) {
  628. this.taskNodeList = response.data;
  629. this.backLoading = false;
  630. this.backButtonDisabled = false;
  631. if (this.taskNodeList.length > 0) {
  632. this.selectedBackNodeIndex = 0;
  633. this.backForm.nodeCode = this.taskNodeList[0].nodeCode;
  634. this.selectedBackNodeName = this.taskNodeList[0].nodeName;
  635. }
  636. }
  637. } catch (error) {
  638. console.error('获取退回节点失败:', error);
  639. uni.showToast({
  640. title: '获取退回节点失败',
  641. icon: 'none',
  642. });
  643. }
  644. },
  645. // 确认选择驳回节点
  646. onBackNodeConfirm(e) {
  647. const { indexs, value } = e;
  648. this.selectedBackNodeIndex = indexs[0];
  649. this.backForm.nodeCode = value[0].nodeCode;
  650. this.selectedBackNodeName = value[0].nodeName;
  651. this.showBackNodePicker = false;
  652. },
  653. // 取消选择驳回节点
  654. onBackNodeClose() {
  655. this.showBackNodePicker = false;
  656. },
  657. // 关闭退回弹窗
  658. closeBackDialog() {
  659. this.backVisible = false;
  660. this.showBackNodePicker = false;
  661. this.selectedBackNodeIndex = 0;
  662. this.selectedBackNodeName = '';
  663. },
  664. // 退回流程
  665. async handleBackProcess() {
  666. this.backForm.taskId = this.taskId;
  667. try {
  668. const result = await this.showConfirm('是否确认驳回?');
  669. if (result.confirm) {
  670. this.loading = true;
  671. this.backLoading = true;
  672. this.backButtonDisabled = true;
  673. const response = await request({
  674. url: '/workflow/task/backProcess',
  675. method: 'POST',
  676. data: this.backForm,
  677. });
  678. if (response.code === 200) {
  679. this.visible = false;
  680. this.closeBackDialog();
  681. console.log('驳回成功');
  682. this.$emit('submit-callback');
  683. uni.showToast({
  684. title: '操作成功',
  685. icon: 'success',
  686. });
  687. } else {
  688. uni.showToast({
  689. title: response.msg || '操作失败',
  690. icon: 'none',
  691. });
  692. }
  693. }
  694. } catch (error) {
  695. console.error('退回失败:', error);
  696. uni.showToast({
  697. title: '操作失败',
  698. icon: 'none',
  699. });
  700. } finally {
  701. this.loading = false;
  702. this.backLoading = false;
  703. this.backButtonDisabled = false;
  704. }
  705. },
  706. // 打开抄送人员
  707. openUserSelectCopy() {
  708. this.$refs.userSelectCopyRef.open();
  709. },
  710. // 确认抄送人员
  711. userSelectCopyCallBack(data) {
  712. if (data && data.length > 0) {
  713. this.selectCopyUserList = data;
  714. this.selectCopyUserIds = this.selectCopyUserList
  715. .map((item) => item.userId)
  716. .join(',');
  717. }
  718. },
  719. // 删除抄送人员
  720. handleCopyCloseTag(user) {
  721. const userId = user.userId;
  722. const index = this.selectCopyUserList.findIndex((item) => item.userId === userId);
  723. this.selectCopyUserList.splice(index, 1);
  724. this.selectCopyUserIds = this.selectCopyUserList.map((item) => item.userId).join(',');
  725. },
  726. // 加签
  727. openMultiInstanceUser() {
  728. this.$refs.multiInstanceUserRef.open();
  729. },
  730. // 加签确认
  731. async addMultiInstanceUser(data) {
  732. if (data && data.length > 0) {
  733. const taskOperationBo = {
  734. userIds: data.map((e) => e.userId),
  735. taskId: this.taskId,
  736. message: this.form.message,
  737. };
  738. try {
  739. const result = await this.showConfirm('是否确认提交?');
  740. if (result.confirm) {
  741. this.loading = true;
  742. this.buttonDisabled = true;
  743. const response = await request({
  744. url: '/workflow/task/taskOperation/addSignature',
  745. method: 'POST',
  746. data: taskOperationBo,
  747. });
  748. if (response.code === 200) {
  749. this.visible = false;
  750. this.$emit('submit-callback');
  751. uni.showToast({
  752. title: '操作成功',
  753. icon: 'success',
  754. });
  755. } else {
  756. uni.showToast({
  757. title: response.msg || '操作失败',
  758. icon: 'none',
  759. });
  760. }
  761. }
  762. } catch (error) {
  763. console.error('加签失败:', error);
  764. uni.showToast({
  765. title: '操作失败',
  766. icon: 'none',
  767. });
  768. } finally {
  769. this.loading = false;
  770. this.buttonDisabled = false;
  771. }
  772. } else {
  773. uni.showToast({
  774. title: '请选择用户!',
  775. icon: 'none',
  776. });
  777. }
  778. },
  779. // 减签
  780. async deleteMultiInstanceUser(row) {
  781. try {
  782. const result = await this.showConfirm('是否确认提交?');
  783. if (result.confirm) {
  784. this.loading = true;
  785. this.buttonDisabled = true;
  786. const taskOperationBo = {
  787. userIds: [row.userId],
  788. taskId: this.taskId,
  789. message: this.form.message,
  790. };
  791. const response = await request({
  792. url: '/workflow/task/taskOperation/reductionSignature',
  793. method: 'POST',
  794. data: taskOperationBo,
  795. });
  796. if (response.code === 200) {
  797. this.visible = false;
  798. this.deleteSignatureVisible = false;
  799. this.$emit('submit-callback');
  800. uni.showToast({
  801. title: '操作成功',
  802. icon: 'success',
  803. });
  804. } else {
  805. uni.showToast({
  806. title: response.msg || '操作失败',
  807. icon: 'none',
  808. });
  809. }
  810. }
  811. } catch (error) {
  812. console.error('减签失败:', error);
  813. uni.showToast({
  814. title: '操作失败',
  815. icon: 'none',
  816. });
  817. } finally {
  818. this.loading = false;
  819. this.buttonDisabled = false;
  820. }
  821. },
  822. // 打开转办
  823. openTransferTask() {
  824. this.$refs.transferTaskRef.open();
  825. },
  826. // 转办
  827. async handleTransferTask(data) {
  828. if (data && data.length > 0) {
  829. const taskOperationBo = {
  830. userId: data[0].userId,
  831. taskId: this.taskId,
  832. message: this.form.message,
  833. };
  834. try {
  835. const result = await this.showConfirm('是否确认提交?');
  836. if (result.confirm) {
  837. this.loading = true;
  838. this.buttonDisabled = true;
  839. const response = await request({
  840. url: '/workflow/task/taskOperation/transferTask',
  841. method: 'POST',
  842. data: taskOperationBo,
  843. });
  844. if (response.code === 200) {
  845. this.visible = false;
  846. this.$emit('submit-callback');
  847. uni.showToast({
  848. title: '操作成功',
  849. icon: 'success',
  850. });
  851. } else {
  852. uni.showToast({
  853. title: response.msg || '操作失败',
  854. icon: 'none',
  855. });
  856. }
  857. }
  858. } catch (error) {
  859. console.error('转办失败:', error);
  860. uni.showToast({
  861. title: '操作失败',
  862. icon: 'none',
  863. });
  864. } finally {
  865. this.loading = false;
  866. this.buttonDisabled = false;
  867. }
  868. } else {
  869. uni.showToast({
  870. title: '请选择用户!',
  871. icon: 'none',
  872. });
  873. }
  874. },
  875. // 打开委托
  876. openDelegateTask() {
  877. this.$refs.delegateTaskRef.open();
  878. },
  879. // 委托
  880. async handleDelegateTask(data) {
  881. if (data && data.length > 0) {
  882. const taskOperationBo = {
  883. userId: data[0].userId,
  884. taskId: this.taskId,
  885. message: this.form.message,
  886. };
  887. try {
  888. const result = await this.showConfirm('是否确认提交?');
  889. if (result.confirm) {
  890. this.loading = true;
  891. this.buttonDisabled = true;
  892. const response = await request({
  893. url: '/workflow/task/taskOperation/delegateTask',
  894. method: 'POST',
  895. data: taskOperationBo,
  896. });
  897. if (response.code === 200) {
  898. this.visible = false;
  899. this.$emit('submit-callback');
  900. uni.showToast({
  901. title: '操作成功',
  902. icon: 'success',
  903. });
  904. } else {
  905. uni.showToast({
  906. title: response.msg || '操作失败',
  907. icon: 'none',
  908. });
  909. }
  910. }
  911. } catch (error) {
  912. console.error('委托失败:', error);
  913. uni.showToast({
  914. title: '操作失败',
  915. icon: 'none',
  916. });
  917. } finally {
  918. this.loading = false;
  919. this.buttonDisabled = false;
  920. }
  921. } else {
  922. uni.showToast({
  923. title: '请选择用户!',
  924. icon: 'none',
  925. });
  926. }
  927. },
  928. // 终止任务
  929. async handleTerminationTask() {
  930. const params = {
  931. taskId: this.taskId,
  932. comment: this.form.message,
  933. };
  934. try {
  935. const result = await this.showConfirm('是否确认终止?');
  936. if (result.confirm) {
  937. this.loading = true;
  938. this.buttonDisabled = true;
  939. const response = await request({
  940. url: '/workflow/task/terminationTask',
  941. method: 'POST',
  942. data: params,
  943. });
  944. if (response.code === 200) {
  945. this.visible = false;
  946. this.$emit('submit-callback');
  947. uni.showToast({
  948. title: '操作成功',
  949. icon: 'success',
  950. });
  951. } else {
  952. uni.showToast({
  953. title: response.msg || '操作失败',
  954. icon: 'none',
  955. });
  956. }
  957. }
  958. } catch (error) {
  959. console.error('终止失败:', error);
  960. uni.showToast({
  961. title: '操作失败',
  962. icon: 'none',
  963. });
  964. } finally {
  965. this.loading = false;
  966. this.buttonDisabled = false;
  967. }
  968. },
  969. // 获取减签用户
  970. async handleTaskUser() {
  971. try {
  972. const response = await request({
  973. url: `/workflow/task/currentTaskAllUser/${this.taskId}`,
  974. method: 'GET',
  975. });
  976. if (response.code === 200) {
  977. this.deleteUserList = response.data;
  978. if (this.deleteUserList && this.deleteUserList.length > 0) {
  979. this.deleteUserList.forEach((e) => {
  980. e.nodeName = this.task.nodeName;
  981. });
  982. }
  983. this.deleteSignatureVisible = true;
  984. }
  985. } catch (error) {
  986. console.error('获取减签用户失败:', error);
  987. uni.showToast({
  988. title: '获取减签用户失败',
  989. icon: 'none',
  990. });
  991. }
  992. },
  993. // 选择人员
  994. choosePeople(data) {
  995. if (!data.permissionFlag) {
  996. uni.showToast({
  997. title: '没有可选择的人员,请联系管理员!',
  998. icon: 'none',
  999. });
  1000. return;
  1001. }
  1002. this.popUserIds = data.permissionFlag;
  1003. this.nodeCode = data.nodeCode;
  1004. this.$refs.porUserRef.open();
  1005. },
  1006. // 确认选择人员
  1007. handlePopUser(userList) {
  1008. const userIds = userList.map((item) => item.userId);
  1009. const nickNames = userList.map((item) => item.nickName);
  1010. this.form.assigneeMap[this.nodeCode] = userIds.join(',');
  1011. this.nickName[this.nodeCode] = nickNames.join(',');
  1012. },
  1013. // 显示确认对话框
  1014. showConfirm(content) {
  1015. return new Promise((resolve) => {
  1016. // 临时隐藏所有弹窗,避免层级冲突
  1017. const originalVisible = this.visible;
  1018. const originalBackVisible = this.backVisible;
  1019. const originalDeleteSignatureVisible = this.deleteSignatureVisible;
  1020. this.visible = false;
  1021. this.backVisible = false;
  1022. this.deleteSignatureVisible = false;
  1023. uni.showModal({
  1024. title: '提示',
  1025. content,
  1026. success: (res) => {
  1027. // 恢复所有弹窗显示状态
  1028. this.visible = originalVisible;
  1029. this.backVisible = originalBackVisible;
  1030. this.deleteSignatureVisible = originalDeleteSignatureVisible;
  1031. resolve(res);
  1032. },
  1033. fail: () => {
  1034. // 恢复所有弹窗显示状态
  1035. this.visible = originalVisible;
  1036. this.backVisible = originalBackVisible;
  1037. this.deleteSignatureVisible = originalDeleteSignatureVisible;
  1038. resolve({ confirm: false });
  1039. },
  1040. });
  1041. });
  1042. },
  1043. // 获取抄送用户标签类型
  1044. getCopyUserType() {
  1045. return 'primary'; // 抄送用户使用主要样式
  1046. },
  1047. },
  1048. };
  1049. </script>
  1050. <style scoped>
  1051. .submit-verify-container {
  1052. height: 100%;
  1053. display: flex;
  1054. flex-direction: column;
  1055. background: #fff;
  1056. border-radius: 12rpx;
  1057. }
  1058. .header {
  1059. display: flex;
  1060. justify-content: space-between;
  1061. align-items: center;
  1062. padding: 30rpx;
  1063. border-bottom: 1rpx solid #f0f0f0;
  1064. }
  1065. .title {
  1066. font-size: 32rpx;
  1067. font-weight: 600;
  1068. color: #333;
  1069. }
  1070. .content {
  1071. flex: 1;
  1072. padding: 30rpx;
  1073. width: 90% !important;
  1074. }
  1075. .form-item {
  1076. margin-bottom: 40rpx;
  1077. }
  1078. .label {
  1079. display: block;
  1080. font-size: 28rpx;
  1081. font-weight: 500;
  1082. color: #333;
  1083. margin-bottom: 16rpx;
  1084. }
  1085. .checkbox-row {
  1086. display: flex;
  1087. gap: 40rpx;
  1088. flex-wrap: wrap;
  1089. }
  1090. .copy-section {
  1091. width: 100%;
  1092. }
  1093. .add-copy-btn {
  1094. width: 200rpx;
  1095. height: 60rpx;
  1096. font-size: 24rpx;
  1097. margin-bottom: 20rpx;
  1098. }
  1099. .copy-users {
  1100. display: flex;
  1101. flex-wrap: wrap;
  1102. gap: 16rpx;
  1103. max-height: 200rpx;
  1104. overflow-y: auto;
  1105. padding: 8rpx;
  1106. border: 1rpx solid #e6e6e6;
  1107. border-radius: 8rpx;
  1108. background-color: #fafafa;
  1109. }
  1110. .copy-tag {
  1111. margin: 4rpx 8rpx 4rpx 0;
  1112. }
  1113. .next-approver-section {
  1114. width: 100%;
  1115. }
  1116. .approver-item {
  1117. margin-bottom: 20rpx;
  1118. }
  1119. .node-name {
  1120. display: block;
  1121. font-size: 24rpx;
  1122. color: #666;
  1123. margin-bottom: 8rpx;
  1124. }
  1125. .approver-select {
  1126. width: 100%;
  1127. }
  1128. .footer {
  1129. padding: 30rpx;
  1130. border-top: 1rpx solid #f0f0f0;
  1131. background: #fff;
  1132. }
  1133. .buttons-grid {
  1134. display: grid;
  1135. grid-template-columns: repeat(3, 1fr);
  1136. gap: 20rpx;
  1137. width: 100%;
  1138. direction: rtl;
  1139. }
  1140. .action-btn {
  1141. height: 80rpx;
  1142. border-radius: 40rpx;
  1143. font-size: 28rpx;
  1144. font-weight: 500;
  1145. min-width: 0;
  1146. direction: ltr;
  1147. }
  1148. .submit-btn {
  1149. background: linear-gradient(
  1150. 135deg,
  1151. rgb(0, 81, 217) 0%,
  1152. rgb(51, 122, 235) 50%,
  1153. rgb(102, 163, 255) 100%
  1154. ) !important;
  1155. color: #fff;
  1156. border: none;
  1157. }
  1158. .submit-btn::after {
  1159. border: none;
  1160. }
  1161. .submit-btn:not([disabled]):active {
  1162. background: linear-gradient(
  1163. 135deg,
  1164. rgb(0, 71, 197) 0%,
  1165. rgb(41, 112, 225) 50%,
  1166. rgb(92, 153, 245) 100%
  1167. ) !important;
  1168. }
  1169. .back-container,
  1170. .delete-signature-container {
  1171. height: 100%;
  1172. display: flex;
  1173. flex-direction: column;
  1174. background: #fff;
  1175. border-radius: 12rpx;
  1176. }
  1177. .confirm-btn {
  1178. background: linear-gradient(
  1179. 135deg,
  1180. rgb(0, 81, 217) 0%,
  1181. rgb(51, 122, 235) 50%,
  1182. rgb(102, 163, 255) 100%
  1183. ) !important;
  1184. color: #fff;
  1185. border: none;
  1186. }
  1187. .confirm-btn::after {
  1188. border: none;
  1189. }
  1190. .confirm-btn:not([disabled]):active {
  1191. background: linear-gradient(
  1192. 135deg,
  1193. rgb(0, 71, 197) 0%,
  1194. rgb(41, 112, 225) 50%,
  1195. rgb(92, 153, 245) 100%
  1196. ) !important;
  1197. }
  1198. .cancel-btn {
  1199. flex: 1;
  1200. height: 80rpx;
  1201. border-radius: 40rpx;
  1202. font-size: 28rpx;
  1203. font-weight: 500;
  1204. }
  1205. .empty-data {
  1206. display: flex;
  1207. justify-content: center;
  1208. align-items: center;
  1209. height: 300rpx;
  1210. }
  1211. .user-list {
  1212. width: 100%;
  1213. }
  1214. .user-item {
  1215. display: flex;
  1216. align-items: center;
  1217. justify-content: space-between;
  1218. padding: 24rpx 0;
  1219. border-bottom: 1rpx solid #f0f0f0;
  1220. }
  1221. .user-info {
  1222. flex: 1;
  1223. }
  1224. .task-name {
  1225. display: block;
  1226. font-size: 28rpx;
  1227. font-weight: 500;
  1228. color: #333;
  1229. margin-bottom: 8rpx;
  1230. }
  1231. .user-name {
  1232. font-size: 24rpx;
  1233. color: #666;
  1234. }
  1235. .delete-btn {
  1236. height: 60rpx;
  1237. padding: 0 24rpx;
  1238. font-size: 24rpx;
  1239. }
  1240. .picker-input {
  1241. display: flex;
  1242. align-items: center;
  1243. justify-content: space-between;
  1244. height: 80rpx;
  1245. padding: 0 24rpx;
  1246. background-color: #f8f9fa;
  1247. border: 1rpx solid #e6e6e6;
  1248. border-radius: 8rpx;
  1249. cursor: pointer;
  1250. }
  1251. .picker-input:active {
  1252. background-color: #f0f0f0;
  1253. }
  1254. .picker-text {
  1255. flex: 1;
  1256. font-size: 28rpx;
  1257. color: #333;
  1258. }
  1259. .picker-text:empty::before {
  1260. content: attr(placeholder);
  1261. color: #999;
  1262. }
  1263. .horizontal-item {
  1264. display: flex;
  1265. align-items: center;
  1266. gap: 20rpx;
  1267. }
  1268. .horizontal-label {
  1269. flex-shrink: 0;
  1270. }
  1271. .horizontal-control {
  1272. flex: 1;
  1273. width: 100%;
  1274. }
  1275. .horizontal-control .picker-input {
  1276. width: 80% !important;
  1277. }
  1278. </style>