手动实现antd pro中的ProTable

1,156 阅读2分钟

组件按照功能拆分为三个组件

  • TableFilter组件
  • ToolBar组件
  • antd中的table组件

TableFilter主要实现的功能为根据columns参数,来动态配置需要作为条件筛选的参数,从而实现对表格数据的多重筛选功能,以及点击查询和重置按钮的功能

image.png

ToolBar实现的功能为配置表格名称表格按钮工具栏等相关

image.png

ProTable组件

const {
    columns,
    disabled = false,
    expandButton,
    extraParams = {},
    pagination = {},
    title = undefined,
    dataSource = [],
    request = () => {},
    options = {
      size: false, // 控制表格size
      list: false, // 选择控制表格显示列数
      fullScreen: false, //控制表格全屏显示
    },
    ...rest
  } = props;
  const [searchProps, setSearchProps] = useState({}); //用于记录filter组件搜索字段,方便切换分页保存搜索条件

  //用于控制保存工具栏中列表列数选项状态,保存全屏状态以及保存表格密度状态
  const [toolBarProps, setToolBarProps] = useState({
    fullScreen: false,
    headerList: columns
      ?.filter((item: any) => !item?.hidden)
      ?.map((item: any) => item?.title), // 控制表单可选展示列
    density: 'default',
  }) as any;

  const onChangeToolBar: any = (value: any) => {
    setToolBarProps(value);
  };
  const myColumns = useMemo(() => {
    return columns?.filter((item: any) =>
      toolBarProps.headerList?.some((o: any) => o === item?.title),
    );
  }, [toolBarProps]);
  const onFinish = (v: any) => {
    setSearchProps({
      ...searchProps,
      ...v,
    });
    request({
      ...extraParams,
      ...searchProps,
      ...v,
      pageNum: v?.pageNum || 1,
    });
  };
  const refresh = () => {
    setSearchProps({});
    request(extraParams);
  };

  const uniqueFunc = (arr: any[], uniId: string | number) => {
    const res = new Map();
    return (
      arr?.filter((item) => !res.has(item[uniId]) && res.set(item[uniId], 1)) ||
      []
    );
  };
  
  return (
    <div id="proTableId">
      <TableFilter
        columns={columns}
        onFinish={onFinish}
        refresh={refresh}
        disabled={disabled}
      />

      <ToolBar
        columns={columns}
        options={options}
        onChangeToolBar={onChangeToolBar}
        value={toolBarProps}
        title={title}
        expandButton={expandButton}
      />

      <Table
        columns={myColumns}
        pagination={{
          ...pagination,
          onChange: (pageNum, pageSize) => {
            if (pagination?.onChange) {
              pagination.onChange(pageNum, pageSize);
            } else {
              onFinish({ pageNum, pageSize });
            }
          },
        }}
        size={rest?.size || toolBarProps?.density}
        rowKey={'id'}
        {...rest}
        dataSource={uniqueFunc(dataSource, 'id')}
      />
    </div>
  );

TableFilter组件

