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