SPU = Standard Product Unit (标准产品单位)
这样讲可能还是很难理解到底什么是标准产品单位。那么如果以苹果手机为例来说,iPhone 11是一个SPU还是SKU呢?
答案是 SPU 到底什么是SPU呢?我的理解是:SPU是一个标准化的,能相对的包含某些商品显著特征的商品的集合。例如:SPU = 品牌 + 型号 “苹果”+“iPhone 11” 即组成了一个SPU,这里可以称之为“商品”。需要注意的是上面提到的SPU=品牌+型号这个也是相对的意义上的,受场景的和需求的制约。
stock keeping unit(库存量单位)
这个的理解,可以在上面的等式上进一步理解为:SKU = SPU + 规格。这里的规格是个统称。比如手机来说,内存,屏幕大小,颜色等是规格;如衣服,颜色,图案,尺码。
下图中sku_list中的specs数组中的是当前spu有的规格项
"spu_detail": {
"id":2,
"title":"林间有风自营针织衫",
"subtitle":"秋日冬款,浪漫满屋",
"category_id":12,
"root_category_id":2,
"price":"77.00",
"img":"http://i1.sleeve.7yue.pro/assets/ecf8d824-19d4-4db2-a5da-872ab014fecd.png",
"for_theme_img":"https://gitee.com/lrelia7/sleeve-static/raw/master/theme/spu1.png",
"description":null,
"discount_price":"62.00",
"tags":"秋日冬款$浪漫满屋",
"is_test":true,
"online":true,
"sku_list":[
{
"id":2,
"price":77.76,
"discount_price":null,
"online":true,
"img":"http://i1.sleeve.7yue.pro/assets/2d22ffec-b1c1-43e0-ad21-25aa5c26ab34.png",
"title":"金属灰·七龙珠",
"spu_id":2,
"category_id":17,
"root_category_id":3,
"specs":[
{
"key_id":1,
"key":"颜色",
"value_id":45,
"value":"金属灰"
},
{
"key_id":3,
"key":"图案",
"value_id":9,
"value":"七龙珠"
},
{
"key_id":4,
"key":"尺码",
"value_id":14,
"value":"小号 S"
}
],
"code":"2$1-45#3-9#4-14",
"stock":5
},
{
"id":3,
"price":66,
"discount_price":59,
"online":true,
"img":"http://i1.sleeve.7yue.pro/assets/c6b52c90-5b10-4823-baef-6c37d3d3532f.png",
"title":"青芒色·灌篮高手",
"spu_id":2,
"category_id":17,
"root_category_id":3,
"specs":[
{
"key_id":1,
"key":"颜色",
"value_id":42,
"value":"青芒色"
},
{
"key_id":3,
"key":"图案",
"value_id":10,
"value":"灌篮高手"
},
{
"key_id":4,
"key":"尺码",
"value_id":15,
"value":"中号 M"
}
],
"code":"2$1-42#3-10#4-15",
"stock":999
},
{
"id":4,
"price":88,
"discount_price":null,
"online":true,
"img":"http://i1.sleeve.7yue.pro/assets/c6b52c90-5b10-4823-baef-6c37d3d3532f.png",
"title":"青芒色·圣斗士",
"spu_id":2,
"category_id":17,
"root_category_id":3,
"specs":[
{
"key_id":1,
"key":"颜色",
"value_id":42,
"value":"青芒色"
},
{
"key_id":3,
"key":"图案",
"value_id":11,
"value":"圣斗士"
},
{
"key_id":4,
"key":"尺码",
"value_id":16,
"value":"大号 L"
}
],
"code":"2$1-42#3-11#4-16",
"stock":8
},
{
"id":5,
"price":77,
"discount_price":59,
"online":true,
"img":"http://i1.sleeve.7yue.pro/assets/09f32ac8-1af4-4424-b221-44b10bd0986e.png",
"title":"橘黄色·七龙珠",
"spu_id":2,
"category_id":17,
"root_category_id":3,
"specs":[
{
"key_id":1,
"key":"颜色",
"value_id":44,
"value":"橘黄色"
},
{
"key_id":3,
"key":"图案",
"value_id":9,
"value":"七龙珠"
},
{
"key_id":4,
"key":"尺码",
"value_id":14,
"value":"小号 S"
}
],
"code":"2$1-44#3-9#4-14",
"stock":7
}
],
"spu_img_list":[
{
"id":165,
"img":"http://i1.sleeve.7yue.pro/assets/5605cd6c-f869-46db-afe6-755b61a0122a.png",
"spu_id":2
},
{
"id":166,
"img":"http://i1.sleeve.7yue.pro/assets/ecf8d824-19d4-4db2-a5da-872ab014fecd.png",
"spu_id":2
},
{
"id":167,
"img":"http://i1.sleeve.7yue.pro/assets/8451cebd-5a8d-4758-bb2a-652e026a7c00.png",
"spu_id":2
}
],
"spu_detail_img_list":[
{
"id":24,
"img":"http://i2.sleeve.7yue.pro/n4.png",
"spu_id":2,
"index":1
},
{
"id":27,
"img":"http://i2.sleeve.7yue.pro/n7.png",
"spu_id":2,
"index":2
},
{
"id":25,
"img":"http://i2.sleeve.7yue.pro/n5.png",
"spu_id":2,
"index":3
},
{
"id":26,
"img":"http://i2.sleeve.7yue.pro/n6.png",
"spu_id":2,
"index":4
}
],
"sketch_spec_id":1,
"default_sku_id":2
}
将上述的规格提取如下,有四个不同的规格的单品。
SKU名 | 颜色 | 图案 | 尺码 |
---|---|---|---|
sku1 | 金属灰 | 七龙珠 | 小号 S |
sku2 | 青芒色 | 灌篮高手 | 中号 M |
sku3 | 青芒色 | 圣斗士 | 大号 L |
sku4 | 橘黄色 | 七龙珠 | 小号 S |
这里可能会奇怪的点在于,返回的数据格式中并没有返回这个SPU有哪些规格,而是直接返回了有库存的单品的明细。这就意味着需要我们自己去过滤出可以选择的单品的规格。
如上图所示,我们需要的规格数据是横向排布的。即需要把下图中红色框纵向的规格值,横向排布。
应用到代码中即是使用二维数组的形式:[ [ ] , [ ] , [ ] ]
首先将一个sku的规格数组循环出来,放到数组m中。将数组丢给Matrix对象处理
_createMatrix(skuList) {
const m = []
skuList.forEach(sku => {
m.push(sku.specs)
})
return new Matrix(m)
}
将m数组丢给matrix类之后,通过内部rowNum(),colNum()方法
Matrix
class Matrix {
m
constructor(matrix) {
this.m = matrix
}
// m = [[1,2,3],[4,5,6],[7,8,9]]
// 显示如下矩阵
// 1,2,3
// 4,5,6
// 7,8,9
/**
* 获取行数
* @returns {*}
*/
get rowNum() {
return this.m.length
}
/**
* 获取列数
* @returns {*}
*/
get colNum() {
return this.m[0].length
}
each(cb) {
for (let j = 0; j < this.colNum; j++) {
for (let i = 0; i < this.rowNum; i++) {
const element = this.m[i][j]
cb(element, i, j)
}
}
}
transpose() {
const desArr = []
for (let j = 0; j<this.colNum; j++) {
desArr[j] = []
for (let i = 0; i < this.rowNum; i++) {
// 矩阵转置就是需要把x,y对调
desArr[j][i] = this.m[i][j]
}
}
return desArr
}
}
export {
Matrix
}
上图代码中的each方法就在循环遍历m数组,将数组中的元素提取出来,然后调用cb方法返回
/**
* 遍历方式-转置矩阵
*/
initFence1() {
const matrix = this._createMatrix(this.skuList)
const fences = []
let currentJ = -1
matrix.each((element, i, j) => {
if (currentJ !== j) {
// 列号不同,创建一个新的fence
currentJ = j
fences[currentJ] = this._createFence(element)
}
// 列号相同则将当前element的信息设置到当前的fence中
fences[currentJ].pushValueTitle(element.value)
})
}
_createFence(element) {
const fence = new Fence()
return fence
}
上图代码中需要注意的是,i代表行号,j代表列号,数组在循环取出的过程中,如何判断数组当前列已循环完毕,是由j的值来判断的,如果j的值变化,则说明,当前元素为新的一列的第一个元素了。那么就要新建一个fence来存储这一列的元素。
矩阵转置的归结就是把x,y对调。上面Matrix类中的transpose方法中就是把传入的specs数组按照二维数组下标,x轴,y轴转换。这里需要注意的是,transpose方法中的for循环外层必须是y,内层是x。这是因为一列为一个规格。而不是一行为一个规格。
/**
* 矩阵转置-转置矩阵
*/
initFence() {
const matrix = this._createMatrix(this.skuList)
const fences = []
const AT = matrix.transpose()
AT.forEach(specs => {
const fence = new Fence(specs)
fence.init()
fences.push(fence)
})
}