Просмотр исходного кода

Merge branch 'master' of http://60.205.254.193:3000/GCLS/eep_page

dsy 5 месяцев назад
Родитель
Сommit
964ebb0655
17 измененных файлов с 661 добавлено и 975 удалено
  1. 4 0
      src/icons/svg/play-stroke-icon.svg
  2. 13 2
      src/views/book/courseware/create/components/base/common/UploadFile.vue
  3. 0 2
      src/views/book/courseware/create/components/base/h5_games/H5Games.vue
  4. 0 2
      src/views/book/courseware/create/components/base/upload_preview/UploadPreview.vue
  5. 0 2
      src/views/book/courseware/create/components/question/character_structure/CharacterStructure.vue
  6. 0 2
      src/views/book/courseware/create/components/question/drawing/Drawing.vue
  7. 0 2
      src/views/book/courseware/create/components/question/image_text/ImageText.vue
  8. 257 26
      src/views/book/courseware/create/components/question/newWord_template/NewWordTemplate.vue
  9. 31 1
      src/views/book/courseware/create/components/question/newWord_template/NewWordTemplateSetting.vue
  10. 0 2
      src/views/book/courseware/create/components/question/video_interaction/VideoInteraction.vue
  11. 0 1
      src/views/book/courseware/create/components/question/write/Write.vue
  12. 8 8
      src/views/book/courseware/data/bookType.js
  13. 0 66
      src/views/book/courseware/data/newWordTemplate copy.js
  14. 51 3
      src/views/book/courseware/data/newWordTemplate.js
  15. 64 855
      src/views/book/courseware/preview/components/newWord_template/NewWordTemplatePreview.vue
  16. 232 0
      src/views/book/courseware/preview/components/newWord_template/components/Strockplayredline.vue
  17. 1 1
      src/views/book/courseware/preview/components/new_word/components/Strockplayredline.vue

+ 4 - 0
src/icons/svg/play-stroke-icon.svg

@@ -0,0 +1,4 @@
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 0H22V22C9.99865 18.0776 5.33153 13.0958 0 0Z" fill="currentColor"/>
+<path d="M11.9911 11C12.1384 11 12.2682 10.9487 12.4295 10.8563L16.5721 8.51995C16.8737 8.34892 17 8.21551 17 8C17 7.78449 16.8737 7.6545 16.5721 7.48005L12.4295 5.14367C12.2682 5.05131 12.1384 5 11.9911 5C11.7034 5 11.5 5.21551 11.5 5.55416V10.4458C11.5 10.7879 11.7034 11 11.9911 11Z" fill="white" fill-opacity="0.85"/>
+</svg>

+ 13 - 2
src/views/book/courseware/create/components/base/common/UploadFile.vue

@@ -166,6 +166,16 @@ export default {
       type: Object,
       default: () => ({}),
     },
