|
@@ -1,12 +1,24 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div>
|
|
<div>
|
|
|
<template v-if="isTextMode">
|
|
<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>
|
|
|
|
|
|
|
|
<template v-if="isImageMode">
|
|
<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>
|
|
</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>
|
|
</template>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
@@ -27,12 +39,18 @@ export default {
|
|
|
default: "",
|
|
default: "",
|
|
|
},
|
|
},
|
|
|
type: [String, Number],
|
|
type: [String, Number],
|
|
|
- imgBase64: String
|
|
|
|
|
|
|
+ imgBase64: {
|
|
|
|
|
+ type: [String, Array],
|
|
|
|
|
+ default: ''
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
displayHtml: "",
|
|
displayHtml: "",
|
|
|
|
|
+ isFromXK: false,
|
|
|
|
|
+ imageLoading: false,
|
|
|
|
|
+ imageError: false,
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
|
|
|
|
@@ -43,6 +61,12 @@ export default {
|
|
|
isImageMode() {
|
|
isImageMode() {
|
|
|
return String(this.type) === "2";
|
|
return String(this.type) === "2";
|
|
|
},
|
|
},
|
|
|
|
|
+ imgSrc() {
|
|
|
|
|
+ return this.normalizeImg(this.imgBase64);
|
|
|
|
|
+ },
|
|
|
|
|
+ hasImage() {
|
|
|
|
|
+ return this.imgSrc !== '';
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
watch: {
|
|
watch: {
|
|
@@ -56,6 +80,15 @@ export default {
|
|
|
type() {
|
|
type() {
|
|
|
this.updateDisplay();
|
|
this.updateDisplay();
|
|
|
},
|
|
},
|
|
|
|
|
+
|
|
|
|
|
+ imgBase64: {
|
|
|
|
|
+ immediate: true,
|
|
|
|
|
+ handler() {
|
|
|
|
|
+ const src = this.normalizeImg(this.imgBase64);
|
|
|
|
|
+ this.imageLoading = !!src;
|
|
|
|
|
+ this.imageError = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
|
mounted() {
|
|
@@ -75,59 +108,79 @@ export default {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 如果没有编辑器公式,直接使用原始 HTML
|
|
|
|
|
if (!isWangEditorFormula(value)) {
|
|
if (!isWangEditorFormula(value)) {
|
|
|
|
|
+ this.isFromXK = false;
|
|
|
this.displayHtml = value;
|
|
this.displayHtml = value;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 将 HTML 放入临时容器,逐个替换公式 span 为 MathJax 渲染节点(优先使用 tex2svgPromise)
|
|
|
|
|
const container = document.createElement('div');
|
|
const container = document.createElement('div');
|
|
|
container.innerHTML = value;
|
|
container.innerHTML = value;
|
|
|
const spans = Array.from(container.querySelectorAll(FORMULA_SELECTOR));
|
|
const spans = Array.from(container.querySelectorAll(FORMULA_SELECTOR));
|
|
|
|
|
|
|
|
if (spans.length === 0) {
|
|
if (spans.length === 0) {
|
|
|
|
|
+ this.isFromXK = false;
|
|
|
this.displayHtml = value;
|
|
this.displayHtml = value;
|
|
|
return;
|
|
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>
|
|
</script>
|
|
|
|
|
|
|
@@ -150,4 +203,54 @@ export default {
|
|
|
.mjx-msqrt>.mjx-surd {
|
|
.mjx-msqrt>.mjx-surd {
|
|
|
vertical-align: -0.12em !important;
|
|
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>
|
|
</style>
|