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 = <span className="tableSellTitle">
            <p className="title">销量</p>
            <Tooltip placement="right" title="销量=商品累计支付件数 - 商品累计退款件数">
                <Icon type="question-circle" />
            </Tooltip>
        </span>
        //固定列
        let fixColumn = [
            {
                title: '价格(元)',
                dataIndex: `price`,
                align:'center',
                width:120,
                render: (text, record, index) => (
                    <InputNumber min={0.01} max={100000} value={text} onChange={this.handleInput.bind(this,'singleCharPrice',record)} style={{width: 120}} disabled={isSeckill} onBlur={this.handleInput.bind(this,'singleBlurCharPrice',record)}/>
                )
            },
            {
                title: '库存',
                align:'center',
                dataIndex: `stock`,
                width:120,
                render: (text, record, index) => (
                    <InputNumber min={0} max={99999} value={text} onChange={this.handleInput.bind(this,'singleCharStock',record)} style={{width: 120}} disabled={isSeckill} onBlur={this.handleInput.bind(this,'singleCharBlurStock',record)}/>
                )
            },
            {
                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;
    }
    
<div className="item char charItem">
    <div>
        <span className="txt" style={{marginLeft: 11}}>商品规格:</span>
        {
            visibleOuterAttrBtn && <span>
                <Button onClick={this.handleBtn.bind(this,'addFirstAttr')} className="addChar" disabled={isSeckill}>+ 添加属性</Button><span className="charTips">( 最多添加5个商品属性 )</span>
            </span>
        }
    </div>
    {
        !visibleOuterAttrBtn && <div className="charContainer charaContainer">
            <ul>
                {
                    attr && attr.length > 0 && attr.map((item,index)=>(
                        <li className="charItemLi">
                            <div>
                                <div className="charName">
                                    <p className="char_titl">属性名:</p>
                                    <Input placeholder="例如:尺码" value={item.attr_name} onChange={this.getAttr.bind(this,{tag:'name',index})} onBlur={this.handleInput.bind(this,'charBlurName',index)} disabled={isSeckill}/>
                                    {
                                        index == 0 ? <Checkbox disabled={isSeckill} onChange={this.handleBtn.bind(this,'allowAddCharPic')} checked={allowAddCharPic}><span>添加属性值图片</span></Checkbox> : ''
                                    }
                                </div>
                                <div className="charValue">
                                    <p className="char_titl" style={item.attr_val && item.attr_val.length > 0 ? {}: {marginBottom: '20px'}}>属性值:</p>
                                    <div className="charValContainer">
                                        {
                                            item.attr_val && item.attr_val.length > 0 && item.attr_val.map((val,idx)=>(
                                                <div className="char_container">
                                                    <div className="char_val">
                                                        <Input placeholder={idx == 0 ? "例如:S,M,L,XL" : ''} onChange={this.handleInput.bind(this,'changeAttrVal',{'outer':index,'inner':idx,'item':val})} onBlur={this.handleInput.bind(this,'changeBlurAttrVal',{'outer':index,'inner':idx,'item':val})} value={val.txt} disabled={isSeckill} />
                                                        <Icon type="close-circle" theme="filled" onClick={this.handleBtn.bind(this,'delCharVal',{index,idx})} style={isSeckill ? {cursor:'not-allowed'} : {}}/>
                                                    </div>
                                                    {
                                                        (allowAddCharPic && index == 0) ? <div className="char_val_upload">
                                                            <Upload 
                                                                name="goods" 
                                                                listType="picture-card" 
                                                                showUploadList="false" 
                                                                className="picUpload"
                                                                action=""
                                                                beforeUpload={this.beforeGoodsUpload}
                                                                onChange={this.handleBtn.bind(this,'uploadCharPic',{index,idx})}
                                                                headers={config.headerAuth}
                                                                showUploadList={false}
                                                                accept=".png,.jpg,.jpeg,.gif"
                                                                disabled={isSeckill}
                                                                >
                                                                    {val.img ? <img src={val.img} alt="属性值图片" style={{width:'86px',height:'86px',marginRight:0}} /> : uploadButton}
                                                            </Upload>
                                                        </div> : ''
                                                    }
                                                </div>
                                            ))
                                        }
                                        {
                                            (item.attr_val && item.attr_val.length < 20) ? <div style={{display:'flex',height:'18px',alignItems:'center'}}>
                                                <a href="javascript:;" onClick={this.handleBtn.bind(this,'addCharValue',index)} style={isSeckill ? {cursor:'not-allowed'} : {}}>添加属性值</a>
                                                <Tooltip placement="right" title="每个属性最多可添加20个属性值">
                                                    <Icon type="question-circle" />
                                                </Tooltip>
                                            </div> : ''
                                        }
                                    </div>
                                    
                                </div>
                                {
                                    (allowAddCharPic && item.attr_val && item.attr_val.length > 0 && index == 0) ? <p className="tips">(仅支持为第一组属性设置规属性值图片;买家选择不同属性值会看到对应规格图片,建议尺寸:800 x 800像素,图片大小:2M以内,图片格式:jpg/png/gif)</p> : ''
                                }
                            </div>
                            {
                                isSeckill ? <img src={require('../../../assets/application/icon-del.png')} style={isSeckill ? {marginLeft:10,cursor:'not-allowed',width:'17px',height:'17px'} : {marginLeft:10,cursor:'pointer',width:'17px',height:'17px'}}/> : <Popconfirm title="确定要删除该条属性吗?" onConfirm={this.delAttr.bind(this,index)} okText="确定" cancelText="取消">
                                    <img src={require('../../../assets/application/icon-del.png')} style={isSeckill ? {marginLeft:10,cursor:'not-allowed',width:'17px',height:'17px'} : {marginLeft:10,cursor:'pointer',width:'17px',height:'17px'}}/>
                                </Popconfirm>
                            }
                        </li>
                    ))
                }
            </ul>
            {
                visibleAttrBtn && <span>
                    <Button onClick={this.handleBtn.bind(this,'addNewAttr')} className="addChar" disabled={isSeckill}>+ 添加属性</Button><span className="charTips">( 最多添加5个商品属性 )</span>
                </span>
            }
        </div>
    }
</div>
{
    // 点击添加属性值 才显示
    isShowCharDetail ? <div className="item charDetail">
        <span className="txt" style={{marginLeft: 11}}>属性明细:</span>
        <div className="charTableCon">
            <Table columns={charColumns} dataSource={charData} bordered pagination={false} className="charTable" scroll={{ y: 454 }} footer={()=><div className="charTabFooter">
                <p>批量设置:</p>
                {
                    sortOpt ? <div>
                        <InputNumber min={0} max={sortOpt == 'price' ? 100000 : sortOpt == 'stock' ? 99999 : 99999} value={sortOpt == 'price' ? totalPrice : sortOpt == 'stock' ? totalStock : '0'} onChange={this.handleInput.bind(this,sortOpt)} style={{width: 120}}/>
                        <Button type="link" onClick={this.handleBtn.bind(this,'saveTotalSet')}>保存</Button>
                        <Button type="link" onClick={this.handleBtn.bind(this,'cancelTotalSet')}>取消</Button>
                    </div> : <div className="btnDiv">
                        <Button type="link" onClick={this.handleBtn.bind(this,'setTotalPrice')} disabled={isSeckill}>价格</Button>
                        <Button type="link" onClick={this.handleBtn.bind(this,'setTotalStock')} disabled={isSeckill}>库存</Button>
                    </div>
                }
            </div>}/>
        </div>
    </div> : ''
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值