前言
公司商城業(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> : ''
}