Browse Source

试卷预览自动分页逻辑调整

zhangjie 3 years ago
parent
commit
d54a6d69fd

+ 179 - 94
src/features/examwork/StudentExamDetail/PreviewPaperDialog.vue

@@ -46,7 +46,8 @@ import { numberToChinese, numberToUpperCase } from "./spins/renderJSON";
 import ContItem from "./spins/ContItem.vue";
 import previewTem from "./spins/previewTem";
 import { randomCode } from "@/utils/utils";
-import { STRUCT_TYPES } from "./spins/paperSetting";
+import { STRUCT_TYPES, MAX_WIDTH } from "./spins/paperSetting";
+const OPTION_NAME = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
 export default {
   name: "preview-paper-dialog",
@@ -205,7 +206,35 @@ export default {
 
       this.paperStruct = studentPaperStructJson;
     },
-    // transformRichJson(richJson) {},
+    transformRichJson(richJson) {
+      let contents = [];
+      let curBlock = [];
+      const checkNeedSplitSection = (block) => {
+        if (block.type !== "image") return false;
+
+        if (block.param) {
+          if (block.param.width) return block.param.width > MAX_WIDTH / 2;
+          if (block.param.height) return block.param.height > 150;
+        }
+        return true;
+      };
+
+      richJson.sections.forEach((section) => {
+        section.blocks.forEach((block) => {
+          if (checkNeedSplitSection(block) && curBlock.length) {
+            contents.push({ sections: [{ blocks: [...curBlock] }] });
+            curBlock = [];
+          }
+          curBlock.push(block);
+        });
+
+        if (curBlock.length) {
+          contents.push({ sections: [{ blocks: [...curBlock] }] });
+          curBlock = [];
+        }
+      });
+      return contents;
+    },
     parseRenderStructList() {
       let renderStructList = [];
       renderStructList.push({
@@ -222,17 +251,12 @@ export default {
 
         detail.questions.forEach((question) => {
           if (question.subQuestions) {
-            if (question.body) {
-              renderStructList.push({
-                cls: "topic-title",
-                type: "json",
-                content: {
-                  isCommon: true,
-                  number: question.number,
-                  body: question.body,
-                },
-              });
-            }
+            const bodys = this.parseTopicTitle(
+              question.body,
+              `${question.number}、`
+            );
+            renderStructList.push(...bodys);
+
             question.subQuestions.forEach((sq) => {
               const contents = this.parseSimpleQuestion(sq, false);
               renderStructList.push(...contents);
@@ -252,84 +276,37 @@ export default {
     },
     parseSimpleQuestion(question, isCommon) {
       let contents = [];
-      contents.push({
-        cls: "topic-title",
-        type: "json",
-        content: {
-          isSub: !isCommon,
-          number: question.number,
-          body: question.body,
-        },
-      });
+
+      const tbodys = this.parseTopicTitle(
+        question.body,
+        isCommon ? `${question.number}、` : `${question.number})`
+      );
+      contents.push(...tbodys);
 
       if (question.options && question.options.length) {
         question.options.forEach((op) => {
-          contents.push({
-            cls: "topic-option",
-            type: "json",
-            content: {
-              isSub: false,
-              number: numberToUpperCase(op.number),
-              body: op.body,
-            },
-          });
+          const obodys = this.parseTopicOption(op.body, op.number);
+          contents.push(...obodys);
         });
       }
 
-      // if (question.structType === STRUCT_TYPES.BOOLEAN_CHOICE) {
-      //   console.log(question.answer, question.studentAnswer);
-      // }
-
       if (question.answer !== null) {
         // console.log(question.answer);
-        contents.push({
-          cls: "topic-answer",
-          type: "json",
-          content: {
-            answerType: "standard",
-            structType: question.structType,
-            body: question.answer,
-          },
-        });
+        const conts = this.parseTopicAnswer(
+          question.answer,
+          question.structType,
+          false
+        );
+        contents.push(...conts);
       }
 
       if (question.studentAnswer !== null) {
-        // console.log(question.studentAnswer);
-        // 简答题的特殊处理,解决上传的大图无法分页问题。文字内容不受影响。
-        if (question.structType === STRUCT_TYPES.TEXT) {
-          // console.log(question.studentAnswer);
-          let aindex = 0;
-          question.studentAnswer.forEach((answer) => {
-            answer.sections.forEach((section) => {
-              contents.push({
-                cls: "topic-answer std-answer",
-                type: "json",
-                content: {
-                  answerType: "student",
-                  structType: question.structType,
-                  hideTitle: aindex !== 0,
-                  body: [
-                    {
-                      sections: [section],
-                    },
-                  ],
-                },
-              });
-              aindex++;
-            });
-          });
-        } else {
-          // console.log(question.studentAnswer);
-          contents.push({
-            cls: "topic-answer std-answer",
-            type: "json",
-            content: {
-              answerType: "student",
-              structType: question.structType,
-              body: question.studentAnswer,
-            },
-          });
-        }
+        const conts = this.parseTopicAnswer(
+          question.studentAnswer,
+          question.structType,
+          true
+        );
+        contents.push(...conts);
       }
 
       contents.push({
@@ -340,6 +317,127 @@ export default {
 
       return contents;
     },
+    parseTopicTitle(richJson, numberVal) {
+      if (!richJson) {
+        return {
+          cls: "topic-title",
+          type: "json",
+          content: {
+            sections: [
+              {
+                blocks: [
+                  {
+                    type: "text",
+                    value: numberVal,
+                    param: { bold: true },
+                  },
+                ],
+              },
+            ],
+          },
+        };
+      }
+
+      const bodys = this.transformRichJson(richJson);
+
+      return bodys.map((body, index) => {
+        if (index === 0) {
+          body.sections[0].blocks.unshift({
+            type: "text",
+            value: numberVal,
+            param: { bold: true },
+          });
+        }
+        return {
+          cls: "topic-title",
+          type: "json",
+          content: body,
+        };
+      });
+    },
+    parseTopicOption(richJson, number) {
+      const bodys = this.transformRichJson(richJson);
+
+      return bodys.map((body, index) => {
+        if (index === 0) {
+          body.sections[0].blocks.unshift({
+            type: "text",
+            value: `${numberToUpperCase(number)}、`,
+          });
+        }
+        return {
+          cls: "topic-option",
+          type: "json",
+          content: body,
+        };
+      });
+    },
+    parseTopicAnswer(answer, structType, isStdAns) {
+      // console.log(answer, structType, isStdAns);
+      const answerTitleBlock = {
+        type: "text",
+        value: isStdAns ? "学生答案:" : "标准答案:",
+        param: {
+          bold: true,
+        },
+      };
+      let contents = [];
+      if (
+        structType === STRUCT_TYPES.FILL_BLANK ||
+        structType === STRUCT_TYPES.TEXT
+      ) {
+        contents.push({
+          cls: "topic-answer",
+          type: "json",
+          content: {
+            sections: [
+              {
+                blocks: [{ ...answerTitleBlock }],
+              },
+            ],
+          },
+        });
+        answer.forEach((item) => {
+          const bodys = this.transformRichJson(item);
+          const conts = bodys.map((body, index) => {
+            if (index === 0) {
+              // body.sections[0].blocks.unshift({
+              //   type: "text",
+              //   value: "",
+              // });
+            }
+            return {
+              cls: "topic-answer",
+              type: "json",
+              content: body,
+            };
+          });
+          contents.push(...conts);
+        });
+      } else {
+        let ansCont = "";
+        if (structType === STRUCT_TYPES.BOOLEAN_CHOICE) {
+          ansCont = answer ? "对" : "错";
+        } else {
+          ansCont = answer.map((item) => OPTION_NAME[item - 1]).join("");
+        }
+        contents.push({
+          cls: "topic-answer",
+          type: "json",
+          content: {
+            sections: [
+              {
+                blocks: [
+                  { ...answerTitleBlock },
+                  { type: "text", value: ansCont },
+                ],
+              },
+            ],
+          },
+        });
+      }
+      return contents;
+    },
     loadImg(url) {
       return new Promise((resolve, reject) => {
         const img = new Image();
@@ -368,20 +466,7 @@ export default {
       let imgUrls = [];
       this.renderStructList.forEach((item) => {
         if (item.type === "text") return;
-        if (item.cls === "topic-title" || item.cls === "topic-option") {
-          imgUrls.push(...this.getRichJsonImgUrls(item.content.body));
-          return;
-        }
-        // 简答题才可能会有图片
-        if (
-          item.cls.indexOf("answer") &&
-          item.content.structType === STRUCT_TYPES.TEXT &&
-          item.content.body
-        ) {
-          item.content.body.forEach((body) => {
-            imgUrls.push(...this.getRichJsonImgUrls(body));
-          });
-        }
+        imgUrls.push(...this.getRichJsonImgUrls(item.content));
       });
 
       // console.log(imgUrls);

+ 4 - 14
src/features/examwork/StudentExamDetail/spins/ContItem.vue

@@ -1,21 +1,16 @@
 <template>
   <div :class="clses" :id="data.id">
-    <template v-if="data.type === 'text'">
-      <p>{{ data.content }}</p>
-    </template>
-    <template v-else>
-      <component :is="getTypeComp" :data="data.content"></component>
-    </template>
+    <p v-if="data.type === 'text'">{{ data.content }}</p>
+    <rich-text v-else :text-json="data.content"></rich-text>
   </div>
 </template>
 
 <script>
-import TopicAnswer from "./TopicAnswer.vue";
-import TopicTitle from "./TopicTitle.vue";
+import RichText from "./RichText.vue";
 
 export default {
   name: "cont-item",
-  components: { TopicAnswer, TopicTitle },
+  components: { RichText },
   props: {
     data: {
       type: Object,
@@ -28,11 +23,6 @@ export default {
     clses() {
       return ["cont-item", `cont-${this.data.cls}`];
     },
-    getTypeComp() {
-      return this.data.cls.indexOf("topic-answer") !== -1
-        ? "topic-answer"
-        : "topic-title";
-    },
   },
   data() {
     return {};

+ 0 - 69
src/features/examwork/StudentExamDetail/spins/TopicAnswer.vue

@@ -1,69 +0,0 @@
-<template>
-  <div class="cont-item-detail">
-    <h4 v-if="!data.hideTitle" class="cont-part-title">
-      {{ data.answerType === "student" ? "学生答案" : "标准答案" }}
-    </h4>
-    <h4 v-else class="cont-part-title"></h4>
-    <div
-      v-if="data.structType === STRUCT_TYPES.BOOLEAN_CHOICE"
-      class="cont-part-body"
-    >
-      {{ data.body ? "对" : "错" }}
-    </div>
-    <div
-      v-else-if="data.structType === STRUCT_TYPES.FILL_BLANK"
-      class="cont-part-body"
-    >
-      <p v-for="(cont, cindex) in data.body" :key="cindex">
-        <rich-text :text-json="cont"></rich-text>
-      </p>
-    </div>
-    <div
-      v-else-if="data.structType === STRUCT_TYPES.TEXT"
-      class="cont-part-body"
-    >
-      <p v-for="(cont, cindex) in data.body" :key="cindex">
-        <rich-text :text-json="cont"></rich-text>
-      </p>
-    </div>
-    <div v-else class="cont-part-body">
-      {{ answer }}
-    </div>
-  </div>
-</template>
-
-<script>
-import RichText from "./RichText.vue";
-import { STRUCT_TYPES } from "./paperSetting";
-
-export default {
-  name: "topic-answer",
-  components: { RichText },
-  props: {
-    data: {
-      type: Object,
-      default() {
-        return {};
-      },
-    },
-  },
-  data() {
-    return {
-      STRUCT_TYPES,
-      optionNames: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
-    };
-  },
-  computed: {
-    answer() {
-      const SELECT_TYPES = [
-        this.STRUCT_TYPES.SINGLE_CHOICE,
-        this.STRUCT_TYPES.MULTIPLE_CHOICE,
-      ];
-      return SELECT_TYPES.includes(this.data.structType)
-        ? this.data.body.map((item) => this.optionNames[item - 1]).join("")
-        : "";
-    },
-  },
-  methods: {},
-};
-</script>

+ 0 - 29
src/features/examwork/StudentExamDetail/spins/TopicTitle.vue

@@ -1,29 +0,0 @@
-<template>
-  <div class="cont-item-detail">
-    <h4 v-if="data.isSub" class="cont-part-title">{{ data.number }})</h4>
-    <h4 v-else class="cont-part-title">{{ data.number }}、</h4>
-    <div class="cont-part-body">
-      <rich-text :text-json="data.body"></rich-text>
-    </div>
-  </div>
-</template>
-
-<script>
-import RichText from "./RichText.vue";
-
-export default {
-  name: "topic-title",
-  components: { RichText },
-  props: {
-    data: {
-      type: Object,
-      default() {
-        return {};
-      },
-    },
-  },
-  data() {
-    return {};
-  },
-};
-</script>

+ 3 - 0
src/features/examwork/StudentExamDetail/spins/paperSetting.js

@@ -8,3 +8,6 @@ export const STRUCT_TYPES = {
   LISTENING: 7,
   MATCHES: 8,
 };
+
+export const MAX_WIDTH = 714;
+export const MAX_HEIGHT = 1042;

+ 1 - 1
src/features/examwork/StudentExamDetail/spins/previewTem.js

@@ -1,6 +1,6 @@
 const resetCss = `a,body,div,footer,h1,h2,h3,h4,h5,h6,header,input,li,ol,p,span,td,th,tr,ul{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:rgba(255,255,255,0)}li{list-style:none}em,i,u{font-style:normal}button{font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",Arial,sans-serif}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:700}fieldset,img{border:0}abbr{border:0;font-variant:normal}a{text-decoration:none;color:inherit}img{vertical-align:middle}body{font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:14px;color:#545454;background:#f5f5f5;min-width:1366px}`;
 
-const previewCss = `.paper-content{width:210mm;height:297mm;padding:40px;margin:0 auto;overflow:hidden;}.cont-item{page-break-after:always;}.cont-item p{margin:0;}.cont-paper-name{font-size:20px;padding-bottom:30px;text-align:center;color:#333;}.cont-detail-name{font-size:16px;font-weight:600;padding:15px 0;color:#333;}.cont-topic-title{padding-bottom:5px;}.cont-item-detail{min-height:20px;}.cont-part-title{font-size:14px;line-height:20px;float:left;margin:0;}.cont-part-body{min-height:20px;}.cont-part-body img{max-width:100%;max-height:1042px;}.cont-topic-answer .cont-part-title{width:72px;font-weight:600;color:#333;}.cont-topic-answer .cont-part-body{margin-left:72px;}.cont-topic-answer + .cont-topic-title{padding-top:15px;}.cont-topic-score{color: #f56c6c;padding-bottom: 5px;}`;
+const previewCss = `.paper-content{width:210mm;height:297mm;padding:40px;margin:0 auto;overflow:hidden;page-break-after:always;}.cont-item{min-height:20px;}.cont-item p{margin:0;}.cont-paper-name{font-size:20px;padding-bottom:30px;text-align:center;color:#333;}.cont-detail-name{font-size:16px;font-weight:600;padding:15px 0 5px;color:#333;}.cont-item-detail{min-height:20px;}.rich-text img{max-width:100%;max-height:1042px;height:auto !important;}.cont-topic-score{color:#f56c6c;padding-bottom:5px;}.cont-topic-score + .cont-topic-title{padding-top:10px;}`;
 
 export default (domeStr) => {
   return `

+ 10 - 27
src/styles/paper-preview.css

@@ -4,10 +4,11 @@
   padding: 40px;
   margin: 0 auto;
   overflow: hidden;
+  page-break-after: always;
 }
 
 .cont-item {
-  page-break-after: always;
+  min-height: 20px;
 }
 .cont-item p {
   margin: 0;
@@ -21,42 +22,24 @@
 .cont-detail-name {
   font-size: 16px;
   font-weight: 600;
-  padding: 15px 0;
+  padding: 15px 0 5px;
   color: #333;
 }
-.cont-topic-title {
-  padding-bottom: 5px;
-}
+
 .cont-item-detail {
   min-height: 20px;
 }
-.cont-part-title {
-  font-size: 14px;
-  line-height: 20px;
-  float: left;
-  margin: 0;
-}
-.cont-part-body {
-  min-height: 20px;
-}
-.cont-part-body img {
+
+.rich-text img {
   max-width: 100%;
   max-height: 1042px;
+  height: auto !important;
 }
 
-.cont-topic-answer .cont-part-title {
-  width: 72px;
-  font-weight: 600;
-  color: #333;
-}
-.cont-topic-answer .cont-part-body {
-  margin-left: 72px;
-}
-
-.cont-topic-answer + .cont-topic-title {
-  padding-top: 15px;
-}
 .cont-topic-score {
   color: #f56c6c;
   padding-bottom: 5px;
 }
+.cont-topic-score + .cont-topic-title {
+  padding-top: 10px;
+}