本组件开发基本本身项目需求,通用只是指适用于需求类似的项目。具体可以直接看代码,就算用不上,希望也能提供对应的逻辑给你们。基于Uni框架,不同平台换个标签代码就好了
最终效果图如下:
好了,话不多说,直接上代码(回头有时间再讲解为什么这么写)
组件代码:
<template>
<view class="sku-wrapper" @tap.stop>
<view class="content">
<scroll-view scroll-y="true">
<view class="grace-product-attr-list" v-for="(item, index) in skuData" :key="index">
<view class="title">{{ item.name }}</view>
<view class="grace-select-tags">
<view class="group">
<view
v-for="(i, sonIndex) in item.leaf"
:data-isclick="i.is_click ? 0 : 1"
:data-disabled="i.disabled ? 0 : 1"
:data-value="i.id"
:data-curindex="index"
:key="sonIndex"
@tap.stop="colorChange"
:class="[i.disabled ? 'disabled' : i.is_click ? 'grace-checkeds' : '', 'label unchecked']"
>
{{ i.name }}
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
props: {
// 规格的数据
specData: {
type: Array,
default: () => []
},
// 规格组
skuGroup: {
type: Array,
default: () => []
}
},
data() {
return {
skuChooseId: [],
currentSkuGroup: {
name: '请选择商品规格'
},
skuData: [],
skuGroupData: [],
specIdArr: [],
skuAllData: {}
}
},
watch: {
specData: {
handler(newVal) {
if (newVal.length !== 0 && this.skuGroup.length !== 0) {
this.handleData()
}
},
deep: true
}
},
mounted() {
if (this.specData.length !== 0 && this.skuGroup.length !== 0) {
this.handleData()
}
},
methods: {
// 处理传入的规格数据
handleData() {
this.skuData = JSON.parse(JSON.stringify(this.specData))
this.skuGroupData = JSON.parse(JSON.stringify(this.skuGroup))
this.skuChooseId = []
for (let i of this.skuGroupData) {
let current = i.key.split('_')
// 关联KEY只有一个时默认禁用库存为零的
if (current.length == 1) {
for (let j of this.skuData) {
if (j.leaf.length == 1) {
// 如果规格只有一个,则默认选择第一个
setTimeout(() => {
// 组件第一次挂载是拿不到值会报错
this.colorChange({
// 调用函数,模拟点击选择第一个
value: j.leaf[0].id,
isclick: 1,
disabled: j.leaf[0].disabled ? 0 : 1,
curindex: 0
})
}, 300)
}
}
}
}
},
// 点击选中的规格item
colorChange(e) {
let _this = this
this.specIdArr = []
this.currentSkuGroup = {}
let parms = {}
if (e.currentTarget) {
parms = e.currentTarget.dataset
} else {
parms = e
}
let checkVal = String(parms.value)
let isClick = parms.isclick
let disabled = parms.disabled
let curIndex = parms.curindex
let speclength = _this.skuData.length // 获取规格总长度
if (parms.disabled == 0) return
for (let i of this.skuGroupData) {
let current = i.key.split('_') // 关联当前规格的ID
if (current.includes(checkVal)) {
this.setSkuData(current, checkVal, false)
}
}
for (let i = 0; i < _this.skuData[curIndex].leaf.length; i++) {
if (checkVal == _this.skuData[curIndex].leaf[i].id) {
let isClick = _this.skuData[curIndex].leaf[i].is_click;
_this.skuData[curIndex].leaf[i].is_click = !isClick;
if (isClick) {
_this.skuChooseId[curIndex] = '';
} else {
_this.skuChooseId[curIndex] = Number(checkVal);
}
let skuChoose = _this.skuChooseId.filter(item => item);
if (skuChoose.length == speclength) {
// 当选中所有规格时候,遍历规格组取值
let groupId = _this.skuChooseId.join('_');
console.info('groupId=>', groupId);
_this.skuGroupData.map((item, index) => {
let thisArr = item.key.split('_');
thisArr = thisArr.map((item, index) => {
item = Number(item);
return item;
});
const subtraction = _this.skuChooseId.concat(thisArr).filter(v => {
return _this.skuChooseId.includes(v) ^ thisArr.includes(v);
});
// 若差集为空数组[],则两个数组一样
if (subtraction.length == 0) {
_this.currentSkuGroup = item; // 存储当前已选中的规格组的所有信息
}
});
} else {
_this.currentSkuGroup = {};
}
} else {
_this.skuData[curIndex].leaf[i].is_click = false;
}
}
_this.affirmSku();
},
// 设置SKU
setSkuData(current, checkVal, flag) {
this.skuData = this.skuData.map(i => {
i.leaf.map(j => {
// filter过滤自身ID,查找关联当前ID的规格ID是否存在
if (current.filter(item => item != checkVal).includes(j.id.toString())) {
// 当前规格ID不存在数据则设置&&未选择
if (!this.specIdArr.includes(j.id) && !j.is_click) {
j.disabled = flag;
}
// 防止覆盖
this.specIdArr.push(j.id);
}
return j;
});
return i;
});
console.info('skuData', this.skuData);
},
// 确定规格
affirmSku() {
this.$emit('affirmSku', this.currentSkuGroup); // 选择结束后,抛出已选出来的规格数据
}
}
};
</script>
<style lang="scss" scoped>
.grace-product-attr-list {
.title {
font-size: 24rpx;
margin-bottom: 20rpx;
color: #333333;
}
.grace-select-tags {
.group {
width: 100%;
display: flex;
flex-wrap: wrap;
.label {
display: block;
font-size: 24rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 8rpx;
padding: 0 20rpx;
margin-bottom: 20rpx;
margin-right: 20rpx;
border: 2rpx solid transparent;
color: #333;
background-color: #f5f5f5;
overflow: hidden;
}
.disabled {
opacity: 0.4;
}
.grace-checkeds {
color: #6d69f7;
background-color: #f0efff;
border: 2rpx solid #6d69f7;
}
}
}
}
</style>
使用:(案例是商品详情,只贴使用的地方出来哦)
<!-- 规格sku -->
<view class="detail-sku bg-white margin-bottom-sm">
<view class="detail-loading cu-load loading" v-if="skuLoading"></view>
<sku v-on:affirmSku="affirmSku" :specData="specData" :skuGroup="skuGroup" v-else></sku>
</view>
<script>
import Sku from '@/components/shop/sku/sku.vue'
export default {
components: {
Sku
},
data() {
return {
detailData: null, // demo数据在下面
skuLoading: true, // sku的loading加载
specData: [],
skuGroup: [],
}
}, methods: {
// 获得最终选中规格的数据
affirmSku(e) {
if (this.selectSkuData && e.id !== this.selectSkuData.id) this.num = 1
this.selectSkuData = e
console.log('确定选择规格', e)
},
// 获得sku规格数据进行处理
getSpec() {
let specData = this.detailData.spec
if (specData && specData.length > 0) {
specData.forEach((spec_item, index) => {
spec_item.leaf.forEach(item => {
item.is_click = false
})
})
}
this.specData = specData
this.skuGroup = this.detailData.sku // 规格组数据
this.skuLoading = false
},
// 获取商品详情
getDetail() {
// 这里写自己的详情请求,然后进行sku数据处理
this.getSpec()
}
}
}
</script>
最后贴上这个组件所需要的数据格式
detailData: {
"spec":[ { "id":1, "name":"大小", "sort":0, "leaf":[ { "id":1, "goods_id":1, "goods_spec_id":1, "goods_spec_name":"大小", "name":"大", "is_click":0 }, { "id":5, "goods_id":1, "goods_spec_id":1, "goods_spec_name":"大小", "name":"中", "is_click":0 }, { "id":6, "goods_id":1, "goods_spec_id":1, "goods_spec_name":"大小", "name":"小", "is_click":0 } ] }, { "id":3, "name":"甜度", "sort":0, "leaf":[ { "id":2, "goods_id":1, "goods_spec_id":3, "goods_spec_name":"甜度", "name":"10", "is_click":0 }, { "id":3, "goods_id":1, "goods_spec_id":3, "goods_spec_name":"甜度", "name":"7", "is_click":0 }, { "id":4, "goods_id":1, "goods_spec_id":3, "goods_spec_name":"甜度", "name":"5", "is_click":0 } ] } ], "sku":[ { "id":18, "store_id":2, "goods_id":4, "key":"1_2", "name":"大小:大 甜度:10 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":1 }, { "id":19, "store_id":2, "goods_id":4, "key":"1_3", "name":"大小:大 甜度:7 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":2 }, { "id":20, "store_id":2, "goods_id":4, "key":"1_4", "name":"大小:大 甜度:5 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":3 }, { "id":21, "store_id":2, "goods_id":4, "key":"5_2", "name":"大小:中 甜度:10 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":4 }, { "id":22, "store_id":2, "goods_id":4, "key":"5_3", "name":"大小:中 甜度:7 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":5 }, { "id":23, "store_id":2, "goods_id":4, "key":"5_4", "name":"大小:中 甜度:5 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":6 }, { "id":24, "store_id":2, "goods_id":4, "key":"6_2", "name":"大小:小 甜度:10 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":7 }, { "id":25, "store_id":2, "goods_id":4, "key":"6_3", "name":"大小:小 甜度:7 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":8 }, { "id":26, "store_id":2, "goods_id":4, "key":"6_4", "name":"大小:小 甜度:5 ", "price":30, "supply_price":20, "admin_goods_id":1, "admin_sku_id":9 } ],
}