const TableFilter: FC<TableFilterProps> = ({
  columns = [],
  onFinish = () => {},
  initialValues = {},
  disabled = false,
  expandButton = () => {},
  refresh = () => {},
  loading = false,
  datePickerFooterText = '',
}) => {
  const [form] = Form.useForm();

  const getComponent = (item: any) => {
    if (item?.searchOptions && item?.searchOptions?.length) {
      return (
        <Select allowClear placeholder="请选择" options={item?.searchOptions} />
      );
    }
    if (item?.dateTime) {
      return (
        <DatePicker
          style={{ width: '100%' }}
          format="YYYY-MM-DD HH:mm:ss"
          showTime={showTime}
          renderExtraFooter={() => (
            <div>
              <Button size="small" onClick={handleChooseTime}>
                选择时间
              </Button>
              {datePickerFooterText && (
                <span style={{ marginLeft: 20 }}>{datePickerFooterText}</span>
              )}
            </div>
          )}
        />
      );
    }
    
    return <Input allowClear placeholder={item?.placeholder || '请输入'} />;
  };
  const length =
    columns?.filter((item: any) => item?.showSearch || item?.dateTime)
      ?.length || 0;
  const getFields = () => {
    const children: any = [];
    columns
      ?.filter((item: any) => item?.showSearch || item?.dateTime)
      ?.map((item: any) => {
        children.push(
          <div className={styles.colStyle} key={item?.dataIndex}>
            <Form.Item
              key={item?.dataIndex}
              label={item?.title}
              name={item?.dataIndex}
              rules={item?.rules}
            >
              {getComponent(item)}
            </Form.Item>
          </div>,
        );
        return item;
      });

    return children;
  };
  const onMyFinish = (values: any) => {
    Object.keys(values).forEach((item) => {
      const key = values[item];
      if (key === '' || key === undefined || key === null) {
        delete values[item];
      }
    });
    onFinish(values);
  };
  const offset = () => {
    if (length % 4 === 0) {
      return 18;
    }
    return length % 3 === 1 ? 12 : length % 3 === 2 ? 6 : 0;
  };

  return (
    <div
      className={!length ? styles.null : styles.TableFilterBox}
      style={offset() < 18 ? { paddingBottom: '0' } : undefined}
    >
      <Form
        form={form}
        initialValues={initialValues}
        onFinish={onMyFinish}
        style={{ width: '100%' }}
        autoComplete="off"
      >
        <div className={styles.RowStyle}>
          {getFields()}
          {Array(length < 4 ? 0 : 3 - (length % 4))
            ?.fill(null)
            .map((_, i) => (
              <div
                className={styles.colStyle}
                style={{ opacity: 0, height: 0, padding: 0, marginBottom: 0 }}
                key={i?.toString()}
              />
            ))}
          <Form.Item>
            <div className={styles.colStyle}>
              <Button
                style={{ margin: '0 8px' }}
                onClick={() => {
                  form.resetFields();
                  refresh();
                }}
                disabled={disabled}
                loading={loading}
              >
                重置
              </Button>
              <Button
                style={{ margin: '0 8px' }}
                type="primary"
                htmlType="submit"
                disabled={disabled}
                loading={loading}
              >
                查询
              </Button>
              {expandButton()}
             
            </div>
          </Form.Item>
        </div>
      </Form>
    </div>
  );
};
export default memo(TableFilter);

ToolBar组件

