refactor: AppStack极简化为direction+gap,响应式方向由使用页传入

This commit is contained in:
liyy 2026-09-03 14:44:40 +08:00
parent 6cfb3b2f93
commit bf5e826bbe
2 changed files with 23 additions and 70 deletions

View File

@ -3,27 +3,17 @@ import { computed } from 'vue'
const props = withDefaults(
defineProps<{
/** 主轴方向;默认纵向 */
/** 单行横向 / 单列纵向;默认纵向。响应式由使用处自行决定传 row 还是 column */
direction?: 'row' | 'column'
/**
* 子项间距兼容三种写法
* - token space-3 / space-4 / space-5
* - 纯数字字符串'12' 12px
* - 其它 CSS '12px 16px''var(--space-3)' 等原样透传
* 缺省纵向 space-520px横向 space-416px
* 子项间距token space-3或纯数字字符串'12' 12px
* 缺省纵向 space-520px横向 space-416px
*/
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 raw = props.gap.trim()
if (!raw) return props.direction === 'row' ? 'var(--space-4)' : 'var(--space-5)'
@ -36,12 +26,7 @@ const gapStyle = computed(() => {
<template>
<div
class="app-stack"
:class="{
'app-stack--row': direction === 'row',
'app-stack--wrap': wrap,
[`app-stack--align-${align}`]: align !== 'stretch',
[`app-stack--justify-${justify}`]: justify !== 'start'
}"
:class="{ 'app-stack--row': direction === 'row' }"
:style="{ '--stack-gap': gapStyle }"
>
<slot />
@ -49,6 +34,7 @@ const gapStyle = computed(() => {
</template>
<style scoped>
/* 默认样式纵向、stretch、start特殊布局由使用页绑 class 覆盖 */
.app-stack {
display: flex;
flex-direction: column;
@ -60,34 +46,4 @@ const gapStyle = computed(() => {
.app-stack--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>

View File

@ -120,7 +120,12 @@ async function confirmRemove() {
<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
label="最近成绩"
:value="`${latest?.total ?? 0} 分`"
@ -143,7 +148,7 @@ async function confirmRemove() {
:tone="deltaTone"
:sub-tone="deltaTone"
/>
</section>
</AppStack>
<!-- 成绩趋势 -->
<AppCard title="行测成绩趋势" icon="chart">
@ -158,10 +163,10 @@ async function confirmRemove() {
<AppCard title="模块均分对比" icon="chart">
<ul class="mock-page__modules">
<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>
<strong class="mock-page__module-value">{{ m.average }}<small> </small></strong>
</div>
</AppStack>
<div class="mock-page__module-track">
<span class="mock-page__module-fill" :style="{ width: `${Math.min(100, m.average)}%` }" />
</div>
@ -186,14 +191,14 @@ async function confirmRemove() {
</div>
<p v-if="r.note" class="mock-page__record-note">{{ r.note }}</p>
</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)">
<AppIcon name="pen" :size="15" />编辑
</AppButton>
<AppButton variant="ghost" size="md" class="is-danger" @click="removing = r">
<AppIcon name="close" :size="15" />删除
</AppButton>
</div>
</AppStack>
</li>
</ul>
</AppCard>
@ -206,22 +211,22 @@ async function confirmRemove() {
确定要删除{{ removing.name }}{{ formatDateShort(removing.date) }}{{ removing.total }} 删除后趋势与模块分析将同步更新且不可恢复
</p>
<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="danger" :loading="deleting" @click="confirmRemove">
{{ deleting ? '删除中…' : '确认删除' }}
</AppButton>
</div>
</AppStack>
</template>
</AppModal>
</AppStack>
</template>
<style scoped>
.mock-page__stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
/* PC 横向等宽(移动端 is-col 时自然撑满不限制高度) */
.mock-page__stats:not(.is-col) > :deep(*) {
flex: 1 1 0;
min-width: 0;
}
.mock-page__sub {
margin: 0 0 var(--space-3);
@ -245,7 +250,6 @@ async function confirmRemove() {
gap: var(--space-4);
}
.mock-page__module-head {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: var(--space-2);
@ -335,8 +339,6 @@ async function confirmRemove() {
color: var(--text-muted);
}
.mock-page__record-actions {
display: flex;
gap: var(--space-1);
flex: none;
}
.mock-page__record-actions .is-danger {
@ -349,15 +351,10 @@ async function confirmRemove() {
color: var(--text-primary);
}
.mock-page__confirm-actions {
display: flex;
justify-content: flex-end;
gap: var(--space-3);
}
@media (max-width: 767px) {
.mock-page__stats {
grid-template-columns: 1fr;
}
.mock-page__record {
flex-direction: column;
}