封装公共el-form表单(记录)

news2025/1/13 13:54:57

1.公共表单组件 

//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>

(1)声明表单类型 

// conf/uniTypes.js
export const  TEXT = 'text';
export const SELECT = 'select';
export const PASSWORD = 'password';
export const TEXTAREA = 'textarea';
export const RADIO = 'radio';
export const DATE_PICKER= 'datepicker';

 (2)封装深拷贝

// utils/index.js

export const deepClone = (obj)=>{  
    // 处理环形对象带来的递归栈内存溢出
    let cache = new WeakMap(); // 避免强引用
    var objType = '[object Object]';
    function innerDeepClone(obj) {
        // 处理了基本数据类型  undefined null function
        if (typeof obj !== 'object' || !obj) {
            return obj;
        }
        // obj不是方法的参数, 改变的this,由于不同类型的对象的type不同,
        // toString从不同this拿到的就不一样
        // var type = Object.prototype.toString.call(obj);
        if (cache.has(obj)){
            return cache.get(obj);
        }
        let tmp;
        // 处理非对象和数组
        if (obj instanceof Map) {
            tmp = new Map();
            cache.set(obj,tmp);
            obj.forEach((val, key) => {
                tmp.set(innerDeepClone(key), innerDeepClone(val))
            })
        } else if (obj instanceof Set) {
            tmp = new Set();
            cache.set(obj,tmp);
            obj.forEach(val => {
                tmp.add(innerDeepClone(val))
            })
        } else if (obj instanceof RegExp || obj instanceof Date) {
            tmp = new obj.constructor(obj);  
            cache.set(obj,tmp);
        } else {
            tmp = new obj.constructor();
            cache.set(obj,tmp);
            for (let key in obj) {
                tmp[key] = innerDeepClone(obj[key]);
            }
        }
        return tmp;
    }
    return innerDeepClone(obj)
}

2.使用公共表单组件

<template>
  <div>
    <GFormCreator :config="conf" @submit="createLoan"/>
    <!-- <hr>
    <GFormCreator ref="f2" :config="conf">
      <template #btn="{t}">
        <el-button @click="test1">提交{{ t }}</el-button>
      </template>
    </GFormCreator> -->

  </div>
</template>

<script>
import conf from './loan-input-page'
import { createLoanApi } from '@/api/loan'
export default {
  methods: {
    async createLoan(user){
      let res = await createLoanApi(user);
      this.$notify.success('添加成功');
    },
    test2(data){
      console.log('数据:',data)
    },
    test1() {
      console.log(this.$refs.f2.getData())
    }
  },
  data() {
    return {
      conf
    }
  }
}
</script> 
/loan-input-page.js
import {
    TEXT,
    SELECT,
    PASSWORD,
    TEXTAREA,
    RADIO,
    DATE_PICKER
} from '@/conf/uiTypes';


//性别
export const sexOptions = [
    { value: "man", label: "男" },
    { value: "woman", label: "女" }
];
//行业
export const companyOptions = [
    { value: "education", label: "教育" },
    { value: "finance", label: "金融" }
];
//婚否
export const marriageOptions = [
    { value: "married", label: "已婚" },
    { value: "unmarried", label: "未婚" }
];
//学历
export const educationOptions = [
    { value: "college", label: "大学" },
    { value: "highschool", label: "高中" }
];

// 优化 => data方法中,默认是会Object.defineProperty => 当触发属性的get/set => 页面的更新

