EditPaper.vue 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. <template>
  2. <div class="edit-paper">
  3. <div class="edit-header">
  4. <div class="edit-header-top box-justify">
  5. <div class="header-info">
  6. <el-button
  7. class="is-back"
  8. icon="el-icon-arrow-left"
  9. @click="toBack"
  10. size="mini"
  11. style="margin-right: 0"
  12. ></el-button>
  13. <!-- <div class="header-info-item">
  14. <span>课程代码:</span>
  15. <span>{{ paper.course.code }}</span>
  16. </div> -->
  17. <div class="header-info-item">
  18. <!-- <span>课程名称:</span> -->
  19. <span style="color: #262626; font-size: 16px; font-weight: bold">{{
  20. paper.course.name
  21. }}</span>
  22. </div>
  23. <div class="header-info-item">
  24. <span>试卷名称:</span>
  25. <span>
  26. <el-tooltip class="item" effect="dark" placement="top-start">
  27. <div slot="content">{{ paper.name }}</div>
  28. <el-input
  29. v-model="paper.name"
  30. size="mini"
  31. class="header-info-input"
  32. placeholder="试卷名称"
  33. ></el-input>
  34. </el-tooltip>
  35. </span>
  36. </div>
  37. <div class="header-info-item">
  38. <span>试卷标题:</span>
  39. <span>
  40. <el-tooltip class="item" effect="dark" placement="top-start">
  41. <div slot="content">{{ paper.title }}</div>
  42. <el-input
  43. v-model="paper.title"
  44. size="mini"
  45. class="header-info-input"
  46. placeholder="试卷标题"
  47. ></el-input>
  48. </el-tooltip>
  49. </span>
  50. </div>
  51. <div class="header-info-item">
  52. <span>试卷总分:</span>
  53. <span>{{ paper.totalScore }}</span>
  54. </div>
  55. </div>
  56. <div class="header-btns">
  57. <el-dropdown trigger="click" @command="importCommand">
  58. <div class="avatar-wrapper">
  59. <el-button>
  60. 导入
  61. <i class="el-icon-arrow-down" />
  62. </el-button>
  63. </div>
  64. <el-dropdown-menu slot="dropdown" class="user-dropdown">
  65. <el-dropdown-item command="answer">
  66. <span>导入答案</span>
  67. </el-dropdown-item>
  68. <el-dropdown-item command="property">
  69. <span>导入属性</span>
  70. </el-dropdown-item>
  71. </el-dropdown-menu>
  72. </el-dropdown>
  73. <el-button
  74. v-if="checkDuplicateBtnShow"
  75. type="primary"
  76. size="small"
  77. plain
  78. @click="toCheckDuplicate"
  79. >
  80. 进入查重
  81. </el-button>
  82. <el-button
  83. type="danger"
  84. size="small"
  85. plain
  86. class="maintain"
  87. @click="toDeletePaper(paper.id)"
  88. >
  89. 删除
  90. </el-button>
  91. <el-button type="primary" size="small" @click="toSavePaper">
  92. 保存
  93. </el-button>
  94. <!-- <el-button
  95. size="small"
  96. type="primary"
  97. plain
  98. @click="toImportPaperAnswer"
  99. >导入答案</el-button
  100. >
  101. <el-button
  102. size="small"
  103. type="primary"
  104. plain
  105. @click="toImportPaperProperty"
  106. >导入属性</el-button
  107. > -->
  108. <!-- <el-button
  109. size="small"
  110. type="danger"
  111. plain
  112. icon="icon icon-back"
  113. @click="toBack"
  114. >返回</el-button
  115. > -->
  116. </div>
  117. </div>
  118. <div class="edit-header-bottom box-justify">
  119. <div>
  120. <el-button
  121. plain
  122. size="small"
  123. icon="el-icon-circle-plus"
  124. @click="addBigQuestion"
  125. >
  126. 新增大题
  127. </el-button>
  128. <el-button plain size="small" @click="toViewBaseInfo">
  129. 基础构成
  130. </el-button>
  131. <el-button plain size="small" @click="toViewQuestypeInfo">
  132. 题型分布
  133. </el-button>
  134. <el-button plain size="small" @click="toViewBlueInfo">
  135. 蓝图分布
  136. </el-button>
  137. <!-- <el-button plain size="small" @click="toViewAuditInfo">
  138. 审核记录
  139. </el-button> -->
  140. <el-button
  141. v-if="
  142. enableCardEdit &&
  143. authButtons.includes('exam_paper_manager-edit_card')
  144. "
  145. plain
  146. size="small"
  147. @click="toEditCard"
  148. >
  149. 编辑题卡
  150. </el-button>
  151. <el-button
  152. v-if="enableCardEdit"
  153. type="danger"
  154. size="small"
  155. plain
  156. class="maintain"
  157. @click="toDeletCard"
  158. >
  159. 清除题卡
  160. </el-button>
  161. </div>
  162. <div>
  163. <tool-tip-btn
  164. name="shenhejilu"
  165. content="审核记录"
  166. @click="toViewAuditInfo"
  167. ></tool-tip-btn>
  168. <tool-tip-btn
  169. name="yulan"
  170. content="预览"
  171. @click="previewPDF2"
  172. ></tool-tip-btn>
  173. <tool-tip-btn
  174. name="shuxingyincangxianshi"
  175. :content="(quesTagShow ? '隐藏' : '显示') + '属性'"
  176. @click="toSwitchQuesTagShowHide"
  177. ></tool-tip-btn>
  178. <tool-tip-btn
  179. name="daochu"
  180. content="导出试卷蓝图"
  181. :disabled="downloading"
  182. @click="toExportPaperBlue"
  183. ></tool-tip-btn>
  184. <tool-tip-btn
  185. name="daanyincangxianshi"
  186. :content="(quesAnswerShow ? '隐藏' : '显示') + '答案'"
  187. @click="toSwitchQuesAnswerShowHide"
  188. ></tool-tip-btn>
  189. <!-- <el-button size="small" type="primary" plain @click="previewPDF2"
  190. >预览</el-button
  191. > -->
  192. <!-- <el-button
  193. type="primary"
  194. size="small"
  195. plain
  196. @click="toSwitchQuesTagShowHide"
  197. >
  198. {{ quesTagShow ? "隐藏" : "显示" }}属性
  199. </el-button> -->
  200. <!-- <el-button
  201. size="small"
  202. type="primary"
  203. plain
  204. :loading="downloading"
  205. @click="toExportPaperBlue"
  206. >导出试卷蓝图</el-button
  207. > -->
  208. <!-- <el-button
  209. type="primary"
  210. size="small"
  211. plain
  212. @click="toSwitchQuesAnswerShowHide"
  213. >
  214. {{ quesAnswerShow ? "隐藏" : "显示" }}答案
  215. </el-button> -->
  216. </div>
  217. </div>
  218. </div>
  219. <div class="edit-body">
  220. <!-- 考试说明 -->
  221. <div class="edit-part-list">
  222. <div class="edit-part">
  223. <div class="edit-cont">
  224. <div
  225. class="edit-cont-title"
  226. style="display: flex; align-items: center"
  227. >
  228. <h3>考试说明</h3>
  229. <svg-btn
  230. name="bianji"
  231. color="#6D5FF6"
  232. hoverBgColor="#F0EFFE"
  233. class="hover-show"
  234. @click="toEditExamRemark"
  235. style="margin-left: 10px"
  236. >编辑</svg-btn
  237. >
  238. </div>
  239. <!-- <div class="edit-cont-action">
  240. <el-button
  241. type="primary"
  242. plain
  243. size="small"
  244. @click="toEditExamRemark"
  245. >编辑</el-button
  246. >
  247. </div> -->
  248. <div class="edit-cont-body">
  249. <rich-text :text-json="paper.examRemark"></rich-text>
  250. </div>
  251. </div>
  252. </div>
  253. </div>
  254. <!-- 循环大题 -->
  255. <div
  256. v-for="(paperDetail, detailIndex) in paper.paperDetails"
  257. :key="paperDetail.id"
  258. class="edit-part-list"
  259. >
  260. <div class="edit-part level1">
  261. <div class="edit-cont">
  262. <div class="edit-cont-title">
  263. <h3>
  264. <span>{{ paperDetail.cnNum }}</span> <span>.</span>
  265. <span>{{ paperDetail.name }}</span>
  266. </h3>
  267. </div>
  268. <rich-text
  269. class="edit-cont-body"
  270. :text-json="paperDetail.description"
  271. ></rich-text>
  272. </div>
  273. <div class="level1-hover-show">
  274. <div style="color: #595959; margin-bottom: 5px">
  275. 共{{ paperDetail.unitCount }}小题,满分{{ paperDetail.score }}分
  276. </div>
  277. <div class="gray-btn-group">
  278. <span @click="toSelectPaperDetailQues(paperDetail)">选题</span>
  279. <span @click="toEditPaperDetail(paperDetail)">编辑</span>
  280. <span
  281. v-if="detailIndex"
  282. @click="toMovePaperDetail(paperDetail, 'up')"
  283. >上移</span
  284. >
  285. <span v-if="detailIndex < paper.paperDetails.length - 1"
  286. >下移</span
  287. >
  288. <span @click="toDeletePaperDetail(paperDetail)">删除</span>
  289. <span
  290. @click.stop="
  291. paperDetail.showQuestions = !paperDetail.showQuestions
  292. "
  293. >{{ paperDetail.showQuestions ? "收起" : "展开" }}</span
  294. >
  295. </div>
  296. <!--
  297. <div>
  298. <el-button
  299. size="small"
  300. type="primary"
  301. plain
  302. @click="toSelectPaperDetailQues(paperDetail)"
  303. >选题
  304. </el-button>
  305. <el-button
  306. size="small"
  307. type="primary"
  308. plain
  309. @click="toEditPaperDetail(paperDetail)"
  310. >编辑
  311. </el-button>
  312. <el-button
  313. v-if="detailIndex"
  314. size="small"
  315. type="primary"
  316. plain
  317. @click="toMovePaperDetail(paperDetail, 'up')"
  318. >上移
  319. </el-button>
  320. <el-button
  321. v-if="detailIndex < paper.paperDetails.length - 1"
  322. size="small"
  323. type="primary"
  324. plain
  325. @click="toMovePaperDetail(paperDetail, 'down')"
  326. >下移
  327. </el-button>
  328. <el-button
  329. size="small"
  330. type="danger"
  331. @click="toDeletePaperDetail(paperDetail)"
  332. >删除
  333. </el-button>
  334. <el-button
  335. size="small"
  336. type="primary"
  337. plain
  338. :icon="
  339. paperDetail.showQuestions
  340. ? 'el-icon-arrow-up'
  341. : 'el-icon-arrow-down'
  342. "
  343. @click.stop="
  344. paperDetail.showQuestions = !paperDetail.showQuestions
  345. "
  346. ></el-button>
  347. </div> -->
  348. </div>
  349. <div v-show="quesTagShow" class="edit-property">
  350. <div class="edit-property-box">
  351. <div
  352. v-for="(paperDetailTag, tagIndex) in paperDetail.tags"
  353. :key="tagIndex"
  354. class="edit-property-item"
  355. >
  356. <div class="edit-property-body level1">
  357. <div class="edit-property-title">
  358. {{ paperDetailTag.tag }}
  359. </div>
  360. <div class="edit-property-content">
  361. {{ paperDetailTag.content }}
  362. </div>
  363. </div>
  364. </div>
  365. <template v-if="paperDetail.selective">
  366. <div class="edit-property-item">
  367. <div class="edit-property-body level1">
  368. <div class="edit-property-title">选做题数</div>
  369. <div class="edit-property-content">
  370. {{ paperDetail.selectiveCount }}
  371. </div>
  372. </div>
  373. </div>
  374. <div class="edit-property-item">
  375. <div class="edit-property-body level1">
  376. <div class="edit-property-title">取分规则</div>
  377. <div class="edit-property-content">
  378. {{ paperDetail.selectiveRule | selectiveRuleTypeFilter }}
  379. </div>
  380. </div>
  381. </div>
  382. </template>
  383. </div>
  384. </div>
  385. </div>
  386. <!-- 循环小题 -->
  387. <div v-show="paperDetail.showQuestions" class="edit-paper-questions">
  388. <template
  389. v-for="(paperDetailUnit, unitIndex) in paperDetail.paperDetailUnits"
  390. >
  391. <div
  392. :key="`question-${unitIndex}`"
  393. :class="[
  394. 'edit-part',
  395. 'level2',
  396. {
  397. 'question-duplicate':
  398. paperDetailUnit.question.checkDuplicateStatus ==
  399. 'TO_BE_DISPOSE',
  400. 'is-danger': !paperDetailUnit.score,
  401. },
  402. ]"
  403. >
  404. <div class="level2-hover-show">
  405. <div class="box-justify">
  406. <div class="gray-btn-group">
  407. <span
  408. v-if="
  409. paperDetailUnit.question.checkDuplicateStatus ===
  410. 'TO_BE_DISPOSE'
  411. "
  412. @click="
  413. toCheckDuplicateQuestion(paperDetailUnit.question.id)
  414. "
  415. >进入查重</span
  416. >
  417. <span @click="toChangeQues(paperDetailUnit)">换题</span>
  418. <span @click="toEditQues(paperDetailUnit)">编辑</span>
  419. <span
  420. v-if="unitIndex"
  421. @click="
  422. toMoveQues(paperDetail.id, paperDetailUnit.id, 'up')
  423. "
  424. >上移</span
  425. >
  426. <span
  427. v-if="unitIndex < paperDetail.paperDetailUnits.length - 1"
  428. @click="
  429. toMoveQues(paperDetail.id, paperDetailUnit.id, 'down')
  430. "
  431. >下移</span
  432. >
  433. <span @click="toDeleteQues(paperDetailUnit)">删除</span>
  434. <span
  435. v-if="isNested(paperDetailUnit.questionType)"
  436. @click="
  437. paperDetailUnit.showSubQuestions =
  438. !paperDetailUnit.showSubQuestions
  439. "
  440. >{{
  441. paperDetailUnit.showSubQuestions ? "收起" : "展开"
  442. }}</span
  443. >
  444. </div>
  445. <span class="tips-info">
  446. {{ paperDetailUnit.question.bodyLengthText }}
  447. </span>
  448. </div>
  449. <!-- <el-button
  450. v-if="
  451. paperDetailUnit.question.checkDuplicateStatus ===
  452. 'TO_BE_DISPOSE'
  453. "
  454. size="small"
  455. type="primary"
  456. plain
  457. @click="
  458. toCheckDuplicateQuestion(paperDetailUnit.question.id)
  459. "
  460. >进入查重
  461. </el-button>
  462. <el-button
  463. size="small"
  464. type="primary"
  465. plain
  466. @click="toChangeQues(paperDetailUnit)"
  467. >换题
  468. </el-button>
  469. <el-button
  470. size="small"
  471. type="primary"
  472. plain
  473. @click="toEditQues(paperDetailUnit)"
  474. >编辑
  475. </el-button>
  476. <el-button
  477. v-if="unitIndex"
  478. size="small"
  479. type="primary"
  480. plain
  481. @click="
  482. toMoveQues(paperDetail.id, paperDetailUnit.id, 'up')
  483. "
  484. >上移
  485. </el-button>
  486. <el-button
  487. v-if="unitIndex < paperDetail.paperDetailUnits.length - 1"
  488. size="small"
  489. type="primary"
  490. plain
  491. @click="
  492. toMoveQues(paperDetail.id, paperDetailUnit.id, 'down')
  493. "
  494. >下移
  495. </el-button>
  496. <el-button
  497. type="danger"
  498. size="small"
  499. @click="toDeleteQues(paperDetailUnit)"
  500. >删除
  501. </el-button>
  502. <el-button
  503. v-if="isNested(paperDetailUnit.questionType)"
  504. size="small"
  505. :icon="
  506. paperDetailUnit.showSubQuestions
  507. ? 'el-icon-arrow-up'
  508. : 'el-icon-arrow-down'
  509. "
  510. @click.stop="
  511. paperDetailUnit.showSubQuestions =
  512. !paperDetailUnit.showSubQuestions
  513. "
  514. ></el-button> -->
  515. </div>
  516. <div class="edit-cont">
  517. <div class="edit-cont-title">
  518. <span>{{ paperDetailUnit.number }}.</span>
  519. <rich-text
  520. :text-json="paperDetailUnit.question.quesBody"
  521. ></rich-text>
  522. <span :class="{ 'color-danger': !paperDetailUnit.score }">
  523. ({{ paperDetailUnit.score }}分)
  524. </span>
  525. </div>
  526. <div class="edit-cont-body">
  527. <div
  528. v-for="(quesOption, optionIndex) in paperDetailUnit.question
  529. .quesOptions"
  530. :key="optionIndex"
  531. class="paper-option"
  532. >
  533. <span>{{ optionIndex | optionOrderWordFilter }}. </span>
  534. <rich-text :text-json="quesOption.optionBody"></rich-text>
  535. </div>
  536. <div v-if="!isNested(paperDetailUnit.questionType)">
  537. <div v-show="quesAnswerShow" class="paper-answer">
  538. <span>答案:</span>
  539. <question-answer
  540. :data="paperDetailUnit.question"
  541. ></question-answer>
  542. </div>
  543. </div>
  544. </div>
  545. <div
  546. v-if="paperDetailUnit.question.quesProperties"
  547. class="edit-cont-props"
  548. style="margin-top: 10px"
  549. >
  550. <el-tag
  551. v-for="(content, propIndex) in paperDetailUnit.question
  552. .quesProperties"
  553. :key="propIndex"
  554. type="primary"
  555. effect="dark"
  556. style="margin-right: 5px; margin-bottom: 5px"
  557. >
  558. {{ content.courseProperty && content.courseProperty.name }}
  559. <span style="margin: 0 3px">/</span>
  560. {{ content.firstProperty && content.firstProperty.name }}
  561. <span v-if="content.secondProperty" style="margin: 0 3px"
  562. >/</span
  563. >
  564. {{ content.secondProperty && content.secondProperty.name }}
  565. </el-tag>
  566. </div>
  567. </div>
  568. <div v-show="quesTagShow" class="edit-property">
  569. <div class="edit-property-box">
  570. <div
  571. v-for="(questionTag, tagIndex) in paperDetailUnit.question
  572. .tags"
  573. :key="tagIndex"
  574. class="edit-property-item"
  575. >
  576. <div class="edit-property-body level2">
  577. <div class="edit-property-title">
  578. {{ questionTag.tag }}
  579. </div>
  580. <div class="edit-property-content">
  581. {{ questionTag.content }}
  582. </div>
  583. </div>
  584. </div>
  585. </div>
  586. </div>
  587. </div>
  588. <div
  589. v-if="isNested(paperDetailUnit.questionType)"
  590. v-show="paperDetailUnit.showSubQuestions"
  591. :key="`question-sub-${unitIndex}`"
  592. class="edit-paper-question-subs"
  593. >
  594. <div
  595. v-for="(subQuestion, subIndex) in paperDetailUnit.question
  596. .subQuestions"
  597. :key="subIndex"
  598. :class="[
  599. 'edit-part',
  600. {
  601. 'is-danger': !paperDetailUnit.subScoreList[subIndex],
  602. },
  603. ]"
  604. >
  605. <div class="edit-cont">
  606. <div class="edit-cont-title">
  607. <span>{{ subIndex + 1 }}. </span>
  608. <rich-text :text-json="subQuestion.quesBody"></rich-text>
  609. <span
  610. :class="{
  611. 'color-danger': !paperDetailUnit.subScoreList[subIndex],
  612. }"
  613. >
  614. ({{ paperDetailUnit.subScoreList[subIndex] }}分)
  615. </span>
  616. </div>
  617. <div
  618. v-if="!isMatchingQuestion(paperDetailUnit.questionType)"
  619. class="edit-cont-body"
  620. >
  621. <div
  622. v-for="(
  623. subQuesOption, subOptIndex
  624. ) in subQuestion.quesOptions"
  625. :key="subOptIndex"
  626. class="paper-option"
  627. >
  628. <span>{{ subOptIndex | optionOrderWordFilter }}. </span>
  629. <rich-text
  630. :text-json="subQuesOption.optionBody"
  631. ></rich-text>
  632. </div>
  633. </div>
  634. <div v-show="quesAnswerShow" class="paper-answer">
  635. <span>答案:</span>
  636. <question-answer :data="subQuestion"></question-answer>
  637. <!-- <rich-text :text-json="subQuestion.quesAnswer"></rich-text> -->
  638. </div>
  639. <div
  640. v-if="subQuestion.quesProperties"
  641. class="edit-cont-props"
  642. style="margin-top: 10px"
  643. >
  644. <el-tag
  645. v-for="(content, propIndex) in subQuestion.quesProperties"
  646. :key="propIndex"
  647. type="primary"
  648. effect="dark"
  649. style="margin-right: 5px; margin-bottom: 5px"
  650. >
  651. {{
  652. content.courseProperty && content.courseProperty.name
  653. }}
  654. <span style="margin: 0 3px">/</span>
  655. {{ content.firstProperty && content.firstProperty.name }}
  656. <span v-if="content.secondProperty" style="margin: 0 3px"
  657. >/</span
  658. >
  659. {{
  660. content.secondProperty && content.secondProperty.name
  661. }}
  662. </el-tag>
  663. </div>
  664. </div>
  665. <!-- <div
  666. v-if="subQuestion.quesProperties"
  667. class="edit-cont-props"
  668. style="margin-top: 10px"
  669. >
  670. <el-tag
  671. v-for="(content, propIndex) in subQuestion.quesProperties"
  672. :key="propIndex"
  673. type="primary"
  674. effect="dark"
  675. style="margin-right: 5px; margin-bottom: 5px"
  676. >
  677. {{ content.courseProperty && content.courseProperty.name }}
  678. <span style="margin: 0 3px">/</span>
  679. {{ content.firstProperty && content.firstProperty.name }}
  680. <span v-if="content.secondProperty" style="margin: 0 3px"
  681. >/</span
  682. >
  683. {{ content.secondProperty && content.secondProperty.name }}
  684. </el-tag>
  685. </div> -->
  686. <div v-show="quesTagShow" class="edit-property">
  687. <div class="edit-property-box">
  688. <div
  689. v-for="(subQuestionTag, tagIndex) in subQuestion.tags"
  690. :key="tagIndex"
  691. class="edit-property-item"
  692. >
  693. <div class="edit-property-body level2">
  694. <div class="edit-property-title">
  695. {{ subQuestionTag.tag }}
  696. </div>
  697. <div class="edit-property-content">
  698. {{ subQuestionTag.content }}
  699. </div>
  700. </div>
  701. </div>
  702. </div>
  703. </div>
  704. </div>
  705. </div>
  706. </template>
  707. </div>
  708. </div>
  709. </div>
  710. <!-- 编辑考试说明 -->
  711. <modify-rich-text
  712. ref="ModifyRichText"
  713. :content="paper.examRemark"
  714. title="考试说明"
  715. @modified="examRemarkModified"
  716. ></modify-rich-text>
  717. <!-- 编辑大题弹框 -->
  718. <modify-detail-struct
  719. ref="ModifyDetailStruct"
  720. :detail="curDetail"
  721. only-name
  722. show-selective
  723. @modified="detailModified"
  724. ></modify-detail-struct>
  725. <!-- 基础构成 -->
  726. <paper-base-info ref="PaperBaseInfo" :paper-id="paperId"></paper-base-info>
  727. <!-- 上传答案文件 -->
  728. <import-file-dialog
  729. ref="ImportAnswerDialog"
  730. dialog-title="上传答案文件"
  731. :template-url="answerTemplateUrl"
  732. :upload-url="uploadAnswerUrl"
  733. add-file-param="dataFile"
  734. @uploaded="initPaper"
  735. ></import-file-dialog>
  736. <!-- 上传属性文件 -->
  737. <import-file-dialog
  738. ref="ImportPorpertyDialog"
  739. dialog-title="上传属性文件"
  740. :template-url="propertyTemplateUrl"
  741. :upload-url="uploadPropertyUrl"
  742. add-file-param="dataFile"
  743. @uploaded="initPaper"
  744. ></import-file-dialog>
  745. <!-- 题型分布 -->
  746. <paper-questype-info
  747. ref="PaperQuestypeInfo"
  748. :paper-id="paperId"
  749. ></paper-questype-info>
  750. <!-- 蓝图分布 -->
  751. <paper-blue-info
  752. ref="PaperBlueInfo"
  753. :paper-id="paperId"
  754. :course-id="paper.course.id"
  755. ></paper-blue-info>
  756. <!-- 审核记录 -->
  757. <paper-audit-info
  758. ref="PaperAuditInfo"
  759. :paper-id="paperId"
  760. ></paper-audit-info>
  761. <!-- 试题编辑 -->
  762. <question-edit-dialog
  763. ref="QuestionEditDialog"
  764. :question="curQuestion"
  765. edit-mode="paper"
  766. @modified="questionEdited"
  767. ></question-edit-dialog>
  768. <!-- SelectQuestionDialog -->
  769. <select-question-dialog
  770. ref="SelectQuestionDialog"
  771. :course-id="paper.course.id"
  772. :disabled-question-ids="paperQuestionIds"
  773. select-mode="paper"
  774. @confirm="questionSelected"
  775. ></select-question-dialog>
  776. </div>
  777. </template>
  778. <script>
  779. import {
  780. paperDetailApi,
  781. paperSaveApi,
  782. paperDeleteApi,
  783. paperDetailUpdateApi,
  784. paperDetailMoveApi,
  785. paperDetailDeleteApi,
  786. paperQuestionMoveApi,
  787. paperQuestionDeleteApi,
  788. paperQuestionSaveApi,
  789. paperDetailAddQuestionApi,
  790. paperQuestionChangeApi,
  791. paperCardDeleteApi,
  792. paperPropertyExportApi,
  793. paperDetailAddApi,
  794. } from "../api";
  795. import { QUESTION_API } from "@/constants/constants";
  796. import ImportFileDialog from "@/components/ImportFileDialog.vue";
  797. import PaperBaseInfo from "../components/PaperBaseInfo.vue";
  798. import PaperQuestypeInfo from "../components/PaperQuestypeInfo.vue";
  799. import PaperBlueInfo from "../components/PaperBlueInfo.vue";
  800. import PaperAuditInfo from "../components/PaperAuditInfo.vue";
  801. import ModifyDetailStruct from "../components/ModifyDetailStruct.vue";
  802. import ModifyRichText from "@/components/ModifyRichText.vue";
  803. import QuestionEditDialog from "../../question/components/QuestionEditDialog.vue";
  804. import SelectQuestionDialog from "../components/SelectQuestionDialog.vue";
  805. import QuestionAnswer from "../../question/components/QuestionAnswer.vue";
  806. import { calcSum } from "@/plugins/utils";
  807. import { downloadByApi } from "@/plugins/download";
  808. import { mapGetters } from "vuex";
  809. export default {
  810. name: "EditPaper",
  811. components: {
  812. PaperBaseInfo,
  813. PaperQuestypeInfo,
  814. PaperAuditInfo,
  815. PaperBlueInfo,
  816. ModifyDetailStruct,
  817. ModifyRichText,
  818. QuestionEditDialog,
  819. ImportFileDialog,
  820. SelectQuestionDialog,
  821. QuestionAnswer,
  822. },
  823. data() {
  824. return {
  825. paperId: "",
  826. parentView: "",
  827. paper: {
  828. name: "",
  829. title: "",
  830. totalScore: 0,
  831. course: {
  832. id: "",
  833. code: "",
  834. name: "",
  835. },
  836. examRemark: "",
  837. paperDetails: [],
  838. },
  839. checkDuplicateBtnShow: false,
  840. quesTagShow: true,
  841. quesAnswerShow: false,
  842. curDetail: {},
  843. curQuestion: {},
  844. paperQuestionIds: [],
  845. changeSelectQuestionMap: {},
  846. downloading: false,
  847. // upload answer
  848. uploadAnswerUrl: "",
  849. answerTemplateUrl: "",
  850. // upload property
  851. propertyTemplateUrl: "",
  852. uploadPropertyUrl: "",
  853. };
  854. },
  855. computed: {
  856. user() {
  857. return this.$store.state.user;
  858. },
  859. enableCardEdit() {
  860. return this.paper.auditStatus === "PASS";
  861. },
  862. ...mapGetters(["authButtons"]),
  863. },
  864. created() {
  865. let qt = sessionStorage.getItem("quesTagShow");
  866. if (qt) {
  867. this.quesTagShow = qt === "true";
  868. }
  869. let qa = sessionStorage.getItem("quesAnswerShow");
  870. if (qa) {
  871. this.quesAnswerShow = qa === "true";
  872. }
  873. this.paperId = Number(this.$route.params.id);
  874. this.parentView = this.$route.params.parentView;
  875. this.initPaper();
  876. },
  877. methods: {
  878. async initPaper() {
  879. const res = await paperDetailApi(this.paperId);
  880. res.data.paperDetails.forEach((detail) => {
  881. detail.showQuestions = true;
  882. detail.paperDetailUnits.forEach((question) => {
  883. question.showSubQuestions = true;
  884. });
  885. });
  886. this.paper = res.data;
  887. this.paper.title = this.paper.title || this.paper.name;
  888. this.showCheckDuplicate();
  889. if (!this.checkPaperQuestionAllHasScore()) {
  890. this.$notify.error("有小题未设置分值!");
  891. }
  892. },
  893. previewPDF2() {
  894. window.open(
  895. this.getRouterPath({
  896. name: "PaperTemplateBuild",
  897. params: {
  898. paperId: this.paper.id,
  899. },
  900. })
  901. );
  902. },
  903. // header-actions
  904. showCheckDuplicate() {
  905. if (this.paper.checkDuplicateStatus === "DISPOSED") {
  906. this.checkDuplicateBtnShow = false;
  907. return;
  908. }
  909. this.checkDuplicateBtnShow = this.paper.paperDetails.some((detail) =>
  910. detail.paperDetailUnits.some(
  911. (question) =>
  912. question.question.checkDuplicateStatus === "TO_BE_DISPOSE"
  913. )
  914. );
  915. },
  916. toCheckDuplicate() {
  917. this.$router.push({
  918. name: "check_duplicate_info",
  919. query: {
  920. basePaperId: this.paper.id,
  921. from: "paper",
  922. },
  923. });
  924. },
  925. checkPaperQuestionAllHasScore() {
  926. return !this.paper.paperDetails.some((detail) => {
  927. return detail.paperDetailUnits.some((qUnit) => !qUnit.score);
  928. });
  929. },
  930. // 保存试卷
  931. async toSavePaper() {
  932. const confirm = await this.$confirm(`确定保存吗?`, "系统通知", {
  933. type: "warning",
  934. }).catch(() => {});
  935. if (confirm !== "confirm") return;
  936. const res = await paperSaveApi(this.paper).catch(() => {});
  937. if (!res) return;
  938. this.$message.success("保存成功!");
  939. },
  940. // 删除试卷
  941. async toDeletePaper() {
  942. const confirm = await this.$confirm(`确定要删除该试卷吗?`, "系统通知", {
  943. type: "warning",
  944. }).catch(() => {});
  945. if (confirm !== "confirm") return;
  946. const res = await paperDeleteApi(this.paper.id).catch(() => {});
  947. if (!res) return;
  948. this.$message.success("删除成功!");
  949. this.toBack();
  950. },
  951. importCommand(command) {
  952. if (command == "answer") {
  953. this.toImportPaperAnswer();
  954. } else {
  955. this.toImportPaperProperty();
  956. }
  957. },
  958. // 导入答案
  959. toImportPaperAnswer() {
  960. this.answerTemplateUrl = `${QUESTION_API}/paper/answer/export/${this.paperId}`;
  961. this.uploadAnswerUrl = `${QUESTION_API}/paper/answer/import/${this.paperId}`;
  962. this.$refs.ImportAnswerDialog.open();
  963. },
  964. // 导出试卷蓝图
  965. async toExportPaperBlue() {
  966. if (this.downloading) return;
  967. this.downloading = true;
  968. const res = await downloadByApi(() => {
  969. return paperPropertyExportApi(this.paperId);
  970. }).catch((e) => {
  971. this.$message.error(e || "下载失败,请重新尝试!");
  972. });
  973. this.downloading = false;
  974. if (!res) return;
  975. this.$message.success("下载成功!");
  976. },
  977. // 导入属性
  978. toImportPaperProperty() {
  979. this.propertyTemplateUrl = `${QUESTION_API}/paper/property/template?paperId=${this.paperId}`;
  980. this.uploadPropertyUrl = `${QUESTION_API}/paper/property/import?paperId=${this.paperId}`;
  981. this.$refs.ImportPorpertyDialog.open();
  982. },
  983. toBack() {
  984. window.history.go(-1);
  985. },
  986. // 显示/隐藏答案
  987. toSwitchQuesAnswerShowHide() {
  988. this.quesAnswerShow = !this.quesAnswerShow;
  989. sessionStorage.setItem("quesAnswerShow", this.quesAnswerShow);
  990. },
  991. // 显示/隐藏属性
  992. toSwitchQuesTagShowHide() {
  993. this.quesTagShow = !this.quesTagShow;
  994. sessionStorage.setItem("quesTagShow", this.quesTagShow);
  995. },
  996. // 查看基础构成
  997. toViewBaseInfo() {
  998. this.$refs.PaperBaseInfo.open();
  999. },
  1000. // 查看题型分布
  1001. toViewQuestypeInfo() {
  1002. this.$refs.PaperQuestypeInfo.open();
  1003. },
  1004. // 查看蓝图分布
  1005. toViewBlueInfo() {
  1006. this.$refs.PaperBlueInfo.open();
  1007. },
  1008. // 查看审核信息
  1009. toViewAuditInfo() {
  1010. this.$refs.PaperAuditInfo.open();
  1011. },
  1012. // 编辑题卡
  1013. toEditCard() {
  1014. this.$router.push({
  1015. name: "CardEdit",
  1016. params: {
  1017. idType: "paper",
  1018. paperOrCardId: this.paperId,
  1019. },
  1020. });
  1021. },
  1022. async toDeletCard() {
  1023. const confirm = await this.$confirm(
  1024. `确定要清除当前试卷的题卡吗?`,
  1025. "提示",
  1026. {
  1027. type: "warning",
  1028. }
  1029. ).catch(() => {});
  1030. if (confirm !== "confirm") return;
  1031. await paperCardDeleteApi(this.paperId);
  1032. this.$message.success("操作成功!");
  1033. },
  1034. // header-action ----end
  1035. // 考试说明
  1036. toEditExamRemark() {
  1037. this.$refs.ModifyRichText.open();
  1038. },
  1039. examRemarkModified(content) {
  1040. this.paper.examRemark = content;
  1041. this.toSavePaper();
  1042. },
  1043. getPaperQuestionIds() {
  1044. let ids = [];
  1045. this.paper.paperDetails.forEach((detail) => {
  1046. let qids = detail.paperDetailUnits.map((q) => q.question.id);
  1047. ids.push(...qids);
  1048. });
  1049. return ids;
  1050. },
  1051. // detail-action
  1052. // 大题选题
  1053. toSelectPaperDetailQues(detail) {
  1054. this.curDetail = detail;
  1055. this.paperQuestionIds = this.getPaperQuestionIds();
  1056. this.$refs.SelectQuestionDialog.open();
  1057. },
  1058. async questionSelected(questions) {
  1059. if (!questions.length) {
  1060. this.$message.error("请选择试题!");
  1061. return;
  1062. }
  1063. const res = await paperDetailAddQuestionApi(
  1064. this.paperId,
  1065. this.curDetail.id,
  1066. questions
  1067. ).catch(() => {});
  1068. if (!res) return;
  1069. this.$message.success("添加成功!");
  1070. this.initPaper();
  1071. },
  1072. addBigQuestion() {
  1073. this.curDetail = {};
  1074. this.$refs.ModifyDetailStruct.open();
  1075. },
  1076. // 大题编辑
  1077. toEditPaperDetail(detail) {
  1078. this.curDetail = { ...detail, detailName: detail.name };
  1079. this.$refs.ModifyDetailStruct.open();
  1080. },
  1081. async detailModified(detail, isAdd) {
  1082. if (isAdd) {
  1083. const res = paperDetailAddApi({
  1084. paperId: this.paperId,
  1085. name: detail.detailName,
  1086. description: detail.description,
  1087. }).catch(() => {});
  1088. if (!res) return;
  1089. this.$message.success("新增成功!");
  1090. } else {
  1091. detail.name = detail.detailName;
  1092. const res = await paperDetailUpdateApi(this.paperId, detail).catch(
  1093. () => {}
  1094. );
  1095. if (!res) return;
  1096. this.$message.success("修改成功!");
  1097. }
  1098. // const paperDetail = this.paper.paperDetails.find(
  1099. // (item) => item.id === detail.id
  1100. // );
  1101. // if (!paperDetail) return;
  1102. // paperDetail.name = detail.detailName;
  1103. // paperDetail.description = detail.description;
  1104. this.initPaper();
  1105. },
  1106. // 移动大题
  1107. async toMovePaperDetail(detail, vector) {
  1108. const vectorStr = vector == "up" ? "上移" : "下移";
  1109. const confirm = await this.$confirm(
  1110. `确定要${vectorStr}该大题吗?`,
  1111. "提示",
  1112. {
  1113. type: "warning",
  1114. }
  1115. ).catch(() => {});
  1116. if (confirm !== "confirm") return;
  1117. const res = await paperDetailMoveApi({
  1118. paperId: this.paperId,
  1119. detailId: detail.id,
  1120. vector,
  1121. }).catch(() => {});
  1122. if (!res) return;
  1123. this.$message.success("操作成功!");
  1124. this.initPaper();
  1125. },
  1126. // 删除大题
  1127. async toDeletePaperDetail(detail) {
  1128. if (detail.paperDetailUnits.length) {
  1129. this.$message.error("大题下还有小题,不可删除!");
  1130. return;
  1131. }
  1132. const confirm = await this.$confirm(`确定要删除该大题吗?`, "提示", {
  1133. type: "warning",
  1134. }).catch(() => {});
  1135. if (confirm !== "confirm") return;
  1136. const res = await paperDetailDeleteApi({
  1137. paperId: this.paperId,
  1138. detailId: detail.id,
  1139. }).catch(() => {});
  1140. if (!res) return;
  1141. this.$message.success("操作成功!");
  1142. this.initPaper();
  1143. },
  1144. // detail-action ----end
  1145. // question-action
  1146. toCheckDuplicateQuestion(questionId) {
  1147. this.$router.push({
  1148. name: "check_duplicate_info",
  1149. query: {
  1150. quesId: questionId,
  1151. basePaperId: this.paperId,
  1152. from: "paper",
  1153. },
  1154. });
  1155. },
  1156. // 更换试题
  1157. async toChangeQues(row) {
  1158. let question = row.question;
  1159. if (!this.changeSelectQuestionMap[question.sourceDetailId])
  1160. this.changeSelectQuestionMap[question.sourceDetailId] = [];
  1161. const res = await paperQuestionChangeApi(
  1162. row.id,
  1163. this.changeSelectQuestionMap[question.sourceDetailId].join()
  1164. ).catch(() => {});
  1165. if (!res) return;
  1166. if (!this.changeSelectQuestionMap[question.sourceDetailId].length) {
  1167. this.changeSelectQuestionMap[question.sourceDetailId].push(question.id);
  1168. }
  1169. this.changeSelectQuestionMap[question.sourceDetailId].push(res.data);
  1170. this.initPaper();
  1171. },
  1172. // 编辑小题
  1173. toEditQues(row) {
  1174. this.curQuestion = row;
  1175. this.$refs.QuestionEditDialog.open();
  1176. },
  1177. async questionEdited(question) {
  1178. let subScoreList = [];
  1179. if (question.subQuestions && question.subQuestions.length) {
  1180. subScoreList = question.subQuestions.map((item) => item.score || 0);
  1181. question.score = calcSum(subScoreList);
  1182. }
  1183. let questionUnit = {
  1184. id: this.curQuestion.id,
  1185. question,
  1186. score: question.score,
  1187. };
  1188. if (subScoreList.length) questionUnit.subScoreList = subScoreList;
  1189. const res = await paperQuestionSaveApi(questionUnit).catch(() => {});
  1190. if (!res) return;
  1191. this.$message.success("保存成功!");
  1192. this.initPaper();
  1193. },
  1194. // 移动小题
  1195. async toMoveQues(detailId, unitid, vector) {
  1196. const vectorStr = vector == "up" ? "上移" : "下移";
  1197. const confirm = await this.$confirm(
  1198. `确定要${vectorStr}该小题吗?`,
  1199. "提示",
  1200. {
  1201. type: "warning",
  1202. }
  1203. ).catch(() => {});
  1204. if (confirm !== "confirm") return;
  1205. const res = await paperQuestionMoveApi({
  1206. detailId,
  1207. unitid,
  1208. vector,
  1209. }).catch(() => {});
  1210. if (!res) return;
  1211. this.$message.success("操作成功!");
  1212. this.initPaper();
  1213. },
  1214. // 删除小题
  1215. async toDeleteQues(paperDetailUnit) {
  1216. const confirm = await this.$confirm(`确定要删除该题吗?`, "提示", {
  1217. type: "warning",
  1218. }).catch(() => {});
  1219. if (confirm !== "confirm") return;
  1220. const res = await paperQuestionDeleteApi(paperDetailUnit.id).catch(
  1221. () => {}
  1222. );
  1223. if (!res) return;
  1224. this.$message.success("操作成功!");
  1225. this.initPaper();
  1226. },
  1227. // other
  1228. isNested(questionType) {
  1229. const nestedQuestion = [
  1230. "PARAGRAPH_MATCHING",
  1231. "BANKED_CLOZE",
  1232. "CLOZE",
  1233. "READING_COMPREHENSION",
  1234. "LISTENING_QUESTION",
  1235. ];
  1236. return nestedQuestion.includes(questionType);
  1237. },
  1238. isMatchingQuestion(questionType) {
  1239. const typeQuestion = ["PARAGRAPH_MATCHING", "BANKED_CLOZE"];
  1240. return typeQuestion.includes(questionType);
  1241. },
  1242. },
  1243. };
  1244. </script>
  1245. <style lang="scss" scoped>
  1246. .edit-paper {
  1247. .edit-part {
  1248. &.level2 {
  1249. padding-bottom: 60px;
  1250. & > .edit-cont {
  1251. .edit-cont-title > .rich-text {
  1252. vertical-align: top;
  1253. }
  1254. }
  1255. .level2-hover-show {
  1256. display: none;
  1257. position: absolute;
  1258. bottom: 15px;
  1259. left: 20px;
  1260. z-index: 1;
  1261. width: calc(100% - 450px);
  1262. }
  1263. &:hover {
  1264. .level2-hover-show {
  1265. display: block;
  1266. }
  1267. }
  1268. }
  1269. &.level1 {
  1270. padding-bottom: 80px;
  1271. .level1-hover-show {
  1272. display: none;
  1273. position: absolute;
  1274. bottom: 15px;
  1275. left: 20px;
  1276. z-index: 1;
  1277. }
  1278. &:hover {
  1279. .level1-hover-show {
  1280. display: block;
  1281. }
  1282. }
  1283. }
  1284. .hover-show {
  1285. visibility: hidden;
  1286. }
  1287. &:hover {
  1288. .hover-show {
  1289. visibility: visible;
  1290. }
  1291. }
  1292. .gray-btn-group {
  1293. height: 32px;
  1294. border-radius: 6px;
  1295. background: #f2f3f5;
  1296. display: flex;
  1297. align-items: center;
  1298. & > span {
  1299. color: #8c8c8c;
  1300. font-size: 14px;
  1301. padding: 0 12px;
  1302. cursor: pointer;
  1303. transition: all 0.3s;
  1304. &:not(:first-child) {
  1305. border-left: 1px solid #e5e5e5;
  1306. }
  1307. &:hover {
  1308. color: #262626;
  1309. }
  1310. }
  1311. }
  1312. }
  1313. }
  1314. </style>