@antsoo-lib/core 2.0.0 → 2.0.3

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/core.css +1 -0
  3. package/dist/index.cjs +57 -0
  4. package/dist/index.js +4154 -0
  5. package/dist/types/BaseSearch/index.d.ts +59 -0
  6. package/dist/types/BaseTable/index.d.ts +204 -0
  7. package/dist/types/Form/CoreForm.d.ts +82 -0
  8. package/dist/types/Form/types.d.ts +57 -0
  9. package/dist/types/SSelectPage/index.d.ts +102 -0
  10. package/dist/types/index.d.ts +13 -0
  11. package/dist/types/render/AreaCascader.d.ts +5 -0
  12. package/dist/types/render/AutoComplete.d.ts +4 -0
  13. package/dist/types/render/Button.d.ts +4 -0
  14. package/dist/types/render/Cascader.d.ts +5 -0
  15. package/dist/types/render/Checkbox.d.ts +4 -0
  16. package/dist/types/render/CheckboxGroup.d.ts +4 -0
  17. package/dist/types/render/Custom.d.ts +8 -0
  18. package/dist/types/render/DatePicker.d.ts +4 -0
  19. package/dist/types/render/Input.d.ts +4 -0
  20. package/dist/types/render/InputGroup.d.ts +5 -0
  21. package/dist/types/render/InputNumber.d.ts +4 -0
  22. package/dist/types/render/InputPassword.d.ts +4 -0
  23. package/dist/types/render/InputRange.d.ts +9 -0
  24. package/dist/types/render/RadioGroup.d.ts +4 -0
  25. package/dist/types/render/Select.d.ts +4 -0
  26. package/dist/types/render/SselectPage.d.ts +4 -0
  27. package/dist/types/render/Switch.d.ts +4 -0
  28. package/dist/types/render/Tree.d.ts +9 -0
  29. package/dist/types/render/TreeSelect.d.ts +4 -0
  30. package/dist/types/render/Upload.d.ts +4 -0
  31. package/dist/types/render/helper.d.ts +10 -0
  32. package/dist/types/render/index.d.ts +43 -0
  33. package/dist/types/render/registry.d.ts +9 -0
  34. package/dist/types/render/state.d.ts +19 -0
  35. package/dist/types/render/types.d.ts +435 -0
  36. package/dist/types/utils/attrMapping.d.ts +26 -0
  37. package/package.json +10 -12
  38. package/src/BaseSearch/index.vue +371 -0
  39. package/src/BaseTable/index.vue +62 -36
  40. package/src/Form/CoreForm.vue +782 -0
  41. package/src/Form/types.ts +86 -0
  42. package/src/SSelectPage/index.vue +607 -0
  43. package/src/index.ts +15 -1
  44. package/src/{BaseTable/renderers → render}/AreaCascader.tsx +3 -3
  45. package/src/{BaseTable/renderers → render}/AutoComplete.tsx +3 -3
  46. package/src/{BaseTable/renderers → render}/Button.tsx +2 -2
  47. package/src/{BaseTable/renderers → render}/Cascader.tsx +3 -3
  48. package/src/{BaseTable/renderers → render}/Checkbox.tsx +2 -2
  49. package/src/{BaseTable/renderers → render}/CheckboxGroup.tsx +2 -2
  50. package/src/render/Custom.tsx +19 -0
  51. package/src/{BaseTable/renderers → render}/DatePicker.tsx +2 -2
  52. package/src/{BaseTable/renderers → render}/Input.tsx +3 -3
  53. package/src/{BaseTable/renderers → render}/InputGroup.tsx +3 -3
  54. package/src/{BaseTable/renderers → render}/InputNumber.tsx +3 -3
  55. package/src/{BaseTable/renderers → render}/InputPassword.tsx +3 -3
  56. package/src/render/InputRange.tsx +154 -0
  57. package/src/{BaseTable/renderers → render}/RadioGroup.tsx +2 -2
  58. package/src/{BaseTable/renderers → render}/Select.tsx +2 -2
  59. package/src/{BaseTable/renderers → render}/SselectPage.tsx +3 -3
  60. package/src/{BaseTable/renderers → render}/Switch.tsx +2 -2
  61. package/src/render/Tree.tsx +136 -0
  62. package/src/{BaseTable/renderers → render}/TreeSelect.tsx +2 -2
  63. package/src/{BaseTable/renderers → render}/Upload.tsx +4 -5
  64. package/src/{BaseTable/utils.tsx → render/helper.tsx} +86 -9
  65. package/src/{BaseTable/renderers → render}/index.ts +45 -4
  66. package/src/{BaseTable → render}/types.ts +62 -2
  67. package/src/utils/attrMapping.ts +106 -0
  68. package/vite.config.ts +15 -2
  69. package/index.css +0 -2
  70. package/index.ts +0 -21
  71. package/src/BaseTable/helpers.tsx +0 -91
  72. /package/src/{BaseTable → render}/registry.ts +0 -0
  73. /package/src/{BaseTable → render}/state.ts +0 -0
