Without further ado, let’s see what we need to do, okay

There are a lot of questions about the “choice of multiple specifications of commodities” on the mining or various websites, such as all the permutations and combinations. When I wrote the requirements, I also found a lot of answers on the Internet. The ideas are very good, but they are not close to the business scene. (Some are close to the business scenario, but don’t fit the business scenario I need).

In a real business scenario, all the supported specifications need to be presented for the first rendering, and the unselected specifications need to be gray. We need to find out the remaining optional and optional specifications according to the specifications selected by the user each time. The effect is as follows:

What is the sku

The word “SKU” is often heard. What is “SKU”? Colloquial speaking is id card, product id card. Every one of us has an ID number. Similarly, every product in the e-commerce system also has an ID number, which is SKU. Stock Keeping Unit (SKU for short) is defined as the smallest available unit for keeping stock control. For example, in textiles, an SKU usually represents a size, color, or style. STOCK KEEP UNIT. This is a method for customers to number and classify goods after they take them to the warehouse

Information contained in the SKU

1. The product items

This can be understood in conjunction with the above explanation of individual, SKU and variety. That is, as long as the properties are different, they are different skUs. It can be said that this is analyzed and understood from the perspective of SKU as a product. There are many kinds of attributes, that is, the same product is no longer the same as long as there are different ways for people to save, manage, sell, and service it.

2. The coding

This concept is based on information systems and cargo code management, as described in “Items”, different skUs have different codes. In this way, we can analyze the inventory and sales status according to different SKU data. But the product here as “items” said, is not a general product concept, but a very precise product concept.

3. Unit

It’s basically management-based, and the name is a product of digital management. But what’s the difference between the units here and our usual units? Look at the difference in the package unit of the product, the SKU is different – you know.

The role of product SKU in e-commerce warehouse

1. From the perspective of goods

SKU refers to a single item whose attributes have been determined. That is to say, the same goods should be defined as different SKUs as long as people have different ways of storing, managing, selling and serving them

2. From the perspective of business management

The SKU also contains information about the package unit. For example, SKU#123 is a 330ml bottle of stout (in bottles); SKU#456 refers to 330ml bottles of stout (in the unit of lift, 6 bottles for 1 lift); SKU#789 is a 330ml bottle of stout (in cases, 24 bottles to a case). As the units of measurement (packaging units) are different, for the needs of business management, they should be classified into different SKUs. Of course, there can be an algorithm for unit conversion to assist in SKU conversion.

3. From the perspective of information systems and cargo coding