+    index: {
+      // 如果是二维数组里循环上传 一维索引
+      type: Number,
+      default: null,
+    },
+    indexs: {
+      // 如果是二维数组里循环上传 二维索引
+      type: Number,
+      default: null,
+    },
   },
   data() {
     return {
@@ -201,7 +211,7 @@ export default {
   watch: {
     content: {
       handler(val) {
-        this.$emit('updateFileList', val);
+        this.$emit('updateFileList', val, this.index, this.indexs);
       },
       deep: true,
     },
@@ -284,7 +294,8 @@ export default {
         this.type === 'picture' ||
         this.type === 'image_text' ||
         this.type === 'drawing' ||
-        this.type === 'character_structure'
+        this.type === 'character_structure' ||
+        this.type === 'newWord_template'
       ) {
         fileType = ['jpg', 'png', 'jpeg'];
         typeTip = '图片文件只能是 jpg、png、jpeg 格式!';

+ 0 - 2
src/views/book/courseware/create/components/base/h5_games/H5Games.vue

@@ -3,8 +3,6 @@
     <template #content>
       <UploadFile
         key="upload_games"
-        :courseware-id="courseware_id"
-        :component-id="id"
         :type="data.type"
         :total-size="200"
         :file-list="data.file_list"

+ 0 - 2
src/views/book/courseware/create/components/base/upload_preview/UploadPreview.vue

@@ -2,8 +2,6 @@
   <ModuleBase :type="data.type">
     <template #content>
       <UploadFile
-        :courseware-id="courseware_id"
-        :component-id="id"
         :type="data.type"
         :single-size="data.single_size"
         :total-size="data.total_size"

+ 0 - 2
src/views/book/courseware/create/components/question/character_structure/CharacterStructure.vue

@@ -3,8 +3,6 @@
     <template #content>
       <UploadFile
         key="upload_image"
-        :courseware-id="courseware_id"
-        :component-id="id"
         :type="data.type"
         :file-list="data.image_list"
         :file-id-list="data.image_id_list"

+ 0 - 2
src/views/book/courseware/create/components/question/drawing/Drawing.vue

@@ -3,8 +3,6 @@
     <template #content>
       <UploadFile
         key="upload_image"
-        :courseware-id="courseware_id"
-        :component-id="id"
         :type="data.type"
         :total-size="data.total_size"
         :file-list="data.image_list"

+ 0 - 2
src/views/book/courseware/create/components/question/image_text/ImageText.vue

@@ -3,8 +3,6 @@
     <template #content>
       <UploadFile
         key="upload_image"
-        :courseware-id="courseware_id"
-        :component-id="id"
         :type="data.type"
         :total-size="data.total_size"
         :file-list="data.image_list"

+ 257 - 26
src/views/book/courseware/create/components/question/newWord_template/NewWordTemplate.vue

@@ -1,61 +1,227 @@
 <template>
   <ModuleBase :type="data.type">
     <template #content>
-      <el-button icon="el-icon-plus" style="margin: 24px 0" @click="addElement">增加一个</el-button>
+      <div class="fun-type">
+        <a
+          v-for="{ value, label } in modelList"
+          :key="value"
+          :class="[data.property.model === value ? 'active' : '']"
+          @click="data.property.model = value"
+          >{{ label }}</a
+        >
+      </div>
+      <div class="content-box" v-for="(item, index) in data.option_list" :key="index">
+        <div class="content-item">
+          <el-input
+            v-model="item.content"
+            maxlength="10"
+            show-word-limit
+            placeholder="输入汉字,@:代表图片"
+            @blur="handleMindMap"
+          ></el-input>
+          <el-button @click="identify(item)">识别</el-button>
+        </div>
+        <div class="content-items" v-for="(items, indexs) in item.content_list" :key="indexs">
+          <template v-if="items">
+            <label>内容:{{ items.con }} </label>
+            <UploadFile
+              v-if="items.type === 'img'"
+              :type="data.type"
+              :file-list="items.file_list"
+              :file-id-list="items.file_id_list"
+              :label-text="'图片'"
+              :accept-file-type="acceptFileType"
+              :index="index"
+              :indexs="indexs"
+              :limit="1"
+              @updateFileList="updateFileList"
+            />
+            <div class="option-item">
+              <template v-if="isEnable(data.property.is_enable_pinyin)">
+                <span>拼音</span>
+                <el-input v-model="items.pinyin"></el-input>
+              </template>
+              <template v-if="isEnable(data.property.is_enable_shiyi) && data.property.model === 'input'">
+                <span>释义</span>
+                <el-input v-model="items.shiyi"></el-input>
+              </template>
+              <template v-if="data.property.model === 'miao' && items.type === 'hanzi'">
+                <span>可输入</span>
+                <el-radio-group v-model="items.is_can_input_answer">
+                  <el-radio :label="true">是</el-radio>
+                  <el-radio :label="false">否</el-radio>
+                </el-radio-group>
+              </template>
+
+              <template v-if="data.property.model === 'miao' && items.type === 'hanzi' && items.is_can_input_answer">
+                <span>答案</span>
+                <el-input v-model="items.answer"></el-input>
+                <span>例子</span>
+                <el-radio-group v-model="items.is_example">
+                  <el-radio :label="true">是</el-radio>
+                  <el-radio :label="false">否</el-radio>
+                </el-radio-group>
+              </template>
+            </div>
+          </template>
+        </div>
+      </div>
+      <el-button icon="el-icon-plus" style="margin: 10px 0" @click="addElement">增加一个</el-button>
     </template>
   </ModuleBase>
 </template>
 
 <script>
 import ModuleMixin from '../../common/ModuleMixin';
+import UploadFile from '../../base/common/UploadFile.vue';
 
-import { getNewWordTemplateData } from '@/views/book/courseware/data/newWordTemplate';
+import {
+  getNewWordTemplateData,
+  modelList,
+  answer_list,
+  getOption,
+  isEnable,
+} from '@/views/book/courseware/data/newWordTemplate';
+import { GetStaticResources } from '@/api/app';
 
 export default {
   name: 'NewWordTemplatePage',
-  components: {},
+  components: { UploadFile },
   mixins: [ModuleMixin],
   data() {
     return {
       data: getNewWordTemplateData(),
+      modelList,
+      answer_list,
+      getOption,
+      acceptFileType: '.png,.jpg,.jpeg',
     };
   },
   watch: {
     // 'data.option': 'handleMindMap',
   },
   methods: {
-    // 删除行
-    handleDelete(index) {
-      this.data.option.splice(index, 1);
-    },
-    // 上移下移
-    moveElement(dItem, index, type) {
-      let obj = JSON.parse(JSON.stringify(dItem));
-      if (type == 'up' && index > 0) {
-        this.data.option.splice(index - 1, 0, obj);
-        this.data.option.splice(index + 1, 1);
-      }
-      if (type == 'down' && index < this.data.option.length - 1) {
-        this.data.option[index] = this.data.option.splice(index + 1, 1, this.data.option[index])[0];
-      }
+    updateFileList({ file_list, file_id_list }, index, indexs) {
+      this.data.option_list[index].content_list[indexs].file_list = file_list;
+      this.data.option_list[index].content_list[indexs].file_id_list = file_id_list;
     },
     // 增加
     addElement() {
-      this.data.option.push(getOption());
+      this.data.option_list.push(getOption());
     },
     handleMindMap() {
       // 思维导图数据
       let node_list = [];
-      this.data.option.forEach((item) => {
+      this.data.option_list.forEach((item) => {
         node_list.push({
-          name: item.con.replace(/<[^>]*>?/gm, ''),
+          name: item.content.replace(/<[^>]*>?/gm, ''),
           id: Math.random().toString(36).substring(2, 12),
         });
       });
       this.data.mind_map.node_list = node_list;
     },
-    handleBlurCon() {
-      this.handleMindMap();
+    // 识别
+    async identify(items) {
+      let con = items.content.trim();
+      if (con) {
+        items.content_list = [];
+        let arr = con.split('');
+        const regex = /[\u4E00-\u9FFF]/;
+        let str = '';
+        arr.forEach((item) => {
+          if (regex.test(item)) {
+            str += item;
+          }
+        });
+        let MethodName = 'hz_resource_manager-GetMultHZStrokesContent';
+        let data = {
+          hz_str: str,
+        };
+
+        await GetStaticResources(MethodName, data)
+          .then((res) => {
+            for (let key in res) {
+              if (key != 'status' && key != ',' && res[key]) {
+                res[key] = JSON.parse(res[key]);
+              }
+            }
+            let hzDetailList = res;
+            arr.forEach((item, index) => {
+              let objs = {};
+              if (item === '@') {
+                // 图片
+                objs = {
+                  con: item,
+                  type: 'img',
+                  file_list: [],
+                  file_id_list: [],
+                  pinyin: '',
+                  shiyi: '',
+                  answer: '',
+                  answer_pinyin: '',
+                  answer_en: '',
+                  is_example: false,
+                  is_can_input_answer: true,
+                };
+              } else if (item === '#') {
+                // 书写
+                objs = {
+                  con: item,
+                  type: 'write',
+                  img: '',
+                  base64: '',
+                  pinyin: '',
+                  shiyi: '',
+                  answer: '',
+                  is_example: false,
+                  answer_pinyin: '',
+                  answer_en: '',
+                  is_can_input_answer: true,
+                };
+              } else if (regex.test(item)) {
+                // 汉字
+                let hz_list = [];
+                let res = JSON.parse(JSON.stringify(hzDetailList[item]));
+                let obj = {
+                  con: item,
+                  hzDetail: {
+                    hz_json: res,
+                  },
+                };
+                hz_list.push(obj);
+                objs = {
+                  con: item,
+                  type: 'hanzi',
+                  hz_info: hz_list,
+                  pinyin: cnchar.spell(item, 'array', 'low', 'tone').join(' '),
+                  shiyi: '',
+                  answer: '',
+                  is_example: false,
+                  answer_pinyin: '',
+                  answer_en: '',
+                  is_can_input_answer: true,
+                };
+              } else {
+                // 连字符
+                objs = {
+                  con: item,
+                  type: 'lian',
+                  pinyin: '',
+                  shiyi: '',
+                  answer: '',
+                  answer_pinyin: '',
+                  answer_en: '',
+                  is_example: false,
+                  is_can_input_answer: true,
+                };
+              }
+              this.$set(items.content_list, index, objs);
+            });
+          })
+          .catch(() => {});
+      } else {
+        this.$message.warning('请先输入内容');
+      }
     },
   },
 };
@@ -89,9 +255,74 @@ export default {
     cursor: pointer;
   }
 }
-</style>
-<style lang="scss">
-.tox .tox-editor-header {
-  z-index: 3 !important;
+
+.fun-type {
+  display: flex;
+  gap: 5px;
+  width: 100%;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #e5e6eb;
+
+  a {
+    padding: 5px 10px;
+    font-weight: normal;
+    color: #1d2129;
+    cursor: pointer;
+    background: #f2f3f5;
+    border: 1px solid #f2f3f5;
+    border-radius: 2px;
+
+    &.active {
+      color: #165dff;
+      background: #e7eeff;
+      border-color: #165dff;
+    }
+  }
+}
+
+.content-box {
+  margin: 10px 0;
+}
+
+.content-item {
+  display: flex;
+  gap: 16px;
+
+  .el-input {
+    max-width: 400px;
+  }
+}
+
+.content-items {
+  margin: 10px 0;
+
+  label {
+    font-size: 14px;
+    line-height: 34px;
+    color: #4e5969;
+  }
+
+  .option-item {
+    display: flex;
+    align-items: center;
+    margin-top: 5px;
+
+    span {
+      flex-shrink: 0;
+      width: max-content;
+      margin-right: 10px;
+      font-size: 14px;
+      color: #4e5969;
+    }
+
+    .el-input {
+      max-width: 100px;
+      margin-right: 30px;
+    }
+
+    .el-radio-group {
+      margin-right: 30px;
+    }
+  }
 }
 </style>

+ 31 - 1
src/views/book/courseware/create/components/question/newWord_template/NewWordTemplateSetting.vue

@@ -2,6 +2,34 @@
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
       <SerailNumber :property="property" />
+      <el-form-item label="播放笔顺">
+        <el-radio-group v-model="property.is_enable_play_structure">
+          <el-radio v-for="{ value, label } in showList" :key="value" :label="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="拼音">
+        <el-radio-group v-model="property.is_enable_pinyin">
+          <el-radio v-for="{ value, label } in showList" :key="value" :label="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="标红笔画">
+        <el-radio-group v-model="property.is_enable_high_strokes">
+          <el-radio v-for="{ value, label } in showList" :key="value" :label="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="释义" v-if="property.model === 'input'">
+        <el-radio-group v-model="property.is_enable_shiyi">
+          <el-radio v-for="{ value, label } in showList" :key="value" :label="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
     </el-form>
   </div>
 </template>
@@ -9,7 +37,7 @@
 <script>
 import SettingMixin from '@/views/book/courseware/create/components/common/SettingMixin';
 
-import { getNewWordTemplateProperty } from '@/views/book/courseware/data/newWordTemplate';
+import { getNewWordTemplateProperty, showList, displayList } from '@/views/book/courseware/data/newWordTemplate';
 
 export default {
   name: 'NewWordTemplateSetting',
@@ -17,6 +45,8 @@ export default {
   data() {
     return {
       property: getNewWordTemplateProperty(),
+      showList,
+      displayList,
     };
   },
   methods: {},

+ 0 - 2
src/views/book/courseware/create/components/question/video_interaction/VideoInteraction.vue

@@ -3,8 +3,6 @@
     <template #content>
       <UploadFile
         key="upload_image"
-        :courseware-id="courseware_id"
-        :component-id="id"
         :type="data.type"
         :total-size="data.total_size"
         :file-list="data.video_list"

+ 0 - 1
src/views/book/courseware/create/components/question/write/Write.vue

@@ -9,7 +9,6 @@
         </template>
         <template v-else>
           <UploadFile
-            :courseware-id="courseware_id"
             :type="'picture'"
             :single-size="data.single_size"
             :total-size="data.total_size"

+ 8 - 8
src/views/book/courseware/data/bookType.js

@@ -407,14 +407,14 @@ export const bookTypeOption = [
         set: DrawingSetting,
         preview: DrawingPreview,
       },
-      // {
-      //   value: 'newWord_template',
-      //   label: '生字',
-      //   icon: '',
-      //   component: NewWordTemplate,
-      //   set: NewWordTemplateSetting,
-      //   preview: NewWordTemplatePreview,
-      // },
+      {
+        value: 'newWord_template',
+        label: '生字',
+        icon: '',
+        component: NewWordTemplate,
+        set: NewWordTemplateSetting,
+        preview: NewWordTemplatePreview,
+      },
       {
         value: 'character_structure',
         label: '汉字结构',

+ 0 - 66
src/views/book/courseware/data/newWordTemplate copy.js

@@ -1,66 +0,0 @@
-import {
-  displayList,
-  serialNumberTypeList,
-  serialNumberPositionList,
-  arrangeTypeList,
-  switchOption,
-  isEnable,
-} from '@/views/book/courseware/data/common';
-
-export { arrangeTypeList, switchOption, isEnable };
-
-// 显示
-export const showList = [
-  {
-    value: 'true',
-    label: '显示',
-  },
-  {
-    value: 'false',
-    label: '不显示',
-  },
-];
-
-// 汉字框
-export const frameList = [
-  {
-    value: 'tian',
-    label: '田字格',
-  },
-  {
-    value: 'fang',
-    label: '方框',
-  },
-  {
-    value: 'none',
-    label: '无',
-  },
-];
-
-export function getNewWordTemplateProperty() {
-  return {
-    serial_number: 1,
-    sn_type: serialNumberTypeList[0].value,
-    sn_position: serialNumberPositionList[3].value,
-    sn_display_mode: displayList[0].value,
-
-    is_enable_pinyin: showList[0].value,
-    
-  };
-}
-
-export function getNewWordTemplateData() {
-  return {
-    type: 'newWord_template',
-    title: '生字',
-    property: getNewWordTemplateProperty(),
-    content: '',
-    content_list: [],
-    mind_map: {
-      node_list: [{ name: '生字' }], // 思维导图数据
-    },
-    answer: {
-      answer_list: [],
-    },
-  };
-}

+ 51 - 3
src/views/book/courseware/data/newWordTemplate.js

@@ -6,8 +6,9 @@ import {
   switchOption,
   isEnable,
 } from '@/views/book/courseware/data/common';
+import { getRandomNumber } from '@/utils';
 
-export { arrangeTypeList, switchOption, isEnable };
+export { arrangeTypeList, switchOption, isEnable, displayList };
 
 // 显示
 export const showList = [
@@ -37,6 +38,45 @@ export const frameList = [
   },
 ];
 
+// 模式类型
+export const modelList = [
+  {
+    value: 'miao',
+    label: '标红笔画',
+  },
+  {
+    value: 'input',
+    label: '输入拼音释义',
+  },
+];
+
+// 答题方式
+export const answer_list = [
+  {
+    value: 'pinyin',
+    label: '填拼音',
+  },
+  {
+    value: 'en',
+    label: '填英文',
+  },
+];
+
+export function getOption() {
+  return {
+    content: '',
+    pinyin: '',
+    mark: getRandomNumber(),
+    is_example: false,
+    answer: '',
+    hz_info: [],
+    file_list: [],
+    file_id_list: [],
+    content_list: [],
+    is_common_pinyin: false
+  };
+}
+
 export function getNewWordTemplateProperty() {
   return {
     serial_number: 1,
@@ -44,7 +84,15 @@ export function getNewWordTemplateProperty() {
     sn_position: serialNumberPositionList[3].value,
     sn_display_mode: displayList[0].value,
 
+    is_enable_play_structure: showList[0].value,
     is_enable_pinyin: showList[0].value,
+    is_enable_high_strokes: showList[1].value,
+    model:modelList[0].value,
+    
+    // miao模式
+
+    // input模式
+    is_enable_shiyi: showList[0].value,
     
   };
 }
@@ -54,8 +102,8 @@ export function getNewWordTemplateData() {
     type: 'newWord_template',
     title: '生字',
     property: getNewWordTemplateProperty(),
-    content: '',
-    content_list: [],
+    option_list: [getOption()],
+    answer_type:'',
     mind_map: {
       node_list: [{ name: '生字' }], // 思维导图数据
     },

+ 64 - 855
src/views/book/courseware/preview/components/newWord_template/NewWordTemplatePreview.vue

@@ -1,9 +1,38 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="newWord-preview" :style="getAreaStyle()">
+  <div class="newWord-template-preview" :style="getAreaStyle()">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
-    <div class="main"></div>
+    <div class="main">
+      <div class="item-box" v-for="(item, index) in data.option_list" :key="index">
+        <div class="number-box">
+          <span class="number">{{ index + 1 }}</span>
+        </div>
+        <div class="items" v-for="(items, indexs) in item.content_list" :key="indexs">
+          <template v-if="items && items.type === 'img'">
+            <el-image
+              class="items-image"
+              v-if="items.file_list[0]"
+              :src="items.file_list[0].file_url"
+              fit="contain"
+            ></el-image>
+          </template>
+          <template v-else-if="items && items.type === 'lian'">
+            <span class="items-lian">{{ items.con }}</span>
+          </template>
+          <Strockplayredline
+            v-else-if="items && items.type === 'hanzi'"
+            :Book_text="items.con"
+            :playStorkes="isEnable(data.property.is_enable_play_structure)"
+            :curItem="isEnable(data.property.is_enable_high_strokes) ? items : null"
+            :type="data.type"
+            :targetDiv="'newWordTemplate' + index + indexs"
+            :hz_json="items.hz_info[0].hzDetail.hz_json"
+            class="hanzi-storck"
+          />
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -11,11 +40,11 @@
 import { getNewWordTemplateData } from '@/views/book/courseware/data/newWordTemplate';
 
 import PreviewMixin from '../common/PreviewMixin';
-
+import Strockplayredline from './components/Strockplayredline.vue';
 export default {
   name: 'NewWordPreview',
 
-  components: {},
+  components: { Strockplayredline },
   mixins: [PreviewMixin],
   data() {
     return {
@@ -39,876 +68,56 @@ export default {
 <style lang="scss" scoped>
 @use '@/styles/mixin.scss' as *;
 
-.newWord-preview {
-  @include preview-base;
-
-  .NPC-zhedie {
-    width: 1007px;
-
-    // width: 780px;
-    margin-bottom: 24px;
-
-    .aduioLine-box {
-      margin-bottom: 8px;
-    }
-
-    .practiceBox {
-      position: fixed;
-      top: 0;
-      left: 0;
-      z-index: 999;
-      box-sizing: border-box;
-      width: 100%;
-      height: 100vh;
-      overflow: hidden;
-      overflow-y: auto;
-      background: rgba(0, 0, 0, 19%);
-    }
-
-    .NPC-word-list {
-      overflow: auto;
-      background: #f7f7f7;
-
-      .NPC-word-tab-common,
-      .collocation,
-      .tabNum-box {
-        flex-shrink: 0;
-      }
-    }
-
-    .NPC-word-table {
-      width: 100%;
-
-      :deep p {
-        margin: 0;
-      }
-
-      > .NPC-word-tr {
-        margin-bottom: 8px;
-        background: #fff;
-        border-radius: 8px;
-
-        .NPC-word-row {
-          position: relative;
-          display: flex;
-
-          // flex-flow: wrap;
-          justify-content: flex-start;
-          padding: 8px 13px 8px 12px;
-          padding-right: 80px;
-          cursor: pointer;
-          border-radius: 8px;
-
-          &.active {
-            background: linear-gradient(0deg, rgba(0, 0, 0, 8%), rgba(0, 0, 0, 8%)), #fff;
-          }
-
-          .right-box {
-            position: absolute;
-            top: 8px;
-            right: 5px;
-            display: flex;
-            gap: 5px;
-          }
-
-          > span {
-            font-size: 16px;
-            line-height: 150%;
-            color: #000;
-          }
-        }
-
-        .NPC-word-tab-common {
-          box-sizing: border-box;
-          width: 125px;
-          padding-left: 8px;
-        }
-      }
-    }
-  }
-
-  .NPC-word-list {
-    padding: 20px 24px;
-    border: 1px solid rgba(0, 0, 0, 10%);
-    border-top: none;
-    border-radius: 0 0 8px 8px;
-  }
-
-  .detail-icon {
-    display: block;
-    width: 24px;
-    height: 24px;
-    cursor: pointer;
-    opacity: 0.5;
-  }
-
-  .tabNum-box {
-    position: relative;
-
-    .star-label {
-      position: absolute;
-      top: 1px;
-      right: -6px;
-      width: 6px;
-      height: 6px;
-    }
-  }
-
-  .play-btn {
-    display: block;
-    width: 16px;
-    height: 16px;
-    margin-top: 4px;
-    background: url('@/assets/fill/voice-pause-red.png') no-repeat left top;
-    background-size: 100% 100%;
-
-    &.active {
-      background: url('@/assets/fill/voice-play-red.png') no-repeat left top;
-      background-size: 100% 100%;
-    }
-  }
-
-  .tabNum {
-    display: block;
-    width: 16px;
-    height: 16px;
-    margin-top: 4px;
-    margin-left: 8px;
-    font-family: 'robot', 'alabo';
-    font-size: 12px;
-    line-height: 16px;
-    color: #fff;
-    text-align: center;
-    background: #de4444;
-    border-radius: 50%;
-  }
-
-  .NPC-word-tab-box {
-    width: 240px;
-
-    span {
-      display: block;
-      width: 100%;
-      margin: 2px 0;
-      color: #000;
-    }
-  }
-
-  .NPC-word-tab-pinyin {
-    font-family: 'League';
-
-    // white-space: nowrap;
-    font-size: 12px;
-    word-break: break-word;
-
-    &.NPC-word-tab-pinyin-red {
-      color: #e35454;
-    }
-
-    &.NPC-word-tab-pinyin-green {
-      color: #24b99e;
-    }
-
-    &.NPC-word-tab-pinyin-brown {
-      color: #bd8865;
-    }
-  }
-
-  .NPC-word-tab-word {
-    font-family: '楷体';
-    font-size: 16px;
-    white-space: nowrap;
-
-    &.NPC-word-tab-word-red {
-      color: #e35454;
-    }
-
-    &.NPC-word-tab-word-green {
-      color: #24b99e;
-    }
-
-    &.NPC-word-tab-word-brown {
-      color: #bd8865;
-    }
-
-    &-break {
-      word-break: break-word;
-      white-space: normal;
-    }
-  }
-
-  .NPC-word-tab-cixing {
-    box-sizing: border-box;
-
-    // width: 48px;
-    width: 60px;
-    font-family: 'robot', 'alabo';
-    text-align: left;
-    word-break: break-word;
-
-    // font-style: italic;  // 要求改为正体
-    &.NPC-word-tab-cixing-red {
-      color: #e35454;
-    }
-
-    &.NPC-word-tab-cixing-green {
-      color: #24b99e;
-    }
-
-    &.NPC-word-tab-cixing-brown {
-      color: #bd8865;
-    }
-
-    &.hasCn {
-      font-size: 13px;
-    }
-  }
-
-  .NPC-word-tab-def {
-    box-sizing: border-box;
-
-    // flex: 1;
-    font-family: 'robot', 'alabo';
-    word-break: break-word;
-    white-space: pre-wrap;
-
-    &.NPC-word-tab-def-red {
-      color: #e35454;
-    }
-
-    &.NPC-word-tab-def-green {
-      color: #24b99e;
-    }
-
-    &.NPC-word-tab-def-brown {
-      color: #bd8865;
-    }
+.newWord-template-preview {
+  .item-box {
+    display: flex;
+    align-items: end;
   }
 
-  .collocation {
+  .number-box {
     display: flex;
-    width: 100%;
-
-    // padding-top: 8px;
-
-    > span {
-      flex-shrink: 0;
-      font-size: 16px;
-      font-weight: 400;
-      line-height: 24px;
-      color: #000;
-    }
+    align-items: center;
+    height: 80px;
+    margin-right: 16px;
 
-    > div b {
+    .number {
       display: block;
-    }
-
-    > b,
-    > div b {
-      flex: 1;
-      font-family: 'robot', '楷体', 'alabo';
-      font-size: 16px;
-      font-weight: 400;
+      width: 24px;
+      height: 24px;
+      font-family: 'arial';
+      font-size: 14px;
+      font-weight: bold;
       line-height: 24px;
-      color: rgba(0, 0, 0, 65%);
-    }
-  }
-
-  @keyframes firstrotate {
-    0% {
-      transform: rotateZ(0deg);
-    }
-
-    100% {
-      transform: rotateZ(180deg);
-    }
-  }
-
-  @keyframes huifuRotate {
-    0% {
-      transform: rotateZ(180deg);
-    }
-
-    100% {
-      transform: rotateZ(0deg);
-    }
-  }
-
-  .luyin-box-wordphrase {
-    height: 24px;
-  }
-
-  .NPC-word-tile {
-    display: flex;
-    flex-flow: wrap;
-    gap: 20px;
-    padding: 20px 0;
-  }
-
-  .writeTop {
-    position: relative;
-    display: flex;
-    column-gap: 8px;
-    width: 400px;
-
-    .left,
-    .right {
-      position: relative;
-      box-sizing: border-box;
-      width: 100%;
-      min-height: 270px;
-      padding: 8px 12px 18px;
-      overflow: hidden;
-      background: #fff;
-      border: 4px solid #fff;
-      border-radius: 24px;
-
-      .header-info {
-        display: flex;
-        justify-content: space-between;
-        width: 100%;
-        margin-bottom: 12px;
-
-        :deep .el-input__inner {
-          height: 24px;
-          padding: 0;
-          font-size: 24px;
-          font-weight: 400;
-          line-height: 100%;
-          color: rgba(0, 0, 0, 100%);
-          border: none;
-        }
-
-        .label {
-          :deep .el-input__inner {
-            text-align: right;
-          }
-        }
-      }
-    }
-
-    min-height: 332px;
-    transition: 0.6s;
-    perspective: 1000px;
-    transform-style: preserve-3d;
-
-    .left-preview {
-      padding-top: 40px;
-
-      // padding-bottom: 32px;
-      // position: absolute;
-      backface-visibility: hidden;
-    }
-
-    .header-info-preview {
-      position: absolute;
-      top: 0;
-      left: 0;
-      z-index: 1;
-      width: 100%;
-
-      h5 {
-        padding: 0 12px;
-        margin: 0;
-        font-size: 20px;
-        font-weight: 400;
-        line-height: 32px;
-        color: #000;
-      }
-
-      label {
-        position: absolute;
-        top: -4px;
-        right: -4px;
-        padding: 0 16px 0 8px;
-        font-size: 20px;
-        font-weight: 500;
-        line-height: 150%;
-        color: #fff;
-        background: #fff;
-        border-radius: 0 8px;
-      }
-    }
-
-    .left-big {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .del-btn {
-      position: absolute;
-      right: 8px;
-      bottom: 8px;
-      padding: 5px 8px;
-      font-size: 24px;
-      color: #fff;
-      cursor: pointer;
-      background: #f56767;
-      border-radius: 40px;
-    }
-
-    .overturn-btn {
-      position: absolute;
-      right: 8px;
-      bottom: 8px;
-      width: 40px;
-      height: 40px;
-      padding: 8px;
-      font-size: 24px;
-      line-height: 1;
       color: #fff;
-      cursor: pointer;
-      background: #e0e0e0;
-      border-radius: 8px;
-    }
-
-    .el-icon-zoom-in,
-    .filt-check {
-      position: absolute;
-      bottom: 8px;
-      left: 8px;
-      width: 30px;
-      height: 30px;
-      font-size: 24px;
-      cursor: pointer;
-
-      :deep .el-checkbox__inner {
-        width: 24px;
-        height: 24px;
-      }
-
-      :deep .el-checkbox__inner::after {
-        left: 8px;
-        width: 6px;
-        height: 14px;
-      }
-    }
-
-    .right {
-      display: flex;
-      flex-flow: wrap;
-      row-gap: 8px;
-      align-items: center;
-      padding: 16px 24px 26px;
-
-      .card-label {
-        width: 100%;
-        height: 22px;
-        font-size: 14px;
-        font-weight: 400;
-        line-height: 22px;
-        color: #4e5969;
-      }
-
-      :deep .el-textarea {
-        height: 64px;
-      }
-
-      .config-box {
-        display: flex;
-        align-items: center;
-        width: 100%;
-
-        span {
-          margin-right: 8px;
-          font-size: 14px;
-          line-height: 20px;
-          color: #000;
-        }
-
-        .el-color-picker {
-          height: 32px;
-        }
-
-        :deep .el-color-picker__trigger {
-          height: 32px;
-        }
-
-        .el-radio {
-          margin-right: 8px;
-        }
-
-        .el-radio-group {
-          display: flex;
-        }
-
-        :deep .el-radio__input.is-checked .el-radio__inner {
-          background: #000;
-          border-color: #000;
-        }
-
-        :deep .el-radio__input.is-checked + .el-radio__label {
-          color: #000;
-        }
-      }
-    }
-
-    .right-preview {
-      display: block;
-      padding: 36px;
-
-      .pinyin-box {
-        margin-bottom: 8px;
-        font-family: 'League';
-        font-feature-settings: 'cv01' on;
-
-        // font-size: 18px;
-        line-height: 120%;
-        color: #de4444;
-        text-align: center;
-      }
-
-      .hz-box {
-        width: 100%;
-
-        .hz-item {
-          text-align: center;
-
-          :deep .strockplayInner {
-            width: 76px;
-            height: 76px;
-          }
-
-          p {
-            margin: 0 0 8px;
-            font-family: 'League';
-            font-size: 18px;
-            font-feature-settings: 'cv01' on;
-            line-height: 120%;
-            color: #de4444;
-          }
-        }
-      }
-
-      :deep .audio-wrapper {
-        box-sizing: border-box;
-        width: 50px;
-        height: 50px;
-        padding: 13px;
-        margin: 0 auto 8px;
-        cursor: pointer;
-        background: #f3f3f3;
-        border-radius: 40px;
-
-        .voice-play {
-          width: 24px;
-          height: 24px;
-        }
-      }
-
-      .definition_list-box {
-        margin-top: 16px;
-        white-space: pre;
-
-        > div {
-          display: flex;
-          margin-bottom: 8px;
-
-          label,
-          p {
-            width: 40px;
-            font-size: 14px;
-            font-weight: 400;
-            line-height: 150%;
-            color: #000;
-          }
-
-          label {
-            width: 47px;
-          }
-
-          p {
-            flex: 1;
-            line-height: 0;
-            word-break: break-word;
-            white-space: pre-wrap;
-          }
-        }
-      }
-
-      :deep p {
-        margin: 0;
-        line-height: 1.5;
-      }
-    }
-
-    .right-preview-rota {
-      transform: rotateY(180deg);
-    }
-
-    .item-image {
-      position: relative;
-      overflow: hidden;
-      font-size: 0;
-
-      // background: #f2f3f5;
-      border-radius: 8px;
-
-      .item-image-del {
-        position: absolute;
-        top: 8px;
-        right: 8px;
-        display: block;
-        width: 16px;
-        height: 16px;
-        padding: 8px;
-        font-size: 16px;
-        color: #ee3232;
-        cursor: pointer;
-        background-color: #fff;
-        border-radius: 50%;
-        box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 25%);
-      }
-    }
-
-    .item-con {
-      display: flex;
-      align-items: center;
-      width: 50%;
-      margin-top: 16px;
-
-      label {
-        width: 44px;
-        font-size: 14px;
-        font-weight: 400;
-        line-height: 22px;
-        color: #4e5969;
-      }
-
-      :deep .el-input__inner {
-        width: 235px;
-        height: 32px;
-        font-family: '楷体';
-        font-size: 14px;
-        font-weight: 400;
-        line-height: 22px;
-        background: #f2f3f5;
-        border: none;
-        border-radius: 2px;
-      }
-
-      .pinyin {
-        :deep .el-input__inner {
-          font-family: 'League';
-        }
-      }
-    }
-
-    .con-preview {
-      margin-top: 8px;
-      font-family: '楷体';
-      font-size: 38px;
-      font-weight: 400;
-      line-height: 100%;
-      color: #000;
       text-align: center;
-
-      &-big {
-        margin: 0;
-        font-size: 86px;
-      }
-    }
-
-    .writeTop-row {
-      display: flex;
-      justify-content: center;
+      background: #346cda;
+      border-radius: 100%;
     }
   }
 
-  .flipped {
-    transform: rotateY(180deg);
-  }
-
-  .flipped-back {
-    transform: rotateY(180deg);
-  }
-
-  .hz-box {
-    display: flex;
-    width: max-content;
-  }
-
-  .writeTop-item {
-    border: 1px solid #de4444;
-  }
-
-  .writeTop-item-noLeft {
-    border-left: none;
-  }
-}
-
-.newWord-table {
-  .cell {
-    width: max-content;
-    white-space: nowrap;
+  .items {
+    font-size: 0;
   }
 
-  :deep thead {
-    display: none;
-  }
-}
-</style>
-<style lang="scss">
-.NPC-zhedie {
-  .topTitle {
-    display: flex;
-    justify-content: space-between;
-    width: 100%;
-    height: 48px;
-    padding-right: 16px;
-    padding-left: 24px;
+  .items-image,
+  .hanzi-storck {
+    width: 80px;
+    height: 80px;
     overflow: hidden;
-    background: #e35454;
-    border: 1px solid rgba(0, 0, 0, 10%);
-    border-radius: 8px 8px 0 0;
-
-    .NPC-top-left {
-      display: flex;
-      align-items: center;
-      justify-content: flex-start;
-
-      .NPC-topTitle-text {
-        margin-right: 8px;
-        font-family: 'sourceR';
-        font-size: 16px;
-        font-weight: bold;
-        color: #fff;
-        white-space: pre;
-      }
-    }
-
-    .NPC-top-right {
-      display: flex;
-      gap: 4px;
-      align-items: center;
-      justify-content: flex-start;
-      cursor: pointer;
-
-      &-text {
-        font-size: 14px;
-        font-weight: normal;
-        line-height: 16px;
-        color: #fff;
-      }
-
-      img {
-        width: 16px;
-        height: 16px;
-      }
-    }
-
-    img {
-      width: 24px;
-      height: 24px;
-    }
-
-    .rotate {
-      animation-name: firstrotate;
-      animation-timing-function: linear;
-      animation-direction: 2s;
-      animation-fill-mode: both;
-    }
-  }
-
-  .topTitleWhite {
-    display: flex;
-    justify-content: space-between;
-    width: 100%;
-    height: 48px;
-    padding-right: 16px;
-    padding-left: 24px;
-    overflow: hidden;
-    background: #fff;
-    border: 1px solid rgba(0, 0, 0, 10%);
-    border-radius: 8px 8px 0 0;
-
-    .NPC-top-left {
-      display: flex;
-      align-items: center;
-      justify-content: flex-start;
-
-      .NPC-topTitle-text {
-        margin-right: 8px;
-        font-family: 'sourceR';
-        font-size: 16px;
-        font-weight: bold;
-        color: #000;
-      }
-    }
-
-    .NPC-top-right {
-      display: flex;
-      align-items: center;
-      justify-content: flex-start;
-      cursor: pointer;
-
-      &-text {
-        font-size: 14px;
-        font-weight: normal;
-        line-height: 16px;
-        color: #000;
-      }
-
-      img {
-        width: 16px;
-        height: 16px;
-        margin-left: 4px;
-      }
-    }
-
-    img {
-      width: 24px;
-      height: 24px;
-    }
-
-    .rotate {
-      animation-name: firstrotate;
-      animation-timing-function: linear;
-      animation-direction: 2s;
-      animation-fill-mode: both;
-    }
-  }
-
-  .el-collapse-item__content {
-    padding-bottom: 0;
-  }
-
-  .el-slider__button {
-    width: 8px;
-    height: 8px;
-  }
-
-  .el-slider__runway {
-    padding: 0;
-    margin: 0;
-  }
-
-  .el-slider {
-    position: relative;
-
-    // top: -3px;
-  }
-
-  .el-collapse {
-    box-sizing: border-box;
-    background: #f7f7f7;
+    border: 2px solid #346cda;
     border-radius: 8px;
   }
 
-  .el-collapse-item__wrap {
-    background: #f7f7f7;
-    border: 1px solid rgba(0, 0, 0, 10%);
-    border-top: 0;
-    border-radius: 0 0 8px 8px;
-  }
-
-  .el-collapse-item__arrow {
-    display: none;
+  .items-lian {
+    display: block;
+    height: 80px;
+    padding: 0 10px;
+    font-size: 34px;
+    line-height: 80px;
+    color: #346cda;
   }
 
-  .el-table__row {
-    padding: 4px 0;
+  .hanzi-storck {
   }
 }
 </style>

+ 232 - 0
src/views/book/courseware/preview/components/newWord_template/components/Strockplayredline.vue

@@ -0,0 +1,232 @@
+<!--  -->
+<template>
+  <div class="strockplayRedInner">
+    <!-- <div
+      @click="playHanzi"
+      :class="[
+        'strock-play-box',
+        themeColor == 'green'
+          ? 'green-border'
+          : themeColor == 'red'
+          ? 'red-border'
+          : themeColor == 'brown'
+          ? 'brown-border'
+          : 'blue-border',
+      ]"
+      v-if="playStorkes"
+    ></div> -->
+    <template v-if="Book_text != '〇'">
+      <svg-icon
+        icon-class="play-stroke-icon"
+        :className="tianColor ? 'strock-play-red' : 'strock-play-box'"
+        v-if="playStorkes"
+        @click="playHanzi"
+      />
+      <div :id="targetDiv" class="character-target-div" :style="{ padding: '5px' }"></div>
+    </template>
+    <template v-else>
+      <span class="book-text">{{ Book_text }}</span>
+    </template>
+    <svg-icon icon-class="tian" :className="tianColor ? 'tian-bg-red' : 'tian-bg'" />
+  </div>
+</template>
+
+<script>
+import { GetStaticResources } from '@/api/app';
+const HanziWriter = require('hanzi-writer');
+export default {
+  components: {},
+  props: [
+    'targetDiv',
+    'Book_text',
+    'playStorkes',
+    'strokeColor',
+    'wordNum',
+    'themeColor',
+    'tianColor',
+    'curItem',
+    'type',
+    'judgeAnswer',
+    'hz_json',
+  ],
+  data() {
+    return {
+      writer: null,
+    };
+  },
+  computed: {},
+  watch: {
+    targetDiv: {
+      handler: function (val, oldVal) {
+        if (val != oldVal) {
+          let _this = this;
+          _this.$nextTick(() => {
+            _this.initHanziwrite();
+          });
+        }
+      },
+      // 深度观察监听
+      deep: true,
+    },
+  },
+  //方法集合
+  methods: {
+    initHanziwrite() {
+      let _this = this;
+      if (_this.Book_text == '〇') return false;
+      _this.writer = null;
+      //var ren = require("hanzi-writer-data/国");
+      if (_this.curItem) {
+        _this.writer = HanziWriter.default.create(_this.targetDiv, _this.Book_text, {
+          padding: 5,
+          showOutline: true,
+          strokeColor: _this.strokeColor ? (_this.judgeAnswer ? 'rgba(44, 44, 44, 0.45)' : _this.strokeColor) : '#333',
+          radicalColor: _this.judgeAnswer ? '#333' : '#ED5050',
+          charDataLoader: function (char, onComplete) {
+            let charData = _this.handleData(_this.curItem.answer);
+            onComplete(charData);
+          },
+        });
+      } else {
+        _this.writer = HanziWriter.default.create(_this.targetDiv, _this.Book_text, {
+          charDataLoader: function (char, onComplete) {
+            let MethodName = 'hz_resource_manager-GetHZStrokesContent';
+            let data = {
+              hz: char,
+            };
+            GetStaticResources(MethodName, data).then((res) => {
+              onComplete(res);
+            });
+          },
+          padding: 5,
+          showOutline: true,
+          strokeColor: _this.strokeColor ? _this.strokeColor : '#333',
+        });
+      }
+    },
+    handleData(stroke_num) {
+      if (this.hz_json) {
+        let charData = JSON.parse(JSON.stringify(this.hz_json));
+        if (stroke_num) {
+          let stroke_arr = null;
+          if (this.type == 'newWord_template') {
+            stroke_arr = stroke_num.split('、');
+          } else {
+            stroke_arr = stroke_num.split('#');
+          }
+          stroke_arr = stroke_arr.map((item) => (item = Number(item) - 1));
+          charData.radStrokes = stroke_arr;
+        } else if (charData) {
+          charData.radStrokes = [];
+        }
+        return charData;
+      }
+    },
+    playHanzi(event) {
+      let _this = this;
+      _this.writer.animateCharacter();
+      event.stopPropagation();
+    },
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {
+    let _this = this;
+    _this.$nextTick(() => {
+      _this.initHanziwrite();
+    });
+  },
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang="scss" scoped>
+//@import url(); 引入公共css类
+.strockplayRedInner {
+  position: relative;
+  width: 128px; //444px
+  height: 128px; //480px
+}
+
+.character-target-div {
+  z-index: 99999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  font-family: 'FZJCGFKTK';
+}
+
+.strockplayRedInner {
+  .strock-play-box {
+    position: absolute;
+    top: -2px;
+    right: -2px;
+    z-index: 998;
+
+    // @include font_color("sub_color");
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 22px;
+    height: 22px;
+    color: #346cda;
+    cursor: pointer;
+    background: none;
+  }
+
+  .strock-play-red {
+    position: absolute;
+    top: -2px;
+    right: -2px;
+    z-index: 999;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 22px;
+    height: 22px;
+    color: #d47064;
+    cursor: pointer;
+  }
+
+  .tian-bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    stroke: rgba(157, 202, 255, 20%);
+  }
+
+  .tian-bg-red {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    stroke: rgba(212, 112, 100, 20%);
+  }
+}
+
+.animate-butto {
+  width: 240px;
+  height: 160px;
+  font-size: 28px;
+}
+
+.book-text {
+  display: block;
+  width: 100%;
+  height: 100%;
+  font-size: 96px;
+  line-height: 128px;
+  text-align: center;
+}
+</style>

+ 1 - 1
src/views/book/courseware/preview/components/new_word/components/Strockplayredline.vue

@@ -60,7 +60,7 @@ export default {
       _this.writer = null;
       // var ren = require("hanzi-writer-data/国");
       _this.writer = HanziWriter.default.create(_this.targetDiv, _this.Book_text, {
-        charDataLoader (char, onComplete) {
+        charDataLoader(char, onComplete) {
           let MethodName = 'hz_resource_manager-GetHZStrokesContent';
           let data = {
             hz: char,