vue实现一个sku列表
最近接到一个需求类似于商城选购的一个sku列表,大致要实现的效果如下:sku的专业名词解释为:库存保有单位即库存进出计量的单位,可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理但我个人理解则为:当你选择到某一个属性,与这个相关的属性应该会发生相对应的变化,这里的变化指的是这个选项是否是可选。假设我现在的数据结构如下:data: [ //
最近接到一个需求类似于商城选购的一个sku列表,大致要实现的效果如下:
sku的专业名词解释为:
库存保有单位即库存进出计量的单位,
可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理
但我个人理解则为:当你选择到某一个属性,与这个相关的属性应该会发生相对应的变化,这里的变化指的是这个选项是否是可选。
假设我现在的数据结构如下:
data: [ // 库存
{ id: "1", specs: ["紫色", "套餐一", "64G"], total: 50 },
{ id: "2", specs: ["紫色", "套餐一", "128G"], total: 60 },
{ id: "3", specs: ["紫色", "套餐二", "128G"], total: 160 },
{ id: "4", specs: ["黑色", "套餐三", "256G"], total: 40 },
{ id: "5", specs: ["白色", "套餐三", "64G"], total: 480 },
{ id: "6", specs: ["红色", "套餐一", "64G"], total: 120 }
],
commoditySpecs: [ // 商品类型 ["红色", "紫色", "白色", "黑色"] ["套餐一", "套餐二", "套餐三", "套餐四"] ["64G", "128G", "256G"]
{ key: "color", title: "颜色", list: [{ id: "red", name: "红色", disable: false, active: false }, { id: "zise", name: "紫色" }, { id: "white", name: "白色" }, { id: "black", name: "黑色" }, { id: "blue", name: "蓝色" }] },
{ key: "status", title: "套餐", list: [{ id: "one", name: "套餐一", disable: false }, { id: "two", name: "套餐二" }, { id: "three", name: "套餐三" }, { id: "four", name: "套餐四" }, { id: "five", name: "套餐五" }] },
{ key: "size", title: "内存", list: [{ id: "small", name: "64G" }, { id: "mini", name: "128G" }, { id: "big", name: "256G" }] }
],
如果我们现在选择到的颜色类型为"红色",那么这个颜色可以选择的套餐和内存为,“套餐一"和"64G”,效果如下:
同理如果你继续往下面选择"套餐一",则只会有"64G"这个选项。
思路捋清楚之后,我就思考一下代码如何实现,其实sku的列表的难点在于它们之间状态的联动。那么我们可以把data中的specs数据看作为一个"顶点",那么这个"顶点"会与其它的元素进行一个相关联。
比如现在的库存data为:
data: [ // 库存
{ id: "1", specs: ["紫色", "套餐一", "64G"], total: 50 },
{ id: "2", specs: ["紫色", "套餐一", "128G"], total: 60 },
{ id: "3", specs: ["紫色", "套餐二", "128G"], total: 160 },
{ id: "4", specs: ["黑色", "套餐三", "256G"], total: 40 },
{ id: "5", specs: ["白色", "套餐三", "64G"], total: 480 },
{ id: "6", specs: ["红色", "套餐一", "64G"], total: 120 }
],
我们就可以找出与"紫色"相关联的相邻节点,那么在这里与“紫色”相关联的节点为,[“套餐一”,“64G”,“套餐一”,“128G”,“套餐二”,“128G”],以此类推"黑色"相关联的节点为:[“套餐三”,“256G”]等等,如果我们能够获取相邻的节点则可以判断出它是否可以进行点击。但是在这个过程中,我们需要主要数组去重这样的一个小细节即可。
initData () {
this.data.forEach(v => {
let specs = v.specs;
specs.forEach(s => {
if (!this.obj[s]) {
this.obj[s] = specs;
} else {
this.obj[s] = this.obj[s].concat(specs);
}
});
});
console.log(this.obj);
},
getVertex (name) {
if (!this.obj[name]) {
return [];
}
return Array.from(new Set(this.obj[name].filter(v => v !== name)));
},
console.log(this.getVertex("红色")); // ["套餐一","64G"]
那么还有一个技术难点就是再次点击某个列表取消选择的时候,我的思路如下:
(1)取消选择的时候应去判断当前是否还有被选择到元素,如果没有则让它回到初始的状态,则是为全部都没有被选择的情况
(2)如果取消选择存在有其它元素被选择的情况,则需要获取到其它元素关联的并集。从而决定属性是否可选。
完整代码如下:
<template>
<div class="sku">
<div class="list" v-for="(item,index) in commoditySpecs" :key="index">
<div class="title">{{item.title}}</div>
<div class="shopList">
<span
v-for="(shopItem,sIndex) in item.list"
:key="sIndex"
class="shopList-item"
:class="{disable:shopItem.disable,active:shopItem.active}"
@click="handClickFun(shopItem,sIndex,item.title,item.key)"
>{{shopItem.name}}</span>
</div>
</div>
<div class="tips">
<p>选择的颜色是:{{colorName}}</p>
<p>选择的套餐是:{{statusName}}</p>
<p>选择的内存是:{{sizeName}}</p>
<p>总数为:{{cTotal}}件</p>
</div>
</div>
</template>
<script>
export default {
name: "sku",
data () {
return {
data: [ // 库存
{ id: "1", specs: ["紫色", "套餐一", "64G"], total: 50 },
{ id: "2", specs: ["紫色", "套餐一", "128G"], total: 60 },
{ id: "3", specs: ["紫色", "套餐二", "128G"], total: 160 },
{ id: "4", specs: ["黑色", "套餐三", "256G"], total: 40 },
{ id: "5", specs: ["白色", "套餐三", "64G"], total: 480 },
{ id: "6", specs: ["红色", "套餐一", "64G"], total: 120 }
],
commoditySpecs: [ // 商品类型 ["红色", "紫色", "白色", "黑色"] ["套餐一", "套餐二", "套餐三", "套餐四"] ["64G", "128G", "256G"]
{ key: "color", title: "颜色", list: [{ id: "red", name: "红色", disable: false, active: false }, { id: "zise", name: "紫色" }, { id: "white", name: "白色" }, { id: "black", name: "黑色" }, { id: "blue", name: "蓝色" }] },
{ key: "status", title: "套餐", list: [{ id: "one", name: "套餐一", disable: false }, { id: "two", name: "套餐二" }, { id: "three", name: "套餐三" }, { id: "four", name: "套餐四" }, { id: "five", name: "套餐五" }] },
{ key: "size", title: "内存", list: [{ id: "small", name: "64G" }, { id: "mini", name: "128G" }, { id: "big", name: "256G" }] }
],
item: {},
obj: {}
// colorName: "", // 颜色名称
// statusName: "", // 套餐名称
// sizeName: "" // 内存名称
};
},
computed: {
cTotal () { // 计算总数
let color = this.colorName;
let status = this.statusName;
let sizeName = this.sizeName;
let str = color + "" + status + "" + sizeName;
let obj = this.data.find(v => v.specs.join("") === str);
if (obj) {
return obj.total;
}
return 0;
},
colorName () {
let data = this.commoditySpecs[0].list.find(v => v.active);
if (data) {
return data.name;
}
return "";
},
statusName () {
let data = this.commoditySpecs[1].list.find(v => v.active);
if (data) {
return data.name;
}
return "";
},
sizeName () {
let data = this.commoditySpecs[2].list.find(v => v.active);
if (data) {
return data.name;
}
return "";
}
},
methods: {
// 设置相关元素的状态
dfs (data, relation) {
if (!data || data.length === 0) {
return;
}
let stack = [];
data.forEach(v => {
stack.push(v);
});
while (stack.length) {
const result = stack.shift();
if (relation.includes(result.name)) {
this.$set(result, "disable", false);
} else {
this.$set(result, "disable", true);
if (result.disable && result.active) {
this.$set(result, "active", false);
}
}
if (result.list && result.list.length > 0) {
stack = [...stack, ...result.list];
}
}
return data;
},
// 初始化所有都是可以选择
init () {
this.commoditySpecs.forEach(v => {
let list = v.list;
list.forEach(s => {
this.$set(s, "disable", false);
this.$set(s, "active", false);
});
});
},
// 初始化数据
initData () {
this.data.forEach(v => {
let specs = v.specs;
specs.forEach(s => {
if (!this.obj[s]) {
this.obj[s] = specs;
} else {
this.obj[s] = this.obj[s].concat(specs);
}
});
});
console.log(this.obj);
},
getVertex (name) {
if (!this.obj[name]) {
return [];
}
return Array.from(new Set(this.obj[name].filter(v => v !== name)));
},
handClickFun (item, sIndex, title, key) {
this.item = item;
if (item.disable) {
return;
}
if (!item.active) { // 没有选择的情况
// 当前列取消选择
let currentData = this.commoditySpecs.filter(v => v.title === title);
currentData.forEach(v => {
let list = v.list;
list.forEach(s => {
this.$set(s, "active", false);
});
});
let relation = this.getVertex(item.name);
console.log(relation);
if (relation.length === 0) { // 没有库存了
let restData = this.commoditySpecs.filter(v => v.title !== title);
restData.forEach(v => {
let list = v.list;
list.forEach(s => {
this.$set(s, "disable", true);
});
});
} else { // 有库存
let restData = this.commoditySpecs.filter(v => v.title !== title);
this.dfs(restData, relation);
}
this.$set(item, "active", true);
} else { // 取消选择的情况
this.$set(item, "active", false);
let choseData = this.commoditySpecs.reduce(
(total, current) => total.concat(current.list.filter(v => v.active)),
[]
);
if (choseData.length === 0) { // 当前没有选中的元素
this.init();
} else { // 当前存在选中的元素
let choseDataName = choseData.map(v => v.name);
let arr = [];
choseData.forEach(v => {
let currentData = this.getVertex(v.name);
arr = arr.concat(currentData);
});
arr = arr.concat(choseDataName);
this.dfs(this.commoditySpecs, arr);
}
}
}
},
mounted () {
this.init();
this.initData();
console.log(this.getVertex("红色"));
}
};
</script>
<style lang="scss" scoped>
.shopList {
display: flex;
text-indent: 10px;
.shopList-item {
margin-right: 8px;
background: #ffffff;
cursor: pointer;
&.active {
background: red;
}
&.disable {
background: #dddddd;
}
}
}
</style>
完整的github地址为:https://github.com/whenTheMorningDark/vue-kai-admin/blob/master/src/views/sku/index.vue,如果对你有帮助,请点一下star。
更多推荐
所有评论(0)