export default Object.freeze({
    cards: [
        {
            name: "个人基本信息",
            children: [
                [
                    { label: "姓名", key: "name", type: TEXT },
                    { label: "出生日期", key: "birthday", type: DATE_PICKER },
                    {
                        label: "性别",
                        key: "sex",
                        type: SELECT,
                        options: sexOptions
                    }
                ],
                [{ label: "身份证", key: "identity_card", type: TEXT }],
                [
                    {
                        label: "婚姻状态",
                        key: "marriage",
                        type: "select",
                        options: marriageOptions
                    },
                    {
                        label: "教育程度",
                        key: "education",
                        type: "select",
                        options: educationOptions
                    },
                    { label: "居住地址", key: "address1", type: TEXT }
                ],
                [
                    { label: "户籍地址", key: "address2", type: TEXT },
                    { label: "居住电话", key: "phone", type: TEXT },
                    { label: "手机号", key: "mobile_phone", type: TEXT }
                ]
            ].map(row => row.map(item => ({ colspan: 8, ...item })))
        },
        {
            name: "职业信息",
            children: [
                [   // element原生属性
                    { label: "现职公司", key: "company", type: TEXT },
                    {
                        label: "所属行业",
                        attrs: { placeholder: '请选择Green' },
                        key: "trade",
                        type: "select",
                        options: companyOptions
                    },
                    { label: "职位", key: "position", type: TEXT },
                    { label: "公司地址", key: "address3", type: TEXT }
                ].map(item => ({ colspan: 6, ...item })),
                [
                    { label: "公司类型", key: "company_type", type: TEXT },
                    { label: "公司邮箱", key: "company_email", type: TEXT },
                    { label: "公司电话", key: "company_phone", type: TEXT }
                ].map(item => ({ colspan: 8, ...item }))
            ]
        },
        {
            name: "收支情况",
            children: [[{ label: "收支情况", key: "income", type: TEXT, colspan: 12 }]]
        },
        {
            name: "家庭联系人",
            children: [
                [
                    { label: "关系1", key: "contact", type: TEXT },
                    { label: "姓名", key: "contact_name", type: TEXT },
                    { label: "手机", key: "contact_phone", type: TEXT }
                ].map(item => ({ colspan: 12, ...item }))
            ]
        },
        {
            name: "工作证明人",
            children: [
                [
                    { label: "关系2", key: "contact2", colspan: 12, type: TEXT },
                    { label: "姓名", key: "contact2_name", colspan: 12, type: TEXT },
                    { label: "手机", key: "contact2_phone", colspan: 12, type: TEXT }
                ],
                [
                    { label: "部门", key: "contact2_dep", colspan: 12, type: TEXT },
                    { label: "职位", key: "contact2_pos", colspan: 12, type: TEXT }
                ],
                [{ label: "备注", key: "remark", type: "textarea" }]
            ]
        }
    ],
    rules: {
        name: [
            { required: true, message: "请输入姓名", trigger: "blur" },
            {
                min: 2,
                max: 5,
                message: "长度在 2 到 5 个字符",
                trigger: "blur"
            }
        ],
        identity_card: [
            { required: true, message: "请输入身份证", trigger: "change" }
        ],
        birthday: [
            {
                type: 'date',
                required: true,
                message: "请选择日期",
                trigger: "change"
            }
        ],
        sex: [{ required: true, message: "请选择性别", trigger: "change" }],
        marriage: [
            { required: true, message: "请选择婚姻状态", trigger: "change" }
        ],
        education: [
            { required: true, message: "请选择教育程度", trigger: "change" }
        ],
        trade: [
            { required: true, message: "请选择所属行业", trigger: "change" }
        ],

        address1: [
            { required: true, message: "请输入居住地址", trigger: "blur" }
        ],
        address2: [
            { required: true, message: "请输入户籍地址", trigger: "blur" }
        ],
        phone: [{ required: true, message: "请输入居住电话", trigger: "blur" }],
        mobile_phone: [
            { required: true, message: "请输入手机号", trigger: "blur" }
        ],
        company: [
            { required: true, message: "请输入现职公司全称", trigger: "blur" }
        ],
        position: [{ required: true, message: "请输入职位", trigger: "blur" }],
        address3: [
            { required: true, message: "请输入公司地址", trigger: "blur" }
        ],
        company_type: [
            { required: true, message: "请输入公司类型", trigger: "blur" }
        ],
        company_email: [
            { required: true, message: "请输入公司邮箱", trigger: "blur" }
        ],
        company_phone: [
            { required: true, message: "请输入公司电话", trigger: "blur" }
        ],
        income: [
            { required: true, message: "请输入收支情况", trigger: "blur" }
        ],
        contact: [{ required: true, message: "请输入关系1", trigger: "blur" }],
        contact_name: [
            { required: true, message: "请输入姓名", trigger: "blur" }
        ],
        contact_phone: [
            { required: true, message: "请输入手机", trigger: "blur" }
        ],
        contact2: [{ required: true, message: "请输入关系2", trigger: "blur" }],
        contact2_name: [
            { required: true, message: "请输入姓名", trigger: "blur" }
        ],
        contact2_phone: [
            { required: true, message: "请输入手机", trigger: "blur" }
        ],
        contact2_dep: [
            { required: true, message: "请输入部门", trigger: "blur" }
        ],
        contact2_pos: [
            { required: true, message: "请输入职位", trigger: "blur" }
        ]
    },
}
)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/917232.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PHP】基础语法变量常量

