猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,变身猿人找到工作不是问题。还等什么呢?关注公号,取基础代码,一起实战吧。
上两个章节,猿人君教会了你如何选择类目,以及加载商品发布需要准备的数据,今天,猿人君会教授你商品发布中的一个核心知识,sku数据生成。
功能概览
在商品发布详情页面,勾选销售属性下的销售属性值后,页面会根据选择的销售属性值,动态生成需要详细填写信息的sku数据。事实上,商品是对sku的一个聚合,我们真正售卖的东西,是sku。关于商品和sku的区别,大家可以看之前的设计文章。
数据库设计
本章节主要讲述内容,在于sku数据生成以及页面数据处理问题,稍微偏算法一些,不涉及数据库相关内容。
前端功能实现
勾选销售属性值,生成需要填写的sku列表功能实现。
勾选销售属性值后,页面生成需要填写的sku列表。在上一章节,我们已经为此做好了数据准备,我们先回顾下,销售属性的获取功能。
从功能上来讲,当我们勾选一个销售属性时,那么就会生成条相应的记录。比如,某个商品有两个销售属性——容量和颜色。其中容量有2G和4G两个属性值,颜色可能有多种,比如,红,黄,蓝三种颜色。那么当我们勾选将这些属性值勾选之后,页面会生成2G,红色;2G,黄色;2G,蓝色;4G,红色;4G,黄色;4G,蓝色。这么六种sku。以此类推,要是有更多的销售属性和属性值,那么就会产生更多的sku了。
嗯,这似乎是一个比较头疼的问题,需要将勾选的销售属性值,进行一一组合。简单点来讲,就选中的属性和属性值而言,对应的sku组合,是一个笛卡尔积组合可能性的问题。
这个问题的痛点在于不知道勾选了哪些销售属性和属性值,从而导致循环的不确定性,为了简化问题,我们先假设全量输出这些可能性,我们可以先用java来模拟这一段算法。我们考虑,有销售属性aa,bb,cc,dd。aa分别有a1,a2两个属性值,bb只有1个属性值b1,cc有属性值c1,c2,c3,dd有属性值d1,d2。
如果全部勾选的话,我们得到组合值将类似于这样的情况:a1,b1,c1,d2;a1,b1,c1,d1这样的组合。我们先定义代表属性和属性值的数组。
private static String[] aa={"a1","a2"};
private static String[] bb={"b1"};
private static String[] cc={"c1","c2","c3"};
private static String[] dd={"d1","d2"};
再对比我们所需要的组合结果,你会发现我们要的值类似于如下下标aa[0],bb[0],cc[0],dd[1];aa[0],bb[0],cc[0],dd[0]……,也就是说,要完成a1的说有组合可能性,需要遍历bb,cc,dd的每一个下标来完成这个过程。
为了方便组合,我们可以将上述的结构看成一个二维数组。
private static String[][] xyz={aa,bb,cc,dd};
想要获取每一个值的组合,我们分别根据下标遍历每一个数组就好了,但是在循环遍历的过程中,我们需要事先知晓当前的数组,已经遍历到哪一个位置了,否则会导致数组越界。为了解决这一个问题,我我们定义一个数组。
private static int[] counter={0,0,0,0};
用于记录二维数组中,每一个数组遍历到的次数。
除此之外我们怎样才能保证,二维数组中的每一个数组元素中的元素都被遍历到呢?这个就需要用到递归了。
private static void handleCounterIndex(){
counter[counterIndex]++;
if (counter[counterIndex]>=xyz[counterIndex].length){
counter[counterIndex]=0;
counterIndex--;
if (counterIndex>=0){
handleCounterIndex();
}
counterIndex=xyz.length-1;
}
}
那会产生多少种结果呢?自然是所有属性值的乘积了。我们可以看下完整的java程序:
public class Test {
private static String[] aa={"a1","a2"};
private static String[] bb={"b1"};
private static String[] cc={"c1","c2","c3"};
private static String[] dd={"d1","d2"};
private static String[][] xyz={aa,bb,cc,dd};
private static int counterIndex =xyz.length-1;
private static int[] counter={0,0,0,0};
public static void main(String[] args) {
int myLength=aa.length*bb.length*cc.length*dd.length;
for (int i=0;i<myLength;i++){
System.out.print(aa[counter[0]]);
System.out.print("\t");
System.out.print(bb[counter[1]]);
System.out.print("\t");
System.out.print(cc[counter[2]]);
System.out.print("\t");
System.out.print(dd[counter[3]]);
System.out.println();
handleCounterIndex();
}
}
private static void handleCounterIndex(){
counter[counterIndex]++;
if (counter[counterIndex]>=xyz[counterIndex].length){
counter[counterIndex]=0;
counterIndex--;
if (counterIndex>=0){
handleCounterIndex();
}
counterIndex=xyz.length-1;
}
}
}
嗯,以上只是固定的长度的写法,有这个基础了,我们将java程序转变为需要为我们需要的JavaScript程序就好了。
// 销售属性选择改变
checksaleBoxClick(value) {
// console.log('执行了')
this.ruleForm.tableList = []
// 默认都未勾选
this.initSelectValues()
for (let i = 0; i < value.length; i++) {
// 选中选项
this.selectValues(value[i])
}
// console.log(this.saleCheckedList)
// 初始化需要做笛卡尔集的选项
var spos = []
for (let i = 0; i < this.salePropertyList.length; i++) {
if (this.salePropertyList[i].checked === 1) {
var svalues = []
for (let j = 0; j < this.salePropertyList[i].valueList.length; j++) {
if (this.salePropertyList[i].valueList[j].checked === 1) {
var obj = {
id: this.salePropertyList[i].valueList[j].id,
name: this.salePropertyList[i].valueList[j].name
}
svalues.push(obj)
}
}
var obj2 = {
id: this.salePropertyList[i].id,
name: this.salePropertyList[i].name,
valueList: []
}
obj2.valueList = svalues
spos.push(obj2)
}
}
// 勾选属性小于1表示未选中
if (spos.length < 1) {
return
}
var counterIndex = spos.length - 1
var mylength = 1
var counter = []
for (let i = 0; i < spos.length; i++) {
counter.push(0)
mylength *= spos[i].valueList.length
}
for (let k = 0; k < mylength; k++) {
var str = ''
var valueList = []
for (let i = 0; i < spos.length; i++) {
if (undefined !== spos[i].valueList[counter[i]]) {
str = str + spos[i].name + ':' + spos[i].valueList[counter[i]].name + '\t'
var valueObj = {
propertyId: spos[i].id,
propertyValueId: spos[i].valueList[counter[i]].id
}
valueList.push(valueObj)
}
}
this.initTableRow(str, valueList)
// 处理行记录
this.handle(counter, spos, counterIndex)
console.log(str)
}
},
// 每一个值和其它值组合
initTableRow(proValueName, proValues) {
var skuObj = {
proValues: proValues,
propertyValue: proValueName,
skuName: '',
supplyPrice: null,
// 卖家SKU编码
skuCode: '',
costPrice: null,
skuFileList: [],
imgList: []
}
this.ruleForm.tableList.push(skuObj)
},
handle(counter, dataList, counterIndex) {
counter[counterIndex]++
// counter和列数比较
if (counter[counterIndex] >= dataList[counterIndex].valueList.length) {
counter[counterIndex] = 0
counterIndex--
if (counterIndex >= 0) {
this.handle(counter, dataList, counterIndex)
}
counterIndex = dataList.length - 1
}
}