Browse Source

question - sub question render

hehaitao 1 year ago
parent
commit
8ffe636a9b

+ 31 - 0
src/assets/styles/common.scss

@@ -594,6 +594,34 @@
   flex: 5;
 }
 
+.gap5 {
+  gap: 5px;
+}
+.gap-x-5 {
+  row-gap: 5px;
+}
+.gap-y-5 {
+  column-gap: 5px;
+}
+.gap10 {
+  gap: 10px;
+}
+.gap-x-10 {
+  row-gap: 10px;
+}
+.gap-y-10 {
+  column-gap: 10px;
+}
+.gap20 {
+  gap: 20px;
+}
+.gap-x-20 {
+  row-gap: 20px;
+}
+.gap-y-20 {
+  column-gap: 20px;
+}
+
 .pd10 {
   padding: 10px;
 }
@@ -638,6 +666,9 @@
 .bd-f2 {
   border: 1px solid #f2f2f2;
 }
+.bd-ddd {
+  border: 1px solid #dddddd;
+}
 
 .bg-border {
   background-color: #EEEEEE;

+ 34 - 0
src/components/MxCollectButton/MxCollectButton.vue

@@ -0,0 +1,34 @@
+<template>
+  <div class="fx-row ai-cen gap5 f14 pointer el-button el-button--text" @click="handleClick">
+    <svg-icon :icon-class="value?'star-fill-16':'star-16'" :class="value?'f-primary':'f-666'"/>
+    <span class="f-666">{{ value ? '取消收藏' : '点击收藏' }}</span>
+  </div>
+</template>
+
+<script>
+import {createInjectDefaultClosure, nonsensicalFn} from "@/utils";
+
+export default {
+  name: "MxCollectButton",
+  inject: {
+    collectFn: createInjectDefaultClosure(nonsensicalFn),
+    collectCancelFn: createInjectDefaultClosure(nonsensicalFn)
+  },
+  props: {
+    value: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    handleClick() {
+      if (!this.value) this.collectFn()
+      else this.collectCancelFn()
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 119 - 0
src/components/MxQuestionReadonly/MxQuestionReadonly.vue

@@ -0,0 +1,119 @@
+<template>
+  <div class="fx-column bd-ddd pd20" :id="`question_`+question.id">
+    <mx-question-content :options="standardOptions"/>
+    <el-divider/>
+    <div class="fx-row fx-bet-cen">
+      <div class="fx-row ai-cen gap20 f12 f-999">
+        <span>ID: {{ question.id }}</span>
+        <span>题型: {{ question.qtpye }}</span>
+        <span>难度: {{ diffText }}</span>
+      </div>
+      <div class="fx-row ai-cen gap20">
+        <mx-collect-button :value="question.collect" />
+        <el-button type="text" @click="$emit('correct')">
+          <i class="el-icon-info f-red"></i>
+          <span class="f-666">试题纠错</span>
+        </el-button>
+        <el-button type="text" @click="toggleParse">
+          <i class="el-icon-search f-warning"></i>
+          <span class="f-666">查看详情</span>
+        </el-button>
+      </div>
+    </div>
+    <div :id="parseEleId" v-if="showParse" class="mt20">
+      <div v-html="'【解答】'+question.answer2" class="mb20"></div>
+      <div v-html="'【解析】'+question.parse"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+import MxQuestionContent from "@/components/MxPaper/mx-question-content.vue";
+import MxCollectButton from "@/components/MxCollectButton/MxCollectButton.vue";
+import {queCancelCollect, queCollect} from "@/api/webApi/webQue";
+
+// 收拢旧的question模型
+export default {
+  name: "MxQuestionReadonly",
+  components: {MxCollectButton, MxQuestionContent},
+  props: {
+    seq: {
+      type: String | Number,
+      default: ''
+    },
+    question: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      showParse: false
+    }
+  },
+  computed: {
+    standardOptions() {
+      return {
+        question: this.toStandardQuestion(this.question, this.seq),
+        allowAnswer: false,
+        allowScore: false,
+        examineeType: '',
+        paperOptions: {}
+      }
+    },
+    diffText() {
+      if (this.question.diff > 3) return '难'
+      if (this.question.diff < 3) return '易'
+      return '中'
+    },
+    parseEleId() {
+      return `question_parse_${this.question.id}`
+    }
+  },
+  provide() {
+    return {
+      collectFn: this.questionCollect,
+      collectCancelFn: this.questionCollectCancel
+    }
+  },
+  methods: {
+    toStandardQuestion(q, seq) {
+      const options = []
+      const prefix = 'option'
+      Object.keys(q).forEach(prop => {
+        if (prop.startsWith(prefix) && prop.length > prefix.length) {
+          const val = q[prop]
+          if (val) options.push(val)
+        }
+      })
+      return {
+        ...q,
+        seq: q.seq || seq,
+        questionId: q.id,
+        options,
+        subQuestions: q.subquestions?.length ? q.subquestions.map((item, i) => this.toStandardQuestion(item, `(${i + 1})`)) : undefined
+      }
+    },
+    questionCollect() {
+      queCollect({questionId: this.question.id}).then((res) => {
+        this.question.collect = !this.question.collect
+        this.msgSuccess('收藏成功')
+      })
+    },
+    questionCollectCancel() {
+      queCancelCollect({questionId: this.question.id}).then((res) => {
+        this.question.collect = !this.question.collect
+        this.msgSuccess('取消收藏成功')
+      })
+    },
+    toggleParse() {
+      this.showParse = !this.showParse
+      if (this.showParse) this.$nextTick(_ => this.mxGlobal.MathQueue(this.parseEleId))
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 12 - 0
src/utils/index.js

@@ -473,3 +473,15 @@ export function formatDuration(totalSeconds, short = true) {
     return month + '月' + day + '天' + hours + '小时' + minutes + '分' + seconds + '秒'
   }
 }
+
+export function createInjectDefaultClosure(defVal) {
+  return {
+    default: function () {
+      return () => defVal
+    }
+  }
+}
+
+export function nonsensicalFn() {
+
+}

+ 26 - 261
src/views/questioncenter/PaperPreview.vue

@@ -1,73 +1,17 @@
 <template>
-  <div class="preview_container">
-    <el-card>
-      <div class="paper_header">
-        <div class="paper_title">{{ paperName }}</div>
-        <div class="operation">
-          <div class="shoucan">
-            <div v-if="collectFlag" class="fx-row ai-center" @click="cancelCollect()">
-              <img src="@/assets/images/icon_shoucang_s.png" alt=""/>
-              <span class="sc_text">已收藏</span>
-            </div>
-            <div v-else @click="Collect()" class="fx-row ai-center">
-              <img src="@/assets/images/icon_shoucang_n.png" alt=""/>
-              <span class="sc_text">收藏</span>
-            </div>
-            <span class="btn" @click="paperDownLoad(paperId)">下载试卷</span>
-          </div>
+  <div class="app-container">
+    <el-card shadow="never">
+      <!-- 标题 -->
+      <div class="fx-row fx-bet-cen gap20 mb20">
+        <div class="f18 bold f-333">{{ paperName }}</div>
+        <div class="fx-row fx-bet-cen gap20">
+          <mx-collect-button :value="collectFlag" />
+          <el-button type="primary" @click="paperDownLoad(paperId)">下载试卷</el-button>
         </div>
       </div>
       <!-- 题目 -->
-      <div class="paper_questions" id="question_list">
-        <div class="que_item" v-for="(item, index) in opticList" :key="item.id">
-          <div class="que_content" style="display: flex">
-            <div>{{ index + 1 }}.</div>
-            <span v-html="item.title"></span>
-          </div>
-          <div class="option-list">
-            <span v-html="'A&nbsp'+item.optionA" v-if="item.optionA != ''"></span>
-            <span v-html="'B&nbsp'+item.optionB" v-if="item.optionB != ''"></span>
-            <span v-html="'C&nbsp'+item.optionC" v-if="item.optionC != ''"></span>
-            <span v-html="'D&nbsp'+item.optionD" v-if="item.optionD != ''"></span>
-          </div>
-          <div class="subQueBox" v-if="item.issub == 1 && item.subquestions != null">
-            <div class="subQueItem" v-for="(item,index) in item.subquestions" :key="index" v-html="item.title">
-
-            </div>
-          </div>
-          <div class="que_footer">
-            <div class="spans">
-              <span class="id">ID: {{ item.id }}</span>
-              <span>题型: {{ item.qtpye }}</span>
-              <span>难度: 一般</span>
-            </div>
-            <div class="operation">
-              <div class="shoucan">
-                <div v-show="item.collect" @click="toCancelCollectQue(item)" style="display: flex;align-items: center;">
-                  <img src="@/assets/images/icon_shoucang_s.png" alt=""/>
-                  <span>已收藏</span>
-                </div>
-                <div v-show="!item.collect" @click="toCollectQue(item)" style="display: flex;align-items: center;">
-                  <img src="@/assets/images/icon_shoucang_n.png" alt=""/>
-                  <span>收藏</span>
-                </div>
-              </div>
-              <div class="jiucuo mr20" @click="$refs.correct.open(item.id)">
-                <img src="@/assets/images/icon_jiucuo.png" alt=""/>
-                <span>纠错</span>
-              </div>
-              <div class="detail" @click="handleParseVisible(item)">
-                <img src="@/assets/images/icon_chakan.png" alt=""/>
-                <span>查看详情>></span>
-              </div>
-            </div>
-          </div>
-          <!-- NOTE: 改v-if否则全部公式渲染太费时间 -->
-          <div :id="`question_parse_${item.id}`" v-if="item.updateTime" class="info">
-            <div v-html="'【解答】'+item.answer2" class="mb20"></div>
-            <div v-html="'【解析】'+item.parse"></div>
-          </div>
-        </div>
+      <div class="fx-column gap10" id="question_list">
+        <mx-question-readonly v-for="(q,i) in opticList" :seq="i+1" :question="q" @correct="$refs.correct.open(q.id)"/>
       </div>
     </el-card>
     <correct-question ref="correct"></correct-question>
@@ -83,11 +27,13 @@ import {
   queCancelCollect,
   queCollect
 } from '@/api/webApi/webQue.js'
-import { mapGetters } from 'vuex'
+import {mapGetters} from 'vuex'
 import correctQuestion from '../../components/MxPaper/plus/correct-question-dialog.vue'
+import MxCollectButton from "@/components/MxCollectButton/MxCollectButton.vue";
+import MxQuestionReadonly from "@/components/MxQuestionReadonly/MxQuestionReadonly.vue";
 
 export default {
-  components: { correctQuestion },
+  components: {MxQuestionReadonly, MxCollectButton, correctQuestion},
   data() {
     return {
       collectFlag: false,
@@ -99,49 +45,35 @@ export default {
   computed: {
     ...mapGetters(['period'])
   },
+  provide() {
+    return {
+      collectFn: this.paperCollect,
+      collectCancelFn: this.paperCollectCancel
+    }
+  },
   created() {
     this.paperId = this.$route.query.paperId
     this.paperName = this.$route.query.paperName
-    isCollected({ paperId: this.paperId }).then((res) => {
-      console.log(res)
+    isCollected({paperId: this.paperId}).then((res) => {
       this.collectFlag = res.data
     })
     this.getOptics()
   },
   methods: {
     getOptics() {
-      preview({ paperId: this.paperId }).then((res) => {
+      preview({paperId: this.paperId}).then((res) => {
         this.opticList = res.data
         setTimeout(_ => this.mxGlobal.MathQueue('question_list'))
       })
     },
-    async handleParseVisible(item) {
-      item.updateTime = !item.updateTime
-      const parseEl = `question_parse_${item.id}`
-      this.$nextTick(_ => this.mxGlobal.MathQueue(parseEl))
-    },
-    toCollectQue(item) {
-      queCollect({ questionId: item.id }).then((res) => {
-        item.collect = !item.collect
-        this.msgSuccess('收藏成功')
-        console.log(res)
-      })
-    },
-    toCancelCollectQue(item) {
-      queCancelCollect({ questionId: item.id }).then((res) => {
-        item.collect = !item.collect
-        this.msgSuccess('取消收藏成功')
-        console.log(res)
-      })
-    },
-    cancelCollect() {
-      papersCancelCollect({ paperId: this.paperId }).then((res) => {
+    paperCollectCancel() {
+      papersCancelCollect({paperId: this.paperId}).then((res) => {
         this.collectFlag = false
         this.msgSuccess('取消收藏成功')
       })
     },
-    Collect() {
-      papersCollect({ paperId: this.paperId }).then((res) => {
+    paperCollect() {
+      papersCollect({paperId: this.paperId}).then((res) => {
         this.collectFlag = true
         this.msgSuccess('收藏成功')
       })
@@ -153,171 +85,4 @@ export default {
 }
 </script>
 <style scoped>
-.preview_container {
-  padding: 20px;
-}
-
-.paper_header {
-  overflow: hidden;
-  margin-bottom: 33px;
-}
-
-.paper_title {
-  height: 25px;
-  font-size: 18px;
-  font-family: PingFangSC-Medium, PingFang SC;
-  font-weight: 500;
-  color: #292929;
-  line-height: 25px;
-  float: left;
-}
-
-.operation {
-  float: right;
-}
-
-.shoucan {
-  display: flex;
-  align-items: center;
-  margin-right: 53px;
-  font-size: 14px;
-  font-family: PingFangSC-Regular, PingFang SC;
-  font-weight: 400;
-}
-
-.shoucan img {
-  margin-right: 6px;
-  cursor: pointer;
-}
-
-.shoucan .sc_text {
-  color: #9f9f9f;
-  line-height: 20px;
-  cursor: pointer;
-  margin-right: 28px;
-}
-
-.shoucan .btn {
-  padding: 6px 20px;
-  cursor: pointer;
-  background: #47c6a2;
-  color: white;
-  border-radius: 4px;
-}
-
-.que_item {
-  border-radius: 1px;
-  border: 1px solid #dedede;
-  margin-bottom: 8px;
-}
-
-.que_content {
-  padding: 12px 24px 0 33px;
-  font-size: 14px;
-  font-family: PingFangSC-Medium, PingFang SC;
-  font-weight: 500;
-  color: #4c4c4c;
-  line-height: 27px;
-  margin-bottom: 10px;
-}
-
-.options {
-  padding-left: 36px;
-  font-size: 14px;
-  font-family: PingFangSC-Regular, PingFang SC;
-  font-weight: 400;
-  color: #4c4c4c;
-  line-height: 27px;
-  border-bottom: 1px solid #dedede;
-}
-
-.options .option {
-  margin-bottom: 10px;
-}
-
-.que_footer {
-  border-top: 1px solid #dedede;
-  padding-left: 33px;
-  overflow: hidden;
-  padding-bottom: 23px;
-  padding-top: 21px;
-}
-
-.que_footer .spans {
-  float: left;
-  font-size: 12px;
-  font-family: PingFangSC-Regular, PingFang SC;
-  font-weight: 400;
-  color: #979797;
-  line-height: 20px;
-}
-
-.que_footer .spans > span {
-  margin-right: 20px;
-}
-
-.operation {
-  display: flex;
-  align-items: center;
-  font-size: 14px;
-  font-family: PingFangSC-Regular, PingFang SC;
-  font-weight: 400;
-  color: #47c6a2;
-  line-height: 20px;
-}
-
-.operation > div {
-  display: flex;
-  align-items: center;
-  cursor: pointer;
-}
-
-.operation .shoucan {
-  margin-right: 46px;
-}
-
-.operation .detail span {
-  border-radius: 1px;
-  border-bottom: 1px solid #47c6a2;
-}
-
-.operation .detail {
-  margin-right: 32px;
-}
-
-.operation > div > img {
-  margin-right: 10px;
-}
-
-.operation .addQue {
-  padding: 7px;
-  border-radius: 4px;
-  border: 1px solid #979797;
-  margin-right: 24px;
-}
-
-.info {
-  padding: 12px 40px;
-}
-
-.option-list {
-  display: flex;
-  flex-direction: column;
-  margin-bottom: 40px;
-  font-size: 14px;
-}
-
-.option-list span {
-  padding: 5px 40px;
-  clear: both;
-}
-
-.subQueBox {
-  padding: 5px 40px;
-}
-
-.subQueBox .subQueItem {
-  margin-bottom: 10px;
-  font-size: 14px;
-}
 </style>