@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 +1492 -316
- package/dist/index.css +703 -59
- package/dist/index.css.map +1 -1
- package/dist/index.js +2247 -897
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +2186 -835
- package/dist/index.modern.js.map +1 -1
- package/dist/locale/en-US.js +31 -0
- package/dist/locale/en-US.js.map +1 -0
- package/dist/locale/en-US.modern.js +31 -0
- package/dist/locale/en-US.modern.js.map +1 -0
- package/dist/locale/react-filter.js +65 -0
- package/dist/locale/react-filter.js.map +1 -0
- package/dist/locale/react-filter.modern.js +65 -0
- package/dist/locale/react-filter.modern.js.map +1 -0
- package/dist/locale/zh-CN.js +31 -0
- package/dist/locale/zh-CN.js.map +1 -0
- package/dist/locale/zh-CN.modern.js +31 -0
- package/dist/locale/zh-CN.modern.js.map +1 -0
- package/package.json +56 -37
- package/LICENSE +0 -21
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
|
-
-
|
|
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 {
|
|
36
|
-
const {
|
|
37
|
-
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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(`搜索参数: ${JSON.stringify(params, null, 2)}`);
|
|
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
|
-
-
|
|
124
|
-
-
|
|
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 {
|
|
128
|
-
const {
|
|
129
|
-
const {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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(`搜索参数: ${JSON.stringify(params, null, 2)}`);
|
|
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(<
|
|
331
|
+
render(<AdvancedFilterExample />);
|
|
195
332
|
|
|
196
333
|
```
|
|
197
334
|
|
|
198
|
-
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
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 {
|
|
204
|
-
const {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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={`请选择${label}`}
|
|
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
|
|
231
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
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: `${val || '?'}-${value?.value?.[1] || '?'}`,
|
|
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: `${value?.value?.[0] || '?'}-${val || '?'}`,
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
|
309
|
-
|
|
310
|
-
|
|
|
311
|
-
|
|
|
312
|
-
|
|
|
313
|
-
|
|
|
314
|
-
|
|
|
315
|
-
|
|
|
316
|
-
|
|
|
317
|
-
|
|
|
318
|
-
|
|
|
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
|
-
####
|
|
1031
|
+
#### SuperSelectFilterItem 通用选择器筛选
|
|
321
1032
|
|
|
322
|
-
|
|
323
|
-
CheckboxList(多选列表)
|
|
1033
|
+
基于 `@kne/super-select` 的通用选择器筛选项,支持单选/多选、搜索、全选等功能。
|
|
324
1034
|
|
|
325
|
-
|
|
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
|
-
|
|
1049
|
+
// 多选
|
|
1050
|
+
<SuperSelectFilterItem
|
|
1051
|
+
label="部门"
|
|
1052
|
+
options={[
|
|
1053
|
+
{ value: 'tech', label: '技术研发部' },
|
|
1054
|
+
{ value: 'product', label: '产品设计部' }
|
|
1055
|
+
]}
|
|
1056
|
+
/>
|
|
336
1057
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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|array | - |
|
|
1309
|
+
```javascript
|
|
1310
|
+
import { takeFilterEntry } from '@kne/react-filter';
|
|
347
1311
|
|
|
348
|
-
|
|
1312
|
+
// URL: ?filterParams[city]=上海:010,北京:020
|
|
1313
|
+
takeFilterEntry(searchParams, 'city', { multi: true });
|
|
1314
|
+
// => [{ label: '上海', value: '010' }, { label: '北京', value: '020' }]
|
|
349
1315
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
1373
|
+
---
|
|
356
1374
|
|
|
357
|
-
|
|
358
|
-
|----------|------------------------------------------------------------------------------|-------------------------|-----|
|
|
359
|
-
| children | 筛选项的每一项的值,必须为 List.Item,也可以为一个render function可以接收到参数render({value,onChange}) | List.Item|function | - |
|
|
360
|
-
| size | 最大可以选中数量,等于1的时候为单选,操作逻辑和多选略有不同 | number |2|
|
|
361
|
-
| options| 筛选项,为一个数组,每一项的值为{label,value}该属性有值优先取该值忽略children | array |-|
|
|
1375
|
+
#### useUrlFilterValue
|
|
362
1376
|
|
|
363
|
-
|
|
1377
|
+
`useUrlFilter` 的简化版,基于 `filterParams[key]` 格式自动解析 URL 参数,支持单选和多选。
|
|
364
1378
|
|
|
365
|
-
|
|
|
366
|
-
|
|
367
|
-
|
|
|
368
|
-
| label | 筛选项显示项 | string | - |
|
|
1379
|
+
| 参数 | 类型 | 说明 |
|
|
1380
|
+
|---------|------------------------|--------------------------------|
|
|
1381
|
+
| mapping | `string[] \| Object` | URL 参数映射,支持数组、对象两种格式 |
|
|
369
1382
|
|
|
370
|
-
|
|
1383
|
+
**数组形式(默认单选):**
|
|
1384
|
+
```javascript
|
|
1385
|
+
import { useUrlFilterValue } from '@kne/react-filter';
|
|
371
1386
|
|
|
372
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
+
```
|