商品SKU选择器实现思路,包简单

背景

又又又做了个商城,这回的商城规格发现不是个全相乘的关系。例如,假设有3种规格,每种类型的商品(sku)只需要将这三种规格都填上,且组合起来不重复就行,爱是啥值是啥值,爱有有几个sku有几个,主打一个随性。

所以我们接收到的数据可能是这样的,本文接下来的故事将围绕这个具体的示例展开:

js 复制代码
const goodsSkus = [
  { 颜色: 黄, 尺码: 1, 形状: 圆 },
  { 颜色: 黄, 尺码: 2, 形状: 圆 },
  { 颜色: 黄, 尺码: 4, 形状: 圆 },
  { 颜色: 黄, 尺码: 1, 形状: 方 },
  { 颜色: 黄, 尺码: 3, 形状: 三角 },
  { 颜色: 蓝, 尺码: 1, 形状: 方 },
  { 颜色: 蓝, 尺码: 3, 形状: 圆 },
  { 颜色: 红, 尺码: 1, 形状: 三角 },
  { 颜色: 红, 尺码: 2, 形状: 方 },
]

所以当规格选中之后,希望能将可选的规格区分出来,用以保证在最后选中的结果是有效可靠的。这是这篇文章主要讨论的内容。

接下来直接是正题

目标是构建一个可选规格的池子,随着选中的规格的变化而变化。

一个简单的选中

现在我们选中了 颜色=黄 的规格,那么我们就可以得出有效的sku为:

js 复制代码
const validGoodsSkus = [
  { 颜色: 黄, 尺码: 1, 形状: 圆 },
  { 颜色: 黄, 尺码: 2, 形状: 圆 },
  { 颜色: 黄, 尺码: 4, 形状: 圆 },
  { 颜色: 黄, 尺码: 1, 形状: 方 },
  { 颜色: 黄, 尺码: 3, 形状: 三角 },
]

从这里面我们可以得出有效的规格池子长这样:

ini 复制代码
{
  颜色: [黄],
  尺码: [1, 2, 3, 4],
  形状: [圆, 方, 三角],
}

这样,就可以得到个简单的有效范围了。但这样有一个缺点,我们虽然选择了 ,但颜色规格的其他值此时并没有被约束掉,应该也是可选的才对。这就来到了第二个问题,怎么知道某个选中规格的其他有效值是什么?

全有效值的判断

我们来思考一个问题,一个规格哪些值可选,是由什么决定的呢? 没错,是由除了该规格以外的其他规格的已选项来决定的,我们只需要把所有规格都算一遍就好了。

为了方便,现在我们弄了个规格数组:

js 复制代码
const specs = [颜色, 尺码, 形状]

现在来举个例子,假如我选中了【颜色:黄 + 尺码:1】,这时候应该有哪些可选值呢?

  • 颜色 的可选值由除了它以外的尺码、形状 的选中值来确定,这里只有尺码是有效值,我们来寻找尺码=1的数据:
js 复制代码
const validGoodsSkus = [
  { 颜色: 黄, 尺码: 1, 形状: 圆 },
  { 颜色: 黄, 尺码: 1, 形状: 方 },
  { 颜色: 蓝, 尺码: 1, 形状: 方 },
  { 颜色: 红, 尺码: 1, 形状: 三角 },
]

将这几个sku中的颜色属性值取出来去下重,就可以得到颜色的有效值了:

js 复制代码
const validSpecs = {
    颜色: [黄, 蓝, 红],
}
  • 尺码 的可选值由除了它以外的颜色、形状 的选中值来确定,这里只有颜色是有效值,我们来寻找颜色=黄的数据:
js 复制代码
const validGoodsSkus = [
  { 颜色: 黄, 尺码: 1, 形状: 圆 },
  { 颜色: 黄, 尺码: 2, 形状: 圆 },
  { 颜色: 黄, 尺码: 4, 形状: 圆 },
  { 颜色: 黄, 尺码: 1, 形状: 方 },
  { 颜色: 黄, 尺码: 3, 形状: 三角 },
]

将这几个sku中的尺码属性值取出来去下重,就可以得到尺码的有效值了:

js 复制代码
const validSpecs = {
    颜色: [黄, 蓝, 红],
    尺码: [1, 2, 3, 4]
}
  • 形状 的可选值由除了它以外的颜色、尺码 的选中值来确定,这里只有颜色是有效值,我们来寻找颜色=黄&&尺码=1的数据:
js 复制代码
const validGoodsSkus = [
  { 颜色: 黄, 尺码: 1, 形状: 圆 },
  { 颜色: 黄, 尺码: 1, 形状: 方 },
]

将这几个sku中的形状属性值取出来去下重,就可以得到尺码的有效值了:

