Appearance
介绍
yoyoo-ui 是什么
yoyoo-ui 是一款面向后台管理系统的 配置驱动型 Vue 3 组件库,基于 Element Plus 二次封装。
它将 CRUD 场景中最通用的表格、表单、弹窗抽象为可配置的组件,通过一份配置对象即可驱动完整的 UI 渲染和数据交互,而非在每个页面中重复编写相似的结构代码。
下面通过一个简单的「用户列表 + 新增/编辑弹窗」场景,对比三种开发方式的差异。
传统开发:重复的模板代码
vue
<template>
<div>
<el-table :data="list" @selection-change="onSelect">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'info'">
{{ row.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button @click="openEdit(row)">编辑</el-button>
<el-popconfirm title="确认删除?">
<el-button type="danger">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:page="page" :total="total" />
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑' : '新增'">
<el-form :model="form">
<el-form-item label="姓名">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="年龄">
<el-input-number v-model="form.age" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确认</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
const list = ref([])
const page = ref(1)
const total = ref(0)
const dialogVisible = ref(false)
const isEdit = ref(false)
const form = reactive({})
const fetchList = () => {
api.getList({ page: page.value }).then((res) => {
list.value = res.list
total.value = res.total
})
}
const openEdit = (row) => {
isEdit.value = true
Object.assign(form, row)
dialogVisible.value = true
}
const handleSubmit = () => {
api.save(form).then(() => {
dialogVisible.value = false
fetchList()
})
}
onMounted(fetchList)
</script>重量级框架:庞大的配置体系
以 Formily(阿里开源的配置驱动表单框架)为例,实现同样的功能需要学习和理解其复杂的 Schema 体系:
tsx
import { createForm } from '@formily/core'
import { FormProvider, Field } from '@formily/react'
import { FormItem, Input, NumberPicker } from '@formily/antd'
import { SchemaComponent } from '@formily/react-schema-component'
// 需要理解 ISchema、Field、Reactions、Decorator 等大量概念
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
title: '姓名',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': { placeholder: '请输入姓名' },
// 需要掌握 reactions 响应式机制
'x-reactions': {
dependencies: ['age'],
fulfill: { schema: { 'x-component-props': { disabled: '{{$deps[0] > 100}}' } } },
},
},
age: {
type: 'string',
title: '年龄',
'x-decorator': 'FormItem',
'x-component': 'NumberPicker',
// 需要理解 validator 的复杂配置
'x-validator': [
{ maximum: 150, message: '年龄不能超过150' },
],
},
// 当需要嵌套布局时,还需理解 JSON Schema 的 allOf/oneOf/if-then-else
},
}
// 还需要手动管理表单与表格的结合,内置组件不提供表格Formily 提供了极其强大的能力,但代价是陡峭的学习曲线——需要理解 JSON Schema、Reactions 响应式协议、Validator 校验协议、Decorator 装饰器模式等一整套抽象概念,配置链路过长,排查问题困难。
yoyoo-ui:简洁的配置 + 原生 Vue 3 响应式
ts
// 表格配置
const tableColumns = [
{ label: '姓名', field: 'name' },
{ label: '年龄', field: 'age', type: 'number' },
{ label: '状态', field: 'status', type: 'enum', enum: { 1: '启用', 0: '禁用' } },
{ label: '操作', type: 'operate', buttons: [
{ label: '编辑', click: ({ row }) => form.open({ form: { model: row, columns: formColumns } }) },
{ label: '删除', type: 'danger', confirm: '确认删除?', click: ({ row }) => del(row.id) },
]},
]
// 表单配置 — 字段属性支持函数,与 Vue 3 响应式无缝联动
const formColumns = [
{ label: '姓名', field: 'name', required: true },
// type 直接使用 Element Plus 组件名,props 透传原生属性
{ label: '年龄', field: 'age', type: 'el-input-number', props: { min: 0, max: 150 } },
// show 为函数:根据表单数据实时控制显隐
{ label: '家属姓名', field: 'familyName', show: (model) => model.age < 18 },
// disabled 为函数:根据表单数据控制禁用
{ label: '邮箱', field: 'email', disabled: (model) => !model.name },
// required 为函数:条件必填
{ label: '手机号', field: 'phone', required: (model) => model.country === 'CN' },
// props 本身支持函数,根据表单数据动态返回配置
{ label: '数量', field: 'count', type: 'el-input-number',
props: (model) => ({ max: model.stock }) },
// events 绑定组件事件
{ label: '备注', field: 'remark', type: 'el-input',
events: { change: (v) => console.log('备注变化', v) } },
// slots 传入插槽
{ label: '搜索', field: 'search', type: 'el-input',
slots: { prefix: () => <el-icon><Search /></el-icon> } },
// 自定义表单项:type 直接使用全局注册的组件名
{ label: '评分', field: 'rate', type: 'custom-slider', props: { min: 0, max: 100 } },
]
// 渲染
<DataTable query={fetchList} :columns={tableColumns} />yoyoo-ui 的配置只描述 业务字段本身,字段的 show、disabled、required、props 等属性均可直接传入函数,利用 Vue 3 的响应式系统自动计算。无需学习额外的 Schema 协议——写 Vue 的方式就是写配置的方式。
与同类配置组件的区别
与市面上一些「大而全」的配置组件库不同,yoyoo-ui 保持轻量——配置表简洁明了,无冗余概念;同时通过 render 函数、slots 插槽、events 事件注入等方式保留了直接使用 Vue 原生能力兜底的灵活性。当业务需要扩展时,可通过全局注册自定义表单项、列类型、选择器等方式水平扩展,组件的功能边界由你定义,而非受限于框架预设。
为什么要用 yoyoo-ui
消灭重复的模板代码
表格和表单是后台管理系统中使用频率最高的两类界面。传统方式下,每新增一个功能模块,都需要重新编写完整的表格列、表单项、弹窗、校验逻辑——这些代码的结构几乎完全一致,只是字段和接口不同。yoyoo-ui 通过配置对象来描述界面,一份配置即可驱动完整的表格或表单渲染,配合函数式弹窗可进一步将「新增/编辑」场景缩减为几行配置。
响应式数据开箱即用
组件内部的 model 绑定、分页参数、搜索条件、表单校验等状态管理均已内置,无需手动维护响应式数据。配置中的字段属性(如 show、disabled、required)支持动态函数计算,可根据当前表单数据实时联动。
灵活兜底,不被框架束缚
配置驱动最怕的就是「配置覆盖不到的需求就没办法」。yoyoo-ui 在保持配置高效性的同时,保留了完整的灵活性:
- 自定义渲染 — 列类型、表单项均支持通过
render函数定制 - 动态显隐 —
show属性支持函数,根据当前行或表单数据控制显隐 - 事件注入 — 通过
events配置绑定组件事件,通过slots传入插槽内容 - 全局配置 — 上传、日期格式、权限、分页等全局选项可统一设置,按需覆盖
可水平扩展
- 自定义表单项 — 任意 Vue 组件全局注册后,即可通过
type在 DataForm 中使用,组件协议仅需modelValue+update:modelValue - 自定义列类型 — DataTable 支持通过全局配置注册自定义列类型渲染函数
- 自定义选择器 — DataPicker 支持通过
typeRender扩展选择项的展示方式
核心组件
DataTable 表格
最核心的数据展示组件。通过 columns 配置描述每一列的字段名、数据类型、格式和操作按钮,通过 query 传入异步查询函数即可自动完成数据拉取、分页、搜索。内置支持文本、数字、金额、百分比、枚举、时间、图片、操作按钮等多种列类型。
配合 search 搜索表单、tabs 标签筛选、selection 行选择、buttons 顶部操作按钮,一个完整的列表页仅需一份配置即可搭建完成。
DataForm 动态表单
通过 columns 配置动态渲染表单控件,支持文本、数字、日期、选择器、上传、开关等 20+ 控件类型。内置表单校验、文本模式(详情展示)、分组布局、内联布局等能力。
特别适用于字段数量多、布局多变的表单场景——调整字段顺序、增减字段、修改校验规则等只需修改配置,无需改动模板代码。
useFormLayout 函数式弹窗
传统的弹窗表单需要声明 visible 变量、绑定 dialog 组件、处理打开关闭事件——每新增一个弹窗就要重复这一套模板。useFormLayout 以函数调用的方式替代模板声明,内部封装了弹窗/抽屉的打开、关闭、表单校验、loading 状态和提交流程。支持嵌入 DataForm 表单或任意自定义 VNode 内容。
组件概览
浏览器兼容性
支持 Vue 3 和 Element Plus 所兼容的所有现代浏览器。