SOURCE

const LogMap = {
    "customerName": "客户姓名",
    "gender": "客户性别",
    "age": "客户年龄",
    "customerSource": "客户来源",
    "mark": "备注",
    // "tagMap": {},
    "intentionalInsureType": "意向险种",
    // "defineTag": [],
    "focusOther": "关注其他平台",
    "salary": "家庭年收入",
    "debt": "家庭负债",
    "customerDesc": "客户简介",
    "addressDetail": "详细地址",
    "loginIdList": "会员ID",
    "contactDVOList": {
        "name": "家庭成员姓名",
        "mobile": "家庭成员手机号",
        "gender": "家庭成员性别",
        "isDefault": "默认联系人",
        "relation": "家庭成员关系",
    },
    "tagDesc": "添加自定义标签",
}


class DiffLog {
    constructor(preData, curData, logMap) {
        this.preData = preData
        this.curData = curData
        this.logMap = logMap
        this.logs = []
        this.reverse = false
    }

    getLog(preData = this.preData, curData = this.curData) {
        // 判断preData:
        // base 、 arr 、 object
        // 一、类型相同: 
        // 1、值相同: 不生成log
        // 2、值不同: 生成log:
        //   1)数组: 遍历出不同,生成
        //   2)对象: 遍历
        //   3) base:  比对
        // 二、类型不同:
        // 1、pre null,cur: (新增)
        //   1) obj: 遍历属性生成
        //   2) arr: 遍历生成
        //   3) base: map 取生成
        // 2、cur null, pre: (删除)
        //   1) obj: 遍历属性生成
        //   2) arr: 遍历生成
        //   3) base: map 取生成
        // 
        // 公共方法: 
        //      1、判断类型
        //      2、判断有效值
        //      3、数组遍历不同
        //      4、对象遍历不同
        //      5、获取label
        //


        // for (const [preKey, preValue] of Object.entries(preData)) {
        //     const curValue = curData[preKey]
        //     const logItem = this.generateLog(preKey, preValue, curValue)
        //     logItem && this.logs.push(logItem)
        //     // console.log(preKey,preValue, curValue, logStr)
        // }
        this.getObjectLog(preData, curData)
        console.log(this.logs)
        return this.logs
    }



    // ======= 获取log分类 =======
    getObjectLog(preData, curData) {
        // 修改删除
        for (const [preKey, preValue] of Object.entries(preData)) {
            const curValue = curData[preKey]
            const logItem = this.generateLog(preKey, preValue, curValue);
            logItem && this.logs.push(logItem)
        }
        // 增加
        for (const [curKey, curValue] of Object.entries(curData)) {
            const preValue = preData[curKey]
            if (this.validValue(curValue)) continue;
            this.reverse = true
            const logItem = this.generateLog(curKey, curValue, preValue);
            logItem && this.logs.push(logItem)
        }
        this.reverse = false
    }



    generateLog(key, preValue, curValue, reverse) {
        let logStr = ''
        switch (this.getType(preValue)) {
            case 'String':
                logStr = this.generateBaseTypeLog(key, preValue, curValue);
                break;
            case 'Number':
                logStr = this.generateBaseTypeLog(key, preValue, curValue);
                break;
            case 'Object':
                this.generateObjectTypeLog(key, preValue, curValue);
                break;
            case 'Array':
                this.generateArrayTypeLog(key, preValue, curValue);
                break;
            case 'Null':

        }
        return logStr
    }
    // =======  ========
    generateBaseTypeLog(key, preValue, curValue) {
        if (!this.validValue(preValue)) return this.addPropertyLog(key, preValue)
        if (this.validValue(curValue)) {
            if (!this.isEquil(preValue, curValue)) {
                return this.modifyPropertyLog(key, preValue, curValue)
            }
        } else {
            return this.reverse ? this.addPropertyLog(key, preValue) :
                this.deletePropertyLog(key, preValue)
        }
        return ''
    }

    generateObjectTypeLog(key, preValue, curValue) {
        if (this.validValue(curValue)) {
            const _DiffLog = new DiffLog(preValue, curValue, this.logMap[key])
            this.logs = this.logs.concat(_DiffLog.getLog())
            return _DiffLog.getLog()
        } else {
            this.logs.push(this.deletePropertyLog(key, preValue))
        }
    }

