mzm-auto-form
v1.2.22
Published
基于vue3、element-plus、typeScript实现的数据驱动式动态表单组件
Maintainers
Readme
mzm-auto-form
基于 Vue3、Element-Plus、TypeScript 实现的数据驱动式动态表单组件。旨在通过 JSON 配置的方式快速构建复杂表单。
特性
- 🚀 数据驱动: 完全通过配置对象渲染表单,摆脱冗长的 HTML 模板。
- 📦 开箱即用: 内置丰富的表单控件类型(输入框、选择器、日期、开关、上传等)。
- 🔗 响应式联动: 支持基于
formData状态的动态显示/隐藏、动态禁用、动态校验规则与动态选项列表。 - 🎯 完美兼容 Element Plus: 继承
el-form及内部控件的所有属性与事件,支持插槽注入。 - 🤖 大模型友好 (LLM-Friendly): 提供了清晰的 TypeScript 接口定义,方便在通过 AI 生成代码时保持严格的类型约束。
安装
npm i mzm-auto-form引入
1. 全局注册 (main.ts)
import { createApp } from "vue";
import App from "./App.vue";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import MzmAutoForm from "mzm-auto-form";
import "mzm-auto-form/lib/index.css";
const app = createApp(App);
app.use(ElementPlus);
app.use(MzmAutoForm);
app.mount("#app");2. 局部引入 (在组件中)
import { mzmAutoForm } from "mzm-auto-form";
import "mzm-auto-form/lib/index.css";基础用法
通过定义 configList 数组即可快速渲染表单:
<template>
<mzm-auto-form
ref="formRef"
:config-list="formConfig"
label-width="120px"
@loaded="handleLoaded"
>
<!-- 自定义插槽内容 -->
<template #customSlot="{ formData, formItem }">
<el-button @click="formData[formItem.key] = 'custom'">
自定义按钮:{{ formData[formItem.key] }}
</el-button>
</template>
</mzm-auto-form>
<el-button
type="primary"
@click="submit"
>提交</el-button
>
</template>
<script setup lang="ts">
import { ref } from "vue";
import {
mzmAutoForm,
AutoFormItemType,
type AutoFormItem,
} from "mzm-auto-form";
import "mzm-auto-form/lib/index.css";
const formRef = ref<InstanceType<typeof mzmAutoForm>>();
const formConfig = ref<AutoFormItem[]>([
{
key: "username",
type: AutoFormItemType.Input,
formItemProps: { label: "用户名" },
props: { placeholder: "请输入用户名", clearable: true },
rules: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
},
{
key: "role",
type: AutoFormItemType.Select,
formItemProps: { label: "角色" },
options: [
{ label: "管理员", value: "admin" },
{ label: "普通用户", value: "user" },
],
},
{
key: "customField",
slotName: "customSlot",
formItemProps: { label: "自定义插槽" },
},
]);
const handleLoaded = (formData: any) => {
console.log("表单加载完成,初始数据:", formData);
};
const submit = async () => {
// 获取 element-plus 的 el-form 实例并校验
await formRef.value?.formRef?.validate();
// 获取组装好的表单数据(自动剔除 isHide 为 true 的字段)
const data = formRef.value?.getFormData();
console.log("提交数据:", data);
};
</script>API 文档
组件属性 (Props)
除了支持 Element-Plus el-form 的所有原生属性外,还支持:
| 属性名 | 说明 | 类型 | 默认值 |
| ------------ | ------------------------------------------ | ---------------- | ------ |
| configList | 表单项配置列表(核心),详情见下文接口定义 | AutoFormItem[] | [] |
组件事件 (Emits)
| 事件名 | 说明 | 回调参数 |
| --------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| loaded | 表单初始化完成(默认值已挂载)时触发 | (formData: Record<string, any>) => void |
| {customEvent} | 通过 eventsIn 注入的事件抛出 | (eventObj: any, formData: Record<string, any>, itemConfig: AutoFormItem, configList: VisibleConfigItem[]) => void |
暴露方法 (Exposes)
通过组件的 ref 可调用以下方法/属性:
| 方法名/属性名 | 说明 | 类型 |
| ------------------ | -------------------------------------------------------------------- | ----------------------------------- |
| getFormData() | 获取当前表单数据(自动剔除 isHide 为 true 的字段数据) | () => Record<string, any> |
| resetForm() | 重置表单到初始状态(重新挂载默认值) | () => void |
| formRef | 获取原生的 Element Plus el-form 实例,可用于调用 validate 等方法 | Ref<FormInstance> |
| originalFormData | 获取表单全量的响应式数据对象 | Ref<Record<string, any>> |
| setFormData | 单独设置表单某个字段的响应式数据 | (key: string, value: any) => void |
TypeScript 类型定义 (大模型生成必读规范)
mzm-auto-form 依赖高度抽象的配置项。当通过 AI 或手动生成代码时,请严格参考并遵循以下 AutoFormItem 与 AutoFormItemType 接口定义,保证配置的正确性与类型安全。
AutoFormItem 接口定义
export interface AutoFormItem {
/** 表单项绑定字段名 (唯一标识) */
key?: string;
/** 是否隐藏当前项(隐藏后不仅不渲染,getFormData 也不会返回该字段) */
isHide?:
| boolean
| ((formData: Record<string, any>, formItem: AutoFormItem) => boolean);
/** 是否禁用当前项控件 */
isDisabled?:
| boolean
| ((formData: Record<string, any>, formItem: AutoFormItem) => boolean);
/** 控件类型枚举,参考 AutoFormItemType */
type?: AutoFormItemType;
/** 控件默认值,支持直接赋值或函数返回(支持 Promise) */
defaultValue?: any | (() => any);
/** el-form-item 包装层的配置属性 (如 label, label-width 等) */
formItemProps?: Record<string, any>;
/** 表单项验证规则,兼容 Element Plus 规则,支持动态返回 */
rules?:
| Record<string, any>
| ((
formData: Record<string, any>,
formItem: AutoFormItem,
) => Record<string, any>);
/** 内部真实控件 (如 el-input) 的属性配置 */
props?: Record<string, any>;
/** select/radio/checkbox 等控件的选项列表,支持动态异步获取 */
options?:
| { label: string; value: any }[]
| ((
formData: Record<string, any>,
formItem: AutoFormItem,
configList: VisibleConfigItem[],
) =>
| { label: string; value: any }[]
| Promise<{ label: string; value: any }[]>);
/** 绑定到控件上的事件对象 (原生) */
events?: Record<string, Function>;
/** 事件注入:将控件的事件转发抛出给外层组件的 @[emitName] 事件 */
eventsIn?: { innerName: string; emitName: string }[];
/** 输入框后缀单位文本(仅对 Input 等部分非文本域控件生效) */
valueUnit?: any;
/** 表单项底部描述文本 */
describe?: string;
/** 表单控件内部插槽配置,映射外层插槽到控件内 */
internalSlots?: { innerName: string; slotName: string }[];
/** 初始化数据格式化钩子 */
defaultValueFormatter?: (
defaultValue: any,
itemConfig: AutoFormItem,
configList: VisibleConfigItem[],
) => any;
/** 提交前数据格式化钩子 */
valueFormatter?: (
value: any,
formData: Record<string, any>,
itemConfig: AutoFormItem,
configList: VisibleConfigItem[],
) => any;
/** 自定义整个表单项控件内容的插槽名(优先级高于 type,会替代内置控件) */
slotName?: string;
/** 开发者自定义存储的额外配置数据,组件不消费此字段 */
otherData?: any;
}
```
### VisibleConfigItem 接口定义
在内部运行和事件回调中,组件会提供 `VisibleConfigItem`,它继承自 `AutoFormItem` 并携带了处理后的内部属性。
```typescript
export interface VisibleConfigItem extends AutoFormItem {
_options?: { label: string; value: any }[];
_props?: Record<string, any>;
_events?: Record<string, Function>;
_rules?: Record<string, any>;
}
```
### AutoFormItemType 控件类型枚举
支持丰富的内置控件,使用时推荐引入 `AutoFormItemType` 进行类型安全的赋值。
```typescript
export enum AutoFormItemType {
Input = "el-input", // 输入框
InputNumber = "el-input-number", // 数字步进器
InputTag = "el-input-tag", // 标签输入框
ColorPickerPanel = "el-color-picker-panel", // 颜色选择器
Slider = "el-slider", // 滑块
Select = "el-select", // 下拉选择器
SelectTree = "el-tree-select", // 树形下拉选择器
Radio = "el-radio-group", // 单选框组
RadioButton = "el-radio-group_button", // 按钮样式单选框组
Checkbox = "el-checkbox-group", // 复选框组
DatePicker = "el-date-picker", // 日期选择器
TimePicker = "el-time-picker", // 时间选择器
Switch = "el-switch", // 开关
Cascader = "el-cascader", // 级联选择器
Upload = "mzm-upload", // 文件上传 (内置定制版)
}
```
## 进阶用法示例
### 1. 动态表单联动 (显隐与禁用)
可以通过传入函数实现复杂的动态联动,函数参数会自动注入当前表单的完整数据 `formData`。
```typescript
{
key: 'reason',
type: AutoFormItemType.Input,
formItemProps: { label: '退回原因' },
// 当 status 为 reject 时才显示
isHide: (formData) => formData.status !== 'reject',
// 动态验证规则
rules: (formData) => {
return formData.status === 'reject'
? [{ required: true, message: '请填写退回原因' }]
: [];
}
}
```
### 2. 异步拉取选项列表 (Options)
`options` 支持返回一个 `Promise`,组件会自动在内部等待并渲染,适用于需要调用后端接口获取字典的场景。
```typescript
{
key: 'userId',
type: AutoFormItemType.Select,
formItemProps: { label: '选择用户' },
options: async (formData, item) => {
// 模拟后端接口请求
const res = await fetchUserList(formData.departmentId);
return res.data.map(user => ({ label: user.name, value: user.id }));
}
}
```
### 3. 级联面板 (Cascader) 的使用
级联面板组件在配置 `options` 时,需要传入符合 `CascaderOption` 规范的多层级数组。`mzm-auto-form` 内部已经自动处理了其选项挂载及 `label` 的渲染插槽逻辑。
```typescript
{
key: 'region',
type: AutoFormItemType.Cascader,
formItemProps: { label: '所属区域' },
props: {
// Cascader 的原生配置项
props: {
expandTrigger: 'hover',
value: 'code',
label: 'name'
},
placeholder: '请选择所在区域',
clearable: true
},
// 可以是同步的树形数据,也可以是异步请求获取
options: [
{
code: 'zhejiang',
name: '浙江',
children: [
{ code: 'hangzhou', name: '杭州' },
{ code: 'ningbo', name: '宁波' }
]
},
{
code: 'jiangsu',
name: '江苏',
children: [
{ code: 'nanjing', name: '南京' }
]
}
]
}
```
### 4. 文件上传 (Upload) 的使用
`mzm-auto-form` 内置了深度定制的文件上传组件 (`mzm-upload`),支持图片预览、列表管理、单文件/多文件控制,同时允许动态获取 Token 和上传参数。
它的返回值 `modelValue` 默认支持两种类型:
- **单文件** (默认 `limit: 1`):返回 `URL` 字符串
- **多文件** (`limit > 1`):返回逗号分隔的 `URL` 字符串 或 字符串数组。
**关键专属属性 (`props`):**
- `action` (string): **必填**,上传的服务器地址 (同 Element Plus)。
- `uploadType` ('file' | 'image'): 上传样式类型,默认 `file`,设置为 `image` 会展示照片墙模式。
- `limit` (number): 最大允许上传个数,默认 1。
- `resolveResponse` (Function): 核心钩子,用于从接口返回的数据中提取文件 URL 赋值给表单。
- `headers` / `data` / `accept`: 均支持直接传对象/字符串,或传入 **动态函数** `(formData, formItem) => value`。
- `baseUrl` (string): 拼接在返回的 URL 前的域名前缀(多用于相对路径处理)。
```typescript
{
key: 'avatar',
type: AutoFormItemType.Upload,
formItemProps: { label: '用户头像' },
props: {
action: '/api/upload', // 接口地址
uploadType: 'image', // 使用图片墙模式
limit: 1, // 限制1张
tip: '只能上传 jpg/png 文件,且不超过 2MB',
// 动态获取 Token 等请求头
headers: (formData, item) => ({
Authorization: `Bearer ${localStorage.getItem('token')}`
}),
// 动态附带额外的表单数据
data: (formData) => ({
userId: formData.id,
fileType: 'avatar'
}),
// 核心:提取服务端返回的实际 URL 给到 formData.avatar
// 假设服务端返回: { code: 200, data: { fileUrl: 'https://xxx.png' } }
resolveResponse: (response) => {
return response.data?.fileUrl || '';
}
}
}
```
### 5. `mzm-upload` 组件
除了通过 `mzm-auto-form` 以配置方式使用外,`mzm-upload` 也作为一个独立的组件向外暴露,可以在任何 Vue 组件中直接使用。它深度封装了 `el-upload`,提供了更简便的数据绑定(支持逗号分隔的 URL 字符串)和更完善的文件/图片管理体验。
#### 基础用法
```vue
<template>
<!-- 图片墙模式 (单图) -->
<mzm-upload
v-model="avatar"
action="/api/upload"
upload-type="image"
:limit="1"
:resolve-response="res => res.data.url"
/>
<!-- 普通文件模式 (多文件) -->
<mzm-upload
v-model="fileList"
action="/api/upload"
upload-type="file"
:limit="5"
:resolve-response="res => res.data.url"
tip="支持上传多份文档,每个不超过10MB"
@clickFile="handleFileClick"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { MzmUpload } from 'mzm-auto-form'; // 或者根据你的导出方式引入
// 单文件绑定字符串
const avatar = ref('https://example.com/avatar.png');
// 多文件绑定字符串(逗号分隔)或数组
const fileList = ref('url1.pdf,url2.pdf');
const handleFileClick = (url: string) => {
console.log('点击了文件:', url);
window.open(url, '_blank');
};
</script>
```
#### 组件属性 (Props)
除了支持 `el-upload` 的原生属性(如 `action`、`multiple`、`name` 等)外,额外支持:
| 属性名 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| `modelValue` | 绑定的值。支持逗号分隔的 URL 字符串,或字符串数组 | `string \| string[]` | `""` |
| `limit` | 最大允许上传个数。当 `limit=1` 时,再次上传会自动替换当前文件 | `number` | `1` |
| `uploadType` | 上传样式类型。`image` 为照片墙,`file` 为普通文件列表 | `'file' \| 'image'` | `'file'` |
| `baseUrl` | 拼接在文件 URL 前的域名前缀,用于处理相对路径的回显 | `string` | `""` |
| `tip` | 上传组件底部的提示文本 | `string` | - |
| `disabled` | 是否禁用上传及删除操作 | `boolean` | `false` |
| `resolveResponse` | 核心钩子:用于从接口响应中提取实际的文件 URL 字符串 | `(response: any) => string` | 内部默认逻辑 |
| `headers` | 请求头配置。支持对象或动态函数(表单配置时有效) | `Record<string, any> \| Function` | `{}` |
| `data` | 上传时附带的额外参数。支持对象或动态函数(表单配置时有效) | `Record<string, any> \| Function` | `{}` |
| `accept` | 接受上传的文件类型。支持字符串或动态函数 | `string \| Function` | `""` |
#### 组件事件 (Emits)
| 事件名 | 说明 | 回调参数 |
| --- | --- | --- |
| `update:modelValue` | 绑定的值发生变化时触发 | `(val: string) => void` |
| `change` | 文件上传成功、删除或列表变动时触发 | `(info: any, val: string) => void` |
| `clickFile` | 点击普通文件列表项的文件名时触发(仅 `uploadType="file"` 有效) | `(url: string) => void` |
#### 插槽 (Slots)
| 插槽名 | 说明 | 作用域参数 |
| --- | --- | --- |
| `default` | 自定义上传触发器按钮 | - |
| `tip` | 自定义底部提示内容 | - |
| `file` | 自定义文件列表项的展示内容 | `{ file: UploadFile }` |
### 6. 事件绑定与事件注入 (`events` vs `eventsIn`)
组件支持两种方式来处理表单控件内部抛出的原生事件(如 `change`、`blur`、`focus` 等),它们在适用场景和代码结构上有所不同:
#### (1) `events`:直接绑定(内联回调)
通过 `events` 对象,你可以直接在配置项中为控件绑定回调函数。
**适用场景**:适合处理简单的内部逻辑或调试,不需要将事件与外部 Vue 组件的方法解耦。
**参数说明**:
在 `events` 中定义的回调函数,接收四个参数:`(args, formData, itemConfig, configList)`
- `args`: 底层组件原生事件抛出的参数数组(例如 `change` 事件抛出的值会作为 `args[0]`)
- `formData`: 当前表单的全量响应式数据对象
- `itemConfig`: 当前表单项的配置对象
- `configList`: 完整的表单配置列表(类型为 `VisibleConfigItem[]`)
```typescript
{
key: 'username',
type: AutoFormItemType.Input,
formItemProps: { label: '用户名' },
events: {
// 监听 el-input 的 blur 事件
blur: (args, formData, itemConfig, configList) => {
console.log('用户名输入框失焦', args[0]); // args[0] 通常是原生 event 对象
},
// 监听 el-input 的 change 事件
change: (args, formData, itemConfig, configList) => {
console.log('值发生改变:', args[0]); // args[0] 是输入的新值
// 直接在此处修改 formData 中的其他值
formData.otherField = '联动值';
}
}
}
```
#### (2) `eventsIn`:事件注入(向外抛出)
如果在事件触发时需要调用外部组件的复杂方法(例如触发外层的响应式修改、请求其他接口等),直接在 JSON 配置里写函数会导致代码耦合严重或难以获取 Vue 上下文(如访问不到外部的 `ref` 变量)。
此时可以使用 `eventsIn`,将控件的内部事件**转发并作为自定义事件抛出**给 `<mzm-auto-form>` 的外层监听。
- `innerName`: 控件原生触发的事件名(如 `change`)。
- `emitName`: 转发给 `<mzm-auto-form>` 抛出的自定义事件名。
> **注意:** 如果 `events` 和 `eventsIn` 存在监听同一个内部事件发生冲突,默认以 `eventsIn` 优先级更高。
**适用场景**:适合将业务逻辑集中在 Vue 模板 `<script setup>` 内统一管理,保持表单配置 JSON 的纯粹性。
**参数说明**:
抛出到外层的自定义事件回调函数,接收四个参数:`(eventArgs, formData, itemConfig, configList)`
- `eventArgs`: 底层组件原生事件抛出的**参数数组**(即包含原生抛出的所有参数,如 `[val, event]`)
- `formData`: 当前表单的全量数据
- `itemConfig`: 当前表单项的配置
- `configList`: 完整的表单配置列表(类型为 `VisibleConfigItem[]`)
**示例:**
```vue
<template>
<mzm-auto-form
:config-list="formConfig"
@on-city-change="handleCityChange"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { AutoFormItemType, type AutoFormItem, type VisibleConfigItem } from "mzm-auto-form";
const formConfig = ref<AutoFormItem[]>([
{
key: "city",
type: AutoFormItemType.Select,
formItemProps: { label: "城市" },
options: [
{ label: "北京", value: "beijing" },
{ label: "上海", value: "shanghai" },
],
eventsIn: [
{
innerName: "change", // 监听底层 el-select 的 change 事件
emitName: "on-city-change", // 转发为 on-city-change 事件向外 emit
},
],
},
]);
// 在外部组件中统一处理复杂的业务逻辑
// 接收参数为固定格式:(eventArgs: any[], formData: any, itemConfig: AutoFormItem, configList: VisibleConfigItem[])
const handleCityChange = (
eventArgs: any[],
formData: any,
itemConfig: AutoFormItem,
configList: VisibleConfigItem[],
) => {
const selectedValue = eventArgs[0]; // 获取 el-select 抛出的第一个参数(选中的值)
console.log("当前选中的城市值:", selectedValue);
console.log("当前表单全量数据:", formData);
// 可以在这里无缝访问外部的 Vue 响应式变量或调用接口
// fetchCityArea(selectedValue).then(...)
};
</script>
```