addPage.vue 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. <!-- 新建试卷 -->
  2. <template>
  3. <div class="page-container testPaperManagement-addPage">
  4. <div class="page-container-top-max-big-box">
  5. <p class="top-1-p">{{newData.id?'编辑试卷':'新建试卷'}}</p>
  6. <p class="top-2-p" @click="backPage()">返回</p>
  7. <p class="top-3-p" v-if="newData.id&&!showType" @click="resetButton()">重置</p>
  8. <p class="top-4-p" @click="submitButton()" v-if="!showType">保存</p>
  9. </div>
  10. <div class="content-box scrollbar-box">
  11. <el-form class="add-form-box" :model="newData" ref="form" label-width="100px" :rules="rules">
  12. <!-- 第一块:基本信息 -->
  13. <div class="section-box">
  14. <!-- 试卷名称 -->
  15. <el-form-item label="试卷名称" prop="paperName" class="full-width-item">
  16. <el-input v-model="newData.paperName" maxLength="30" placeholder="请输入名称" :disabled="showType"/>
  17. </el-form-item>
  18. <!-- 总分数 / 及格分数 / 答题时间 -->
  19. <div class="flex-row-box">
  20. <el-form-item label="总分数" prop="totalScore">
  21. <div class="input-unit-box">
  22. <el-input-number
  23. :disabled="showType"
  24. class="form-input-number"
  25. style="width:160px"
  26. :min="0"
  27. :max="9999"
  28. :step="1" :precision="0" step-strictly
  29. v-model="newData.totalScore"
  30. :controls="false"
  31. >
  32. </el-input-number>
  33. <span class="unit-text">分</span>
  34. </div>
  35. </el-form-item>
  36. <el-form-item label="及格分数" prop="passScore">
  37. <div class="input-unit-box">
  38. <el-input-number
  39. :disabled="showType"
  40. class="form-input-number"
  41. style="width:160px"
  42. :min="0"
  43. :max="9999"
  44. :step="1" :precision="0" step-strictly
  45. v-model="newData.passScore"
  46. :controls="false"
  47. >
  48. </el-input-number>
  49. <span class="unit-text">分</span>
  50. </div>
  51. </el-form-item>
  52. <!--<el-form-item label="答题时间" prop="answerDuration">-->
  53. <!--<div class="input-unit-box">-->
  54. <!--<el-input-number-->
  55. <!--:disabled="showType"-->
  56. <!--class="form-input-number"-->
  57. <!--style="width:160px"-->
  58. <!--:min="0"-->
  59. <!--:max="9999"-->
  60. <!--:step="1" :precision="0" step-strictly-->
  61. <!--v-model="newData.answerDuration"-->
  62. <!--:controls="false"-->
  63. <!--&gt;-->
  64. <!--</el-input-number>-->
  65. <!--<span class="unit-text">分钟</span>-->
  66. <!--</div>-->
  67. <!--</el-form-item>-->
  68. </div>
  69. <!-- 专业类别 / 是否共享 -->
  70. <div class="flex-row-box">
  71. <el-form-item label="专业类别" prop="disciplineTypeId">
  72. <el-select :disabled="showType" v-model="newData.disciplineTypeId" placeholder="专业类别" style="width:200px;">
  73. <el-option
  74. v-for="item in optionListA"
  75. :key="item.id"
  76. :label="item.disciplineName"
  77. :value="item.id"
  78. />
  79. </el-select>
  80. </el-form-item>
  81. <el-form-item label="是否共享" prop="shareStatus">
  82. <el-switch
  83. :disabled="showType"
  84. v-model="newData.shareStatus"
  85. :active-value="1"
  86. :inactive-value="0"
  87. active-color="#0183FB"
  88. inactive-color="#AAAAAA"
  89. />
  90. <span class="share-tip">{{newData.shareStatus == 1 ? '共享开启,全校所有学院单位都可见' : '不共享,仅本学院单位及上级都可见'}}</span>
  91. </el-form-item>
  92. </div>
  93. </div>
  94. <!-- 分隔线 -->
  95. <div class="section-divider"></div>
  96. <!-- 第二块:组卷方式 & 知识点 -->
  97. <div class="section-box">
  98. <!-- 组卷方式 -->
  99. <el-form-item label="组卷方式" prop="assemblyMode">
  100. <el-radio-group v-model="newData.assemblyMode" :disabled="showType">
  101. <el-radio :label="1">固定组卷</el-radio>
  102. <el-radio :label="2">随机组卷</el-radio>
  103. </el-radio-group>
  104. </el-form-item>
  105. <!-- 选择知识点 -->
  106. <el-form-item label="选择知识点" prop="knowledgePointId" class="knowledge-form-item">
  107. <!-- 统计信息条 -->
  108. <div class="stat-bar">
  109. <span>总题目数:<b>{{totalQuestionCount}}</b></span>
  110. <span style="margin-left:20px;">总分:<b>{{totalScore}}</b></span>
  111. <span style="margin-left:20px;">
  112. <span v-if="remainScore > 0">还需设置 <b>{{remainScore}}</b> 分的题目</span>
  113. <span v-if="remainScore < 0">超过总分数 <b>{{Math.abs(remainScore)}}</b> 分</span>
  114. </span>
  115. </div>
  116. <!-- 已选知识点标签 -->
  117. <div class="knowledge-tag-box">
  118. <el-cascader
  119. :disabled="showType"
  120. ref="knowledgeCascader"
  121. v-model="newData.knowledgePointId"
  122. :options="optionListKnowledge"
  123. :props="{
  124. multiple :true,
  125. emitPath: false,
  126. checkStrictly: true,
  127. value: 'id',
  128. label: 'knowledgePointName',
  129. children: 'children',
  130. }"
  131. :show-all-levels="false"
  132. filterable
  133. placeholder="选择知识点"
  134. style="width:1420px;"
  135. @change="onKnowledgeSelect"
  136. />
  137. </div>
  138. <!-- 知识点明细表格 -->
  139. <div class="knowledge-table" v-if="fixedKnowledgePointConfigs.length > 0">
  140. <div class="table-header">
  141. <span class="col-name">知识点</span>
  142. <span class="col-count">试题数量</span>
  143. <span class="col-score">每题分数</span>
  144. <span class="col-fixed">{{newData.assemblyMode==1?'固定选题':'随机选题'}}</span>
  145. </div>
  146. <div
  147. class="table-row"
  148. v-for="(item, index) in fixedKnowledgePointConfigs"
  149. :key="index"
  150. @dragstart="onBigDragStart($event, index)"
  151. @dragover.prevent="onBigDragOver($event, index)"
  152. @drop="onBigDrop($event, index)"
  153. @dragend="onDragEnd"
  154. >
  155. <div class="table-big-row">
  156. <span class="col-name color-name">
  157. <span class="roller-span" v-show="newData.assemblyMode==2"
  158. @click="rollerButton(index)"
  159. :class="item.rollerType?'el-icon-arrow-up':'el-icon-arrow-down'"></span>
  160. {{item.name}}
  161. </span>
  162. <span class="col-count color-name">{{item.questionTotalCount}}</span>
  163. <span class="col-score">
  164. <span class="number-stepper">
  165. <el-input-number
  166. :disabled="showType"
  167. v-if="newData.assemblyMode==1"
  168. @change="calculationMaxData()"
  169. class="form-input-number"
  170. style="width:160px"
  171. :min="0"
  172. :max="9999"
  173. :step="1" :precision="0" step-strictly
  174. v-model="item.questionScore"
  175. >
  176. </el-input-number>
  177. </span>
  178. </span>
  179. <span class="col-fixed">
  180. <span class="item-num-span" v-if="newData.assemblyMode==1">{{item.questionIds.length}}</span>
  181. <span class="el-icon-circle-plus" v-if="newData.assemblyMode==1&&!showType" @click="addFixed(index)"></span>
  182. <span class="el-icon-rank" draggable="true" v-if="!showType"></span>
  183. </span>
  184. </div>
  185. <div v-if="newData.assemblyMode==2 && item.rollerType" class="table-min-row">
  186. <span class="col-name">
  187. <span class="roller-span">单选题</span>
  188. </span>
  189. <span class="col-count color-name">{{item.singleQuestionCount}}</span>
  190. <span class="col-score">
  191. <span class="number-stepper">
  192. <el-input-number
  193. :disabled="showType"
  194. class="form-input-number"
  195. @change="calculationMaxData()"
  196. style="width:160px"
  197. :min="0"
  198. :max="9999"
  199. :step="1" :precision="0" step-strictly
  200. v-model="item.scoreA"
  201. >
  202. </el-input-number>
  203. </span>
  204. </span>
  205. <span class="col-fixed">
  206. <span class="number-stepper">
  207. <el-input-number
  208. :disabled="showType"
  209. class="form-input-number"
  210. @change="calculationMaxData()"
  211. style="width:80px"
  212. :min="0"
  213. :max="item.singleQuestionCount"
  214. :step="1" :precision="0" step-strictly
  215. v-model="item.checkNumA"
  216. :controls="false"
  217. >
  218. </el-input-number>
  219. </span>
  220. </span>
  221. </div>
  222. <div v-if="newData.assemblyMode==2 && item.rollerType" class="table-min-row">
  223. <span class="col-name">
  224. <span class="roller-span">多选题</span>
  225. </span>
  226. <span class="col-count color-name">{{item.multipleQuestionCount}}</span>
  227. <span class="col-score">
  228. <span class="number-stepper">
  229. <el-input-number
  230. :disabled="showType"
  231. class="form-input-number"
  232. @change="calculationMaxData()"
  233. style="width:160px"
  234. :min="0"
  235. :max="9999"
  236. :step="1" :precision="0" step-strictly
  237. v-model="item.scoreB"
  238. >
  239. </el-input-number>
  240. </span>
  241. </span>
  242. <span class="col-fixed">
  243. <span class="number-stepper">
  244. <el-input-number
  245. :disabled="showType"
  246. class="form-input-number"
  247. @change="calculationMaxData()"
  248. style="width:80px"
  249. :min="0"
  250. :max="item.multipleQuestionCount"
  251. :step="1" :precision="0" step-strictly
  252. v-model="item.checkNumB"
  253. :controls="false"
  254. >
  255. </el-input-number>
  256. </span>
  257. </span>
  258. </div>
  259. <div v-if="newData.assemblyMode==2 && item.rollerType" class="table-min-row">
  260. <span class="col-name">
  261. <span class="roller-span">判断题</span>
  262. </span>
  263. <span class="col-count color-name">{{item.judgeQuestionCount}}</span>
  264. <span class="col-score">
  265. <span class="number-stepper">
  266. <el-input-number
  267. :disabled="showType"
  268. class="form-input-number"
  269. @change="calculationMaxData()"
  270. style="width:160px"
  271. :min="0"
  272. :max="9999"
  273. :step="1" :precision="0" step-strictly
  274. v-model="item.scoreC"
  275. >
  276. </el-input-number>
  277. </span>
  278. </span>
  279. <span class="col-fixed">
  280. <span class="number-stepper">
  281. <el-input-number
  282. :disabled="showType"
  283. class="form-input-number"
  284. @change="calculationMaxData()"
  285. style="width:80px"
  286. :min="0"
  287. :max="item.judgeQuestionCount"
  288. :step="1" :precision="0" step-strictly
  289. v-model="item.checkNumC"
  290. :controls="false"
  291. >
  292. </el-input-number>
  293. </span>
  294. </span>
  295. </div>
  296. </div>
  297. </div>
  298. </el-form-item>
  299. </div>
  300. </el-form>
  301. </div>
  302. <el-drawer
  303. size="100%"
  304. custom-class="testPaper-select-drawer"
  305. :append-to-body="true"
  306. :show-close="false"
  307. title="固定选题"
  308. ref="drawer"
  309. :visible.sync="drawer"
  310. direction="rtl"
  311. >
  312. <drawer-item v-if="drawer" :drawerPropsData="drawerPropsData"/>
  313. </el-drawer>
  314. </div>
  315. </template>
  316. <script>
  317. import {
  318. examElPaperAdd,examElPaperEdit,examElKnowledgePointTreeList,examElPaperDetail,
  319. examElDisciplineTypeList,examElQuestionBankKnowledgePointCount
  320. } from '@/api/safetyEducationExaminationNew/index'
  321. import drawerItem from './drawerItem.vue'
  322. import { accAdd,accMul } from '@/utils/calculation'
  323. export default {
  324. name: 'addPage',
  325. components: { drawerItem },
  326. props: {
  327. propsData: {}
  328. },
  329. data() {
  330. const validateData = (rule, value, callback) => {
  331. if (value == 0) {
  332. callback(new Error('总分数不能为0'))
  333. } else if (this.newData.passScore > value) {
  334. callback(new Error('总分数不能小于及格分数'))
  335. } else {
  336. callback()
  337. }
  338. }
  339. const validateData1 = (rule, value, callback) => {
  340. if (value == 0) {
  341. callback(new Error('及格分数不能为0'))
  342. } else if (this.newData.passScore > value) {
  343. callback(new Error('及格分数不能大于总分数'))
  344. } else {
  345. callback()
  346. }
  347. }
  348. const validateData2 = (rule, value, callback) => {
  349. if (value == 0) {
  350. callback(new Error('答题时间不能为0'))
  351. } else {
  352. callback()
  353. }
  354. }
  355. return {
  356. totalQuestionCount:0,
  357. totalScore:0,
  358. newData: {
  359. paperName: '',
  360. totalScore: 0,
  361. passScore: 0,
  362. // answerDuration: 0,
  363. disciplineTypeId: null,
  364. shareStatus: true,
  365. assemblyMode: 1,
  366. knowledgePointId: []
  367. },
  368. rules: {
  369. paperName: [
  370. { required: true, message: '请输入试卷名称', trigger: 'blur' }
  371. ],
  372. totalScore: [
  373. { required: true, message: '请输入总分数', trigger: 'blur' },
  374. { validator: validateData, trigger: 'blur' }
  375. ],
  376. passScore: [
  377. { required: true, message: '请输入及格分数', trigger: 'blur' },
  378. { validator: validateData1, trigger: 'blur' }
  379. ],
  380. // answerDuration: [
  381. // { required: true, message: '请输入答题时间', trigger: 'blur' },
  382. // { validator: validateData2, trigger: 'blur' }
  383. // ],
  384. assemblyMode: [
  385. { required: true, message: '请选择组卷方式', trigger: 'change' }
  386. ],
  387. knowledgePointId: [
  388. { required: true, message: '请选择知识点', trigger: 'change' }
  389. ]
  390. },
  391. optionListA: [],
  392. optionListKnowledge: [],
  393. fixedKnowledgePointConfigs: [],
  394. //弹窗相关
  395. drawer: false,
  396. drawerPropsData:{},
  397. //拖拽相关
  398. dragInfo: {
  399. type: null, // 'big' 或 'small'
  400. bigIndex: null,
  401. smallIndex: null
  402. },
  403. //预览状态
  404. showType:false,
  405. }
  406. },
  407. computed: {
  408. remainScore() {
  409. // return Math.max(0, Number(this.newData.totalScore || 0) - this.totalScore)
  410. return Number(this.newData.totalScore || 0) - this.totalScore
  411. }
  412. },
  413. watch: {
  414. 'newData.knowledgePointId': {
  415. handler(newVal, oldVal) {
  416. // 假设最多只能选 2 个
  417. if (newVal && newVal.length > 10) {
  418. this.msgError('最多选择10个知识点')
  419. this.$nextTick(() => {
  420. // 将值回退到上一次的状态
  421. this.newData.knowledgePointId = oldVal;
  422. });
  423. }
  424. },
  425. deep: false,
  426. immediate: false
  427. }
  428. },
  429. mounted() {
  430. this.initialize()
  431. this.examElKnowledgePointTreeList()
  432. this.examElDisciplineTypeList()
  433. },
  434. methods: {
  435. //弹窗返回
  436. drawerReturnButton(){
  437. this.$refs.drawer.closeDrawer();
  438. },
  439. //弹窗确定
  440. drawerConfirmButton(obj){
  441. this.fixedKnowledgePointConfigs[obj.index].questionIds = [...obj.questionIds]
  442. this.calculationMaxData();
  443. this.$refs.drawer.closeDrawer();
  444. },
  445. initialize() {
  446. let self = this;
  447. if (this.propsData.id) {
  448. if(this.propsData.showType){
  449. this.$set(this,'showType',true);
  450. }
  451. examElPaperDetail({id:this.propsData.id}).then(response => {
  452. let obj = {
  453. id:this.propsData.id,
  454. paperName:response.data.paperName,
  455. totalScore:response.data.totalScore,
  456. passScore:response.data.passScore,
  457. // answerDuration:response.data.answerDuration,
  458. disciplineTypeId:response.data.disciplineTypeId,
  459. shareStatus:response.data.shareStatus,
  460. assemblyMode:response.data.assemblyMode,
  461. knowledgePointId:[],
  462. };
  463. let list = [];
  464. if(response.data.assemblyMode == 1){
  465. for(let i=0;i<response.data.fixedKnowledgePointDetails.length;i++){
  466. obj.knowledgePointId.push(response.data.fixedKnowledgePointDetails[i].knowledgePointId)
  467. list.push({
  468. id: response.data.fixedKnowledgePointDetails[i].knowledgePointId,
  469. name:response.data.fixedKnowledgePointDetails[i].knowledgePointName,
  470. rollerType: true,
  471. questionIds: response.data.fixedKnowledgePointDetails[i].questionIds,
  472. questionScore: response.data.fixedKnowledgePointDetails[i].questionScore,//每题分数
  473. fixedCount: response.data.fixedKnowledgePointDetails[i].questionIds.length,//选题数量
  474. questionTotalCount:response.data.fixedKnowledgePointDetails[i].questionTotalCount,
  475. singleQuestionCount:0,
  476. multipleQuestionCount:0,
  477. judgeQuestionCount:0,
  478. scoreA:0,
  479. checkNumA:0,
  480. scoreB:0,
  481. checkNumB:0,
  482. scoreC:0,
  483. checkNumC:0,
  484. })
  485. }
  486. }else if(response.data.assemblyMode == 2){
  487. for(let i=0;i<response.data.randomKnowledgePointDetails.length;i++){
  488. obj.knowledgePointId.push(response.data.randomKnowledgePointDetails[i].knowledgePointId)
  489. let minObj = {
  490. id: response.data.randomKnowledgePointDetails[i].knowledgePointId,
  491. name:response.data.randomKnowledgePointDetails[i].knowledgePointName,
  492. rollerType: true,
  493. questionIds: [],
  494. questionScore: 0,//每题分数
  495. fixedCount: 0,//选题数量
  496. questionTotalCount:response.data.randomKnowledgePointDetails[i].questionTotalCount,
  497. singleQuestionCount:0,
  498. multipleQuestionCount:0,
  499. judgeQuestionCount:0,
  500. scoreA:0,
  501. checkNumA:0,
  502. scoreB:0,
  503. checkNumB:0,
  504. scoreC:0,
  505. checkNumC:0,
  506. }
  507. for(let o=0;o<response.data.randomKnowledgePointDetails[i].rules.length;o++){
  508. if(response.data.randomKnowledgePointDetails[i].rules[o].questionType == 1){
  509. minObj.singleQuestionCount = response.data.randomKnowledgePointDetails[i].rules[o].questionTotalCount
  510. minObj.scoreA = response.data.randomKnowledgePointDetails[i].rules[o].questionScore
  511. minObj.checkNumA = response.data.randomKnowledgePointDetails[i].rules[o].questionCount
  512. }
  513. if(response.data.randomKnowledgePointDetails[i].rules[o].questionType == 2){
  514. minObj.multipleQuestionCount = response.data.randomKnowledgePointDetails[i].rules[o].questionTotalCount
  515. minObj.scoreB = response.data.randomKnowledgePointDetails[i].rules[o].questionScore
  516. minObj.checkNumB = response.data.randomKnowledgePointDetails[i].rules[o].questionCount
  517. }
  518. if(response.data.randomKnowledgePointDetails[i].rules[o].questionType == 3){
  519. minObj.judgeQuestionCount = response.data.randomKnowledgePointDetails[i].rules[o].questionTotalCount
  520. minObj.scoreC = response.data.randomKnowledgePointDetails[i].rules[o].questionScore
  521. minObj.checkNumC = response.data.randomKnowledgePointDetails[i].rules[o].questionCount
  522. }
  523. }
  524. list.push(minObj);
  525. }
  526. }
  527. this.$set(this,'newData',obj);
  528. this.$set(this,'fixedKnowledgePointConfigs',list);
  529. this.calculationMaxData();
  530. })
  531. }
  532. },
  533. backPage() {
  534. this.$parent.tableButton(6)
  535. },
  536. resetButton() {
  537. this.initialize();
  538. },
  539. submitButton() {
  540. let self = this;
  541. this.$refs["form"].validate(valid => {
  542. if (valid) {
  543. if(this.remainScore != 0){
  544. this.msgError('总分数与试题分数不一致')
  545. return
  546. }
  547. this.$confirm('是否确认保存?', "提示", {
  548. confirmButtonText: "确定",
  549. cancelButtonText: "取消",
  550. type: "warning"
  551. }).then(function() {
  552. }).then(() => {
  553. self.dataIntegration();
  554. }).catch(() => {});
  555. }
  556. })
  557. },
  558. dataIntegration(){
  559. let self = this;
  560. let obj = {
  561. paperName:this.newData.paperName,
  562. totalScore:this.newData.totalScore,
  563. passScore:this.newData.passScore,
  564. // answerDuration:this.newData.answerDuration,
  565. disciplineTypeId:this.newData.disciplineTypeId,
  566. shareStatus:this.newData.shareStatus,
  567. assemblyMode:this.newData.assemblyMode,
  568. fixedKnowledgePointConfigs:[],
  569. randomKnowledgePointConfigs:[],
  570. };
  571. for(let i=0;i<self.fixedKnowledgePointConfigs.length;i++){
  572. if(this.newData.assemblyMode == 1){
  573. obj.fixedKnowledgePointConfigs.push({
  574. knowledgePointId:self.fixedKnowledgePointConfigs[i].id,
  575. questionScore:self.fixedKnowledgePointConfigs[i].questionScore,
  576. questionIds:self.fixedKnowledgePointConfigs[i].questionIds,
  577. })
  578. }else if(this.newData.assemblyMode == 2){
  579. obj.randomKnowledgePointConfigs.push({
  580. knowledgePointId:self.fixedKnowledgePointConfigs[i].id,
  581. rules:[
  582. {
  583. questionType:1,
  584. questionCount:self.fixedKnowledgePointConfigs[i].checkNumA,
  585. questionScore:self.fixedKnowledgePointConfigs[i].scoreA,
  586. },
  587. {
  588. questionType:2,
  589. questionCount:self.fixedKnowledgePointConfigs[i].checkNumB,
  590. questionScore:self.fixedKnowledgePointConfigs[i].scoreB,
  591. },
  592. {
  593. questionType:3,
  594. questionCount:self.fixedKnowledgePointConfigs[i].checkNumC,
  595. questionScore:self.fixedKnowledgePointConfigs[i].scoreC,
  596. },
  597. ],
  598. })
  599. }
  600. }
  601. if (this.newData.id){
  602. obj.id = this.newData.id;
  603. this.examElPaperEdit(obj);
  604. }else{
  605. this.examElPaperAdd(obj);
  606. }
  607. },
  608. examElPaperAdd(obj){
  609. examElPaperAdd(obj).then(response => {
  610. this.msgSuccess(response.message);
  611. this.$parent.tableButton(6);
  612. })
  613. },
  614. examElPaperEdit(obj){
  615. examElPaperEdit(obj).then(response => {
  616. this.msgSuccess(response.message);
  617. this.$parent.tableButton(6);
  618. })
  619. },
  620. //收起展开
  621. rollerButton(idnex){
  622. this.$set(this.fixedKnowledgePointConfigs[idnex],'rollerType',!this.fixedKnowledgePointConfigs[idnex].rollerType);
  623. },
  624. //change触发过滤知识点列表
  625. onKnowledgeSelect(val) {
  626. if (!Array.isArray(val)) return
  627. for (let i = this.fixedKnowledgePointConfigs.length - 1; i >= 0; i--) {
  628. const currentItem = this.fixedKnowledgePointConfigs[i]
  629. if (!val.includes(currentItem.id)) {
  630. this.fixedKnowledgePointConfigs.splice(i, 1)
  631. }
  632. }
  633. val.forEach(id => {
  634. const exists = this.fixedKnowledgePointConfigs.some(item => item.id === id)
  635. if (!exists) {
  636. const node = this.$refs.knowledgeCascader.getCheckedNodes()
  637. let name = ''
  638. for (let i = 0; i < node.length; i++) {
  639. if (id == node[i].value) {
  640. if(node[i].level == 2){
  641. name = node[i].parent.label +'-'+ node[i].label
  642. }else{
  643. name = node[i].label
  644. }
  645. }
  646. }
  647. if(this.fixedKnowledgePointConfigs[9]){
  648. return
  649. }
  650. this.examElQuestionBankKnowledgePointCount(name,id);
  651. }
  652. })
  653. },
  654. //查询知识点题目数量
  655. examElQuestionBankKnowledgePointCount(name,id) {
  656. examElQuestionBankKnowledgePointCount({knowledgePointId:id}).then(response => {
  657. this.fixedKnowledgePointConfigs.push({
  658. id: id,
  659. name,
  660. rollerType: true,
  661. questionIds: [],
  662. questionScore: 0,//每题分数
  663. fixedCount: 0,//选题数量
  664. questionTotalCount:response.data.questionTotalCount,//试题总量
  665. singleQuestionCount:response.data.singleQuestionCount,//单选
  666. multipleQuestionCount:response.data.multipleQuestionCount,//多选
  667. judgeQuestionCount:response.data.judgeQuestionCount,//判断
  668. scoreA:0,
  669. checkNumA:0,
  670. scoreB:0,
  671. checkNumB:0,
  672. scoreC:0,
  673. checkNumC:0,
  674. })
  675. })
  676. },
  677. addFixed(index) {
  678. this.$set(this,'drawerPropsData',{
  679. titleName:this.fixedKnowledgePointConfigs[index].name,
  680. knowledgePointId:this.fixedKnowledgePointConfigs[index].id,
  681. questionScore:this.fixedKnowledgePointConfigs[index].questionScore,
  682. questionIds:this.fixedKnowledgePointConfigs[index].questionIds,
  683. index:index
  684. });
  685. this.drawer = true
  686. },
  687. // ========== 大项拖拽逻辑 ==========
  688. onBigDragStart(e, index) {
  689. if(this.showType){
  690. return
  691. }
  692. this.dragInfo = { type: 'big', bigIndex: index, smallIndex: null }
  693. //拖拽优化相关
  694. const rowEl = e.target.closest('.table-row');
  695. if (rowEl) {
  696. rowEl.classList.add('dragging');
  697. e.dataTransfer.setDragImage(rowEl, 1177, 30);
  698. }
  699. // 给拖拽中的元素加个样式
  700. // e.target.classList.add('dragging')
  701. },
  702. onBigDragOver(e, index) {
  703. if(this.showType){
  704. return
  705. }
  706. // 只有当拖拽的是大项时才允许放置
  707. if (this.dragInfo.type === 'big') {
  708. e.currentTarget.classList.add('drag-over')
  709. }
  710. },
  711. onBigDrop(e, targetIndex) {
  712. if(this.showType){
  713. return
  714. }
  715. if (this.dragInfo.type !== 'big') return
  716. const sourceIndex = this.dragInfo.bigIndex
  717. if (sourceIndex === targetIndex) return
  718. // 核心:交换数组元素
  719. const movedItem = this.fixedKnowledgePointConfigs.splice(sourceIndex, 1)[0]
  720. this.fixedKnowledgePointConfigs.splice(targetIndex, 0, movedItem)
  721. },
  722. // 拖拽结束,清理样式
  723. onDragEnd(e) {
  724. if(this.showType){
  725. return
  726. }
  727. //拖拽优化相关
  728. const rowEl = e.target.closest('.table-row');
  729. if (rowEl) {
  730. rowEl.classList.remove('dragging');
  731. }
  732. // e.target.classList.remove('dragging')
  733. document.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'))
  734. this.dragInfo = { type: null, bigIndex: null, smallIndex: null }
  735. },
  736. // 辅助方法:获取当前鼠标悬停的大项索引
  737. getCurrentBigIndex(e) {
  738. if(this.showType){
  739. return
  740. }
  741. const minBox = e.currentTarget.closest('.for-max-box')
  742. return Array.from(this.$el.querySelectorAll('.for-max-box')).indexOf(minBox)
  743. },
  744. //查询全部专业类别
  745. examElDisciplineTypeList() {
  746. examElDisciplineTypeList({}).then(response => {
  747. this.$set(this, 'optionListA', response.data)
  748. })
  749. },
  750. //知识点
  751. examElKnowledgePointTreeList() {
  752. examElKnowledgePointTreeList({}).then(response => {
  753. const list = response.data || []
  754. this.formatTreeData(list)
  755. this.$set(this, 'optionListKnowledge', list)
  756. })
  757. },
  758. //计算总分总题
  759. calculationMaxData(){
  760. let self = this;
  761. let totalQuestionCount = 0;
  762. let totalScore = 0;
  763. for(let i=0;i<self.fixedKnowledgePointConfigs.length;i++){
  764. if(self.newData.assemblyMode == 1){
  765. //固定组卷
  766. totalQuestionCount = self.accAdd(totalQuestionCount,self.fixedKnowledgePointConfigs[i].questionIds.length)
  767. let minTotalScore = self.accMul(self.fixedKnowledgePointConfigs[i].questionScore,self.fixedKnowledgePointConfigs[i].questionIds.length);
  768. totalScore = self.accAdd(totalScore,minTotalScore)
  769. }else if(self.newData.assemblyMode == 2){
  770. let numA = self.accMul(self.fixedKnowledgePointConfigs[i].scoreA,self.fixedKnowledgePointConfigs[i].checkNumA);
  771. let numB = self.accMul(self.fixedKnowledgePointConfigs[i].scoreB,self.fixedKnowledgePointConfigs[i].checkNumB);
  772. let numC = self.accMul(self.fixedKnowledgePointConfigs[i].scoreC,self.fixedKnowledgePointConfigs[i].checkNumC);
  773. totalScore = self.accAdd(totalScore,numA)
  774. totalScore = self.accAdd(totalScore,numB)
  775. totalScore = self.accAdd(totalScore,numC)
  776. totalQuestionCount = self.accAdd(totalQuestionCount,self.fixedKnowledgePointConfigs[i].checkNumA)
  777. totalQuestionCount = self.accAdd(totalQuestionCount,self.fixedKnowledgePointConfigs[i].checkNumB)
  778. totalQuestionCount = self.accAdd(totalQuestionCount,self.fixedKnowledgePointConfigs[i].checkNumC)
  779. }
  780. }
  781. this.$set(this,'totalQuestionCount',totalQuestionCount);
  782. this.$set(this,'totalScore',totalScore);
  783. },
  784. //乘法
  785. accMul(arg1,arg2){
  786. var m=0,s1=arg1.toString(),s2=arg2.toString();
  787. try{m+=s1.split(".")[1].length}catch(e){}
  788. try{m+=s2.split(".")[1].length}catch(e){}
  789. return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)
  790. },
  791. //加法
  792. accAdd(arg1,arg2){
  793. var r1,r2,m;
  794. try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0}
  795. try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0}
  796. m=Math.pow(10,Math.max(r1,r2))
  797. return ((arg1*m+arg2*m)/m).toFixed(0);
  798. },
  799. }
  800. }
  801. </script>
  802. <style scoped lang="scss">
  803. .testPaperManagement-addPage {
  804. .content-box {
  805. flex: 1;
  806. overflow-y: auto;
  807. padding: 20px;
  808. }
  809. .add-form-box {
  810. .section-box {
  811. padding: 20px 0;
  812. }
  813. .section-divider {
  814. height: 1px;
  815. background: #e8e8e8;
  816. margin: 0 0 10px;
  817. }
  818. .full-width-item {
  819. ::v-deep .el-input {
  820. width: 100%;
  821. }
  822. ::v-deep .el-form-item__content {
  823. width: calc(100% - 100px);
  824. }
  825. }
  826. .flex-row-box {
  827. display: flex;
  828. flex-wrap: wrap;
  829. gap: 0 40px;
  830. }
  831. .input-unit-box {
  832. display: flex;
  833. align-items: center;
  834. .unit-text {
  835. margin-left: 8px;
  836. color: #333;
  837. white-space: nowrap;
  838. }
  839. }
  840. .share-tip {
  841. margin-left: 12px;
  842. color: #999;
  843. font-size: 13px;
  844. }
  845. /* 知识点区域 */
  846. .knowledge-form-item {
  847. ::v-deep .el-form-item__content {
  848. width: calc(100% - 100px);
  849. }
  850. }
  851. .stat-bar {
  852. background: #f5f5f5;
  853. padding: 10px 16px;
  854. margin-bottom: 10px;
  855. font-size: 14px;
  856. color: #333;
  857. b {
  858. color: #0183fa;
  859. }
  860. }
  861. .knowledge-tag-box {
  862. display: flex;
  863. flex-wrap: wrap;
  864. gap: 8px;
  865. align-items: center;
  866. padding: 10px;
  867. border: 1px solid #e8e8e8;
  868. border-radius: 4px;
  869. min-height: 48px;
  870. margin-bottom: 16px;
  871. }
  872. .knowledge-tag {
  873. display: inline-flex;
  874. align-items: center;
  875. background: #e6f4ff;
  876. border: 1px solid #91caff;
  877. color: #0183fa;
  878. border-radius: 4px;
  879. padding: 4px 10px;
  880. font-size: 13px;
  881. i {
  882. margin-left: 6px;
  883. cursor: pointer;
  884. color: #0183fa;
  885. font-size: 12px;
  886. &:hover {
  887. color: #e74c3c;
  888. }
  889. }
  890. }
  891. /* 知识点明细表格 */
  892. .knowledge-table {
  893. border: 1px solid #e8e8e8;
  894. border-radius: 4px;
  895. overflow: hidden;
  896. .table-header {
  897. display: flex;
  898. align-items: center;
  899. padding: 12px 16px;
  900. .col-name {
  901. flex: 3;
  902. }
  903. .col-count {
  904. flex: 2;
  905. text-align: center;
  906. }
  907. .col-score {
  908. flex: 2;
  909. text-align: center;
  910. }
  911. .col-fixed {
  912. flex: 3;
  913. display: flex;
  914. align-items: center;
  915. }
  916. .color-name {
  917. color: #0183fa;
  918. }
  919. }
  920. .table-row {
  921. flex: 1;
  922. display: flex;
  923. flex-direction: column;
  924. overflow: hidden;
  925. padding: 12px 0;
  926. .table-big-row {
  927. flex: 1;
  928. display: flex;
  929. align-items: center;
  930. overflow: hidden;
  931. /*padding: 0 16px 12px;*/
  932. /*border-bottom:1px solid #dedede;*/
  933. padding:0 16px;
  934. .col-name {
  935. flex: 3;
  936. }
  937. .col-count {
  938. flex: 2;
  939. text-align: center;
  940. }
  941. .col-score {
  942. flex: 2;
  943. text-align: center;
  944. }
  945. .col-fixed {
  946. flex: 3;
  947. display: flex;
  948. align-items: center;
  949. }
  950. .roller-span {
  951. color: #999;
  952. font-size: 24px;
  953. margin-right: 12px;
  954. cursor: pointer;
  955. display: inline-block;
  956. }
  957. .color-name {
  958. color: #0183fa;
  959. }
  960. .item-num-span {
  961. display: inline-block;
  962. width: 80px;
  963. line-height: 40px;
  964. border-radius: 4px;
  965. border: 1px solid #dedede;
  966. text-align: center;
  967. }
  968. .el-icon-circle-plus {
  969. font-size: 28px;
  970. color: #0183fa;
  971. cursor: pointer;
  972. margin-left: 28px;
  973. }
  974. .el-icon-rank {
  975. font-size: 28px;
  976. color: #999;
  977. cursor: pointer;
  978. margin-left: 28px;
  979. }
  980. }
  981. .table-min-row {
  982. flex: 1;
  983. display: flex;
  984. align-items: center;
  985. overflow: hidden;
  986. padding: 12px 16px 0;
  987. /*border-top:1px solid #dedede;*/
  988. .col-name {
  989. flex: 3;
  990. }
  991. .col-count {
  992. flex: 2;
  993. text-align: center;
  994. }
  995. .col-score {
  996. flex: 2;
  997. text-align: center;
  998. }
  999. .col-fixed {
  1000. flex: 3;
  1001. display: flex;
  1002. align-items: center;
  1003. }
  1004. .roller-span {
  1005. color: #333;
  1006. font-size: 16px;
  1007. cursor: pointer;
  1008. margin-left:200px;
  1009. }
  1010. .color-name {
  1011. color: #0183fa;
  1012. }
  1013. .item-num-span {
  1014. display: inline-block;
  1015. width: 80px;
  1016. line-height: 40px;
  1017. border-radius: 4px;
  1018. border: 1px solid #dedede;
  1019. text-align: center;
  1020. }
  1021. .el-icon-circle-plus {
  1022. font-size: 28px;
  1023. color: #0183fa;
  1024. cursor: pointer;
  1025. margin-left: 40px;
  1026. }
  1027. .el-icon-rank {
  1028. font-size: 28px;
  1029. color: #999;
  1030. cursor: pointer;
  1031. margin-left: 40px;
  1032. }
  1033. }
  1034. }
  1035. .table-header {
  1036. background: #fafafa;
  1037. font-weight: 600;
  1038. font-size: 14px;
  1039. color: #333;
  1040. border-bottom: 1px solid #e8e8e8;
  1041. }
  1042. .table-row {
  1043. border-bottom: 1px solid #f0f0f0;
  1044. &:last-child {
  1045. border-bottom: none;
  1046. }
  1047. }
  1048. .link-text {
  1049. color: #0183fa;
  1050. cursor: pointer;
  1051. }
  1052. .number-stepper {
  1053. display: inline-flex;
  1054. align-items: center;
  1055. border-radius: 4px;
  1056. overflow: hidden;
  1057. .stepper-btn {
  1058. width: 32px;
  1059. height: 36px;
  1060. background: #f5f7fa;
  1061. border: none;
  1062. cursor: pointer;
  1063. font-size: 16px;
  1064. color: #333;
  1065. &:hover {
  1066. background: #e6f0fe;
  1067. }
  1068. }
  1069. .stepper-val {
  1070. min-width: 80px;
  1071. text-align: center;
  1072. font-size: 14px;
  1073. padding: 0 4px;
  1074. }
  1075. }
  1076. }
  1077. }
  1078. .drag-handle {
  1079. cursor: move;
  1080. user-select: none; /* 防止拖拽时选中文字 */
  1081. }
  1082. .dragging {
  1083. opacity: 0.5;
  1084. background-color: #f5f7fa;
  1085. }
  1086. .drag-over {
  1087. /*border-top: 2px solid #409EFF !important; !* 放置位置的指示线 *!*/
  1088. }
  1089. }
  1090. </style>
  1091. <style lang="scss">
  1092. .testPaper-select-drawer{
  1093. .el-drawer__body{
  1094. padding:0;
  1095. }
  1096. .el-drawer__header{
  1097. margin:0;
  1098. }
  1099. }
  1100. </style>