Form表单组件配置模式一直以来颇受广大开发者争论,大家常争论的点在于:难维护,不直观,不灵活,节省不了什么代码量。这篇文章将颠覆传统JSON配置,以一种简洁,高效的配置方式推动更好的表单开发范式。让世界上没有难写的增删改查!让Web开发如喝咖啡一样优雅!
最简示例
简单的配置就可以驱动生成一个表单,简化了Dom相关代码。
<template>
<zy-form
:model="formData"
:fields="fields"
/>
</template>
<script>
export default {
name: 'App',
data() {
return {
formData: {
name: '',
age: '',
},
fields: ({f1})=>{
return [
f1('名称','name','input'),
f1('年龄','age','number'),
]
}
};
}
};
</script>
表单项配置扩展
有过一定开发经验的小伙伴们,通过上面的示例,大家也能猜到该组件到底想做什么了。但是实际开发过程中,表单项的属性是比较复杂的,如element UI里面的 input Attributes内容就有10多项,还有select、radio、checkbox等等这些,那我们又该如何优雅的进行配置呢?
- input
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
//配置扩展可以调用config方法,传一个配置对象即可
return [
f1('名称','name','input').config({
type: 'textarea', //类型
placeholder: '请输入您的名称', //占位文字
clearable:true, //是否可清空
maxlength:150 //最大输入长度
//...略
})
]
}
};
}
};
</script>
- select
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
//select,radio,checkbox 都需要传选项集合,可以通过 l 方法
//配置扩展可以调用config方法,传一个配置对象即可
let sexList = [
{label:'男',value:1},
{label:'女',value:2}
]
return [
f1('性别','sex','select').l(sexList).config({
multiple: false, //是否多选
placeholder: '请选择', //占位文字
clearable:true, //是否可清空
filterable:true //是否可搜索
//...略
})
]
}
};
}
};
</script>
细心的小伙伴可能会说,select、radio、checkbox这些选项如果是通过后端API接口获取的,又该如果配置进去呢?
<script>
export default {
name: 'App',
data() {
return {
//只需要在l方法里面传入函数体
//这里需要注意的是l方法只解析[{label:'',value:''}]格式
//假如接口数据是[{name:'',id:''}],则需要在l方法的第二次参数配置key与val
fields: ({f1})=>{
return [
f1('性别','sex','select').l(this.getList,{k:'name',v:'id'})
]
}
};
},
methods:{
getList(){
return this.$axios.get('xxx').then(res=>{
return res.data //必须返回数组
})
}
}
};
</script>
表单项方法注册
<script>
export default {
name: 'App',
data() {
return {
//如果select需要加入change方法,那么只需要写入event
fields: ({f1})=>{
return [
f1('性别','sex','select').l(this.getList,{k:'name',v:'id'}).event({
change(val,self,row){
console.log('选中的值',val) // 1
console.log('当前选中的所有属性',self) // {name:'奥迪',id:'1',...}
console.log('当前配置项',row)
}
}),
f1('名称','name','input').event({
input(val){
console.log('输入的值',val)
},
blur(val){
console.log('失去焦点',val)
},
//...
}),
]
}
};
},
methods:{
getList(){
return this.$axios.get('xxx').then(res=>{
return res.data //必须返回数组
})
}
}
};
</script>
表单项校验规则
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
//r方法默认开启了非空校验!
return [
f1('名称','name','input').r()
]
}
};
}
};
</script>
同时r方法还内置了常用的正则判断,如:手机号,身份证号,邮箱,大小写,数字等
类型 | 功能 |
---|---|
URL | URL格式 |
LowerCase | 小写字母 |
UpperCase | 大写字母 |
Alphabets | 字母 |
邮箱 | |
Phone | 手机号 |
Number | 数字 |
Fax | 传真 |
IntPlus | 正整数 |
Encode | 编码只能使用字母、数字、下划线、中划线 |
Encode2 | 编码只能使用字母、数字 |
Encode3 | 编码只能使用字母、数字、下划线 |
IdCard | 身份证号码 |
CarNum | 车牌号 |
CNandEN | 中文、英文 |
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
return [
f1('邮箱','xxx','input').r('Email') //验证邮箱
f1('多重验证','xxx','input').r(['CNandEN','URL']) //数组形式传入多个
f1('自定义验证','xxx','input').r(/^\d+$/) //直接传入正则
f1('多个自定义验证','xxx','input').r([/^\d+$/,/^[a-zA-Z\u4e00-\u9fa5]+$/])
//直接传入正则数组
]
}
};
}
};
</script>
效果
同时还一种验证情况,就是可以为空。但是如果不为空的情况下格式就必须校验。只需r方法传入第二个参数为false
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
return [
//验证邮箱,邮箱可以为空,一旦输入就必须验证
f1('邮箱','email','input').r('Email',false)
]
}
};
}
};
</script>
组件联动
不同表单项之间可能有一些联动关系,比如当姓名为空时禁用年龄的编辑,只需要通过配置表单项的disabled属性即可,disabled属性支持Boolean和Function两种,其中函数的参数对当前表单的model值。属性只要是布尔值的都支持Boolean和Function两种!
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{ //函数式配置,返回数组
return [
f1('姓名','name','input'),
f1('年龄','old','input').config({
disabled:(formData) => !formData.name
})
]
}
};
}
};
</script>
如果想要配置没有输入name时隐藏age表单项也非常简单,只需要开启h方法,和disabled类似。
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
return [
f1('姓名','name','input'),
f1('年龄','old','input').h((formData)=>{
return formData.name
})
]
}
};
}
};
</script>
自定义表单项
如果某个表单项比较复杂,你也可以将其封装成单独的组件,只要支持双向绑定v-model,就可以和配置表单无缝结合。
<script>
import CustomComponent from './CustomComponent.vue'
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{ //函数式配置,返回数组
return [
f1('自定义组件','xxx',CustomComponent),
]
}
};
}
};
</script>
插槽
我推荐大家能将特殊的表单项封装成组件进行使用,然而有时可能确实没必要,那么也支持插槽方式进行扩展。 如果要定制表单项label的实现,可以使用名为 '属性名_label'的插槽进行扩展,以表单项name为例,就是 slot="name_label"。 如果要定制表单项的实现,则使用名为属性名的插槽。
<template>
<config-form
:model="formData"
:fields="fields"
>
<template #name_label>
<span style="color:red;">您的大名</span>
</template>
<template #name>
<input v-model="formData.name" /> <i class="el-icon-question"></i>
</template>
</config-form>
</template>
<script>
export default {
name: 'App',
data() {
return {
formData: {
name: ''
},
fields: ({f1})=>{ //函数式配置,返回数组
return [
f1('名称','name'),
]
}
};
}
};
</script>
表单布局
默认的表单布局是一行两列。如果需要改变布局。只需要在 config-form 组件上面传入 row 属性,布局默认采用的是element ui的栅格(24/2)
<template>
<config-form
:model="formData"
:fields="fields"
:row='3'
>
</config-form>
</template>
<script>
export default {
name: 'App',
data() {
return {
formData: {
name: ''
},
fields: ({f1})=>{ //函数式配置,返回数组
//如果定义一行三列,栅格则是24/3
return [
f1('名称','name'),
f1('名称','name'),
f1('名称','name'),
f1('名称','name'),
f1('名称','name'),
f1('名称','name'),
]
}
};
}
};
</script>
如果想要灵活的自定义,比如希望第一行是1列,第二行是2列,第三行是3列。只需要把返回的数组改成二维数组。
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{ //函数式配置,返回数组
return [
[
f1('名称','name'),
],
[
f1('名称','name'),
f1('名称','name'),
],
[
f1('名称','name'),
f1('名称','name'),
f1('名称','name'),
]
]
}
};
}
};
</script>
效果
自定义一些常用配置的方法
其实通过上面的案例,大家也熟悉了配置的一些规律,无非就是采用函数链式调用的形式,取代了JSON配置, 如插件内置了 r、l、h、config、event这些方法都可以以链式调用的形式,增强表单的一些功能属性。那么如果配置较多的情况下,config是不是会显得非常臃肿。其实这一点我也帮你们考虑好了。
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
//配置扩展可以调用config方法太臃肿了
return [
f1('名称','name','input').config({
type: 'textarea', //类型
placeholder: '请输入您的名称', //占位文字
clearable:true, //是否可清空
maxlength:150 //最大输入长度
//...略10项
})
]
}
};
}
};
</script>
main.js
<script>
import ConfigForm from "@zy-form/v2";
import ConfigFormPluginElement from '@config-form/plugin-element'
Vue.use(ConfigForm, {
presets: [ConfigFormPluginElement], //组件预设
componentMaps: {},//自定义组件支持的类型
configuration: { //预设常用的方法配置项
//占位
p: function (value) {
return {
placeholder:value
}
},
//最大长度
ml: function (value) {
return {
maxlength:value
}
},
//是否可清空
c:function () {
return {
clearable:true
}
},
//类型
t:function(val){
return {
type:val
}
}
},
//设置每个组件的默认样式
defaultProps: {
form: {
labelWidth: '120px'
}
}
})
</script>
app.vue
<script>
export default {
name: 'App',
data() {
return {
fields: ({f1})=>{
//通过入口文件的配置预设,减少业务代码的繁琐配置
return [
f1('名称','name','input')
.p('请输入您的名称')
.c()
.ml(150)
.t('textarea').config({
//...略10项
})
]
}
};
}
};
</script>
上面介绍了这么多,感兴趣的朋友一定想亲自试一试。由于组件还在测试调优阶段。很多功能还在陆续更新,胆子大家人们可以直接上项目,组件还算稳定。只是有些功能还需要加强设计与优化。同时也感谢该组件的初代设计者同时也是掘金优秀作者:@前端林叔
如何下载与使用
目前只支持vue2,目前需要借助ElementUI,确保项目中已经使用了ElementUI。
npm i @zy-form/v2 @config-form/plugin-element
main.js中引入:
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import ConfigForm from "@zy-form/v2";
import ConfigFormPluginElement from '@config-form/plugin-element'
Vue.config.productionTip = false
Vue.use(ElementUI);
Vue.use(ConfigForm, {
presets: [ConfigFormPluginElement], //组件预设
componentMaps: {
//自定义组件支持的类型
},
//设置每个组件的默认样式
defaultProps: {
form: {
labelWidth: '120px'
}
}
})
new Vue({
render: h => h(App),
}).$mount('#app')