zhangjie 2 rokov pred
rodič
commit
6daf3b70a4

+ 0 - 7
src/modules/card/assets/styles/card-preview.scss

@@ -901,13 +901,6 @@
         margin: 0 2px;
       }
     }
-
-    &::after {
-      content: "";
-      display: inline-block;
-      width: 100%;
-      height: 0;
-    }
   }
   &.is-side {
     -webkit-transform: rotate(-90deg);

+ 67 - 0
src/modules/paper-export/assets/styles/paper-temp-preview.scss

@@ -89,6 +89,47 @@
   }
 }
 
+// elem-field-text
+.elem-field-text {
+  .text-body {
+    width: 100%;
+    position: relative;
+    height: 26px;
+    line-height: 26px;
+    overflow: hidden;
+
+    > span {
+      display: inline-block;
+      vertical-align: middle;
+      position: relative;
+      z-index: 9;
+
+      &.text-label {
+        background-color: #fff;
+      }
+    }
+
+    &.underline {
+      &::before {
+        content: "";
+        display: block;
+        position: absolute;
+        width: 100%;
+        border-bottom: 1px solid #000;
+        bottom: 2px;
+        left: 0;
+        z-index: 1;
+      }
+    }
+  }
+  &.is-side {
+    -webkit-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    -webkit-transform-origin: 0 100%;
+    transform-origin: 0 100%;
+  }
+}
+
 // elem-pane-box
 .elem-pane-box {
   .elem-body {
@@ -109,6 +150,7 @@
     }
   }
 }
+// elem-rich-text
 .elem-rich-text {
   display: inline-block;
   white-space: pre-wrap;
@@ -118,6 +160,30 @@
     max-height: 100%;
     height: auto !important;
   }
+
+  &.is-detail-title {
+    overflow: hidden;
+
+    .rich-text {
+      display: inline;
+    }
+  }
+
+  .detail-score-table {
+    float: left;
+    width: 120px;
+    border-spacing: 0;
+    border-collapse: collapse;
+    margin-right: 10px;
+
+    td,
+    th {
+      padding: 5px;
+      height: 30px;
+      border: 1px solid #000;
+      font-size: 16px;
+    }
+  }
 }
 // elem-paper-struct
 .elem-paper-struct {
@@ -227,6 +293,7 @@
       line-height: 18px;
       margin-left: 5px;
       border-bottom: 1px solid #000;
+      text-align: center;
     }
   }
 }

+ 4 - 1
src/modules/paper-export/components/ElementPropEdit.vue

@@ -27,7 +27,8 @@
 
 <script>
 import { mapState, mapMutations, mapActions } from "vuex";
-import { getElementName } from "../elementModel";
+import { getElementName, DISABLE_EDIT_ELEMENT } from "../elementModel";
+
 import EditText from "../../card/elements/text/EditText";
 import EditFieldText from "../elements/field-text/EditFieldText";
 import EditImage from "../../card/elements/image/EditImage";
@@ -73,6 +74,8 @@ export default {
     },
     curEditComponent() {
       if (!this.curElement.type) return;
+      if (DISABLE_EDIT_ELEMENT.includes(this.curElement.type)) return;
+
       let type = this.curElement.type.toLowerCase().replace("_", "-");
       if (type.indexOf("line-") === 0) type = "line";
       return `edit-${type}`;

+ 109 - 0
src/modules/paper-export/components/PaperBuildConfig.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="paper-build-config">
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      size="small"
+      inline
+    >
+      <el-form-item>
+        <el-checkbox v-model="modalForm.showDetailNo"
+          >是否显示大题序号</el-checkbox
+        >
+      </el-form-item>
+      <el-form-item>
+        <el-checkbox v-model="modalForm.showDetailScoreTable"
+          >是否显示大题给分板</el-checkbox
+        >
+      </el-form-item>
+      <br />
+
+      <el-form-item
+        v-for="item in configSources"
+        :key="item.field"
+        :label="`${item.name}:`"
+        :prop="item.field"
+      >
+        <el-radio-group v-if="item.options" v-model="modalForm[item.field]">
+          <el-radio-button
+            v-for="option in item.options"
+            :key="option"
+            :label="option"
+          ></el-radio-button>
+        </el-radio-group>
+        <el-input
+          v-else
+          v-model.trim="modalForm[item.field]"
+          maxlength="5"
+          :placeholder="`请输入${item.name}`"
+        >
+          <span v-if="item.unit" slot="append">{{ item.unit }}</span>
+        </el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="confirm">确定</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "PaperBuildConfig",
+  props: {
+    configSources: {
+      type: Array,
+      default() {
+        return [];
+      },
+    },
+  },
+  data() {
+    return {
+      modalForm: {
+        showDetailNo: true,
+        showDetailScoreTable: false,
+      },
+      rules: {},
+    };
+  },
+  watch: {
+    configSources: {
+      immediate: true,
+      handler() {
+        this.initModalForm();
+      },
+    },
+  },
+  methods: {
+    initModalForm() {
+      let modalForm = { showDetailNo: true, showDetailScoreTable: false };
+      let rules = {};
+      this.configSources
+        .filter((item) => item.enable)
+        .forEach((item) => {
+          modalForm[item.field] = "";
+          rules[item.field] = {
+            required: true,
+            message: `请设置${item.name}`,
+            trigger: "change",
+          };
+        });
+      this.modalForm = modalForm;
+      this.rules = rules;
+    },
+    checkData() {
+      return this.$refs.modalFormComp.validate();
+    },
+    getData() {
+      return { ...this.modalForm };
+    },
+    async confirm() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+      this.$emit("confirm", this.getData());
+    },
+  },
+};
+</script>

