项目地址:https://github.com/hackftz/sku-implement/tree/master
前言:最近正好有sku商品的需求,看了一些思想后,自己实现了,如果路过的觉得写得还行的,麻烦点个赞哦,感谢!
注:代码原创,copy请标注来源哦!
效果如下:
- 代码部分
- jsx文件
import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
// 所有行以及选项
const rows = [
['红色', '黑色'],
['皮革', '纺织布'],
['中国', '美国', '俄罗斯'],
];
// 所有可卖商品
const products = [
['红色', '皮革', '中国'],
['红色', '纺织布', '美国'],
['黑色', '纺织布', '中国'],
['黑色', '纺织布', '俄罗斯'],
];
// 用户选项
let initialSelectTypes = [];
for (let i = 0; i < rows.length; i++) {
initialSelectTypes[i] = '';
}
// 用户已经选择的项
const [selectTypes, setSelectTypes] = useState(initialSelectTypes);
// 扁平化所有可选项
const flatRows = rows.flat();
// 所有可选项的长度
const flagRowsLength = flatRows.length;
// 获得矩阵
const getMatrix = () => {
// 创建矩阵 不能使用引用类型去创建矩阵 修改时会修改一列!!
// const matrix = new Array(flagRowsLength).fill(new Array(flagRowsLength).fill(0));
// 创建矩阵
let matrix = [];
for (let i = 0; i < flagRowsLength; i++) {
matrix[i] = [];
for (let j = 0; j < flagRowsLength; j++) {
matrix[i][j] = 0;
}
}
// 利用可卖商品去填充矩阵
for (let i = 0; i < products.length; i++) {
const product = products[i];
for (let j = 0; j < product.length; j++) {
// 首位商品名
const prod = product[j];
// 横纵方向的首位坐标
const flatRowsIndexOfProd = flatRows.indexOf(prod);
// 除去首位商品名 剩余的商品名
const otherProds = product.filter((item) => item !== prod);
for (let i = 0; i < otherProds.length; i++) {
const otherProd = otherProds[i];
// 这里是将横/纵向命中的交点都填充
const flatRowsIndexOfOtherProd = flatRows.indexOf(otherProd);
matrix[flatRowsIndexOfProd][flatRowsIndexOfOtherProd] = 1;
}
}
}
return matrix;
};
const matrix = getMatrix();
console.table(matrix);
// 获得可选项的type数组集合
function getCanSelectTypes(selectTypes, products) {
let canSelectTypes = [];
// 初始值
if (selectTypes.every((item) => item === '')) {
// 将products扁平化 再过滤重复项
const flatProducts = products.flat();
canSelectTypes = [...new Set(flatProducts)];
} else {
// 重要!!!!
// 首先把selectTypes挤挤 有值的往前推移 使用sort
const forwardSelectTypes = selectTypes.slice().sort((a, b) => {
if (a && b) {
return 1;
}
if (a && !b) {
return -1;
}
if (!a && b) {
return 1;
}
if (!a && !b) {
return 0;
}
});
// forwardSelectTypes转成矩阵
const forwardMatrix = forwardSelectTypes.filter(Boolean).map((item) => {
const index = flatRows.indexOf(item);
return matrix[index];
});
// 初始值用于累加计算
let initialCanSelectTypes = [];
for (let i = 0; i < flatRows.length; i++) {
initialCanSelectTypes[i] = 1;
}
canSelectTypes = forwardMatrix
.reduce((acc, cur) => {
for (let i = 0; i < acc.length; i++) {
acc[i] = acc[i] & cur[i];
}
return acc;
}, initialCanSelectTypes)
.map((item, index) => {
if (item === 1) {
return flatRows[index];
}
})
.filter(Boolean);
}
return canSelectTypes;
}
// 计算出的可选项
const canSelectTypes = getCanSelectTypes(selectTypes, products);
// 点击选项
const clickType = (index, type) => {
// let newSelectTypes = selectTypes.slice();
let newSelectTypes = JSON.parse(JSON.stringify(selectTypes));
if (newSelectTypes[index] === '') {
newSelectTypes[index] = type;
} else {
if (newSelectTypes[index] === type) {
// 引用问题 直接移除index元素
// newSelectTypes[index] === '';
newSelectTypes.splice(index, 1, '');
} else {
newSelectTypes[index] = type;
}
}
console.log('%c newSelectTypes after ⧭', 'color: #d0bfff', newSelectTypes);
setSelectTypes(newSelectTypes);
};
return (
<div className="App">
{rows.map((types, index) => {
return (
<div className="row" key={types}>
{types.map((type) => {
// 是否可选
const isSelect = selectTypes.includes(type);
// 不用disabled的选项
const noDisabled = canSelectTypes.includes(type);
return (
<button className={'type ' + `${isSelect ? 'select ' : ''}` + `${!isSelect ? (noDisabled ? '' : 'disabled') : ''}`} key={type} onClick={() => clickType(index, type)}>
{type}
</button>
);
})}
</div>
);
})}
</div>
);
}
export default App;
复制代码
- css
.App {
padding: 40px;
}
.row {
height: 50px;
line-height: 50px;
min-width: 400px;
}
.type {
padding: 4px 10px;
display: inline-block;
margin-right: 15px;
background-color: white;
border: 1px solid gray;;
color: gray;
cursor: pointer;
}
.type.select {
color: red;
border: 1px solid red;
box-shadow:none;
}
.type.disabled {
color: lightgray;
border: 1px solid lightgray;
box-shadow:none;
pointer-events: none;
}
复制代码
- 实现逻辑
-
我们需要有这么几个变量
用户已经选择的项——selectTypes
计算出的可选项——canSelectTypes
是否可选——isSelect
不用disabled——noDisabled -
我们需要有这么几个常量
扁平化所有可选项——flatRows
所有可选项的长度——flagRowsLength -
我们需要有这么几个方法
getMatrix——获得矩阵,用来比较
获得可选项的type数组集合——getCanSelectTypes -
关键点!首先我们要利用初始数据生成矩阵,矩阵是网络结构,可以清晰可以看到哪些选项之间可以关联,形成最终的商品选项。
这就是我要生成的根据可售卖的商品形成的矩阵。
- 细节部分
– 创建矩阵 不能使用引用类型去创建矩阵 修改时会修改一列!!
– 横/纵向都要填充完整,虽然最后我们还是可以横向去判断
– 将products扁平化 再过滤重复项
– 每次用户点击的选项,会存在空选项,此时需要把selectTypes有值的部分往前挤挤,有值的往前推移,使用sort
– 累加每个数组,使用对每个数组元素进行位运算
– 数组引用问题,采用splice移除旧项
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END