ApprovalAction.vue 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  1. <template>
  2. <div
  3. :class="
  4. ((userAuthority || ownerApply) && is_end == 0 && share != 1) ||
  5. is_end == 0
  6. ? 'approvalAction'
  7. : null
  8. "
  9. class="approvalContainer"
  10. >
  11. <m-header v-if="share != 1" :appHide="true">
  12. <template #right>
  13. <van-icon
  14. @click="onShare"
  15. :name="iconShare"
  16. style="transform: translateY(5px)"
  17. size="22"
  18. ></van-icon>
  19. </template>
  20. </m-header>
  21. <!-- <van-notice-bar
  22. v-if="activeIndex !== nodeStepList.length && is_end===1"
  23. style="margin-bottom: .1rem"
  24. :text="alertMessage"
  25. :scrollable="false"
  26. /> -->
  27. <van-cell
  28. class="headerInfo"
  29. v-if="processStructureValue.workOrder.priority"
  30. >
  31. <template #title>
  32. <div class="titleSection" v-if="title">
  33. <span class="titleContent">{{ title }}</span>
  34. <template v-if="processStructureValue.workOrder.priority === 2">
  35. <van-tag
  36. size="small"
  37. style="background: #fff9f6; vertical-align: text-bottom"
  38. plain
  39. type="warning"
  40. >紧急</van-tag
  41. >
  42. </template>
  43. <template v-else-if="processStructureValue.workOrder.priority === 3">
  44. <van-tag
  45. size="small"
  46. style="background: #fff6f7; vertical-align: text-bottom"
  47. plain
  48. type="danger"
  49. >非常紧急</van-tag
  50. >
  51. </template>
  52. <template v-else-if="processStructureValue.workOrder.priority === 1">
  53. <van-tag
  54. size="small"
  55. style="background: #edfff6; vertical-align: text-bottom"
  56. plain
  57. type="success"
  58. >一般</van-tag
  59. >
  60. </template>
  61. </div>
  62. <template v-if="is_end == 0">
  63. <span v-if="userAuthority" class="waitStatus">等待我审批</span>
  64. <span v-else class="waitStatus">{{ principals }}</span>
  65. </template>
  66. <template v-else>
  67. <template v-if="is_cancel == 0">
  68. <span
  69. v-if="processStructureValue.workOrder.is_denied"
  70. class="waitStatus"
  71. >审批拒绝</span
  72. >
  73. <span v-else class="waitStatus">审批通过</span>
  74. <img
  75. v-if="processStructureValue.workOrder.is_denied"
  76. class="imgStatus"
  77. src="./images/icon_reject.png"
  78. />
  79. <img v-else class="imgStatus" src="./images/icon_resolve.png" />
  80. </template>
  81. <template v-else>
  82. <span class="waitStatus">已关闭</span>
  83. <img class="imgStatus" src="./images/icon_close_round.png" />
  84. </template>
  85. </template>
  86. </template>
  87. </van-cell>
  88. <div
  89. style="
  90. margin: 0 0.12rem 0.12rem;
  91. border-radius: 0.05rem;
  92. overflow: hidden;
  93. "
  94. >
  95. <template v-for="(tplItem, tplIndex) in processStructureValue.tpls">
  96. <form-modal
  97. style="padding-bottom: 0.12rem; background: #fff"
  98. :key="tplIndex"
  99. v-if="
  100. (currentNode.hideTpls !== undefined &&
  101. currentNode.hideTpls !== null &&
  102. currentNode.hideTpls.indexOf(tplItem.form_structure.id) !== -1) ||
  103. currentNode.writeTpls === undefined ||
  104. currentNode.writeTpls === null ||
  105. currentNode.writeTpls.indexOf(tplItem.form_structure.id) === -1
  106. "
  107. :formData="tplItem.form_structure"
  108. :value="tplItem.form_data"
  109. :preview="true"
  110. />
  111. </template>
  112. </div>
  113. <template v-if="userAuthority">
  114. <van-form
  115. ref="headForm"
  116. validate-first
  117. @submit="onSubmitHead"
  118. :scroll-to-error="true"
  119. :show-error="false"
  120. >
  121. <template v-for="(tplItem, tplIndex) in processStructureValue.tpls">
  122. <form-modal
  123. :key="tplIndex"
  124. v-if="
  125. currentNode.writeTpls !== undefined &&
  126. currentNode.writeTpls !== null &&
  127. currentNode.writeTpls.indexOf(tplItem.form_structure.id) > -1
  128. ? true
  129. : false
  130. "
  131. :ref="'generateForm-' + tplItem.id"
  132. :value="tplItem.form_data"
  133. :formData="tplItem.form_structure"
  134. />
  135. </template>
  136. </van-form>
  137. </template>
  138. <div class="step_section" v-if="circulationHistoryList.length > 0">
  139. <div class="step_title">流程</div>
  140. <van-steps
  141. v-if="
  142. currentNode.clazz !== undefined &&
  143. currentNode.clazz !== null &&
  144. currentNode.clazz !== ''
  145. "
  146. :active="activeIndex"
  147. direction="vertical"
  148. inactive-color="#D8D8D8"
  149. active-color="#D8D8D8"
  150. >
  151. <template v-for="(item, index) in circulationHistoryList">
  152. <van-step
  153. v-if="
  154. item.isHideNode === false ||
  155. item.isHideNode === undefined ||
  156. item.isHideNode == null ||
  157. item.id === processStructureValue.workOrder.current_state
  158. "
  159. :key="index"
  160. class="step"
  161. >
  162. <template #inactive-icon>
  163. <i class="inactive-icon"></i>
  164. </template>
  165. <template #active-icon>
  166. <van-icon :name="iconWait" />
  167. </template>
  168. <template #finish-icon>
  169. <template v-if="item.circulation == '转交'">
  170. <van-icon :name="iconSystemTransfer" />
  171. </template>
  172. <template v-else>
  173. <van-icon :name="iconReject" v-if="item.status == 0" />
  174. <van-icon :name="iconComplete" v-else />
  175. </template>
  176. </template>
  177. <div class="step-t">
  178. <p class="apply-state">{{ item.state || item.label }}</p>
  179. <p class="apply-time">
  180. {{
  181. item.create_time
  182. ? dayjs(item.create_time).format("MM-DD HH:mm")
  183. : null
  184. }}
  185. </p>
  186. </div>
  187. <!-- 判断是否有审核数据 -->
  188. <template v-if="!item.create_time">
  189. <p
  190. class="apply-status"
  191. v-if="item.assignUsers && item.assignUsers.length > 0"
  192. >
  193. <!-- 判断是否是自己审批 -->
  194. <template
  195. v-if="
  196. item.assignUsers[0].userId == userInfo.userId &&
  197. activeIndex == index
  198. "
  199. >
  200. 我(审批中)
  201. </template>
  202. <template v-else>
  203. <template v-if="item.isCounterSign">
  204. <span
  205. v-for="(au, aIndex) in item.assignUsers"
  206. :key="aIndex"
  207. >
  208. {{ au.username
  209. }}{{ aIndex < item.assignUsers.length - 1 ? "," : null }}
  210. </span>
  211. </template>
  212. <template v-else>
  213. {{ item.assignUsers[0].username }}
  214. </template>
  215. {{ activeIndex == index ? `(审批中)` : null }}
  216. <!-- {{ item.assignUsers[0].username }}{{ activeIndex == index ? `(审批中)` : null }} -->
  217. </template>
  218. </p>
  219. </template>
  220. <template v-else>
  221. <p class="apply-status" v-if="item.processor">
  222. {{ item.processor
  223. }}{{ item.circulation ? `(${item.circulation})` : null }}
  224. </p>
  225. <p
  226. class="remarks"
  227. v-if="item.remarks"
  228. v-html="dataModelFormatBr(item.remarks)"
  229. ></p>
  230. <!-- 判断是否有添加图片 -->
  231. <div
  232. class="imgUploader"
  233. v-if="
  234. item.fileUrl &&
  235. item.fileUrl.file &&
  236. item.fileUrl.image.length > 0
  237. "
  238. >
  239. <van-uploader
  240. v-model="item.fileUrl.image"
  241. disabled
  242. class="preview-uploader"
  243. :preview-size="40"
  244. :deletable="false"
  245. :max-count="item.fileUrl.image.length"
  246. ></van-uploader>
  247. </div>
  248. <!-- 判断是否有添加的文件 -->
  249. <div
  250. v-if="
  251. item.fileUrl &&
  252. item.fileUrl.file &&
  253. item.fileUrl.file.length > 0
  254. "
  255. >
  256. <FilePreview :dataModel="item.fileUrl.file" />
  257. </div>
  258. </template>
  259. <!-- 有抄送人并且,本节点已经审批完成了 -->
  260. <template
  261. v-if="
  262. item.cc_user && item.cc_user.length > 0 && activeIndex > index
  263. "
  264. >
  265. <!-- 已抄送1人 -->
  266. <div class="ccUsers" @click="onCCChange(item)">
  267. <span>已抄送{{ item.cc_user.length }}人</span>
  268. <van-icon
  269. v-show="!item.ccStatus"
  270. style="color: #cccccc"
  271. name="arrow-down"
  272. />
  273. <van-icon
  274. v-show="item.ccStatus"
  275. style="color: #cccccc"
  276. name="arrow-up"
  277. />
  278. </div>
  279. <div class="ccUserDetail" v-if="item.ccStatus">
  280. <span>{{ item.cc_user.join(",") }}</span>
  281. </div>
  282. </template>
  283. </van-step>
  284. </template>
  285. </van-steps>
  286. </div>
  287. <!-- 是自己审批 或者 是自己提交的记录 并且不是分享连接 -->
  288. <van-goods-action v-if="share != 1 && is_end == 0">
  289. <van-goods-action-icon
  290. v-if="ownerApply"
  291. :icon="iconDing"
  292. text="催办"
  293. @click="handleUrge"
  294. />
  295. <van-goods-action-icon
  296. v-if="ownerApply"
  297. :icon="iconClose"
  298. text="关闭"
  299. @click="handleUnity"
  300. />
  301. <van-goods-action-icon
  302. v-if="userAuthority"
  303. :icon="iconTransfer"
  304. text="转交"
  305. @click="exchange"
  306. />
  307. <!-- -->
  308. <van-goods-action-icon :icon="iconCommit" text="评论" @click="onCommit" />
  309. <template v-if="userAuthority">
  310. <van-goods-action-button
  311. v-for="(item, index) in btn_group"
  312. :key="index"
  313. :type="item.className"
  314. :disabled="submitDisabled"
  315. :text="item.labelShow"
  316. plain
  317. @click="onCheckSubmit(item)"
  318. />
  319. <!-- 拒绝按钮内置 -->
  320. <van-goods-action-button
  321. v-if="endNodeDetail.id"
  322. :disabled="submitDisabled"
  323. plain
  324. :text="endNodeDetail.label"
  325. type="danger"
  326. @click="onCheckSubmit(endNodeDetail)"
  327. />
  328. </template>
  329. </van-goods-action>
  330. <!-- 审批时填写意见 -->
  331. <van-popup
  332. v-model="showMarks"
  333. closeable
  334. class="markModel"
  335. @closed="
  336. () => {
  337. remarks = '';
  338. fileUrl = [];
  339. }
  340. "
  341. >
  342. <div class="popup-marks">
  343. <h2>确认{{ popupMarkTxt }}</h2>
  344. <van-field
  345. name="remarks"
  346. v-model="remarks"
  347. type="textarea"
  348. class="remarks-input"
  349. :border="false"
  350. row="4"
  351. :autosize="{ maxHeight: 300, minHeight: 180 }"
  352. maxlength="200"
  353. show-word-limit
  354. :placeholder="popupMarkPlaceholder || '请输入审批意见'"
  355. />
  356. <div style="padding: 10px 16px">
  357. <Upload :fileUrl="fileUrl" />
  358. </div>
  359. <van-button type="info" round @click="popupSubmit"
  360. >确认{{ popupMarkTxt }}</van-button
  361. >
  362. </div>
  363. </van-popup>
  364. <!-- 转交 -->
  365. <van-popup
  366. v-model="transferStatus"
  367. position="bottom"
  368. :style="{ height: '100%', width: '100%' }"
  369. >
  370. <m-header name="转交" :backUrl="backUrl2" />
  371. <transfer-modal
  372. v-if="transferStatus"
  373. :popupForm="popupForm"
  374. :columns="columns"
  375. :nodeList="nodeList"
  376. />
  377. </van-popup>
  378. </div>
  379. </template>
  380. <script>
  381. import MHeader from "@/components/header";
  382. import {
  383. processStructure,
  384. handleWorkOrder,
  385. sysUserList,
  386. urgeWorkOrder,
  387. getInfo,
  388. unityWorkOrder,
  389. asyncPlayLog,
  390. orderComment,
  391. } from "./api";
  392. import FormModal from "./modal/formModal";
  393. import TransferModal from "./modal/transferModal";
  394. import Upload from "./modal/upload";
  395. import copy from "copy-to-clipboard";
  396. import dayjs from "dayjs";
  397. import FilePreview from "./modal/filePreview.vue";
  398. export default {
  399. name: "approvalAction",
  400. components: { MHeader, FormModal, TransferModal, Upload, FilePreview },
  401. data() {
  402. let query = this.$route.query;
  403. return {
  404. iconComplete: require("./images/system-complete-default.png"),
  405. iconWait: require("./images/system-wait.png"),
  406. iconReject: require("./images/system-reject.png"),
  407. iconSystemTransfer: require("./images/system-transfer.png"),
  408. iconTransfer: require("./images/icon_transfer.png"),
  409. iconCommit: require("./images/icon_commit.png"),
  410. iconDing: require("./images/icon_ding.png"),
  411. iconClose: require("./images/icon_close.png"),
  412. iconShare: require("./images/icon_share.png"),
  413. showMarks: false,
  414. popupMarkTxt: null,
  415. popupMarkPlaceholder: null,
  416. showMarksType: null, // 操作类型
  417. backUrl2: {
  418. callBack: () => {
  419. this.transferStatus = false;
  420. },
  421. },
  422. dayjs,
  423. active: 0,
  424. // classify: query.classify,
  425. processId: query.processId,
  426. workOrderId: query.workOrderId,
  427. principals: null,
  428. share: query.share,
  429. title: null,
  430. submitDisabled: false,
  431. ruleForm: {
  432. title: "",
  433. priority: 1,
  434. process: "",
  435. classify: "",
  436. state: [],
  437. source: "",
  438. source_state: "",
  439. process_method: "",
  440. tpls: {
  441. form_structure: [],
  442. form_data: [],
  443. },
  444. tasks: [],
  445. },
  446. currentNode: {
  447. hideTpls: null,
  448. writeTpls: null,
  449. },
  450. isActiveProcessing: false,
  451. tpls: [],
  452. remarks: "", // 备注信息
  453. fileUrl: [], // 文件链接
  454. alertMessage: "",
  455. nodeStepList: [],
  456. circulationHistoryList: [],
  457. endNodeDetail: {}, // 结束结节信息
  458. activeIndex: 0,
  459. processStructureValue: {
  460. workOrder: { title: "" },
  461. },
  462. selectItem: null,
  463. btn_group: [],
  464. transferStatus: false,
  465. columns: [],
  466. popupForm: {
  467. work_order_id: "",
  468. node_id: "",
  469. user_id: "",
  470. remarks: "",
  471. fileUrl: [],
  472. },
  473. userInfo: {},
  474. userAuthority: false, // 是否是自己审批
  475. ownerApply: false, // 是否是自己提交的申请
  476. is_end: 0, // 是否结束
  477. is_cancel: 0, // 是否取消
  478. };
  479. },
  480. async mounted() {
  481. await this.getProcessNodeList();
  482. if (this.share != 1) {
  483. await this.getUserInfo();
  484. }
  485. },
  486. methods: {
  487. onShare() {
  488. const url = window.location.href + "&share=1";
  489. copy(url);
  490. copy(url);
  491. this.$toast("分享连接复制成功");
  492. },
  493. // 获取对应元素的值
  494. getFormDataDetail(formData, model) {
  495. let modelStatus = {
  496. status: false,
  497. value: null,
  498. };
  499. for (let data in formData) {
  500. if (typeof formData[data] == "object") {
  501. // 没有子表单里面有子表单
  502. for (let child in formData[data]) {
  503. if (child == model) {
  504. modelStatus = {
  505. status: true,
  506. model: child,
  507. value: formData[data][child]
  508. ? formData[data][child].split(",")
  509. : [],
  510. };
  511. }
  512. }
  513. } else {
  514. if (data == model) {
  515. modelStatus = {
  516. status: true,
  517. model: data,
  518. value: formData[data] ? formData[data].split(",") : [],
  519. };
  520. }
  521. }
  522. }
  523. return modelStatus;
  524. },
  525. getSelectValueObject(tpls, type = "value", tplValues = []) {
  526. const tempData = tpls || [];
  527. let selectList = [];
  528. tempData.forEach((temp, index) => {
  529. let tempList = temp.form_structure.list || [];
  530. let tempSelectList = tplValues[index] || [];
  531. let listArray = [];
  532. tempList.forEach((list) => {
  533. if (list.type == "select") {
  534. if (type == "value") {
  535. const result = this.getFormDataDetail(temp.form_data, list.model);
  536. if (result.status) {
  537. listArray.push(result);
  538. }
  539. } else {
  540. let selectOptions = [];
  541. let selectValue = [];
  542. tempSelectList.forEach((tsl) => {
  543. if (tsl.model == list.model) {
  544. selectOptions = list.options?.options || [];
  545. selectValue = tsl.value || [];
  546. }
  547. });
  548. selectOptions.forEach((so) => {
  549. if (selectValue.includes(so.value)) {
  550. let tempRo = so.relationOptions || [];
  551. listArray.push(...tempRo);
  552. }
  553. });
  554. }
  555. }
  556. if (list.type == "subform") {
  557. let childList = list.columns || [];
  558. childList.forEach((child) => {
  559. let childList = child.list || [];
  560. childList.forEach((c) => {
  561. if (c.type == "select") {
  562. if (type == "value") {
  563. const originObj = JSON.parse(JSON.stringify(c));
  564. const result = this.getFormDataDetail(
  565. temp.form_data,
  566. originObj.model
  567. );
  568. if (result.status) {
  569. listArray.push(result);
  570. }
  571. } else {
  572. let selectOptions = [];
  573. let selectValue = [];
  574. tempSelectList.forEach((tsl) => {
  575. if (tsl.model == c.model) {
  576. selectOptions = c.options?.options || [];
  577. selectValue = tsl.value || [];
  578. }
  579. });
  580. selectOptions.forEach((so) => {
  581. if (selectValue.includes(so.value)) {
  582. let tempRo = so.relationOptions || [];
  583. listArray.push(...tempRo);
  584. }
  585. });
  586. }
  587. }
  588. });
  589. });
  590. }
  591. });
  592. selectList.push(listArray);
  593. });
  594. return selectList;
  595. },
  596. async getProcessNodeList() {
  597. try {
  598. let params = {
  599. processId: this.processId,
  600. workOrderId: this.workOrderId,
  601. };
  602. const userId = sessionStorage.getItem("userId");
  603. if (this.share != 1 && userId) {
  604. params.userId = userId;
  605. }
  606. let res = await processStructure(params);
  607. this.isActiveProcessing = false;
  608. let tempData = res.data.tpls;
  609. // 获取对应模板中,下拉框的key, value
  610. let selectList = this.getSelectValueObject(tempData);
  611. // 获取对应模板中,需要隐藏的字段
  612. let hiddenFormList = this.getSelectValueObject(
  613. tempData,
  614. "hiddenForm",
  615. selectList
  616. );
  617. tempData.forEach((temp, index) => {
  618. let tempList = temp.form_structure.list || [];
  619. tempList.forEach((item) => {
  620. if (hiddenFormList[index].length > 0) {
  621. if (item.type != "text" && !item.options.relationStatus) {
  622. item.hidden = true;
  623. } else {
  624. item.hidden = false;
  625. }
  626. // item.hidden = false
  627. if (hiddenFormList[index].includes(item.model)) {
  628. item.hidden = false;
  629. }
  630. } else {
  631. item.hidden = false;
  632. }
  633. // 子表单
  634. if (item.type == "subform") {
  635. let childList = item.columns || [];
  636. let subFormStatus = true;
  637. childList.forEach((child) => {
  638. let childList = child.list || [];
  639. childList.forEach((c) => {
  640. if (hiddenFormList[index].length > 0) {
  641. if (c.type != "text" && !c.options.relationStatus) {
  642. c.hidden = true;
  643. } else {
  644. c.hidden = false;
  645. subFormStatus = false;
  646. }
  647. if (hiddenFormList[index].includes(c.model)) {
  648. c.hidden = false;
  649. subFormStatus = false;
  650. }
  651. } else {
  652. c.hidden = false;
  653. subFormStatus = false;
  654. }
  655. });
  656. });
  657. item.hidden = subFormStatus;
  658. }
  659. });
  660. });
  661. this.processStructureValue = res.data;
  662. this.circulationHistoryList =
  663. this.processStructureValue.circulationHistory;
  664. this.userAuthority = this.processStructureValue.userAuthority;
  665. this.is_end = res.data.workOrder.is_end;
  666. this.is_cancel = res.data.workOrder.is_cancel;
  667. if (res.data.workOrder.title) {
  668. this.title = res.data.workOrder.title;
  669. }
  670. // 获取当前展示节点列表
  671. this.nodeStepList = [];
  672. this.tempNodeStepList = [];
  673. const nodes = this.processStructureValue.nodes;
  674. this.principals = "处理中";
  675. for (var i = 0; i < nodes.length; i++) {
  676. if (
  677. nodes[i].id === this.processStructureValue.workOrder.current_state
  678. ) {
  679. // 当前节点
  680. this.nodeStepList.push(nodes[i]);
  681. this.activeIndex = this.nodeStepList.length - 1;
  682. if (i + 1 === nodes.length) {
  683. this.activeIndex = this.nodeStepList.length;
  684. }
  685. this.currentNode = nodes[i];
  686. // 处理是认谁在处理,已处理完成则显示处理中
  687. const assignUsers = nodes[i].assignUsers;
  688. if (assignUsers && assignUsers.length > 0) {
  689. this.principals = assignUsers[0].username + "处理中";
  690. }
  691. } else if (!nodes[i].isHideNode) {
  692. // 非隐藏节点
  693. this.nodeStepList.push(nodes[i]);
  694. }
  695. // 判断节点里面是否有结束节点,而且一个流程里面只能有一个结束结点,
  696. if (nodes[i].clazz == "end") {
  697. this.endNodeDetail = JSON.parse(JSON.stringify(nodes[i]));
  698. this.endNodeDetail.target = nodes[i].id;
  699. this.endNodeDetail.flowProperties = 0; // 拒绝属性
  700. this.endNodeDetail.label = "拒绝";
  701. }
  702. }
  703. this.circulationHistoryList.reverse();
  704. // 如果审批流程没有结束则,流程和历史记录合并显示;结束了,就只显示历史记录
  705. if (!this.is_end) {
  706. this.circulationHistoryList.forEach((cir) => {
  707. this.nodeStepList.forEach((node) => {
  708. if (cir.source == node.id) {
  709. cir.label = node.label;
  710. cir.assignType = node.assignType;
  711. cir.assignValue = node.assignValue;
  712. cir.assignUsers = node.assignUsers;
  713. cir.id = node.id;
  714. }
  715. });
  716. });
  717. let tempNodes = [];
  718. this.nodeStepList.forEach((node) => {
  719. let count = 0;
  720. this.circulationHistoryList.forEach((cir) => {
  721. if (node.id === cir.source) {
  722. count += 1;
  723. }
  724. });
  725. if (count <= 0) {
  726. tempNodes.push(node);
  727. }
  728. });
  729. this.circulationHistoryList.push(...tempNodes);
  730. this.circulationHistoryList.forEach((cir, index) => {
  731. if (cir.id == this.processStructureValue.workOrder.current_state) {
  732. this.activeIndex = index;
  733. if (index + 1 == this.circulationHistoryList.length) {
  734. this.activeIndex = this.circulationHistoryList.length;
  735. }
  736. }
  737. });
  738. } else {
  739. this.activeIndex = this.circulationHistoryList.length;
  740. }
  741. // 添加抄送状态
  742. this.circulationHistoryList.forEach((res) => {
  743. res.ccStatus = true;
  744. const file = res.file_url ? JSON.parse(res.file_url) : [];
  745. const tempFile = {
  746. image: [],
  747. file: [],
  748. };
  749. file.forEach((item) => {
  750. if (item.type == "image") {
  751. tempFile.image.push(item);
  752. } else if (item.type == "file") {
  753. tempFile.file.push(item);
  754. }
  755. });
  756. res.fileUrl = tempFile;
  757. // console.log(tempFile, res);
  758. });
  759. // 如果回退到初始节点则可编辑。
  760. if (this.activeIndex === 0 && this.currentNode.clazz === "start") {
  761. this.currentNode.writeTpls = [];
  762. for (var tplTmp of this.processStructureValue.tpls) {
  763. this.currentNode.writeTpls.push(tplTmp.form_structure.id);
  764. }
  765. }
  766. // 判断是否需要主动处理
  767. for (var stateValue of this.processStructureValue.workOrder.state) {
  768. if (
  769. this.processStructureValue.workOrder.current_state ===
  770. stateValue.id &&
  771. stateValue.processor.length > 1
  772. ) {
  773. this.isActiveProcessing = true;
  774. break;
  775. }
  776. }
  777. let psv = res.data?.edges || [];
  778. let btn_group = [];
  779. psv.forEach((item) => {
  780. // console.log(this.currentNode)
  781. // 过滤其它类型的操作
  782. if (
  783. this.is_end === 0 &&
  784. item.source === this.currentNode.id &&
  785. item.flowProperties == 1
  786. ) {
  787. if (item.flowProperties == 1) {
  788. item.className = "info";
  789. item.labelShow = "同意";
  790. } else if (item.flowProperties == 0) {
  791. item.className = "danger";
  792. } else if (item.flowProperties == 2) {
  793. item.className = "info";
  794. }
  795. btn_group.push(item);
  796. } else {
  797. item.className = "info";
  798. }
  799. });
  800. this.btn_group = btn_group;
  801. // console.log(this.endNodeDetail, btn_group , 'btn_group')
  802. // console.log(this.circulationHistoryList);
  803. this.getAlertMessage();
  804. } catch {
  805. //
  806. }
  807. },
  808. relationFormChange(value) {
  809. let temp = value || [];
  810. this.formData.list?.forEach((item) => {
  811. item.hidden = false;
  812. if (temp.includes(item.model)) {
  813. item.hidden = true;
  814. }
  815. // 子表单
  816. if (item.type == "subform") {
  817. let childList = item.columns || [];
  818. childList.forEach((child) => {
  819. if (child.list?.length > 0) {
  820. child.list.forEach((c) => {
  821. c.hidden = false;
  822. if (temp.includes(c.originModel)) {
  823. c.hidden = true;
  824. }
  825. });
  826. }
  827. });
  828. // 重置数据
  829. let subForm = this.$refs.subform;
  830. subForm.forEach((item) => {
  831. item.reSetFormData();
  832. });
  833. }
  834. });
  835. },
  836. async getUserInfo() {
  837. // 获取用户信息
  838. try {
  839. let user = await getInfo();
  840. this.userInfo = user.data;
  841. this.ownerApply =
  842. this.processStructureValue.workOrder.creator == this.userInfo.userId
  843. ? true
  844. : false;
  845. } catch {
  846. //
  847. }
  848. },
  849. // 获取提示消息
  850. getAlertMessage() {
  851. if (this.is_end === 1) {
  852. this.alertMessage = "当前工单已结束";
  853. }
  854. },
  855. // 转交
  856. async exchange() {
  857. let workOrder = this.processStructureValue.workOrder;
  858. this.popupForm.work_order_id = workOrder.id;
  859. this.nodeList = workOrder.state || [];
  860. this.nodeList.forEach((item) => {
  861. item.text = item.label;
  862. });
  863. if (this.nodeList.length === 1) {
  864. this.popupForm.node_id = this.nodeList[0].id;
  865. }
  866. try {
  867. // 获取所有用户
  868. const res = await sysUserList({ pageSize: 9999 });
  869. const users = res.data.list || [];
  870. users.forEach((item) => {
  871. this.columns.push({
  872. avatar: item.avatar,
  873. text: item.nickName,
  874. phone: item.phone,
  875. id: item.userId,
  876. });
  877. });
  878. } catch {
  879. //
  880. }
  881. this.transferStatus = true;
  882. },
  883. // 评论
  884. onCommit() {
  885. this.showMarksType = "commit";
  886. this.showMarks = true;
  887. this.popupMarkTxt = "评论";
  888. this.popupMarkPlaceholder = "请输入评论内容";
  889. },
  890. // 催办
  891. handleUrge() {
  892. this.$dialog
  893. .confirm({
  894. title: "催办",
  895. allowHtml: true,
  896. message:
  897. '<span style="font-size:15px ">对此工单处理人进行催办通知提醒, 是否继续?</span><br><span style="color: #c33; font-size: 10px">注意:十分钟内只能催办一次。</span>',
  898. confirmButtonColor: "#01C1B5",
  899. })
  900. .then(async () => {
  901. try {
  902. let workOrder = this.processStructureValue.workOrder;
  903. let workOrderId = workOrder.id;
  904. await urgeWorkOrder({ workOrderId });
  905. this.$toast("已进行催办通知");
  906. } catch {
  907. //
  908. }
  909. });
  910. },
  911. handleUnity() {
  912. // 关闭
  913. this.$dialog
  914. .confirm({
  915. title: "提示",
  916. allowHtml: true,
  917. message:
  918. '<span style="font-size:15px ">此操作将会关闭该工单, 是否继续?</span>',
  919. confirmButtonColor: "#01C1B5",
  920. })
  921. .then(async () => {
  922. try {
  923. let workOrder = this.processStructureValue.workOrder;
  924. let work_oroder_id = workOrder.id;
  925. await unityWorkOrder({ work_oroder_id });
  926. this.$toast("已关闭工单");
  927. setTimeout(() => {
  928. this.$router.push({
  929. path: "/myApproval",
  930. query: {
  931. classify: 2,
  932. },
  933. });
  934. }, 500);
  935. } catch {
  936. //
  937. }
  938. });
  939. },
  940. onCheckSubmit(item) {
  941. this.selectItem = item;
  942. this.showMarksType = "check";
  943. this.popupMarkPlaceholder = "";
  944. this.$refs.headForm.validate().then(() => {
  945. this.showMarks = true;
  946. this.popupMarkTxt = item.label;
  947. });
  948. },
  949. async popupSubmit() {
  950. // 判断是否评论
  951. console.log("111", this.showMarksType);
  952. if (this.showMarksType === "commit") {
  953. if (!this.remarks) {
  954. return this.$toast("请输入评论内容");
  955. }
  956. try {
  957. await orderComment({
  958. workOrderId: parseInt(this.workOrderId),
  959. remarks: this.remarks,
  960. fileUrl: JSON.stringify(this.fileUrl || []),
  961. });
  962. this.showMarks = false;
  963. this.popupMarkTxt = null;
  964. // 初始化
  965. this.getProcessNodeList();
  966. } catch {
  967. //
  968. }
  969. } else {
  970. this.$refs.headForm.submit();
  971. this.showMarks = false;
  972. this.popupMarkTxt = null;
  973. }
  974. },
  975. onCCChange(item) {
  976. item.ccStatus = !item.ccStatus;
  977. this.$forceUpdate();
  978. },
  979. async onSubmitHead(values) {
  980. // remarks
  981. let { ...res } = values;
  982. let promiseList = [];
  983. this.tpls = [];
  984. for (let tpl of this.processStructureValue.tpls) {
  985. this.tpls.push({
  986. tplDataId: tpl.id,
  987. tplId: tpl.form_structure.id,
  988. });
  989. // 说明是当前用户需要填写的列表数据
  990. if (
  991. this.currentNode.writeTpls !== undefined &&
  992. this.currentNode.writeTpls !== null &&
  993. this.currentNode.writeTpls.indexOf(tpl.form_structure.id) > -1
  994. ) {
  995. let fd = this.formatData(res);
  996. promiseList.push(fd);
  997. } else {
  998. // 已存在的表单数据
  999. promiseList.push(tpl.form_data);
  1000. }
  1001. }
  1002. for (let tplDataIndex in this.tpls) {
  1003. this.tpls[tplDataIndex].tplValue = promiseList[tplDataIndex];
  1004. }
  1005. let params = {
  1006. tasks: this.processStructureValue.process.task,
  1007. source_state: this.processStructureValue.workOrder.current_state,
  1008. target_state: this.selectItem.target,
  1009. circulation: this.selectItem.label,
  1010. flow_properties:
  1011. this.selectItem.flowProperties === undefined
  1012. ? 2
  1013. : parseInt(this.selectItem.flowProperties),
  1014. work_order_id: parseInt(this.workOrderId),
  1015. remarks: this.remarks,
  1016. fileUrl: JSON.stringify(this.fileUrl || []),
  1017. tpls: this.tpls,
  1018. };
  1019. let fileList = [];
  1020. this.tpls &&
  1021. this.tpls.forEach((tpl) => {
  1022. for (let val in tpl.tplValue) {
  1023. if (val.indexOf("file") != -1) {
  1024. const file = tpl.tplValue[val] || [];
  1025. file.forEach((item) => {
  1026. fileList.push(item.url);
  1027. });
  1028. }
  1029. }
  1030. });
  1031. try {
  1032. // const fileUrl = ''
  1033. // console.log(params)
  1034. // return
  1035. // 确认提交
  1036. await handleWorkOrder(params);
  1037. await asyncPlayLog({
  1038. workOrderId: parseInt(this.workOrderId),
  1039. fileUrl: fileList.join(","),
  1040. });
  1041. this.submitDisabled = false;
  1042. // workOrderId
  1043. this.getProcessNodeList();
  1044. } catch {
  1045. //
  1046. this.submitDisabled = false;
  1047. }
  1048. },
  1049. formatData(json) {
  1050. let tempJson = {};
  1051. let subFormList = {};
  1052. for (let v in json) {
  1053. let nameList = v.split(".");
  1054. // 只要大于1说明是子表单
  1055. if (nameList.length > 1) {
  1056. nameList.push(v);
  1057. let delName = nameList.shift();
  1058. if (subFormList[delName]) {
  1059. subFormList[delName].push(nameList);
  1060. } else {
  1061. subFormList[delName] = [];
  1062. subFormList[delName].push(nameList);
  1063. }
  1064. } else {
  1065. tempJson[v] = json[v];
  1066. }
  1067. }
  1068. for (let sub in subFormList) {
  1069. let subList = subFormList[sub] || [];
  1070. let subDown = [];
  1071. subList.forEach((item) => {
  1072. if (subDown[item[1]]) {
  1073. subDown[item[1]][item[0]] = json[item[2]];
  1074. } else {
  1075. subDown[item[1]] = {};
  1076. subDown[item[1]][item[0]] = json[item[2]];
  1077. }
  1078. });
  1079. tempJson[sub] = subDown;
  1080. }
  1081. return tempJson;
  1082. },
  1083. dataModelFormatBr(str) {
  1084. return str ? str.replace(/\n/g, "<br />") : str;
  1085. },
  1086. getHideTplData(tpl) {
  1087. // 獲取隱藏模板數據
  1088. let list = tpl.list;
  1089. let data = {};
  1090. list.forEach((item) => {
  1091. if (item.type == "subform") {
  1092. if (item.columns.length > 0) {
  1093. data[item.model] = [];
  1094. let arr = [];
  1095. item.columns?.forEach((col) => {
  1096. if (col.list?.length > 0) {
  1097. arr.push(...col.list);
  1098. }
  1099. });
  1100. let tempSub = {};
  1101. arr.forEach((a) => {
  1102. tempSub[a.model] = a.options.defaultValue;
  1103. });
  1104. data[item.model].push(tempSub);
  1105. } else {
  1106. data[item.model] = "";
  1107. }
  1108. } else {
  1109. data[item.model] = item.options.defaultValue;
  1110. }
  1111. });
  1112. return data;
  1113. },
  1114. },
  1115. };
  1116. </script>
  1117. <style lang="less" scoped>
  1118. @import url("../../assets/commonLess/variable.less");
  1119. .approvalContainer {
  1120. min-height: 100vh;
  1121. }
  1122. .approvalAction {
  1123. margin-bottom: 85px;
  1124. padding-bottom: env(safe-area-inset-bottom);
  1125. }
  1126. /deep/.van-goods-action {
  1127. box-shadow: 0 4px 10px 0 #cecece;
  1128. z-index: 99;
  1129. justify-content: space-around;
  1130. height: 70px;
  1131. .van-goods-action-icon__icon {
  1132. font-size: 20px;
  1133. min-width: 55px;
  1134. display: flex;
  1135. justify-content: center;
  1136. }
  1137. .van-goods-action-button--last {
  1138. margin-right: 0.12rem;
  1139. }
  1140. .van-button--danger {
  1141. background: #fff;
  1142. border: 1px solid #01c1b5;
  1143. color: #01c1b5;
  1144. }
  1145. }
  1146. .style {
  1147. margin-bottom: 0.12rem;
  1148. padding: 12px 16px;
  1149. line-height: 1.3;
  1150. font-size: 16px;
  1151. &.info {
  1152. display: flex;
  1153. flex-direction: column;
  1154. }
  1155. /deep/.van-field__label {
  1156. margin-bottom: 7px;
  1157. width: auto;
  1158. color: #111f2c;
  1159. }
  1160. /deep/.van-field__value {
  1161. padding: 0.03rem 0;
  1162. }
  1163. }
  1164. .headerInfo {
  1165. margin-bottom: 0.1rem;
  1166. padding: 0.18rem 0.12rem;
  1167. overflow: inherit;
  1168. /deep/.van-cell__title {
  1169. flex-direction: column;
  1170. align-items: flex-start;
  1171. }
  1172. }
  1173. .titleSection {
  1174. .titleContent {
  1175. font-size: 0.18rem;
  1176. color: #333333;
  1177. padding-right: 0.08rem;
  1178. }
  1179. }
  1180. .waitStatus {
  1181. color: #f39001;
  1182. font-size: 0.14rem;
  1183. display: inline-block;
  1184. padding-top: 0.06rem;
  1185. }
  1186. .imgStatus {
  1187. bottom: -35%;
  1188. width: 0.75rem;
  1189. height: 0.75rem;
  1190. position: absolute;
  1191. z-index: 98;
  1192. right: 0.2rem;
  1193. }
  1194. .markModel {
  1195. width: 90%;
  1196. border-radius: 0.08rem;
  1197. padding: 0.2rem 0;
  1198. }
  1199. .popup-marks {
  1200. // width: 95%;
  1201. h2 {
  1202. font-size: 0.16rem;
  1203. text-align: center;
  1204. }
  1205. /deep/.van-cell {
  1206. font-size: 0.14rem;
  1207. }
  1208. /deep/.van-button {
  1209. width: 90%;
  1210. margin-left: 5%;
  1211. font-size: 0.16rem;
  1212. }
  1213. }
  1214. .step_section {
  1215. margin: 0px 0.12rem 0.12rem;
  1216. border-radius: 0.1rem;
  1217. overflow: hidden;
  1218. .step_title {
  1219. background: #fff;
  1220. padding: 0.1rem 0.16rem;
  1221. color: #333333;
  1222. font-size: 0.18rem;
  1223. font-weight: 500;
  1224. }
  1225. /deep/.van-steps--vertical {
  1226. padding-left: 0.45rem;
  1227. .van-step__circle-container,
  1228. .van-step__line {
  1229. left: -22px;
  1230. }
  1231. }
  1232. .step {
  1233. font-size: 0.13rem;
  1234. .step-t {
  1235. display: flex;
  1236. align-content: center;
  1237. justify-content: space-between;
  1238. font-size: 0.16rem;
  1239. color: #444;
  1240. }
  1241. .inactive-icon {
  1242. width: 0.1rem;
  1243. height: 0.1rem;
  1244. background: #d8d8d8;
  1245. display: inline-block;
  1246. border-radius: 1rem;
  1247. }
  1248. .apply-state {
  1249. line-height: 1.5;
  1250. flex-basis: 73%;
  1251. }
  1252. .apply-time {
  1253. font-size: 0.14rem;
  1254. color: #999999;
  1255. flex-basis: 27%;
  1256. }
  1257. .apply-status {
  1258. padding: 0.05rem 0;
  1259. color: #999;
  1260. }
  1261. .remarks {
  1262. background: #f5f5f5;
  1263. padding: 0.08rem;
  1264. color: #323233;
  1265. }
  1266. .van-icon {
  1267. font-size: 0.17rem;
  1268. }
  1269. }
  1270. .ccUsers {
  1271. display: flex;
  1272. align-items: center;
  1273. justify-content: space-between;
  1274. color: #444;
  1275. padding-top: 0.08rem;
  1276. }
  1277. .ccUserDetail {
  1278. color: #444;
  1279. }
  1280. }
  1281. .imgUploader {
  1282. background: #f5f5f5;
  1283. margin-top: 8px;
  1284. margin-bottom: 8px;
  1285. }
  1286. .preview-uploader {
  1287. vertical-align: middle;
  1288. /deep/.van-uploader__wrapper--disabled {
  1289. opacity: 1;
  1290. }
  1291. /deep/.van-uploader__preview {
  1292. margin-bottom: 0;
  1293. }
  1294. /deep/.van-uploader__preview-image {
  1295. background: #f5f5f5;
  1296. border-radius: 4px;
  1297. padding: 2px;
  1298. }
  1299. }
  1300. .remarks-input {
  1301. background: #f7f8f9;
  1302. margin: 0.14rem 0.14rem 0;
  1303. width: auto;
  1304. font-size: 0.14rem;
  1305. border-radius: 8px;
  1306. }
  1307. </style>