别再手写 CRUD 了:我把 Element Plus 的工作量压缩成一份列 schema

34 阅读5分钟

先说说我为什么造这个轮子。

之前写 React 的时候,我最喜欢的就是 antd 的 ProComponents:ProTable 一份列 schema 下去,搜索栏、表格、分页、工具栏全出来了,ProForm 的联动和弹窗表单也是拿来就用。那段时间写中后台是真的爽——业务代码只写业务,模板代码一行不用重复。

后来换了技术栈,改用 Vue3 + Element Plus。习惯性的想找同款,然后把 npm 翻了个遍:要么是几年没维护的老仓库,要么功能只覆盖一小块(有表格没表单、有表单没布局),要么 API 设计和 ProComponents 完全对不上,用起来处处别扭。用惯了好工具,再回去手写 el-form + el-table 的复读机代码,是真的难受。

所以干脆自己动手:参照 ProComponents 的 API 心智,给 Element Plus 封装了一套 el-pro-components。React 那边验证过的设计,搬到 Vue 这边,少踩坑。今天正式发版,开源出来给同样从 React 转 Vue、或者单纯想省 CRUD 工作量的同学用。


一句话介绍

列 schema 驱动的中后台模板组件库:ProTable / ProForm / ProDescriptions / ProLayout / 弹窗抽屉表单 / 查询筛选区 / 指标卡全套,内置 valueType 渲染器和请求生命周期。一张表格的业务代码,从 200 行压到 30 行。

快速上手

npm i el-pro-components element-plus vue
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'el-pro-components/style.css'   // 组件库自己的样式,记得导入
import App from './App.vue'

createApp(App).use(ElementPlus).mount('#app')

ProTable:一份 columns,表格 + 搜索栏 + 工具栏全有了

先看最典型的场景——用户列表:

<script setup>
const columns = [
  { prop: 'id', label: 'ID', valueType: 'indexBorder', width: 64, search: false },
  { prop: 'name', label: '姓名', width: 130 },
  { prop: 'role', label: '角色', valueType: 'tag',
    valueEnum: { admin: { text: '管理员', color: 'danger' }, editor: { text: '编辑', color: 'primary' } } },
  { prop: 'amount', label: '金额', valueType: 'money', sortable: 'custom', search: false },
  { prop: 'actions', label: '操作', valueType: 'option' },
]

const request = async (params) => {
  // params: { current, pageSize, ...搜索字段 }
  const { data, total } = await fetchUsers(params)
  return { data, total }
}
</script>

<template>
  <ElProTable :columns="columns" :request="request" row-key="id" selection title="用户列表">
    <template #actions="{ row }">
      <ElButton link type="primary" @click="edit(row)">编辑</ElButton>
    </template>
  </ElProTable>
</template>

这一份代码你免费获得的东西:

  • 搜索栏:列上不标 search: false 就自动进搜索区,valueEnum 自动变成下拉框
  • 工具栏:刷新 / 密度切换 / 列设置(可拖拽排序、固定列)/ 全屏,开箱即用
  • 请求生命周期:分页参数自动拼装、loading、翻页回顶、persistence-key 持久化用户的列配置和页大小
  • valueType 渲染:money 千分位、tag 按 valueEnum 上色、dateTime/status/indexBorder……不用再手写 formatter
  • 轮询::polling="2000",监控类页面两行配置搞定

进阶能力也都在:多级表头(children 嵌套)、树形数据、展开行、合并单元格(merge-fields)、受控数据源(data-source 替代 request)、行内编辑(见下文)。

ProForm:联动、分组、动态数组,schema 一把梭

<script setup>
const model = reactive({ name: '', type: 'a', extra: '', contacts: [{ cname: '', phone: '' }] })
const columns = [
  {
    valueType: 'group', label: '基本信息', groupProps: { collapsible: true },
    columns: [{ prop: 'name', label: '姓名', valueType: 'input',
      rules: [{ required: true, message: '请输入姓名' }] }],
  },
  { prop: 'type', label: '类型', valueType: 'radio',
    valueEnum: { a: { text: '类型A' }, b: { text: '类型B' } } },
  // 列级联动:只有类型A才显示
  { prop: 'extra', label: '附加说明', valueType: 'input',
    dependency: { dependsOn: ['type'], visible: (v) => v.type === 'a' } },
  // formList:动态增删的子表单
  { prop: 'contacts', label: '联系人', valueType: 'formList', formSpan: 24,
    children: [
      { prop: 'cname', label: '姓名', valueType: 'input' },
      { prop: 'phone', label: '电话', valueType: 'input' },
    ] },
]
const onSubmit = async (values) => { await save(values); return true }
</script>

