第 4 篇:布局骨架——页面壳、栅格、卡片

第 4 篇:布局骨架------页面壳、栅格、卡片

本篇目标

把「统一布局」做成几个固定的布局组件。之后每个页面 = 布局骨架 + 一份 schema,

AI 不碰布局,布局由这几个组件锁定。

1. 布局拆成四个固定件

组件 职责 放哪
PageShell 整个页面的外层:内边距、最大宽度、纵向节奏 src/components/layout/PageShell.vue
PageHeader 页头:标题 + 右侧操作区 src/components/layout/PageHeader.vue
SectionCard 内容卡片:白底、圆角、可带标题 src/components/layout/SectionCard.vue
FormGrid 表单单栅格:字段按列排布、自动换行 src/components/layout/FormGrid.vue

这四个组件是本系列自研的,代码很短、可自由改。布局规则全部内聚在这里,

后续所有页面共用,等于「布局写死」的落地实现。

2. 逐个实现

PageShell.vue

vue 复制代码
<script setup lang="ts">
defineProps<{ maxWidth?: string }>()
</script>

<template>
  <div class="min-h-screen bg-background">
    <main
      class="mx-auto w-full px-[var(--spacing-page)] py-[var(--spacing-page)]"
      :style="{ maxWidth: maxWidth ?? '960px' }"
    >
      <slot />
    </main>
  </div>
</template>

PageHeader.vue

vue 复制代码
<script setup lang="ts">
defineProps<{ title: string; description?: string }>()
</script>

<template>
  <div class="mb-[var(--spacing-page)] flex flex-wrap items-center justify-between gap-4">
    <div>
      <h1 class="text-[var(--text-title)] font-semibold text-foreground">{{ title }}</h1>
      <p v-if="description" class="mt-1 text-[var(--text-caption)] text-muted-foreground">
        {{ description }}
      </p>
    </div>
    <div class="flex items-center gap-2"><slot name="actions" /></div>
  </div>
</template>

SectionCard.vue

vue 复制代码
<script setup lang="ts">
defineProps<{ title?: string }>()
</script>

<template>
  <section class="rounded-[var(--radius-lg)] border border-border bg-card p-[var(--spacing-card)]">
    <h2 v-if="title" class="mb-4 text-[var(--text-body)] font-semibold text-foreground">
      {{ title }}
    </h2>
    <slot />
  </section>
</template>

FormGrid.vue

vue 复制代码
<script setup lang="ts">
import { computed } from 'vue'

const props = defineProps<{ columns?: number }>()
// 每格最小宽度,窄屏自动换行成单列
const style = computed(() => ({
  gridTemplateColumns: `repeat(${props.columns ?? 2}, minmax(0, 1fr))`,
}))
</script>

<template>
  <div class="grid gap-[var(--spacing-field)]" :style="style">
    <slot />
  </div>
</template>

说明:columns 默认 2 列。移动端如果想强制单列,可在 FormGrid 内加一条媒体查询,

或用 Tailwind 的响应式类自行调整(本系列保持简单,字段多时第 6 篇的 span 属性可跨列)。

3. 组合成一个「固定壳」示例

vue 复制代码
<script setup lang="ts">
import PageShell from '@/components/layout/PageShell.vue'
import PageHeader from '@/components/layout/PageHeader.vue'
import SectionCard from '@/components/layout/SectionCard.vue'
import FormGrid from '@/components/layout/FormGrid.vue'
import { Button } from '@/components/ui/button'
</script>

<template>
  <PageShell>
    <PageHeader title="客户资料" description="统一样式的表单页示例">
      <template #actions><Button>保存</Button></template>
    </PageHeader>

    <SectionCard title="基本信息">
      <FormGrid :columns="2">
        <div>(这里放第 6 篇的 SchemaForm 渲染结果)</div>
      </FormGrid>
    </SectionCard>
  </PageShell>
</template>

4. 为什么这能锁死布局

  • 页边距、最大宽度、卡片圆角、字段间距,全部在四个组件里写死
  • 新页面只需「用这四个组件拼壳 + 塞 schema」,布局不可能歪
  • AI 无论生成多少页,都进同一个壳

5. 验证

  1. 复制上面代码,跑 pnpm dev,确认标题 / 卡片 / 操作区位置正常。
  2. FormGridcolumns 改成 3,看是否自动变三列;缩窄浏览器窗口到手机宽度,确认能换行。
  3. 修改 --spacing-page(第 3 篇变量),整页留白跟着变 → 布局与 Tokens 联动成功。

本篇小结

  • 布局被收敛成 PageShell / PageHeader / SectionCard / FormGrid 四个组件
  • 所有页面共用,布局固定
  • 下一步定义「每次变」的那部分------第 5 篇:Schema 设计
相关推荐
2501_931819701 小时前
车载语音青岛方言数据集搭建阶段 信实翻译垂直场景标准赋能青岛方言标注公司作业
人工智能·语音识别
jay神1 小时前
深度学习如何确定Batch Size的大小?
人工智能·深度学习·计算机视觉·毕业设计·课程设计·batch
何以解忧,唯有..1 小时前
Vue 3 模板语法详解:从插值到指令的完整指南
前端·javascript·vue.js
invicinble1 小时前
【无标题】
前端
EasyGBS1 小时前
从接入到稳定播放:无插件直播H5视频流媒体播放器EasyPlayer.js如何撑起Web端流媒体
开发语言·前端·javascript
不可能片场1 小时前
nohup 之后进程还是死了:GUI 子进程的存活陷阱
前端·electron
Carl_奕然1 小时前
【智能体】Agent的四种设计模式之:React(2026最新版)
javascript·人工智能·python·react.js·设计模式·语言模型
名为沙丁鱼的猫7291 小时前
React/JSX 编码规范
前端·javascript·react.js