文章目录 PHP简介前置知识了解静态网站的特点动态网站特点 PHP基础语法代码标记注释语句分隔(结束)符变量变量的基本概念变量的使用变量命名规则预定义变量可变变量变量传值内存分区 常量基本概念常量定义形式命名规则使用形式系统常量魔术常量 PHP简介 PHP定义&#xff1a;一…

10.应用部署

配置项目编码 配置IDEA IDE编码 不配置会出现idea内部log中文乱码&#xff0c;而外部正常的现象 增加配置代码&#xff1a; -Dfile.encodingUTF-8需要重新启动idea 配置运行看板 services docker 如果docker尚未配置&#xff0c;则docker看板不会显示&#xff0c;但可以先…

睡眠模式下如何快速唤醒电脑,看这里!

这篇文章解释了如何唤醒正在睡觉的电脑,以及如果正常方法不起作用该怎么办。 一、如何从睡眠中唤醒电脑 不管你使用的是什么操作系统,关闭睡眠模式就像唤醒电脑一样简单,你可以通过某种方式与电脑交互来完成: 移动鼠标 滑动触摸板 按键盘上的任意键 有些设备有点不同,只…

淘宝API技术解析:实现店铺所有商品接口的高效获取与处理

当涉及到淘宝店铺的所有商品接口&#xff0c;我们可以利用淘宝开放平台提供的API进行操作。 &#xff08;首先&#xff0c;我们需要确保已经在淘宝开放平台注册并创建了应用&#xff0c;并获取了相应的App Key和App Secret。&#xff09; 1. 获取授权&#xff1a;使用OAuth 2…

自组织地图 (SOM) — 介绍、解释和实现

自组织地图 &#xff08;SOM&#xff09; — 介绍、解释和实现 一、说明 什么是SOM&#xff08;self orgnize map&#xff09;自组织地图&#xff0c;是GNN类似的图神经网络的概念。因为神经网络实质上可以解释为二部图的权重&#xff0c;因此无论GNN还是SOM都有共同的神经网络…

AIGC的发展

AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;&#xff0c;即人工智能生产内容&#xff0c;是人工智能在内容领域的最新进展。AIGC利用人工智能算法通过分析大量数据集&#xff0c;自动生成具有特定风格和主题的内容。AIGC涵盖了多种应用领域&#x…

ITIL4的知识体系及必要性

官方网站 www.itilzj.com 文档资料: wenku.itilzj.com 点击进入IT管理资料库 ITIL4 的必要性及历史 在这个前所未有的变革时代&#xff0c;人们身处着被誉为“第四次工业革命”的时代&#xff0c;这个时代催生出了一个日益加速、变得愈发错综复杂的环境。在这种环境下&#xf…

电脑文件夹密码怎么设置?文件夹加密介绍

文件夹是电脑管理文件必不可少的工具&#xff0c;而我们为了保护文件夹的数据安全&#xff0c;通常会采用设置密码的方法来保护文件夹。那么&#xff0c;电脑文件夹密码该怎么设置呢&#xff1f; 文件夹加密超级大师 文件夹加密超级大师不但可以为电脑文件夹设置密码&#xff…

阿里云大语言模型(LLM)实战训练营,火热开营中!

简介&#xff1a;大语言模型实战训练营已正式开营&#xff0c;汇集阿里云、黑马程序员多位AI领域资深技术专家手把手带您快速实现大语言模型从入门到应用落地&#xff01;完成课程学习任务更有机会领取LAMY钢笔、小米充电宝、双肩包等精美礼品&#xff0c;快来一起学习体验吧~ …

MySQL高可用Orchestrator

