1. Basic understanding of SKU

SkU (Stock Keeping Unit) : The Unit of inventory measurement, which can be in parts, boxes, and pallets.

After clicking on a product, there will be a corresponding product model, as detailed as: what color, what size, what origin, and finally the combination is SKU

For example: today I bought a cell phone, the description of the cell phone is not detailed enough to call it an SKU; I bought a phone, the brand name is Huawei Mate40 Pro Plus, ceramic white, 512GB ram, etc., put all the product information together and call it SKU

The following picture: pot, black, domestic, 20cm size is its SKU

And what we’re going to do is filter out every possible combination of types that the user can pick based on those types; According to the user clicks these buttons, we can know what kind of product the user has selected, and what is the detailed information of this product? It can be shown by the button clicked by the user, and according to the quantity of stock, we should disable the buttons corresponding to the goods without stock.

We’ll continue with this pot as an example

2. View the data of the requested product

The back-end of this data has told me that there are 12 combinations of this productWe can see that the corresponding inventory of this product is out of stock, so users should not click on the corresponding button

Ideas:

Find the inventory of the corresponding item according to the button clicked by the user ==> Generate a path dictionary that can find the target according to the data at the back end

When the user clicks the blue pot, go to the right side to find the data corresponding to the blue, and so on. When the user cannot find the data in the dictionary, it means that there is no stock and the button cannot be clicked

Take black as an example: the combination of black is these items, when the user clicks China again, only 10cm can be found, and the button 20cm or 30cm cannot be clicked

3. Detailed JS code:

3.1 Methods of calculating subsets of sets

First, a power algorithm is used: the main reference is as follows

Js algorithm library and power set algorithm

Main function realization: with the help of the algorithm to generate a path dictionary can be queried

Here’s the code in reference: wrap it in a separate JS file

