Browse Source

提交:表头分离型表格

dusenyao 3 years ago
parent
commit
74c130d041

+ 10 - 0
src/components/Adult/Preview.vue

@@ -340,6 +340,14 @@
                     :task-model="TaskModel"
                   />
                 </template>
+                <template v-if="itemss.type === 'header_separate'">
+                  <header-separate
+                    v-if="refresh"
+                    :cur-que="itemss.data"
+                    :theme-color="themeColor"
+                    :task-model="TaskModel"
+                  />
+                </template>
                 <template v-if="itemss.type == 'toneSelect_chs'">
                   <SelectTone
                     v-if="refresh"
@@ -556,6 +564,7 @@ import VoiceMatrix from "./preview/VoiceMatrix.vue"; // 语音矩阵
 import SelectDrag from "./preview/SelectDrag.vue"; // 选择 -> 拖拽
 import FillDrag from "./preview/FillDrag.vue"; // 填空 -> 拖拽
 import ConfigurableTable from "./preview/ConfigurableTable.vue"; // 可配置表格
+import HeaderSeparate from "./preview/HeaderSeparate.vue"; // 表头分离表格
 import RecordModule from "./preview/RecordModule.vue"; // 录音组件
 import UploadControlView from "./preview/UploadControlView.vue"; // 预览控件
 import DialogueAnswerViewChs from "./preview/DialogueArticleViewChs/DialogueAnswerViewChs.vue"; // 文章模板
@@ -595,6 +604,7 @@ export default {
     FillDrag,
     SelectDrag,
     ConfigurableTable,
+    HeaderSeparate,
     RecordModule,
     UploadControlView,
     DialogueAnswerViewChs,

+ 5 - 4
src/components/Adult/common/data.js

@@ -75,10 +75,11 @@ let fnData = [
     type: "fill_drag",
     name: "填空-拖拽"
   },
-  // {
-  //   type: 'config_table',
-  //   name: '可配置表格'
-  // },
+  {
+    type: "config_table",
+    name: "可配置表格"
+  },
+  { type: "header_separate", name: "表头分离表格" },
   //  {
   //     type: "divider",
   //     name: "分割线",

+ 0 - 177
src/components/Adult/inputModules/ConfigurableTable.vue

@@ -1,177 +0,0 @@
-<!-- 可配置表格 -->
-<template>
-  <div class="config-table">
-    <div class="config-table-options">
-      <div>
-        表格阴影:
-        <el-radio v-model="curQue.isShadow" :label="true">有</el-radio>
-        <el-radio v-model="curQue.isShadow" :label="false">无</el-radio>
-      </div>
-      <div>
-        表格标题:
-        <el-radio v-model="curQue.isTitle" :label="true">有</el-radio>
-        <el-radio v-model="curQue.isTitle" :label="false">无</el-radio>
-        <el-input v-show="curQue.isTitle" v-model="curQue.title" type="text" />
-      </div>
-      <div>
-        表头行背景色:
-        <el-color-picker
-          v-model="curQue.headerBgColor"
-          size="mini"
-          :predefine="predefineColors"
-        />
-      </div>
-      <div>
-        内容对齐方式:
-        <el-radio v-model="curQue.textAlign" label="left">左对齐</el-radio>
-        <el-radio v-model="curQue.textAlign" label="center">居中</el-radio>
-        <el-radio v-model="curQue.textAlign" label="right">右对齐</el-radio>
-      </div>
-      <div>
-        外层边框突出显示:
-        <el-radio v-model="curQue.isTitle" :label="true">是</el-radio>
-        <el-radio v-model="curQue.isTitle" :label="false">否</el-radio>
-      </div>
-      <div>
-        <el-button>增加一行表头</el-button>
-        <el-button>增加一列</el-button>
-        <el-button>增加一行</el-button>
-      </div>
-    </div>
-    <div class="config-table-preview">
-      <table class="preview-table">
-        <caption v-if="curQue.isTitle">
-          {{
-            curQue.title
-          }}
-        </caption>
-        <thead>
-          <tr v-for="(item, i) in curQue.tableData.headers" :key="i">
-            <th v-for="(header, j) in item.content" :key="j">
-              {{ header.text }}
-            </th>
-            <th>
-              <el-switch v-model="item.isMerge" active-text="合并" />
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr
-            v-for="(row, i) in curQue.tableData.body"
-            :key="`row-${i}`"
-          >
-            <td v-for="(col, j) in row.content" :key="`col-${j}`">
-              <el-button size="mini">编辑</el-button>
-            </td>
-            <td>
-              <el-button size="mini">删除</el-button>
-              <el-input v-model.number="row.width">
-                <template slot="prepend">宽度</template>
-              </el-input>
-            </td>
-          </tr>
-        </tbody>
-      </table>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  props: {
-    curQue: {
-      type: Object,
-      default: () => {
-        return {
-          isFirst: true,
-          type: "config_table",
-          name: "可配置表格",
-          isShadow: false,
-          isTitle: false,
-          title: "",
-          textAlign: "center",
-          headerBgColor: "#fff",
-          tableData: {
-            headers: [
-              {
-                isMerge: false,
-                content: [
-                  {
-                    text: "apple"
-                  },
-                  {
-                    text: "table"
-                  }
-                ]
-              },
-              {
-                isMerge: true,
-                content: [{ text: "banner" }, { text: "text" }]
-              }
-            ],
-            body: [
-              {
-                width: 100,
-                content: [{ text: 1 }, { text: 2 }]
-              },
-              { width: 100, content: [{ text: 3 }, { text: 4 }] }
-            ]
-          }
-        };
-      }
-    },
-    changeCurQue: {
-      type: Function,
-      required: true
-    }
-  },
-  data() {
-    return {
-      predefineColors: ["#f6d5a4", "#efeff9", "#e2e1c8"]
-    };
-  },
-  created() {
-    if (this.curQue.isFirst) {
-      this.curQue.isFirst = false;
-      this.changeCurQue(this.curQue);
-    }
-  },
-  methods: {}
-};
-</script>
-
-<style lang="scss" scoped>
-.config-table {
-  border: 1px solid #ccc;
-  border-radius: 4px;
-  padding: 8px 12px;
-
-  &-options {
-    > div {
-      margin-bottom: 12px;
-    }
-  }
-
-  &-preview {
-    border-top: 1px solid #ccc;
-    padding-top: 12px;
-
-    .preview-table {
-      border-collapse: collapse;
-
-      td,
-      th {
-        padding: 8px;
-      }
-
-      td {
-        border: 1px solid #aaa;
-      }
-
-      th {
-        border: 1px solid #aaa;
-      }
-    }
-  }
-}
-</style>

