1、什么是SKU?
2、什么是笛卡尔积?
3、SKU组合代码实现思路
4、SKU选择代码实现思路
什么是SKU?
SKU(Stock Keeping Unit)最小存货单位。
例如我们在某购物APP上选购一款Mac电脑,可以选择颜色、内存、以及其他的一些销售属性。
这些销售属性的组合,比如深空灰16G+512G官方标配版,就可以作为这个商品库存及发货的标准,也就是我们说的一个SKU 如果你明白了SKU的概念,那接下来看看这个例子,这个商品一共有几个SKU?
我是一条分割线
一共是10个SKU,你答对了么?
根据这个商品的尺码和颜色,我们可以得到10种[尺码X颜色]的组合,分别是
[XS黑色,XS军绿色,S黑色,S军绿色,M黑色,M军绿色,L黑色,L军绿色,XL黑色,XL军绿色]
这些SKU组合,我们可以通过笛卡尔积来得到。那么什么是笛卡尔积?
笛卡尔积
在数学中,笛卡尔积是一种对集合的运算。
假设有集合A, 集合B,用A中的元素作为第一个元素,用B中的元素作为第二个元素构成的有序对,所有这样的有序对的集合叫做A和B的笛卡尔积,记作AxB
用符号来表示记为:A×B={(x,y)|x∈A∧y∈B}
例如:A={a, b}, B={1,2,3}
用集合A中的a作为第一个元素,集合B中的1作为第二个元素,得到(a,1)
用集合A中的a作为第一个元素,集合B中的2作为第二个元素,得到(a,2)
用集合A中的a作为第一个元素,集合B中的3作为第二个元素,得到(a,3)
用集合A中的b作为第一个元素,集合B中的1作为第二个元素,得到(b,1)
用集合A中的b作为第一个元素,集合B中的2作为第二个元素,得到(b,2)
用集合A中的b作为第一个元素,集合B中的3作为第二个元素,得到(b,3)
最后我们得到AxB={(a,1),(a,2),(a,3),(b,1),(b,2),(b,3)}
是不是和我们上面计算SKU组合是一个意思呢?
SKU组合代码实现思路
现在基本的概念和组合方法我们都已经Get了,看下如何转变成代码,让计算机帮我们实现笛卡尔积
就以一个实际的例子来引入吧
现在 你是一个服装电商卖家,要上架一款大衣,这款大衣颜色有黑色,白色,灰色;长度有长款,短款;尺码有S,M,L,那么可能的SKU组合我们通过树状图画出来
我们先来用最简单粗暴的循环实现看看
const colors = ["黑色", "白色", "灰色"];
const length = ["长款", "短款"];
const size = ["S", "M", "L"];
// 获取SKU组合的方法
const getSkuList = () => {
const result = [];
colors.forEach((c) => {
length.forEach((l) => {
size.forEach((s) => {
result.push([c, l, s]);
});
});
});
return result; // [["黑色","长款","S"],["黑色","长款","M"],["黑色","长款","L"],["黑色","短款","S"],["黑色","短款","M"],["黑色","短款","L"],["白色","长款","S"],["白色","长款","M"],["白色","长款","L"],["白色","短款","S"],["白色","短款","M"],["白色","短款","L"],["灰色","长款","S"],["灰色","长款","M"],["灰色","长款","L"],["灰色","短款","S"],["灰色","短款","M"],["灰色","短款","L"]]
};
确实结果是没问题的,但这种暴力循环没法应用到实际业务当中去。如果一个商品有10个销售属性,总不能写10层循环吧。况且我们会有各种不同的商品,不同的销售属性,不能像这样在for循环里把循环的数组定义死。
我们可以考虑借助reduce函数,将数组的每一项进行拼接操作,最终整合成一个结果
const colors = ["黑色", "白色", "灰色"];
const length = ["长款", "短款"];
const size = ["S", "M", "L"];
// 方法一
const getSkuList = (attrList) => {
if (attrList.length < 2) return attrList[0] || [];
return attrList.reduce((total, current) => {
const res = [];
total.forEach((t) => {
current.forEach((c) => {
const temp = Array.isArray(t) ? [...t] : [t];
temp.push(c);
res.push(temp);
});
});
return res;
});
}
// 方法二
const getSkuList2 = (attrList) => {
return attrList.reduce(
(total, current) => total.flatMap((t) => current.map((c) => [...t, c])),
[[]]
);
};
getSkuList([colors,length,size]); // [["黑色","长款","S"],["黑色","长款","M"],["黑色","长款","L"],["黑色","短款","S"],["黑色","短款","M"],["黑色","短款","L"],["白色","长款","S"],["白色","长款","M"],["白色","长款","L"],["白色","短款","S"],["白色","短款","M"],["白色","短款","L"],["灰色","长款","S"],["灰色","长款","M"],["灰色","长款","L"],["灰色","短款","S"],["灰色","短款","M"],["灰色","短款","L"]]
从树状图我们能看出来,从上往下到叶子结点的每一条路径,代表着一个SKU组合。其实还有点类似经典的回溯算法组合问题。
那我们再尝试使用回溯算法来实现一下。回溯算法的基本思想我在这里就不多做解释了,感兴趣的同学可以自行了解。
对于我们这个场景来说,回溯的“终止条件”就是当路径长度等于销售属性个数的时候,说明已经访问到叶子结点了,一个SKU组合成功,可以存放到结果中去。每一层的结点就是每一个销售属性的选项,例如第一层就是颜色,第二层是长度,第三层是尺码。我们看下代码如何实现的:
const colors = ["黑色", "白色", "灰色"];
const length = ["长款", "短款"];
const size = ["S", "M", "L"];
// 方法三
const getSkuList = (attrList) => {
const result = [];
const backTracking = (path, level) => {
if (path.length === attrList.length) {
result.push([...path]);
return;
}
attrList[level].forEach((item) => {
path.push(item);
backTracking(path, level + 1);
path.pop();
});
};
backTracking([], 0);
return result;
};
getSkuList([colors,length,size]); // [["黑色","长款","S"],["黑色","长款","M"],["黑色","长款","L"],["黑色","短款","S"],["黑色","短款","M"],["黑色","短款","L"],["白色","长款","S"],["白色","长款","M"],["白色","长款","L"],["白色","短款","S"],["白色","短款","M"],["白色","短款","L"],["灰色","长款","S"],["灰色","长款","M"],["灰色","长款","L"],["灰色","短款","S"],["灰色","短款","M"],["灰色","短款","L"]]
SKU选择代码实现思路
前面我们实现了SKU组合的方法,主要适用于卖家后台系统,在创建商品时,编辑各SKU的信息。
在买家端,买家会选择自己需要的SKU,系统需要根据买家的选择,实时反馈对应SKU的价格、库存等信息。
服务端给到前端的信息是一个完整的SKU列表,数据结构类似这样, 数组里的每一条数据即为一个SKU,包含了SKU的唯一标识skuId, SKU的属性组成,SKU的库存和价格
[
{
skuId: "111",
skuInfo: {
color: "黑色",
length: "长款",
size: "S",
},
stock: 10,
price: 10,
},
{
skuId: "112",
skuInfo: {
color: "黑色",
length: "长款",
size: "M",
},
stock: 9,
price: 9,
},
{
skuId: "113",
skuInfo: {
color: "黑色",
length: "长款",
size: "L",
},
stock: 8,
price: 8,
},
...
]
以及一个完整的销售属性列表,数据结构类似这样:
[
{
label: "颜色",
name: "color",
options: [
{
value: "黑色",
},
{
value: "白色",
},
{
value: "灰色",
},
],
},
{
label: "长度",
name: "length",
options: [
{
value: "长款",
},
{
value: "短款",
},
],
},
{
label: "尺码",
name: "size",
options: [
{
value: "S",
},
{
value: "M",
},
{
value: "L",
},
],
},
]
根据这个销售属性列表,我们很容易可以渲染出SKU选择的组件(Demo为React+antd实现)
<div className="main-content">
<Form name="basic"> {attrs.map((attr) => { return ( <Form.Item key={attr.name} label={attr.label} name={attr.name}> <div className="form-content"> {attr.options.map((opt, index) => ( <Button key={index} type={ opt.value === selectedSku[attr.name] ? "primary" : "default" } className="opt" disabled={!isSkuValid(attr.name, opt.value)} onClick={() => onClick(attr.name, opt.value)} > {opt.value} </Button> ))} </div> </Form.Item> ); })} </Form>
</div>
我们用一个对象来记录已选择的选项, 当点击选项时,更新selectedSku对象的值
const [selectedSku, setSelectedSku] = useState({});
const onClick = (attrKey, optValue) => {
setSelectedSku({
...selectedSku,
[attrKey]: selectedSku[attrKey] === optValue ? "" : optValue,
});
};
实际情况下,某些SKU存在无货的场景,那对应的按钮是置灰无法选择的。但是目前SKU组件是依靠销售属性列表渲染的,没有库存信息,也就是说,我们需要结合销售属性数组和SKU列表,去判断每一个选项是否可选。
const isSkuValid = (attrKey, optValue) => {
// 先假设当前属性值已选中,拼入已选对象里
const tempSelectedSku = {
...selectedSku,
[attrKey]: optValue,
};
// 过滤出已选对象中属性值不为空的
const skuToBeChecked = Object.keys(tempSelectedSku).filter(
(key) => tempSelectedSku[key] !== ""
);
// 在skuList里找到所有包含已选择属性的sku且库存>0的sku
const filteredSkuList = skuList.filter((sku) =>
skuToBeChecked.every(
(skuKey) =>
tempSelectedSku[skuKey] === sku.skuInfo[skuKey] && sku.stock > 0
)
);
return filteredSkuList.length > 0;
};
这样我们就实现了买家端SKU选择的功能。
总结
至此,卖家后台创建商品SKU及买家界面选择SKU的功能基本都已实现,有问题欢迎一起探讨
今天的文章电商最小存货单位SKU——详细代码实现分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15754.html