+ 5 - 0
src/modules/paper-export/components/PaperSideEdit.vue

@@ -30,6 +30,11 @@ const defaultProp = {
     mode: "side",
     h: 100,
   },
+  FIELD_TEXT: {
+    mode: "side",
+    w: 30,
+    h: 200,
+  },
 };
 
 export default {

+ 2 - 1
src/modules/paper-export/components/RightClickMenu.vue

@@ -42,6 +42,7 @@ import { mapState, mapMutations, mapActions } from "vuex";
 import { deepCopy } from "../../card/plugins/utils";
 import Clickoutside from "element-ui/src/utils/clickoutside";
 import { findElementById } from "../store";
+import { DISABLE_EDIT_ELEMENT } from "../elementModel";
 
 export default {
   name: "RightClickMenu",
@@ -70,7 +71,7 @@ export default {
       );
     },
     NO_EDIT_ELEMENT() {
-      return ["SCORE_TABLE"].includes(this.curElement.type);
+      return DISABLE_EDIT_ELEMENT.includes(this.curElement.type);
     },
   },
   mounted() {

+ 3 - 0
src/modules/paper-export/elementModel.js

@@ -37,6 +37,8 @@ const EDITABLE_TOPIC = [
   "PAPER_PROPS",
 ];
 
+const DISABLE_EDIT_ELEMENT = ["SCORE_TABLE"];
+
 const ELEMENT_INFOS = {
   LINES: {
     name: "多横线",
@@ -141,4 +143,5 @@ export {
   PAGE_CONFIG,
   ELEMENT_LIST,
   TOPIC_LIST,
+  DISABLE_EDIT_ELEMENT,
 };

+ 9 - 1
src/modules/paper-export/elements/field-text/EditFieldText.vue

@@ -32,7 +32,7 @@
           <el-radio-button label="left">左</el-radio-button>
           <el-radio-button label="center">居中</el-radio-button>
           <el-radio-button label="right">右</el-radio-button>
-          <el-radio-button label="justify">两端</el-radio-button>
+          <!-- <el-radio-button label="justify">两端</el-radio-button> -->
         </el-radio-group>
       </el-form-item>
       <el-form-item prop="field" label="字段:">
@@ -49,6 +49,12 @@
           ></el-option>
         </el-select>
       </el-form-item>
+      <el-form-item>
+        <el-checkbox v-model="modalForm.showLabel">显示字段名称</el-checkbox>
+        <el-checkbox v-model="modalForm.showUnderline"
+          >显示字段内容下划线</el-checkbox
+        >
+      </el-form-item>
     </el-form>
   </div>
 </template>
@@ -69,6 +75,8 @@ const initModalForm = {
   textAlign: null,
   content: [],
   contentStr: "",
+  showLabel: false,
+  showUnderline: false,
 };
 
 export default {

+ 10 - 4
src/modules/paper-export/elements/field-text/ElemFieldText.vue

@@ -1,17 +1,23 @@
 <template>
   <div
-    :class="['elem-text', { 'is-side': data.mode === 'side' }]"
+    :class="['elem-field-text', { 'is-side': data.mode === 'side' }]"
     :style="elemStyles"
   >
-    <div class="text-body" :style="styles">
-      <div>{{ data.content || data.fieldName }}</div>
+    <div
+      :class="['text-body', { underline: data.showUnderline }]"
+      :style="styles"
+    >
+      <span v-if="data.showLabel" class="text-label"
+        >{{ data.fieldName }}:</span
+      >
+      <span>{{ data.content || data.fieldName }}</span>
     </div>
   </div>
 </template>
 
 <script>
 export default {
-  name: "ElemText",
+  name: "ElemFieldText",
   props: {
     data: {
       type: Object,

+ 7 - 1
src/modules/paper-export/elements/field-text/model.js

@@ -19,6 +19,8 @@ const MODEL = {
   mode: "normal", // side:侧边模式,normal:正常模式
   field: "",
   fieldName: "",
+  showLabel: false,
+  showUnderline: false,
   content: "$样例内容",
 };
 
@@ -36,9 +38,13 @@ const FIELD_LIST = [
     field: "paperName",
   },
   {
-    name: "科目名称",
+    name: "课程名称",
     field: "courseName",
   },
+  {
+    name: "课程代码",
+    field: "courseCode",
+  },
   {
     name: "试卷总分",
     field: "totalScore",

+ 1 - 1
src/modules/paper-export/elements/rich-text/ElemRichText.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="elem-rich-text" :style="data.styles">
+  <div :class="['elem-rich-text', data.classNames]" :style="data.styles">
     <rich-text
       v-if="data.contType !== 'gap'"
       :text-json="data.content"

+ 1 - 0
src/modules/paper-export/elements/rich-text/model.js

@@ -14,6 +14,7 @@ const MODEL = {
   contType: "content",
   styles: {},
   textStyles: {},
+  classNames: "",
   content: {
     sections: [{ blocks: [] }],
   },

+ 119 - 9
src/modules/paper-export/views/PaperTemplateBuild.vue

@@ -5,6 +5,7 @@
         <el-select
           v-model="seqMode"
           class="margin-right-10"
+          size="small"
           @change="seqModeChange"
         >
           <el-option value="MODE1" label="单题型连续"></el-option>
@@ -17,6 +18,7 @@
           class="margin-right-10"
           placeholder="请选择"
           value-key="id"
+          size="small"
           @change="paperTempChange"
         >
           <el-option
@@ -27,8 +29,15 @@
           >
           </el-option>
         </el-select>
-        <el-button type="primary" @click="toDownload">下载试卷</el-button>
+        <el-button type="primary" size="small" @click="toDownload"
+          >下载试卷</el-button
+        >
       </div>
+      <paper-build-config
+        ref="PaperBuildConfig"
+        :config-sources="configSources"
+        @confirm="buildConfigChange"
+      ></paper-build-config>
       <paper-template-view
         ref="PaperTemplateView"
         class="preview-body"
@@ -41,6 +50,7 @@
 
 <script>
 import PaperTemplateView from "../components/PaperTemplateView.vue";
+import PaperBuildConfig from "../components/PaperBuildConfig.vue";
 import { getModel as getRichTextModel } from "../elements/rich-text/model";
 import { getModel as getPageModel } from "../elements/page/model";
 import { getElementId, randomCode, deepCopy } from "../../card/plugins/utils";
@@ -69,7 +79,7 @@ const checkRichTextHasCont = function (data) {
 
 export default {
   name: "PaperTemplateBuild",
-  components: { PaperTemplateView },
+  components: { PaperTemplateView, PaperBuildConfig },
   data() {
     return {
       paperId: this.$route.params.paperId,
@@ -91,6 +101,12 @@ export default {
       paperStructs: [],
       TEXT_INDENT_SIZE: 28,
       textIndent: 28,
+      configModalForm: {
+        showDetailNo: true,
+        showDetailScoreTable: false,
+      },
+      configSources: [],
+      prepareDownloadPdf: false,
     };
   },
   mounted() {
@@ -197,7 +213,8 @@ export default {
       this.resetClozeSerialNo(this.paperJson);
       this.fieldData = {
         paperName: res.data.name,
-        courseName: `${res.data.course.name}(${res.data.course.code})`,
+        courseName: res.data.course.name,
+        courseCode: res.data.course.code,
         totalScore: res.data.totalScore,
         rootOrgName: res.data.rootOrgName,
       };
@@ -234,6 +251,44 @@ export default {
       const res = await paperTemplateListApi("PAPER_EXPORT");
       this.paperTempList = res.data;
     },
+    getConfigSources() {
+      const { pages } = this.paperTempJson;
+      let sources = [],
+        fieldAble = {};
+      pages.forEach((page) => {
+        page.columns.forEach((column) => {
+          column.elements.forEach((element) => {
+            if (element.type !== "PAPER_PROPS") return;
+            if (!sources.length) {
+              sources = deepCopy(element.props);
+            }
+            element.props.forEach((prop) => {
+              fieldAble[prop.field] = fieldAble[prop.field] || prop.enable;
+            });
+          });
+        });
+      });
+
+      sources.forEach((item) => {
+        item.enable = fieldAble[item.field];
+      });
+      this.configSources = sources;
+    },
+    buildConfigChange(val) {
+      this.configModalForm = val;
+      this.updaterFieldInfo();
+
+      this.$nextTick(() => {
+        this.buildData();
+
+        if (this.prepareDownloadPdf) {
+          this.$nextTick(async () => {
+            await this.downloadPaperPdf().catch(() => {});
+            this.prepareDownloadPdf = false;
+          });
+        }
+      });
+    },
     paperTempChange(paperTemp) {
       // console.log(paperTemp);
       this.curPaperTemp = paperTemp;
@@ -242,6 +297,7 @@ export default {
         : { pages: [], pageConfig: {} };
       this.paperTempJson = paperTempJson;
       this.pages = paperTempJson.pages;
+      this.getConfigSources();
       this.updaterFieldInfo();
 
       this.$nextTick(() => {
@@ -255,6 +311,12 @@ export default {
           column.elements.forEach((elem) => {
             if (elem.type === "PAPER_STRUCT") {
               elem.structs = this.paperStructs;
+            } else if (elem.type === "SCORE_TABLE") {
+              elem.detailCount = this.paperStructs.length;
+            } else if (elem.type === "PAPER_PROPS") {
+              elem.props.forEach((prop) => {
+                prop.value = this.configModalForm[prop.field];
+              });
             }
 
             if (!elem.elements || !elem.elements.length) return;
@@ -451,19 +513,25 @@ export default {
       return contents;
     },
     parseDetailTitle(data) {
-      let content = this.getRichStruct([
-        {
-          type: "text",
-          value: `${data.cnNum}、`,
-        },
+      let blocks = [
         {
           type: "text",
           value: `${data.name}(共${data.unitCount}小题,满分${data.score}分)`,
         },
-      ]);
+      ];
+      if (this.configModalForm.showDetailNo) {
+        blocks.unshift({
+          type: "text",
+          value: `${data.cnNum}、`,
+        });
+      }
+      let content = this.getRichStruct(blocks);
       return getRichTextModel({
         styles: { width: "100%", fontWeight: 600 },
         content,
+        classNames: this.configModalForm.showDetailScoreTable
+          ? "is-detail-title"
+          : "",
       });
     },
     parseTitleOption(
@@ -529,9 +597,27 @@ export default {
       this.pages = pages;
     },
     buildReleasePages() {
+      this.addDetailScoreTable();
       this.resetRenderStructSize();
       // console.log(this.renderStructList);
       this.buildPageAutoPage();
+
+      this.$nextTick(() => {
+        this.addDetailScoreTable();
+      });
+    },
+    addDetailScoreTable() {
+      if (!this.configModalForm.showDetailScoreTable) return;
+
+      const scoreTableHtml = `<table class="detail-score-table"><tr><th>得分</th><th>评分人</th></tr><tr><td></td><td></td></tr></table>`;
+      const dom = document.createElement("div");
+      dom.innerHTML = scoreTableHtml;
+      document.querySelectorAll(".is-detail-title").forEach((node) => {
+        const hasScoreTable =
+          node.firstChild.className.includes("detail-score-table");
+        if (hasScoreTable) return;
+        node.insertBefore(dom.firstChild.cloneNode(true), node.firstChild);
+      });
     },
     resetRenderStructSize() {
       let curOptions = [];
@@ -828,6 +914,20 @@ export default {
       return previewTemp(this.$refs.PaperTemplateView.$el.outerHTML);
     },
     async toDownload() {
+      const valid = await this.$refs.PaperBuildConfig.checkData().catch(
+        () => {}
+      );
+      if (!valid) return;
+
+      const configData = this.$refs.PaperBuildConfig.getData();
+      if (JSON.stringify(configData) === JSON.stringify(this.configModalForm)) {
+        this.downloadPaperPdf();
+      } else {
+        this.prepareDownloadPdf = true;
+        this.buildConfigChange(configData);
+      }
+    },
+    async downloadPaperPdf() {
       const htmlCont = this.getPreviewTemp();
 
       if (this.downloading) return;
@@ -864,4 +964,14 @@ export default {
   margin-top: 10px;
   margin-bottom: 10px;
 }
+.paper-template-build .paper-build-config {
+  padding: 10px 15px 2px;
+  margin-top: 5px;
+  background: #fff;
+  border-radius: 10px;
+}
+.paper-build-config .el-form-item {
+  margin-bottom: 16px;
+  margin-right: 30px;
+}
 </style>