浏览代码

汉字组件

natasha 1 年之前
父节点
当前提交
9c06691cc7

+ 9 - 6
src/icons/svg/components/uploadControl.svg

@@ -1,7 +1,10 @@
-<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
-  <path
-    d="M11.6673 4.66699H35.0007L46.6673 16.3337V49.0003C46.6673 50.289 45.6227 51.3337 44.334 51.3337H11.6673C10.3787 51.3337 9.33398 50.289 9.33398 49.0003V7.00033C9.33398 5.71166 10.3787 4.66699 11.6673 4.66699Z"
-    stroke="black" stroke-width="4" stroke-linejoin="round" />
-  <path d="M18.6758 23.333L22.1758 39.6663L28.0091 27.9997L33.8424 39.6663L37.3424 23.333" stroke="black"
-    stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="16" height="16" fill="" />
+  <g id="Frame 427320222">
+    <g id="upload-line">
+      <path id="Vector"
+        d="M2 12.6654H14V13.9987H2V12.6654ZM8.66667 3.88432V11.332H7.33333V3.88432L3.28595 7.9317L2.34315 6.9889L8 1.33203L13.6569 6.9889L12.7141 7.9317L8.66667 3.88432Z"
+        fill="black" />
+    </g>
+  </g>
 </svg>

+ 3 - 5
src/icons/svg/components/uploadPreview.svg

@@ -1,7 +1,5 @@
-<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
   <path
-    d="M11.6673 4.66699H35.0007L46.6673 16.3337V49.0003C46.6673 50.289 45.6227 51.3337 44.334 51.3337H11.6673C10.3787 51.3337 9.33398 50.289 9.33398 49.0003V7.00033C9.33398 5.71166 10.3787 4.66699 11.6673 4.66699Z"
-    stroke="black" stroke-width="4" stroke-linejoin="round" />
-  <path d="M21 21.0098H35" stroke="black" stroke-width="4" stroke-linecap="round" />
-  <path d="M28.0098 21.0098V39.6667" stroke="black" stroke-width="4" stroke-linecap="round" />
+    d="M8.00028 2C11.595 2 14.5857 4.58651 15.2127 8C14.5857 11.4135 11.595 14 8.00028 14C4.4055 14 1.41485 11.4135 0.787842 8C1.41485 4.58651 4.4055 2 8.00028 2ZM8.00028 12.6667C10.824 12.6667 13.2403 10.7013 13.8519 8C13.2403 5.29869 10.824 3.33333 8.00028 3.33333C5.17648 3.33333 2.76023 5.29869 2.1486 8C2.76023 10.7013 5.17648 12.6667 8.00028 12.6667ZM8.00028 11C6.3434 11 5.00026 9.65687 5.00026 8C5.00026 6.34315 6.3434 5 8.00028 5C9.65708 5 11.0003 6.34315 11.0003 8C11.0003 9.65687 9.65708 11 8.00028 11ZM8.00028 9.66667C8.92075 9.66667 9.66695 8.92047 9.66695 8C9.66695 7.07953 8.92075 6.33333 8.00028 6.33333C7.07982 6.33333 6.33359 7.07953 6.33359 8C6.33359 8.92047 7.07982 9.66667 8.00028 9.66667Z"
+    fill="#4E5969" />
 </svg>

+ 132 - 0
src/views/book/courseware/create/components/question/character/Character.vue

