基于 el-form 封装一个依赖 json 动态渲染的表单控件( 二 )

封装各种表单子控件按照原子性原则,子控件封装得比较细,直接看图:

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
代码有点多,不一一介绍了,感兴趣的可以看源码 。
封装表单控件基础工作做好之后,我们就可以封装 el-form 了 。
定义属性依据 el-form 的属性我们定义几个关键性属性
介绍属性/** * 表单控件需要的属性 */export const formProps = {modelValue: Object, // 完整的modelpartModel: Object, // 根据选项过滤后的modelminiModel: Object, // 精简的model/** 自定义子控件 key:value形式* * key: 编号 。1:插槽;100-200:保留编号* * value:string:标签;函数:异步组件,类似路由的设置*/customerControl: { // 自定义的表单子组件type: Object,defaule: () => {}},colOrder: { // 表单字段的排序的依据type: Array,default: () => []},formColCount: { // 表单的列数type: Number,default: 1},reload: {type: Boolean, // 是否重新加载配置,需要来回取反default: false},itemMeta: {type: Object, // 表单子控件的属性default: () => {}},ruleMeta: { // 验证信息type: Object,default: () => {}},formColShow: { // 数据变化,联动组件是否显示type: Object,default: () => {}} }定义内部model一般一个 model 就可以,只是这里做了一个组件联动的,那么如果只需要获取可见的组件的值呢,于是做了局部model 。
基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
实现多行多列和布局调整采用 el-col 实现,通过控制 span 来实现多列,所以理论上最多支持24列,当然这个要看屏幕宽度了 。
/** * 处理一个字段占用几个td的需求 * @param { object } props 表单组件的属性 * @returns*/const getColSpan = (props) => {// 确定一个组件占用几个格子const formColSpan = reactive({})// 表单子控件的属性const formItemProps = props.itemMeta// 根据配置里面的colCount,设置 formColSpanconst setFormColSpan = () => {const formColCount = props.formColCount // 列数const moreColSpan = 24 / formColCount // 一个格子占多少份if (formColCount === 1) {// 一列的情况for (const key in formItemProps) {const m = formItemProps[key]if (typeof m.colCount === 'undefined') {formColSpan[m.controlId] = moreColSpan} else {if (m.colCount >= 1) {// 单列,多占的也只有24格formColSpan[m.controlId] = moreColSpan} else if (m.colCount < 0) {// 挤一挤的情况,24 除以 占的份数formColSpan[m.controlId] = moreColSpan / (0 - m.colCount)}}}} else {// 多列的情况for (const key in formItemProps) {const m = formItemProps[key]if (typeof m.colCount === 'undefined') {formColSpan[m.controlId] = moreColSpan} else {if (m.colCount < 0 || m.colCount === 1) {// 多列,挤一挤的占一份formColSpan[m.controlId] = moreColSpan} else if (m.colCount > 1) {// 多列,占的格子数 * 份数formColSpan[m.controlId] = moreColSpan * m.colCount}}}}}return {formColSpan,setFormColSpan}}首先计算一下一列要用多少个span,也就是用24除以列数 。
然后判断是不是单列,单列要处理多个组件占用一个位置的需求,多列要处理一个组件占用多个位置的需求 。
实现扩展表单子控件可以多种多样,无法完全封装进入表单控件,那么就需要表单控件支持子控件的扩展 。
这里要感谢 vue 的动态组件功能,让扩展子控件变得非常方便 。
我们使用 component 和动态组件来实现表单子控件的加载 。
<component:is="formItemListKey[getCtrMeta(ctrId).controlType]"v-model="formModel[getCtrMeta(ctrId).colName]"v-bind="getCtrMeta(ctrId)"@my-change="myChange"></component>export const formItemList = {// 文本类 defineComponent'el-form-text': defineAsyncComponent(() => import('./t-text.vue')),'el-form-area': defineAsyncComponent(() => import('./t-area.vue')),'el-form-url': defineAsyncComponent(() => import('./t-url.vue')),'el-form-password': defineAsyncComponent(() => import('./t-password.vue')),// 数字'el-form-number': defineAsyncComponent(() => import('./n-number.vue')),'el-form-range': defineAsyncComponent(() => import('./n-range.vue')),// 日期、时间'el-form-date': defineAsyncComponent(() => import('./d-date.vue')),'el-form-datetime': defineAsyncComponent(() => import('./d-datetime.vue')),'el-form-year': defineAsyncComponent(() => import('./d-year.vue')),'el-form-month': defineAsyncComponent(() => import('./d-month.vue')),'el-form-week': defineAsyncComponent(() => import('./d-week.vue')),'el-form-time-select': defineAsyncComponent(() => import('./d-time-select.vue')),'el-form-time-picker': defineAsyncComponent(() => import('./d-time-picker.vue')),// 选择、开关'el-form-checkbox': defineAsyncComponent(() => import('./s-checkbox.vue')),'el-form-switch': defineAsyncComponent(() => import('./s-switch.vue')),'el-form-checkboxs': defineAsyncComponent(() => import('./s-checkboxs.vue')),'el-form-radIOS': defineAsyncComponent(() => import('./s-radios.vue')),'el-form-select': defineAsyncComponent(() => import('./s-select.vue')),'el-form-selwrite': defineAsyncComponent(() => import('./s-selwrite.vue')),'el-form-select-cascader': defineAsyncComponent(() => import('./s-select-cascader.vue'))}/** * 动态组件的字典,便于v-for循环里面设置控件 */export const formItemListKey = {// 文本类100: formItemList['el-form-area'], // 多行文本101: formItemList['el-form-text'], // 单行文本102: formItemList['el-form-password'], // 密码103: formItemList['el-form-text'], // 电话104: formItemList['el-form-text'], // 邮件105: formItemList['el-form-url'], // url106: formItemList['el-form-text'], // 搜索// 数字120: formItemList['el-form-number'], // 数字121: formItemList['el-form-range'], // 滑块// 日期、时间110: formItemList['el-form-date'], // 日期111: formItemList['el-form-datetime'], // 日期 + 时间112: formItemList['el-form-month'], // 年月113: formItemList['el-form-week'], // 年周114: formItemList['el-form-year'], // 年115: formItemList['el-form-time-picker'], // 任意时间116: formItemList['el-form-time-select'], // 选择固定时间// 选择、开关150: formItemList['el-form-checkbox'], // 勾选151: formItemList['el-form-switch'], // 开关152: formItemList['el-form-checkboxs'], // 多选组153: formItemList['el-form-radios'], // 单选组160: formItemList['el-form-select'], // 下拉161: formItemList['el-form-selwrite'], // 下拉多选162: formItemList['el-form-select-cascader'] // 下拉联动}


推荐阅读