Compare commits

...

9 Commits

7 changed files with 69 additions and 123 deletions

View File

@ -72,7 +72,7 @@ const weakTone = ['danger', 'warning', 'primary']
description="继续保持每日刷题节奏,稳步提升正确率。" description="继续保持每日刷题节奏,稳步提升正确率。"
/> />
<div v-else class="weak-list"> <div v-else class="weak-list">
<div v-for="(w, i) in data.weakPoints" :key="`${w.module}-${w.point}`" class="weak-item"> <AppStack direction="row" gap="space-3" class="weak-item" v-for="(w, i) in data.weakPoints" :key="`${w.module}-${w.point}`">
<span class="weak-item__rank" :class="`is-${weakTone[i % weakTone.length]}`">{{ i + 1 }}</span> <span class="weak-item__rank" :class="`is-${weakTone[i % weakTone.length]}`">{{ i + 1 }}</span>
<div class="weak-item__body"> <div class="weak-item__body">
<p class="weak-item__title">{{ w.module }} · {{ w.point }}</p> <p class="weak-item__title">{{ w.module }} · {{ w.point }}</p>
@ -82,7 +82,7 @@ const weakTone = ['danger', 'warning', 'primary']
</div> </div>
</div> </div>
<span class="weak-item__tag">{{ w.reasons[0] || '待改进' }}</span> <span class="weak-item__tag">{{ w.reasons[0] || '待改进' }}</span>
</div> </AppStack>
</div> </div>
</AppCard> </AppCard>
@ -150,9 +150,7 @@ const weakTone = ['danger', 'warning', 'primary']
gap: var(--space-4); gap: var(--space-4);
} }
.weak-item { .weak-item {
display: flex; --stack-align: flex-start;
align-items: flex-start;
gap: var(--space-3);
} }
.weak-item__rank { .weak-item__rank {
display: grid; display: grid;

View File

@ -147,7 +147,7 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
/> />
<!-- ============ 移动端今日计划 ============ --> <!-- ============ 移动端今日计划 ============ -->
<div v-else-if="isMobile && todayData" class="plan-mobile"> <template v-else-if="isMobile && todayData">
<header class="plan-mobile__head"> <header class="plan-mobile__head">
<h1>今日计划</h1> <h1>今日计划</h1>
<p>{{ planMessage }}</p> <p>{{ planMessage }}</p>
@ -191,10 +191,10 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
<!-- 本周计划 --> <!-- 本周计划 -->
<AppCard class="plan-week"> <AppCard class="plan-week">
<div class="plan-week__head"> <AppStack direction="row" class="plan-week__head">
<h3>本周计划</h3> <h3>本周计划</h3>
<span class="plan-week__tag"> {{ reviewData?.weekNumber ?? 1 }} </span> <span class="plan-week__tag"> {{ reviewData?.weekNumber ?? 1 }} </span>
</div> </AppStack>
<div class="plan-week__meta"> <div class="plan-week__meta">
<span>完成 {{ weekMetric.done }} / 目标 {{ weekMetric.total }} </span> <span>完成 {{ weekMetric.done }} / 目标 {{ weekMetric.total }} </span>
</div> </div>
@ -212,13 +212,13 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
<!-- 明日预告 --> <!-- 明日预告 -->
<AppCard v-if="tomorrowTasks.length > 0" title="明日预告"> <AppCard v-if="tomorrowTasks.length > 0" title="明日预告">
<div class="plan-tomorrow__row" v-for="task in tomorrowTasks" :key="task.id"> <AppStack direction="row" gap="space-3" class="plan-tomorrow__row" v-for="task in tomorrowTasks" :key="task.id">
<span class="plan-tomorrow__dot" /> <span class="plan-tomorrow__dot" />
<div class="plan-tomorrow__body"> <div class="plan-tomorrow__body">
<p class="plan-tomorrow__title">{{ task.title }}</p> <p class="plan-tomorrow__title">{{ task.title }}</p>
<p class="plan-tomorrow__meta">{{ task.type }}<template v-if="task.module"> · {{ task.module }}</template> · {{ task.target }}</p> <p class="plan-tomorrow__meta">{{ task.type }}<template v-if="task.module"> · {{ task.module }}</template> · {{ task.target }}</p>
</div> </div>
</div> </AppStack>
</AppCard> </AppCard>
</template> </template>
@ -231,10 +231,10 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
> >
<AppButton variant="primary" @click="showCreate = true">创建今日任务</AppButton> <AppButton variant="primary" @click="showCreate = true">创建今日任务</AppButton>
</AppEmpty> </AppEmpty>
</div> </template>
<!-- ============ 桌面端备考计划 ============ --> <!-- ============ 桌面端备考计划 ============ -->
<div v-else-if="todayData" class="plan-desktop"> <template v-else-if="todayData">
<AppPageHeader title="备考计划" :subtitle="`安排阶段任务,稳步推进备考进度 · 备考第 ${profileDays} 天`" sticky-mobile> <AppPageHeader title="备考计划" :subtitle="`安排阶段任务,稳步推进备考进度 · 备考第 ${profileDays} 天`" sticky-mobile>
<RouterLink class="btn-link" to="/plan/review"> <RouterLink class="btn-link" to="/plan/review">
<AppIcon name="chart" :size="16" />周报复盘 <AppIcon name="chart" :size="16" />周报复盘
@ -317,7 +317,7 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
</div> </div>
</AppCard> </AppCard>
</div> </div>
</div> </template>
<!-- 新建任务弹层 --> <!-- 新建任务弹层 -->
<AppModal v-if="showCreate" title="新建计划任务" :max-width="480" @close="showCreate = false"> <AppModal v-if="showCreate" title="新建计划任务" :max-width="480" @close="showCreate = false">
@ -365,12 +365,8 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
<style scoped> <style scoped>
/* 指标卡 */ /* 指标卡 */
.plan-metrics {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--card-gap-desktop);
}
.plan-metric { .plan-metric {
flex: 1;
background: var(--bg-card); background: var(--bg-card);
border: 1px solid var(--border-subtle); border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
@ -459,7 +455,7 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
.plan-side__list { .plan-side__list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-4); gap: var(--space-2);
} }
.plan-side__date { .plan-side__date {
margin: 0 0 var(--space-1); margin: 0 0 var(--space-1);
@ -567,9 +563,8 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
gap: var(--space-3); gap: var(--space-3);
} }
.plan-week__head { .plan-week__head {
display: flex; --stack-align: center;
align-items: center; --stack-justify: space-between;
justify-content: space-between;
} }
.plan-week__head h3 { .plan-week__head h3 {
margin: 0; margin: 0;
@ -615,9 +610,7 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
text-decoration: none; text-decoration: none;
} }
.plan-tomorrow__row { .plan-tomorrow__row {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) 0; padding: var(--space-2) 0;
} }
.plan-tomorrow__dot { .plan-tomorrow__dot {

View File

@ -164,7 +164,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
<p>本卷共 {{ finishResult.total }} · 作答 {{ finishResult.answered }} </p> <p>本卷共 {{ finishResult.total }} · 作答 {{ finishResult.answered }} </p>
</header> </header>
<div class="result__hero"> <AppStack direction="row" gap="space-6" class="result__hero">
<div class="result__score"> <div class="result__score">
<strong class="num">{{ finishResult.correctCount }}<em>/{{ finishResult.total }}</em></strong> <strong class="num">{{ finishResult.correctCount }}<em>/{{ finishResult.total }}</em></strong>
<span>答对题数</span> <span>答对题数</span>
@ -178,18 +178,18 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
<strong class="num">{{ formatDuration(finishResult.durationSeconds) }}</strong> <strong class="num">{{ formatDuration(finishResult.durationSeconds) }}</strong>
<span>用时</span> <span>用时</span>
</div> </div>
</div> </AppStack>
<AppCard v-if="wrongItems.length > 0" title="错题回顾" icon="alert"> <AppCard v-if="wrongItems.length > 0" title="错题回顾" icon="alert">
<div class="wrong-list"> <div class="wrong-list">
<div v-for="(q, i) in wrongItems" :key="q.questionId" class="wrong-list__item"> <div v-for="(q, i) in wrongItems" :key="q.questionId" class="wrong-list__item">
<span class="wrong-list__idx">{{ i + 1 }}</span> <span class="wrong-list__idx">{{ i + 1 }}</span>
<div class="wrong-list__body"> <div class="wrong-list__body">
<div class="wrong-list__ans"> <AppStack direction="row" gap="space-2" class="wrong-list__ans">
<span>你的答案 <b class="is-wrong">{{ q.userAnswer }}</b></span> <span>你的答案 <b class="is-wrong">{{ q.userAnswer }}</b></span>
<span class="wrong-list__arrow"><AppIcon name="chevron-right" :size="14" /></span> <span class="wrong-list__arrow"><AppIcon name="chevron-right" :size="14" /></span>
<span>正确答案 <b class="is-right">{{ q.correctAnswer }}</b></span> <span>正确答案 <b class="is-right">{{ q.correctAnswer }}</b></span>
</div> </AppStack>
<p class="wrong-list__analysis">{{ q.analysis }}</p> <p class="wrong-list__analysis">{{ q.analysis }}</p>
<AppButton <AppButton
variant="ghost" variant="ghost"
@ -267,7 +267,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
<!-- ===== 单题结果态 ===== --> <!-- ===== 单题结果态 ===== -->
<div v-if="answered && result" class="single-result"> <div v-if="answered && result" class="single-result">
<div class="single-result__banner" :class="result.correct ? 'is-correct' : 'is-wrong'"> <AppStack direction="row" gap="space-3" class="single-result__banner" :class="result.correct ? 'is-correct' : 'is-wrong'">
<span class="single-result__icon"> <span class="single-result__icon">
<AppIcon :name="result.correct ? 'check' : 'close'" :size="18" /> <AppIcon :name="result.correct ? 'check' : 'close'" :size="18" />
</span> </span>
@ -276,7 +276,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
<span v-if="!result.correct">正确答案是 {{ result.correctAnswer }} · 本题已自动收录到错题本</span> <span v-if="!result.correct">正确答案是 {{ result.correctAnswer }} · 本题已自动收录到错题本</span>
<span v-else>回答正确继续加油</span> <span v-else>回答正确继续加油</span>
</div> </div>
</div> </AppStack>
<AppCard v-if="result.analysis" title="解析" icon="book-open"> <AppCard v-if="result.analysis" title="解析" icon="book-open">
<p class="single-result__analysis">{{ result.analysis }}</p> <p class="single-result__analysis">{{ result.analysis }}</p>
@ -465,9 +465,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
gap: var(--space-4); gap: var(--space-4);
} }
.single-result__banner { .single-result__banner {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-3);
padding: var(--space-4); padding: var(--space-4);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
color: var(--on-accent); color: var(--on-accent);
@ -553,9 +551,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
color: var(--text-secondary); color: var(--text-secondary);
} }
.result__hero { .result__hero {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-6);
padding: var(--card-padding-desktop); padding: var(--card-padding-desktop);
background: var(--bg-card); background: var(--bg-card);
border: 1px solid var(--border-subtle); border: 1px solid var(--border-subtle);
@ -623,9 +619,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
flex: 1; flex: 1;
} }
.wrong-list__ans { .wrong-list__ans {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-2);
font-size: var(--fs-subtitle); font-size: var(--fs-subtitle);
} }
.wrong-list__ans .is-wrong { .wrong-list__ans .is-wrong {

View File

@ -22,10 +22,10 @@ const QUESTION_TYPES = [
备考通当前聚焦行测闭环申论功能正在规划中 备考通当前聚焦行测闭环申论功能正在规划中
</p> </p>
<div class="essay__types"> <div class="essay__types">
<div v-for="t in QUESTION_TYPES" :key="t.name" class="essay__type"> <AppStack direction="row" gap="space-3" class="essay__type" v-for="t in QUESTION_TYPES" :key="t.name">
<AppBadge variant="primary">{{ t.name }}</AppBadge> <AppBadge variant="primary">{{ t.name }}</AppBadge>
<span class="essay__type-desc">{{ t.desc }}</span> <span class="essay__type-desc">{{ t.desc }}</span>
</div> </AppStack>
</div> </div>
</AppCard> </AppCard>
@ -49,10 +49,10 @@ const QUESTION_TYPES = [
<!-- 说明提示 --> <!-- 说明提示 -->
<AppCard :padded="true"> <AppCard :padded="true">
<div class="essay__notice"> <AppStack direction="row" gap="space-3" class="essay__notice">
<AppIcon name="alert" :size="18" /> <AppIcon name="alert" :size="18" />
<p>当前为占位页面不保存任何写作内容建议先完成行测刷题 错题复习 模考分析闭环</p> <p>当前为占位页面不保存任何写作内容建议先完成行测刷题 错题复习 模考分析闭环</p>
</div> </AppStack>
</AppCard> </AppCard>
</AppStack> </AppStack>
</template> </template>
@ -70,9 +70,7 @@ const QUESTION_TYPES = [
gap: var(--space-3); gap: var(--space-3);
} }
.essay__type { .essay__type {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-3);
padding: var(--space-3); padding: var(--space-3);
border: 1px solid var(--border-subtle); border: 1px solid var(--border-subtle);
border-radius: var(--radius-md); border-radius: var(--radius-md);
@ -104,9 +102,7 @@ const QUESTION_TYPES = [
flex: none; flex: none;
} }
.essay__notice { .essay__notice {
display: flex; --stack-align: flex-start;
align-items: flex-start;
gap: var(--space-3);
} }
.essay__notice svg { .essay__notice svg {
color: var(--warning); color: var(--warning);

View File

@ -172,7 +172,7 @@ const subtitle = computed(() => {
</AppPageHeader> </AppPageHeader>
<!-- tab --> <!-- tab -->
<div class="practice-tabs"> <div class="practice-tabs" v-if="isMobile">
<RouterLink <RouterLink
v-for="t in tabs" v-for="t in tabs"
:key="t.key" :key="t.key"
@ -209,13 +209,13 @@ const subtitle = computed(() => {
:class="{ 'module-card--active': activeModule === m.name }" :class="{ 'module-card--active': activeModule === m.name }"
@click="selectModule = m.name" @click="selectModule = m.name"
> >
<div class="module-card__row"> <AppStack direction="row" class="module-card__row">
<span class="module-card__name">{{ m.name }}</span> <span class="module-card__name">{{ m.name }}</span>
<span v-if="activeModule === m.name" class="module-card__check"> <span v-if="activeModule === m.name" class="module-card__check">
<AppIcon name="check" :size="14" /> <AppIcon name="check" :size="14" />
</span> </span>
</div> </AppStack>
<div class="module-card__meta"> <AppStack direction="row" gap="space-1" class="module-card__meta">
<span class="module-card__count">{{ m.total }} </span> <span class="module-card__count">{{ m.total }} </span>
<span class="module-card__dot">·</span> <span class="module-card__dot">·</span>
<span <span
@ -224,7 +224,7 @@ const subtitle = computed(() => {
> >
掌握度 {{ formatPercent(masteryByModule.get(m.name) ?? 0) }} 掌握度 {{ formatPercent(masteryByModule.get(m.name) ?? 0) }}
</span> </span>
</div> </AppStack>
</button> </button>
</div> </div>
<AppEmpty v-if="(modules.data.value?.modules.length ?? 0) === 0" title="还没有题目" description="请先到题库管理导入题目,再开始刷题。"> <AppEmpty v-if="(modules.data.value?.modules.length ?? 0) === 0" title="还没有题目" description="请先到题库管理导入题目,再开始刷题。">
@ -260,10 +260,10 @@ const subtitle = computed(() => {
<AppEmpty v-if="reasons.length === 0" icon="chart" title="暂无错因数据" description="完成刷题后根据错题统计错因分布。" /> <AppEmpty v-if="reasons.length === 0" icon="chart" title="暂无错因数据" description="完成刷题后根据错题统计错因分布。" />
<div v-else class="reason-bars"> <div v-else class="reason-bars">
<div v-for="r in reasons" :key="r.reason" class="reason-bar"> <div v-for="r in reasons" :key="r.reason" class="reason-bar">
<div class="reason-bar__row"> <AppStack direction="row" class="reason-bar__row">
<span class="reason-bar__label">{{ r.reason }}</span> <span class="reason-bar__label">{{ r.reason }}</span>
<span class="reason-bar__pct">{{ formatPercent(r.percent) }}</span> <span class="reason-bar__pct">{{ formatPercent(r.percent) }}</span>
</div> </AppStack>
<div class="reason-bar__track"> <div class="reason-bar__track">
<div class="reason-bar__fill" :style="{ width: `${r.percent}%` }" /> <div class="reason-bar__fill" :style="{ width: `${r.percent}%` }" />
</div> </div>
@ -282,15 +282,15 @@ const subtitle = computed(() => {
class="pending-item" class="pending-item"
:to="{ name: 'practice-wrong' }" :to="{ name: 'practice-wrong' }"
> >
<div class="pending-item__meta"> <AppStack direction="row" gap="space-2" class="pending-item__meta">
<span class="pending-item__module">{{ item.module }}</span> <span class="pending-item__module">{{ item.module }}</span>
<span class="pending-item__sub">{{ item.subModule }}</span> <span class="pending-item__sub">{{ item.subModule }}</span>
</div> </AppStack>
<div class="pending-item__text">{{ item.stem }}</div> <div class="pending-item__text">{{ item.stem }}</div>
<div class="pending-item__foot"> <AppStack direction="row" class="pending-item__foot">
<span class="pending-item__reason">{{ item.wrongReason }}</span> <span class="pending-item__reason">{{ item.wrongReason }}</span>
<span class="pending-item__count">已复习 {{ item.reviewCount }} </span> <span class="pending-item__count">已复习 {{ item.reviewCount }} </span>
</div> </AppStack>
</RouterLink> </RouterLink>
</div> </div>
</AppCard> </AppCard>
@ -372,13 +372,13 @@ const subtitle = computed(() => {
class="wrong-book-item" class="wrong-book-item"
:to="{ name: 'practice-wrong-detail', params: { id: item.id } }" :to="{ name: 'practice-wrong-detail', params: { id: item.id } }"
> >
<div class="wrong-book-item__head"> <AppStack direction="row" gap="space-2" class="wrong-book-item__head">
<div class="wrong-book-item__tags"> <div class="wrong-book-item__tags">
<AppBadge variant="primary">{{ item.module }}</AppBadge> <AppBadge variant="primary">{{ item.module }}</AppBadge>
<AppBadge v-if="item.subModule" variant="soft">{{ item.subModule }}</AppBadge> <AppBadge v-if="item.subModule" variant="soft">{{ item.subModule }}</AppBadge>
</div> </div>
<AppBadge :variant="wrongStatusVariant(item.status)">{{ wrongStatusLabel(item.status) }}</AppBadge> <AppBadge :variant="wrongStatusVariant(item.status)">{{ wrongStatusLabel(item.status) }}</AppBadge>
</div> </AppStack>
<p class="wrong-book-item__stem">{{ item.stem }}</p> <p class="wrong-book-item__stem">{{ item.stem }}</p>
<div class="wrong-book-item__foot"> <div class="wrong-book-item__foot">
<span v-if="item.wrongReason" class="wrong-book-item__reason">错因 · {{ item.wrongReason }}</span> <span v-if="item.wrongReason" class="wrong-book-item__reason">错因 · {{ item.wrongReason }}</span>
@ -462,9 +462,8 @@ const subtitle = computed(() => {
box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 14%, transparent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 14%, transparent);
} }
.module-card__row { .module-card__row {
display: flex; --stack-align: center;
align-items: center; --stack-justify: space-between;
justify-content: space-between;
} }
.module-card__name { .module-card__name {
font-size: var(--fs-card-title); font-size: var(--fs-card-title);
@ -481,9 +480,7 @@ const subtitle = computed(() => {
color: var(--on-accent); color: var(--on-accent);
} }
.module-card__meta { .module-card__meta {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-1);
font-size: var(--fs-label); font-size: var(--fs-label);
color: var(--text-secondary); color: var(--text-secondary);
} }
@ -531,9 +528,8 @@ const subtitle = computed(() => {
gap: var(--space-3); gap: var(--space-3);
} }
.reason-bar__row { .reason-bar__row {
display: flex; --stack-align: center;
align-items: center; --stack-justify: space-between;
justify-content: space-between;
margin-bottom: var(--space-1); margin-bottom: var(--space-1);
font-size: var(--fs-subtitle); font-size: var(--fs-subtitle);
} }
@ -579,9 +575,7 @@ const subtitle = computed(() => {
padding-block: var(--space-1); padding-block: var(--space-1);
} }
.pending-item__meta { .pending-item__meta {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-2);
font-size: var(--fs-label); font-size: var(--fs-label);
} }
.pending-item__module { .pending-item__module {
@ -601,9 +595,8 @@ const subtitle = computed(() => {
overflow: hidden; overflow: hidden;
} }
.pending-item__foot { .pending-item__foot {
display: flex; --stack-align: center;
align-items: center; --stack-justify: space-between;
justify-content: space-between;
font-size: var(--fs-label); font-size: var(--fs-label);
color: var(--text-secondary); color: var(--text-secondary);
} }
@ -679,10 +672,8 @@ const subtitle = computed(() => {
box-shadow: var(--shadow-level-1); box-shadow: var(--shadow-level-1);
} }
.wrong-book-item__head { .wrong-book-item__head {
display: flex; --stack-align: center;
align-items: center; --stack-justify: space-between;
justify-content: space-between;
gap: var(--space-2);
} }
.wrong-book-item__tags { .wrong-book-item__tags {
display: flex; display: flex;

View File

@ -9,7 +9,8 @@ import AppPageHeader from '../../components/base/AppPageHeader.vue'
import AppStack from '../../components/base/AppStack.vue' import AppStack from '../../components/base/AppStack.vue'
import AppLoading from '../../components/feedback/AppLoading.vue' import AppLoading from '../../components/feedback/AppLoading.vue'
import AppError from '../../components/feedback/AppError.vue' import AppError from '../../components/feedback/AppError.vue'
import { formatDateShort, formatDateWeekday } from '../../utils/format' import { formatDateWeekday } from '../../utils/format'
import { useResponsive } from '../../composables/useResponsive'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
@ -19,6 +20,8 @@ const loading = ref(true)
const error = ref('') const error = ref('')
const detail = ref<ReviewDetail | null>(null) const detail = ref<ReviewDetail | null>(null)
const { isMobile } = useResponsive()
// //
const submitting = ref(false) const submitting = ref(false)
const assessed = ref(false) const assessed = ref(false)
@ -151,26 +154,26 @@ function goAiExplain() {
</div> </div>
<!-- 操作区重做此题 / AI 讲解 / 标记掌握 / 自评 --> <!-- 操作区重做此题 / AI 讲解 / 标记掌握 / 自评 -->
<div v-if="!assessed" class="wd__actions"> <AppStack v-if="!assessed" :direction="isMobile ? 'column' : 'row'" gap="var(--space-3)">
<AppButton block size="lg" icon="refresh" @click="router.push(`/practice/session`)" disabled> <AppButton block size="lg" icon="refresh" @click="router.push(`/practice/session`)" disabled>
重做此题 重做此题
</AppButton> </AppButton>
<AppButton block size="lg" variant="secondary" icon="sparkles" @click="goAiExplain"> <AppButton block size="lg" variant="secondary" icon="sparkles" @click="goAiExplain">
AI 讲解 AI 讲解
</AppButton> </AppButton>
</div> </AppStack>
<!-- 自评区 --> <!-- 自评区 -->
<section v-if="!assessed && detail.status !== 'mastered'" class="wd__assess"> <section v-if="!assessed && detail.status !== 'mastered'" class="wd__assess">
<p class="wd__assess-title">这次复习结果如何用于安排下次复习</p> <p class="wd__assess-title">这次复习结果如何用于安排下次复习</p>
<div class="wd__assess-btns"> <AppStack :direction="isMobile ? 'column' : 'row'" gap="var(--space-3)" class="wd__assess-btns">
<AppButton block size="lg" variant="secondary" :loading="submitting" @click="assess(true)"> <AppButton block size="lg" variant="secondary" :loading="submitting" @click="assess(true)">
记对了 记对了
</AppButton> </AppButton>
<AppButton block size="lg" variant="danger" :loading="submitting" @click="assess(false)"> <AppButton block size="lg" variant="danger" :loading="submitting" @click="assess(false)">
记错了 记错了
</AppButton> </AppButton>
</div> </AppStack>
<div v-if="chosenReason" class="wd__reason-hint">记错原因{{ chosenReason }}</div> <div v-if="chosenReason" class="wd__reason-hint">记错原因{{ chosenReason }}</div>
<div class="wd__reason-tags"> <div class="wd__reason-tags">
<button <button
@ -343,15 +346,6 @@ function goAiExplain() {
color: var(--text-primary); color: var(--text-primary);
} }
/* 操作区 */
.wd__actions {
display: flex;
gap: var(--space-3);
}
.wd__actions .btn {
flex: 1;
}
/* 自评区 */ /* 自评区 */
.wd__assess { .wd__assess {
background: var(--bg-soft); background: var(--bg-soft);
@ -368,10 +362,6 @@ function goAiExplain() {
font-weight: var(--fw-button); font-weight: var(--fw-button);
color: var(--text-primary); color: var(--text-primary);
} }
.wd__assess-btns {
display: flex;
gap: var(--space-3);
}
.wd__reason-hint { .wd__reason-hint {
font-size: var(--fs-label); font-size: var(--fs-label);
color: var(--warning); color: var(--warning);
@ -489,13 +479,4 @@ function goAiExplain() {
color: var(--text-secondary); color: var(--text-secondary);
margin-left: auto; margin-left: auto;
} }
@media (max-width: 767px) {
.wd__actions {
flex-direction: column;
}
.wd__assess-btns {
flex-direction: column;
}
}
</style> </style>

View File

@ -86,7 +86,7 @@ function editProfile() {
/> />
<!-- ============ 移动端我的 ============ --> <!-- ============ 移动端我的 ============ -->
<div v-else-if="isMobile && d" class="profile-mobile"> <template v-else-if="isMobile && d">
<header class="profile-mobile__head"> <header class="profile-mobile__head">
<h1>我的</h1> <h1>我的</h1>
</header> </header>
@ -165,10 +165,10 @@ function editProfile() {
<p class="profile-tip__text">{{ weakTip }}</p> <p class="profile-tip__text">{{ weakTip }}</p>
</div> </div>
</AppCard> </AppCard>
</div> </template>
<!-- ============ 桌面端我的 ============ --> <!-- ============ 桌面端我的 ============ -->
<div v-else-if="d" class="profile-desktop"> <template v-else-if="d">
<AppPageHeader title="我的" subtitle="管理个人资料与备考偏好" sticky-mobile> <AppPageHeader title="我的" subtitle="管理个人资料与备考偏好" sticky-mobile>
<AppButton variant="secondary" @click="editProfile"> <AppButton variant="secondary" @click="editProfile">
<AppIcon name="user" :size="16" />编辑资料 <AppIcon name="user" :size="16" />编辑资料
@ -235,15 +235,15 @@ function editProfile() {
</AppCard> </AppCard>
<!-- 本周学习建议 --> <!-- 本周学习建议 -->
<div class="tip-banner"> <AppStack direction="row" gap="space-3" class="tip-banner">
<span class="tip-banner__icon"><AppIcon name="target" :size="20" /></span> <span class="tip-banner__icon"><AppIcon name="target" :size="20" /></span>
<div class="tip-banner__body"> <div class="tip-banner__body">
<p class="tip-banner__title">本周学习建议</p> <p class="tip-banner__title">本周学习建议</p>
<p class="tip-banner__text">{{ weakTip }}</p> <p class="tip-banner__text">{{ weakTip }}</p>
</div> </div>
<RouterLink class="tip-banner__action" to="/plan/review">去加强</RouterLink> <RouterLink class="tip-banner__action" to="/plan/review">去加强</RouterLink>
</div> </AppStack>
</div> </template>
</AppStack> </AppStack>
</template> </template>
@ -391,9 +391,7 @@ function editProfile() {
/* 学习建议 */ /* 学习建议 */
.tip-banner { .tip-banner {
display: flex; --stack-align: center;
align-items: center;
gap: var(--space-3);
margin-top: var(--space-2); margin-top: var(--space-2);
padding: var(--space-4) var(--space-5); padding: var(--space-4) var(--space-5);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
@ -435,11 +433,6 @@ function editProfile() {
} }
/* 移动端 */ /* 移动端 */
.profile-mobile {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.profile-mobile__head h1 { .profile-mobile__head h1 {
margin: 0; margin: 0;
font-size: 22px; font-size: 22px;