@kne/react-filter 0.1.28 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,8 @@
1
-
2
1
  # react-filter
3
2
 
4
-
5
3
  ### 描述
6
4
 
7
- 筛选器
8
-
5
+ A React filter component library
9
6
 
10
7
  ### 安装
11
8
 
@@ -13,375 +10,1554 @@
13
10
  npm i --save @kne/react-filter
14
11
  ```
15
12
 
16
-
17
13
  ### 概述
18
14
 
19
- 用户列表数据的筛选
15
+ ### React Filter
16
+
17
+ 一个功能强大的 React 筛选组件库,支持多种筛选字段类型、灵活的布局方式,以及完善的 URL 参数双向同步能力。
18
+
19
+ ### 主要特性
20
+
21
+ - **多种筛选字段类型**:支持输入框、数字区间、日期选择、日期范围、下拉选择等常用筛选类型,以及职能、行业、城市等业务选择器
22
+ - **灵活布局**:支持普通筛选(横向布局)和高级筛选(垂直布局)两种模式
23
+ - **展开收起**:筛选行支持展开收起功能,优化页面空间利用
24
+ - **已选值展示**:自动展示已选筛选条件,支持单独删除和清空全部
25
+ - **弹出层交互**:支持弹出层形式的筛选交互,确认后才生效
26
+ - **URL 参数同步**:支持筛选值与 URL 参数的双向序列化/反序列化,自动清除已消费参数
27
+ - **数据格式拦截器**:内置 `{id, name}` ↔ `{label, value}` 格式转换拦截器,适配 SuperSelect 场景
28
+ - **声明式值映射**:提供 `createFilterValueMapper` 按字段声明转换规则,简化 `getFilterValue` 结果处理
29
+ - **国际化支持**:内置中英文语言包,支持多语言切换
30
+ - **高阶组件**:提供 `withFilterValue` 和 `withFieldItem` 高阶组件,便于扩展自定义字段
31
+
32
+ ### 适用场景
33
+
34
+ - 数据列表页面的筛选功能
35
+ - 复杂表单的筛选条件配置
36
+ - 多条件组合查询场景
37
+ - 需要展示已选筛选条件的场景
38
+ - 需要筛选状态与 URL 参数同步保持的页面
39
+
40
+ ### 快速开始
41
+
42
+ ```javascript
43
+ import Filter, { fields } from '@kne/react-filter';
44
+ import '@kne/react-filter/dist/index.css';
45
+
46
+ const { InputFilterItem, NumberRangeFilterItem, DatePickerFilterItem } = fields;
47
+
48
+ function MyComponent() {
49
+ const [filterValue, setFilterValue] = useState([]);
50
+
51
+ const handleSearch = () => {
52
+ const params = Filter.getFilterValue(filterValue);
53
+ console.log('筛选参数:', params);
54
+ };
55
+
56
+ return (
57
+ <Filter
58
+ value={filterValue}
59
+ onChange={setFilterValue}
60
+ list={[
61
+ [
62
+ { type: InputFilterItem, props: { name: 'keyword', label: '关键词' } },
63
+ { type: NumberRangeFilterItem, props: { name: 'amount', label: '金额' } }
64
+ ],
65
+ [
66
+ { type: DatePickerFilterItem, props: { name: 'date', label: '日期' } }
67
+ ]
68
+ ]}
69
+ displayLine={1}
70
+ extra={<Button type="primary" onClick={handleSearch}>搜索</Button>}
71
+ />
72
+ );
73
+ }
74
+ ```
75
+
76
+ ### 核心组件
77
+
78
+ | 组件 | 说明 |
79
+ |------|------|
80
+ | `Filter` | 主筛选组件,横向布局,支持展开收起 |
81
+ | `AdvancedFilter` | 高级筛选组件,垂直布局 |
82
+ | `FilterValueDisplay` | 已选值展示组件 |
83
+ | `PopoverItem` | 弹出层筛选项组件 |
84
+ | `FilterItem` | 筛选项容器组件 |
85
+ | `FilterLines` | 筛选行组件 |
86
+ | `FilterProvider` | 状态管理组件 |
87
+
88
+ ### 筛选字段
20
89
 
21
- * 默认提供了常见的几种筛选项
90
+ | 字段组件 | 说明 |
91
+ |----------|------|
92
+ | `InputFilterItem` | 输入框筛选 |
93
+ | `NumberRangeFilterItem` | 数字区间筛选 |
94
+ | `DatePickerFilterItem` | 日期选择筛选 |
95
+ | `DateRangePickerFilterItem` | 日期范围筛选 |
96
+ | `TypeDateRangePickerFilterItem` | 类型日期范围筛选(日/周/月切换) |
97
+ | `SuperSelectFilterItem` | 通用选择器筛选(单选/多选/搜索/全选) |
98
+ | `SelectFunctionFilterItem` | 职能筛选(多级数据、拼音搜索) |
99
+ | `SelectIndustryFilterItem` | 行业筛选(多级数据、拼音搜索) |
100
+ | `SelectAddressFilterItem` | 城市筛选(国内外城市搜索) |
22
101
 
23
- * 支持自定义筛选项类型扩展
102
+ ### URL 参数工具
24
103
 
104
+ | 工具 | 说明 |
105
+ |------|------|
106
+ | `useUrlFilter` | 从 URL 参数初始化筛选状态的 hook |
107
+ | `useUrlFilterValue` | 简化版 URL 筛选 hook,自动解析 filterParams[key] 格式 |
108
+ | `filterToUrlParams` | 将筛选值序列化为 URL 参数 |
109
+ | `parseFilterEntry` | 解析 URL 参数中的单个筛选值项 |
110
+ | `takeFilterEntry` | 从 URL 参数中读取筛选值项 |
111
+ | `createUrlFilterReader` | 创建 URL 筛选参数读取器 |
112
+ | `createUrlParamsReader` | 创建通用 URL 参数读取器 |
113
+ | `stripConsumedUrlParams` | 移除已消费的 URL 参数 |
25
114
 
26
- ### 示例(全屏)
115
+ ### 其他工具
116
+
117
+ | 工具 | 说明 |
118
+ |------|------|
119
+ | `pickSelectValues` | 从筛选值中提取原始值数组 |
120
+ | `createFilterValueMapper` | 声明式创建 mapFilterValue 函数 |
121
+ | `filterInterceptors` | `{single, multi}` 拦截器集合 |
122
+ | `singleSelectInterceptor` | 单选格式转换拦截器 |
123
+ | `multiSelectInterceptor` | 多选格式转换拦截器 |
124
+
125
+
126
+ ### 示例
27
127
 
28
128
  #### 示例代码
29
129
 
30
- - 基础示例
31
- - 展示一个筛选器的普通模式
32
- - reactFilter(@kne/react-filter),(@kne/react-filter/dist/index.css),message(antd/lib/message),_(lodash)
130
+ - 基础筛选
131
+ - 使用 Filter 主组件,展示关键词、金额、日期、部门等多种筛选字段的组合使用
132
+ - _ReactFilter(@kne/current-lib_react-filter)[import * as _ReactFilter from "@kne/react-filter"],(@kne/current-lib_react-filter/dist/index.css),antd(antd)
33
133
 
34
134
  ```jsx
35
- const {useState} = React;
36
- const {default: Filter, createExtraButton} = reactFilter;
37
- const {default: antdMessage} = message;
135
+ const { default: Filter, fields } = _ReactFilter;
136
+ const {
137
+ InputFilterItem, NumberRangeFilterItem, DatePickerFilterItem,
138
+ DateRangePickerFilterItem, TypeDateRangePickerFilterItem,
139
+ SuperSelectFilterItem, SelectFunctionFilterItem,
140
+ SelectIndustryFilterItem, SelectAddressFilterItem
141
+ } = fields;
142
+ const { Flex, Button, message } = antd;
143
+ const { useState } = React;
38
144
 
39
- const {City, List, Range, Text, Selector, Check, DateTime} = Filter.type;
40
-
41
- const {isNumber} = _;
145
+ const departmentOptions = [
146
+ { value: 'tech', label: '技术研发部' },
147
+ { value: 'product', label: '产品设计部' },
148
+ { value: 'operation', label: '运营管理部' },
149
+ { value: 'hr', label: '人力资源部' },
150
+ { value: 'finance', label: '财务部' },
151
+ { value: 'marketing', label: '市场营销部' }
152
+ ];
42
153
 
