fix: 修复完形填空、阅读理解题目显示问题

This commit is contained in:
李梦 2026-02-16 19:13:16 +08:00
parent c75e009bea
commit bc6e74dc9f
2 changed files with 485 additions and 7 deletions

4
.env
View File

@ -1,5 +1,5 @@
#VITE_HOST = http://192.168.0.114:9053 #VITE_HOST = http://192.168.0.114:9053
VITE_HOST = http://43.136.52.196:9053 #VITE_HOST = http://43.136.52.196:9053
#VITE_HOST= https://ai.xuexiaole.com VITE_HOST= https://ai.xuexiaole.com
VITE_OSS_HOST = https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com VITE_OSS_HOST = https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com
VITE_WS_URL = wss://test.qiaoying.vip/wss/websocket VITE_WS_URL = wss://test.qiaoying.vip/wss/websocket

View File

@ -29,12 +29,47 @@
mode="aspectFit" mode="aspectFit"
/> />
</view> </view>
<!-- 子题目 --> <!-- 子题目非阅读理解 / 非完形填空 -->
<view v-if="data?.children?.length" class="left-children"> <view v-if="data?.children?.length && questionType !== 'yuedu' && questionType !== 'wanxing'" class="left-children">
<view v-for="(child, cidx) in data.children" :key="child.id" class="child-title"> <view v-for="(child, cidx) in data.children" :key="child.id" class="child-title">
<rich-text :nodes="filterTitleWithoutImages(child.title, cidx)" /> <rich-text :nodes="filterTitleWithoutImages(child.title, cidx)" />
</view> </view>
</view> </view>
<!-- 阅读理解子题题干 + 选项纵向布局 -->
<view v-if="isYueDu && yueDuList.length" class="left-yuedu">
<view v-for="(subQ, sIdx) in yueDuList" :key="sIdx" class="left-yuedu-item">
<view class="left-yuedu-stem">
<text class="left-yuedu-idx">{{ sIdx + 1 }}.</text>
<view v-if="subQ.stem" class="left-yuedu-stem-text">
<rich-text :nodes="subQ.stem" />
</view>
</view>
<view :class="['left-yuedu-opts', { vertical: !subQ.horizontal }]">
<view v-for="(opt, oIdx) in subQ.options" :key="oIdx" class="left-yuedu-opt">
<text class="left-yuedu-opt-label">{{ opt.label }}.</text>
<view class="left-yuedu-opt-value">
<rich-text :nodes="opt.value" />
</view>
</view>
</view>
</view>
</view>
<!-- 完形填空序号 + 选项横向紧凑布局无题干 -->
<view v-if="isWanXing && yueDuList.length" class="left-wanxing">
<view v-for="(subQ, sIdx) in yueDuList" :key="sIdx" class="left-wanxing-row">
<text class="left-wanxing-idx">{{ sIdx + 1 }}</text>
<view class="left-wanxing-opts">
<view v-for="(opt, oIdx) in subQ.options" :key="oIdx" class="left-wanxing-opt">
<text class="left-wanxing-opt-label">{{ opt.label }}.</text>
<view class="left-wanxing-opt-value">
<rich-text :nodes="opt.value" />
</view>
</view>
</view>
</view>
</view>
</scroll-view> </scroll-view>
<!-- 分隔线 + 折叠按钮 --> <!-- 分隔线 + 折叠按钮 -->
@ -131,6 +166,32 @@
</view> </view>
</template> </template>
<!-- 阅读理解 / 完形填空答题卡 -->
<template v-else-if="questionType === 'yuedu' || questionType === 'wanxing'">
<view class="yuedu-answer-card">
<view class="yuedu-card-title">
<text>答题卡</text>
</view>
<view v-for="(subQ, sIdx) in yueDuList" :key="sIdx" class="yuedu-card-row">
<text class="yuedu-card-num">{{ sIdx + 1 }}</text>
<view class="yuedu-card-btns">
<view
v-for="(opt, oIdx) in subQ.options"
:key="oIdx"
:class="['yuedu-card-btn', {
active: getYueDuAnswer(sIdx) === opt.label,
correct: showYueDuCorrect(sIdx, opt.label),
wrong: showYueDuWrong(sIdx, opt.label)
}]"
@click="selectYueDuOption(sIdx, opt.label)"
>
<text>{{ opt.label }}</text>
</view>
</view>
</view>
</view>
</template>
<!-- 其他题型上传图片 - 支持多图 --> <!-- 其他题型上传图片 - 支持多图 -->
<template v-else> <template v-else>
<view class="upload-area"> <view class="upload-area">
@ -718,14 +779,27 @@ const collectAllRawHtml = (data: any): string[] => {
if (data.children?.length) { if (data.children?.length) {
data.children.forEach((c: any) => { if (c.title) htmls.push(c.title); }); data.children.forEach((c: any) => { if (c.title) htmls.push(c.title); });
} }
// //
try { try {
let opts = data.optionsMu ?? data.options; let opts = data.optionsMu ?? data.options;
if (typeof opts === 'string') opts = JSON.parse(opts); if (typeof opts === 'string') opts = JSON.parse(opts);
if (Array.isArray(opts)) { if (Array.isArray(opts)) {
opts.forEach((item: any) => { opts.forEach((item: any) => {
const v = typeof item === 'string' ? item : (item?.value ?? item?.optionValue ?? ''); if (typeof item === 'string') {
if (v) htmls.push(String(v)); if (item) htmls.push(item);
} else if (item?.stem || item?.options) {
// / { stem, options: [...] }
if (item.stem) htmls.push(String(item.stem));
if (Array.isArray(item.options)) {
item.options.forEach((sub: any) => {
const sv = typeof sub === 'string' ? sub : (sub?.value ?? sub?.optionValue ?? '');
if (sv) htmls.push(String(sv));
});
}
} else {
const v = item?.value ?? item?.optionValue ?? '';
if (v) htmls.push(String(v));
}
}); });
} }
} catch { /* ignore parse error */ } } catch { /* ignore parse error */ }
@ -835,11 +909,86 @@ const fillBlankNum = computed(() => {
return props.data?.fillBlankNum || 0; return props.data?.fillBlankNum || 0;
}); });
// ========== / ==========
// options admin normalizedOptions
const rawParsedOptions = computed(() => {
const data = props.data;
let raw = data?.optionsMu != null ? data.optionsMu
: data?.options != null ? data.options : null;
if (typeof raw === 'string') {
try { raw = JSON.parse(raw); } catch { raw = null; }
}
if (!Array.isArray(raw)) return [];
// [[...]] [...]
let list = raw;
while (Array.isArray(list) && list.length === 1 && Array.isArray(list[0])) {
list = list[0];
}
return list;
});
// stem + options
const isYueDu = computed(() => {
const list = rawParsedOptions.value;
if (!list.length) return false;
return list.some((item: any) => item?.options && item?.stem);
});
// options stem
const isWanXing = computed(() => {
if (isYueDu.value) return false;
const list = rawParsedOptions.value;
if (!list.length) return false;
return list.some((item: any) => item?.options && !item?.stem);
});
// / { index, stem, options: [{label, value}] }
const yueDuList = computed(() => {
if (!isYueDu.value && !isWanXing.value) return [];
return rawParsedOptions.value.map((item: any, idx: number) => {
//
const rawSubOpts = Array.isArray(item.options) ? item.options : [];
const subOpts = rawSubOpts.map((opt: any, oIdx: number) => {
let v = typeof opt === 'string' ? opt : (opt?.value ?? opt?.optionValue ?? '');
v = v != null ? String(v) : '';
v = fixMathSymbolsInHtml(v);
v = processFormula(v, failedFormulaUrls.value);
v = v.replace(/http:\/\//gi, 'https://');
return {
label: (typeof opt === 'string') ? String.fromCharCode(65 + oIdx) : (opt?.label || opt?.optionKey || String.fromCharCode(65 + oIdx)),
value: v,
};
});
//
let stem = '';
if (item.stem) {
stem = String(item.stem);
stem = fixMathSymbolsInHtml(stem);
stem = processFormula(stem, failedFormulaUrls.value);
stem = stem.replace(/http:\/\//gi, 'https://');
}
// < 10
let horizontal = false;
if (subOpts.length > 0) {
const maxLen = Math.max(...subOpts.map((o: any) => String(o.value || '').replace(/<[^>]*>/g, '').length));
horizontal = maxLen < 10;
}
return { index: idx, stem, options: subOpts, horizontal };
});
});
// //
const questionType = computed(() => { const questionType = computed(() => {
const data = props.data; const data = props.data;
if (!data) return 'other'; if (!data) return 'other';
//
if (isYueDu.value) return 'yuedu';
//
if (isWanXing.value) return 'wanxing';
// //
if (['判断', '判断题'].includes(data.titleChannelTypeName)) { if (['判断', '判断题'].includes(data.titleChannelTypeName)) {
return 'judgment'; return 'judgment';
@ -941,6 +1090,61 @@ const selectJudgment = (value: string) => {
emit('submit', { submitAnswer: value, toNext: true }); emit('submit', { submitAnswer: value, toNext: true });
}; };
// ========== / ==========
// submitAnswer
const yueDuAnswerList = computed(() => {
if (!submitAnswer.value) return [];
return submitAnswer.value.split(',');
});
//
const getYueDuAnswer = (sIdx: number): string => {
return yueDuAnswerList.value[sIdx] || '';
};
//
const selectYueDuOption = (sIdx: number, key: string) => {
if (props.reportFlag) return;
const answers = [...yueDuAnswerList.value];
//
while (answers.length <= sIdx) answers.push('');
answers[sIdx] = key;
submitAnswer.value = answers.join(',');
emit('submit', { submitAnswer: submitAnswer.value });
};
//
const correctAnswerList = computed(() => {
const raw = props.data?.answer;
if (!raw) return [];
if (Array.isArray(raw)) return raw.map(String);
if (typeof raw === 'string') {
// JSON ["A","B"]
try {
const parsed = JSON.parse(raw);
if (Array.isArray(parsed)) return parsed.map(String);
} catch { /* not JSON */ }
// "A,B,C"
return raw.split(',').map((s: string) => s.trim());
}
return [];
});
//
const showYueDuCorrect = (sIdx: number, key: string): boolean => {
if (!props.reportFlag) return false;
return correctAnswerList.value[sIdx] === key;
};
//
const showYueDuWrong = (sIdx: number, key: string): boolean => {
if (!props.reportFlag) return false;
const myAnswer = yueDuAnswerList.value[sIdx];
const correctAnswer = correctAnswerList.value[sIdx];
return myAnswer === key && myAnswer !== correctAnswer;
};
// //
const getInputValue = (idx: number) => { const getInputValue = (idx: number) => {
const answers = submitAnswer.value.split(','); const answers = submitAnswer.value.split(',');
@ -1285,6 +1489,179 @@ onUnmounted(() => {
margin-bottom: 16rpx; margin-bottom: 16rpx;
} }
} }
// +
.left-yuedu {
margin-top: 16rpx;
padding: 0 10rpx;
.left-yuedu-item {
margin-bottom: 16rpx;
padding: 10rpx 12rpx;
background: rgba(79, 142, 247, 0.04);
border-radius: 10rpx;
border-left: 4rpx solid #8fb5ff;
&:last-child {
margin-bottom: 0;
}
}
.left-yuedu-stem {
display: flex;
align-items: flex-start;
margin-bottom: 6rpx;
.left-yuedu-idx {
font-family: $font-special;
font-size: 12rpx;
font-weight: 700;
color: #4F8EF7;
margin-right: 4rpx;
flex-shrink: 0;
line-height: 1.6;
}
.left-yuedu-stem-text {
flex: 1;
font-size: 12rpx;
color: $font-color;
line-height: 1.6;
:deep(image) {
max-width: 100%;
height: auto;
}
:deep(.formula-text) {
font-size: 13rpx;
font-family: 'Times New Roman', 'STIXGeneral', Georgia, serif;
font-style: italic;
color: #1a1a1a;
}
}
}
.left-yuedu-opts {
padding-left: 16rpx;
display: flex;
flex-wrap: wrap;
gap: 2rpx 16rpx;
//
&.vertical {
flex-direction: column;
gap: 2rpx;
.left-yuedu-opt {
width: 100%;
}
}
.left-yuedu-opt {
display: flex;
align-items: flex-start;
line-height: 1.6;
.left-yuedu-opt-label {
font-size: 12rpx;
color: #8f9df7;
font-weight: 600;
margin-right: 3rpx;
flex-shrink: 0;
}
.left-yuedu-opt-value {
font-size: 12rpx;
color: $font-color;
:deep(image) {
max-width: 100%;
height: auto;
}
:deep(.formula-text) {
font-size: 13rpx;
font-family: 'Times New Roman', 'STIXGeneral', Georgia, serif;
font-style: italic;
color: #1a1a1a;
}
}
}
}
}
// + admin
.left-wanxing {
margin-top: 16rpx;
padding: 0 10rpx;
.left-wanxing-row {
display: flex;
align-items: flex-start;
gap: 8rpx;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
}
.left-wanxing-idx {
min-width: 20rpx;
height: 20rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 4rpx;
border-radius: 4rpx;
background: #f0f4ff;
font-family: $font-special;
font-size: 11rpx;
font-weight: 700;
color: #4F8EF7;
flex-shrink: 0;
line-height: 20rpx;
}
.left-wanxing-opts {
display: flex;
flex-wrap: wrap;
gap: 2rpx 14rpx;
flex: 1;
}
.left-wanxing-opt {
display: flex;
align-items: flex-start;
line-height: 1.6;
.left-wanxing-opt-label {
font-size: 12rpx;
color: #8f9df7;
font-weight: 600;
margin-right: 2rpx;
flex-shrink: 0;
}
.left-wanxing-opt-value {
font-size: 12rpx;
color: $font-color;
:deep(image) {
max-width: 100%;
height: auto;
}
:deep(.formula-text) {
font-size: 13rpx;
font-family: 'Times New Roman', 'STIXGeneral', Georgia, serif;
font-style: italic;
color: #1a1a1a;
}
}
}
}
} }
.right { .right {
@ -1452,6 +1829,107 @@ onUnmounted(() => {
} }
} }
// /
.yuedu-answer-card {
.yuedu-card-title {
text-align: center;
margin-bottom: 12rpx;
padding-bottom: 8rpx;
border-bottom: 2rpx solid #e8ecf4;
text {
font-family: $font-special;
font-size: 13rpx;
font-weight: 700;
color: #8f9df7;
letter-spacing: 2rpx;
}
}
.yuedu-card-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
gap: 8rpx;
&:last-child {
margin-bottom: 0;
}
}
.yuedu-card-num {
width: 22rpx;
height: 22rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6rpx;
background: #f0f4ff;
font-family: $font-special;
font-size: 11rpx;
font-weight: 700;
color: #4F8EF7;
flex-shrink: 0;
}
.yuedu-card-btns {
display: flex;
gap: 8rpx;
flex: 1;
flex-wrap: wrap;
}
.yuedu-card-btn {
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f7fa;
border: 2rpx solid #e0e4ea;
transition: all 0.2s ease;
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.05);
text {
font-family: $font-special;
font-size: 13rpx;
font-weight: 600;
color: #666;
}
&.active {
background: linear-gradient(135deg, #7eacfe 0%, #5b8def 100%);
border-color: #5b8def;
box-shadow: 0 2rpx 8rpx rgba(91, 141, 239, 0.35);
text {
color: #fff;
}
}
&.correct {
background: linear-gradient(135deg, #6dd5a0 0%, #4cd964 100%);
border-color: #4cd964;
box-shadow: 0 2rpx 8rpx rgba(76, 217, 100, 0.35);
text {
color: #fff;
}
}
&.wrong {
background: linear-gradient(135deg, #ff7875 0%, #ff4d4f 100%);
border-color: #ff4d4f;
box-shadow: 0 2rpx 8rpx rgba(255, 77, 79, 0.35);
text {
color: #fff;
}
}
}
}
.judgment-list { .judgment-list {
display: flex; display: flex;
justify-content: center; justify-content: center;