目录 一 Orchestrator简介 二 Orchestrator功能 1 Discovery(发现复制拓扑) 2 Refactoring(重构复制拓扑) 3 Recovery(恢复主库故障) 三 orchestrator支持的操作方式 四 部署要求 五 下载 六 安装 1 下载软件包 2 解压软件包 3 创建账号 第一种是 orc后端MySQL数据…

识别图片翻译怎么操作?几个好方法了解一下

图片翻译是一项非常有用的工具&#xff0c;可以让人们轻松地识别和翻译图片中的文字&#xff0c;对于一些步入职场的小伙伴尤其是从事文案编辑的朋友更是一个需要的技能&#xff0c;很多时候我们参考的多语言文献资料都是图片的格式&#xff0c;翻译内容的话会比较麻烦&#xf…

【python】python开源代理ip池

一、前言 随着互联网的不断发展&#xff0c;越来越多的应用需要使用高匿代理IP才能访问目标网站&#xff0c;而代理IP作为一种能够隐藏本机真实IP地址的工具&#xff0c;在网络抓取、搜索引擎排名、广告投放、反爬虫等方面有着广泛的应用场景。但是&#xff0c;由于代理IP的稳…

黑客自学路线

谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一般分为三大圈&#xff1a;娱乐圈 技术圈 职业圈。 娱乐圈&#xff1a;主要是初中生和高中生较多&#xff0c;玩网恋…

玩转软件|钉钉个人版内测启动:AI探索未来的工作方式

目录 前言 正文 AI为核心&#xff0c;个人效率为王&#xff01; 指令中心&#xff0c;解锁AI技巧&#xff01; 灵感Store&#xff0c;探索更多可能&#xff01; 未来的AI&#xff0c;即将问世&#xff01; 个人内测体验 前言 重磅消息&#xff1a;钉钉个人版在8月16日正…

【JavaSE】详解final关键字

在Java中&#xff0c;final可以用来修饰类、方法和变量。final修饰类&#xff0c;表示该类无法被继承&#xff0c;并且此类的设计已被认为很完美而不需要进行修改或扩展。final修饰类中的方法&#xff0c;表示不可以被重写&#xff1b;也就是把该方法锁定了&#xff0c;以防止继…

最小栈00

题目链接 最小栈 题目描述 注意点 pop、top 和 getMin 操作总是在 非空栈 上调用 解答思路 由于栈先进后出的特点&#xff0c;对于任意一次入栈操作&#xff0c;只要该元素未被弹出&#xff0c;则其前面插入的元素一定都还在栈中&#xff0c;所以每次入栈时只需要根据其前…

Lnton羚通视频算法算力云平台【PyTorch】教程:学习Datasets-DataLoader基础知识

Dataset & DataLoader PyTorch 提供了两个数据处理的基本方法&#xff1a;torch.utils.data.DataLoader torch.utils.data.Dataset 允许使用预加载的数据集以及自己的数据。 Dataset 存储样本及其对应的标签&#xff0c; DataLoader 在 Dataset 基础上封装了一个可迭代的对…

苍穹外卖 day3 实现登录过程中MD5加密

一 原来是明文存的 密码可见度太高&#xff0c;MD5加密为密文图像 效果 二 密文实现步骤 修改明文密码&#xff0c;改成密文 123456 密文值&#xff1a;e10adc3949ba59abbe56e057f20f883e代码如下所示 在这里插入代码片 package com.sky.service.impl;import com.sky.con…

[bug日志]springboot多模块启动,在yml配置启动端口8081,但还是启动了8080

【问题描述】 配置的启动端口是8081&#xff0c;实际启动端口是8080 【解决方法】 1.检查application.yml的配置是否有错误(配置项中&#xff0c;显示白色就错&#xff0c;橙色无措) 2.检查pom.xml的打包方式配置项配置&#xff0c;主pom.xml中的配置项一般为&#xff1a;&l…

司徒理财:8.23黄金最新行情走势分析及操作策略

黄金走势分析&#xff1a;      黄金下跌遇阻&#xff0c;短线开启震荡调整走势&#xff0c;但跌势依旧没有改变&#xff0c;没有突破1906压力前&#xff0c;还是偏空走势&#xff0c;反弹继续干空。趋势行情&#xff0c;不要轻言翻转&#xff01;即便下跌结束&#xff0c;…