• 多规格商品SKU 组件封装


    前言

     做电商项目呢,离不开多规格商品,SKU 也是弄了许久才搞出来,主要是多层级的联动关系,用ID和库存来判断是否是按钮禁止状态

    下面就放下代码:

    以封装的小程序为例:

     WXML:

    <view class="sku-box" wx:if="{{cpSkuTree.length}}">
      <view class="sku-row" wx:for="{{cpSkuTree}}" wx:key="{{index}}">
        <view class="sku-title">{{item.k}}</view>
        <view class="sku-wrap flex-row">
          <view class="sku-item {{iitem.disabled ? 'disabled': ''}} {{iitem.selected ? 'selected': ''}}" wx:for="{{item.v}}" wx:for-item="iitem" wx:for-index="iindex" wx:key="{{iindex}}" data-index="{{index}}" data-iindex="{{iindex}}" data-k="{{item}}" data-value="{{iitem}}" catchtap="selectSku">{{iitem.name}}</view>
        </view>
      </view>
    </view>
    View Code

    JS:

    const computedBehavior = require('miniprogram-computed')
    
    Component({
      behaviors: [computedBehavior],
      properties: {
        skuTree: {
          type: Array,
          value: [],
          observer: function (newVal) {
            let cpNewVal = JSON.parse(JSON.stringify(newVal))
            cpNewVal.forEach( row => {
              row.v.forEach(item => {
                Object.assign(item, {
                  selected: false,
                  disabled: false
                })
              })
            })
            this.setData({
              cpSkuTree: cpNewVal
            })
            this.judgeAllItem()
          }
        },
        skuList: {
          type: Array,
          value: []
        }
      },
      data: {
        cpSkuTree: [],
        // 选择的 sku 组合
        selectedSku: {
        }
      },
      computed: {
        
      },
      methods: {
        // 点击sku按钮
        selectSku (e) {
          const k = e.currentTarget.dataset.k
          const index = e.currentTarget.dataset.index
          const value = e.currentTarget.dataset.value
          const iindex = e.currentTarget.dataset.iindex
          value.disabled = true
          if (this.data.cpSkuTree[index].v[iindex].disabled) {
            return
          }
          // 勾选或者反选
          const key = `selectedSku.${k.ks}`
          if (!this.data.cpSkuTree[index].v[iindex].selected) {
            // 勾选把值记住
            this.setData({
              [key]: value.id
            })
          } else {
            // 反选把值删掉
            this.setData({
              [key]: ''
            })
          }
          this.setData({
            [`cpSkuTree[${index}].v[${iindex}].selected`]: !this.data.cpSkuTree[index].v[iindex].selected
          })
          this.cancelOption(index, value)
          this.judgeAllItem()
          this.changePic(index, iindex)
          if (this.isAllSelected()) {
            const skuData = this.getSkuComb()
            this.triggerEvent('selectChange', skuData)
          } else {
            this.triggerEvent('selectChange', null)
          }
        },
        /**
         * 取消同一组所有选项
         */
        cancelOption (index, value) {
          let rowList = this.data.cpSkuTree[index].v
          for (let i = 0; i < rowList.length; i++) {
            if (rowList[i].id != value.id) {
              this.setData({
                [`cpSkuTree[${index}].v[${i}].selected`]: false
              })
            }
          }
        },
        /**
         * 循环判断是否可选
         */
        judgeAllItem () {
          let tree = this.data.cpSkuTree
          for (let i = 0; i < tree.length; i++) {
            let v = tree[i].v
            for (let j = 0; j < v.length; j++) {
              if (this.isSkuChoosable(tree[i].ks, v[j].id)) {
                this.setData({
                  [`cpSkuTree[${i}].v[${j}].disabled`]: false
                })
              } else {
                this.setData({
                  [`cpSkuTree[${i}].v[${j}].disabled`]: true
                })
              }
            }
          }
          this.getSelectedText()
        },
        /**
         * 判断可选项的库存
         */
        isSkuChoosable (ks, id) {
          const list = this.data.skuList
          const selectedSku = this.data.selectedSku
          // 先假设已经选中剩余按钮
          let matchedSku = Object.assign({}, selectedSku, {
            [ks]: id
          })
    
          // 将matchedSku中有效的key提取
          let skusToCheck = Object.keys(matchedSku).filter(
            skuKey => matchedSku[skuKey] != ''
          )
          // 有效key值匹配有多少sku
          let filterSku = list.filter(sku => 
            skusToCheck.every(
              skuKey => matchedSku[skuKey] == sku[skuKey]
            )
          )
          // 假设按钮包含所有sku的库存数
          let stock = filterSku.reduce((total, sku) => {
            total += sku.stock_num
            return total
          }, 0)
          return stock > 0
    
        },
        /**
         * 判断是否选完所有规格
         */
        isAllSelected () {
          let selectedSku = this.data.selectedSku
          let selected = Object.keys(selectedSku).filter(
            skuKey => selectedSku[skuKey] !== ''
          )
          return selected.length === this.data.cpSkuTree.length
        },
        /**
         * 获得已经确定的组合
         */
        getSkuComb () {
          let selectedSku = this.data.selectedSku
          let list = this.data.skuList
          let skusToCheck = []
          this.data.cpSkuTree.forEach(item => {
            skusToCheck.push(item.ks)
          })
          let filteredSku = list.filter(sku => (
              skusToCheck.every(skuKey => selectedSku[skuKey] == sku[skuKey])
            )
          )
          return filteredSku[0]
        },
        /**
         * 修改图片
         */
        changePic (index, iindex) {
          if (index == 0) {
            this.triggerEvent('changePic', this.data.cpSkuTree[index].v[iindex].picUrl)
          }
        },
        // 选择属性文字
        getSelectedText () {
          let selectedSku = this.data.selectedSku
          let text = ''
          Object.keys(selectedSku).forEach(skuKey => {
            const id = selectedSku[skuKey]
            const tree = this.data.cpSkuTree
            for (let i = 0; i < tree.length; i++) {
              const v = tree[i].v
              for (let j = 0; j < v.length; j++) {
                if (v[j].id == id) {
                  text = `${text} ${v[j].name}`
                }
              }
            }
          })
          this.triggerEvent('textChange', text)
        }
      }
    })
    View Code

    上面都有注释:

    CSS

    .flex-row {
      display: flex;
      flex-direction: row;
    }
    
    .flex-col {
      display: flex;
      flex-direction: column;
    }
    .sku-box {
      width: 100%;
      background: #fff;
      padding: 40rpx;
    }
    .sku-row {
      margin-bottom: 36rpx;
    }
    .sku-title {
      color: #333;
      font-size: 24rpx;
      line-height: 1;
    }
    .sku-wrap {
      margin-top: 36rpx;
      flex-wrap: wrap;
    }
    .sku-item {
      min-width: 160rpx;
      padding: 18rpx 14rpx;
      color: #555555;
      font-size: 24rpx;
      line-height: 1;
      border-radius: 4rpx;
      background: #EAEAEA;
      border: 1rpx solid #EAEAEA;
      text-align: center;
      margin-bottom: 20rpx;
    }
    .sku-item+.sku-item {
      margin-left: 24rpx;
    }
    .sku-item.disabled {
      color: #ababab;
    }
    .sku-item.selected {
      color: #38ADFF;
      background:rgba(56,173,255,0.1);
      border: 1rpx solid #38ADFF;
    }
    View Code

    然后封装使用在 父组件使用,

     这个就是效果图,

    嘻嘻 这样就算封装完毕

  • 相关阅读:
    bzoj1724[Usaco2006 Nov]Fence Repair 切割木板*
    vue-cli脚手架和webpack-simple模板项目
    Vue-router 进阶
    前端路由vue-router介绍
    vue的一些特殊特性
    生命周期钩子
    过滤器
    RSA加密算法
    欧几里得算法
    动态规划
  • 原文地址:https://www.cnblogs.com/yf-html/p/11926820.html
Copyright © 2020-2023  润新知