Antd Select组件 DropdownRender扩展

先上效果图~

 

Antd Select组件 DropdownRender扩展_第1张图片

import React, {
  useState,
  useCallback,
  useMemo,
  useEffect,
  useRef,
} from 'react';
import {
  Select,
  SelectProps,
  Space,
  Checkbox,
  Input,
  Empty,
  Tooltip,
} from 'antd';
import { debounce, isArray, isEmpty, isFunction } from 'lodash-es';
import classnames from 'classnames';
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
import { useIntl } from 'umi';


type Key = string | number;

type OptionType = {
  label: string | React.ReactNode;
  value: Key;
};

type TypeOptMap = {
  [key: string]: OptionType;
};

type CustomTagProps = {
  label: React.ReactNode;
  value: any;
  disabled?: boolean;
  onClose?: (event?: React.MouseEvent) => void;
  closable?: boolean;
};

type PluralSelectProps = Omit<
  SelectProps,
  'onChange' | 'options' | 'value' | 'mode'
> & {
  onChange?: (value: Key[]) => void;
  options?: OptionType[];
  value?: Key[];
  showSearch?: boolean;
};

const transformMap = (params: OptionType[]) => {
  const result: { [key: string]: OptionType } = {};
  if (isEmpty(params) || !isArray(params)) return result;
  params.forEach((item) => {
    result[item.value] = item;
  });
  return result;
};

const labelVisible = (searchValue: string, label: string) => {
  return !searchValue || (searchValue && label.indexOf(searchValue) > -1);
};

const PluralSelect: React.FC = ({
  options,
  maxTagCount = 'responsive',
  value = [],
  onChange,
  placeholder,
  className,
  dropdownClassName,
  showSearch = true,
  ...props
}) => {
  const { formatMessage } = useIntl();
  const [currentValues, setCurrentValues] = useState(value);
  const [searchValue, setSearchValue] = useState('');
  const searchInputRef: any = useRef();
  const valueCacheRef = useRef();

  const optMap: TypeOptMap = useMemo(() => {
    return transformMap(options || []) || {};
  }, [options]);

  const allValues = useMemo(() => {
    return options?.map((opt) => opt?.value) || [];
  }, [options]);

  // 半选状态
  const indeterminate = useMemo((): boolean => {
    return (
      Boolean(currentValues?.length) &&
      isArray(options) &&
      currentValues?.length < options.length
    );
  }, [currentValues, options]);

  // 全选状态
  const checkedAll = useMemo((): boolean => {
    return Boolean(
      options?.length && currentValues?.length === options?.length,
    );
  }, [currentValues, options]);

  // 选择checkbox
  const handleChange = useCallback(
    (event: CheckboxChangeEvent) => {
      const {
        target: { value, checked },
      } = event;
      let mergedValues: Key[] = [];
      if (checked) {
        mergedValues = [...currentValues, value];
        setCurrentValues(mergedValues);
      } else {
        mergedValues = currentValues.filter(
          (each) => String(each) !== String(value),
        );
        setCurrentValues(mergedValues);
      }
      isFunction(onChange) && onChange?.(mergedValues);
    },
    [currentValues, onChange],
  );

  // 输入搜索条件
  const handleInputKeyword = useCallback(() => {
    const value = searchInputRef.current?.input?.value || '';
    setSearchValue(value);
  }, []);

  // 搜索(防抖)
  const debounceSearch = useMemo(() => debounce(handleInputKeyword, 200), [
    handleInputKeyword,
  ]);

  // 全选或者全不选
  const handleCheckAllChange = useCallback(
    (event: CheckboxChangeEvent) => {
      const {
        target: { checked },
      } = event;
      isFunction(onChange) && onChange?.(checked ? allValues : []);
      setCurrentValues(checked ? allValues : []);
    },
    [onChange, allValues],
  );

  // select 变化
  const handleSelectChange = useCallback(
    (values: Key[]) => {
      setCurrentValues(values);
      isFunction(onChange) && onChange?.(values);
    },
    [onChange],
  );

  useEffect(() => {
    // 当 value 发生变化时,更新 currentValues
    const currentValueStr = value ? JSON.stringify(value) : '';
    if (currentValueStr === valueCacheRef.current) return;
    valueCacheRef.current = currentValueStr;
    setCurrentValues(value);
  }, [value]);

  // 卸载时清空
  useEffect(() => {
    return () => {
      setSearchValue('');
    };
  }, []);

  const renderTitle = (title: string | React.ReactNode) => {
    if (!searchValue) {
      return {title};
    }

    const strTitle = String(title);
    const index = strTitle.indexOf(searchValue);
    const beforeStr = strTitle.substring(0, index);
    const afterStr = strTitle.substring(index + searchValue.length);
    // 高亮搜索关键字
    const currentTitle =
      index > -1 ? (
        <>
          {beforeStr}
          {searchValue}
          {afterStr}
        
      ) : (
        strTitle
      );

    return {currentTitle};
  };

  const CheckboxOptions = () => {
    if (isEmpty(options) || !isArray(options))
      return ;
    return (
      
        
          {formatMessage({ id: 'ACTION_SELECT_ALL' })}
        
        
          
            {isArray(options) &&
              options.map(
                ({ label, value }, idx) =>
                  labelVisible(searchValue, String(label)) && (
                    
                      {renderTitle(label)}
                    
                  ),
              )}
          
        
      
    );
  };

  const tagRender = (props: CustomTagProps) => {
    const { value } = props;
    const { label } = optMap[value];
    return (
      
        
          {label}
        
      
    );
  };

  const dropdownRender = () => (
    
{showSearch && ( )}
); return (