Vue3电商项目实战-商品详情模块4【10-★规格组件-禁用效果-思路分析、11-★规格组件-禁用效果-路径字典、12-★规格组件-禁用效果-设置状态、13-★规格组件-数据通讯】


10-★规格组件-禁用效果-思路分析

目标:大致了解禁用效果的整体思路,注意只是了解。

在这里插入图片描述

大致步骤:

  • 1.根据后台返回的skus数据得到有效sku组合
  • 2.根据有效的sku组合得到所有的子集集合
  • 3.根据子集集合组合成一个路径字典,也就是对象。
  • 4.在组件初始化的时候去判断每个规格是否点击
  • 5.在点击规格的时候去判断其他规格是否可点击
  • 6.判断的依据是,拿着说有规格和现在已经选中的规则取搭配,得到可走路径。
    • 1.如果可走路径在字典中,可点击
    • 2.如果可走路径不在字典中,禁用

11-★规格组件-禁用效果-路径字典

目的:根据后台skus数据得到可走路径字典对象

  • js算法库 https://github.com/trekhleb/javascript-algorithms
  • 幂集算法 https://raw.githubusercontent.com/trekhleb/javascript-algorithms/master/src/algorithms/sets/power-set/bwPowerSet.js

src/vender/power-set.js

/**
 * Find power-set of a set using BITWISE approach.
 *
 * @param {*[]} originalSet
 * @return {*[][]}
 */
export default function bwPowerSet(originalSet) {
  const subSets = [];

  // We will have 2^n possible combinations (where n is a length of original set).
  // It is because for every element of original set we will decide whether to include
  // it or not (2 options for each set element).
  const numberOfCombinations = 2 ** originalSet.length;

  // Each number in binary representation in a range from 0 to 2^n does exactly what we need:
  // it shows by its bits (0 or 1) whether to include related element from the set or not.
  // For example, for the set {1, 2, 3} the binary number of 0b010 would mean that we need to
  // include only "2" to the current set.
  for (let combinationIndex = 0; combinationIndex < numberOfCombinations; combinationIndex += 1) {
    const subSet = [];

    for (let setElementIndex = 0; setElementIndex < originalSet.length; setElementIndex += 1) {
      // Decide whether we need to include current element into the subset or not.
      if (combinationIndex & (1 << setElementIndex)) {
        subSet.push(originalSet[setElementIndex]);
      }
    }

    // Add current subset to the list of all subsets.
    subSets.push(subSet);
  }

  return subSets;
}

src/views/goods/components/goods-sku.vue