The SKU is just a code. Different goods (commodity name) have different codes (SKU#). This code is related to the defined goods one by one, so that we can record and analyze the inventory and sales situation according to the data of different SKU.

Generally speaking, skUS are customized and used within a system (such as a company or factory). Redefinition or SKU conversion is required across the system.

The business scenario

The author has not been in contact with commodity multi-specification business, as well as their own small program for this aspect, contact is not much, code quality is insufficient, do not mind.

Using the technology stack

Mainly for the taro+react+hook version of the small program, because the previous development is using JS +class, so the hook is used less (although I also want to use hook)

Let’s focus on parsing the SKU selector

The main code

1. The format of the sku
All SKU specifications for the SPU (spu_spec_ALL_values)

Indicates that there are two specifications. Name indicates the name of the specification and VALUES indicates the type of the specification

All skU product specifications (SKU_SETTing_Specs) currently on the shelf

Enter the SkU Specification selected by default on the Product Details page.

Code 2.

I mainly present the core code of payModal here, but not the code of the commodity detail page

Main idea: in the product details page default selected specifications (if there is no SKU specifications, or SKU inventory is 0, then ash) -> switch to click different specifications -> access to the selected SKU information, add to the shopping cart or buy immediately, or view the SKU details

Since the applet uses mobx and the class adds an observer() package, the interface data obtained for object or array needs to be resolved using mobx's built-in toJS method

  • The core code
const [skuInfo, setSkuInfo] = useState({}); Const [skuHold, setSkuHold] = useState(0); UseEffect (() => {if (toJS(specification) && toJS(specification).length) { setActiveSizeValue(formatSpecification(toJS(specification))); setDrawOptions(); } setSkuHold(stock); SetSkuInfo ({skuStock: stock, // stock skuImage: sku_img, // sku image skuOriPrice: ori_price, // old skuIsShowLinePrice: SkuShowPrice: show_price // the current skU price}); }, [spu_spec_all_values, specification, sku_setting_specs, ori_price, Is_show_line_price]) /** * Core code * @param selectedSpec Selected array * @param currentSpecName Name of the currently clicked specification * @param Value */ const skuCore = (selectedSpec, currentSpecName, value, selectedSpec) typeClick) => { const spec1 = typeClick ! == 'click' && value ? value : spec; const skus = toJS(sku_setting_specs); Object.keys(spec1).forEach((sk) => { if (sk ! Const specSelectedValue = spec1[object.keys (spec1).find((_sk) => sk === _sk)) const specSelectedValue = spec1[object.keys (spec1).find((_sk) => sk === _sk) | | ']. Find (= > sv (sv). Select) spec1 [sk] forEach ((sv) = > {/ / judgement of the specifications of the current value is chosen, if it is selected, do not have to decide whether you can click on the skip cycle if (! Select) {const _ssTemp = [...selectedSpec] // If (!! currentSpecSelectedValue) { const sIndex = _ssTemp.findIndex((_sv) => _sv === `${sk}:${currentSpecSelectedValue.value}`)  _ssTemp.splice(sIndex, 1)} _ssTemp. Push (` ${sk} : ${sv. Value} `) const _tmpPath = [] / / find the contains the path of all sku skus. ForEach ((sku) = > {/ / Const querSkus = _sstemp. filter((_sst) => {const querySpec = objTransformArr(sku.specs).some(p => { return p === _sst }) return querySpec }) const i = querSkus.length if (i === Const hasHoldPath = _tmpPath. Find ((p) => p.stock) // So if you want to have a skU that is not zero then you can say let isNotEmpty = hasHoldPath? hasHoldPath.stock : 0 sv.disable = ! IsNotEmpty}})}}) Inventory, etc. The if (judgeCanAdd (skus)) {const sku_info = getSkuInfoByKey (spec1) | | {}; / / sku information const hold = sku_info. Stock | | 0; // Get the inventory of the SKU setSkuHold(hold); setSkuInfo({ skuStock: sku_info.stock, skuImage: sku_info.image, skuOriPrice: sku_info.ori_price, skuIsShowLinePrice: sku_info.is_show_line_price, skuShowPrice: sku_info.show_price }); }}Copy the code
  • Each specification is initialized through skUS
Const formatSpecification = (specification) => {if (! Array.isarray (Specification)) {return []} return specification. Map (I => '${i.key. :${i.key.}')} // activeSizeValue format [" size :S", "ml: 100mL "] const [activeSizeValue, setActiveSizeValue] = useState([]); {value: "S", disable: false, select: true}} const [spec, setSpec] = useState({}); UseEffect (() => {// If multiple specifications exist, run skus to initialize each specification if (toJS(specification) && toJS(specification).length) {// Initialize the default specification to activeSizeValue setActiveSizeValue(formatSpecification(toJS(specification))); setDrawOptions(); }}, [specification]) /** * Const setDrawOptions = () => {// All skU specifications of the SPU const SkUS = toJS(spu_spec_all_values) || []; let _tags = {}; // Temporary store const _tempTagsStrArray = {}; / / temporary storage const defaultValue = toJS (specification) | | []; Name const nameKeys = defaultValue.map(item => item.key); Skus.foreach (s => {s.vues. ForEach (p => {// the key does not exist, initialize the object if (! _tags[s.name]) { _tags[s.name] = [] _tempTagsStrArray[s.name] = []; } // if (! _tempTagsStrArray[s.name].includes(p)) { _tempTagsStrArray[s.name].push(p); Const result = defaultvalue. some(function (item) {if (item.val === p && item.key === s.name) {return true; } }) _tags[s.name].push({ value: p, disable: true, select: result ? true : false }) } }) }); setSpecDisable(_tags) }Copy the code
  • Whether to set specifications is optional
Const [num, setNum] = useState(0) * if the path with the combination of the property without the attribute cannot click * / const setSpecDisable = (tags, isReset) = > {const skus = toJS (sku_setting_specs) | | []; const defaultValue = toJS(specification) || []; Object.keys(tags).forEach((sk) => { tags[sk].forEach((sv) => { const currentSpec = `${sk}:${sv.value}`; Find ((sku) => {for (let key in sku. Specs) {const queryProperty = `${key}:${sku.specs[key]}` === currentSpec; If (queryProperty) {return queryProperty && sku. Stock}}}) sv.disable =! querySku }) }) setSpec({ ... Tags}) // activeSizeValue is not empty if (! Const a = formatSpecification(defaultValue); const a = formatSpecification(defaultValue); // The first time you enter the render interface data, After is click data / / num > (defaultValue. Length - 1) the length of the default specification const b = activeSizeValue. Length | | num > (defaultValue. Length - 1) ? activeSizeValue : a; defaultValue.forEach(item => { if (b.length) { skuCore(b, item.key, tags); setNum(num + 1) } }) } }Copy the code
  • Spec click Event
/** * k - specifications name, eg. Size * currentSpectValue - specifications value, eg. */ const onPressSpecOption = (k, currentValue) => {let isCancel = false; / / found in all the spec in the corresponding attribute const currentSpects = spec [Object. The keys (spec). Find ((sk) = > sk = = = k) | | '] | | []; / / on a selected attribute const prevSelectedSpectValue = currentSpects. Find ((cspec) = > cspec. Select) | | {} / / set the value of a selected before is not selected prevSelectedSpectValue.select = false; If (prevSelectedSpectValue === currentSpectValue) {isCancel = true} else {// Set the current state to currentSpectvalue. select = true} Const selectedSpec = object.keys (spec).filter((sk) => spec[sk].find((sv) => sv.select)) .reduce((prev, currentSpecKey) => { return [...prev, ` ${currentSpecKey} : ${spec [currentSpecKey] find ((__v) = > __v. Select), value}} `], []) if (isCancel) {/ / if it is cancelled and all not checked if (! Selectedspec.length) {// setSpecDisable(spec, If (selectedSpec. Length) {skuCore(selectedSpec, k, ", 'click'); } // assign the selectedSpec to activeSizeValue setActiveSizeValue(selectedSpec); setSpec({ ... spec }); }Copy the code
  • Determine if you can purchase or add to your cart
Const [canFlag, setCanFlag] = useState(false); Const judgeCanAdd = (skus = []) => {const judgecanks = object.keys (spec); Filter ((sk) => spec[sk].some((sv) => sv.select)).length // Compare the selected length with the required one. Let _cf = s === = sks.length if (! skus || ! skus.length) { _cf = false } if (skus && skus.length === 1 && ! Object.keys(skus[0].specs).length && skus[0].stock <= 0) { _cf = false } setCanFlag(_cf) return _cf }Copy the code
  • Return specification information
/** * @param _spec spec attribute * returns all information */ const getSkuInfoByKey = (_spec) => {// Selected spec: [{name: spec name, value: spec content}] const selectedSpec = {}; Object.keys(_spec).forEach((k) => { const selectedValue = _spec[k].find((sv) => sv.select); SelectedSpec [k] = selectedValue.value}}) const skus =. SelectedSpec [k] = selectedValue toJS(sku_setting_specs) || []; Const querySku = skus.find((sku) => {// compare two arrays to find two skUs that do not exist isObjShallowEqual(selectedSpec, sku.specs) return diffSkus }) || {}; return querySku; }Copy the code

conclusion

Because the code is constantly being revised and changed, all the redundant and poor quality code will be optimized later, please understand (because of business needs, time is too tight, make do with the function first, later in the optimization).

If you found this article useful to you, please give it a like!!

You can also scan the code to enter the small program to view, scan the following two-dimensional code 👇