feat(mobile): learning page fold round 2 — deepen APP-like compression

用户复查后继续压缩(折叠不删减):
- 岗位 SOP:要点默认只显示 2 条,与答案共用「展开全文(含N条要点)」
  开关(原要点全量常显,该面板 1122px 是首因)
- 训练前预习卡:默认只显示「要点」,红线/可说话术收进
  「展开红线与可说话术」(练前场景里预习卡与练页预习视图重复度高)
- 错题本默认 3→2 条(展开全部不变)
- 学习后动作:三个竖排大按钮改为一行三列

高度变化:5452(初始)→3890(第一轮)→3039px(本轮,累计 -44%);
剩余为 8 个功能面板骨架高度,继续压需砍面板,violates 不影响功能原则。
面板分布:错题本 701→544、岗位SOP 1122→738、预习卡 596→361、
学习后动作 234→116。

验证:本地折叠/展开断言全过 + 生产复核 height<3300、双折叠就位。
发布备份 h5-before-$TS。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 13:43:57 +08:00
co-authored by Claude Fable 5
parent 0a7eb38e05
commit c0538ad8c3
+36 -17
View File
@@ -117,7 +117,7 @@
</button>
<text v-else-if="item.retryAssignmentId" class="mistake-completed">已完成</text>
</view>
<button v-if="visibleMistakes.length > 3" class="fold-toggle" @click="showAllMistakes = !showAllMistakes">
<button v-if="visibleMistakes.length > 2" class="fold-toggle" @click="showAllMistakes = !showAllMistakes">
{{ showAllMistakes ? '收起 ▲' : `展开全部 ${visibleMistakes.length} 条 ▼` }}
</button>
</view>
@@ -228,14 +228,14 @@
</button>
</view>
<text v-if="knowledgeResult?.answer" class="answer-text">{{ displayedAnswer }}</text>
<button v-if="answerIsLong" class="fold-toggle" @click="showFullAnswer = !showFullAnswer">
{{ showFullAnswer ? '收起全文 ▲' : '展开全文 ▼' }}
</button>
<view v-if="knowledgeResult?.keyPoints?.length" class="keypoints">
<text v-for="(point, index) in knowledgeResult.keyPoints" :key="point" class="keypoint">
<view v-if="displayedKeyPoints.length" class="keypoints">
<text v-for="(point, index) in displayedKeyPoints" :key="point" class="keypoint">
{{ index + 1 }}. {{ cleanOrderedText(point) }}
</text>
</view>
<button v-if="sopFoldNeeded" class="fold-toggle" @click="showFullAnswer = !showFullAnswer">
{{ showFullAnswer ? '收起全文 ▲' : `展开全文(含 ${knowledgeResult?.keyPoints?.length || 0} 条要点)▼` }}
</button>
<view v-if="knowledgeResult?.snippets?.length" class="snippet-list">
<view
v-for="(snippet, index) in knowledgeResult.snippets?.slice(0, 3)"
@@ -265,14 +265,19 @@
<text class="prep-label">要点</text>
<text v-for="item in prepCard.keyPoints.slice(0, 3)" :key="`point-${item}`" class="prep-line">{{ item }}</text>
</view>
<view class="prep-block red">
<text class="prep-label">红线</text>
<text v-for="item in prepCard.redLines.slice(0, 3)" :key="`red-${item}`" class="prep-line">{{ item }}</text>
</view>
<view class="prep-block script">
<text class="prep-label">可说话术</text>
<text v-for="item in prepCard.scripts.slice(0, 3)" :key="`script-${item}`" class="prep-line">{{ item }}</text>
</view>
<template v-if="showPrepDetail">
<view class="prep-block red">
<text class="prep-label">红线</text>
<text v-for="item in prepCard.redLines.slice(0, 3)" :key="`red-${item}`" class="prep-line">{{ item }}</text>
</view>
<view class="prep-block script">
<text class="prep-label">可说话术</text>
<text v-for="item in prepCard.scripts.slice(0, 3)" :key="`script-${item}`" class="prep-line">{{ item }}</text>
</view>
</template>
<button class="fold-toggle" @click="showPrepDetail = !showPrepDetail">
{{ showPrepDetail ? '收起 ▲' : '展开红线与可说话术 ▼' }}
</button>
</view>
<view v-else-if="!prepLoading" class="empty">暂无可用预习卡</view>
<button class="primary-button full-action" @click="goPractice">练一遍</button>
@@ -366,13 +371,21 @@ const ANSWER_PREVIEW_CHARS = 140;
const showAllMistakes = ref(false);
const showFullAnswer = ref(false);
const expandedSnippets = ref<Record<number, boolean>>({});
const displayedMistakes = computed(() => (showAllMistakes.value ? visibleMistakes.value : visibleMistakes.value.slice(0, 3)));
const displayedMistakes = computed(() => (showAllMistakes.value ? visibleMistakes.value : visibleMistakes.value.slice(0, 2)));
const answerIsLong = computed(() => (knowledgeResult.value?.answer || '').length > ANSWER_PREVIEW_CHARS);
const displayedAnswer = computed(() => {
const answer = knowledgeResult.value?.answer || '';
if (showFullAnswer.value || answer.length <= ANSWER_PREVIEW_CHARS) return answer;
return `${answer.slice(0, ANSWER_PREVIEW_CHARS)}…`;
});
const KEYPOINT_PREVIEW_COUNT = 2;
const displayedKeyPoints = computed(() => {
const points = knowledgeResult.value?.keyPoints || [];
return showFullAnswer.value ? points : points.slice(0, KEYPOINT_PREVIEW_COUNT);
});
const sopFoldNeeded = computed(() =>
answerIsLong.value || (knowledgeResult.value?.keyPoints?.length || 0) > KEYPOINT_PREVIEW_COUNT);
const showPrepDetail = ref(false);
const toggleSnippet = (index: number) => {
expandedSnippets.value = { ...expandedSnippets.value, [index]: !expandedSnippets.value[index] };
};
@@ -1045,11 +1058,17 @@ onUnload(invalidateRequests);
.snippet-list,
.action-list {
display: flex;
flex-direction: column;
gap: 12px;
gap: 10px;
margin-top: 14px;
}
.action-list .action-button {
flex: 1;
min-height: 44px;
padding: 0 4px;
font-size: 14px;
}
.task-row {
display: flex;
gap: 12px;