@@ -2,33 +2,41 @@ import type { AnyObject } from '@antsoo-lib/shared'
2
2
 
3
3
  import type { VNode } from 'vue'
4
4
 
5
- import type { ToolbarState } from '../state'
6
- import type { ToolbarItem } from '../types'
7
5
  import { renderAreaCascader } from './AreaCascader'
8
6
  import { renderAutoComplete } from './AutoComplete'
9
7
  import { renderButton } from './Button'
10
8
  import { renderCascader } from './Cascader'
11
9
  import { renderCheckbox } from './Checkbox'
12
10
  import { renderCheckboxGroup } from './CheckboxGroup'
11
+ import { renderCustom } from './Custom'
13
12
  import { renderDatePicker } from './DatePicker'
14
13
  import { renderInput } from './Input'
15
14
  import { renderInputGroup } from './InputGroup'
16
15
  import { renderInputNumber } from './InputNumber'
17
16
  import { renderInputPassword } from './InputPassword'
17
+ import { renderInputRange } from './InputRange'
18
18
  import { renderRadioGroup } from './RadioGroup'
19
19
  import { renderSelect } from './Select'
20
20
  import { renderSselectPage } from './SselectPage'
21
21
  import { renderSwitch } from './Switch'
22
+ import { renderTree } from './Tree'
22
23
  import { renderTreeSelect } from './TreeSelect'
23
24
  import { renderUpload } from './Upload'
25
+ import type { ToolbarState } from './state'
26
+ import type { ToolbarItem } from './types'
24
27
 
25
28
  // 渲染器映射类型定义
26
- type Renderer = (item: ToolbarItem, toolbarState: ToolbarState, formValues?: AnyObject) => VNode
29
+ export type Renderer = (
30
+ item: ToolbarItem,
31
+ toolbarState: ToolbarState,
32
+ formValues?: AnyObject,
33
+ ) => VNode
27
34
 
28
35
  // 渲染器映射
