Skip to content

页面布局 ​

搭界面要从外往里搭。先把页面的骨架和每一块占多大地方定下来,再往里填内容。反过来做,先写内容再补容器,最后每一段都会自己发明一个盒子,页面就变成一列层层包着的卡片。

下面四步,一步一步往里走。

  1. 选外壳:整页用 AppShell,还是页面里的一块用 Layout。
  2. 分区域:页眉、正文、页脚、侧边各放什么。
  3. 对齐:所有区域共用一条内容线。
  4. 调间距:先定间距,再决定要不要密度更紧的组件。

第一步:选外壳 ​

整页的外框交给 AppShell。它管顶栏、侧导航、横幅、正文,还会在窄屏把侧导航收进抽屉。

vue
<script setup lang="ts">
import { AppShell } from '@astryx-vue/core/AppShell'
import Nav from './Nav.vue'
import PageBody from './PageBody.vue'
// TopBar、Nav、PageBody 换成你自己的组件
import TopBar from './TopBar.vue'
</script>

<template>
  <AppShell :content-padding="0" :mobile-nav="{ breakpoint: 'md' }">
    <template #topNav>
      <TopBar />
    </template>
    <template #sideNav>
      <Nav />
    </template>
    <PageBody />
  </AppShell>
</template>

AppShell 的插槽有 topNav、sideNav、banner、默认的正文,以及窄屏抽屉用的 mobileNav。

contentPadding 是正文区的内边距,用间距刻度写。不传就贴边,由里面的 Layout 自己管边距——所以外壳和内层不要同时加内边距,不然会叠成两倍。

页面里的一块——一张卡片、一个对话框、Tab 里的一栏——用 Layout。Layout 不负责整页,它只在自己的范围里分区域。

第二步:分区域 ​

Layout 最多分五块,默认插槽是正文,其余是具名插槽:

插槽放什么
header这一块的标题和主操作
start起始边的侧栏或面板
默认插槽主要内容,通常包一层 LayoutContent
end结束边的侧栏或面板
footer提交、取消这类必须一直够得着的按钮
vue
<script setup lang="ts">
import { Button } from '@astryx-vue/core/Button'
import { Layout, LayoutContent, LayoutFooter, LayoutHeader } from '@astryx-vue/core/Layout'
</script>

<template>
  <Layout :padding="4" default-has-dividers>
    <template #header>
      <LayoutHeader>订单详情</LayoutHeader>
    </template>
    <LayoutContent>
      正文内容,超出时只在这一块里滚动。
    </LayoutContent>
    <template #footer>
      <LayoutFooter>
        <Button label="保存" />
      </LayoutFooter>
    </template>
  </Layout>
</template>

height 取 fill(默认)时,Layout 撑满容器,只有正文在中间滚动,页眉和页脚留在原地。取 auto 时随内容长高,整页一起滚。

页眉、页脚、正文都齐了以后,把它们之间的线交给 defaultHasDividers 一次开,不用每个区域各写一个 hasDivider。

第三步:对齐同一条内容线 ​

Layout 的 padding 只写一次,它落在所有区域上:页眉、正文、页脚、两侧面板的左右内边距用的是同一个值。所以它们的文字自然对齐在同一条竖线上。 这条线会被两种写法打断,都很好躲:

  • 在 LayoutContent 里再包一层带 padding 的 div,正文就比页眉往里缩了一截。
  • 在页眉里自己写 padding,页眉就比正文往外多了一截。

要缩进某一块,用那个区域组件自己的 padding,或者干脆用 HStack 加 gap,别在区域外面再套一层盒子。

正文太长不好读时,用 contentWidth 限制宽度:数字按像素,也可以写 '100%'、'40rem'。宽度收紧以后,正文在中间居中,上下的分隔线还是拉满整行。

第四步:排局部,调间距 ​

区域里再往下分,就该 HStack、VStack、Grid 出场了。它们只负责摆放,不画边框也不给底色。

vue
<script setup lang="ts">
import { Button } from '@astryx-vue/core/Button'
import { Card } from '@astryx-vue/core/Card'
import { Grid } from '@astryx-vue/core/Grid'
import { HStack, VStack } from '@astryx-vue/core/Stack'
// OrderCard 换成你自己的组件
import OrderCard from './OrderCard.vue'
</script>

<template>
  <!-- 一行:标题在左,按钮在右 -->
  <HStack justify="between" align="center" :gap="3">
    <span>最近的订单</span>
    <Button label="刷新" variant="secondary" />
  </HStack>

  <!-- 一列:每两块之间 16px -->
  <VStack :gap="4">
    <OrderCard />
    <OrderCard />
  </VStack>

  <!-- 自动增减列:每列至少 240px,最多 4 列 -->
  <Grid :columns="{ minWidth: 240, max: 4 }" :gap="3">
    <Card v-for="item in items" :key="item.id" />
  </Grid>
</template>

gap、padding 收的是间距刻度:2 就是 --astryx-spacing-2,也就是 8px。写刻度而不是写 16px,主题换了、根字号变了,间距会跟着一起动。

Grid 的 columns 写数字就是固定那么多列(日历那种一周七格适合这么写);写 { minWidth, max } 就是按容器宽度自动增减列数。

间距定完,再看密度。同样一块内容,可以调的不止间距:

  • 表格、列表这类一行一条的密集数据,用行和分隔线,不要每条包一张卡片。
  • Card 留给独立的小部件、图集和设置分组。
  • 侧栏、抽屉、卡片里放不下完整版式时,换成紧凑形态的组件(例如 EmptyState 的 isCompact)。

该做与不该做 ​

指引实践
推荐先定外壳和区域,再写内容。
推荐内边距只写一次,写在 Layout 或 AppShell 上。
推荐区域之间的边界用分隔线,不用留白硬撑。
避免每个区域各写一套 padding,文字线对不齐。
避免每一块内容都包一张 Card,页面变成一列盒子。
避免用 div 加内联 margin 代替 HStack、VStack、Grid。

接下来 ​