<template>
  <ElProForm :columns="columns" :model="model" :span="8" @submit="onSubmit" />
</template>

提交/重置按钮是默认带的,校验、重置、valuesChange 事件都是内置的。跨字段的「任意子树级联」用 ElProFormDependency(作用域插槽拿依赖字段值),短信验证码输入框用 ElProFormCaptcha(自带 60s 倒计时,发送失败不进倒计时)。

弹窗 / 抽屉表单:trigger 插槽直接包住按钮

<ElProModalForm v-model:open="open" :columns="modalColumns" title="新建" :on-finish="onFinish">
  <template #trigger><ElButton type="primary">新建</ElButton></template>
</ElProModalForm>

on-finish 返回 true 自动关弹窗,返回 false 或抛错留在弹窗里继续改——「接口炸了把用户刚填的东西丢掉」这种事故不会发生。ElProDrawerForm 同款 API。

行内编辑:编辑/保存/删除按钮自动渲染

<ElProEditableTable
  :columns="editColumns" :data-source="rows" row-key="id"
  :editable="{ type: 'multiple', onSave: async (key, row) => { await save(row); return true } }"
/>
  • type: 'single':一次只编辑一行,点另一行会先保存当前行(保存失败不切换)
  • 保存前按列上的 rules 校验草稿;onSave 返回 false 行留在编辑态
  • 新增行靠 onSave 的回包拿后端主键,返回对象会合并进该行

全家福清单(v0.1.0)

分类组件
表格ElProTable、ElProEditableTable
表单ElProForm、ElProStepsForm、ElProModalForm、ElProDrawerForm、ProFormGroup、ElProFormDependency、ElProFormCaptcha、ElProFieldSet
筛选ElProQueryFilter、ElProLightFilter
展示ElProDescriptions、ElProList、ElProCard、ElProCheckCard(Group)、ElProStatistic(Card/Group)、ElProSkeleton、ElProCardDivider
布局ElProLayout、ElProPageContainer、ElProPageHeader、ElProFooterToolbar

工具函数也直接导出:enumToOptions / formatDate / formatFromNow / formatQuarter / getISOWeek / parseDate / formatNumber / useTable,写需求时顺手就能用。

定位与边界

说实话的版本:

  • ✅ 适合:Vue3 + Element Plus 技术栈的中后台/管理端,想省 CRUD 模板工作量
  • ✅ API 心智对齐 antd ProComponents——columns + valueEnum + request 这套核心概念和 React 版同名同义。你在 React 项目里学过的肌肉记忆直接复用,迁移成本接近零;这也是我封它的第一原则:让「从 React 转 Vue」的同学不需要重新学一遍
  • ⚠️ 0.1.0 早期版本,API 可能还会调;文档站还在完善中,先看 GitHub README 和类型提示
  • 🚫 不打算替代 Element Plus——它是站在 EP 肩膀上的高阶封装,el-* 组件想混用随时混

最后

从 React 的 ProComponents 用到 Vue 自己封一套,中间最深的感受是:好工具的价值不只是省时间,而是让你不再容忍重复劳动。这个库还在早期,但核心链路(表格 → 表单 → 弹窗)已经在我自己的项目里跑起来了。

仓库里 README 有完整的安装说明和示例,后续会补文档站和更多 valueType。如果你也是从 React 转 Vue 的,或者这库帮你省了时间,去给个 Star 就是最大的鼓励;踩到坑欢迎提 issue,看到就修。

📖 在线文档(含示例):april-zheng.github.io/el-pro-comp… 🐙 github.com/April-Zheng… 📦 www.npmjs.com/package/el-…