Ant Design 基于dropdownRender属性对下拉菜单的封装

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组件

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值