当前位置:   article > 正文

加购商品sku选择算法方案 (vue)(淘宝)_vue sku算法

vue sku算法

目录

1.什么是SKU?

2.问题描述

3.解决办法

3.1 场景还原

演示地址:https://codepen.io/louyanping723/pen/gOLeqWb

3.2 数据加工

演示地址:https://codepen.io/louyanping723/pen/wvojPKd

3.3 关联 SKU 验证

演示地址:https://codepen.io/louyanping723/pen/gOLzXoR

附录:参考思路文档


1.什么是SKU?

在连锁零售门店中有时称单品为一个SKU(中文译为最小存货单位,英文全称为Stock Keeping Unit,简称SKU,定义为保存库存控制的最小可用单位,例如纺织品中一个SKU通常表示规格、颜色、款式)。

通俗一点就是如下图所示:男款+41+黑灰,就构成了一个SKU

 

2.问题描述

拿手机举例,假设一台手机有颜色和内存和运营商 3 种可选属性,颜色有黑色和白色,内存只有 16G 和 32G 。我们把属性组合一下,列举出所有的 SKU ,同时也显示出库存数量和价格:

颜色

内存

库存

价格

黑色

16G

0

5999

黑色

32G

11

5999

白色

16G

12

6299

白色

32G

13

6299

 

3.解决办法

3.1 场景还原

要解决这个问题,我们先模拟一个商品购买选择 SKU 的场景。一般情况下,后台会通过接口提供给我们两组数据,分别是 属性集 和 数据集 ,这里用两组固定数据模拟一下:

  1. // 属性集
  2. var key = [
  3. {name: '颜色', item: ['黑', '金', '白']},
  4. {name: '内存', item: ['16G', '32G']},
  5. {name: '运营商', item: ['电信', '移动', '联通']}
  6. ];
  7. // 数据集
  8. var sku = {
  9. '黑;16G;电信': {price: 100, count: 10},
  10. '黑;16G;移动': {price: 101, count: 11},
  11. '黑;16G;联通': {price: 102, count: 0},
  12. '黑;32G;电信': {price: 103, count: 13},
  13. '黑;32G;移动': {price: 104, count: 14},
  14. '黑;32G;联通': {price: 105, count: 0},
  15. '金;16G;电信': {price: 106, count: 16},
  16. '金;16G;移动': {price: 107, count: 17},
  17. '金;16G;联通': {price: 108, count: 18},
  18. '金;32G;电信': {price: 109, count: 0},
  19. '金;32G;移动': {price: 110, count: 20},
  20. '金;32G;联通': {price: 111, count: 21},
  21. '白;16G;电信': {price: 112, count: 0},
  22. '白;16G;移动': {price: 113, count: 23},
  23. '白;16G;联通': {price: 114, count: 24},
  24. '白;32G;电信': {price: 115, count: 0},
  25. '白;32G;移动': {price: 116, count: 26},
  26. '白;32G;联通': {price: 117, count: 27}
  27. };

有了这两组数据,就可以实现最基本的 SKU 选择功能了。用 属性集 去渲染 DOM,当用户选择好 SKU 后,程序将用户选择的属性拼接成一个 sku 字符串,比如 金;16G;电信 ,再根据这个字符串去 数据集 里获取库存和价格,演示如下:

演示地址:https://codepen.io/louyanping723/pen/gOLeqWb

这里的属性集我加了一个itemIndex的属性,标识当前选中的属性索引值,用于选中高亮。

 

上面这个演示有个最大的问题就是,必须把每个属性都选择后,才能获取到对应的库存和价格,如果没有选择完整,就无法获取对应的数据。

原因也很简单,因为 数据集 里没有提供。比如只选择了  ,那么当前拼接出来的 sku 则是 白;; ,自然找不到这条 sku 的相关数据。那要怎么解决呢?那就把 数据集 加工一下。

3.2 数据加工

我拿 数据集 里某一条 sku 举例,比如 黑;16G;电信 ,将这个 sku 进行更小的拆分组合,希望得到以下的结果:

  • ;;
  • 黑;;
  • ;16G;
  • ;;电信
  • 黑;16G;
  • 黑;;电信
  • ;16G;电信
  • 黑;16G;电信

这里会涉及到本文中最核心的一个算法,让我们再仔细看下举例的这个 sku ,如果将它转为数组,就是:

['黑', '16G', '电信']

如果把最终希望得到的结果也转为数组,那就是:

  1. ['', '', '']
  2. ['黑', '', '']
  3. ['', '16G', '']
  4. ['', '', '电信']
  5. ['黑', '16G', '']
  6. ['黑', '', '电信']
  7. ['', '16G', '电信']
  8. ['黑', '16G', '电信']

然后仔细观察一下这组数据,看出些端倪了么?

没看出来的话,我们把这个 sku 再增加一个属性,如果数组是这样子的:

['黑', '16G', '电信', '2800mAh']

那最终希望得到的结果也会有变化:

  1. // 4选0
  2. ['', '', '', '']
  3. // 4选1
  4. ['黑', '', '', '']
  5. ['', '16G', '', '']
  6. ['', '', '电信', '']
  7. ['', '', '', '2800mAh']
  8. // 4选2
  9. ['黑', '16G', '', '']
  10. ['黑', '', '电信', '']
  11. ['黑', '', '', '2800mAh']
  12. ['', '16G', '电信', '']
  13. ['', '16G', '', '2800mAh']
  14. ['', '', '电信', '2800mAh']
  15. // 4选3
  16. ['黑', '16G', '电信', '']
  17. ['黑', '16G', '', '2800mAh']
  18. ['黑', '', '电信', '2800mAh']
  19. ['', '16G', '电信', '2800mAh']
  20. // 4选4
  21. ['黑', '16G', '电信', '2800mA']

