Ver Fonte

card split

zhangjie há 3 anos atrás
pai
commit
17fd12a000
32 ficheiros alterados com 1873 adições e 1045 exclusões
  1. 35 37
      card/api.js
  2. 45 180
      card/components/CardDesign.vue
  3. 111 0
      card/components/CardView.vue
  4. 4 65
      card/modules/free/cardFormatTransform.js
  5. 47 182
      card/modules/free/components/CardFreeDesign.vue
  6. 113 0
      card/modules/free/components/CardFreeView.vue
  7. 0 225
      card/modules/free/views/CardFreePreview.vue
  8. 7 9
      card/router/index.js
  9. 229 0
      card/views/CardEdit.vue
  10. 230 0
      card/views/CardFreeEdit.vue
  11. 131 0
      card/views/CardFreePreview.vue
  12. 15 86
      card/views/CardPreview.vue
  13. 0 229
      card/views/CardRulePreview.vue
  14. 3 3
      card/views/Home.vue
  15. 1 1
      src/modules/base/components/ModifyCardInfo.vue
  16. 1 1
      src/modules/base/views/CardManage.vue
  17. 11 2
      src/modules/card/api.js
  18. 9 15
      src/modules/card/router.js
  19. 229 0
      src/modules/card/views/CardEdit.vue
  20. 230 0
      src/modules/card/views/CardFreeEdit.vue
  21. 131 0
      src/modules/card/views/CardFreePreview.vue
  22. 170 0
      src/modules/card/views/CardPreview.vue
  23. 111 0
      src/modules/card/views/CardRulePreview.vue
  24. 2 2
      src/modules/customer/views/CustomerCard.vue
  25. 1 1
      src/modules/exam/components/ApplyContent.vue
  26. 1 1
      src/modules/exam/components/CardOptionDialog.vue
  27. 1 1
      src/modules/exam/components/CreateTaskApply.vue
  28. 1 1
      src/modules/exam/components/ModifyTaskPaper.vue
  29. 1 1
      src/modules/exam/components/WaitTaskFlow.vue
  30. 1 1
      src/modules/exam/views/TaskApplyManage.vue
  31. 1 1
      src/modules/exam/views/TaskPaperManage.vue
  32. 1 1
      src/modules/exam/views/WaitTask.vue

+ 35 - 37
card/api.js

@@ -1,42 +1,40 @@
-import { $postParam, $post } from "@/plugins/axios";
+import { randomCode } from "./plugins/utils";
+import Vue from "vue";
 
