美文网首页
封装一个下拉选择Table(可分页、可搜索)DropdownTa

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

作者: importUIKit | 来源:发表于2021-11-30 17:05 被阅读0次

    实例演示如下:

    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<HTMLElement>
      ) => React.ReactNode;
      role?: string;
      showLessItems?: boolean;
      [key: string]: any;
    }
    
    export type DropdownTableProps<T, D> = {
      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<string, any>,
      D extends Record<string, any>
    >({
      columns,
      mode = "radio",
      placeholder = "",
      optionValueProp = "value",
      optionLabelProp = "label",
      searchPlaceholder = "",
      limit,
      onChange,
      dropdownStyle,
      defaultOptions,
      value,
      tableProps,
      onSearch,
    }: DropdownTableProps<T, D>) => {
      const ref = useRef<any>();
      const [thisSelectedRowKeys, setThisSelectedRowKeys] = useState<string[]>([]);
      const [selectedRowObjects, setSelectedRowObjects] = useState<
        { value: string; label: string }[]
      >([]);
      const [keyword, setKeyword] = useState<string>("");
      const [searchTag, setSearchTag] = useState<number>(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 (
        <>
          <Select
            ref={ref}
            placeholder={placeholder}
            showSearch={false}
            allowClear
            showArrow
            onChange={handleChange}
            style={{ width: "100%" }}
            options={selectedRowObjects}
            mode="multiple"
            onClear={() => {
              listenDataToCallBack([], []);
            }}
            // tagRender={tagRender}
            value={thisSelectedRowKeys}
            dropdownStyle={dropdownStyle}
            dropdownRender={() => {
              return (
                <div style={{ ...dropdownStyle, padding: 12 }}>
                  <Search
                    value={keyword}
                    placeholder={searchPlaceholder}
                    style={{
                      marginBottom: 12,
                    }}
                    allowClear
                    onSearch={() => {
                      setSearchTag(searchTag + 1);
                    }}
                    onChange={(e) => {
                      if (e.target.value === "") {
                        setKeyword("");
                        onSearch?.("");
                        setSearchTag(searchTag + 1);
                        return;
                      }
                      setKeyword(e.target.value);
                      onSearch?.(e.target.value);
                    }}
                    enterButton
                  />
                  <Table
                    {...tableProps}
                    onRow={(record) => ({
                      onClick: () => {
                        clickRow(record);
                      },
                    })}
                    size="small"
                    rowSelection={{ ...getRowSelection() }}
                    columns={columns}
                    rowKey={optionValueProp}
                  />
                </div>
              );
            }}
          />
        </>
      );
    };
    
    export default DropdownTable;
    
    

    使用实例demo代码

    import "./index.less";
    import DropdownTable from "./DropdownTable";
    import { useAntdTable } from "ahooks";
    import { Button, Form, message } from "antd";
    import { useState } from "react";
    
    const columns = [
      {
        title: "用户名",
        dataIndex: "id",
        key: "id",
      },
      {
        title: "姓名",
        dataIndex: "name",
        key: "name",
      },
    ];
    
    const names = ["张", "李", "陈", "荀", "诸葛", "牛", "刘"];
    const dataSource: { name: string; id: string }[] = [];
    for (let i = 0; i < 20; i++) {
      const index = Math.floor(Math.random() * names.length);
      dataSource.push({
        name: `${names[index]}${i + 1}`,
        id: `${i + 1}`,
      });
    }
    
    const getData = (current: number, pageSize: number, searchKey?: string) => {
      console.log(current, pageSize);
    
      return new Promise((resolve) => {
        setTimeout(() => {
          const start = (current - 1) * pageSize;
          const array: { name: string; id: string }[] = [];
    
          if (searchKey) {
            array.push(
              ...[...dataSource].filter((rs) => rs.name.indexOf(searchKey) !== -1)
            );
            if (array.length > 5) {
              resolve({
                total: array.length,
                list: [...array].splice(start, pageSize),
              });
            } else {
              resolve({
                total: array.length,
                list: array,
              });
            }
          } else {
            array.push(...[...dataSource].splice(start, pageSize));
            resolve({
              total: 20,
              list: array,
            });
          }
        }, 1000);
      });
    };
    
    const CustomComponentPage = () => {
      const [form] = Form.useForm();
    
      const [searchKey, setSearchKey] = useState("");
    
      const { tableProps } = useAntdTable<
        {
          total: number;
          list: { name: string; id: string }[];
        },
        { name: string; id: string },
        { name: string; id: string }
      >(
        (rs) => {
          const { current, pageSize } = rs;
          console.log("res", rs);
    
          // console.log("current:", current, pageSize);
          return getData(current, pageSize, searchKey);
        },
        {
          refreshDeps: [searchKey],
          defaultPageSize: 5,
          formatResult: (res) => {
            return res;
          },
        }
      );
      console.log(tableProps.pagination);
    
      return (
        <div className="custom-component-page">
          <div className="custom-component-page__demo">
            普通单选用法:
            <DropdownTable
              columns={columns}
              mode="radio"
              placeholder="点击选择用户"
              searchPlaceholder="请输入用户名或者姓名搜索"
              optionValueProp="id"
              optionLabelProp="name"
              onChange={(selectedKeys) => {
                console.log("selectedKeys:", selectedKeys);
              }}
              tableProps={{ ...(tableProps as any) }}
              dropdownStyle={{ minWidth: 360 }} // 设置下拉表最小宽度,此处设置width无效必须设置minWidth
            />
          </div>
          <div className="custom-component-page__demo">
            普通多选用法:
            <DropdownTable
              columns={columns}
              mode="checkbox"
              placeholder="点击选择用户"
              searchPlaceholder="请输入用户名或者姓名搜索"
              optionValueProp="id"
              optionLabelProp="name"
              onChange={(selectedKeys) => {
                console.log("selectedKeys:", selectedKeys);
              }}
              tableProps={{ ...(tableProps as any) }}
              dropdownStyle={{ minWidth: 360 }} // 设置下拉表最小宽度,此处设置width无效必须设置minWidth
            />
          </div>
          <div className="custom-component-page__demo">
            <Form
              onFinish={(values) => {
                console.log("values:", values);
                message.info(`获取到表单数据${JSON.stringify(values)}`);
              }}
              form={form}
              initialValues={{ table: ["3", "4"] }}
            >
              <Form.Item label="form设置初始值" name="table">
                <DropdownTable
                  columns={columns}
                  defaultOptions={[...dataSource]
                    .map((rs) => {
                      return { value: rs.id, label: rs.name };
                    })
                    .splice(2, 2)}
                  mode="checkbox"
                  placeholder="点击选择用户"
                  searchPlaceholder="请输入用户名或者姓名搜索"
                  optionValueProp="id"
                  optionLabelProp="name"
                  onChange={(selectedKeys) => {
                    console.log("selectedKeys:", selectedKeys);
                  }}
                  onSearch={(keyword) => {
                    setSearchKey(keyword);
                  }}
                  tableProps={{ ...(tableProps as any) }}
                  dropdownStyle={{ minWidth: 360 }} // 设置下拉表最小宽度,此处设置width无效必须设置minWidth
                />
              </Form.Item>
            </Form>
            <Button
              onClick={() => {
                form.submit();
              }}
            >
              提交
            </Button>
          </div>
        </div>
      );
    };
    export default CustomComponentPage;
    
    

    相关文章

      网友评论

          本文标题:封装一个下拉选择Table(可分页、可搜索)DropdownTa

          本文链接:https://www.haomeiwen.com/subject/vsjlxrtx.html