InfoExamTask.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. <template>
  2. <div class="info-exam-task">
  3. <div class="part-box part-box-pad part-box-border part-box-gray">
  4. <el-form
  5. ref="examTaskComp"
  6. :model="examTask"
  7. :rules="rules"
  8. label-width="120px"
  9. >
  10. <el-row>
  11. <el-col :span="12">
  12. <el-form-item prop="teachingRoomId" label="教研室:">
  13. <teaching-room-select
  14. v-model="examTask.teachingRoomId"
  15. @change="teachingRoomChange"
  16. ></teaching-room-select>
  17. </el-form-item>
  18. </el-col>
  19. <el-col :span="12">
  20. <el-form-item prop="courseCode" label="课程(代码):">
  21. <el-select
  22. v-model="examTask.courseCode"
  23. placeholder="请选择"
  24. filterable
  25. @change="courseChange"
  26. >
  27. <el-option
  28. v-for="item in courses"
  29. :key="item.code"
  30. :value="item.code"
  31. :label="`${item.name}(${item.code})`"
  32. >
  33. </el-option>
  34. </el-select>
  35. </el-form-item>
  36. </el-col>
  37. </el-row>
  38. <el-row>
  39. <el-col :span="12">
  40. <el-form-item prop="cardRuleId" label="题卡规则:">
  41. <el-input v-model.trim="cardRuleName" readonly></el-input>
  42. </el-form-item>
  43. </el-col>
  44. <el-col :span="12">
  45. <el-form-item label="试卷编号:">
  46. <el-input
  47. v-model.trim="examTask.paperNumber"
  48. placeholder="请输入试卷编号"
  49. :maxlength="50"
  50. clearable
  51. ></el-input>
  52. </el-form-item>
  53. </el-col>
  54. </el-row>
  55. <el-row>
  56. <el-col :span="12">
  57. <el-form-item label="拟卷教师:">
  58. <el-input
  59. v-model.trim="examTask.teacherName"
  60. placeholder="请输入拟卷教师"
  61. :maxlength="50"
  62. clearable
  63. ></el-input>
  64. </el-form-item>
  65. </el-col>
  66. <el-col :span="12">
  67. <el-form-item label="试卷名称:">
  68. <el-input
  69. v-model.trim="examTask.paperName"
  70. placeholder="请输入试卷名称"
  71. :maxlength="100"
  72. clearable
  73. ></el-input>
  74. </el-form-item>
  75. </el-col>
  76. </el-row>
  77. </el-form>
  78. </div>
  79. <div class="apply-content task-detail">
  80. <div class="task-body">
  81. <div class="mb-2 text-right">
  82. <el-button
  83. type="info"
  84. icon="el-icon-circle-plus-outline"
  85. @click="addAtachment"
  86. >增加卷型</el-button
  87. >
  88. </div>
  89. <table class="table mb-2">
  90. <tr>
  91. <th>试卷类型</th>
  92. <th>试卷文件</th>
  93. <th>答题卡创建方式</th>
  94. <th>答题卡</th>
  95. <th>操作</th>
  96. </tr>
  97. <tr v-for="(attachment, index) in paperAttachments" :key="index">
  98. <td>{{ attachment.name }}卷</td>
  99. <td>
  100. <el-button
  101. type="text"
  102. class="btn-primary"
  103. @click="toUpload(attachment)"
  104. >
  105. <i
  106. :class="[
  107. 'icon',
  108. attachment.attachmentId ? 'icon-files-act' : 'icon-files'
  109. ]"
  110. ></i
  111. >{{
  112. attachment.attachmentId
  113. ? attachment.filename
  114. : "点击上传试卷文件"
  115. }}
  116. </el-button>
  117. </td>
  118. <td :rowspan="paperAttachments.length" v-if="index === 0">
  119. {{ createCardTypeName }}
  120. </td>
  121. <td :rowspan="paperAttachments.length" v-if="index === 0">
  122. <el-select v-model="examTaskDetail.cardId" placeholder="请选择">
  123. <el-option
  124. v-for="item in cards"
  125. :key="item.id"
  126. :value="item.id"
  127. :label="item.title"
  128. >
  129. </el-option>
  130. </el-select>
  131. </td>
  132. <td>
  133. <el-button
  134. class="btn-danger"
  135. type="text"
  136. @click="deleteAttachment(index)"
  137. >删除</el-button
  138. >
  139. </td>
  140. </tr>
  141. <tr v-if="!paperAttachments.length">
  142. <td colspan="5">
  143. <p class="tips-info text-center">暂无数据</p>
  144. </td>
  145. </tr>
  146. </table>
  147. <p class="tips-info tips-dark mb-2">
  148. 提示:多卷型试卷由于会绑定一个答题卡模板,因此试卷结构必须相同。多卷型试卷之间客观题要求试题内容相同,可允许大题内的小题题序不同。
  149. </p>
  150. <el-form>
  151. <el-form-item label="单次抽卷卷型数量:" label-width="150">
  152. <el-input-number
  153. v-model="examTaskDetail.drawCount"
  154. :min="1"
  155. :max="maxFetchCount"
  156. :step="1"
  157. step-strictly
  158. :controls="false"
  159. ></el-input-number>
  160. </el-form-item>
  161. </el-form>
  162. <h4 class="mb-2">备注说明:</h4>
  163. <el-input
  164. class="mb-2"
  165. v-model="examTaskDetail.remark"
  166. type="textarea"
  167. resize="none"
  168. :rows="2"
  169. :maxlength="100"
  170. clearable
  171. show-word-limit
  172. placeholder="建议不超过100个字"
  173. ></el-input>
  174. <h4 class="mb-2">上传入库审核表(最多4张)</h4>
  175. <div class="image-list">
  176. <div
  177. class="image-item"
  178. v-for="(img, index) in paperConfirmAttachments"
  179. :key="index"
  180. >
  181. <img
  182. :src="img.url"
  183. :alt="img.filename"
  184. title="点击查看大图"
  185. @click="toPreview(index)"
  186. />
  187. <div class="image-delete">
  188. <i
  189. class="el-icon-delete-solid"
  190. @click="deletePaperConfirmAttachment(index)"
  191. ></i>
  192. </div>
  193. </div>
  194. <div
  195. v-if="paperConfirmAttachments.length < 4"
  196. class="image-item image-add"
  197. title="上传入库审核表"
  198. @click="toUploadPaperConfirm"
  199. >
  200. <i class="el-icon-plus"></i>
  201. </div>
  202. </div>
  203. </div>
  204. </div>
  205. <!-- upload-paper-dialog -->
  206. <upload-paper-dialog
  207. :paper-attachment="curAttachment"
  208. :upload-type="curUploadType"
  209. @confirm="uploadConfirm"
  210. ref="UploadPaperDialog"
  211. ></upload-paper-dialog>
  212. <!-- image-preview -->
  213. <simple-image-preview
  214. :cur-image="curImage"
  215. @on-prev="toPrevImage"
  216. @on-next="toNextImage"
  217. ref="SimpleImagePreview"
  218. ></simple-image-preview>
  219. </div>
  220. </template>
  221. <script>
  222. import UploadPaperDialog from "../UploadPaperDialog";
  223. import SimpleImagePreview from "@/components/SimpleImagePreview";
  224. import { CARD_SOURCE_TYPE } from "@/constants/enumerate";
  225. import { cardForSelectList } from "../../api";
  226. import { courseQuery } from "../../../base/api";
  227. export default {
  228. name: "info-exam-task",
  229. components: { UploadPaperDialog, SimpleImagePreview },
  230. props: {
  231. datas: {
  232. type: Object,
  233. default() {
  234. return {};
  235. }
  236. }
  237. },
  238. data() {
  239. return {
  240. task: {},
  241. rules: {
  242. teachingRoomId: [
  243. {
  244. required: true,
  245. message: "请选择教研室",
  246. trigger: "change"
  247. }
  248. ],
  249. courseCode: [
  250. {
  251. required: true,
  252. message: "请选择课程",
  253. trigger: "change"
  254. }
  255. ],
  256. cardRuleId: [
  257. {
  258. required: true,
  259. message: "请选择题卡规则",
  260. trigger: "change"
  261. }
  262. ]
  263. },
  264. examTask: {},
  265. cards: [],
  266. courses: [],
  267. cardRuleName: "全部通卡",
  268. // exam-task-detail
  269. examTaskDetail: { makeMethod: "" },
  270. paperConfirmAttachmentId: { attachmentId: "", filename: "", url: "" },
  271. paperAttachments: [],
  272. paperConfirmAttachments: [],
  273. curAttachment: {},
  274. curUploadType: "paper",
  275. attachmentLimitCount: 26,
  276. abc: "abcdefghijklmnopqrstuvwxyz".toUpperCase(),
  277. // image-preview
  278. curImage: {},
  279. curImageIndex: 0
  280. };
  281. },
  282. computed: {
  283. createCardTypeName() {
  284. return CARD_SOURCE_TYPE[this.examTaskDetail.makeMethod] || "";
  285. },
  286. maxFetchCount() {
  287. return this.paperAttachments.length < 1
  288. ? 1
  289. : this.paperAttachments.length;
  290. }
  291. },
  292. mounted() {
  293. this.initData();
  294. },
  295. methods: {
  296. initData() {
  297. console.log(this.datas.examTask);
  298. this.examTask = { ...this.datas.examTask };
  299. this.examTaskDetail = { ...this.datas.examTaskDetail };
  300. this.paperAttachments = this.examTaskDetail.paperAttachmentIds
  301. ? JSON.parse(this.examTaskDetail.paperAttachmentIds)
  302. : [];
  303. if (!this.paperAttachments.length) {
  304. this.addAtachment();
  305. }
  306. this.paperConfirmAttachments = this.examTaskDetail
  307. .paperConfirmAttachmentIds
  308. ? JSON.parse(this.examTaskDetail.paperConfirmAttachmentIds)
  309. : [];
  310. this.getCourses();
  311. this.getCardList();
  312. this.$emit("on-ready");
  313. },
  314. async getCardList() {
  315. if (!this.examTask.courseCode) return;
  316. const data = await cardForSelectList({
  317. courseCode: this.examTask.courseCode,
  318. paperType: this.paperAttachments.map(item => item.name).join(","),
  319. cardRuleId: this.examTask.cardRuleId
  320. });
  321. this.cards = data || [];
  322. },
  323. async getCourses() {
  324. if (!this.examTask.teachingRoomId) return;
  325. const res = await courseQuery({
  326. teachingRoomId: this.examTask.teachingRoomId
  327. });
  328. this.courses = res || [];
  329. },
  330. async checkData() {
  331. const valid = await this.$refs.examTaskComp.validate().catch(() => {});
  332. if (!valid) {
  333. this.$emit("on-ready");
  334. return;
  335. }
  336. const attachmentValid = !this.paperAttachments.some(
  337. item => !item.attachmentId
  338. );
  339. // 设置了入库强制包含试卷时,校验试卷是否上传。
  340. if (this.examTaskDetail.includePaper && !attachmentValid) {
  341. this.$message.error("请完成试卷文件上传!");
  342. this.$emit("on-ready");
  343. return;
  344. }
  345. // if (!this.paperConfirmAttachments.length) {
  346. // this.$message.error("请上传入库审核表!");
  347. // return;
  348. // }
  349. if (!this.examTaskDetail.cardId) {
  350. this.$message.error("请选择题卡!");
  351. this.$emit("on-ready");
  352. return;
  353. }
  354. this.updateData();
  355. this.$emit("next-step");
  356. },
  357. getData() {
  358. return {
  359. examTask: this.examTask,
  360. examTaskDetail: this.getTaskDetailData()
  361. };
  362. },
  363. updateData() {
  364. this.$emit("data-change", this.getData());
  365. },
  366. // exam-task-detail edit
  367. addAtachment() {
  368. if (this.paperAttachments.length >= this.attachmentLimitCount) return;
  369. const newAttachment = {
  370. name: this.abc[this.paperAttachments.length],
  371. attachmentId: "",
  372. filename: "",
  373. pages: 0
  374. };
  375. this.paperAttachments.push(newAttachment);
  376. },
  377. deleteAttachment(index) {
  378. if (this.paperAttachments.length <= 1) {
  379. this.$message.error("试卷类型数量不得少于1");
  380. return;
  381. }
  382. this.paperAttachments.splice(index, 1);
  383. this.paperAttachments.forEach((item, itemIndex) => {
  384. item.name = this.abc[itemIndex];
  385. });
  386. if (
  387. this.examTaskDetail.drawCount &&
  388. this.examTaskDetail.drawCount > this.paperAttachments.length
  389. ) {
  390. this.examTaskDetail.drawCount = this.paperAttachments.length;
  391. }
  392. },
  393. toUpload(attachment) {
  394. this.curUploadType = "paper";
  395. this.curAttachment = {
  396. ...attachment
  397. };
  398. this.$refs.UploadPaperDialog.open();
  399. },
  400. toUploadPaperConfirm() {
  401. if (this.paperConfirmAttachments.length >= 4) return;
  402. this.curUploadType = "paperConfirm";
  403. this.curAttachment = {
  404. ...this.paperConfirmAttachmentId
  405. };
  406. this.$refs.UploadPaperDialog.open();
  407. },
  408. uploadConfirm(attachment, uploadType) {
  409. if (uploadType === "paper") {
  410. const index = this.paperAttachments.findIndex(
  411. item => item.name === attachment.name
  412. );
  413. this.paperAttachments.splice(index, 1, { ...attachment });
  414. } else {
  415. this.paperConfirmAttachments.push(attachment);
  416. }
  417. },
  418. deletePaperConfirmAttachment(index) {
  419. this.paperConfirmAttachments.splice(index, 1);
  420. },
  421. toViewCard() {
  422. window.open(
  423. this.getRouterPath({
  424. name: "CardPreview",
  425. params: {
  426. cardId: this.examTaskDetail.cardId,
  427. viewType: "view"
  428. }
  429. })
  430. );
  431. },
  432. cardConfirm(data) {
  433. this.examTaskDetail = this.$objAssign(this.examTaskDetail, data);
  434. },
  435. teachingRoomChange() {
  436. this.examTask.courseCode = "";
  437. this.examTask.courseName = "";
  438. this.courses = [];
  439. this.examTaskDetail.cardId = "";
  440. this.cards = [];
  441. this.getCourses();
  442. },
  443. courseChange(val) {
  444. if (val) {
  445. const course = this.courses.find(item => item.code === val);
  446. this.examTask.courseName = course.name;
  447. } else {
  448. this.examTask.courseName = "";
  449. }
  450. this.examTaskDetail.cardId = "";
  451. this.cards = [];
  452. this.getCardList();
  453. },
  454. getTaskDetailData() {
  455. let data = { ...this.examTaskDetail };
  456. data.paperType = this.paperAttachments.map(item => item.name).join(",");
  457. data.paperAttachmentIds = JSON.stringify(this.paperAttachments, (k, v) =>
  458. k === "url" ? undefined : v
  459. );
  460. data.paperConfirmAttachmentIds = JSON.stringify(
  461. this.paperConfirmAttachments
  462. );
  463. return data;
  464. },
  465. // image-preview
  466. toPreview(index) {
  467. this.curImageIndex = index;
  468. this.selectImage(index);
  469. this.$refs.SimpleImagePreview.open();
  470. },
  471. selectImage(index) {
  472. this.curImage = this.paperConfirmAttachments[index];
  473. },
  474. toPrevImage() {
  475. if (this.curImageIndex === 0) {
  476. this.curImageIndex = this.paperConfirmAttachments.length - 1;
  477. } else {
  478. this.curImageIndex--;
  479. }
  480. this.selectImage(this.curImageIndex);
  481. },
  482. toNextImage() {
  483. if (this.curImageIndex === this.paperConfirmAttachments.length - 1) {
  484. this.curImageIndex = 0;
  485. } else {
  486. this.curImageIndex++;
  487. }
  488. this.selectImage(this.curImageIndex);
  489. }
  490. }
  491. };
  492. </script>