封装公共el-form表单

1.公共表单组件

javascript 复制代码
//commonForm.vue
<script>
import {
    TEXT,
    SELECT,
    PASSWORD,
    TEXTAREA,
    RADIO,
    DATE_PICKER
} from '@/conf/uiTypes'
import { deepClone } from '@/utils'
export default {
    name: 'GFormCreator',
    props: {
        config: {  // title/items
            type: Object,
            required: true
        }
    },
    created() {
        const { items, cards, rules } = this.config;

        // 绑定表单验证器this
        for (let key in rules) {
            rules[key].forEach(r => {
                // 若该方法是全局方法,第二次bind会失效,因为bind只能绑定一次
                if (r.validator) {
                    r.validator = r.validator.bind(this)
                }
            }) 

        }


        if (cards) {
            cards.forEach(card => {
                this.reactiveFields(card.children);
            })
        } else if (items) {
            this.reactiveFields(items);
        }
    },
    data() {
        return {
            ruleForm: {}
        }
    },
    methods: {
        reactiveFields(items) {
            console.log(items);
            if (!items) return;
            items.forEach((row, rowIndex) => {
                row.forEach((item, colIndex) => {
                    // this.ruleForm[item.key] = item.value;
                    // Object.defineProperty 对所有key进行响应式,更改后更新
                    // 无法检测到动态添加的key,访问、设置,set/get都无法触发响应式 
                    if (this.ruleForm.hasOwnProperty(item.key)) {
                        // 异常抛出,外部方法没有捕获的画,程序结束
                        throw new Error(`行:${rowIndex + 1}_列${colIndex + 1}` + '已经存在相同的key:' + item.key + ',value:' + item.value)
                    }
                    this.$set(this.ruleForm, item.key, item.value);
                })
            });
        },
        renderItem(item) {
            const fd = this.ruleForm;
            const attrs = item.attrs;
            switch (item.type) {
                case TEXT:
                case PASSWORD:
                case TEXTAREA:
                    // v-model = @input + :value
                    return <el-input attrs={attrs} v-model={fd[item.key]} type={item.type} ></el-input>
                case SELECT:
                    return <el-select attrs={attrs} v-model={fd[item.key]}>
                        {item.options.map(opt => {
                            return <el-option value={opt.value} label={opt.label}></el-option>
                        })}
                    </el-select>
                case DATE_PICKER:
                    return <el-date-picker
                        attrs={attrs}
                        v-model={fd[item.key]}
                        type="date"
                        placeholder="选择日期">
                    </el-date-picker>
                case RADIO:  //  {  label:'xxx' radios:[ { attrs:{},label:'xxx' }  ] }
                    return item?.radios?.map(radio => {
                        console.log('radio:', fd[item.key])
                        return <el-radio
                            attrs={radio.attrs}

                            v-model={fd[item.key]} label={radio.label}>{radio.title}</el-radio>
                    })
                default:
                    return <h2>未匹配{item.type}</h2>
            }
        },
        renderColumns(columns) {
            return columns.map(col => {
                return <el-col span={col.colspan}>
                    <el-form-item label={col.label} prop={col.key}>
                        {this.renderItem(col)}
                    </el-form-item>
                </el-col>

            })
        },
        renderRows(rows) {
            return rows.map(rowArr => {
                return <el-row>{this.renderColumns(rowArr)}</el-row>
            })
        },
        getData() {
            return deepClone(this.ruleForm)
        },
        passData() {
            // submit
            this.$emit('submit', deepClone(this.ruleForm));
        },
        // 外部验证
        // 内部验证返回数据
        doSubmit() {
            this.$refs.form.validate(valid => {
                if (valid) {
                    return this.$emit('submit', deepClone(this.ruleForm));
                } else {
                    console.log('验证失败');
                    return false;
                }
            })
        },
        valid(callback) {
            this.$refs.form.validate(valid => {
                    if (valid) {
                        return callback(deepClone(this.ruleForm))
                    } else {
                        callback(false);
                        console.log('验证失败');
                        return false;
                    }
                }
            );
        },

        reset() {
            this.$refs.form.resetFields();
        }, 
        renderCards(cards) {
            let { renderRows } = this;
            return cards.map(card => {
                // 渲染name和children(renderRows) 
                return (
                    <el-card class="box-card" header={card.name}>
                        {card.children && renderRows(card.children)}
                    </el-card >
                )

                // 实现方式1
                // let h = this.$createElement;
                return h('el-card', { class: 'box-card' }, [
                    h('template', { slot: 'header' }, [
                        h('span', card.name)
                    ]),
                    card.children && renderRows(card.children)
                ]);
                // 实现方式2
                return (
                    <el-card class="box-card">
                        <template slot="header">
                            <span>{card.name}</span>
                        </template>
                        {card.children && renderRows(card.children)}
                    </el-card >
                );
            })
        }

    },
    render() {
        const { title, items, rules, cards } = this.config;
        const { ruleForm, $scopedSlots: { btn } } = this;

        return (
            <div class="form-box">
                {title && <h2>{title}</h2>} 
                <el-form ref="form" attrs={{ model: ruleForm, }} rules={rules} label-width="80px">
                    {cards ? this.renderCards(cards) : this.renderRows(items)}
                </el-form> 
                <div class="btn-bow">
                    {btn ? btn({ t: '我是scopod' }) : (
                        <div>
                            <el-button type="primary" onClick={e => this.doSubmit()}>提交</el-button>
                            <el-button onClick={e => this.reset()}>重置</el-button>
                        </div>
                    )}
                </div> 
            </div>

        )
    }
}
</script>

<style scoped>
.el-input,
.el-select,
.form-box .el-date-editor {
    width: 100%;
}

:deep(.el-card__header) {
    text-align: left;
}

.box-card {
    margin-bottom: 10px;
}
</style>
相关推荐
九月十九2 分钟前
AviatorScript用法
java·服务器·前端
Jane - UTS 数据传输系统26 分钟前
VUE+ Element-plus , el-tree 修改默认左侧三角图标,并使没有子级的那一项不展示图标
javascript·vue.js·elementui
_.Switch1 小时前
Python Web开发:使用FastAPI构建视频流媒体平台
开发语言·前端·python·微服务·架构·fastapi·媒体
菜鸟阿康学习编程1 小时前
JavaWeb 学习笔记 XML 和 Json 篇 | 020
xml·java·前端
索然无味io2 小时前
XML外部实体注入--漏洞利用
xml·前端·笔记·学习·web安全·网络安全·php
ThomasChan1232 小时前
Typescript 多个泛型参数详细解读
前端·javascript·vue.js·typescript·vue·reactjs·js
爱学习的狮王3 小时前
ubuntu18.04安装nvm管理本机node和npm
前端·npm·node.js·nvm
东锋1.33 小时前
使用 F12 查看 Network 及数据格式
前端
zhanggongzichu3 小时前
npm常用命令
前端·npm·node.js
anyup_前端梦工厂3 小时前
从浏览器层面看前端性能:了解 Chrome 组件、多进程与多线程
前端·chrome