c-admin-kit 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 +232 -0
- package/dist/c-admin-kit.css +1 -0
- package/dist/c-admin-kit.js +2482 -0
- package/dist/c-admin-kit.umd.cjs +10 -0
- package/dist/components/AsyncButton/index.vue.d.ts +48 -0
- package/dist/components/CollapsibleContainer/index.vue.d.ts +73 -0
- package/dist/components/CustomDrawer/index.vue.d.ts +149 -0
- package/dist/components/ImagePreview/index.vue.d.ts +34 -0
- package/dist/components/SearchBox/index.vue.d.ts +122 -0
- package/dist/components/SelectWithAll/index.vue.d.ts +169 -0
- package/dist/components/SelectWithPage/index.vue.d.ts +78 -0
- package/dist/components/SimpleTable/index.vue.d.ts +337 -0
- package/dist/components/SimpleTable/useTableColumnConfig.d.ts +5 -0
- package/dist/components/diff/index.vue.d.ts +33 -0
- package/dist/components/index.d.ts +12 -0
- package/dist/composables/index.d.ts +6 -0
- package/dist/composables/useConfirmAction.d.ts +5 -0
- package/dist/composables/useConfirmSubmit.d.ts +5 -0
- package/dist/composables/useDialog.d.ts +5 -0
- package/dist/composables/useDownload.d.ts +5 -0
- package/dist/composables/useForm.d.ts +5 -0
- package/dist/composables/useListPage.d.ts +5 -0
- package/dist/index.d.ts +9 -0
- package/dist/types.d.ts +282 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/scroll-to.d.ts +7 -0
- package/dist/utils/searchFieldFactory.d.ts +34 -0
- package/dist/utils/treeManager.d.ts +64 -0
- package/dist/utils/validate.d.ts +30 -0
- package/package.json +86 -0
- package/src/components/AsyncButton/index.vue +58 -0
- package/src/components/CollapsibleContainer/index.vue +240 -0
- package/src/components/CustomDrawer/index.vue +167 -0
- package/src/components/ImagePreview/index.vue +88 -0
- package/src/components/SearchBox/index.vue +582 -0
- package/src/components/SelectWithAll/index.vue +281 -0
- package/src/components/SelectWithPage/index.vue +204 -0
- package/src/components/SimpleTable/index.vue +781 -0
- package/src/components/SimpleTable/useTableColumnConfig.ts +139 -0
- package/src/components/diff/index.vue +265 -0
- package/src/components/index.ts +35 -0
- package/src/composables/index.ts +6 -0
- package/src/composables/useConfirmAction.ts +62 -0
- package/src/composables/useConfirmSubmit.ts +68 -0
- package/src/composables/useDialog.ts +48 -0
- package/src/composables/useDownload.ts +83 -0
- package/src/composables/useForm.ts +114 -0
- package/src/composables/useListPage.ts +243 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +44 -0
- package/src/types.ts +344 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/scroll-to.ts +60 -0
- package/src/utils/searchFieldFactory.ts +302 -0
- package/src/utils/treeManager.ts +218 -0
- package/src/utils/validate.ts +64 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import type { Component, Ref, VNode } from 'vue'
|
|
2
|
+
|
|
3
|
+
// ============================================================
|
|
4
|
+
// 通用基础类型
|
|
5
|
+
// ============================================================
|
|
6
|
+
|
|
7
|
+
/** 通用记录类型 */
|
|
8
|
+
export type AnyRecord = Record<string, any>
|
|
9
|
+
|
|
10
|
+
/** API 函数类型 */
|
|
11
|
+
export type ApiFn<P = AnyRecord, R = any> = (params: P) => Promise<R>
|
|
12
|
+
|
|
13
|
+
/** 路由跳转位置 */
|
|
14
|
+
export interface NavigateLocation {
|
|
15
|
+
path: string
|
|
16
|
+
query?: AnyRecord
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ============================================================
|
|
20
|
+
// Table Column 类型
|
|
21
|
+
// ============================================================
|
|
22
|
+
|
|
23
|
+
export interface StatusMapItem {
|
|
24
|
+
text: string
|
|
25
|
+
type: 'success' | 'danger' | 'warning' | 'info' | ''
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TableColumn {
|
|
29
|
+
/** 列类型 */
|
|
30
|
+
type?: 'selection' | 'index' | 'drag' | 'status'
|
|
31
|
+
/** 字段名 */
|
|
32
|
+
prop?: string
|
|
33
|
+
/** 列标题 */
|
|
34
|
+
label?: string
|
|
35
|
+
/** 列宽 */
|
|
36
|
+
width?: number | string
|
|
37
|
+
/** 最小列宽 */
|
|
38
|
+
minWidth?: number | string
|
|
39
|
+
/** 对齐方式 */
|
|
40
|
+
align?: 'left' | 'center' | 'right'
|
|
41
|
+
/** 固定列 */
|
|
42
|
+
fixed?: boolean | 'left' | 'right'
|
|
43
|
+
/** 是否可排序 */
|
|
44
|
+
sortable?: boolean | 'custom'
|
|
45
|
+
/** 是否显示溢出提示 */
|
|
46
|
+
showOverflowTooltip?: boolean
|
|
47
|
+
/** 插槽名 */
|
|
48
|
+
slot?: string
|
|
49
|
+
/** 头部插槽名 */
|
|
50
|
+
headerSlot?: string
|
|
51
|
+
/** 自定义渲染 */
|
|
52
|
+
render?: (row: AnyRecord, column: AnyRecord, index: number) => VNode
|
|
53
|
+
/** 格式化函数 */
|
|
54
|
+
formatter?: (row: AnyRecord, column: AnyRecord, cellValue: any, index: number) => string
|
|
55
|
+
/** 状态映射 */
|
|
56
|
+
statusMap?: Record<string | number, StatusMapItem>
|
|
57
|
+
/** Tag 效果 */
|
|
58
|
+
effect?: 'dark' | 'light' | 'plain'
|
|
59
|
+
/** Tag 尺寸 */
|
|
60
|
+
size?: 'large' | 'default' | 'small'
|
|
61
|
+
/** 内部标识 key */
|
|
62
|
+
_key?: string
|
|
63
|
+
[key: string]: any
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================
|
|
67
|
+
// Search Field 类型
|
|
68
|
+
// ============================================================
|
|
69
|
+
|
|
70
|
+
export interface SearchFieldBase {
|
|
71
|
+
prop: string
|
|
72
|
+
label: string
|
|
73
|
+
component?: string
|
|
74
|
+
type?: string
|
|
75
|
+
placeholder?: string
|
|
76
|
+
required?: boolean
|
|
77
|
+
visible?: boolean | ((form: AnyRecord) => boolean)
|
|
78
|
+
defaultValue?: any
|
|
79
|
+
multiple?: boolean
|
|
80
|
+
slotName?: string
|
|
81
|
+
props?: AnyRecord
|
|
82
|
+
[key: string]: any
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface DateRangeConfig {
|
|
86
|
+
startField: string
|
|
87
|
+
endField: string
|
|
88
|
+
format: (date: any) => string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface RelationConfig {
|
|
92
|
+
dependOn: string
|
|
93
|
+
getOptions: ((parentValue: any, form?: AnyRecord) => any[] | Promise<any[]>) | Record<string, any[]>
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface SearchField extends SearchFieldBase {
|
|
97
|
+
options?: Array<{ label: string; value: any; disabled?: boolean }>
|
|
98
|
+
dateRange?: DateRangeConfig
|
|
99
|
+
relation?: RelationConfig
|
|
100
|
+
defaultOptions?: Array<{ label: string; value: any }>
|
|
101
|
+
returnAllIds?: boolean
|
|
102
|
+
fileIds?: string[]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ============================================================
|
|
106
|
+
// useListPage 类型
|
|
107
|
+
// ============================================================
|
|
108
|
+
|
|
109
|
+
export interface UseListPageOptions {
|
|
110
|
+
apiList?: ApiFn
|
|
111
|
+
apiChangeState?: ApiFn
|
|
112
|
+
apiDelete?: ApiFn & { batch?: ApiFn<{ ids: any[] }> }
|
|
113
|
+
apiBatchDelete?: ApiFn
|
|
114
|
+
apiExport?: ApiFn
|
|
115
|
+
addPath?: string
|
|
116
|
+
editPath?: string
|
|
117
|
+
navigate?: (location: NavigateLocation) => void
|
|
118
|
+
exportFileName?: string
|
|
119
|
+
stateKey?: string
|
|
120
|
+
idKey?: string
|
|
121
|
+
activeValue?: any
|
|
122
|
+
inactiveValue?: any
|
|
123
|
+
statusMap?: Record<string | number, string>
|
|
124
|
+
deleteConfirmText?: string
|
|
125
|
+
deleteSuccessText?: string
|
|
126
|
+
deleteErrorText?: string
|
|
127
|
+
openDialog?: (data?: any) => void
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface UseListPageReturn {
|
|
131
|
+
tableRef: Ref<any>
|
|
132
|
+
searchParams: Ref<AnyRecord>
|
|
133
|
+
handleSearch: (params?: AnyRecord) => void
|
|
134
|
+
handleReset: () => void
|
|
135
|
+
changeState: (row: AnyRecord, callback?: (row: AnyRecord) => void) => Promise<void>
|
|
136
|
+
handleDelete: (row: AnyRecord, callback?: (row: AnyRecord) => void) => Promise<void>
|
|
137
|
+
handleBatchDelete: (rows: AnyRecord[], callback?: (rows: AnyRecord[]) => void) => Promise<void>
|
|
138
|
+
handleDialog: (data?: any) => void
|
|
139
|
+
exportExcel: () => Promise<void>
|
|
140
|
+
handleAdd: (query?: AnyRecord) => void
|
|
141
|
+
handleEdit: (row: AnyRecord, query?: AnyRecord) => void
|
|
142
|
+
handleView: (row: AnyRecord, detailPath: string, query?: AnyRecord) => void
|
|
143
|
+
refresh: () => void
|
|
144
|
+
apiList?: ApiFn
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ============================================================
|
|
148
|
+
// useForm 类型
|
|
149
|
+
// ============================================================
|
|
150
|
+
|
|
151
|
+
export interface UseFormOptions<T extends AnyRecord = AnyRecord> {
|
|
152
|
+
initFormData?: T | (() => T)
|
|
153
|
+
rules?: AnyRecord
|
|
154
|
+
onSuccess?: (result: any) => void
|
|
155
|
+
onError?: (error: any) => void
|
|
156
|
+
transform?: (formData: T) => any
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface UseFormReturn<T extends AnyRecord = AnyRecord> {
|
|
160
|
+
formRef: Ref<any>
|
|
161
|
+
formData: T
|
|
162
|
+
loading: Ref<boolean>
|
|
163
|
+
rules: AnyRecord
|
|
164
|
+
submit: (
|
|
165
|
+
apiCall: ApiFn,
|
|
166
|
+
successMessage?: string,
|
|
167
|
+
customTransform?: (formData: T) => any,
|
|
168
|
+
skipValidate?: boolean,
|
|
169
|
+
) => Promise<any>
|
|
170
|
+
validate: () => Promise<boolean>
|
|
171
|
+
validateField: (field: string, callback?: (...args: any[]) => void) => void
|
|
172
|
+
reset: () => void
|
|
173
|
+
clearValidate: (props?: string | string[]) => void
|
|
174
|
+
setFormData: (data: Partial<T>) => void
|
|
175
|
+
resetFormData: () => void
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ============================================================
|
|
179
|
+
// useConfirmAction 类型
|
|
180
|
+
// ============================================================
|
|
181
|
+
|
|
182
|
+
export type ConfirmType = 'success' | 'warning' | 'info' | 'error'
|
|
183
|
+
|
|
184
|
+
export interface UseConfirmActionOptions {
|
|
185
|
+
onSuccess?: (result: any) => void
|
|
186
|
+
onError?: (error: any) => void
|
|
187
|
+
confirmTitle?: string
|
|
188
|
+
confirmButtonText?: string
|
|
189
|
+
cancelButtonText?: string
|
|
190
|
+
confirmType?: ConfirmType
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface UseConfirmActionReturn {
|
|
194
|
+
execute: (params: AnyRecord) => Promise<any>
|
|
195
|
+
loading: Ref<boolean>
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ============================================================
|
|
199
|
+
// useConfirmSubmit 类型
|
|
200
|
+
// ============================================================
|
|
201
|
+
|
|
202
|
+
export interface UseConfirmSubmitOptions {
|
|
203
|
+
title?: string
|
|
204
|
+
message?: string
|
|
205
|
+
confirmButtonText?: string
|
|
206
|
+
cancelButtonText?: string
|
|
207
|
+
type?: ConfirmType
|
|
208
|
+
successMessage?: string
|
|
209
|
+
cancelMessage?: string
|
|
210
|
+
errorMessage?: string
|
|
211
|
+
showCancelMessage?: boolean
|
|
212
|
+
messageBoxOptions?: AnyRecord
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ============================================================
|
|
216
|
+
// useDownload 类型
|
|
217
|
+
// ============================================================
|
|
218
|
+
|
|
219
|
+
export interface UseDownloadOptions {
|
|
220
|
+
filename?: string
|
|
221
|
+
fileExtension?: string
|
|
222
|
+
mimeType?: string
|
|
223
|
+
timeout?: number
|
|
224
|
+
loadingMessage?: string
|
|
225
|
+
successMessage?: string
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface UseDownloadReturn {
|
|
229
|
+
downloading: Ref<boolean>
|
|
230
|
+
download: (params?: AnyRecord) => Promise<void>
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ============================================================
|
|
234
|
+
// useDialog 类型
|
|
235
|
+
// ============================================================
|
|
236
|
+
|
|
237
|
+
export interface UseDialogOptions {
|
|
238
|
+
width?: string
|
|
239
|
+
title?: string
|
|
240
|
+
beforeClose?: (done: () => void) => void
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface UseDialogReturn {
|
|
244
|
+
visible: Ref<boolean>
|
|
245
|
+
dialogTitle: Ref<string>
|
|
246
|
+
dialogData: AnyRecord
|
|
247
|
+
width: string
|
|
248
|
+
open: (data?: AnyRecord, customTitle?: string) => void
|
|
249
|
+
close: () => void
|
|
250
|
+
handleClose: (done: () => void) => void
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ============================================================
|
|
254
|
+
// useTableColumnConfig 类型
|
|
255
|
+
// ============================================================
|
|
256
|
+
|
|
257
|
+
export interface TableColumnConfigProps {
|
|
258
|
+
columns?: TableColumn[]
|
|
259
|
+
tableKey?: string
|
|
260
|
+
getColumnConfigApi?: ApiFn | null
|
|
261
|
+
saveColumnConfigApi?: ApiFn | null
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface UseTableColumnConfigReturn {
|
|
265
|
+
computedColumns: import('vue').ComputedRef<TableColumn[]>
|
|
266
|
+
settingColumns: import('vue').ComputedRef<TableColumn[]>
|
|
267
|
+
visibleColKeys: Ref<string[]>
|
|
268
|
+
tempVisibleColKeys: Ref<string[]>
|
|
269
|
+
popoverRef: Ref<any>
|
|
270
|
+
savingColumns: Ref<boolean>
|
|
271
|
+
initTableConfig: () => Promise<void>
|
|
272
|
+
handlePopoverShow: () => void
|
|
273
|
+
handleCancelSettings: () => void
|
|
274
|
+
handleConfirmSettings: () => Promise<void>
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ============================================================
|
|
278
|
+
// SearchFieldFactory 类型
|
|
279
|
+
// ============================================================
|
|
280
|
+
|
|
281
|
+
export interface DateRangeFieldConfig {
|
|
282
|
+
prop?: string
|
|
283
|
+
label?: string
|
|
284
|
+
startField?: string
|
|
285
|
+
endField?: string
|
|
286
|
+
format?: string
|
|
287
|
+
props?: AnyRecord
|
|
288
|
+
[key: string]: any
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface InputFieldConfig {
|
|
292
|
+
prop: string
|
|
293
|
+
label: string
|
|
294
|
+
placeholder?: string
|
|
295
|
+
maxlength?: number
|
|
296
|
+
props?: AnyRecord
|
|
297
|
+
[key: string]: any
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export interface SelectFieldConfig {
|
|
301
|
+
prop: string
|
|
302
|
+
label: string
|
|
303
|
+
options?: Array<{ label: string; value: any }>
|
|
304
|
+
multiple?: boolean
|
|
305
|
+
props?: AnyRecord
|
|
306
|
+
[key: string]: any
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export interface CascadeSelectFieldConfig extends SelectFieldConfig {
|
|
310
|
+
dependOn: string
|
|
311
|
+
getOptions: (parentValue: any) => Promise<any[]>
|
|
312
|
+
defaultOptions?: Array<{ label: string; value: any }>
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface CascaderFieldConfig {
|
|
316
|
+
prop: string
|
|
317
|
+
label: string
|
|
318
|
+
options?: any[]
|
|
319
|
+
labelKey?: string
|
|
320
|
+
valueKey?: string
|
|
321
|
+
checkStrictly?: boolean
|
|
322
|
+
returnAllIds?: boolean
|
|
323
|
+
props?: AnyRecord
|
|
324
|
+
[key: string]: any
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export interface NumberFieldConfig {
|
|
328
|
+
prop: string
|
|
329
|
+
label: string
|
|
330
|
+
min?: number
|
|
331
|
+
max?: number
|
|
332
|
+
step?: number
|
|
333
|
+
props?: AnyRecord
|
|
334
|
+
[key: string]: any
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export interface SwitchFieldConfig {
|
|
338
|
+
prop: string
|
|
339
|
+
label: string
|
|
340
|
+
activeValue?: any
|
|
341
|
+
inactiveValue?: any
|
|
342
|
+
props?: AnyRecord
|
|
343
|
+
[key: string]: any
|
|
344
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 缓动平滑滚动函数
|
|
3
|
+
*/
|
|
4
|
+
function easeInOutQuad(t: number, b: number, c: number, d: number): number {
|
|
5
|
+
t /= d / 2
|
|
6
|
+
if (t < 1) {
|
|
7
|
+
return c / 2 * t * t + b
|
|
8
|
+
}
|
|
9
|
+
t--
|
|
10
|
+
return -c / 2 * (t * (t - 2) - 1) + b
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const requestAnimFrame = (function() {
|
|
14
|
+
if (typeof window === 'undefined') return (_cb: FrameRequestCallback) => 0
|
|
15
|
+
return window.requestAnimationFrame ||
|
|
16
|
+
(window as any).webkitRequestAnimationFrame ||
|
|
17
|
+
(window as any).mozRequestAnimationFrame ||
|
|
18
|
+
function(callback: FrameRequestCallback) { return window.setTimeout(callback, 1000 / 60) }
|
|
19
|
+
})()
|
|
20
|
+
|
|
21
|
+
function move(amount: number): void {
|
|
22
|
+
if (typeof document === 'undefined') return
|
|
23
|
+
document.documentElement.scrollTop = amount
|
|
24
|
+
if (document.body.parentNode) {
|
|
25
|
+
;(document.body.parentNode as HTMLElement).scrollTop = amount
|
|
26
|
+
}
|
|
27
|
+
document.body.scrollTop = amount
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function position(): number {
|
|
31
|
+
if (typeof document === 'undefined') return 0
|
|
32
|
+
return document.documentElement.scrollTop || ((document.body.parentNode as HTMLElement)?.scrollTop) || document.body.scrollTop || 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 平滑滚动到指定位置
|
|
37
|
+
* @param to 目标位置
|
|
38
|
+
* @param duration 滚动时长(ms)
|
|
39
|
+
* @param callback 完成回调
|
|
40
|
+
*/
|
|
41
|
+
export function scrollTo(to: number, duration: number = 500, callback?: () => void): void {
|
|
42
|
+
const start = position()
|
|
43
|
+
const change = to - start
|
|
44
|
+
const increment = 20
|
|
45
|
+
let currentTime = 0
|
|
46
|
+
|
|
47
|
+
const animateScroll = function() {
|
|
48
|
+
currentTime += increment
|
|
49
|
+
const val = easeInOutQuad(currentTime, start, change, duration)
|
|
50
|
+
move(val)
|
|
51
|
+
if (currentTime < duration) {
|
|
52
|
+
requestAnimFrame(animateScroll)
|
|
53
|
+
} else {
|
|
54
|
+
if (typeof callback === 'function') {
|
|
55
|
+
callback()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
animateScroll()
|
|
60
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 搜索字段配置工厂函数
|
|
3
|
+
* 配合 CSearchBox 使用,用于快速生成规范的表单字段配置
|
|
4
|
+
*/
|
|
5
|
+
import dayjs from "dayjs"
|
|
6
|
+
import type {
|
|
7
|
+
AnyRecord,
|
|
8
|
+
DateRangeFieldConfig,
|
|
9
|
+
InputFieldConfig,
|
|
10
|
+
SelectFieldConfig,
|
|
11
|
+
CascadeSelectFieldConfig,
|
|
12
|
+
CascaderFieldConfig,
|
|
13
|
+
NumberFieldConfig,
|
|
14
|
+
SwitchFieldConfig,
|
|
15
|
+
} from '../types'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 通用标准日期格式常量
|
|
19
|
+
*/
|
|
20
|
+
export const DATE_FORMAT = {
|
|
21
|
+
DATE: "YYYY-MM-DD",
|
|
22
|
+
DATETIME: "YYYY-MM-DD HH:mm:ss",
|
|
23
|
+
TIME: "HH:mm:ss",
|
|
24
|
+
MONTH: "YYYY-MM",
|
|
25
|
+
YEAR: "YYYY",
|
|
26
|
+
DATE_CN: "YYYY年MM月DD日",
|
|
27
|
+
DATETIME_CN: "YYYY年MM月DD日 HH:mm:ss",
|
|
28
|
+
} as const
|
|
29
|
+
|
|
30
|
+
// 通用基础字段创建函数:自动透传所有剩余参数
|
|
31
|
+
const createField = (baseConfig: AnyRecord, config: AnyRecord = {}): AnyRecord => {
|
|
32
|
+
const { ...extra } = config
|
|
33
|
+
return {
|
|
34
|
+
...baseConfig,
|
|
35
|
+
...extra,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const SearchFieldFactory = {
|
|
40
|
+
dateRange(config: DateRangeFieldConfig = {} as DateRangeFieldConfig): AnyRecord {
|
|
41
|
+
const {
|
|
42
|
+
prop = "updateTime",
|
|
43
|
+
label = "更新时间",
|
|
44
|
+
startField = "updateStartTime",
|
|
45
|
+
endField = "updateEndTime",
|
|
46
|
+
format = DATE_FORMAT.DATETIME,
|
|
47
|
+
props = {},
|
|
48
|
+
} = config
|
|
49
|
+
|
|
50
|
+
const base: AnyRecord = {
|
|
51
|
+
prop,
|
|
52
|
+
label,
|
|
53
|
+
component: "el-date-picker",
|
|
54
|
+
dateRange: {
|
|
55
|
+
startField,
|
|
56
|
+
endField,
|
|
57
|
+
format: (date: any) => (date ? dayjs(date).format(format) : ""),
|
|
58
|
+
},
|
|
59
|
+
props: {
|
|
60
|
+
type: "datetimerange",
|
|
61
|
+
rangeSeparator: "至",
|
|
62
|
+
startPlaceholder: "开始日期",
|
|
63
|
+
endPlaceholder: "结束日期",
|
|
64
|
+
valueFormat: DATE_FORMAT.DATETIME,
|
|
65
|
+
clearable: true,
|
|
66
|
+
defaultTime: [
|
|
67
|
+
new Date(2000, 1, 1, 0, 0, 0),
|
|
68
|
+
new Date(2000, 1, 1, 23, 59, 59),
|
|
69
|
+
],
|
|
70
|
+
...props,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return createField(base, config)
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
input(config: InputFieldConfig = {} as InputFieldConfig): AnyRecord | null {
|
|
78
|
+
const { prop, label, placeholder, maxlength, props = {} } = config
|
|
79
|
+
|
|
80
|
+
if (!prop || !label) {
|
|
81
|
+
console.error("input 字段配置缺少必填参数 prop 或 label:", config)
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const base: AnyRecord = {
|
|
86
|
+
label,
|
|
87
|
+
prop,
|
|
88
|
+
component: "el-input",
|
|
89
|
+
props: {
|
|
90
|
+
placeholder: placeholder || `请输入${label}`,
|
|
91
|
+
clearable: true,
|
|
92
|
+
maxlength,
|
|
93
|
+
showWordLimit: !!maxlength,
|
|
94
|
+
...props,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return createField(base, config)
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
select(config: SelectFieldConfig = {} as SelectFieldConfig): AnyRecord | null {
|
|
102
|
+
const { prop, label, options = [], multiple = false, props = {} } = config
|
|
103
|
+
|
|
104
|
+
if (!prop || !label) {
|
|
105
|
+
console.error("select 字段配置缺少必填参数 prop 或 label:", config)
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const base: AnyRecord = {
|
|
110
|
+
label,
|
|
111
|
+
prop,
|
|
112
|
+
component: "el-select",
|
|
113
|
+
options,
|
|
114
|
+
props: {
|
|
115
|
+
placeholder: `请选择${label}`,
|
|
116
|
+
clearable: true,
|
|
117
|
+
multiple,
|
|
118
|
+
collapseTags: multiple,
|
|
119
|
+
collapseTagsTooltip: multiple,
|
|
120
|
+
filterable: true,
|
|
121
|
+
...props,
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return createField(base, config)
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
cascadeSelect(config: CascadeSelectFieldConfig = {} as CascadeSelectFieldConfig): AnyRecord | null {
|
|
129
|
+
const {
|
|
130
|
+
prop,
|
|
131
|
+
label,
|
|
132
|
+
dependOn,
|
|
133
|
+
getOptions,
|
|
134
|
+
defaultOptions = [],
|
|
135
|
+
multiple = false,
|
|
136
|
+
props = {},
|
|
137
|
+
} = config
|
|
138
|
+
|
|
139
|
+
if (!prop || !label || !dependOn || !getOptions) {
|
|
140
|
+
console.error("cascadeSelect 配置不完整:", config)
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const base: AnyRecord = {
|
|
145
|
+
label,
|
|
146
|
+
prop,
|
|
147
|
+
component: "el-select",
|
|
148
|
+
options: [],
|
|
149
|
+
relation: { dependOn, getOptions },
|
|
150
|
+
defaultOptions,
|
|
151
|
+
props: {
|
|
152
|
+
placeholder: `请选择${label}`,
|
|
153
|
+
clearable: true,
|
|
154
|
+
multiple,
|
|
155
|
+
collapseTags: multiple,
|
|
156
|
+
collapseTagsTooltip: multiple,
|
|
157
|
+
...props,
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return createField(base, config)
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
cascader(config: CascaderFieldConfig = {} as CascaderFieldConfig): AnyRecord | null {
|
|
165
|
+
const {
|
|
166
|
+
prop,
|
|
167
|
+
label,
|
|
168
|
+
options = [],
|
|
169
|
+
labelKey = "name",
|
|
170
|
+
valueKey = "id",
|
|
171
|
+
checkStrictly = true,
|
|
172
|
+
returnAllIds = true,
|
|
173
|
+
props = {},
|
|
174
|
+
} = config
|
|
175
|
+
|
|
176
|
+
if (!prop || !label) {
|
|
177
|
+
console.error("cascader 字段配置缺少必填参数 prop 或 label:", config)
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const base: AnyRecord = {
|
|
182
|
+
label,
|
|
183
|
+
prop,
|
|
184
|
+
component: "el-cascader",
|
|
185
|
+
returnAllIds,
|
|
186
|
+
props: {
|
|
187
|
+
options,
|
|
188
|
+
placeholder: `请选择${label}`,
|
|
189
|
+
clearable: true,
|
|
190
|
+
props: {
|
|
191
|
+
label: labelKey,
|
|
192
|
+
value: valueKey,
|
|
193
|
+
children: "children",
|
|
194
|
+
checkStrictly,
|
|
195
|
+
},
|
|
196
|
+
...props,
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return createField(base, config)
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
number(config: NumberFieldConfig = {} as NumberFieldConfig): AnyRecord | null {
|
|
204
|
+
const { prop, label, min, max, step = 1, props = {} } = config
|
|
205
|
+
|
|
206
|
+
if (!prop || !label) {
|
|
207
|
+
console.error("number 字段配置缺少必填参数 prop 或 label:", config)
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const base: AnyRecord = {
|
|
212
|
+
label,
|
|
213
|
+
prop,
|
|
214
|
+
component: "el-input-number",
|
|
215
|
+
props: {
|
|
216
|
+
placeholder: `请输入${label}`,
|
|
217
|
+
min,
|
|
218
|
+
max,
|
|
219
|
+
step,
|
|
220
|
+
controlsPosition: "right",
|
|
221
|
+
style: { width: "100%" },
|
|
222
|
+
...props,
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return createField(base, config)
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
date(config: AnyRecord = {}): AnyRecord | null {
|
|
230
|
+
const { prop, label, format = DATE_FORMAT.DATE, props = {} } = config
|
|
231
|
+
|
|
232
|
+
if (!prop || !label) {
|
|
233
|
+
console.error("date 字段配置缺少必填参数 prop 或 label:", config)
|
|
234
|
+
return null
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const base: AnyRecord = {
|
|
238
|
+
prop,
|
|
239
|
+
label,
|
|
240
|
+
component: "el-date-picker",
|
|
241
|
+
props: {
|
|
242
|
+
type: "date",
|
|
243
|
+
placeholder: `请选择${label}`,
|
|
244
|
+
valueFormat: format,
|
|
245
|
+
clearable: true,
|
|
246
|
+
...props,
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return createField(base, config)
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
switch(config: SwitchFieldConfig = {} as SwitchFieldConfig): AnyRecord | null {
|
|
254
|
+
const {
|
|
255
|
+
prop,
|
|
256
|
+
label,
|
|
257
|
+
activeValue = 1,
|
|
258
|
+
inactiveValue = 0,
|
|
259
|
+
props = {},
|
|
260
|
+
} = config
|
|
261
|
+
|
|
262
|
+
if (!prop || !label) {
|
|
263
|
+
console.error("switch 字段配置缺少必填参数 prop 或 label:", config)
|
|
264
|
+
return null
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const base: AnyRecord = {
|
|
268
|
+
label,
|
|
269
|
+
prop,
|
|
270
|
+
component: "el-switch",
|
|
271
|
+
props: {
|
|
272
|
+
activeValue,
|
|
273
|
+
inactiveValue,
|
|
274
|
+
...props,
|
|
275
|
+
},
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return createField(base, config)
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// 常见预设字段快捷方式
|
|
283
|
+
export const CommonSearchFields = {
|
|
284
|
+
updateTimeRange(config: DateRangeFieldConfig = {} as DateRangeFieldConfig) {
|
|
285
|
+
return SearchFieldFactory.dateRange({ ...config })
|
|
286
|
+
},
|
|
287
|
+
createTimeRange(config: Partial<DateRangeFieldConfig> = {}) {
|
|
288
|
+
return SearchFieldFactory.dateRange(Object.assign({ prop: "createTime", label: "创建时间" }, config) as DateRangeFieldConfig)
|
|
289
|
+
},
|
|
290
|
+
status(options: Array<{ label: string; value: any }>, config: Partial<SelectFieldConfig> = {}) {
|
|
291
|
+
return SearchFieldFactory.select(Object.assign({ prop: "state", label: "状态" }, config, { options }) as SelectFieldConfig)
|
|
292
|
+
},
|
|
293
|
+
keyword(config: Partial<InputFieldConfig> = {}) {
|
|
294
|
+
return SearchFieldFactory.input(Object.assign({ prop: "keyword", label: "关键词" }, config) as InputFieldConfig)
|
|
295
|
+
},
|
|
296
|
+
name(config: Partial<InputFieldConfig> = {}) {
|
|
297
|
+
return SearchFieldFactory.input(Object.assign({ prop: "name", label: "名称" }, config) as InputFieldConfig)
|
|
298
|
+
},
|
|
299
|
+
category(options: any[], config: Partial<CascaderFieldConfig> = {}) {
|
|
300
|
+
return SearchFieldFactory.cascader(Object.assign({ prop: "categoryId", label: "目录" }, config, { options }) as CascaderFieldConfig)
|
|
301
|
+
},
|
|
302
|
+
}
|