@antsoo-lib/core 0.0.0 → 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/CHANGELOG.md +13 -0
- package/dist/core.css +1 -0
- package/dist/index.cjs +61 -0
- package/dist/index.js +50858 -0
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/core/src/BaseTable/helpers.d.ts +9 -0
- package/dist/types/core/src/BaseTable/index.d.ts +190 -0
- package/dist/types/core/src/BaseTable/registry.d.ts +9 -0
- package/dist/types/core/src/BaseTable/renderers/AreaCascader.d.ts +5 -0
- package/dist/types/core/src/BaseTable/renderers/AutoComplete.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/Button.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/Cascader.d.ts +5 -0
- package/dist/types/core/src/BaseTable/renderers/Checkbox.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/CheckboxGroup.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/DatePicker.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/Input.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/InputGroup.d.ts +5 -0
- package/dist/types/core/src/BaseTable/renderers/InputNumber.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/InputPassword.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/RadioGroup.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/Select.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/SselectPage.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/Switch.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/TreeSelect.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/Upload.d.ts +4 -0
- package/dist/types/core/src/BaseTable/renderers/index.d.ts +24 -0
- package/dist/types/core/src/BaseTable/state.d.ts +19 -0
- package/dist/types/core/src/BaseTable/types.d.ts +391 -0
- package/dist/types/core/src/BaseTable/utils.d.ts +8 -0
- package/dist/types/core/src/index.d.ts +2 -0
- package/index.css +2 -0
- package/index.ts +21 -0
- package/package.json +39 -3
- package/src/BaseTable/helpers.tsx +91 -0
- package/src/BaseTable/index.vue +881 -0
- package/src/BaseTable/registry.ts +20 -0
- package/src/BaseTable/renderers/AreaCascader.tsx +64 -0
- package/src/BaseTable/renderers/AutoComplete.tsx +101 -0
- package/src/BaseTable/renderers/Button.tsx +62 -0
- package/src/BaseTable/renderers/Cascader.tsx +45 -0
- package/src/BaseTable/renderers/Checkbox.tsx +65 -0
- package/src/BaseTable/renderers/CheckboxGroup.tsx +57 -0
- package/src/BaseTable/renderers/DatePicker.tsx +83 -0
- package/src/BaseTable/renderers/Input.tsx +140 -0
- package/src/BaseTable/renderers/InputGroup.tsx +115 -0
- package/src/BaseTable/renderers/InputNumber.tsx +205 -0
- package/src/BaseTable/renderers/InputPassword.tsx +81 -0
- package/src/BaseTable/renderers/RadioGroup.tsx +63 -0
- package/src/BaseTable/renderers/Select.tsx +96 -0
- package/src/BaseTable/renderers/SselectPage.tsx +107 -0
- package/src/BaseTable/renderers/Switch.tsx +60 -0
- package/src/BaseTable/renderers/TreeSelect.tsx +81 -0
- package/src/BaseTable/renderers/Upload.tsx +92 -0
- package/src/BaseTable/renderers/index.ts +67 -0
- package/src/BaseTable/state.ts +37 -0
- package/src/BaseTable/types.ts +507 -0
- package/src/BaseTable/utils.tsx +144 -0
- package/src/index.ts +3 -0
- package/vite.config.ts +48 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Component } from 'vue'
|
|
2
|
+
|
|
3
|
+
// 外部组件注册表
|
|
4
|
+
const componentRegistry: Record<string, Component> = {}
|
|
5
|
+
|
|
6
|
+
// 注册外部组件
|
|
7
|
+
export function registerBaseTableComponents(components: {
|
|
8
|
+
SselectPage?: Component
|
|
9
|
+
AutoComplete?: Component
|
|
10
|
+
Cascader?: Component
|
|
11
|
+
AreaCascader?: Component
|
|
12
|
+
Upload?: Component
|
|
13
|
+
}) {
|
|
14
|
+
Object.assign(componentRegistry, components)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 获取注册的组件,如果未注册则返回 undefined
|
|
18
|
+
export function getRegisteredComponent(name: string): Component | undefined {
|
|
19
|
+
return componentRegistry[name]
|
|
20
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AnyObject } from '@antsoo-lib/shared'
|
|
2
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
3
|
+
|
|
4
|
+
import { markRaw } from 'vue'
|
|
5
|
+
import type { VNode } from 'vue'
|
|
6
|
+
|
|
7
|
+
import { getRegisteredComponent } from '../registry'
|
|
8
|
+
import type { ToolbarState } from '../state'
|
|
9
|
+
import type { AreaCascaderProps, AreaCascaderToolbarItem } from '../types'
|
|
10
|
+
|
|
11
|
+
// 渲染地区级联选择器
|
|
12
|
+
export function renderAreaCascader(
|
|
13
|
+
config: AreaCascaderToolbarItem,
|
|
14
|
+
toolbarState: ToolbarState,
|
|
15
|
+
allValues?: AnyObject,
|
|
16
|
+
): VNode {
|
|
17
|
+
const { key = '', props = {}, events = {}, disabled = false } = config
|
|
18
|
+
|
|
19
|
+
// 处理动态禁用逻辑
|
|
20
|
+
const isDisabled = isFunction(disabled)
|
|
21
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
22
|
+
: disabled
|
|
23
|
+
const finalProps: AreaCascaderProps = { ...props, disabled: isDisabled }
|
|
24
|
+
|
|
25
|
+
// 计算安全的当前值(优先取 toolbarState,再兜底 props.value),不做任何业务级聚合
|
|
26
|
+
const safeValue =
|
|
27
|
+
toolbarState.getValue(key) !== undefined ? toolbarState.getValue(key) : props.value
|
|
28
|
+
|
|
29
|
+
// 处理事件,统一注入上下文,保持中立
|
|
30
|
+
const processedEvents: AnyObject = {}
|
|
31
|
+
Object.keys(events).forEach((eventName) => {
|
|
32
|
+
const handler = events[eventName as keyof typeof events]
|
|
33
|
+
if (!handler || !isFunction(handler)) return
|
|
34
|
+
const normalizedName = eventName.startsWith('on')
|
|
35
|
+
? eventName
|
|
36
|
+
: `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`
|
|
37
|
+
processedEvents[normalizedName] = (...args: any[]) => {
|
|
38
|
+
handler(...args, allValues ?? toolbarState.getAllValues(), toolbarState)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// 默认的 v-model 更新(仅当存在 key 时写入状态)
|
|
43
|
+
if (!processedEvents['onUpdate:value']) {
|
|
44
|
+
processedEvents['onUpdate:value'] = (val: any) => {
|
|
45
|
+
if (key) toolbarState.setValue(key, val)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const AreaCascaderComp = getRegisteredComponent('AreaCascader')
|
|
50
|
+
if (!AreaCascaderComp) {
|
|
51
|
+
return <span style={{ color: 'red' }}>AreaCascader component missing</span>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const Comp = markRaw(AreaCascaderComp) as any
|
|
55
|
+
return (
|
|
56
|
+
<Comp
|
|
57
|
+
key={key}
|
|
58
|
+
value={safeValue}
|
|
59
|
+
{...finalProps}
|
|
60
|
+
{...(config.attr ? { attr: config.attr } : {})}
|
|
61
|
+
{...processedEvents}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { VoidFunction } from '@antsoo-lib/shared'
|
|
2
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
3
|
+
|
|
4
|
+
import { computed, markRaw, nextTick } from 'vue'
|
|
5
|
+
import type { ComponentPublicInstance, VNode } from 'vue'
|
|
6
|
+
|
|
7
|
+
import { getRegisteredComponent } from '../registry'
|
|
8
|
+
import type { ToolbarState } from '../state'
|
|
9
|
+
import type { AutoCompleteToolbarItem, ToolbarItem } from '../types'
|
|
10
|
+
|
|
11
|
+
export function renderAutoComplete(item: ToolbarItem, toolbarState: ToolbarState): VNode {
|
|
12
|
+
const { key, events = {} } = item
|
|
13
|
+
const { props = {}, disabled = false } = item as AutoCompleteToolbarItem
|
|
14
|
+
|
|
15
|
+
const isDisabled = isFunction(disabled)
|
|
16
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
17
|
+
: disabled
|
|
18
|
+
|
|
19
|
+
// 处理事件,注入工具栏状态
|
|
20
|
+
const processedEvents: Record<string, VoidFunction> = {}
|
|
21
|
+
Object.keys(events).forEach((eventName) => {
|
|
22
|
+
processedEvents[eventName] = (...args: any[]) => {
|
|
23
|
+
// 对于 SselectPage,先更新状态,再调用外部事件处理函数
|
|
24
|
+
if (eventName === 'onUpdate:value' || eventName === 'onChange') {
|
|
25
|
+
const value = args[0] // SselectPage 的第一个参数是选中的值
|
|
26
|
+
// 立即更新工具栏状态,确保外部事件处理函数能获取到最新值
|
|
27
|
+
toolbarState.setValue(key, value)
|
|
28
|
+
|
|
29
|
+
// 对于 onChange 事件,使用 nextTick 确保在下一个 tick 中执行外部处理函数
|
|
30
|
+
if (eventName === 'onChange') {
|
|
31
|
+
nextTick(() => {
|
|
32
|
+
events[eventName]?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
33
|
+
})
|
|
34
|
+
return // 提前返回,避免重复执行
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 调用原始事件处理函数,传入完整的上下文
|
|
39
|
+
events[eventName]?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// 如果没有设置 onChange 事件,添加默认的状态更新
|
|
44
|
+
if (!processedEvents['onUpdate:value'] && !processedEvents.onChange) {
|
|
45
|
+
processedEvents['onUpdate:value'] = (value: any) => {
|
|
46
|
+
toolbarState.setValue(key, value)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 处理 extraParams 的响应式更新
|
|
51
|
+
// 如果 props.extraParams 是函数,则动态计算
|
|
52
|
+
const computedProps = computed(() => {
|
|
53
|
+
const allValues = toolbarState.getAllValues()
|
|
54
|
+
const dynamicProps: any = { ...props, disabled: isDisabled }
|
|
55
|
+
|
|
56
|
+
// 动态计算 extraParams
|
|
57
|
+
if (isFunction(dynamicProps.extraParams)) {
|
|
58
|
+
dynamicProps.extraParams = dynamicProps.extraParams(allValues, toolbarState)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return dynamicProps
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// 如果需要自动聚焦,添加 ref 处理
|
|
65
|
+
if (props.autoFocus) {
|
|
66
|
+
processedEvents.ref = (el: Element | ComponentPublicInstance | null) => {
|
|
67
|
+
const component = el as ComponentPublicInstance
|
|
68
|
+
if (component) {
|
|
69
|
+
// 使用 nextTick 确保 DOM 已经渲染完成
|
|
70
|
+
nextTick(() => {
|
|
71
|
+
try {
|
|
72
|
+
;(component as any).$refs.selectRef?.focus?.()
|
|
73
|
+
} catch {
|
|
74
|
+
// 忽略焦点设置失败的错误
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 计算安全的默认值
|
|
82
|
+
const rawVal = toolbarState.getValue(key)
|
|
83
|
+
const propVal = props.value
|
|
84
|
+
const safeValue =
|
|
85
|
+
rawVal !== undefined && rawVal !== null
|
|
86
|
+
? rawVal
|
|
87
|
+
: propVal !== undefined && propVal !== null
|
|
88
|
+
? propVal
|
|
89
|
+
: undefined
|
|
90
|
+
|
|
91
|
+
const AutoCompleteComponent = getRegisteredComponent('AutoComplete')
|
|
92
|
+
if (!AutoCompleteComponent) {
|
|
93
|
+
console.warn('BaseTable: AutoComplete component is not registered.')
|
|
94
|
+
return <span style={{ color: 'red' }}>AutoComplete component missing</span>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const Comp = markRaw(AutoCompleteComponent) as any
|
|
98
|
+
return (
|
|
99
|
+
<Comp key={key} value={safeValue} allowClear {...computedProps.value} {...processedEvents} />
|
|
100
|
+
)
|
|
101
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Button } from '@antsoo-lib/components'
|
|
2
|
+
import type { VoidFunction } from '@antsoo-lib/shared'
|
|
3
|
+
|
|
4
|
+
import { reactive } from 'vue'
|
|
5
|
+
import type { VNode } from 'vue'
|
|
6
|
+
|
|
7
|
+
import type { ToolbarState } from '../state'
|
|
8
|
+
import type { ButtonToolbarItem, ToolbarItem } from '../types'
|
|
9
|
+
|
|
10
|
+
// 按钮渲染函数
|
|
11
|
+
export function renderButton(item: ToolbarItem, toolbarState: ToolbarState): VNode {
|
|
12
|
+
const buttonItem = item as ButtonToolbarItem
|
|
13
|
+
const { key, props = {}, events = {}, disabled = false } = buttonItem
|
|
14
|
+
const reactiveProps = reactive({ ...props })
|
|
15
|
+
|
|
16
|
+
const isDisabled =
|
|
17
|
+
typeof disabled === 'function'
|
|
18
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
19
|
+
: !!disabled
|
|
20
|
+
|
|
21
|
+
// 为每个按钮创建独立的 loading 状态键
|
|
22
|
+
const loadingKey = `${key}_loading`
|
|
23
|
+
|
|
24
|
+
// 初始化按钮的 loading 状态(如果还未设置)
|
|
25
|
+
if (toolbarState.getValue(loadingKey) === undefined) {
|
|
26
|
+
toolbarState.setValue(loadingKey, false)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 创建 loading 控制对象,传入事件方法中
|
|
30
|
+
const loadingControl = {
|
|
31
|
+
setLoading: (loading: boolean) => {
|
|
32
|
+
toolbarState.setValue(loadingKey, loading)
|
|
33
|
+
},
|
|
34
|
+
getLoading: () => {
|
|
35
|
+
return toolbarState.getValue(loadingKey) || false
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 处理事件,注入工具栏状态和 loading 控制
|
|
40
|
+
const processedEvents: Record<string, VoidFunction> = {}
|
|
41
|
+
Object.keys(events).forEach((eventName) => {
|
|
42
|
+
const eventHandler = events[eventName]
|
|
43
|
+
if (eventHandler) {
|
|
44
|
+
processedEvents[eventName] = (...args: any[]) => {
|
|
45
|
+
// 调用原始事件处理函数,并传入 loading 控制对象
|
|
46
|
+
eventHandler(loadingControl, toolbarState.getAllValues(), toolbarState, ...args)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const finalProps = {
|
|
52
|
+
...reactiveProps,
|
|
53
|
+
disabled: isDisabled || reactiveProps.disabled || false,
|
|
54
|
+
loading: toolbarState.getValue(loadingKey) || reactiveProps.loading || false,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Button key={key} {...finalProps} {...processedEvents}>
|
|
59
|
+
{item.label || '按钮'}
|
|
60
|
+
</Button>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AnyObject } from '@antsoo-lib/shared'
|
|
2
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
3
|
+
|
|
4
|
+
import { markRaw } from 'vue'
|
|
5
|
+
import type { VNode } from 'vue'
|
|
6
|
+
|
|
7
|
+
import { getRegisteredComponent } from '../registry'
|
|
8
|
+
import type { ToolbarState } from '../state'
|
|
9
|
+
import type { CascaderToolbarItem } from '../types'
|
|
10
|
+
|
|
11
|
+
// 渲染级联选择器
|
|
12
|
+
export function renderCascader(
|
|
13
|
+
config: CascaderToolbarItem,
|
|
14
|
+
toolbarState: ToolbarState,
|
|
15
|
+
allValues: AnyObject,
|
|
16
|
+
): VNode {
|
|
17
|
+
const { key = '', props = {}, events = {} } = config
|
|
18
|
+
const value = toolbarState.getValue(key)
|
|
19
|
+
|
|
20
|
+
// 处理事件
|
|
21
|
+
const cascaderEvents: AnyObject = {}
|
|
22
|
+
Object.keys(events).forEach((eventName) => {
|
|
23
|
+
const handler = events[eventName as keyof typeof events]
|
|
24
|
+
if (handler && isFunction(handler)) {
|
|
25
|
+
cascaderEvents[eventName] = (...args: any[]) => {
|
|
26
|
+
handler(...args, allValues, toolbarState)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// 特殊处理 update:value 事件
|
|
32
|
+
cascaderEvents['update:value'] = (val: any) => {
|
|
33
|
+
toolbarState.setValue(key, val)
|
|
34
|
+
events['onUpdate:value']?.(val, allValues, toolbarState)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 渲染组件
|
|
38
|
+
const CascaderComp = getRegisteredComponent('Cascader')
|
|
39
|
+
if (!CascaderComp) {
|
|
40
|
+
return <span style={{ color: 'red' }}>Cascader component missing</span>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const Comp = markRaw(CascaderComp) as any
|
|
44
|
+
return <Comp value={value} {...props} {...cascaderEvents} />
|
|
45
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Checkbox } from '@antsoo-lib/components'
|
|
2
|
+
import type { CheckboxProps } from '@antsoo-lib/components'
|
|
3
|
+
import type { VoidFunction } from '@antsoo-lib/shared'
|
|
4
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
5
|
+
|
|
6
|
+
import { markRaw } from 'vue'
|
|
7
|
+
import type { VNode } from 'vue'
|
|
8
|
+
|
|
9
|
+
import type { ToolbarState } from '../state'
|
|
10
|
+
import type { CheckboxToolbarItem, ToolbarItem } from '../types'
|
|
11
|
+
|
|
12
|
+
// 复选框渲染函数
|
|
13
|
+
export function renderCheckbox(item: ToolbarItem, toolbarState: ToolbarState): VNode {
|
|
14
|
+
const checkboxItem = item as CheckboxToolbarItem
|
|
15
|
+
const { key, events = {} } = checkboxItem
|
|
16
|
+
const { props = {}, disabled = false } = checkboxItem
|
|
17
|
+
|
|
18
|
+
const isDisabled = isFunction(disabled)
|
|
19
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
20
|
+
: disabled
|
|
21
|
+
const finalProps: Partial<CheckboxProps> & {
|
|
22
|
+
disabled?: boolean
|
|
23
|
+
checked?: boolean
|
|
24
|
+
value?: boolean
|
|
25
|
+
} = {
|
|
26
|
+
...props,
|
|
27
|
+
disabled: isDisabled,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 处理事件,注入工具栏状态
|
|
31
|
+
const processedEvents: Record<string, VoidFunction> = {}
|
|
32
|
+
Object.keys(events).forEach((eventName) => {
|
|
33
|
+
processedEvents[eventName] = (...args: any[]) => {
|
|
34
|
+
// 对于复选框,自动更新状态
|
|
35
|
+
if (eventName === 'onUpdate:checked' || eventName === 'onChange') {
|
|
36
|
+
const checked = args[0]?.target?.checked ?? args[0] // 兼容不同的事件格式
|
|
37
|
+
toolbarState.setValue(key, checked)
|
|
38
|
+
// 显式触发 onUpdate:value,确保 BaseForm 能接收到数据更新
|
|
39
|
+
events['onUpdate:value']?.(checked, toolbarState.getAllValues(), toolbarState)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 调用原始事件处理函数
|
|
43
|
+
events[eventName]?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// 如果没有设置onChange事件,添加默认的状态更新
|
|
48
|
+
if (!processedEvents['onUpdate:checked'] && !processedEvents.onChange) {
|
|
49
|
+
processedEvents['onUpdate:checked'] = (checked: boolean) => {
|
|
50
|
+
toolbarState.setValue(key, checked)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const Comp = markRaw(Checkbox)
|
|
55
|
+
return (
|
|
56
|
+
<Comp
|
|
57
|
+
key={key}
|
|
58
|
+
checked={toolbarState.getValue(key) ?? finalProps.checked ?? finalProps.value ?? false}
|
|
59
|
+
{...finalProps}
|
|
60
|
+
{...processedEvents}
|
|
61
|
+
>
|
|
62
|
+
{checkboxItem.label}
|
|
63
|
+
</Comp>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CheckboxGroup } from '@antsoo-lib/components'
|
|
2
|
+
import type { CheckboxGroupProps } from '@antsoo-lib/components'
|
|
3
|
+
import type { VoidFunction } from '@antsoo-lib/shared'
|
|
4
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
5
|
+
|
|
6
|
+
import { markRaw } from 'vue'
|
|
7
|
+
import type { VNode } from 'vue'
|
|
8
|
+
|
|
9
|
+
import type { ToolbarState } from '../state'
|
|
10
|
+
import type { CheckboxGroupToolbarItem, ToolbarItem } from '../types'
|
|
11
|
+
|
|
12
|
+
// 复选框组渲染函数
|
|
13
|
+
export function renderCheckboxGroup(item: ToolbarItem, toolbarState: ToolbarState): VNode {
|
|
14
|
+
const groupItem = item as CheckboxGroupToolbarItem
|
|
15
|
+
const { key, events = {} } = groupItem
|
|
16
|
+
const { props = {}, disabled = false } = groupItem
|
|
17
|
+
|
|
18
|
+
const isDisabled = isFunction(disabled)
|
|
19
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
20
|
+
: disabled
|
|
21
|
+
const finalProps: Partial<CheckboxGroupProps> & { disabled?: boolean; value?: any[] } = {
|
|
22
|
+
...props,
|
|
23
|
+
disabled: isDisabled,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 处理事件,注入工具栏状态
|
|
27
|
+
const processedEvents: Record<string, VoidFunction> = {}
|
|
28
|
+
Object.keys(events).forEach((eventName) => {
|
|
29
|
+
processedEvents[eventName] = (...args: any[]) => {
|
|
30
|
+
// 对于复选框组,自动更新状态
|
|
31
|
+
if (eventName === 'onUpdate:value' || eventName === 'onChange') {
|
|
32
|
+
const value = args[0] // CheckboxGroup 的 onChange 直接返回选中的值数组
|
|
33
|
+
toolbarState.setValue(key, value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 调用原始事件处理函数
|
|
37
|
+
events[eventName]?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// 如果没有设置onChange事件,添加默认的状态更新
|
|
42
|
+
if (!processedEvents['onUpdate:value'] && !processedEvents.onChange) {
|
|
43
|
+
processedEvents['onUpdate:value'] = (value) => {
|
|
44
|
+
toolbarState.setValue(key, value)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const Comp = markRaw(CheckboxGroup)
|
|
49
|
+
return (
|
|
50
|
+
<Comp
|
|
51
|
+
key={key}
|
|
52
|
+
value={toolbarState.getValue(key) || finalProps.value || []}
|
|
53
|
+
{...finalProps}
|
|
54
|
+
{...processedEvents}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { DatePicker, RangePicker } from '@antsoo-lib/components'
|
|
2
|
+
import type { DatePickerProps } from '@antsoo-lib/components'
|
|
3
|
+
import { rangePresets } from '@antsoo-lib/shared'
|
|
4
|
+
import type { VoidFunction } from '@antsoo-lib/shared'
|
|
5
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
6
|
+
|
|
7
|
+
import { markRaw, nextTick } from 'vue'
|
|
8
|
+
import type { ComponentPublicInstance, VNode } from 'vue'
|
|
9
|
+
|
|
10
|
+
import type { ToolbarState } from '../state'
|
|
11
|
+
import type { DatePickerToolbarItem, ToolbarItem } from '../types'
|
|
12
|
+
|
|
13
|
+
// 日期选择器渲染函数
|
|
14
|
+
export function renderDatePicker(item: ToolbarItem, toolbarState: ToolbarState): VNode {
|
|
15
|
+
const dateItem = item as DatePickerToolbarItem
|
|
16
|
+
const { key, events = {} } = dateItem
|
|
17
|
+
const { props = {}, disabled = false } = dateItem
|
|
18
|
+
|
|
19
|
+
const isDisabled = isFunction(disabled)
|
|
20
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
21
|
+
: disabled
|
|
22
|
+
const finalProps: Partial<DatePickerProps> & {
|
|
23
|
+
disabled?: boolean
|
|
24
|
+
value?: any
|
|
25
|
+
autoFocus?: boolean
|
|
26
|
+
} = {
|
|
27
|
+
...props,
|
|
28
|
+
disabled: isDisabled,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 处理事件,注入工具栏状态
|
|
32
|
+
const processedEvents: Record<string, VoidFunction> = {}
|
|
33
|
+
Object.keys(events).forEach((eventName) => {
|
|
34
|
+
processedEvents[eventName] = (...args: any[]) => {
|
|
35
|
+
// 对于日期选择器,自动更新状态
|
|
36
|
+
if (eventName === 'onUpdate:value' || eventName === 'onChange') {
|
|
37
|
+
const value = args[0]
|
|
38
|
+
toolbarState.setValue(key, value)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 调用原始事件处理函数
|
|
42
|
+
events[eventName]?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// 如果没有设置onChange事件,添加默认的状态更新
|
|
47
|
+
if (!processedEvents['onUpdate:value'] && !processedEvents.onChange) {
|
|
48
|
+
processedEvents['onUpdate:value'] = (value) => {
|
|
49
|
+
toolbarState.setValue(key, value)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 如果需要自动聚焦,添加ref处理
|
|
54
|
+
if (finalProps.autoFocus) {
|
|
55
|
+
processedEvents.ref = (el: Element | ComponentPublicInstance | null) => {
|
|
56
|
+
const element = el as HTMLElement
|
|
57
|
+
if (element) {
|
|
58
|
+
// 使用nextTick确保DOM已经渲染完成
|
|
59
|
+
nextTick(() => {
|
|
60
|
+
try {
|
|
61
|
+
element?.focus?.()
|
|
62
|
+
} catch {
|
|
63
|
+
// 忽略焦点设置失败的错误
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isRange = dateItem.range
|
|
71
|
+
const Comp = markRaw(isRange ? RangePicker : DatePicker) as any
|
|
72
|
+
return (
|
|
73
|
+
<Comp
|
|
74
|
+
key={key}
|
|
75
|
+
value={toolbarState.getValue(key) || finalProps.value}
|
|
76
|
+
allowClear
|
|
77
|
+
presets={isRange ? (rangePresets as any) : undefined}
|
|
78
|
+
popupClassName={isRange ? 'custom-date-picker-presets' : undefined}
|
|
79
|
+
{...finalProps}
|
|
80
|
+
{...processedEvents}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Input, Textarea } from '@antsoo-lib/components'
|
|
2
|
+
import type { InputProps } from '@antsoo-lib/components'
|
|
3
|
+
import type { VoidFunction } from '@antsoo-lib/shared'
|
|
4
|
+
import { isFunction } from '@antsoo-lib/utils'
|
|
5
|
+
|
|
6
|
+
import { markRaw, nextTick } from 'vue'
|
|
7
|
+
import type { ComponentPublicInstance, VNode } from 'vue'
|
|
8
|
+
|
|
9
|
+
import { renderSlotContent } from '../helpers'
|
|
10
|
+
import type { ToolbarState } from '../state'
|
|
11
|
+
import type { InputToolbarItem, ToolbarItem } from '../types'
|
|
12
|
+
|
|
13
|
+
// 输入框渲染函数
|
|
14
|
+
export function renderInput(item: ToolbarItem, toolbarState: ToolbarState): VNode {
|
|
15
|
+
const inputItem = item as InputToolbarItem
|
|
16
|
+
const { key, events = {}, slots } = inputItem
|
|
17
|
+
const { props = {}, disabled = false } = inputItem
|
|
18
|
+
|
|
19
|
+
const isDisabled = isFunction(disabled)
|
|
20
|
+
? disabled(toolbarState.getAllValues(), toolbarState)
|
|
21
|
+
: disabled
|
|
22
|
+
|
|
23
|
+
// Create a new props object to avoid mutating the original
|
|
24
|
+
const finalProps: Partial<InputProps> & {
|
|
25
|
+
disabled?: boolean
|
|
26
|
+
autoFocus?: boolean
|
|
27
|
+
value?: string
|
|
28
|
+
} = {
|
|
29
|
+
...props,
|
|
30
|
+
disabled: isDisabled,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 处理事件,注入工具栏状态
|
|
34
|
+
const processedEvents: Record<string, VoidFunction> = {}
|
|
35
|
+
Object.keys(events).forEach((eventName) => {
|
|
36
|
+
processedEvents[eventName] = (...args: any[]) => {
|
|
37
|
+
// 对于输入框:
|
|
38
|
+
// - v-model 使用 onUpdate:value 直接写入值
|
|
39
|
+
// - onChange 只在存在 event.target.value 时写入,避免将原始事件对象误写入状态
|
|
40
|
+
if (eventName === 'onUpdate:value') {
|
|
41
|
+
toolbarState.setValue(key, args[0])
|
|
42
|
+
} else if (eventName === 'onChange') {
|
|
43
|
+
const firstArg = args[0]
|
|
44
|
+
const hasTarget = firstArg && typeof firstArg === 'object' && 'target' in firstArg
|
|
45
|
+
const nextVal = hasTarget ? firstArg.target?.value : undefined
|
|
46
|
+
if (nextVal !== undefined) toolbarState.setValue(key, nextVal)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 调用原始事件处理函数
|
|
50
|
+
events[eventName]?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// 如果没有设置onChange事件,添加默认的状态更新
|
|
55
|
+
if (!processedEvents['onUpdate:value'] && !processedEvents.onChange) {
|
|
56
|
+
processedEvents['onUpdate:value'] = (value: string) => {
|
|
57
|
+
toolbarState.setValue(key, value)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 自动全选逻辑:合并 onFocus 事件
|
|
62
|
+
const userOnFocus = processedEvents.onFocus || events.onFocus
|
|
63
|
+
processedEvents.onFocus = (...args: any[]) => {
|
|
64
|
+
const e = args[0]
|
|
65
|
+
try {
|
|
66
|
+
if (e && e.target && typeof (e.target as HTMLInputElement).select === 'function') {
|
|
67
|
+
const target = e.target as HTMLInputElement
|
|
68
|
+
target.select()
|
|
69
|
+
|
|
70
|
+
// 阻止鼠标松开时的默认光标定位行为(仅一次),防止全选被取消
|
|
71
|
+
const preventMouseUp = (ev: Event) => {
|
|
72
|
+
ev.preventDefault()
|
|
73
|
+
target.removeEventListener('mouseup', preventMouseUp)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 监听 mouseup,确保在点击聚焦时能阻止光标重置
|
|
77
|
+
target.addEventListener('mouseup', preventMouseUp, { once: true })
|
|
78
|
+
|
|
79
|
+
// 清理机制:如果用户是键盘聚焦或其他方式,未触发 mouseup 就失焦了,需要移除监听器
|
|
80
|
+
const cleanup = () => {
|
|
81
|
+
target.removeEventListener('mouseup', preventMouseUp)
|
|
82
|
+
target.removeEventListener('blur', cleanup)
|
|
83
|
+
}
|
|
84
|
+
target.addEventListener('blur', cleanup)
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// ignore
|
|
88
|
+
}
|
|
89
|
+
// 执行用户原有逻辑
|
|
90
|
+
userOnFocus?.(...args, toolbarState.getAllValues(), toolbarState)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 如果需要自动聚焦,添加ref处理
|
|
94
|
+
if (finalProps.autoFocus) {
|
|
95
|
+
processedEvents.ref = (el: Element | ComponentPublicInstance | null) => {
|
|
96
|
+
const element = el as HTMLElement
|
|
97
|
+
if (element) {
|
|
98
|
+
// 使用nextTick确保DOM已经渲染完成
|
|
99
|
+
nextTick(() => {
|
|
100
|
+
try {
|
|
101
|
+
element?.focus?.()
|
|
102
|
+
} catch {
|
|
103
|
+
// 忽略焦点设置失败的错误
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 构建插槽对象
|
|
111
|
+
const slotsObject: Record<string, () => VNode | undefined> = {}
|
|
112
|
+
if (slots) {
|
|
113
|
+
if (slots.addonBefore) {
|
|
114
|
+
slotsObject.addonBefore = () => renderSlotContent(slots.addonBefore!, toolbarState)
|
|
115
|
+
}
|
|
116
|
+
if (slots.addonAfter) {
|
|
117
|
+
slotsObject.addonAfter = () => renderSlotContent(slots.addonAfter!, toolbarState)
|
|
118
|
+
}
|
|
119
|
+
if (slots.prefix) {
|
|
120
|
+
slotsObject.prefix = () => renderSlotContent(slots.prefix!, toolbarState)
|
|
121
|
+
}
|
|
122
|
+
if (slots.suffix) {
|
|
123
|
+
slotsObject.suffix = () => renderSlotContent(slots.suffix!, toolbarState)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const Comp = markRaw(inputItem.textarea ? Textarea : Input)
|
|
128
|
+
return (
|
|
129
|
+
<Comp
|
|
130
|
+
key={key}
|
|
131
|
+
value={toolbarState.getValue(key) || finalProps.value || ''}
|
|
132
|
+
allowClear
|
|
133
|
+
autocomplete="off"
|
|
134
|
+
maxlength={50}
|
|
135
|
+
{...finalProps}
|
|
136
|
+
{...processedEvents}
|
|
137
|
+
v-slots={slotsObject}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
}
|