ApplyContent.vue 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  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. examName: data.examName,
  698. examModel: data.examModel
  699. });
  700. this.curTaskApply = this.$objAssign(initTaskApply, data || {});
  701. this.curTaskApply.examId =
  702. this.curTaskApply.examId || this.examTask.examId;
  703. this.curTaskApply.examTaskId = this.examTask.id;
  704. this.curTaskApply.courseCode = this.examTask.courseCode;
  705. this.curTaskApply.courseName = this.examTask.courseName;
  706. this.curTaskApply.cardRuleId = this.examTask.cardRuleId;
  707. this.curTaskApply.customCard = this.examTask.customCard;
  708. this.curTaskApply.paperNumber = this.examTask.paperNumber;
  709. this.curTaskApply.setup = this.examTask.setup;
  710. this.curTaskApply.includePaper =
  711. data.printContent.indexOf("PAPER") !== -1;
  712. this.paperAttachments = this.curTaskApply.paperAttachmentIds
  713. ? JSON.parse(this.curTaskApply.paperAttachmentIds)
  714. : [];
  715. const exposedPaperType = data.exposedPaperType || "";
  716. let exposedPaperTypes = exposedPaperType.split(",");
  717. exposedPaperTypes.sort((a, b) => (a > b ? -1 : 1));
  718. const maxExposedPaperType = exposedPaperTypes[0];
  719. this.paperAttachments.forEach(paper => {
  720. paper.canDelete = maxExposedPaperType
  721. ? paper.name > maxExposedPaperType
  722. : true;
  723. paper.isExposed = exposedPaperTypes.includes(paper.name);
  724. });
  725. this.paperConfirmAttachments = this.curTaskApply.paperConfirmAttachmentIds
  726. ? JSON.parse(this.curTaskApply.paperConfirmAttachmentIds)
  727. : [];
  728. if (this.IS_APPLY) this.getCardList();
  729. // flow
  730. if (this.curTaskApply.flowStatus === "START") {
  731. await this.getFlowList();
  732. } else {
  733. await this.getFlowHistory();
  734. }
  735. if (this.IS_AUDIT) this.getCurFlowNodeInfo();
  736. },
  737. async getCardList() {
  738. if (!this.curTaskApply.courseCode || !this.curTaskApply.examId) return;
  739. const data = await cardForSelectList({
  740. courseCode: this.curTaskApply.courseCode,
  741. examId: this.curTaskApply.examId,
  742. paperNumber: this.curTaskApply.paperNumber
  743. });
  744. this.cards = data || [];
  745. },
  746. async getFlowHistory() {
  747. if (!this.curTaskApply.flowId) return;
  748. const data = await taskFlowDetail(
  749. this.curTaskApply.flowId
  750. ).catch(() => {});
  751. if (!data) return;
  752. const FLOW_STATUS = {
  753. SUBMIT: "success",
  754. APPROVE: "success",
  755. EXCHANGE: "success",
  756. REJECT: "danger",
  757. END: "success"
  758. };
  759. let nextFlowId = "";
  760. this.flowHistoryList = data.tfFlowViewLogResultList.map((item, index) => {
  761. const nextItem = data.tfFlowViewLogResultList[index + 1];
  762. nextFlowId = nextItem ? nextItem.flowId : item.flowId;
  763. item.nextIsNewFlow = nextFlowId !== item.flowId;
  764. item.type = FLOW_STATUS[item.approveOperation];
  765. item.attachments = item.paperAttachmentId
  766. ? JSON.parse(item.paperAttachmentId)
  767. : [];
  768. item.isApproveSetFlowNextNode = false;
  769. return item;
  770. });
  771. const flowIsEnd = data.currFlowTaskResult.taskKey === "end";
  772. this.flowHistoryList.push({
  773. type: flowIsEnd ? "success" : "current",
  774. stepKey: this.$randomCode(),
  775. approveSetup: data.currFlowTaskResult.setup, //审批人节点
  776. approveRemark: data.currFlowTaskResult.taskName, //审批信息
  777. approveOperation: flowIsEnd ? "END" : "",
  778. approveUserName: data.currFlowTaskResult.approveUserNames, //审批人
  779. createTime: null,
  780. nextIsNewFlow: false,
  781. isApproveSetFlowNextNode: false,
  782. attachments: []
  783. });
  784. },
  785. async getFlowList() {
  786. if (!this.curTaskApply.flowId) return;
  787. const data = await flowDetailByFlowId(this.curTaskApply.flowId);
  788. if (!data) return;
  789. const modelType =
  790. data.flowTaskResultList[0] && data.flowTaskResultList[0].modelType;
  791. this.flowInfo = {
  792. customFlowId: data.id,
  793. version: data.version
  794. };
  795. const nextFlowNodeIndex = 1;
  796. this.IS_NEED_SELECT_APPROVE_USER = modelType === "APPROVE_SET";
  797. const flowList = data.flowTaskResultList || [];
  798. this.flowList = flowList.map((flow, index) => {
  799. flow.isApproveSetFlowNextNode =
  800. this.IS_NEED_SELECT_APPROVE_USER && index === nextFlowNodeIndex;
  801. return flow;
  802. });
  803. if (this.flowList.length) {
  804. this.flowList[0].type = "success";
  805. }
  806. this.nextFlowTaskResult = this.flowList[nextFlowNodeIndex];
  807. },
  808. async getCurFlowNodeInfo() {
  809. const data = await taskFlowNodeInfo(this.curTaskApply.flowTaskId);
  810. this.rejectSetupList = (data && data.rejectSetupList) || [];
  811. this.rejectSetupList.forEach(item => {
  812. item.name = `【${item.taskName}】${item.approveUserNames}`;
  813. });
  814. this.nextFlowTaskResult = data.nextFlowTaskResult;
  815. // 被打回给命题老师之后,命题老师只能通过。
  816. if (data.setup === 1 && !this.rejectSetupList.length) {
  817. this.TASK_AUDIT_RESULT = { PASS: "通过" };
  818. this.auditModal.approvePass = "PASS";
  819. }
  820. // 判断发起人自选,是否需要选择下个审核人员
  821. const { modelType, approveUserNames, taskKey } = this.nextFlowTaskResult;
  822. this.IS_NEED_SELECT_APPROVE_USER =
  823. modelType === "APPROVE_SET" &&
  824. taskKey.toLowerCase() !== "end" &&
  825. !approveUserNames;
  826. this.flowHistoryList[
  827. this.flowHistoryList.length - 1
  828. ].isApproveSetFlowNextNode = this.IS_NEED_SELECT_APPROVE_USER;
  829. },
  830. approvePassChange() {
  831. this.auditRules.remark[0].required =
  832. this.auditModal.approvePass === "REJECT";
  833. },
  834. addAtachment() {
  835. if (this.paperAttachments.length >= this.attachmentLimitCount) return;
  836. const name = this.abc[this.paperAttachments.length];
  837. const newAttachment = {
  838. name,
  839. attachmentId: "",
  840. filename: "",
  841. cardId: "",
  842. cardType: "",
  843. createMethod: "",
  844. cardTitle: "",
  845. pages: 0,
  846. canDelete: true,
  847. isExposed: false,
  848. used: false,
  849. createId: null
  850. };
  851. this.paperAttachments.push(newAttachment);
  852. },
  853. deleteAttachment(index) {
  854. if (this.paperAttachments.length <= 1) {
  855. this.$message.error("试卷类型数量不得少于1");
  856. return;
  857. }
  858. this.paperAttachments.splice(index, 1);
  859. this.paperAttachments.forEach((item, itemIndex) => {
  860. item.name = this.abc[itemIndex];
  861. });
  862. if (
  863. this.curTaskApply.drawCount &&
  864. this.curTaskApply.drawCount > this.paperAttachments.length
  865. ) {
  866. this.curTaskApply.drawCount = this.paperAttachments.length;
  867. }
  868. },
  869. toUpload(attachment) {
  870. this.curUploadType = "paper";
  871. this.curAttachment = {
  872. ...attachment
  873. };
  874. this.$refs.UploadPaperDialog.open();
  875. },
  876. toUploadPaperConfirm() {
  877. if (this.paperConfirmAttachments.length >= 4) return;
  878. this.curUploadType = "paperConfirm";
  879. this.curAttachment = {
  880. ...this.paperConfirmAttachmentId
  881. };
  882. this.$refs.UploadPaperDialog.open();
  883. },
  884. uploadConfirm(attachment, uploadType) {
  885. if (uploadType === "paper") {
  886. const index = this.paperAttachments.findIndex(
  887. item => item.name === attachment.name
  888. );
  889. this.paperAttachments.splice(index, 1, { ...attachment });
  890. } else {
  891. this.paperConfirmAttachments.push(attachment);
  892. }
  893. },
  894. deletePaperConfirmAttachment(index) {
  895. this.paperConfirmAttachments.splice(index, 1);
  896. },
  897. toViewCard(attachment) {
  898. window.open(
  899. this.getRouterPath({
  900. name: "CardPreview",
  901. params: {
  902. cardId: attachment.cardId,
  903. viewType: "view"
  904. }
  905. })
  906. );
  907. },
  908. async toCopyCard(attachment) {
  909. this.curAttachment = { ...attachment };
  910. const newCardId = await copyCard(
  911. attachment.cardId,
  912. this.curTaskApply.courseCode
  913. );
  914. this.cardModified(newCardId);
  915. },
  916. toEditCard(attachment) {
  917. this.curAttachment = { ...attachment };
  918. this.$ls.set("prepareTcPCard", {
  919. id: attachment.cardId,
  920. examTaskId: this.curTaskApply.examTaskId,
  921. courseCode: this.curTaskApply.courseCode,
  922. courseName: this.curTaskApply.courseName,
  923. makeMethod: this.curTaskApply.makeMethod,
  924. cardRuleId: this.curTaskApply.cardRuleId,
  925. type: attachment.cardType,
  926. createMethod: attachment.createMethod
  927. });
  928. this.$refs.ModifyCard.open();
  929. },
  930. async cardModified(cardId) {
  931. if (!cardId) return;
  932. await this.getCardList();
  933. let card = this.cards.find(item => item.id === cardId);
  934. const aind = this.paperAttachments.findIndex(
  935. item => item.name === this.curAttachment.name
  936. );
  937. if (aind !== -1 && card) {
  938. this.paperAttachments[aind].cardId = card.id;
  939. this.paperAttachments[aind].cardType = card.type;
  940. this.paperAttachments[aind].createMethod = card.createMethod;
  941. this.paperAttachments[aind].cardTitle = card.title;
  942. this.paperAttachments[aind].used = card.used;
  943. this.paperAttachments[aind].createId = card.createId;
  944. }
  945. },
  946. cardChange(attachment) {
  947. const card = this.cards.find(item => item.id === attachment.cardId);
  948. if (card) {
  949. attachment.cardType = card.type;
  950. attachment.createMethod = card.createMethod;
  951. attachment.cardTitle = card.title;
  952. attachment.used = card.used;
  953. attachment.createId = card.createId;
  954. }
  955. },
  956. cardOptionOpened(visible, attachment) {
  957. if (!visible) return;
  958. const selectedCardIds = this.paperAttachments.map(item => item.cardId);
  959. this.cards = this.cards.map(card => {
  960. card.disabled =
  961. card.id !== attachment.cardId &&
  962. selectedCardIds.includes(card.id) &&
  963. card.type !== "GENERIC";
  964. return card;
  965. });
  966. },
  967. toCreateCard(attachment) {
  968. if (!this.examTask.cardRuleId) {
  969. this.$message.error("题卡规则缺失!");
  970. return;
  971. }
  972. this.curAttachment = { ...attachment };
  973. // 这里只允许新建标准专卡
  974. this.$ls.set("prepareTcPCard", {
  975. courseCode: this.examTask.courseCode,
  976. courseName: this.examTask.courseName,
  977. schoolName: this.$ls.get("schoolName"),
  978. makeMethod: "SELF",
  979. cardRuleId: this.examTask.cardRuleId,
  980. type: "CUSTOM",
  981. createMethod: "STANDARD"
  982. });
  983. this.$refs.ModifyCard.open();
  984. },
  985. cancel() {
  986. this.$emit("cancel");
  987. },
  988. async downloadPaper(attachment) {
  989. if (!attachment.attachmentId) return;
  990. const data = await attachmentPreview(attachment.attachmentId);
  991. window.open(data.url);
  992. },
  993. toSelectNextFlowUser() {
  994. if (!this.IS_NEED_SELECT_APPROVE_USER) return;
  995. this.userLimitCount =
  996. this.nextFlowTaskResult.approveUserCountType === "ONE" ? 1 : 0;
  997. this.userFilterRoles =
  998. this.nextFlowTaskResult.approveUserSelectRange === "ROLE"
  999. ? this.nextFlowTaskResult.approveUserSelectRoles.map(item => item.id)
  1000. : [];
  1001. this.selectUserType = "approve";
  1002. this.curSelectedUsers = this.approveUsers;
  1003. this.$refs.SelectUserDialog.open();
  1004. },
  1005. toSelectExchangeUser() {
  1006. this.userLimitCount = 1;
  1007. this.userFilterRoles = [];
  1008. this.curSelectedUsers = this.exchangeUsers;
  1009. this.selectUserType = "exchange";
  1010. this.$refs.SelectUserDialog.open();
  1011. },
  1012. userSelected(users) {
  1013. if (this.selectUserType === "exchange") {
  1014. this.exchangeUsers = users;
  1015. this.auditModal.userId = users[0].id;
  1016. } else {
  1017. this.approveUsers = users;
  1018. }
  1019. },
  1020. getTaskData() {
  1021. let data = { ...this.curTaskApply };
  1022. data.paperType = this.paperAttachments.map(item => item.name).join(",");
  1023. data.paperAttachmentIds = JSON.stringify(this.paperAttachments, (k, v) =>
  1024. k === "url" ? undefined : v
  1025. );
  1026. data.paperConfirmAttachmentIds = JSON.stringify(
  1027. this.paperConfirmAttachments
  1028. );
  1029. if (this.IS_NEED_SELECT_APPROVE_USER)
  1030. data.approveUserIds = this.approveUsers.map(item => item.id);
  1031. return data;
  1032. },
  1033. checkDataValid() {
  1034. // 设置了入库强制包含试卷时,校验试卷是否上传。
  1035. // 未设置入库强制包含试卷时,若有试卷上传,则需要上传全部。若无试卷上传,则通过。
  1036. if (this.curTaskApply.includePaper) {
  1037. const attachmentValid = !this.paperAttachments.some(
  1038. item => !item.attachmentId
  1039. );
  1040. if (!attachmentValid) {
  1041. this.$message.error("请完成试卷文件上传!");
  1042. return;
  1043. }
  1044. } else {
  1045. const hasUploadPaperAttachments = this.paperAttachments.filter(
  1046. item => item.attachmentId
  1047. );
  1048. if (
  1049. hasUploadPaperAttachments.length > 0 &&
  1050. hasUploadPaperAttachments.length !== this.paperAttachments.length
  1051. ) {
  1052. this.$message.error("有试卷文件未完成上传!");
  1053. return;
  1054. }
  1055. }
  1056. // if (!this.paperConfirmAttachments.length) {
  1057. // this.$message.error("请上传附件!");
  1058. // return;
  1059. // }
  1060. let cardValid = !this.paperAttachments.some(item => !item.cardId);
  1061. if (!cardValid) {
  1062. this.$message.error("有试卷类型未选择题卡!");
  1063. return;
  1064. }
  1065. const usedCards = this.paperAttachments
  1066. .filter(item => item.cardId && item.used)
  1067. .map(item => item.name);
  1068. if (!usedCards.length) {
  1069. this.$message.error(`${usedCards.join()}卷选择的题卡已经被使用过!`);
  1070. return;
  1071. }
  1072. if (this.IS_NEED_SELECT_APPROVE_USER && !this.approveUsers.length) {
  1073. this.$message.error("请设置审核人员!");
  1074. return;
  1075. }
  1076. return true;
  1077. },
  1078. async toSave() {
  1079. if (this.isSubmit) return;
  1080. this.isSubmit = true;
  1081. const datas = this.getTaskData();
  1082. datas.operateType = "STAGE";
  1083. const data = await updateTaskApply(datas).catch(() => {});
  1084. this.isSubmit = false;
  1085. if (!data) return;
  1086. this.$message.success("保存成功!");
  1087. },
  1088. async silentSave() {
  1089. const datas = this.getTaskData();
  1090. datas.operateType = "STAGE";
  1091. await updateTaskApply(datas).catch(() => {});
  1092. },
  1093. async submit() {
  1094. if (!this.checkDataValid()) return;
  1095. const result = await this.$confirm(
  1096. "任务确定提交后,则不可更改试卷及答题卡内容,确定提交该任务?",
  1097. "提示",
  1098. {
  1099. type: "warning"
  1100. }
  1101. ).catch(() => {});
  1102. if (result !== "confirm") return;
  1103. const datas = this.getTaskData();
  1104. datas.operateType = "SUBMIT";
  1105. const data = await updateTaskApply(datas).catch(() => {});
  1106. if (!data) return;
  1107. this.$message.success("提交成功!");
  1108. this.$emit("modified");
  1109. },
  1110. async toAuditSubmit() {
  1111. const valid = await this.$refs.auditModalComp.validate().catch(() => {});
  1112. if (!valid) return;
  1113. if (
  1114. this.auditModal.approvePass === "PASS" &&
  1115. this.IS_NEED_SELECT_APPROVE_USER &&
  1116. !this.approveUsers.length
  1117. ) {
  1118. this.$message.error("请设置审核人员!");
  1119. return;
  1120. }
  1121. const actionName = this.TASK_AUDIT_RESULT[this.auditModal.approvePass];
  1122. const result = await this.$confirm(
  1123. `确定${actionName}该申请吗?`,
  1124. "提示",
  1125. {
  1126. type: "warning"
  1127. }
  1128. ).catch(() => {});
  1129. if (result !== "confirm") return;
  1130. if (this.auditModal.approvePass === "EXCHANGE") {
  1131. let datas = {
  1132. taskId: this.curTaskApply.flowTaskId,
  1133. userId: this.auditModal.userId
  1134. };
  1135. const data = await taskFlowApproverExchange(datas).catch(() => {});
  1136. if (!data) return;
  1137. } else {
  1138. let datas = { ...this.auditModal };
  1139. datas.taskId = this.curTaskApply.flowTaskId;
  1140. if (
  1141. this.auditModal.approvePass === "PASS" &&
  1142. this.IS_NEED_SELECT_APPROVE_USER
  1143. )
  1144. datas.approveUserIds = this.approveUsers.map(item => item.id);
  1145. const data = await taskFlowApprover(datas).catch(() => {});
  1146. if (!data) return;
  1147. }
  1148. this.$message.success("审批成功!");
  1149. this.$emit("modified");
  1150. },
  1151. async toAuditApply() {
  1152. const result = await this.$confirm("确定提交该任务吗?", "提示", {
  1153. type: "warning"
  1154. }).catch(() => {});
  1155. if (result !== "confirm") return;
  1156. const datas = {
  1157. examTaskDetail: this.getTaskData(),
  1158. flowTaskId: this.curTaskApply.flowTaskId,
  1159. approvePass: "PASS"
  1160. };
  1161. const data = await taskAuditApply(datas).catch(() => {});
  1162. if (!data) return;
  1163. this.$message.success("审批成功!");
  1164. this.$emit("modified");
  1165. },
  1166. // image-preview
  1167. toPreview(index) {
  1168. this.curImageIndex = index;
  1169. this.selectImage(index);
  1170. this.$refs.SimpleImagePreview.open();
  1171. },
  1172. selectImage(index) {
  1173. this.curImage = this.paperConfirmAttachments[index];
  1174. },
  1175. toPrevImage() {
  1176. if (this.curImageIndex === 0) {
  1177. this.curImageIndex = this.paperConfirmAttachments.length - 1;
  1178. } else {
  1179. this.curImageIndex--;
  1180. }
  1181. this.selectImage(this.curImageIndex);
  1182. },
  1183. toNextImage() {
  1184. if (this.curImageIndex === this.paperConfirmAttachments.length - 1) {
  1185. this.curImageIndex = 0;
  1186. } else {
  1187. this.curImageIndex++;
  1188. }
  1189. this.selectImage(this.curImageIndex);
  1190. }
  1191. }
  1192. };
  1193. </script>