Explorar o código

paper params md

zhangjie %!s(int64=5) %!d(string=hai) anos
pai
achega
52a8223e81

+ 41 - 0
src/assets/styles/card-design.scss

@@ -663,3 +663,44 @@
     }
   }
 }
+// paper-params
+.paper-params {
+  .params-dialog-title {
+    font-size: 16px;
+    > span {
+      font-size: 14px;
+      margin-left: 24px;
+      color: $--color-text-regular;
+    }
+  }
+  .params-main {
+    margin-top: -10px;
+    padding: 0 10px;
+  }
+  .params-head {
+    margin-bottom: 20px;
+  }
+  .params-part {
+    margin: 15px 0;
+  }
+  .params-title {
+    margin-bottom: 10px;
+  }
+  .params-subtitle {
+    margin-bottom: 10px;
+    > span {
+      display: inline-block;
+      vertical-align: middle;
+      &:first-child {
+        margin-right: 20px;
+      }
+    }
+    .el-input-number {
+      width: 60px;
+      margin: 0 5px;
+    }
+  }
+  .param-sum-score {
+    color: $--color-danger;
+  }
+}

+ 10 - 7
src/assets/styles/card-preview.scss

@@ -152,9 +152,6 @@
   &-main {
     position: relative;
     height: 100%;
-    .page-column-element:nth-of-type(1) .element-item-topic-head {
-      margin-top: 0;
-    }
   }
   &-body {
     position: absolute;
@@ -174,8 +171,7 @@
       }
 
       &-topic-head {
-        margin-top: 10px;
-        border-top: 1px solid #333;
+        border: 0;
       }
       &-fill-question,
       &-fill-line {
@@ -782,13 +778,20 @@
 // elem-topic-head
 .elem-topic-head {
   text-align: center;
-  > h3 {
+  .elem-body {
+    padding: 0;
+    border: 1px solid #333;
+  }
+  &-pad {
+    padding-top: 10px;
+  }
+  h3 {
     font-size: 16px;
     height: 29px;
     line-height: 28px;
     border-bottom: 1px dotted #333;
   }
-  > p {
+  p {
     font-size: 12px;
     height: 29px;
     line-height: 29px;

+ 2 - 1
src/assets/styles/home.scss

@@ -326,6 +326,7 @@
   border-collapse: collapse;
   text-align: center;
   margin-bottom: 25px;
+  border-radius: 20px;
 
   th {
     padding: 17px;
@@ -354,7 +355,7 @@
 
   &-striped {
     tr:nth-of-type(odd) {
-      background-color: $--color-background;
+      background-color: $--color-white;
     }
   }
 }

+ 247 - 36
src/modules/card/components/PaperParams.vue

@@ -2,14 +2,19 @@
   <el-dialog
     class="paper-params"
     :visible.sync="modalIsShow"
-    top="10vh"
+    top="10px"
+    width="785px"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body
     @open="dialogOpen"
-    @close="dialogClose"
   >
-    <h1 slot="title">请上传阅卷参数 <span>卷面总分合计:100分 </span></h1>
+    <h1 class="params-dialog-title" slot="title">
+      请上传阅卷参数
+      <span
+        >卷面总分合计:<em class="param-sum-score">{{ pageSumScore }}分</em>
+      </span>
+    </h1>
     <div class="params-main">
       <div class="params-head">
         <el-button
@@ -23,22 +28,111 @@
           >主观题区</el-button
         >
       </div>
-      <div class="params-body"></div>
+      <div class="params-body">
+        <div style="text-align:right;">
+          <upload-button
+            btn-icon="icon icon-share"
+            btn-content="选择上传主观题标答文件(doc/docx)"
+            btn-type="warning"
+            :upload-url="uploadUrl"
+            :format="['doc', 'docx']"
+            @upload-error="uplaodError"
+            @upload-success="uploadSuccess"
+            style="margin: 0;"
+            v-if="topicType"
+          >
+          </upload-button>
+        </div>
+        <div class="params-part" v-for="topic in curTopicList" :key="topic.id">
+          <h2 class="params-title">{{ topic.topicName }}</h2>
+          <p class="params-subtitle">
+            <span>题型:{{ topic.name }}</span>
+            <span v-if="topic.type !== 'COMPOSITION'">
+              通配小题分值:
+              <el-input-number
+                v-model="topic.commonQuestionScore"
+                size="small"
+                :min="0.1"
+                :max="200"
+                :step="0.1"
+                step-strictly
+                :controls="false"
+                @change="commonQuestionScoreChange(topic)"
+              ></el-input-number
+              >分,</span
+            >
+            <span
+              >本大题总分合计:<em class="param-sum-score"
+                >{{ topic.sumScore }}分</em
+              ></span
+            >
+          </p>
+          <div class="params-table" v-if="topic.type !== 'COMPOSITION'">
+            <table class="table table-striped">
+              <tr>
+                <th>题号</th>
+                <th>分值</th>
+                <th>题号</th>
+                <th>分值</th>
+              </tr>
+              <tr v-for="(group, gindex) in topic.questions" :key="gindex">
+                <template v-for="question in group">
+                  <td :key="`${question.questionNo}-1`">
+                    <span>{{ question.questionNo }}</span>
+                  </td>
+                  <td :key="`${question.questionNo}-2`">
+                    <el-input-number
+                      v-model="question.score"
+                      size="small"
+                      :min="0.1"
+                      :max="200"
+                      :step="0.1"
+                      step-strictly
+                      :controls="false"
+                      @change="questionScoreChange(topic)"
+                      v-if="question.questionNo"
+                    ></el-input-number>
+                  </td>
+                </template>
+              </tr>
+            </table>
+          </div>
+          <div class="params-table" v-else>
+            <el-input-number
+              v-model="topic.sumScore"
+              :min="0.1"
+              :max="200"
+              :step="0.1"
+              step-strictly
+              :controls="false"
+              @change="getPageSumScore"
+            ></el-input-number>
+          </div>
+        </div>
+      </div>
     </div>
-    <div slot="footer">
+    <div slot="footer" style="text-align: right;">
       <el-button type="danger" @click="cancel" plain>取消</el-button>
-      <el-button type="primary" :disabled="isSubmit" @click="submit"
-        >确认</el-button
-      >
+      <el-button type="primary" @click="submit">确认</el-button>
     </div>
   </el-dialog>
 </template>
 
 <script>
+import { calcSum, isEmptyObject, objAssign } from "@/plugins/utils";
+import UploadButton from "@/components/UploadButton";
+
 export default {
   name: "paper-params",
+  components: { UploadButton },
   props: {
     pages: {
+      type: Array,
+      default() {
+        return [];
+      }
+    },
+    paperParams: {
       type: Object,
       default() {
         return {};
@@ -49,14 +143,40 @@ export default {
     return {
       modalIsShow: false,
       topicType: 0,
+      pageSumScore: 0,
+      subjectiveAttachmentId: "",
+      curTopicList: [],
       objectives: [],
       subjectives: [],
-      objectiveScores: {},
-      subjectiveScores: {}
+      cacheScores: {},
+      initTopic: {
+        id: "",
+        topicNo: "",
+        topicName: "",
+        type: ""
+      },
+      // import
+      uploadUrl: this.GLOBAL.domain + "/api/print/basic/sys/saveAttachment"
     };
   },
   methods: {
+    selectType(topicType) {
+      this.topicType = topicType;
+      this.curTopicList = topicType ? this.subjectives : this.objectives;
+    },
+    listIncludeItem(list, item) {
+      const index = list.findIndex(elem => elem.id === item.id);
+      return index !== -1;
+    },
     dialogOpen() {
+      if (!isEmptyObject(this.paperParams)) {
+        this.getCacheScore([
+          ...this.paperParams.objectives,
+          ...this.paperParams.subjectives
+        ]);
+        this.subjectiveAttachmentId = this.paperParams.subjectiveAttachmentId;
+      }
+      // 每次打开时,重新生成试题结构,以便避免题卡结构变化之后,当前页面的试题结构没有变的问题
       let objectiveList = [];
       let subjectiveList = [];
       let objectives = [];
@@ -64,7 +184,11 @@ export default {
       this.pages.forEach(page => {
         page.columns.forEach(column => {
           column.elements.forEach(element => {
-            if (element.sign && element.type !== "TOPIC_HEAD") {
+            if (
+              element.sign &&
+              element.type !== "TOPIC_HEAD" &&
+              element.type !== "CARD_HEAD"
+            ) {
               if (element.sign === "objective") objectiveList.push(element);
               if (element.sign === "subjective") subjectiveList.push(element);
             }
@@ -73,38 +197,63 @@ export default {
       });
       // 客观题
       objectiveList.forEach(item => {
-        let topic = {
-          index: objectives.length,
+        const topic = item.parent || item;
+        if (this.listIncludeItem(objectives, topic)) return;
+        let data = {
           sumScore: 0,
-          choiceList: this.getChoiceList(item.optionCount, item.isFill),
-          questions: this.getQuestions(item),
-          ...item
+          commonQuestionScore: 1,
+          name: this.getObjectiveTopicName(topic),
+          choiceList: this.getChoiceList(topic.optionCount, topic.isFill),
+          questions: this.getQuestions(topic),
+          ...objAssign(this.initTopic, topic)
         };
-        objectives.push(topic);
+        data.sumScore = this.getTopicSumScore(data);
+        objectives.push(data);
       });
       // 主观题
       subjectiveList.forEach(item => {
-        if (item.type === "FILL_LINE") {
-          let topic = {
-            index: subjectives.length,
-            sumScore: 0,
-            questions: this.getQuestions(item),
-            ...item
-          };
-          subjectives.push(topic);
+        const topic = item.parent || item;
+        if (this.listIncludeItem(subjectives, topic)) return;
+        let data = {
+          sumScore: 0,
+          commonQuestionScore: 1,
+          name: this.getSubjectiveTopicName(topic),
+          ...objAssign(this.initTopic, topic)
+        };
+        if (topic.type === "COMPOSITION") {
+          data.sumScore = this.cacheScores[topic.topicNo] || 1;
+        } else {
+          data.questions = this.getQuestions(topic);
+          data.sumScore = this.getTopicSumScore(data);
         }
+        subjectives.push(data);
       });
+
+      this.subjectives = subjectives;
+      this.objectives = objectives;
+      this.getPageSumScore();
+      this.selectType(0);
     },
-    getQuestions(data) {
+    getQuestions(topic) {
       let questions = [];
-      for (let j = data.startNumber; j < data.questionsCount; j++) {
-        let quesiton = {
+      let numPerColumn = Math.ceil(topic.questionsCount / 2);
+      for (let j = topic.startNumber; j <= numPerColumn; j++) {
+        let group = [];
+        let question = {
           questionNo: j,
-          score: 0
+          score: this.cacheScores[`${topic.topicNo}-${j}`] || 1
         };
-        // if (data.type === "FILL_QUESTION")
-        //   quesiton.answers = data.isMultiply ? [] : "";
-        questions[j] = quesiton;
+        if (topic.type === "FILL_QUESTION")
+          question.answers = topic.isMultiply ? [] : "";
+        group[0] = question;
+
+        if (numPerColumn + j <= topic.questionsCount) {
+          group[1] = { ...question, questionNo: numPerColumn + j };
+        } else {
+          group[1] = { questionNo: "" };
+        }
+
+        questions.push(group);
       }
       return questions;
     },
@@ -117,22 +266,84 @@ export default {
         return "选择题(单选)";
       }
     },
+    getSubjectiveTopicName(data) {
+      const names = {
+        EXPLAIN: "解答题",
+        COMPOSITION: "作文题",
+        FILL_LINE: "填空题"
+      };
+      return names[data.type];
+    },
     getChoiceList(num, isFill) {
-      const options = !isFill ? "abcdefghijklmn" : "√×";
+      const options = !isFill ? "abcdefghijklmnopqrstuv" : "√×";
       return options
         .toUpperCase()
         .slice(0, num)
         .split("");
     },
-    dialogClose() {},
+    commonQuestionScoreChange(topic) {
+      topic.questions.map(group => {
+        group.map(question => {
+          question.score = topic.commonQuestionScore;
+        });
+      });
+      this.questionScoreChange(topic);
+    },
+    questionScoreChange(topic) {
+      topic.sumScore = this.getTopicSumScore(topic);
+      this.getPageSumScore();
+    },
+    getTopicSumScore(topic) {
+      const scoreList = topic.questions.map(group => {
+        return calcSum(group.map(question => question.score || 0));
+      });
+      return calcSum(scoreList);
+    },
+    getPageSumScore() {
+      this.pageSumScore =
+        calcSum(this.objectives.map(item => item.sumScore)) +
+        calcSum(this.subjectives.map(item => item.sumScore));
+    },
+    getCacheScore(topics) {
+      topics = topics || [...this.objectives, ...this.subjectives];
+      let cacheScores = {};
+      topics.map(topic => {
+        if (topic.type === "COMPOSITION") {
+          cacheScores[`${topic.topicNo}`] = topic.sumScore;
+        } else {
+          topic.questions.map(group => {
+            group.map(question => {
+              cacheScores[`${topic.topicNo}-${question.questionNo}`] =
+                question.score;
+            });
+          });
+        }
+      });
+      this.cacheScores = cacheScores;
+    },
+    // import
+    uplaodError(msg) {
+      this.$message.error(msg);
+    },
+    uploadSuccess(response) {
+      this.$message.success("上传成功!");
+      this.subjectiveAttachmentId = response.data.id;
+    },
+    // dialog
     cancel() {
       this.modalIsShow = false;
     },
     open() {
       this.modalIsShow = true;
     },
-    selectType(topicType) {
-      this.topicType = topicType;
+    submit() {
+      this.$emit("confirm", {
+        pageSumScore: this.pageSumScore,
+        objectives: this.objectives,
+        subjectives: this.subjectives,
+        subjectiveAttachmentId: this.subjectiveAttachmentId
+      });
+      this.modalIsShow = false;
     }
   }
 };

+ 2 - 1
src/modules/card/components/SavePage.vue

@@ -26,7 +26,7 @@ export default {
     };
   },
   computed: {
-    ...mapState("card", ["pages", "cardConfig"])
+    ...mapState("card", ["pages", "cardConfig", "paperParams"])
   },
   methods: {
     getFillAreaIndex() {
@@ -318,6 +318,7 @@ export default {
         {
           version: CARD_VERSION,
           cardConfig: this.cardConfig,
+          paperParams: this.paperParams,
           pages: this.parsePageExchange()
         },
         (k, v) => (k.startsWith("_") ? undefined : v)

+ 0 - 2
src/modules/card/components/elementEdit/Composition.vue

@@ -48,8 +48,6 @@ export default {
     ...mapMutations("card", ["setCurDragElement", "setCurElement"]),
     ...mapActions("card", ["rebuildPages"]),
     dropInnerElement(e) {
-      console.log(e);
-
       // 作文题中只允许创建一个文本题,不允许创建线条
       if (this.data.type.includes("LINE")) return;
       const index = this.data.elements.findIndex(item => item.type === "TEXT");

+ 13 - 4
src/modules/card/components/elementPreview/TopicHead.vue

@@ -1,7 +1,9 @@
 <template>
-  <div class="page-element elem-topic-head">
-    <h3>{{ data.typeName }}答题区</h3>
-    <p>{{ data.content }}</p>
+  <div :class="classes">
+    <div class="elem-body">
+      <h3>{{ data.typeName }}答题区</h3>
+      <p>{{ data.content }}</p>
+    </div>
   </div>
 </template>
 
@@ -16,7 +18,14 @@ export default {
   data() {
     return {};
   },
-  mounted() {},
+  computed: {
+    classes() {
+      return [
+        "elem-topic-head",
+        { "elem-topic-head-pad": !this.data.isColumnFirst }
+      ];
+    }
+  },
   methods: {}
 };
 </script>

+ 0 - 1
src/modules/card/components/elementPropEdit/EditComposition.vue

@@ -5,7 +5,6 @@
     title="作文题"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 0 - 1
src/modules/card/components/elementPropEdit/EditExplain.vue

@@ -5,7 +5,6 @@
     title="解答题"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 0 - 1
src/modules/card/components/elementPropEdit/EditFillLine.vue

@@ -5,7 +5,6 @@
     title="填空题"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 0 - 1
src/modules/card/components/elementPropEdit/EditFillQuestion.vue

@@ -5,7 +5,6 @@
     title="选择题"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 0 - 1
src/modules/card/components/elementPropEdit/EditImage.vue

@@ -5,7 +5,6 @@
     title="图片编辑"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 0 - 1
src/modules/card/components/elementPropEdit/EditLine.vue

@@ -5,7 +5,6 @@
     title="线条编辑"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 0 - 1
src/modules/card/components/elementPropEdit/EditText.vue

@@ -5,7 +5,6 @@
     title="文本编辑"
     top="10vh"
     width="640px"
-    :modal="false"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body

+ 5 - 1
src/modules/card/elementModel.js

@@ -58,6 +58,7 @@ const TOPIC_HEAD_PROP = {
   h: 60,
   content: "",
   typeName: "",
+  isColumnFirst: false,
   sign: "objective" // objective:客观题,subjective:主观题
 };
 
@@ -355,13 +356,16 @@ const getCardHeadModel = cardConfig => {
  *
  * @param {String} content
  * @param {String} type objective:客观题,subjective:主观题
+ * @param {Boolean} isColumnFirst 是否是当前栏的第一个元件
  */
-const getTopicHead = (content, type) => {
+const getTopicHead = (content, type, isColumnFirst) => {
   const typeName = type === "objective" ? "客观题" : "主观题";
   const element = deepCopy(TOPIC_HEAD_PROP);
   element.sign = type;
   element.typeName = typeName;
   element.content = content;
+  element.isColumnFirst = isColumnFirst;
+  element.h = isColumnFirst ? element.h : element.h + 10;
   element.id = getElementId();
   return element;
 };

+ 10 - 1
src/modules/card/store.js

@@ -15,6 +15,7 @@ const state = {
   pages: [],
   topicNos: [],
   cardConfig: {},
+  paperParams: {},
   openElementEditDialog: false
 };
 
@@ -34,6 +35,9 @@ const mutations = {
   setCardConfig(state, cardConfig) {
     state.cardConfig = Object.assign({}, state.cardConfig, cardConfig);
   },
+  setPaperParams(state, paperParams) {
+    state.paperParams = paperParams;
+  },
   setCurPageNo(state, curPageNo) {
     state.curPageNo = curPageNo;
   },
@@ -55,6 +59,7 @@ const mutations = {
     state.curPageNo = 0;
     state.pages = [];
     state.cardConfig = {};
+    state.paperParams = {};
     state.openElementEditDialog = false;
     state.topicNos = [];
   }
@@ -388,7 +393,11 @@ const actions = {
       let elementList = [element];
       if (checkElementIsCurColumnFirstType(element)) {
         elementList.unshift(
-          getTopicHead(state.cardConfig[`${element.sign}Notice`], element.sign)
+          getTopicHead(
+            state.cardConfig[`${element.sign}Notice`],
+            element.sign,
+            !curColumnElements.length
+          )
         );
       }
 

+ 27 - 4
src/modules/card/views/CardDesign.vue

@@ -90,7 +90,9 @@
         <div class="action-part">
           <div class="action-part-title"><h2>阅卷参数</h2></div>
           <div class="action-part-body">
-            <el-button type="primary">上传阅卷参数(0)</el-button>
+            <el-button type="primary" @click="modifyParams"
+              >上传阅卷参数({{ paperParams.pageSumScore }})</el-button
+            >
           </div>
         </div>
       </div>
@@ -244,6 +246,13 @@
     <div class="design-preview-frame" v-if="cardPreviewUrl">
       <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
     </div>
+    <!-- paper-params -->
+    <paper-params
+      :pages="pages"
+      :paper-params="paperParams"
+      @confirm="paperParamsModified"
+      ref="PaperParams"
+    ></paper-params>
   </div>
 </template>
 
@@ -273,6 +282,7 @@ import RightClickMenu from "../components/RightClickMenu";
 import CardHeadSample from "../components/elementEdit/CardHeadSample";
 import SavePage from "../components/SavePage";
 import PageNumber from "../components/pageInfo/PageNumber";
+import PaperParams from "../components/PaperParams";
 
 export default {
   name: "card-design",
@@ -284,7 +294,8 @@ export default {
     RightClickMenu,
     CardHeadSample,
     SavePage,
-    PageNumber
+    PageNumber,
+    PaperParams
   },
   data() {
     return {
@@ -302,6 +313,7 @@ export default {
     ...mapState("card", [
       "cardConfig",
       "pages",
+      "paperParams",
       "curElement",
       "curPageNo",
       "topicNos"
@@ -323,6 +335,7 @@ export default {
       "setOpenElementEditDialog",
       "setCurDragElement",
       "setPages",
+      "setPaperParams",
       "initTopicNos",
       "initState"
     ]),
@@ -353,6 +366,7 @@ export default {
         this.cardDetailId = tempData.id;
         this.setPages(cont.pages);
         this.setCardConfig(cont.cardConfig);
+        this.setPaperParams(cont.paperParams);
         this.initTopicNos();
       } else {
         // 没有题卡内容时,直接创建新的内容
@@ -429,6 +443,13 @@ export default {
       this.setCurPageNo(pindex);
       this.setCurElement({});
     },
+    // paper-params
+    modifyParams() {
+      this.$refs.PaperParams.open();
+    },
+    paperParamsModified(paperParams) {
+      this.setPaperParams(paperParams);
+    },
     // save
     getCardData(contentTemp = "") {
       const tcPCard = this.$objAssign(
@@ -453,11 +474,13 @@ export default {
       let data = {
         tcPCard,
         tcPCardDetail: {
-          content: this.$refs.SavePage.getPageModel()
+          content: this.$refs.SavePage.getPageModel(),
+          subjectiveAttachmentId:
+            this.paperParams["subjectiveAttachmentId"] || "",
+          contentTemp
         }
       };
       if (this.cardDetailId) data.tcPCardDetail.id = this.cardDetailId;
-      if (contentTemp) data.tcPCardDetail.contentTemp = contentTemp;
 
       if (this.prepareTcPCard.taskId && this.prepareTcPCard.cardSource === "1")
         data.tcPExamTaskDetail = {

+ 5 - 0
src/plugins/utils.js

@@ -221,6 +221,10 @@ function calcSum(dataList) {
   }, 0);
 }
 
+function isEmptyObject(obj) {
+  return !Object.keys(obj).length;
+}
+
 /**
  * 解决后台返回的数据中number位数超过16位的情况
  * @param {String} text json格式字符串
@@ -248,5 +252,6 @@ export {
   getNumList,
   removeHtmlTag,
   calcSum,
+  isEmptyObject,
   jsonBigNumberToString
 };