相信有人已经看出来了,这里需要实现的一个算法就是:

从 m 个不同元素中取出 n 个元素的组合数,而且是有序的。

但是其实从上面这样的列举法列出来,算法不太好写,因为是有序的选择,可能需要位移的偏移量来处理,比较麻烦和复杂。

那么换一种列举的方法,大家就可以看得比较明显了,如下所示:

  1. ['', '', '']
  2. ['', '', '电信']
  3. ----
  4. ['', '16G', '']
  5. ['', '16G', '电信']
  6. -------------
  7. ['黑', '', '']
  8. ['黑', '', '电信']
  9. ----
  10. ['黑', '16G', '']
  11. ['黑', '16G', '电信']

观察以上,其实数组一个选项要不就是,要不就是'',第二个选项要不就是16G,要不就是'' ,第三个选项要不就是电信,要不就是''

这样就很好理解了,数组的每一项要不就是当前的标签,要不就是,那么我们设置一个横向遍历的数组来递归进行组合就可以了,如下所示:

  1. [
  2. ['黑', '']
  3. ['16G', ''],
  4. ['电信', '']
  5. ]

核心递归遍历方法:

  1. recombine(arr) {
  2. const labelArr = arr;
  3. // 获取重新组合的数组格式 例如[['黑', ''],['16G', ''],['电信', '']]
  4. // 进行横向遍历,填充组合数组
  5. const newLabelArr = labelArr.map((item) => [item, '']);
  6. // 进行横向遍历,填充组合数组,从0开始递归
  7. let resultArr = this.getCombineArr(newLabelArr,0);
  8. return resultArr;
  9. },
  10. getCombineArr(arr,index) {
  11. let resultArr = [];
  12. let newArr = [];
  13. const recursion = (arr,index) => {
  14. for(let i=0;i<arr[index].length;i++) {
  15. ​newArr[index] = arr[index][i]
  16. if(index===(arr.length-1)) {
  17. resultArr.push(JSON.parse(JSON.stringify(newArr)));
  18. } else {
  19. // 递归
  20. recursion(arr,index+1)
  21. recursion(arr,index)
  22. return resultArr;
  23. },

解决到这一步后,后面的工作就相对轻松了。

我们已经能根据 黑;16G;电信 得到左边这样一组数据,黑;16G;移动 得到右边这样的数据。以此类推。

观察上面的数据,我们发现有相同属性的集合,比如['', '', ''] ['', '16G', ''] ['黑', '16G', ''] 等等……

这时候只需把属性数据一样的库存进行累加,同时把价格存到一个数组里。这样把 数据集 里所有的 sku 都循环一遍后,对应的加工以后数据集的库存数就统计出来了。比如每个 sku 都会出现 ['', '', ''] ,那累计得出的自然也就是该商品的总库存数量;再比如 sku 里有出现过 ['黑', '', ''] ,最终累计得出的就是该商品颜色为黑色的库存数量。数据格式如下图所示:

 

展示的时候直接取数组里的最大和最小价格展示即可。

  1. const maxPrice = Math.max(...priceArr);
  2. const minPrice = Math.min(...priceArr);

至此,我们已经能实现用户选择一个或多个属性时,均能展示当前的库存和价格信息,演示如下:

 

演示地址:https://codepen.io/louyanping723/pen/wvojPKd

 

3.3 关联 SKU 验证

现在只差最后一步了,就是希望当用户点击属性选择的时候,程序能去验证一些可能点击的属性,提前把 0 库存的属性设为禁止选中状态

判断流程:

  1. 属性集遍历,按钮渲染绑定disabled状态::disabled="!checkSku(label,index)
  2. 传入当前渲染的属性标签 和 当前属性标签 所在属性集 的索引值。
  3. 根据属性集的itemIndex属性 获取当前已经 点击选中的属性标签。组成数组choosedLabelArr。(如['白', '', ''])
  4. 假设当前渲染的属性标签 选中,替换3中的数组choosedLabelArr索引为当前标签 :  choosedLabelArr[index] = str;
  5. 遍历加工完的数据集,是否有库存可选,有就可点击,没有就禁用。

 

核心代码如下图:

  1. checkSku(str,index) {
  2. // 先获取已经选中的标签
  3. let choosedLabelArr = [];
  4. const key = this.key;
  5. key.forEach((item) => {
  6. choosedLabelArr.push(
  7. item.itemIndex>-1 ? item.item[item.itemIndex] : ''
  8. )
  9. });
  10. // 假设当前标签已选中
  11. choosedLabelArr[index] = str;
  12. let nowChooseStr = choosedLabelArr.join(';');
  13. // 遍历加工完的数据集,是否有库存可选,有就可点击,没有就禁用
  14. let canChoose = false;
  15. const resultSKU = this.resultSKU;
  16. for(let skuLey in resultSKU) {
  17. if(skuLey.indexOf(nowChooseStr)>-1) {
  18. if(resultSKU[skuLey].count>0) {
  19. canChoose = true;
  20. break;
  21. }
  22. }
  23. }
  24. return canChoose;
  25. }

 

演示地址:https://codepen.io/louyanping723/pen/gOLzXoR

附录:

参考思路文档:https://hooray.github.io/posts/8b2bd6f8/(核心算法已自己全部重新编写)

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/871930
推荐阅读
相关标签
  

闽ICP备14008679号