第 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. 把 FormGrid 的 columns 改成 3,看是否自动变三列;缩窄浏览器窗口到手机宽度,确认能换行。
  3. 修改 --spacing-page(第 3 篇变量),整页留白跟着变 → 布局与 Tokens 联动成功。

本篇小结

  • 布局被收敛成 PageShell / PageHeader / SectionCard / FormGrid 四个组件
  • 所有页面共用,布局固定
  • 下一步定义「每次变」的那部分------第 5 篇:Schema 设计
相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
AI的探索之旅4 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A4 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄4 天前
JavaScript 隐式全局变量解析
javascript