Browse Source

选择题、判断题的分词校对,将分词校对做成公用文件

dsy 1 month ago
parent
commit
7ccee719ae

+ 6 - 3
src/views/book/courseware/create/components/common/ModuleMixin.js

@@ -316,7 +316,7 @@ const mixin = {
      * @param {Number} j 列索引
      * @param {String} attr 属性名
      */
-    createParsedTextInfoPinyin(text, i = -1, j = -1, attr = 'option_list') {
+    createParsedTextInfoPinyin(text, i = -1, j = -1, attr = 'option_list', fc_list = []) {
       let data = null;
       if (i === -1) {
         data = this.data;
@@ -328,17 +328,19 @@ const mixin = {
 
       if (text === '') {
         data.pinyin_proofread_word_list = [];
-        return;
+        return Promise.resolve({ parsed_text: null, rich_text: null });
       }
 
       data.paragraph_list_parameter.text = text;
 
-      PinyinBuild_OldFormat({
+      return PinyinBuild_OldFormat({
         ...data.paragraph_list_parameter,
         is_first_sentence_first_hz_pinyin_first_char_upper_case:
           this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case,
         is_fill_space: 'true',
         is_rich_text: 'true',
+        is_custom_fc: fc_list.length > 0,
+        fc_paragraph_list: fc_list,
       }).then(({ parsed_text, rich_text }) => {
         if (parsed_text) {
           // 合并 activeTextStyle
@@ -362,6 +364,7 @@ const mixin = {
         if (rich_text) {
           data.rich_text_list = rich_text.text_list;
         }
+        return { parsed_text, rich_text };
       });
     },
     /**

+ 6 - 12
src/views/book/courseware/create/components/common/SelectUpload.vue

@@ -16,18 +16,12 @@
       >
         <el-button>{{ showText }}</el-button>
       </el-upload>
-      <el-button
-size="small"
-class="upload-button" :type="isNormal ? '' : 'primary'"
-@click="selectAndUpload"
-        >本地上传</el-button
-      >
-      <el-button
-v-if="isShowResource"
-size="small" :type="isNormal ? '' : 'primary'"
-@click="useResource"
-        >使用资源</el-button
-      >
+      <el-button size="small" class="upload-button" :type="isNormal ? '' : 'primary'" @click="selectAndUpload">
+        本地上传
+      </el-button>
+      <el-button v-if="isShowResource" size="small" :type="isNormal ? '' : 'primary'" @click="useResource">
+        使用资源
+      </el-button>
     </div>
     <SelectResource
       :visible.sync="visibleResource"

+ 62 - 0
src/views/book/courseware/create/components/common/WordProofread.vue

@@ -0,0 +1,62 @@
+<template>
+  <span class="word-proofread-wrapper">
+    <el-button class="btn" :style="buttonStyle" @click.native="showWordFlag = true">{{ buttonText }}</el-button>
+    <el-dialog
+      v-if="showWordFlag"
+      :visible.sync="showWordFlag"
+      :show-close="true"
+      :close-on-click-modal="true"
+      :modal-append-to-body="true"
+      :append-to-body="true"
+      :lock-scroll="true"
+      width="80%"
+      class="practiceBox"
+    >
+      <CheckWord :data="safeWordData" @saveWord="handleSaveWord" />
+    </el-dialog>
+  </span>
+</template>
+
+<script>
+import CheckWord from '@/views/book/courseware/create/components/base/common/CheckWord.vue';
+
+export default {
+  name: 'WordProofread',
+  components: {
+    CheckWord,
+  },
+  props: {
+    wordData: {
+      type: Object,
+      default: () => ({ detail: [] }),
+    },
+    buttonText: {
+      type: String,
+      default: '分词校对',
+    },
+    buttonStyle: {
+      type: String,
+      default: '',
+    },
+  },
+  data() {
+    return {
+      showWordFlag: false,
+    };
+  },
+  computed: {
+    safeWordData() {
+      if (!this.wordData || !Array.isArray(this.wordData.detail)) {
+        return { detail: [] };
+      }
+      return this.wordData;
+    },
+  },
+  methods: {
+    handleSaveWord(saveArr) {
+      this.$emit('save', saveArr);
+      this.showWordFlag = false;
+    },
+  },
+};
+</script>

+ 23 - 0
src/views/book/courseware/create/components/common/wordProofread.js

@@ -0,0 +1,23 @@
+/**
+ * 构建校对数据
+ * @param {Array} paragraphList 段落列表
+ * @returns {Object} 返回构建后的数据对象
+ */
+export function buildWordProofreadData(paragraphList = []) {
+  const detail = [];
+  paragraphList.forEach((paragraph, index) => {
+    const sentenceList = [];
+    const segList = [];
+    paragraph.forEach((sentence) => {
+      const sentenceText = sentence.reduce((acc, wordItem) => `${acc + (wordItem?.text || '')}`, '');
+      const seg = sentence.map((wordItem) => wordItem?.text || '');
+      sentenceList.push(sentenceText);
+      segList.push(seg);
+    });
+    detail[index] = {
+      sentences: sentenceList,
+      segList,
+    };
+  });
+  return { detail };
+}

+ 46 - 2
src/views/book/courseware/create/components/question/fill/Fill.vue

@@ -76,6 +76,12 @@
         <div>
           <el-button @click="parsedContentPinyin()">识别</el-button>
           <el-button @click="openMultilingual">多语言</el-button>
+          <WordProofread
+            v-show="isEnable(data.property.view_pinyin)"
+            button-style="margin-left: 10px"
+            :word-data="wordData"
+            @save="saveWord"
+          />
         </div>
 
         <div v-if="data.answer.answer_list.length > 0" class="title">答案:</div>
@@ -117,6 +123,8 @@ import SoundRecord from '@/views/book/courseware/create/components/question/fill
 import UploadAudio from '@/views/book/courseware/create/components/question/fill/components/UploadAudio.vue';
 import PinyinText from '@/components/PinyinText.vue';
 import AddWord from './components/AddWord.vue';
+import WordProofread from '@/views/book/courseware/create/components/common/WordProofread.vue';
+import { buildWordProofreadData } from '@/views/book/courseware/create/components/common/wordProofread';
 
 import { getFillData, arrangeTypeList, fillTypeList } from '@/views/book/courseware/data/fill';
 import { addTone, handleToneValue, mergePunctuationToWords } from '@/views/book/courseware/data/common';
@@ -132,6 +140,7 @@ export default {
     UploadAudio,
     PinyinText,
     AddWord,
+    WordProofread,
   },
   mixins: [ModuleMixin],
   data() {
@@ -141,6 +150,8 @@ export default {
       sanitizeHTML,
       visibleWord: false,
       rich_text_list: [],
+      wordData: { detail: [] }, // 分词校对数据
+      inited: false,
       loading: false,
     };
   },
@@ -157,6 +168,15 @@ export default {
   watch: {
     'data.property.arrange_type': 'handleMindMap',
     'data.property.fill_font': 'handleMindMap',
+    'data.content': {
+      handler(newVal) {
+        if (!this.inited && newVal) {
+          this.ensureWordProofreadData().finally(() => {
+            this.inited = true;
+          });
+        }
+      },
+    },
     'data.property': {
       handler(val) {
         if (this.property.isSilentPropertyWatch) return;
@@ -168,11 +188,29 @@ export default {
     },
   },
   methods: {
+    async ensureWordProofreadData() {
+      const text = this.data.content || '';
+      if (Array.isArray(this.wordData?.detail) && this.wordData.detail.length > 0) {
+        return;
+      }
+      if (!text) {
+        this.wordData = { detail: [] };
+        return;
+      }
+      const { parsed_text } = await PinyinBuild_OldFormat({
+        text,
+        is_first_sentence_first_hz_pinyin_first_char_upper_case:
+          this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case,
+        is_fill_space: 'true',
+        is_rich_text: 'true',
+      });
+      this.wordData = buildWordProofreadData(parsed_text?.paragraph_list || []);
+    },
     /**
      * 解析文本内容,生成分词和拼音
      * @param {boolean} isPrompt 是否为提示操作,默认为 false
      */
-    async parsedContentPinyin(isPrompt = false) {
+    async parsedContentPinyin(isPrompt = false, fcList = []) {
       if (isPrompt) {
         try {
           await this.$confirm('刷新将会重置填空和拼音,是否刷新?', '提示', {
@@ -191,8 +229,10 @@ export default {
           this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case,
         is_fill_space: 'true',
         is_rich_text: 'true',
+        is_custom_fc: fcList.length > 0,
+        fc_paragraph_list: fcList,
       })
-        .then(({ rich_text }) => {
+        .then(({ rich_text, parsed_text }) => {
           if (!rich_text) return;
           if (!Array.isArray(rich_text.text_list)) return;
 
@@ -206,12 +246,16 @@ export default {
               word_list: mergePunctuationToWords(item.word_list),
             };
           });
+          this.wordData = buildWordProofreadData(parsed_text?.paragraph_list || []);
           this.data.model_essay = this.parseRichText();
         })
         .finally(() => {
           this.loading = false;
         });
     },
+    async saveWord(saveArr) {
+      await this.parsedContentPinyin(false, saveArr);
+    },
     // 解析富文本,构建 model_essay 结构
     parseRichText() {
       let text_list = this.rich_text_list || [];

+ 104 - 16
src/views/book/courseware/create/components/question/select/Select.vue

@@ -51,16 +51,30 @@
             @click.native="handleViewPinyin(true)"
           />
         </el-divider>
-        <PinyinText
-          v-for="(item, i) in data.option_list"
-          v-show="isEnable(data.property.view_pinyin)"
-          :key="i"
-          ref="PinyinText"
-          :paragraph-list="item.paragraph_list"
-          :rich-text-list="item.rich_text_list"
-          :pinyin-position="data.property.pinyin_position"
-          @fillCorrectPinyin="fillCorrectPinyin($event, i)"
-        />
+        <div v-show="isEnable(data.property.view_pinyin)" class="proofread-table-wrapper">
+          <table class="proofread-table">
+            <tbody>
+              <tr v-for="(item, i) in data.option_list" :key="item.mark">
+                <td class="pinyin-cell">
+                  <PinyinText
+                    ref="PinyinText"
+                    :paragraph-list="item.paragraph_list"
+                    :rich-text-list="item.rich_text_list"
+                    :pinyin-position="data.property.pinyin_position"
+                    @fillCorrectPinyin="fillCorrectPinyin($event, i)"
+                  />
+                </td>
+                <td class="proofread-cell">
+                  <WordProofread
+                    button-text="分词校对"
+                    :word-data="getOptionWordData(item)"
+                    @save="saveOptionWord($event, i)"
+                  />
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
       </div>
 
       <MultilingualFill
@@ -86,6 +100,8 @@
 <script>
 import ModuleMixin from '../../common/ModuleMixin';
 import PinyinText from '@/components/PinyinText.vue';
+import WordProofread from '@/views/book/courseware/create/components/common/WordProofread.vue';
+import { buildWordProofreadData } from '@/views/book/courseware/create/components/common/wordProofread';
 
 import { getSelectData, getOption, arrangeTypeList, selectTypeList } from '@/views/book/courseware/data/select';
 import { serialNumberTypeList, computeOptionMethods } from '@/views/book/courseware/data/common';
@@ -94,12 +110,16 @@ export default {
   name: 'SelectPage',
   components: {
     PinyinText,
+    WordProofread,
   },
   mixins: [ModuleMixin],
   data() {
     return {
       data: getSelectData(),
       curSelectIndex: -1,
+      optionWordDataMap: {},
+      optionWordDataSourceMap: {},
+      inited: false,
       loading: false,
     };
   },
@@ -111,11 +131,24 @@ export default {
   watch: {
     'data.property.arrange_type': 'handlerMindMap',
     'data.answer.answer_list': 'handlerMindMap',
+    'data.option_list': {
+      handler(newVal) {
+        if (this.loading) return;
+        if (this.inited || !Array.isArray(newVal) || newVal.length <= 0) return;
+        if (!this.isEnable(this.data.property.view_pinyin)) return;
+        this.handleViewPinyin().finally(() => {
+          this.inited = true;
+        });
+      },
+      deep: true,
+    },
     'data.property': {
       handler(val) {
         if (this.property.isSilentPropertyWatch) return;
         if (val.view_pinyin === 'true') {
-          this.handleViewPinyin();
+          this.handleViewPinyin().finally(() => {
+            this.inited = true;
+          });
         }
       },
       deep: true,
@@ -150,18 +183,40 @@ export default {
             pinyin_proofread_word_list: [],
           };
         });
+        this.optionWordDataMap = {};
+        this.optionWordDataSourceMap = {};
         this.loading = false;
         return;
       }
-      if (this.data.option_list.length > 0 && this.data.option_list[0].paragraph_list.length > 0) return;
-      this.data.option_list.forEach((item, i) => {
+      const tasks = this.data.option_list.map((item, i) => {
         const text = item.content;
-        if (!text) return;
+        if (!text) return Promise.resolve();
         item.paragraph_list_parameter.text = text;
-        this.createParsedTextInfoPinyin(text, i);
+        return this.createParsedTextInfoPinyin(text, i).then(({ parsed_text }) => {
+          this.$set(this.optionWordDataMap, item.mark, buildWordProofreadData(parsed_text?.paragraph_list || []));
+          this.$set(this.optionWordDataSourceMap, item.mark, text);
+        });
       });
+      await Promise.all(tasks);
       this.loading = false;
     },
+    getOptionWordData(option) {
+      if (!option || !option.mark) return { detail: [] };
+      return this.optionWordDataMap[option.mark] || { detail: [] };
+    },
+    async saveOptionWord(saveArr, optionIndex) {
+      const option = this.data.option_list[optionIndex];
+      if (!option) return;
+      const { parsed_text } = await this.createParsedTextInfoPinyin(
+        option.content,
+        optionIndex,
+        -1,
+        'option_list',
+        saveArr,
+      );
+      this.$set(this.optionWordDataMap, option.mark, buildWordProofreadData(parsed_text?.paragraph_list || []));
+      this.$set(this.optionWordDataSourceMap, option.mark, option.content || '');
+    },
     computedOptionNumber(number) {
       let type = serialNumberTypeList.find((item) => item.value === this.data.property.option_serial_type)?.value;
       if (!type) return number + 1;
@@ -199,7 +254,10 @@ export default {
      * @description 添加选项
      */
     addOption() {
-      this.data.option_list.push(getOption());
+      const newOption = getOption();
+      this.data.option_list.push(newOption);
+      this.$set(this.optionWordDataMap, newOption.mark, { detail: [] });
+      this.$set(this.optionWordDataSourceMap, newOption.mark, '');
     },
     /**
      * @description 删除选项
@@ -210,7 +268,12 @@ export default {
         this.$message.warning('请至少保留两个选项');
         return;
       }
+      const option = this.data.option_list[index];
       this.data.option_list.splice(index, 1);
+      if (option?.mark) {
+        this.$delete(this.optionWordDataMap, option.mark);
+        this.$delete(this.optionWordDataSourceMap, option.mark);
+      }
     },
     handlerMindMap() {
       const direction = this.data.property.arrange_type === arrangeTypeList[0].value ? '横排' : '竖排';
@@ -320,4 +383,29 @@ export default {
     cursor: pointer;
   }
 }
+
+.proofread-table-wrapper {
+  margin-top: 8px;
+}
+
+.proofread-table {
+  width: 100%;
+  border-collapse: collapse;
+
+  td {
+    padding: 8px;
+    vertical-align: top;
+    border: 1px solid #ebeef5;
+  }
+
+  .pinyin-cell {
+    width: calc(100% - 140px);
+  }
+
+  .proofread-cell {
+    width: 140px;
+    text-align: center;
+    white-space: nowrap;
+  }
+}
 </style>