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

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" }
        ]
    },
}
)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【人工智能】—_贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

文章目录 频率学派 vs. 贝叶斯学派贝叶斯学派Probability&#xff08;概率&#xff09;:独立性/条件独立性&#xff1a;Probability Theory&#xff08;概率论&#xff09;:Graphical models &#xff08;概率图模型&#xff09;什么是图模型&#xff08;Graphical Models&…

L1-044 稳赢(Python实现) 测试点全过

题目 大家应该都会玩“锤子剪刀布”的游戏&#xff1a;两人同时给出手势&#xff0c;胜负规则如图所示&#xff1a; 现要求你编写一个稳赢不输的程序&#xff0c;根据对方的出招&#xff0c;给出对应的赢招。但是&#xff01;为了不让对方输得太惨&#xff0c;你需要每隔K次就…

React【React是什么?、创建项目 、React组件化、 JSX语法、条件渲染、列表渲染、事件处理】(一)

文章目录 React是什么&#xff1f; 为什么要学习React React开发前准备 创建React项目 React项目结构简介 React组件化 初识JSX 渲染JSX描述的页面 JSX语法 JSX的Class与Style属性 JSX生成的React元素 条件渲染&#xff08;一&#xff09; 条件渲染 &#xff0…

Gorilla LLM:连接海量 API 的大型语言模型

如果你对这篇文章感兴趣&#xff0c;而且你想要了解更多关于AI领域的实战技巧&#xff0c;可以关注「技术狂潮AI」公众号。在这里&#xff0c;你可以看到最新最热的AIGC领域的干货文章和案例实战教程。 一、前言 在当今这个数字化时代&#xff0c;大型语言模型&#xff08;LLM…

LeetCode--HOT100题(41)

目录 题目描述&#xff1a;102. 二叉树的层序遍历&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;102. 二叉树的层序遍历&#xff08;中等&#xff09; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&am…

【LeetCode】28 . 找出字符串中第一个匹配项的下标

28 . 找出字符串中第一个匹配项的下标&#xff08;简单&#xff09; 方法&#xff1a;双指针法 思路 使用 find 函数枚举原串 ss 中的每个字符作为「发起点」&#xff0c;每次从原串的「发起点」和匹配串的「首位」开始尝试匹配&#xff1a; 匹配成功&#xff1a;返回本次匹配…

leetcode 739. 每日温度

2023.8.28 本题用暴力双层for循环解会超时&#xff0c;所以使用单调栈来解决&#xff0c;本质上是用空间换时间。维护一个单调递减栈&#xff0c;存储的是数组的下标。 代码如下&#xff1a; class Solution { public:vector<int> dailyTemperatures(vector<int>&…

YOLOv5引入FasterNet主干网络,目标检测速度提升明显

目录 一、背景介绍1.1 目标检测算法简介1.2 YOLOv5简介及发展历程 二、主干网络选择的重要性2.1 主干网络在目标检测中的作用2.2 YOLOv5使用的默认主干网络 三、FasterNet简介与原理解析3.1 FasterNet概述3.2 FasterNet的网络结构3.2.1 基础网络模块3.2.2 快速特征融合模块3.2.…

uniapp项目实战系列(1):导入数据库,启动后端服务,开启代码托管

目录 前言前期准备1.数据库的导入2.运行后端服务2.1数据库的后端配置2.2后端服务下载依赖&#xff0c;第三方库2.3启动后端服务 3.开启gitcode代码托管 ✨ 原创不易&#xff0c;还希望各位大佬支持一下&#xff01; &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&…

网络编程 http 相关基础概念

文章目录 表单是什么http请求是什么http请求的结构和说明关于http方法 GET和POST区别http常见状态码http响应http 请求是无状态的含义html是什么 &#xff08;前端内容&#xff0c;了解即可&#xff09;html 常见标签 &#xff08;前端内容&#xff0c;了解即可&#xff09;关于…

Android | 关于 OOM 的那些事儿

作者&#xff1a;345丶 前言 Android 系统对每个app都会有一个最大的内存限制&#xff0c;如果超出这个限制&#xff0c;就会抛出 OOM&#xff0c;也就是Out Of Memory 。本质上是抛出的一个异常&#xff0c;一般是在内存超出限制之后抛出的。最为常见的 OOM 就是内存泄露(大量…

SAP_ABAP_OO_ALV案例

SAP ABAP顾问能力模型梳理_企业数字化建设者的博客-CSDN博客SAP Abap顾问能力模型https://blog.csdn.net/java_zhong1990/article/details/132469977 一、OO_ ALV ,面向对象开发ALV报表 基于对收款清账平台的开发&#xff0c;解释 OO_ALV开发的程序结构与代码模板参考 1.1 代…

Unity血条制作

一、使用UGUI制作血条 我一般使用image制作血条&#xff0c;当然&#xff0c;也可以使用滑动组件Slider。image的具体操作步骤如下 普通血条 1、在Hierarchy面板中&#xff0c;创建两个image组件&#xff0c;将其中一个设置为另外一个的子节点 2、在Inspector面板中&#…

fatal: ServicePointManager 不支持具有 socks5 方案的代理。

报错 解决前 git config --global --list 查看git的设置 解决后 // 代理更改为http (7890是我的代理软件clash的port默认的&#xff0c;有些博客使用的是1080&#xff0c;依个人情况而定) git config --global http.proxy http://127.0.0.1:7890 git config --global https…

Android——基本控件(下)(十九)

1. 菜单&#xff1a;Menu 1.1 知识点 &#xff08;1&#xff09;掌握Android中菜单的使用&#xff1b; &#xff08;2&#xff09;掌握选项菜单&#xff08;OptionsMenu&#xff09;的使用&#xff1b; &#xff08;3&#xff09;掌握上下文菜单&#xff08;ContextMenu&am…

No message found under code ‘-1‘ for locale ‘zh_CN‘.

导出中的报错&#xff1a;No message found under code -1 for locale zh_CN. 报错原因&#xff1a;页面中展示的数据和后端excel中的数据不一致导致 具体原因&#xff1a;

14-数据结构-二叉树的创建以及前中后遍历,以及结点和叶子节点的计算(C语言)

概述&#xff1a; 二叉树&#xff0c;这里采用孩子链表存储法&#xff0c;即一个数据域和两个左右孩子指针域。随后递归进行遍历即可。在创建二叉树的时候&#xff0c;先创建各个二叉树结点&#xff08;这里的结点采用动态分配&#xff0c;因此结点为指针变量&#xff09;&…

为什么说计算机科学与计算机无关, 什么是真正的计算机科学?

什么是计算机科学呢? 我们可能很容易望文生义地理解为"不就是关于计算机的科学吗? "然而一位来自 MIT 计算机系的教授认为"计算机科学"不但不是科学, 而且而且还跟计算机无关!这是怎么回事呢? 视频链接见这里. 下面我们就来分享一下他对于计算机科学的看…

vue拖拽div盒子实现上下拖动互换

vue拖拽div盒子实现上下拖动互换 <div v-for"(item, index) in formList" :key"index" draggable"true"dragstart"handleDragStart($event, item)"dragenter"handleDragEnter($event, item)"dragover.prevent"han…

C语言_分支和循环语句(2)

文章目录 前言一、for 循环1.1语法1.2 for 语句的循环控制变量1.3 一些 for 循环的变种 二、do ... while()循环2.1 do 语句的语法2.2 do ... while 循环中的 break 和 continue2.3 练习1 **- 计算n的阶乘**2. - **在一个有序数组中查找具体的某个数字 n** 二分查找算法&#x…