TaskPaper.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. <template>
  2. <div>
  3. <!-- menu -->
  4. <div
  5. v-if="checkPrivilege('button', 'SelectTikuPaper', 'TaskApplyManage')"
  6. class="mb-4 tab-btns"
  7. >
  8. <el-button
  9. v-for="tab in tabs"
  10. :key="tab.val"
  11. size="medium"
  12. :type="curTab == tab.val ? 'primary' : 'default'"
  13. @click="selectMenu(tab.val)"
  14. >{{ tab.name }}
  15. </el-button>
  16. </div>
  17. <!-- table -->
  18. <table class="table mb-2">
  19. <colgroup>
  20. <col width="100" />
  21. <col width="240" />
  22. <col />
  23. <col v-if="taskStatus.IS_APPLY && !taskStatus.IS_REBUILD" width="80" />
  24. </colgroup>
  25. <tr>
  26. <th>试卷类型</th>
  27. <th>试卷文件</th>
  28. <th>
  29. 答题卡
  30. <span v-if="examTaskInstr" class="tips-markedness">
  31. ({{ examTaskInstr }})
  32. </span>
  33. </th>
  34. <th v-if="taskStatus.IS_APPLY && !taskStatus.IS_REBUILD">操作</th>
  35. </tr>
  36. <tr v-for="(attachment, index) in paperAttachments" :key="index">
  37. <td>
  38. <span>{{ attachment.name }}卷</span>
  39. <span class="color-gray-2" v-if="attachment.isExposed">(已曝光)</span>
  40. </td>
  41. <template v-if="IS_TIKU_TAB">
  42. <!-- 试卷文件 -->
  43. <td>
  44. <template v-if="!attachment.isExposed && taskStatus.IS_APPLY">
  45. <div class="box-justify">
  46. <el-button
  47. type="text"
  48. class="btn-primary box-grow"
  49. @click="toSelect(attachment)"
  50. >
  51. <i
  52. :class="[
  53. 'icon',
  54. attachment.attachmentId ? 'icon-files-act' : 'icon-files',
  55. ]"
  56. ></i
  57. >{{ attachment.filename || "选择试卷" }}
  58. </el-button>
  59. <el-button
  60. type="text"
  61. class="btn-primary box-static"
  62. :disabled="!attachment.paperUrl"
  63. @click="toViewPaper(attachment)"
  64. >预览</el-button
  65. >
  66. </div>
  67. </template>
  68. <el-button
  69. v-else
  70. type="text"
  71. class="btn-primary"
  72. @click="downloadPaper(attachment)"
  73. >
  74. <div
  75. :class="{
  76. 'color-primary': auditLogCache.paper[attachment.attachmentId],
  77. }"
  78. >
  79. <i
  80. class="icon icon-download mr-1"
  81. v-if="attachment.attachmentId"
  82. ></i>
  83. {{ attachment.filename }}
  84. </div>
  85. </el-button>
  86. </td>
  87. <!-- 答题卡 -->
  88. <td>
  89. <template v-if="taskStatus.IS_APPLY">
  90. <el-select
  91. v-model="attachment.cardId"
  92. placeholder="请选择"
  93. style="width: 260px; margin-right: 10px"
  94. @change="(val) => tkCardChange(val, attachment)"
  95. >
  96. <el-option
  97. v-for="item in attachment.cards"
  98. :key="item.id"
  99. :value="item.id"
  100. :label="item.title"
  101. >
  102. <div class="box-justify">
  103. <div class="box-grow">{{ item.title }}</div>
  104. <el-button
  105. class="btn-danger btn-icon box-static"
  106. type="text"
  107. icon="el-icon-remove"
  108. @click="toDeleteCard(item, attachment)"
  109. ></el-button>
  110. </div>
  111. </el-option>
  112. </el-select>
  113. <el-button
  114. class="btn-primary"
  115. type="text"
  116. :disabled="!attachment.cardId"
  117. @click="toViewCard(attachment)"
  118. >预览</el-button
  119. >
  120. <el-button
  121. v-if="!taskStatus.IS_REBUILD"
  122. class="btn-primary"
  123. type="text"
  124. :disabled="!attachment.cardId"
  125. @click="toEditCard(attachment)"
  126. >编辑</el-button
  127. >
  128. </template>
  129. <el-button
  130. v-else
  131. type="text"
  132. class="btn-primary"
  133. @click="toViewCard(attachment)"
  134. ><i
  135. :class="{
  136. 'color-primary': auditLogCache.card[attachment.cardId],
  137. }"
  138. >{{ attachment.cardTitle || "预览" }}</i
  139. ></el-button
  140. >
  141. </td>
  142. </template>
  143. <template v-else>
  144. <!-- 试卷文件 -->
  145. <td>
  146. <el-button
  147. v-if="!attachment.isExposed && taskStatus.IS_APPLY"
  148. type="text"
  149. class="btn-primary"
  150. @click="toUpload(attachment)"
  151. >
  152. <i
  153. :class="[
  154. 'icon',
  155. attachment.attachmentId ? 'icon-files-act' : 'icon-files',
  156. ]"
  157. ></i
  158. >{{
  159. attachment.attachmentId
  160. ? attachment.filename
  161. : "点击上传试卷文件"
  162. }}
  163. </el-button>
  164. <el-button
  165. v-else
  166. type="text"
  167. class="btn-primary"
  168. @click="downloadPaper(attachment)"
  169. >
  170. <div
  171. :class="{
  172. 'color-primary': auditLogCache.paper[attachment.attachmentId],
  173. }"
  174. >
  175. <i
  176. class="icon icon-download mr-1"
  177. v-if="attachment.attachmentId"
  178. ></i>
  179. {{ attachment.filename }}
  180. </div>
  181. </el-button>
  182. </td>
  183. <!-- 答题卡 -->
  184. <td>
  185. <template v-if="taskStatus.IS_APPLY">
  186. <el-select
  187. class="mr-2"
  188. v-model="attachment.cardId"
  189. placeholder="请选择"
  190. style="width: 200px"
  191. filterable
  192. @change="cardChange(attachment)"
  193. >
  194. <el-option
  195. v-for="item in cards"
  196. :key="item.id"
  197. :value="item.id"
  198. :label="item.title"
  199. :disabled="item.disabled"
  200. >
  201. <div class="box-justify">
  202. <div class="box-grow">
  203. <span
  204. :class="[
  205. item.type === 'GENERIC'
  206. ? 'color-success'
  207. : 'color-primary',
  208. 'mr-1',
  209. {
  210. 'color-danger': item.used,
  211. },
  212. ]"
  213. >[{{ item.type === "GENERIC" ? "通" : "专" }}]</span
  214. >
  215. {{ item.title }}
  216. </div>
  217. <el-button
  218. v-if="item.type !== 'GENERIC'"
  219. class="btn-danger btn-icon box-static"
  220. type="text"
  221. icon="el-icon-remove"
  222. @click="toDeleteCard(item, attachment)"
  223. ></el-button>
  224. </div>
  225. </el-option>
  226. </el-select>
  227. <span
  228. v-if="attachment.cardId"
  229. :class="[
  230. attachment.cardType === 'GENERIC'
  231. ? 'color-success'
  232. : 'color-primary',
  233. 'mr-1',
  234. {
  235. 'color-danger': attachment.used,
  236. },
  237. ]"
  238. >[{{ attachment.cardType === "GENERIC" ? "通" : "专" }}]</span
  239. >
  240. <el-button
  241. class="btn-primary"
  242. type="text"
  243. :disabled="!attachment.cardId"
  244. @click="toViewCard(attachment)"
  245. >预览</el-button
  246. >
  247. <template v-if="!taskStatus.IS_REBUILD">
  248. <el-button
  249. class="btn-primary"
  250. type="text"
  251. :disabled="
  252. !attachment.cardId ||
  253. (attachment.cardType === 'GENERIC' &&
  254. attachment.createMethod !== 'STANDARD')
  255. "
  256. @click="toCopyCard(attachment)"
  257. >复制</el-button
  258. >
  259. <el-button
  260. class="btn-primary"
  261. type="text"
  262. :disabled="
  263. !attachment.cardId ||
  264. attachment.cardType === 'GENERIC' ||
  265. !(!attachment.used && attachment.createId === user.id)
  266. "
  267. @click="toEditCard(attachment)"
  268. >编辑</el-button
  269. >
  270. <el-button
  271. class="btn-primary"
  272. type="text"
  273. :disabled="!taskStatus.CAN_CREATE_CARD"
  274. @click="toCreateCard(attachment)"
  275. >新建</el-button
  276. >
  277. </template>
  278. </template>
  279. <el-button
  280. v-else
  281. type="text"
  282. class="btn-primary"
  283. @click="toViewCard(attachment)"
  284. >
  285. <i
  286. :class="{
  287. 'color-primary': auditLogCache.card[attachment.cardId],
  288. }"
  289. >
  290. {{ attachment.cardTitle || "预览" }}
  291. </i>
  292. </el-button>
  293. </td>
  294. </template>
  295. <!-- 操作 -->
  296. <td
  297. v-if="taskStatus.IS_APPLY && !taskStatus.IS_REBUILD"
  298. class="text-right"
  299. >
  300. <el-button
  301. v-if="index === paperAttachments.length - 1"
  302. class="btn-primary btn-icon"
  303. type="text"
  304. icon="el-icon-circle-plus"
  305. @click="addAtachment"
  306. ></el-button>
  307. <el-button
  308. v-if="attachment.canDelete"
  309. class="btn-danger btn-icon"
  310. type="text"
  311. icon="el-icon-remove"
  312. @click="deleteAttachment(index)"
  313. ></el-button>
  314. </td>
  315. </tr>
  316. <tr v-if="!paperAttachments.length">
  317. <td colspan="5">
  318. <p class="tips-info text-center">暂无数据</p>
  319. </td>
  320. </tr>
  321. </table>
  322. <el-form v-if="!taskStatus.IS_REBUILD">
  323. <el-form-item label="单次抽卷卷型数量:" label-width="150">
  324. <el-input-number
  325. v-model="curTaskApply.drawCount"
  326. :min="1"
  327. :max="maxFetchCount"
  328. :step="1"
  329. :controls="false"
  330. step-strictly
  331. :disabled="!taskStatus.IS_APPLY || taskStatus.IS_EXPOSED_MODE"
  332. ></el-input-number>
  333. <!-- :disabled="!taskStatus.IS_APPLY || taskStatus.IS_EXPOSED_MODE" -->
  334. </el-form-item>
  335. </el-form>
  336. <!-- 附件 -->
  337. <h4 class="mb-2">
  338. 附件<span v-if="taskStatus.IS_APPLY">(最多4个)</span>:
  339. </h4>
  340. <div class="image-list">
  341. <div
  342. v-for="(item, index) in imageAttachments"
  343. :key="`image${index}`"
  344. class="image-item"
  345. >
  346. <img
  347. :src="item.url"
  348. :alt="item.filename"
  349. title="点击查看大图"
  350. @click="toPreview(index)"
  351. />
  352. <div v-if="taskStatus.IS_APPLY" class="image-delete">
  353. <i
  354. class="el-icon-delete-solid"
  355. @click="deletePaperConfirmAttachment(item)"
  356. ></i>
  357. </div>
  358. </div>
  359. <div
  360. v-if="paperConfirmAttachments.length < 4 && taskStatus.IS_APPLY"
  361. class="image-item image-add"
  362. title="上传附件"
  363. @click="toUploadPaperConfirm"
  364. >
  365. <i class="el-icon-plus"></i>
  366. </div>
  367. <br />
  368. <div
  369. class="audio-item"
  370. v-for="(item, index) in audioAttachments"
  371. :key="`audio${index}`"
  372. >
  373. <audio :src="item.url" :alt="item.filename" controls></audio>
  374. <div class="audio-delete">
  375. <i
  376. class="el-icon-delete-solid"
  377. @click="deletePaperConfirmAttachment(item)"
  378. ></i>
  379. </div>
  380. </div>
  381. </div>
  382. <div
  383. v-if="!taskStatus.IS_APPLY && !paperConfirmAttachments.length"
  384. class="image-list-none"
  385. >
  386. 暂无数据
  387. </div>
  388. <!-- 附件说明 -->
  389. <h4 class="mb-2">附件说明:</h4>
  390. <el-input
  391. v-if="taskStatus.IS_APPLY"
  392. class="mb-2"
  393. v-model="curTaskApply.remark"
  394. type="textarea"
  395. resize="none"
  396. :rows="2"
  397. :maxlength="100"
  398. clearable
  399. show-word-limit
  400. placeholder="建议不超过100个字"
  401. ></el-input>
  402. <div class="color-gray-2" v-else>
  403. <p v-if="curTaskApply.remark">{{ curTaskApply.remark }}</p>
  404. <p v-else>暂无</p>
  405. </div>
  406. <!-- upload-paper-dialog -->
  407. <upload-paper-dialog
  408. :paper-attachment="curAttachment"
  409. :upload-type="curUploadType"
  410. @confirm="uploadConfirm"
  411. ref="UploadPaperDialog"
  412. ></upload-paper-dialog>
  413. <!-- image-preview -->
  414. <simple-image-preview
  415. :cur-image="curImage"
  416. @on-prev="toPrevImage"
  417. @on-next="toNextImage"
  418. ref="SimpleImagePreview"
  419. ></simple-image-preview>
  420. <!-- ModifyCard -->
  421. <modify-card ref="ModifyCard" @modified="cardModified"></modify-card>
  422. <!-- SelectTikuPaperDialog -->
  423. <select-tiku-paper-dialog
  424. ref="SelectTikuPaperDialog"
  425. :row="curAttachment"
  426. @confirm="tikuPaperSelected"
  427. ></select-tiku-paper-dialog>
  428. <!-- CardBuildDialog -->
  429. <card-build-dialog
  430. ref="CardBuildDialog"
  431. :presetData="cardBuildPresetData"
  432. @confirm="cardBuildConfirm"
  433. ></card-build-dialog>
  434. <!-- PreviewAttachment -->
  435. <preview-attachment
  436. ref="PreviewAttachment"
  437. :attachment-id="curAttachmentId"
  438. ></preview-attachment>
  439. <!-- card-preview-dialog -->
  440. <card-preview-dialog
  441. ref="CardPreviewDialog"
  442. :card-id="curAttachment.cardId"
  443. show-watermark
  444. ></card-preview-dialog>
  445. </div>
  446. </template>
  447. <script>
  448. import { mapState, mapMutations, mapActions } from "vuex";
  449. import { cardForSelectList } from "../../api";
  450. import { attachmentPreview } from "../../../login/api";
  451. import { deleteCard } from "../../../base/api";
  452. import { copyCard } from "../../../card/api";
  453. import UploadPaperDialog from "../UploadPaperDialog.vue";
  454. import SelectTikuPaperDialog from "../createExamAndPrintTask/SelectTikuPaperDialog.vue";
  455. import CardBuildDialog from "../../../card/components/CardBuildDialog.vue";
  456. import CardPreviewDialog from "../../../card/components/CardPreviewDialog.vue";
  457. import SimpleImagePreview from "../../../../components/SimpleImagePreview.vue";
  458. import ModifyCard from "../../../card/components/ModifyCard.vue";
  459. import PreviewAttachment from "@/components/PreviewAttachment.vue";
  460. export default {
  461. name: "task-paper",
  462. components: {
  463. UploadPaperDialog,
  464. SimpleImagePreview,
  465. ModifyCard,
  466. CardBuildDialog,
  467. CardPreviewDialog,
  468. SelectTikuPaperDialog,
  469. PreviewAttachment,
  470. },
  471. data() {
  472. return {
  473. tabs: [
  474. {
  475. name: "上传本地试卷",
  476. val: "upload",
  477. },
  478. {
  479. name: "从题库选择试卷",
  480. val: "tiku",
  481. },
  482. ],
  483. curTab: "upload",
  484. uuid: "",
  485. user: {},
  486. paperConfirmAttachmentId: {
  487. attachmentId: "",
  488. filename: "",
  489. url: "",
  490. fileType: "",
  491. },
  492. paperAttachments: [],
  493. paperConfirmAttachments: [],
  494. curAttachment: {},
  495. curUploadType: "paper",
  496. attachmentLimitCount: 26,
  497. abc: "abcdefghijklmnopqrstuvwxyz".toUpperCase(),
  498. cards: [],
  499. examTaskInstr: this.$ls.get("schoolInfo", { examTaskInstr: "" })
  500. .examTaskInstr,
  501. // attachmentId preview
  502. curAttachmentId: "",
  503. // card-build
  504. cardBuildPresetData: {},
  505. // image-preview
  506. curImage: {},
  507. curImageIndex: 0,
  508. };
  509. },
  510. computed: {
  511. ...mapState("exam", [
  512. "editType",
  513. "examTask",
  514. "curTaskApply",
  515. "taskStatus",
  516. "auditLogCache",
  517. ]),
  518. IS_TIKU_TAB() {
  519. return this.curTab === "tiku";
  520. },
  521. maxFetchCount() {
  522. return this.paperAttachments.length < 1
  523. ? 1
  524. : this.paperAttachments.length;
  525. },
  526. imageAttachments() {
  527. return this.paperConfirmAttachments.filter(
  528. (item) => item.fileType === "image"
  529. );
  530. },
  531. audioAttachments() {
  532. return this.paperConfirmAttachments.filter(
  533. (item) => item.fileType === "audio"
  534. );
  535. },
  536. },
  537. mounted() {
  538. this.initData();
  539. },
  540. methods: {
  541. ...mapMutations("exam", ["setEditType", "setExamTask", "setCurTaskApply"]),
  542. ...mapActions("exam", ["addPreviewLog"]),
  543. initData() {
  544. this.user = this.$ls.get("user", {});
  545. this.paperAttachments = this.curTaskApply.paperAttachmentIds
  546. ? JSON.parse(this.curTaskApply.paperAttachmentIds)
  547. : [];
  548. if (!this.paperAttachments.length) {
  549. this.addAtachment();
  550. }
  551. const pAttachment = this.paperAttachments.some((item) => !!item.paperId);
  552. if (pAttachment) {
  553. this.curTab = "tiku";
  554. this.uuid = pAttachment.uuid || this.$randomCode(32);
  555. } else {
  556. this.curTab = "upload";
  557. this.uuid = this.$randomCode(32);
  558. }
  559. const exposedPaperType = this.curTaskApply.exposedPaperType || "";
  560. let exposedPaperTypes = exposedPaperType.split(",");
  561. exposedPaperTypes.sort((a, b) => (a > b ? -1 : 1));
  562. const maxExposedPaperType = exposedPaperTypes[0];
  563. this.paperAttachments.forEach((paper) => {
  564. paper.canDelete = maxExposedPaperType
  565. ? paper.name > maxExposedPaperType
  566. : true;
  567. paper.isExposed = exposedPaperTypes.includes(paper.name);
  568. });
  569. this.paperConfirmAttachments = this.curTaskApply.paperConfirmAttachmentIds
  570. ? JSON.parse(this.curTaskApply.paperConfirmAttachmentIds)
  571. : [];
  572. if (this.taskStatus.IS_APPLY) this.getCardList();
  573. },
  574. async selectMenu(tab) {
  575. if (!this.taskStatus.IS_APPLY) return;
  576. const attachment = this.paperAttachments[0];
  577. if (attachment.cardId || attachment.attachmentId) {
  578. const result = await this.$confirm(
  579. "更改类型会清空已设置数据,确定要更改类型?",
  580. "提示",
  581. {
  582. type: "warning",
  583. }
  584. ).catch(() => {});
  585. if (result !== "confirm") return;
  586. this.paperAttachments = [];
  587. this.addAtachment();
  588. }
  589. this.curTab = tab;
  590. },
  591. async getCardList() {
  592. if (!this.curTaskApply.courseId || !this.curTaskApply.examId) return;
  593. const data = await cardForSelectList({
  594. courseId: this.curTaskApply.courseId,
  595. examId: this.curTaskApply.examId,
  596. paperNumber: this.curTaskApply.paperNumber,
  597. });
  598. this.cards = data || [];
  599. if (this.taskStatus.IS_REBUILD) {
  600. this.cards = this.cards.filter((item) => item.type === "GENERIC");
  601. }
  602. },
  603. async getTkCardList(attachment) {
  604. if (
  605. !this.curTaskApply.courseId ||
  606. !this.curTaskApply.examId ||
  607. !attachment.paperId
  608. )
  609. return;
  610. const data = await cardForSelectList({
  611. courseId: this.curTaskApply.courseId,
  612. examId: this.curTaskApply.examId,
  613. paperId: attachment.paperId,
  614. });
  615. attachment.cards = data || [];
  616. },
  617. addAtachment() {
  618. if (this.paperAttachments.length >= this.attachmentLimitCount) return;
  619. const name = this.abc[this.paperAttachments.length];
  620. const newAttachment = {
  621. name,
  622. attachmentId: "",
  623. filename: "",
  624. paperId: null,
  625. paperUrl: null,
  626. uuid: null,
  627. cardId: "",
  628. cardType: "",
  629. createMethod: "",
  630. cardTitle: "",
  631. pages: 0,
  632. canDelete: true,
  633. isExposed: false,
  634. used: false,
  635. createId: null,
  636. cards: [],
  637. };
  638. this.paperAttachments.push(newAttachment);
  639. },
  640. deleteAttachment(index) {
  641. if (this.paperAttachments.length <= 1) {
  642. this.$message.error("试卷类型数量不得少于1");
  643. return;
  644. }
  645. this.paperAttachments.splice(index, 1);
  646. this.paperAttachments.forEach((item, itemIndex) => {
  647. item.name = this.abc[itemIndex];
  648. });
  649. if (
  650. this.curTaskApply.drawCount &&
  651. this.curTaskApply.drawCount > this.paperAttachments.length
  652. ) {
  653. this.curTaskApply.drawCount = this.paperAttachments.length;
  654. }
  655. },
  656. toUpload(attachment) {
  657. this.curUploadType = "paper";
  658. this.curAttachment = {
  659. ...attachment,
  660. };
  661. this.$refs.UploadPaperDialog.open();
  662. },
  663. toUploadPaperConfirm() {
  664. if (this.paperConfirmAttachments.length >= 4) return;
  665. this.curUploadType = "paperConfirm";
  666. this.curAttachment = {
  667. ...this.paperConfirmAttachmentId,
  668. };
  669. this.$refs.UploadPaperDialog.open();
  670. },
  671. uploadConfirm(attachment, uploadType) {
  672. if (uploadType === "paper") {
  673. const index = this.paperAttachments.findIndex(
  674. (item) => item.name === attachment.name
  675. );
  676. this.paperAttachments.splice(index, 1, { ...attachment });
  677. } else {
  678. this.paperConfirmAttachments.push(attachment);
  679. }
  680. },
  681. deletePaperConfirmAttachment(data) {
  682. const index = this.paperConfirmAttachments.findIndex(
  683. (item) => item.url === data.url
  684. );
  685. this.paperConfirmAttachments.splice(index, 1);
  686. },
  687. toViewCard(attachment) {
  688. this.addPreviewLog({ attachment, type: "card" });
  689. this.curAttachment = { ...attachment };
  690. this.$refs.CardPreviewDialog.open();
  691. },
  692. async toCopyCard(attachment) {
  693. this.curAttachment = { ...attachment };
  694. const newCardId = await copyCard(
  695. attachment.cardId,
  696. this.curTaskApply.courseId
  697. );
  698. this.cardModified({ id: newCardId });
  699. },
  700. toEditCard(attachment) {
  701. this.curAttachment = { ...attachment };
  702. this.$ls.set("prepareTcPCard", {
  703. id: attachment.cardId,
  704. examTaskId: this.curTaskApply.examTaskId,
  705. courseId: this.curTaskApply.courseId,
  706. courseName: this.curTaskApply.courseName,
  707. makeMethod: this.curTaskApply.makeMethod,
  708. cardRuleId: this.curTaskApply.cardRuleId,
  709. type: attachment.cardType,
  710. createMethod: attachment.createMethod,
  711. });
  712. this.$refs.ModifyCard.open();
  713. },
  714. async cardModified(data) {
  715. // data: {id,title}
  716. if (!data.id) return;
  717. if (this.IS_TIKU_TAB) {
  718. const aind = this.paperAttachments.findIndex(
  719. (item) => item.name === this.curAttachment.name
  720. );
  721. this.paperAttachments[aind].cardTitle = data.title;
  722. await this.getTkCardList(this.curAttachment);
  723. return;
  724. }
  725. await this.getCardList();
  726. let card = this.cards.find((item) => item.id === data.id);
  727. const aind = this.paperAttachments.findIndex(
  728. (item) => item.name === this.curAttachment.name
  729. );
  730. if (aind !== -1 && card) {
  731. this.paperAttachments[aind].cardId = card.id;
  732. this.paperAttachments[aind].cardType = card.type;
  733. this.paperAttachments[aind].createMethod = card.createMethod;
  734. this.paperAttachments[aind].cardTitle = card.title;
  735. this.paperAttachments[aind].used = card.used;
  736. this.paperAttachments[aind].createId = card.createId;
  737. }
  738. },
  739. cardChange(attachment) {
  740. const card = this.cards.find((item) => item.id === attachment.cardId);
  741. if (card) {
  742. attachment.cardType = card.type;
  743. attachment.createMethod = card.createMethod;
  744. attachment.cardTitle = card.title;
  745. attachment.used = card.used;
  746. attachment.createId = card.createId;
  747. }
  748. },
  749. tkCardChange(card, attachment) {
  750. attachment.cardTitle = card.title;
  751. },
  752. async toDeleteCard(card, attachment) {
  753. const confirm = await this.$confirm(
  754. `确定要删除题卡【${card.title}】吗?`,
  755. "提示",
  756. {
  757. type: "warning",
  758. }
  759. ).catch(() => {});
  760. if (confirm !== "confirm") return;
  761. await deleteCard(card.id);
  762. this.$message.success("删除成功!");
  763. if (attachment.cardId === card.id) {
  764. attachment.cardId = null;
  765. attachment.cardTitle = "";
  766. }
  767. if (this.IS_TIKU_TAB) {
  768. attachment.cards = attachment.cards.filter(
  769. (item) => item.id !== card.id
  770. );
  771. } else {
  772. await this.getCardList();
  773. }
  774. },
  775. async toCreateCard(attachment) {
  776. if (!this.examTask.cardRuleId) {
  777. this.$message.error("题卡规则缺失!");
  778. return;
  779. }
  780. const res = await this.$prompt("确定要提交当前题卡吗?", "提示", {
  781. type: "warning",
  782. showInput: true,
  783. inputPlaceholder: "请输入题卡名称",
  784. inputValidator: (val) => {
  785. if (!val) return "请输入题卡名称!";
  786. if (val.length > 50) return "题卡名称不得超过50个字符!";
  787. return true;
  788. },
  789. }).catch(() => {});
  790. if (!res || res.action !== "confirm") return;
  791. this.curAttachment = { ...attachment };
  792. // 这里只允许新建标准专卡
  793. this.$ls.set("prepareTcPCard", {
  794. courseId: this.examTask.courseId,
  795. courseName: this.examTask.courseName,
  796. schoolName: this.$ls.get("schoolName"),
  797. makeMethod: "SELF",
  798. cardName: res.value,
  799. cardRuleId: this.examTask.cardRuleId,
  800. type: "CUSTOM",
  801. createMethod: "STANDARD",
  802. });
  803. this.$refs.ModifyCard.open();
  804. },
  805. async downloadPaper(attachment) {
  806. if (!attachment.attachmentId) return;
  807. this.addPreviewLog({ attachment, type: "paper" });
  808. const data = await attachmentPreview(attachment.attachmentId);
  809. window.open(data.url);
  810. // TODO:
  811. },
  812. // select-paper
  813. toSelect(attachment) {
  814. this.curAttachment = {
  815. ...attachment,
  816. courseId: this.examTask.courseId,
  817. };
  818. this.$refs.SelectTikuPaperDialog.open();
  819. },
  820. async tikuPaperSelected(data) {
  821. this.cardBuildPresetData = {
  822. examId: this.examTask.examId,
  823. courseId: this.examTask.courseId,
  824. courseName: this.examTask.courseName,
  825. schoolName: this.$ls.get("schoolName"),
  826. makeMethod: "SELF",
  827. cardName: "",
  828. cardRuleId: this.examTask.cardRuleId,
  829. type: "CUSTOM",
  830. createMethod: "STANDARD",
  831. paperId: data.id,
  832. paperName: data.name,
  833. uuid: this.uuid,
  834. };
  835. this.$refs.CardBuildDialog.open();
  836. },
  837. cardBuildConfirm(data) {
  838. if (!data.success) {
  839. this.$message.error(data.message);
  840. return;
  841. }
  842. const ind = this.paperAttachments.findIndex(
  843. (item) => item.name === this.curAttachment.name
  844. );
  845. if (ind === -1) return;
  846. const info = data.data;
  847. this.curAttachment = { ...this.paperAttachments[ind] };
  848. this.paperAttachments[ind] = Object.assign(this.paperAttachments[ind], {
  849. paperId: this.cardBuildPresetData.paperId,
  850. cardType: this.cardBuildPresetData.type,
  851. createMethod: this.cardBuildPresetData.createMethod,
  852. filename: this.cardBuildPresetData.paperName,
  853. cardId: info.id,
  854. cardTitle: info.title,
  855. uuid: info.uuid,
  856. attachmentId: info.attachmentId,
  857. paperUrl: info.paperUrl,
  858. });
  859. this.getTkCardList(this.paperAttachments[ind]);
  860. },
  861. toViewPaper(attachment) {
  862. if (!attachment.paperUrl) return;
  863. window.open(attachment.paperUrl);
  864. // TODO:
  865. },
  866. toViewAttachment(curAttachmentId) {
  867. this.curAttachmentId = curAttachmentId;
  868. this.$refs.PreviewAttachment.open();
  869. },
  870. // image-preview
  871. toPreview(index) {
  872. this.curImageIndex = index;
  873. this.selectImage(index);
  874. this.$refs.SimpleImagePreview.open();
  875. },
  876. selectImage(index) {
  877. this.curImage = this.imageAttachments[index];
  878. },
  879. toPrevImage() {
  880. if (this.curImageIndex === 0) {
  881. this.curImageIndex = this.imageAttachments.length - 1;
  882. } else {
  883. this.curImageIndex--;
  884. }
  885. this.selectImage(this.curImageIndex);
  886. },
  887. toNextImage() {
  888. if (this.curImageIndex === this.imageAttachments.length - 1) {
  889. this.curImageIndex = 0;
  890. } else {
  891. this.curImageIndex++;
  892. }
  893. this.selectImage(this.curImageIndex);
  894. },
  895. // action
  896. getData() {
  897. let data = { ...this.curTaskApply };
  898. data.paperType = this.paperAttachments.map((item) => item.name).join(",");
  899. data.paperAttachmentIds = JSON.stringify(this.paperAttachments, (k, v) =>
  900. k === "url" ? undefined : v
  901. );
  902. data.paperConfirmAttachmentIds = JSON.stringify(
  903. this.paperConfirmAttachments
  904. );
  905. return data;
  906. },
  907. checkData() {
  908. if (this.IS_TIKU_TAB) {
  909. const paperValid = !this.paperAttachments.some((item) => !item.paperId);
  910. if (!paperValid) {
  911. this.$message.error("请完成试卷选择!");
  912. return;
  913. }
  914. const cardValid = !this.paperAttachments.some((item) => !item.cardId);
  915. if (!cardValid) {
  916. this.$message.error("有试卷类型未选择题卡!");
  917. return;
  918. }
  919. } else {
  920. // 设置了入库强制包含试卷时,校验试卷是否上传。
  921. // 未设置入库强制包含试卷时,若有试卷上传,则需要上传全部。若无试卷上传,则通过。
  922. if (this.curTaskApply.includePaper) {
  923. const attachmentValid = !this.paperAttachments.some(
  924. (item) => !item.attachmentId
  925. );
  926. if (!attachmentValid) {
  927. this.$message.error("请完成试卷文件上传!");
  928. return;
  929. }
  930. } else {
  931. const hasUploadPaperAttachments = this.paperAttachments.filter(
  932. (item) => item.attachmentId
  933. );
  934. if (
  935. hasUploadPaperAttachments.length > 0 &&
  936. hasUploadPaperAttachments.length !== this.paperAttachments.length
  937. ) {
  938. this.$message.error("有试卷文件未完成上传!");
  939. return;
  940. }
  941. }
  942. const cardValid = !this.paperAttachments.some((item) => !item.cardId);
  943. if (!cardValid) {
  944. this.$message.error("有试卷类型未选择题卡!");
  945. return;
  946. }
  947. }
  948. return true;
  949. },
  950. },
  951. };
  952. </script>