wangguoxi 2 mesi fa
parent
commit
7c7558da2c

+ 1 - 1
src/App.vue

@@ -18,7 +18,7 @@ export default {
     },
     methods: {
         SubmitLogin() {
-            const username = '500072347220708';
+            const username = '5000715069857';
             const password = '123456';
             // const type = '1';
             const schoolType = sessionStorage.getItem('schoolType');

+ 139 - 36
src/views/analysisReport/wrongQuestion/Preview.vue

@@ -1,12 +1,24 @@
 <template>
     <div>
         <template v-if="isTextMode">
-            <div style="color: #000 !important;" ref="previewRoot" v-html="displayHtml"></div>
+            <template v-if="isFromXK">
+                <div class="text-preview" style="color: #000 !important;" ref="previewRoot" v-html="displayHtml"></div>
+            </template>
+            <template v-else>
+                <div style="color: #000 !important;" v-html="displayHtml"></div>
+            </template>
         </template>
 
         <template v-if="isImageMode">
-            <el-image class="question_img" :src="imgBase64" :preview-src-list="[imgBase64]">
+            <el-image v-if="hasImage && !imageError" class="question_img" :src="imgSrc" :preview-src-list="[imgSrc]"
+                @load="onImageLoad" @error="onImageError">
             </el-image>
+
+            <div v-else-if="imageLoading && !imageError" class="image-loading">正在加载图片...</div>
+
+            <div v-else-if="imageError" class="image-error">图片加载失败</div>
+
+            <div v-else class="image-empty">暂无图片</div>
         </template>
     </div>
 </template>
@@ -27,12 +39,18 @@ export default {
             default: "",
         },
         type: [String, Number],
-        imgBase64: String
+        imgBase64: {
+            type: [String, Array],
+            default: ''
+        }
     },
 
     data() {
         return {
             displayHtml: "",
+            isFromXK: false,
+            imageLoading: false,
+            imageError: false,
         };
     },
 
@@ -43,6 +61,12 @@ export default {
         isImageMode() {
             return String(this.type) === "2";
         },
+        imgSrc() {
+            return this.normalizeImg(this.imgBase64);
+        },
+        hasImage() {
+            return this.imgSrc !== '';
+        },
     },
 
     watch: {
@@ -56,6 +80,15 @@ export default {
         type() {
             this.updateDisplay();
         },
+
+        imgBase64: {
+            immediate: true,
+            handler() {
+                const src = this.normalizeImg(this.imgBase64);
+                this.imageLoading = !!src;
+                this.imageError = false;
+            }
+        }
     },
 
     mounted() {
@@ -75,59 +108,79 @@ export default {
                 return;
             }
 
-            // 如果没有编辑器公式,直接使用原始 HTML
             if (!isWangEditorFormula(value)) {
+                this.isFromXK = false;
                 this.displayHtml = value;
                 return;
             }
 
-            // 将 HTML 放入临时容器,逐个替换公式 span 为 MathJax 渲染节点(优先使用 tex2svgPromise)
             const container = document.createElement('div');
             container.innerHTML = value;
             const spans = Array.from(container.querySelectorAll(FORMULA_SELECTOR));
 
             if (spans.length === 0) {
+                this.isFromXK = false;
                 this.displayHtml = value;
                 return;
             }
 
-            // 如果 MathJax 支持 tex2svgPromise,使用它逐个替换为渲染后的节点(避免 typeset 的双重渲染)
-            if (window.MathJax && window.MathJax.tex2svgPromise) {
-                for (const span of spans) {
-                    const latex = span.getAttribute('data-value') || '';
-                    try {
-                        const node = await window.MathJax.tex2svgPromise(latex, { display: false });
-                        // tex2svgPromise 返回一个包含 mjx-container 的节点,使用其 outerHTML 替换原始 span
-                        span.replaceWith(node);
-                    } catch (e) {
-                        console.warn('tex2svgPromise 渲染失败,保留原始公式标记', e);
+            if (window.MathJax) {
+                if (window.MathJax.tex2svgPromise) {
+                    for (const span of spans) {
+                        const latex = span.getAttribute('data-value') || '';
+                        try {
+                            const node = await window.MathJax.tex2svgPromise(latex, { display: false });
+                            span.replaceWith(node);
+                        } catch (e) {
+                            console.warn('tex2svgPromise 渲染失败,保留原始公式标记', e);
+                            span.textContent = `\\(${span.getAttribute('data-value')}\\)`;
+                        }
                     }
-                }
+                    this.displayHtml = container.innerHTML;
+                } else {
+                    for (const span of spans) {
+                        const latex = span.getAttribute('data-value') || '';
+                        const textNode = document.createTextNode(`\\(${latex}\\)`);
+                        span.replaceWith(textNode);
+                    }
+                    this.isFromXK = true;
+                    this.displayHtml = container.innerHTML;
 
-                this.displayHtml = container.innerHTML;
-                this.$nextTick(() => {
-                    // 确保 MathJax 渲染节点的样式生效
-                    this.$emit('rendered');
-                });
-                return;
+                    this.$nextTick(async () => {
+                        try {
+                            if (window.MathJax.typesetPromise) {
+                                await window.MathJax.typesetPromise([this.$refs.previewRoot]);
+                            }
+                        } catch (error) {
+                            console.error('MathJax typeset 失败', error);
+                        }
+                    });
+                }
+            } else {
+                this.displayHtml = value;
             }
 
-            // 回退:没有 tex2svgPromise,则保留原始替换为 LaTeX 标记并调用 typeset
-            this.displayHtml = value.replace(/<span[^>]*data-w-e-type="formula"[^>]*data-value="([^"]+)"[^>]*><\/span>/g, (_, latex) => `\\(${latex}\\)`);
-
-            this.$nextTick(async () => {
-                try {
-                    if (window.MathJax && window.MathJax.typesetPromise) {
-                        await window.MathJax.typesetPromise([this.$refs.previewRoot]);
-                    }
-                } catch (error) {
-                    console.error('MathJax 加载失败', error);
-                } finally {
-                    this.$emit('rendered');
-                }
+            this.$nextTick(() => {
+                this.$emit('rendered');
             });
         },
-    },
+
+        normalizeImg(src) {
+            if (Array.isArray(src)) return src[0] || '';
+            if (typeof src === 'string') return src.trim();
+            return '';
+        },
+
+        onImageLoad() {
+            this.imageLoading = false;
+            this.imageError = false;
+        },
+
+        onImageError() {
+            this.imageLoading = false;
+            this.imageError = true;
+        }
+    }
 };
 </script>
 
@@ -150,4 +203,54 @@ export default {
 .mjx-msqrt>.mjx-surd {
     vertical-align: -0.12em !important;
 }
+
+.el-image {
+    max-width: 720px;
+}
+</style>
+
+<style lang="scss">
+.text-preview {
+    img {
+        vertical-align: middle !important;
+    }
+
+    table {
+        width: 100%;
+        border-collapse: collapse;
+        border: 1px solid #ebeef5;
+        text-align: center;
+    }
+
+    table td,
+    table th {
+        padding: 0 10px;
+        border: 1px solid #ebeef5;
+        color: #666;
+        height: 30px;
+    }
+
+    // 第一行作为默认头部
+    // table tbody tr:first-child {
+    //     background-color: #F5F7FA;
+    //     font-weight: bold;
+    // }
+
+    table thead th {
+        background-color: #F5F7FA;
+        width: 100px;
+    }
+
+    table tr:nth-child(odd) {
+        background: #F9F9F9;
+    }
+
+    table tr:nth-child(even) {
+        background: #fff;
+    }
+}
+
+.question_img {
+    max-width: 720px;
+}
 </style>

+ 35 - 14
src/views/analysisReport/wrongQuestion/index.vue

@@ -27,7 +27,9 @@
                                 <div class="left">
                                     <span class="number mr_10">{{ countGlobalIndex(index + 1) }}</span>
                                     <span class="gray">试题类型:</span>
-                                    <span class="black mr_10">{{ question.questionType }}</span>
+                                    <span class="black mr_10" style="font-weight: 500;">
+                                        {{ question.questionType }}
+                                    </span>
                                     <span class="tag" v-if="question.classScoreRate < 40">高频错题</span>
                                 </div>
                             </div>
@@ -120,7 +122,7 @@
                                 <div class="flex">
                                     <div class="flex_left">答&nbsp;&nbsp;&nbsp;&nbsp;案:</div>
                                     <div class="flex_right">
-                                        <Preview :type="question.sourceType" :content="question.questionData.answer"
+                                        <Preview :type="question.sourceType" :content="question.questionData?.answer"
                                             :imgBase64="question.answerImg" />
                                     </div>
                                 </div>
@@ -128,7 +130,7 @@
                                 <div class="flex">
                                     <div class="flex_left">解&nbsp;&nbsp;&nbsp;&nbsp;析:</div>
                                     <div class="flex_right">
-                                        <Preview :type="question.sourceType" :content="question.questionData.analysis"
+                                        <Preview :type="question.sourceType" :content="question.questionData?.analysis"
                                             :imgBase64="question.parseImg" />
                                     </div>
                                 </div>
@@ -298,7 +300,6 @@ export default {
 
         countLevel(num) {
             // 1 :拔高题  2:进阶题  3:巩固题
-            console.log('num', num);
             const arr = ['拔高题', '进阶题', '巩固题']
             const color = ['bg_hard', 'bg_medium', 'bg_easy',]
             return {
@@ -335,19 +336,39 @@ export default {
                 this.uploadPaperUrls = uploadPaperUrls;
                 this.pageParam.total = res.data.total * 1;
 
-                for (let i = 0; i < questionList.length; i++) {
-                    const { sourceType, titleCoordinates, answerCoordinates, parseCoordinates, paintingPosition } = questionList[i]
-                    questionList[i].answerShow = false;
-                    questionList[i].parseShow = false;
+                // 并行合并图片:先为每道题准备 promise,然后使用 Promise.all 并发执行
+                const processedQuestions = await Promise.all(questionList.map(async (q) => {
+                    q.answerShow = false;
+                    q.parseShow = false;
+                    const { sourceType, titleCoordinates, answerCoordinates, parseCoordinates, paintingPosition } = q;
+
                     if (sourceType === 2) {
-                        questionList[i].questionImg = await mergeImage(this.uploadPaperUrls, titleCoordinates);;
-                        questionList[i].answerImg = await mergeImage(this.answerUrls, answerCoordinates);
-                        questionList[i].parseImg = await mergeImage(this.answerUrls, parseCoordinates);
-                        questionList[i].studentAnswerImg = await mergeImage(this.answerUrls, paintingPosition);
+                        try {
+                            const [questionImg, answerImg, parseImg, studentAnswerImg] = await Promise.all([
+                                mergeImage(this.uploadPaperUrls, titleCoordinates),
+                                mergeImage(this.answerUrls, answerCoordinates),
+                                mergeImage(this.answerUrls, parseCoordinates),
+                                mergeImage(this.answerUrls, paintingPosition),
+                            ]);
+
+                            q.questionImg = questionImg || '';
+                            q.answerImg = answerImg || '';
+                            q.parseImg = parseImg || '';
+                            q.studentAnswerImg = studentAnswerImg || '';
+                        } catch (e) {
+                            // 如果合并过程中某张图失败,记录空字符串并继续,避免阻塞全部渲染
+                            q.questionImg = q.questionImg || '';
+                            q.answerImg = q.answerImg || '';
+                            q.parseImg = q.parseImg || '';
+                            q.studentAnswerImg = q.studentAnswerImg || '';
+                            console.error('mergeImage error', e);
+                        }
                     }
-                }
 
-                this.questionList = questionList;
+                    return q;
+                }));
+
+                this.questionList = processedQuestions;
 
                 // 缓慢滚动到顶部
                 this.scrollToTop();

+ 3 - 3
src/views/analysisReport/wrongQuestion/loadImg.js

@@ -1,10 +1,10 @@
 export async function mergeImage(urlList, CoordinateList) {
     if (!Array.isArray(urlList) || !Array.isArray(CoordinateList)) {
-        return [];
+        return '';
     }
 
     if (CoordinateList.length === 0 || urlList.length === 0) {
-        return [];
+        return '';
     }
 
     const imageUrlList = []
@@ -55,7 +55,7 @@ export function MergerImage(imageUrlList) {
         try {
             // 确保是数组并过滤出有效字符串
             const inputs = imageUrlList.filter(item => item);
-            if (inputs.length === 0) return resolve(null);
+            if (inputs.length === 0) return resolve('');
 
             // 使用 loadImage 加载并直接用返回的宽高创建 canvas(避免再去读取图片尺寸)
             const canvases = [];