js 复制代码
const validSpecs = {
    颜色: [黄, 蓝, 红],
    尺码: [1, 2, 3, 4],
    形状: [圆, 方]
}

由此我们得出了选中【颜色:黄 + 尺码:1】时的完整的规格有效可选值的池子。

其他实现细节(选阅)

其实基本上都说完了,但我比较臭屁,觉得实现也写得不错,所以决定写下来。

记录已选中对象

想了想选中值和规格是一对一的形式,key:value形式的结构正好了~用来储存多个键值对用Object和Map都很合适,他们用key查找插入删除在操作上都很方便(小数据量不考虑效率问题)。但Map还有一个特殊功能,记录存入的顺序,可能会在一些展示位用到它(咱用到了)。

js 复制代码
const select = ref(new Map())

可选规格合集构造

除了上述的已选项,我们还需要一个规格类型数组和sku列表,就用上面提到过的specs和goodsSkus就好。

js 复制代码
const specs = [颜色, 尺码, 形状]
const goodsSkus = [...]
ts 复制代码
// 可选的规格值合集
const validSpecs = computed(() => {
  // 所有规格种类循环
  return specs.reduce((it, item) => {
    // 筛选除了目前循环对应规格种类外的其他规格选中值组成新的Map类型
    const currentLastSelect = new Map();
    Array.from(select.value.entries())
      .filter(([key, value]) => key !== item)
      .forEach(([key, value]) => currentLastSelect.set(key, value));

    // 筛序除当前循环规格外,其他规格满足条件的商品
    const vailSkus = filterSkuBySelectMap(currentLastSelect, goodsSkus);

    // 当前循环规格的可选值
    it[item] = [...new Set(vailSkus.map((sku) => sku[item]) || [])];
    return it;
  }, {});
});

// good商品sku过滤器,筛选满足条件的商品
const filterSkuBySelectMap = (targetSelect: Map, skuList: { [key: string]: string }[]) => {
  return skuList.filter((item) =>
    Array.from(targetSelect.entries()).every(
      ([key, value]) => value === item[key],
    ),
  );
};

其他方法(不重要,选阅的选阅)

例如选中啊、是否可选、变换成展示用列表一类的,随便写一下就行

js 复制代码
// 选中or取消选中
const onSelect = (skuValue, specsName) => {
  const _select = select.value;
  const lastValue = _select.get(specsName);

  _select.delete(specsName);
  if (lastValue !== skuValue) _select.set(specsName, skuValue);
};

// 规格值是否选中
const isSelect = (specsName: string, skuValue: string, _select: Map) => {
  return _select.get(specsName) === skuValue;
};

// 是否无效的规格值
const isInvalid = (specsName: string, skuValue: string, _ableSelectList) => {
  return !_ableSelectList[specsName].includes(skuValue);
};

// 展示用的规格选择列表[{ [规格名]: 规格值 }] => [{key: 规格名, value: 规格值[]}]
const showSpecsList = computed(() => {
  return specs.reduce((it, item) => ({
        ...it,
        { key: item, value: [...new Set(goodsSkus.map((skuItem) => skuItem[item]))] }
    }), []);
});

// 所有规格都选择完成,筛选出的唯一的sku
const currentSelectSku = computed(() => {
  const _select = select.value;
  if (_select.size !== specs.length) return false;
  
  return goodsSkus.find((sku) =>
    Array.from(_select.entries()).every(
      ([selectKey, selectValue]) =>
        sku[selectKey] === selectValue,
    ),
  );
});

这样,就包含完一个简单的sku选择的基本方法了~

相关推荐
daols8835 分钟前
vue vxe-table 自适应列宽,根据内容自适应宽度的2种使用方式
vue.js·vxe-table
小小小小宇1 小时前
虚拟列表兼容老DOM操作
前端
悦悦子a啊1 小时前
Python之--基本知识
开发语言·前端·python
安全系统学习2 小时前
系统安全之大模型案例分析
前端·安全·web安全·网络安全·xss
涛哥码咖2 小时前
chrome安装AXURE插件后无效
前端·chrome·axure
OEC小胖胖3 小时前
告别 undefined is not a function:TypeScript 前端开发优势与实践指南
前端·javascript·typescript·web
行云&流水3 小时前
Vue3 Lifecycle Hooks
前端·javascript·vue.js
Sally璐璐3 小时前
零基础学HTML和CSS:网页设计入门
前端·css
老虎06273 小时前
JavaWeb(苍穹外卖)--学习笔记04(前端:HTML,CSS,JavaScript)
前端·javascript·css·笔记·学习·html
三水气象台3 小时前
用户中心Vue3网页开发(1.0版)
javascript·css·vue.js·typescript·前端框架·html·anti-design-vue