@creekjs/web-components 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/.fatherrc.ts +13 -0
- package/.turbo/daemon/deec863de02760ed-turbo.log.2024-11-20 +0 -0
- package/README.md +1026 -0
- package/dist/bg-center/index.js +28 -0
- package/dist/creek-config-provider/CreekConfigContext.js +2 -0
- package/dist/creek-config-provider/index.js +14 -0
- package/dist/creek-hooks/index.js +1 -0
- package/dist/creek-hooks/useViewportHeight.js +147 -0
- package/dist/creek-icon/index.js +37 -0
- package/dist/creek-keep-alive/index.js +20 -0
- package/dist/creek-layout/CollapseButton.js +59 -0
- package/dist/creek-layout/Exception/NotFound.js +13 -0
- package/dist/creek-layout/Exception/NotFoundPage.js +5 -0
- package/dist/creek-layout/Exception/index.js +8 -0
- package/dist/creek-layout/HeaderContent/FullScreen.js +50 -0
- package/dist/creek-layout/HeaderContent/UserInfo.js +58 -0
- package/dist/creek-layout/HeaderContent/index.js +33 -0
- package/dist/creek-layout/index.js +86 -0
- package/dist/creek-loading/index.js +52 -0
- package/dist/creek-search/CreekSearch.js +51 -0
- package/dist/creek-search/CreekSearchContext.js +546 -0
- package/dist/creek-search/CreekSearchFilterDisplay.js +97 -0
- package/dist/creek-search/CreekSearchInput.js +96 -0
- package/dist/creek-search/CreekSearchValueSelector.js +422 -0
- package/dist/creek-search/index.js +5 -0
- package/dist/creek-search/type.js +1 -0
- package/dist/creek-table/SearchTable.js +121 -0
- package/dist/creek-table/TableOptionRender.js +65 -0
- package/dist/creek-table/TableViewContent.js +45 -0
- package/dist/creek-table/hooks/index.js +3 -0
- package/dist/creek-table/hooks/useAdaptiveToolBar.js +48 -0
- package/dist/creek-table/hooks/useAutoAddFilterToColumns.js +93 -0
- package/dist/creek-table/hooks/useElementDistance.js +58 -0
- package/dist/creek-table/index.js +25 -0
- package/dist/creek-table/toolBarRender.js +36 -0
- package/dist/creek-table/type.js +1 -0
- package/dist/index.js +7 -0
- package/package.json +19 -0
- package/src/bg-center/index.tsx +26 -0
- package/src/creek-config-provider/CreekConfigContext.tsx +7 -0
- package/src/creek-config-provider/index.tsx +12 -0
- package/src/creek-hooks/index.ts +1 -0
- package/src/creek-hooks/useViewportHeight.tsx +154 -0
- package/src/creek-icon/index.tsx +34 -0
- package/src/creek-keep-alive/index.tsx +11 -0
- package/src/creek-layout/CollapseButton.tsx +66 -0
- package/src/creek-layout/Exception/NotFound.tsx +12 -0
- package/src/creek-layout/Exception/NotFoundPage.tsx +4 -0
- package/src/creek-layout/Exception/index.tsx +10 -0
- package/src/creek-layout/HeaderContent/FullScreen.tsx +46 -0
- package/src/creek-layout/HeaderContent/UserInfo.tsx +54 -0
- package/src/creek-layout/HeaderContent/index.tsx +27 -0
- package/src/creek-layout/index.tsx +98 -0
- package/src/creek-loading/index.tsx +35 -0
- package/src/creek-search/CreekSearch.tsx +59 -0
- package/src/creek-search/CreekSearchContext.tsx +593 -0
- package/src/creek-search/CreekSearchFilterDisplay.tsx +84 -0
- package/src/creek-search/CreekSearchInput.tsx +75 -0
- package/src/creek-search/CreekSearchValueSelector.tsx +324 -0
- package/src/creek-search/index.tsx +5 -0
- package/src/creek-search/type.ts +9 -0
- package/src/creek-table/SearchTable.tsx +115 -0
- package/src/creek-table/TableOptionRender.tsx +57 -0
- package/src/creek-table/TableViewContent.tsx +44 -0
- package/src/creek-table/hooks/index.ts +4 -0
- package/src/creek-table/hooks/useAdaptiveToolBar.tsx +45 -0
- package/src/creek-table/hooks/useAutoAddFilterToColumns.tsx +90 -0
- package/src/creek-table/hooks/useElementDistance.tsx +64 -0
- package/src/creek-table/index.tsx +16 -0
- package/src/creek-table/toolBarRender.tsx +28 -0
- package/src/creek-table/type.ts +21 -0
- package/src/index.tsx +8 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
import { ParamsType, ProColumnType, ProTableProps } from '@ant-design/pro-components';
|
|
2
|
+
import dayjs, { Dayjs } from 'dayjs';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import React, { createContext, ReactNode, useContext, useRef, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
import { CreekSearchAddFilterOption, CreekSearchFilter } from './type';
|
|
7
|
+
|
|
8
|
+
// ValueType配置接口
|
|
9
|
+
interface ValueTypeConfig {
|
|
10
|
+
// 是否需要显示值选择器
|
|
11
|
+
showValueSelector: boolean;
|
|
12
|
+
// 日期格式化字符串
|
|
13
|
+
dateFormat?: string;
|
|
14
|
+
// 是否为范围类型
|
|
15
|
+
isRange?: boolean;
|
|
16
|
+
// 是否为日期时间类型
|
|
17
|
+
isDateTime?: boolean;
|
|
18
|
+
// 显示名称
|
|
19
|
+
displayName: string;
|
|
20
|
+
// 组件类型标识
|
|
21
|
+
componentType: 'input' | 'select' | 'date' | 'dateRange' | 'time' | 'timeRange' | 'number' | 'switch' | 'radio' | 'checkbox';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ValueType配置映射表
|
|
25
|
+
const VALUE_TYPE_CONFIG_MAP: Record<ProColumnType<any, any>['valueType'], ValueTypeConfig> = {
|
|
26
|
+
// 文本输入类
|
|
27
|
+
text: {
|
|
28
|
+
showValueSelector: false,
|
|
29
|
+
displayName: '文本',
|
|
30
|
+
componentType: 'input',
|
|
31
|
+
},
|
|
32
|
+
textarea: {
|
|
33
|
+
showValueSelector: false,
|
|
34
|
+
displayName: '多行文本',
|
|
35
|
+
componentType: 'input',
|
|
36
|
+
},
|
|
37
|
+
password: {
|
|
38
|
+
showValueSelector: false,
|
|
39
|
+
displayName: '密码',
|
|
40
|
+
componentType: 'input',
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
// 数字类
|
|
44
|
+
digit: {
|
|
45
|
+
showValueSelector: true,
|
|
46
|
+
displayName: '数字',
|
|
47
|
+
componentType: 'number',
|
|
48
|
+
},
|
|
49
|
+
money: {
|
|
50
|
+
showValueSelector: true,
|
|
51
|
+
displayName: '金额',
|
|
52
|
+
componentType: 'number',
|
|
53
|
+
},
|
|
54
|
+
percent: {
|
|
55
|
+
showValueSelector: true,
|
|
56
|
+
displayName: '百分比',
|
|
57
|
+
componentType: 'number',
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// 选择类
|
|
61
|
+
select: {
|
|
62
|
+
showValueSelector: true,
|
|
63
|
+
displayName: '选择器',
|
|
64
|
+
componentType: 'select',
|
|
65
|
+
},
|
|
66
|
+
radio: {
|
|
67
|
+
showValueSelector: true,
|
|
68
|
+
displayName: '单选',
|
|
69
|
+
componentType: 'radio',
|
|
70
|
+
},
|
|
71
|
+
radioButton: {
|
|
72
|
+
showValueSelector: true,
|
|
73
|
+
displayName: '单选按钮',
|
|
74
|
+
componentType: 'radio',
|
|
75
|
+
},
|
|
76
|
+
checkbox: {
|
|
77
|
+
showValueSelector: true,
|
|
78
|
+
displayName: '多选',
|
|
79
|
+
componentType: 'checkbox',
|
|
80
|
+
},
|
|
81
|
+
switch: {
|
|
82
|
+
showValueSelector: true,
|
|
83
|
+
displayName: '开关',
|
|
84
|
+
componentType: 'radio',
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// 日期类 - 单选
|
|
88
|
+
date: {
|
|
89
|
+
showValueSelector: true,
|
|
90
|
+
dateFormat: 'YYYY-MM-DD',
|
|
91
|
+
isDateTime: true,
|
|
92
|
+
isRange: false,
|
|
93
|
+
displayName: '日期',
|
|
94
|
+
componentType: 'date',
|
|
95
|
+
},
|
|
96
|
+
dateWeek: {
|
|
97
|
+
showValueSelector: true,
|
|
98
|
+
dateFormat: 'YYYY-wo',
|
|
99
|
+
isDateTime: true,
|
|
100
|
+
isRange: false,
|
|
101
|
+
displayName: '周',
|
|
102
|
+
componentType: 'date',
|
|
103
|
+
},
|
|
104
|
+
dateMonth: {
|
|
105
|
+
showValueSelector: true,
|
|
106
|
+
dateFormat: 'YYYY-MM',
|
|
107
|
+
isDateTime: true,
|
|
108
|
+
isRange: false,
|
|
109
|
+
displayName: '月份',
|
|
110
|
+
componentType: 'date',
|
|
111
|
+
},
|
|
112
|
+
dateQuarter: {
|
|
113
|
+
showValueSelector: true,
|
|
114
|
+
dateFormat: 'YYYY-[Q]Q',
|
|
115
|
+
isDateTime: true,
|
|
116
|
+
isRange: false,
|
|
117
|
+
displayName: '季度',
|
|
118
|
+
componentType: 'date',
|
|
119
|
+
},
|
|
120
|
+
dateYear: {
|
|
121
|
+
showValueSelector: true,
|
|
122
|
+
dateFormat: 'YYYY',
|
|
123
|
+
isDateTime: true,
|
|
124
|
+
isRange: false,
|
|
125
|
+
displayName: '年份',
|
|
126
|
+
componentType: 'date',
|
|
127
|
+
},
|
|
128
|
+
dateTime: {
|
|
129
|
+
showValueSelector: true,
|
|
130
|
+
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
131
|
+
isDateTime: true,
|
|
132
|
+
isRange: false,
|
|
133
|
+
displayName: '日期时间',
|
|
134
|
+
componentType: 'date',
|
|
135
|
+
},
|
|
136
|
+
time: {
|
|
137
|
+
showValueSelector: true,
|
|
138
|
+
dateFormat: 'HH:mm:ss',
|
|
139
|
+
isDateTime: true,
|
|
140
|
+
isRange: false,
|
|
141
|
+
displayName: '时间',
|
|
142
|
+
componentType: 'time',
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// 日期类 - 范围
|
|
146
|
+
dateRange: {
|
|
147
|
+
showValueSelector: true,
|
|
148
|
+
dateFormat: 'YYYY-MM-DD',
|
|
149
|
+
isDateTime: true,
|
|
150
|
+
isRange: true,
|
|
151
|
+
displayName: '日期范围',
|
|
152
|
+
componentType: 'dateRange',
|
|
153
|
+
},
|
|
154
|
+
dateTimeRange: {
|
|
155
|
+
showValueSelector: true,
|
|
156
|
+
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
157
|
+
isDateTime: true,
|
|
158
|
+
isRange: true,
|
|
159
|
+
displayName: '日期时间范围',
|
|
160
|
+
componentType: 'dateRange',
|
|
161
|
+
},
|
|
162
|
+
timeRange: {
|
|
163
|
+
showValueSelector: true,
|
|
164
|
+
dateFormat: 'HH:mm:ss',
|
|
165
|
+
isDateTime: true,
|
|
166
|
+
isRange: true,
|
|
167
|
+
displayName: '时间范围',
|
|
168
|
+
componentType: 'timeRange',
|
|
169
|
+
},
|
|
170
|
+
dateWeekRange: {
|
|
171
|
+
showValueSelector: true,
|
|
172
|
+
dateFormat: 'YYYY-wo',
|
|
173
|
+
isDateTime: true,
|
|
174
|
+
isRange: true,
|
|
175
|
+
displayName: '周范围',
|
|
176
|
+
componentType: 'dateRange',
|
|
177
|
+
},
|
|
178
|
+
dateMonthRange: {
|
|
179
|
+
showValueSelector: true,
|
|
180
|
+
dateFormat: 'YYYY-MM',
|
|
181
|
+
isDateTime: true,
|
|
182
|
+
isRange: true,
|
|
183
|
+
displayName: '月份范围',
|
|
184
|
+
componentType: 'dateRange',
|
|
185
|
+
},
|
|
186
|
+
dateQuarterRange: {
|
|
187
|
+
showValueSelector: true,
|
|
188
|
+
dateFormat: 'YYYY-[Q]Q',
|
|
189
|
+
isDateTime: true,
|
|
190
|
+
isRange: true,
|
|
191
|
+
displayName: '季度范围',
|
|
192
|
+
componentType: 'dateRange',
|
|
193
|
+
},
|
|
194
|
+
dateYearRange: {
|
|
195
|
+
showValueSelector: true,
|
|
196
|
+
dateFormat: 'YYYY',
|
|
197
|
+
isDateTime: true,
|
|
198
|
+
isRange: true,
|
|
199
|
+
displayName: '年份范围',
|
|
200
|
+
componentType: 'dateRange',
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export interface SearchContextValue<T extends ParamsType, U extends ParamsType, ValueType = 'text'> {
|
|
205
|
+
// 状态
|
|
206
|
+
searchValue: string;
|
|
207
|
+
filters: CreekSearchFilter[];
|
|
208
|
+
showValueSelector: boolean;
|
|
209
|
+
selectedColumn: ProColumnType<T, U>;
|
|
210
|
+
tempValue: any;
|
|
211
|
+
|
|
212
|
+
// 引用
|
|
213
|
+
inputRef: React.RefObject<any>;
|
|
214
|
+
|
|
215
|
+
// 配置
|
|
216
|
+
columns: ProTableProps<T, U, ValueType>['columns'];
|
|
217
|
+
filterOptions: ProTableProps<T, U, ValueType>['columns'];
|
|
218
|
+
onSubmit?: (params: U) => void;
|
|
219
|
+
beforeSearchSubmit?: (params: Record<string, any>) => Record<string, any>;
|
|
220
|
+
|
|
221
|
+
// 状态更新方法
|
|
222
|
+
setSearchValue: (value: string) => void;
|
|
223
|
+
setFilters: React.Dispatch<React.SetStateAction<CreekSearchFilter[]>>;
|
|
224
|
+
setShowValueSelector: (show: boolean) => void;
|
|
225
|
+
setSelectedColumn: (column: any) => void;
|
|
226
|
+
setTempValue: (value: any) => void;
|
|
227
|
+
|
|
228
|
+
// 业务方法
|
|
229
|
+
handleSearch: (value: string) => void;
|
|
230
|
+
handleSelectColumn: (value: string) => void;
|
|
231
|
+
addFilter: (key: string, option: CreekSearchAddFilterOption) => void;
|
|
232
|
+
confirmAddFilter: () => void;
|
|
233
|
+
cancelValueSelector: () => void;
|
|
234
|
+
removeFilter: (filterId: string) => void;
|
|
235
|
+
handelRest: () => void;
|
|
236
|
+
|
|
237
|
+
// 工具方法
|
|
238
|
+
getColumnOptions: (column: any) => Array<{ label: string; value: string }>;
|
|
239
|
+
getDisplayText: (column: any, value: any) => any;
|
|
240
|
+
hasOptions: (column: any) => boolean;
|
|
241
|
+
filtersToParams: (filters: CreekSearchFilter[]) => ParamsType;
|
|
242
|
+
getValueTypeConfig: (valueType?: ProColumnType<T, U>['valueType']) => ValueTypeConfig;
|
|
243
|
+
formatDateValue: (value: any, config: ValueTypeConfig) => string | string[];
|
|
244
|
+
shouldShowValueSelector: (column: any) => boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const SearchContext = createContext<SearchContextValue<any, any, any> | null>(null);
|
|
248
|
+
|
|
249
|
+
export const useSearchContext = <T extends ParamsType, U extends ParamsType, ValueType = 'text'>() => {
|
|
250
|
+
const context = useContext(SearchContext);
|
|
251
|
+
if (!context) {
|
|
252
|
+
throw new Error('useSearchContext must be used within CreekSearchProvider');
|
|
253
|
+
}
|
|
254
|
+
return context as SearchContextValue<T, U, ValueType>;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
interface CreekSearchProviderProps<T extends ParamsType, U extends ParamsType, ValueType = 'text'> {
|
|
258
|
+
columns: ProTableProps<T, U, ValueType>['columns'];
|
|
259
|
+
onSubmit?: (params: U) => void;
|
|
260
|
+
beforeSearchSubmit?: (params: Record<string, any>) => Record<string, any>;
|
|
261
|
+
children: ReactNode;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export const CreekSearchProvider = <T extends ParamsType, U extends ParamsType, ValueType = 'text'>({
|
|
265
|
+
columns = [],
|
|
266
|
+
onSubmit,
|
|
267
|
+
beforeSearchSubmit,
|
|
268
|
+
children,
|
|
269
|
+
}: CreekSearchProviderProps<T, U, ValueType>) => {
|
|
270
|
+
const [searchValue, setSearchValue] = useState<string>('');
|
|
271
|
+
const [filters, setFilters] = useState<CreekSearchFilter[]>([]);
|
|
272
|
+
const [showValueSelector, setShowValueSelector] = useState(false);
|
|
273
|
+
const [selectedColumn, setSelectedColumn] = useState<any>(null);
|
|
274
|
+
const [tempValue, setTempValue] = useState<any>(null);
|
|
275
|
+
|
|
276
|
+
const inputRef = useRef<any>(null);
|
|
277
|
+
|
|
278
|
+
// 筛选条件配置
|
|
279
|
+
const filterOptions = columns?.filter((item) => item.search !== false || item.hideInSearch !== false);
|
|
280
|
+
|
|
281
|
+
// 获取valueType配置
|
|
282
|
+
const getValueTypeConfig = (valueType?: ProColumnType<T, U>['valueType']) => {
|
|
283
|
+
// 如果valueType为undefined,使用默认的text类型
|
|
284
|
+
const validValueType = valueType || 'text';
|
|
285
|
+
return (VALUE_TYPE_CONFIG_MAP[validValueType] || VALUE_TYPE_CONFIG_MAP['text']) as unknown as ValueTypeConfig;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// 判断是否需要显示值选择器
|
|
289
|
+
const shouldShowValueSelector = (column: any): boolean => {
|
|
290
|
+
const config = getValueTypeConfig(column.valueType);
|
|
291
|
+
return config.showValueSelector || hasOptions(column);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// 格式化日期值
|
|
295
|
+
const formatDateValue = (value: any, config: ValueTypeConfig): string | string[] => {
|
|
296
|
+
if (!value || !config.isDateTime) return value;
|
|
297
|
+
|
|
298
|
+
const formatValue = (val: any): string => {
|
|
299
|
+
if (!val) return val;
|
|
300
|
+
const day = val.format ? (val as Dayjs) : dayjs(val);
|
|
301
|
+
return day.format(config.dateFormat);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
if (config.isRange && Array.isArray(value)) {
|
|
305
|
+
const [start, end] = value;
|
|
306
|
+
return [formatValue(start), formatValue(end)];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return formatValue(value);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// 获取选项数据 - 兼容 valueEnum 和 fieldProps.options
|
|
313
|
+
const getColumnOptions = (column: any) => {
|
|
314
|
+
// 优先使用 valueEnum
|
|
315
|
+
if (column.valueEnum) {
|
|
316
|
+
if (typeof column.valueEnum === 'object') {
|
|
317
|
+
return Object.entries(column.valueEnum).map(([key, value]: [string, any]) => ({
|
|
318
|
+
label: typeof value === 'object' ? value.text || value.label || key : value,
|
|
319
|
+
value: key,
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 然后检查 fieldProps.options
|
|
325
|
+
if (column.fieldProps?.options) {
|
|
326
|
+
return column.fieldProps.options.map((option: any) => ({
|
|
327
|
+
label: option.label || option.text || option.value,
|
|
328
|
+
value: option.value,
|
|
329
|
+
}));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return [];
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// 获取显示文本 - 兼容 valueEnum 和 fieldProps.options
|
|
336
|
+
const getDisplayText = (column: any, value: any) => {
|
|
337
|
+
// 处理空值
|
|
338
|
+
if (value === undefined || value === null || value === '') {
|
|
339
|
+
return value;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const config = getValueTypeConfig(column.valueType);
|
|
343
|
+
|
|
344
|
+
// 处理日期时间类型
|
|
345
|
+
if (config.isDateTime) {
|
|
346
|
+
const formattedValue = formatDateValue(value, config);
|
|
347
|
+
if (config.isRange && Array.isArray(formattedValue)) {
|
|
348
|
+
return `${formattedValue[0]} ~ ${formattedValue[1]}`;
|
|
349
|
+
}
|
|
350
|
+
return formattedValue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 处理数组类型(多选等)
|
|
354
|
+
if (Array.isArray(value)) {
|
|
355
|
+
return value.join(' | ');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 先尝试从 valueEnum 获取
|
|
359
|
+
if (column.valueEnum && column.valueEnum[value]) {
|
|
360
|
+
const enumItem = column.valueEnum[value];
|
|
361
|
+
return typeof enumItem === 'object' ? enumItem.text || enumItem.label || value : enumItem;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 再尝试从 fieldProps.options 获取
|
|
365
|
+
if (column.fieldProps?.options) {
|
|
366
|
+
const option = column.fieldProps.options.find((opt: any) => opt.value === value);
|
|
367
|
+
if (option) {
|
|
368
|
+
return option.label || option.text || option.value;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (column.valueType === 'switch' && typeof value === 'boolean') {
|
|
373
|
+
return value ? '开启' : '关闭';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return value;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// 判断是否有选项配置
|
|
380
|
+
const hasOptions = (column: any) => {
|
|
381
|
+
return !!(column.valueEnum || column.fieldProps?.options);
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// 将 filters 转换成 params 对象
|
|
385
|
+
const filtersToParams: SearchContextValue<T, U, ValueType>['filtersToParams'] = (filters) => {
|
|
386
|
+
let params = {} as ParamsType;
|
|
387
|
+
|
|
388
|
+
// 将 filters 数组转换为 params 对象
|
|
389
|
+
filters.forEach((filter) => {
|
|
390
|
+
if (filter.dataIndex && filter.value !== undefined && filter.value !== null) {
|
|
391
|
+
params[filter.dataIndex] = filter.value;
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// 如果存在 beforeSearchSubmit 钩子,则调用它来修改 params
|
|
396
|
+
if (beforeSearchSubmit && _.isFunction(beforeSearchSubmit)) {
|
|
397
|
+
params = beforeSearchSubmit(params);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return params;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// 处理搜索输入
|
|
404
|
+
const handleSearch = (value: string): void => {
|
|
405
|
+
// 检测筛选条件格式 "条件名: 值"
|
|
406
|
+
const colonIndex = value.lastIndexOf(':');
|
|
407
|
+
|
|
408
|
+
if (colonIndex > 0) {
|
|
409
|
+
const filterType = value.substring(0, colonIndex).trim();
|
|
410
|
+
const filterValue = value.substring(colonIndex + 1).trim();
|
|
411
|
+
|
|
412
|
+
const matchingFilter = filterOptions.find((f) => f.title === filterType || f.key === filterType);
|
|
413
|
+
|
|
414
|
+
if (matchingFilter && filterValue) {
|
|
415
|
+
addFilter(matchingFilter.dataIndex as string, {
|
|
416
|
+
value: filterValue,
|
|
417
|
+
displayText: getDisplayText(matchingFilter, filterValue),
|
|
418
|
+
});
|
|
419
|
+
setSearchValue('');
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
setSearchValue(value);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// 处理选择列名
|
|
427
|
+
const handleSelectColumn = (value: string) => {
|
|
428
|
+
const selectedOption = filterOptions.find((option) => option.dataIndex === value);
|
|
429
|
+
|
|
430
|
+
if (selectedOption) {
|
|
431
|
+
const shouldShow = shouldShowValueSelector(selectedOption);
|
|
432
|
+
|
|
433
|
+
setSelectedColumn(selectedOption);
|
|
434
|
+
setShowValueSelector(shouldShow);
|
|
435
|
+
setSearchValue(`${selectedOption.title}: `);
|
|
436
|
+
setTempValue(null);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// 添加筛选条件
|
|
441
|
+
const addFilter: SearchContextValue<T, U, ValueType>['addFilter'] = (key, options) => {
|
|
442
|
+
const filterConfig = filterOptions.find((f) => f.dataIndex === key);
|
|
443
|
+
if (!filterConfig) return;
|
|
444
|
+
|
|
445
|
+
setFilters((prev) => {
|
|
446
|
+
let _prev = prev || [];
|
|
447
|
+
|
|
448
|
+
const existingFilter = _prev.find((f) => f.dataIndex === key);
|
|
449
|
+
|
|
450
|
+
if (existingFilter) {
|
|
451
|
+
_prev = _prev.map((item) => {
|
|
452
|
+
if (item.dataIndex === key) {
|
|
453
|
+
return {
|
|
454
|
+
...item,
|
|
455
|
+
value: options.value,
|
|
456
|
+
displayText: getDisplayText(filterConfig, options.value),
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
return item;
|
|
460
|
+
});
|
|
461
|
+
} else {
|
|
462
|
+
_prev.push({
|
|
463
|
+
dataIndex: key,
|
|
464
|
+
title: filterConfig.title as string,
|
|
465
|
+
value: options.value,
|
|
466
|
+
displayText: getDisplayText(filterConfig, options.value),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const _filters = [..._prev];
|
|
471
|
+
const params = filtersToParams(_filters);
|
|
472
|
+
|
|
473
|
+
if (onSubmit && _.isFunction(onSubmit)) {
|
|
474
|
+
onSubmit(params as U);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return _filters;
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// 确认添加筛选条件
|
|
482
|
+
const confirmAddFilter = () => {
|
|
483
|
+
|
|
484
|
+
console.log(selectedColumn, '1111');
|
|
485
|
+
|
|
486
|
+
if (!selectedColumn || tempValue == null) return;
|
|
487
|
+
|
|
488
|
+
const { valueType, dataIndex } = selectedColumn;
|
|
489
|
+
const config = getValueTypeConfig(valueType);
|
|
490
|
+
|
|
491
|
+
let value: any = tempValue;
|
|
492
|
+
let displayValue: string = '';
|
|
493
|
+
|
|
494
|
+
if (config.isDateTime) {
|
|
495
|
+
const formattedValue = formatDateValue(tempValue, config);
|
|
496
|
+
value = formattedValue;
|
|
497
|
+
|
|
498
|
+
if (config.isRange && Array.isArray(formattedValue)) {
|
|
499
|
+
displayValue = `${formattedValue[0]} ~ ${formattedValue[1]}`;
|
|
500
|
+
} else {
|
|
501
|
+
displayValue = formattedValue as string;
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
displayValue = getDisplayText(selectedColumn, tempValue);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
console.log(value, 'value');
|
|
508
|
+
addFilter(dataIndex as string, { value, displayText: displayValue });
|
|
509
|
+
|
|
510
|
+
// 收尾
|
|
511
|
+
setSearchValue('');
|
|
512
|
+
setShowValueSelector(false);
|
|
513
|
+
setSelectedColumn(null);
|
|
514
|
+
setTempValue(null);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// 取消选择
|
|
518
|
+
const cancelValueSelector = () => {
|
|
519
|
+
setShowValueSelector(false);
|
|
520
|
+
setSelectedColumn(null);
|
|
521
|
+
setTempValue(null);
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
// 删除筛选条件
|
|
525
|
+
const removeFilter = (filterId: string) => {
|
|
526
|
+
setFilters((prev) => {
|
|
527
|
+
const _prev = prev?.filter((f) => f.dataIndex !== filterId);
|
|
528
|
+
const _filters = [..._prev];
|
|
529
|
+
const params = filtersToParams(_filters);
|
|
530
|
+
|
|
531
|
+
if (onSubmit && _.isFunction(onSubmit)) {
|
|
532
|
+
onSubmit(params as U);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return _prev || [];
|
|
536
|
+
});
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// 重置所有条件
|
|
540
|
+
const handelRest = () => {
|
|
541
|
+
setFilters(() => {
|
|
542
|
+
if (onSubmit && _.isFunction(onSubmit)) {
|
|
543
|
+
onSubmit({} as U);
|
|
544
|
+
}
|
|
545
|
+
return [];
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const contextValue: SearchContextValue<T, U, ValueType> = {
|
|
550
|
+
// 状态
|
|
551
|
+
searchValue,
|
|
552
|
+
filters,
|
|
553
|
+
showValueSelector,
|
|
554
|
+
selectedColumn,
|
|
555
|
+
tempValue,
|
|
556
|
+
|
|
557
|
+
// 引用
|
|
558
|
+
inputRef,
|
|
559
|
+
|
|
560
|
+
// 配置
|
|
561
|
+
columns,
|
|
562
|
+
filterOptions,
|
|
563
|
+
onSubmit,
|
|
564
|
+
beforeSearchSubmit,
|
|
565
|
+
|
|
566
|
+
// 状态更新方法
|
|
567
|
+
setSearchValue,
|
|
568
|
+
setFilters,
|
|
569
|
+
setShowValueSelector,
|
|
570
|
+
setSelectedColumn,
|
|
571
|
+
setTempValue,
|
|
572
|
+
|
|
573
|
+
// 业务方法
|
|
574
|
+
handleSearch,
|
|
575
|
+
handleSelectColumn,
|
|
576
|
+
addFilter,
|
|
577
|
+
confirmAddFilter,
|
|
578
|
+
cancelValueSelector,
|
|
579
|
+
removeFilter,
|
|
580
|
+
handelRest,
|
|
581
|
+
|
|
582
|
+
// 工具方法
|
|
583
|
+
getColumnOptions,
|
|
584
|
+
getDisplayText,
|
|
585
|
+
hasOptions,
|
|
586
|
+
filtersToParams,
|
|
587
|
+
getValueTypeConfig,
|
|
588
|
+
formatDateValue,
|
|
589
|
+
shouldShowValueSelector,
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
return <SearchContext.Provider value={contextValue}>{children}</SearchContext.Provider>;
|
|
593
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { DeleteOutlined } from '@ant-design/icons';
|
|
2
|
+
import { ParamsType } from '@ant-design/pro-components';
|
|
3
|
+
import { Flex, Space, Tag } from 'antd';
|
|
4
|
+
import { createStyles } from 'antd-style';
|
|
5
|
+
import classNames from 'classnames';
|
|
6
|
+
|
|
7
|
+
import { useSearchContext } from './CreekSearchContext';
|
|
8
|
+
import { CreekSearchFilter } from './type';
|
|
9
|
+
|
|
10
|
+
// 样式定义
|
|
11
|
+
const useStyles = createStyles(({ token }) => ({
|
|
12
|
+
filtersDisplay: {
|
|
13
|
+
padding: `0 0 ${token.padding}px`,
|
|
14
|
+
display: 'flex',
|
|
15
|
+
alignItems: 'center',
|
|
16
|
+
flexWrap: 'wrap',
|
|
17
|
+
gap: token.marginXS,
|
|
18
|
+
},
|
|
19
|
+
filterTag: {
|
|
20
|
+
borderRadius: token.borderRadiusSM,
|
|
21
|
+
fontSize: token.fontSizeSM,
|
|
22
|
+
backgroundColor: '#f2f3f5',
|
|
23
|
+
padding: `2px 12px 2px 8px`,
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
filterTagTitle: {
|
|
27
|
+
color: '#81838A',
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
filterTagValue: {
|
|
31
|
+
color: '#42464E',
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
clearButtonContainer: {
|
|
35
|
+
marginLeft: token.marginXS,
|
|
36
|
+
borderLeft: `1px solid #EBEDF1`,
|
|
37
|
+
paddingLeft: 8,
|
|
38
|
+
height: 20,
|
|
39
|
+
},
|
|
40
|
+
clearTextContainer: {
|
|
41
|
+
color: '#1E2128',
|
|
42
|
+
fontWeight: 500,
|
|
43
|
+
cursor: 'pointer',
|
|
44
|
+
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
export type CreekFilterDisplayProps<T, U, ValueType> = {
|
|
49
|
+
className?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const CreekFilterDisplay = <T extends ParamsType, U extends ParamsType, ValueType = 'text'>(props: CreekFilterDisplayProps<T, U, ValueType>) => {
|
|
53
|
+
const { styles } = useStyles();
|
|
54
|
+
const { filters, removeFilter, handelRest } = useSearchContext<T, U, ValueType>();
|
|
55
|
+
|
|
56
|
+
const { className } = props;
|
|
57
|
+
|
|
58
|
+
// 渲染筛选条件标签
|
|
59
|
+
const renderFilterTags = (filterList: CreekSearchFilter[]) => (
|
|
60
|
+
<Space size={4} wrap>
|
|
61
|
+
{filterList?.map((filter) => (
|
|
62
|
+
<Tag key={filter.dataIndex as string} closable onClose={() => removeFilter(filter.dataIndex as string)} className={styles.filterTag}>
|
|
63
|
+
<span className={styles.filterTagTitle}> {filter.title as string}:</span>
|
|
64
|
+
<span className={styles.filterTagValue}> {filter.displayText}</span>
|
|
65
|
+
</Tag>
|
|
66
|
+
))}
|
|
67
|
+
</Space>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// 如果没有筛选条件,不渲染
|
|
71
|
+
if (!filters || filters.length === 0) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Flex className={classNames(styles.filtersDisplay, className)} wrap>
|
|
77
|
+
<span>{renderFilterTags(filters)}</span>
|
|
78
|
+
<Space className={styles.clearButtonContainer} align="center" size={4} onClick={handelRest}>
|
|
79
|
+
<DeleteOutlined />
|
|
80
|
+
<span className={styles.clearTextContainer}>清空</span>
|
|
81
|
+
</Space>
|
|
82
|
+
</Flex>
|
|
83
|
+
);
|
|
84
|
+
};
|