Vue3实现 SKU 规格

245 阅读2分钟

效果图

image.png

1 HTML 基本结构

1.1 遍历 SKU 规格数据

  <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 代码

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
}