ModifyTaskPaper.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. <template>
  2. <div class="modify-task-paper">
  3. <el-dialog
  4. class="task-detail"
  5. :visible.sync="modalIsShow"
  6. :title="title"
  7. top="10vh"
  8. width="900px"
  9. :close-on-click-modal="false"
  10. :close-on-press-escape="false"
  11. append-to-body
  12. @open="visibleChange"
  13. >
  14. <div class="part-box part-box-pad part-box-border">
  15. <el-form class="form-info" label-width="100px">
  16. <el-row>
  17. <el-col :span="12">
  18. <el-form-item label="试卷编号:">
  19. <span>{{ instance.paperNumber }}</span>
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="12">
  23. <el-form-item label="课程(代码):">
  24. <span>
  25. {{ instance.courseName }}({{ instance.courseCode }})
  26. </span>
  27. </el-form-item>
  28. </el-col>
  29. </el-row>
  30. <el-row>
  31. <el-col :span="12">
  32. <el-form-item label="卷型:">
  33. <span>{{ instance.paperType }}</span>
  34. </el-form-item>
  35. </el-col>
  36. <el-col :span="12">
  37. <el-form-item label="命题老师:">
  38. <span>{{ instance.userName }}</span>
  39. </el-form-item>
  40. </el-col>
  41. </el-row>
  42. <el-row>
  43. <el-col :span="12">
  44. <el-form-item label="已曝光:">
  45. <span>{{
  46. instance.exposedPaperType | defaultFieldFilter
  47. }}</span>
  48. </el-form-item>
  49. </el-col>
  50. <el-col :span="12">
  51. <el-form-item label="未曝光:">
  52. <span>{{
  53. instance.unexposedPaperType | defaultFieldFilter
  54. }}</span>
  55. </el-form-item>
  56. </el-col>
  57. </el-row>
  58. </el-form>
  59. </div>
  60. <div>
  61. <div v-if="IS_EDIT" class="mb-2 text-right">
  62. <el-button
  63. type="info"
  64. icon="el-icon-circle-plus-outline"
  65. @click="addAtachment"
  66. >增加卷型</el-button
  67. >
  68. </div>
  69. <table class="table">
  70. <colgroup>
  71. <col width="100" />
  72. <col width="280" />
  73. <col />
  74. <col v-if="IS_EDIT" width="60" />
  75. </colgroup>
  76. <tr>
  77. <th>试卷类型</th>
  78. <th>试卷文件</th>
  79. <th>答题卡</th>
  80. <th v-if="IS_EDIT">操作</th>
  81. </tr>
  82. <tr v-for="(attachment, index) in paperAttachments" :key="index">
  83. <td>
  84. <span>{{ attachment.name }}卷</span>
  85. <span class="color-gray-2" v-if="attachment.isExposed"
  86. >(已曝光)</span
  87. >
  88. </td>
  89. <td>
  90. <el-button
  91. v-if="!attachment.isExposed && IS_EDIT"
  92. type="text"
  93. class="btn-primary"
  94. @click="toUpload(attachment)"
  95. >
  96. <i
  97. :class="[
  98. 'icon',
  99. attachment.attachmentId ? 'icon-files-act' : 'icon-files',
  100. ]"
  101. ></i>
  102. {{
  103. attachment.attachmentId
  104. ? attachment.filename
  105. : "点击上传试卷文件"
  106. }}
  107. </el-button>
  108. <el-button
  109. v-else
  110. type="text"
  111. class="btn-primary"
  112. @click="downloadPaper(attachment)"
  113. >
  114. <i
  115. class="icon icon-download mr-1"
  116. v-if="attachment.attachmentId"
  117. ></i>
  118. {{ attachment.filename }}
  119. </el-button>
  120. </td>
  121. <td>
  122. <template v-if="CAN_EDIT_CARD">
  123. <el-select
  124. class="mr-2"
  125. v-model="attachment.cardId"
  126. placeholder="请选择"
  127. style="width: 200px"
  128. filterable
  129. @visible-change="
  130. (visible) => cardOptionOpened(visible, attachment)
  131. "
  132. @change="cardChange(attachment)"
  133. >
  134. <el-option
  135. v-for="item in cards"
  136. :key="item.id"
  137. :value="item.id"
  138. :label="item.title"
  139. :disabled="item.disabled"
  140. >
  141. <span
  142. :class="[
  143. item.type === 'GENERIC'
  144. ? 'color-success'
  145. : 'color-primary',
  146. 'mr-1',
  147. {
  148. 'color-danger': item.used,
  149. },
  150. ]"
  151. >[{{ item.type === "GENERIC" ? "通" : "专" }}]</span
  152. >
  153. {{ item.title }}
  154. </el-option>
  155. </el-select>
  156. <span
  157. v-if="attachment.cardId"
  158. :class="[
  159. attachment.cardType === 'GENERIC'
  160. ? 'color-success'
  161. : 'color-primary',
  162. 'mr-1',
  163. {
  164. 'color-danger': attachment.used,
  165. },
  166. ]"
  167. >[{{ attachment.cardType === "GENERIC" ? "通" : "专" }}]</span
  168. >
  169. <el-button
  170. class="btn-primary"
  171. type="text"
  172. :disabled="!attachment.cardId"
  173. @click="toViewCard(attachment)"
  174. >预览</el-button
  175. >
  176. <el-button
  177. class="btn-primary"
  178. type="text"
  179. :disabled="
  180. !attachment.cardId ||
  181. (attachment.cardType === 'GENERIC' &&
  182. attachment.createMethod !== 'STANDARD')
  183. "
  184. @click="toCopyCard(attachment)"
  185. >复制</el-button
  186. >
  187. <el-button
  188. class="btn-primary"
  189. type="text"
  190. :disabled="
  191. !attachment.cardId ||
  192. attachment.cardType === 'GENERIC' ||
  193. !(!attachment.used && attachment.createId === user.id) ||
  194. !(
  195. attachment.used &&
  196. attachment.savedCardId &&
  197. attachment.savedCardId === attachment.cardId
  198. )
  199. "
  200. @click="toEditCard(attachment)"
  201. >编辑</el-button
  202. >
  203. <el-button
  204. class="btn-primary"
  205. type="text"
  206. :disabled="!canCreateCard"
  207. @click="toCreateCard(attachment)"
  208. >新建</el-button
  209. >
  210. </template>
  211. <el-button
  212. v-else
  213. type="text"
  214. class="btn-primary"
  215. @click="toViewCard(attachment)"
  216. >
  217. {{ attachment.cardTitle || "预览" }}
  218. </el-button>
  219. </td>
  220. <td v-if="IS_EDIT">
  221. <el-button
  222. v-if="attachment.canDelete"
  223. class="btn-danger btn-icon"
  224. type="text"
  225. icon="el-icon-remove"
  226. @click="deleteAttachment(index)"
  227. ></el-button>
  228. </td>
  229. </tr>
  230. </table>
  231. <el-form>
  232. <el-form-item label="单次抽卷卷型数量:" label-width="150">
  233. <el-input-number
  234. v-model="curTaskApply.drawCount"
  235. :min="1"
  236. :max="maxFetchCount"
  237. :step="1"
  238. step-strictly
  239. :controls="false"
  240. :disabled="!IS_EDIT || exposedMode"
  241. ></el-input-number>
  242. </el-form-item>
  243. </el-form>
  244. </div>
  245. <div slot="footer">
  246. <el-button
  247. v-if="IS_EDIT"
  248. type="primary"
  249. :disabled="isSubmit"
  250. @click="submit"
  251. >确认提交</el-button
  252. >
  253. <el-button @click="cancel">取消</el-button>
  254. </div>
  255. </el-dialog>
  256. <!-- upload-paper-dialog -->
  257. <upload-paper-dialog
  258. :paper-attachment="curAttachment"
  259. :upload-type="curUploadType"
  260. @confirm="uploadConfirm"
  261. ref="UploadPaperDialog"
  262. ></upload-paper-dialog>
  263. <!-- ModifyCard -->
  264. <modify-card ref="ModifyCard" @modified="cardModified"></modify-card>
  265. <!-- card-preview-dialog -->
  266. <card-preview-dialog
  267. ref="CardPreviewDialog"
  268. :card-id="curAttachment.cardId"
  269. ></card-preview-dialog>
  270. </div>
  271. </template>
  272. <script>
  273. import UploadPaperDialog from "./UploadPaperDialog";
  274. import ModifyCard from "../../card/components/ModifyCard";
  275. import CardPreviewDialog from "../../card/components/CardPreviewDialog.vue";
  276. import { taskApplyDetail, taskPaperApplyEdit, cardForSelectList } from "../api";
  277. import { attachmentPreview } from "../../login/api";
  278. import { COMMON_CARD_RULE_ID } from "@/constants/enumerate";
  279. import { copyCard } from "../../card/api";
  280. const initTaskApply = {
  281. examId: "",
  282. examTaskId: "",
  283. paperType: "A",
  284. paperAttachmentIds: "",
  285. cardId: "",
  286. cardRuleId: "",
  287. makeMethod: "",
  288. courseCode: "",
  289. courseName: "",
  290. drawCount: 1,
  291. exposedPaperType: "",
  292. // 题卡状态
  293. status: "",
  294. // 考务规则
  295. includePaper: false,
  296. customCard: false,
  297. };
  298. export default {
  299. name: "modify-task-paper",
  300. components: { UploadPaperDialog, ModifyCard, CardPreviewDialog },
  301. props: {
  302. instance: {
  303. type: Object,
  304. default() {
  305. return {};
  306. },
  307. },
  308. editType: {
  309. type: String,
  310. default: "PREVIEW",
  311. validator: (val) => ["EDIT", "PREVIEW"].includes(val),
  312. },
  313. },
  314. computed: {
  315. title() {
  316. const names = {
  317. EDIT: "申请编辑卷库",
  318. PREVIEW: "卷库详情",
  319. };
  320. return names[this.editType];
  321. },
  322. IS_PREVIEW() {
  323. return this.editType === "PREVIEW";
  324. },
  325. IS_EDIT() {
  326. return this.editType === "EDIT";
  327. },
  328. CAN_EDIT_CARD() {
  329. return this.editType === "EDIT" && !this.instance.exposedPaperType;
  330. },
  331. maxFetchCount() {
  332. return this.paperAttachments.length < 1
  333. ? 1
  334. : this.paperAttachments.length;
  335. },
  336. canCreateCard() {
  337. return (
  338. this.curTaskApply.courseCode &&
  339. this.curTaskApply.examId &&
  340. this.curTaskApply.cardRuleId !== COMMON_CARD_RULE_ID
  341. );
  342. },
  343. exposedMode() {
  344. return !!this.curTaskApply.exposedPaperType;
  345. },
  346. },
  347. data() {
  348. return {
  349. isSubmit: false,
  350. modalIsShow: false,
  351. curTaskApply: { ...initTaskApply },
  352. paperAttachments: [],
  353. curAttachment: {},
  354. curUploadType: "paper",
  355. abc: "abcdefghijklmnopqrstuvwxyz".toUpperCase(),
  356. cards: [],
  357. user: this.$ls.get("user", {}),
  358. };
  359. },
  360. methods: {
  361. async getCardList() {
  362. if (!this.curTaskApply.courseCode || !this.curTaskApply.examId) return;
  363. const data = await cardForSelectList({
  364. courseCode: this.curTaskApply.courseCode,
  365. examId: this.curTaskApply.examId,
  366. paperNumber: this.instance.paperNumber,
  367. });
  368. this.cards = data || [];
  369. },
  370. async visibleChange() {
  371. const data = await taskApplyDetail(this.instance.id);
  372. this.curTaskApply = this.$objAssign(initTaskApply, data || {});
  373. this.curTaskApply.courseCode = this.instance.courseCode;
  374. this.curTaskApply.courseName = this.instance.courseName;
  375. this.curTaskApply.cardRuleId = this.instance.cardRuleId;
  376. this.curTaskApply.includePaper =
  377. data.printContent.indexOf("PAPER") !== -1;
  378. this.paperAttachments = data.paperAttachmentIds
  379. ? JSON.parse(data.paperAttachmentIds)
  380. : [];
  381. const exposedPaperType = data.exposedPaperType || "";
  382. let exposedPaperTypes = exposedPaperType.split(",");
  383. exposedPaperTypes.sort((a, b) => (a > b ? -1 : 1));
  384. const maxExposedPaperType = exposedPaperTypes[0];
  385. this.paperAttachments.forEach((paper) => {
  386. paper.canDelete = maxExposedPaperType
  387. ? paper.name > maxExposedPaperType
  388. : true;
  389. paper.isExposed = exposedPaperTypes.includes(paper.name);
  390. paper.savedCardId = paper.cardId;
  391. });
  392. this.$nextTick(() => {
  393. if (this.CAN_EDIT_CARD) {
  394. this.getCardList();
  395. }
  396. });
  397. },
  398. cancel() {
  399. this.modalIsShow = false;
  400. },
  401. open() {
  402. this.modalIsShow = true;
  403. },
  404. addAtachment() {
  405. if (this.paperAttachments.length >= this.abc.length) return;
  406. const name = this.abc[this.paperAttachments.length];
  407. const newAttachment = {
  408. name,
  409. attachmentId: "",
  410. filename: "",
  411. savedCardId: null,
  412. cardId: "",
  413. cardType: "",
  414. createMethod: "",
  415. cardTitle: "",
  416. pages: 0,
  417. canDelete: true,
  418. isExposed: false,
  419. used: false,
  420. createId: null,
  421. };
  422. this.paperAttachments.push(newAttachment);
  423. },
  424. deleteAttachment(index) {
  425. if (this.paperAttachments.length <= 1) {
  426. this.$message.error("试卷类型数量不得少于1");
  427. return;
  428. }
  429. this.paperAttachments.splice(index, 1);
  430. this.paperAttachments.forEach((item, itemIndex) => {
  431. item.name = this.abc[itemIndex];
  432. });
  433. if (
  434. this.curTaskApply.drawCount &&
  435. this.curTaskApply.drawCount > this.paperAttachments.length
  436. ) {
  437. this.curTaskApply.drawCount = this.paperAttachments.length;
  438. }
  439. },
  440. toUpload(attachment) {
  441. this.curUploadType = "paper";
  442. this.curAttachment = {
  443. ...attachment,
  444. };
  445. this.$refs.UploadPaperDialog.open();
  446. },
  447. uploadConfirm(attachment, uploadType) {
  448. if (uploadType === "paper") {
  449. const index = this.paperAttachments.findIndex(
  450. (item) => item.name === attachment.name
  451. );
  452. this.paperAttachments.splice(index, 1, { ...attachment });
  453. }
  454. },
  455. async downloadPaper(attachment) {
  456. if (!attachment.attachmentId) return;
  457. const data = await attachmentPreview(attachment.attachmentId);
  458. window.open(data.url);
  459. },
  460. toViewCard(attachment) {
  461. this.curAttachment = { ...attachment };
  462. this.$refs.CardPreviewDialog.open();
  463. },
  464. async toCopyCard(attachment) {
  465. this.curAttachment = { ...attachment };
  466. const newCardId = await copyCard(
  467. attachment.cardId,
  468. this.curTaskApply.courseCode
  469. );
  470. this.cardModified({ id: newCardId });
  471. },
  472. toEditCard(attachment) {
  473. this.curAttachment = { ...attachment };
  474. this.$ls.set("prepareTcPCard", {
  475. id: attachment.cardId,
  476. examTaskId: this.curTaskApply.examTaskId,
  477. courseCode: this.curTaskApply.courseCode,
  478. courseName: this.curTaskApply.courseName,
  479. makeMethod: this.curTaskApply.makeMethod,
  480. cardRuleId: this.curTaskApply.cardRuleId,
  481. paperType: this.paperAttachments.map((item) => item.name).join(","),
  482. type: attachment.cardType,
  483. createMethod: attachment.createMethod,
  484. });
  485. this.$refs.ModifyCard.open();
  486. },
  487. async cardModified(data) {
  488. if (!data.id) return;
  489. await this.getCardList();
  490. let card = this.cards.find((item) => item.id === data.id);
  491. const aind = this.paperAttachments.findIndex(
  492. (item) => item.name === this.curAttachment.name
  493. );
  494. if (aind !== -1 && card) {
  495. this.paperAttachments[aind].cardId = card.id;
  496. this.paperAttachments[aind].cardType = card.type;
  497. this.paperAttachments[aind].createMethod = card.createMethod;
  498. this.paperAttachments[aind].cardTitle = card.title;
  499. this.paperAttachments[aind].used = card.used;
  500. this.paperAttachments[aind].createId = card.createId;
  501. }
  502. },
  503. async toCreateCard(attachment) {
  504. if (!this.curTaskApply.cardRuleId) {
  505. this.$message.error("题卡规则缺失!");
  506. return;
  507. }
  508. const res = await this.$prompt("请输入题卡名称?", "提示", {
  509. type: "warning",
  510. showInput: true,
  511. inputPlaceholder: "请输入题卡名称",
  512. inputValidator: (val) => {
  513. if (!val) return "请输入题卡名称!";
  514. if (val.length > 50) return "题卡名称不得超过50个字符!";
  515. return true;
  516. },
  517. }).catch(() => {});
  518. if (!res || res.action !== "confirm") return;
  519. this.curAttachment = { ...attachment };
  520. // 这里只允许新建标准专卡
  521. this.$ls.set("prepareTcPCard", {
  522. courseCode: this.curTaskApply.courseCode,
  523. courseName: this.curTaskApply.courseName,
  524. schoolName: this.$ls.get("schoolName"),
  525. makeMethod: "SELF",
  526. cardName: res.value,
  527. cardRuleId: this.curTaskApply.cardRuleId,
  528. type: "CUSTOM",
  529. createMethod: "STANDARD",
  530. });
  531. this.$refs.ModifyCard.open();
  532. },
  533. cardChange(attachment) {
  534. const card = this.cards.find((item) => item.id === attachment.cardId);
  535. if (card) {
  536. attachment.cardType = card.type;
  537. attachment.createMethod = card.createMethod;
  538. attachment.cardTitle = card.title;
  539. attachment.used = card.used;
  540. attachment.createId = card.createId;
  541. }
  542. },
  543. cardOptionOpened(visible, attachment) {
  544. if (!visible) return;
  545. // const selectedCardIds = this.paperAttachments.map((item) => item.cardId);
  546. // this.cards = this.cards.map((card) => {
  547. // card.disabled =
  548. // card.id !== attachment.cardId &&
  549. // selectedCardIds.includes(card.id) &&
  550. // card.type !== "GENERIC";
  551. // return card;
  552. // });
  553. },
  554. checkDataValid() {
  555. // 设置了入库强制包含试卷时,校验试卷是否上传。
  556. // 未设置入库强制包含试卷时,若有试卷上传,则需要上传全部。若无试卷上传,则通过。
  557. if (this.curTaskApply.includePaper) {
  558. const attachmentValid = !this.paperAttachments.some(
  559. (item) => !item.attachmentId
  560. );
  561. if (!attachmentValid) {
  562. this.$message.error("请完成试卷文件上传!");
  563. return;
  564. }
  565. } else {
  566. const hasUploadPaperAttachments = this.paperAttachments.filter(
  567. (item) => item.attachmentId
  568. );
  569. if (
  570. hasUploadPaperAttachments.length > 0 &&
  571. hasUploadPaperAttachments.length !== this.paperAttachments.length
  572. ) {
  573. this.$message.error("有试卷文件未完成上传!");
  574. return;
  575. }
  576. }
  577. let cardValid = !this.paperAttachments.some((item) => !item.cardId);
  578. if (!cardValid) {
  579. this.$message.error("有试卷类型未选择题卡!");
  580. return;
  581. }
  582. // const usedCards = this.paperAttachments
  583. // .filter((item) => item.cardId && item.used)
  584. // .map((item) => item.name);
  585. // if (usedCards.length) {
  586. // this.$message.error(`${usedCards.join()}卷选择的题卡已经被使用过!`);
  587. // return;
  588. // }
  589. return true;
  590. },
  591. async submit() {
  592. if (!this.checkDataValid()) return;
  593. this.$confirm("确定要提交申请修改当前任务吗?", "提示", {
  594. type: "warning",
  595. })
  596. .then(async () => {
  597. const datas = {
  598. examTaskId: this.curTaskApply.examTaskId,
  599. drawCount: this.curTaskApply.drawCount,
  600. paperType: this.paperAttachments.map((item) => item.name).join(","),
  601. paperAttachmentIds: JSON.stringify(this.paperAttachments),
  602. };
  603. const data = await taskPaperApplyEdit(datas).catch(() => {});
  604. if (!data) return;
  605. this.$message.success("提交成功!");
  606. this.$emit("modified");
  607. this.cancel();
  608. })
  609. .catch(() => {});
  610. },
  611. },
  612. };
  613. </script>