yudada-form
v0.1.12
Published
Dynamic form component for Vue 3 + Element Plus
Maintainers
Readme
yudada-form
基于 Vue 3 + Element Plus 的配置驱动动态表单组件,通过 config JSON 自动渲染表单字段、生成校验规则,并把字段变化事件透传给父组件(可用于“无查询按钮也能自动刷新数据”的交互)。
特性一览
- 支持
config为Array(普通表单)或Object(分组表单) - 支持多类型表单控件(输入、选择、日期时间、开关、滑块、评分、穿梭框等)
- 支持自动校验:
required自动生成校验规则;支持rules和customRules叠加 - 支持动态显示/隐藏:
visible或show(formData, item)函数 - 支持字段联动:
- 父组件监听
field-change/field-input - 或在字段配置中使用
onChange/onInput
- 父组件监听
- 支持
slot与suffixSlot:在控件内部渲染自定义内容、以及在表单项后缀渲染自定义内容 - 支持表单方法透出:
validate/validateField/resetFields/clearValidate - 支持扩展/覆盖内置组件映射:
customComponentMap
安装
pnpm add yudada-form
pnpm add element-plus使用
全局注册(推荐)
import { createApp } from "vue";
import ElementPlus from "element-plus";
import YudadaForm from "yudada-form";
import "element-plus/dist/index.css";
import App from "./App.vue";
const app = createApp(App);
app.use(ElementPlus);
app.use(YudadaForm);
app.mount("#app");模板中直接使用:
<yudada-form :config="formConfig" v-model="formData" />从
0.1.2起,组件样式会随 JS 自动注入;一般不需要再手动import "yudada-form/style.css"。
局部引入
import { YudadaForm } from "yudada-form";<template>
<YudadaForm v-model="formData" :config="formConfig" />
</template>组件 Props
组件的 props 主要用于控制表单整体样式、状态与分组行为:
config:Array | Object(必填),表单字段配置;Array为普通表单,Object为分组表单modelValue:Object(可用v-model),表单数据对象labelWidth:string,label 宽度labelPosition:"left" | "right" | "top",label 位置(默认right)inline:boolean,是否内联布局size:"" | "medium" | "small" | "mini",Element Plus 尺寸disabled:boolean,禁用整个表单;单字段也可通过字段disabled进一步控制showSectionTitle:boolean,分组表单是否显示 section 标题(默认true)validateOnRuleChange:boolean,校验规则变更时是否重新触发校验(默认true)sectionClass:string,给分组内容区域添加额外 classdefaultShowLabel:boolean,整张表单字段是否默认显示 label(默认true)defaultShowColon:boolean,整张表单字段是否默认显示 label 后的冒号(默认true)customComponentMap:Object,扩展/覆盖type -> component映射(用于自定义组件类型)
最简单用法(JSON 生成表单)
<template>
<yudada-form :config="formConfig" v-model="formData" />
</template>
<script setup lang="ts">
import { ref } from "vue";
const formData = ref({});
const formConfig = ref([
{ label: "用户名", type: "input", model: "username", required: true },
{ label: "密码", type: "password", model: "password", required: true },
{
label: "性别",
type: "select",
model: "gender",
options: [
{ label: "男", value: "male" },
{ label: "女", value: "female" },
],
},
]);
</script>config 字段结构(核心)
config 的每个字段项至少需要:
label:展示文字type:控件类型(决定用哪个 Element Plus 组件)model:表单数据字段名(会读写v-model的对象属性)
普通表单 vs 分组表单
- 普通表单:
config为Array - 分组表单:
config为Object,例如:
const formConfig = {
baseInfo: [
{ label: "姓名", type: "input", model: "name", required: true },
],
otherInfo: [
{ label: "备注", type: "textarea", model: "remark" },
],
};分组表单下,内部校验 prop 会使用 ${sectionName}.${item.model}(例如 baseInfo.name),因此 validateField/clearValidate 若传入 prop,需要按这个规则使用。
字段项常用配置
字段项(item)支持常见字段属性如下(均为可选,按需使用):
required: boolean:是否必填(会自动生成rules)rules: Array:Element Plus 表单校验规则(会直接参与校验)customRules: Array:额外校验规则(会与自动生成/已有rules进行合并)defaultValue: any:当modelValue中不存在该model时的默认值覆盖disabled: boolean:字段禁用(会与组件级disabled共同生效)placeholder: string:输入提示labelWidth: string:覆盖该字段的 label 宽度showLabel: boolean:仅控制当前字段是否显示 label(优先级高于组件defaultShowLabel)showColon: boolean:仅控制当前字段是否显示 label 后冒号(优先级高于组件defaultShowColon)className: string:覆盖该字段classfullWidth: boolean:追加full-width类formItemProps: Object:透传给el-form-itemvisible: boolean:控制该字段是否渲染(false则不渲染)show: (formData, item) => boolean:根据其他字段值动态决定是否渲染onChange: (value, formData, item) => void:控件change触发时回调(可做字段联动)onInput: (value, formData, item) => void:控件input触发时回调(可做字段联动)slot: string:指定要渲染的具名 slot 名称(在控件内部)suffixSlot: string:指定要渲染的后缀具名 slot 名称(在el-form-item后缀位置)
options / valueKey / labelKey
对于 select、select-multiple、radio、checkbox-group、cascader 等:
options: Array:选项列表valueKey: string:当 option 为对象时,用哪个字段作为 value(默认value)labelKey: string:当 option 为对象时,用哪个字段作为 label(默认label)- option 上可带
disabled: boolean:用于禁用单个选项
自动校验规则与触发器
组件内部在初始化 config 后,会为每个字段生成 formRules:
- 若
required: true且未显式提供rules,组件会自动生成一条规则:required: truemessage: "${label}不能为空"trigger根据字段type自动映射(输入类默认blur,选择类/开关类/滑块/评分/穿梭框默认change)
- 若提供了
customRules: Array,则会把它们追加到当前规则数组中 - 若提供了
rules: Array,则会直接使用(并且仍可能被customRules追加)
触发器映射(来自组件内部 getTriggerType):
input/textarea/number/input-number/password:blurselect/select-multiple/cascader/date*/time*/radio/checkbox-group/switch/slider/rate/color/transfer:change
表单校验方法
父组件可通过 ref 调用(组件内部直接转发到 el-form):
validate(callback):整体校验validateField(props, callback):单字段校验resetFields():重置所有字段并清除校验状态clearValidate(props):清除指定字段校验状态
示例:
<script setup lang="ts">
import { ref } from "vue";
const formRef = ref();
const submit = () => {
formRef.value?.validate((valid: boolean) => {
if (!valid) return;
// 在此提交 formData
});
};
</script>
<template>
<yudada-form ref="formRef" :config="formConfig" v-model="formData" />
<el-button type="primary" @click="submit">提交</el-button>
</template>字段联动与“自动查询”(无查询按钮也能刷新)
组件会对每次字段变化触发事件:
field-change:当控件触发change时触发field-input:当控件触发input时触发
事件签名:
field-change(fieldModel, value, item)field-input(fieldModel, value, item)
父组件监听事件并查询(推荐)
<yudada-form
v-model="formData"
:config="formConfig"
@field-change="handleFieldChanged"
@field-input="handleFieldChanged"
/>const handleFieldChanged = (fieldModel: string, value: any) => {
// 这里做 debounce 更稳妥,避免输入框频繁请求
queryData({ ...formData.value });
};注意:组件内部还会在
formData变化时持续 emitchange,因此如果你只想要“字段变化后立即查询”,监听field-change/field-input通常更精确。
config 内部直接做联动(onChange / onInput)
你也可以在字段配置里实现联动。onChange/onInput 会收到当前 formData 引用:
{
label: "类型",
type: "select",
model: "type",
options: [
{ label: "默认", value: "default" },
{ label: "其他", value: "other" },
],
onChange: (value, formData) => {
// 直接改同一个 formData 对象,其他字段会自动跟着更新显示
formData.remark = value === "other" ? "" : "不需要备注";
},
}动态显示/隐藏(show / visible)
{
label: "备注",
type: "textarea",
model: "remark",
show: (formData) => formData.type === "other",
}或者:
{
label: "备注",
type: "textarea",
model: "remark",
visible: false,
}Label / 冒号显示控制(全局 + 单项覆盖)
组件支持两层控制方式:
- 组件级默认值(作用于整张表单)
defaultShowLabel:默认是否显示 label(默认true)defaultShowColon:默认是否显示冒号(默认true)
- 字段级覆盖(作用于单个字段)
showLabelshowColon
优先级规则:字段级配置 > 组件级默认值。
示例:整表默认不显示,单字段开启
<template>
<yudada-form
v-model="formData"
:config="formConfig"
:default-show-label="false"
:default-show-colon="false"
label-width="100px"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
const formData = ref({});
const formConfig = ref([
{
label: "流程编号",
type: "input",
model: "flowNo",
// 覆盖全局:当前字段显示 label 和冒号
showLabel: true,
showColon: true,
},
{
label: "任务类型",
type: "select",
model: "taskType",
options: [],
// 跟随全局:不显示 label/冒号
},
{
label: "申请人",
type: "select",
model: "applicant",
options: [],
// 仅显示 label,不显示冒号
showLabel: true,
showColon: false,
},
]);
</script>说明:当字段最终不显示 label(
showLabel=false)且未单独设置labelWidth时,组件会自动把该字段label-width设为0px,避免左侧空白占位。
分组表单标题
当 config 为对象且启用 showSectionTitle=true 时,组件会渲染每个 section 的标题。
组件内置了一个标题映射:
baseInfo->基本信息otherInfo->其他信息contractInfo->合同信息detailInfo->详细信息
其余 sectionName 会原样显示。
你也可以通过 sectionClass 控制表单分组区域的 class。
事件列表(emits)
组件 emits 如下:
update:modelValue:formData变化时同步更新父组件的v-modelchange:formData变化时触发(等价于“表单整体变化”)field-change:字段change触发(更适合选择/开关类联动)field-input:字段input触发(更适合输入框联动)
slots:slot 与 suffixSlot
字段项支持两个槽位配置:
slot: string:在“具体控件内部”渲染具名 slotsuffixSlot: string:在el-form-item的suffix位置渲染具名 slot
示例(在组件内部插入内容):
{
label: "用户名",
type: "input",
model: "username",
slot: "usernamePrefix",
}父组件:
<yudada-form :config="formConfig" v-model="formData">
<template #usernamePrefix="{ item, value }">
<span style="margin-right: 8px;">当前:{{ value }}</span>
</template>
</yudada-form>suffixSlot 示例同理,只是 slot 会挂在 el-form-item 的 suffix:
{ label: "年龄", type: "input-number", model: "age", suffixSlot: "ageSuffix" }控件类型映射(type -> 组件)
组件内置映射(未匹配到时默认用 el-input):
input/number/password:el-inputtextarea:el-inputinput-number:el-input-numberselect/select-multiple:el-selectcascader:el-cascadertime-picker:el-time-pickertime-select:el-time-selectdate/date-select/datetime/datetime-picker/year-select/month/week/daterange/datetimerange/monthrange:el-date-pickerradio:el-radio-groupcheckbox-group:el-checkbox-groupswitch:el-switchslider:el-sliderrate:el-ratecolor:el-color-pickertransfer:el-transfer
扩展/覆盖内置组件:customComponentMap
当你业务里有自定义组件类型(例如你项目原来用 gj-* 组件)时,可以通过 customComponentMap 扩展 type -> component 映射。
const customComponentMap = {
"map-select": MyMapSelect,
};<yudada-form
:config="formConfig"
v-model="formData"
:custom-component-map="customComponentMap"
/>默认值行为
当 modelValue 初始对象中不包含某字段的 model 时,组件会在初始化时补默认值:
select-multiple:[]checkbox-group:[]switch:falserate:0slider:0- 其他:
""
若字段项提供了 defaultValue,则优先使用 defaultValue。
构建与发布(仓库侧)
pnpm i
pnpm build发布到 npm(或私服):
npm login
npm publish