react+sku 实现商品属性组合

前言

公司商城业务需把原本的统一属性分割为单商品的属性值,在编写的过程中,困难度很大,借鉴了很多案例,最终实现,现分享出来,供大家参考。

实现效果图

image.png

数据格式对照图

CDD1ECB3-EDC1-4df7-B708-AE6667989B88.png

微信图片_20210520100336.png

A6F0E480-8FC0-46fb-9627-4646D8CCF79F.png

utils.js

//商品属性组合算法 笛卡尔积算法
export function calcDescartes(){
    return Array.prototype.reduce.call(arguments,function(a, b) {
        let ret = [];
        a.forEach(function(a_item) {
            b.forEach(function(b_item) {
                ret.push(a_item.concat([b_item]));
            });
        });
        return ret;
    }, [[]]);
}

变量定义及注释代码

import { Modal, Input, InputNumber,Button, Upload, message, Row, Col, Icon, Radio, Switch,Tabs,Spin,Tooltip,Popconfirm,DatePicker, Popover,Checkbox,Table  } from 'antd';
import {calcDescartes} from '../../../utils/utils.js';
this.state = {
    attr:[{attr_name:'',attr_val:[]}],//商品属性
    recordAttr:[{attr_name:'',attr_val:[]}],//商品属性 记录 用于对比
    isShowCharDetail:false,//是否展示属性明细  点击添加属性值的时候才显示
    charTableData:[],//属性明细数据
    recordCharTableData:[],//属性明细数据 记录数据 用于对比
    visibleAttrBtn:true,//属性添加按钮是否显示
    sortOpt:'',//批量设置方式 price价格 stock库存
    totalPrice:0,//批量价格值
    totalStock:0,//批量库存值
    visibleOuterAttrBtn:true,//属性外层的 添加属性按钮是否显示 是否展示属性可编辑区域
    allowAddCharPic:false,//是否允许第一个属性添加属性值图片
    disabledStorage:false,//库存是否可以修改  编辑时 秒杀按钮已开启状态不能修改库存
    isEdit:false,//是否在编辑页面
    isSeckill:false,//商品是否在秒杀中
    overCharVis:false,//属性明细表超过200就提示用户
    isOverChar:false,//明细是否超过200
};

js代码 基本逻辑

