diff --git a/src/components/Loading/index.vue b/src/components/Loading/index.vue
index a363812..997f32e 100644
--- a/src/components/Loading/index.vue
+++ b/src/components/Loading/index.vue
@@ -1,18 +1,45 @@
-
-
-
-
- {{ text }}
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ text || '暂无数据' }}
+ {{ subText }}
+
@@ -20,19 +47,25 @@
diff --git a/src/pages/doWork/components/Question.vue b/src/pages/doWork/components/Question.vue
index 7767a89..a188a62 100644
--- a/src/pages/doWork/components/Question.vue
+++ b/src/pages/doWork/components/Question.vue
@@ -290,8 +290,8 @@ const filterTitleWithoutImages = (title?: string, cidx?: number | string) => {
t = t.replace(/http:\/\//gi, 'https://');
// 先修复数学符号(< > 转义、AUB→∪),避免被 HTML 解析截断
t = fixMathSymbolsInHtml(t);
- // 处理公式
- t = processFormula(t);
+ // 处理公式(传入失败 URL 集合,触发响应式依赖以便失败后自动重渲染)
+ t = processFormula(t, failedFormulaUrls.value);
if (cidx !== undefined) {
t = `(${Number(cidx) + 1})` + t;
@@ -320,6 +320,10 @@ const handleRichTextClick = (e: any) => {
const submitAnswer = ref('');
const submitAnswerPic = ref('');
+// ========== 公式图片预加载 + 静默重试 ==========
+// 加载永久失败的公式图片 URL 集合(重试耗尽后加入,触发模板重新渲染用文本兜底)
+const failedFormulaUrls = ref>(new Set());
+
// 监听题目变化,初始化答案
watch(
() => props.data?.id,
@@ -620,7 +624,8 @@ const simpleFormulaToHtml = (formula: string): string => {
};
// 处理数学公式:使用在线 LaTeX 渲染服务
-const processFormula = (html: string): string => {
+// failedUrls: 可选,加载永久失败的 URL 集合;命中时用文本兜底而非
+const processFormula = (html: string, failedUrls?: Set): string => {
if (html == null || typeof html !== 'string') return '';
if (!html) return '';
@@ -648,10 +653,130 @@ const processFormula = (html: string): string => {
// 复杂公式使用 LaTeX 图片渲染
const imageUrl = latexToImageUrl(formula);
+
+ // 如果该图片经过多次重试仍失败,使用 LaTeX 文本兜底
+ if (failedUrls?.has(imageUrl)) {
+ const display = formula.length > 60 ? formula.substring(0, 60) + '…' : formula;
+ return `[${display}]`;
+ }
+
return `
`;
});
};
+// ---------- 公式图片预加载工具函数 ----------
+
+// 从原始 HTML 中提取所有复杂公式对应的图片 URL(逻辑与 processFormula 一致)
+const extractFormulaImageUrls = (html: string): string[] => {
+ if (html == null || typeof html !== 'string') return [];
+ const formulaRegex = /"']+|"[^"]*"|'[^']*')*)data-w-e-type=["']formula["']((?:[^<>"']+|"[^"]*"|'[^']*')*)>[\s\S]*?<\/span>/gi;
+ const urls: string[] = [];
+ let match;
+ while ((match = formulaRegex.exec(html)) !== null) {
+ const valueMatch = match[0].match(/data-value=["']([^"']+)["']/);
+ if (!valueMatch) continue;
+ let formula = valueMatch[1]
+ .replace(/\\\\/g, '\\')
+ .replace(/\\"/g, '"')
+ .replace(/\\'/g, "'");
+ if (!formula.trim()) continue;
+ if (isSimpleFormula(formula)) continue;
+ urls.push(latexToImageUrl(formula));
+ }
+ return urls;
+};
+
+// 单张图片预加载,失败时自动重试(递增延迟 1s → 2s → 3s)
+const preloadImageWithRetry = (url: string, maxRetries = 3): Promise => {
+ return new Promise((resolve) => {
+ let attempt = 0;
+ const tryLoad = () => {
+ uni.getImageInfo({
+ src: url,
+ success: () => resolve(true),
+ fail: () => {
+ attempt++;
+ if (attempt < maxRetries) {
+ setTimeout(tryLoad, attempt * 1000);
+ } else {
+ resolve(false);
+ }
+ },
+ });
+ };
+ tryLoad();
+ });
+};
+
+// 收集题目中所有可能含公式的原始 HTML(题干、选项、答案、解析等)
+const collectAllRawHtml = (data: any): string[] => {
+ if (!data) return [];
+ const htmls: string[] = [];
+ if (data.pidTitle) htmls.push(data.pidTitle);
+ if (data.titleMu || data.title) htmls.push(data.titleMu || data.title);
+ // 子题目
+ if (data.children?.length) {
+ data.children.forEach((c: any) => { if (c.title) htmls.push(c.title); });
+ }
+ // 选项
+ try {
+ let opts = data.optionsMu ?? data.options;
+ if (typeof opts === 'string') opts = JSON.parse(opts);
+ if (Array.isArray(opts)) {
+ opts.forEach((item: any) => {
+ const v = typeof item === 'string' ? item : (item?.value ?? item?.optionValue ?? '');
+ if (v) htmls.push(String(v));
+ });
+ }
+ } catch { /* ignore parse error */ }
+ // 答案、解析、AI 等
+ if (data.answerMu || data.answer) htmls.push(data.answerMu || data.answer);
+ if (data.titleAnalyzeMu || data.titleAnalyze) htmls.push(data.titleAnalyzeMu || data.titleAnalyze);
+ if (data.aiAnswer != null) htmls.push(String(data.aiAnswer));
+ if (data.aiAnalyze != null) htmls.push(String(data.aiAnalyze));
+ return htmls;
+};
+
+// 监听题目数据变化,后台预加载所有公式图片,失败自动重试,彻底失败后切换文本兜底
+let preloadGeneration = 0;
+watch(
+ () => props.data,
+ async (newData) => {
+ const generation = ++preloadGeneration;
+ // 新题目先清空失败集合(乐观渲染,先展示图片)
+ failedFormulaUrls.value = new Set();
+
+ if (!newData) return;
+
+ // 收集题目所有原始 HTML,提取其中的公式图片 URL
+ const allUrls = new Set();
+ for (const html of collectAllRawHtml(newData)) {
+ const processed = fixMathSymbolsInHtml(html);
+ extractFormulaImageUrls(processed).forEach((url) => allUrls.add(url));
+ }
+
+ if (allUrls.size === 0) return;
+
+ // 并发预加载每张公式图片(每张最多重试 3 次,间隔递增)
+ const results = await Promise.all(
+ Array.from(allUrls).map(async (url) => ({
+ url,
+ ok: await preloadImageWithRetry(url, 3),
+ })),
+ );
+
+ // 如果用户已切到下一题(generation 过期),丢弃本次结果
+ if (generation !== preloadGeneration) return;
+
+ const failed = results.filter((r) => !r.ok).map((r) => r.url);
+ if (failed.length > 0) {
+ console.warn(`[Question] ${failed.length} 张公式图片加载失败,已切换为文本兜底`, failed);
+ // 设置新 Set 触发响应式更新 → filterTitleWithoutImages / optionList / filterTitle 重新求值 → 失败图片用文本替代
+ failedFormulaUrls.value = new Set(failed);
+ }
+ },
+ { immediate: true },
+);
// 选项列表 - 单选题/多选题:仅用 optionsMu 或 options,不用 optionList
// options/optionsMu 可能是 JSON 字符串,需先解析为数组
@@ -688,7 +813,7 @@ const optionList = computed(() => {
}
value = fixMathSymbolsInHtml(value);
- value = processFormula(value);
+ value = processFormula(value, failedFormulaUrls.value);
value = typeof value === 'string' ? value.replace(/http:\/\//gi, 'https://') : '';
// 选项内容为空时显示占位,避免只看到选项字母没有内容
// if (!value || (typeof value === 'string' && !value.trim())) {
@@ -762,7 +887,7 @@ const filterTitle = (title?: string, cidx?: number | string) => {
if (!title) return '';
let t = title.replace(/http:\/\//gi, 'https://');
t = fixMathSymbolsInHtml(t);
- t = processFormula(t);
+ t = processFormula(t, failedFormulaUrls.value);
if (cidx !== undefined) {
t = `(${Number(cidx) + 1})` + t;
}
diff --git a/src/pages/teacher/index/index.vue b/src/pages/teacher/index/index.vue
index e82a00d..c466c2d 100644
--- a/src/pages/teacher/index/index.vue
+++ b/src/pages/teacher/index/index.vue
@@ -584,6 +584,7 @@ $teacher-bg: 'https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com/urm/main_bg.s
flex-wrap: wrap;
gap: 6rpx;
justify-content: center;
+ margin-top: 8rpx;
}
.action-item {
@@ -596,7 +597,7 @@ $teacher-bg: 'https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com/urm/main_bg.s
background: rgba(255, 255, 255, 0.85);
border-radius: 8rpx;
border: 1rpx solid rgba(196, 181, 255, 0.2);
- margin-top: 8rpx;
+ // margin-top: 8rpx;
width: 100%;
.action-icon {
font-size: 18rpx;
diff --git a/src/pages/teacher/weakKnowledge/index.vue b/src/pages/teacher/weakKnowledge/index.vue
index bc8a261..1b819b7 100644
--- a/src/pages/teacher/weakKnowledge/index.vue
+++ b/src/pages/teacher/weakKnowledge/index.vue
@@ -8,7 +8,7 @@
-
+
{{ errorMsg }}
@@ -23,7 +23,7 @@
薄弱知识点 Top10
- 暂无薄弱知识点数据
+