ApplyContent.vue 38 KB

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