//处理点击事件
handleBtn(tag,params,e){
    let {verifyStore,allowAddCharPic,charTableData,sortOpt,totalPrice,totalStock,isEdit,isSeckill,isOverChar} = this.state;
    switch(tag){
        case 'addFirstAttr':
            //第一次添加商品属性
            this.setState({
                visibleOuterAttrBtn:false
            })
            break;
        case 'addNewAttr':
            //正常添加商品属性
            if(isOverChar){
                this.setState({
                    overCharVis:true
                })
                return;
            }
            let newAttr = this.state.attr;
            if(newAttr && newAttr.length){
                newAttr[newAttr.length] = {attr_name:'',attr_val:[]};
                this.setState({
                    visibleAttrBtn:newAttr.length > 4 ? false : true,
                    attr:newAttr,
                    recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
                })
            }
            break;
        case 'allowAddCharPic':
            //是否允许添加属性值图片
            this.setState({
                allowAddCharPic:params.target.checked
            },()=>{
                let allowAttr = this.state.attr;
                if(allowAttr && allowAttr.length > 0){
                    if(allowAttr[0].attr_val && allowAttr[0].attr_val.length > 0){
                        allowAttr[0].attr_val.forEach(val=>{
                            params.target.checked ? val.img = '' : delete val.img;
                        })
                    }
                    this.setState({
                        attr:allowAttr,
                        recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
                    })
                }
            })
            break;
        case 'addCharValue':
            //添加属性值 attr:[{attr_name:'',attr_val:[{txt:'',img:''}]}]
            if(isOverChar){
                this.setState({
                    overCharVis:true
                })
                return;
            }
            if(isSeckill){
                return;
            }
            let operAttr = this.state.attr;
            let addObj = {
                txt:''
            };//新增的属性值 根据是否选中可添加属性值图片来决定内容是什么
            if(operAttr && operAttr.length > 0){
                (allowAddCharPic && params == 0) ? addObj.img = '' : '';
                if(operAttr[params].attr_val.length < 20){
                    operAttr[params].attr_val.push(addObj);
                    this.setState({
                        attr:operAttr,
                        recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
                    },()=>{
                        this.handleCharDetailShow(operAttr);
                    })
                }
            }
            break;
        case 'delCharVal':
            //删除属性值
            if(isSeckill){
                return;
            }
            let delAttr = this.state.attr;//属性
            if(delAttr && delAttr.length > 0){
                delAttr[params.index].attr_val.splice(params.idx, 1);
                this.setState({
                    attr:delAttr,
                    recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
                },()=>{
                    this.handleCharData(delAttr);
                    this.handleCharDetailShow(delAttr);
                })
            }
            break;
        case 'uploadCharPic':
            //上传属性值照片
            let uploadAttr = this.state.attr;
            uploadOss(e,{option:'uploadGoodsCharPic'}).then((value)=>{
                if(uploadAttr && uploadAttr.length > 0){
                    uploadAttr[params.index].attr_val[params.idx].img = value;
                    this.setState({
                        attr:uploadAttr,
                        recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
                    })
                }
            }).catch(res=>{
                message.error(res);
                return;
            })   
            break;
        case 'setTotalPrice':
            //批量设置 属性价格
            this.setState({
                sortOpt:'price'
            })
            break;
        case 'setTotalStock':
            //批量设置 属性库存
            this.setState({
                sortOpt:'stock'
            })
            break;
        case 'saveTotalSet':
            //保存批量设置属性
            if(charTableData && charTableData.length > 0){
                let stockArr = [];//storage
                let axios_url = '';//请求地址
                let obj_data = {};//请求需要传递的参数
                let ids = [];//传递参数需要的ids
                switch(sortOpt){
                    case 'price':
                        //价格
                        charTableData.forEach(item=>{
                            item.price = totalPrice;
                            ids.push(item.id);
                        })
                        axios_url = 'xxx';
                        obj_data.price = totalPrice;
                        break;
                    case 'stock':
                        //库存
                        charTableData.forEach(item=>{
                            item.stock = totalStock;
                            stockArr.push(item.stock);
                            ids.push(item.id);
                        })
                        axios_url = 'xxx/setSkuStock';
                        obj_data.stock = totalStock;
                        this.setState({
                            storage:JSON.stringify(stockArr) != '[]' ? stockArr.reduce((total,num)=>{return Number(total) + Number(num)}) : 0
                        })
                        break;
                }
                this.setState({
                    charTableData,
                    recordCharTableData:charTableData,//记录修改
                },()=>{
                    obj_data.ids = ids.join(',');
                    request(axios_url, {
                        method: 'POST',
                        headers: config.headers,
                        body:config.parseJson(obj_data)
                    }).then((res) => {
                    })
                    this.setState({
                        sortOpt:'',
                        totalPrice:0,
                        totalStock:0
                    })
                })
            }else{
                message.warn('请先添加属性');
                return;
            }
            break;
        case 'cancelTotalSet':
            //取消批量设置属性
            this.setState({
                sortOpt:'',
                totalPrice:0,
                totalStock:0
            })
            break;
    }
}
//输入框整合
handleInput(tag,e,opt){
    const {charTableData,isEdit,recordCharTableData,recordAttr,attr,id} = this.state;
    switch(tag){
        case 'saveStorage':
            //商品库存失去焦点  普通商品(无属性商品  需实时保存)
            if(isEdit){
                //编辑情况下 修改普通商品的库存 需要实时保存 避免超卖
                let storageObj = {
                    id:id ? id : '',
                    stock:e.target.value
                };
                request(`xxx`, {
                    method: 'POST',
                    headers: config.headers,
                    body:config.parseJson(storageObj)
                }).then((res) => {
                    // console.log(res)
                })
            }
            break;
        case 'changeAttrVal':
            //修改属性值 attr:[{attr_name:'',attr_val:[{txt:''},{txt:''}]}],//商品属性
            let changeAttr = this.state.attr;
            let changeAttrLength = changeAttr.length;
            if(changeAttr && changeAttrLength > 0){
                // console.log('changeAttr[e.outer].attr_val[e.inner].txt: ',changeAttr[e.outer].attr_val[e.inner].txt)
                changeAttr[e.outer].attr_val[e.inner].txt = opt.target.value.substring(0,20);
                changeAttr[e.outer].attr_val[e.inner].id = `${changeAttr[e.outer].attr_val[e.inner].id}-c`
                this.setState({
                    attr:changeAttr,
                },()=>{
                    this.handleCharDetailShow(this.state.attr);//处理属性明细显示隐藏
                })
            }
            break;
        case 'changeBlurAttrVal':
            //属性值不能相同
            // console.log('e: ',e)
            // console.log('opt: ',opt.target.value)
            // console.log('judgeAttr: ',this.state.attr)
            let judgeAttr = this.state.attr;//需要判断是否存在相同的属性值
            let judgeAttrVal = [];//用于收集修改的属性值所属的属性名项下的所有属性值
            if(judgeAttr && judgeAttr.length > 0){
                for(let index = 0;index < judgeAttr.length;index++){
                    if((index == e.outer) && judgeAttr[index].attr_val && judgeAttr[index].attr_val.length > 0){
                        //在同一个属性名下面不允许出现相同的属性值
                        judgeAttr[index].attr_val.forEach(item=>{
                            judgeAttrVal.push(item.txt);
                        })
                        let sameIndex = judgeAttrVal.indexOf(opt.target.value);//找到相同属性值的位置
                        // console.log(sameIndex)
                        if(sameIndex > -1 && sameIndex != e.inner){
                            //已经存在相同的属性值了
                            judgeAttr[index].attr_val[e.inner].txt = '';
                            this.setState({
                                attr:judgeAttr
                            })
                            message.error('已经添加了相同的属性值');
                            return;
                        }
                    }
                }
                this.handleCharData(this.state.attr)
            }
            // this.handleCharData(this.state.attr);//处理属性明细数据
            break;
        case 'singleCharPrice':
            //属性明细 单个价格
            if(charTableData && charTableData.length > 0){
                charTableData.forEach(item=>{
                    if(item.id == e.id){
                        item.price = opt;
                    }
                })
                this.setState({
                    charTableData,
                    recordCharTableData:charTableData,//记录修改
                },()=>{
                    
                })
            }
            break;
        case 'singleBlurCharPrice':
            //属性明细 单个价格  失去焦点
            if(charTableData && charTableData.length > 0){
                charTableData.forEach(item=>{
                    if(item.id == e.id){
                        item.price = opt.target.value;
                    }
                })
                
                this.setState({
                    charTableData,
                    recordCharTableData:charTableData,//记录修改
                },()=>{
                    let singleObj = {
                        ids:e.id,
                        stock:e.stock,
                        price:e.price
                    }
                    request(`xxxxxs/setSku`, {
                        method: 'POST',
                        headers: config.headers,
                        body:config.parseJson(singleObj)
                    }).then((res) => {
                        // console.log(res)
                    })
                })
            }
            break;
        case 'singleCharStock':
            //属性明细 单个库存
            if(charTableData && charTableData.length > 0){
                let stockArr = [];
                charTableData.forEach(item=>{
                    if(item.id == e.id){
                        item.stock = opt ? parseInt(opt) : 0;
                    }
                    stockArr.push(parseInt(item.stock));
                })
                this.setState({
                    charTableData,
                    recordCharTableData:charTableData,//记录修改
                    storage:stockArr.reduce((total,num)=>{return parseInt(total) + parseInt(num)})
                },()=>{
                    
                })
            }
            break;
        case 'singleCharBlurStock':
            //属性明细 单个库存  失去焦点
            if(charTableData && charTableData.length > 0){
                let stockArr = [];
                charTableData.forEach(item=>{
                    if(item.id == e.id){
                        item.stock = opt.target.value ? parseInt(opt.target.value) : 0;
                    }
                    stockArr.push(parseInt(item.stock));
                })
                
                this.setState({
                    charTableData,
                    recordCharTableData:charTableData,//记录修改
                    storage:stockArr.reduce((total,num)=>{return parseInt(total) + parseInt(num)})
                },()=>{
                    let singleObj = {
                        ids:e.id,
                        stock:e.stock,
                        price:e.price
                    }
                    request(`xxx`, {
                        method: 'POST',
                        headers: config.headers,
                        body:config.parseJson(singleObj)
                    }).then((res) => {
                        // console.log(res)
                    })
                })
            }
            break;
        case 'price':
            //批量设置价格值
            this.setState({
                totalPrice:e
            })
            break;
        case 'stock':
            //批量设置库存值
            this.setState({
                totalStock:parseInt(e)
            })
            break;
        case 'charBlurName':
            //属性名失去焦点 判断是否存在重复的属性名
            if(attr && attr.length > 0){
                for(let index = 0;index < attr.length;index++){
                    if(index != e){
                        if(attr[index].attr_name == opt.target.value){
                            attr[e].attr_name = '';
                            this.setState({
                                attr
                            })
                            message.error('已经添加了相同的属性名');
                            return;
                        }
                    }
                }
                this.handleCharData(this.state.attr)
            }
            break;
    }
}
//获取属性
getAttr(tag,e){
    const {isEdit} = this.state;
    let attr = this.state.attr;
    e.target.value = e.target.value.replace(/[^\u4E00-\u9FA5A-Za-z0-9,,]+$/g,"");
    switch(tag.tag){
        case 'name':
            attr[tag.index].attr_name = e.target.value.substring(0,6);
            attr[tag.index].attr_val = [];
            break;
        case 'value':
            attr[tag.index].attr_val = e.target.value;
            break;
    }
    this.setState({
        attr,
        recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
    },()=>{
        this.handleCharDetailShow(this.state.attr);
        this.handleCharData(this.state.attr)
    })
}
//删除属性
delAttr(params){
    let delAttr = this.state.attr;
    if(delAttr && delAttr.length > 0){
        // delAttr.length = delAttr.length - 1;
        delAttr.forEach(item=>{
            delete delAttr[params];
        })
    }
    this.setState({
        visibleAttrBtn:true,
        attr:delAttr.filter(d=>d),
        recordAttr:JSON.parse(JSON.stringify(this.state.attr)),//记录数据
    },()=>{
        let attr = this.state.attr;
        if(attr && JSON.stringify(attr) != '[]'){
            //没有全部删除属性
            this.setState({
                visibleAttrBtn:true,
            })
        }else{
            //属性全部删除
            this.setState({
                attr:[{attr_name:'',attr_val:[]}],
                isShowCharDetail:false,//隐藏属性明细表格
                visibleOuterAttrBtn:true,
                charTableData:[],//清空属性明细数据
                storage:0,//总库存清零
            })
        }
        this.handleCharData(this.state.attr);
    }) 
}
//处理属性明细展示数据 新增/重置属性
handleCharDetailData(attr){
    //添加商品的数据处理情况 重新渲染表格数据
    let charData = [];
    let dataObj = {};//实际数据整理
    const {recordCharTableData,charTableData,recordAttr,isEdit} = this.state;
    // console.log('recordCharTableData: ',recordCharTableData)
    // // console.log('charTableData: ',charTableData)
    // console.log('recordAttr: ',this.state.recordAttr)
    // console.log('attr: ',attr)
    if(recordAttr.length != attr.length){
        //新增了某一列 删除了某一列
        // console.log('操作:新增了某一列 删除了某一列')
        attr.forEach((val,idx)=>{
            let arrItem = [];//单个属性拼接
            let totalArr = [];//整理后的数据
            let attrValLength = val.attr_val.length;
            if(val.attr_val && attrValLength > 0){
                val.attr_val.forEach((item,index)=>{
                    arrItem.push({key:index,txt:item.txt});
                })
                charData.push(arrItem);
            }
            // let t1 = + new Date();
            // console.log('t1: ' + t1);
            // console.log(totalArr)
            totalArr = calcDescartes(...charData);
            // console.log(totalArr)
            // let t2 = + new Date();
            // console.log('t2: ' + t2);
            let itemObj = {};//组装数组中的每一项
            let zbArr = [];//组装数据
            let totalArrLength = totalArr.length;
            if(totalArr && totalArrLength > 0){
                totalArr.forEach((data,_idx)=>{
                    itemObj = {
                        id:_idx + 1
                    };
                    data.forEach((_item,idx_)=>{
                        itemObj[`v${idx_ + 1}_t`] = attr[`${idx_}`].attr_name;
                        itemObj[`v${idx_ + 1}`] = _item.txt;
                        itemObj[`v${idx_ + 1}_id`] = _item.key;
                        itemObj[`v${idx_ + 1}_tid`] = idx_;
                        itemObj['key'] = (itemObj['key'] ? itemObj['key'] : '') + itemObj[`v${idx_ + 1}_tid`] + '-' + itemObj[`v${idx_ + 1}_id`] + '_';
                    })
                    itemObj.price = 0.01;
                    itemObj.stock = 0;
                    itemObj.sellNum = 0;
                    zbArr.push(itemObj);
                })                                
            }
            // let t3 = + new Date();
            // console.log('t3: ' + t3);
            // console.log(zbArr.length)
            this.setState({
                charTableData:zbArr
            },()=>{
                this.setState({
                    recordCharTableData:JSON.parse(JSON.stringify(this.state.charTableData)),//记录数据
                })
                let stockArr = [];//库存数组
                zbArr.forEach(item=>{
                    stockArr.push(item.stock);
                })
                this.setState({
                    storage:stockArr.reduce((total,num)=>{return Number(total) + Number(num)})
                })
                if(this.state.charTableData && this.state.charTableData.length > 201){
                    this.setState({
                        overCharVis:true,
                        isOverChar:true
                    })
                    return;
                }else{
                    this.setState({
                        isOverChar:false
                    })
                }
            })
        })
    }else{
        //在原本的属性列上新增属性值和修改属性名
        // console.log('在原本的属性列上新增属性值和修改属性名')
        let charData = [];
        let dataObj = {};//实际数据整理
        let receiveAttr = attr;//收到的属性数据
        let filterAttr = [];//过滤出来有效的属性数据
        if(receiveAttr && receiveAttr.length > 0){
            receiveAttr.forEach(item=>{
                if(item.attr_val && item.attr_val.length > 0){
                    //把有属性值的属性项筛选出来
                    filterAttr.push(item);
                }
            })
        }
        filterAttr.forEach((val,idx)=>{
            let arrItem = [];//单个属性拼接
            let totalArr = [];//整理后的数据
            if(val.attr_val && val.attr_val.length > 0){
                val.attr_val.forEach((item,index)=>{
                    arrItem.push({key:index,txt:item.txt});
                })
                charData.push(arrItem);
            }
            // console.log('charData: ',charData)
            totalArr = calcDescartes(...charData);
            
            // console.log('totalArr: ',totalArr)
            let itemObj = {};//组装数组中的每一项
            let zbArr = [];//组装数据
            let totalArrLength = totalArr.length;
            if(totalArr && totalArrLength > 0){
                totalArr.forEach((data,_idx)=>{
                    itemObj = {
                        id:_idx + 1
                    };
                    data.forEach((_item,idx_)=>{
                        itemObj[`v${idx_ + 1}_t`] = filterAttr[`${idx_}`].attr_name;
                        itemObj[`v${idx_ + 1}`] = _item.txt;
                        itemObj[`v${idx_ + 1}_id`] = _item.key;
                        itemObj[`v${idx_ + 1}_tid`] = idx_;
                        itemObj['key'] = (itemObj['key'] ? itemObj['key'] : '') + itemObj[`v${idx_ + 1}_tid`] + '-' + itemObj[`v${idx_ + 1}_id`] + '_';
                    })
                    itemObj.price = 0.01;
                    itemObj.stock = 0;
                    itemObj.sellNum = 0;
                    zbArr.push(itemObj);
                })
                
                if(idx == (filterAttr.length - 1)){
                    //数据整理完整后进行处理
                    // console.log(`原始数据: `,recordCharTableData)
                    // console.log(`重组数据: `,zbArr)  
                    let zbKey = [];//重组数据的key集合
                    zbArr.forEach(item=>{
                        zbKey.push(item.key);
                    })
                    // console.log('recordCharTableData: ',recordCharTableData)
                    //循环原始数据
                    recordCharTableData.forEach(item=>{
                        //先判断key值是否与重组数据的key集合一致
                        if(zbKey.indexOf(item.key) > -1){
                            // console.log('找到了key: ',item.key);
                            // console.log('filterAttr: ',filterAttr)
                            //再把key进行数组分割
                            let key_arr = item.key.split('_').filter(d=>d);
                            let is_diff = false;//没有修改值
                            //需要原始数据当前项和attr里面的值是否一致 一致把当前项覆盖到重组数据的key位置处  需要把item中的每一项都对比一遍 再进行替换处理
                            for(let k_i = 0; k_i < key_arr.length; k_i++){
                                let index = k_i;
                                key_arr[k_i] = key_arr[k_i].split('-');
                                // console.log('key_arr[k_i]: ',key_arr[k_i])
                                // console.log('item[`v${index + 1}_t`]--item[`v${index + 1}`]: ', item[`v${index + 1}_t`],item[`v${index + 1}`])
                                // console.log('filterAttr[`${key_arr[k_i][0]}`].attr_name---filterAttr[`${key_arr[k_i][0]}`][`attr_val`][`${key_arr[k_i][1]}`][`txt`]: ', filterAttr[`${key_arr[k_i][0]}`].attr_name,filterAttr[`${key_arr[k_i][0]}`]['attr_val'][`${key_arr[k_i][1]}`]['txt'])
                                if(item[`v${index + 1}_t`] == filterAttr[`${key_arr[k_i][0]}`].attr_name){
                                    if(item[`v${index + 1}`] == filterAttr[`${key_arr[k_i][0]}`]['attr_val'][`${key_arr[k_i][1]}`]['txt']){
                                        // console.log('属性值没有变化 原始数据项直接覆盖重组数据项: ',item)
                                        // zbArr[zbKey.indexOf(item.key)] = item;
                                    }else{
                                        //属性值有变化
                                        is_diff = true; 
                                        // console.log('zbArr :',zbArr)
                                        // console.log('属性值有变化 使用原本的数据: ',zbArr[zbKey.indexOf(item.key)])
                                        // zbArr[zbKey.indexOf(item.key)] = zbArr[zbKey.indexOf(item.key)];
                                        break;
                                    }
                                }else{
                                    //列名有变化
                                }
                            }
                            // console.log('is_diff: ' + is_diff);
                            if(is_diff){
                                // console.log('属性值有变化 使用原本的数据: ',zbArr[zbKey.indexOf(item.key)])
                                zbArr[zbKey.indexOf(item.key)] = zbArr[zbKey.indexOf(item.key)];
                            }else{
                                // console.log('属性值没有变化 原始数据项直接覆盖重组数据项: ',item)
                                zbArr[zbKey.indexOf(item.key)] = item;
                            }
                        }else{
                            // console.log('没找到key')
                        }
                    })
                    // console.log(`新__原始数据: `,recordCharTableData)
                    // console.log(`新__重组数据: `,zbArr) 
                    // console.log(zbArr.length)
                    this.setState({
                        charTableData:zbArr
                    },()=>{
                        let stockArr = [];//库存数组
                        zbArr.forEach(item=>{
                            stockArr.push(item.stock);
                        })
                        this.setState({
                            storage:stockArr.reduce((total,num)=>{return Number(total) + Number(num)})
                        })
                        if(this.state.charTableData && this.state.charTableData.length > 201){
                            this.setState({
                                overCharVis:true,
                                isOverChar:true
                            })
                            return;
                        }else{
                            this.setState({
                                isOverChar:false
                            })
                        }
                    })
                }                          
            }
        })
    }
    
}
//处理属性明细数据条件判断  新增/编辑 属性
handleCharData(attr){
    //区分编辑和添加
    //新增尺码M属性值  先判断第一列有几个属性值 有几个属性值就插入几次 先插入第一条数据 用1*第二列数值的位置插入
    const {isEdit,charTableData,recordCharTableData,recordAttr} = this.state;        
    if(isEdit){
        //编辑 
        // console.log('recordAttr: ',recordAttr)
        // console.log('attr: ',attr)
        // console.log('charTableData: ',charTableData)
        // console.log('recordCharTableData: ',recordCharTableData)
        if(recordAttr.length != attr.length){
            //新增了某一列 删除了某一列
            // console.log('操作:新增了某一列 删除了某一列')
            this.handleCharDetailData(attr);
        }else{
            //在原本的属性列上新增属性值和修改属性名
            // console.log('在原本的属性列上新增属性值和修改属性名')
            let charData = [];
            let dataObj = {};//实际数据整理
            let receiveAttr = attr;//收到的属性数据
            let filterAttr = [];//过滤出来有效的属性数据
            if(receiveAttr && receiveAttr.length > 0){
                receiveAttr.forEach(item=>{
                    if(item.attr_val && item.attr_val.length > 0){
                        //把有属性值的属性项筛选出来
                        filterAttr.push(item);
                    }
                })
            }
            filterAttr.forEach((val,idx)=>{
                let arrItem = [];//单个属性拼接
                let totalArr = [];//整理后的数据
                if(val.attr_val && val.attr_val.length > 0){
                    val.attr_val.forEach((item,index)=>{
                        arrItem.push({key:index,txt:item.txt});
                    })
                    charData.push(arrItem);
                }
                // console.log('charData: ',charData)
                totalArr = calcDescartes(...charData);
                let itemObj = {};//组装数组中的每一项
                let zbArr = [];//组装数据
                let totalArrLength = totalArr.length;
                // console.log('totalArr: ',totalArr)
                if(totalArr && totalArrLength > 0){
                    totalArr.forEach((data,_idx)=>{
                        itemObj = {
                            id:_idx + 1
                        };
                        data.forEach((_item,idx_)=>{
                            itemObj[`v${idx_ + 1}_t`] = filterAttr[`${idx_}`].attr_name;
                            itemObj[`v${idx_ + 1}`] = _item.txt;
                            itemObj[`v${idx_ + 1}_id`] = _item.key;
                            itemObj[`v${idx_ + 1}_tid`] = idx_;
                            itemObj['key'] = (itemObj['key'] ? itemObj['key'] : '') + itemObj[`v${idx_ + 1}_tid`] + '-' + itemObj[`v${idx_ + 1}_id`] + '_';
                        })
                        itemObj.price = 0.01;
                        itemObj.stock = 0;
                        itemObj.sellNum = 0;
                        // console.log('itemObj: ',itemObj)
                        zbArr.push(itemObj);
                        // console.log('zbArr: ',zbArr)
                    })
                    // console.log('zbArr: ',zbArr)
                    if(idx == (filterAttr.length - 1)){
                        //数据整理完整后进行处理
                        // console.log(`原始数据: `,recordCharTableData)
                        // console.log(`重组数据: `,zbArr)  
                        let zbKey = [];//重组数据的key集合
                        zbArr.forEach(item=>{
                            zbKey.push(item.key);
                        })
                        //循环原始数据
                        recordCharTableData.forEach(item=>{
                            //先判断key值是否与重组数据的key集合一致
                            if(zbKey.indexOf(item.key) > -1){
                                // console.log('找到了key: ',item.key);
                                // console.log('filterAttr: ',filterAttr)
                                //再把key进行数组分割
                                let key_arr = item.key.split('_').filter(d=>d);
                                let is_diff = false;//没有修改值
                                //需要原始数据当前项和attr里面的值是否一致 一致把当前项覆盖到重组数据的key位置处  需要把item中的每一项都对比一遍 再进行替换处理
                                for(let k_i = 0; k_i < key_arr.length; k_i++){
                                    let index = k_i;
                                    key_arr[k_i] = key_arr[k_i].split('-');
                                    // console.log('key_arr[k_i]: ',key_arr[k_i])
                                    // console.log('item[`v${index + 1}_t`]--item[`v${index + 1}`]: ', item[`v${index + 1}_t`],item[`v${index + 1}`])
                                    // console.log('filterAttr[`${key_arr[k_i][0]}`].attr_name---filterAttr[`${key_arr[k_i][0]}`][`attr_val`][`${key_arr[k_i][1]}`][`txt`]: ', filterAttr[`${key_arr[k_i][0]}`].attr_name,filterAttr[`${key_arr[k_i][0]}`]['attr_val'][`${key_arr[k_i][1]}`]['txt'])
                                    if(item[`v${index + 1}_t`] == filterAttr[`${key_arr[k_i][0]}`].attr_name){
                                        if(item[`v${index + 1}`] == filterAttr[`${key_arr[k_i][0]}`]['attr_val'][`${key_arr[k_i][1]}`]['txt']){
                                            // console.log('属性值没有变化 原始数据项直接覆盖重组数据项: ',item)
                                            // zbArr[zbKey.indexOf(item.key)] = item;
                                        }else{
                                            //属性值有变化
                                            is_diff = true; 
                                            // console.log('zbArr :',zbArr)
                                            // console.log('属性值有变化 使用原本的数据: ',zbArr[zbKey.indexOf(item.key)])
                                            // zbArr[zbKey.indexOf(item.key)] = zbArr[zbKey.indexOf(item.key)];
                                            break;
                                        }
                                    }else{
                                        //列名有变化
                                        is_diff = true; 
                                        break;
                                    }
                                }
                                // console.log('zbArr: ',zbArr)
                                // console.log('is_diff: ' + is_diff);
                                if(is_diff){
                                    // console.log('属性值有变化 使用原本的数据: ',zbArr[zbKey.indexOf(item.key)])
                                    zbArr[zbKey.indexOf(item.key)] = zbArr[zbKey.indexOf(item.key)];
                                }else{
                                    // console.log('属性值没有变化 原始数据项直接覆盖重组数据项: ',item)
                                    zbArr[zbKey.indexOf(item.key)] = item;
                                }
                            }
                        })
                        // console.log(`新__原始数据: `,recordCharTableData)
                        // console.log(`新__重组数据: `,zbArr) 
                        // console.log(zbArr.length)
                        this.setState({
                            charTableData:zbArr
                        },()=>{
                            let stockArr = [];//库存数组
                            zbArr.forEach(item=>{
                                stockArr.push(item.stock);
                            })
                            this.setState({
                                storage:stockArr.reduce((total,num)=>{return Number(total) + Number(num)})
                            })
                            if(this.state.charTableData && this.state.charTableData.length > 201){
                                this.setState({
                                    overCharVis:true,
                                    isOverChar:true
                                })
                                return;
                            }else{
                                this.setState({
                                    isOverChar:false
                                })
                            }
                        })
                    }                          
                }
            })
        }   
    }else{
        //添加
        this.handleCharDetailData(attr);
    }
}

