效果图
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"><liv-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.valueconst 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.namesubIndex.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].namearrt[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
}