Explorar o código

富文本组件细节

zq hai 1 semana
pai
achega
c7774d6325
Modificáronse 2 ficheiros con 122 adicións e 51 borrados
  1. 0 6
      src/components/CommonPreview.vue
  2. 122 45
      src/components/PinyinText.vue

+ 0 - 6
src/components/CommonPreview.vue

@@ -1187,12 +1187,6 @@ export default {
         const parser = new DOMParser();
         const doc = parser.parseFromString(html, 'text/html'); // 解析HTML字符串,返回Document对象
 
-        // 如果解析后只有一个 img 标签,直接返回原 HTML
-        const bodyChildren = doc.body.children;
-        if (bodyChildren.length === 1 && bodyChildren[0].tagName === 'IMG') {
-          return html;
-        }
-
         // 跳过 script 和 style 标签
         const skipTags = new Set(['SCRIPT', 'STYLE']);
 

+ 122 - 45
src/components/PinyinText.vue

@@ -1,31 +1,57 @@
 <template>
   <div class="pinyin-area" :style="{ 'text-align': pinyinOverallPosition, padding: pinyinPadding }">
-    <AudioPlay v-if="isEnable(isEnableVoice)" :file-id="audioFileId" :theme-color="unifiedAttrib && unifiedAttrib.topic_color ? unifiedAttrib.topic_color : ''
-      " />
+    <AudioPlay
+      v-if="isEnable(isEnableVoice)"
+      :file-id="audioFileId"
+      :theme-color="unifiedAttrib && unifiedAttrib.topic_color ? unifiedAttrib.topic_color : ''"
+    />
 
     <!-- 新规则:使用 richTextList -->
     <div v-if="richTextList && richTextList.length > 0" class="rich-text-container">
       <template v-for="(block, index) in parsedBlocks">
         <!-- 文字块:包含 word_list -->
-        <span v-if="block.type === 'text'" :key="'text-' + index" class="pinyin-sentence"
-          :style="[{ 'align-items': pinyinPosition === 'top' ? 'flex-end' : 'flex-start' }, block.styleObj]">
+        <span
+          v-if="block.type === 'text'"
+          :key="'text-' + index"
+          class="pinyin-sentence"
+          :style="[{ 'align-items': pinyinPosition === 'top' ? 'flex-end' : 'flex-start' }, block.styleObj]"
+        >
           <span v-for="(word, wIndex) in block.word_list" :key="wIndex" class="pinyin-text">
-            <span :class="{ active: visible && word_index == wIndex && sentence_index === block.index }"
-              :title="isPreview ? '' : '点击校对'" :style="{
+            <span
+              :class="{ active: visible && word_index == wIndex && sentence_index === block.index }"
+              :title="isPreview ? '' : '点击校对'"
+              :style="{
                 cursor: isPreview ? '' : 'pointer',
                 'align-items': wIndex == 0 ? 'flex-start' : 'center',
-              }" @click="correctPinyin(word, block.paragraphIndex, block.oldIndex, wIndex)">
-              <span v-if="pinyinPosition === 'top' && hasPinyinInParagraphNeVersion(block.paragraphIndex)"
-                class="pinyin">
-                {{ getPinyinText(word) }}</span>
+              }"
+              @click="correctPinyin(word, block.paragraphIndex, block.oldIndex, wIndex)"
+            >
+              <span
+                v-if="pinyinPosition === 'top' && hasPinyinInParagraphNeVersion(block.paragraphIndex)"
+                class="pinyin"
+              >
+                {{ getPinyinText(word) }}</span
+              >
               <span class="py-char" :style="textStyle(word)">{{ convertText(word.text) }}</span>
-              <span v-if="pinyinPosition !== 'top' && hasPinyinInParagraphNeVersion(block.paragraphIndex)"
-                class="pinyin">
-                {{ getPinyinText(word) }}</span>
+              <span
+                v-if="pinyinPosition !== 'top' && hasPinyinInParagraphNeVersion(block.paragraphIndex)"
+                class="pinyin"
+              >
+                {{ getPinyinText(word) }}</span
+              >
             </span>
           </span>
         </span>
-
+        <!-- 图片块 -->
+        <img
+          v-else-if="block.type === 'image'"
+          :key="'image-' + index"
+          :src="block.src"
+          :alt="block.alt"
+          :width="block.width"
+          :height="block.height"
+          class="inline-image"
+        />
         <!-- 换行符 -->
         <br v-else-if="block.type === 'newline'" :key="'newline-' + index" />
       </template>
@@ -34,33 +60,63 @@
     <!-- 老规则:使用 paragraphList -->
     <template v-else-if="paragraphList && paragraphList.length > 0">
       <div v-for="(paragraph, pIndex) in paragraphList" :key="pIndex" class="pinyin-paragraph">
-        <div v-for="(sentence, sIndex) in paragraph" :key="sIndex" class="pinyin-sentence"
-          :style="{ 'align-items': pinyinPosition === 'top' ? 'flex-end' : 'flex-start' }">
+        <div
+          v-for="(sentence, sIndex) in paragraph"
+          :key="sIndex"
+          class="pinyin-sentence"
+          :style="{ 'align-items': pinyinPosition === 'top' ? 'flex-end' : 'flex-start' }"
+        >
           <span v-for="(word, wIndex) in sentence" :key="wIndex" class="pinyin-text">
