natasha 10 月之前
父節點
當前提交
d27eb54a36

+ 149 - 0
src/views/book/courseware/create/components/question/article/Article.vue

@@ -0,0 +1,149 @@
+<template>
+  <ModuleBase :type="data.type">
+    <template #content>
+      <!-- eslint-disable max-len -->
+      <div class="fill-wrapper">
+        <el-input v-model="data.content" placeholder="输入" type="textarea" @change="handleChangeContent"></el-input>
+      </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 { getArticleData } from '@/views/book/courseware/data/article';
+import { GetStaticResources } from '@/api/app';
+import cnchar from 'cnchar';
+
+export default {
+  name: 'ArticlePage',
+  components: {
+    SoundRecord,
+    UploadAudio,
+  },
+  mixins: [ModuleMixin],
+  data() {
+    return {
+      data: getArticleData(),
+    };
+  },
+  created() {
+    // console.log(this.data);
+  },
+  methods: {
+    // 解析输入内容
+    handleChangeContent() {
+      if (this.data.content.trim()) {
+        let contentArr = this.data.content.split('\n');
+        let contentList = [];
+        contentArr.forEach((item, index) => {
+          if (item.trim()) {
+            contentList.push({
+              con: item.trim(),
+              pinyin: cnchar.spell(item.trim(), 'array', 'low', 'tone').join(' '),
+              audio_file_id: '',
+            });
+            this.handleMatic(item.trim(), contentList.length - 1);
+          }
+        });
+        this.data.content_list = contentList;
+      } else {
+        this.data.content_list = [];
+      }
+    },
+    // 自动生成音频
+    handleMatic(con, index) {
+      GetStaticResources('tool-TextToVoiceFile', {
+        text: con.replace(/<[^>]+>/g, ''),
+      })
+        .then(({ status, file_id }) => {
+          if (status === 1) {
+            this.data.content_list[index].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>

+ 79 - 0
src/views/book/courseware/create/components/question/article/ArticleSetting.vue

@@ -0,0 +1,79 @@
+<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.mp3_position">
+          <el-radio v-for="{ value, label } in positionList" :key="value" :label="value" :value="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <el-divider />
+      <el-form-item label="生词短语">
+        <el-radio-group v-model="property.is_enable_new_word">
+          <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="property.is_enable_read">
+          <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="property.is_enable_word">
+          <el-radio v-for="{ value, label } in switchOption" :key="value" :label="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-divider />
+      <el-form-item label="拼音">
+        <el-switch v-model="property.is_enable_pinyin" active-text="" inactive-text=""> </el-switch>
+      </el-form-item>
+      <el-form-item label="拼音位置" v-if="property.is_enable_pinyin">
+        <el-radio-group v-model="property.pinyin_position">
+          <el-radio v-for="{ value, label } in positionList" :key="value" :label="value" :value="value">
+            {{ label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="" v-if="property.is_enable_pinyin">
+        <el-checkbox v-model="property.is_enable_sentence_case">句首大写</el-checkbox>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import SettingMixin from '@/views/book/courseware/create/components/common/SettingMixin';
+
+import { getArticleProperty, switchOption, positionList, isEnable } from '@/views/book/courseware/data/article';
+
+export default {
+  name: 'ArticleSetting',
+  mixins: [SettingMixin],
+  data() {
+    return {
+      property: getArticleProperty(),
+      switchOption,
+      isEnable,
+      positionList,
+    };
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.el-form {
+  @include setting-base;
+}
+</style>

+ 71 - 0
src/views/book/courseware/data/article.js

@@ -0,0 +1,71 @@
+import {
+  displayList,
+  serialNumberTypeList,
+  serialNumberPositionList,
+  arrangeTypeList,
+  switchOption,
+  isEnable,
+} from '@/views/book/courseware/data/common';
+
+export { arrangeTypeList, switchOption, isEnable };
+
+
+
+// 显示
+export const positionList = [
+  {
+    value: 'top',
+    label: '上',
+  },
+  {
+    value: 'bottom',
+    label: '下',
+  },
+];
+
+// 汉字框
+export const frameList = [
+  {
+    value: 'tian',
+    label: '田字格',
+  },
+  {
+    value: 'fang',
+    label: '方框',
+  },
+  {
+    value: 'none',
+    label: '无',
+  },
+];
+
+export function getArticleProperty() {
+  return {
+    serial_number: 1,
+    sn_type: serialNumberTypeList[0].value,
+    sn_position: serialNumberPositionList[0].value,
+    sn_display_mode: displayList[0].value,
+    mp3_position: positionList[0].value,
+    is_enable_new_word: switchOption[0].value,
+    is_enable_read: switchOption[0].value,
+    is_enable_word: switchOption[0].value,
+    is_enable_pinyin: true,
+    pinyin_position: positionList[0].value,
+    is_enable_sentence_case: true,
+  };
+}
+
+export function getArticleData() {
+  return {
+    type: 'article',
+    title: '课文组件',
+    property: getArticleProperty(),
+    content: '', // 课文内容
+    mp3_list: [], // 音频列表
+    detail: [], // 分段分句分词详情
+    word_time: [], // 字幕时间
+    new_word_list: [], // 生词列表
+    sentence_list: [], // 句子列表
+    sentence_list_mp: [], //句子+分词数组
+  };
+}

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

@@ -44,6 +44,8 @@ import Notes from '../create/components/question/notes/Notes.vue';
 import NotesSetting from '../create/components/question/notes/NotesSetting.vue';
 import OtherWord from '../create/components/question/other_word/OtherWord.vue';
 import OtherWordSetting from '../create/components/question/other_word/OtherWordSetting.vue';
+import Article from '../create/components/question/article/Article.vue';
+import ArticleSetting from '../create/components/question/article/ArticleSetting.vue';
 
 import AudioPreview from '@/views/book/courseware/preview/components/audio/AudioPreview.vue';
 import DividerPreview from '@/views/book/courseware/preview/components/divider/DividerPreview.vue';
@@ -68,6 +70,7 @@ import WritePreview from '../preview/components/write/WritePreview.vue';
 import NewWordPreview from '../preview/components/new_word/NewWordPreview.vue';
 import NotesPreview from '../preview/components/notes/NotesPreview.vue';
 import OtherWordPreview from '../preview/components/other_word/OtherWordPreview.vue';
+import ArticlePreview from '../preview/components/article/index.vue'
 
 export const bookTypeOption = [
   {
@@ -178,6 +181,14 @@ export const bookTypeOption = [
     label: '题型组件',
     children: [
       {
+        value: 'article',
+        label: '文章组件',
+        icon: '',
+        component: Article,
+        set: ArticleSetting,
+        preview: ArticlePreview,
+      },
+      {
         value: 'select',
         label: '选择组件',
         icon: '',

+ 136 - 0
src/views/book/courseware/preview/components/article/index.vue

@@ -0,0 +1,136 @@
+<!-- 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 { getArticleData, fillFontList, arrangeTypeList, audioPositionList } from '@/views/book/courseware/data/article';
+
+import PreviewMixin from '../common/PreviewMixin';
+import AudioFill from '../fill/components/AudioFillPlay.vue';
+import SoundRecord from '../../common/SoundRecord.vue';
+
+export default {
+  name: 'ArticlePreview',
+  components: {
+    AudioFill,
+    SoundRecord,
+  },
+  mixins: [PreviewMixin],
+  data() {
+    return {
+      data: getArticleData(),
+    };
+  },
+  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>