import React, { useEffect, useState } from "react";
import { Select ,Checkbox,Menu } from "antd";
import _ from 'lodash';
import { nanoid } from "nanoid";
const MemuSelectSimple = props => {
const {data, dataOptions,dataOptionsMap,onChange} = props
const [options,setOptions] = useState([])
// 用于初始化数据勾选状态
const optionsChange = (data,options) => {
const tempOptions = _.cloneDeep(options)
const resOptions = tempOptions.map(item=>{
if(data.includes(item.value)){
item.checked = true
}
let count = 0
item.children?.map(subItem=>{
if(data.includes(subItem.value)){
subItem.checked= true
count ++
}
return subItem
})
if(item.children && item.children.length){
if(!count){
item.checked=false
item.indeterminate=false
}else if (item.children.length===count){
item.checked=true
item.indeterminate=false
}else{
item.checked=false
item.indeterminate=true
}
}
return item
})
return resOptions
}
useEffect(()=>{
const tempOptions = optionsChange(data,dataOptions)
setOptions(tempOptions)
},[])
useEffect(()=>{
console.log('data',data);
},[data])
const handleChange = option => {
// 全部清空 []
if(!option.length){
onChange([])
setOptions(dataOptions)
return
}
// 部分清空
const optionsMapKeys = Object.keys(dataOptionsMap).map(item=>+item)
const optionsMapValues = Object.values(dataOptionsMap)
let optionsValue = []
optionsMapValues.forEach((item,index)=>{
if(option.includes(item)){
optionsValue.push(optionsMapKeys[index])
}
})
onChange(optionsValue)
// 设置勾选状态
// optionsValue
const tempOptions = _.cloneDeep(dataOptions)
const resOptions = tempOptions.map(item=>{
if(optionsValue.includes(item.value)){
item.checked = true
}
let count = 0
item.children?.map(subItem=>{
if(optionsValue.includes(subItem.value)){
subItem.checked= true
count ++
}
return subItem
})
if(item.children && item.children.length){
if(!count){
item.checked=false
item.indeterminate=false
}else if (item.children.length===count){
item.checked=true
item.indeterminate=false
}else{
item.checked=false
item.indeterminate=true
}
}
return item
})
setOptions(resOptions)
}
const onItemChange =(e, option) => {
const checked = e.target.checked
const tempOptions = options.concat()
const selectedItems = []
const resOptions = tempOptions.map(item=>{
if(item.value===option.value){
if(item.children && item.children.length){
item.children = item.children?.map(subitem=>{
selectedItems.push(subitem.value)
subitem.checked = checked
return subitem
})
item.checked = checked
item.indeterminate = false
} else {
selectedItems.push(item.value)
item.checked = checked
}
}
return item
})
setOptions(resOptions)
if(checked){
const tempData = [...new Set(data.concat(selectedItems))]
onChange(tempData)
}else{
const tempData = data.filter(item=>!selectedItems.includes(item))
onChange(tempData)
}
}
const onSubItemChange = (e,item,parentIndex,subIndex) => {
const checked = e.target.checked
const tempOptions = options.concat()
tempOptions[parentIndex].children[subIndex].checked = checked
let checkedCount = 0
tempOptions[parentIndex].children.forEach(item=>{
if(item.checked){
checkedCount++
}
})
if(tempOptions[parentIndex].children.length===checkedCount){
// 全部选中
tempOptions[parentIndex].checked = true
tempOptions[parentIndex].indeterminate = false
}else if(!checkedCount) {
// 全部未选中
tempOptions[parentIndex].checked = false
tempOptions[parentIndex].indeterminate = false
}else{
// 部分选中
tempOptions[parentIndex].checked = false
tempOptions[parentIndex].indeterminate = true
}
setOptions(tempOptions)
if(checked){
const tempData = data.concat()
tempData.push(item.value)
onChange([...new Set(tempData)])
} else {
console.log(item.value,data);
const tempData = data.filter(option=>option!==item.value)
onChange(tempData)
}
}
const selectProps = {
mode: 'multiple',
placeholder:'请选择',
allowClear:true,
options,
value:data.map(item=>dataOptionsMap[item]),
onChange:handleChange,
style:{
width:300
},
showSearch: false,
dropdownRender:()=>{
return <>
<Menu mode='inline'>
{
options.map((item,index) => {
if(item.children && item.children.length){
return <Menu.SubMenu key={nanoid() + index} title={<Checkbox checked={item.checked} indeterminate={item.indeterminate} onClick={e=>onItemChange(e,item)}>{item.label}</Checkbox>}>
{
item.children?.map((subItem,subIndex)=>(
<Menu.Item key={nanoid() + subIndex}>
<Checkbox checked={subItem.checked} onClick={e=>onSubItemChange(e,subItem,index,subIndex)}>{subItem.label}</Checkbox>
</Menu.Item>
))
}
</Menu.SubMenu>
}
return <Menu.Item key={nanoid() + index} >
<Checkbox checked={item.checked} onClick={e=>onItemChange(e,item)}>{item.label}</Checkbox>
</Menu.Item>
})
}
</Menu>
</>
}
}
return (
<div style={{margin:'30px'}}>
<Select {...selectProps}></Select>
</div>
);
};
export default MemuSelectSimple;
使用案例
import React, { useEffect, useState } from "react";
import MemuSelectSimple from "./pages/memuSelectSimple/memuSelectSimple";
const Demo = () => {
const [data, setData] = useState([1, 24, 5, 6]);
const options = [
{
label: "奔驰S450",
value: 1,
checked: false,
children: [],
},
{
label: "宝马",
value: "BMW",
indeterminate: false,
checked: false,
children: [
{
label: "宝马3系",
value: 2,
checked: false,
},
{
label: "宝马5系",
value: 3,
checked: false,
},
{
label: "宝马7系",
value: 4,
checked: false,
},
],
},
{
label: "奥迪",
value: "AODI",
indeterminate: false,
checked: false,
children: [
{
label: "奥迪Q5",
value: 5,
checked: false,
},
{
label: "奥迪A7",
value: 6,
checked: false,
},
],
},
];
const [optionsMap,setOptionsMap] = useState({})
const handleDataChange = (val) => {
console.log("data change", data);
setData(val);
};
// 处理扁平化树结构
const setTreeToFlat = (arr, flatKey = "children") => {
let res = [];
arr.forEach((item) => {
let obj = {};
for (const key in item) {
if (key !== flatKey) {
obj[key] = item[key];
}
}
res.push(obj);
if (
item[flatKey] &&
Array.isArray(item[flatKey]) &&
item[flatKey].length
)
res.push(...setTreeToFlat(item[flatKey]));
});
return res;
};
useEffect(() => {
// 需要将数据过滤 剩下的都是可被支配的数据的值
const flatArray = setTreeToFlat(options).filter(
(item) => typeof item.value === "number"
);
const treeDataMap = {};
flatArray.forEach((item, index) => {
treeDataMap[item.value] = flatArray[index].label;
});
setOptionsMap(treeDataMap)
}, []);
return (
<div>
<MemuSelectSimple
{...{
data,
dataOptions: options,
dataOptionsMap: optionsMap,
onChange: handleDataChange,
}}
/>
</div>
);
};
export default Demo;
ps: 由于数据结构的不确定性, 所以映射关系需在处理之后再传给select组件