封装一个下拉选择Table(可分页、可搜索)DropdownTable

实例演示如下:

demo.gif

提示:项目依赖于antd
代码直接贴了,可以直接使用
github代码地址

import { Select, Table, Input, message, TablePaginationConfig } from "antd";
import { useRef, useState, useEffect } from "react";
import * as React from "react";
// import type { PaginationConfig } from "antd";

const { Search } = Input;

export interface PaginationConfig {
  total?: number;
  defaultCurrent?: number;
  disabled?: boolean;
  current?: number;
  defaultPageSize?: number;
  pageSize?: number;
  onChange?: (page: number, pageSize?: number) => void;
  hideOnSinglePage?: boolean;
  showSizeChanger?: boolean;
  pageSizeOptions?: string[];
  onShowSizeChange?: (current: number, size: number) => void;
  showQuickJumper?:
    | boolean
    | {
        goButton?: React.ReactNode;
      };
  showTotal?: (total: number, range: [number, number]) => React.ReactNode;
  simple?: boolean;
  style?: React.CSSProperties;
  locale?: Object;
  className?: string;
  prefixCls?: string;
  selectPrefixCls?: string;
  itemRender?: (
    page: number,
    type: "page" | "prev" | "next" | "jump-prev" | "jump-next",
    originalElement: React.ReactElement
  ) => React.ReactNode;
  role?: string;
  showLessItems?: boolean;
  [key: string]: any;
}

export type DropdownTableProps = {
  columns?: T[]; // table列配置
  mode?: "radio" | "checkbox"; // 单选 多选
  placeholder?: string | ""; // placeholder
  optionValueProp?: string | "value";
  optionLabelProp?: string | "label";
  searchPlaceholder?: string | ""; // 搜索框的searchPlaceholder
  limit?: number | undefined; //限制最多选择几个
  onChange?: (value: string[]) => void; // 选择值改变后
  dropdownStyle?: React.CSSProperties; // 下拉框样式
  defaultOptions?: { value: string; label: string }[]; // 设置默认选项,在需要回填时使用
  value?: string[] | string; // 设置值
  tableProps?: {
    dataSource: D[];
    loading: boolean;
    onChange?: (
      pagination?: TablePaginationConfig,
      filters?: any,
      sorter?: any
    ) => void;
    pagination?: TablePaginationConfig | false;
    [key: string]: any;
  }; // table的参数
  onSearch?: (keyword: string) => void;
};

const DropdownTable = <
  T extends Record,
  D extends Record
