前言
今天在写项目中的一个小组件,感觉挺不错的,然后分享给大家,如果以后需要使用这个就,看一下这个博客就行
先说一下SKU和SPU的概念:
- SPU(Standard Product Unit)标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
- SKU(Stock Keeping Unit)库存量单位,即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
SKU 是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。
SPU:代表一种商品,拥有很多相同的属性。 SKU:代表的是改商品可选择的规格的任意的组合,他是库存单位的唯一标识
比如在这个下图中出现的使用场景
思路
- 从后端获取到所有带有SKU的组合的列表数据,找到有库存的SKU的列表,下面是有库存的SKU
[蓝色,中国,10cm]
[绿色,中国,20cm]
[蓝色,日本,30cm]
[黑色,日本,30cm]
然后计算SKU集合的笛卡尔集,得到后面的这个值,如下面的代码:
[蓝色,中国,10cm]--->[蓝色,中国,10cm,蓝色#中国,蓝色#10cm,中国#10cm,蓝色#中国#10cm]
[绿色,中国,20cm]--->[绿色,中国,20cm,绿色#中国,绿色#20cm,中国#20cm,绿色#中国#20cm]
[蓝色,日本,30cm]--->[蓝色,日本,30cm,蓝色#日本,蓝色#30cm,日本#30cm,蓝色#日本#30cm]
[黑色,日本,30cm]--->[黑色,日本,30cm,黑色#日本,黑色#30cm,日本#30cm,黑色#日本#30cm]
然后生成一个专用的路径的字典,如下代码
const dic = {
'蓝色':[skuId,skuId],
'中国':[skuId],
蓝色#中国#10cm:[skuId]
蓝色#中国:[skuId]
}
开始啦
在goods/components 文件夹中新建一个goods-sku.vue 的文件
基础布局代码,如下
<template>
<div class="goods-sku">
<dl>
<dt>颜色</dt>
<dd>
<img class="selected" src="https://yanxuan-item.nosdn.127.net/d77c1f9347d06565a05e606bd4f949e0.png" alt="">
<img class="disabled" src="https://yanxuan-item.nosdn.127.net/d77c1f9347d06565a05e606bd4f949e0.png" alt="">
</dd>
</dl>
<dl>
<dt>尺寸</dt>
<dd>
<span class="disabled">10英寸</span>
<span class="selected">20英寸</span>
<span>30英寸</span>
</dd>
</dl>
<dl>
<dt>版本</dt>
<dd>
<span>美版</span>
<span>港版</span>
</dd>
</dl>
</div>
</template>
<script>
export default {
name: 'GoodsSku'
}
</script>
<style scoped lang="less">
.sku-state-mixin () {
border: 1px solid #e4e4e4;
margin-right: 10px;
cursor: pointer;
&.selected {
border-color: @xtxColor;
}
&.disabled {
opacity: 0.6;
border-style: dashed;
cursor: not-allowed;
}
}
.goods-sku {
padding-left: 10px;
padding-top: 20px;
dl {
display: flex;
padding-bottom: 20px;
align-items: center;
dt {
width: 50px;
color: #999;
}
dd {
flex: 1;
color: #666;
> img {
width: 50px;
height: 50px;
.sku-state-mixin ();
}
> span {
display: inline-block;
height: 30px;
line-height: 28px;
padding: 0 20px;
.sku-state-mixin ();
}
}
}
}
</style>
然后再组件中使用
<div class="spec">
<GoodsName :goods="detail" />
<GoodsSku />
</div>
<script>
import GoodsSku from './components/goods-sku'
components:{GoodsSku}
</script>
开始渲染组件
在商品规格组件把:specs="detail.specs" 注入进去 代码如下
<div class="spec">
<GoodsName :goods="detail" />
<GoodsSku :specs="detail.specs" />
</div>
在子组件中接一下数据
props:{
specs:{
type:Array,
default:()=>[]
}
}
然后渲染页面,代码如下
<template>
<div class="goods-sku">
<dl v-for='(item, index) in specs' :key='index'>
<dt>{{item.name}}</dt>
<dd>
<template v-for='(tag, n) in item.values' :key='n'>
<img :class='{selected: tag.selected}' v-if='tag.picture' :src="tag.picture" alt="" >
<span :class='{selected: tag.selected}' v-else >{{tag.name}}</span>
</template>
</dd>
</dl>
</div>
</template>
绑定按钮点击事件,完成选中和取消选中
- 当前点击的是选中,取消即可
- 当前点击的是未选中,先当前规格按钮全部取消,当前按钮选中
代码如下
<template>
<div class="goods-sku">
<dl v-for='(item, i) in specs' :key='i'>
<dt>{{item.name}}</dt>
<dd>
<template v-for='(tag, n) in item.values' :key='n'>
<img :class='{selected: tag.selected}' v-if='tag.picture' :src="tag.picture" alt="" @click='toggle(tag, item.values)'>
<span :class='{selected: tag.selected}' v-else @click='toggle(tag, item.values)'>{{tag.name}}</span>
</template>
</dd>
</dl>
</div>
</template>
<script>
export default {
name: 'GoodsSku',
props: {
specs: {
type: Array,
default: () => []
}
},
setup () {
const toggle = (tag, list) => {
tag.selected = !tag.selected
list.forEach(item => {
if (item.name !== tag.name) {
item.selected = false
}
})
}
return { toggle }
}
}
</script>
禁用效果的思路分析 大致步骤:
- 根据后台返回的skus数据得到有效(有库存)sku组合在字典中新建一个js文件
export default function bwPowerSet(originalSet) {
const subSets = [];
const numberOfCombinations = 2 ** originalSet.length;
for (let combinationIndex = 0; combinationIndex < numberOfCombinations; combinationIndex += 1) {
const subSet = [];
for (let setElementIndex = 0; setElementIndex < originalSet.length; setElementIndex += 1) {
if (combinationIndex & (1 << setElementIndex)) {
subSet.push(originalSet[setElementIndex]);
}
}
subSets.push(subSet);
}
return subSets;
}
- 根据有效的sku组合得到所有的子集集合(笛卡尔集)
- 根据子集集合组合成一个路径字典,也就是对象。
import powerSet from '@/vendor/power-set.js'
const usePathMap = (skus) => {
const result = {}
const spliter = '※'
skus.forEach(sku => {
console.log(sku.inventory)
if (sku.inventory === 0) return
const spec = sku.specs.map(item => item.valueName)
const specSet = powerSet(spec)
specSet.forEach(item => {
if (item.length === 0) return
const key = item.join(spliter)
if (result[key]) {
result[key].push(sku.id)
} else {
result[key] = [sku.id]
}
})
})
return result
}
参照示例
禁用效果-设置状态
目的:在组件初始化的时候,点击规格的时候,去更新其他按钮的禁用状态。 代码如下
const getSelectedValues = (specs) => {
const result = []
specs.forEach((item, index) => {
const spec = item.values.find(tag => tag.selected)
if (spec) {
result[index] = spec.name
} else {
result[index] = undefined
}
})
return result
}
const updateDisabledStatus = (specs, pathMap) => {
specs.forEach((spec, i) => {
const seletedValues = getSelectedValue(specs)
spec.values.forEach(tag => {
if (tag.selected) {
return
} else {
seletedValues[i] = tag.name
}
let currentPath = seletedValues.filter(item => item)
if (currentPath.length > 0) {
currentPath = currentPath.join(spliter)
tag.disabled = !pathMap[currentPath]
}
})
})
}
setup (props) {
const pathMap = getPathMap(props.goods.skus)
+ updateDisabledStatus(props.specs, pathMap)
const clickSpecs = (item, val) => {
+ if (val.disabled) return
if (val.selected) {
val.selected = false
} else {
item.values.find(bv => { bv.selected = false })
val.selected = true
}
+ updateDisabledStatus(props.specs, pathMap)
}
return { clickSpecs }
}
总结
这个知识也是前端工程师必会的,加油
- spu代表一种商品,拥有很多相同的属性。
- sku代表该商品可选规格的任意组合,他是库存单位的唯一标识。
下面是我的个人理解,好了,今天就到这里了,明天见
|