基于Antd Table自定义通用columns列筛选配置

5,418 阅读4分钟

“携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

实现背景

由于在项目中需要实现这样一个需求:
”对表格列表的某一列进行筛选,包括利用输入搜索、多选实现表格数据的过滤筛选“,由于antd的Table组件可以通过filterDropdown属性实现自定义筛选菜单,该属性类型为ReactNode | (props: FilterDropdownProps) => ReactNode,该函数负责渲染图层,需要开发人员自行编写各种交互,这恰好能够满足我们这个需求。

分析实现

分析需求可得到,该自定义筛选菜单包括:

  • 头部区域的输入框,该输入框主要负责实现多选框的过滤筛选
  • 中间区域的多选框内容,该部分主要负责渲染当前列column的数据数组,以便后续实现表格column的数据筛选
  • 底部区域的操作按钮,包括全选checkbox、重置按钮和确定筛选按钮 点击table某一列的header内的搜索过滤图标,弹出的筛选弹框大致效果图如下所示:

1.png

首先来熟悉一下涉及到表格列筛选的属性api:

  • filterDropdown:自定义筛选菜单
  • filtered:标识数据是否经过过滤,筛选状态时,column的header筛选图标会高亮
  • filterIcon:自定义filter图标
  • filterMultiple: 是否可以多选,默认为true
  • filterMode: 指定筛选菜单的用户界面,可选'menu'或'tree',默认'menu'
  • filters: 表头的筛选菜单项,这是实现筛选框多选的重要属性

自定义筛选菜单的实现

首先编写自定义筛选菜单,本文使用(props: FilterDropdownProps) => ReactNode函数来定义filterDropdown属性的值。代码如下: 首先定义接口,主要涉及到表头筛选菜单项类型ColumnFilterItem、多选项类型CheckboxItem和自定义筛选菜单传入的props的类型FilterDropdownProps:

export interface ColumnFilterItem {
  text: string;
  value: string;
  children?: ColumnFilterItem[];
}

export interface CheckboxItem {
  label: string;
  value: string;
}
export interface FilterDropdownProps {
  setSelectedKeys: (selectedKeys: React.Key[]) => void;
  selectedKeys: React.Key[];
  confirm: (param?: any) => void;
  clearFilters?: () => void;
  filters: ColumnFilterItem[];
}

然后定义筛选菜单函数: 需要传入以下几个属性,他们分别表示的含义如下:

  • selectedKeys:设置筛选时选中的value,为数组
  • setSelectedKeys: 筛选值改变时调用的设置方法
  • confirm: 当点击确定按钮时调用此方法,能够使Table组件感应到,然后自动调用onFilter属性对应的方法,comfirm方法可以传入一个Object类型的参数,以便于控制回传的selectedKeys和控制弹出框的关闭closeDropdown
  • clearFilters: 该方法用于清空筛选,以便让Table组件感知到 首先定义方法:
const ColumnFilterSearch = ({
  setSelectedKeys,
  selectedKeys,
  confirm,
  clearFilters,
  filters,
}: FilterDropdownProps) => {}

然后在方法内部定义传入的菜单项值options,菜单项需要格式化为label-value类型的对象:

// 初始多选项数据为传入的菜单项格式化后的值
  const options = filters?.map((item: ColumnFilterItem) => ({
    label: item.text,
    value: item.value,
  }));

定义方法中需要使用到的数据变量,包括全选状态变量checkAll、全选状态效果变量indeterminate和实时过滤后的菜单项filterOptions,代码如下:

  // 实现全选效果
  const [indeterminate, setIndeterminate] = useState(true);
  // 是否全选
  const [checkAll, setCheckAll] = useState(false);
  // 筛选后的所以数据项
  const [filterOptions, setFilterOptions] = useState(options);

实现全选按钮切换效果,全选状态改变时,需要使用map方法设置selectedKeys,并设置checkAll的值

  // 全选按钮切换函数
  const onCheckAllChange = (e: any) => {
    setSelectedKeys(
      e.target.checked ? filterOptions.map((item: any) => item.value) : []
    );
    setIndeterminate(false);
    setCheckAll(e.target.checked);
  };

当我们通过多选不同的多选框选项时,需要实时改变选中的选项值和全选状态,代码如下:

 // 选项改变时同时设置选中的值和是否满足全选
  const checkboxOnChange = (checkedValues: any) => {
    setSelectedKeys(checkedValues);
    setIndeterminate(
      !!checkedValues.length && checkedValues.length < filterOptions.length
    );
    setCheckAll(checkedValues.length === filterOptions.length);
  };

由于需要重置功能,即点击重置按钮,恢复取消全选状态、实现初始化时的效果,代码如下:

  //重置时,设置选中的值为[],全选状态为false,同时调用clearFilters()函数实现清除筛选
  const handleReset = (clearFilters: () => void) => {
    clearFilters();
    setSelectedKeys([]);
    setCheckAll(false);
  };

因为可以通过头部的输入框进行筛选菜单项,为了提升输入体验,采用了防抖技术,等待输入完成后300ms后进行过滤:

// 实现输入过滤多选项,采用了防抖函数
  const handleInputChange = debounce(
    (e: any) => {
      const filterOptions = options.filter((item: any) =>
        item.label.includes(e.target.value)
      );
      setFilterOptions(filterOptions);
    },
    { ms: 300 }
  );

最后补上页面样式和结构的主要代码,主要使用到antd的组件、<Checkbox.Group />组件、组件和、组件:

  return (
    <div classNames="wrap">
      <Input
        placeholder="搜索"
        prefix={<SearchIcon />}
        onChange={(e) => handleInputChange(e)}
        allowClear
        bordered={false}
      />
      <Checkbox.Group
        options={filterOptions}
        value={selectedKeys}
        onChange={checkboxOnChange}
      ></Checkbox.Group>
      <div className="footer-btn">
        <Checkbox
          indeterminate={indeterminate}
          onChange={onCheckAllChange}
          checked={checkAll}
        >
          全选
        </Checkbox>
        <Space>
          <Button
            size="small"
            type="primary"
            onClick={clearFilters && handleReset.bind(null, clearFilters)}
          >
            重置
          </Button>
          <Button
            size="small"
            type="primary"
            onClick={confirm.bind(null, { selectedKeys, closeDropdown: true })}
          >
            确定
          </Button>
        </Space>
      </div>
    </div>
  );

其他筛选相关的属性配置

可以自定义column表头的筛选图标,通过filtered参数实现切换筛选状态的样式:

  filterIcon: (filtered: boolean) => (
    <SearchOutlined style={{ color: filtered ? '#eee' : undefined }} />
  ),

最终确定筛选后实现表格列筛选的属性onFilter,该属性对应的函数会循环遍历selectedKeys,去依次执行每个筛选条件value和每行record,我们可以通过value和record实现最终的筛选功能。

  onFilter: (value: string, record: any) => {
    console.log(value, record);
    return record.列的dataIndex.includes(value);
  },

以上代码有就实现了依据table某列数据去筛选表格数据。