43
154
  const BaseExample = () => {
44
- const [value, setValue] = useState({});
45
- console.log(value);
46
- return <Filter value={value} onChange={(value) => {
47
- setValue(value);
48
- }} more={[
49
- <Text label="测试字段" name="test1"/>,
50
- <Text label="测试字段2" name="test2" onBeforeSearch={(value) => {
51
- antdMessage.error(`提交被拦截,输入值${value}`);
52
- return false;
53
- }}/>,
54
- <Text.Number label="测试字段7" name="test7"/>,
55
- <Range label="测试字段3" name="test3"/>,
56
- <Selector label="测试字段4" name="test4"
57
- options={[{
58
- label: "英语", value: "en"
59
- }, {
60
- label: "日语", value: "jap"
61
- }, {
62
- label: "法语", value: "fac"
63
- }]}/>,
64
- <DateTime label="测试字段5" name="test5" showTime/>,
65
- <DateTime.Range label="测试字段6" name="test6"/>
66
- ]} list={[[<City label="目前城市" name="currentCity" size={5}/>], [<List label="学历" name="degree" size={2}>
67
- <List.Item value="0" label="初中"/>
68
- <List.Item value="1" label="高中"/>
69
- <List.Item value="2" label="大学专科"/>
70
- <List.Item value="3" label="大学本科"/>
71
- </List>], [<List label="求职意向" name="yixiang" size={1}
72
- options={[{label: "暂时不找工作", value: 0}, {label: "在职看机会", value: 1}, {
73
- label: "离职正在找工作", value: 2
74
- }]}/>], [<Range label="年龄" name="age" template={(value) => {
75
- if (!value || value.length === 0) {
76
- return "";
77
- }
78
- if (!isNumber(value[0]) && isNumber(value[1])) {
79
- return `${value[1]}岁以下`;
80
- }
81
- if (!isNumber(value[1]) && isNumber(value[0])) {
82
- return `${value[0]}岁以上`;
83
- }
84
- return `${value[0]}-${value[1]}岁`;
85
- }}/>, <Text label="行业" name="inter"/>, <Selector label="语言" name="lang"
86
- options={[{
87
- label: "英语", value: "en"
88
- }, {
89
- label: "日语", value: "jap"
90
- }, {
91
- label: "法语", value: "fac"
92
- }]}/>], [<List size={1}
93
- label="薪资范围"
94
- name="sarl"
95
- options={[{
96
- label: "10k以下", value: [null, 10]
97
- }, {
98
- label: "10k-20k", value: [10, 20]
99
- }, {
100
- label: "20k以上", value: [20, null]
101
- }]}
102
- more={createExtraButton({
103
- type: "range", template: (value) => {
104
- if (!value || value.length === 0) {
105
- return "";
106
- }
107
- if (!isNumber(value[0]) && isNumber(value[1])) {
108
- return `${value[1]}k以下`;
109
- }
110
- if (!isNumber(value[1]) && isNumber(value[0])) {
111
- return `${value[0]}k以上`;
112
- }
113
- return `${value[0]}k-${value[1]}k`;
114
- }
115
- })}/>, <Check label="海外经历" name="out"/>]]}/>;
155
+ const [filterValue, setFilterValue] = useState([]);
156
+
157
+ const handleSearch = () => {
158
+ const params = Filter.getFilterValue(filterValue);
159
+ message.info(&#96;搜索参数: ${JSON.stringify(params, null, 2)}&#96;);
160
+ console.log('筛选参数:', params);
161
+ };
162
+
163
+ return (
164
+ <Flex vertical gap={16}>
165
+ <Filter
166
+ value={filterValue}
167
+ onChange={setFilterValue}
168
+ list={[
169
+ [
170
+ {
171
+ type: InputFilterItem,
172
+ props: { name: 'keyword', label: '关键词', placeholder: '请输入关键词搜索' }
173
+ },
174
+ {
175
+ type: NumberRangeFilterItem,
176
+ props: { name: 'amount', label: '金额', unit: '元', min: 0, max: 999999 }
177
+ },
178
+ {
179
+ type: DatePickerFilterItem,
180
+ props: { name: 'createTime', label: '创建时间', format: 'YYYY-MM-DD' }
181
+ },
182
+ {
183
+ type: DateRangePickerFilterItem,
184
+ props: { name: 'dateRange', label: '日期范围', format: 'YYYY-MM-DD' }
185
+ },
186
+ {
187
+ type: TypeDateRangePickerFilterItem,
188
+ props: { name: 'typeDateRange', label: '快捷日期' }
189
+ }
190
+ ],
191
+ [
192
+ {
193
+ type: SuperSelectFilterItem,
194
+ props: { name: 'department', label: '部门', options: departmentOptions }
195
+ },
196
+ {
197
+ type: SelectFunctionFilterItem,
198
+ props: { name: 'function', label: '职能' }
199
+ },
200
+ {
201
+ type: SelectIndustryFilterItem,
202
+ props: { name: 'industry', label: '行业' }
203
+ },
204
+ {
205
+ type: SelectAddressFilterItem,
206
+ props: { name: 'city', label: '城市' }
207
+ }
208
+ ]
209
+ ]}
210
+ displayLine={1}
211
+ />
212
+ <Flex gap={8}>
213
+ <span>当前筛选值:</span>
214
+ <pre style={{ margin: 0, background: '#f5f5f5', padding: 8, borderRadius: 4, flex: 1 }}>{JSON.stringify(filterValue, null, 2)}</pre>
215
+ </Flex>
216
+ </Flex>
217
+ );
116
218
  };
117
219
 
118
- render(<BaseExample/>);
220
+ render(<BaseExample />);
119
221
 
120
222
  ```
121
223
 
122
- - 简单示例
123
- - 展示一个简单的筛选项,一般放在列表页title extra位置
124
- - reactFilter(@kne/react-filter),(@kne/react-filter/dist/index.css),space(antd/lib/space)
224
+ - 高级筛选
225
+ - 使用 AdvancedFilter 组件实现更复杂的筛选布局
226
+ - _ReactFilter(@kne/current-lib_react-filter)[import * as _ReactFilter from "@kne/react-filter"],(@kne/current-lib_react-filter/dist/index.css),antd(antd)
125
227
 
126
228
  ```jsx
127
- const {useState} = React;
128
- const {default: Filter} = reactFilter;
129
- const {default: Space} = space;
130
-
131
- const {RadioList, Selector, Text, CheckboxList} = Filter.type;
132
-
133
- const Example = () => {
134
- const [value, setValue] = useState([]);
135
- const [radioValue, setRadioValue] = useState({});
136
- const [checkboxValue, setCheckboxValue] = useState({});
137
- console.log(value, radioValue, checkboxValue);
138
- return <Space direction="vertical">
139
- <Filter value={value} onChange={setValue} labelHidden itemLabelHidden list={[[
140
- <Text name="positionName" label="职位名称"/>,
141
- <Selector name="type" options={[
142
- {
143
- label: '全职',
144
- value: 1
145
- }, {
146
- label: '兼职',
147
- value: 2
148
- }
149
- ]} label="职位类型"/>
150
- ]]}/>
151
- <Filter value={radioValue} onChange={setRadioValue} labelHidden list={[[
152
- <RadioList name="state" label="客户状态" options={[
153
- {
154
- label: "全部状态",
155
- value: 0
156
- }, {
157
- label: "待开发",
158
- value: 1
159
- }, {
160
- label: "开发中",
161
- value: 2
162
- }, {
163
- label: "已签约",
164
- value: 3
165
- }, {
166
- label: "已终止",
167
- value: 4
168
- }
169
- ]}/>
170
- ]]}/>
171
- <Filter value={checkboxValue} onChange={setCheckboxValue} labelHidden list={[[
172
- <CheckboxList name="state" label="客户状态" options={[
173
- {
174
- label: "全部状态",
175
- value: 0
176
- }, {
177
- label: "待开发",
178
- value: 1
179
- }, {
180
- label: "开发中",
181
- value: 2
182
- }, {
183
- label: "已签约",
184
- value: 3
185
- }, {
186
- label: "已终止",
187
- value: 4
188
- }
189
- ]}/>
190
- ]]}/>
191
- </Space>
229
+ const { AdvancedFilter } = _ReactFilter;
230
+ const { InputFilterItem, ListFilterItem, CityFilterItem } = AdvancedFilter.fields;
231
+ const { Flex, Button, message } = antd;
232
+ const { useState } = React;
233
+
234
+ const AdvancedFilterExample = () => {
235
+ const [filterValue, setFilterValue] = useState([]);
236
+
237
+ const handleSearch = () => {
238
+ const params = {};
239
+ filterValue.forEach(item => {
240
+ params[item.name] = Array.isArray(item.value)
241
+ ? item.value.map(v => v.value)
242
+ : item.value?.value;
243
+ });
244
+ message.info(&#96;搜索参数: ${JSON.stringify(params, null, 2)}&#96;);
245
+ console.log('筛选参数:', params);
246
+ };
247
+
248
+ return (
249
+ <Flex vertical gap={16}>
250
+ <AdvancedFilter
251
+ value={filterValue}
252
+ onChange={setFilterValue}
253
+ list={[
254
+ [
255
+ {
256
+ type: InputFilterItem,
257
+ props: {
258
+ name: 'name',
259
+ label: '姓名'
260
+ }
261
+ },
262
+ {
263
+ type: InputFilterItem,
264
+ props: {
265
+ name: 'phone',
266
+ label: '手机号'
267
+ }
268
+ }
269
+ ],
270
+ [
271
+ {
272
+ type: ListFilterItem,
273
+ props: {
274
+ name: 'status',
275
+ label: '状态',
276
+ single: true,
277
+ items: [
278
+ { label: '待处理', value: 'pending' },
279
+ { label: '处理中', value: 'processing' },
280
+ { label: '已完成', value: 'completed' },
281
+ { label: '已取消', value: 'cancelled' }
282
+ ]
283
+ }
284
+ }
285
+ ],
286
+ [
287
+ {
288
+ type: ListFilterItem,
289
+ props: {
290
+ name: 'tags',
291
+ label: '标签',
292
+ single: false,
293
+ maxLength: 3,
294
+ items: [
295
+ { label: '前端', value: 'frontend' },
296
+ { label: '后端', value: 'backend' },
297
+ { label: '全栈', value: 'fullstack' },
298
+ { label: 'UI设计', value: 'ui' },
299
+ { label: '产品', value: 'product' }
300
+ ]
301
+ }
302
+ }
303
+ ],
304
+ [
305
+ {
306
+ type: CityFilterItem,
307
+ props: {
308
+ name: 'city',
309
+ label: '城市',
310
+ maxLength: 3
311
+ }
312
+ }
313
+ ]
314
+ ]}
315
+ />
316
+ <Flex justify="end">
317
+ <Button type="primary" onClick={handleSearch}>
318
+ 查询
319
+ </Button>
320
+ </Flex>
321
+ <Flex gap={8}>
322
+ <span>当前筛选值:</span>
323
+ <pre style={{ margin: 0, background: '#f5f5f5', padding: 8, borderRadius: 4, flex: 1 }}>
324
+ {JSON.stringify(filterValue, null, 2)}
325
+ </pre>
326
+ </Flex>
327
+ </Flex>
328
+ );
192
329
  };
193
330
 
194
- render(<Example/>);
331
+ render(<AdvancedFilterExample />);
195
332
 
196
333
  ```
197
334
 
198
- - 扩展示例
199
- - 展示扩展筛选项,自定义筛选项
200
- - reactFilter(@kne/react-filter),modal(antd/lib/modal),input(antd/lib/input),space(antd/lib/space),datePicker(antd/lib/date-picker),monment(moment),_(lodash),(@kne/react-filter/dist/index.css)
335
+ - 筛选字段组件
336
+ - 展示所有筛选字段组件类型,包括输入筛选、数字区间、日期选择、下拉选择以及 SuperSelect 业务选择器(职能/行业/城市)
337
+ - _ReactFilter(@kne/current-lib_react-filter)[import * as _ReactFilter from "@kne/react-filter"],(@kne/current-lib_react-filter/dist/index.css),antd(antd)
201
338
 
202
339
  ```jsx
203
- const {default: ReactFilter, useFilterContext, FilterItem, withFilterItem, SearchButton} = reactFilter;
204
- const {default: Modal} = modal;
205
- const {default: Input} = input;
206
- const {default: Space} = space;
207
- const {default: DatePicker} = datePicker;
208
- const {useRef, useState} = React;
209
- const {get} = _;
210
-
211
- const MyFilter = (props) => {
212
- const ref = useRef();
213
- const {value, onChange} = useFilterContext();
214
- return <FilterItem {...props} onClick={() => {
215
- Modal.confirm({
216
- icon: null,
217
- content: <Input ref={ref} defaultValue={get(value[props.name], 'value')}/>,
218
- onOk: () => {
219
- const value = ref.current.input.value;
220
- onChange(props.name, {
221
- label: value,
222
- value
223
- });
224
- }
225
- });
226
- console.log(value);
227
- }}/>;
340
+ const { fields, PopoverItem } = _ReactFilter;
341
+ const {
342
+ InputFilterItem, NumberRangeFilterItem, DatePickerFilterItem,
343
+ DateRangePickerFilterItem, TypeDateRangePickerFilterItem,
344
+ SuperSelectFilterItem, SelectFunctionFilterItem,
345
+ SelectIndustryFilterItem, SelectAddressFilterItem
346
+ } = fields;
347
+ const { Input, InputNumber, Space, Flex, Select, Divider, Tag } = antd;
348
+ const { useState } = React;
349
+
350
+ // 自定义下拉选择筛选项
351
+ const SelectFilterItem = ({ label, value, onChange, options = [] }) => {
352
+ return (
353
+ <PopoverItem
354
+ label={label}
355
+ value={value}
356
+ onChange={onChange}
357
+ >
358
+ {({ value, onChange }) => (
359
+ <Select
360
+ style={{ width: 200 }}
361
+ placeholder={&#96;请选择${label}&#96;}
362
+ value={value?.value}
363
+ onChange={(val, option) => {
364
+ onChange({
365
+ value: val,
366
+ label: option?.label || val
367
+ });
368
+ }}
369
+ options={options}
370
+ />
371
+ )}
372
+ </PopoverItem>
373
+ );
228
374
  };
229
375
 
230
- const DateRange = withFilterItem(({
231
- name,
232
- size,
233
- isMore,
234
- defaultActive,
235
- onBlur,
236
- onSearch,
237
- onBeforeSearch,
238
- startProps,
239
- endProps,
240
- placeholder
241
- }) => {
242
- const ref = useRef();
243
-
244
- const sort = (a, b) => {
245
- if (a && b) {
246
- return new Date(a) - new Date(b);
247
- }
248
- return 0;
249
- };
376
+ const FilterFieldsExample = () => {
377
+ const [values, setValues] = useState({});
250
378
 
251
- return <SearchButton size={size} template={(value) => {
252
- if (!value[0] && value[1]) {
253
- return `${value[1]}之前`;
254
- }
255
- if (!value[1] && value[0]) {
256
- return `${value[0]}之后`;
257
- }
258
- return value.join('~');
259
- }} isMore={isMore} onBlur={onBlur} defaultActive={defaultActive}
260
- name={name}
261
- onSearch={onSearch}
262
- onBeforeSearch={onBeforeSearch}>
263
- {({value, setValue, setActive}) => {
264
- return <div ref={ref}>
265
- <Space>
266
- <DatePicker size="small" {...startProps} getPopupContainer={() => ref.current}
267
- value={get(value, 0) && monment(get(value, 0))}
268
- onChange={(inputValue) => {
269
- const target = [inputValue && inputValue.format('YYYY-MM-DD HH:mm:ss'), get(value, 1)];
270
- setValue(target.sort(sort));
271
- }} onFocus={() => {
272
- setActive(true);
273
- }} placeholder={Array.isArray(placeholder) ? placeholder[0] : (placeholder || "请输入")}/>
274
- ~
275
- <DatePicker size="small" {...endProps} getPopupContainer={() => ref.current}
276
- value={get(value, 1) && monment(get(value, 1))}
277
- onChange={(inputValue) => {
278
- const target = [get(value, 0), inputValue && inputValue.format('YYYY-MM-DD HH:mm:ss')];
279
- setValue(target.sort(sort));
280
- }} onFocus={() => {
281
- setActive(true);
282
- }} placeholder={Array.isArray(placeholder) ? placeholder[0] : (placeholder || "请输入")}/>
283
- </Space>
284
- </div>
285
- }}
286
- </SearchButton>
287
- });
379
+ const fieldConfigs = [
380
+ {
381
+ name: 'input',
382
+ label: '输入筛选',
383
+ component: InputFilterItem,
384
+ props: {}
385
+ },
386
+ {
387
+ name: 'numberRange',
388
+ label: '数字区间',
389
+ component: NumberRangeFilterItem,
390
+ props: { unit: '万', min: 0 }
391
+ },
392
+ {
393
+ name: 'date',
394
+ label: '日期选择',
395
+ component: DatePickerFilterItem,
396
+ props: { picker: 'date' }
397
+ },
398
+ {
399
+ name: 'month',
400
+ label: '月份选择',
401
+ component: DatePickerFilterItem,
402
+ props: { picker: 'month' }
403
+ },
404
+ {
405
+ name: 'dateRange',
406
+ label: '日期范围',
407
+ component: DateRangePickerFilterItem,
408
+ props: {}
409
+ },
410
+ {
411
+ name: 'typeDateRange',
412
+ label: '类型日期范围',
413
+ component: TypeDateRangePickerFilterItem,
414
+ props: {}
415
+ },
416
+ {
417
+ name: 'select',
418
+ label: '下拉选择',
419
+ component: SelectFilterItem,
420
+ props: {
421
+ options: [
422
+ { value: 'pending', label: '待处理' },
423
+ { value: 'processing', label: '处理中' },
424
+ { value: 'completed', label: '已完成' },
425
+ { value: 'cancelled', label: '已取消' }
426
+ ]
427
+ }
428
+ }
429
+ ];
288
430
 
289
- const Example = () => {
290
- const [value, setValue] = useState({});
291
- return <ReactFilter value={value} onChange={setValue} moreTrigger="click"
292
- more={[<MyFilter name="test1" label="测试"/>, <DateRange name="test2" label="测试2"/>]}/>
431
+ return (
432
+ <Flex vertical gap={24}>
433
+ <h4>筛选字段组件展示</h4>
434
+ <Flex wrap gap={16}>
435
+ {fieldConfigs.map(({ name, label, component: Component, props }) => (
436
+ <Component
437
+ key={name}
438
+ label={label}
439
+ value={values[name]}
440
+ onChange={(val) => setValues(prev => ({ ...prev, [name]: val }))}
441
+ {...props}
442
+ />
443
+ ))}
444
+ </Flex>
445
+ <Flex gap={8}>
446
+ <span>当前值:</span>
447
+ <pre style={{ margin: 0, background: '#f5f5f5', padding: 8, borderRadius: 4, flex: 1 }}>
448
+ {JSON.stringify(values, null, 2)}
449
+ </pre>
450
+ </Flex>
451
+ </Flex>
452
+ );
293
453
  };
294
454
 
295
- render(<Example/>);
455
+ // SuperSelect 业务选择器示例
456
+ const departmentOptions = [
457
+ { value: 'tech', label: '技术研发部' },
458
+ { value: 'product', label: '产品设计部' },
459
+ { value: 'operation', label: '运营管理部' },
460
+ { value: 'hr', label: '人力资源部' },
461
+ { value: 'finance', label: '财务部' },
462
+ { value: 'marketing', label: '市场营销部' }
463
+ ];
464
+
465
+ const SuperSelectExample = () => {
466
+ const [values, setValues] = useState({});
467
+
468
+ return (
469
+ <Flex vertical gap={24}>
470
+ <Flex align="center" gap={8}>
471
+ <h4 style={{ margin: 0 }}>SuperSelect 业务选择器</h4>
472
+ <Tag color="blue">单选/多选</Tag>
473
+ <Tag color="blue">搜索</Tag>
474
+ <Tag color="blue">全选</Tag>
475
+ </Flex>
476
+ <p style={{ margin: 0, color: '#666', fontSize: 12 }}>
477
+ 基于 @kne/super-select 的通用选择器筛选项,支持单选/多选、搜索、全选等功能
478
+ </p>
479
+ <Flex wrap gap={16}>
480
+ <SuperSelectFilterItem
481
+ label="部门(多选)"
482
+ value={values.dept}
483
+ onChange={(val) => setValues(prev => ({ ...prev, dept: val }))}
484
+ options={departmentOptions}
485
+ />
486
+ <SuperSelectFilterItem
487
+ label="状态(单选)"
488
+ single
489
+ value={values.status}
490
+ onChange={(val) => setValues(prev => ({ ...prev, status: val }))}
491
+ options={[
492
+ { value: 'active', label: '启用' },
493
+ { value: 'inactive', label: '停用' }
494
+ ]}
495
+ />
496
+ <SuperSelectFilterItem
497
+ label="角色(全选)"
498
+ value={values.role}
499
+ onChange={(val) => setValues(prev => ({ ...prev, role: val }))}
500
+ options={[
501
+ { value: 'admin', label: '管理员' },
502
+ { value: 'editor', label: '编辑者' },
503
+ { value: 'viewer', label: '查看者' }
504
+ ]}
505
+ allowSelectedAll
506
+ />
507
+ </Flex>
508
+ <pre style={{ margin: 0, background: '#f5f5f5', padding: 8, borderRadius: 4 }}>
509
+ {JSON.stringify(values, null, 2)}
510
+ </pre>
511
+ </Flex>
512
+ );
513
+ };
514
+
515
+ // 业务选择器示例(职能/行业/城市)
516
+ const BusinessSelectExample = () => {
517
+ const [values, setValues] = useState({});
518
+
519
+ return (
520
+ <Flex vertical gap={24}>
521
+ <Flex align="center" gap={8}>
522
+ <h4 style={{ margin: 0 }}>业务选择器筛选项</h4>
523
+ <Tag color="blue">多级数据</Tag>
524
+ <Tag color="blue">拼音搜索</Tag>
525
+ <Tag color="blue">国际化</Tag>
526
+ </Flex>
527
+ <p style={{ margin: 0, color: '#666', fontSize: 12 }}>
528
+ 基于 @kne/super-select-plus 的职能、行业、城市选择器,支持多级数据、拼音搜索、国际化
529
+ </p>
530
+ <Flex wrap gap={16}>
531
+ <SelectFunctionFilterItem
532
+ label="职能"
533
+ value={values.function}
534
+ onChange={(val) => setValues(prev => ({ ...prev, function: val }))}
535
+ />
536
+ <SelectIndustryFilterItem
537
+ label="行业"
538
+ value={values.industry}
539
+ onChange={(val) => setValues(prev => ({ ...prev, industry: val }))}
540
+ />
541
+ <SelectAddressFilterItem
542
+ label="城市(多选)"
543
+ value={values.city}
544
+ onChange={(val) => setValues(prev => ({ ...prev, city: val }))}
545
+ />
546
+ <SelectAddressFilterItem
547
+ label="城市(单选)"
548
+ single
549
+ value={values.singleCity}
550
+ onChange={(val) => setValues(prev => ({ ...prev, singleCity: val }))}
551
+ />
552
+ </Flex>
553
+ </Flex>
554
+ );
555
+ };
556
+
557
+ render(<FilterFieldsExample />);
558
+ render(<Divider />);
559
+ render(<SuperSelectExample />);
560
+ render(<Divider />);
561
+ render(<BusinessSelectExample />);
296
562
 
297
563
  ```
298
564
 
565
+ - 已选值展示
566
+ - 使用 FilterValueDisplay 组件展示已选择的筛选条件,支持单独删除和清空全部
567
+ - _ReactFilter(@kne/current-lib_react-filter)[import * as _ReactFilter from "@kne/react-filter"],(@kne/current-lib_react-filter/dist/index.css),antd(antd)
568
+
569
+ ```jsx
570
+ const { FilterValueDisplay } = _ReactFilter;
571
+ const { Flex } = antd;
572
+ const { useState } = React;
573
+
574
+ const FilterValueDisplayExample = () => {
575
+ const [filterValue, setFilterValue] = useState([
576
+ { name: 'keyword', label: '关键词', value: { label: 'React', value: 'React' } },
577
+ { name: 'status', label: '状态', value: { label: '已完成', value: 'completed' } },
578
+ { name: 'amount', label: '金额', value: { label: '100-500万', value: [100, 500] } },
579
+ {
580
+ name: 'tags',
581
+ label: '标签',
582
+ value: [
583
+ { label: '前端', value: 'frontend' },
584
+ { label: 'React', value: 'react' }
585
+ ]
586
+ }
587
+ ]);
588
+
589
+ return (
590
+ <Flex vertical gap={16}>
591
+ <h4>已选筛选条件展示</h4>
592
+ <FilterValueDisplay
593
+ value={filterValue}
594
+ onChange={setFilterValue}
595
+ extraExpand={
596
+ <span style={{ fontSize: 12, color: '#999' }}>
597
+ 共 {filterValue.length} 项
598
+ </span>
599
+ }
600
+ />
601
+ <Flex gap={8}>
602
+ <span>当前值:</span>
603
+ <pre style={{ margin: 0, background: '#f5f5f5', padding: 8, borderRadius: 4, flex: 1 }}>
604
+ {JSON.stringify(filterValue, null, 2)}
605
+ </pre>
606
+ </Flex>
607
+ </Flex>
608
+ );
609
+ };
610
+
611
+ render(<FilterValueDisplayExample />);
612
+
613
+ ```
614
+
615
+ - 弹出层筛选
616
+ - 使用 PopoverItem 组件实现弹出层形式的筛选项,支持文本输入、数字输入、下拉选择和数值范围等多种交互形式
617
+ - _ReactFilter(@kne/current-lib_react-filter)[import * as _ReactFilter from "@kne/react-filter"],(@kne/current-lib_react-filter/dist/index.css),antd(antd)
618
+
619
+ ```jsx
620
+ const { PopoverItem } = _ReactFilter;
621
+ const { Input, InputNumber, Space, Select, Radio, Flex } = antd;
622
+ const { useState } = React;
623
+
624
+ const PopoverItemExample = () => {
625
+ const [inputValue, setInputValue] = useState(null);
626
+ const [numberValue, setNumberValue] = useState(null);
627
+ const [selectValue, setSelectValue] = useState(null);
628
+ const [rangeValue, setRangeValue] = useState(null);
629
+
630
+ return (
631
+ <Flex vertical gap={24}>
632
+ <h4>弹出层筛选组件示例</h4>
633
+ <Flex wrap gap={16}>
634
+ {/* 输入框筛选 */}
635
+ <PopoverItem
636
+ label="文本输入"
637
+ value={inputValue}
638
+ onChange={setInputValue}
639
+ >
640
+ {({ value, onChange }) => (
641
+ <Input
642
+ style={{ width: 240 }}
643
+ placeholder="请输入文本"
644
+ value={value?.value || ''}
645
+ onChange={(e) => onChange(
646
+ e.target.value ? { label: e.target.value, value: e.target.value } : null
647
+ )}
648
+ />
649
+ )}
650
+ </PopoverItem>
651
+
652
+ {/* 数字输入筛选 */}
653
+ <PopoverItem
654
+ label="数字输入"
655
+ value={numberValue}
656
+ onChange={setNumberValue}
657
+ onValidate={(val) => val?.value !== undefined}
658
+ >
659
+ {({ value, onChange }) => (
660
+ <InputNumber
661
+ style={{ width: 240 }}
662
+ placeholder="请输入数字"
663
+ value={value?.value}
664
+ onChange={(val) => onChange(
665
+ val !== null ? { label: String(val), value: val } : null
666
+ )}
667
+ />
668
+ )}
669
+ </PopoverItem>
670
+
671
+ {/* 下拉选择筛选 */}
672
+ <PopoverItem
673
+ label="状态选择"
674
+ value={selectValue}
675
+ onChange={setSelectValue}
676
+ >
677
+ {({ value, onChange }) => (
678
+ <Select
679
+ style={{ width: 240 }}
680
+ placeholder="请选择状态"
681
+ value={value?.value}
682
+ onChange={(val, option) => onChange({
683
+ value: val,
684
+ label: option?.label || val
685
+ })}
686
+ options={[
687
+ { value: 'active', label: '激活' },
688
+ { value: 'inactive', label: '未激活' },
689
+ { value: 'pending', label: '待处理' }
690
+ ]}
691
+ />
692
+ )}
693
+ </PopoverItem>
694
+
695
+ {/* 数字范围筛选 */}
696
+ <PopoverItem
697
+ label="数值范围"
698
+ value={rangeValue}
699
+ onChange={setRangeValue}
700
+ onValidate={(val) => {
701
+ const range = val?.value;
702
+ return !(range && range[0] !== undefined && range[1] !== undefined && range[1] < range[0]);
703
+ }}
704
+ >
705
+ {({ value, onChange }) => (
706
+ <Space.Compact>
707
+ <InputNumber
708
+ style={{ width: 100 }}
709
+ placeholder="最小值"
710
+ value={value?.value?.[0]}
711
+ onChange={(val) => onChange({
712
+ label: &#96;${val || '?'}-${value?.value?.[1] || '?'}&#96;,
713
+ value: [val, value?.value?.[1]]
714
+ })}
715
+ />
716
+ <Input
717
+ style={{ width: 30, textAlign: 'center', borderLeft: 0, borderRight: 0 }}
718
+ placeholder="~"
719
+ disabled
720
+ />
721
+ <InputNumber
722
+ style={{ width: 100 }}
723
+ placeholder="最大值"
724
+ value={value?.value?.[1]}
725
+ onChange={(val) => onChange({
726
+ label: &#96;${value?.value?.[0] || '?'}-${val || '?'}&#96;,
727
+ value: [value?.value?.[0], val]
728
+ })}
729
+ />
730
+ </Space.Compact>
731
+ )}
732
+ </PopoverItem>
733
+ </Flex>
734
+
735
+ <Flex vertical gap={8}>
736
+ <h5>当前值:</h5>
737
+ <pre style={{ margin: 0, background: '#f5f5f5', padding: 12, borderRadius: 4 }}>
738
+ {JSON.stringify({
739
+ 文本输入: inputValue,
740
+ 数字输入: numberValue,
741
+ 状态选择: selectValue,
742
+ 数值范围: rangeValue
743
+ }, null, 2)}
744
+ </pre>
745
+ </Flex>
746
+ </Flex>
747
+ );
748
+ };
749
+
750
+ render(<PopoverItemExample />);
751
+
752
+ ```
299
753
 
300
754
  ### API
301
755
 
302
- | 属性名 | 说明 | 类型 | 默认值 |
303
- |-----------------|------------------------------------------------------------------------------|----------|-------|
304
- | value | 筛选器的值,该组件为一个受控组件 | object | - |
305
- | onChange | 筛选器的值发生改变的时候调用的函数 | function | - |
306
- | list | 筛选器的选项,为一个二维数组,二维数组中的每一个数组为一行,内部数组的每一项为一个筛选选项,筛选选项为Filter.type中的一项或者是自行扩展的选项 | array | [] |
307
- | more | 筛选项更多选项,为一个一维数组,数组中的每一项为一个筛选项,和list不同的是该筛选项默认为收起状态 | array | [] |
308
- | moreLabel | 更多选项的label | string | 更多 |
309
- | moreTrigger | 更多选项的触发方式,默认为hover触发,页可以设置为click | string | hover |
310
- | label | 筛选器的label | string |筛选项|
311
- | labelHidden | 筛选器的label及筛选内容行是否隐藏 | boolean | false |
312
- | itemLabelHidden | 筛选项的label是否隐藏 | boolean |false|
313
- | unfoldText | 展开提示文字 |string| 展开 |
314
- | foldText | 收起提示文字 |string|收起|
315
- |displayLine| 默认展开行数 |number|2|
316
- |defaultDisplay| 默认是否展开 |boolean|false|
317
- |isExtra| 是否将list切分成收起项和默认展开项 |boolean|true|
318
- |extra| 额外内容 |boolean|null|
756
+ ### Filter 主组件
757
+
758
+ 筛选组件,用于展示筛选项和处理筛选条件。
759
+
760
+ #### 属性
761
+
762
+ | 属性 | 类型 | 默认值 | 说明 |
763
+ |--------------|------------------------------------------------------|--------|-----------------|
764
+ | value | `Array<{ name: string, label: string, value: any }>` | - | 筛选值数组 |
765
+ | defaultValue | `Array<{ name: string, label: string, value: any }>` | `[]` | 默认筛选值 |
766
+ | onChange | `(value: Array) => void` | - | 筛选值变化回调 |
767
+ | list | `Array<Array>` | `[]` | 筛选项配置数组,支持多行 |
768
+ | displayLine | `number` | `1` | 默认展示的行数,超出部分折叠 |
769
+ | label | `string` | `'筛选'` | 筛选区域标题 |
770
+ | extra | `ReactNode` | - | 额外操作区域,通常放置搜索按钮 |
771
+ | extraExpand | `ReactNode` | - | 已选区域额外内容 |
772
+ | className | `string` | - | 自定义类名 |
773
+
774
+ #### 静态方法
775
+
776
+ | 方法 | 说明 |
777
+ |-------------------------------------------------|-----------------------------------------------------------------------|
778
+ | `Filter.getFilterValue(filterValue)` | 将筛选值数组转换为参数对象,如 `{ name: value }` |
779
+ | `Filter.useFilter()` | 获取 Filter Context,返回 `{ value, onChange }` |
780
+ | `Filter.pickSelectValues(value)` | 从筛选值中提取原始值数组,支持 `{ value }`、`{ id }` 格式 |
781
+ | `Filter.createFilterValueMapper(fieldMappers)` | 声明式创建 mapFilterValue 函数,按字段映射转换规则 |
782
+ | `Filter.filterToUrlParams(filterValue, options)`| 将筛选值数组序列化为 URLSearchParams,保留 label 信息 |
783
+ | `Filter.parseFilterEntry(str)` | 解析 URL 参数中的单个筛选值项为 `{ label, value }` |
784
+ | `Filter.takeFilterEntry(searchParams, key, options)` | 从 URL 参数中读取筛选值项,支持单选/多选 |
785
+ | `Filter.createUrlFilterReader(searchParams)` | 创建 URL 筛选参数读取器,自动追踪已消费的参数 key |
786
+ | `Filter.useUrlFilter(options)` | 从 URL 参数初始化 Filter 状态的 hook(React Router 环境) |
787
+ | `Filter.useUrlFilterValue(mapping)` | useUrlFilter 的简化版,基于 filterParams[key] 格式自动解析 URL 参数 |
788
+ | `Filter.createUrlParamsReader(searchParams)` | 创建通用 URL 参数读取器,自动追踪已消费的参数 key |
789
+ | `Filter.stripConsumedUrlParams(searchParams, consumedKeys)` | 从 URL 参数中移除已消费的 key,返回新的 URLSearchParams 或 null |
790
+ | `Filter.filterInterceptors.single` | 单选拦截器:`{id, name}` ↔ `{label, value}` 数据格式转换 |
791
+ | `Filter.filterInterceptors.multi` | 多选拦截器:`[{id, name}]` ↔ `[{label, value}]` 数据格式转换 |
792
+
793
+ #### 使用示例
794
+
795
+ ```javascript
796
+ import Filter, { fields } from '@kne/react-filter';
797
+
798
+ const { InputFilterItem, NumberRangeFilterItem } = fields;
799
+
800
+ <Filter
801
+ value={filterValue}
802
+ onChange={setFilterValue}
803
+ list={[
804
+ [
805
+ { type: InputFilterItem, props: { name: 'keyword', label: '关键词' } },
806
+ { type: NumberRangeFilterItem, props: { name: 'amount', label: '金额' } }
807
+ ]
808
+ ]}
809
+ displayLine={1}
810
+ extra={<Button type="primary">搜索</Button>}
811
+ />
812
+ ```
813
+
814
+ ---
815
+
816
+ ### AdvancedFilter 高级筛选组件
817
+
818
+ 高级筛选组件,用于更复杂的筛选场景,采用垂直布局。
819
+
820
+ #### 属性
821
+
822
+ | 属性 | 类型 | 默认值 | 说明 |
823
+ |--------------|------------------------------------------------------|------|----------|
824
+ | value | `Array<{ name: string, label: string, value: any }>` | - | 筛选值数组 |
825
+ | defaultValue | `Array<{ name: string, label: string, value: any }>` | `[]` | 默认筛选值 |
826
+ | onChange | `(value: Array) => void` | - | 筛选值变化回调 |
827
+ | list | `Array<Array>` | `[]` | 筛选项配置数组 |
828
+ | more | `Array` | - | 额外折叠的筛选项 |
829
+ | className | `string` | - | 自定义类名 |
830
+
831
+ #### 使用示例
832
+
833
+ ```javascript
834
+ import { AdvancedFilter, fields } from '@kne/react-filter';
835
+
836
+ <AdvancedFilter
837
+ value={filterValue}
838
+ onChange={setFilterValue}
839
+ list={[
840
+ [
841
+ { type: InputFilterItem, props: { name: 'name', label: '姓名' } }
842
+ ]
843
+ ]}
844
+ />
845
+ ```
846
+
847
+ ---
848
+
849
+ ### FilterValueDisplay 已选值展示
850
+
851
+ 展示已选择的筛选条件,支持单独删除和清空全部。
852
+
853
+ #### 属性
854
+
855
+ | 属性 | 类型 | 默认值 | 说明 |
856
+ |-------------|------------------------------------------------------|-----|---------|
857
+ | value | `Array<{ name: string, label: string, value: any }>` | - | 筛选值数组 |
858
+ | onChange | `(value: Array) => void` | - | 筛选值变化回调 |
859
+ | extraExpand | `ReactNode` | - | 额外展示内容 |
860
+
861
+ ---
862
+
863
+ ### PopoverItem 弹出层筛选项
864
+
865
+ 弹出层形式的筛选项,支持确认取消操作。
866
+
867
+ #### 属性
868
+
869
+ | 属性 | 类型 | 默认值 | 说明 |
870
+ |------------------|---------------------------------------------|----------------|-----------|
871
+ | label | `string` | - | 筛选项标签 |
872
+ | value | `{ label: string, value: any }` | - | 当前值 |
873
+ | onChange | `(value: object) => void` | - | 值变化回调 |
874
+ | onValidate | `(value: object) => boolean` | - | 确认按钮校验函数 |
875
+ | onOpenChange | `(open: boolean) => void` | - | 弹出层状态变化回调 |
876
+ | placement | `string` | `'bottomLeft'` | 弹出层位置 |
877
+ | overlayClassName | `string` | - | 弹出层自定义类名 |
878
+ | children | `(props: { value, onChange }) => ReactNode` | - | 内容渲染函数 |
879
+
880
+ #### 使用示例
881
+
882
+ ```javascript
883
+ import { PopoverItem } from '@kne/react-filter';
884
+
885
+ <PopoverItem
886
+ label="文本输入"
887
+ value={inputValue}
888
+ onChange={setInputValue}
889
+ >
890
+ {({ value, onChange }) => (
891
+ <Input
892
+ value={value?.value}
893
+ onChange={(e) => onChange({ label: e.target.value, value: e.target.value })}
894
+ />
895
+ )}
896
+ </PopoverItem>
897
+ ```
898
+
899
+ ---
900
+
901
+ ### FilterItem 筛选项容器
902
+
903
+ 筛选项的基础容器组件。
904
+
905
+ #### 属性
906
+
907
+ | 属性 | 类型 | 默认值 | 说明 |
908
+ |----------|-------------|-----|------------|
909
+ | label | `string` | - | 筛选项标签 |
910
+ | open | `boolean` | - | 是否展开状态 |
911
+ | active | `boolean` | - | 是否激活状态(有值) |
912
+ | children | `ReactNode` | - | 子元素 |
913
+
914
+ ---
915
+
916
+ ### FilterLines 筛选行
917
+
918
+ 筛选行组件,支持多行展开收起。
919
+
920
+ #### 属性
921
+
922
+ | 属性 | 类型 | 默认值 | 说明 |
923
+ |-------------|----------------|--------|---------|
924
+ | list | `Array<Array>` | `[]` | 筛选项配置数组 |
925
+ | displayLine | `number` | `1` | 默认展示行数 |
926
+ | label | `string` | `'筛选'` | 标题 |
927
+ | extra | `ReactNode` | - | 额外操作区域 |
928
+ | className | `string` | - | 自定义类名 |
929
+
930
+ ---
931
+
932
+ ### FilterProvider 状态管理
933
+
934
+ Filter 状态管理组件,用于自定义 Filter 结构。
935
+
936
+ #### 属性
937
+
938
+ | 属性 | 类型 | 默认值 | 说明 |
939
+ |--------------|---------------------------------------|------|----------|
940
+ | value | `Array` | - | 筛选值数组 |
941
+ | defaultValue | `Array` | `[]` | 默认筛选值 |
942
+ | onChange | `(value: Array) => void` | - | 筛选值变化回调 |
943
+ | children | `ReactNode \| (context) => ReactNode` | - | 子元素或渲染函数 |
944
+
945
+ ---
946
+
947
+ ### 高阶组件
948
+
949
+ #### withFilterValue
950
+
951
+ 为组件注入筛选值和变更函数。
952
+
953
+ ```javascript
954
+ import { withFilterValue } from '@kne/react-filter';
955
+
956
+ const MyFilterItem = withFilterValue(({ name, label, value, onChange, ...props }) => {
957
+ return <Component value={value} onChange={onChange}/>;
958
+ });
959
+ ```
960
+
961
+ #### withFieldItem
962
+
963
+ 为组件包装 FilterItem 样式。
964
+
965
+ ```javascript
966
+ import { withFieldItem } from '@kne/react-filter';
967
+
968
+ const MyFieldItem = withFieldItem(MyComponent);
969
+ ```
970
+
971
+ ---
972
+
973
+ ### 筛选字段组件
974
+
975
+ #### InputFilterItem 输入筛选
976
+
977
+ 弹出层形式的输入框筛选组件。
978
+
979
+ | 属性 | 类型 | 默认值 | 说明 |
980
+ |-------------|----------------------|-----|--------|
981
+ | name | `string` | - | 字段名称 |
982
+ | label | `string` | - | 标签 |
983
+ | placeholder | `string` | - | 占位符 |
984
+ | onValidate | `(value) => boolean` | - | 确认校验函数 |
985
+
986
+ #### NumberRangeFilterItem 数字区间筛选
987
+
988
+ 数字区间输入筛选组件。
989
+
990
+ | 属性 | 类型 | 默认值 | 说明 |
991
+ |-------------|----------|-----|------|
992
+ | name | `string` | - | 字段名称 |
993
+ | label | `string` | - | 标签 |
994
+ | unit | `string` | - | 单位 |
995
+ | min | `number` | - | 最小值 |
996
+ | max | `number` | - | 最大值 |
997
+ | placeholder | `string` | - | 占位符 |
998
+
999
+ #### DatePickerFilterItem 日期筛选
1000
+
1001
+ 日期选择筛选组件。
1002
+
1003
+ | 属性 | 类型 | 默认值 | 说明 |
1004
+ |--------|------------------------------------------------------|----------------|-------|
1005
+ | name | `string` | - | 字段名称 |
1006
+ | label | `string` | - | 标签 |
1007
+ | picker | `'date' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'date'` | 选择器类型 |
1008
+ | format | `string` | `'YYYY-MM-DD'` | 日期格式 |
1009
+
1010
+ #### DateRangePickerFilterItem 日期范围筛选
1011
+
1012
+ 日期范围选择筛选组件。
1013
+
1014
+ | 属性 | 类型 | 默认值 | 说明 |
1015
+ |--------|-------------------------------------|----------------|------|
1016
+ | name | `string` | - | 字段名称 |
1017
+ | label | `string` | - | 标签 |
1018
+ | format | `string` | `'YYYY-MM-DD'` | 日期格式 |
1019
+ | header | `ReactNode \| (props) => ReactNode` | - | 头部内容 |
1020
+
1021
+ #### TypeDateRangePickerFilterItem 类型日期范围筛选
1022
+
1023
+ 支持按日/周/月切换的日期范围选择筛选组件。
1024
+
1025
+ | 属性 | 类型 | 默认值 | 说明 |
1026
+ |--------|----------|----------------|------|
1027
+ | name | `string` | - | 字段名称 |
1028
+ | label | `string` | - | 标签 |
1029
+ | format | `string` | `'YYYY-MM-DD'` | 日期格式 |
319
1030
 
320
- #### Filter.type
1031
+ #### SuperSelectFilterItem 通用选择器筛选
321
1032
 
322
- City(城市选择), List(列表选择,可以多选或者单选), Range(数据范围), Text(文字输入) Text.Number, Selector(下拉选项), Check(单项选择), RadioList(单选列表),
323
- CheckboxList(多选列表)
1033
+ 基于 `@kne/super-select` 的通用选择器筛选项,支持单选/多选、搜索、全选等功能。
324
1034
 
325
- DateTime(时间选择),DateTime.Time DateTime.Range DateTime.Week DateTime.Month DateTime.Year
1035
+ | 属性 | 类型 | 默认值 | 说明 |
1036
+ |------------------|-----------------------------|---------|----------|
1037
+ | name | `string` | - | 字段名称 |
1038
+ | label | `string` | - | 标签 |
1039
+ | options | `Array<{ value, label }>` | - | 选项数据 |
1040
+ | single | `boolean` | `false` | 是否单选 |
1041
+ | allowSelectedAll | `boolean` | `false` | 是否支持全选 |
1042
+ | maxLength | `number` | - | 最多可选数量 |
326
1043
 
327
- 公共参数
1044
+ **使用示例:**
328
1045
 
329
- | 属性名 | 说明 | 类型 | 默认值 |
330
- |-------|--------------------------------------------|--------|-----|
331
- | label | 筛选项名称 | string | - |
332
- | name | 筛选项的key | string | - |
333
- | placeholder| 筛选项的placeholder(如果可以显示的话),如果不传值通常会去label的值 | string |-|
1046
+ ```javascript
1047
+ import { SuperSelectFilterItem } from '@kne/react-filter';
334
1048
 
335
- #### Text|Text.Number|Range
1049
+ // 多选
1050
+ <SuperSelectFilterItem
1051
+ label="部门"
1052
+ options={[
1053
+ { value: 'tech', label: '技术研发部' },
1054
+ { value: 'product', label: '产品设计部' }
1055
+ ]}
1056
+ />
336
1057
 
337
- | 属性名 | 说明 | 类型 | 默认值 |
338
- |-------|-------------------------------------------------------|--------|-----|
339
- |onBeforeSearch| 带有确定按钮的筛选项,在点击确定后会调用该方法,如果该方法返回为false,则该选项的值不会被提交给筛选项 | function | - |
1058
+ // 单选
1059
+ <SuperSelectFilterItem
1060
+ label="状态"
1061
+ single
1062
+ options={[
1063
+ { value: 'active', label: '启用' },
1064
+ { value: 'inactive', label: '停用' }
1065
+ ]}
1066
+ />
1067
+ ```
1068
+
1069
+ > 注意:需要安装 `@kne/super-select` 依赖。
1070
+
1071
+ #### SelectFunctionFilterItem 职能筛选
1072
+
1073
+ 基于 `@kne/super-select-plus` 的职能选择器筛选项,支持多级职能数据选择、拼音搜索。
1074
+
1075
+ | 属性 | 类型 | 默认值 | 说明 |
1076
+ |-----------|-----------|---------|----------|
1077
+ | name | `string` | - | 字段名称 |
1078
+ | label | `string` | - | 标签 |
1079
+ | single | `boolean` | `false` | 是否单选 |
1080
+ | maxLength | `number` | - | 最多可选数量 |
1081
+
1082
+ > 注意:需要安装 `@kne/super-select-plus` 依赖。
1083
+
1084
+ #### SelectIndustryFilterItem 行业筛选
1085
+
1086
+ 基于 `@kne/super-select-plus` 的行业选择器筛选项,支持多级行业数据选择、拼音搜索。
1087
+
1088
+ | 属性 | 类型 | 默认值 | 说明 |
1089
+ |-----------|-----------|---------|----------|
1090
+ | name | `string` | - | 字段名称 |
1091
+ | label | `string` | - | 标签 |
1092
+ | single | `boolean` | `false` | 是否单选 |
1093
+ | maxLength | `number` | - | 最多可选数量 |
1094
+
1095
+ > 注意:需要安装 `@kne/super-select-plus` 依赖。
1096
+
1097
+ #### SelectAddressFilterItem 城市筛选
1098
+
1099
+ 基于 `@kne/super-select-plus` 的城市选择器筛选项,支持国内外城市搜索选择。
1100
+
1101
+ | 属性 | 类型 | 默认值 | 说明 |
1102
+ |-----------|-----------|---------|----------|
1103
+ | name | `string` | - | 字段名称 |
1104
+ | label | `string` | - | 标签 |
1105
+ | single | `boolean` | `false` | 是否单选 |
1106
+ | maxLength | `number` | - | 最多可选数量 |
1107
+
1108
+ > 注意:需要安装 `@kne/super-select-plus` 依赖。
1109
+
1110
+ #### CityFilterItem(高级筛选)
1111
+
1112
+ 城市选择器的高级筛选版本,用于 `AdvancedFilter` 组件的 `list` 配置中。展示热门城市标签,支持搜索选择其他城市。
1113
+
1114
+ | 属性 | 类型 | 默认值 | 说明 |
1115
+ |-----------|-----------|---------|------------|
1116
+ | single | `boolean` | `false` | 是否单选 |
1117
+ | maxLength | `number` | `5` | 最多可选数量 |
1118
+
1119
+ **在高级筛选中使用:**
1120
+
1121
+ ```javascript
1122
+ import { AdvancedFilter } from '@kne/react-filter';
1123
+ import { CityFilterItem } from './AdvancedFilter/fields';
1124
+
1125
+ <AdvancedFilter
1126
+ list={[
1127
+ [{ type: CityFilterItem, props: { label: '城市', single: true } }]
1128
+ ]}
1129
+ />
1130
+ ```
1131
+
1132
+ ---
1133
+
1134
+ ### TypeDateRangePickerField 类型日期范围选择器
1135
+
1136
+ 支持按日/周/月切换的日期范围选择器基础组件。
1137
+
1138
+ | 属性 | 类型 | 默认值 | 说明 |
1139
+ |-----------------|------------------------------------------------------------|---------------------------------|----------|
1140
+ | value | `{ type: string, value: [Date, Date] }` | - | 当前值 |
1141
+ | defaultValue | `{ type: string, value: [Date, Date] }` | `{ type: 'date', value: null }` | 默认值 |
1142
+ | onChange | `(value: object) => void` | - | 值变化回调 |
1143
+ | shortcuts | `boolean` | `true` | 是否显示快捷选项 |
1144
+ | shortcutOptions | `Array<{ label: string, getValue: () => [Dayjs, Dayjs] }>` | - | 自定义快捷选项 |
1145
+
1146
+ **value 结构:**
1147
+
1148
+ ```typescript
1149
+ interface TypeDateRangeValue {
1150
+ type: 'date' | 'week' | 'month'; // 日期类型
1151
+ value: [Date, Date] | null; // 日期范围 [开始时间, 结束时间]
1152
+ }
1153
+ ```
1154
+
1155
+ **默认快捷选项:**
1156
+
1157
+ - 近7天:`dayjs().subtract(7, 'day')` 至今天
1158
+ - 本月:本月第一天至最后一天
1159
+ - 近三个月:`dayjs().subtract(3, 'month')` 至今天
1160
+ - 当年:本年第一天至最后一天
1161
+
1162
+ **自定义快捷选项示例:**
1163
+
1164
+ ```javascript
1165
+ import { TypeDateRangePickerField } from '@kne/react-filter';
1166
+
1167
+ <TypeDateRangePickerField
1168
+ shortcuts={true}
1169
+ shortcutOptions={[
1170
+ {
1171
+ label: '最近一周',
1172
+ getValue: () => [dayjs().subtract(7, 'day').startOf('day'), dayjs().endOf('day')]
1173
+ },
1174
+ {
1175
+ label: '最近一月',
1176
+ getValue: () => [dayjs().subtract(1, 'month').startOf('day'), dayjs().endOf('day')]
1177
+ }
1178
+ ]}
1179
+ />
1180
+ ```
1181
+
1182
+ ---
1183
+
1184
+ ### SearchInput 搜索输入
1185
+
1186
+ 搜索输入组件。
1187
+
1188
+ | 属性 | 类型 | 默认值 | 说明 |
1189
+ |-------------|----------|-----|------|
1190
+ | name | `string` | - | 字段名称 |
1191
+ | label | `string` | - | 标签 |
1192
+ | placeholder | `string` | - | 占位符 |
1193
+
1194
+ ---
1195
+
1196
+ ### 工具方法
1197
+
1198
+ #### getFilterValue
1199
+
1200
+ 将筛选值数组转换为参数对象。
1201
+
1202
+ ```javascript
1203
+ import { getFilterValue } from '@kne/react-filter';
1204
+
1205
+ const filterValue = [
1206
+ { name: 'keyword', value: { label: 'test', value: 'test' } },
1207
+ { name: 'status', value: [{ label: '已完成', value: 'done' }] }
1208
+ ];
1209
+
1210
+ const params = getFilterValue(filterValue);
1211
+ // { keyword: 'test', status: ['done'] }
1212
+ ```
1213
+
1214
+ ---
1215
+
1216
+ ### 筛选值结构
1217
+
1218
+ 筛选值数组中的每一项结构:
1219
+
1220
+ ```typescript
1221
+ interface FilterValueItem {
1222
+ name: string; // 字段名称
1223
+ label: string; // 字段标签(用于展示)
1224
+ value: { // 单个值
1225
+ label: string; // 显示文本
1226
+ value: any; // 实际值
1227
+ } | Array<{ // 或多个值
1228
+ label: string;
1229
+ value: any;
1230
+ }> | null; // 或空值
1231
+ }
1232
+ ```
1233
+
1234
+ ---
1235
+
1236
+ ### URL 参数相关
1237
+
1238
+ #### filterToUrlParams
1239
+
1240
+ 将筛选值数组序列化为 URLSearchParams,保留 label 信息以便反序列化还原完整筛选状态。
1241
+
1242
+ | 参数 | 类型 | 默认值 | 说明 |
1243
+ |----------------|----------------|-----------------|--------------------------|
1244
+ | filterValue | `Array` | - | 筛选值数组 |
1245
+ | options.prefix | `string` | `'filterParams'` | URL 参数前缀,设为空字符串则不加前缀 |
1246
+
1247
+ **序列化格式**:
1248
+ - 单值且 `label === value`:`prefix[name]=value`(如输入框)
1249
+ - 单值且 `label !== value`:`prefix[name]=label:value`
1250
+ - 多值:`prefix[name]=label1:value1,label2:value2`
1251
+
1252
+ **使用示例:**
1253
+
1254
+ ```javascript
1255
+ import { filterToUrlParams } from '@kne/react-filter';
1256
+
1257
+ const params = filterToUrlParams([
1258
+ { name: 'keyword', label: '关键词', value: { label: '测试', value: '测试' } },
1259
+ { name: 'city', label: '城市', value: [{ label: '上海', value: '010' }, { label: '北京', value: '020' }] },
1260
+ ]);
1261
+ // params.toString() => 'filterParams[keyword]=测试&filterParams[city]=上海:010,北京:020'
1262
+
1263
+ // 自定义前缀
1264
+ filterToUrlParams(filterValue, { prefix: 'f' });
1265
+ // => 'f[keyword]=测试'
1266
+
1267
+ // 无前缀(直接平铺到 URL)
1268
+ filterToUrlParams(filterValue, { prefix: '' });
1269
+ // => 'keyword=测试'
1270
+ ```
1271
+
1272
+ ---
1273
+
1274
+ #### parseFilterEntry
1275
+
1276
+ 解析 URL 参数中的单个筛选值项,反序列化为 `{ label, value }` 对象。
1277
+
1278
+ | 参数 | 类型 | 说明 |
1279
+ |-----|----------|---------------|
1280
+ | str | `string` | URL 参数中的原始字符串 |
1281
+
1282
+ **解析规则**:
1283
+ - 无冒号:label 和 value 相同,如 `"测试"` → `{ label: '测试', value: '测试' }`
1284
+ - 有冒号:冒号前为 label,冒号后为 value,如 `"启用:active"` → `{ label: '启用', value: 'active' }`
1285
+
1286
+ ```javascript
1287
+ import { parseFilterEntry } from '@kne/react-filter';
1288
+
1289
+ parseFilterEntry('测试');
1290
+ // => { label: '测试', value: '测试' }
1291
+
1292
+ parseFilterEntry('启用:active');
1293
+ // => { label: '启用', value: 'active' }
1294
+ ```
1295
+
1296
+ ---
1297
+
1298
+ #### takeFilterEntry
1299
+
1300
+ 从 URL 参数中读取筛选值项,返回单选 `{ label, value }` 或多选数组。
340
1301
 
341
- ##### Range|DateTime.Range
1302
+ | 参数 | 类型 | 默认值 | 说明 |
1303
+ |-----------------|-------------------|-----------------|------------------|
1304
+ | searchParams | `URLSearchParams` | - | URL 参数对象 |
1305
+ | key | `string` | - | 参数名(不含前缀) |
1306
+ | options.multi | `boolean` | `false` | 是否多选 |
1307
+ | options.prefix | `string` | `'filterParams'` | URL 参数前缀 |
342
1308
 
343
- | 属性名 | 说明 | 类型 | 默认值 |
344
- |-------|--------------------------------------------------------------|----------|-----|
345
- | template | 将一组数据范围显示成一段用户可阅读的问题的模版,参数为筛选项的值,要求返回一个描述的字符串 | function | - |
346
- |placeholder| 当为字符串时前后两个输入框显示同样内容,当为一个数组时,第一个输入框显示数组的第一个值,第二个输入框显示为数组的第二个值 | string&#124;array | - |
1309
+ ```javascript
1310
+ import { takeFilterEntry } from '@kne/react-filter';
347
1311
 
348
- #### Range
1312
+ // URL: ?filterParams[city]=上海:010,北京:020
1313
+ takeFilterEntry(searchParams, 'city', { multi: true });
1314
+ // => [{ label: '上海', value: '010' }, { label: '北京', value: '020' }]
349
1315
 
350
- | 属性名 | 说明 | 类型 | 默认值 |
351
- |-------|--------------------------|--------|-----|
352
- |startProps| 该参数会结构以后传给第一个InputNumber | object |-|
353
- |endProps| 该参数会结构以后传给第二个InputNumber | object |-|
1316
+ takeFilterEntry(searchParams, 'keyword', { prefix: '' });
1317
+ // => { label: '测试', value: '测试' }
1318
+ ```
1319
+
1320
+ ---
1321
+
1322
+ #### createUrlFilterReader
1323
+
1324
+ 创建 URL 筛选参数读取器,自动追踪已消费的参数 key。配合 `useUrlFilter` 使用,readUrlParams 返回的 consumedKeys 可被自动清除。
1325
+
1326
+ | 参数 | 类型 | 默认值 | 说明 |
1327
+ |-----------------|-------------------|-----------------|----------|
1328
+ | searchParams | `URLSearchParams` | - | URL 参数对象 |
1329
+ | options.prefix | `string` | `'filterParams'` | URL 参数前缀 |
1330
+
1331
+ **返回值**:`{ takeFilterEntry, getConsumedKeys }`
1332
+
1333
+ ```javascript
1334
+ import { createUrlFilterReader } from '@kne/react-filter';
1335
+
1336
+ const { takeFilterEntry, getConsumedKeys } = createUrlFilterReader(searchParams);
1337
+ const keyword = takeFilterEntry('keyword');
1338
+ const city = takeFilterEntry('city', { multi: true });
1339
+ // getConsumedKeys() => ['filterParams[keyword]', 'filterParams[city]']
1340
+ ```
1341
+
1342
+ ---
1343
+
1344
+ #### useUrlFilter
1345
+
1346
+ 从 URL 参数初始化 Filter 状态的 hook,读取 URL 参数构建初始筛选值,并在挂载后自动清除已消费的 URL 参数。
1347
+
1348
+ > 需要 React Router 环境支持 `useSearchParams`。
1349
+
1350
+ | 参数 | 类型 | 说明 |
1351
+ |---------------------|------------|----------------------------------------------|
1352
+ | options.readUrlParams | `Function` | 读取 URL 参数并返回 `{ consumedKeys: string[], ...data }` |
1353
+ | options.buildFilter | `Function` | 接收 readUrlParams 的返回值,构建初始 filter 数组 |
1354
+
1355
+ **返回值**:`[filter, setFilter]`
1356
+
1357
+ ```javascript
1358
+ import { useUrlFilter, createUrlParamsReader } from '@kne/react-filter';
1359
+
1360
+ const [filter, setFilter] = useUrlFilter({
1361
+ readUrlParams: (searchParams) => {
1362
+ const { take, getConsumedKeys } = createUrlParamsReader(searchParams);
1363
+ const orgId = take('tenantOrgId');
1364
+ return { consumedKeys: getConsumedKeys(), orgId };
1365
+ },
1366
+ buildFilter: ({ orgId }) => [
1367
+ { name: 'status', value: { label: '开启', value: 'open' } },
1368
+ ...(orgId ? [{ name: 'tenantOrgId', value: { label: orgId, value: orgId } }] : [])
1369
+ ]
1370
+ });
1371
+ ```
354
1372
 
355
- #### List
1373
+ ---
356
1374
 
357
- | 属性名 | 说明 | 类型 | 默认值 |
358
- |----------|------------------------------------------------------------------------------|-------------------------|-----|
359
- | children | 筛选项的每一项的值,必须为 List.Item,也可以为一个render function可以接收到参数render({value,onChange}) | List.Item&#124;function | - |
360
- | size | 最大可以选中数量,等于1的时候为单选,操作逻辑和多选略有不同 | number |2|
361
- | options| 筛选项,为一个数组,每一项的值为{label,value}该属性有值优先取该值忽略children | array |-|
1375
+ #### useUrlFilterValue
362
1376
 
363
- #### List.Item
1377
+ `useUrlFilter` 的简化版,基于 `filterParams[key]` 格式自动解析 URL 参数,支持单选和多选。
364
1378
 
365
- | 属性名 | 说明 | 类型 | 默认值 |
366
- |-------|--------|--------|-----|
367
- | value | 筛选项的值 | - | - |
368
- | label | 筛选项显示项 | string | - |
1379
+ | 参数 | 类型 | 说明 |
1380
+ |---------|------------------------|--------------------------------|
1381
+ | mapping | `string[] \| Object` | URL 参数映射,支持数组、对象两种格式 |
369
1382
 
370
- #### SearchButton
1383
+ **数组形式(默认单选):**
1384
+ ```javascript
1385
+ import { useUrlFilterValue } from '@kne/react-filter';
371
1386
 
372
- 用于自行封装过滤选项时的工具组件。Text,Text.Number,Range使用该组件实现。该组件可以让筛选项有一个包裹层在鼠标经过时显示,同时有一个确定按钮,在确定按钮点击时筛选项的结果会提交到Filter。
1387
+ const [filter, setFilter] = useUrlFilterValue(['keyword', 'status']);
1388
+ // URL: ?filterParams[keyword]=前端开发&filterParams[status]=招聘中:active
1389
+ // → filter: [
1390
+ // { name: 'keyword', value: { label: '前端开发', value: '前端开发' } },
1391
+ // { name: 'status', value: { label: '招聘中', value: 'active' } }
1392
+ // ]
1393
+ ```
1394
+
1395
+ **对象形式(多选 + 自定义):**
1396
+ ```javascript
1397
+ const [filter, setFilter] = useUrlFilterValue({
1398
+ keyword: true, // 单选
1399
+ city: { multi: true }, // 多选
1400
+ status: (parsed) => { // 自定义转换
1401
+ return parsed ? { name: 'status', value: parsed } : null;
1402
+ }
1403
+ });
1404
+ // URL: ?filterParams[keyword]=测试&filterParams[city]=上海:010,北京:020
1405
+ ```
1406
+
1407
+ ---
1408
+
1409
+ #### createUrlParamsReader
1410
+
1411
+ 创建通用 URL 参数读取器,自动追踪已消费的参数 key。
1412
+
1413
+ | 参数 | 类型 | 说明 |
1414
+ |--------------|-------------------|----------|
1415
+ | searchParams | `URLSearchParams` | URL 参数对象 |
1416
+
1417
+ **返回值**:`{ take, getConsumedKeys }`
1418
+ - `take(key)` - 读取参数值,记录已消费
1419
+ - `getConsumedKeys()` - 返回已消费的 key 列表
1420
+
1421
+ ```javascript
1422
+ import { createUrlParamsReader } from '@kne/react-filter';
373
1423
 
374
- | 属性名 | 说明 | 类型 | 默认值 |
375
- |-------|---------------------------------------------------------------------------|---------|-------|
376
- | defaultActive | 默认是否激活活跃状态 | boolean | false |
377
- |defaultValue| 默认值通常不需要传,会取筛选器的对应项的值 |-|-|
378
- |buttonText| 确认按钮的文案 | string | 确定 |
379
- |template| 格式化结果函数 |function|-|
380
- |children| 是一个函数,需要传一个render函数返回内层的输入组件,会接收到children({value, setValue, setActive})参数 |function|-|
381
- |onBlur| 会模拟一个失去焦点的事件,点击该组件区域外的地方会触发并执行该函数 |function|-|
382
- |onSearch| 点击确认按钮时触发函数 |function|-|
383
- |onBeforeSearch| 点击确认按钮会先触发该函数如果该函数显示返回false值则不会再触发onSearch函数,用于再筛选项的值提交给筛选器之前的数据验证|function|-|
384
- |onActiveChange|激活状态改变时触发函数|function|-|
1424
+ const { take, getConsumedKeys } = createUrlParamsReader(searchParams);
1425
+ const orgId = take('tenantOrgId');
1426
+ const orgName = take('orgName');
1427
+ // getConsumedKeys() => ['tenantOrgId', 'orgName']
1428
+ ```
1429
+
1430
+ ---
1431
+
1432
+ #### stripConsumedUrlParams
1433
+
1434
+ 从 URL 参数中移除已消费的 key,返回新的 URLSearchParams 或 null(无变化时)。
1435
+
1436
+ | 参数 | 类型 | 说明 |
1437
+ |--------------|-------------------|-------------------|
1438
+ | searchParams | `URLSearchParams` | 当前 URL 参数 |
1439
+ | consumedKeys | `string[]` | 需要移除的 key 列表 |
1440
+
1441
+ **返回值**:`URLSearchParams | null`
1442
+
1443
+ ```javascript
1444
+ import { stripConsumedUrlParams } from '@kne/react-filter';
1445
+
1446
+ const nextParams = stripConsumedUrlParams(searchParams, ['tenantOrgId', 'orgName']);
1447
+ if (nextParams) {
1448
+ setSearchParams(nextParams, { replace: true });
1449
+ }
1450
+ ```
385
1451
 
386
- 其他参数参考对应内部组件的参数
1452
+ ---
387
1453
 
1454
+ ### 拦截器
1455
+
1456
+ 用于 SuperSelect 组件的 `{id, name}` 与 Filter 的 `{label, value}` 数据格式互转。
1457
+
1458
+ #### singleSelectInterceptor
1459
+
1460
+ 单选拦截器:`{id, name}` ↔ `{label, value}`。
1461
+
1462
+ | 属性 | 类型 | 说明 |
1463
+ |---------|------------|---------------------------------------|
1464
+ | input | `Function` | `{id, name}` → `{label, value}` 的转换 |
1465
+ | output | `Function` | `{label, value}` → `{id, name}` 的转换 |
1466
+
1467
+ #### multiSelectInterceptor
1468
+
1469
+ 多选拦截器:`[{id, name}]` ↔ `[{label, value}]`。
1470
+
1471
+ | 属性 | 类型 | 说明 |
1472
+ |---------|------------|-------------------------------------------|
1473
+ | input | `Function` | `[{id, name}]` → `[{label, value}]` 的转换 |
1474
+ | output | `Function` | `[{label, value}]` → `[{id, name}]` 的转换 |
1475
+
1476
+ #### filterInterceptors
1477
+
1478
+ 拦截器集合对象。
1479
+
1480
+ ```javascript
1481
+ import { filterInterceptors, singleSelectInterceptor, multiSelectInterceptor } from '@kne/react-filter';
1482
+
1483
+ // 两种引用方式等价
1484
+ filterInterceptors.single === singleSelectInterceptor; // true
1485
+ filterInterceptors.multi === multiSelectInterceptor; // true
1486
+ ```
1487
+
1488
+ **使用示例:**
1489
+
1490
+ ```javascript
1491
+ import { filterInterceptors } from '@kne/react-filter';
1492
+
1493
+ // 在 SuperSelect 组件中使用单选拦截
1494
+ <SuperSelect
1495
+ valueKey="id"
1496
+ labelKey="name"
1497
+ interceptor={filterInterceptors.single}
1498
+ /* ... */
1499
+ />
1500
+
1501
+ // 多选拦截
1502
+ <SuperSelect
1503
+ valueKey="id"
1504
+ labelKey="name"
1505
+ interceptor={filterInterceptors.multi}
1506
+ /* ... */
1507
+ />
1508
+ ```
1509
+
1510
+ ---
1511
+
1512
+ ### 工具方法
1513
+
1514
+ #### pickSelectValues
1515
+
1516
+ 从筛选值中提取原始值数组。支持原始值、`{ value }` 对象、`{ id }` 对象以及它们的数组。
1517
+
1518
+ | 参数 | 类型 | 说明 |
1519
+ |-------|-------|-------------|
1520
+ | value | `any` | 筛选值,支持多种格式 |
1521
+
1522
+ ```javascript
1523
+ import { pickSelectValues } from '@kne/react-filter';
1524
+
1525
+ pickSelectValues([{ value: 1 }, { id: 2 }, '3']);
1526
+ // => ['1', '2', '3']
1527
+
1528
+ pickSelectValues({ value: 'open' });
1529
+ // => ['open']
1530
+
1531
+ pickSelectValues(null);
1532
+ // => []
1533
+ ```
1534
+
1535
+ #### createFilterValueMapper
1536
+
1537
+ 声明式创建 mapFilterValue 函数。`Filter.getFilterValue` 默认只读取 `{ value }`,而 SuperSelectFilterItem 等组件使用 `{ id, name }` 格式,需要额外处理。此工具通过声明字段映射规则,自动生成转换函数。
1538
+
1539
+ | 参数 | 类型 | 说明 |
1540
+ |--------------|----------|-------------------|
1541
+ | fieldMappers | `Object` | 字段名到映射规则的映射 |
1542
+
1543
+ **映射规则类型:**
1544
+
1545
+ | 规则 | 说明 |
1546
+ |-----------|----------------------------------------|
1547
+ | `'string'` | 确保值为字符串类型 |
1548
+ | `'multi'` | 多选,从 filter entry 提取值数组 |
1549
+ | `'single'` | 单选,从 filter entry 提取第一个值 |
1550
+ | `Function` | 自定义转换,接收 `(rawValue, { entry, filter, value })` |
1551
+
1552
+ ```javascript
1553
+ import { createFilterValueMapper } from '@kne/react-filter';
1554
+
1555
+ const mapFilterValue = createFilterValueMapper({
1556
+ id: 'string',
1557
+ roles: 'multi',
1558
+ tenantOrgId: 'single',
1559
+ status: (rawValue) => normalizeStatus(rawValue)
1560
+ });
1561
+
1562
+ const filterValue = mapFilterValue(filter, Filter.getFilterValue);
1563
+ ```