refactor: 移动端去除layout顶栏,页面头统一收敛到AppPageHeader
This commit is contained in:
parent
fbf134ccc6
commit
bf80cf15b6
@ -1,21 +1,59 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
withDefaults(
|
import { useRouter } from 'vue-router'
|
||||||
|
import AppIcon from './AppIcon.vue'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
title: string
|
title?: string
|
||||||
subtitle?: string
|
subtitle?: string
|
||||||
|
/** page:一级页头(h1+副标题+右段动作);detail:详情/子页返回头(紧凑标题,默认带返回) */
|
||||||
|
size?: 'page' | 'detail'
|
||||||
|
/** 是否渲染左段返回按钮 */
|
||||||
|
back?: boolean
|
||||||
|
/** 返回目标;缺省时使用 history.back() */
|
||||||
|
backTo?: string
|
||||||
/** 移动端是否纵向堆叠(部分页面移动版按钮在标题下方) */
|
/** 移动端是否纵向堆叠(部分页面移动版按钮在标题下方) */
|
||||||
stackMobile?: boolean
|
stackMobile?: boolean
|
||||||
|
/** 移动端吸顶(替代原 layout topbar 的吸顶行为;仅移动端生效) */
|
||||||
|
stickyMobile?: boolean
|
||||||
}>(),
|
}>(),
|
||||||
{ subtitle: '', stackMobile: false }
|
{ title: '', subtitle: '', size: 'page', back: false, backTo: '', stackMobile: false, stickyMobile: false }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
if (props.backTo) router.push(props.backTo)
|
||||||
|
else router.back()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header class="page-head" :class="{ 'page-head--stack': stackMobile }">
|
<header
|
||||||
|
class="page-head"
|
||||||
|
:class="{
|
||||||
|
'page-head--stack': stackMobile,
|
||||||
|
'page-head--detail': size === 'detail',
|
||||||
|
'page-head--sticky-mobile': stickyMobile
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-if="back"
|
||||||
|
type="button"
|
||||||
|
class="page-head__back"
|
||||||
|
aria-label="返回"
|
||||||
|
@click="goBack"
|
||||||
|
>
|
||||||
|
<AppIcon name="chevron-left" :size="20" />
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="page-head__title">
|
<div class="page-head__title">
|
||||||
<h1>{{ title }}</h1>
|
<slot name="title">
|
||||||
<p v-if="subtitle">{{ subtitle }}</p>
|
<h1 v-if="title">{{ title }}</h1>
|
||||||
|
<p v-if="subtitle">{{ subtitle }}</p>
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="$slots.default" class="page-head__actions"><slot /></div>
|
<div v-if="$slots.default" class="page-head__actions"><slot /></div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
@ -49,9 +87,53 @@ withDefaults(
|
|||||||
gap: var(--space-3);
|
gap: var(--space-3);
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 返回按钮(左段) */
|
||||||
|
.page-head__back {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
flex: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--motion-hover-fast) var(--ease-default);
|
||||||
|
}
|
||||||
|
.page-head__back:hover {
|
||||||
|
background: var(--bg-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 详情/子页返回头:紧凑行内布局 */
|
||||||
|
.page-head--detail {
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
.page-head--detail .page-head__title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
.page-head--detail h1 {
|
||||||
|
font-size: var(--fs-h3);
|
||||||
|
font-weight: var(--fw-h1);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.page-head--stack {
|
.page-head--stack {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
/* 吸顶(替代原 layout topbar;负边距让背景铺满屏宽) */
|
||||||
|
.page-head--sticky-mobile {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 30;
|
||||||
|
margin-left: calc(-1 * var(--padding-mobile));
|
||||||
|
margin-right: calc(-1 * var(--padding-mobile));
|
||||||
|
padding: var(--space-2) var(--padding-mobile);
|
||||||
|
background: var(--bg-canvas);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,22 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import AppIcon from '../components/base/AppIcon.vue'
|
import AppIcon from '../components/base/AppIcon.vue'
|
||||||
import { mobileNav } from '../router/nav'
|
import { mobileNav } from '../router/nav'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const title = computed(() => String(route.meta.title ?? '备考通'))
|
|
||||||
// 首页以自己的问候语作为页头,不再重复显示顶部栏标题
|
|
||||||
const isHome = computed(() => route.path === '/')
|
|
||||||
// 刷题相关页面自带页头(AppPageHeader),隐藏顶栏标题避免重复
|
|
||||||
const hideTopbar = computed(() => isHome.value || route.path.startsWith('/practice'))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="layout-mobile">
|
<div class="layout-mobile">
|
||||||
<header v-if="!hideTopbar" class="topbar">
|
|
||||||
<h1>{{ title }}</h1>
|
|
||||||
</header>
|
|
||||||
<main class="content"><slot /></main>
|
<main class="content"><slot /></main>
|
||||||
<nav class="tabbar">
|
<nav class="tabbar">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
@ -37,22 +28,6 @@ const hideTopbar = computed(() => isHome.value || route.path.startsWith('/practi
|
|||||||
.layout-mobile {
|
.layout-mobile {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
.topbar {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 10;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 56px;
|
|
||||||
padding: 0 var(--padding-mobile);
|
|
||||||
background: var(--bg-canvas);
|
|
||||||
}
|
|
||||||
.topbar h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: var(--fw-h1);
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
.content {
|
.content {
|
||||||
padding: 0 var(--padding-mobile) var(--tabbar-height-mobile);
|
padding: 0 var(--padding-mobile) var(--tabbar-height-mobile);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,7 +130,7 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mock-form">
|
<div class="mock-form">
|
||||||
<AppPageHeader :title="title" subtitle="仅支持行测五个模块分数,总分自动汇总">
|
<AppPageHeader :title="title" subtitle="仅支持行测五个模块分数,总分自动汇总" sticky-mobile>
|
||||||
<AppButton variant="ghost" @click="back">
|
<AppButton variant="ghost" @click="back">
|
||||||
<AppIcon name="chevron-left" :size="16" />返回模考分析
|
<AppIcon name="chevron-left" :size="16" />返回模考分析
|
||||||
</AppButton>
|
</AppButton>
|
||||||
|
|||||||
@ -98,7 +98,7 @@ async function confirmRemove() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<AppPageHeader title="模考分析" subtitle="行测成绩趋势 · 目标分差 · 模块对比" :stack-mobile="isMobile">
|
<AppPageHeader title="模考分析" subtitle="行测成绩趋势 · 目标分差 · 模块对比" :stack-mobile="isMobile" sticky-mobile>
|
||||||
<AppButton variant="primary" @click="goEntry">
|
<AppButton variant="primary" @click="goEntry">
|
||||||
<AppIcon name="plus" :size="16" />录入成绩
|
<AppIcon name="plus" :size="16" />录入成绩
|
||||||
</AppButton>
|
</AppButton>
|
||||||
|
|||||||
@ -8,8 +8,7 @@ import { formatDateShort } from '../../utils/format'
|
|||||||
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 AppEmpty from '../../components/feedback/AppEmpty.vue'
|
import AppEmpty from '../../components/feedback/AppEmpty.vue'
|
||||||
import AppIcon from '../../components/base/AppIcon.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppButton from '../../components/base/AppButton.vue'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = computed(() => String(route.params.id ?? ''))
|
const id = computed(() => String(route.params.id ?? ''))
|
||||||
@ -32,12 +31,9 @@ const html = computed(() => renderMarkdown(detail.data.value?.content ?? ''))
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<template v-else-if="detail.data.value">
|
<template v-else-if="detail.data.value">
|
||||||
<header class="head">
|
<AppPageHeader back class="news-detail__head" sticky-mobile>
|
||||||
<AppButton class="head__back" variant="ghost" size="md" aria-label="返回" @click="$router.back()">
|
<template #title><span class="head__label">要闻详情</span></template>
|
||||||
<AppIcon name="chevron-left" :size="20" />
|
</AppPageHeader>
|
||||||
</AppButton>
|
|
||||||
<span class="head__label">要闻详情</span>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article class="article">
|
<article class="article">
|
||||||
<section class="article__hero">
|
<section class="article__hero">
|
||||||
@ -61,25 +57,9 @@ const html = computed(() => renderMarkdown(detail.data.value?.content ?? ''))
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.head {
|
.news-detail__head {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
margin-bottom: var(--space-4);
|
margin-bottom: var(--space-4);
|
||||||
}
|
}
|
||||||
.head__back {
|
|
||||||
display: grid !important;
|
|
||||||
place-items: center;
|
|
||||||
width: 36px !important;
|
|
||||||
height: 36px !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
border: none !important;
|
|
||||||
border-radius: 50% !important;
|
|
||||||
background: var(--bg-card) !important;
|
|
||||||
color: var(--text-primary) !important;
|
|
||||||
gap: 0 !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.head__label {
|
.head__label {
|
||||||
font-size: var(--fs-subtitle);
|
font-size: var(--fs-subtitle);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
|||||||
@ -96,7 +96,7 @@ async function doImport() {
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 页头 -->
|
<!-- 页头 -->
|
||||||
<AppPageHeader :title="isMobile ? '要闻公告' : '要闻'" subtitle="最新招考资讯与政策动态">
|
<AppPageHeader :title="isMobile ? '要闻公告' : '要闻'" subtitle="最新招考资讯与政策动态" sticky-mobile>
|
||||||
<AppButton variant="ghost" @click="openImport">订阅推送</AppButton>
|
<AppButton variant="ghost" @click="openImport">订阅推送</AppButton>
|
||||||
<AppButton variant="primary" @click="openImport">
|
<AppButton variant="primary" @click="openImport">
|
||||||
<AppIcon name="inbox" :size="16" />发布公告
|
<AppIcon name="inbox" :size="16" />发布公告
|
||||||
|
|||||||
@ -4,8 +4,7 @@ import { plansApi } from '../../api'
|
|||||||
import { useRequest } from '../../composables/useRequest'
|
import { useRequest } from '../../composables/useRequest'
|
||||||
import { formatDateShort, formatMinutes, weekdayName } from '../../utils/format'
|
import { formatDateShort, formatMinutes, weekdayName } from '../../utils/format'
|
||||||
import AppCard from '../../components/base/AppCard.vue'
|
import AppCard from '../../components/base/AppCard.vue'
|
||||||
import AppIcon from '../../components/base/AppIcon.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppButton from '../../components/base/AppButton.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 AppEmpty from '../../components/feedback/AppEmpty.vue'
|
import AppEmpty from '../../components/feedback/AppEmpty.vue'
|
||||||
@ -45,13 +44,10 @@ const weakTone = ['danger', 'warning', 'primary']
|
|||||||
|
|
||||||
<template v-else-if="data">
|
<template v-else-if="data">
|
||||||
<!-- 页头 -->
|
<!-- 页头 -->
|
||||||
<header class="review__head">
|
<AppPageHeader back sticky-mobile>
|
||||||
<AppButton class="review__back" variant="ghost" size="md" aria-label="返回" @click="$router.back()">
|
<template #title><h1 class="review__title">周报复盘</h1></template>
|
||||||
<AppIcon name="chevron-left" :size="20" />
|
|
||||||
</AppButton>
|
|
||||||
<h1>周报复盘</h1>
|
|
||||||
<span class="review__export">本周小结</span>
|
<span class="review__export">本周小结</span>
|
||||||
</header>
|
</AppPageHeader>
|
||||||
|
|
||||||
<!-- 周区间 + 汇总 -->
|
<!-- 周区间 + 汇总 -->
|
||||||
<section class="review-hero">
|
<section class="review-hero">
|
||||||
@ -120,32 +116,13 @@ const weakTone = ['danger', 'warning', 'primary']
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-5);
|
gap: var(--space-5);
|
||||||
}
|
}
|
||||||
.review__head {
|
.review__title {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
.review__back {
|
|
||||||
display: grid !important;
|
|
||||||
place-items: center;
|
|
||||||
width: 36px !important;
|
|
||||||
height: 36px !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
border: none !important;
|
|
||||||
border-radius: 50% !important;
|
|
||||||
background: var(--bg-card) !important;
|
|
||||||
color: var(--text-primary) !important;
|
|
||||||
gap: 0 !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.review__head h1 {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: var(--fs-h1);
|
font-size: var(--fs-h1);
|
||||||
font-weight: var(--fw-h1);
|
font-weight: var(--fw-h1);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
.review__export {
|
.review__export {
|
||||||
margin-left: auto;
|
|
||||||
font-size: var(--fs-label);
|
font-size: var(--fs-label);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -234,7 +234,7 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
|
|||||||
|
|
||||||
<!-- ============ 桌面端备考计划 ============ -->
|
<!-- ============ 桌面端备考计划 ============ -->
|
||||||
<div v-else-if="todayData" class="plan-desktop">
|
<div v-else-if="todayData" class="plan-desktop">
|
||||||
<AppPageHeader title="备考计划" :subtitle="`安排阶段任务,稳步推进备考进度 · 备考第 ${profileDays} 天`">
|
<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" />周报复盘
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import AppCard from '../../components/base/AppCard.vue'
|
|||||||
import AppButton from '../../components/base/AppButton.vue'
|
import AppButton from '../../components/base/AppButton.vue'
|
||||||
import AppIcon from '../../components/base/AppIcon.vue'
|
import AppIcon from '../../components/base/AppIcon.vue'
|
||||||
import AppBadge from '../../components/base/AppBadge.vue'
|
import AppBadge from '../../components/base/AppBadge.vue'
|
||||||
|
import AppPageHeader from '../../components/base/AppPageHeader.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'
|
||||||
|
|
||||||
@ -68,15 +69,11 @@ const mistakeText = computed(() => {
|
|||||||
|
|
||||||
<template v-else-if="explain">
|
<template v-else-if="explain">
|
||||||
<!-- 顶栏 -->
|
<!-- 顶栏 -->
|
||||||
<header class="ai-explain__top">
|
<AppPageHeader :title="title" size="detail" back>
|
||||||
<button type="button" class="ai-explain__back" @click="back">
|
|
||||||
<AppIcon name="chevron-left" :size="20" />
|
|
||||||
</button>
|
|
||||||
<h1 class="ai-explain__title">{{ title }}</h1>
|
|
||||||
<AppBadge :variant="isAi ? 'primary' : 'soft'">
|
<AppBadge :variant="isAi ? 'primary' : 'soft'">
|
||||||
{{ isAi ? 'AI 生成' : '题库解析回退' }}
|
{{ isAi ? 'AI 生成' : '题库解析回退' }}
|
||||||
</AppBadge>
|
</AppBadge>
|
||||||
</header>
|
</AppPageHeader>
|
||||||
|
|
||||||
<!-- 结论 -->
|
<!-- 结论 -->
|
||||||
<AppCard title="讲解结论" icon="sparkles">
|
<AppCard title="讲解结论" icon="sparkles">
|
||||||
@ -131,30 +128,6 @@ const mistakeText = computed(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-5);
|
gap: var(--space-5);
|
||||||
}
|
}
|
||||||
.ai-explain__top {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
.ai-explain__back {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
flex: none;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--bg-card);
|
|
||||||
border: 1px solid var(--border-subtle);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.ai-explain__title {
|
|
||||||
flex: 1;
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--fs-h2);
|
|
||||||
font-weight: var(--fw-h2);
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
.ai-explain__summary {
|
.ai-explain__summary {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: var(--fs-body);
|
font-size: var(--fs-body);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { reviewApi, type ReviewDetail } from '../../api'
|
|||||||
import AppButton from '../../components/base/AppButton.vue'
|
import AppButton from '../../components/base/AppButton.vue'
|
||||||
import AppBadge from '../../components/base/AppBadge.vue'
|
import AppBadge from '../../components/base/AppBadge.vue'
|
||||||
import AppIcon from '../../components/base/AppIcon.vue'
|
import AppIcon from '../../components/base/AppIcon.vue'
|
||||||
|
import AppPageHeader from '../../components/base/AppPageHeader.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 { formatDateShort, formatDateWeekday } from '../../utils/format'
|
||||||
@ -75,10 +76,6 @@ async function markMastered() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function back() {
|
|
||||||
router.back()
|
|
||||||
}
|
|
||||||
|
|
||||||
const statusLabel = computed(() =>
|
const statusLabel = computed(() =>
|
||||||
assessResult.value
|
assessResult.value
|
||||||
? assessResult.value.status === 'mastered'
|
? assessResult.value.status === 'mastered'
|
||||||
@ -119,15 +116,9 @@ function goAiExplain() {
|
|||||||
|
|
||||||
<template v-else-if="detail">
|
<template v-else-if="detail">
|
||||||
<!-- 顶栏:返回 + 标题 + 状态 -->
|
<!-- 顶栏:返回 + 标题 + 状态 -->
|
||||||
<header class="wd__top">
|
<AppPageHeader title="错题详情" size="detail" back>
|
||||||
<button type="button" class="wd__back" @click="back">
|
<AppBadge :variant="statusMeta.variant">{{ statusLabel }}</AppBadge>
|
||||||
<AppIcon name="chevron-left" :size="20" />
|
</AppPageHeader>
|
||||||
</button>
|
|
||||||
<h1 class="wd__title">错题详情</h1>
|
|
||||||
<span class="wd__status-badge">
|
|
||||||
<AppBadge :variant="statusMeta.variant">{{ statusLabel }}</AppBadge>
|
|
||||||
</span>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- 题目卡片 -->
|
<!-- 题目卡片 -->
|
||||||
<section class="wd__question">
|
<section class="wd__question">
|
||||||
@ -255,34 +246,6 @@ function goAiExplain() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 顶栏 */
|
/* 顶栏 */
|
||||||
.wd__top {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
.wd__back {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
flex: none;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--bg-card);
|
|
||||||
border: 1px solid var(--border-subtle);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.wd__title {
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--fs-h3);
|
|
||||||
font-weight: var(--fw-h1);
|
|
||||||
color: var(--text-primary);
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.wd__status-badge {
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 题目 */
|
/* 题目 */
|
||||||
.wd__question {
|
.wd__question {
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
|
|||||||
@ -168,7 +168,7 @@ function editProfile() {
|
|||||||
|
|
||||||
<!-- ============ 桌面端我的 ============ -->
|
<!-- ============ 桌面端我的 ============ -->
|
||||||
<div v-else-if="d" class="profile-desktop">
|
<div v-else-if="d" class="profile-desktop">
|
||||||
<AppPageHeader title="我的" subtitle="管理个人资料与备考偏好">
|
<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" />编辑资料
|
||||||
</AppButton>
|
</AppButton>
|
||||||
|
|||||||
@ -326,7 +326,7 @@ function markAnswer(key: OptionKey) {
|
|||||||
<div class="questions">
|
<div class="questions">
|
||||||
<!-- ======================= 手动录入模式 ======================= -->
|
<!-- ======================= 手动录入模式 ======================= -->
|
||||||
<template v-if="mode === 'entry'">
|
<template v-if="mode === 'entry'">
|
||||||
<AppPageHeader title="题库管理" subtitle="手动录入题目 · 支持逐题与批量 JSON 导入">
|
<AppPageHeader title="题库管理" subtitle="手动录入题目 · 支持逐题与批量 JSON 导入" sticky-mobile>
|
||||||
<AppButton variant="ghost" @click="mode = 'list'">
|
<AppButton variant="ghost" @click="mode = 'list'">
|
||||||
<AppIcon name="chevron-left" :size="16" />返回列表
|
<AppIcon name="chevron-left" :size="16" />返回列表
|
||||||
</AppButton>
|
</AppButton>
|
||||||
@ -454,7 +454,7 @@ function markAnswer(key: OptionKey) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<AppPageHeader title="题库管理" subtitle="手动录入题目 · 支持逐题与批量 JSON 导入">
|
<AppPageHeader title="题库管理" subtitle="手动录入题目 · 支持逐题与批量 JSON 导入" sticky-mobile>
|
||||||
<AppButton variant="secondary" @click="openImport">
|
<AppButton variant="secondary" @click="openImport">
|
||||||
<AppIcon name="inbox" :size="16" />导入真题
|
<AppIcon name="inbox" :size="16" />导入真题
|
||||||
</AppButton>
|
</AppButton>
|
||||||
|
|||||||
@ -96,7 +96,7 @@ async function save() {
|
|||||||
|
|
||||||
<template v-else-if="settings.data.value">
|
<template v-else-if="settings.data.value">
|
||||||
<!-- 页头 -->
|
<!-- 页头 -->
|
||||||
<AppPageHeader title="设置" subtitle="调整偏好、通知与数据策略" stack-mobile>
|
<AppPageHeader title="设置" subtitle="调整偏好、通知与数据策略" stack-mobile sticky-mobile>
|
||||||
<AppButton variant="primary" :disabled="saving" @click="save">
|
<AppButton variant="primary" :disabled="saving" @click="save">
|
||||||
<AppIcon name="check" :size="16" />{{ saving ? '保存中…' : '保存设置' }}
|
<AppIcon name="check" :size="16" />{{ saving ? '保存中…' : '保存设置' }}
|
||||||
</AppButton>
|
</AppButton>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user