BaseExerciseList.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. <template>
  2. <div class="components-el-container">
  3. <!-- 题目列表部分 -->
  4. <div v-if="exerciseList.length === 0" class="no-data-text">
  5. <img src="../../../assets/icon/no_data.svg" width="120" />
  6. <span style="margin-top:15px;color:#808080">暂无数据</span>
  7. </div>
  8. <div class="content-wrap" ref="mathJaxContainer" v-else>
  9. <Loading :top="200" v-show="dataLoading" type="1"></Loading>
  10. <div class="list-view" :key="typeIndex" v-for="(typeItem,typeIndex) in listData">
  11. <p v-show="viewModel === 'type' && typeItem.list.length" class="type-name">{{ numberConvertToUppercase(getLatestTypeIndex(typeItem.type) + 1) }}
  12. : {{ exersicesType[typeItem.type] }}
  13. ({{ typeItem.score || 0 }} 分) </p>
  14. <div v-for="(item,index) of typeItem.list" :key="index" :class='["exercise-item",isError(item.id) ? "exercise-item-error":""]'
  15. @mouseenter="exerciseMouseover($event)" @mouseleave="exerciseMouseleave($event)">
  16. <!-- 工具栏部分 -->
  17. <div class="item-tools-wrap">
  18. <!-- <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleSetScore(item,exerciseList.indexOf(item),typeItem.list,index)">
  19. <Icon type="ios-list-box-outline" />配分</div> -->
  20. <!-- <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleToolEdit(typeItem.list,item,index)">
  21. <Icon type="ios-brush-outline" />编辑</div> -->
  22. <div class="item-tools-t flex-row-center" v-show="isShowTools && noAnswerList.indexOf(item) > -1"
  23. @click.stop="onSetAnswer(item,exerciseList.indexOf(item),typeItem.list,index)">
  24. <Icon type="ios-brush-outline" />设置正确答案</div>
  25. <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleDelete(typeItem.list,item,index)">
  26. <Icon type="ios-archive-outline" />删除</div>
  27. <div class="item-tools-t flex-row-center" v-show="index != 0 && isShowTools" @click.stop="handleMoveUp(typeItem.list,index)">
  28. <Icon type="md-arrow-up" />上移</div>
  29. <div class="item-tools-t flex-row-center" v-show="index != (typeItem.list.length - 1) && isShowTools"
  30. @click.stop="handleMoveDown(typeItem.list,index)">
  31. <Icon type="md-arrow-down" />下移</div>
  32. </div>
  33. <div class="item-error-wrap" v-if="errorList.indexOf(item) > -1">
  34. <span v-if="isNoAnswer(item)">请为当前客观题设置正确答案!</span>
  35. <span v-else>试题题干或选项信息有误,请删除或者重新导入正确文档!</span>
  36. </div>
  37. <div @click="onQuestionToggle(exerciseList.indexOf(item),item.id,$event,typeItem.list)">
  38. <!-- 题干部分 -->
  39. <div class="item-question">
  40. <div v-if="viewModel === 'list'">
  41. <div class="item-question-order">{{ index + 1 }} : </div>
  42. <div class="item-question-text" v-html="item.question"></div>
  43. </div>
  44. <div v-else>
  45. <div class="item-question-order">{{ pageSize * (pageNum - 1) + index + 1 }} : </div>
  46. <div class="item-question-text" v-html="item.question" @click="onRichTextClick($event)"></div>
  47. </div>
  48. </div>
  49. <!-- 选项部分 -->
  50. <div v-for="(option,optionIndex) in item.option" :key="optionIndex" class="item-options">
  51. <div class="item-option-content">
  52. <div class="item-option-order">{{String.fromCharCode(64 + parseInt(optionIndex+1))}} :</div>
  53. <div class="item-option-text" v-html="option.value" @click="onRichTextClick($event)"></div>
  54. </div>
  55. </div>
  56. </div>
  57. <div class="item-btn-toggle" @click.stop v-show="isShowTools" >
  58. <template>
  59. <InputNumber :max="item.score + surPlusScore" :min="0" v-model="item.score" style="display: inline-block ;width: 60px;margin-right: 10px;height: 30px;"
  60. @click.stop></InputNumber>
  61. <span style="margin-right: 20px;">分</span>
  62. <!-- <span class="item-score" title="设置题目分数" @click.stop="onSetSingleItem(item,index)" v-else>{{ item.score }} 分</span> -->
  63. </template>
  64. <Icon :type="collapseList.indexOf(index) > -1 ? 'ios-arrow-dropup' : 'ios-arrow-dropdown'" />
  65. </div>
  66. <!-- 答案以及解析 -->
  67. <transition name="slide">
  68. <!-- <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area"> -->
  69. <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area">
  70. <div v-if="item.type !== 'compose'">
  71. <!-- 答案展示部分 -->
  72. <div class="item-explain" v-show="isShowAnswer">
  73. <span class="explain-title">【答ㅤ案】</span>
  74. <div class="item-explain-details" @click="onRichTextClick($event)">
  75. <!-- 问答题答案 -->
  76. <div v-if="item.type === 'subjective'">
  77. <span v-for="(answer,index) in item.answer" :key="index" v-html="item.answer.length ? answer : '未设置答案'"></span>
  78. </div>
  79. <!-- 填空题答案 -->
  80. <div v-else-if="item.type === 'complete'">
  81. <span :class="[ item.type === 'complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer"
  82. :key="index" v-html="answer"></span>
  83. </div>
  84. <!-- 其余题型答案 -->
  85. <div v-else>
  86. <span :class="[ item.type === 'complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer"
  87. :key="index">{{answer}}</span>
  88. </div>
  89. </div>
  90. </div>
  91. <!-- 解析部分 -->
  92. <div class="item-explain" v-show="isShowAnswer">
  93. <span class="explain-title">【解ㅤ析】</span>
  94. <div class="item-explain-details">
  95. <span v-html="item.explain || '暂无解析'" @click="onRichTextClick($event)"></span>
  96. </div>
  97. </div>
  98. <!-- 知识点部分 -->
  99. <div class="item-explain" v-show="isShowAnswer">
  100. <span class="explain-title">【知识点】</span>
  101. <div class="item-explain-details">
  102. <span v-if="! (_.compact(item.points).length)">暂未绑定知识点</span>
  103. <div v-else>
  104. <span v-for="(point,index) in item.points" :key="index" class="item-point-tag">
  105. {{ point }}
  106. </span>
  107. </div>
  108. </div>
  109. </div>
  110. </div>
  111. <!-- 如果是综合题 则加载子题渲染组件 -->
  112. <div v-else>
  113. <BaseChild :children="item.children"></BaseChild>
  114. </div>
  115. </div>
  116. </transition>
  117. </div>
  118. </div>
  119. </div>
  120. <!-- 单个试题配分弹框 -->
  121. <Modal v-model="answerModal" title="设置答案" @on-ok="onConfirmAnswer">
  122. <div v-if="curErrorItem.type === 'single'">
  123. <RadioGroup v-model="curErrorSingleAnswer" type="button">
  124. <Radio v-for="(option,index) in curErrorItem.option" :key="index" :label="option.code"></Radio>
  125. </RadioGroup>
  126. </div>
  127. <div v-else>
  128. <CheckboxGroup v-model="curErrorMulAnswer">
  129. <Checkbox v-for="(option,index) in curErrorItem.option" :key="index" :label="option.code" border></Checkbox>
  130. </CheckboxGroup>
  131. </div>
  132. </Modal>
  133. <!-- 题型配分弹窗 -->
  134. <Modal v-model="typeScoreModel" title="题型配分" footer-hide>
  135. <span class="type-score-item">试卷总分 : {{ paperInfo.score }}</span>
  136. <span class="type-score-item">已分配总分 : {{ getTotalScore(groupTypeList) }}</span>
  137. <div v-for="(modalItem,modalIndex) in groupTypeList" :key="modalIndex" class="type-score-item">
  138. <div v-if="modalItem.list.length">
  139. <span>{{ exersicesType[modalItem.type] }} </span>
  140. <span>共 {{ modalItem.list.length }} 道题,总分配:</span>
  141. <InputNumber :max="paperInfo.score" :min="0" :step="scoreStep" v-model="modalItem.score"></InputNumber>
  142. <span> 分</span>
  143. <div v-show="modalItem.type === 'multiple'" style="margin-top: 10px;">
  144. <div class="rule-item">
  145. <RadioGroup v-model="multipleRule" vertical>
  146. <Radio :label="1">
  147. <Icon type="social-android"></Icon>
  148. <span>默认全对得满分</span>
  149. </Radio>
  150. <Radio :label="2">
  151. <Icon type="social-windows"></Icon>
  152. <span>少选得一半分数,错选或者不选不得分</span>
  153. </Radio>
  154. <Radio :label="3">
  155. <Icon type="social-windows"></Icon>
  156. <span>少选按个数得分,错选或者不选不得分</span>
  157. </Radio>
  158. <Radio :label="4">
  159. <Icon type="social-windows"></Icon>
  160. <span>依照(选项数 - 2 * 错选数)/ 选项数,负分则不得分</span>
  161. </Radio>
  162. </RadioGroup>
  163. </div>
  164. </div>
  165. </div>
  166. </div>
  167. <Button class="type-score-btn" @click="onConfirmTypeScore" type="info" :disabled="getTotalScore(groupTypeList) > paperInfo.score">确认</Button>
  168. <p style="color:red;text-align:center;font-weight:bold;margin-top:10px" v-show="getTotalScore(groupTypeList) > paperInfo.score">配分已超试卷总分,请重新分配!</p>
  169. </Modal>
  170. <!-- 编辑试题弹窗 -->
  171. <!-- <Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide title="编辑习题">
  172. <BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="paperEdit" ref="paperEdit"></BaseEditExercise>
  173. <div slot="footer">
  174. <Button type="success">确认</Button>
  175. </div>
  176. </Modal> -->
  177. <!-- 音频播放弹窗 -->
  178. <Modal v-model="playAudioModal" width="400" footer-hide @on-visible-change="onAudioModalChange">
  179. <div class="modal-header" slot="header">音频播放</div>
  180. <BaseAudioPlayer :audioSrc="curAudioSrc" :audioName="curAudioName" ref="audioPlayer"></BaseAudioPlayer>
  181. </Modal>
  182. <!-- 视频播放弹窗 -->
  183. <Modal v-model="playVideoModal" width="400" footer-hide @on-visible-change="onVideoModalChange">
  184. <div class="modal-header" slot="header">视频播放</div>
  185. <BaseVideoPlayer :videoSrc="curVideoSrc" :audioName="curVideoName" ref="videoPlayer"></BaseVideoPlayer>
  186. </Modal>
  187. </div>
  188. </template>
  189. <script>
  190. export default {
  191. props: {
  192. paper: {
  193. type: Object,
  194. default: null
  195. },
  196. isShowTools: {
  197. type: Boolean,
  198. default: false
  199. }
  200. },
  201. data() {
  202. return {
  203. schoolCode: '',
  204. dataLoading: false,
  205. curEditItemId: null,
  206. exerciseList: [],
  207. schoolInfo: {},
  208. isShowUploadList: false,
  209. multipleRule: 1,
  210. typeScoreModel: false,
  211. setPaperInfoModal: false,
  212. editExerciseModal: false,
  213. exersicesType: {
  214. single: '单选题',
  215. multiple: '多选题',
  216. judge: '判断题',
  217. complete: '填空题',
  218. subjective: '问答题',
  219. compose: '综合题'
  220. },
  221. exersicesDiff: ['容易', '较易', '一般', '较难', '困难'],
  222. diffColors: ['#32CF74', '#E8BE15', '#F19300', '#EB5E00', '#D30000'],
  223. totalNum: 0,
  224. isShowAnswer: true,
  225. importLoading: false,
  226. pageSize: 99,
  227. pageNum: 1,
  228. currentPage: 1,
  229. collapseList: [],
  230. periodList: [],
  231. gradeList: [],
  232. subjectList: [],
  233. basketList: [],
  234. originData: [],
  235. groupList: [],
  236. groupTypeList: [],
  237. orderList: [],
  238. typeList: ['single', 'multiple', 'judge', 'complete', 'subjective', 'compose'],
  239. viewModel: 'type',
  240. filterParams: {},
  241. surPlusScore: 0,
  242. curItemScore: 0,
  243. curIndex: 0,
  244. lastScore: 0,
  245. scoreStep: 0.5,
  246. curTypeItems: [],
  247. scoreModal: false,
  248. currentExercise: {},
  249. allPointList: [],
  250. playAudioModal: false,
  251. playVideoModal: false,
  252. curAudioSrc: "",
  253. curAudioName: "",
  254. curVideoSrc: "",
  255. curVideoName: "",
  256. modifyItems: [],
  257. errorList: [],
  258. paperInfo: {
  259. score: 0
  260. },
  261. curErrorItem: {
  262. type:''
  263. },
  264. curErrorSingleAnswer: 'A',
  265. curErrorMulAnswer: [],
  266. noAnswerList:[],
  267. answerModal: false
  268. }
  269. },
  270. created() {
  271. //console.log(this.exerciseList)
  272. },
  273. methods: {
  274. /* 音频弹窗切换事件 */
  275. onAudioModalChange(val) {
  276. if (!val) {
  277. this.$refs.audioPlayer.onCloseAudio()
  278. }
  279. },
  280. /* 视频弹窗切换事件 */
  281. onVideoModalChange(val) {
  282. if (!val) {
  283. this.$refs.videoPlayer.onCloseAudio()
  284. }
  285. },
  286. /* 音频点击播放事件 */
  287. onRichTextClick(e) {
  288. if (e.srcElement.classList[0] === 'richText-audio') {
  289. this.playAudioModal = true
  290. this.curAudioSrc = e.srcElement.dataset.url
  291. this.curAudioName = e.srcElement.dataset.name
  292. } else if (e.srcElement.classList[0] === 'richText-video') {
  293. this.playVideoModal = true
  294. this.curVideoSrc = e.srcElement.dataset.url
  295. this.curVideoName = e.srcElement.dataset.name
  296. }
  297. },
  298. /**
  299. * 题干展开与收缩
  300. * @param index
  301. * @param id
  302. */
  303. onQuestionToggle(index, id, e) {
  304. e.stopPropagation()
  305. this.curEditItemId = null
  306. let listIndex = this.collapseList.indexOf(index)
  307. if (listIndex > -1) {
  308. this.collapseList.splice(listIndex, 1)
  309. } else {
  310. /** 如果是首次展开 则需要获取详细数据 否则直接展开 */
  311. if (!this.exerciseList[index].answer.length) {
  312. this.collapseList.push(index)
  313. this.pageScrollTo(e.target.offsetTop + 420) // 490就是 content-wrap 距离顶部高度
  314. } else {
  315. this.collapseList.push(index)
  316. this.pageScrollTo(e.target.offsetTop + 420) // 490就是 content-wrap 距离顶部高度
  317. }
  318. }
  319. this.$emit('toggleChange', this.collapseList)
  320. },
  321. /**
  322. * 数字与中文转换
  323. * @param num
  324. */
  325. numberConvertToUppercase(num) {
  326. num = Number(num)
  327. var upperCaseNumber = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '百', '千', '万', '亿']
  328. var length = String(num).length
  329. if (length === 1) {
  330. return upperCaseNumber[num]
  331. } else if (length === 2) {
  332. if (num === 10) {
  333. return upperCaseNumber[num]
  334. } else if (num > 10 && num < 20) {
  335. return '十' + upperCaseNumber[String(num).charAt(1)]
  336. } else {
  337. return upperCaseNumber[String(num).charAt(0)] + '十' + upperCaseNumber[String(num).charAt(1)].replace('零', '')
  338. }
  339. }
  340. },
  341. /**
  342. * 页面滚动事件
  343. * @param scrollDistance 页面滚动距离
  344. */
  345. pageScrollTo(scrollDistance) {
  346. //let parentVm = this.$parent.$parent.$parent.$parent
  347. //parentVm.$refs['evScroll'].scrollTo(
  348. // {
  349. // y: scrollDistance
  350. // },
  351. // 500, 'easeInQuad'
  352. //)
  353. },
  354. // 题目鼠标滑过事件
  355. exerciseMouseover(e) {
  356. e.preventDefault()
  357. e.target.children[0].style.display = 'block'
  358. },
  359. // 题目鼠标移出事件
  360. exerciseMouseleave(e) {
  361. e.preventDefault()
  362. e.target.children[0].style.display = 'none'
  363. },
  364. // 子题顺序操作
  365. moveItems(arr, index1, index2) {
  366. arr[index1] = arr.splice(index2, 1, arr[index1])[0]
  367. return arr
  368. },
  369. // 子题上移操作
  370. handleMoveUp(arr, index) {
  371. this.moveItems(arr, index, index - 1)
  372. },
  373. // 子题下移操作
  374. handleMoveDown(arr, index) {
  375. this.moveItems(arr, index, index + 1)
  376. },
  377. /**
  378. * 删除试卷中的单个试题
  379. * @param index
  380. */
  381. handleDelete(arr, item, index) {
  382. this.$Modal.confirm({
  383. title: '提示',
  384. content: '<p>确认删除该试题吗?</p>',
  385. okText: '确认',
  386. cancelText: '取消',
  387. onOk: () => {
  388. this.$Message.success('删除成功')
  389. this.surPlusScore += item.score
  390. this.$emit('scoreUpdate', this.surPlusScore)
  391. arr.splice(index, 1)
  392. this.exerciseList.splice(this.exerciseList.indexOf(item), 1)
  393. this.$emit('dataUpdate', this.exerciseList)
  394. this.$EventBus.$emit('onPaperItemChange', this.exerciseList)
  395. if(this.errorList.indexOf(item) > -1){
  396. this.errorList.splice(this.errorList.indexOf(item),1)
  397. }
  398. }
  399. })
  400. },
  401. /**
  402. * 编辑单个试题
  403. * @param item
  404. */
  405. handleToolEdit(arr, item, index) {
  406. console.log('试卷当前编辑的题目')
  407. console.log(item)
  408. this.currentExerciseIndex = this.exerciseList.indexOf(item) // 清单列表下的index
  409. this.curTypeItems = arr
  410. this.currentExercise = item
  411. this.editExerciseModal = true
  412. },
  413. /**
  414. * 给单个试题配分
  415. * @param item
  416. * @param index
  417. */
  418. onSetAnswer(item, index, arr, groupIndex) {
  419. this.currentExerciseIndex = index
  420. this.curIndex = groupIndex
  421. this.curTypeItems = arr
  422. this.curErrorItem = item
  423. this.answerModal = true
  424. },
  425. onConfirmAnswer() {
  426. if(this.curErrorItem.type === 'single'){
  427. this.curErrorItem.answer = [this.curErrorSingleAnswer]
  428. let errorIndex = this.errorList.map(i => i.id).indexOf(this.curErrorItem.id)
  429. if(errorIndex > -1){
  430. this.errorList.splice(errorIndex,1)
  431. }
  432. }else{
  433. if(this.curErrorMulAnswer.length){
  434. this.curErrorItem.answer = this.curErrorMulAnswer
  435. let errorIndex = this.errorList.map(i => i.id).indexOf(this.curErrorItem.id)
  436. if(errorIndex > -1){
  437. this.errorList.splice(errorIndex,1)
  438. }
  439. }else{
  440. this.curErrorItem.answer = []
  441. this.errorList.push(this.curErrorItem)
  442. }
  443. }
  444. },
  445. /**
  446. * 给单个试题配分
  447. * @param item
  448. * @param index
  449. */
  450. handleSetScore(item, index, arr, groupIndex) {
  451. this.currentExerciseIndex = index
  452. this.curIndex = groupIndex
  453. this.curTypeItems = arr
  454. this.curItemScore = item.score
  455. this.lastScore = item.score
  456. this.scoreModal = true
  457. },
  458. /**
  459. * 配分变化时的剩余分数处理
  460. * @param val
  461. */
  462. onScoreChange(val) {
  463. this.surPlusScore = this.surPlusScore + this.lastScore - val
  464. this.lastScore = val
  465. this.$emit('scoreUpdate', this.surPlusScore)
  466. },
  467. /** 确认单个试题配分 */
  468. onConfirmScore() {
  469. this.$set(this.exerciseList[this.currentExerciseIndex], 'score', this.curItemScore);
  470. this.$set(this.curTypeItems[this.curIndex], 'score', this.curItemScore);
  471. },
  472. /** 按照题型配分 */
  473. onConfirmTypeScore() {
  474. /** 重新计算剩余分配分数 */
  475. this.surPlusScore = this.paperInfo.score - this.groupTypeList.reduce((p, e) => parseInt(p) + parseInt(e.score), 0)
  476. if (this.surPlusScore < 0) {
  477. this.$Message.warning("当前配分超过试卷总分,请重新分配!")
  478. } else {
  479. /* 按照题型配分后平均分配给每个子题 */
  480. this.groupTypeList.forEach(item => {
  481. item.list.forEach((exercise, exerciseIndex) => {
  482. // 先找到原始列表里面的当前题目
  483. let listItem = this.exerciseList.filter(item => item.id === exercise.id)[0]
  484. // 先判断是否总分除以题目数量能否除尽
  485. let remainder = item.score % item.list.length
  486. // 如果可以整除 则直接计算
  487. if (remainder === 0) {
  488. exercise.score = item.score / item.list.length
  489. } else {
  490. // 如果不能整除 则前面所有取整 最后一题加上余数 即可完成配分
  491. let integerScore = parseInt(item.score / item.list.length)
  492. let lastItem = exerciseIndex === item.list.length - 1
  493. exercise.score = lastItem ? integerScore + remainder : integerScore
  494. }
  495. listItem.score = exercise.score
  496. })
  497. })
  498. /** 回到题型视图 */
  499. this.groupList = this.groupTypeList
  500. this.$parent.viewModel = 'type'
  501. this.$parent.paperInfo.multipleRule = this.multipleRule
  502. this.typeScoreModel = false
  503. this.$emit('scoreUpdate', this.surPlusScore)
  504. }
  505. },
  506. /** 编辑成功 */
  507. onEditSuccess(item) {
  508. if (item.id) {
  509. this.$refs.paperEdit.isLoading = false
  510. this.editExerciseModal = false
  511. this.$Message.success("修改成功!")
  512. this.exerciseList.splice(this.currentExerciseIndex, 1, item)
  513. this.curTypeItems.splice(this.curIndex, 1, item)
  514. this.$emit('dataUpdate', this.exerciseList)
  515. let existIndex = this.modifyItems.map(i => i.id).indexOf(item.id)
  516. if (existIndex < 0) {
  517. this.modifyItems.push(item)
  518. } else {
  519. this.modifyItems[existIndex] = item
  520. }
  521. } else {
  522. this.editExerciseModal = false
  523. this.exerciseList.splice(this.currentExerciseIndex, 1, item)
  524. this.curTypeItems.splice(this.curIndex, 1, item)
  525. let listIndex = this.collapseList.indexOf(this.currentExerciseIndex);
  526. if (listIndex > -1) {
  527. this.collapseList.splice(listIndex, 1)
  528. }
  529. this.$Message.success("修改成功!")
  530. }
  531. },
  532. /**
  533. * 全部展开与全部折叠
  534. * @param isAllOpen
  535. */
  536. onHandleToggle(isAllOpen) {
  537. if (isAllOpen) {
  538. this.collapseList = []
  539. } else {
  540. this.collapseList = [...this.exerciseList.keys()]
  541. }
  542. },
  543. /** 打开题型配分 */
  544. onSetScoreByType() {
  545. this.typeScoreModel = true;
  546. },
  547. /**
  548. * 获取题目列表总分
  549. * @param arr
  550. */
  551. getTotalScore(arr) {
  552. arr.forEach(i => {
  553. if (i.list.length === 0) {
  554. i.score = 0
  555. }
  556. })
  557. return arr.reduce((p, e) => p + parseInt(e.score), 0)
  558. },
  559. /**
  560. * 拿到当前页面所有知识点集合
  561. * @param arr
  562. */
  563. getPointIds(arr) {
  564. let ids = []
  565. arr.forEach(i => {
  566. ids = ids.concat(i.points)
  567. })
  568. return this._.compact([...new Set(ids)])
  569. },
  570. getLatestTypeIndex(type) {
  571. let arr = []
  572. this.groupList.forEach(i => {
  573. if (i.list.length) {
  574. arr.push(i.type)
  575. }
  576. })
  577. return arr.indexOf(type)
  578. }
  579. },
  580. mounted() {
  581. this.$EventBus.$off('importFinish')
  582. this.$EventBus.$on('importFinish', list => {
  583. this.errorList = list
  584. this.noAnswerList = list.filter(i => i.answer.length === 0)
  585. console.log(list)
  586. console.log(this.noAnswerList)
  587. })
  588. },
  589. computed: {
  590. listData() {
  591. return this.viewModel === 'type' ? this.groupList : this.orderList
  592. },
  593. isError() {
  594. return id => {
  595. return this.errorList.length && this.errorList.map(i => i.id).indexOf(id) > -1
  596. }
  597. },
  598. isNoAnswer() {
  599. return item => {
  600. return (item.question.replace(/\s*/g, "")) && item.option.length && !item.answer.length
  601. }
  602. }
  603. },
  604. watch: {
  605. paper: {
  606. handler(newPaper) {
  607. if (newPaper) {
  608. console.log('获取的题目信息')
  609. console.log(newPaper)
  610. let that = this
  611. this.groupList = []
  612. this.exerciseList = []
  613. this.orderList = []
  614. this.paperInfo = newPaper
  615. if (newPaper.item.length) {
  616. newPaper.item.forEach(i => {
  617. if (!i.score) i.score = 0
  618. })
  619. this.orderList.push({
  620. list: newPaper.item
  621. })
  622. /* 处理试卷内题目按照题型排序 */
  623. this.typeList.forEach(item => {
  624. this._.mapKeys(this._.groupBy(newPaper.item, 'type'), function(value, key) {
  625. if (key === item) { /* 按照题型排序,并且计算每种题型的总分 */
  626. that.groupList.push({
  627. type: key,
  628. list: value,
  629. score: value.reduce((p, e) => parseInt(p) + parseInt(e.score), 0)
  630. })
  631. that.exerciseList = that.exerciseList.concat(value)
  632. }
  633. })
  634. });
  635. }
  636. this.originData = this.exerciseList
  637. this.groupTypeList = this.groupList
  638. this.totalNum = newPaper.item.length
  639. this.surPlusScore = newPaper.score - newPaper.item.reduce((p, e) => parseInt(p) + parseInt(e.score), 0);
  640. this.$emit('scoreUpdate', this.surPlusScore)
  641. this.pageScrollTo(0)
  642. this.$nextTick(() => {
  643. window.MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.$refs.mathJaxContainer]);
  644. })
  645. }
  646. },
  647. deep: true
  648. },
  649. viewModel: {
  650. handler(newValue) {
  651. if (newValue) {
  652. if (newValue === 'list') {
  653. this.groupList = [{
  654. type: 'all',
  655. score: this.exerciseList.reduce((p, e) => p + e.score, 0),
  656. list: this.exerciseList
  657. }]
  658. } else {
  659. this.groupList = this.groupTypeList
  660. }
  661. }
  662. },
  663. immediate: true
  664. }
  665. }
  666. }
  667. </script>
  668. <style scoped>
  669. @import "../index/PickExercise.css";
  670. </style>
  671. <style scoped>
  672. .components-el-container .ivu-page {
  673. display: flex;
  674. flex-direction: row;
  675. justify-content: center;
  676. margin: 20px 0;
  677. }
  678. .components-el-container .exercise-item-error {
  679. background-color: rgb(255 152 152 / 60%);
  680. }
  681. .components-el-container .exercise-item-error:hover {
  682. border: 2px solid red !important;
  683. }
  684. .components-el-container .type-name {
  685. font-size: 18px;
  686. font-weight: bold;
  687. margin-top: 20px;
  688. }
  689. .components-el-container /deep/ .ivu-input-number-handler-down-inner,
  690. .components-el-container /deep/ .ivu-input-number-handler-up-inner {
  691. right: 8px;
  692. font-size: 16px;
  693. }
  694. .components-el-container .exercise-item:hover {
  695. border: 2px solid #01b4ef;
  696. }
  697. .components-el-container .exercise-item {
  698. position: relative;
  699. margin-top: 30px;
  700. font-size: 14px;
  701. }
  702. .components-el-container .exercise-item p {
  703. width: 92%;
  704. }
  705. .components-el-container .exercise-item .item-error-wrap {
  706. position: absolute;
  707. right: -2px;
  708. bottom: 0;
  709. width: auto;
  710. height: 30px;
  711. line-height: 30px;
  712. padding: 0 10px;
  713. margin: 0;
  714. background: #ff5656;
  715. color: #fff
  716. }
  717. .components-el-container .exercise-item .item-tools-wrap {
  718. position: absolute;
  719. right: -2px;
  720. top: -32px;
  721. width: auto;
  722. height: 30px;
  723. margin: 0;
  724. padding: 0;
  725. background: #01b4ef;
  726. display: none;
  727. }
  728. .components-el-container .exercise-item .item-tools-t {
  729. height: 100%;
  730. padding: 0 15px;
  731. float: left;
  732. color: white;
  733. cursor: pointer;
  734. }
  735. .components-el-container .exercise-item .item-tools-t:hover {
  736. background: #4a5ae6;
  737. }
  738. .components-el-container .exercise-item .item-score {
  739. margin-right: 10px;
  740. color: #fff;
  741. background: #01b4ef;
  742. padding: 4px 10px;
  743. border-radius: 5px;
  744. }
  745. .components-el-container .exercise-item .item-explain-details {
  746. /* width: 90%; */
  747. }
  748. .components-el-container .exercise-item .item-tools-t .ivu-icon {
  749. color: white;
  750. font-size: 14px;
  751. margin: 0 3px;
  752. }
  753. .type-score-btn {
  754. width: 80%;
  755. margin-left: 10%;
  756. height: 40px;
  757. line-height: 40px;
  758. }
  759. .type-score-item {
  760. margin: 20px 10px;
  761. }
  762. /*横向垂直水平居中*/
  763. .flex-row-center {
  764. display: flex;
  765. flex-direction: row;
  766. justify-content: center;
  767. align-items: center;
  768. }
  769. .no-data-text {
  770. width: 100%;
  771. padding: 30px;
  772. background: #fff;
  773. display: flex;
  774. flex-direction: column;
  775. justify-content: center;
  776. align-items: center;
  777. margin-top: 10px;
  778. font-size: 16px;
  779. }
  780. </style>
  781. <style>
  782. .edit-exercise-modal .ivu-modal-body {
  783. max-height: 700px;
  784. overflow: auto;
  785. padding: 0;
  786. }
  787. </style>