likes
comments
collection
share

实现一个配置分离的复杂表单

作者站长头像
站长
· 阅读数 11

实现一个配置分离的复杂表单

示例使用uniApp开发,示例传送门:访问地址(h5)

主要功能

  • 表单通过单独文件配置,按规定格式配置即可自动生成表单输入项。
  • 可控制表单输入类型:文本输入(可限制输入类型如numbertext)、单选、颜色选择、checkbox(布尔类型控制)。
  • 支持配置默认值、placeholder、字段提示文字(弹窗显示)
  • 可单独控制是否显示,也可设置联动显示(根据某一字段的值控制是否显示)
  • 精准追查到每一项的改动。

!!!请不要用到生产环境中,只是一个小Demo,功能上会有不完整,不会涵盖到所有的输入类型,输入校验也没有。

先上示例图: 实现一个配置分离的复杂表单


实现一个配置分离的复杂表单


实现一个配置分离的复杂表单


生成的结果

导出结果的数据结构如下: 实现一个配置分离的复杂表单

部分表单配置项

考虑到当表单项太多情况下放到一起会显得太用臃肿,采用分组的形式。 分组可单独控制是否显示, key是最后生成结果的对象键,对应上面结果的 nameOpt 分组中每一项的配置以数组形式保存在nameOpt.configOpt

  • label: 显示的名称
  • isShow: 控制当前项是否显示
  • name: 生成配置的键
  • value: 当前值 为null时 最后的结果取 default的值
  • default: 默认值
  • type: 表单类型(目前定义有 Boolean, Input, Color, Radio)
  • root:导出在 root的对象下,加入这个可以使当前项显示在当前分组下,最后导出结果时却在其他的对象下(跨分组甚至独立出来)
  • fieldInfo: 字段提示文字(设置了这个表单右侧会有个问号,点击会弹窗显示这个里面的东东)
  • fieldType: type = Input时生效,指定输入框的类型
  • placeholder: placeholder不解释
  • radioList: type = Radio时生效,可选择的列表
  • showWhen: Array<Object> : 联动控制是否显示(根据其他项的值控制是否显示本项,可 n 对 一, 可跨分组控制)
    • 以下示例配置中showWhen中的每一项:optKey:控制项的分组keyroot:控制项的root, name:控制项的name,value:控制项中值在value中存在则显示。

以下是一个分组的配置。

const nameOpt = {
	title: '标题配置',
	isShow: true,
	key: 'nameOpt',
	configOpt: [
		{ label: '是否展示', isShow: true, name: 'isShow', value: true, default: true, type: 'Boolean', root: 'nameOpt', fieldInfo: '是否展示标题'},
		{ label: '标题文字', isShow: true, name: 'title', value: null, default: '', root: 'nameOpt', type: 'Input', fieldType: 'text', placeholder: '标题文字' },
		{ label: '是否加粗', isShow: true, name: 'bold', value: true, default: true, root: 'nameOpt',type: 'Boolean' },
		{ label: '文字颜色', isShow: true, name: 'color', value: null, default: '#ffffff', root: 'nameOpt',type: 'Color', fieldType: 'text',placeholder:'#ffffff' },
		{
			label: "对齐方式",
			isShow: true,
			name: 'align',
			value: "center",
			type: "Radio",
			default: 'center',
			root: 'nameOpt',
			placeholder: '',
			radioList: [
                { label: '左', value: 'left', }, { label: '居中', value: 'center' }, { label: '右', value: 'right' }],
            showWhen: [{
                    optKey: 'tableColumnOpt',
                    root: 'tableColumnOpt', 
                    name: 'columnFmt', 
                    value: ['number','currency', 'percentage']
                }
			],
		}
	]
}
// ...

实现思路