-export const cardConfigInfos = id => {
-  return $postParam("/api/admin/basic/card_rule/get_one", { id });
-  // return Promise.resolve({
-  //   id: "173438690998091776",
-  //   createId: "173437828976345088",
-  //   createTime: 1632291806278,
-  //   updateId: null,
-  //   updateTime: 1632291806278,
-  //   schoolId: "2",
-  //   orgId: "173436480729907200",
-  //   name: "测试题卡规则1",
-  //   examNumberStyle: "PRINT",
-  //   paperType: "PRINT",
-  //   examAbsent: true,
-  //   writeSign: true,
-  //   requiredFields:
-  //     '[{"code":"ticketNumber","name":"考号","enable":true,"selected":false},{"code":"siteNumber","name":"座位号","enable":true,"selected":false},{"code":"studentName","name":"姓名","enable":true,"selected":false},{"code":"courseName","name":"课程名称","enable":true,"selected":false}]',
-  //   extendFields: "[]",
-  //   // extendFields:
-  //   //   '[{"code":"studentCode","name":"学号","enable":true,"selected":false},{"code":"courseCode","name":"课程代码","enable":true,"selected":false},{"code":"paperNumber","name":"试卷编号","enable":true,"selected":false},{"code":"campusName","name":"校区","enable":true,"selected":false},{"code":"examPlace","name":"考点","enable":true,"selected":false},{"code":"examRoom","name":"考场","enable":true,"selected":false},{"code":"examDate","name":"考试日期","enable":true,"selected":false},{"code":"examTime","name":"考试时间","enable":true,"selected":false}]',
-  //   titleRule: "测试题卡规则1",
-  //   attention: "测试题卡规则1",
-  //   objectiveAttention: "测试题卡规则1",
-  //   subjectiveAttention: "测试题卡规则1",
-  //   enable: true,
-  //   remark: "测试题卡规则1",
-  //   orgIds: null
-  // });
+export const cardConfigInfos = () => {
+  return Promise.resolve({
+    id: "173438690998091776",
+    createId: "173437828976345088",
+    createTime: 1632291806278,
+    updateId: null,
+    updateTime: 1632291806278,
+    schoolId: "2",
+    orgId: "173436480729907200",
+    name: "测试题卡规则1",
+    examNumberStyle: "PRINT",
+    paperType: "PRINT",
+    examAbsent: true,
+    writeSign: true,
+    requiredFields:
+      '[{"code":"ticketNumber","name":"考号","enable":true,"selected":false},{"code":"siteNumber","name":"座位号","enable":true,"selected":false},{"code":"studentName","name":"姓名","enable":true,"selected":false},{"code":"courseName","name":"课程名称","enable":true,"selected":false}]',
+    extendFields: "[]",
+    // extendFields:
+    //   '[{"code":"studentCode","name":"学号","enable":true,"selected":false},{"code":"courseCode","name":"课程代码","enable":true,"selected":false},{"code":"paperNumber","name":"试卷编号","enable":true,"selected":false},{"code":"campusName","name":"校区","enable":true,"selected":false},{"code":"examPlace","name":"考点","enable":true,"selected":false},{"code":"examRoom","name":"考场","enable":true,"selected":false},{"code":"examDate","name":"考试日期","enable":true,"selected":false},{"code":"examTime","name":"考试时间","enable":true,"selected":false}]',
+    titleRule: "测试题卡规则1",
+    attention: "测试题卡规则1",
+    objectiveAttention: "测试题卡规则1",
+    subjectiveAttention: "测试题卡规则1",
+    enable: true,
+    remark: "测试题卡规则1",
+    orgIds: null
+  });
 };
-export const cardDetail = cardId => {
-  return $postParam("/api/admin/exam/card/get_one", { cardId });
-  // const cardData = Vue.ls.get("cardData", {});
-  // return Promise.resolve(cardData);
+export const cardDetail = () => {
+  const cardData = Vue.ls.get("cardData", {});
+  return Promise.resolve(cardData);
 };
 
-export const saveCard = (datas, config = {}) => {
-  return $post("/api/admin/exam/card/save", datas, config);
-  // Vue.ls.set("cardData", datas);
-  // return Promise.resolve(randomCode());
+export const saveCard = datas => {
+  Vue.ls.set("cardData", datas);
+  return Promise.resolve(randomCode());
 };

+ 45 - 180
card/views/CardDesign.vue → card/components/CardDesign.vue

@@ -228,10 +228,6 @@
     <element-prop-edit ref="ElementPropEdit"></element-prop-edit>
     <!-- right-click-menu -->
     <right-click-menu @inset-topic="insetNewTopic"></right-click-menu>
-    <!-- card-view-frame -->
-    <div class="design-preview-frame" v-if="cardPreviewUrl">
-      <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
-    </div>
     <!-- paper-params -->
     <paper-params
       :pages="pages"
@@ -250,7 +246,6 @@
 
 <script>
 import { mapState, mapMutations, mapActions } from "vuex";
-import { cardConfigInfos, cardDetail, saveCard } from "../api";
 import {
   getElementModel,
   getCardHeadModel,
@@ -271,6 +266,18 @@ import TopicSelectDialog from "../components/TopicSelectDialog";
 
 export default {
   name: "card-design",
+  props: {
+    content: {
+      type: Object,
+      default() {
+        return {
+          pages: [],
+          cardConfig: {},
+          paperParams: {}
+        };
+      }
+    }
+  },
   components: {
     // CardConfigPropEdit,
     TopicElementEdit,
@@ -285,20 +292,11 @@ export default {
   },
   data() {
     return {
-      cardId: this.$route.params.cardId || this.$ls.get("cardId"),
-      prepareTcPCard: this.$ls.get("prepareTcPCard", {
-        examTaskId: "",
-        courseCode: "",
-        courseName: "",
-        makeMethod: "SELF",
-        cardRuleId: ""
-      }),
       ELEMENT_LIST,
       TOPIC_LIST,
       topicList: [],
       steps: ["添加标题", "基本设置", "试题配置", "预览生成"],
       columnWidth: 0,
-      cardPreviewUrl: "",
       isSubmit: false,
       canSave: false
     };
@@ -313,9 +311,6 @@ export default {
       "curPage",
       "curPageNo"
     ]),
-    isEdit() {
-      return !!this.cardId;
-    },
     cardHeadSampleData() {
       if (!this.cardConfig["pageSize"]) return;
       const data = getCardHeadModel(this.cardConfig);
@@ -324,12 +319,7 @@ export default {
     }
   },
   mounted() {
-    if (!this.prepareTcPCard.examTaskId && !this.isEdit) {
-      this.$message.error("找不到命题任务,请退出题卡制作!");
-      return;
-    }
     this.initCard();
-    this.registWindowSubmit();
   },
   methods: {
     ...mapMutations("card", [
@@ -354,47 +344,19 @@ export default {
       "initTopicsFromPages"
     ]),
     async initCard() {
-      if (this.isEdit) {
-        await this.getCardTempDetail();
-      } else {
-        await this.getCardConfig();
-        this.initPageData();
-      }
-      this.addWatch();
-    },
-    getCardTitle(titleRule) {
-      const fieldMap = {
-        courseCode: this.prepareTcPCard.courseCode,
-        courseName: this.prepareTcPCard.courseName,
-        schoolName: this.prepareTcPCard.schoolName
-      };
-      Object.entries(fieldMap).forEach(([key, val]) => {
-        titleRule = titleRule.replace("${" + key + "}", val);
-      });
-      return titleRule;
-    },
-    async getCardTempDetail() {
-      const detData = await cardDetail(this.cardId);
-      // this.canSave = !detData.operateStatus;
+      const { cardConfig, pages, paperParams } = this.content;
+      this.setCardConfig(cardConfig);
+      this.setPaperParams(paperParams);
 
-      // 可能存在题卡内容没有记录的情况
-      if (detData.content) {
-        const cont = JSON.parse(detData.content);
-        this.setPages(cont.pages);
-        this.setCardConfig(cont.cardConfig);
-        this.setPaperParams(cont.paperParams);
+      if (pages && pages.length) {
+        this.setPages(pages);
         this.initTopicsFromPages();
         this.resetTopicSeries();
         this.setCurPage(0);
       } else {
-        await this.getCardConfig();
-        // 没有题卡内容时,直接创建新的内容
-        if (detData.makeMethod === "CUST") {
-          this.setCardConfig({ cardTitle: detData.title });
-        }
-
         this.initPageData();
       }
+      this.addWatch();
     },
     initPageData() {
       this.modifyCardHead({
@@ -405,29 +367,6 @@ export default {
         this.setCurPage(0);
       });
     },
-    async getCardConfig() {
-      const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
-      if (!data) {
-        this.$message.error("找不到题卡规则!");
-        return;
-      }
-      let config = {
-        ...data,
-        ...{
-          pageSize: "A3",
-          columnNumber: 2,
-          columnGap: 20,
-          showForbidArea: true,
-          cardDesc: "",
-          makeMethod: this.prepareTcPCard.makeMethod
-        }
-      };
-      config.aOrB = true; // 默认开启A/B卷型
-      config.requiredFields = JSON.parse(config.requiredFields);
-      config.extendFields = JSON.parse(config.extendFields);
-      config.cardTitle = this.getCardTitle(config.titleRule);
-      this.setCardConfig(config);
-    },
     addNewTopic(item) {
       let element = getElementModel(item.type);
       element.w = document.getElementById("topic-column").offsetWidth;
@@ -461,22 +400,6 @@ export default {
         });
       });
     },
-    // 操作
-    async toPreview() {
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      const result = await this.save().catch(() => {});
-      this.isSubmit = false;
-      if (!result) return;
-      const { href } = this.$router.resolve({
-        name: "CardPreview",
-        params: {
-          cardId: this.cardId,
-          viewType: "view"
-        }
-      });
-      window.open(href);
-    },
     swithPage(pindex) {
       if (this.curPageNo === pindex) return;
       this.setCurPage(pindex);
@@ -491,25 +414,13 @@ export default {
     },
     // save
     getCardData(htmlContent = "", model = "") {
-      let data = {
+      const data = {
         title: this.cardConfig.cardTitle,
         content: model,
-        htmlContent,
-        type: "CUSTOM",
-        ...this.prepareTcPCard
+        htmlContent
       };
-      if (this.cardId) data.id = this.cardId;
       return data;
     },
-    getRequestConfig() {
-      return this.prepareTcPCard.makeMethod === "CUST"
-        ? {
-            headers: {
-              schoolId: this.prepareTcPCard.schoolId
-            }
-          }
-        : {};
-    },
     checkElementCovered() {
       let elements = [];
       this.pages.forEach(page => {
@@ -547,9 +458,9 @@ export default {
 
       return true;
     },
-    getModel() {
+    getCardJson() {
       // 防止页面未渲染完成,各试题高度未及时更新,保存数据有误的问题
-      return new Promise((resolve, reject) => {
+      return new Promise(resolve => {
         setTimeout(() => {
           const data = JSON.stringify(
             {
@@ -564,90 +475,44 @@ export default {
         }, 100);
       });
     },
-    async save() {
-      if (!this.checkCardValid()) return;
-      const model = await this.getModel();
-      let datas = this.getCardData("", model);
-      datas.status = "STAGE";
-      const result = await saveCard(datas, this.getRequestConfig());
-      this.cardId = result;
-      this.$ls.set("cardId", this.cardId);
-      return true;
+    async toPreview() {
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const model = await this.getCardJson();
+      const datas = this.getCardData("", model);
+      this.$emit("on-preview", datas);
     },
     async toSave() {
+      if (!this.checkCardValid()) return;
+
       if (this.isSubmit) return;
       this.isSubmit = true;
-      const result = await this.save().catch(() => {});
-      this.isSubmit = false;
-      if (result) this.$message.success("保存成功!");
+      const model = await this.getCardJson();
+      const datas = this.getCardData("", model);
+      this.$emit("on-save", datas);
     },
     toSubmit() {
       if (this.isSubmit) return;
-
       if (!this.checkCardValid()) return;
 
-      this.$confirm("确定要提交当前题卡吗?", "提示", {
-        type: "warning"
-      })
-        .then(() => {
-          window.cardData = {
-            cardConfig: this.cardConfig,
-            pages: this.pages,
-            paperParams: this.paperParams
-          };
-          this.isSubmit = true;
-          const { href } = this.$router.resolve({
-            name: "CardPreview",
-            params: {
-              cardId: 1,
-              viewType: "frame"
-            }
-          });
-          this.cardPreviewUrl = href;
-        })
-        .catch(() => {});
-    },
-    registWindowSubmit() {
-      window.submitCardTemp = async (htmlContent, model) => {
-        const datas = this.getCardData(htmlContent, model);
-        datas.status = "SUBMIT";
-        const result = await saveCard(
-          datas,
-          this.getRequestConfig()
-        ).catch(() => {});
-        this.cardPreviewUrl = "";
-        this.isSubmit = false;
-        window.cardData = null;
-        if (result) {
-          this.cardId = result;
-          this.$ls.set("cardId", this.cardId);
-          this.canSave = false;
-          this.$message.success("提交成功!");
-          this.goback();
-        } else {
-          this.$message.error("提交失败,请重新尝试!");
-        }
-      };
+      this.$emit("on-submit", {
+        cardConfig: this.cardConfig,
+        pages: this.pages,
+        paperParams: this.paperParams
+      });
     },
     toExit() {
-      this.$confirm(
-        "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
-        "提示",
-        {
-          type: "warning"
-        }
-      )
-        .then(() => {
-          this.goback();
-        })
-        .catch(() => {});
+      this.$emit("on-exit");
+    },
+    loading() {
+      this.isSubmit = true;
+    },
+    unloading() {
+      this.isSubmit = false;
     }
   },
   beforeDestroy() {
-    this.$ls.remove("cardId");
-    this.$ls.remove("prepareTcPCard");
     this.initState();
-    delete window.submitCardTemp;
   }
 };
 </script>

+ 111 - 0
card/components/CardView.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="card-view">
+    <template v-for="(page, pageNo) in pages">
+      <div
+        :class="[
+          'page-box',
+          `page-box-${cardConfig.pageSize}`,
+          `page-box-${pageNo % 2}`
+        ]"
+        :key="pageNo"
+      >
+        <div
+          :class="['page-locators', `page-locators-${page.locators.length}`]"
+        >
+          <ul
+            class="page-locator-group"
+            v-for="(locator, iind) in page.locators"
+            :key="iind"
+          >
+            <li
+              v-for="(elem, eindex) in locator"
+              :key="eindex"
+              :id="elem.id"
+            ></li>
+          </ul>
+        </div>
+        <!-- inner edit area -->
+        <div class="page-main-inner">
+          <div
+            :class="['page-main', `page-main-${page.columns.length}`]"
+            :style="{ margin: `0 -${page.columnGap / 2}px` }"
+          >
+            <div
+              class="page-column"
+              v-for="(column, columnNo) in page.columns"
+              :key="columnNo"
+              :style="{ padding: `0 ${page.columnGap / 2}px` }"
+            >
+              <div
+                class="page-column-main"
+                :id="[`column-${pageNo}-${columnNo}`]"
+              >
+                <div class="page-column-body" v-if="column.elements.length">
+                  <topic-element-preview
+                    class="page-column-element"
+                    v-for="element in column.elements"
+                    :key="element.id"
+                    :data="element"
+                  ></topic-element-preview>
+                </div>
+                <div class="page-column-body" v-else>
+                  <div
+                    class="page-column-forbid-area"
+                    v-if="cardConfig.showForbidArea"
+                  >
+                    <p>该区域严禁作答</p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- outer edit area -->
+        <div class="page-main-outer">
+          <page-number
+            type="rect"
+            :total="pages.length"
+            :current="pageNo + 1"
+          ></page-number>
+          <page-number
+            type="text"
+            :total="pages.length"
+            :current="pageNo + 1"
+          ></page-number>
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+
+<script>
+import TopicElementPreview from "./TopicElementPreview";
+import PageNumber from "./PageNumber";
+import previewTemp from "../previewTemp";
+import exchangeMixins from "../mixins/exchange";
+
+export default {
+  name: "card-view",
+  components: { TopicElementPreview, PageNumber },
+  mixins: [exchangeMixins],
+  props: {
+    pages: {
+      type: Array,
+      default() {
+        return [];
+      }
+    },
+    cardConfig: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  methods: {
+    getPreviewTemp(htmlContent) {
+      return previewTemp(htmlContent);
+    }
+  }
+};
+</script>

+ 4 - 65
card/modules/free/cardFormatTransform.js

@@ -56,7 +56,7 @@ function getOffsetInfo(dom) {
   return infos.map(num => num.toFixed(10) * 1);
 }
 
-// locator: this.getLocatorInfo(page.locators),
+// locator: [],
 // barcode: [],
 // info_area: [],
 // fill_area: [],
@@ -102,7 +102,7 @@ const elementInfoFunc = {
           let options = [];
           questionItem.childNodes.forEach((optionItem, optionIndex) => {
             if (optionIndex)
-              options[optionIndex - 1] = this.getOffsetInfo(optionItem);
+              options[optionIndex - 1] = getOffsetInfo(optionItem);
           });
           listInfos[questionIndex] = {
             main_number: element.topicNo,
@@ -223,7 +223,7 @@ function getPageNumberInfo() {
   dom
     .querySelector(".page-number-rect-list")
     .childNodes.forEach((item, index) => {
-      options[index] = this.getOffsetInfo(item);
+      options[index] = getOffsetInfo(item);
     });
   return [
     {
@@ -263,7 +263,7 @@ function parsePageExchange(pages) {
     elements.forEach(elemGroup => {
       elemGroup.forEach(element => {
         if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;
-        const info = elementInfoFunc[element.typ](element);
+        const info = elementInfoFunc[element.type](element);
         Object.entries(info).forEach(([key, vals]) => {
           exchange[key] = exchange[key].concat(vals);
         });
@@ -297,64 +297,3 @@ export function getPageModel({ cardConfig, paperParams, pages }) {
     (k, v) => (k.startsWith("_") ? undefined : v)
   );
 }
-
-// TODO:缺考涂填
-
-// export default {
-//   methods: {
-//     getCardHeadInfo(element) {
-//       const dom = this.getPreviewElementById(element.id);
-//       const headArea = this.getOffsetInfo(dom);
-//       let fill_area = [];
-//       let barcode = [];
-//       // 缺考涂填
-//       if (element.examAbsent && !element.isSimple) {
-//         fill_area.push({
-//           field: "absent",
-//           index: this.getFillAreaIndex("absent"),
-//           single: true,
-//           horizontal: true,
-//           items: [
-//             {
-//               main_number: null,
-//               sub_number: null,
-//               options: [
-//                 this.getOffsetInfo(document.getElementById("dynamic-miss-area"))
-//               ]
-//             }
-//           ]
-//         });
-//       }
-//       // A/B卷类型
-//       if (element.aOrB && !element.isSimple) {
-//         // fill_area
-//         let options = [];
-//         document
-//           .getElementById("head-dynamic-aorb")
-//           .querySelectorAll(".head-dynamic-rect")
-//           .forEach((optionItem, optionIndex) => {
-//             options[optionIndex] = this.getOffsetInfo(optionItem);
-//           });
-//         fill_area.push({
-//           field: "paperType",
-//           index: this.getFillAreaIndex("paperType"),
-//           single: true,
-//           horizontal: true,
-//           items: [
-//             {
-//               main_number: null,
-//               sub_number: null,
-//               options
-//             }
-//           ]
-//         });
-//       }
-
-//       return {
-//         info_area: [headArea],
-//         fill_area,
-//         barcode
-//       };
-//     }
-//   }
-// };

+ 47 - 182
card/modules/free/views/CardFreeDesign.vue → card/modules/free/components/CardFreeDesign.vue

@@ -190,16 +190,11 @@
     ></shortcut-key>
     <!-- help-dialog -->
     <help-dialog ref="HelpDialog"></help-dialog>
-    <!-- card-view-frame -->
-    <div class="design-preview-frame" v-if="cardPreviewUrl">
-      <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
-    </div>
   </div>
 </template>
 
 <script>
 import { mapState, mapMutations, mapActions } from "vuex";
-import { cardConfigInfos, cardDetail, saveCard } from "../../../api";
 import { getElementModel, ELEMENT_LIST, TOPIC_LIST } from "../elements/model";
 import { getModel as getPageModel } from "../../../elements/page/model";
 import { CARD_VERSION } from "../../../enumerate";
@@ -217,6 +212,17 @@ import PageNumber from "../../../components/PageNumber";
 
 export default {
   name: "card-free-design",
+  props: {
+    content: {
+      type: Object,
+      default() {
+        return {
+          pages: [],
+          cardConfig: {}
+        };
+      }
+    }
+  },
   components: {
     TopicColumnEdit,
     PagePropEdit,
@@ -230,19 +236,10 @@ export default {
   },
   data() {
     return {
-      cardId: this.$route.params.cardId || this.$ls.get("cardId"),
-      prepareTcPCard: this.$ls.get("prepareTcPCard", {
-        examTaskId: "",
-        courseCode: "",
-        courseName: "",
-        makeMethod: "SELF",
-        cardRuleId: ""
-      }),
       ELEMENT_LIST,
       TOPIC_LIST,
       topicList: [],
       columnWidth: 0,
-      cardPreviewUrl: "",
       isSubmit: false,
       canSave: false
     };
@@ -256,18 +253,10 @@ export default {
       "curPageNo",
       "curDragElement",
       "curColumnId"
-    ]),
-    isEdit() {
-      return !!this.cardId;
-    }
+    ])
   },
   mounted() {
-    // if (!this.prepareTcPCard.examTaskId && !this.isEdit) {
-    //   this.$message.error("找不到命题任务,请退出题卡制作!");
-    //   return;
-    // }
     this.initCard();
-    this.registWindowSubmit();
   },
   methods: {
     ...mapMutations("free", [
@@ -286,41 +275,13 @@ export default {
       "modifyElement"
     ]),
     async initCard() {
-      if (this.isEdit) {
-        await this.getCardTempDetail();
-      } else {
-        await this.getCardConfig();
-        this.initPageData();
-      }
-    },
-    getCardTitle(titleRule) {
-      const fieldMap = {
-        courseCode: this.prepareTcPCard.courseCode,
-        courseName: this.prepareTcPCard.courseName,
-        schoolName: this.prepareTcPCard.schoolName
-      };
-      Object.entries(fieldMap).forEach(([key, val]) => {
-        titleRule = titleRule.replace("${" + key + "}", val);
-      });
-      return titleRule;
-    },
-    async getCardTempDetail() {
-      const detData = await cardDetail(this.cardId);
-      // this.canSave = !detData.operateStatus;
+      const { cardConfig, pages } = this.content;
+      this.setCardConfig(cardConfig);
 
-      // 可能存在题卡内容没有记录的情况
-      if (detData.content) {
-        const cont = JSON.parse(detData.content);
-        this.setPages(cont.pages);
-        this.setCardConfig(cont.cardConfig);
+      if (pages && pages.length) {
+        this.setPages(pages);
         this.setCurPageNo(0);
       } else {
-        await this.getCardConfig();
-        // 没有题卡内容时,直接创建新的内容
-        if (detData.makeMethod === "CUST") {
-          this.setCardConfig({ cardTitle: detData.title });
-        }
-
         this.initPageData();
       }
     },
@@ -333,29 +294,6 @@ export default {
       const page = getPageModel(this.cardConfig);
       this.addPage(page);
     },
-    async getCardConfig() {
-      const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
-      if (!data) {
-        this.$message.error("找不到题卡规则!");
-        return;
-      }
-      let config = {
-        ...data,
-        ...{
-          pageSize: "A3",
-          columnNumber: 2,
-          columnGap: 20,
-          showForbidArea: true,
-          cardDesc: "",
-          makeMethod: this.prepareTcPCard.makeMethod
-        }
-      };
-      config.aOrB = true; // 默认开启A/B卷型
-      config.requiredFields = JSON.parse(config.requiredFields);
-      config.extendFields = JSON.parse(config.extendFields);
-      config.cardTitle = this.getCardTitle(config.titleRule);
-      this.setCardConfig(config);
-    },
     addNewTopic(item) {
       let element = getElementModel(item.type);
 
@@ -368,30 +306,6 @@ export default {
       this.setCurDragElement(getElementModel(element.type));
     },
     // 操作
-    skSave() {
-      this.toSave();
-    },
-    skSubmit() {
-      this.toSubmit();
-    },
-    skPreview() {
-      this.toPreview();
-    },
-    async toPreview() {
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      const result = await this.save().catch(() => {});
-      this.isSubmit = false;
-      if (!result) return;
-      const { href } = this.$router.resolve({
-        name: "CardFreePreview",
-        params: {
-          cardId: this.cardId,
-          viewType: "view"
-        }
-      });
-      window.open(href);
-    },
     swithPage(pindex) {
       if (this.curPageNo === pindex) return;
       this.setCurPageNo(pindex);
@@ -409,22 +323,10 @@ export default {
       let data = {
         title: this.cardConfig.cardTitle,
         content: model,
-        htmlContent,
-        type: "CUSTOM",
-        ...this.prepareTcPCard
+        htmlContent
       };
-      if (this.cardId) data.id = this.cardId;
       return data;
     },
-    getRequestConfig() {
-      return this.prepareTcPCard.makeMethod === "CUST"
-        ? {
-            headers: {
-              schoolId: this.prepareTcPCard.schoolId
-            }
-          }
-        : {};
-    },
     getCardJson() {
       // 防止页面未渲染完成,各试题高度未及时更新,保存数据有误的问题
       return new Promise(resolve => {
@@ -442,21 +344,28 @@ export default {
         }, 100);
       });
     },
-    async save() {
-      const cardJson = await this.getCardJson();
-      let datas = this.getCardData("", cardJson);
-      datas.status = "STAGE";
-      const result = await saveCard(datas, this.getRequestConfig());
-      this.cardId = result;
-      this.$ls.set("cardId", this.cardId);
-      return true;
+    skSave() {
+      this.toSave();
+    },
+    skSubmit() {
+      this.toSubmit();
+    },
+    skPreview() {
+      this.toPreview();
+    },
+    async toPreview() {
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const model = await this.getCardJson();
+      const datas = this.getCardData("", model);
+      this.$emit("on-preview", datas);
     },
     async toSave() {
       if (this.isSubmit) return;
       this.isSubmit = true;
-      const result = await this.save().catch(() => {});
-      this.isSubmit = false;
-      if (result) this.$message.success("保存成功!");
+      const model = await this.getCardJson();
+      const datas = this.getCardData("", model);
+      this.$emit("on-save", datas);
     },
     toSubmit() {
       if (this.isSubmit) return;
@@ -466,71 +375,27 @@ export default {
         return;
       }
 
-      this.$confirm("确定要提交当前题卡吗?", "提示", {
-        type: "warning"
-      })
-        .then(() => {
-          window.cardData = {
-            cardConfig: this.cardConfig,
-            pages: this.pages,
-            paperParams: this.paperParams
-          };
-          this.isSubmit = true;
-          const { href } = this.$router.resolve({
-            name: "CardPreview",
-            params: {
-              cardId: 1,
-              viewType: "frame"
-            }
-          });
-          this.cardPreviewUrl = href;
-        })
-        .catch(() => {});
-    },
-    registWindowSubmit() {
-      window.submitCardTemp = async (htmlContent, model) => {
-        const datas = this.getCardData(htmlContent, model);
-        datas.status = "SUBMIT";
-        const result = await saveCard(
-          datas,
-          this.getRequestConfig()
-        ).catch(() => {});
-        this.cardPreviewUrl = "";
-        this.isSubmit = false;
-        window.cardData = null;
-        if (result) {
-          this.cardId = result;
-          this.$ls.set("cardId", this.cardId);
-          this.canSave = false;
-          this.$message.success("提交成功!");
-          this.goback();
-        } else {
-          this.$message.error("提交失败,请重新尝试!");
-        }
-      };
+      this.$emit("on-submit", {
+        cardConfig: this.cardConfig,
+        pages: this.pages,
+        paperParams: this.paperParams
+      });
     },
     toExit() {
-      this.$confirm(
-        "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
-        "提示",
-        {
-          type: "warning"
-        }
-      )
-        .then(() => {
-          this.goback();
-        })
-        .catch(() => {});
+      this.$emit("on-exit");
     },
     showHelp() {
       this.$refs.HelpDialog.open();
+    },
+    loading() {
+      this.isSubmit = true;
+    },
+    unloading() {
+      this.isSubmit = false;
     }
   },
   beforeDestroy() {
-    this.$ls.remove("cardId");
-    this.$ls.remove("prepareTcPCard");
     this.initState();
-    delete window.submitCardTemp;
   }
 };
 </script>

+ 113 - 0
card/modules/free/components/CardFreeView.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="card-free-view">
+    <template v-for="(page, pageNo) in pages">
+      <div
+        :class="[
+          'page-box',
+          `page-box-${page.pageSize}`,
+          `page-box-${pageNo % 2}`
+        ]"
+        :key="pageNo"
+      >
+        <div
+          :class="['page-locators', `page-locators-${page.locators.length}`]"
+        >
+          <ul
+            class="page-locator-group"
+            v-for="(locator, iind) in page.locators"
+            :key="iind"
+          >
+            <li
+              v-for="(elem, eindex) in locator"
+              :key="eindex"
+              :id="elem.id"
+            ></li>
+          </ul>
+        </div>
+        <!-- inner edit area -->
+        <div class="page-main-inner">
+          <div
+            :class="['page-main', `page-main-${page.columns.length}`]"
+            :style="{ margin: `0 -${page.columnGap / 2}px` }"
+          >
+            <div
+              class="page-column"
+              v-for="(column, columnNo) in page.columns"
+              :key="columnNo"
+              :style="{ padding: `0 ${page.columnGap / 2}px` }"
+            >
+              <div
+                class="page-column-main"
+                :id="[`column-${pageNo}-${columnNo}`]"
+              >
+                <div class="page-column-body" v-if="column.elements.length">
+                  <topic-element-preview
+                    class="page-column-element"
+                    v-for="element in column.elements"
+                    :key="element.id"
+                    :data="element"
+                  ></topic-element-preview>
+                </div>
+                <div class="page-column-body" v-else>
+                  <div
+                    class="page-column-forbid-area"
+                    v-if="cardConfig.showForbidArea"
+                  >
+                    <p>该区域严禁作答</p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- outer edit area -->
+        <div class="page-main-outer">
+          <page-number
+            type="rect"
+            :total="pages.length"
+            :current="pageNo + 1"
+          ></page-number>
+          <page-number
+            type="text"
+            :total="pages.length"
+            :current="pageNo + 1"
+          ></page-number>
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+
+<script>
+import TopicElementPreview from "../components/TopicElementPreview";
+import PageNumber from "../../../components/PageNumber";
+import previewTemp from "../../../previewTemp";
+import { getPageModel } from "../cardFormatTransform";
+
+export default {
+  name: "card-free-view",
+  components: { TopicElementPreview, PageNumber },
+  props: {
+    pages: {
+      type: Array,
+      default() {
+        return [];
+      }
+    },
+    cardConfig: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  methods: {
+    getPageModel(cardData) {
+      return getPageModel(cardData);
+    },
+    getPreviewTemp(htmlContent) {
+      return previewTemp(htmlContent);
+    }
+  }
+};
+</script>

+ 0 - 225
card/modules/free/views/CardFreePreview.vue

@@ -1,225 +0,0 @@
-<template>
-  <div :class="classes">
-    <div class="preview-frame" id="preview-frame" v-if="IS_COMMON_CARD"></div>
-    <div class="preview-body" v-else>
-      <template v-for="(page, pageNo) in pages">
-        <div
-          :class="[
-            'page-box',
-            `page-box-${page.pageSize}`,
-            `page-box-${pageNo % 2}`
-          ]"
-          :key="pageNo"
-        >
-          <div
-            :class="['page-locators', `page-locators-${page.locators.length}`]"
-          >
-            <ul
-              class="page-locator-group"
-              v-for="(locator, iind) in page.locators"
-              :key="iind"
-            >
-              <li
-                v-for="(elem, eindex) in locator"
-                :key="eindex"
-                :id="elem.id"
-              ></li>
-            </ul>
-          </div>
-          <!-- inner edit area -->
-          <div class="page-main-inner">
-            <div
-              :class="['page-main', `page-main-${page.columns.length}`]"
-              :style="{ margin: `0 -${page.columnGap / 2}px` }"
-            >
-              <div
-                class="page-column"
-                v-for="(column, columnNo) in page.columns"
-                :key="columnNo"
-                :style="{ padding: `0 ${page.columnGap / 2}px` }"
-              >
-                <div
-                  class="page-column-main"
-                  :id="[`column-${pageNo}-${columnNo}`]"
-                >
-                  <div class="page-column-body" v-if="column.elements.length">
-                    <topic-element-preview
-                      class="page-column-element"
-                      v-for="element in column.elements"
-                      :key="element.id"
-                      :data="element"
-                    ></topic-element-preview>
-                  </div>
-                  <div class="page-column-body" v-else>
-                    <div
-                      class="page-column-forbid-area"
-                      v-if="cardConfig.showForbidArea"
-                    >
-                      <p>该区域严禁作答</p>
-                    </div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-          <!-- outer edit area -->
-          <div class="page-main-outer">
-            <page-number
-              type="rect"
-              :total="pages.length"
-              :current="pageNo + 1"
-            ></page-number>
-            <page-number
-              type="text"
-              :total="pages.length"
-              :current="pageNo + 1"
-            ></page-number>
-          </div>
-        </div>
-      </template>
-    </div>
-  </div>
-</template>
-
-<script>
-import TopicElementPreview from "../components/TopicElementPreview";
-import PageNumber from "../../../components/PageNumber";
-import { cardDetail } from "../../../api";
-import previewTemp from "../../../previewTemp";
-import { getPageModel } from "../cardFormatTransform";
-import { deepCopy } from "../../../plugins/utils";
-const JsBarcode = require("jsbarcode");
-
-export default {
-  name: "card-free-preview",
-  components: { TopicElementPreview, PageNumber },
-  data() {
-    return {
-      isPrint: this.$route.params.viewType !== "view",
-      isFrame: this.$route.params.viewType === "frame",
-      cardId: this.$route.params.cardId,
-      cardConfig: {},
-      pages: [],
-      IS_COMMON_CARD: false
-    };
-  },
-  computed: {
-    classes() {
-      return [
-        "card-free-preview",
-        {
-          "card-print": this.isPrint
-        }
-      ];
-    }
-  },
-  mounted() {
-    if (this.isFrame) {
-      this.initFrame();
-    } else {
-      this.init();
-    }
-  },
-  methods: {
-    initFrame() {
-      const cardData = window.parent.cardData;
-      if (!cardData) return;
-
-      const { cardConfig, pages } = deepCopy(cardData);
-      let fieldInfos = {};
-      [...cardConfig.requiredFields, ...cardConfig.extendFields]
-        .filter(item => item.enable)
-        .map(item => {
-          fieldInfos[item.code] = "${" + item.code + "}";
-        });
-      this.cardConfig = cardConfig;
-      this.pages = this.appendFieldInfo(pages, fieldInfos);
-
-      this.$nextTick(() => {
-        const cardContentTemp = previewTemp(this.$el.outerHTML);
-        const model = getPageModel(cardData);
-        window.parent &&
-          window.parent.submitCardTemp &&
-          window.parent.submitCardTemp(cardContentTemp, model);
-      });
-    },
-    async init() {
-      const detData = await cardDetail(this.cardId);
-
-      this.IS_COMMON_CARD = detData.type === "GENERIC";
-      // 通卡展示
-      if (this.IS_COMMON_CARD) {
-        this.$nextTick(() => {
-          this.initHtmlTemp(detData.htmlContent);
-        });
-        return;
-      }
-      // 常规卡展示
-      if (!detData.content) {
-        this.$message.error("很抱歉,当前题卡还没开始制作!");
-        return;
-      }
-      const { cardConfig, pages } = JSON.parse(detData.content);
-      const fieldInfos = this.fetchFieldInfos(cardConfig, {});
-
-      this.cardConfig = cardConfig;
-      this.pages = this.appendFieldInfo(pages, fieldInfos);
-    },
-    initHtmlTemp(htmlTemp) {
-      const iframeDom = document.createElement("iframe");
-      document.getElementById("preview-frame").appendChild(iframeDom);
-      const wwidth = window.innerWidth - 10;
-      const wheight = window.innerHeight - 10;
-      iframeDom.style.cssText = `width: ${wwidth}px;height: ${wheight}px;border:none;outline:none;`;
-      const iframeDoc = iframeDom.contentDocument;
-      iframeDoc.open();
-      iframeDoc.write(htmlTemp);
-      iframeDoc.close();
-    },
-    fetchFieldInfos(cardConfig, stdInfo) {
-      let fieldInfos = {};
-      const defContent = "相关信息";
-      [...cardConfig.requiredFields, ...cardConfig.extendFields]
-        .filter(item => item.enable)
-        .map(item => {
-          fieldInfos[item.code] = stdInfo[item.code] || defContent;
-        });
-
-      return fieldInfos;
-    },
-    getBase64Barcode(str) {
-      const canvas = document.createElement("CANVAS");
-      JsBarcode(canvas, str, {
-        width: 2,
-        height: 30,
-        displayValue: false,
-        marginLeft: 20,
-        marginRight: 20,
-        marginTop: 0,
-        marginBottom: 0
-      });
-
-      return canvas.toDataURL();
-    },
-    appendFieldInfo(pages, fieldInfos) {
-      const VALID_ELEMENTS_FOR_EXTERNAL = ["BARCODE", "FILL_FIELD"];
-      pages.forEach(page => {
-        page.columns.forEach(column => {
-          column.elements.forEach(element => {
-            if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;
-
-            if (element.type === "BARCODE") {
-              const field = element.fields[0] && element.fields[0].code;
-              element.content = `data:image/png;base64,${fieldInfos[field]}`;
-              return;
-            }
-
-            element.fieldInfos = fieldInfos;
-          });
-        });
-      });
-      return pages;
-    }
-  }
-};
-</script>

+ 7 - 9
card/router/index.js

@@ -11,18 +11,16 @@ const routes = [
     component: Home
   },
   {
-    path: "/card/design/:cardId?",
-    name: "CardDesign",
+    path: "/card/edit/:cardId?",
+    name: "CardEdit",
     component: () =>
-      import(/* webpackChunkName: "CardDesign" */ "../views/CardDesign.vue")
+      import(/* webpackChunkName: "CardEdit" */ "../views/CardEdit.vue")
   },
   {
-    path: "/card/free-design/:cardId?",
-    name: "CardFreeDesign",
+    path: "/card/free-edit/:cardId?",
+    name: "CardFreeEdit",
     component: () =>
-      import(
-        /* webpackChunkName: "CardDesign" */ "../modules/free/views/CardFreeDesign.vue"
-      )
+      import(/* webpackChunkName: "CardEdit" */ "../views/CardFreeEdit.vue")
   },
   {
     // viewType::: view:预览,print:打印,frame:iframe嵌套
@@ -37,7 +35,7 @@ const routes = [
     name: "CardFreePreview",
     component: () =>
       import(
-        /* webpackChunkName: "CardPreview" */ "../modules/free/views/CardFreePreview.vue"
+        /* webpackChunkName: "CardPreview" */ "../views/CardFreePreview.vue"
       )
   },
   {

+ 229 - 0
card/views/CardEdit.vue

@@ -0,0 +1,229 @@
+<template>
+  <div class="card-edit">
+    <card-design
+      v-if="dataReady"
+      ref="CardDesign"
+      :content="cardContent"
+      @on-preview="toPreview"
+      @on-save="toSave"
+      @on-submit="toSubmit"
+      @on-exit="toExit"
+    ></card-design>
+
+    <!-- card-view-frame -->
+    <div class="design-preview-frame" v-if="cardPreviewUrl">
+      <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
+    </div>
+  </div>
+</template>
+
+<script>
+import { cardConfigInfos, cardDetail, saveCard } from "../api";
+import CardDesign from "../components/CardDesign";
+
+export default {
+  name: "card-edit",
+  components: {
+    CardDesign
+  },
+  data() {
+    return {
+      cardId: this.$route.params.cardId || this.$ls.get("cardId"),
+      prepareTcPCard: this.$ls.get("prepareTcPCard", {
+        examTaskId: "",
+        courseCode: "",
+        courseName: "",
+        makeMethod: "SELF",
+        cardRuleId: ""
+      }),
+      cardContent: {},
+      cardPreviewUrl: "",
+      canSave: false,
+      dataReady: false
+    };
+  },
+  computed: {
+    isEdit() {
+      return !!this.cardId;
+    }
+  },
+  mounted() {
+    if (!this.prepareTcPCard.examTaskId && !this.isEdit) {
+      this.$message.error("找不到命题任务,请退出题卡制作!");
+      return;
+    }
+    this.initCard();
+    this.registWindowSubmit();
+  },
+  methods: {
+    async initCard() {
+      this.dataReady = false;
+      if (this.isEdit) {
+        await this.getCardTempDetail();
+      } else {
+        const cardConfig = await this.getCardConfig();
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+      this.dataReady = true;
+    },
+    getCardTitle(titleRule) {
+      const fieldMap = {
+        courseCode: this.prepareTcPCard.courseCode,
+        courseName: this.prepareTcPCard.courseName,
+        schoolName: this.prepareTcPCard.schoolName
+      };
+      Object.entries(fieldMap).forEach(([key, val]) => {
+        titleRule = titleRule.replace("${" + key + "}", val);
+      });
+      return titleRule;
+    },
+    async getCardTempDetail() {
+      const detData = await cardDetail(this.cardId);
+      // 可能存在题卡内容没有记录的情况
+      if (detData.content) {
+        this.cardContent = JSON.parse(detData.content);
+      } else {
+        let cardConfig = await this.getCardConfig();
+        // 没有题卡内容时,直接创建新的内容
+        if (detData.makeMethod === "CUST") {
+          cardConfig.cardTitle = detData.title;
+        }
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+    },
+    async getCardConfig() {
+      const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
+      if (!data) {
+        this.$message.error("找不到题卡规则!");
+        return;
+      }
+      let config = {
+        ...data,
+        ...{
+          pageSize: "A3",
+          columnNumber: 2,
+          columnGap: 20,
+          showForbidArea: true,
+          cardDesc: "",
+          makeMethod: this.prepareTcPCard.makeMethod
+        }
+      };
+      config.aOrB = true; // 默认开启A/B卷型
+      config.requiredFields = JSON.parse(config.requiredFields);
+      config.extendFields = JSON.parse(config.extendFields);
+      config.cardTitle = this.getCardTitle(config.titleRule);
+      return config;
+    },
+    // 操作
+    getRequestConfig() {
+      return this.prepareTcPCard.makeMethod === "CUST"
+        ? {
+            headers: {
+              schoolId: this.prepareTcPCard.schoolId
+            }
+          }
+        : {};
+    },
+    getCardData(htmlContent = "", model = "") {
+      let data = this.$refs.CardDesign.getCardData(htmlContent, model);
+      data = {
+        ...data,
+        type: "CUSTOM",
+        ...this.prepareTcPCard
+      };
+      if (this.cardId) data.id = this.cardId;
+      return data;
+    },
+    async toPreview(datas) {
+      await this.toSave(datas);
+
+      const { href } = this.$router.resolve({
+        name: "CardPreview",
+        params: {
+          cardId: this.cardId,
+          viewType: "view"
+        }
+      });
+      window.open(href);
+    },
+    // save
+    async toSave(datas) {
+      datas.status = "STAGE";
+      const result = await saveCard(
+        datas,
+        this.getRequestConfig()
+      ).catch(() => {});
+
+      this.$refs.CardDesign.unloading();
+      if (!result) return;
+
+      this.cardId = result;
+      this.$ls.set("cardId", this.cardId);
+      this.$message.success("保存成功!");
+    },
+    async toSubmit(cardData) {
+      const res = await this.$confirm("确定要提交当前题卡吗?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+      if (res !== "confirm") return;
+
+      window.cardData = cardData;
+      const { href } = this.$router.resolve({
+        name: "CardPreview",
+        params: {
+          cardId: 1,
+          viewType: "frame"
+        }
+      });
+      this.cardPreviewUrl = href;
+    },
+    registWindowSubmit() {
+      window.submitCardTemp = async (htmlContent, model) => {
+        const datas = this.getCardData(htmlContent, model);
+        datas.status = "SUBMIT";
+        const result = await saveCard(
+          datas,
+          this.getRequestConfig()
+        ).catch(() => {});
+        this.cardPreviewUrl = "";
+        window.cardData = null;
+        if (result) {
+          this.cardId = result;
+          this.$ls.set("cardId", this.cardId);
+          this.canSave = false;
+          this.$message.success("提交成功!");
+          this.goback();
+        } else {
+          this.$message.error("提交失败,请重新尝试!");
+        }
+      };
+    },
+    toExit() {
+      this.$confirm(
+        "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
+        "提示",
+        {
+          type: "warning"
+        }
+      )
+        .then(() => {
+          this.goback();
+        })
+        .catch(() => {});
+    }
+  },
+  beforeDestroy() {
+    this.$ls.remove("cardId");
+    this.$ls.remove("prepareTcPCard");
+    delete window.submitCardTemp;
+  }
+};
+</script>

+ 230 - 0
card/views/CardFreeEdit.vue

@@ -0,0 +1,230 @@
+<template>
+  <div class="card-free-edit">
+    <card-free-design
+      v-if="dataReady"
+      ref="CardFreeDesign"
+      :content="cardContent"
+      @on-preview="toPreview"
+      @on-save="toSave"
+      @on-submit="toSubmit"
+      @on-exit="toExit"
+    ></card-free-design>
+    <!-- card-view-frame -->
+    <div class="design-preview-frame" v-if="cardPreviewUrl">
+      <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
+    </div>
+  </div>
+</template>
+
+<script>
+import { cardConfigInfos, cardDetail, saveCard } from "../api";
+import CardFreeDesign from "../modules/free/components/CardFreeDesign";
+
+export default {
+  name: "card-free-edit",
+  components: {
+    CardFreeDesign
+  },
+  data() {
+    return {
+      cardId: this.$route.params.cardId || this.$ls.get("cardId"),
+      prepareTcPCard: this.$ls.get("prepareTcPCard", {
+        examTaskId: "",
+        courseCode: "",
+        courseName: "",
+        makeMethod: "SELF",
+        cardRuleId: ""
+      }),
+      cardContent: {},
+      cardPreviewUrl: "",
+      isSubmit: false,
+      canSave: false,
+      dataReady: false
+    };
+  },
+  computed: {
+    isEdit() {
+      return !!this.cardId;
+    }
+  },
+  mounted() {
+    // if (!this.prepareTcPCard.examTaskId && !this.isEdit) {
+    //   this.$message.error("找不到命题任务,请退出题卡制作!");
+    //   return;
+    // }
+    this.initCard();
+    this.registWindowSubmit();
+  },
+  methods: {
+    async initCard() {
+      this.dataReady = false;
+      if (this.isEdit) {
+        await this.getCardTempDetail();
+      } else {
+        const cardConfig = await this.getCardConfig();
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+      this.dataReady = true;
+    },
+    getCardTitle(titleRule) {
+      const fieldMap = {
+        courseCode: this.prepareTcPCard.courseCode,
+        courseName: this.prepareTcPCard.courseName,
+        schoolName: this.prepareTcPCard.schoolName
+      };
+      Object.entries(fieldMap).forEach(([key, val]) => {
+        titleRule = titleRule.replace("${" + key + "}", val);
+      });
+      return titleRule;
+    },
+    async getCardTempDetail() {
+      const detData = await cardDetail(this.cardId);
+
+      // 可能存在题卡内容没有记录的情况
+      if (detData.content) {
+        this.cardContent = JSON.parse(detData.content);
+      } else {
+        let cardConfig = await this.getCardConfig();
+        // 没有题卡内容时,直接创建新的内容
+        if (detData.makeMethod === "CUST") {
+          this.setCardConfig({ cardTitle: detData.title });
+        }
+
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+    },
+    async getCardConfig() {
+      const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
+      if (!data) {
+        this.$message.error("找不到题卡规则!");
+        return;
+      }
+      let config = {
+        ...data,
+        ...{
+          pageSize: "A3",
+          columnNumber: 2,
+          columnGap: 20,
+          showForbidArea: true,
+          cardDesc: "",
+          makeMethod: this.prepareTcPCard.makeMethod
+        }
+      };
+      config.aOrB = true; // 默认开启A/B卷型
+      config.requiredFields = JSON.parse(config.requiredFields);
+      config.extendFields = JSON.parse(config.extendFields);
+      config.cardTitle = this.getCardTitle(config.titleRule);
+      return config;
+    },
+    // 操作
+    getRequestConfig() {
+      return this.prepareTcPCard.makeMethod === "CUST"
+        ? {
+            headers: {
+              schoolId: this.prepareTcPCard.schoolId
+            }
+          }
+        : {};
+    },
+    getCardData(htmlContent = "", model = "") {
+      let data = this.$refs.CardFreeDesign.getCardData(htmlContent, model);
+      data = {
+        ...data,
+        type: "CUSTOM",
+        ...this.prepareTcPCard
+      };
+      if (this.cardId) data.id = this.cardId;
+      return data;
+    },
+    async toPreview(datas) {
+      await this.toSave(datas);
+
+      const { href } = this.$router.resolve({
+        name: "CardFreePreview",
+        params: {
+          cardId: this.cardId,
+          viewType: "view"
+        }
+      });
+      window.open(href);
+    },
+    async toSave(datas) {
+      datas.status = "STAGE";
+      const result = await saveCard(
+        datas,
+        this.getRequestConfig()
+      ).catch(() => {});
+
+      this.$refs.CardFreeDesign.unloading();
+      if (!result) return;
+
+      this.cardId = result;
+      this.$ls.set("cardId", this.cardId);
+      this.$message.success("保存成功!");
+    },
+    async toSubmit(cardData) {
+      const res = await this.$confirm("确定要提交当前题卡吗?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+      if (res !== "confirm") return;
+
+      window.cardData = cardData;
+      const { href } = this.$router.resolve({
+        name: "CardFreePreview",
+        params: {
+          cardId: 1,
+          viewType: "frame"
+        }
+      });
+      this.cardPreviewUrl = href;
+    },
+    registWindowSubmit() {
+      window.submitCardTemp = async (htmlContent, model) => {
+        const datas = this.getCardData(htmlContent, model);
+        datas.status = "SUBMIT";
+        const result = await saveCard(
+          datas,
+          this.getRequestConfig()
+        ).catch(() => {});
+        this.cardPreviewUrl = "";
+        window.cardData = null;
+        if (result) {
+          this.cardId = result;
+          this.$ls.set("cardId", this.cardId);
+          this.canSave = false;
+          this.$message.success("提交成功!");
+          this.goback();
+        } else {
+          this.$message.error("提交失败,请重新尝试!");
+        }
+      };
+    },
+    toExit() {
+      this.$confirm(
+        "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
+        "提示",
+        {
+          type: "warning"
+        }
+      )
+        .then(() => {
+          this.goback();
+        })
+        .catch(() => {});
+    }
+  },
+  beforeDestroy() {
+    this.$ls.remove("cardId");
+    this.$ls.remove("prepareTcPCard");
+    delete window.submitCardTemp;
+  }
+};
+</script>

+ 131 - 0
card/views/CardFreePreview.vue

@@ -0,0 +1,131 @@
+<template>
+  <div :class="classes">
+    <card-free-view
+      v-if="pages.length"
+      ref="CardFreeView"
+      class="preview-body"
+      :pages="pages"
+      :card-config="cardConfig"
+    ></card-free-view>
+  </div>
+</template>
+
+<script>
+import CardFreeView from "../modules/free/components/CardFreeView";
+import { cardDetail } from "../api";
+import { deepCopy } from "../plugins/utils";
+const JsBarcode = require("jsbarcode");
+
+export default {
+  name: "card-free-preview",
+  components: { CardFreeView },
+  data() {
+    return {
+      isPrint: this.$route.params.viewType !== "view",
+      isFrame: this.$route.params.viewType === "frame",
+      cardId: this.$route.params.cardId,
+      pages: [],
+      cardConfig: {}
+    };
+  },
+  computed: {
+    classes() {
+      return [
+        "card-free-preview",
+        {
+          "card-print": this.isPrint
+        }
+      ];
+    }
+  },
+  mounted() {
+    if (this.isFrame) {
+      this.initFrame();
+    } else {
+      this.init();
+    }
+  },
+  methods: {
+    initFrame() {
+      const cardData = window.parent.cardData;
+      if (!cardData) return;
+
+      const { cardConfig, pages } = deepCopy(cardData);
+      let fieldInfos = {};
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          fieldInfos[item.code] = "${" + item.code + "}";
+        });
+      this.cardConfig = cardConfig;
+      this.pages = this.appendFieldInfo(pages, fieldInfos);
+
+      this.$nextTick(() => {
+        const cardContentTemp = this.$refs.CardFreeView.getPreviewTemp(
+          this.$el.outerHTML
+        );
+        const model = this.$refs.CardFreeView.getPageModel(cardData);
+        window.parent &&
+          window.parent.submitCardTemp &&
+          window.parent.submitCardTemp(cardContentTemp, model);
+      });
+    },
+    async init() {
+      const detData = await cardDetail(this.cardId);
+      if (!detData.content) {
+        this.$message.error("很抱歉,当前题卡还没开始制作!");
+        return;
+      }
+      const { cardConfig, pages } = JSON.parse(detData.content);
+      const fieldInfos = this.fetchFieldInfos(cardConfig, {});
+
+      this.cardConfig = cardConfig;
+      this.pages = this.appendFieldInfo(pages, fieldInfos);
+    },
+    fetchFieldInfos(cardConfig, stdInfo) {
+      let fieldInfos = {};
+      const defContent = "相关信息";
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          fieldInfos[item.code] = stdInfo[item.code] || defContent;
+        });
+
+      return fieldInfos;
+    },
+    getBase64Barcode(str) {
+      const canvas = document.createElement("CANVAS");
+      JsBarcode(canvas, str, {
+        width: 2,
+        height: 30,
+        displayValue: false,
+        marginLeft: 20,
+        marginRight: 20,
+        marginTop: 0,
+        marginBottom: 0
+      });
+
+      return canvas.toDataURL();
+    },
+    appendFieldInfo(pages, fieldInfos) {
+      const VALID_ELEMENTS_FOR_EXTERNAL = ["BARCODE", "FILL_FIELD"];
+      pages.forEach(page => {
+        page.columns.forEach(column => {
+          column.elements.forEach(element => {
+            if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;
+
+            if (element.type === "BARCODE") {
+              const field = element.fields[0] && element.fields[0].code;
+              element.content = `data:image/png;base64,${fieldInfos[field]}`;
+              return;
+            }
+
+            element.fieldInfos = fieldInfos;
+          });
+        });
+      });
+      return pages;
+    }
+  }
+};
+</script>

+ 15 - 86
card/views/CardPreview.vue

@@ -1,99 +1,25 @@
 <template>
   <div :class="classes">
-    <div class="preview-frame" id="preview-frame" v-if="IS_COMMON_CARD"></div>
-    <div class="preview-body" v-else>
-      <template v-for="(page, pageNo) in pages">
-        <div
-          :class="[
-            'page-box',
-            `page-box-${cardConfig.pageSize}`,
-            `page-box-${pageNo % 2}`
-          ]"
-          :key="pageNo"
-        >
-          <div
-            :class="['page-locators', `page-locators-${page.locators.length}`]"
-          >
-            <ul
-              class="page-locator-group"
-              v-for="(locator, iind) in page.locators"
-              :key="iind"
-            >
-              <li
-                v-for="(elem, eindex) in locator"
-                :key="eindex"
-                :id="elem.id"
-              ></li>
-            </ul>
-          </div>
-          <!-- inner edit area -->
-          <div class="page-main-inner">
-            <div
-              :class="['page-main', `page-main-${page.columns.length}`]"
-              :style="{ margin: `0 -${page.columnGap / 2}px` }"
-            >
-              <div
-                class="page-column"
-                v-for="(column, columnNo) in page.columns"
-                :key="columnNo"
-                :style="{ padding: `0 ${page.columnGap / 2}px` }"
-              >
-                <div
-                  class="page-column-main"
-                  :id="[`column-${pageNo}-${columnNo}`]"
-                >
-                  <div class="page-column-body" v-if="column.elements.length">
-                    <topic-element-preview
-                      class="page-column-element"
-                      v-for="element in column.elements"
-                      :key="element.id"
-                      :data="element"
-                    ></topic-element-preview>
-                  </div>
-                  <div class="page-column-body" v-else>
-                    <div
-                      class="page-column-forbid-area"
-                      v-if="cardConfig.showForbidArea"
-                    >
-                      <p>该区域严禁作答</p>
-                    </div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-          <!-- outer edit area -->
-          <div class="page-main-outer">
-            <page-number
-              type="rect"
-              :total="pages.length"
-              :current="pageNo + 1"
-            ></page-number>
-            <page-number
-              type="text"
-              :total="pages.length"
-              :current="pageNo + 1"
-            ></page-number>
-          </div>
-        </div>
-      </template>
-    </div>
+    <div v-if="IS_COMMON_CARD" class="preview-frame" id="preview-frame"></div>
+    <card-view
+      v-if="!IS_COMMON_CARD && pages.length"
+      ref="CardView"
+      class="preview-body"
+      :pages="pages"
+      :card-config="cardConfig"
+    ></card-view>
   </div>
 </template>
 
 <script>
-import TopicElementPreview from "../components/TopicElementPreview";
-import PageNumber from "../components/PageNumber";
+import CardView from "../components/CardView";
 import { cardDetail } from "../api";
-import previewTemp from "../previewTemp";
-import exchangeMixins from "../mixins/exchange";
 import { deepCopy } from "../plugins/utils";
 const JsBarcode = require("jsbarcode");
 
 export default {
   name: "card-preview",
-  components: { TopicElementPreview, PageNumber },
-  mixins: [exchangeMixins],
+  components: { CardView },
   data() {
     return {
       isPrint: this.$route.params.viewType !== "view",
@@ -147,8 +73,10 @@ export default {
       this.pages = this.appendFieldInfo(pages, fieldInfos);
 
       this.$nextTick(() => {
-        const cardContentTemp = previewTemp(this.$el.outerHTML);
-        const model = this.getPageModel(cardData);
+        const cardContentTemp = this.$refs.CardView.getPreviewTemp(
+          this.$el.outerHTML
+        );
+        const model = this.$refs.CardView.getPageModel(cardData);
         window.parent &&
           window.parent.submitCardTemp &&
           window.parent.submitCardTemp(cardContentTemp, model);
@@ -160,6 +88,7 @@ export default {
       this.IS_COMMON_CARD = detData.type === "GENERIC";
       // 通卡展示
       if (this.IS_COMMON_CARD) {
+        // TODO:通卡展示逻辑要调整
         this.$nextTick(() => {
           this.initHtmlTemp(detData.htmlContent);
         });

+ 0 - 229
card/views/CardRulePreview.vue

@@ -1,229 +0,0 @@
-<template>
-  <div :class="classes">
-    <div class="preview-body">
-      <template v-for="(page, pageNo) in pages">
-        <div
-          :class="[
-            'page-box',
-            `page-box-${cardConfig.pageSize}`,
-            `page-box-${pageNo % 2}`
-          ]"
-          :key="pageNo"
-        >
-          <div
-            :class="['page-locators', `page-locators-${page.locators.length}`]"
-          >
-            <ul
-              class="page-locator-group"
-              v-for="(locator, iind) in page.locators"
-              :key="iind"
-            >
-              <li
-                v-for="(elem, eindex) in locator"
-                :key="eindex"
-                :id="elem.id"
-              ></li>
-            </ul>
-          </div>
-          <!-- inner edit area -->
-          <div class="page-main-inner">
-            <div
-              :class="['page-main', `page-main-${page.columns.length}`]"
-              :style="{ margin: `0 -${page.columnGap / 2}px` }"
-            >
-              <div
-                class="page-column"
-                v-for="(column, columnNo) in page.columns"
-                :key="columnNo"
-                :style="{ padding: `0 ${page.columnGap / 2}px` }"
-              >
-                <div
-                  class="page-column-main"
-                  :id="[`column-${pageNo}-${columnNo}`]"
-                >
-                  <div class="page-column-body" v-if="column.elements.length">
-                    <topic-element-preview
-                      class="page-column-element"
-                      v-for="element in column.elements"
-                      :key="element.id"
-                      :data="element"
-                    ></topic-element-preview>
-                  </div>
-                  <div class="page-column-body" v-else>
-                    <div
-                      class="page-column-forbid-area"
-                      v-if="cardConfig.showForbidArea"
-                    >
-                      <p>该区域严禁作答</p>
-                    </div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-          <!-- outer edit area -->
-          <div class="page-main-outer">
-            <page-number
-              type="rect"
-              :total="pages.length"
-              :current="pageNo + 1"
-            ></page-number>
-            <page-number
-              type="text"
-              :total="pages.length"
-              :current="pageNo + 1"
-            ></page-number>
-          </div>
-        </div>
-      </template>
-    </div>
-  </div>
-</template>
-
-<script>
-import TopicElementPreview from "../components/TopicElementPreview";
-import PageNumber from "../components/PageNumber";
-import { cardConfigInfos } from "../api";
-const JsBarcode = require("jsbarcode");
-import { getCardHeadModel } from "../elementModel";
-
-const pages = [
-  {
-    type: "PAGE",
-    columnGap: 20,
-    locators: [
-      [
-        {
-          type: "LOCATOR",
-          id: "locator-0-00"
-        },
-        {
-          type: "LOCATOR",
-          id: "locator-0-01"
-        }
-      ],
-      [
-        {
-          type: "LOCATOR",
-          id: "locator-0-10"
-        },
-        {
-          type: "LOCATOR",
-          id: "locator-0-11"
-        }
-      ],
-      [
-        {
-          type: "LOCATOR",
-          id: "locator-0-20"
-        },
-        {
-          type: "LOCATOR",
-          id: "locator-0-21"
-        }
-      ]
-    ],
-    columns: [
-      {
-        type: "COLUMN",
-        elements: []
-      },
-      {
-        type: "COLUMN",
-        elements: []
-      }
-    ]
-  }
-];
-
-export default {
-  name: "card-preview",
-  components: { TopicElementPreview, PageNumber },
-  data() {
-    return {
-      cardRuleId: this.$route.params.cardRuleId,
-      cardConfig: {},
-      pages
-    };
-  },
-  computed: {
-    classes() {
-      return ["card-preview"];
-    }
-  },
-  mounted() {
-    if (!this.cardRuleId) {
-      this.$message.error("题卡规则id缺失!");
-      return;
-    }
-    this.init();
-  },
-  methods: {
-    async init() {
-      const data = await cardConfigInfos(this.cardRuleId);
-      if (!data) {
-        this.$message.error("找不到题卡规则!");
-        return;
-      }
-      let config = {
-        ...data,
-        ...{
-          pageSize: "A3",
-          columnNumber: 2,
-          columnGap: 20,
-          showForbidArea: true,
-          cardDesc: ""
-        }
-      };
-      config.aOrB = true; // 默认开启A/B卷型
-      config.requiredFields = JSON.parse(config.requiredFields);
-      config.extendFields = JSON.parse(config.extendFields);
-      config.cardTitle = config.titleRule;
-
-      this.cardConfig = config;
-      let cardHeadElement = getCardHeadModel(this.cardConfig);
-      cardHeadElement.fieldInfos = this.fetchFieldInfos(config, {});
-      this.pages[0].columns[0].elements.push(cardHeadElement);
-    },
-    fetchFieldInfos(cardConfig, stdInfo) {
-      let fieldInfos = {};
-      const defContent = "相关信息";
-      const defNumber = "123456789";
-      [...cardConfig.requiredFields, ...cardConfig.extendFields]
-        .filter(item => item.enable)
-        .map(item => {
-          console.log(item);
-          fieldInfos[item.code] = stdInfo[item.code] || defContent;
-        });
-      if (cardConfig.examNumberStyle === "PRINT") {
-        fieldInfos.examNumber = this.getBase64Barcode(
-          stdInfo["examNumber"] || defNumber
-        );
-        fieldInfos.examNumberStr = stdInfo["examNumber"] || defNumber;
-      }
-      if (cardConfig.aOrB && cardConfig.paperType === "PRINT") {
-        fieldInfos.paperType = this.getBase64Barcode(
-          stdInfo["paperType"] || defNumber
-        );
-        fieldInfos.paperTypeName = stdInfo["paperTypeName"] || "A";
-      }
-
-      return fieldInfos;
-    },
-    getBase64Barcode(str) {
-      const canvas = document.createElement("CANVAS");
-      JsBarcode(canvas, str, {
-        width: 2,
-        height: 30,
-        displayValue: false,
-        marginLeft: 20,
-        marginRight: 20,
-        marginTop: 0,
-        marginBottom: 0
-      });
-
-      return canvas.toDataURL();
-    }
-  }
-};
-</script>

+ 3 - 3
card/views/Home.vue

@@ -82,7 +82,7 @@ export default {
         cardRuleId: "1"
       });
       this.$router.push({
-        name: "CardFreeDesign",
+        name: "CardFreeEdit",
         params: {
           cardId: row.id
         }
@@ -97,7 +97,7 @@ export default {
         cardRuleId: "1"
       });
       this.$router.push({
-        name: "CardDesign"
+        name: "CardEdit"
       });
     },
     toFreeAdd() {
@@ -109,7 +109,7 @@ export default {
         cardRuleId: "1"
       });
       this.$router.push({
-        name: "CardFreeDesign"
+        name: "CardFreeEdit"
       });
     }
   }

+ 1 - 1
src/modules/base/components/ModifyCardInfo.vue

@@ -240,7 +240,7 @@ export default {
 
       if (!this.IS_UPLOAD && !this.isEdit) {
         this.$router.push({
-          name: this.IS_STANDARD ? "CardDesign" : "CardFreeDesign"
+          name: this.IS_STANDARD ? "CardEdit" : "CardFreeEdit"
         });
       }
     },

+ 1 - 1
src/modules/base/views/CardManage.vue

@@ -196,7 +196,7 @@ export default {
     toEditCard(row) {
       this.curCard = row;
       this.$router.push({
-        name: row.createType === "STANDARD" ? "CardDesign" : "CardFreeDesign",
+        name: row.createType === "STANDARD" ? "CardEdit" : "CardFreeEdit",
         params: {
           cardId: row.id
         }

+ 11 - 2
src/modules/card/api.js

@@ -1,3 +1,12 @@
-import { cardConfigInfos, cardDetail, saveCard } from "../../../card/api";
+import { $postParam, $post } from "@/plugins/axios";
 
-export { cardConfigInfos, cardDetail, saveCard };
+export const cardConfigInfos = id => {
+  return $postParam("/api/admin/basic/card_rule/get_one", { id });
+};
+export const cardDetail = cardId => {
+  return $postParam("/api/admin/exam/card/get_one", { cardId });
+};
+
+export const saveCard = (datas, config = {}) => {
+  return $post("/api/admin/exam/card/save", datas, config);
+};

+ 9 - 15
src/modules/card/router.js

@@ -1,36 +1,30 @@
 export default [
   {
-    path: "/card/design/:cardId?",
-    name: "CardDesign",
+    path: "/card/edit/:cardId?",
+    name: "CardEdit",
     component: () =>
-      import(
-        /* webpackChunkName: "CardDesign" */ "../../../card/views/CardDesign.vue"
-      )
+      import(/* webpackChunkName: "CardEdit" */ "./views/CardEdit.vue")
   },
   {
     // viewType::: view:预览,print:打印,frame:iframe嵌套
     path: "/card/preview/:cardId/:viewType",
     name: "CardPreview",
     component: () =>
-      import(
-        /* webpackChunkName: "CardPreview" */ "../../../card/views/CardPreview.vue"
-      )
+      import(/* webpackChunkName: "CardPreview" */ "./views/CardPreview.vue")
   },
   {
     path: "/card/card-rule/preview/:cardRuleId",
     name: "CardRulePreview",
     component: () =>
       import(
-        /* webpackChunkName: "CardRulePreview" */ "../../../card/views/CardRulePreview.vue"
+        /* webpackChunkName: "CardRulePreview" */ "./views/CardRulePreview.vue"
       )
   },
   {
-    path: "/card/free-design/:cardId?",
-    name: "CardFreeDesign",
+    path: "/card/free-edit/:cardId?",
+    name: "CardFreeEdit",
     component: () =>
-      import(
-        /* webpackChunkName: "CardDesign" */ "../../../card/modules/free/views/CardFreeDesign.vue"
-      )
+      import(/* webpackChunkName: "CardEdit" */ "./views/CardFreeEdit.vue")
   },
   {
     // viewType::: view:预览,print:打印,frame:iframe嵌套
@@ -38,7 +32,7 @@ export default [
     name: "CardFreePreview",
     component: () =>
       import(
-        /* webpackChunkName: "CardPreview" */ "../../../card/modules/free/views/CardFreePreview.vue"
+        /* webpackChunkName: "CardPreview" */ "./views/CardFreePreview.vue"
       )
   }
 ];

+ 229 - 0
src/modules/card/views/CardEdit.vue

@@ -0,0 +1,229 @@
+<template>
+  <div class="card-edit">
+    <card-design
+      v-if="dataReady"
+      ref="CardDesign"
+      :content="cardContent"
+      @on-preview="toPreview"
+      @on-save="toSave"
+      @on-submit="toSubmit"
+      @on-exit="toExit"
+    ></card-design>
+
+    <!-- card-view-frame -->
+    <div class="design-preview-frame" v-if="cardPreviewUrl">
+      <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
+    </div>
+  </div>
+</template>
+
+<script>
+import { cardConfigInfos, cardDetail, saveCard } from "../api";
+import CardDesign from "../../../../card/components/CardDesign";
+
+export default {
+  name: "card-edit",
+  components: {
+    CardDesign
+  },
+  data() {
+    return {
+      cardId: this.$route.params.cardId || this.$ls.get("cardId"),
+      prepareTcPCard: this.$ls.get("prepareTcPCard", {
+        examTaskId: "",
+        courseCode: "",
+        courseName: "",
+        makeMethod: "SELF",
+        cardRuleId: ""
+      }),
+      cardContent: {},
+      cardPreviewUrl: "",
+      canSave: false,
+      dataReady: false
+    };
+  },
+  computed: {
+    isEdit() {
+      return !!this.cardId;
+    }
+  },
+  mounted() {
+    if (!this.prepareTcPCard.examTaskId && !this.isEdit) {
+      this.$message.error("找不到命题任务,请退出题卡制作!");
+      return;
+    }
+    this.initCard();
+    this.registWindowSubmit();
+  },
+  methods: {
+    async initCard() {
+      this.dataReady = false;
+      if (this.isEdit) {
+        await this.getCardTempDetail();
+      } else {
+        const cardConfig = await this.getCardConfig();
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+      this.dataReady = true;
+    },
+    getCardTitle(titleRule) {
+      const fieldMap = {
+        courseCode: this.prepareTcPCard.courseCode,
+        courseName: this.prepareTcPCard.courseName,
+        schoolName: this.prepareTcPCard.schoolName
+      };
+      Object.entries(fieldMap).forEach(([key, val]) => {
+        titleRule = titleRule.replace("${" + key + "}", val);
+      });
+      return titleRule;
+    },
+    async getCardTempDetail() {
+      const detData = await cardDetail(this.cardId);
+      // 可能存在题卡内容没有记录的情况
+      if (detData.content) {
+        this.cardContent = JSON.parse(detData.content);
+      } else {
+        let cardConfig = await this.getCardConfig();
+        // 没有题卡内容时,直接创建新的内容
+        if (detData.makeMethod === "CUST") {
+          cardConfig.cardTitle = detData.title;
+        }
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+    },
+    async getCardConfig() {
+      const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
+      if (!data) {
+        this.$message.error("找不到题卡规则!");
+        return;
+      }
+      let config = {
+        ...data,
+        ...{
+          pageSize: "A3",
+          columnNumber: 2,
+          columnGap: 20,
+          showForbidArea: true,
+          cardDesc: "",
+          makeMethod: this.prepareTcPCard.makeMethod
+        }
+      };
+      config.aOrB = true; // 默认开启A/B卷型
+      config.requiredFields = JSON.parse(config.requiredFields);
+      config.extendFields = JSON.parse(config.extendFields);
+      config.cardTitle = this.getCardTitle(config.titleRule);
+      return config;
+    },
+    // 操作
+    getRequestConfig() {
+      return this.prepareTcPCard.makeMethod === "CUST"
+        ? {
+            headers: {
+              schoolId: this.prepareTcPCard.schoolId
+            }
+          }
+        : {};
+    },
+    getCardData(htmlContent = "", model = "") {
+      let data = this.$refs.CardDesign.getCardData(htmlContent, model);
+      data = {
+        ...data,
+        type: "CUSTOM",
+        ...this.prepareTcPCard
+      };
+      if (this.cardId) data.id = this.cardId;
+      return data;
+    },
+    async toPreview(datas) {
+      await this.toSave(datas);
+
+      const { href } = this.$router.resolve({
+        name: "CardPreview",
+        params: {
+          cardId: this.cardId,
+          viewType: "view"
+        }
+      });
+      window.open(href);
+    },
+    // save
+    async toSave(datas) {
+      datas.status = "STAGE";
+      const result = await saveCard(
+        datas,
+        this.getRequestConfig()
+      ).catch(() => {});
+
+      this.$refs.CardDesign.unloading();
+      if (!result) return;
+
+      this.cardId = result;
+      this.$ls.set("cardId", this.cardId);
+      this.$message.success("保存成功!");
+    },
+    async toSubmit(cardData) {
+      const res = await this.$confirm("确定要提交当前题卡吗?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+      if (res !== "confirm") return;
+
+      window.cardData = cardData;
+      const { href } = this.$router.resolve({
+        name: "CardPreview",
+        params: {
+          cardId: 1,
+          viewType: "frame"
+        }
+      });
+      this.cardPreviewUrl = href;
+    },
+    registWindowSubmit() {
+      window.submitCardTemp = async (htmlContent, model) => {
+        const datas = this.getCardData(htmlContent, model);
+        datas.status = "SUBMIT";
+        const result = await saveCard(
+          datas,
+          this.getRequestConfig()
+        ).catch(() => {});
+        this.cardPreviewUrl = "";
+        window.cardData = null;
+        if (result) {
+          this.cardId = result;
+          this.$ls.set("cardId", this.cardId);
+          this.canSave = false;
+          this.$message.success("提交成功!");
+          this.goback();
+        } else {
+          this.$message.error("提交失败,请重新尝试!");
+        }
+      };
+    },
+    toExit() {
+      this.$confirm(
+        "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
+        "提示",
+        {
+          type: "warning"
+        }
+      )
+        .then(() => {
+          this.goback();
+        })
+        .catch(() => {});
+    }
+  },
+  beforeDestroy() {
+    this.$ls.remove("cardId");
+    this.$ls.remove("prepareTcPCard");
+    delete window.submitCardTemp;
+  }
+};
+</script>

+ 230 - 0
src/modules/card/views/CardFreeEdit.vue

@@ -0,0 +1,230 @@
+<template>
+  <div class="card-free-edit">
+    <card-free-design
+      v-if="dataReady"
+      ref="CardFreeDesign"
+      :content="cardContent"
+      @on-preview="toPreview"
+      @on-save="toSave"
+      @on-submit="toSubmit"
+      @on-exit="toExit"
+    ></card-free-design>
+    <!-- card-view-frame -->
+    <div class="design-preview-frame" v-if="cardPreviewUrl">
+      <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
+    </div>
+  </div>
+</template>
+
+<script>
+import { cardConfigInfos, cardDetail, saveCard } from "../api";
+import CardFreeDesign from "../../../../card/modules/free/components/CardFreeDesign";
+
+export default {
+  name: "card-free-edit",
+  components: {
+    CardFreeDesign
+  },
+  data() {
+    return {
+      cardId: this.$route.params.cardId || this.$ls.get("cardId"),
+      prepareTcPCard: this.$ls.get("prepareTcPCard", {
+        examTaskId: "",
+        courseCode: "",
+        courseName: "",
+        makeMethod: "SELF",
+        cardRuleId: ""
+      }),
+      cardContent: {},
+      cardPreviewUrl: "",
+      isSubmit: false,
+      canSave: false,
+      dataReady: false
+    };
+  },
+  computed: {
+    isEdit() {
+      return !!this.cardId;
+    }
+  },
+  mounted() {
+    // if (!this.prepareTcPCard.examTaskId && !this.isEdit) {
+    //   this.$message.error("找不到命题任务,请退出题卡制作!");
+    //   return;
+    // }
+    this.initCard();
+    this.registWindowSubmit();
+  },
+  methods: {
+    async initCard() {
+      this.dataReady = false;
+      if (this.isEdit) {
+        await this.getCardTempDetail();
+      } else {
+        const cardConfig = await this.getCardConfig();
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+      this.dataReady = true;
+    },
+    getCardTitle(titleRule) {
+      const fieldMap = {
+        courseCode: this.prepareTcPCard.courseCode,
+        courseName: this.prepareTcPCard.courseName,
+        schoolName: this.prepareTcPCard.schoolName
+      };
+      Object.entries(fieldMap).forEach(([key, val]) => {
+        titleRule = titleRule.replace("${" + key + "}", val);
+      });
+      return titleRule;
+    },
+    async getCardTempDetail() {
+      const detData = await cardDetail(this.cardId);
+
+      // 可能存在题卡内容没有记录的情况
+      if (detData.content) {
+        this.cardContent = JSON.parse(detData.content);
+      } else {
+        let cardConfig = await this.getCardConfig();
+        // 没有题卡内容时,直接创建新的内容
+        if (detData.makeMethod === "CUST") {
+          this.setCardConfig({ cardTitle: detData.title });
+        }
+
+        this.cardContent = {
+          pages: [],
+          cardConfig,
+          paperParams: {}
+        };
+      }
+    },
+    async getCardConfig() {
+      const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
+      if (!data) {
+        this.$message.error("找不到题卡规则!");
+        return;
+      }
+      let config = {
+        ...data,
+        ...{
+          pageSize: "A3",
+          columnNumber: 2,
+          columnGap: 20,
+          showForbidArea: true,
+          cardDesc: "",
+          makeMethod: this.prepareTcPCard.makeMethod
+        }
+      };
+      config.aOrB = true; // 默认开启A/B卷型
+      config.requiredFields = JSON.parse(config.requiredFields);
+      config.extendFields = JSON.parse(config.extendFields);
+      config.cardTitle = this.getCardTitle(config.titleRule);
+      return config;
+    },
+    // 操作
+    getRequestConfig() {
+      return this.prepareTcPCard.makeMethod === "CUST"
+        ? {
+            headers: {
+              schoolId: this.prepareTcPCard.schoolId
+            }
+          }
+        : {};
+    },
+    getCardData(htmlContent = "", model = "") {
+      let data = this.$refs.CardFreeDesign.getCardData(htmlContent, model);
+      data = {
+        ...data,
+        type: "CUSTOM",
+        ...this.prepareTcPCard
+      };
+      if (this.cardId) data.id = this.cardId;
+      return data;
+    },
+    async toPreview(datas) {
+      await this.toSave(datas);
+
+      const { href } = this.$router.resolve({
+        name: "CardFreePreview",
+        params: {
+          cardId: this.cardId,
+          viewType: "view"
+        }
+      });
+      window.open(href);
+    },
+    async toSave(datas) {
+      datas.status = "STAGE";
+      const result = await saveCard(
+        datas,
+        this.getRequestConfig()
+      ).catch(() => {});
+
+      this.$refs.CardFreeDesign.unloading();
+      if (!result) return;
+
+      this.cardId = result;
+      this.$ls.set("cardId", this.cardId);
+      this.$message.success("保存成功!");
+    },
+    async toSubmit(cardData) {
+      const res = await this.$confirm("确定要提交当前题卡吗?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+      if (res !== "confirm") return;
+
+      window.cardData = cardData;
+      const { href } = this.$router.resolve({
+        name: "CardFreePreview",
+        params: {
+          cardId: 1,
+          viewType: "frame"
+        }
+      });
+      this.cardPreviewUrl = href;
+    },
+    registWindowSubmit() {
+      window.submitCardTemp = async (htmlContent, model) => {
+        const datas = this.getCardData(htmlContent, model);
+        datas.status = "SUBMIT";
+        const result = await saveCard(
+          datas,
+          this.getRequestConfig()
+        ).catch(() => {});
+        this.cardPreviewUrl = "";
+        window.cardData = null;
+        if (result) {
+          this.cardId = result;
+          this.$ls.set("cardId", this.cardId);
+          this.canSave = false;
+          this.$message.success("提交成功!");
+          this.goback();
+        } else {
+          this.$message.error("提交失败,请重新尝试!");
+        }
+      };
+    },
+    toExit() {
+      this.$confirm(
+        "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
+        "提示",
+        {
+          type: "warning"
+        }
+      )
+        .then(() => {
+          this.goback();
+        })
+        .catch(() => {});
+    }
+  },
+  beforeDestroy() {
+    this.$ls.remove("cardId");
+    this.$ls.remove("prepareTcPCard");
+    delete window.submitCardTemp;
+  }
+};
+</script>

+ 131 - 0
src/modules/card/views/CardFreePreview.vue

@@ -0,0 +1,131 @@
+<template>
+  <div :class="classes">
+    <card-free-view
+      v-if="pages.length"
+      ref="CardFreeView"
+      class="preview-body"
+      :pages="pages"
+      :card-config="cardConfig"
+    ></card-free-view>
+  </div>
+</template>
+
+<script>
+import CardFreeView from "../../../../card/modules/free/components/CardFreeView";
+import { cardDetail } from "../api";
+import { deepCopy } from "@/plugins/utils";
+const JsBarcode = require("jsbarcode");
+
+export default {
+  name: "card-free-preview",
+  components: { CardFreeView },
+  data() {
+    return {
+      isPrint: this.$route.params.viewType !== "view",
+      isFrame: this.$route.params.viewType === "frame",
+      cardId: this.$route.params.cardId,
+      pages: [],
+      cardConfig: {}
+    };
+  },
+  computed: {
+    classes() {
+      return [
+        "card-free-preview",
+        {
+          "card-print": this.isPrint
+        }
+      ];
+    }
+  },
+  mounted() {
+    if (this.isFrame) {
+      this.initFrame();
+    } else {
+      this.init();
+    }
+  },
+  methods: {
+    initFrame() {
+      const cardData = window.parent.cardData;
+      if (!cardData) return;
+
+      const { cardConfig, pages } = deepCopy(cardData);
+      let fieldInfos = {};
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          fieldInfos[item.code] = "${" + item.code + "}";
+        });
+      this.cardConfig = cardConfig;
+      this.pages = this.appendFieldInfo(pages, fieldInfos);
+
+      this.$nextTick(() => {
+        const cardContentTemp = this.$refs.CardFreeView.getPreviewTemp(
+          this.$el.outerHTML
+        );
+        const model = this.$refs.CardFreeView.getPageModel(cardData);
+        window.parent &&
+          window.parent.submitCardTemp &&
+          window.parent.submitCardTemp(cardContentTemp, model);
+      });
+    },
+    async init() {
+      const detData = await cardDetail(this.cardId);
+      if (!detData.content) {
+        this.$message.error("很抱歉,当前题卡还没开始制作!");
+        return;
+      }
+      const { cardConfig, pages } = JSON.parse(detData.content);
+      const fieldInfos = this.fetchFieldInfos(cardConfig, {});
+
+      this.cardConfig = cardConfig;
+      this.pages = this.appendFieldInfo(pages, fieldInfos);
+    },
+    fetchFieldInfos(cardConfig, stdInfo) {
+      let fieldInfos = {};
+      const defContent = "相关信息";
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          fieldInfos[item.code] = stdInfo[item.code] || defContent;
+        });
+
+      return fieldInfos;
+    },
+    getBase64Barcode(str) {
+      const canvas = document.createElement("CANVAS");
+      JsBarcode(canvas, str, {
+        width: 2,
+        height: 30,
+        displayValue: false,
+        marginLeft: 20,
+        marginRight: 20,
+        marginTop: 0,
+        marginBottom: 0
+      });
+
+      return canvas.toDataURL();
+    },
+    appendFieldInfo(pages, fieldInfos) {
+      const VALID_ELEMENTS_FOR_EXTERNAL = ["BARCODE", "FILL_FIELD"];
+      pages.forEach(page => {
+        page.columns.forEach(column => {
+          column.elements.forEach(element => {
+            if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;
+
+            if (element.type === "BARCODE") {
+              const field = element.fields[0] && element.fields[0].code;
+              element.content = `data:image/png;base64,${fieldInfos[field]}`;
+              return;
+            }
+
+            element.fieldInfos = fieldInfos;
+          });
+        });
+      });
+      return pages;
+    }
+  }
+};
+</script>

+ 170 - 0
src/modules/card/views/CardPreview.vue

@@ -0,0 +1,170 @@
+<template>
+  <div :class="classes">
+    <div v-if="IS_COMMON_CARD" class="preview-frame" id="preview-frame"></div>
+    <card-view
+      v-if="!IS_COMMON_CARD && pages.length"
+      ref="CardView"
+      class="preview-body"
+      :pages="pages"
+      :card-config="cardConfig"
+    ></card-view>
+  </div>
+</template>
+
+<script>
+import CardView from "../../../../card/components/CardView";
+import { cardDetail } from "../api";
+import { deepCopy } from "@/plugins/utils";
+const JsBarcode = require("jsbarcode");
+
+export default {
+  name: "card-preview",
+  components: { CardView },
+  data() {
+    return {
+      isPrint: this.$route.params.viewType !== "view",
+      isFrame: this.$route.params.viewType === "frame",
+      cardId: this.$route.params.cardId,
+      cardConfig: {},
+      pages: [],
+      IS_COMMON_CARD: false
+    };
+  },
+  computed: {
+    classes() {
+      return [
+        "card-preview",
+        {
+          "card-print": this.isPrint
+        }
+      ];
+    }
+  },
+  mounted() {
+    if (this.isFrame) {
+      this.initFrame();
+    } else {
+      this.init();
+    }
+  },
+  methods: {
+    initFrame() {
+      const cardData = window.parent.cardData;
+      if (!cardData) return;
+
+      const { cardConfig, pages } = deepCopy(cardData);
+      let fieldInfos = {};
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          fieldInfos[item.code] = "${" + item.code + "}";
+        });
+      if (cardConfig.examNumberStyle === "PRINT") {
+        fieldInfos.examNumber = "data:image/png;base64,${examNumber}";
+        fieldInfos.examNumberStr = "${examNumberStr}";
+      }
+
+      if (cardConfig.aOrB && cardConfig.paperType === "PRINT") {
+        fieldInfos.paperType = "data:image/png;base64,${paperType}";
+        fieldInfos.paperTypeName = "${paperTypeName}";
+      }
+
+      this.cardConfig = cardConfig;
+      this.pages = this.appendFieldInfo(pages, fieldInfos);
+
+      this.$nextTick(() => {
+        const cardContentTemp = this.$refs.CardView.getPreviewTemp(
+          this.$el.outerHTML
+        );
+        const model = this.$refs.CardView.getPageModel(cardData);
+        window.parent &&
+          window.parent.submitCardTemp &&
+          window.parent.submitCardTemp(cardContentTemp, model);
+      });
+    },
+    async init() {
+      const detData = await cardDetail(this.cardId);
+
+      this.IS_COMMON_CARD = detData.type === "GENERIC";
+      // 通卡展示
+      if (this.IS_COMMON_CARD) {
+        // TODO:通卡展示逻辑要调整
+        this.$nextTick(() => {
+          this.initHtmlTemp(detData.htmlContent);
+        });
+        return;
+      }
+      // 常规卡展示
+      if (!detData.content) {
+        this.$message.error("很抱歉,当前题卡还没开始制作!");
+        return;
+      }
+      const { cardConfig, pages } = JSON.parse(detData.content);
+      const fieldInfos = this.fetchFieldInfos(cardConfig, {});
+
+      this.cardConfig = cardConfig;
+      this.pages = this.appendFieldInfo(pages, fieldInfos);
+    },
+    initHtmlTemp(htmlTemp) {
+      const iframeDom = document.createElement("iframe");
+      document.getElementById("preview-frame").appendChild(iframeDom);
+      const wwidth = window.innerWidth - 10;
+      const wheight = window.innerHeight - 10;
+      iframeDom.style.cssText = `width: ${wwidth}px;height: ${wheight}px;border:none;outline:none;`;
+      const iframeDoc = iframeDom.contentDocument;
+      iframeDoc.open();
+      iframeDoc.write(htmlTemp);
+      iframeDoc.close();
+    },
+    fetchFieldInfos(cardConfig, stdInfo) {
+      let fieldInfos = {};
+      const defContent = "相关信息";
+      const defNumber = "123456789";
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          fieldInfos[item.code] = stdInfo[item.code] || defContent;
+        });
+      if (cardConfig.examNumberStyle === "PRINT") {
+        fieldInfos.examNumber = this.getBase64Barcode(
+          stdInfo["examNumber"] || defNumber
+        );
+        fieldInfos.examNumberStr = stdInfo["examNumber"] || defNumber;
+      }
+      if (cardConfig.aOrB && cardConfig.paperType === "PRINT") {
+        fieldInfos.paperType = this.getBase64Barcode(
+          stdInfo["paperType"] || defNumber
+        );
+        fieldInfos.paperTypeName = stdInfo["paperTypeName"] || "A";
+      }
+
+      return fieldInfos;
+    },
+    getBase64Barcode(str) {
+      const canvas = document.createElement("CANVAS");
+      JsBarcode(canvas, str, {
+        width: 2,
+        height: 30,
+        displayValue: false,
+        marginLeft: 20,
+        marginRight: 20,
+        marginTop: 0,
+        marginBottom: 0
+      });
+
+      return canvas.toDataURL();
+    },
+    appendFieldInfo(pages, fieldInfos) {
+      pages.forEach((page, pageNo) => {
+        if (pageNo % 2) return;
+        const cardHeadElement = page.columns[0].elements[0];
+
+        if (cardHeadElement.type === "CARD_HEAD") {
+          cardHeadElement.fieldInfos = fieldInfos;
+        }
+      });
+      return pages;
+    }
+  }
+};
+</script>

+ 111 - 0
src/modules/card/views/CardRulePreview.vue

@@ -0,0 +1,111 @@
+<template>
+  <div :class="classes">
+    <card-view
+      v-if="pages.length"
+      ref="CardView"
+      class="preview-body"
+      :pages="pages"
+      :card-config="cardConfig"
+    ></card-view>
+  </div>
+</template>
+
+<script>
+import CardView from "../../../../card/components/CardView";
+import { getNewPage, getCardHeadModel } from "../../../../card/elementModel";
+import { cardConfigInfos } from "../api";
+const JsBarcode = require("jsbarcode");
+
+export default {
+  name: "card-rule-preview",
+  components: { CardView },
+  data() {
+    return {
+      cardRuleId: this.$route.params.cardRuleId,
+      cardConfig: {},
+      pages: []
+    };
+  },
+  computed: {
+    classes() {
+      return ["card-preview", "card-rule-preview"];
+    }
+  },
+  mounted() {
+    if (!this.cardRuleId) {
+      this.$message.error("题卡规则id缺失!");
+      return;
+    }
+    this.init();
+  },
+  methods: {
+    async init() {
+      const data = await cardConfigInfos(this.cardRuleId);
+      if (!data) {
+        this.$message.error("找不到题卡规则!");
+        return;
+      }
+      let config = {
+        ...data,
+        ...{
+          pageSize: "A3",
+          columnNumber: 2,
+          columnGap: 20,
+          showForbidArea: true,
+          cardDesc: ""
+        }
+      };
+      config.aOrB = true; // 默认开启A/B卷型
+      config.requiredFields = JSON.parse(config.requiredFields);
+      config.extendFields = JSON.parse(config.extendFields);
+      config.cardTitle = config.titleRule;
+
+      this.cardConfig = config;
+      let cardHeadElement = getCardHeadModel(this.cardConfig);
+      cardHeadElement.fieldInfos = this.fetchFieldInfos(config, {});
+      let page = getNewPage(0, { pageSize: "A3", columnNumber: 2 });
+      page.columns[0].elements.push(cardHeadElement);
+      this.pages.push(page);
+    },
+    fetchFieldInfos(cardConfig, stdInfo) {
+      let fieldInfos = {};
+      const defContent = "相关信息";
+      const defNumber = "123456789";
+      [...cardConfig.requiredFields, ...cardConfig.extendFields]
+        .filter(item => item.enable)
+        .map(item => {
+          // console.log(item);
+          fieldInfos[item.code] = stdInfo[item.code] || defContent;
+        });
+      if (cardConfig.examNumberStyle === "PRINT") {
+        fieldInfos.examNumber = this.getBase64Barcode(
+          stdInfo["examNumber"] || defNumber
+        );
+        fieldInfos.examNumberStr = stdInfo["examNumber"] || defNumber;
+      }
+      if (cardConfig.aOrB && cardConfig.paperType === "PRINT") {
+        fieldInfos.paperType = this.getBase64Barcode(
+          stdInfo["paperType"] || defNumber
+        );
+        fieldInfos.paperTypeName = stdInfo["paperTypeName"] || "A";
+      }
+
+      return fieldInfos;
+    },
+    getBase64Barcode(str) {
+      const canvas = document.createElement("CANVAS");
+      JsBarcode(canvas, str, {
+        width: 2,
+        height: 30,
+        displayValue: false,
+        marginLeft: 20,
+        marginRight: 20,
+        marginTop: 0,
+        marginBottom: 0
+      });
+
+      return canvas.toDataURL();
+    }
+  }
+};
+</script>

+ 2 - 2
src/modules/customer/views/CustomerCard.vue

@@ -311,7 +311,7 @@ export default {
       });
       // 打开题卡编辑页,创建题卡,并预设需要绑定的任务
       this.$router.push({
-        name: "CardDesign",
+        name: "CardEdit",
         params: {
           cardId: row.cardId
         }
@@ -330,7 +330,7 @@ export default {
     }
   },
   beforeRouteLeave(to, from, next) {
-    if (to.name === "CardDesign") {
+    if (to.name === "CardEdit") {
       this.$ls.set("cachePageInfo", {
         page: this.current,
         filter: this.filter

+ 1 - 1
src/modules/exam/components/ApplyContent.vue

@@ -764,7 +764,7 @@ export default {
     toEditCard() {
       this.cachePrepareTcpCard();
       this.$router.push({
-        name: "CardDesign",
+        name: "CardEdit",
         params: {
           cardId: this.curTaskApply.cardId
         }

+ 1 - 1
src/modules/exam/components/CardOptionDialog.vue

@@ -281,7 +281,7 @@ export default {
         });
         // 打开题卡编辑页,创建题卡,并预设需要绑定的任务
         this.$router.push({
-          name: "CardDesign"
+          name: "CardEdit"
         });
         return;
       }

+ 1 - 1
src/modules/exam/components/CreateTaskApply.vue

@@ -581,7 +581,7 @@ export default {
     toEditCard() {
       this.cachePrepareTcpCard();
       this.$router.push({
-        name: "CardDesign",
+        name: "CardEdit",
         params: {
           cardId: this.examTaskDetail.cardId
         }

+ 1 - 1
src/modules/exam/components/ModifyTaskPaper.vue

@@ -380,7 +380,7 @@ export default {
     toEditCard() {
       this.cachePrepareTcpCard();
       this.$router.push({
-        name: "CardDesign",
+        name: "CardEdit",
         params: {
           cardId: this.curTaskApply.cardId
         }

+ 1 - 1
src/modules/exam/components/WaitTaskFlow.vue

@@ -121,7 +121,7 @@ export default {
   },
   beforeRouteLeave(to, from, next) {
     console.log(to);
-    if (to.name === "CardDesign") {
+    if (to.name === "CardEdit") {
       this.$ls.set("cachePageInfo", {
         page: this.current,
         curRowId: this.curTask.id

+ 1 - 1
src/modules/exam/views/TaskApplyManage.vue

@@ -349,7 +349,7 @@ export default {
     }
   },
   beforeRouteLeave(to, from, next) {
-    if (to.name === "CardDesign") {
+    if (to.name === "CardEdit") {
       this.$ls.set("cachePageInfo", {
         page: this.current,
         filter: this.filter,

+ 1 - 1
src/modules/exam/views/TaskPaperManage.vue

@@ -382,7 +382,7 @@ export default {
     }
   },
   beforeRouteLeave(to, from, next) {
-    if (to.name === "CardDesign") {
+    if (to.name === "CardEdit") {
       this.$ls.set("cachePageInfo", {
         page: this.current,
         filter: this.filter,

+ 1 - 1
src/modules/exam/views/WaitTask.vue

@@ -51,7 +51,7 @@ export default {
     }
   },
   beforeRouteEnter(to, from, next) {
-    if (from.name === "CardDesign") {
+    if (from.name === "CardEdit") {
       next(vm => {
         vm.tab = "flow";
       });