@@ -0,0 +1,132 @@
+<template>
+  <ModuleBase :type="data.type">
+    <template #content>
+      <!-- eslint-disable max-len -->
+      <div class="fill-wrapper">
+        <el-input v-model="data.content" placeholder="输入" type="textarea"></el-input>
+        <span class="tips">输入字或词请一字一行</span>
+      </div>
+    </template>
+  </ModuleBase>
+</template>
+
+<script>
+import ModuleMixin from '../../common/ModuleMixin';
+import SoundRecord from '@/views/book/courseware/create/components/question/fill/components/SoundRecord.vue';
+import UploadAudio from '@/views/book/courseware/create/components/question/fill/components/UploadAudio.vue';
+
+import { getCharacterData } from '@/views/book/courseware/data/character';
+import { GetStaticResources } from '@/api/app';
+
+export default {
+  name: 'CharacterPage',
+  components: {
+    SoundRecord,
+    UploadAudio,
+  },
+  mixins: [ModuleMixin],
+  data() {
+    return {
+      data: getCharacterData(),
+    };
+  },
+  methods: {
+    uploads(file_id) {
+      this.data.audio_file_id = file_id;
+    },
+    deleteFiles() {
+      this.data.audio_file_id = '';
+    },
+    // 自动生成音频
+    handleMatic() {
+      GetStaticResources('tool-TextToVoiceFile', {
+        text: this.data.content.replace(/<[^>]+>/g, ''),
+      })
+        .then(({ status, file_id }) => {
+          if (status === 1) {
+            this.data.audio_file_id = file_id;
+          }
+        })
+        .catch(() => {});
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.fill-wrapper {
+  display: flex;
+  flex-direction: column;
+  row-gap: 16px;
+  align-items: flex-start;
+
+  :deep .rich-wrapper {
+    width: 100%;
+  }
+
+  .tips {
+    font-size: 12px;
+    color: #999;
+  }
+
+  .auto-matic,
+  .upload-audio-play {
+    :deep .upload-wrapper {
+      margin-top: 0;
+    }
+
+    .audio-wrapper {
+      :deep .audio-play {
+        width: 16px;
+        height: 16px;
+        color: #000;
+        background-color: initial;
+      }
+
+      :deep .audio-play.not-url {
+        color: #a1a1a1;
+      }
+
+      :deep .voice-play {
+        width: 16px;
+        height: 16px;
+      }
+    }
+  }
+
+  .auto-matic {
+    display: flex;
+    flex-shrink: 0;
+    column-gap: 12px;
+    align-items: center;
+    width: 200px;
+    padding: 5px 12px;
+    background-color: $fill-color;
+    border-radius: 2px;
+
+    .auto-btn {
+      font-size: 16px;
+      font-weight: 400;
+      line-height: 22px;
+      color: #1d2129;
+      cursor: pointer;
+    }
+  }
+
+  .correct-answer {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+
+    .el-input {
+      width: 180px;
+
+      :deep &__prefix {
+        display: flex;
+        align-items: center;
+        color: $text-color;
+      }
+    }
+  }
+}
+</style>

+ 77 - 0
src/views/book/courseware/create/components/question/character/CharacterSetting.vue

@@ -0,0 +1,77 @@
+<template>
+  <div>
+    <el-form :model="property" label-width="72px" label-position="left">
+      <SerailNumber :property="property" />
+      <el-form-item label="汉字框">
+        <el-radio-group v-model="property.frame_type">
+          <el-radio v-for="{ value, label } in frameList" :key="value" :label="value" :value="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="框颜色">
+        <el-color-picker v-model="property.frame_color"></el-color-picker>
+      </el-form-item>
+
+      <el-divider />
+      <el-form-item label="笔画动画">
+        <el-radio-group v-model="property.is_enable_stroke">
+          <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_voice">
+          <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>
+
+<script>
+import SettingMixin from '@/views/book/courseware/create/components/common/SettingMixin';
+
+import {
+  getCharacterProperty,
+  switchOption,
+  funList,
+  showList,
+  isEnable,
+  frameList,
+} from '@/views/book/courseware/data/character';
+
+export default {
+  name: 'CharacterSetting',
+  mixins: [SettingMixin],
+  data() {
+    return {
+      property: getCharacterProperty(),
+      switchOption,
+      isEnable,
+      funList,
+      showList,
+      frameList,
+    };
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.el-form {
+  @include setting-base;
+}
+</style>

+ 49 - 0
src/views/book/courseware/create/components/question/new_word/NewWord.vue

@@ -0,0 +1,49 @@
+<template>
+  <ModuleBase :type="data.type">
+    <template #content>
+      <el-form :model="data" label-width="72px" label-position="left">
+        <el-form-item label="控件规格">
+          <el-radio-group v-model="data.size">
+            <el-radio v-for="{ value, label } in sizeList" :key="value" :label="value">
+              {{ label }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="录音评分">
+          <el-radio-group v-model="data.is_enable_score">
+            <el-radio v-for="{ value, label } in switchOption" :key="value" :label="value">
+              {{ label }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="输入">
+          <el-radio-group v-model="data.is_enable_input">
+            <el-radio v-for="{ value, label } in switchOption" :key="value" :label="value">
+              {{ label }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+    </template>
+  </ModuleBase>
+</template>
+
+<script>
+import ModuleMixin from '../../common/ModuleMixin';
+
+import { getRecordInputData, sizeList, switchOption } from '@/views/book/courseware/data/recordInput';
+
+export default {
+  name: 'RecordInputPage',
+  components: {},
+  mixins: [ModuleMixin],
+  data() {
+    return {
+      data: getRecordInputData(),
+      sizeList,
+      switchOption,
+    };
+  },
+  methods: {},
+};
+</script>

+ 44 - 0
src/views/book/courseware/create/components/question/new_word/NewWordSetting.vue

@@ -0,0 +1,44 @@
+<template>
+  <div>
+    <el-form :model="property" label-width="72px" label-position="left">
+      <SerailNumber :property="property" />
+    </el-form>
+  </div>
+</template>
+
+<script>
+import SettingMixin from '@/views/book/courseware/create/components/common/SettingMixin';
+
+import {
+  getRecordInputProperty,
+  arrangeTypeList,
+  audioPositionList,
+  audioGenerationMethodList,
+  fillFontList,
+  switchOption,
+} from '@/views/book/courseware/data/recordInput';
+
+export default {
+  name: 'RecordInputSetting',
+  mixins: [SettingMixin],
+  data() {
+    return {
+      property: getRecordInputProperty(),
+      arrangeTypeList,
+      audioPositionList,
+      audioGenerationMethodList,
+      fillFontList,
+      switchOption,
+    };
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.el-form {
+  @include setting-base;
+}
+</style>

+ 19 - 0
src/views/book/courseware/data/bookType.js

@@ -34,6 +34,8 @@ import PinyinBase from '../create/components/base/pinyin_base/PinyinBase.vue';
 import PinyinBaseSetting from '../create/components/base/pinyin_base/PinyinBaseSetting.vue';
 import CharacterBase from '../create/components/base/character_base/CharacterBase.vue';
 import CharacterBaseSetting from '../create/components/base/character_base/CharacterBaseSetting.vue';
+import Character from '../create/components/question/character/Character.vue';
+import CharacterSetting from '../create/components/question/character/CharacterSetting.vue';
 
 import AudioPreview from '@/views/book/courseware/preview/components/audio/AudioPreview.vue';
 import DividerPreview from '@/views/book/courseware/preview/components/divider/DividerPreview.vue';
@@ -53,6 +55,7 @@ import UploadControlPreview from '../preview/components/upload_control/UploadCon
 import UploadPreviewPreview from '../preview/components/upload_preview/UploadPreviewPreview.vue';
 import PinyinBasePreview from '../preview/components/pinyin_base/PinyinBasePreview.vue';
 import CharacterBasePreview from '../preview/components/character_base/CharacterBasePreview.vue';
+import CharacterPreview from '../preview/components/character/CharacterPreview.vue';
 
 export const bookTypeOption = [
   {
@@ -210,6 +213,22 @@ export const bookTypeOption = [
         set: RecordInputSetting,
         preview: RecordInputPreview,
       },
+      {
+        value: 'new_word',
+        label: '生词组件',
+        icon: '',
+        component: RecordInput,
+        set: RecordInputSetting,
+        preview: RecordInputPreview,
+      },
+      {
+        value: 'character',
+        label: '汉字组件',
+        icon: '',
+        component: Character,
+        set: CharacterSetting,
+        preview: CharacterPreview,
+      },
     ],
   },
 ];

+ 69 - 0
src/views/book/courseware/data/character.js

@@ -0,0 +1,69 @@
+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 getCharacterProperty() {
+  return {
+    serial_number: 1,
+    sn_type: serialNumberTypeList[0].value,
+    sn_position: serialNumberPositionList[0].value,
+    sn_display_mode: displayList[0].value,
+
+    is_enable_pinyin: showList[0].value,
+    frame_type: 'tian',
+    frame_color: '#F13232',
+    is_enable_stroke: showList[0].value,
+    is_enable_voice: showList[0].value,
+  };
+}
+
+export function getCharacterData() {
+  return {
+    type: 'character',
+    title: '汉字',
+    property: getCharacterProperty(),
+    content: '',
+    contentList: [],
+    audio_file_id: '',
+    answer: {
+      answer_list: [],
+    },
+  };
+}

+ 1 - 1
src/views/book/courseware/data/characterBase.js

@@ -94,7 +94,7 @@ export function getCharacterBaseProperty() {
 export function getCharacterBaseData() {
   return {
     type: 'character_base',
-    title: '拼音',
+    title: '基础汉字',
     property: getCharacterBaseProperty(),
     content: '',
     pinyin: '',

+ 4 - 4
src/views/book/courseware/data/pinyinBase.js

@@ -40,10 +40,10 @@ export const funList = [
     value: 'show',
     label: '拼音展示',
   },
-  {
-    value: 'input',
-    label: '输入',
-  },
+  // {
+  //   value: 'input',
+  //   label: '输入',
+  // },
 ];
 
 // 标声调类型

+ 141 - 0
src/views/book/courseware/preview/components/character/CharacterPreview.vue

@@ -0,0 +1,141 @@
+<!-- eslint-disable vue/no-v-html -->
+<template>
+  <div class="select-preview" :style="getAreaStyle()">
+    <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
+
+    <div class="main" :style="getMainStyle()">预览开发中</div>
+  </div>
+</template>
+
+<script>
+import {
+  getCharacterData,
+  fillFontList,
+  arrangeTypeList,
+  audioPositionList,
+} from '@/views/book/courseware/data/character';
+
+import PreviewMixin from '../common/PreviewMixin';
+import AudioFill from '../fill/components/AudioFillPlay.vue';
+import SoundRecord from '../../common/SoundRecord.vue';
+
+export default {
+  name: 'CharacterPreview',
+  components: {
+    AudioFill,
+    SoundRecord,
+  },
+  mixins: [PreviewMixin],
+  data() {
+    return {
+      data: getCharacterData(),
+    };
+  },
+  computed: {
+    fontFamily() {
+      return fillFontList.find(({ value }) => this.data.property.fill_font === value).font;
+    },
+  },
+  created() {
+    this.answer.answer_list = this.data.model_essay
+      .map((item) => {
+        return item
+          .map(({ type, content, mark }) => {
+            if (type === 'input') {
+              return {
+                value: content,
+                mark,
+              };
+            }
+          })
+          .filter((item) => item);
+      })
+      .flat();
+  },
+  methods: {
+    getMainStyle() {
+      const isRow = this.data.property.arrange_type === arrangeTypeList[0].value;
+      const isFront = this.data.property.audio_position === audioPositionList[0].value;
+      const isEnableVoice = this.data.property.is_enable_voice_answer === 'true';
+      let _list = [
+        { name: 'audio', value: '24px' },
+        { name: 'fill', value: '1fr' },
+      ];
+      if (!isFront) {
+        _list = _list.reverse();
+      }
+      let grid = isRow
+        ? `"${_list[0].name} ${_list[1].name}${isEnableVoice ? ' record' : ''}" auto / ${_list[0].value} ${_list[1].value}${isEnableVoice ? ' 160px' : ''}`
+        : `"${_list[0].name}" ${_list[0].value} "${_list[1].name}" ${_list[1].value}${isEnableVoice ? `" record" 32px ` : ''} / 1fr`;
+      let style = {
+        'grid-auto-flow': isRow ? 'column' : 'row',
+        'column-gap': isRow ? '16px' : undefined,
+        'row-gap': isRow ? undefined : '8px',
+        grid,
+      };
+      return style;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.select-preview {
+  @include preview-base;
+
+  .main {
+    display: grid;
+    align-items: center;
+  }
+
+  .fill-wrapper {
+    grid-area: fill;
+    font-size: 16pt;
+
+    p {
+      margin: 0;
+    }
+
+    .el-input {
+      display: inline-flex;
+      align-items: center;
+      width: 120px;
+      margin: 0 2px;
+
+      &.pinyin :deep input.el-input__inner {
+        font-family: 'PINYIN-B', sans-serif;
+      }
+
+      &.chinese :deep input.el-input__inner {
+        font-family: 'arial', sans-serif;
+      }
+
+      &.english :deep input.el-input__inner {
+        font-family: 'arial', sans-serif;
+      }
+
+      :deep input.el-input__inner {
+        padding: 0;
+        font-size: 16pt;
+        color: $font-color;
+        text-align: center;
+        background-color: #fff;
+        border-width: 0;
+        border-bottom: 1px solid $font-color;
+        border-radius: 0;
+      }
+    }
+  }
+
+  .record-box {
+    padding: 6px 12px;
+    background-color: $fill-color;
+
+    :deep .record-time {
+      width: 100px;
+    }
+  }
+}
+</style>

+ 2 - 0
src/views/book/courseware/preview/components/record_input/RecordInputPreview.vue

@@ -22,6 +22,7 @@
 
 <script>
 import { getFillData } from '@/views/book/courseware/data/fill';
+import { getConfig } from '@/utils/auth';
 
 import PreviewMixin from '../common/PreviewMixin';
 import SoundRecord from '../../common/SoundRecord.vue';
@@ -39,6 +40,7 @@ export default {
   },
   computed: {},
   created() {
+    // console.log(getConfig());
     this.answer.answer_list = {
       answerRecordList: [],
       input: '',