>({
  columns,
  mode = "radio",
  placeholder = "",
  optionValueProp = "value",
  optionLabelProp = "label",
  searchPlaceholder = "",
  limit,
  onChange,
  dropdownStyle,
  defaultOptions,
  value,
  tableProps,
  onSearch,
}: DropdownTableProps) => {
  const ref = useRef();
  const [thisSelectedRowKeys, setThisSelectedRowKeys] = useState([]);
  const [selectedRowObjects, setSelectedRowObjects] = useState<
    { value: string; label: string }[]
  >([]);
  const [keyword, setKeyword] = useState("");
  const [searchTag, setSearchTag] = useState(1);

  useEffect(() => {
    if (typeof value === "string") {
      setThisSelectedRowKeys([value]);
      return;
    }
    setThisSelectedRowKeys(value || []);
  }, [value]);

  useEffect(() => {
    setSelectedRowObjects((old) => {
      if (defaultOptions) {
        return [...old, ...defaultOptions];
      }
      return [...old];
    });
  }, [defaultOptions]);

  const listenDataToCallBack = (
    keys: string[],
    objects: { value: string; label: string }[]
  ) => {
    setThisSelectedRowKeys(keys);
    setSelectedRowObjects(objects);
    onChange?.(keys);
  };

  const rowChangeBackArray = (key: string) => {
    if (mode !== "checkbox") {
      return [key];
    }
    const index = thisSelectedRowKeys.indexOf(key);
    const newArray = [...thisSelectedRowKeys];
    if (index === -1) {
      newArray.push(key);
    } else {
      newArray.splice(index, 1);
    }
    return newArray;
  };

  const rowObjectChangeBackArray = (valueString: string, label: string) => {
    if (mode !== "checkbox") {
      return [{ value: valueString, label }];
    }
    const index = thisSelectedRowKeys.indexOf(valueString);
    const newArray = [...selectedRowObjects];
    if (index === -1) {
      newArray.push({ value: valueString, label });
    } else {
      newArray.splice(index, 1);
    }
    return newArray;
  };

  const clickRow = (record: D) => {
    if (mode === "radio") {
      ref.current.selectRef.current.blur();
    }

    const key = record[optionValueProp];
    const newArray = rowChangeBackArray(key);
    const newObjectArray = rowObjectChangeBackArray(
      key,
      record[optionLabelProp]
    );

    if (limit && newArray.length > limit) {
      message.info(`最多只能选择${limit}个`);
      return;
    }
    listenDataToCallBack(newArray, newObjectArray);
  };

  const onSelectAllTable = (
    changeRows: any[],
    selected: boolean,
    selectData: any[],
    id: string,
    isRow: boolean
  ): string[] => {
    const selectCode = [...selectData];
    if (selected) {
      for (let index = 0; index < changeRows.length; index++) {
        const element = changeRows[index];
        if (isRow) {
          selectCode.push(element);
        } else {
          selectCode.push(element[id]);
        }
      }
      return selectCode;
    }
    const result = [];
    for (let i = 0; i < selectCode.length; i++) {
      let k = 0;
      const item = isRow ? selectCode[i][id] : selectCode[i];
      for (let j = 0; j < changeRows.length; j++) {
        if (item !== changeRows[j][id]) {
          k += 1;
          if (k === changeRows.length) {
            result.push(selectCode[i]);
          }
        }
      }
    }
    return result;
  };

  // rowSelection
  const rowSelection = {
    type: mode,
    selectedRowKeys: thisSelectedRowKeys,
    onSelect: (
      record: T,
      selected: boolean,
      selectedRows: string[],
      nativeEvent: React.TouchEvent
    ) => {
      nativeEvent.stopPropagation();
      const key = record[optionValueProp];
      const label = record[optionLabelProp];
      const newArray = rowChangeBackArray(key);
      const newObjectArray = rowObjectChangeBackArray(key, label);
      if (limit && newArray.length > limit) {
        message.info(`最多只能选择${limit}个`);
        return;
      }
      listenDataToCallBack(newArray, newObjectArray);
    },
    onSelectAll: (
      selected: boolean,
      selectedRows: string[],
      changeRows: any[]
    ) => {
      const newKeys = onSelectAllTable(
        changeRows,
        selected,
        thisSelectedRowKeys,
        optionValueProp,
        false
      );
      if (limit && newKeys.length > limit) {
        message.info(`最多只能选择${limit}个`);
        return;
      }
      const newArray = [...selectedRowObjects];
      if (selected) {
        for (let i = 0; i < changeRows.length; i++) {
          const item = changeRows[i];
          const valueString = item[optionValueProp];
          if (thisSelectedRowKeys.indexOf(valueString) === -1) {
            const label = item[optionLabelProp];
            newArray.push({ label, value: valueString });
          }
        }
      } else {
        for (let i = 0; i < changeRows.length; i++) {
          const item = changeRows[i];
          const valueString = item[optionValueProp];
          const index = thisSelectedRowKeys.indexOf(valueString);
          if (index !== -1) {
            newArray.splice(index, 1);
          }
        }
      }
      listenDataToCallBack(newKeys, newArray);
    },
  };

  const getRowSelection = (): any => {
    if (mode === "radio") {
      return {
        columnWidth: 0,
        type: mode,
        renderCell: () => "",
        selectedRowKeys: thisSelectedRowKeys,
      };
    }
    return rowSelection;
  };

  const handleChange = (v: string[]) => {
    setThisSelectedRowKeys(v);
    onChange?.(v);
  };

  return (
    <>