ApplyContent.vue 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. <template>
  2. <div class="apply-content task-detail">
  3. <div class="task-body">
  4. <div v-if="IS_APPLY" class="mb-2 text-right">
  5. <el-button
  6. type="info"
  7. icon="el-icon-circle-plus-outline"
  8. @click="addAtachment"
  9. >增加卷型</el-button
  10. >
  11. </div>
  12. <table class="table mb-2">
  13. <tr>
  14. <th>试卷类型</th>
  15. <th>试卷文件</th>
  16. <th>答题卡</th>
  17. <th v-if="IS_APPLY">操作</th>
  18. </tr>
  19. <tr v-for="(attachment, index) in paperAttachments" :key="index">
  20. <td>
  21. <span>{{ attachment.name }}卷</span>
  22. <span class="color-gray-2" v-if="attachment.isExposed"
  23. >(已曝光)</span
  24. >
  25. </td>
  26. <td>
  27. <el-button
  28. v-if="!attachment.isExposed && IS_APPLY"
  29. type="text"
  30. class="btn-primary"
  31. @click="toUpload(attachment)"
  32. >
  33. <i
  34. :class="[
  35. 'icon',
  36. attachment.attachmentId ? 'icon-files-act' : 'icon-files'
  37. ]"
  38. ></i
  39. >{{
  40. attachment.attachmentId
  41. ? attachment.filename
  42. : "点击上传试卷文件"
  43. }}
  44. </el-button>
  45. <el-button
  46. v-else
  47. type="text"
  48. class="btn-primary"
  49. @click="downloadPaper(attachment)"
  50. >
  51. <i
  52. class="icon icon-download mr-1"
  53. v-if="attachment.attachmentId"
  54. ></i>
  55. <i>{{ attachment.filename }}</i>
  56. </el-button>
  57. </td>
  58. <td>
  59. <template v-if="IS_APPLY">
  60. <el-select
  61. class="mr-2"
  62. v-model="attachment.cardId"
  63. placeholder="请选择"
  64. style="width: 200px"
  65. filterable
  66. @visible-change="
  67. visible => cardOptionOpened(visible, attachment)
  68. "
  69. @change="cardChange(attachment)"
  70. >
  71. <el-option
  72. v-for="item in cards"
  73. :key="item.id"
  74. :value="item.id"
  75. :label="item.title"
  76. :disabled="item.disabled"
  77. >
  78. </el-option>
  79. </el-select>
  80. <el-button
  81. class="btn-primary"
  82. type="text"
  83. :disabled="!attachment.cardId"
  84. @click="toViewCard(attachment)"
  85. >预览</el-button
  86. >
  87. <el-button
  88. class="btn-primary"
  89. type="text"
  90. :disabled="
  91. !attachment.cardId ||
  92. (attachment.cardType === 'GENERIC' &&
  93. attachment.createMethod !== 'STANDARD')
  94. "
  95. @click="toCopyCard(attachment)"
  96. >复制</el-button
  97. >
  98. <el-button
  99. class="btn-primary"
  100. type="text"
  101. :disabled="
  102. !attachment.cardId || attachment.cardType === 'GENERIC'
  103. "
  104. @click="toEditCard(attachment)"
  105. >编辑</el-button
  106. >
  107. <el-button
  108. class="btn-primary"
  109. type="text"
  110. :disabled="!canCreateCard"
  111. @click="toCreateCard(attachment)"
  112. >新建</el-button
  113. >
  114. </template>
  115. <el-button
  116. v-else
  117. type="text"
  118. class="btn-primary"
  119. @click="toViewCard(attachment)"
  120. >{{ attachment.cardTitle || "预览" }}</el-button
  121. >
  122. </td>
  123. <td v-if="IS_APPLY">
  124. <el-button
  125. v-if="attachment.canDelete"
  126. class="btn-danger"
  127. type="text"
  128. @click="deleteAttachment(index)"
  129. >删除</el-button
  130. >
  131. </td>
  132. </tr>
  133. <tr v-if="!paperAttachments.length">
  134. <td colspan="5">
  135. <p class="tips-info text-center">暂无数据</p>
  136. </td>
  137. </tr>
  138. </table>
  139. <el-form>
  140. <el-form-item label="单次抽卷卷型数量:" label-width="150">
  141. <el-input-number
  142. v-model="curTaskApply.drawCount"
  143. :min="1"
  144. :max="maxFetchCount"
  145. :step="1"
  146. :controls="false"
  147. step-strictly
  148. :disabled="!IS_APPLY || exposedMode"
  149. ></el-input-number>
  150. <!-- :disabled="!IS_APPLY || exposedMode" -->
  151. </el-form-item>
  152. </el-form>
  153. <h4 class="mb-2">附件<span v-if="IS_APPLY">(最多4张)</span>:</h4>
  154. <div class="image-list">
  155. <div
  156. class="image-item"
  157. v-for="(img, index) in paperConfirmAttachments"
  158. :key="index"
  159. >
  160. <img
  161. :src="img.url"
  162. :alt="img.filename"
  163. title="点击查看大图"
  164. @click="toPreview(index)"
  165. />
  166. <div v-if="IS_APPLY" class="image-delete">
  167. <i
  168. class="el-icon-delete-solid"
  169. @click="deletePaperConfirmAttachment(index)"
  170. ></i>
  171. </div>
  172. </div>
  173. <div
  174. v-if="paperConfirmAttachments.length < 4 && IS_APPLY"
  175. class="image-item image-add"
  176. title="上传附件"
  177. @click="toUploadPaperConfirm"
  178. >
  179. <i class="el-icon-plus"></i>
  180. </div>
  181. </div>
  182. <div
  183. v-if="!IS_APPLY && !paperConfirmAttachments.length"
  184. class="image-list-none"
  185. >
  186. 暂无数据
  187. </div>
  188. <h4 class="mb-2">附件说明:</h4>
  189. <el-input
  190. v-if="IS_APPLY"
  191. class="mb-2"
  192. v-model="curTaskApply.remark"
  193. type="textarea"
  194. resize="none"
  195. :rows="2"
  196. :maxlength="100"
  197. clearable
  198. show-word-limit
  199. placeholder="建议不超过100个字"
  200. ></el-input>
  201. <div class="color-gray-2" v-else>
  202. <p v-if="curTaskApply.remark">{{ curTaskApply.remark }}</p>
  203. <p v-else>暂无</p>
  204. </div>
  205. <!-- 入库申请-审核阶段 -->
  206. <!-- audit history -->
  207. <div
  208. v-if="flowHistoryList.length && curTaskApply.flowStatus !== 'START'"
  209. class="task-audit-history flow-timeline"
  210. >
  211. <h4 class="mb-4">审核记录:</h4>
  212. <el-timeline>
  213. <el-timeline-item
  214. v-for="flow in flowHistoryList"
  215. :key="flow.stepKey"
  216. :type="flow.type"
  217. hide-timestamp
  218. size="large"
  219. placement="top"
  220. :class="{ 'timeline-item-stop': flow.nextIsNewFlow }"
  221. >
  222. <div class="flow-item">
  223. <div class="flow-item-content">
  224. <p v-if="flow.createTime" class="flow-item-time">
  225. {{ flow.createTime | timestampFilter }}
  226. </p>
  227. <h4 class="flow-item-title">{{ flow.approveUserName }}</h4>
  228. <p class="flow-item-desc">
  229. <span
  230. v-if="flow.approveOperation"
  231. :class="{
  232. 'color-danger': flow.approveOperation === 'REJECT'
  233. }"
  234. >{{
  235. flow.approveOperation | flowApproveOperationTypeFilter
  236. }}</span
  237. >
  238. <span>{{ flow.approveRemark }}</span>
  239. </p>
  240. <div
  241. v-if="flow.attachments.length"
  242. class="flow-item-attachment"
  243. >
  244. <span>附件:</span>
  245. <el-button
  246. v-for="item in flow.attachments"
  247. :key="item.name"
  248. type="text"
  249. class="btn-primary"
  250. @click="downloadPaper(item)"
  251. >{{ item.name }}卷:{{ item.filename }}</el-button
  252. >
  253. </div>
  254. <div
  255. v-if="flow.isApproveSetFlowNextNode && approveUsers.length"
  256. class="flow-item-users"
  257. >
  258. <span>审批人:</span>
  259. <el-tag
  260. v-for="user in approveUsers"
  261. :key="user.id"
  262. size="small"
  263. :disable-transitions="false"
  264. >
  265. {{ user.name }}
  266. </el-tag>
  267. </div>
  268. </div>
  269. <div
  270. v-if="
  271. flow.isApproveSetFlowNextNode &&
  272. auditModal.approvePass === 'PASS'
  273. "
  274. class="flow-item-action"
  275. >
  276. <el-button
  277. class="user-select"
  278. icon="el-icon-plus"
  279. @click="toSelectNextFlowUser"
  280. ></el-button>
  281. <p v-if="!approveUsers.length" class="tips-info tips-error">
  282. 请选择审核人
  283. </p>
  284. </div>
  285. </div>
  286. </el-timeline-item>
  287. </el-timeline>
  288. </div>
  289. <!-- 入库申请-申请阶段 -->
  290. <div
  291. v-if="flowList.length && curTaskApply.flowStatus === 'START'"
  292. class="task-audit-history flow-timeline"
  293. >
  294. <h4 class="mb-4">流程:</h4>
  295. <el-timeline>
  296. <el-timeline-item
  297. v-for="flow in flowList"
  298. :key="flow.taskKey"
  299. :type="flow.type"
  300. >
  301. <div class="flow-item">
  302. <div class="flow-item-content">
  303. <h4 class="flow-item-title">{{ flow.taskName }}</h4>
  304. <p v-if="flow.approveUserNames" class="flow-item-desc">
  305. {{ flow.approveUserNames }}
  306. </p>
  307. <div
  308. v-if="flow.isApproveSetFlowNextNode && approveUsers.length"
  309. class="flow-item-users"
  310. >
  311. <span>审批人:</span>
  312. <el-tag
  313. v-for="user in approveUsers"
  314. :key="user.id"
  315. size="small"
  316. :disable-transitions="false"
  317. >
  318. {{ user.name }}
  319. </el-tag>
  320. </div>
  321. </div>
  322. <div
  323. v-if="flow.isApproveSetFlowNextNode"
  324. class="flow-item-action"
  325. >
  326. <el-button
  327. class="user-select"
  328. icon="el-icon-plus"
  329. @click="toSelectNextFlowUser"
  330. ></el-button>
  331. <p v-if="!approveUsers.length" class="tips-info tips-error">
  332. 请选择审核人
  333. </p>
  334. </div>
  335. </div>
  336. </el-timeline-item>
  337. </el-timeline>
  338. </div>
  339. <!-- audit -->
  340. <div v-if="IS_AUDIT" class="task-audit">
  341. <el-form
  342. ref="auditModalComp"
  343. :model="auditModal"
  344. :rules="auditRules"
  345. label-width="90px"
  346. >
  347. <el-form-item prop="approvePass" label="审批操作:">
  348. <el-radio-group
  349. v-model="auditModal.approvePass"
  350. @change="approvePassChange"
  351. >
  352. <el-radio
  353. v-for="(val, key) in TASK_AUDIT_RESULT"
  354. :key="key"
  355. :label="key"
  356. >{{ val }}</el-radio
  357. >
  358. </el-radio-group>
  359. </el-form-item>
  360. <el-form-item
  361. v-if="auditModal.approvePass === 'REJECT'"
  362. prop="setup"
  363. label="驳回节点:"
  364. >
  365. <el-select
  366. v-model="auditModal.setup"
  367. placeholder="请选择"
  368. style="width: 100%"
  369. >
  370. <el-option
  371. v-for="item in rejectSetupList"
  372. :key="item.taskKey"
  373. :value="item.setup"
  374. :label="item.name"
  375. >
  376. </el-option>
  377. </el-select>
  378. </el-form-item>
  379. <el-form-item
  380. v-if="auditModal.approvePass !== 'EXCHANGE'"
  381. :key="auditModal.approvePass"
  382. prop="remark"
  383. label="审批意见:"
  384. >
  385. <el-input
  386. class="mb-2"
  387. v-model="auditModal.remark"
  388. type="textarea"
  389. resize="none"
  390. :rows="5"
  391. :maxlength="100"
  392. clearable
  393. show-word-limit
  394. placeholder="建议不超过100个字"
  395. ref="ReasonInput"
  396. ></el-input>
  397. </el-form-item>
  398. <el-form-item
  399. v-if="auditModal.approvePass === 'EXCHANGE'"
  400. prop="userId"
  401. label="审批人:"
  402. >
  403. <el-tag
  404. v-for="user in exchangeUsers"
  405. :key="user.id"
  406. :disable-transitions="false"
  407. size="medium"
  408. >
  409. {{ user.name }}
  410. </el-tag>
  411. <el-button
  412. class="ml-2"
  413. size="mini"
  414. type="primary"
  415. @click="toSelectExchangeUser"
  416. >选择用户</el-button
  417. >
  418. </el-form-item>
  419. </el-form>
  420. </div>
  421. </div>
  422. <div class="task-action">
  423. <el-button
  424. v-if="IS_APPLY"
  425. type="primary"
  426. :disabled="isSubmit"
  427. @click="submit"
  428. >确认提交</el-button
  429. >
  430. <el-button
  431. v-if="IS_APPLY && curTaskApply.flowStatus !== 'REJECT'"
  432. type="primary"
  433. :disabled="isSubmit"
  434. @click="toSave"
  435. >暂存</el-button
  436. >
  437. <el-button
  438. v-if="IS_AUDIT"
  439. type="primary"
  440. :disabled="isSubmit"
  441. @click="toAuditSubmit"
  442. >确定</el-button
  443. >
  444. <!-- <el-button
  445. v-if="IS_AUDIT_APPLY"
  446. type="primary"
  447. :disabled="isSubmit"
  448. @click="toAuditApply"
  449. >提交</el-button
  450. > -->
  451. <el-button @click="cancel">取消</el-button>
  452. </div>
  453. <!-- upload-paper-dialog -->
  454. <upload-paper-dialog
  455. :paper-attachment="curAttachment"
  456. :upload-type="curUploadType"
  457. @confirm="uploadConfirm"
  458. ref="UploadPaperDialog"
  459. ></upload-paper-dialog>
  460. <!-- image-preview -->
  461. <simple-image-preview
  462. :cur-image="curImage"
  463. @on-prev="toPrevImage"
  464. @on-next="toNextImage"
  465. ref="SimpleImagePreview"
  466. ></simple-image-preview>
  467. <!-- select-user-dialog -->
  468. <select-user-dialog
  469. v-if="IS_AUDIT || IS_NEED_SELECT_APPROVE_USER"
  470. ref="SelectUserDialog"
  471. :user-limit-count="userLimitCount"
  472. :filter-roles="userFilterRoles"
  473. :users="curSelectedUsers"
  474. @modified="userSelected"
  475. ></select-user-dialog>
  476. <!-- ModifyCard -->
  477. <modify-card ref="ModifyCard" @modified="cardModified"></modify-card>
  478. </div>
  479. </template>
  480. <script>
  481. import UploadPaperDialog from "./UploadPaperDialog";
  482. import {
  483. taskApplyDetail,
  484. updateTaskApply,
  485. taskAuditApply,
  486. cardForSelectList
  487. } from "../api";
  488. import { attachmentPreview } from "../../login/api";
  489. import SimpleImagePreview from "@/components/SimpleImagePreview";
  490. import SelectUserDialog from "../../base/components/SelectUserDialog";
  491. import ModifyCard from "../../card/components/ModifyCard";
  492. import { TASK_AUDIT_RESULT, COMMON_CARD_RULE_ID } from "@/constants/enumerate";
  493. import {
  494. taskFlowDetail,
  495. taskFlowApproverExchange,
  496. taskFlowNodeInfo,
  497. taskFlowApprover,
  498. flowDetailByFlowId
  499. } from "../../base/api";
  500. import { copyCard } from "../../card/api";
  501. const initTaskApply = {
  502. examId: "",
  503. examTaskId: "",
  504. paperNumber: "",
  505. paperType: "A",
  506. paperAttachmentIds: "",
  507. paperConfirmAttachmentIds: "",
  508. cardId: "",
  509. cardRuleId: "",
  510. makeMethod: "",
  511. remark: "",
  512. courseCode: "",
  513. courseName: "",
  514. drawCount: 1,
  515. exposedPaperType: "",
  516. // 流程
  517. flowId: "",
  518. flowStatus: "",
  519. setup: 0,
  520. // 工作台任务id
  521. flowTaskId: "",
  522. // 题卡状态
  523. status: "",
  524. // 考务规则
  525. review: false,
  526. includePaper: false,
  527. customCard: false
  528. };
  529. export default {
  530. name: "apply-content",
  531. components: {
  532. UploadPaperDialog,
  533. SimpleImagePreview,
  534. SelectUserDialog,
  535. ModifyCard
  536. },
  537. props: {
  538. examTask: {
  539. type: Object,
  540. default() {
  541. return {};
  542. }
  543. },
  544. editType: {
  545. type: String,
  546. default: ""
  547. }
  548. },
  549. data() {
  550. return {
  551. isSubmit: false,
  552. curTaskApply: { ...initTaskApply },
  553. paperConfirmAttachmentId: { attachmentId: "", filename: "", url: "" },
  554. paperAttachments: [],
  555. paperConfirmAttachments: [],
  556. curAttachment: {},
  557. curUploadType: "paper",
  558. attachmentLimitCount: 26,
  559. abc: "abcdefghijklmnopqrstuvwxyz".toUpperCase(),
  560. cards: [],
  561. task: {},
  562. reason: "",
  563. TASK_AUDIT_RESULT: { ...TASK_AUDIT_RESULT, EXCHANGE: "转他人审批" },
  564. // audit
  565. flowList: [],
  566. flowInfo: {},
  567. auditModal: {
  568. approvePass: "PASS",
  569. setup: "",
  570. remark: "",
  571. userId: ""
  572. },
  573. auditRules: {
  574. approvePass: [
  575. {
  576. required: true
  577. }
  578. ],
  579. setup: [
  580. {
  581. required: true,
  582. validator: (rule, value, callback) => {
  583. if (this.auditModal.approvePass === "REJECT" && !value) {
  584. callback(new Error(`请选择要驳回到的节点`));
  585. } else {
  586. callback();
  587. }
  588. },
  589. trigger: "change"
  590. }
  591. ],
  592. remark: [
  593. {
  594. required: false,
  595. validator: (rule, value, callback) => {
  596. if (this.auditModal.approvePass === "REJECT" && !value) {
  597. callback(new Error(`请输入审批意见`));
  598. } else {
  599. callback();
  600. }
  601. },
  602. trigger: "change"
  603. }
  604. ],
  605. userId: [
  606. {
  607. required: true,
  608. message: "请选择审批人",
  609. trigger: "change"
  610. }
  611. ]
  612. },
  613. flowHistoryList: [],
  614. rejectSetupList: [], // 可以驳回的节点
  615. exchangeUsers: [],
  616. selectUserType: "exchange", // exchange:转审,approve:下一节点审核
  617. curSelectedUsers: [],
  618. // 选择下一节点审批人
  619. IS_NEED_SELECT_APPROVE_USER: false,
  620. nextFlowTaskResult: {}, //下一节点信息
  621. approveUsers: [],
  622. userLimitCount: 1,
  623. userFilterRoles: [],
  624. // image-preview
  625. curImage: {},
  626. curImageIndex: 0
  627. };
  628. },
  629. computed: {
  630. IS_APPLY() {
  631. return this.editType
  632. ? this.editType === "APPLY"
  633. : this.curTaskApply.setup === 1 || this.curTaskApply.setup === null;
  634. },
  635. IS_PREVIEW() {
  636. return this.editType
  637. ? this.editType === "PREVIEW"
  638. : this.curTaskApply.setup !== null && this.curTaskApply.setup <= 0;
  639. },
  640. IS_AUDIT() {
  641. const IS_COMMON_AUDIT = this.editType
  642. ? this.editType === "AUDIT"
  643. : this.curTaskApply.setup > 1;
  644. return IS_COMMON_AUDIT;
  645. },
  646. maxFetchCount() {
  647. return this.paperAttachments.length < 1
  648. ? 1
  649. : this.paperAttachments.length;
  650. },
  651. canCreateCard() {
  652. return (
  653. this.curTaskApply.courseCode &&
  654. this.curTaskApply.examId &&
  655. this.curTaskApply.cardRuleId !== COMMON_CARD_RULE_ID
  656. );
  657. },
  658. exposedMode() {
  659. return !!this.curTaskApply.exposedPaperType;
  660. }
  661. },
  662. mounted() {
  663. this.initData();
  664. },
  665. methods: {
  666. async initData() {
  667. const data = await taskApplyDetail(
  668. this.examTask.id,
  669. this.examTask.source
  670. );
  671. this.$emit("info-update", {
  672. semesterName: data.semesterName,
  673. examName: data.examName,
  674. examModel: data.examModel
  675. });
  676. this.curTaskApply = this.$objAssign(initTaskApply, data || {});
  677. this.curTaskApply.examId =
  678. this.curTaskApply.examId || this.examTask.examId;
  679. this.curTaskApply.examTaskId = this.examTask.id;
  680. this.curTaskApply.courseCode = this.examTask.courseCode;
  681. this.curTaskApply.courseName = this.examTask.courseName;
  682. this.curTaskApply.cardRuleId = this.examTask.cardRuleId;
  683. this.curTaskApply.customCard = this.examTask.customCard;
  684. this.curTaskApply.paperNumber = this.examTask.paperNumber;
  685. this.curTaskApply.setup = this.examTask.setup;
  686. this.curTaskApply.includePaper =
  687. data.printContent.indexOf("PAPER") !== -1;
  688. this.paperAttachments = this.curTaskApply.paperAttachmentIds
  689. ? JSON.parse(this.curTaskApply.paperAttachmentIds)
  690. : [];
  691. const exposedPaperType = data.exposedPaperType || "";
  692. let exposedPaperTypes = exposedPaperType.split(",");
  693. exposedPaperTypes.sort((a, b) => (a > b ? -1 : 1));
  694. const maxExposedPaperType = exposedPaperTypes[0];
  695. this.paperAttachments.forEach(paper => {
  696. paper.canDelete = maxExposedPaperType
  697. ? paper.name > maxExposedPaperType
  698. : true;
  699. paper.isExposed = exposedPaperTypes.includes(paper.name);
  700. });
  701. this.paperConfirmAttachments = this.curTaskApply.paperConfirmAttachmentIds
  702. ? JSON.parse(this.curTaskApply.paperConfirmAttachmentIds)
  703. : [];
  704. if (this.IS_APPLY) this.getCardList();
  705. // flow
  706. if (this.curTaskApply.flowStatus === "START") {
  707. await this.getFlowList();
  708. } else {
  709. await this.getFlowHistory();
  710. }
  711. if (this.IS_AUDIT) this.getCurFlowNodeInfo();
  712. },
  713. async getCardList() {
  714. if (!this.curTaskApply.courseCode || !this.curTaskApply.examId) return;
  715. const data = await cardForSelectList({
  716. courseCode: this.curTaskApply.courseCode,
  717. examId: this.curTaskApply.examId,
  718. paperNumber: this.curTaskApply.paperNumber
  719. });
  720. this.cards = data || [];
  721. },
  722. async getFlowHistory() {
  723. if (!this.curTaskApply.flowId) return;
  724. const data = await taskFlowDetail(
  725. this.curTaskApply.flowId
  726. ).catch(() => {});
  727. if (!data) return;
  728. const FLOW_STATUS = {
  729. SUBMIT: "success",
  730. APPROVE: "success",
  731. EXCHANGE: "success",
  732. REJECT: "danger",
  733. END: "success"
  734. };
  735. let nextFlowId = "";
  736. this.flowHistoryList = data.tfFlowViewLogResultList.map((item, index) => {
  737. const nextItem = data.tfFlowViewLogResultList[index + 1];
  738. nextFlowId = nextItem ? nextItem.flowId : item.flowId;
  739. item.nextIsNewFlow = nextFlowId !== item.flowId;
  740. item.type = FLOW_STATUS[item.approveOperation];
  741. item.attachments = item.paperAttachmentId
  742. ? JSON.parse(item.paperAttachmentId)
  743. : [];
  744. item.isApproveSetFlowNextNode = false;
  745. return item;
  746. });
  747. const flowIsEnd = data.currFlowTaskResult.taskKey === "end";
  748. this.flowHistoryList.push({
  749. type: flowIsEnd ? "success" : "current",
  750. stepKey: this.$randomCode(),
  751. approveSetup: data.currFlowTaskResult.setup, //审批人节点
  752. approveRemark: data.currFlowTaskResult.taskName, //审批信息
  753. approveOperation: flowIsEnd ? "END" : "",
  754. approveUserName: data.currFlowTaskResult.approveUserNames, //审批人
  755. createTime: null,
  756. nextIsNewFlow: false,
  757. isApproveSetFlowNextNode: false,
  758. attachments: []
  759. });
  760. },
  761. async getFlowList() {
  762. if (!this.curTaskApply.flowId) return;
  763. const data = await flowDetailByFlowId(this.curTaskApply.flowId);
  764. if (!data) return;
  765. const modelType =
  766. data.flowTaskResultList[0] && data.flowTaskResultList[0].modelType;
  767. this.flowInfo = {
  768. customFlowId: data.id,
  769. version: data.version
  770. };
  771. const nextFlowNodeIndex = 1;
  772. this.IS_NEED_SELECT_APPROVE_USER = modelType === "APPROVE_SET";
  773. const flowList = data.flowTaskResultList || [];
  774. this.flowList = flowList.map((flow, index) => {
  775. flow.isApproveSetFlowNextNode =
  776. this.IS_NEED_SELECT_APPROVE_USER && index === nextFlowNodeIndex;
  777. return flow;
  778. });
  779. if (this.flowList.length) {
  780. this.flowList[0].type = "success";
  781. }
  782. this.nextFlowTaskResult = this.flowList[nextFlowNodeIndex];
  783. },
  784. async getCurFlowNodeInfo() {
  785. const data = await taskFlowNodeInfo(this.curTaskApply.flowTaskId);
  786. this.rejectSetupList = (data && data.rejectSetupList) || [];
  787. this.rejectSetupList.forEach(item => {
  788. item.name = `【${item.taskName}】${item.approveUserNames}`;
  789. });
  790. this.nextFlowTaskResult = data.nextFlowTaskResult;
  791. // 被打回给命题老师之后,命题老师只能通过。
  792. if (data.setup === 1 && !this.rejectSetupList.length) {
  793. this.TASK_AUDIT_RESULT = { PASS: "通过" };
  794. this.auditModal.approvePass = "PASS";
  795. }
  796. // 判断发起人自选,是否需要选择下个审核人员
  797. const { modelType, approveUserNames, taskKey } = this.nextFlowTaskResult;
  798. this.IS_NEED_SELECT_APPROVE_USER =
  799. modelType === "APPROVE_SET" &&
  800. taskKey.toLowerCase() !== "end" &&
  801. !approveUserNames;
  802. this.flowHistoryList[
  803. this.flowHistoryList.length - 1
  804. ].isApproveSetFlowNextNode = this.IS_NEED_SELECT_APPROVE_USER;
  805. },
  806. approvePassChange() {
  807. this.auditRules.remark[0].required =
  808. this.auditModal.approvePass === "REJECT";
  809. },
  810. addAtachment() {
  811. if (this.paperAttachments.length >= this.attachmentLimitCount) return;
  812. const name = this.abc[this.paperAttachments.length];
  813. const newAttachment = {
  814. name,
  815. attachmentId: "",
  816. filename: "",
  817. cardId: "",
  818. cardType: "",
  819. createMethod: "",
  820. cardTitle: "",
  821. pages: 0,
  822. canDelete: true,
  823. isExposed: false
  824. };
  825. this.paperAttachments.push(newAttachment);
  826. },
  827. deleteAttachment(index) {
  828. if (this.paperAttachments.length <= 1) {
  829. this.$message.error("试卷类型数量不得少于1");
  830. return;
  831. }
  832. this.paperAttachments.splice(index, 1);
  833. this.paperAttachments.forEach((item, itemIndex) => {
  834. item.name = this.abc[itemIndex];
  835. });
  836. if (
  837. this.curTaskApply.drawCount &&
  838. this.curTaskApply.drawCount > this.paperAttachments.length
  839. ) {
  840. this.curTaskApply.drawCount = this.paperAttachments.length;
  841. }
  842. },
  843. toUpload(attachment) {
  844. this.curUploadType = "paper";
  845. this.curAttachment = {
  846. ...attachment
  847. };
  848. this.$refs.UploadPaperDialog.open();
  849. },
  850. toUploadPaperConfirm() {
  851. if (this.paperConfirmAttachments.length >= 4) return;
  852. this.curUploadType = "paperConfirm";
  853. this.curAttachment = {
  854. ...this.paperConfirmAttachmentId
  855. };
  856. this.$refs.UploadPaperDialog.open();
  857. },
  858. uploadConfirm(attachment, uploadType) {
  859. if (uploadType === "paper") {
  860. const index = this.paperAttachments.findIndex(
  861. item => item.name === attachment.name
  862. );
  863. this.paperAttachments.splice(index, 1, { ...attachment });
  864. } else {
  865. this.paperConfirmAttachments.push(attachment);
  866. }
  867. },
  868. deletePaperConfirmAttachment(index) {
  869. this.paperConfirmAttachments.splice(index, 1);
  870. },
  871. toViewCard(attachment) {
  872. window.open(
  873. this.getRouterPath({
  874. name: "CardPreview",
  875. params: {
  876. cardId: attachment.cardId,
  877. viewType: "view"
  878. }
  879. })
  880. );
  881. },
  882. async toCopyCard(attachment) {
  883. this.curAttachment = { ...attachment };
  884. const newCardId = await copyCard(
  885. attachment.cardId,
  886. this.curTaskApply.courseCode
  887. );
  888. this.cardModified(newCardId);
  889. },
  890. toEditCard(attachment) {
  891. this.curAttachment = { ...attachment };
  892. this.$ls.set("prepareTcPCard", {
  893. id: attachment.cardId,
  894. examTaskId: this.curTaskApply.examTaskId,
  895. courseCode: this.curTaskApply.courseCode,
  896. courseName: this.curTaskApply.courseName,
  897. makeMethod: this.curTaskApply.makeMethod,
  898. cardRuleId: this.curTaskApply.cardRuleId,
  899. type: attachment.cardType,
  900. createMethod: attachment.createMethod
  901. });
  902. this.$refs.ModifyCard.open();
  903. },
  904. async cardModified(cardId) {
  905. if (!cardId) return;
  906. await this.getCardList();
  907. let card = this.cards.find(item => item.id === cardId);
  908. const aind = this.paperAttachments.findIndex(
  909. item => item.name === this.curAttachment.name
  910. );
  911. if (aind !== -1 && card) {
  912. this.paperAttachments[aind].cardId = card.id;
  913. this.paperAttachments[aind].cardType = card.type;
  914. this.paperAttachments[aind].createMethod = card.createMethod;
  915. this.paperAttachments[aind].cardTitle = card.title;
  916. }
  917. },
  918. cardChange(attachment) {
  919. const card = this.cards.find(item => item.id === attachment.cardId);
  920. if (card) {
  921. attachment.cardType = card.type;
  922. attachment.createMethod = card.createMethod;
  923. attachment.cardTitle = card.title;
  924. }
  925. },
  926. cardOptionOpened(visible, attachment) {
  927. if (!visible) return;
  928. const selectedCardIds = this.paperAttachments.map(item => item.cardId);
  929. this.cards = this.cards.map(card => {
  930. card.disabled =
  931. card.id !== attachment.cardId &&
  932. selectedCardIds.includes(card.id) &&
  933. card.type !== "GENERIC";
  934. return card;
  935. });
  936. },
  937. toCreateCard(attachment) {
  938. if (!this.examTask.cardRuleId) {
  939. this.$message.error("题卡规则缺失!");
  940. return;
  941. }
  942. this.curAttachment = { ...attachment };
  943. // 这里只允许新建标准专卡
  944. this.$ls.set("prepareTcPCard", {
  945. courseCode: this.examTask.courseCode,
  946. courseName: this.examTask.courseName,
  947. schoolName: this.$ls.get("schoolName"),
  948. makeMethod: "SELF",
  949. cardRuleId: this.examTask.cardRuleId,
  950. type: "CUSTOM",
  951. createMethod: "STANDARD"
  952. });
  953. this.$refs.ModifyCard.open();
  954. },
  955. cancel() {
  956. this.$emit("cancel");
  957. },
  958. async downloadPaper(attachment) {
  959. if (!attachment.attachmentId) return;
  960. const data = await attachmentPreview(attachment.attachmentId);
  961. window.open(data.url);
  962. },
  963. toSelectNextFlowUser() {
  964. if (!this.IS_NEED_SELECT_APPROVE_USER) return;
  965. this.userLimitCount =
  966. this.nextFlowTaskResult.approveUserCountType === "ONE" ? 1 : 0;
  967. this.userFilterRoles =
  968. this.nextFlowTaskResult.approveUserSelectRange === "ROLE"
  969. ? this.nextFlowTaskResult.approveUserSelectRoles.map(item => item.id)
  970. : [];
  971. this.selectUserType = "approve";
  972. this.curSelectedUsers = this.approveUsers;
  973. this.$refs.SelectUserDialog.open();
  974. },
  975. toSelectExchangeUser() {
  976. this.userLimitCount = 1;
  977. this.userFilterRoles = [];
  978. this.curSelectedUsers = this.exchangeUsers;
  979. this.selectUserType = "exchange";
  980. this.$refs.SelectUserDialog.open();
  981. },
  982. userSelected(users) {
  983. if (this.selectUserType === "exchange") {
  984. this.exchangeUsers = users;
  985. this.auditModal.userId = users[0].id;
  986. } else {
  987. this.approveUsers = users;
  988. }
  989. },
  990. getTaskData() {
  991. let data = { ...this.curTaskApply };
  992. data.paperType = this.paperAttachments.map(item => item.name).join(",");
  993. data.paperAttachmentIds = JSON.stringify(this.paperAttachments, (k, v) =>
  994. k === "url" ? undefined : v
  995. );
  996. data.paperConfirmAttachmentIds = JSON.stringify(
  997. this.paperConfirmAttachments
  998. );
  999. if (this.IS_NEED_SELECT_APPROVE_USER)
  1000. data.approveUserIds = this.approveUsers.map(item => item.id);
  1001. return data;
  1002. },
  1003. checkDataValid() {
  1004. // 设置了入库强制包含试卷时,校验试卷是否上传。
  1005. // 未设置入库强制包含试卷时,若有试卷上传,则需要上传全部。若无试卷上传,则通过。
  1006. if (this.curTaskApply.includePaper) {
  1007. const attachmentValid = !this.paperAttachments.some(
  1008. item => !item.attachmentId
  1009. );
  1010. if (!attachmentValid) {
  1011. this.$message.error("请完成试卷文件上传!");
  1012. return Promise.reject();
  1013. }
  1014. } else {
  1015. const hasUploadPaperAttachments = this.paperAttachments.filter(
  1016. item => item.attachmentId
  1017. );
  1018. if (
  1019. hasUploadPaperAttachments.length > 0 &&
  1020. hasUploadPaperAttachments.length !== this.paperAttachments.length
  1021. ) {
  1022. this.$message.error("有试卷文件未完成上传!");
  1023. return Promise.reject();
  1024. }
  1025. }
  1026. // if (!this.paperConfirmAttachments.length) {
  1027. // this.$message.error("请上传附件!");
  1028. // return;
  1029. // }
  1030. const cardValid = !this.paperAttachments.some(item => !item.cardId);
  1031. if (!cardValid) {
  1032. this.$message.error("有试卷类型未选择题卡!");
  1033. return;
  1034. }
  1035. if (this.IS_NEED_SELECT_APPROVE_USER && !this.approveUsers.length) {
  1036. this.$message.error("请设置审核人员!");
  1037. return;
  1038. }
  1039. return true;
  1040. },
  1041. async toSave() {
  1042. if (this.isSubmit) return;
  1043. this.isSubmit = true;
  1044. const datas = this.getTaskData();
  1045. datas.operateType = "STAGE";
  1046. const data = await updateTaskApply(datas).catch(() => {});
  1047. this.isSubmit = false;
  1048. if (!data) return;
  1049. this.$message.success("保存成功!");
  1050. },
  1051. async silentSave() {
  1052. const datas = this.getTaskData();
  1053. datas.operateType = "STAGE";
  1054. await updateTaskApply(datas).catch(() => {});
  1055. },
  1056. async submit() {
  1057. if (!this.checkDataValid()) return;
  1058. const result = await this.$confirm(
  1059. "任务确定提交后,则不可更改试卷及答题卡内容,确定提交该任务?",
  1060. "提示",
  1061. {
  1062. type: "warning"
  1063. }
  1064. ).catch(() => {});
  1065. if (result !== "confirm") return;
  1066. const datas = this.getTaskData();
  1067. datas.operateType = "SUBMIT";
  1068. const data = await updateTaskApply(datas).catch(() => {});
  1069. if (!data) return;
  1070. this.$message.success("提交成功!");
  1071. this.$emit("modified");
  1072. },
  1073. async toAuditSubmit() {
  1074. const valid = await this.$refs.auditModalComp.validate().catch(() => {});
  1075. if (!valid) return;
  1076. if (
  1077. this.auditModal.approvePass === "PASS" &&
  1078. this.IS_NEED_SELECT_APPROVE_USER &&
  1079. !this.approveUsers.length
  1080. ) {
  1081. this.$message.error("请设置审核人员!");
  1082. return;
  1083. }
  1084. const actionName = this.TASK_AUDIT_RESULT[this.auditModal.approvePass];
  1085. const result = await this.$confirm(
  1086. `确定${actionName}该申请吗?`,
  1087. "提示",
  1088. {
  1089. type: "warning"
  1090. }
  1091. ).catch(() => {});
  1092. if (result !== "confirm") return;
  1093. if (this.auditModal.approvePass === "EXCHANGE") {
  1094. let datas = {
  1095. taskId: this.curTaskApply.flowTaskId,
  1096. userId: this.auditModal.userId
  1097. };
  1098. const data = await taskFlowApproverExchange(datas).catch(() => {});
  1099. if (!data) return;
  1100. } else {
  1101. let datas = { ...this.auditModal };
  1102. datas.taskId = this.curTaskApply.flowTaskId;
  1103. if (
  1104. this.auditModal.approvePass === "PASS" &&
  1105. this.IS_NEED_SELECT_APPROVE_USER
  1106. )
  1107. datas.approveUserIds = this.approveUsers.map(item => item.id);
  1108. const data = await taskFlowApprover(datas).catch(() => {});
  1109. if (!data) return;
  1110. }
  1111. this.$message.success("审批成功!");
  1112. this.$emit("modified");
  1113. },
  1114. async toAuditApply() {
  1115. const result = await this.$confirm("确定提交该任务吗?", "提示", {
  1116. type: "warning"
  1117. }).catch(() => {});
  1118. if (result !== "confirm") return;
  1119. const datas = {
  1120. examTaskDetail: this.getTaskData(),
  1121. flowTaskId: this.curTaskApply.flowTaskId,
  1122. approvePass: "PASS"
  1123. };
  1124. const data = await taskAuditApply(datas).catch(() => {});
  1125. if (!data) return;
  1126. this.$message.success("审批成功!");
  1127. this.$emit("modified");
  1128. },
  1129. // image-preview
  1130. toPreview(index) {
  1131. this.curImageIndex = index;
  1132. this.selectImage(index);
  1133. this.$refs.SimpleImagePreview.open();
  1134. },
  1135. selectImage(index) {
  1136. this.curImage = this.paperConfirmAttachments[index];
  1137. },
  1138. toPrevImage() {
  1139. if (this.curImageIndex === 0) {
  1140. this.curImageIndex = this.paperConfirmAttachments.length - 1;
  1141. } else {
  1142. this.curImageIndex--;
  1143. }
  1144. this.selectImage(this.curImageIndex);
  1145. },
  1146. toNextImage() {
  1147. if (this.curImageIndex === this.paperConfirmAttachments.length - 1) {
  1148. this.curImageIndex = 0;
  1149. } else {
  1150. this.curImageIndex++;
  1151. }
  1152. this.selectImage(this.curImageIndex);
  1153. }
  1154. }
  1155. };
  1156. </script>