29
- export const rendererMap: Record<string, Renderer> = {
36
+ const defaultRendererMap: Record<string, Renderer> = {
30
37
  button: renderButton,
31
38
  input: renderInput,
39
+ inputRange: renderInputRange as Renderer,
32
40
  inputGroup: renderInputGroup as Renderer,
33
41
  radioGroup: renderRadioGroup,
34
42
  checkbox: renderCheckbox,
@@ -39,11 +47,42 @@ export const rendererMap: Record<string, Renderer> = {
39
47
  inputNumber: renderInputNumber,
40
48
  inputPassword: renderInputPassword,
41
49
  upload: renderUpload,
50
+ tree: renderTree as Renderer,
42
51
  treeSelect: renderTreeSelect,
43
52
  sselectPage: renderSselectPage,
44
53
  autoComplete: renderAutoComplete as Renderer,
45
54
  cascader: renderCascader as Renderer,
46
55
  areaCascader: renderAreaCascader as Renderer,
56
+ custom: renderCustom as Renderer,
57
+ }
58
+
59
+ // 导出的渲染器映射,支持动态修改
60
+ export const rendererMap: Record<string, Renderer> = { ...defaultRendererMap }
61
+
62
+ /**
63
+ * 注册自定义渲染器
64
+ * @param name 渲染器名称
65
+ * @param renderer 渲染函数
66
+ */
67
+ export function registerRenderer(name: string, renderer: Renderer) {
68
+ rendererMap[name] = renderer
69
+ }
70
+
71
+ /**
72
+ * 批量注册自定义渲染器
73
+ * @param renderers 渲染器映射对象
74
+ */
75
+ export function registerRenderers(renderers: Record<string, Renderer>) {
76
+ Object.assign(rendererMap, renderers)
77
+ }
78
+
79
+ /**
80
+ * 获取渲染器
81
+ * @param name 渲染器名称
82
+ * @returns 渲染函数
83
+ */
84
+ export function getRenderer(name: string): Renderer | undefined {
85
+ return rendererMap[name]
47
86
  }
48
87
 
49
88
  export {
@@ -58,10 +97,12 @@ export {
58
97
  renderInputGroup,
59
98
  renderInputNumber,
60
99
  renderInputPassword,
100
+ renderInputRange,
61
101
  renderRadioGroup,
62
102
  renderSelect,
63
103
  renderSselectPage,
64
104
  renderSwitch,
105
+ renderTree,
65
106
  renderTreeSelect,
66
107
  renderUpload,
67
108
  }
@@ -11,6 +11,7 @@ import type {
11
11
  RowProps,
12
12
  SelectProps,
13
13
  SwitchProps,
14
+ TreeProps,
14
15
  TreeSelectProps,
15
16
  UploadProps,
16
17
  } from '@antsoo-lib/components'
@@ -18,6 +19,7 @@ import type { AnyObject, VoidFunction } from '@antsoo-lib/shared'
18
19
 
19
20
  import type { VNode } from 'vue'
20
21
 
22
+ import type { CustomToolbarItem } from './Custom'
21
23
  import type { ToolbarState } from './state'
22
24
 
23
25
  // 事件处理函数类型定义
@@ -205,6 +207,46 @@ export interface TreeSelectToolbarItem extends BaseToolbarItem {
205
207
  }
206
208
  }
207
209
 
210
+ export interface TreeToolbarItem extends BaseToolbarItem {
211
+ type: 'tree'
212
+ props?: Partial<TreeProps> & {
213
+ checkedKeys?: any[]
214
+ expandedKeys?: any[]
215
+ }
216
+ events?: BaseEvents & {
217
+ 'onUpdate:checkedKeys'?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void
218
+ 'onUpdate:expandedKeys'?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void
219
+ onCheck?: (value: any, info: any, allValues: AnyObject, toolbarState: ToolbarState) => void
220
+ onExpand?: (value: any, info: any, allValues: AnyObject, toolbarState: ToolbarState) => void
221
+ onChange?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void
222
+ }
223
+ }
224
+
225
+ export interface InputRangeToolbarItem extends BaseToolbarItem {
226
+ type: 'inputRange'
227
+ props?: {
228
+ startProps?: Record<string, unknown>
229
+ endProps?: Record<string, unknown>
230
+ separator?: string
231
+ startPlaceholder?: string
232
+ endPlaceholder?: string
233
+ style?: AnyObject
234
+ className?: string
235
+ allowClear?: boolean
236
+ }
237
+ attr?: string[]
238
+ events?: BaseEvents & {
239
+ 'onUpdate:value'?: (
240
+ value: [string, string],
241
+ allValues: AnyObject,
242
+ toolbarState: ToolbarState,
243
+ ) => void
244
+ onChange?: (value: [string, string], allValues: AnyObject, toolbarState: ToolbarState) => void
245
+ onStartChange?: (value: string, allValues: AnyObject, toolbarState: ToolbarState) => void
246
+ onEndChange?: (value: string, allValues: AnyObject, toolbarState: ToolbarState) => void
247
+ }
248
+ }
249
+
208
250
  export interface InputNumberToolbarItem extends BaseToolbarItem {
209
251
  type: 'inputNumber'
210
252
  props?: Partial<InputNumberProps> & {
@@ -312,15 +354,23 @@ export interface SselectPageProps {
312
354
  allowClear?: boolean
313
355
  showSearch?: boolean
314
356
  disabled?: boolean
315
- maxTagCount?: number
357
+ maxTagCount?: number | 'responsive'
316
358
  size?: 'large' | 'middle' | 'small'
317
359
  mode?: 'multiple' | 'tags'
360
+ params?: AnyObject
318
361
  api: {
319
362
  url: string
320
363
  method?: 'GET' | 'POST'
321
364
  headers?: Record<string, string>
322
365
  params?: AnyObject
323
366
  data?: AnyObject
367
+ request?: (config: {
368
+ method?: 'GET' | 'POST'
369
+ url: string
370
+ headers?: Record<string, string>
371
+ params?: AnyObject
372
+ data?: AnyObject
373
+ }) => Promise<any>
324
374
  }
325
375
  dataMapping?: {
326
376
  list?: string
@@ -341,6 +391,7 @@ export interface SselectPageProps {
341
391
  transformData?: (data: any) => any
342
392
  autoLoad?: boolean
343
393
  autoFocus?: boolean
394
+ attr?: Record<string, string>
344
395
  [key: string]: any
345
396
  }
346
397
 
@@ -351,7 +402,13 @@ export interface SselectPageToolbarItem extends BaseToolbarItem {
351
402
  }
352
403
  events?: BaseEvents & {
353
404
  'onUpdate:value'?: (value: any, allValues: AnyObject, toolbarState: ToolbarState) => void
354
- onChange?: (value: any, option: any, allValues: AnyObject, toolbarState: ToolbarState) => void
405
+ onChange?: (
406
+ value: any,
407
+ option: any,
408
+ allOptions: any,
409
+ allValues: AnyObject,
410
+ toolbarState: ToolbarState,
411
+ ) => void
355
412
  onSearch?: (value: string, allValues: AnyObject, toolbarState: ToolbarState) => void
356
413
  onClear?: (allValues: AnyObject, toolbarState: ToolbarState) => void
357
414
  }
@@ -457,6 +514,7 @@ export type RenderableToolbarItem =
457
514
  | UploadToolbarItem
458
515
  | InputNumberToolbarItem
459
516
  | SelectToolbarItem
517
+ | TreeToolbarItem
460
518
  | TreeSelectToolbarItem
461
519
  | InputPasswordToolbarItem
462
520
  | SselectPageToolbarItem
@@ -464,6 +522,8 @@ export type RenderableToolbarItem =
464
522
  | InputGroupToolbarItem
465
523
  | CascaderToolbarItem
466
524
  | AreaCascaderToolbarItem
525
+ | InputRangeToolbarItem
526
+ | CustomToolbarItem
467
527
 
468
528
  export type ToolbarItem = RenderableToolbarItem | PresetToolbarItem
469
529
 
@@ -0,0 +1,106 @@
1
+ /**
2
+ * 通用 attr 映射工具函数集合
3
+ * - 支持 attr 为字符串数组(按顺序映射)或对象映射(targetKey -> sourcePath)
4
+ * - 对象映射不再限制 ids/names,允许任意键值对
5
+ */
6
+ import type { AnyObject } from '@antsoo-lib/shared'
7
+ import { isArray, isObject } from '@antsoo-lib/utils'
8
+
9
+ /**
10
+ * 安全获取嵌套属性值,支持 a.b.c 路径
11
+ */
12
+ export function getByPath(obj: any, path?: string): any {
13
+ if (!path || typeof path !== 'string') return undefined
14
+ return path.split('.').reduce((cur: any, key: string) => (cur ? cur[key] : undefined), obj)
15
+ }
16
+
17
+ /**
18
+ * 根据 attr 规则从 formData 聚合初始值:
19
+ * - 数组:返回对应字段的值数组
20
+ * - 对象:返回 { [sourcePath]: formData[targetKey] } 结构,便于组件按源字段进行回显
21
+ */
22
+ export function buildValueFromAttr(formData: AnyObject, attr: string[] | AnyObject): any {
23
+ // 1) 数组:按顺序返回对应值
24
+ if (isArray(attr)) {
25
+ return (attr as string[]).map((k) => formData[k])
26
+ }
27
+
28
+ // 2) 对象:支持两类结构并可混合:
29
+ // a) 分组数组结构(如 { ids: [..], names: [..] } 或 { values: [..], codes: [..] })
30
+ // 返回以数组元素为键的对象:{ [fieldKey]: formData[fieldKey] }
31
+ // b) 键值映射结构(如 { dataEditCompany: "id", dataEditCompanyName: "coName" })
32
+ // 返回以 sourcePath 为键的对象:{ [sourcePath]: formData[targetKey] }
33
+ if (isObject(attr)) {
34
+ const result: AnyObject = {}
35
+ let handled = false
36
+
37
+ // a) 处理分组数组结构:遍历所有值为字符串数组的键
38
+ Object.entries(attr as AnyObject).forEach(([_, groupVal]) => {
39
+ if (isArray(groupVal)) {
40
+ ;(groupVal as string[]).forEach((fieldKey) => {
41
+ if (!fieldKey) return
42
+ result[fieldKey] = formData[fieldKey]
43
+ })
44
+ handled = true
45
+ }
46
+ })
47
+
48
+ // b) 处理键值映射结构:遍历所有值为字符串的键
49
+ Object.entries(attr as AnyObject).forEach(([targetKey, sourcePath]) => {
50
+ if (typeof sourcePath === 'string') {
51
+ result[sourcePath] = formData[targetKey]
52
+ handled = true
53
+ }
54
+ })
55
+
56
+ // 如果所有映射得到的值都为 undefined/null,则不返回占位对象,避免清空场景下出现形如 { id: ... } 的残留对象
57
+ if (!handled) return undefined
58
+ const allUndefined =
59
+ Object.keys(result).length > 0 &&
60
+ Object.values(result).every((v) => v === undefined || v === null)
61
+ return allUndefined ? undefined : result
62
+ }
63
+
64
+ return undefined
65
+ }
66
+
67
+ /**
68
+ * 按 attr 对象映射将选项数据写入 formData:
69
+ * - 支持单选与多选;
70
+ * - 优先使用 option.__raw 原始对象,其次使用 option 自身;
71
+ * - sourcePath 支持嵌套路径(例如 'data.id')。
72
+ */
73
+ export function applyAttrMapping(
74
+ formData: AnyObject,
75
+ toolbarState: { setValue: (k: string, v: any) => void },
76
+ attr: Record<string, string>,
77
+ value: any,
78
+ option: any,
79
+ isMultiple: boolean,
80
+ ) {
81
+ const mapOne = (opt: any, targetKey: string, sourcePath: string) => {
82
+ const raw = opt && (opt.__raw ?? opt)
83
+ const v = getByPath(raw, sourcePath)
84
+ formData[targetKey] = v
85
+ toolbarState.setValue(targetKey, v)
86
+ return v
87
+ }
88
+
89
+ Object.entries(attr).forEach(([targetKey, sourcePath]) => {
90
+ if (!targetKey || !sourcePath || typeof sourcePath !== 'string') return
91
+ if (isMultiple) {
92
+ const arr = Array.isArray(option) ? option : []
93
+ const values = arr.map((opt) => mapOne(opt, targetKey, sourcePath))
94
+ formData[targetKey] = values
95
+ toolbarState.setValue(targetKey, values)
96
+ } else {
97
+ mapOne(option, targetKey, sourcePath)
98
+ }
99
+ })
100
+ }
101
+
102
+ export default {
103
+ buildValueFromAttr,
104
+ applyAttrMapping,
105
+ getByPath,
106
+ }
package/vite.config.ts CHANGED
@@ -12,7 +12,8 @@ export default defineConfig({
12
12
  vue(),
13
13
  vueJsx(),
14
14
  dts({
15
- include: ['index.ts', 'src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
15
+ entryRoot: 'src',
16
+ include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
16
17
  outDir: 'dist/types',
17
18
  cleanVueFileName: true,
18
19
  }),
@@ -20,7 +21,7 @@ export default defineConfig({
20
21
  build: {
21
22
  cssCodeSplit: false,
22
23
  lib: {
23
- entry: resolve(__dirname, 'index.ts'),
24
+ entry: resolve(__dirname, './src/index.ts'),
24
25
  name: 'AntsooTableCore',
25
26
  fileName: (format) => `index.${format === 'es' ? 'js' : 'cjs'}`,
26
27
  formats: ['es', 'cjs'],
@@ -32,6 +33,12 @@ export default defineConfig({
32
33
  '@antsoo-lib/shared',
33
34
  '@antsoo-lib/components',
34
35
  'lodash-es',
36
+ '@vxe-ui/plugin-export-xlsx',
37
+ '@vxe-ui/plugin-menu',
38
+ '@vxe-ui/plugin-render-wangeditor',
39
+ 'vxe-pc-ui',
40
+ 'vxe-table',
41
+ 'xe-utils',
35
42
  /^@ant-design\/icons-svg/,
36
43
  ],
37
44
  output: {
@@ -41,6 +48,12 @@ export default defineConfig({
41
48
  '@antsoo-lib/shared': 'AntsooShared',
42
49
  '@antsoo-lib/components': 'AntsooComponents',
43
50
  'lodash-es': 'lodash-es',
51
+ '@vxe-ui/plugin-export-xlsx': 'VxeExportXlsxPlugin',
52
+ '@vxe-ui/plugin-menu': 'VxeMenuPlugin',
53
+ '@vxe-ui/plugin-render-wangeditor': 'VxeRenderWangEditorPlugin',
54
+ 'vxe-pc-ui': 'VxePCUI',
55
+ 'vxe-table': 'VxeTable',
56
+ 'xe-utils': 'XeUtils',
44
57
  },
45
58
  },
46
59
  },
package/index.css DELETED
@@ -1,2 +0,0 @@
1
- @import 'vxe-pc-ui/lib/style.css';
2
- @import 'vxe-table/lib/style.css';
package/index.ts DELETED
@@ -1,21 +0,0 @@
1
- /**
2
- * antsoo-lib Core
3
- */
4
- import VxeUI from 'vxe-pc-ui'
5
- import VxeTable from 'vxe-table'
6
-
7
- import type { App } from 'vue'
8
-
9
- import './index.css'
10
- import { BaseTable } from './src'
11
-
12
- export const version = '0.0.0'
13
-
14
- export * from 'xe-utils'
15
-
16
- export const CoreTable = {
17
- install(app: App) {
18
- app.component('BaseTable', BaseTable)
19
- app.use(VxeUI).use(VxeTable)
20
- },
21
- }
@@ -1,91 +0,0 @@
1
- import { Select } from '@antsoo-lib/components'
2
- import type { VoidFunction } from '@antsoo-lib/shared'
3
-
4
- import { markRaw, nextTick } from 'vue'
5
- import type { VNode } from 'vue'
6
-
7
- import type { ToolbarState } from './state'
8
- import type { InputGroupToolbarItem, SlotConfig } from './types'
9
-
10
- // 渲染插槽内容的辅助函数
11
- export function renderSlotContent(
12
- slotConfig: SlotConfig,
13
- toolbarState: ToolbarState,
14
- ): VNode | undefined {
15
- if (!slotConfig?.content) return undefined
16
-
17
- // 如果是函数,直接调用
18
- if (typeof slotConfig.content === 'function') {
19
- return slotConfig.content(toolbarState)
20
- }
21
-
22
- // 如果是组件配置对象
23
- const { component, key, props = {}, events = {}, options = [] } = slotConfig.content
24
- const allValues = toolbarState.getAllValues()
25
-
26
- // 处理事件,注入工具栏状态
27
- const processedEvents: Record<string, VoidFunction> = {}
28
- Object.entries(events).forEach(([eventName, handler]) => {
29
- if (eventName === 'onChange') {
30
- // 对于 onChange 事件,使用 nextTick 确保在下一个 tick 中执行外部处理函数
31
- processedEvents[eventName] = (...args: any[]) => {
32
- nextTick(() => {
33
- handler(...args, allValues, toolbarState)
34
- })
35
- }
36
- } else {
37
- // 其他事件保持原有逻辑
38
- processedEvents[eventName] = (...args: any[]) => {
39
- handler(...args, allValues, toolbarState)
40
- }
41
- }
42
- })
43
-
44
- const value = allValues[key]
45
-
46
- // 如果是Select组件且有options,添加选项子节点
47
- if (component === Select && options.length > 0) {
48
- const Comp = markRaw(component)
49
- return (
50
- <Comp value={value ?? ''} {...props} {...processedEvents}>
51
- {options.map((option: any) => (
52
- <Select.Option key={option.value} value={option.value}>
53
- {option.label}
54
- </Select.Option>
55
- ))}
56
- </Comp>
57
- )
58
- }
59
-
60
- // 普通组件渲染
61
- const Comp = markRaw(component)
62
- return <Comp value={value ?? ''} {...props} {...processedEvents} />
63
- }
64
-
65
- export function validateInputGroupConfig(cfg: InputGroupToolbarItem): {
66
- valid: boolean
67
- errors?: string[]
68
- } {
69
- const errors: string[] = []
70
- if (!cfg) errors.push('inputGroup config is required')
71
- if (cfg.type !== 'inputGroup') errors.push("type must be 'inputGroup'")
72
- if (!Array.isArray(cfg.children) || cfg.children.length === 0)
73
- errors.push('children must be a non-empty array')
74
- // 校验子项 key 唯一性
75
- const keys = (cfg.children || []).map((c) => c.key).filter(Boolean) as string[]
76
- const dup = keys.filter((k, i) => keys.indexOf(k) !== i)
77
- if (dup.length) errors.push(`duplicate child keys: ${Array.from(new Set(dup)).join(',')}`)
78
- return { valid: errors.length === 0, errors: errors.length ? errors : undefined }
79
- }
80
-
81
- // InputNumber 的焦点态只用于“显示逻辑(失焦补零)”,不属于业务值:
82
- // - 不写入 ToolbarState,避免污染 getAllValues() 及其下游(按钮事件、联动禁用、外部 expose、deep watch 等)
83
- // - 采用 WeakMap:当 ToolbarState 实例被销毁且无引用时,对应缓存可被 GC 自动回收
84
- const inputNumberFocusState = new WeakMap<ToolbarState, Map<string, boolean>>()
85
- export function getInputNumberFocusState(toolbarState: ToolbarState) {
86
- const existing = inputNumberFocusState.get(toolbarState)
87
- if (existing) return existing
88
- const next = new Map<string, boolean>()
89
- inputNumberFocusState.set(toolbarState, next)
90
- return next
91
- }
File without changes
File without changes