初始化过程 初始配置数据: 实现一个配置分离的复杂表单

  1. 拿到配置的数据,遍历分组转换成数组的形式,同时遍历分组中的configOpt,将存在showWhen的缓存到 this.autoShowItems数组中(方便以后某一项改变时触发是否显示会用到,先缓存起来)。 最终得到的列表数据(editList)与需要根据其他项控制是否显示的数组(autoShowItems): 实现一个配置分离的复杂表单
  2. 根据 autoShowItems处理对应项是否显示状态(避免初始化状态错乱)。
  3. 根据列表数据(editList)生成结果集
// 初始化
initConfigList(tableConfig){
    // 配置数据
    console.log(' =====> tableConfig', tableConfig);
    this.autoShowItems = this.autoShowItems.splice(0)
    let list = []
    // 将配置对象装换成数组,并保存所有autoShowItem 对象信息
    Object.keys(tableConfig).forEach(tKey => {
        list.push(tableConfig[tKey])
        tableConfig[tKey].configOpt.forEach(cItem => {
            if(cItem.showWhen) this.autoShowItems.push({...cItem, selfOptKey: tableConfig[tKey].key})
        })
    })
    // 处理自动显示/隐藏的项
    this.autoShowItems.forEach(item => {
        let changeItem = list.find(cItem => cItem.key === item.selfOptKey)
        if(!changeItem) return
        let changeOptItem = changeItem.configOpt.find(cItem => cItem.root == item.root && cItem.name == item.name)
        if(!changeOptItem) return
        let isShow = false
        item.showWhen.forEach(sItem => {
            let { optKey, root, name, value} = sItem
            const decisionItem = list.find(lItem => lItem.key === optKey)
            let decCitem = decisionItem.configOpt.find(cItem => cItem.root == root && cItem.name == name)
            if(value.includes(decCitem.value)) isShow = true
        })
        changeOptItem.isShow = isShow
    })
    console.log(' =====> list', list);
    console.log(' =====> this.autoShowItems', this.autoShowItems);
    this.editList = [...list]
},
/**
 * 初始化,根据配置列表生成配置对象
 * @param {Object} 
 */
initResultObj(editList) {
    this.resultObj = editList.reduce((obj, item) => {
        item.configOpt.forEach(it => {
            if (!obj[it.root]) obj[it.root] = {}
            obj[it.root][it.name] = (it.value === null || it.value === undefined) ?
                it.default :
                it.value;
        })
        return obj
    }, {})
    console.log(' =====> this.resultObj', this.resultObj);
},

改变表单项 监听每个表单项改变的事件,更新结果集对应的项,并且检查是否在autoShowItems数组中,动态改变对应项的isShow

onFormItemChange(e) {
    this.updateConfigObj(e.item)
    this.checkShowWhen(e.item)
},
// 更新某一项
updateConfigObj(item) {
    const { root, value, name } = item
    this.resultObj[root][name] = value
    console.log(' 数据已更新 =====> this.resultObj', this.resultObj);
    console.log(` 当前更新项 =====> ${root}[${name}],更新值为:${value}`);
    this.checkShowWhen(item)
},
// 检查当前更新项是否会影响到 自动更新的项
checkShowWhen(item) {
    if (!this.autoShowItems || this.autoShowItems.length === 0) return
    this.autoShowItems.forEach(it => {
        // 找到当前元素
        let actOpt = this.editList.find(iit => iit.key === it.selfOptKey)
        if(!actOpt) return 
        let changedItem = actOpt.configOpt.find(cItem => cItem.root == it.root && cItem.name == it.name)
        if(!changedItem) return
        let isShow = false
        it.showWhen.forEach(sItem => {
            let { optKey, root, name, value} = sItem
            const decisionItem = this.editList.find(lItem => lItem.key === optKey)
            let decCitem = decisionItem.configOpt.find(cItem => cItem.root == root && cItem.name == name)
            if(value.includes(decCitem.value)) isShow = true
        })
        changedItem.isShow = isShow
    })
}

源码已发布到 gitCode