소스 검색

card md and bug fix

zhangjie 5 년 전
부모
커밋
17768d3685

+ 62 - 45
src/assets/styles/card-design.scss

@@ -69,7 +69,6 @@
       // background-color: rgba($color: #ffffff, $alpha: 0.7);
     }
   }
-
   .page-column-main {
     &::before {
       content: "";
@@ -84,9 +83,54 @@
     }
   }
   .page-column-element {
+    .element-item-error {
+      box-shadow: 0 0 10px inset $--color-danger;
+    }
+
     > .element-resize {
       width: 100% !important;
     }
+
+    .element-resize {
+      > .resize-control {
+        > .control-point,
+        > .control-line {
+          display: none;
+        }
+      }
+
+      &:hover {
+        > .resize-control {
+          > .control-line {
+            display: block;
+          }
+        }
+      }
+
+      &-act {
+        > .resize-control {
+          > .control-point,
+          > .control-line {
+            display: block;
+          }
+        }
+      }
+    }
+
+    .element-item-topic-number {
+      position: absolute;
+      left: -28px;
+      top: 0;
+      width: 24px;
+      height: 24px;
+      z-index: 99;
+      line-height: 24px;
+      font-size: 14px;
+      text-align: center;
+      background-color: $--color-primary;
+      color: #fff;
+      border-radius: 50% 0 50% 50%;
+    }
   }
 
   // page-main-outer
@@ -108,32 +152,7 @@
     z-index: 1000;
     visibility: hidden;
   }
-  // .topic-design
-  .topic-design {
-    + .resize-control {
-      .control-point,
-      .control-line {
-        display: none;
-      }
-    }
-
-    &:hover {
-      + .resize-control {
-        .control-line {
-          display: block;
-        }
-      }
-    }
 
-    &-act {
-      + .resize-control {
-        .control-point,
-        .control-line {
-          display: block;
-        }
-      }
-    }
-  }
   // card-head-sample
   .card-head-sample {
     position: absolute;
@@ -410,6 +429,23 @@
       margin-left: 10px;
     }
   }
+  .topicno-list {
+    font-size: 0;
+    line-height: 40px;
+    li {
+      display: inline-block;
+      vertical-align: middle;
+      margin: 0 5px 0 0;
+      width: 24px;
+      height: 24px;
+      line-height: 24px;
+      text-align: center;
+      font-size: 14px;
+      border-radius: 5px;
+      background-color: $--color-primary;
+      color: #fff;
+    }
+  }
 }
 // element-prop-edit
 .edit-dialog {
@@ -429,25 +465,6 @@
 }
 //
 // explain-children-element
-.explain-children-element,
-.composition-element {
-  .control-point,
-  .control-line {
-    display: none;
-  }
-
-  &:hover {
-    .resize-line {
-      display: block;
-    }
-  }
-  &-act {
-    .control-point,
-    .control-line {
-      display: block;
-    }
-  }
-}
 .composition-element {
   > .element-resize {
     width: 100% !important;

+ 27 - 9
src/assets/styles/card-preview.scss

@@ -177,13 +177,24 @@
         margin-top: 10px;
         border-top: 1px solid #333;
       }
-      &-fill-question {
+      &-fill-question,
+      &-fill-line {
         border-bottom: 0;
       }
       &-type-last {
         border-bottom: 1px solid #333;
       }
     }
+    &:last-child {
+      .element-item {
+        &-type-pre {
+          border-bottom: 1px solid #333;
+        }
+      }
+      .element-item-card-head.element-item-type-pre {
+        border-bottom: none;
+      }
+    }
   }
 }
 // locator