+ 123 - 0
src/components/Adult/inputModules/ConfigurableTable/components/CellEdit.vue

@@ -0,0 +1,123 @@
+<template>
+  <el-dialog
+    title="单元格编辑"
+    :visible="visible"
+    :before-close="close"
+    :modal="false"
+  >
+    <label>内容类型:</label>
+    <el-select v-model="cellData.type">
+      <el-option
+        v-for="{ value, label } in cellTypeList"
+        :key="value"
+        :label="label"
+        :value="value"
+      />
+    </el-select>
+    <div class="dialog-container">
+      <el-input v-if="cellData.type === 'content'" v-model="cellData.text" />
+
+      <sentence-segword-chs
+        v-else-if="cellData.type === 'pinyin'"
+        :cur-que="cellData.sentence_data"
+        type="adultinput"
+        :is-hide-py-position="true"
+      />
+
+      <template v-else-if="cellData.type === 'preContent'">
+        <el-input v-model="cellData.prefix">
+          <template slot="prepend">前缀</template>
+        </el-input>
+
+        <el-input v-model="cellData.text">
+          <template slot="prepend">内容</template>
+        </el-input>
+      </template>
+
+      <template v-else-if="cellData.type === 'twoAnnotation'">
+        <el-input v-model="cellData.text">
+          <template slot="prepend">英文</template>
+        </el-input>
+
+        <sentence-segword-chs
+          :cur-que="cellData.sentence_data"
+          type="adultinput"
+          :is-hide-py-position="true"
+        />
+      </template>
+    </div>
+    <div slot="footer">
+      <el-button type="primary" @click="confirm">确定</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import SentenceSegwordChs from "@/components/Adult/inputModules/SentenceSegwordChs/index.vue";
+
+export default {
+  components: { SentenceSegwordChs },
+  props: {
+    visible: {
+      type: Boolean,
+      required: true
+    },
+    body: {
+      type: Array,
+      required: true
+    },
+    curIndex: {
+      type: Object,
+      required: true
+    }
+  },
+  data() {
+    return {
+      cellTypeList: [
+        {
+          label: "内容",
+          value: "content"
+        },
+        {
+          label: "前缀 + 内容",
+          value: "preContent"
+        },
+        {
+          label: "拼音",
+          value: "pinyin"
+        },
+        {
+          label: '中文 + 英文 + 拼音',
+          value: 'twoAnnotation'
+        }
+      ]
+    };
+  },
+  computed: {
+    cellData() {
+      return this.body[this.curIndex.row].content[this.curIndex.col];
+    }
+  },
+  methods: {
+    close() {
+      this.$emit("close");
+    },
+
+    confirm() {
+      this.$emit("close");
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+::v-deep .dialog-container {
+  margin-top: 12px;
+}
+</style>
+
+<style lang="scss">
+.v-modal {
+  z-index: 1000 !important;
+}
+</style>

+ 310 - 0
src/components/Adult/inputModules/ConfigurableTable/index.vue

@@ -0,0 +1,310 @@
+<!-- 可配置表格 -->
+<template>
+  <div class="config-table">
+    <div class="config-table-options">
+      <div>
+        表格阴影:
+        <el-radio v-model="curQue.isShadow" :label="true">有</el-radio>
+        <el-radio v-model="curQue.isShadow" :label="false">无</el-radio>
+      </div>
+      <div>
+        表格标题:
+        <el-radio v-model="curQue.isTitle" :label="true">有</el-radio>
+        <el-radio v-model="curQue.isTitle" :label="false">无</el-radio>
+        <el-input v-show="curQue.isTitle" v-model="curQue.title" type="text" />
+      </div>
+      <div>
+        表头行背景色:
+        <el-color-picker
+          v-model="curQue.headerBgColor"
+          size="mini"
+          :predefine="predefineColors"
+        />
+      </div>
+      <div>
+        内容对齐方式:
+        <el-radio v-model="curQue.textAlign" label="left">左对齐</el-radio>
+        <el-radio v-model="curQue.textAlign" label="center">居中</el-radio>
+        <el-radio v-model="curQue.textAlign" label="right">右对齐</el-radio>
+      </div>
+      <div>
+        拼音位置:
+        <el-radio v-model="curQue.pinyinPosition" label="top">上</el-radio>
+        <el-radio v-model="curQue.pinyinPosition" label="bottom">下</el-radio>
+        <el-radio v-model="curQue.pinyinPosition" label="left">左</el-radio>
+        <el-radio v-model="curQue.pinyinPosition" label="right">右</el-radio>
+      </div>
+      <div>
+        外层边框突出显示:
+        <el-radio v-model="curQue.marginHighlight" :label="true">是</el-radio>
+        <el-radio v-model="curQue.marginHighlight" :label="false">否</el-radio>
+      </div>
+      <div>
+        <el-button @click="addTableHeader">增加一行表头</el-button>
+        <el-button @click="addCol">增加一列</el-button>
+        <el-button @click="addRow">增加一行</el-button>
+      </div>
+    </div>
+    <div class="config-table-preview">
+      <table class="preview-table">
+        <caption v-if="curQue.isTitle">
+          {{
+            curQue.title
+          }}
+        </caption>
+        <thead>
+          <tr v-for="(item, i) in curQue.tableData.headers" :key="i">
+            <th v-for="(header, j) in item.content" :key="j">
+              <el-input v-model="header.text" />
+            </th>
+            <th>
+              <el-switch v-model="item.isMerge" active-text="合并" />
+              <el-button
+                size="mini"
+                @click="deleteThead(i)"
+              >删除表头</el-button>
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(row, i) in curQue.tableData.body" :key="`row-${i}`">
+            <td v-for="(col, j) in row.content" :key="`col-${j}`">
+              <el-button size="mini" @click="edit(i, j)">编辑</el-button>
+            </td>
+            <td>
+              <el-button size="mini" @click="deleteRow(i)">删除行</el-button>
+            </td>
+          </tr>
+          <tr>
+            <td
+              v-for="(item, k) in curQue.tableData.colsConfig.width"
+              :key="`tfoot-${k}`"
+            >
+              <el-input v-model.number="item.val" size="mini">
+                <template slot="prepend">宽度</template>
+              </el-input>
+              <el-button size="mini" @click="deleteCol(k)">删除列</el-button>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <cell-edit
+      :visible="visible"
+      :body="curQue.tableData.body"
+      :cur-index="curIndex"
+      @close="close"
+    />
+  </div>
+</template>
+
+<script>
+import CellEdit from "./components/CellEdit.vue";
+
+export default {
+  components: { CellEdit },
+  props: {
+    curQue: {
+      type: Object,
+      default: () => {
+        return {
+          isFirst: true,
+          type: "config_table",
+          name: "可配置表格",
+          isShadow: false,
+          isTitle: false,
+          marginHighlight: false,
+          pinyinPosition: 'top',
+          title: "",
+          textAlign: "center",
+          headerBgColor: "#fff",
+          tableData: {
+            headers: [],
+            body: [
+              {
+                content: [
+                  {
+                    type: "content",
+                    text: "",
+                    prefix: "",
+                    sentence_data: {
+                      type: "sentence_segword_chs",
+                      name: "句子分词",
+                      pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+                      sentence: "", // 句子
+                      segList: [], // 分词结果
+                      seg_words: "",
+                      wordsList: []
+                    }
+                  }
+                ]
+              }
+            ],
+            colsConfig: {
+              width: [{ val: 100 }]
+            }
+          }
+        };
+      }
+    },
+    changeCurQue: {
+      type: Function,
+      required: true
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      curIndex: {
+        col: 0,
+        row: 0
+      },
+      predefineColors: ["#f6d5a4", "#efeff9", "#e2e1c8"]
+    };
+  },
+  computed: {
+    rows() {
+      return this.curQue.tableData.body.length;
+    },
+    cols() {
+      if (this.rows.length <= 0) return 0;
+      return this.curQue.tableData.body[0].content.length;
+    }
+  },
+  created() {
+    if (this.curQue.isFirst) {
+      this.curQue.isFirst = false;
+      this.changeCurQue(this.curQue);
+    }
+  },
+  methods: {
+    edit(i, j) {
+      this.curIndex = {
+        col: j,
+        row: i
+      };
+      this.visible = true;
+    },
+    close() {
+      this.visible = false;
+    },
+    addTableHeader() {
+      if (this.cols <= 0 && this.rows <= 0) {
+        return this.$message.warning("请先添加行或列");
+      }
+      let content = [];
+      for (let i = 0; i < this.cols; i++) {
+        content.push({
+          text: ""
+        });
+      }
+      this.curQue.tableData.headers.push({
+        isMerge: false,
+        content
+      });
+    },
+    deleteThead(i) {
+      this.curQue.tableData.headers.splice(i, 1);
+    },
+
+    addCol() {
+      this.curQue.tableData.body.forEach(({ content }) => {
+        content.push({
+          type: "content",
+          text: "",
+          prefix: "",
+          sentence_data: {
+            type: "sentence_segword_chs",
+            name: "句子分词",
+            pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+            sentence: "", // 句子
+            segList: [], // 分词结果
+            seg_words: "",
+            wordsList: []
+          }
+        });
+      });
+      this.curQue.tableData.headers.forEach(({ content }) => {
+        content.push({
+          text: ""
+        });
+      });
+      this.curQue.tableData.colsConfig.width.push({ val: 100 });
+    },
+    deleteCol(k) {
+      if (this.cols <= 1) return this.$message.warning("必须留一列");
+      this.curQue.tableData.body.forEach(({ content }) => {
+        content.splice(k, 1);
+      });
+      this.curQue.tableData.headers.forEach(({ content }) => {
+        content.splice(k, 1);
+      });
+      this.curQue.tableData.colsConfig.width.splice(k, 1);
+    },
+
+    addRow() {
+      let content = [];
+      for (let i = 0; i < this.cols; i++) {
+        content.push({
+          type: "content",
+          text: "",
+          prefix: "",
+          sentence_data: {
+            type: "sentence_segword_chs",
+            name: "句子分词",
+            pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+            sentence: "", // 句子
+            segList: [], // 分词结果
+            seg_words: "",
+            wordsList: []
+          }
+        });
+      }
+      this.curQue.tableData.body.push({
+        content
+      });
+    },
+    deleteRow(i) {
+      if (this.rows <= 1) return this.$message.warning("必须留一行");
+      this.curQue.tableData.body.splice(i, 1);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.config-table {
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  padding: 8px 12px;
+
+  &-options {
+    > div {
+      margin-bottom: 12px;
+    }
+  }
+
+  &-preview {
+    border-top: 1px solid #ccc;
+    padding-top: 12px;
+
+    .preview-table {
+      border-collapse: collapse;
+
+      td,
+      th {
+        padding: 8px;
+      }
+
+      td {
+        border: 1px solid #aaa;
+      }
+
+      th {
+        border: 1px solid #aaa;
+      }
+    }
+  }
+}
+</style>

+ 94 - 0
src/components/Adult/inputModules/HeaderSeparate/components/CellEdit.vue

@@ -0,0 +1,94 @@
+<template>
+  <el-dialog
+    title="单元格编辑"
+    :visible="visible"
+    :before-close="close"
+    :modal="false"
+  >
+    <label>内容类型:</label>
+    <el-select v-model="cellData.type">
+      <el-option
+        v-for="{ value, label } in cellTypeList"
+        :key="value"
+        :label="label"
+        :value="value"
+      />
+    </el-select>
+    <div class="dialog-container">
+      <el-input v-show="cellData.type === 'content'" v-model="cellData.text" />
+
+      <sentence-segword-chs
+        v-if="cellData.type === 'pinyin'"
+        :cur-que="cellData.sentence_data"
+        type="adultinput"
+        :is-hide-py-position="true"
+      />
+
+    </div>
+    <div slot="footer">
+      <el-button type="primary" @click="confirm">确定</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import SentenceSegwordChs from "@/components/Adult/inputModules/SentenceSegwordChs/index.vue";
+
+export default {
+  components: { SentenceSegwordChs },
+  props: {
+    visible: {
+      type: Boolean,
+      required: true
+    },
+    body: {
+      type: Array,
+      required: true
+    },
+    curIndex: {
+      type: Object,
+      required: true
+    }
+  },
+  data() {
+    return {
+      cellTypeList: [
+        {
+          label: "内容",
+          value: "content"
+        },
+        {
+          label: "拼音",
+          value: "pinyin"
+        }
+      ]
+    };
+  },
+  computed: {
+    cellData() {
+      return this.body[this.curIndex.row].content[this.curIndex.col];
+    }
+  },
+  methods: {
+    close() {
+      this.$emit("close");
+    },
+
+    confirm() {
+      this.$emit("close");
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+::v-deep .dialog-container {
+  margin-top: 12px;
+}
+</style>
+
+<style lang="scss">
+.v-modal {
+  z-index: 1000 !important;
+}
+</style>

+ 316 - 0
src/components/Adult/inputModules/HeaderSeparate/index.vue

@@ -0,0 +1,316 @@
+<template>
+  <div class="header-separate">
+    <div class="header-separate-options">
+      <div>
+        表头英文位置:
+        <el-radio
+          v-model="curQue.headerEnglishPosition"
+          label="top"
+        >上</el-radio>
+        <el-radio
+          v-model="curQue.headerEnglishPosition"
+          label="right"
+        >右</el-radio>
+        <el-radio
+          v-model="curQue.headerEnglishPosition"
+          label="bottom"
+        >下</el-radio>
+        <el-radio
+          v-model="curQue.headerEnglishPosition"
+          label="left"
+        >左</el-radio>
+      </div>
+      <div>
+        单元格拼音位置:
+        <el-radio v-model="curQue.pinyinPosition" label="top">上</el-radio>
+        <el-radio v-model="curQue.pinyinPosition" label="right">右</el-radio>
+        <el-radio v-model="curQue.pinyinPosition" label="bottom">下</el-radio>
+        <el-radio v-model="curQue.pinyinPosition" label="left">左</el-radio>
+      </div>
+      <div>
+        首列对齐方式:
+        <el-radio v-model="curQue.firstColAligin" label="center">居中</el-radio>
+        <el-radio v-model="curQue.firstColAligin" label="follow">跟随内容</el-radio>
+      </div>
+      <div>
+        内容对齐方式:
+        <el-radio v-model="curQue.textAlign" label="left">左对齐</el-radio>
+        <el-radio v-model="curQue.textAlign" label="center">居中</el-radio>
+        <el-radio v-model="curQue.textAlign" label="right">右对齐</el-radio>
+      </div>
+      <div>
+        <el-button @click="addCol">增加一列</el-button>
+        <el-button @click="addRow">增加一行</el-button>
+      </div>
+    </div>
+    <div class="header-separate-preview">
+      <table class="preview-table">
+        <thead>
+          <tr>
+            <th v-for="(num, i) in curQue.tableData.headers" :key="`th-${i}`">
+              <el-input v-model="num.text">
+                <template slot="prepend">文本</template>
+              </el-input>
+              <el-input v-model="num.english">
+                <template slot="prepend">英文</template>
+              </el-input>
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(row, i) in curQue.tableData.body" :key="`tr-${i}`">
+            <td v-for="(col, j) in row.content" :key="`td-${j}`">
+              <el-button @click="edit(i, j)">编辑</el-button>
+            </td>
+            <td>
+              <el-button size="mini" @click="deleteRow(i)">删除行</el-button>
+            </td>
+          </tr>
+          <tr>
+            <td
+              v-for="(width, i) in curQue.tableData.colsConfig.width"
+              :key="`width-${i}`"
+            >
+              <el-input v-model.number="width.val" size="mini">
+                <template slot="prepend">宽度</template>
+              </el-input>
+              <el-button size="mini" @click="deleteCol(i)">删除列</el-button>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <cell-edit
+      :visible="visible"
+      :body="curQue.tableData.body"
+      :cur-index="curIndex"
+      @close="close"
+    />
+  </div>
+</template>
+
+<script>
+import CellEdit from './components/CellEdit';
+
+export default {
+  components: { CellEdit },
+  props: {
+    curQue: {
+      type: Object,
+      default: () => {
+        return {
+          isFirst: true,
+          type: "header_separate",
+          name: "表头分离表格",
+          pinyinPosition: "top",
+          headerEnglishPosition: "top",
+          firstColAligin: "center",
+          textAlign: "center",
+          tableData: {
+            headers: [
+              {
+                text: "",
+                english: ""
+              },
+              {
+                text: "",
+                english: ""
+              },
+              {
+                text: "",
+                english: ""
+              }
+            ],
+            body: [
+              {
+                content: [
+                  {
+                    type: "content",
+                    text: "",
+                    sentence_data: {
+                      type: "sentence_segword_chs",
+                      name: "句子分词",
+                      pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+                      sentence: "", // 句子
+                      segList: [], // 分词结果
+                      seg_words: "",
+                      wordsList: []
+                    }
+                  },
+                  {
+                    type: "content",
+                    text: "",
+                    sentence_data: {
+                      type: "sentence_segword_chs",
+                      name: "句子分词",
+                      pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+                      sentence: "", // 句子
+                      segList: [], // 分词结果
+                      seg_words: "",
+                      wordsList: []
+                    }
+                  },
+                  {
+                    type: "content",
+                    text: "",
+                    sentence_data: {
+                      type: "sentence_segword_chs",
+                      name: "句子分词",
+                      pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+                      sentence: "", // 句子
+                      segList: [], // 分词结果
+                      seg_words: "",
+                      wordsList: []
+                    }
+                  }
+                ]
+              }
+            ],
+            colsConfig: {
+              width: [{ val: 100 }, { val: 100 }, { val: 100 }]
+            }
+          }
+        };
+      }
+    },
+    changeCurQue: {
+      type: Function,
+      required: true
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      curIndex: {
+        col: 0,
+        row: 0
+      }
+    };
+  },
+  computed: {
+    rows() {
+      return this.curQue.tableData.body.length;
+    },
+    cols() {
+      if (this.rows.length <= 0) return 0;
+      return this.curQue.tableData.body[0].content.length;
+    }
+  },
+  created() {
+    if (this.curQue.isFirst) {
+      this.curQue.isFirst = false;
+      this.changeCurQue(this.curQue);
+    }
+  },
+  methods: {
+    addCol() {
+      this.curQue.tableData.body.forEach(({ content }) => {
+        content.push({
+          type: "content",
+          text: "",
+          sentence_data: {
+            type: "sentence_segword_chs",
+            name: "句子分词",
+            pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+            sentence: "", // 句子
+            segList: [], // 分词结果
+            seg_words: "",
+            wordsList: []
+          }
+        });
+      });
+      this.curQue.tableData.headers.push({
+        text: "",
+        english: ""
+      });
+      this.curQue.tableData.colsConfig.width.push({ val: 100 });
+    },
+    deleteCol(i) {
+      if (this.cols <= 3) return this.$message.warning("必须留三列");
+      this.curQue.tableData.body.forEach(({ content }) => {
+        content.splice(i, 1);
+      });
+      this.curQue.tableData.headers.splice(i, 1);
+      this.curQue.tableData.colsConfig.width.splice(i, 1);
+    },
+
+    addRow() {
+      let content = [];
+      for (let i = 0; i < this.cols; i++) {
+        content.push({
+          type: "content",
+          text: "",
+          sentence_data: {
+            type: "sentence_segword_chs",
+            name: "句子分词",
+            pyPosition: "top", // top 拼音在上面;bottom 拼音在下面
+            sentence: "", // 句子
+            segList: [], // 分词结果
+            seg_words: "",
+            wordsList: []
+          }
+        });
+      }
+      this.curQue.tableData.body.push({
+        content
+      });
+    },
+    deleteRow(i) {
+      if (this.rows <= 1) return this.$message.warning("必须留一行");
+      this.curQue.tableData.body.splice(i, 1);
+    },
+
+    edit(i, j) {
+      this.curIndex = {
+        col: j,
+        row: i
+      };
+      this.visible = true;
+    },
+    close() {
+      this.visible = false;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.header-separate {
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  padding: 8px 12px;
+
+  &-options {
+    > div {
+      margin-bottom: 12px;
+    }
+  }
+
+  &-preview {
+    border-top: 1px solid #ccc;
+    padding-top: 12px;
+
+    .preview-table {
+      border-collapse: collapse;
+
+      td,
+      th {
+        padding: 8px;
+
+        .el-input {
+          max-width: 220px;
+        }
+      }
+
+      td {
+        border: 1px solid #aaa;
+      }
+
+      th {
+        border: 1px solid #aaa;
+      }
+    }
+  }
+}
+</style>

+ 226 - 8
src/components/Adult/preview/ConfigurableTable.vue

@@ -4,9 +4,18 @@
       :style="{
         'box-shadow': `${
           curQue.isShadow ? '4px 4px 4px rgba(0, 0, 0, 0.3)' : ''
-        }`
+        }`,
+        border: `${curQue.marginHighlight ? '1.1px solid #949494' : ''}`
       }"
     >
+      <colgroup>
+        <col
+          v-for="(item, i) in curQue.tableData.colsConfig.width"
+          :key="`col-${i}`"
+          :style="{ width: `${item.val}px` }"
+        >
+      </colgroup>
+
       <caption v-if="curQue.isTitle">
         {{
           curQue.title
@@ -23,9 +32,11 @@
         >
           <template v-if="header.isMerge">
             <th :colspan="header.content.length">
-              <span v-for="(item, j) in header.content" :key="`th-${i}-${j}`">
-                {{ item.text }}
-              </span>
+              <div class="thead-merge">
+                <span v-for="(item, j) in header.content" :key="`th-${i}-${j}`">
+                  {{ item.text }}
+                </span>
+              </div>
             </th>
           </template>
           <template v-else>
@@ -36,10 +47,130 @@
         </tr>
       </thead>
 
-      <tbody>
+      <tbody
+        :style="{
+          'text-align': `${curQue.textAlign}`
+        }"
+      >
         <tr v-for="(data, i) in curQue.tableData.body" :key="`tr-${i}`">
-          <td v-for="(item, j) in data" :key="`td-${i}-${j}`">
-            {{ item.text }}
+          <td v-for="(item, j) in data.content" :key="`td-${i}-${j}`">
+            <template v-if="item.type === 'content'">
+              <template v-if="item.text.length > 0">
+                {{ item.text }}
+              </template>
+              <template v-else>
+                <el-input
+                  v-model="item.answer"
+                  type="textarea"
+                  :placeholder="`${isAnswerMode ? '':'输入'}`"
+                  :disabled="isAnswerMode"
+                  :autosize="{ minRows: 1, maxRows: 6 }"
+                  @input="enterAnswer(i, j, $event)"
+                />
+              </template>
+            </template>
+
+            <div v-else-if="item.type == 'preContent'" class="preContent">
+              <span>{{ item.prefix }}</span>
+              <template v-if="item.text.length > 0">
+                {{ item.text }}
+              </template>
+              <template v-else>
+                <el-input
+                  v-model="item.answer"
+                  type="textarea"
+                  :placeholder="`${isAnswerMode ? '':'输入'}`"
+                  :disabled="isAnswerMode"
+                  :autosize="{ minRows: 1, maxRows: 6 }"
+                  @input="enterAnswer(i, j, $event)"
+                />
+              </template>
+            </div>
+
+            <div
+              v-else-if="
+                item.type === 'pinyin' || item.type === 'twoAnnotation'
+              "
+              class="sentence"
+              :style="pinyinStyle"
+            >
+              <div>
+                <span
+                  v-for="({ pinyin, chs }, k) in item.sentence_data.wordsList"
+                  :key="
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'pinyin'
+                        : 'chs'
+                    }-${k}`
+                  "
+                  :class="[
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'pinyin'
+                        : 'chs'
+                    }`
+                  ]"
+                >
+                  {{
+                    curQue.pinyinPosition === "top" ||
+                      curQue.pinyinPosition == "left"
+                      ? pinyin
+                      : chs
+                  }}
+                </span>
+                <span
+                  v-if="
+                    item.type === 'twoAnnotation' &&
+                      (curQue.pinyinPosition === 'right' ||
+                        curQue.pinyinPosition === 'bottom')
+                  "
+                  class="english"
+                >
+                  ({{ item.text }})
+                </span>
+              </div>
+              <div>
+                <span
+                  v-for="({ pinyin, chs }, k) in item.sentence_data.wordsList"
+                  :key="
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'chs'
+                        : 'pinyin'
+                    }-${k}`
+                  "
+                  :class="[
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'chs'
+                        : 'pinyin'
+                    }`
+                  ]"
+                >
+                  {{
+                    curQue.pinyinPosition === "top" ||
+                      curQue.pinyinPosition == "left"
+                      ? chs
+                      : pinyin
+                  }}
+                </span>
+                <span
+                  v-if="
+                    item.type === 'twoAnnotation' &&
+                      (curQue.pinyinPosition === 'top' ||
+                        curQue.pinyinPosition === 'left')
+                  "
+                  class="english"
+                >
+                  ({{ item.text }})
+                </span>
+              </div>
+            </div>
           </td>
         </tr>
       </tbody>
@@ -60,7 +191,58 @@ export default {
     }
   },
   data() {
-    return {};
+    return {
+      isAnswerMode: false
+    };
+  },
+  computed: {
+    pinyinStyle() {
+      let pyPos = this.curQue.pinyinPosition;
+      if (pyPos === "left") {
+        return {
+          "column-gap": "16px"
+        };
+      }
+
+      if (pyPos === "top") {
+        return {
+          "flex-direction": "column"
+        };
+      }
+
+      if (pyPos === "right") {
+        return {
+          "column-gap": "16px"
+        };
+      }
+
+      if (pyPos === "bottom") {
+        return {
+          "flex-direction": "column"
+        };
+      }
+    }
+  },
+  created() {
+    const Bookanswer = this.curQue.Bookanswer;
+    if (Bookanswer) {
+      this.isAnswerMode = true;
+      for (const key in Bookanswer) {
+        let { col, row, value } = Bookanswer[key];
+        this.curQue.tableData.body[col].content[row].answer = value;
+      }
+    } else {
+      this.$set(this.curQue, "Bookanswer", {});
+    }
+  },
+  methods: {
+    enterAnswer(i, j, value) {
+      this.curQue.Bookanswer[`${i}-${j}`] = {
+        col: i,
+        row: j,
+        value
+      };
+    }
   }
 };
 </script>
@@ -68,8 +250,10 @@ export default {
 <style lang="scss" scoped>
 .config-table {
   width: 100%;
+  margin-bottom: 16px;
 
   table {
+    table-layout: fixed;
     font-size: 16px;
     color: #404040;
     border-collapse: collapse;
@@ -83,9 +267,43 @@ export default {
 
     th,
     td {
+      font-weight: normal;
       border: 1px solid #e6e6e6;
       padding: 8px 12px;
     }
+
+    .thead-merge {
+      display: flex;
+      justify-content: space-evenly;
+    }
+
+    .preContent {
+      display: flex;
+      align-items: center;
+
+      ::v-deep .el-textarea__inner {
+        padding-top: 2px;
+      }
+    }
+
+    td {
+      .sentence {
+        display: flex;
+
+        .pinyin {
+          font-family: "GB-PINYINOK-B";
+          opacity: 0.6;
+        }
+
+        .chs {
+          font-family: "FZJCGFKTK";
+        }
+
+        .english {
+          opacity: 0.6;
+        }
+      }
+    }
   }
 }
 </style>

+ 1 - 0
src/components/Adult/preview/FillDrag.vue

@@ -195,6 +195,7 @@ $image-size: 64px;
 .fill-drag {
   width: 100%;
   color: #000;
+  margin-bottom: 16px;
 
   %drag-option {
     width: $image-size;

+ 330 - 0
src/components/Adult/preview/HeaderSeparate.vue

@@ -0,0 +1,330 @@
+<template>
+  <div class="header-separate">
+    <table>
+      <colgroup>
+        <col
+          v-for="(item, i) in curQue.tableData.colsConfig.width"
+          :key="`col-${i}`"
+          :style="{ width: `${item.val}px` }"
+        >
+      </colgroup>
+
+      <thead>
+        <tr>
+          <th
+            v-for="({ text, english }, i) in curQue.tableData.headers"
+            :key="`th-${i}`"
+          >
+            <div
+              class="thead-content"
+              :style="theadStyle"
+            >
+              <span class="chs">{{ text }}</span><span class="english">{{ english }}</span>
+            </div>
+          </th>
+        </tr>
+      </thead>
+
+      <tbody
+        :style="{
+          'text-align': `${curQue.textAlign}`
+        }"
+      >
+        <tr v-for="(row, i) in curQue.tableData.body" :key="`tr-${i}`">
+          <td
+            v-for="(col, j) in row.content"
+            :key="`td-${j}`"
+            :class="[
+              `${j === 0 ? 'mb' : j === row.content.length - 1 ? 'mbr' : 'mc'}`,
+              `${curQue.firstColAligin === 'center' ? 'col-center' : ''}`
+            ]"
+          >
+            <template v-if="col.type === 'content'">
+              <template v-if="col.text.length > 0">
+                {{ col.text }}
+              </template>
+              <template v-else>
+                <el-input
+                  v-model="col.answer"
+                  type="textarea"
+                  :placeholder="`${isAnswerMode ? '' : '输入'}`"
+                  :disabled="isAnswerMode"
+                  :autosize="{ minRows: 1, maxRows: 6 }"
+                  @input="enterAnswer(i, j, $event)"
+                />
+              </template>
+            </template>
+
+            <div
+              v-else-if="col.type === 'pinyin'"
+              class="sentence"
+              :style="pinyinStyle"
+            >
+              <div>
+                <span
+                  v-for="({ pinyin, chs }, k) in col.sentence_data.wordsList"
+                  :key="
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'pinyin'
+                        : 'chs'
+                    }-${k}`
+                  "
+                  :class="[
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'pinyin'
+                        : 'chs'
+                    }`
+                  ]"
+                >
+                  {{
+                    curQue.pinyinPosition === "top" ||
+                      curQue.pinyinPosition == "left"
+                      ? pinyin
+                      : chs
+                  }}
+                </span>
+              </div>
+              <div>
+                <span
+                  v-for="({ pinyin, chs }, k) in col.sentence_data.wordsList"
+                  :key="
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'chs'
+                        : 'pinyin'
+                    }-${k}`
+                  "
+                  :class="[
+                    `${
+                      curQue.pinyinPosition === 'top' ||
+                      curQue.pinyinPosition === 'left'
+                        ? 'chs'
+                        : 'pinyin'
+                    }`
+                  ]"
+                >
+                  {{
+                    curQue.pinyinPosition === "top" ||
+                      curQue.pinyinPosition == "left"
+                      ? chs
+                      : pinyin
+                  }}
+                </span>
+              </div>
+            </div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    curQue: {
+      type: Object,
+      required: true
+    },
+    themeColor: {
+      type: String,
+      required: true
+    }
+  },
+  data() {
+    return {
+      isAnswerMode: false
+    };
+  },
+  computed: {
+    theadStyle() {
+      const hp = this.curQue.headerEnglishPosition;
+      if (hp === 'top') {
+        return {
+          'flex-direction': 'column-reverse'
+        };
+      }
+      if (hp === 'right') {
+        return {
+          'flex-direction': 'row',
+          'column-gap': '8px'
+        };
+      }
+      if (hp === 'bottom') {
+        return {
+          'flex-direction': 'column'
+        };
+      }
+      if (hp === 'left') {
+        return {
+          'flex-direction': 'row-reverse',
+          'column-gap': '8px'
+        };
+      }
+    },
+    pinyinStyle() {
+      let pyPos = this.curQue.pinyinPosition;
+      if (pyPos === "left") {
+        return {
+          "column-gap": "16px"
+        };
+      }
+
+      if (pyPos === "top") {
+        return {
+          "flex-direction": "column"
+        };
+      }
+
+      if (pyPos === "right") {
+        return {
+          "column-gap": "16px"
+        };
+      }
+
+      if (pyPos === "bottom") {
+        return {
+          "flex-direction": "column"
+        };
+      }
+    }
+  },
+  created() {
+    const Bookanswer = this.curQue.Bookanswer;
+    if (Bookanswer) {
+      this.isAnswerMode = true;
+      for (const key in Bookanswer) {
+        let { col, row, value } = Bookanswer[key];
+        this.curQue.tableData.body[col].content[row].answer = value;
+      }
+    } else {
+      this.$set(this.curQue, "Bookanswer", {});
+    }
+  },
+  methods: {
+    enterAnswer(i, j, value) {
+      this.curQue.Bookanswer[`${i}-${j}`] = {
+        col: i,
+        row: j,
+        value
+      };
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.header-separate {
+  width: 100%;
+  margin-bottom: 16px;
+
+  table {
+    table-layout: fixed;
+    font-size: 16px;
+    border-collapse: separate;
+    border-spacing: 6px 0;
+
+    th {
+      color: #4d4c51;
+      font-weight: normal;
+      background-color: #efeff9;
+      padding: 4px 12px;
+      border: 2px solid #afb4d1;
+
+      .thead-content {
+        display: flex;
+        justify-content: center;
+
+        .english {
+          color: #000;
+        }
+      }
+    }
+
+    td {
+      color: #474747;
+      border-bottom: 1px solid transparent;
+      padding: 4px 12px;
+      min-height: 28px;
+
+      .sentence {
+        display: flex;
+      }
+    }
+
+    .pinyin {
+      font-family: "GB-PINYINOK-B";
+    }
+
+    .chs {
+      font-family: "FZJCGFKTK";
+    }
+
+    .mb {
+      border-left: 2px solid transparent;
+      border-bottom-width: 1px;
+      border-image: linear-gradient(
+          transparent 6px,
+          #e7b576 6px,
+          #e7b576 calc(100% - 6px),
+          transparent calc(100% - 6px),
+          transparent calc(100% - 2px),
+          #cecece calc(100% - 2px)
+        )
+        2;
+      border-image-outset: 0 4px 0 0;
+
+      &.col-center {
+        text-align: center;
+      }
+    }
+
+    .mbr {
+      border-right: 2px solid transparent;
+      border-bottom-width: 1px;
+      border-image: linear-gradient(
+          transparent 6px,
+          #e7b576 6px,
+          #e7b576 calc(100% - 6px),
+          transparent calc(100% - 6px),
+          transparent calc(100% - 2px),
+          #cecece calc(100% - 2px)
+        )
+        2;
+      border-image-outset: 0 0 0 4px;
+    }
+
+    .mc {
+      border-left: 2px solid transparent;
+      border-right: 2px solid transparent;
+      border-image: repeating-linear-gradient(
+          transparent,
+          transparent 3px,
+          #cecece 3px,
+          #cecece 7px,
+          transparent 7px
+        )
+        2;
+      border-image-outset: 0 4px 0 4px;
+      position: relative;
+    }
+
+    .mc::after {
+      content: "";
+      position: absolute;
+      top: 100%;
+      left: 0;
+      width: 100%;
+      height: 1px;
+      display: inline-block;
+      background-color: #cecece;
+      box-shadow: 5px 0 #cecece, -5px 0 #cecece;
+    }
+  }
+}
+</style>

+ 1 - 0
src/components/Adult/preview/SelectDrag.vue

@@ -223,6 +223,7 @@ export default {
 .select-drag {
   width: 100%;
   color: #000;
+  margin-bottom: 16px;
 
   .pinyin {
     font-family: "GB-PINYINOK-B";

+ 6 - 1
src/views/adultInput.vue

@@ -363,6 +363,9 @@
                     :change-cur-que="changeCurQue"
                   />
                 </template>
+                <template v-if="topicIitem.type === 'header_separate'">
+                  <header-separate :cur-que="topicIitem.data" :change-cur-que="changeCurQue" />
+                </template>
                 <template v-if="topicIitem.type === 'fill_drag'">
                   <fill-drag
                     :cur-que="topicIitem.data"
@@ -807,7 +810,8 @@ import NumberCombination from "@/components/Adult/inputModules/NumberCombination
 import DialogueAnswerChs from "@/components/Adult/inputModules/DialogueAnswerChs";
 import VoiceMatrix from "@/components/Adult/inputModules/VoiceMatrix.vue";
 import SelectDrag from "@/components/Adult/inputModules/SelectDrag.vue";
-import ConfigurableTable from "@/components/Adult/inputModules/ConfigurableTable.vue";
+import ConfigurableTable from "@/components/Adult/inputModules/ConfigurableTable/index.vue";
+import HeaderSeparate from '@/components/Adult/inputModules/HeaderSeparate/index.vue';
 import FillDrag from "@/components/Adult/inputModules/FillDrag.vue";
 import ImageQuestion from "@/components/Adult/inputModules/ImageQuestion.vue";
 import PurePreview from "@/components/Adult/inputModules/PurePreview.vue";
@@ -900,6 +904,7 @@ export default {
     FillDrag,
     SelectDrag,
     ConfigurableTable,
+    HeaderSeparate,
     ImageQuestion,
     PurePreview,
     ZiLine,