//处理属性明细显示与隐藏
handleCharDetailShow(attr){
    let status_de = '';//明细状态
    let filterArr = [];//把没有属性名的数据过滤掉
    if(attr && attr.length > 0){
        //属性添加部分  必须有一个属性名 和属性值
        attr.forEach((item,index)=>{
            if(item.attr_name && String(item.attr_name).trim() != ''){
                filterArr.push(item);                  
            }
        })
    }
    if(filterArr && filterArr.length > 0){
        //整理数据
        filterArr.forEach((item,index)=>{
            if(item.attr_name && String(item.attr_name).trim() != ''){
                if(item.attr_val.length > 0){
                    item.attr_val.forEach(val=>{
                        if(val.txt && String(val.txt).trim() != ''){
                            status_de = true;
                        }
                    })
                }                   
            }else{
                status_de = false;
            }
        })
    }
    this.setState({
        isShowCharDetail:status_de ? status_de : false
    })
}

render 前端页面代码

let charColumns = [],charData=this.state.charTableData;//属性明细
    let columsObj = {};//列数据整理
    let dataObj = {};//实际数据整理
    let receiveAttr = attr;//收到的属性数据
    let filterAttr = [];//过滤出来有效的属性数据
    if(receiveAttr && receiveAttr.length > 0){
        receiveAttr.forEach(item=>{
            if(item.attr_val && item.attr_val.length > 0){
                //把有属性值的属性项筛选出来
                filterAttr.push(item);
            }
        })
    }
    if(filterAttr && filterAttr.length > 0){
        filterAttr.forEach((val,idx)=>{
            columsObj = {
                title: val.attr_name,
                dataIndex: `v${idx + 1}`,
                width:100,
                render: (text, record, index) => {
                    const obj = {
                        children: text !== null ? text : '',
                        props: {}
                    }
                    obj.props.rowSpan =  idx + 1 == 1 ? mergeCells(text, charData, `v${idx + 1}`, index) : '';
            
                    return obj
                },
            };
            charColumns.push(columsObj);
        }) 
        const sellTitle = 
            

销量

//固定列 let fixColumn = [ { title: '价格(元)', dataIndex: `price`, align:'center', width:120, render: (text, record, index) => ( ) }, { title: '库存', align:'center', dataIndex: `stock`, width:120, render: (text, record, index) => ( ) }, { title: sellTitle, dataIndex: `sellNum`, align:'center', width:100, } ]; // console.log(fixColumn) charColumns = charColumns.concat(fixColumn); } const mergeCells = (text, data, key, index) => { // 上一行该列数据是否一样 if (index !== 0 && text === data[index - 1][key]) { return 0; } let rowSpan = 1; // 判断下一行是否相等 let dataLength = data.length; for (let i = index + 1; i < dataLength; i++) { if (text !== data[i][key]) { break; } rowSpan++; } return rowSpan; }
商品规格: { visibleOuterAttrBtn && ( 最多添加5个商品属性 ) }
{ !visibleOuterAttrBtn &&
    { attr && attr.length > 0 && attr.map((item,index)=>(
  • 属性名:

    { index == 0 ? 添加属性值图片 : '' }

    0 ? {}: {marginBottom: '20px'}}>属性值:

    { item.attr_val && item.attr_val.length > 0 && item.attr_val.map((val,idx)=>(
    { (allowAddCharPic && index == 0) ?
    {val.img ? 属性值图片 : uploadButton}
    : '' }
    )) } { (item.attr_val && item.attr_val.length < 20) ? : '' }
    { (allowAddCharPic && item.attr_val && item.attr_val.length > 0 && index == 0) ?

    (仅支持为第一组属性设置规属性值图片;买家选择不同属性值会看到对应规格图片,建议尺寸:800 x 800像素,图片大小:2M以内,图片格式:jpg/png/gif)

    : '' }
    { isSeckill ? : }
  • )) }
{ visibleAttrBtn && ( 最多添加5个商品属性 ) }
}
{ // 点击添加属性值 才显示 isShowCharDetail ?
属性明细:

批量设置:

{ sortOpt ?
:
}
}/> : '' }

你可能感兴趣的:(react+sku 实现商品属性组合)