@@ -970,11 +981,8 @@
 .elem-fill-line {
   white-space: normal;
 
-  .elem-title {
-    padding-bottom: 0;
-  }
   .elem-body {
-    padding-bottom: 30px;
+    padding: 0 10px;
     font-size: 0;
   }
   .elem-fill-quesiton {
@@ -986,19 +994,29 @@
 
     li {
       height: 50px;
-      border-bottom: 1px solid #000;
       position: relative;
       z-index: 8;
+      &:not(:first-child) {
+        &::after {
+          content: "";
+          display: block;
+          position: absolute;
+          width: 100%;
+          border-bottom: 1px solid #000;
+          bottom: 10px;
+        }
+      }
     }
 
     > li:first-child {
       position: absolute;
-      height: 100%;
       background-color: #fff;
-      top: 0;
+      height: auto;
+      bottom: 5px;
+      top: 5px;
       left: 5px;
       z-index: 9;
-      padding-top: 30px;
+      padding-top: 20px;
       border: none;
     }
   }

+ 6 - 1
src/modules/card/components/PagePropEdit.vue

@@ -52,6 +52,11 @@
           >启用禁答区</el-checkbox
         >
       </el-form-item>
+      <el-form-item label="已建题号">
+        <ul class="topicno-list">
+          <li v-for="item in topicNos" :key="item">{{ item }}</li>
+        </ul>
+      </el-form-item>
     </el-form>
   </div>
 </template>
@@ -98,7 +103,7 @@ export default {
     };
   },
   computed: {
-    ...mapState("card", ["curPageNo", "pages", "cardConfig"]),
+    ...mapState("card", ["curPageNo", "pages", "cardConfig", "topicNos"]),
     curPage() {
       return this.pages[this.curPageNo];
     },

+ 11 - 3
src/modules/card/components/TopicElementEdit.vue

@@ -11,8 +11,10 @@
     </div>
     <element-resize
       v-model="elemData"
+      :class="{ 'element-resize-act': curElement.id === data.id }"
       :active="['b']"
       :move="false"
+      :min-height="20"
       fit-parent
       @on-click="activeCurElement"
       @resize-over="resizeOver"
@@ -21,6 +23,8 @@
       <div :class="classes" :id="data.id" :data-type="data.type">
         <component :is="compName" :data="data"></component>
       </div>
+      <!-- topic-number -->
+      <topic-number :data="data.topicNo"></topic-number>
     </element-resize>
   </div>
 </template>
@@ -35,6 +39,7 @@ import EditExplainChildren from "./elementEdit/ExplainChildren";
 import EditComposition from "./elementEdit/Composition";
 import EditTopicHead from "./elementPreview/TopicHead";
 import ElementResize from "./common/ElementResize.vue";
+import TopicNumber from "./common/TopicNumber";
 
 export default {
   name: "topic-design",
@@ -45,7 +50,8 @@ export default {
     EditFillLine,
     EditExplainChildren,
     EditComposition,
-    ElementResize
+    ElementResize,
+    TopicNumber
   },
   props: {
     data: {
@@ -76,9 +82,11 @@ export default {
         "topic-design",
         "element-item",
         `element-item-${this.elementName}`,
-        this.data["isLast"] ? `element-item-type-last` : "",
+        this.data["isLast"]
+          ? `element-item-type-last`
+          : "element-item-type-pre",
         {
-          "topic-design-act": this.curElement.id === this.data.id
+          "element-item-error": this.data.isCovered
         }
       ];
     }

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

@@ -43,7 +43,8 @@ export default {
       return [
         "topic-preview",
         "element-item",
-        `element-item-${this.elementName}`
+        `element-item-${this.elementName}`,
+        this.data["isLast"] ? `element-item-type-last` : "element-item-type-pre"
       ];
     },
     styles() {

+ 20 - 0
src/modules/card/components/common/TopicNumber.vue

@@ -0,0 +1,20 @@
+<template>
+  <div class="element-item-topic-number">
+    {{ data }}
+  </div>
+</template>
+
+<script>
+export default {
+  name: "topic-number",
+  props: {
+    data: {
+      type: [Number, String]
+    }
+  },
+  data() {
+    return {};
+  },
+  methods: {}
+};
+</script>

+ 2 - 9
src/modules/card/components/elementEdit/CompositionElement.vue

@@ -1,7 +1,8 @@
 <template>
-  <div :class="classes">
+  <div class="composition-element">
     <element-resize
       v-model="elemData"
+      :class="{ 'element-resize-act': curElement.id === data.id }"
       :active="active"
       :move="false"
       @on-click="activeCurElement"
@@ -62,14 +63,6 @@ export default {
     compName() {
       return `elem-${this.data.type.toLowerCase().replace("_", "-")}`;
     },
-    classes() {
-      return [
-        "composition-element",
-        {
-          "composition-element-act": this.curElement.id === this.data.id
-        }
-      ];
-    },
     active() {
       return this.actives[this.data.type];
     }

+ 2 - 9
src/modules/card/components/elementEdit/ExplainChildrenElement.vue

@@ -1,7 +1,8 @@
 <template>
-  <div :class="classes">
+  <div class="explain-children-element">
     <element-resize
       v-model="elemData"
+      :class="{ 'element-resize-act': curElement.id === data.id }"
       :active="active"
       fit-parent
       @change="elementChange"
@@ -64,14 +65,6 @@ export default {
     compName() {
       return `elem-${this.data.type.toLowerCase().replace("_", "-")}`;
     },
-    classes() {
-      return [
-        "explain-children-element",
-        {
-          "explain-children-element-act": this.curElement.id === this.data.id
-        }
-      ];
-    },
     active() {
       return this.actives[this.data.type];
     }

+ 3 - 1
src/modules/card/components/elementPreview/FillLine.vue

@@ -1,6 +1,8 @@
 <template>
   <div class="elem-fill-line">
-    <div class="elem-title" v-if="data.topicName">{{ data.topicName }}</div>
+    <div class="elem-title" v-if="data.startNumber === data.parent.startNumber">
+      {{ data.parent.topicName }}
+    </div>
     <div class="elem-body">
       <ul
         class="elem-fill-quesiton"

+ 27 - 2
src/modules/card/components/elementPropEdit/EditComposition.vue

@@ -19,6 +19,17 @@
       :key="modalForm.id"
       label-width="100px"
     >
+      <el-form-item prop="topicNo" label="大题题号:">
+        <el-input-number
+          style="width:125px;"
+          v-model.number="modalForm.topicNo"
+          :min="1"
+          :max="20"
+          :step="1"
+          step-strictly
+          :controls="false"
+        ></el-input-number>
+      </el-form-item>
       <el-form-item prop="topicName" label="题目名称:">
         <el-input
           v-model.trim="modalForm.topicName"
@@ -27,7 +38,7 @@
           clearable
         ></el-input>
       </el-form-item>
-      <el-form-item prop="startEnd" label="作文行数:">
+      <el-form-item prop="lineCount" label="作文行数:">
         <el-input-number
           style="width:125px;"
           v-model="modalForm.lineCount"
@@ -101,7 +112,21 @@ export default {
         ],
         topicNo: [
           {
-            validate: topicNoValidater,
+            required: true,
+            message: "请输入大题题号",
+            trigger: "change"
+          },
+          {
+            type: "number",
+            validator: topicNoValidater,
+            trigger: "change"
+          }
+        ],
+        lineCount: [
+          {
+            required: true,
+            type: "number",
+            message: "请输入作文行数",
             trigger: "change"
           }
         ]

+ 33 - 9
src/modules/card/components/elementPropEdit/EditExplain.vue

@@ -19,6 +19,17 @@
       :key="modalForm.id"
       label-width="100px"
     >
+      <el-form-item prop="topicNo" label="大题题号:">
+        <el-input-number
+          style="width:125px;"
+          v-model.number="modalForm.topicNo"
+          :min="1"
+          :max="20"
+          :step="1"
+          step-strictly
+          :controls="false"
+        ></el-input-number>
+      </el-form-item>
       <el-form-item prop="topicName" label="题目名称:">
         <el-input
           v-model.trim="modalForm.topicName"
@@ -83,16 +94,18 @@ export default {
     }
   },
   data() {
-    const numberValidater = (rule, value, callback) => {
-      if (this.modalForm.startNumber < this.modalForm.endNumber) {
-        callback(new Error("开始序号不能小于结束序号"));
+    const numberRangeValidater = (rule, value, callback) => {
+      if (!this.modalForm.startNumber || !this.modalForm.endNumber) {
+        return callback(new Error("请输入起止题号"));
+      }
+      if (this.modalForm.startNumber > this.modalForm.endNumber) {
+        callback(new Error("开始题号不能大于结束题号"));
       } else {
         callback();
       }
     };
-
     const topicNoValidater = (rule, value, callback) => {
-      if (this.instance.topicNo === 0) {
+      if (!this.instance.topicNo) {
         // 新增题目
         if (this.topicNos.includes(value)) {
           callback(new Error("当前大题题号已经存在,请重新输入"));
@@ -108,7 +121,6 @@ export default {
         }
       }
     };
-
     return {
       dialogIsShow: false,
       modalForm: { ...initModalForm },
@@ -122,13 +134,25 @@ export default {
         ],
         topicNo: [
           {
-            validate: topicNoValidater,
+            required: true,
+            message: "请输入大题题号",
+            trigger: "change"
+          },
+          {
+            type: "number",
+            validator: topicNoValidater,
             trigger: "change"
           }
         ],
-        startEnd: [
+        endNumber: [
+          {
+            required: true,
+            message: "请输入起止题号",
+            trigger: "change"
+          },
           {
-            validate: numberValidater,
+            type: "number",
+            validator: numberRangeValidater,
             trigger: "change"
           }
         ]

+ 61 - 11
src/modules/card/components/elementPropEdit/EditFillLine.vue

@@ -19,6 +19,17 @@
       :key="modalForm.id"
       label-width="100px"
     >
+      <el-form-item prop="topicNo" label="大题题号:">
+        <el-input-number
+          style="width:125px;"
+          v-model.number="modalForm.topicNo"
+          :min="1"
+          :max="20"
+          :step="1"
+          step-strictly
+          :controls="false"
+        ></el-input-number>
+      </el-form-item>
       <el-form-item prop="topicName" label="题目名称:">
         <el-input
           v-model.trim="modalForm.topicName"
@@ -27,7 +38,7 @@
           clearable
         ></el-input>
       </el-form-item>
-      <el-form-item prop="startEnd" label="起止题号:">
+      <el-form-item prop="endNumber" label="起止题号:">
         <el-input-number
           style="width:40px;"
           v-model="modalForm.startNumber"
@@ -48,7 +59,7 @@
           :controls="false"
         ></el-input-number>
       </el-form-item>
-      <el-form-item label="每行空数:">
+      <el-form-item prop="questionNumberPerLine" label="每行空数:">
         <el-input-number
           style="width:125px;"
           v-model="modalForm.questionNumberPerLine"
@@ -60,7 +71,7 @@
         ></el-input-number>
         <span class="el-input-tips">*指一行显示填空题数量</span>
       </el-form-item>
-      <el-form-item label="每空行数:">
+      <el-form-item prop="lineNumberPerQuestion" label="每空行数:">
         <el-input-number
           style="width:125px;"
           v-model="modalForm.lineNumberPerQuestion"
@@ -117,16 +128,19 @@ export default {
     }
   },
   data() {
-    const numberValidater = (rule, value, callback) => {
-      if (this.modalForm.startNumber < this.modalForm.endNumber) {
-        callback(new Error("开始序号不能小于结束序号"));
+    const numberRangeValidater = (rule, value, callback) => {
+      if (!this.modalForm.startNumber || !this.modalForm.endNumber) {
+        return callback(new Error("请输入起止题号"));
+      }
+      if (this.modalForm.startNumber > this.modalForm.endNumber) {
+        callback(new Error("开始题号不能大于结束题号"));
       } else {
         callback();
       }
     };
 
     const topicNoValidater = (rule, value, callback) => {
-      if (this.instance.topicNo === 0) {
+      if (!this.instance.topicNo) {
         // 新增题目
         if (this.topicNos.includes(value)) {
           callback(new Error("当前大题题号已经存在,请重新输入"));
@@ -147,15 +161,50 @@ export default {
       dialogIsShow: false,
       modalForm: { ...initModalForm },
       rules: {
+        topicName: [
+          {
+            required: true,
+            message: "请输入题目名称",
+            trigger: "change"
+          }
+        ],
         topicNo: [
           {
-            validate: topicNoValidater,
+            required: true,
+            message: "请输入大题题号",
+            trigger: "change"
+          },
+          {
+            type: "number",
+            validator: topicNoValidater,
+            trigger: "change"
+          }
+        ],
+        endNumber: [
+          {
+            required: true,
+            message: "请输入起止题号",
+            trigger: "change"
+          },
+          {
+            type: "number",
+            validator: numberRangeValidater,
+            trigger: "change"
+          }
+        ],
+        questionNumberPerLine: [
+          {
+            required: true,
+            type: "number",
+            message: "请输入选项个数",
             trigger: "change"
           }
         ],
-        startEnd: [
+        lineNumberPerQuestion: [
           {
-            validate: numberValidater,
+            required: true,
+            type: "number",
+            message: "请输入每空行数",
             trigger: "change"
           }
         ]
@@ -164,7 +213,8 @@ export default {
   },
   methods: {
     initData(val) {
-      this.modalForm = { ...val };
+      const valInfo = val.parent || val;
+      this.modalForm = { ...valInfo };
       this.modalForm.endNumber =
         this.modalForm.startNumber + this.modalForm.questionsCount - 1;
     },

+ 18 - 7
src/modules/card/components/elementPropEdit/EditFillQuestion.vue

@@ -22,7 +22,7 @@
       <el-form-item prop="topicNo" label="大题题号:">
         <el-input-number
           style="width:125px;"
-          v-model="modalForm.topicNo"
+          v-model.number="modalForm.topicNo"
           :min="1"
           :max="20"
           :step="1"
@@ -59,7 +59,7 @@
           :controls="false"
         ></el-input-number>
       </el-form-item>
-      <el-form-item label="选项个数:">
+      <el-form-item prop="optionCount" label="选项个数:">
         <el-input-number
           style="width:125px;"
           v-model="modalForm.optionCount"
@@ -122,9 +122,12 @@ export default {
     }
   },
   data() {
-    const numberValidater = (rule, value, callback) => {
-      if (this.modalForm.startNumber < this.modalForm.endNumber) {
-        callback(new Error("开始序号不能小于结束序号"));
+    const numberRangeValidater = (rule, value, callback) => {
+      if (!this.modalForm.startNumber || !this.modalForm.endNumber) {
+        return callback(new Error("请输入起止题号"));
+      }
+      if (this.modalForm.startNumber > this.modalForm.endNumber) {
+        callback(new Error("开始题号不能大于结束题号"));
       } else {
         callback();
       }
@@ -167,7 +170,7 @@ export default {
           },
           {
             type: "number",
-            validate: topicNoValidater,
+            validator: topicNoValidater,
             trigger: "change"
           }
         ],
@@ -179,7 +182,15 @@ export default {
           },
           {
             type: "number",
-            validate: numberValidater,
+            validator: numberRangeValidater,
+            trigger: "change"
+          }
+        ],
+        optionCount: [
+          {
+            required: true,
+            type: "number",
+            message: "请输入选项个数",
             trigger: "change"
           }
         ]

+ 41 - 3
src/modules/card/elementModel.js

@@ -168,6 +168,7 @@ const FILL_QUESTION_PROP = {
   optionGap: 8,
   isFill: false,
   isMultiply: false,
+  isCovered: false,
   fontSize: "14px"
 };
 
@@ -190,7 +191,7 @@ const FILL_LINE_PROP = {
   x: 0,
   y: 0,
   w: 0,
-  h: 170,
+  h: 50,
   sign: "subjective",
   topicName: "",
   topicNo: null,
@@ -198,7 +199,8 @@ const FILL_LINE_PROP = {
   questionsCount: 4,
   questionNumberPerLine: 2,
   lineNumberPerQuestion: 1,
-  numberPre: ""
+  numberPre: "",
+  isCovered: false
 };
 
 // 解答题
@@ -219,6 +221,8 @@ const EXPLAIN_CHILDREN_PROP = {
   w: 0,
   h: 458,
   sign: "subjective",
+  topicNo: null,
+  isCovered: false,
   // 小题序号
   explainNumber: 0,
   // 每一个解答题小题都可以包含其他基础元件,这些基础元件都用绝对定位
@@ -238,6 +242,7 @@ const COMPOSITION_PROP = {
   sign: "subjective",
   topicName: "",
   topicNo: null,
+  isCovered: false,
   lineCount: 5,
   // 每一个作文题都可以包含其他基础元件,但这些基础元件都用相对定位
   elements: []
@@ -361,6 +366,11 @@ const getTopicHead = (content, type) => {
   return element;
 };
 
+/**
+ * 创建选择题数据
+ * @param {Object} model 选择题模型数据
+ * @param {Number} columnNumber 页面栏数
+ */
 const getFillQuesitons = (model, columnNumber) => {
   const parent = { ...model };
   const numberPerChildren = {
@@ -391,6 +401,32 @@ const getFillQuesitons = (model, columnNumber) => {
   return elements;
 };
 
+/**
+ * 创建填空题数据
+ * @param {Object} model 填空题模型数据
+ */
+const getFillLine = model => {
+  const parent = { ...model };
+  const numPerLine = model.questionNumberPerLine;
+  const total = Math.ceil(model.questionsCount / numPerLine);
+  const questionHeight = parent.h * model.lineNumberPerQuestion;
+  let elements = [];
+  for (var i = 0; i < total; i++) {
+    let child = Object.assign(deepCopy(FILL_LINE_PROP), parent);
+    child.id = getElementId();
+    // 动态计算
+    child.h = i ? questionHeight : questionHeight + 34;
+    child.startNumber = model.startNumber + i * numPerLine;
+    child.questionsCount =
+      i === total - 1 ? model.questionsCount - numPerLine * i : numPerLine;
+    child.parent = parent;
+    child.isLast = i === total - 1;
+
+    elements[i] = child;
+  }
+  return elements;
+};
+
 /**
  *
  * @param {Object} explainModel 解答题model
@@ -403,7 +439,8 @@ const getExplainChildren = explainModel => {
   for (var i = 0; i < explainModel.questionsCount; i++) {
     let child = deepCopy(EXPLAIN_CHILDREN_PROP);
     child.id = getElementId();
-    child.w = document.getElementById("column-0-0").offsetWidth;
+    child.w = parent.w;
+    child.topicNo = parent.topicNo;
     child.explainNumber = i + explainModel.startNumber;
     child.parent = parent;
 
@@ -445,6 +482,7 @@ export {
   getTopicModel,
   getExplainChildren,
   getFillQuesitons,
+  getFillLine,
   getElementId,
   ELEMENT_LIST,
   TOPIC_LIST

+ 42 - 61
src/modules/card/store.js

@@ -1,6 +1,7 @@
 import {
   getExplainChildren,
   getFillQuesitons,
+  getFillLine,
   getNewPage,
   getTopicHead,
   getElementId
@@ -47,7 +48,6 @@ const mutations = {
   },
   initTopicNos(state) {
     state.topicNos = getPageTopicNos(state.pages);
-    console.log(state.topicNos);
   },
   initState(state) {
     state.curElement = {};
@@ -152,51 +152,48 @@ const findElementById = (id, pages) => {
   return curElement;
 };
 
+const createFunc = {
+  EXPLAIN(element) {
+    return getExplainChildren(element);
+  },
+  FILL_QUESTION(element) {
+    return getFillQuesitons(element, state.cardConfig.columnNumber);
+  },
+  FILL_LINE(element) {
+    return getFillLine(element);
+  },
+  COMPOSITION(element) {
+    return [element];
+  }
+};
+
 const actions = {
   modifyElement({ state, commit, dispatch }, element) {
-    // 解答题
-    if (element.type === "EXPLAIN") {
-      const positionInfos = fetchAllRelateParentElementPositionInfos(
-        element,
-        state.pages
-      );
+    if (element.type === "COMPOSITION") {
+      const positionInfos = fetchElementPositionInfos(element, state.pages);
       if (positionInfos.length) {
-        // 删除所有解答题
-        positionInfos.reverse().forEach(pos => {
-          const elems =
-            state.pages[pos._pageNo].columns[pos._columnNo].elements;
-          elems.splice(pos._elementNo, 1);
-        });
-        // 创建新的解答题元素
-        const newElements = getExplainChildren(element);
-        const pos = positionInfos.pop();
-        newElements.forEach((newElement, index) => {
-          state.pages[pos._pageNo].columns[pos._columnNo].elements.splice(
-            pos._elementNo + index,
-            0,
-            newElement
-          );
-        });
+        const pos = positionInfos[0];
+        const elements =
+          state.pages[pos._pageNo].columns[pos._columnNo].elements;
+        elements.splice(pos._elementNo, 1, element);
       } else {
         dispatch("addElement", element);
       }
-    } else if (element.type === "FILL_QUESTION") {
+    } else {
+      // 非作文题都是拆分题,即同一个题拆分成多个小题展示
       const positionInfos = fetchAllRelateParentElementPositionInfos(
         element,
         state.pages
       );
       if (positionInfos.length) {
-        // 删除所有相关选择
+        // 删除所有解答题
         positionInfos.reverse().forEach(pos => {
           const elems =
             state.pages[pos._pageNo].columns[pos._columnNo].elements;
           elems.splice(pos._elementNo, 1);
         });
-        // 创建新的选择题元素
-        const newElements = getFillQuesitons(
-          element,
-          state.cardConfig.columnNumber
-        );
+        // 创建新的解答题元素
+        const newElements = createFunc[element.type](element);
         const pos = positionInfos.pop();
         newElements.forEach((newElement, index) => {
           state.pages[pos._pageNo].columns[pos._columnNo].elements.splice(
@@ -208,16 +205,6 @@ const actions = {
       } else {
         dispatch("addElement", element);
       }
-    } else {
-      const positionInfos = fetchElementPositionInfos(element, state.pages);
-      if (positionInfos.length) {
-        const pos = positionInfos[0];
-        const elements =
-          state.pages[pos._pageNo].columns[pos._columnNo].elements;
-        elements.splice(pos._elementNo, 1, element);
-      } else {
-        dispatch("addElement", element);
-      }
     }
     commit("setCurElement", element);
   },
@@ -233,14 +220,7 @@ const actions = {
     }
 
     const elements = state.pages[pos._pageNo].columns[pos._columnNo].elements;
-    let preElements = [];
-    if (element.type === "EXPLAIN") {
-      preElements = getExplainChildren(element);
-    } else if (element.type === "FILL_QUESTION") {
-      preElements = getFillQuesitons(element, state.cardConfig.columnNumber);
-    } else {
-      preElements = [element];
-    }
+    let preElements = createFunc[element.type](element);
     preElements.forEach(preElement => {
       elements.push(preElement);
     });
@@ -251,11 +231,16 @@ const actions = {
     state.pages[0].columns[0].elements.splice(0, 1, element);
   },
   removeElement({ state, commit }, element) {
-    // 解答题时,删除所有小题。
-    if (
-      element.type === "EXPLAIN_CHILDREN" ||
-      element.type === "FILL_QUESTION"
-    ) {
+    if (element.type === "COMPOSITION") {
+      const positionInfos = fetchElementPositionInfos(element, state.pages);
+      if (!positionInfos.length) return;
+      positionInfos.forEach(pos => {
+        const elements =
+          state.pages[pos._pageNo].columns[pos._columnNo].elements;
+        elements.splice(pos._elementNo, 1);
+      });
+    } else {
+      // 非作文题,删除所有小题。
       const positionInfos = fetchAllRelateParentElementPositionInfos(
         element.parent,
         state.pages
@@ -267,15 +252,9 @@ const actions = {
           elems.splice(pos._elementNo, 1);
         });
       }
-    } else {
-      const positionInfos = fetchElementPositionInfos(element, state.pages);
-      if (!positionInfos.length) return;
-      positionInfos.forEach(pos => {
-        const elements =
-          state.pages[pos._pageNo].columns[pos._columnNo].elements;
-        elements.splice(pos._elementNo, 1);
-      });
     }
+    // 删除大题题号
+    state.topicNos.splice(state.topicNos.indexOf(element.topicNo), 1);
 
     commit("setCurElement", {});
   },
@@ -350,6 +329,8 @@ const actions = {
           if (elementDom) {
             element.h = elementDom.offsetHeight;
             element.w = elementDom.offsetWidth;
+            element.isCovered =
+              elementDom.offsetHeight < elementDom.firstChild.offsetHeight;
           }
           // 过滤掉所有topic-head元素,这个元素是动态加的,页面重排时可能会添加重复元件。
           if (element.sign && element.type !== "TOPIC_HEAD") {

+ 25 - 1
src/modules/card/views/CardDesign.vue

@@ -299,7 +299,13 @@ export default {
     };
   },
   computed: {
-    ...mapState("card", ["cardConfig", "pages", "curElement", "curPageNo"]),
+    ...mapState("card", [
+      "cardConfig",
+      "pages",
+      "curElement",
+      "curPageNo",
+      "topicNos"
+    ]),
     isEdit() {
       return !!this.cardId;
     }
@@ -460,6 +466,19 @@ export default {
         };
       return data;
     },
+    checkElementCovered() {
+      let elements = [];
+      this.pages.forEach(page => {
+        page.columns.forEach(column => {
+          column.elements.forEach(element => {
+            if (element.isCovered) {
+              elements.push(element.id);
+            }
+          });
+        });
+      });
+      return elements.length;
+    },
     async save() {
       if (!this.cardConfig.cardName) {
         this.$message.error("题卡标题不能为空!");
@@ -469,6 +488,11 @@ export default {
         });
         return false;
       }
+      if (this.checkElementCovered()) {
+        this.$message.error("题卡中存在被遮挡的元件,请注意调整!");
+        return false;
+      }
+
       const result = await saveCard(this.getCardData());
       this.cardDetailId = result.cardDetailId;
       this.cardId = result.cardId;

+ 4 - 0
src/modules/exam-center/components/CardOptionDialog.vue

@@ -213,6 +213,10 @@ export default {
       this.cancel();
     },
     toPreview() {
+      if (!this.modalForm.refCardId) {
+        this.$message.error("请先选择题卡!");
+        return;
+      }
       window.open(
         this.getRouterPath({
           name: "CardPreview",

+ 3 - 3
src/plugins/formRules.js

@@ -57,13 +57,13 @@ const smscode = [
   }
 ];
 
-const numberValidator = ({ prop, min = 3, max = 20 }) => {
+const numberValidator = ({ prop, min = 1, max = 100 }) => {
   return [
     {
       required: true,
       validator: (rule, value, callback) => {
         if (!value && value !== 0) {
-          return callback(new Error(`${prop}不能为空`));
+          return callback(new Error(`请输入${prop}`));
         }
 
         if (!Number.isInteger(value)) {
@@ -76,7 +76,7 @@ const numberValidator = ({ prop, min = 3, max = 20 }) => {
           }
         }
       },
-      trigger: "blur"
+      trigger: "change"
     }
   ];
 };