/**
 * Find power-set of a set using BITWISE approach.
 *
 * @param {x} [] 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
}

Copy the code

3.2 Generating a Dictionary

Next: Encapsulate a method to get a path dictionary by simply passing in skU data

<script>
// import { ref } from 'vue'
import bwPowerSet from '@/vendor/power-set'
const spliter = '★'
// Get path dictionary object based on skus data
const getPathMap = (skus) = > {
  // console.log(skus.forEach(it => { console.log(it) }))
  const pathMap = {}
  skus.forEach(sku= > {
    // 1. Filter out valid SKUs with inventory
    if (sku.inventory) {
      // 2. Get an array of SKu property values
      const specs = sku.specs.map(spec= > spec.valueName)
      // 3. Get a subset of the SKu property values array
      const powerSet = bwPowerSet(specs)
      // console.log(specs)
      // console.log(powerSet)
      // 4. Set to path dictionary object
      powerSet.forEach(set= > {
        const key = set.join(spliter)
        if (pathMap[key]) {
          // A key has been appended to the array
          pathMap[key].push(sku.id)
        } else {
          // Set an array without a key
          pathMap[key] = [sku.id]
        }
      })
    }
  })
  console.log(pathMap)
  return pathMap
}
export default{
       // I'm not going to write any extra code here
    setup(){
       // Send a request to get commodity data
        const goodsData = ref({})
        findGoods(route.params.id).then(data= > {
          goodsData.value = data.result
          console.log(data)
        })
        const pathMap = getPathMap(goodsData.skus)
        console.log(pathMap)
        
        return { goodsData }
    }
}
</script>
Copy the code

Through the above method to view the final path dictionary, the obtained content is actually as follows

4. Search for the selected content

4.1 Analysis: When does the button start to display the disabled effect

(1) Display the component when it is created

(2) Every time the user clicks the button, the search will be carried out

4.2 Encapsulating Functions

This function takes two arguments, a dictionary and a specification

<script>
const updateDisabledStatus = (pathMap, specs) = > {
  // user select [undefined,' Chinese ',undefined]
  const _selectedArr = getSelectedArr(specs)

  specs.forEach((spec, idx) = > {
    const selectedArr = [..._selectedArr]
    spec.values.forEach(btn= > {
      // Already selected
      if (btn.name === selectedArr[idx]) { return }
      // Add the last option to the last item selected by the user
      selectedArr[idx] = btn.name

      // Delete undefined concatenation string
      const key = selectedArr.filter(v= > v).join(spliter)
      // Set to true if not foundbtn.disabled = ! pathMap[key]// (undefined)
    })
  })
}
</script>
Copy the code

This function takes all the data information for the item and the ID of the current item


// Restore the selected specifications according to the skuId
const initSelectedStatus = (goodsData, skuId) = > {
  // 1. Find the selected specifications
  const sku = goodsData.skus.find(sku= > sku.id === skuId)
  if (sku) {
    const selectArr = sku.specs.map(it= > it.valueName)
    goodsData.specs.forEach((spec, idx) = > {
      spec.values.forEach(value= > {
        value.selected = (value.name === selectArr[idx])
      })
    })
  }
}
Copy the code

4. Implemented functional code (packaged as components, including style code)

<template>
  <div class="goods-sku">
    <dl v-for="(spec, idx) in goodsData.specs" :key="idx">
      <dt>{{ spec.name }}</dt>
      <dd>
        <template v-for="value in spec.values" :key="value.name">
          <img
            @click="clickSpecs(value, spec.values)"
            v-if="value.picture"
            :class="{ selected: value.selected, disabled: value.disabled }"
            :src="value.picture"
            :title="value.name"
          />
          <span
            v-else
            @click="clickSpecs(value, spec.values)"
            :class="{ selected: value.selected, disabled: value.disabled }"
            >{{ value.name }}</span
          >
        </template>
      </dd>
    </dl>
    
  </div>
</template>
<script>
// import { ref } from 'vue'
import bwPowerSet from '@/vendor/power-set'
const spliter = '★'
// Get path dictionary object based on skus data
const getPathMap = (skus) = > {
  // console.log(skus.forEach(it => { console.log(it) }))
  const pathMap = {}
  skus.forEach(sku= > {
    // 1. Filter out valid SKUs with inventory
    if (sku.inventory) {
      // 2. Get an array of SKu property values
      const specs = sku.specs.map(spec= > spec.valueName)
      // 3. Get a subset of the SKu property values array
      const powerSet = bwPowerSet(specs)
      // console.log(specs)
      // console.log(powerSet)
      // 4. Set to path dictionary object
      powerSet.forEach(set= > {
        const key = set.join(spliter)
        if (pathMap[key]) {
          // A key has been appended to the array
          pathMap[key].push(sku.id)
        } else {
          // Set an array without a key
          pathMap[key] = [sku.id]
        }
      })
    }
  })
  console.log(pathMap)
  return pathMap
}

// 1. Obtain the condition that the user has selected
const getSelectedArr = specs= > {
  return specs.map(spec= > {
    // Find the user's selection under this property
    const value = spec.values.find(it= > it.selected === true)

    return value ? value.name : undefined
  })
  // return [undefined,' Chinese ',undefined]
}
// 2. For each button: in the combination of the current button corresponding
// Update the disabled status of the button
const updateDisabledStatus = (pathMap, specs) = > {
  // user select [undefined,' Chinese ',undefined]
  const _selectedArr = getSelectedArr(specs)

  specs.forEach((spec, idx) = > {
    const selectedArr = [..._selectedArr]
    spec.values.forEach(btn= > {
      // Already selected
      if (btn.name === selectedArr[idx]) { return }
      // Add the last option to the last item selected by the user
      selectedArr[idx] = btn.name

      // Delete undefined concatenation string
      const key = selectedArr.filter(v= > v).join(spliter)
      // Set to true if not foundbtn.disabled = ! pathMap[key]// (undefined)})})}// Restore the selected specifications according to the skuId
const initSelectedStatus = (goodsData, skuId) = > {
  // 1. Find the selected specifications
  const sku = goodsData.skus.find(sku= > sku.id === skuId)
  if (sku) {
    const selectArr = sku.specs.map(it= > it.valueName)
    goodsData.specs.forEach((spec, idx) = > {
      spec.values.forEach(value= > {
        value.selected = (value.name === selectArr[idx])
      })
    })
  }
  // 2. Set the corresponding button selected to true
}
export default {
  name: 'GoodsSku'.props: {
    goodsData: {
      type: Object.default: () = > ({
        specs: [].skus: []})},skuId: {
      type: String.default: ' '
    }
  },
  setup (props, { emit }) {
    const clickSpecs = (value, values) = > {
      // If it is disabled, no action is taken
      if (value.disabled) { return }
      if (value.selected) {
        value.selected = false // Selected to unselected
      } else {
        // Change all brothers to unselected
        values.forEach(it= > { it.selected = false })
        value.selected = true // Self: unselected to selected
      }
      updateDisabledStatus(pathMap, props.goodsData.specs)
      // Throw an event to update the skuId to the parent component
      tryEmit()
    }

    // Throw an event to update the skuId to the parent component
    const tryEmit = () = > {
      // Trigger the change event to pass skU data out
      const selectedArr = getSelectedArr(props.goodsData.specs).filter(v= > v)
      if (selectedArr.length === props.goodsData.specs.length) {
        const skuIds = pathMap[selectedArr.join(spliter)]
        const sku = props.goodsData.skus.find(sku= > sku.id === skuIds[0])
        / / pass
        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'}}, {})// Restore the selected specifications according to the skuId
    initSelectedStatus(props.goodsData, props.skuId)
    // Generate a dictionary
    console.log(props.goodsData.skus)
    const pathMap = getPathMap(props.goodsData.skus)

    updateDisabledStatus(pathMap, props.goodsData.specs)

    return { clickSpecs }
  }
}
</script>
<style scoped lang="less">
.sku-state-mixin () {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  &.selected {
    border-color: @xtxColor;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed; }}.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;
    dt {
      width: 50px;
      color: # 999;
    }
    dd {
      flex: 1;
      color: # 666;
      > img {
        width: 50px;
        height: 50px;
        .sku-state-mixin(a); } >span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        .sku-state-mixin(a); }}}}</style>

Copy the code

5. Effect preview