    generateArrayTypeLog(key, preValue, curValue) {
        // 添加
        if (!this.validValue(preValue)) {
            curValue.forEach(itx => {
                if (this.getType(itx) === 'Object') {
                    this.generateObjectTypeLog(key, {}, itx)
                } else if (this.getType(itx) === 'Array') {

                } else {
                    addLogs.push(this.addPropertyLog(key, itx))
                }
            })
            this.logs = this.logs.concat(deletedLog, addLog)
        }
        // 修改
        if (this.validValue(curValue)) {
            const deletedLogs = addLogs = []
            preValue.forEach(itx => {
                const result = curValue.find(ity => this.isEquil(itx, ity))
                if (result) return;
                if (this.getType(itx) === 'Object') {
                    this.generateObjectTypeLog(key, itx, {})
                } else if (this.getType(itx) === 'Array') {

                } else {
                    deletedLogs.push(this.deletePropertyLog(key, itx))
                }
            })

            curValue.forEach(itx => {
                const result = preValue.find(ity => this.isEquil(itx, ity))
                if (result) return;
                if (this.getType(itx) === 'Object') {
                    this.generateObjectTypeLog(key, itx, {})
                } else if (this.getType(itx) === 'Array') {

                } else {
                    addLogs.push(this.addPropertyLog(key, itx))
                }
            })
            this.logs = this.logs.concat(deletedLog, addLog)
        } else {
            this.logs.push(this.deletePropertyLog(key, itx))
        }
    }

    generateAddLog(key, preValue, curValue) {
        const addLogs = []

        for (const [curKey, curValue] of Object.entries(curData)) {

            if (this.getType(curValue) === 'Object') {
                this.generateObjectTypeLog(curKey, {}, curValue)
            } else if (this.getType(curValue) === 'Array') {
                this.generateArrayTypeLog(curKey, {}, curValue)

            } else {
                addLogs.push(this.addPropertyLog(preKey, preValue))
            }

            const curValue = curData[preKey]
            const logItem = this.generateLog(preKey, preValue, curValue);
            logItem && this.logs.push(logItem)
        }

    }

    // ===============
    // 修改内容log
    modifyPropertyLog(key, preValue, curValue) {
        return this.getMapLabel(key) + ':由' + preValue + '修改为' + curValue
    }
    // 删除内容log
    deletePropertyLog(key, preValue) {
        return '删除' + this.getLogLabel(key) + ':' + preValue;
    }
    // 添加内容log
    addPropertyLog(key, curValue) {
        return '增加' + this.getLogLabel(key) + ':' + curValue;
    }




    // ======  基础方法  =========
    // 从字段中获取文案
    getMapLabel(key) {
        return this.logMap[key] ? this.logMap[key] : key;
    }
    // 判断是否相等
    isEquil(preValue, curValue) {
        return preValue.toString() === curValue.toString()
    }
    // 获取类型
    getType(data) {
        return Object.prototype.toString.call(data).slice(8, -1);
    }
    // 是否为有效值
    validValue(value) {
        const type = this.getType(value);
        const validValues = {
            Array: () => value.length > 0,
            Object: () => Object.keys(value).length > 0,
            Number: () => value.toString() !== 'NaN',
            String: () => value.length > 0,
            Undefined: () => false,
            Null: () => false,
            Boolean: () => value,
        };
        return validValues[type] && validValues[type]();
    }
}

const log = new DiffLog(
    {
        gender: 1,
        //   "contactDVOList": {
        //     "name": "1",
        //     // "mobile": "1",
        //     // "gender": "1",
        //     // "isDefault": "1",
        //     // "relation": "1",
        // },
    },
    {
        gender: 2, customerName: 'lisi',
        // "contactDVOList": {
        //     "name": "2",
        //     "mobile": "2",
        //     "gender": "2",
        //     "isDefault": "2",
        //     "relation": "2",
        // },
    }, LogMap
)
console.log(log.getLog())
console 命令行工具 X clear

                    
>
console