背景
在电商网站中,产品有多个规格选择,比如颜色、尺寸等规格。颜色的色值和尺寸的大小等组合组成1个SKU(Stock Keeping Unit,库存量单位)
。
这在做电商相关的项目经常见。 工作中应该会遇到。
当然也可以用第三方例如 vant组件库提供的SKU选择器,不过用别人的组件就要按照别人的数据格式,就很难受。
。
下面是用vue3
的简单实现。
先上效果
实现思路
例如一个商品有多个规格(属性),内含多个规格值(属性值):
颜色:黑色、白色
尺寸:大、中、小
计算sku最终生成的数量:
假设颜色的规格值数量为m1,尺寸的规格值数量为m2
sku数量= m1*m2
要实现的几个操作:
- 根据 sku 列表生成映射,包含的属性作为唯一key(
不用担心会重复,因为录入产品时候会限制不能录入相同名称的规格名称
)。后续根据key取数据。 - 处理选择后的操作。
- 处理库存不足不可选择的情况。
代码实现
<template>
<div class="sku-group" v-for="(values, name) in attrs" :key="name">
<div class="sku-title">{{ name }}</div>
<div class="sku-option" v-for="value in values" :key="value"
:class="{ 'sku-option-selected': selectedAttrs[name] === value, 'sku-option-disabled': !isOptionAvailable(name, value) }"
@click="selectOption(name, value)">
{{ value }}
</div>
</div>
<div class="sku-selected">当前选中的 SKU: {{ selectedSku }}</div>
<div class="sku-message">{{ message }}</div>
</template>
<script setup>
import { reactive, computed , toRefs } from 'vue';
// 创建一个 SKU 映射,键是 SKU 的属性,值是 SKU 对象
function generateSkuMap(skus) {
const map = {};
for (let sku of skus) {
let key = JSON.stringify(sku.attrs);
map[key] = sku;
}
return map;
}
// 找到第一个有库存的 SKU
function findFirstAvailableSku(skus) {
return skus.find(sku => sku.stock > 0);
}
// 初始化状态
const state = reactive({
skus: [
{id:1, attrs:{'颜色':'黑色', '尺寸':'大'}, stock:100},
{id:2, attrs:{'颜色':'黑色', '尺寸':'中'}, stock:100},
{id:3, attrs:{'颜色':'黑色', '尺寸':'小'}, stock:0},
{id:4, attrs:{'颜色':'白色', '尺寸':'大'}, stock:100},
{id:5, attrs:{'颜色':'白色', '尺寸':'中'}, stock:0},
{id:6, attrs:{'颜色':'白色', '尺寸':'小'}, stock:100},
],
attrs: {
'颜色': ['黑色', '白色'],
'尺寸': ['大', '中', '小'],
},
selectedAttrs: {},
message: '',
});
// 创建 SKU 映射
const skuMap = computed(() => generateSkuMap(state.skus));
// 获取当前选中的 SKU
const selectedSku = computed(() => {
let key = JSON.stringify(state.selectedAttrs);
return skuMap.value[key];
});
// 检查一个选项是否可用
const isOptionAvailable = (name, value) => {
let testAttrs = {...state.selectedAttrs, [name]: value};
let key = JSON.stringify(testAttrs);
return skuMap.value[key] && skuMap.value[key].stock > 0;
};
// 选择一个选项
const selectOption = (name, value) => {
if (isOptionAvailable(name, value)) {
state.selectedAttrs[name] = value;
state.message = '';
} else {
state.message = `${JSON.stringify({...state.selectedAttrs, [name]: value})} 暂无库存`;
}
};
// 自动选择第一个有库存的 SKU
const firstSku = findFirstAvailableSku(state.skus);
if (firstSku) {
state.selectedAttrs = {...firstSku.attrs};
}
// 导出响应式引用
const { skus, attrs, selectedAttrs, message } = toRefs(state);
</script>
<style>
.sku-group {
margin-bottom: 1em;
}
.sku-option {
display: inline-block;
margin: 0.5em;
padding: 0.5em;
border: 1px solid #ccc;
cursor: pointer;
}
.sku-option-disabled {
color: #ccc;
cursor: not-allowed;
}
.sku-option-selected {
background-color: #b8d3f1;
}
</style>
总结
sku数据处理:这块主要是对sku列表进行存到map里面,方便后续存取。
展示页面:然后遍历
颜色
、尺寸
这些规格以及它们的规格值
数据到页面。选择处理:获取到当前选择的
规格和规格值
进行替换已选中的规格值(这是一个对象,只需要把规格作为属性值替换即可)。然后JSON序列化字符串作为key
去查找sku列表。找到所属sku数据。判断库存,是否可以选择。
主要是对sku数据的处理。明白实现原理之后,可以方便的移植到小程序、uniapp之类的环境中。