@antsoo-lib/core 1.0.17 → 2.0.2
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/.turbo/turbo-build.log +0 -0
- package/CHANGELOG.md +16 -0
- package/dist/core.css +1 -1
- package/dist/index.cjs +4 -8
- package/dist/index.js +2367 -49071
- package/dist/types/BaseSearch/index.d.ts +59 -0
- package/dist/types/{core/src/BaseTable → BaseTable}/index.d.ts +20 -6
- package/dist/types/Form/CoreForm.d.ts +82 -0
- package/dist/types/Form/types.d.ts +57 -0
- package/dist/types/SSelectPage/index.d.ts +102 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/{core/src/BaseTable/renderers → render}/AreaCascader.d.ts +3 -3
- package/dist/types/{core/src/BaseTable/renderers → render}/AutoComplete.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/Button.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/Cascader.d.ts +3 -3
- package/dist/types/{core/src/BaseTable/renderers → render}/Checkbox.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/CheckboxGroup.d.ts +2 -2
- package/dist/types/render/Custom.d.ts +8 -0
- package/dist/types/{core/src/BaseTable/renderers → render}/DatePicker.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/Input.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/InputGroup.d.ts +3 -3
- package/dist/types/{core/src/BaseTable/renderers → render}/InputNumber.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/InputPassword.d.ts +2 -2
- package/dist/types/render/InputRange.d.ts +9 -0
- package/dist/types/{core/src/BaseTable/renderers → render}/RadioGroup.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/Select.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/SselectPage.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/Switch.d.ts +2 -2
- package/dist/types/render/Tree.d.ts +9 -0
- package/dist/types/{core/src/BaseTable/renderers → render}/TreeSelect.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/renderers → render}/Upload.d.ts +2 -2
- package/dist/types/{core/src/BaseTable/helpers.d.ts → render/helper.d.ts} +2 -1
- package/dist/types/{core/src/BaseTable/renderers → render}/index.d.ts +24 -5
- package/dist/types/{core/src/BaseTable → render}/types.d.ts +48 -4
- package/dist/types/utils/attrMapping.d.ts +26 -0
- package/package.json +12 -15
- package/src/BaseSearch/index.vue +371 -0
- package/src/BaseTable/index.vue +48 -22
- package/src/Form/CoreForm.vue +782 -0
- package/src/Form/types.ts +86 -0
- package/src/SSelectPage/index.vue +607 -0
- package/src/index.ts +15 -1
- package/src/{BaseTable/renderers → render}/AreaCascader.tsx +3 -3
- package/src/{BaseTable/renderers → render}/AutoComplete.tsx +3 -3
- package/src/{BaseTable/renderers → render}/Button.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Cascader.tsx +3 -3
- package/src/{BaseTable/renderers → render}/Checkbox.tsx +2 -2
- package/src/{BaseTable/renderers → render}/CheckboxGroup.tsx +2 -2
- package/src/render/Custom.tsx +19 -0
- package/src/{BaseTable/renderers → render}/DatePicker.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Input.tsx +3 -3
- package/src/{BaseTable/renderers → render}/InputGroup.tsx +3 -3
- package/src/{BaseTable/renderers → render}/InputNumber.tsx +3 -3
- package/src/{BaseTable/renderers → render}/InputPassword.tsx +3 -3
- package/src/render/InputRange.tsx +154 -0
- package/src/{BaseTable/renderers → render}/RadioGroup.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Select.tsx +2 -2
- package/src/{BaseTable/renderers → render}/SselectPage.tsx +3 -3
- package/src/{BaseTable/renderers → render}/Switch.tsx +2 -2
- package/src/render/Tree.tsx +136 -0
- package/src/{BaseTable/renderers → render}/TreeSelect.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Upload.tsx +4 -5
- package/src/{BaseTable/utils.tsx → render/helper.tsx} +86 -9
- package/src/{BaseTable/renderers → render}/index.ts +45 -4
- package/src/{BaseTable → render}/types.ts +62 -2
- package/src/utils/attrMapping.ts +106 -0
- package/vite.config.ts +15 -2
- package/dist/types/core/index.d.ts +0 -6
- package/dist/types/core/src/BaseTable/utils.d.ts +0 -8
- package/dist/types/core/src/index.d.ts +0 -2
- package/index.css +0 -2
- package/index.ts +0 -21
- package/src/BaseTable/helpers.tsx +0 -91
- /package/dist/types/{core/src/BaseTable → render}/registry.d.ts +0 -0
- /package/dist/types/{core/src/BaseTable → render}/state.d.ts +0 -0
- /package/src/{BaseTable → render}/registry.ts +0 -0
- /package/src/{BaseTable → render}/state.ts +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ButtonProps, CascaderProps, CheckboxGroupProps, CheckboxProps, ColProps, DatePickerProps, InputNumberProps, InputProps, RadioGroupProps, RowProps, SelectProps, SwitchProps, TreeSelectProps, UploadProps } from '../../packages/components/index.ts';
|
|
1
|
+
import { ButtonProps, CascaderProps, CheckboxGroupProps, CheckboxProps, ColProps, DatePickerProps, InputNumberProps, InputProps, RadioGroupProps, RowProps, SelectProps, SwitchProps, TreeProps, TreeSelectProps, UploadProps } from '../../packages/components/index.ts';
|
|
2
2
|
import { AnyObject, VoidFunction } from '../../packages/shared/index.ts';
|
|
3
3
|
import { VNode } from 'vue';
|
|
4
|
+
import { CustomToolbarItem } from './Custom';
|
|
4
5
|
import { ToolbarState } from './state';
|
|
5
6
|
export type ToolbarEventHandler = VoidFunction;
|
|
6
7
|
export interface BaseEvents {
|
|
@@ -166,6 +167,40 @@ export interface TreeSelectToolbarItem extends BaseToolbarItem {
|
|
|
166
167
|
onChange?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
167
168
|
};
|
|
168
169
|
}
|
|
170
|
+
export interface TreeToolbarItem extends BaseToolbarItem {
|
|
171
|
+
type: 'tree';
|
|
172
|
+
props?: Partial<TreeProps> & {
|
|
173
|
+
checkedKeys?: any[];
|
|
174
|
+
expandedKeys?: any[];
|
|
175
|
+
};
|
|
176
|
+
events?: BaseEvents & {
|
|
177
|
+
'onUpdate:checkedKeys'?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
178
|
+
'onUpdate:expandedKeys'?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
179
|
+
onCheck?: (value: any, info: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
180
|
+
onExpand?: (value: any, info: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
181
|
+
onChange?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export interface InputRangeToolbarItem extends BaseToolbarItem {
|
|
185
|
+
type: 'inputRange';
|
|
186
|
+
props?: {
|
|
187
|
+
startProps?: Record<string, unknown>;
|
|
188
|
+
endProps?: Record<string, unknown>;
|
|
189
|
+
separator?: string;
|
|
190
|
+
startPlaceholder?: string;
|
|
191
|
+
endPlaceholder?: string;
|
|
192
|
+
style?: AnyObject;
|
|
193
|
+
className?: string;
|
|
194
|
+
allowClear?: boolean;
|
|
195
|
+
};
|
|
196
|
+
attr?: string[];
|
|
197
|
+
events?: BaseEvents & {
|
|
198
|
+
'onUpdate:value'?: (value: [string, string], allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
199
|
+
onChange?: (value: [string, string], allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
200
|
+
onStartChange?: (value: string, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
201
|
+
onEndChange?: (value: string, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
169
204
|
export interface InputNumberToolbarItem extends BaseToolbarItem {
|
|
170
205
|
type: 'inputNumber';
|
|
171
206
|
props?: Partial<InputNumberProps> & {
|
|
@@ -256,15 +291,23 @@ export interface SselectPageProps {
|
|
|
256
291
|
allowClear?: boolean;
|
|
257
292
|
showSearch?: boolean;
|
|
258
293
|
disabled?: boolean;
|
|
259
|
-
maxTagCount?: number;
|
|
294
|
+
maxTagCount?: number | 'responsive';
|
|
260
295
|
size?: 'large' | 'middle' | 'small';
|
|
261
296
|
mode?: 'multiple' | 'tags';
|
|
297
|
+
params?: AnyObject;
|
|
262
298
|
api: {
|
|
263
299
|
url: string;
|
|
264
300
|
method?: 'GET' | 'POST';
|
|
265
301
|
headers?: Record<string, string>;
|
|
266
302
|
params?: AnyObject;
|
|
267
303
|
data?: AnyObject;
|
|
304
|
+
request?: (config: {
|
|
305
|
+
method?: 'GET' | 'POST';
|
|
306
|
+
url: string;
|
|
307
|
+
headers?: Record<string, string>;
|
|
308
|
+
params?: AnyObject;
|
|
309
|
+
data?: AnyObject;
|
|
310
|
+
}) => Promise<any>;
|
|
268
311
|
};
|
|
269
312
|
dataMapping?: {
|
|
270
313
|
list?: string;
|
|
@@ -285,6 +328,7 @@ export interface SselectPageProps {
|
|
|
285
328
|
transformData?: (data: any) => any;
|
|
286
329
|
autoLoad?: boolean;
|
|
287
330
|
autoFocus?: boolean;
|
|
331
|
+
attr?: Record<string, string>;
|
|
288
332
|
[key: string]: any;
|
|
289
333
|
}
|
|
290
334
|
export interface SselectPageToolbarItem extends BaseToolbarItem {
|
|
@@ -294,7 +338,7 @@ export interface SselectPageToolbarItem extends BaseToolbarItem {
|
|
|
294
338
|
};
|
|
295
339
|
events?: BaseEvents & {
|
|
296
340
|
'onUpdate:value'?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
297
|
-
onChange?: (value: any, option: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
341
|
+
onChange?: (value: any, option: any, allOptions: any, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
298
342
|
onSearch?: (value: string, allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
299
343
|
onClear?: (allValues: AnyObject, toolbarState: ToolbarState) => void;
|
|
300
344
|
};
|
|
@@ -364,7 +408,7 @@ export interface PresetFilesToolbarItem extends PresetBaseToolbarItem {
|
|
|
364
408
|
files: FilesPresetConfig;
|
|
365
409
|
}
|
|
366
410
|
export type PresetToolbarItem = PresetDelsToolbarItem | PresetExportToolbarItem | PresetFilesToolbarItem;
|
|
367
|
-
export type RenderableToolbarItem = ButtonToolbarItem | InputToolbarItem | RadioGroupToolbarItem | CheckboxToolbarItem | SwitchToolbarItem | CheckboxGroupToolbarItem | DatePickerToolbarItem | UploadToolbarItem | InputNumberToolbarItem | SelectToolbarItem | TreeSelectToolbarItem | InputPasswordToolbarItem | SselectPageToolbarItem | AutoCompleteToolbarItem | InputGroupToolbarItem | CascaderToolbarItem | AreaCascaderToolbarItem;
|
|
411
|
+
export type RenderableToolbarItem = ButtonToolbarItem | InputToolbarItem | RadioGroupToolbarItem | CheckboxToolbarItem | SwitchToolbarItem | CheckboxGroupToolbarItem | DatePickerToolbarItem | UploadToolbarItem | InputNumberToolbarItem | SelectToolbarItem | TreeToolbarItem | TreeSelectToolbarItem | InputPasswordToolbarItem | SselectPageToolbarItem | AutoCompleteToolbarItem | InputGroupToolbarItem | CascaderToolbarItem | AreaCascaderToolbarItem | InputRangeToolbarItem | CustomToolbarItem;
|
|
368
412
|
export type ToolbarItem = RenderableToolbarItem | PresetToolbarItem;
|
|
369
413
|
export type InputGroupChild = RenderableToolbarItem & {
|
|
370
414
|
span?: number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AnyObject } from '../../packages/shared/index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* 安全获取嵌套属性值,支持 a.b.c 路径
|
|
4
|
+
*/
|
|
5
|
+
export declare function getByPath(obj: any, path?: string): any;
|
|
6
|
+
/**
|
|
7
|
+
* 根据 attr 规则从 formData 聚合初始值:
|
|
8
|
+
* - 数组:返回对应字段的值数组
|
|
9
|
+
* - 对象:返回 { [sourcePath]: formData[targetKey] } 结构,便于组件按源字段进行回显
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildValueFromAttr(formData: AnyObject, attr: string[] | AnyObject): any;
|
|
12
|
+
/**
|
|
13
|
+
* 按 attr 对象映射将选项数据写入 formData:
|
|
14
|
+
* - 支持单选与多选;
|
|
15
|
+
* - 优先使用 option.__raw 原始对象,其次使用 option 自身;
|
|
16
|
+
* - sourcePath 支持嵌套路径(例如 'data.id')。
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyAttrMapping(formData: AnyObject, toolbarState: {
|
|
19
|
+
setValue: (k: string, v: any) => void;
|
|
20
|
+
}, attr: Record<string, string>, value: any, option: any, isMultiple: boolean): void;
|
|
21
|
+
declare const _default: {
|
|
22
|
+
buildValueFromAttr: typeof buildValueFromAttr;
|
|
23
|
+
applyAttrMapping: typeof applyAttrMapping;
|
|
24
|
+
getByPath: typeof getByPath;
|
|
25
|
+
};
|
|
26
|
+
export default _default;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antsoo-lib/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.2",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
"types": "./dist/types/index.d.ts",
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"require": "./dist/index.cjs"
|
|
12
|
-
}
|
|
13
|
-
"./style.css": "./dist/core.css"
|
|
12
|
+
}
|
|
14
13
|
},
|
|
15
14
|
"main": "./dist/index.cjs",
|
|
16
15
|
"module": "./dist/index.js",
|
|
@@ -20,22 +19,20 @@
|
|
|
20
19
|
"registry": "https://registry.npmjs.com/"
|
|
21
20
|
},
|
|
22
21
|
"peerDependencies": {
|
|
22
|
+
"@ant-design/icons-svg": "^4.4.2",
|
|
23
|
+
"@types/lodash-es": "^4.17.12",
|
|
24
|
+
"@vxe-ui/plugin-export-xlsx": "^4.0.7",
|
|
25
|
+
"@vxe-ui/plugin-menu": "^4.0.2",
|
|
26
|
+
"@vxe-ui/plugin-render-wangeditor": "^4.0.2",
|
|
23
27
|
"lodash-es": "^4.17.21",
|
|
24
28
|
"vue": "^3.3.4",
|
|
25
|
-
"@antsoo-lib/icons": "0.1.1",
|
|
26
|
-
"@antsoo-lib/utils": "0.1.2",
|
|
27
|
-
"@antsoo-lib/shared": "0.0.1",
|
|
28
|
-
"@antsoo-lib/components": "0.1.17"
|
|
29
|
-
},
|
|
30
|
-
"dependencies": {
|
|
31
29
|
"vxe-pc-ui": "^4.12.31",
|
|
32
30
|
"vxe-table": "^4.17.48",
|
|
33
|
-
"xe-utils": "^4.0.0"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
38
|
-
"lodash-es": "^4.17.21"
|
|
31
|
+
"xe-utils": "^4.0.0",
|
|
32
|
+
"@antsoo-lib/icons": "0.2.0",
|
|
33
|
+
"@antsoo-lib/utils": "0.2.0",
|
|
34
|
+
"@antsoo-lib/components": "1.0.0",
|
|
35
|
+
"@antsoo-lib/shared": "0.1.0"
|
|
39
36
|
},
|
|
40
37
|
"scripts": {
|
|
41
38
|
"dev": "vite build --watch",
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { Button, Popover } from '@antsoo-lib/components'
|
|
3
|
+
import type { AnyObject } from '@antsoo-lib/shared'
|
|
4
|
+
|
|
5
|
+
import { computed, nextTick, ref } from 'vue'
|
|
6
|
+
import type { Component } from 'vue'
|
|
7
|
+
|
|
8
|
+
import CoreForm from '../Form/CoreForm.vue'
|
|
9
|
+
import type { FormField } from '../Form/types'
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
searchFields?: FormField[]
|
|
13
|
+
labelWidth?: number
|
|
14
|
+
actionsSpan?: number
|
|
15
|
+
gutter?: number
|
|
16
|
+
// 保持内边距
|
|
17
|
+
padding?: boolean
|
|
18
|
+
// 外部(主搜索区)BaseForm 组件
|
|
19
|
+
outerBaseForm?: Component
|
|
20
|
+
// 内部(更多筛选区)BaseForm 组件
|
|
21
|
+
innerBaseForm?: Component
|
|
22
|
+
// 更多筛选区表单的配置项(如 labelWidth, gutter 等),如果不传则使用默认值
|
|
23
|
+
innerBaseFormProps?: AnyObject
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
27
|
+
searchFields: () => [],
|
|
28
|
+
labelWidth: 112,
|
|
29
|
+
actionsSpan: 8,
|
|
30
|
+
gutter: 0,
|
|
31
|
+
padding: true,
|
|
32
|
+
outerBaseForm: () => CoreForm,
|
|
33
|
+
innerBaseForm: undefined,
|
|
34
|
+
innerBaseFormProps: () => ({}),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// 定义 emits
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
search: [values: AnyObject]
|
|
40
|
+
reset: [values: AnyObject]
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
// 使用 ref 而不是 reactive
|
|
44
|
+
const formData = ref<AnyObject>({})
|
|
45
|
+
// 弱类型定义,因为传入的 baseForm 可能不同,但必须实现 BaseForm 的接口
|
|
46
|
+
const baseFormRef = ref<any>()
|
|
47
|
+
const baseFormRef2 = ref<any>()
|
|
48
|
+
|
|
49
|
+
// 新增:表单重置 key,用于强制重新渲染
|
|
50
|
+
const formKey = ref(0)
|
|
51
|
+
|
|
52
|
+
// 更多筛选区域使用的 Form 组件:如果提供了 innerBaseForm 则使用它,否则回退到 outerBaseForm
|
|
53
|
+
const resolvedInnerBaseForm = computed(() => props.innerBaseForm || props.outerBaseForm)
|
|
54
|
+
|
|
55
|
+
const fields = computed(() => {
|
|
56
|
+
return props.searchFields.map((field) => {
|
|
57
|
+
return {
|
|
58
|
+
...field,
|
|
59
|
+
span: props.actionsSpan,
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// 计算首行是否能容纳按钮区域(24 栅格)
|
|
65
|
+
const canInlineActions = computed(() => {
|
|
66
|
+
let used = 0
|
|
67
|
+
for (const field of fields.value) {
|
|
68
|
+
const s = field.span ?? 24
|
|
69
|
+
if (used + s > 24) break
|
|
70
|
+
used += s
|
|
71
|
+
}
|
|
72
|
+
return used + props.actionsSpan <= 24
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// 新增:可见/隐藏筛选项拆分(24 栅格一行)
|
|
76
|
+
const visibleFields = computed<FormField[]>(() => {
|
|
77
|
+
const limit = 24 - (canInlineActions.value ? props.actionsSpan : 0)
|
|
78
|
+
const result: FormField[] = []
|
|
79
|
+
let used = 0
|
|
80
|
+
for (const f of fields.value) {
|
|
81
|
+
const s = f.span ?? 24
|
|
82
|
+
if (used + s > limit) break
|
|
83
|
+
result.push(f)
|
|
84
|
+
used += s
|
|
85
|
+
}
|
|
86
|
+
return result
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const hiddenFields = computed<FormField[]>(() => {
|
|
90
|
+
const visibleCount = visibleFields.value.length
|
|
91
|
+
return fields.value.slice(visibleCount).map((f) => ({ ...f, span: 12 }))
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const showMore = computed(() => hiddenFields.value.length > 0)
|
|
95
|
+
|
|
96
|
+
// 初始写入所有字段的默认值(无论 Popover 是否展开)
|
|
97
|
+
const initAllFieldDefaults = () => {
|
|
98
|
+
const initial: AnyObject = {}
|
|
99
|
+
props.searchFields.forEach((f) => {
|
|
100
|
+
let val = f.props?.value
|
|
101
|
+
|
|
102
|
+
// 尝试格式化 dayjs 对象
|
|
103
|
+
const format = f.props?.valueFormat
|
|
104
|
+
const formatVal = (v: any) =>
|
|
105
|
+
format && v && typeof v.format === 'function' ? v.format(format) : v
|
|
106
|
+
|
|
107
|
+
if (val !== undefined) {
|
|
108
|
+
if (Array.isArray(val)) {
|
|
109
|
+
val = val.map(formatVal).filter((v) => v !== null && v !== undefined && v !== '')
|
|
110
|
+
} else {
|
|
111
|
+
val = formatVal(val)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (f.key) {
|
|
116
|
+
if (val !== undefined && (Array.isArray(val) ? val.length > 0 : true)) {
|
|
117
|
+
initial[f.key] = val
|
|
118
|
+
} else if (
|
|
119
|
+
(f.type === 'select' || f.type === 'sselectPage') &&
|
|
120
|
+
(f.props?.mode === 'multiple' || f.props?.mode === 'tags')
|
|
121
|
+
) {
|
|
122
|
+
// 多选/标签模式默认初始化为 [],避免出现空标签
|
|
123
|
+
initial[f.key] = []
|
|
124
|
+
} else if (f.type === 'checkboxGroup' || f.type === 'upload') {
|
|
125
|
+
initial[f.key] = []
|
|
126
|
+
} else if (f.type === 'treeSelect' && (f.props?.multiple || f.props?.treeCheckable)) {
|
|
127
|
+
initial[f.key] = []
|
|
128
|
+
} else {
|
|
129
|
+
initial[f.key] = null
|
|
130
|
+
}
|
|
131
|
+
} else if (Array.isArray(f.attr)) {
|
|
132
|
+
// 处理 attr 映射(主要用于 datePicker range 模式)
|
|
133
|
+
f.attr.forEach((targetKey: string, idx: number) => {
|
|
134
|
+
if (!targetKey) return
|
|
135
|
+
initial[targetKey] = Array.isArray(val) ? val[idx] : val
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
// 直接替换整个对象以确保响应性
|
|
140
|
+
formData.value = { ...initial }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 重置处理
|
|
144
|
+
const handleReset = async () => {
|
|
145
|
+
// 重新初始化所有字段
|
|
146
|
+
initAllFieldDefaults()
|
|
147
|
+
|
|
148
|
+
// 通过改变 key 强制重新渲染表单组件
|
|
149
|
+
formKey.value += 1
|
|
150
|
+
|
|
151
|
+
// 等待 UI 同步
|
|
152
|
+
await nextTick()
|
|
153
|
+
|
|
154
|
+
// 清除校验状态
|
|
155
|
+
baseFormRef.value?.clearValidate()
|
|
156
|
+
baseFormRef2.value?.clearValidate?.()
|
|
157
|
+
|
|
158
|
+
emit('reset', { ...formData.value })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 组件加载时执行一次初始化
|
|
162
|
+
initAllFieldDefaults()
|
|
163
|
+
|
|
164
|
+
// 获取搜索参数 —— 统一返回 formData(包含可见与隐藏字段)
|
|
165
|
+
const getSearchParams = () => {
|
|
166
|
+
const params = { ...formData.value }
|
|
167
|
+
// 遍历参数,清理无效值
|
|
168
|
+
Object.keys(params).forEach((key) => {
|
|
169
|
+
const val = params[key]
|
|
170
|
+
if (val === '') {
|
|
171
|
+
params[key] = null
|
|
172
|
+
} else if (Array.isArray(val)) {
|
|
173
|
+
// 过滤数组中的空值(如 null, undefined, 空字符串)
|
|
174
|
+
const filtered = val.filter((v) => v !== null && v !== undefined && v !== '')
|
|
175
|
+
// 如果过滤后数组为空,则设为 null,避免传递 [] 给后端可能导致的问题
|
|
176
|
+
params[key] = filtered.length > 0 ? filtered : null
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
return params
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 搜索处理 —— 统一从 formData 取值
|
|
183
|
+
const handleSearch = async () => {
|
|
184
|
+
emit('search', getSearchParams())
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const setFormValue = (values: AnyObject, key?: string[]) => {
|
|
188
|
+
// 1. 同步更新内部数据源,确保 getSearchParams 获取到最新值
|
|
189
|
+
if (key && key.length > 0) {
|
|
190
|
+
key.forEach((k) => {
|
|
191
|
+
if (values[k] !== undefined) {
|
|
192
|
+
formData.value[k] = values[k]
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
} else {
|
|
196
|
+
// 如果没有指定 key,则合并所有 values
|
|
197
|
+
Object.assign(formData.value, values)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 2. 更新表单 UI
|
|
201
|
+
baseFormRef.value?.setFormValue(values, key)
|
|
202
|
+
baseFormRef2.value?.setFormValue(values, key)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 暴露方法给父组件
|
|
206
|
+
defineExpose({
|
|
207
|
+
getSearchParams,
|
|
208
|
+
initAllFieldDefaults,
|
|
209
|
+
setFormValue,
|
|
210
|
+
})
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<template>
|
|
214
|
+
<div class="base-search-container" :class="{ 'is-pd': padding }">
|
|
215
|
+
<component
|
|
216
|
+
:is="outerBaseForm"
|
|
217
|
+
ref="baseFormRef"
|
|
218
|
+
:key="`base-form-${formKey}`"
|
|
219
|
+
v-model:value="formData"
|
|
220
|
+
:fields="visibleFields"
|
|
221
|
+
label-align="right"
|
|
222
|
+
:label-width="labelWidth"
|
|
223
|
+
:inline-actions="canInlineActions"
|
|
224
|
+
:actions-span="actionsSpan"
|
|
225
|
+
:gutter="gutter"
|
|
226
|
+
>
|
|
227
|
+
<template v-if="canInlineActions" #search>
|
|
228
|
+
<div class="base-search-btn-container inline">
|
|
229
|
+
<div class="reset-btn" @click="handleReset">
|
|
230
|
+
<slot name="reset-icon">
|
|
231
|
+
<!-- 默认重置图标(使用 CSS 绘制或 SVG) -->
|
|
232
|
+
<span class="default-reset-icon">↺</span>
|
|
233
|
+
</slot>
|
|
234
|
+
</div>
|
|
235
|
+
<Button type="primary" @click="handleSearch"> 搜索 </Button>
|
|
236
|
+
</div>
|
|
237
|
+
</template>
|
|
238
|
+
</component>
|
|
239
|
+
|
|
240
|
+
<div v-if="!canInlineActions" class="base-search-btn-container">
|
|
241
|
+
<div v-show="showMore">
|
|
242
|
+
<Popover
|
|
243
|
+
v-if="showMore"
|
|
244
|
+
trigger="click"
|
|
245
|
+
placement="bottomLeft"
|
|
246
|
+
overlay-class-name="base-search-popover"
|
|
247
|
+
>
|
|
248
|
+
<template #content>
|
|
249
|
+
<div
|
|
250
|
+
class="popover-form-wrap"
|
|
251
|
+
:style="{ width: '550px', maxHeight: '60vh', overflowY: 'auto' }"
|
|
252
|
+
>
|
|
253
|
+
<component
|
|
254
|
+
:is="resolvedInnerBaseForm"
|
|
255
|
+
ref="baseFormRef2"
|
|
256
|
+
:key="`base-form-2-${formKey}`"
|
|
257
|
+
v-model:value="formData"
|
|
258
|
+
:fields="hiddenFields"
|
|
259
|
+
label-align="right"
|
|
260
|
+
label-position="vertical"
|
|
261
|
+
:gutter="10"
|
|
262
|
+
:label-width="200"
|
|
263
|
+
v-bind="innerBaseFormProps"
|
|
264
|
+
/>
|
|
265
|
+
</div>
|
|
266
|
+
</template>
|
|
267
|
+
<div class="more-btn">
|
|
268
|
+
<slot name="more-icon">
|
|
269
|
+
<!-- 默认更多图标 -->
|
|
270
|
+
<span class="default-more-icon">...</span>
|
|
271
|
+
</slot>
|
|
272
|
+
</div>
|
|
273
|
+
</Popover>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div class="reset-btn" @click="handleReset">
|
|
277
|
+
<slot name="reset-icon">
|
|
278
|
+
<span class="default-reset-icon">↺</span>
|
|
279
|
+
</slot>
|
|
280
|
+
</div>
|
|
281
|
+
<Button type="primary" @click="handleSearch"> 搜索 </Button>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</template>
|
|
285
|
+
|
|
286
|
+
<style lang="scss" scoped>
|
|
287
|
+
.base-search-container {
|
|
288
|
+
width: 100%;
|
|
289
|
+
border-radius: 8px;
|
|
290
|
+
background-color: #fff;
|
|
291
|
+
display: flex;
|
|
292
|
+
gap: 18px;
|
|
293
|
+
|
|
294
|
+
.base-search-btn-container {
|
|
295
|
+
display: flex;
|
|
296
|
+
gap: 12px;
|
|
297
|
+
flex-shrink: 0;
|
|
298
|
+
|
|
299
|
+
.reset-btn,
|
|
300
|
+
.more-btn {
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
justify-content: center;
|
|
304
|
+
width: 32px;
|
|
305
|
+
height: 32px;
|
|
306
|
+
border-radius: 4px;
|
|
307
|
+
border: 1px solid #dedee6;
|
|
308
|
+
box-sizing: border-box;
|
|
309
|
+
|
|
310
|
+
:deep(img),
|
|
311
|
+
:deep(svg) {
|
|
312
|
+
width: 20px;
|
|
313
|
+
height: 20px;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
&:hover {
|
|
317
|
+
cursor: pointer;
|
|
318
|
+
border-color: rgba(0, 179, 148, 1);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
&.inline {
|
|
323
|
+
margin-left: 12px;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.is-pd {
|
|
329
|
+
padding: 16px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
:deep(.form-container) {
|
|
333
|
+
padding: 0;
|
|
334
|
+
border-radius: 0px;
|
|
335
|
+
|
|
336
|
+
.ant-form {
|
|
337
|
+
.ant-row {
|
|
338
|
+
row-gap: 10px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.ant-col {
|
|
342
|
+
.ant-form-item {
|
|
343
|
+
margin-bottom: 0px;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.popover-form-wrap {
|
|
350
|
+
max-width: 100%;
|
|
351
|
+
|
|
352
|
+
:deep(.form-container) {
|
|
353
|
+
.ant-form {
|
|
354
|
+
padding: 0 5px;
|
|
355
|
+
|
|
356
|
+
.ant-row {
|
|
357
|
+
.ant-form-item {
|
|
358
|
+
.ant-form-item-label {
|
|
359
|
+
line-height: normal;
|
|
360
|
+
padding: 0;
|
|
361
|
+
|
|
362
|
+
label {
|
|
363
|
+
line-height: normal;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
</style>
|