Compare commits
4 Commits
6cfb3b2f93
...
cf3e877347
| Author | SHA1 | Date | |
|---|---|---|---|
| cf3e877347 | |||
| 8d33e3e4eb | |||
| ee2991b4b7 | |||
| bf5e826bbe |
@ -1,2 +1,2 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>备考通</title></head><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body></html>
|
<html lang="zh-CN"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>备考通</title><link rel="icon" href="data:,"/></head><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body></html>
|
||||||
|
|||||||
@ -3,27 +3,17 @@ import { computed } from 'vue'
|
|||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
/** 主轴方向;默认纵向 */
|
/** 单行横向 / 单列纵向;默认纵向。响应式由使用处自行决定传 row 还是 column */
|
||||||
direction?: 'row' | 'column'
|
direction?: 'row' | 'column'
|
||||||
/**
|
/**
|
||||||
* 子项间距。兼容三种写法:
|
* 子项间距:token 名(space-3…)或纯数字字符串('12' → 12px)。
|
||||||
* - token 名:space-3 / space-4 / space-5 …
|
* 缺省:纵向 space-5(20px)、横向 space-4(16px)。
|
||||||
* - 纯数字字符串:'12' → 12px
|
|
||||||
* - 其它 CSS 值:'12px 16px'、'var(--space-3)' 等原样透传
|
|
||||||
* 缺省:纵向 space-5(20px),横向 space-4(16px)
|
|
||||||
*/
|
*/
|
||||||
gap?: string
|
gap?: string
|
||||||
/** 交叉轴对齐 */
|
|
||||||
align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline'
|
|
||||||
/** 主轴对齐 */
|
|
||||||
justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'
|
|
||||||
/** 是否允许换行(横向时使用) */
|
|
||||||
wrap?: boolean
|
|
||||||
}>(),
|
}>(),
|
||||||
{ direction: 'column', gap: '', align: 'stretch', justify: 'start', wrap: false }
|
{ direction: 'column', gap: '' }
|
||||||
)
|
)
|
||||||
|
|
||||||
/** token 名 / 纯数字 → 兼容的 CSS gap;其它原样透传 */
|
|
||||||
const gapStyle = computed(() => {
|
const gapStyle = computed(() => {
|
||||||
const raw = props.gap.trim()
|
const raw = props.gap.trim()
|
||||||
if (!raw) return props.direction === 'row' ? 'var(--space-4)' : 'var(--space-5)'
|
if (!raw) return props.direction === 'row' ? 'var(--space-4)' : 'var(--space-5)'
|
||||||
@ -36,12 +26,7 @@ const gapStyle = computed(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="app-stack"
|
class="app-stack"
|
||||||
:class="{
|
:class="{ 'app-stack--row': direction === 'row' }"
|
||||||
'app-stack--row': direction === 'row',
|
|
||||||
'app-stack--wrap': wrap,
|
|
||||||
[`app-stack--align-${align}`]: align !== 'stretch',
|
|
||||||
[`app-stack--justify-${justify}`]: justify !== 'start'
|
|
||||||
}"
|
|
||||||
:style="{ '--stack-gap': gapStyle }"
|
:style="{ '--stack-gap': gapStyle }"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
@ -49,45 +34,17 @@ const gapStyle = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 默认样式:纵向、stretch、start;特殊布局由使用页绑 class 覆盖 */
|
||||||
.app-stack {
|
.app-stack {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
/* 默认 stretch/start;特殊行由使用页 class 设 --stack-align / --stack-justify 覆写 */
|
||||||
justify-content: flex-start;
|
align-items: var(--stack-align, stretch);
|
||||||
|
justify-content: var(--stack-justify, flex-start);
|
||||||
gap: var(--stack-gap, var(--space-5));
|
gap: var(--stack-gap, var(--space-5));
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.app-stack--row {
|
.app-stack--row {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
.app-stack--wrap {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.app-stack--align-center {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.app-stack--align-start {
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
.app-stack--align-end {
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
.app-stack--align-baseline {
|
|
||||||
align-items: baseline;
|
|
||||||
}
|
|
||||||
.app-stack--justify-center {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.app-stack--justify-end {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
.app-stack--justify-between {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.app-stack--justify-around {
|
|
||||||
justify-content: space-around;
|
|
||||||
}
|
|
||||||
.app-stack--justify-evenly {
|
|
||||||
justify-content: space-evenly;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useProfileStore } from '../../stores/profile'
|
|||||||
import { formatDateShort, formatMinutes, greetingByHour } from '../../utils/format'
|
import { formatDateShort, formatMinutes, greetingByHour } from '../../utils/format'
|
||||||
import AppCard from '../../components/base/AppCard.vue'
|
import AppCard from '../../components/base/AppCard.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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 AppBadge from '../../components/base/AppBadge.vue'
|
import AppBadge from '../../components/base/AppBadge.vue'
|
||||||
@ -73,7 +74,7 @@ const mockDelta = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard">
|
<AppStack class="dashboard">
|
||||||
<!-- 加载 / 错误 / 全局空态 -->
|
<!-- 加载 / 错误 / 全局空态 -->
|
||||||
<AppLoading v-if="overview.loading.value" fullscreen />
|
<AppLoading v-if="overview.loading.value" fullscreen />
|
||||||
<AppError
|
<AppError
|
||||||
@ -233,15 +234,10 @@ const mockDelta = computed(() => {
|
|||||||
<WeakPointsCard :weak-points="weakItems" compact />
|
<WeakPointsCard :weak-points="weakItems" compact />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dashboard {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
.num {
|
.num {
|
||||||
font-family: var(--font-num);
|
font-family: var(--font-num);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import AppButton from '../../components/base/AppButton.vue'
|
|||||||
import AppIcon from '../../components/base/AppIcon.vue'
|
import AppIcon from '../../components/base/AppIcon.vue'
|
||||||
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.vue'
|
||||||
import AppError from '../../components/feedback/AppError.vue'
|
import AppError from '../../components/feedback/AppError.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -129,7 +130,7 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mock-form">
|
<AppStack class="mock-form">
|
||||||
<AppPageHeader :title="title" subtitle="仅支持行测五个模块分数,总分自动汇总" sticky-mobile>
|
<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" />返回模考分析
|
||||||
@ -211,15 +212,10 @@ watch(
|
|||||||
</AppButton>
|
</AppButton>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.mock-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
.mock-form__card {
|
.mock-form__card {
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,7 +120,12 @@ async function confirmRemove() {
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 最近成绩 / 目标分差 / 较上次变化 -->
|
<!-- 最近成绩 / 目标分差 / 较上次变化 -->
|
||||||
<section class="mock-page__stats">
|
<AppStack
|
||||||
|
class="mock-page__stats"
|
||||||
|
:class="{ 'is-col': isMobile }"
|
||||||
|
:direction="isMobile ? 'column' : 'row'"
|
||||||
|
gap="space-4"
|
||||||
|
>
|
||||||
<StatCard
|
<StatCard
|
||||||
label="最近成绩"
|
label="最近成绩"
|
||||||
:value="`${latest?.total ?? 0} 分`"
|
:value="`${latest?.total ?? 0} 分`"
|
||||||
@ -143,7 +148,7 @@ async function confirmRemove() {
|
|||||||
:tone="deltaTone"
|
:tone="deltaTone"
|
||||||
:sub-tone="deltaTone"
|
:sub-tone="deltaTone"
|
||||||
/>
|
/>
|
||||||
</section>
|
</AppStack>
|
||||||
|
|
||||||
<!-- 成绩趋势 -->
|
<!-- 成绩趋势 -->
|
||||||
<AppCard title="行测成绩趋势" icon="chart">
|
<AppCard title="行测成绩趋势" icon="chart">
|
||||||
@ -158,10 +163,10 @@ async function confirmRemove() {
|
|||||||
<AppCard title="模块均分对比" icon="chart">
|
<AppCard title="模块均分对比" icon="chart">
|
||||||
<ul class="mock-page__modules">
|
<ul class="mock-page__modules">
|
||||||
<li v-for="m in moduleAverages" :key="m.module" class="mock-page__module">
|
<li v-for="m in moduleAverages" :key="m.module" class="mock-page__module">
|
||||||
<div class="mock-page__module-head">
|
<AppStack direction="row" class="mock-page__module-head">
|
||||||
<span class="mock-page__module-name">{{ m.module }}</span>
|
<span class="mock-page__module-name">{{ m.module }}</span>
|
||||||
<strong class="mock-page__module-value">{{ m.average }}<small> 分</small></strong>
|
<strong class="mock-page__module-value">{{ m.average }}<small> 分</small></strong>
|
||||||
</div>
|
</AppStack>
|
||||||
<div class="mock-page__module-track">
|
<div class="mock-page__module-track">
|
||||||
<span class="mock-page__module-fill" :style="{ width: `${Math.min(100, m.average)}%` }" />
|
<span class="mock-page__module-fill" :style="{ width: `${Math.min(100, m.average)}%` }" />
|
||||||
</div>
|
</div>
|
||||||
@ -186,14 +191,14 @@ async function confirmRemove() {
|
|||||||
</div>
|
</div>
|
||||||
<p v-if="r.note" class="mock-page__record-note">{{ r.note }}</p>
|
<p v-if="r.note" class="mock-page__record-note">{{ r.note }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mock-page__record-actions">
|
<AppStack direction="row" gap="space-1" class="mock-page__record-actions">
|
||||||
<AppButton variant="ghost" size="md" @click="goEdit(r)">
|
<AppButton variant="ghost" size="md" @click="goEdit(r)">
|
||||||
<AppIcon name="pen" :size="15" />编辑
|
<AppIcon name="pen" :size="15" />编辑
|
||||||
</AppButton>
|
</AppButton>
|
||||||
<AppButton variant="ghost" size="md" class="is-danger" @click="removing = r">
|
<AppButton variant="ghost" size="md" class="is-danger" @click="removing = r">
|
||||||
<AppIcon name="close" :size="15" />删除
|
<AppIcon name="close" :size="15" />删除
|
||||||
</AppButton>
|
</AppButton>
|
||||||
</div>
|
</AppStack>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
@ -206,22 +211,22 @@ async function confirmRemove() {
|
|||||||
确定要删除「{{ removing.name }}({{ formatDateShort(removing.date) }},{{ removing.total }} 分)」吗?删除后趋势与模块分析将同步更新,且不可恢复。
|
确定要删除「{{ removing.name }}({{ formatDateShort(removing.date) }},{{ removing.total }} 分)」吗?删除后趋势与模块分析将同步更新,且不可恢复。
|
||||||
</p>
|
</p>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="mock-page__confirm-actions">
|
<AppStack direction="row" gap="space-3" class="mock-page__confirm-actions">
|
||||||
<AppButton variant="ghost" @click="removing = null">取消</AppButton>
|
<AppButton variant="ghost" @click="removing = null">取消</AppButton>
|
||||||
<AppButton variant="danger" :loading="deleting" @click="confirmRemove">
|
<AppButton variant="danger" :loading="deleting" @click="confirmRemove">
|
||||||
{{ deleting ? '删除中…' : '确认删除' }}
|
{{ deleting ? '删除中…' : '确认删除' }}
|
||||||
</AppButton>
|
</AppButton>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
</AppModal>
|
</AppModal>
|
||||||
</AppStack>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.mock-page__stats {
|
/* PC 横向等宽(移动端 is-col 时自然撑满不限制高度) */
|
||||||
display: grid;
|
.mock-page__stats:not(.is-col) > :deep(*) {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
flex: 1 1 0;
|
||||||
gap: var(--space-4);
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.mock-page__sub {
|
.mock-page__sub {
|
||||||
margin: 0 0 var(--space-3);
|
margin: 0 0 var(--space-3);
|
||||||
@ -245,7 +250,6 @@ async function confirmRemove() {
|
|||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
.mock-page__module-head {
|
.mock-page__module-head {
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: var(--space-2);
|
margin-bottom: var(--space-2);
|
||||||
@ -335,8 +339,6 @@ async function confirmRemove() {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
.mock-page__record-actions {
|
.mock-page__record-actions {
|
||||||
display: flex;
|
|
||||||
gap: var(--space-1);
|
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
.mock-page__record-actions .is-danger {
|
.mock-page__record-actions .is-danger {
|
||||||
@ -349,15 +351,10 @@ async function confirmRemove() {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
.mock-page__confirm-actions {
|
.mock-page__confirm-actions {
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.mock-page__stats {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
.mock-page__record {
|
.mock-page__record {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useRequest } from '../../composables/useRequest'
|
|||||||
import { renderMarkdown } from '../../utils/markdown'
|
import { renderMarkdown } from '../../utils/markdown'
|
||||||
import { formatDateShort } from '../../utils/format'
|
import { formatDateShort } from '../../utils/format'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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 AppPageHeader from '../../components/base/AppPageHeader.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
@ -19,7 +20,7 @@ const html = computed(() => renderMarkdown(detail.data.value?.content ?? ''))
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="news-detail">
|
<AppStack class="news-detail" gap="0">
|
||||||
<AppLoading v-if="detail.loading.value" fullscreen />
|
<AppLoading v-if="detail.loading.value" fullscreen />
|
||||||
<AppError
|
<AppError
|
||||||
v-else-if="detail.error.value"
|
v-else-if="detail.error.value"
|
||||||
@ -49,14 +50,10 @@ const html = computed(() => renderMarkdown(detail.data.value?.content ?? ''))
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<AppEmpty v-else icon="news" title="该要闻不存在" description="可能已被删除或链接有误。" />
|
<AppEmpty v-else icon="news" title="该要闻不存在" description="可能已被删除或链接有误。" />
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.news-detail {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.news-detail__head {
|
.news-detail__head {
|
||||||
margin-bottom: var(--space-4);
|
margin-bottom: var(--space-4);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { formatDateShort, formatMinutes, weekdayName } from '../../utils/format'
|
|||||||
import AppCard from '../../components/base/AppCard.vue'
|
import AppCard from '../../components/base/AppCard.vue'
|
||||||
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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'
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ const weakTone = ['danger', 'warning', 'primary']
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="review">
|
<AppStack class="review">
|
||||||
<AppLoading v-if="review.loading.value" fullscreen />
|
<AppLoading v-if="review.loading.value" fullscreen />
|
||||||
<AppError
|
<AppError
|
||||||
v-else-if="review.error.value"
|
v-else-if="review.error.value"
|
||||||
@ -107,15 +108,10 @@ const weakTone = ['danger', 'warning', 'primary']
|
|||||||
</ul>
|
</ul>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.review {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
.review__title {
|
.review__title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: var(--fs-h1);
|
font-size: var(--fs-h1);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import AppIcon from '../../components/base/AppIcon.vue'
|
|||||||
import AppModal from '../../components/base/AppModal.vue'
|
import AppModal from '../../components/base/AppModal.vue'
|
||||||
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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'
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="plan">
|
<AppStack class="plan">
|
||||||
<AppLoading v-if="today.loading.value" fullscreen />
|
<AppLoading v-if="today.loading.value" fullscreen />
|
||||||
<AppError
|
<AppError
|
||||||
v-else-if="today.error.value"
|
v-else-if="today.error.value"
|
||||||
@ -359,15 +360,10 @@ const profileDays = computed(() => (examProfile.data.value ? daysSince(examProfi
|
|||||||
</AppButton>
|
</AppButton>
|
||||||
</template>
|
</template>
|
||||||
</AppModal>
|
</AppModal>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.plan {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
/* 指标卡 */
|
/* 指标卡 */
|
||||||
.plan-metrics {
|
.plan-metrics {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ 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 AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.vue'
|
||||||
import AppError from '../../components/feedback/AppError.vue'
|
import AppError from '../../components/feedback/AppError.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -63,7 +64,7 @@ const mistakeText = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="ai-explain">
|
<AppStack class="ai-explain">
|
||||||
<AppLoading v-if="loading" fullscreen text="正在生成讲解…" />
|
<AppLoading v-if="loading" fullscreen text="正在生成讲解…" />
|
||||||
<AppError v-else-if="error" fullscreen title="讲解加载失败" :message="error" retry @retry="load" />
|
<AppError v-else-if="error" fullscreen title="讲解加载失败" :message="error" retry @retry="load" />
|
||||||
|
|
||||||
@ -109,25 +110,20 @@ const mistakeText = computed(() => {
|
|||||||
|
|
||||||
<!-- 答案 -->
|
<!-- 答案 -->
|
||||||
<AppCard title="正确答案" icon="check">
|
<AppCard title="正确答案" icon="check">
|
||||||
<div class="ai-explain__answer">
|
<AppStack direction="row" gap="space-3" class="ai-explain__answer">
|
||||||
<strong class="ai-explain__answer-key">{{ explain.answer }}</strong>
|
<strong class="ai-explain__answer-key">{{ explain.answer }}</strong>
|
||||||
<span class="ai-explain__answer-label">本题答案为 {{ explain.answer }}</span>
|
<span class="ai-explain__answer-label">本题答案为 {{ explain.answer }}</span>
|
||||||
</div>
|
</AppStack>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
|
|
||||||
<AppButton v-if="!isMobile" variant="ghost" @click="back">
|
<AppButton v-if="!isMobile" variant="ghost" @click="back">
|
||||||
<AppIcon name="chevron-left" :size="16" />返回
|
<AppIcon name="chevron-left" :size="16" />返回
|
||||||
</AppButton>
|
</AppButton>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.ai-explain {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
.ai-explain__summary {
|
.ai-explain__summary {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: var(--fs-body);
|
font-size: var(--fs-body);
|
||||||
@ -205,9 +201,7 @@ const mistakeText = computed(() => {
|
|||||||
background: var(--warning);
|
background: var(--warning);
|
||||||
}
|
}
|
||||||
.ai-explain__answer {
|
.ai-explain__answer {
|
||||||
display: flex;
|
--stack-align: center;
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
}
|
||||||
.ai-explain__answer-key {
|
.ai-explain__answer-key {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ 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 AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.vue'
|
||||||
import AppError from '../../components/feedback/AppError.vue'
|
import AppError from '../../components/feedback/AppError.vue'
|
||||||
import { formatDuration } from '../../utils/format'
|
import { formatDuration } from '../../utils/format'
|
||||||
|
|
||||||
@ -155,7 +156,7 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="answer">
|
<AppStack class="answer">
|
||||||
<!-- ============ 整卷结果页 ============ -->
|
<!-- ============ 整卷结果页 ============ -->
|
||||||
<div v-if="finished && finishResult" class="result">
|
<div v-if="finished && finishResult" class="result">
|
||||||
<header class="result__head">
|
<header class="result__head">
|
||||||
@ -316,15 +317,10 @@ const finishAccuracy = computed(() => finishResult.value?.accuracy ?? 0)
|
|||||||
</AppButton>
|
</AppButton>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.answer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 顶部栏 */
|
/* 顶部栏 */
|
||||||
.answer__top {
|
.answer__top {
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
|||||||
import AppCard from '../../components/base/AppCard.vue'
|
import AppCard from '../../components/base/AppCard.vue'
|
||||||
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 AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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 { useToast } from '../../composables/useToast'
|
import { useToast } from '../../composables/useToast'
|
||||||
@ -74,7 +76,7 @@ function back() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="custom">
|
<AppStack class="custom">
|
||||||
<AppPageHeader title="自定义组卷" subtitle="从题库中勾选题目,组成一套专属练习">
|
<AppPageHeader title="自定义组卷" subtitle="从题库中勾选题目,组成一套专属练习">
|
||||||
<AppButton variant="ghost" icon="chevron-left" @click="back">返回</AppButton>
|
<AppButton variant="ghost" icon="chevron-left" @click="back">返回</AppButton>
|
||||||
</AppPageHeader>
|
</AppPageHeader>
|
||||||
@ -123,15 +125,10 @@ function back() {
|
|||||||
</AppButton>
|
</AppButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.custom {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
.custom__toolbar {
|
.custom__toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
import AppCard from '../../components/base/AppCard.vue'
|
import AppCard from '../../components/base/AppCard.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 AppStack from '../../components/base/AppStack.vue'
|
||||||
|
|
||||||
const QUESTION_TYPES = [
|
const QUESTION_TYPES = [
|
||||||
{ name: '归纳概括', desc: '概括给定资料的主要内容或问题' },
|
{ name: '归纳概括', desc: '概括给定资料的主要内容或问题' },
|
||||||
@ -13,7 +14,7 @@ const QUESTION_TYPES = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="essay">
|
<AppStack class="essay">
|
||||||
<!-- 功能说明 -->
|
<!-- 功能说明 -->
|
||||||
<AppCard title="申论功能说明" icon="book-open">
|
<AppCard title="申论功能说明" icon="book-open">
|
||||||
<p class="essay__intro">
|
<p class="essay__intro">
|
||||||
@ -53,15 +54,10 @@ const QUESTION_TYPES = [
|
|||||||
<p>当前为占位页面,不保存任何写作内容。建议先完成行测「刷题 → 错题复习 → 模考分析」闭环。</p>
|
<p>当前为占位页面,不保存任何写作内容。建议先完成行测「刷题 → 错题复习 → 模考分析」闭环。</p>
|
||||||
</div>
|
</div>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.essay {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
.essay__intro {
|
.essay__intro {
|
||||||
margin: 0 0 var(--space-4);
|
margin: 0 0 var(--space-4);
|
||||||
font-size: var(--fs-body);
|
font-size: var(--fs-body);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ 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 AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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 EssayView from './EssayView.vue'
|
import EssayView from './EssayView.vue'
|
||||||
@ -160,7 +161,7 @@ const subtitle = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="practice">
|
<AppStack class="practice">
|
||||||
<AppPageHeader title="刷题中心" :subtitle="subtitle" :stack-mobile="true">
|
<AppPageHeader title="刷题中心" :subtitle="subtitle" :stack-mobile="true">
|
||||||
<template v-if="tab === 'practice'">
|
<template v-if="tab === 'practice'">
|
||||||
<AppButton variant="secondary" icon="plus" @click="goCustom">自定义组卷</AppButton>
|
<AppButton variant="secondary" icon="plus" @click="goCustom">自定义组卷</AppButton>
|
||||||
@ -393,15 +394,10 @@ const subtitle = computed(() => {
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<EssayView />
|
<EssayView />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.practice {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 子 tab */
|
/* 子 tab */
|
||||||
.practice-tabs {
|
.practice-tabs {
|
||||||
|
|||||||
@ -188,11 +188,11 @@ function goAiExplain() {
|
|||||||
|
|
||||||
<!-- 自评结果反馈 -->
|
<!-- 自评结果反馈 -->
|
||||||
<section v-if="assessed && assessResult" class="wd__result">
|
<section v-if="assessed && assessResult" class="wd__result">
|
||||||
<div class="wd__result-banner" :class="assessResult.correct ? 'is-correct' : 'is-wrong'">
|
<AppStack direction="row" gap="space-2" class="wd__result-banner" :class="assessResult.correct ? 'is-correct' : 'is-wrong'">
|
||||||
<AppIcon :name="assessResult.correct ? 'check' : 'close'" :size="18" />
|
<AppIcon :name="assessResult.correct ? 'check' : 'close'" :size="18" />
|
||||||
<span>{{ assessResult.correct ? '已标记为记对' : '已标记为记错' }}</span>
|
<span>{{ assessResult.correct ? '已标记为记对' : '已标记为记错' }}</span>
|
||||||
</div>
|
</AppStack>
|
||||||
<div class="wd__result-meta">
|
<AppStack direction="row" gap="space-4" class="wd__result-meta">
|
||||||
<div class="wd__result-stat">
|
<div class="wd__result-stat">
|
||||||
<strong>已复习 {{ assessResult.reviewCount }} 次</strong>
|
<strong>已复习 {{ assessResult.reviewCount }} 次</strong>
|
||||||
<span>下次复习</span>
|
<span>下次复习</span>
|
||||||
@ -201,7 +201,7 @@ function goAiExplain() {
|
|||||||
<strong>{{ assessResult.status === 'mastered' ? '已掌握' : assessResult.nextReviewAt }}</strong>
|
<strong>{{ assessResult.status === 'mastered' ? '已掌握' : assessResult.nextReviewAt }}</strong>
|
||||||
<span>{{ assessResult.status === 'mastered' ? '状态' : '下次复习' }}</span>
|
<span>{{ assessResult.status === 'mastered' ? '状态' : '下次复习' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppStack>
|
||||||
<AppButton block size="lg" @click="backToList">返回错题本</AppButton>
|
<AppButton block size="lg" @click="backToList">返回错题本</AppButton>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -216,20 +216,20 @@ function goAiExplain() {
|
|||||||
<section v-if="detail.history.length > 0" class="wd__history">
|
<section v-if="detail.history.length > 0" class="wd__history">
|
||||||
<h2 class="wd__history-title">作答历史</h2>
|
<h2 class="wd__history-title">作答历史</h2>
|
||||||
<div class="wd__history-list">
|
<div class="wd__history-list">
|
||||||
<div v-for="(h, i) in detail.history" :key="i" class="wd__history-item">
|
<AppStack v-for="(h, i) in detail.history" :key="i" direction="row" gap="space-3" class="wd__history-item">
|
||||||
<span class="wd__history-icon" :class="h.correct ? 'is-correct' : 'is-wrong'">
|
<span class="wd__history-icon" :class="h.correct ? 'is-correct' : 'is-wrong'">
|
||||||
<AppIcon :name="h.correct ? 'check' : 'close'" :size="14" />
|
<AppIcon :name="h.correct ? 'check' : 'close'" :size="14" />
|
||||||
</span>
|
</span>
|
||||||
<div class="wd__history-body">
|
<div class="wd__history-body">
|
||||||
<div class="wd__history-main">
|
<AppStack direction="row" gap="space-2" class="wd__history-main">
|
||||||
<span>{{ formatDateWeekday(h.date) }}</span>
|
<span>{{ formatDateWeekday(h.date) }}</span>
|
||||||
<span class="wd__history-badge" :class="h.correct ? 'is-correct' : 'is-wrong'">
|
<span class="wd__history-badge" :class="h.correct ? 'is-correct' : 'is-wrong'">
|
||||||
{{ h.correct ? '答对' : `答错(选了 ${h.userAnswer})` }}
|
{{ h.correct ? '答对' : `答错(选了 ${h.userAnswer})` }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</AppStack>
|
||||||
<div v-if="h.wrongReason" class="wd__history-reason">错因:{{ h.wrongReason }}</div>
|
<div v-if="h.wrongReason" class="wd__history-reason">错因:{{ h.wrongReason }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppStack>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@ -407,9 +407,7 @@ function goAiExplain() {
|
|||||||
padding: var(--card-padding-desktop);
|
padding: var(--card-padding-desktop);
|
||||||
}
|
}
|
||||||
.wd__result-banner {
|
.wd__result-banner {
|
||||||
display: flex;
|
--stack-align: center;
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
padding: var(--space-3) var(--space-4);
|
padding: var(--space-3) var(--space-4);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
color: var(--on-accent);
|
color: var(--on-accent);
|
||||||
@ -421,10 +419,6 @@ function goAiExplain() {
|
|||||||
.wd__result-banner.is-wrong {
|
.wd__result-banner.is-wrong {
|
||||||
background: var(--danger);
|
background: var(--danger);
|
||||||
}
|
}
|
||||||
.wd__result-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
.wd__result-stat {
|
.wd__result-stat {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -464,9 +458,7 @@ function goAiExplain() {
|
|||||||
gap: var(--space-3);
|
gap: var(--space-3);
|
||||||
}
|
}
|
||||||
.wd__history-item {
|
.wd__history-item {
|
||||||
display: flex;
|
--stack-align: flex-start;
|
||||||
align-items: flex-start;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
}
|
||||||
.wd__history-icon {
|
.wd__history-icon {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -488,9 +480,7 @@ function goAiExplain() {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.wd__history-main {
|
.wd__history-main {
|
||||||
display: flex;
|
--stack-align: center;
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
font-size: var(--fs-subtitle);
|
font-size: var(--fs-subtitle);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import AppButton from '../../components/base/AppButton.vue'
|
|||||||
import AppIcon from '../../components/base/AppIcon.vue'
|
import AppIcon from '../../components/base/AppIcon.vue'
|
||||||
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.vue'
|
||||||
import AppError from '../../components/feedback/AppError.vue'
|
import AppError from '../../components/feedback/AppError.vue'
|
||||||
|
|
||||||
const { isMobile } = useResponsive()
|
const { isMobile } = useResponsive()
|
||||||
@ -73,7 +74,7 @@ function editProfile() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="profile">
|
<AppStack class="profile">
|
||||||
<AppLoading v-if="overview.loading.value" fullscreen />
|
<AppLoading v-if="overview.loading.value" fullscreen />
|
||||||
<AppError
|
<AppError
|
||||||
v-else-if="overview.error.value"
|
v-else-if="overview.error.value"
|
||||||
@ -243,15 +244,10 @@ function editProfile() {
|
|||||||
<RouterLink class="tip-banner__action" to="/plan/review">去加强</RouterLink>
|
<RouterLink class="tip-banner__action" to="/plan/review">去加强</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.profile {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 头像卡 */
|
/* 头像卡 */
|
||||||
.me-card {
|
.me-card {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import AppModal from '../../components/base/AppModal.vue'
|
|||||||
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
import AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppBadge from '../../components/base/AppBadge.vue'
|
import AppBadge from '../../components/base/AppBadge.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.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'
|
||||||
|
|
||||||
@ -323,7 +324,7 @@ function markAnswer(key: OptionKey) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="questions">
|
<AppStack class="questions" gap="space-4">
|
||||||
<!-- ======================= 手动录入模式 ======================= -->
|
<!-- ======================= 手动录入模式 ======================= -->
|
||||||
<template v-if="mode === 'entry'">
|
<template v-if="mode === 'entry'">
|
||||||
<AppPageHeader title="题库管理" subtitle="手动录入题目 · 支持逐题与批量 JSON 导入" sticky-mobile>
|
<AppPageHeader title="题库管理" subtitle="手动录入题目 · 支持逐题与批量 JSON 导入" sticky-mobile>
|
||||||
@ -627,15 +628,10 @@ function markAnswer(key: OptionKey) {
|
|||||||
</AppButton>
|
</AppButton>
|
||||||
</template>
|
</template>
|
||||||
</AppModal>
|
</AppModal>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.questions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------- 录入模式 ---------- */
|
/* ---------- 录入模式 ---------- */
|
||||||
.entry {
|
.entry {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ 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 AppPageHeader from '../../components/base/AppPageHeader.vue'
|
||||||
import AppLoading from '../../components/feedback/AppLoading.vue'
|
import AppLoading from '../../components/feedback/AppLoading.vue'
|
||||||
|
import AppStack from '../../components/base/AppStack.vue'
|
||||||
import AppError from '../../components/feedback/AppError.vue'
|
import AppError from '../../components/feedback/AppError.vue'
|
||||||
|
|
||||||
const { isMobile } = useResponsive()
|
const { isMobile } = useResponsive()
|
||||||
@ -83,7 +84,7 @@ async function save() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="settings">
|
<AppStack class="settings">
|
||||||
<AppLoading v-if="settings.loading.value" fullscreen />
|
<AppLoading v-if="settings.loading.value" fullscreen />
|
||||||
<AppError
|
<AppError
|
||||||
v-else-if="settings.error.value"
|
v-else-if="settings.error.value"
|
||||||
@ -199,15 +200,10 @@ async function save() {
|
|||||||
</div>
|
</div>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</AppStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.settings {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-row {
|
.setting-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user