import getPowerSet from '@/vender/power-set'
const spliter = '★'
// 根据skus数据得到路径字典对象
const getPathMap = (skus) => {
  const pathMap = {}
  skus.forEach(sku => {
    // 1. 过滤出有库存有效的sku
    if (sku.inventory) {
      // 2. 得到sku属性值数组
      const specs = sku.specs.map(spec => spec.valueName)
      // 3. 得到sku属性值数组的子集
      const powerSet = getPowerSet(specs)
      // 4. 设置给路径字典对象
      powerSet.forEach(set => {
        const key = set.join(spliter)
        if (pathMap[key]) {
          // 已经有key往数组追加
          pathMap[key].push(sku.id)
        } else {
          // 没有key设置一个数组
          pathMap[key] = [sku.id]
        }
      })
    }
  })
  return pathMap
}
+  setup (props) {
+    const pathMap = getPathMap(props.goods.skus)
+    console.log(pathMap)

参照示例
在这里插入图片描述

12-★规格组件-禁用效果-设置状态

在这里插入图片描述

目的:在组件初始化的时候,点击规格的时候,去更新其他按钮的禁用状态。

大致的步骤:

  • 再需要更新状态的时候获取当前选中的规格数组
  • 遍历所有的规格按钮,拿出按钮的值设置给规格数组,让后得到key
  • 拿着key去路径字典中查找,有就可点击,没有禁用即可。
    在这里插入图片描述

src/views/goods/components/goods-sku.vue

// 得到当前选中规格集合
const getSelectedArr = (specs) => {
  const selectedArr = []
  specs.forEach(spec => {
    const selectedVal = spec.values.find(val => val.selected)
    selectedArr.push(selectedVal ? selectedVal.name : undefined)
  })
  return selectedArr
}
// 更新按钮的禁用状态
const updateDisabledStatus = (specs, pathMap) => {
  specs.forEach((spec, i) => {
    const selectedArr = getSelectedArr(specs)
    spec.values.forEach(val => {
      // 已经选中的按钮不用判断
      if (val.name === selectedArr[i]) return false
      // 未选中的替换对应的值
      selectedArr[i] = val.name
      // 过滤无效值得到key
      const key = selectedArr.filter(v => v).join(spliter)
      // 设置禁用状态
      val.disabled = !pathMap[key]
    })
  })
}
  setup (props) {
    const pathMap = getPathMap(props.goods.skus)
    // 组件初始化的时候更新禁用状态
+    updateDisabledStatus(props.goods.specs, pathMap)
    const clickSpecs = (item, val) => {
      // 如果是禁用状态不作为
+      if (val.disabled) return false
      // 1. 选中与取消选中逻辑
      if (val.selected) {
        val.selected = false
      } else {
        item.values.find(bv => { bv.selected = false })
        val.selected = true
      }
      // 点击的时候更新禁用状态
+      updateDisabledStatus(props.goods.specs, pathMap)
    }
    return { clickSpecs }
  }

13-★规格组件-数据通讯

目的:根据传入的skuId进行默认选中,选择规格后触发change事件传出选择的sku数据。

大致步骤:

  • 根据传入的SKUID选中对应规格按钮
  • 选择规格后传递sku信息给父组件
    • 完整规格,传 skuId 价格 原价 库存 规格文字
    • 不完整的,传 空对象

落的代码:

  • 根据传人的sku设置默认选中的规格 src/views/goods/components/goods-sku.vue
    skuId: {
      type: String,
      default: ''
    }
// 初始化选中状态
const initSelectedStatus = (goods, skuId) => {
  const sku = goods.skus.find(sku => sku.id === skuId)
  if (sku) {
    goods.specs.forEach((spec, i) => {
      const value = sku.specs[i].valueName
      spec.values.forEach(val => {
        val.selected = val.name === value
      })
    })
  }
}
  setup (props, { emit }) {
    const pathMap = getPathMap(props.goods.skus)
    // 根据传入的skuId默认选中规格按钮
+    initSelectedStatus(props.goods, props.skuId)
    // 组件初始化的时候更新禁用状态
    updateDisabledStatus(props.goods.specs, pathMap)
  • 根据选择的完整sku规格传出sku信息
    • 其中传出的specsText是提供给购物车存储使用的。

src/views/goods/components/goods-sku.vue

+  setup (props, { emit }) {
const clickSpecs = (item, val) => {
      // 如果是禁用状态不作为
      if (val.disabled) return false
      // 1. 选中与取消选中逻辑
      if (val.selected) {
        val.selected = false
      } else {
        item.values.find(bv => { bv.selected = false })
        val.selected = true
      }
      // 点击的时候更新禁用状态
      updateDisabledStatus(props.goods.specs, pathMap)
+      // 触发change事件将sku数据传递出去
+      const selectedArr = getSelectedArr(props.goods.specs).filter(v => v)
+      if (selectedArr.length === props.goods.specs.length) {
+        const skuIds = pathMap[selectedArr.join(spliter)]
+        const sku = props.goods.skus.find(sku => sku.id === skuIds[0])
+        // 传递
+        emit('change', {
+          skuId: sku.id,
+          price: sku.price,
+          oldPrice: sku.oldPrice,
+          inventory: sku.inventory,
+          specsText: sku.specs.reduce((p, n) => `${p} ${n.name}${n.valueName}`, '').replace(' ', '')
+        })
+      } else {
+        emit('change', {})
+      }
+    }

src/views/goods/index.vue

          <GoodsSku :goods="goods" @change="changeSku"/>
  setup () {
    const goods = useGoods()
    // sku改变时候触发
+    const changeSku = (sku) => {
+      if (sku.skuId) {
+        goods.value.price = sku.price
+        goods.value.oldPrice = sku.oldPrice
+        goods.value.inventory = sku.inventory
+      }
+    }
+    return { goods, changeSku }
  }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值