-            <span :class="{
-              active: visible && word_index == wIndex && sentence_index === sIndex && paragraph_index === pIndex,
-            }" :title="isPreview ? '' : '点击校对'" :style="{
-              cursor: isPreview ? '' : 'pointer',
-              'align-items': wIndex == 0 ? 'flex-start' : 'center',
-            }" @click="correctPinyin(word, pIndex, sIndex, wIndex)">
-              <span v-if="pinyinPosition === 'top' && hasPinyinInParagraph(sIndex)" class="pinyin"
-                :style="{ 'font-size': pinyinSize }">
-                {{ getPinyinText(word) }}</span>
+            <span
+              :class="{
+                active: visible && word_index == wIndex && sentence_index === sIndex && paragraph_index === pIndex,
+              }"
+              :title="isPreview ? '' : '点击校对'"
+              :style="{
+                cursor: isPreview ? '' : 'pointer',
+                'align-items': wIndex == 0 ? 'flex-start' : 'center',
+              }"
+              @click="correctPinyin(word, pIndex, sIndex, wIndex)"
+            >
+              <span
+                v-if="pinyinPosition === 'top' && hasPinyinInParagraph(sIndex)"
+                class="pinyin"
+                :style="{ 'font-size': pinyinSize }"
+              >
+                {{ getPinyinText(word) }}</span
+              >
               <span class="py-char" :style="textStyle(word)">{{ convertText(word.text) }}</span>
-              <span v-if="pinyinPosition !== 'top' && hasPinyinInParagraph(sIndex)" class="pinyin"
-                :style="{ 'font-size': pinyinSize }">
-                {{ getPinyinText(word) }}</span>
+              <span
+                v-if="pinyinPosition !== 'top' && hasPinyinInParagraph(sIndex)"
+                class="pinyin"
+                :style="{ 'font-size': pinyinSize }"
+              >
+                {{ getPinyinText(word) }}</span
+              >
             </span>
           </span>
         </div>
       </div>
     </template>
 
-    <CorrectPinyin :visible.sync="visible" :new-version="newVersion" :select-content="selectContent"
-      :component-type="componentType" @fillTonePinyin="fillTonePinyin" />
-
-    <el-dialog ref="optimizedDialog" title="" :visible.sync="noteDialogVisible" width="680px" :style="dialogStyle"
-      :close-on-click-modal="false" destroy-on-close @close="noteDialogVisible = false">
+    <CorrectPinyin
+      :visible.sync="visible"
+      :new-version="newVersion"
+      :select-content="selectContent"
+      :component-type="componentType"
+      @fillTonePinyin="fillTonePinyin"
+    />
+
+    <el-dialog
+      ref="optimizedDialog"
+      title=""
+      :visible.sync="noteDialogVisible"
+      width="680px"
+      :style="dialogStyle"
+      :close-on-click-modal="false"
+      destroy-on-close
+      @close="noteDialogVisible = false"
+    >
       <span v-html="sanitizeHTML(note)"></span>
     </el-dialog>
   </div>
@@ -76,13 +132,13 @@ export default {
   name: 'PinyinText',
   components: {
     CorrectPinyin,
-    AudioPlay
+    AudioPlay,
   },
   inject: ['convertText'],
   props: {
-    isEnableVoice:{
+    isEnableVoice: {
       type: String,
-      default: 'false'
+      default: 'false',
     },
     audioFileId: {
       type: String,
@@ -175,19 +231,32 @@ export default {
       for (const item of this.richTextList) {
         oldIndex += 1;
 
-        if (item.is_style === 'true' || item.is_style === true) {
+        if (item.text && typeof item.text === 'string' && item.text.includes('<img')) {
+          // 处理图片
+          const imgMatch = item.text.match(/<img\s+([^>]*)\/?\s*>/i);
+          if (imgMatch) {
+            const attrs = imgMatch[1];
+            const srcMatch = attrs.match(/src=["']([^"']*)["']/i);
+            const altMatch = attrs.match(/alt=["']([^"']*)["']/i);
+            const widthMatch = attrs.match(/width=["']?(\d+)["']?/i);
+            const heightMatch = attrs.match(/height=["']?(\d+)["']?/i);
+
+            blocks.push({
+              type: 'image',
+              src: srcMatch ? srcMatch[1] : '',
+              alt: altMatch ? altMatch[1] : '',
+              width: widthMatch ? widthMatch[1] : null,
+              height: heightMatch ? heightMatch[1] : null,
+            });
+          }
+        } else if (item.is_style === 'true' || item.is_style === true) {
           const tagText = item.text || '';
           // 正则匹配闭标签:匹配 </tagname> 格式
           const closeTagMatch = tagText.match(/^<\/(\w+)>$/);
           if (closeTagMatch) {
             // 闭标签:从栈顶开始查找最近的同名标签并移除(LIFO 原则)
             const tagName = closeTagMatch[1];
-            for (let i = tagStack.length - 1; i >= 0; i--) {
-              if (tagStack[i].tag === tagName) {
-                tagStack.splice(i, 1);
-                break;
-              }
-            }
+            this.removeTagFromStack(tagStack, tagName);
           } else {
             // 开标签:解析标签名和样式并压栈
             const openTagMatch = tagText.match(/^<(\w+)([^>]*)>$/);
@@ -276,6 +345,14 @@ export default {
   },
 
   methods: {
+    removeTagFromStack(tagStack, tagName) {
+      for (let i = tagStack.length - 1; i >= 0; i--) {
+        if (tagStack[i].tag === tagName) {
+          tagStack.splice(i, 1);
+          break;
+        }
+      }
+    },
     textStyle(item) {
       const styles = { ...item.activeTextStyle };
       styles['font-size'] = styles.fontSize;
@@ -420,7 +497,7 @@ export default {
     text-wrap: pretty;
     hanging-punctuation: allow-end;
 
-    >span {
+    > span {
       display: inline-flex;
       flex-direction: column;
       align-items: center;
@@ -451,7 +528,7 @@ export default {
 </style>
 
 <style lang="scss">
-.pinyin-area+.pinyin-area {
+.pinyin-area + .pinyin-area {
   margin-top: 4px;
 }
 </style>