|
|
@@ -88,6 +88,7 @@
|
|
|
ref="PinyinText"
|
|
|
:rich-text-list="item.rich_text_list"
|
|
|
:pinyin-position="data.property.pinyin_position"
|
|
|
+ :body-styles="getBodyStyles()"
|
|
|
@fillCorrectPinyin="fillCorrectPinyin($event, i, -1, 'model_essay')"
|
|
|
/>
|
|
|
</div>
|
|
|
@@ -156,9 +157,6 @@ export default {
|
|
|
},
|
|
|
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:
|
|
|
@@ -174,14 +172,19 @@ export default {
|
|
|
},
|
|
|
parseRichText() {
|
|
|
let text_list = this.rich_text_list || [];
|
|
|
+ const preservedAnyOneAnswers = (this.data.answer.answer_list || [])
|
|
|
+ .filter(({ type }) => type === 'any_one')
|
|
|
+ .map(({ value }) => value);
|
|
|
+ const preservedAnyOneState = { index: 0 };
|
|
|
this.data.answer.answer_list = [];
|
|
|
const arr = [];
|
|
|
let totalText = '';
|
|
|
let totalRichText = [];
|
|
|
+ const openStyleTagStack = [];
|
|
|
for (let i = 0; i < text_list.length; i++) {
|
|
|
const textItem = text_list[i];
|
|
|
const text = textItem.text || '';
|
|
|
- const isStyle = textItem.is_style === 'true';
|
|
|
+ const isStyle = textItem.is_style === 'true' || textItem.is_style === true;
|
|
|
if (isStyle) {
|
|
|
const isRichFill = /class=\s*(\\?["'])rich-fill\1/.test(text);
|
|
|
if (isRichFill) {
|
|
|
@@ -215,37 +218,39 @@ export default {
|
|
|
} else {
|
|
|
totalText += text;
|
|
|
totalRichText = totalRichText.concat(textItem);
|
|
|
+ this.syncOpenStyleTagStack(openStyleTagStack, textItem);
|
|
|
}
|
|
|
} else {
|
|
|
- const splitBlocks = this.splitTextItemByUnderline(textItem);
|
|
|
+ const splitBlocks = this.splitTextItemByUnderline(textItem, preservedAnyOneAnswers, preservedAnyOneState);
|
|
|
+
|
|
|
+ const currentOpenStyleTags = openStyleTagStack.map((tagItem) => ({ ...tagItem }));
|
|
|
+ const firstBlockPrefix =
|
|
|
+ totalText.length > 0
|
|
|
+ ? this.buildFirstBlockStylePrefix(totalRichText, currentOpenStyleTags)
|
|
|
+ : currentOpenStyleTags;
|
|
|
|
|
|
// 样式标签单独成块会导致 PinyinText 的样式栈断开,需并入紧随其后的文本块。
|
|
|
- if (totalText.length > 0) {
|
|
|
- if (splitBlocks.length > 0) {
|
|
|
+ if (splitBlocks.length > 0) {
|
|
|
+ if (totalText.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,
|
|
|
- });
|
|
|
}
|
|
|
+
|
|
|
+ splitBlocks.forEach((block, blockIndex) => {
|
|
|
+ const prevRichTextList = block.rich_text_list || [];
|
|
|
+ block.rich_text_list = [
|
|
|
+ ...(blockIndex === 0 ? firstBlockPrefix : currentOpenStyleTags),
|
|
|
+ ...prevRichTextList,
|
|
|
+ ];
|
|
|
+ });
|
|
|
+
|
|
|
+ totalText = '';
|
|
|
+ totalRichText = [];
|
|
|
+ } else if (totalText.length > 0) {
|
|
|
+ arr.push({
|
|
|
+ content: totalText,
|
|
|
+ type: 'text',
|
|
|
+ rich_text_list: firstBlockPrefix,
|
|
|
+ });
|
|
|
totalText = '';
|
|
|
totalRichText = [];
|
|
|
}
|
|
|
@@ -264,7 +269,66 @@ export default {
|
|
|
|
|
|
return arr;
|
|
|
},
|
|
|
- splitTextItemByUnderline(textItem) {
|
|
|
+ getStyleTagName(tagText = '') {
|
|
|
+ const trimmedText = String(tagText).trim();
|
|
|
+ const closeMatch = trimmedText.match(/^<\/(\w+)>$/);
|
|
|
+ if (closeMatch) return closeMatch[1].toLowerCase();
|
|
|
+
|
|
|
+ const openMatch = trimmedText.match(/^<(\w+)([^>]*)>$/);
|
|
|
+ if (openMatch) return openMatch[1].toLowerCase();
|
|
|
+
|
|
|
+ return '';
|
|
|
+ },
|
|
|
+ isOpenStyleTag(tagItem = {}) {
|
|
|
+ const tagText = String(tagItem?.text || '').trim();
|
|
|
+ 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);
|
|
|
+ },
|
|
|
+ syncOpenStyleTagStack(openStyleTagStack, styleTagItem) {
|
|
|
+ const tagText = String(styleTagItem?.text || '').trim();
|
|
|
+ const isStyleTag = styleTagItem?.is_style === 'true' || styleTagItem?.is_style === true;
|
|
|
+ if (!isStyleTag) return;
|
|
|
+ if (/^<br\s*\/?\s*>$/i.test(tagText)) return;
|
|
|
+
|
|
|
+ const closeMatch = tagText.match(/^<\/(\w+)>$/);
|
|
|
+ if (closeMatch) {
|
|
|
+ const closeTagName = closeMatch[1].toLowerCase();
|
|
|
+ for (let i = openStyleTagStack.length - 1; i >= 0; i--) {
|
|
|
+ const tagName = this.getStyleTagName(openStyleTagStack[i]?.text);
|
|
|
+ if (tagName === closeTagName) {
|
|
|
+ openStyleTagStack.splice(i, 1);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.isOpenStyleTag(styleTagItem)) {
|
|
|
+ openStyleTagStack.push(styleTagItem);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ buildFirstBlockStylePrefix(transitionStyleTags = [], currentOpenStyleTags = []) {
|
|
|
+ const transitionOpenTagNames = transitionStyleTags
|
|
|
+ .filter((tagItem) => this.isOpenStyleTag(tagItem))
|
|
|
+ .map((tagItem) => this.getStyleTagName(tagItem?.text));
|
|
|
+
|
|
|
+ const missingOpenTags = currentOpenStyleTags.filter((tagItem) => {
|
|
|
+ const tagName = this.getStyleTagName(tagItem?.text);
|
|
|
+ return tagName && !transitionOpenTagNames.includes(tagName);
|
|
|
+ });
|
|
|
+
|
|
|
+ return [...missingOpenTags, ...transitionStyleTags];
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 根据文本中的连续下划线分割文本块,并将下划线部分转换为输入块
|
|
|
+ * @param {Object} textItem 富文本中的一个文本项
|
|
|
+ * @param {Array} preservedAnyOneAnswers 预先保存的 any_one 类型答案列表
|
|
|
+ * @param {Object} preservedAnyOneState any_one 答案的当前索引状态
|
|
|
+ */
|
|
|
+ splitTextItemByUnderline(textItem, preservedAnyOneAnswers = [], preservedAnyOneState = { index: 0 }) {
|
|
|
const text = textItem?.text || '';
|
|
|
const matcher = /_{3,}/g;
|
|
|
const blocks = [];
|
|
|
@@ -307,10 +371,11 @@ export default {
|
|
|
],
|
|
|
});
|
|
|
this.data.answer.answer_list.push({
|
|
|
- value: '',
|
|
|
+ value: preservedAnyOneAnswers[preservedAnyOneState.index] || '',
|
|
|
mark,
|
|
|
type: 'any_one',
|
|
|
});
|
|
|
+ preservedAnyOneState.index += 1;
|
|
|
|
|
|
lastIndex = end;
|
|
|
match = matcher.exec(text);
|
|
|
@@ -612,6 +677,10 @@ export default {
|
|
|
removeWord(index) {
|
|
|
this.data.word_list.splice(index, 1);
|
|
|
},
|
|
|
+ getBodyStyles() {
|
|
|
+ if (!this.$refs.richText) return {};
|
|
|
+ return this.$refs.richText.getBodyInitialStyles();
|
|
|
+ },
|
|
|
},
|
|
|
};
|
|
|
</script>
|