# SPU、SKU
# 认识SPU、SKU
一个标准的电商都需要用到spu
,sku
来妥善管理好商品.
TIP
SPU(Standard Product Unit) 标准化产品 -商品
SKU(Stock Keeping Unit) 库存量单位 -商品的规格,单品
这里的SPU-这件商品的信息,SKU-下方可选择配置颜色等
每个
sku
由各种规格组成,而几个规格值属于一类规格名。spu
包含一组sku
,然后单个sku
包含一组规格数组,规格数组里面的对象由规格名和规格值组成。
规格:
颜色: 暗夜绿 黑色
运存: 64GB 256GB
版本 : 全网通 电信
规格名:颜色
规格值 : 暗夜绿 黑色
sku状态判断就像是字典里面查字典,如果查不到组合就是不可选状态,如果有就是可选状态.将skuList抽离成一个矩阵 然后进行旋转
金属灰 七龙珠 小号s
青芒色 灌篮高手 中号M
青芒色 圣斗士 大号L
橘黄色 七龙珠 大号L
# 基本逻辑
可以看到我们spu详情 (opens new window)返回的数据结构是
-- spu
-- detail
-- sku_list
-- sku
-- detail
-- specs
-- spec
-- spec
-- spec
-- code
将里面的拿到的规格数组组成二维数组是这样的
金属灰 七龙珠 小号s
青芒色 灌篮高手 中号M
青芒色 圣斗士 大号L
橘黄色 七龙珠 大号L
可以发现,将矩阵转置后就可以得到正常显示的规格数组,所以我们要做的事情基本可以确定
# 改变数据结构
我们拿到的SKU
数据是一个数组,里面包含了对应每项的规格数组,首先需要将它置为一个二维数组,然后将二维数组进行转置,生成想要的数据。
这里我们使用面向对象形式进行处理数据。
WARNING
- Fence-group sku面板对象
- Fence 栅栏对象
- Cell 规格元素
转置生成的二维数组之后,将每个规格名相同的规格数组保存到对应的Fence下,在这之前可以进行可视规格的相关处理之后,再初始化Fence对象
初始定义好的模型:
class FenceGroup {
// 抽离出一个矩阵
spu
skuList = []
fences = []
constructor(spu) {
this.spu = spu
this.skuList = spu.sku_list
}
}
...
class Fence {
Cells = []
specs
title
title_id
constructor(specs) {
this.specs = specs
this.title = specs[0].key
this.title_id = specs[0].key_id
}
}
...
class Cell{
title
id
status = CellStatus.WAITING
spec
constructor(spec) {
// 设置属性
this.title = spec.value
this.id = spec.value_id
this.spec = spec
}
}
首先获取初始状态的二维数组,这里需要定义一个矩阵 (opens new window)抽分业务,矩阵类需要提供转置遍历等接口。
_createMatrix(){
const m = []
this.sku_list.forEach((sku)=>{
m.push(sku.specs)
})
// matrix 矩阵类
return new Matrix(m)
}
然后需要将矩阵转置,转置之后将每个一维数组都抛给Fence (opens new window)
矩阵转置:
transpose(){
let desArray = []
for(let j = 0;j < this.col(); j++){
desArray[j] =[]
for(let i = 0;i<this.row();i++){
desArray[j][i] = this.m[i][j]
}
}
return desArray
}
初始化规格矩阵
首先需要定义Fence
处理规格矩阵方法
init(){
this.specs.forEach(spec=>{
// 去重
const existed = this.cells.some(cell=>cell.id === spec.value_id)
if(existed){
return
}
// 初始化元素
const cell = new Cell(spec)
this.cells.push(cell)
})
}
Fence-Goup
initFecnce(){
const matrix = this._createMatrix(this.skuList)
const fences = []
const AT = matrix.transpose(this.skuList)
// 这里每个二维数组的规格名已经统一
AT.forEach(specs=>{
const fence = new Fence(specs)
fence.init()
// 暂时判断可视规格
fences.push(fence)
this.fences = fences
})
}
# SKU状态处理
在拿到正确的规格矩阵后,并且可以通过面向对象的方式取到面板数据
接下来我们需要做的事情是:当用户点击一个cell
规格时需要引起其他元素的状态改变,并且高亮选中的路径。确定禁用状态就是sku
算法的核心,状态可分为选中,未选,禁用。
首先规格都是可选的,当用户点击一个规格值时,确认青芒色
和七龙珠
是否在一个sku
路径中存在,如果这条路径找不到,就显示为禁用状态,然后如果存在改变状态为可选后,再确认青芒色+七龙珠
和尺码选项是否存在,确定禁用状态。
WARNING
这样做思想通常是错误的,因为太线性思考了这个问题,可能存在反向选择,我们需要在点击青芒色之后算图案尺码六个路径存在性,不存在则禁用,然后点击灌篮高手时再重新计算青芒色+灌篮高手+尺码
三个路径是否存在,不存在禁用。
规律
总结出来的规律就是: 已选规格的改变,我们都需要计算所有的规格路径,确定好已存在的路径,通过将待确认的路径和其对比就可以确认SKU
的禁用状态。
所以接下来要做的事情有两件:
- 计算已存在的sku路径
- 计算待确定的sku路径
# 计算已存在的路径
在每个SKU
的返回结果中都返回了一个code,比如2$1-45#3-9#4-14
,他代表SpuId
和SPU
的这条路径规格字符串。所以这里需要将它进行一个排列组合,然后将所有的组合推到一个数组中才能拿到所有的sku
路径
这里我们定义一个judge类 (opens new window)处理业务,SkuCode (opens new window)处理code,这里需要借助到传入数组的辅助类排列组合 (opens new window)
处理Sku的code
split(){
const SpuIdAndSpec = this.code.split("$")
this.SpuId = SpuIdAndSpec[0]
// 规格数组 然后进行排列数组
const SpecArray = SPuIdAndSpec[1].split("#")
for(let i = 1;i<SpecArray.length;i++){
// 相当于一个球出现几次的情况
const result = combine(SpecArray,i)
// 转为一维数组
let jointResult = result.map(r=>r.join("#"))
}
this.sequment = this.sequment.concat(joinResult)
}
然后我们需要将每个sku
的排列组合写入一个数组,这样就计算完成了已存在的路径。
// 返回所有的字典的路径
_initpathDirt() {
this.fenceGroup.spu.sku_list.forEach((s) => {
const SkuCode = new Code(s.code)
this.pathDirt = this.pathDirt.concat(SkuCode.seqments)
})
}
最终结果: ["1-45", "3-9", "4-14", "1-45#3-9", "1-45#4-14", "3-9#4-14", "1-45#3-9#4-14", "1-42", "3-10", "4-15", "1-42#3-10", "1-42#4-15", "3-10#4-15", "1-42#3-10#4-15", "1-42", "3-11", "4-16", "1-42#3-11", "1-42#4-16", "3-11#4-16", "1-42#3-11#4-16", "1-44", "3-9", "4-14", "1-44#3-9", "1-44#4-14", "3-9#4-14", "1-44#3-9#4-14"]
# 计算待确认的路径
这里算的上是确定sku
禁用状态最难的地方了。需要改变元素状态,首先需要在cell
中定义状态属性。这里定义为: selected forbidden waiting
首先需要在组件上传递cell
的信息,这里需要进行跨组件传递事件,
这里的x
,y
也需要传递,方便对cell
遍历,这俩各参数分别是横列数,通过遍历所得。
this.triggerEvent('cellTap',{cell:this.properties.Cell,x:this.properties.x,y:this.properties.y},{bubbles:true,composed:true})