react+sku 實現(xiàn)商品屬性組合

前言

公司商城業(yè)務(wù)需把原本的統(tǒng)一屬性分割為單商品的屬性值,在編寫的過程中,困難度很大,借鑒了很多案例,最終實現(xiàn),現(xiàn)分享出來,供大家參考。

實現(xiàn)效果圖

image.png

數(shù)據(jù)格式對照圖

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:[],//屬性明細數(shù)據(jù)
    recordCharTableData:[],//屬性明細數(shù)據(jù) 記錄數(shù)據(jù) 用于對比
    visibleAttrBtn:true,//屬性添加按鈕是否顯示
    sortOpt:'',//批量設(shè)置方式 price價格 stock庫存
    totalPrice:0,//批量價格值
    totalStock:0,//批量庫存值
    visibleOuterAttrBtn:true,//屬性外層的 添加屬性按鈕是否顯示 是否展示屬性可編輯區(qū)域
    allowAddCharPic:false,//是否允許第一個屬性添加屬性值圖片
    disabledStorage:false,//庫存是否可以修改  編輯時 秒殺按鈕已開啟狀態(tài)不能修改庫存
    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)),//記錄數(shù)據(jù)
                })
            }
            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)),//記錄數(shù)據(jù)
                    })
                }
            })
            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:''
            };//新增的屬性值 根據(jù)是否選中可添加屬性值圖片來決定內(nèi)容是什么
            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)),//記錄數(shù)據(jù)
                    },()=>{
                        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)),//記錄數(shù)據(jù)
                },()=>{
                    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)),//記錄數(shù)據(jù)
                    })
                }
            }).catch(res=>{
                message.error(res);
                return;
            })   
            break;
        case 'setTotalPrice':
            //批量設(shè)置 屬性價格
            this.setState({
                sortOpt:'price'
            })
            break;
        case 'setTotalStock':
            //批量設(shè)置 屬性庫存
            this.setState({
                sortOpt:'stock'
            })
            break;
        case 'saveTotalSet':
            //保存批量設(shè)置屬性
            if(charTableData && charTableData.length > 0){
                let stockArr = [];//storage
                let axios_url = '';//請求地址
                let obj_data = {};//請求需要傳遞的參數(shù)
                let ids = [];//傳遞參數(shù)需要的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':
            //取消批量設(shè)置屬性
            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){
                        //在同一個屬性名下面不允許出現(xiàn)相同的屬性值
                        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){
                            //已經(jīng)存在相同的屬性值了
                            judgeAttr[index].attr_val[e.inner].txt = '';
                            this.setState({
                                attr:judgeAttr
                            })
                            message.error('已經(jīng)添加了相同的屬性值');
                            return;
                        }
                    }
                }
                this.handleCharData(this.state.attr)
            }
            // this.handleCharData(this.state.attr);//處理屬性明細數(shù)據(jù)
            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':
            //批量設(shè)置價格值
            this.setState({
                totalPrice:e
            })
            break;
        case 'stock':
            //批量設(shè)置庫存值
            this.setState({
                totalStock:parseInt(e)
            })
            break;
        case 'charBlurName':
            //屬性名失去焦點 判斷是否存在重復(fù)的屬性名
            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('已經(jīng)添加了相同的屬性名');
                            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)),//記錄數(shù)據(jù)
    },()=>{
        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)),//記錄數(shù)據(jù)
    },()=>{
        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:[],//清空屬性明細數(shù)據(jù)
                storage:0,//總庫存清零
            })
        }
        this.handleCharData(this.state.attr);
    }) 
}
//處理屬性明細展示數(shù)據(jù) 新增/重置屬性
handleCharDetailData(attr){
    //添加商品的數(shù)據(jù)處理情況 重新渲染表格數(shù)據(jù)
    let charData = [];
    let dataObj = {};//實際數(shù)據(jù)整理
    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 = [];//整理后的數(shù)據(jù)
            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 = {};//組裝數(shù)組中的每一項
            let zbArr = [];//組裝數(shù)據(jù)
            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)),//記錄數(shù)據(jù)
                })
                let stockArr = [];//庫存數(shù)組
                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 = {};//實際數(shù)據(jù)整理
        let receiveAttr = attr;//收到的屬性數(shù)據(jù)
        let filterAttr = [];//過濾出來有效的屬性數(shù)據(jù)
        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 = [];//整理后的數(shù)據(jù)
            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 = {};//組裝數(shù)組中的每一項
            let zbArr = [];//組裝數(shù)據(jù)
            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)){
                    //數(shù)據(jù)整理完整后進行處理
                    // console.log(`原始數(shù)據(jù): `,recordCharTableData)
                    // console.log(`重組數(shù)據(jù): `,zbArr)  
                    let zbKey = [];//重組數(shù)據(jù)的key集合
                    zbArr.forEach(item=>{
                        zbKey.push(item.key);
                    })
                    // console.log('recordCharTableData: ',recordCharTableData)
                    //循環(huán)原始數(shù)據(jù)
                    recordCharTableData.forEach(item=>{
                        //先判斷key值是否與重組數(shù)據(jù)的key集合一致
                        if(zbKey.indexOf(item.key) > -1){
                            // console.log('找到了key: ',item.key);
                            // console.log('filterAttr: ',filterAttr)
                            //再把key進行數(shù)組分割
                            let key_arr = item.key.split('_').filter(d=>d);
                            let is_diff = false;//沒有修改值
                            //需要原始數(shù)據(jù)當前項和attr里面的值是否一致 一致把當前項覆蓋到重組數(shù)據(jù)的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('屬性值沒有變化 原始數(shù)據(jù)項直接覆蓋重組數(shù)據(jù)項: ',item)
                                        // zbArr[zbKey.indexOf(item.key)] = item;
                                    }else{
                                        //屬性值有變化
                                        is_diff = true; 
                                        // console.log('zbArr :',zbArr)
                                        // console.log('屬性值有變化 使用原本的數(shù)據(jù): ',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('屬性值有變化 使用原本的數(shù)據(jù): ',zbArr[zbKey.indexOf(item.key)])
                                zbArr[zbKey.indexOf(item.key)] = zbArr[zbKey.indexOf(item.key)];
                            }else{
                                // console.log('屬性值沒有變化 原始數(shù)據(jù)項直接覆蓋重組數(shù)據(jù)項: ',item)
                                zbArr[zbKey.indexOf(item.key)] = item;
                            }
                        }else{
                            // console.log('沒找到key')
                        }
                    })
                    // console.log(`新__原始數(shù)據(jù): `,recordCharTableData)
                    // console.log(`新__重組數(shù)據(jù): `,zbArr) 
                    // console.log(zbArr.length)
                    this.setState({
                        charTableData:zbArr
                    },()=>{
                        let stockArr = [];//庫存數(shù)組
                        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
                            })
                        }
                    })
                }                          
            }
        })
    }
    
}
//處理屬性明細數(shù)據(jù)條件判斷  新增/編輯 屬性
handleCharData(attr){
    //區(qū)分編輯和添加
    //新增尺碼M屬性值  先判斷第一列有幾個屬性值 有幾個屬性值就插入幾次 先插入第一條數(shù)據(jù) 用1*第二列數(shù)值的位置插入
    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 = {};//實際數(shù)據(jù)整理
            let receiveAttr = attr;//收到的屬性數(shù)據(jù)
            let filterAttr = [];//過濾出來有效的屬性數(shù)據(jù)
            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 = [];//整理后的數(shù)據(jù)
                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 = {};//組裝數(shù)組中的每一項
                let zbArr = [];//組裝數(shù)據(jù)
                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)){
                        //數(shù)據(jù)整理完整后進行處理
                        // console.log(`原始數(shù)據(jù): `,recordCharTableData)
                        // console.log(`重組數(shù)據(jù): `,zbArr)  
                        let zbKey = [];//重組數(shù)據(jù)的key集合
                        zbArr.forEach(item=>{
                            zbKey.push(item.key);
                        })
                        //循環(huán)原始數(shù)據(jù)
                        recordCharTableData.forEach(item=>{
                            //先判斷key值是否與重組數(shù)據(jù)的key集合一致
                            if(zbKey.indexOf(item.key) > -1){
                                // console.log('找到了key: ',item.key);
                                // console.log('filterAttr: ',filterAttr)
                                //再把key進行數(shù)組分割
                                let key_arr = item.key.split('_').filter(d=>d);
                                let is_diff = false;//沒有修改值
                                //需要原始數(shù)據(jù)當前項和attr里面的值是否一致 一致把當前項覆蓋到重組數(shù)據(jù)的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('屬性值沒有變化 原始數(shù)據(jù)項直接覆蓋重組數(shù)據(jù)項: ',item)
                                            // zbArr[zbKey.indexOf(item.key)] = item;
                                        }else{
                                            //屬性值有變化
                                            is_diff = true; 
                                            // console.log('zbArr :',zbArr)
                                            // console.log('屬性值有變化 使用原本的數(shù)據(jù): ',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('屬性值有變化 使用原本的數(shù)據(jù): ',zbArr[zbKey.indexOf(item.key)])
                                    zbArr[zbKey.indexOf(item.key)] = zbArr[zbKey.indexOf(item.key)];
                                }else{
                                    // console.log('屬性值沒有變化 原始數(shù)據(jù)項直接覆蓋重組數(shù)據(jù)項: ',item)
                                    zbArr[zbKey.indexOf(item.key)] = item;
                                }
                            }
                        })
                        // console.log(`新__原始數(shù)據(jù): `,recordCharTableData)
                        // console.log(`新__重組數(shù)據(jù): `,zbArr) 
                        // console.log(zbArr.length)
                        this.setState({
                            charTableData:zbArr
                        },()=>{
                            let stockArr = [];//庫存數(shù)組
                            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 = '';//明細狀態(tài)
    let filterArr = [];//把沒有屬性名的數(shù)據(jù)過濾掉
    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){
        //整理數(shù)據(jù)
        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 = {};//列數(shù)據(jù)整理
    let dataObj = {};//實際數(shù)據(jù)整理
    let receiveAttr = attr;//收到的屬性數(shù)據(jù)
    let filterAttr = [];//過濾出來有效的屬性數(shù)據(jù)
    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="銷量=商品累計支付件數(shù) - 商品累計退款件數(shù)">
                <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) => {
        // 上一行該列數(shù)據(jù)是否一樣
        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}}>商品規(guī)格:</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">(僅支持為第一組屬性設(shè)置規(guī)屬性值圖片;買家選擇不同屬性值會看到對應(yīng)規(guī)格圖片,建議尺寸:800 x 800像素,圖片大?。?M以內(nèi),圖片格式: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>批量設(shè)置:</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> : ''
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容