vue实现复杂的表单Dom操作

春节过后公司需要上线一个抽奖优惠活动,在后台管理页面中涉及到表单的动态添加、修改dom等复杂操作。开发的过程中遇到许多的知识点和困难点,于是总结了以下一些技巧经验。

一、业务需求

如图所示,在抽奖页面中,涉及到获取奖品、添加奖品、修改奖品、删除奖品(增删改查)、上传图片等功能。

  • 奖品类型分别是租期、实物、谢谢惠顾。谢谢惠顾类型由后台返回,不可添加只能修改,且类型不能修改,只能修改奖品名称和中奖概率和奖品展示。
  • 租期类型没有图片字段、实物类型没有天数字段。
  • 每一列上传图片功能。
  • 当对当前列的数据进行修改时,按钮文案从修改变成保存,并且显示不同的按钮背景颜色。
  • 删除数据时要区分dom数据还是服务端返回的真实数据,前者则不需要调用删除接口。

二、vue版本
基于vue2.x语法和element-ui框架。

三、难点分析

  • 如何区分前端追加的dom数据和服务端返回的真实数据?(即添加和修改的区别)
  • 每一列的表单编辑中状态如何实现?(修改按钮-->保存按钮)

四、关键代码实现

// data数据
lotteryList: [
                // {
                //     hl_name: "",
                //     hl_type: null,
                //     hl_days: "",                                                                                                                                                    
                //     hl_lnums: "", //库存
                //     hl_shownum: "", //展示数量
                //     hl_img: "",
                //     isImgUpload: false, // 上传图片开关 前端加的判断
                //     isEdit: false, // 是否编辑状态 前端加的判断
                //     hl_lucky: true, // 1开 2关 提交的时候需要转换一下
                //     hl_rate: "", // 概率
                // },
            ],
            watchLotteryList: [], // 用来监听列表的数据变化
            originLotteryList: [],  // 源数据 深拷贝后的
            curIndex: null

// 动态添加dom
onAdd() {
            // 默认追加的dom数据是没有id的 因此根据id来区别真假数据
            this.lotteryList.push({
                hl_name: "",
                hl_type: null,
                hl_days: "",
                hl_lnums: "",
                hl_shownum: "",
                hl_img: "",
                isImgUpload: false, // 前端加的判断
                isEdit: false, // 前端加的判断
                hl_lucky: true,
                hl_rate: "",
            });
        },
// 获取抽奖列表
        getList() {
            getLottery({}).then(res => {
                console.log('res: ', res);
                if(res.state == 0){
                    if (res.data) {
                        let data = res.data
                        data.filter(item => {
                            item.hl_type = Number(item.hl_type)  // 需要number类型 否则el-select不会回显
                            item.hl_lucky = item.hl_lucky == 1 ? true : false;
                            item.isEdit = false // 追加一个编辑状态
                        })
                        console.log('data: ', data);
                        this.lotteryList = data
                        
                        // this.watchLotteryList = JSON.parse(JSON.stringify(data))
                        // 保持一份初始数据 不被双向绑定给影响到
                        this.originLotteryList = JSON.parse(JSON.stringify(data))
                        // 这一份数据用来监听列表数据的 因为直接监听lotteryList再修改原数据lotteryList会造成监听无限执行
                        this.watchLotteryList = data
                    } else {
                        this.lotteryList = []
                    }
                }
            })
        },
// 删除
onDelete(index) {
           if (this.lotteryList[index].hl_id) {
               // 真实数据
               console.log('真实数据: ');
               delLottery({
                   hl_id: this.lotteryList[index].hl_id
               }).then(res => {
                   if (res.state == 0) {
                       this.$message({
                           message: '删除成功',
                           type: 'success'
                       });
                       this.getList()
                   }
               })
           } else {
               // dom数据
               console.log('dom数据: ');
               this.lotteryList.splice(index, 1);
           }
       },
watch: {
        // lotteryList: {
        // 不要监听列表用的lotteryList数据 因为最终要修改lotteryList数据 会造成监听无限执行
        watchLotteryList: {
            // 深度监听会造成val和oldVal的值相同
            handler(val, oldVal) {
                setTimeout(() => {
                    console.log('---', this.curIndex, this.lotteryList[this.curIndex])
                    // 存在id 即编辑状态下
                    if (this.curIndex !=null && this.lotteryList[this.curIndex].hl_id) {
                            console.log('originLotteryList: ', this.originLotteryList[this.curIndex]);
                            console.log('val: ', val[this.curIndex]);
                            if (this.isEqual(this.originLotteryList[this.curIndex], val[this.curIndex])) {
                                console.log('2个对象数据一致')
                                this.lotteryList[this.curIndex].isEdit = false
                                // 原始数据也要同步修改
                                this.originLotteryList[this.curIndex].isEdit = false
                            } else {
                                console.log('2个对象数据不一致!!!')
                                this.lotteryList[this.curIndex].isEdit = true
                                this.originLotteryList[this.curIndex].isEdit = true
                            }
                            console.log('最后', this.lotteryList[this.curIndex])
                            // this.curIndex = null
                    }
                }, 50)
            },
            deep: true
        },
    },
// 记录当前选中的数据 select则用@change事件 其他元素添加@click事件
        onItem(index) {
            console.log('选中下标', index)
            this.curIndex = index
        },
// 对比2个对象是否一致
        isEqual(objA, objB){
            //相等
            if(objA === objB) return objA !== 0 || 1/objA === 1/objB;
            //空判断
            if(objA == null || objB == null) return objA === objB;
            //类型判断
            if(Object.prototype.toString.call(objA) !== Object.prototype.toString.call(objB)) return false;

            switch(Object.prototype.toString.call(objA)){
                case '[object RegExp]':
                case '[object String]':
                    //字符串转换比较
                    return '' + objA ==='' + objB;
                case '[object Number]':
                    //数字转换比较,判断是否为NaN
                    if(+objA !== +objA){
                        return +objB !== +objB;
                    }

                    return +objA === 0?1/ +objA === 1/objB : +objA === +objB;
                case '[object Date]':
                case '[object Boolean]':
                    return +objA === +objB;
                case '[object Array]':
                    //判断数组
                    for(let i = 0; i < objA.length; i++){
                        if (!this.isEqual(objA[i],objB[i])) return false;
                    }
                    return true;
                case '[object Object]':
                    //判断对象
                    let keys = Object.keys(objA);
                    for(let i = 0; i < keys.length; i++){
                        if (!this.isEqual(objA[keys[i]],objB[keys[i]])) return false;
                    }

                    keys = Object.keys(objB);
                    for(let i = 0; i < keys.length; i++){
                        if (!this.isEqual(objA[keys[i]],objB[keys[i]])) return false;
                    }

                    return true;
                default :
                    return false;
            }
        },
// 防抖函数
        debounce(func, wait = 300, timer, immediate = false) {
            //  移除定时器
            if (timer !== null) {
                clearTimeout(timer)
            }
            //  是否立即执行
            if (immediate) {
                //  立即执行
                const callNow = !timer
                timer = setTimeout(() => {
                    timer = null
                }, wait)
                if (callNow) typeof func === 'function' && func()
            } else {
                //  设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
                timer = setTimeout(() => {
                typeof func === 'function' && func()
                }, wait)
            }
        },
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,835评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,900评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,481评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,303评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,375评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,729评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,877评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,633评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,088评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,443评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,563评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,251评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,827评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,712评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,943评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,240评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,435评论 2 348

推荐阅读更多精彩内容