UNPKG

element-plus-fast-form

Version:

基于 Vue 3 和 Element-Plus 的表单和 CRUD 组件封装库

602 lines (491 loc) 20.8 kB
# element-plus-fast-form 使用说明 本项目基于 Element Plus,采用"配置驱动+类封装+组合式 API"模式,适合中后台低代码动态表单等复杂场景 ## 🚀 为什么选择 ElementPlusFastForm? **与传统 Vue 表单组件相比,FastForm 具有革命性的性能优势:** - **⚡ 极致性能**:采用路径驱动架构,所有操作均为 O(1) 复杂度,嵌套表单场景下性能提升 **10-100** - **🎯 嵌套表单原生支持**:内置嵌套表单引擎,支持 `"parent.0.child"` 路径语法,无需手动维护复杂的嵌套状态 - **🔧 配置驱动开发**:声明式配置减少 80% 样板代码,告别繁琐的响应式状态管理 - **📦 单一数据源**:集中式状态管理,精确更新机制避免全量重渲染,内存占用减少 60-80% - **🛠️ 企业级架构**:支持动态配置表单联动多实例管理等复杂业务需求 传统 Vue 表单组件在处理复杂嵌套表单时存在性能瓶颈和开发复杂度问题,而 ElementPlusFastForm 通过先进的架构设计彻底解决了这些痛点,是现代 Vue 项目的理想选择 ## 目录 - [功能特性](#功能特性) - [预览](#预览) - [安装与引入](#安装与引入) - [API](#api) - [类型定义(types)](#类型定义) - [组件透传属性](#组件透传属性) ## 功能特性 ### 1. 多组件类型支持 - **Element Plus 组件**:支持所有 Element Plus 表单组件(el-inputel-selectel-cascader 等) - **自定义 Vue 组件**:支持传入自定义组件,自动传递 formValue 和相关方法 - **插槽组件**:使用 'slot' 类型实现完全自定义的表单项渲染 - **文本组件**:支持纯文本或 HTML 字符串组件渲染 ### 2. 后缀文字支持 - 表单项支持添加后缀文字,常用于显示单位或说明 - 自动调整布局,确保后缀文字正确显示 - 支持与表单项宽度的自适应配合 ### 3. 异步配置支持 - **响应式配置**:支持 reactive 和 ref 包装的配置对象 - **动态加载**:支持异步获取表单配置并自动更新渲染 - **批量替换**:提供 setFormConfigs 方法实现整个表单结构的动态切换 ### 4. 嵌套表单(表单列表) - 支持数组形式的嵌套表单结构 - 提供 addItem 和 removeItem 方法动态增删列表项 - 每个列表项可包含多个表单字段 - 支持在嵌套表单中使用插槽或自定义组件 ### 5. 表单联动功能 - **方法联动**:通过 hooks 方法实现表单项之间的联动 - **组件联动**:在自定义组件中实现复杂的联动逻辑 - **插槽联动**:通过插槽实现灵活的联动交互 ### 6. 多表单实例管理 - 同一页面支持多个独立的表单实例 - 每个表单实例拥有独立的配置数据和验证 ### 7. 动态表单操作 - **组件属性动态修改**:运行时修改表单项的 optionsplaceholder 等属性 - **表单项增删改**:动态添加删除修改表单项配置 - **表单禁用控制**:支持整个表单或单个表单项的禁用状态切换 ### 8. 灵活的布局系统 - 基于 Element PlusRow/Col 栅格系统 - 支持响应式布局配置 - 表单项自动适应容器宽度 - 支持自定义表单项和整体布局样式 ### 9. 完整的表单验证 - 继承 Element Plus 的完整验证体系 - 支持同步和异步验证规则 - 提供表单实例引用,支持手动验证控制 ### 10. TypeScript 支持 - 完整的类型定义覆盖 - 智能提示和类型检查 - 类型安全的 API 设计 ## 预览 [https://zhu-jiayu.github.io/element-plus-fast-form-demo/](https://zhu-jiayu.github.io/element-plus-fast-form-demo/) ## 安装与引入 此库依赖以下包,这些包需要在您的项目中安装: ```bash npm install vue@^3.2.13 element-plus@^2.7.4 @element-plus/icons-vue@^2.0.0 ``` ### 1. 安装 npm 包 ```bash npm install element-plus-fast-form # 或 yarn add element-plus-fast-form ``` ### 2. 在项目中引入 ```ts import { useForm } from "element-plus-fast-form"; // 或全局注册 import ElementPlusFastForm from "element-plus-fast-form"; app.use(ElementPlusFastForm); ``` ## API ### hooks: useForm **定义:** ```ts function useForm(config: IFormProps): IUseForm; ``` | 方法名 | 入参类型 | 出参类型 | 说明 | | ------- | --------------------------------------------------------- | -------- | -------------------------- | | useForm | IFormProps \| Reactive\<IFormProps\> \| Ref\<IFormProps\> | IUseForm | 获取表单实例和所有操作方法 | 注意点:入参类型如果是响应式数据,只作用于表单初始化时的异步渲染,如果需要修改表单属性,请使用 setFormConfig 或 setComponentProps 方法 ### useForm 返回属性 | 属性名 | 类型 | 说明 | | ------------ | ------------------------------ | ---------------------------- | | FastForm | DefineComponent | 表单组件,直接用于模板渲染 | | formValue | FormValueType | 响应式表单数据对象 | | rawFormValue | FormValueType | 原始表单数据对象(非响应式) | | formRef | Ref<FormInstance \| undefined> | el-form 实例引用 | ### useForm 返回方法 | 方法名 | 入参类型 | 返回类型 | 作用说明 | | ----------------- | ------------------------------------------------- | -------- | ------------------------------------------- | | addItem | prop: string, config?: IFormconfig[] | void | 添加嵌套表单(config 不传则添加相同的表单) | | removeItem | prop: string, key: number | void | 删除嵌套表单(如数组表单 remove) | | setComponentProps | prop: string, componentProps: Record<string, any> | void | 动态设置表单项组件属性(支持嵌套) | | setFormValue | formData: Record<string, any> | void | 动态设置表单数据 | | setFormConfig | prop: string, config: Partial<IFormconfig> | void | 动态设置表单项配置(支持嵌套) | | setFormConfigs | newFormConfig: IFormconfig[], isRemoveValue?: boolean | Promise<void> | 批量替换整个表单配置, 第2个参数用于是否删除旧值 | | addFormConfig | config: IFormconfig, targetProp?: string, index?: number | void | 动态添加表单项(支持嵌套) | | removeFormConfig | props: string[] | void | 动态删除表单项(支持嵌套) | | setFormDisabled | disabled: boolean | void | 禁用/启用整个表单 | - `removeItem(prop: string, key: number)` 删除指定嵌套表单的第 key 项 - `setComponentProps(prop, componentProps)` 动态修改某个表单项的组件属性(如 optionsplaceholderdisabled 等) **支持嵌套格式**:`prop` 可以是 `"parentProp.index.childProp"` 格式 - `setFormValue(formData)` 批量设置表单数据,常用于一键填充或重置 - `setFormConfig(prop, config)` 动态修改某个表单项的配置(如校验规则label组件类型等) **支持嵌套格式**:`prop` 可以是 `"parentProp.index.childProp"` 格式 - `setFormConfigs(newFormConfig, isRemoveValue?)` **批量替换整个表单配置**,用于异步加载配置或动态切换表单结构2个参数用于是否删除旧值 - `addFormConfig(config, targetProp?, index?)` 动态添加表单项 - `config`:要添加的表单配置 - `targetProp`:目标位置,支持 `"parentProp.index"` 格式添加到嵌套列表 - `index`:插入位置(仅在顶层添加时有效) - `removeFormConfig(props)` 批量删除表单项,参数为 prop 数组 **支持嵌套格式**:数组元素可以是 `"parentProp.index.childProp"` 格式 - `setFormDisabled(disabled)` 禁用或启用整个表单 ### 嵌套表单操作格式说明 部分方法支持嵌套表单的操作,使用特定的字符串格式来指定嵌套位置: #### 路径格式 - **3段式**:`"parentProp.index.childProp"` - 用于设置/删除嵌套表单项 - **2段式**:`"parentProp.index"` - 用于向嵌套列表添加表单项 - **普通**:`"normalProp"` - 普通表单项操作 #### 使用示例 ```typescript // 设置嵌套表单项的属性 setFormConfig("addresses.0.street", { formItemProps: { label: "街道地址" } }); // 向嵌套列表添加新字段 addFormConfig(newFieldConfig, "addresses.0"); // 删除嵌套表单项 removeFormConfig(["addresses.0.street", "addresses.1.city"]); // 设置嵌套表单项的组件属性 setComponentProps("addresses.0.street", { placeholder: "请输入街道地址" }); ``` ## 类型定义 ### 1. IOptions ```ts /** * 表单组件通用选项类型,继承自 Element Plus 的 Select Option 类型。 * 用于 el-select、el-cascader、el-radio-group、el-checkbox-group 等组件的 options 配置。 */ export interface IOptions { /** 选项显示文本 */ label: string | number; /** 选项实际值 */ value: string | number | boolean | object; /** 是否禁用该选项 */ disabled?: boolean; /** 可选:唯一标识(适合树形结构或自定义 key) */ key?: string | number; /** 子选项(用于级联/树形结构) */ children?: IOptions[]; /** 其它自定义属性(兼容 Element Plus Option 类型) */ [key: string]: any; } ``` ### 2. IFormconfig ```ts /** * 单个表单项的配置类型 */ export interface IFormconfig { /** 组件类型(如 el-input、el-select、slot、自定义组件等) */ component?: string | AsyncComponent | DefineComponent<any, any, any> | "slot"; /** el-col 组件属性(如 span、offset 等) */ colProps?: Record<string, any>; /** 组件本身的属性(参考element-plus 组件属性) */ componentProps?: Partial<{ options: IOptions[]; // 选项数组 placeholder: string; // 占位符 data: IOptions[]; // 其它数据 [key: string]: any; // 其它自定义属性 }>; /** el-form-item 组件属性(参考element-plus el-form-item 属性) */ formItemProps: Partial<{ prop: string; // 字段名 label: string; // 标签 rules: Array<FormItemRule>; // 校验规则 model: FormValueType; // 表单默认值 [key: string]: any; // 其他属性,参考element-plus el-form-item 属性) }>; /** 嵌套子表单项(二维数组,用于嵌套表单) */ children?: Array<Array<IFormconfig>>; /** 默认值,优先级高于formItemProps.model */ defaultValue?: any; /** 后缀文字(如单位:%、元等) */ suffix?: string; } ``` ### 3. IFormProps ```ts /** * 表单整体配置类型 */ export interface IFormProps<T = IFormconfig> { /** 表单项配置数组 */ formConfig: T[]; /** el-row 组件属性 */ rowProps?: Record<string, any>; /** el-col 组件属性 */ colProps?: Record<string, any>; /** el-form 组件属性 */ formProps?: Record<string, any>; /** 是否显示嵌套表单操作按钮 */ showOperate?: boolean; } ``` ### 4. FormValueType ```ts /** * 表单数据对象类型,key 为 prop,value 为表单项的值 */ export type FormValueType = Record<string, any>; ``` ### 5. IUseForm ```ts /** * useForm 返回的所有方法和属性类型 */ export interface IUseForm { /** 表单组件(直接用于模板渲染) */ FastForm: DefineComponent; /** 响应式表单数据对象 */ formValue: FormValueType; /** 原始表单数据对象(非响应式) */ rawFormValue: FormValueType; /** el-form 实例引用 */ formRef: Ref<FormInstance | undefined>; /** 添加嵌套表单项(如数组表单 push) */ addItem: (prop: string, config?: IFormconfig[]) => void; /** 删除嵌套表单项(如数组表单 remove) */ removeItem: (prop: string, key: number) => void; /** 动态设置表单项组件属性(支持嵌套格式:parentProp.index.childProp) */ setComponentProps: ( prop: string, componentProps: Record<string, any> ) => void; /** 动态设置表单数据 */ setFormValue: (formData: Record<string, any>) => void; /** 动态设置表单项配置(支持嵌套格式:parentProp.index.childProp) */ setFormConfig: (prop: string, config: Partial<IFormconfig>) => void; /** 批量替换整个表单配置 */ setFormConfigs: (newFormConfig: IFormconfig[], isRemoveValue?: boolean) => Promise<void>; /** 动态添加表单项(支持嵌套格式:targetProp 可为 parentProp.index) */ addFormConfig: (config: IFormconfig, targetProp?: string, index?: number) => void; /** 动态删除表单项(支持嵌套格式:props 元素可为 parentProp.index.childProp) */ removeFormConfig: (props: string[]) => void; /** 禁用/启用整个表单 */ setFormDisabled: (disabled: boolean) => void; } ``` ## 组件透传属性 根据配置中的 `component` 类型,表单会自动透传不同的属性和方法给对应的组件: ### 1. Slot 组件(component: 'slot') 当组件类型为 `'slot'` 时,会调用对应的插槽函数并透传以下属性: ```ts // 插槽函数接收的参数类型 interface SlotProps { /** 整个表单的数据对象 */ formValue: FormValueType; /** 当前字段的值 */ modelValue?: any; /** 嵌套列表索引(仅在嵌套表单中存在) */ nestedKey?: number; /** 嵌套列表prop(仅在嵌套表单中存在) */ nestedProp?: string; } ``` **使用示例:** ```vue <template> <FastForm> <!-- 普通表单项的 slot --> <template #fieldName="{ formValue, modelValue }"> <div>当前值: {{ modelValue }}</div> <div>整个表单: {{ formValue }}</div> </template> <!-- 嵌套列表表单项的 slot --> <template #points2="{ formValue, nestedKey, nestedProp }"> <div class="nested-slot-content"> <!-- 自定义输入框 --> <el-input placeholder="请输入" v-model="formValue[nestedProp][nestedKey].points2" /> </div> </template> </FastForm> </template> ``` ### 2. 自定义 Vue 组件 当组件类型为自定义 Vue 组件时,会透传以下属性和方法: ```ts // 自定义组件接收的 props 类型 interface CustomComponentProps { /** 整个表单的数据对象 */ formValue: FormValueType; /** 当前字段的属性名 */ prop?: string; /** 当前字段的值 */ modelValue?: any; /** 嵌套列表索引 */ nestedKey?: number; /** 嵌套列表prop */ nestedProp?: string; /** 值更新回调函数 */ "onUpdate:modelValue"?: (value: any) => void; /** 配置中的所有组件属性 */ [key: string]: any; // 来自 itemConfig.componentProps // Form 类的所有公共方法: /** 添加嵌套表单项 */ addItem: (prop: string, config?: IFormconfig[]) => void; /** 删除嵌套表单项 */ removeItem: (prop: string, key: number) => void; /** 动态设置表单项组件属性(支持嵌套格式:parentProp.index.childProp) */ setComponentProps: ( prop: string, componentProps: Record<string, any> ) => void; /** 动态设置表单数据 */ setFormValue: (formData: Record<string, any>) => void; /** 动态设置表单项配置(支持嵌套格式:parentProp.index.childProp) */ setFormConfig: (prop: string, config: Partial<IFormconfig>) => void; /** 批量替换整个表单配置 */ setFormConfigs: (newFormConfig: IFormconfig[], isRemoveValue?: boolean) => Promise<void>; /** 动态添加表单项(支持嵌套格式:targetProp 可为 parentProp.index) */ addFormConfig: (config: IFormconfig, targetProp?: string, index?: number) => void; /** 动态删除表单项(支持嵌套格式:props 元素可为 parentProp.index.childProp) */ removeFormConfig: (props: string[]) => void; /** 禁用/启用整个表单 */ setFormDisabled: (disabled: boolean) => void; } ``` **使用示例:** ```vue <!-- 自定义头像上传组件 Avatar-upload/index.vue --> <template> <el-upload class="avatar-uploader" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" > <img v-if="imageUrl" :src="imageUrl" class="avatar" /> <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> </el-upload> </template> <script lang="ts" setup> import { ref, defineProps, watch, defineEmits } from "vue"; import { ElMessage } from "element-plus"; import { Plus } from "@element-plus/icons-vue"; const imageUrl = ref(""); // 接收透传的属性 const props = defineProps({ formValue: { // 整个表单数据对象 type: Object, }, modelValue: { // 当前字段的值 type: String, default: "", }, prop: { // 当前字段的属性名 type: String, }, // 还可以接收 Form 类的所有公共方法,如 addItem, removeItem, setComponentProps, setFormValue 等 }); // 监听 modelValue 变化,处理表单重置等场景 watch( () => props.modelValue, () => { imageUrl.value = props.modelValue; } ); // 定义事件,实现双向绑定 const emits = defineEmits(["update:modelValue"]); const handleAvatarSuccess = (response, uploadFile) => { imageUrl.value = URL.createObjectURL(uploadFile.raw!); }; const beforeAvatarUpload = async (rawFile) => { if (!["image/jpeg", "image/jpg", "image/png"].includes(rawFile.type)) { ElMessage.error("请传图片"); return false; } else if (rawFile.size / 1024 / 1024 > 2) { ElMessage.error("图片大小不能超过 2MB!"); return false; } // 处理文件并更新表单值 const filedata = await getImageInfo(rawFile); imageUrl.value = filedata; emits("update:modelValue", filedata); // 通知表单更新值 return true; }; const getImageInfo = (file: any): Promise<string> => { let fileReader = new FileReader(); fileReader.readAsDataURL(file); return new Promise((resolve) => { fileReader.onload = function (e) { resolve(this.result as string); }; }); }; </script> ``` **在表单配置中使用:** ```ts import AvatarUpload from "@/components/Avatar-upload/index.vue"; const formConfig: IFormconfig[] = [ { component: AvatarUpload, // 使用自定义组件 formItemProps: { prop: "avatar", label: "头像上传", }, // componentProps 中的属性也会透传给组件 componentProps: { // 可以传递其他自定义属性 }, }, ]; ``` ### 3. String 组件(HTML 标签字符串) 当组件类型为字符串(如 `'div'``'span'` 等)时,会透传: ```ts // String 组件接收的属性 interface StringComponentProps { /** 配置中的所有组件属性 */ [key: string]: any; // 来自 itemConfig.componentProps } // 子元素内容为 itemConfig.defaultValue 或 null ``` **使用示例:** ```ts const config: IFormconfig = { component: "div", componentProps: { class: "custom-text", style: { color: "red" }, }, defaultValue: "这是一个文本内容", formItemProps: { prop: "textField", label: "文本标签", }, }; ``` ### 4. Element Plus 组件(el- 前缀) 当组件类型为 Element Plus 组件(如 `'el-input'``'el-select'` 等)时,会自动透传: ```ts // Element Plus 组件接收的属性 interface ElementComponentProps { /** 当前字段的值 */ modelValue?: any; /** 值更新回调函数 */ "onUpdate:modelValue": (value: any) => void; /** 配置中的所有组件属性 */ [key: string]: any; // 来自 itemConfig.componentProps /** 自动设置的样式 */ style: { width: string; // 默认为 '100%' 或来自 componentProps.style.width }; /** 嵌套列表索引 */ nestedKey?: number; /** 嵌套列表prop */ nestedProp?: string; } ``` **注意事项:** - 所有透传的属性都来自于 `itemConfig.componentProps` 配置 - 自定义组件会额外获得 Form 类的所有公共方法 - 透传的方法包括表单操作方法(如 `addItem``removeItem`)和动态配置方法(如 `setComponentProps``setFormValue` 等) - 嵌套表单中的组件会额外获得 `nestedKey` 参数 - 组件的双向绑定通过 `modelValue` 和 `onUpdate:modelValue` 实现