又一款比肩阿里的表单生成器

0 阅读3分钟

推动更好的表单开发范式。更简洁,更高效,更容易CV. 让世界上没有难写的增删改查!

form表单组件配置模式一直以来颇受广大开发者争论,大家常争论的点在于:难维护,不直观,不灵活,节省不了什么代码量。

那么@zy-form/v2将颠覆传统JSON配置,以一种简洁,高效配置方式让你们开发如喝咖啡一样优雅!

最简示例

下面是一个最简单的使用方式,通过fields配置一个表单有哪些表单项,每个表单项使用什么类型的表单,通过这样简单的配置就可以驱动生成一个表单,简化了Dom相关代码。

<template>
    <zy-form
        :model="formData"
        :fields="fields"
    />
</template>
<script>
export default {
    name: 'App',
    data() {
        return {
            formData: {
                name: '',
                age: '',
                sex: '',
                hobby:''
            },
            fields: ({f1})=>{ //函数式配置,返回数组
                let list1 = [
                     {label:'男',value:1},
                     {label:'女',value:2}
                ]
                let list2 = [
                    {label:'唱歌',value:1},
                    {label:'跳舞',value:2},
                    {label:'Rap',value:3},
                    {label:'篮球',value:4}
                ]
                return [
                    f1('名称','name','input'),
                    f1('年龄','age','number'),
                    f1('性别','sex','radio').l(list1),
                    f1('爱好','hobby','select').l(list2),
                ]
            }
        };
    }
};
</script>
效果

微信截图_20241126144508.png

表单项配置扩展

表单项组件可能有不同的属性,比如input组件可能需要配置type、placeholder等属性,select,radio;同样地,如果需要监听组件的事件与配置。配置属性可以传入config方法,事件注册可以传入event方法。

<script>
export default {
    name: 'App',
    data() {
        return {
            fields: ({f1})=>{ //函数式配置,返回数组
                let list2 = [
                    {label:'唱歌',value:1},
                    {label:'跳舞',value:2},
                    {label:'Rap',value:3},
                    {label:'篮球',value:4}
                ]
                return [
                    f1('名称','name','input').config({
                        type: 'textarea',
                        placeholder: '请输入您的名称'
                    }).event({
                        input:function(e){
                            console.log('输入框触发事件', e)
                        }
                    }),
                    f1('爱好','hobby','select').l(list2).event({
                        change:function(val,self,row){
                            console.log('下拉触发事件', val,self,row)
                        }
                    }),,
                ]
            }
        };
    }
};
</script>
效果

微信截图_20241126155820.png

表单项校验规则

表单项的校验规则也放到r方法中配置,这样就把一个表单项的所有配置集中到了一起,更加的高内聚。只要执行r函数默认就开启了非空判断!

<script>
export default {
    name: 'App',
    data() {
        return {
            fields: ({f1})=>{ //函数式配置,返回数组
                return [
                    f1('名称','name','input').r()
                ]
            }
        };
    }
};
</script>

同时r方法还内置了常用的正则判断,如:手机号,身份证号,邮箱,大小写,数字等

类型功能
URLURL格式
LowerCase小写字母
UpperCase大写字母
Alphabets字母
Email邮箱
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>
效果

0.png

同时还一种验证情况,就是可以为空。但是如果不为空的情况下格式就必须校验。只需r方法传入第二个参数为false

<script>
export default {
    name: 'App',
    data() {
        return {
            fields: ({f1})=>{ //函数式配置,返回数组
                return [
                    f1('邮箱','xxx','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>
效果

1111.png

上面介绍了这么多,感兴趣的朋友一定想亲自试一试。由于组件还在测试调优阶段。很多功能还在陆续更新,胆子大家人们可以直接上项目,组件还算稳定。只是有些功能还需要加强设计与优化。该组件的初代设计者同时也是掘金优秀作者:@前端林叔。 本组件经过作者同意进行了加强与优化。

如何下载与使用

目前只支持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')