|
|
@@ -14,7 +14,7 @@
|
|
|
:font-size="data?.unified_attrib?.font_size"
|
|
|
:font-family="data?.unified_attrib?.font"
|
|
|
:font-color="data?.unified_attrib?.text_color"
|
|
|
- @handleRichTextBlur="identifyText(false)"
|
|
|
+ @handleRichTextBlur="parsedContentPinyin"
|
|
|
/>
|
|
|
<div v-if="data.property.fill_type === fillTypeList[1].value" class="select-vocabulary">
|
|
|
<h5 class="title">选词列表:</h5>
|
|
|
@@ -52,7 +52,7 @@
|
|
|
</template>
|
|
|
|
|
|
<div>
|
|
|
- <el-button @click="identifyText">识别</el-button>
|
|
|
+ <el-button @click="parsedContentPinyin">识别</el-button>
|
|
|
<el-button @click="openMultilingual">多语言</el-button>
|
|
|
</div>
|
|
|
|
|
|
@@ -78,21 +78,18 @@
|
|
|
icon="el-icon-refresh"
|
|
|
title="刷新"
|
|
|
class="refresh-pinyin-btn"
|
|
|
- @click.native="handleViewPinyin"
|
|
|
+ @click.native="parsedContentPinyin"
|
|
|
/>
|
|
|
</el-divider>
|
|
|
<template v-if="isEnable(data.property.view_pinyin)">
|
|
|
<div v-for="(item, i) in data.model_essay" :key="i" class="pinyin-text-list">
|
|
|
- <template v-for="(li, j) in item">
|
|
|
- <PinyinText
|
|
|
- :key="`${i}-${j}`"
|
|
|
- ref="PinyinText"
|
|
|
- :paragraph-list="li.paragraph_list"
|
|
|
- :rich-text-list="li.rich_text_list"
|
|
|
- :pinyin-position="data.property.pinyin_position"
|
|
|
- @fillCorrectPinyin="fillCorrectPinyin($event, i, j, 'model_essay')"
|
|
|
- />
|
|
|
- </template>
|
|
|
+ <PinyinText
|
|
|
+ :key="`pinyin-${i}`"
|
|
|
+ ref="PinyinText"
|
|
|
+ :rich-text-list="item.rich_text_list"
|
|
|
+ :pinyin-position="data.property.pinyin_position"
|
|
|
+ @fillCorrectPinyin="fillCorrectPinyin($event, i, -1, 'model_essay')"
|
|
|
+ />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -127,6 +124,7 @@ import { addTone, handleToneValue } from '@/views/book/courseware/data/common';
|
|
|
import { getRandomNumber } from '@/utils';
|
|
|
import { TextToAudioFile } from '@/api/app';
|
|
|
import { sanitizeHTML } from '@/utils/common';
|
|
|
+import { PinyinBuild_OldFormat } from '@/api/book';
|
|
|
|
|
|
export default {
|
|
|
name: 'FillPage',
|
|
|
@@ -143,6 +141,7 @@ export default {
|
|
|
fillTypeList,
|
|
|
sanitizeHTML,
|
|
|
visibleWord: false,
|
|
|
+ rich_text_list: [],
|
|
|
};
|
|
|
},
|
|
|
watch: {
|
|
|
@@ -150,12 +149,250 @@ export default {
|
|
|
'data.property.fill_font': 'handleMindMap',
|
|
|
'data.property': {
|
|
|
handler() {
|
|
|
- this.handleViewPinyin();
|
|
|
+ this.parsedContentPinyin();
|
|
|
},
|
|
|
deep: true,
|
|
|
},
|
|
|
},
|
|
|
methods: {
|
|
|
+ async parsedContentPinyin() {
|
|
|
+ if (!this.isEnable(this.data.property.view_pinyin)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ PinyinBuild_OldFormat({
|
|
|
+ text: this.data.content,
|
|
|
+ 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',
|
|
|
+ }).then(({ rich_text }) => {
|
|
|
+ if (rich_text) {
|
|
|
+ this.rich_text_list = rich_text.text_list;
|
|
|
+ this.data.model_essay = this.parseRichText();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ parseRichText() {
|
|
|
+ let text_list = this.rich_text_list || [];
|
|
|
+ this.data.answer.answer_list = [];
|
|
|
+ const arr = [];
|
|
|
+ let totalText = '';
|
|
|
+ let totalRichText = [];
|
|
|
+ for (let i = 0; i < text_list.length; i++) {
|
|
|
+ const textItem = text_list[i];
|
|
|
+ const text = textItem.text || '';
|
|
|
+ const isStyle = textItem.is_style === 'true';
|
|
|
+ if (isStyle) {
|
|
|
+ const isRichFill = /class=\s*(\\?["'])rich-fill\1/.test(text);
|
|
|
+ if (isRichFill) {
|
|
|
+ if (totalText.length > 0) {
|
|
|
+ arr.push({
|
|
|
+ content: totalText,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: totalRichText,
|
|
|
+ });
|
|
|
+ totalText = '';
|
|
|
+ totalRichText = [];
|
|
|
+ }
|
|
|
+ let nextData = text_list[i + 1] || {};
|
|
|
+ let nextTag = text_list[i + 2] || {};
|
|
|
+ const mark = getRandomNumber();
|
|
|
+ arr.push({
|
|
|
+ content: text + (nextData.text || '') + (nextTag.text || ''),
|
|
|
+ type: 'input',
|
|
|
+ input: '',
|
|
|
+ audio_answer_list: [],
|
|
|
+ mark,
|
|
|
+ rich_text_list: [textItem, nextData, nextTag],
|
|
|
+ });
|
|
|
+ this.data.answer.answer_list.push({
|
|
|
+ value: nextData.text || '',
|
|
|
+ mark,
|
|
|
+ type: 'only_one',
|
|
|
+ });
|
|
|
+ // 跳过下一个文本和标签数据
|
|
|
+ i += 2;
|
|
|
+ } else {
|
|
|
+ totalText += text;
|
|
|
+ totalRichText = totalRichText.concat(textItem);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const splitBlocks = this.splitTextItemByUnderline(textItem);
|
|
|
+
|
|
|
+ // 样式标签单独成块会导致 PinyinText 的样式栈断开,需并入紧随其后的文本块。
|
|
|
+ if (totalText.length > 0) {
|
|
|
+ if (splitBlocks.length > 0) {
|
|
|
+ splitBlocks[0].content = `${totalText}${splitBlocks[0].content || ''}`;
|
|
|
+ const inheritedOpenStyleTags = totalRichText.filter((tagItem) => {
|
|
|
+ const tagText = tagItem?.text || '';
|
|
|
+ const isStyleTag = tagItem?.is_style === 'true' || tagItem?.is_style === true;
|
|
|
+ if (!isStyleTag) return false;
|
|
|
+ if (/^<\//.test(tagText)) return false;
|
|
|
+ if (/^<br\s*\/?\s*>$/i.test(tagText)) return false;
|
|
|
+ return /^<\w+[^>]*>$/.test(tagText);
|
|
|
+ });
|
|
|
+
|
|
|
+ splitBlocks.forEach((block, blockIndex) => {
|
|
|
+ const prevRichTextList = block.rich_text_list || [];
|
|
|
+ block.rich_text_list = [
|
|
|
+ ...(blockIndex === 0 ? totalRichText : inheritedOpenStyleTags),
|
|
|
+ ...prevRichTextList,
|
|
|
+ ];
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ arr.push({
|
|
|
+ content: totalText,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: totalRichText,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ totalText = '';
|
|
|
+ totalRichText = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ arr.push(...splitBlocks);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (totalText.length > 0) {
|
|
|
+ arr.push({
|
|
|
+ content: totalText,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: totalRichText,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return arr;
|
|
|
+ },
|
|
|
+ splitTextItemByUnderline(textItem) {
|
|
|
+ const text = textItem?.text || '';
|
|
|
+ const matcher = /_{3,}/g;
|
|
|
+ const blocks = [];
|
|
|
+ let lastIndex = 0;
|
|
|
+ let match = matcher.exec(text);
|
|
|
+
|
|
|
+ while (match) {
|
|
|
+ const underlineText = match[0] || '';
|
|
|
+ const start = match.index;
|
|
|
+ const end = start + underlineText.length;
|
|
|
+
|
|
|
+ if (start > lastIndex) {
|
|
|
+ const textContent = text.slice(lastIndex, start);
|
|
|
+ blocks.push({
|
|
|
+ content: textContent,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: [
|
|
|
+ {
|
|
|
+ ...textItem,
|
|
|
+ text: textContent,
|
|
|
+ word_list: this.sliceWordListByTextRange(textItem.word_list, lastIndex, start),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const mark = getRandomNumber();
|
|
|
+ blocks.push({
|
|
|
+ content: underlineText,
|
|
|
+ type: 'input',
|
|
|
+ input: '',
|
|
|
+ audio_answer_list: [],
|
|
|
+ mark,
|
|
|
+ rich_text_list: [
|
|
|
+ {
|
|
|
+ ...textItem,
|
|
|
+ text: underlineText,
|
|
|
+ word_list: this.sliceWordListByTextRange(textItem.word_list, start, end),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ this.data.answer.answer_list.push({
|
|
|
+ value: '',
|
|
|
+ mark,
|
|
|
+ type: 'any_one',
|
|
|
+ });
|
|
|
+
|
|
|
+ lastIndex = end;
|
|
|
+ match = matcher.exec(text);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (lastIndex < text.length) {
|
|
|
+ const textContent = text.slice(lastIndex);
|
|
|
+ blocks.push({
|
|
|
+ content: textContent,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: [
|
|
|
+ {
|
|
|
+ ...textItem,
|
|
|
+ text: textContent,
|
|
|
+ word_list: this.sliceWordListByTextRange(textItem.word_list, lastIndex, text.length),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (blocks.length === 0) {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ content: text,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: [textItem],
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return blocks;
|
|
|
+ },
|
|
|
+ sliceWordListByTextRange(wordList = [], rangeStart = 0, rangeEnd = 0) {
|
|
|
+ if (!Array.isArray(wordList) || rangeEnd <= rangeStart) return [];
|
|
|
+
|
|
|
+ const result = [];
|
|
|
+ let cursor = 0;
|
|
|
+
|
|
|
+ wordList.forEach((wordItem) => {
|
|
|
+ const wordText = wordItem?.text || '';
|
|
|
+ const wordStart = cursor;
|
|
|
+ const wordEnd = wordStart + wordText.length;
|
|
|
+ cursor = wordEnd;
|
|
|
+
|
|
|
+ const overlapStart = Math.max(rangeStart, wordStart);
|
|
|
+ const overlapEnd = Math.min(rangeEnd, wordEnd);
|
|
|
+
|
|
|
+ if (overlapStart >= overlapEnd) return;
|
|
|
+
|
|
|
+ const relativeStart = overlapStart - wordStart;
|
|
|
+ const relativeEnd = overlapEnd - wordStart;
|
|
|
+
|
|
|
+ result.push(this.sliceWordItem(wordItem, relativeStart, relativeEnd));
|
|
|
+ });
|
|
|
+
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ sliceWordItem(wordItem, start, end) {
|
|
|
+ const fullText = wordItem?.text || '';
|
|
|
+ const slicedText = fullText.slice(start, end);
|
|
|
+ const slicedWord = {
|
|
|
+ ...wordItem,
|
|
|
+ text: slicedText,
|
|
|
+ };
|
|
|
+
|
|
|
+ const sourcePinyinList = Array.isArray(wordItem?.pinyin_list) ? wordItem.pinyin_list : [];
|
|
|
+
|
|
|
+ if (sourcePinyinList.length === fullText.length) {
|
|
|
+ slicedWord.pinyin_list = sourcePinyinList.slice(start, end);
|
|
|
+ slicedWord.pinyin = (slicedWord.pinyin_list || []).join(' ').trim();
|
|
|
+ return slicedWord;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (start === 0 && end === fullText.length) {
|
|
|
+ slicedWord.pinyin_list = [...sourcePinyinList];
|
|
|
+ return slicedWord;
|
|
|
+ }
|
|
|
+
|
|
|
+ slicedWord.pinyin_list = new Array(slicedText.length).fill('');
|
|
|
+ slicedWord.pinyin = '';
|
|
|
+ return slicedWord;
|
|
|
+ },
|
|
|
/**
|
|
|
* 识别文本中
|
|
|
* @param {Boolean} isUpdatePinyin 是否更新拼音,默认为 true
|
|
|
@@ -490,6 +727,8 @@ export default {
|
|
|
}
|
|
|
|
|
|
.pinyin-text-list {
|
|
|
+ display: inline;
|
|
|
+
|
|
|
:deep .pinyin-area {
|
|
|
display: inline;
|
|
|
}
|