const ToolBar: FC<ToolBarProps> = ({
  columns = [],
  onChangeToolBar = () => {},
  value = {},
  options = {
    size: false,
    list: false,
    fullscreen: false,
  },
  className = '',
  title = '',
  expandButton = () => {},
}) => {
  const defaultHeaderList = useMemo(() => {
    //column增加hidden属性即可在工具栏中默认展示列表中排除该项
    return columns
      ?.filter((item: any) => !item?.hidden)
      ?.map((item: any) => item?.title);
  }, [columns]);

  const [indeterminate, setIndeterminate] = React.useState(true);
  const [checkAll, setCheckAll] = React.useState(false);

  const plainOptions = useMemo(() => {
    return columns?.map((item: any) => item?.title);
  }, [columns]);

  const onChangeCheck = (list: Array<any>) => {
    onChangeToolBar({
      ...value,
      headerList: list,
    });
    setIndeterminate(!!list.length && list.length < plainOptions.length);
    setCheckAll(list.length === plainOptions.length);
  };

  const onCheckAllChange = (e: any) => {
    onChangeToolBar({
      ...value,
      headerList: e.target.checked ? plainOptions : [],
    });
    setIndeterminate(false);
    setCheckAll(e.target.checked);
  };
  const CheckboxGroupRef = useRef() as any;

  const PopoverTitle = (
    <div className={`${styles.PopoverTitle}`}>
      <Checkbox
        indeterminate={indeterminate}
        onChange={onCheckAllChange}
        checked={checkAll}
      >
        全选
      </Checkbox>
      <Button
        type="link"
        onClick={() => {
          onChangeToolBar({
            ...value,
            headerList: defaultHeaderList,
          });
        }}
      >
        重置
      </Button>
    </div>
  );

  const Popovercontent = (
    <div className={`${className} ${styles.Popovercontent}`}>
      <CheckboxGroup
        ref={CheckboxGroupRef}
        options={plainOptions}
        style={{ maxWidth: '172px' }}
        value={value?.headerList || []}
        onChange={onChangeCheck}
      />
    </div>
  );
  const menu = (
    <Menu>
      <Menu.Item
        key="default"
        onClick={() => {
          onChangeToolBar({
            ...value,
            density: 'default',
          });
        }}
      >
        默认
      </Menu.Item>
      <Menu.Item
        key="middle"
        onClick={() => {
          onChangeToolBar({
            ...value,
            density: 'middle',
          });
        }}
      >
        中等
      </Menu.Item>

      <Menu.Item
        key="small"
        onClick={() => {
          onChangeToolBar({
            ...value,
            density: 'small',
          });
        }}
      >
        紧凑
      </Menu.Item>
    </Menu>
  );
  const lunchFullScreen: any = (e: any) => {
    if (e.requestFullscreen) {
      e.requestFullscreen();
    } else if (e.webkitRequestFullscreen) {
      e.webkitRequestFullscreen();
    } else if (e.mozRequestFullScreen) {
      e.mozRequestFullScreen();
    } else if (e.msRequestFullscreen) {
      e.msRequestFullscreen();
    }
  };
  const exitFullScreen = () => {
    const mydocument = document as any;
    if (mydocument.exitFullscreen) {
      mydocument.exitFullscreen();
    } else if (mydocument?.webkitExitFullscreen) {
      mydocument?.webkitExitFullscreen();
    } else if (mydocument?.mozCancelFullScreen) {
      mydocument?.mozCancelFullScreen();
    } else if (mydocument?.msExitFullscreen) {
      mydocument?.msExitFullscreen();
    }
  };
  const checkParams = () => {
    return (
      title ||
      Object.values(options).filter((i: any) => i).length ||
      !!expandButton
    );
  };
  return (
    <div className={checkParams() ? styles.toolBar : styles.hidden}>
      <div className={styles.leftBar}>
        <div className={styles.TableTitle}>{title}</div>
      </div>
      <div className={styles.rightBar}>
        <div className={styles.btnBox}>{expandButton()}</div>
        <div className={styles.expandBtn}>
          {options?.size && (
            <div className={styles.density} id="ColumnHeightOutlined">
              <Tooltip placement="top" title="密度">
                <Popover
                  placement="bottom"
                  className={styles.Popover}
                  content={menu}
                  trigger="click"
                  getPopupContainer={() =>
                    document.getElementById('ColumnHeightOutlined') as any
                  }
                >
                  <ColumnHeightOutlined
                    style={{
                      fontSize: '16px',
                      margin: '0 8px',
                      cursor: 'pointer',
                    }}
                  />
                </Popover>
              </Tooltip>
            </div>
          )}
          {options?.list && columns?.length !== 0 && (
            <div className={styles.headerlist} id="SettingOutlined">
              <Tooltip placement="top" title="列表">
                <Popover
                  placement="bottom"
                  className={styles.Popover}
                  title={PopoverTitle}
                  content={Popovercontent}
                  trigger="click"
                  getPopupContainer={() =>
                    document.getElementById('SettingOutlined') as any
                  }
                >
                  <SettingOutlined
                    style={{
                      fontSize: '16px',
                      margin: '0 8px',
                      cursor: 'pointer',
                    }}
                  />
                </Popover>
              </Tooltip>
            </div>
          )}
          {options?.fullScreen && (
            <div className={styles.fullScreen}>
              <Tooltip placement="top" title="全屏">
                <Popover
                  placement="bottom"
                  className={styles.Popover}
                  trigger="click"
                  getPopupContainer={() =>
                    document.getElementById('SettingOutlined') as any
                  }
                >
                  <div>
                    {!value?.fullscreen && (
                      <FullscreenOutlined
                        onClick={() => {
                          onChangeToolBar({
                            ...value,
                            fullscreen: true,
                          });
                          lunchFullScreen(
                            document.getElementById('proTableId'),
                          );
                        }}
                        style={{
                          fontSize: '16px',
                          margin: '0 8px',
                          cursor: 'pointer',
                        }}
                      />
                    )}
                    {value?.fullscreen && (
                      <FullscreenExitOutlined
                        onClick={() => {
                          exitFullScreen();
                          onChangeToolBar({
                            ...value,
                            fullscreen: false,
                          });
                        }}
                        style={{
                          fontSize: '16px',
                          margin: '0 8px',
                          cursor: 'pointer',
                        }}
                      />
                    )}
                  </div>
                </Popover>
              </Tooltip>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export default memo(ToolBar);