“携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情”
实现背景
由于在项目中需要实现这样一个需求:
”对表格列表的某一列进行筛选,包括利用输入搜索、多选实现表格数据的过滤筛选“,由于antd的Table组件可以通过filterDropdown属性实现自定义筛选菜单,该属性类型为ReactNode | (props: FilterDropdownProps) => ReactNode,该函数负责渲染图层,需要开发人员自行编写各种交互,这恰好能够满足我们这个需求。
分析实现
分析需求可得到,该自定义筛选菜单包括:
- 头部区域的输入框,该输入框主要负责实现多选框的过滤筛选
- 中间区域的多选框内容,该部分主要负责渲染当前列column的数据数组,以便后续实现表格column的数据筛选
- 底部区域的操作按钮,包括全选checkbox、重置按钮和确定筛选按钮 点击table某一列的header内的搜索过滤图标,弹出的筛选弹框大致效果图如下所示:
首先来熟悉一下涉及到表格列筛选的属性api:
filterDropdown:自定义筛选菜单filtered:标识数据是否经过过滤,筛选状态时,column的header筛选图标会高亮filterIcon:自定义filter图标filterMultiple: 是否可以多选,默认为truefilterMode: 指定筛选菜单的用户界面,可选'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和控制弹出框的关闭closeDropdownclearFilters: 该方法用于清空筛选,以便让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某列数据去筛选表格数据。