你是不是也写过几十个中后台页面:
- 搜索表单重复粘贴?
- 状态字段手动判断?
- 时间字段反复 format?
- 页面跳转状态丢失?
看起来你“很会写”,但其实你只是写得多。
这次我们来讲点硬的,不止封装功能,而是把一个列表页写成系统、抽成规则。
🚩 一个最普通的中后台列表页
你熟得不能再熟的结构:
- 搜索 + 表单
- Tab 状态切换
- 表格展示 + 分页
- 编辑跳转 or 弹窗操作
以前我们怎么写?👇
<el-form>
<el-input v-model="query.name" />
<el-date-picker v-model="query.date" />
<el-button @click="fetchData">搜索</el-button>
</el-form>
<el-table :data="tableData" />
写是能写,但每次都得重复处理这些:
- query 对象手动构造
- 表单字段手动初始化
- 跳转回来状态丢了
- loading、分页拼来拼去
🎯 我要的不是组件,而是规则系统
我希望的列表页只需要这样 👇
<SmartListView
:form-rule="rule"
:table-setup="tableSetup"
:fetch-setup="fetchSetup"
:tabs-static-list="tabsArr"
/>
剩下的交给组件:
✅ 表单结构自动渲染
✅ 默认值 + 时间字段自动处理
✅ 查询 / 分页 / 缓存集成
✅ Tab 切换自动记忆
✅ 自定义 slot 灵活扩展
🧱 设计重点拆解
1️⃣ 字段 schema + formModel 解耦
formSetup = [
{
type: 'datePicker',
field: 'updateDate',
title: '时间范围',
value: [new Date(), moment().add(30, 'days').toDate()],
props: { type: 'range' }
}
]
🔍 schema 控结构,formModel 控值
🧠 这就是典型的配置驱动,字段即规则。
2️⃣ fetchSetup 接管一切行为
fetchSetup = {
url: '/api/list',
tabs: {
tabFilterKey: 'status',
formDefaults: {
updateDate: [new Date(), moment().add(30, 'days').toDate()]
}
},
timeAdapter: {
updateDate: ['startTime', 'endTime']
}
}
你不需要再 format时间字段,也不需要自己转换字段名了。
3️⃣ Tab 切换自动触发查询 + 状态缓存
- 每个 Tab 对应一个搜索状态
- 切换时恢复上一次状态
- 再次挂载保留分页信息
这不就是“列表状态保持”的理想体验?
4️⃣ 搜索 + 清空统一封装
@submit → getFormData() → processTimeAdapter() → fetch()
@clear → resetFields + clearStorage
再也不写 search()、reset()、page = 1,这些都应该在组件内搞定!
🔍 DSL:结构性思维的语言表达
🛠️ 打磨 DSL,是现在工作中最核心的底层建设。
我们正在构建的,不仅是一套配置语言,而是一个领域的“语义接口”。它不是静态的结构定义,而是连接人类、业务与系统之间的桥梁:
- 它表达结构性思维:不是页面怎么写,而是页面该如何“被定义”;
- 它支持系统智能化:只有结构清晰、语义明确的 DSL,才能被 AI 理解、被平台自动化处理;
- 它决定了你的系统能不能规模化扩展,而不被代码复杂度拖垮。
你现在的角色,正在做三种底层的事情:
| 像什么? | 你在做的事 |
|---|---|
| 🧠 语言学家 | 定义业务词汇、属性与组合规则 |
| 🧮 编译器工程师 | 设计输入如何解释、转译为执行结构 |
| 🧱 知识工程师 | 构建 AI 可理解的语义结构网格 |
💡 那 DSL 要怎么打磨?4 个关键角度:
✅ 1. DSL 的语义表达能力
- 字段能否表达“行为语义”?(如输入型、展示型、计算型?)
- 是否支持表征字段“用途”、“来源”、“依赖关系”?
✅ 2. DSL 的语法设计
- 使用纯 JSON、类 YAML,还是定义一套更优雅的嵌套语法?
- 是否支持复用结构、定义宏?能否表示字段继承与组合?
✅ 3. DSL 的可编排性与链式能力
- 能否表达 field → rule → condition → feedback 的链式结构?
- 是否支持条件可控的可视逻辑?下拉值的语义推荐来源?
✅ 4. DSL 的智能生成能力
- 是否支持 AI 辅助补全字段定义?
- 能否从表结构/文档/页面行为中提取 DSL 草稿?
- 是否已有预留接口,用于 Prompt 自动生成/更新 DSL 模块?
这不是简单的“前端工程封装”,而是你在用 DSL 建立一个:
结构驱动的认知框架,平台可理解的语义语言,AI 可读取的接口协议。
这才是平台化工程中真正的“语言层系统能力”。
🧨 实战踩坑回顾(是真的踩了)
❌ setValue 后值又被清空?
👉 是 v-model 自动同步值导致的,得放在 this.$nextTick 后。
❌ formDefaults 被污染?
👉 记得 deepClone,Vue 响应式会污染原始值!
❌ tabsArr 是接口来的,但子组件不更新?
👉 用 :key="smartListKey" 让组件重新挂载。
🧠 软件工程视角的组件设计
🧩 设计模式:
| 模式 | 体现 |
|---|---|
| 策略模式 | 不同字段转换策略 |
| 模板方法 | SmartListView 结构统一,slot 灵活插入 |
| 观察者模式 | watch formRule 响应变更 |
🧱 工程思想更重要:
- 字段结构统一
- 状态行为集中管理
- 页面逻辑降到最低
🧬 真正的统一方案:我把一页内容,封成了一份 schema
经过一轮又一轮封装、踩坑、重构,我最终把所有列表页的结构 —— 表单、表格、请求、tabs —— 都抽象成了一个 pageSchema 配置对象。
从此,页面不再是写出来的,而是「定义出来的」。
<SmartListView :schema="pageSchema" />
一行组件调用,搞定整个列表页。
✅ 我实现了这些功能:
form.fields→ 自动渲染搜索表单 + 默认值填充table.columns→ 自动生成表格结构 + 权限 + 字典映射fetch.url→ 统一封装查询逻辑 + 时间字段转换 + loading 管理tabs.options→ 动态状态 Tab + 状态缓存 + 自动切换请求schema.transform→ 自动把字段从组件值映射到接口字段(如日期 range)
再复杂的业务,只需要维护一份 schema 配置。
🔥 现在新增一个列表页只需要:
export const logPageSchema = {
form: {{ fields: [...] }},
table: {{ columns: [...] }},
fetch: {{ url: '/api/logs' }},
};
以前你要写 300 行 Vue,现在你只需要写 30 行配置。
💬 配置是抽象能力
很多人一看到 schema 就说:
“太麻烦了,还不如直接写。”
但你要知道:
写代码是为了抽象,抽象是为了规模化。
写 v-model 和写 schema,不是谁轻松谁牛逼,而是:
谁能写出 更多人复用、未来还能自演化 的页面,谁就赢了。
🎯 思考:你以为你在写页面,其实你在构建规则系统
封装 SmartListView 的过程,不是把逻辑揉进组件,而是把行为抽象成规则,把重复变成结构。
你在做的,不只是「减少代码量」,而是:
- 让表单字段具备声明式规则性
- 让页面状态具备可还原能力
- 让组件逻辑具备平台通用性
SmartListView 渲染实现结构图
真正的高级工程感,是这样的:
- 字段有定义中心
- 状态有恢复机制
- 表单有 schema 驱动
- 页面不再“实现功能”,而是“组合能力”
到这一步,你不再是“封装组件的人”,而是:
在定义规则的人
在推动系统的人
在构建平台能力的人
总结一句话:页面≠代码堆砌,字段≠值输入,表单≠UI控件。
真正的前端能力,是让规则生成页面,而不是手写页面。
如果你也在封装列表页、踩坑表单 schema、搞不定状态回填——欢迎评论区分享,我们一起把组件写成系统,写成能力!
🏁 结语:页面 ≠ 技术,规则才是资产
页面能跑只是初级
页面清晰是中级
页面可控是高级
真正的高级前端,不写页面,而是定义页面怎么写。
欢迎留言交流 👉 你最近写的列表页,还在重复写啥?