封装公共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>
相关推荐
Java后端的Ai之路7 小时前
06-React-Props
前端·react.js·前端框架
半个落月7 小时前
LangChain.js Agent 中间件实战:生命周期、动态工具与文件权限
javascript·langchain
Flynt7 小时前
开源"间谍卫星"说数据全是真的,我把它的免key数据源挨个试了一遍
前端·开源·数据可视化
Csvn8 小时前
渲染管线
前端
程序员大阳8 小时前
副队长CSS教程(33)–圆角与透明
前端·css·透明·圆角
志尊宝8 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金9 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉9 小时前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化
IMPYLH9 小时前
HTML 的 <wbr> 元素
前端·html
Dovis(誓平步青云)9 小时前
常用命令藏得太深?怎么做一个快捷命令坞
运维·前端·自动化·音视频·prometheus