多規格商品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

 

然後封裝使用在 父組件使用,

 

 

 

 這個就是效果圖,

嘻嘻 這樣就算封裝完畢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章