效果图
1 HTML 基本结构
1.1 遍历 SKU 规格数据
<template>
<div class="productConten">
<div v-for="(productItem, productIndex) in specifications" :key="productItem.name">
<div class="productTitle">{{ productItem.name }}</div>
<ul class="productItem">
<li
v-for="(oItem, oIndex) in productItem.item"
:key="oItem.name"
@click="selectArrtBtn(oItem, productIndex, oIndex)"
:class="{ noneActive: !oItem.checked, active: subIndex[productIndex] == oIndex }"
>
{{ oItem.name }}
</li>
</ul>
</div>
</div>
<div v-if="boxArr.id">
{{ boxArr.difference + '--' + boxArr.price }}
</div>
</template>
2 编写 css 基本样式
<style lang="scss" scoped>
.productConten {
width: 500px;
padding: 40px;
.productTitle {
font-weight: 500;
margin: 10px 0;
}
.productItem {
display: flex;
margin-bottom: 30px;
li {
background: #fff;
color: #000;
padding: 4px 10px;
border: 1px solid #eee;
margin-right: 10px;
}
.noneActive {
background-color: #ccc;
opacity: 0.4;
color: #000;
pointer-events: none;
}
.active {
background-color: #c41e3a;
color: #fff;
border: 1px solid #c41e3a;
}
}
}
</style>
3 js 代码
<script setup>
import { onMounted, ref } from 'vue'
// 规格数据
const specifications = ref([
{
name: '颜色',
item: [{ name: '白色' }, { name: '黑色' }, { name: '红色' }]
},
{
name: '尺码',
item: [{ name: 'x' }, { name: 'xl' }]
}
])
// 商品仓库数据
const dataList = ref([
{
id: '19',
price: '200.00',
stock: '19',
difference: '红色,x'
},
{
id: '20',
price: '300.00',
stock: '29',
difference: '白色,x'
},
{
id: '21',
price: '300.00',
stock: '10',
difference: '黑色,x'
},
{
id: '21',
price: '300.00',
stock: '10',
difference: '黑色,xl'
},
{
id: '24',
price: '500.00',
stock: '10',
difference: '白色,xl'
}
])
//存放要和选中的值进行匹配的数据
const shopItemInfo = ref({})
//存放被选中的值
const selectArr = ref([])
//是否选中 因为不确定是多规格还是单规格,所以这里定义数组来判断
const subIndex = ref([])
const boxArr = ref({})
onMounted(() => {
const difference = dataList.value
const attrList = specifications.value
// 1 修改数据结构格式,改成键值对的方式,以方便和选中之后的值进行匹配
for (let i = 0; i < difference.length; i++) {
shopItemInfo.value[difference[i].difference] = difference[i]
}
// 2 给每个规格的选项添加一个checked属性,用来判断是否选中
for (let i = 0; i < attrList.length; i++) {
for (let j = 0; j < attrList[i].item.length; j++) {
attrList[i].item[j].checked = true
}
}
specifications.value = attrList
})
/**
* 点击选择规格
* @param {Object} item 规格的值
* @param {Number} index 规格标题的索引
* @param {Number} arrtIndex 规格的索引
* */
const selectArrtBtn = (item, index, arrtIndex) => {
// 1 如果数组中没有选中的值,就添加到数组中,有则清空之前选中的值
if (selectArr.value[index] != item.name) {
selectArr.value[index] = item.name
subIndex.value[index] = arrtIndex
} else {
selectArr.value[index] = ''
// 去掉选中的颜色
subIndex.value[index] = -1
}
checkItem()
// 3 在能选中的值中查找是否有
const selectObj = shopItemInfo.value[selectArr.value]
if (selectObj) {
boxArr.value = selectObj
}
}
const checkItem = () => {
const arrt = specifications.value
// 定义数组存储被选中的值
let result = []
for (let i in arrt) {
result[i] = selectArr.value[i] ? selectArr.value[i] : ''
}
for (let i in arrt) {
// 把选中的值提取出来
let last = result[i]
for (let k in arrt[i].item) {
result[i] = arrt[i].item[k].name
arrt[i].item[k].checked = isMay(result)
}
// 还原,目的是记录点下去那个值,避免下一次执行循环时被覆盖
result[i] = last
}
specifications.value = arrt
}
const isMay = (result) => {
for (let i in result) {
if (result[i] == '') return true
}
return !shopItemInfo.value[result] ? false : shopItemInfo.value[result]?.stock == 0 ? false : true
}