@quiteer/naive-extra 0.0.1
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/LICENSE +21 -0
- package/README.md +86 -0
- package/dist/components/breadcrumb/index.d.ts +0 -0
- package/dist/components/breadcrumb/index.vue.d.ts +3 -0
- package/dist/components/breadcrumb/props.d.ts +0 -0
- package/dist/components/button/action/index.d.ts +3 -0
- package/dist/components/button/action/index.vue.d.ts +118 -0
- package/dist/components/button/action/props.d.ts +63 -0
- package/dist/components/button/action/utils.d.ts +8 -0
- package/dist/components/button/base/index.d.ts +3 -0
- package/dist/components/button/base/index.vue.d.ts +36 -0
- package/dist/components/button/base/props.d.ts +27 -0
- package/dist/components/button/index.d.ts +4 -0
- package/dist/components/button/types.d.ts +2 -0
- package/dist/components/form/helper.d.ts +11 -0
- package/dist/components/form/index.d.ts +3 -0
- package/dist/components/form/index.vue.d.ts +642 -0
- package/dist/components/form/props.d.ts +34 -0
- package/dist/components/icon/IconPicker.vue.d.ts +13 -0
- package/dist/components/icon/iconify.d.ts +25 -0
- package/dist/components/icon/index.d.ts +3 -0
- package/dist/components/icon/index.vue.d.ts +12 -0
- package/dist/components/layout/const.d.ts +22 -0
- package/dist/components/layout/context.d.ts +77 -0
- package/dist/components/layout/index.d.ts +5 -0
- package/dist/components/layout/index.vue.d.ts +80 -0
- package/dist/components/layout/layout-parts/AppBreadcrumb.vue.d.ts +3 -0
- package/dist/components/layout/layout-parts/AppFooter.vue.d.ts +18 -0
- package/dist/components/layout/layout-parts/AppHeader.vue.d.ts +18 -0
- package/dist/components/layout/layout-parts/AppLeftLogoInfo.vue.d.ts +3 -0
- package/dist/components/layout/layout-parts/AppMain.vue.d.ts +18 -0
- package/dist/components/layout/layout-parts/AppSidebar.vue.d.ts +4067 -0
- package/dist/components/layout/layout-parts/LayoutTransition.vue.d.ts +58 -0
- package/dist/components/layout/mode.d.ts +0 -0
- package/dist/components/layout/props.d.ts +35 -0
- package/dist/components/layout/types.d.ts +59 -0
- package/dist/components/layout/utils.d.ts +97 -0
- package/dist/components/provider/index.d.ts +3 -0
- package/dist/components/provider/index.vue.d.ts +19 -0
- package/dist/components/provider/props.d.ts +33 -0
- package/dist/components/search-bar/index.d.ts +3 -0
- package/dist/components/search-bar/index.vue.d.ts +1288 -0
- package/dist/components/search-bar/props.d.ts +15 -0
- package/dist/components/table/TableSetting.vue.d.ts +15 -0
- package/dist/components/table/index.d.ts +4 -0
- package/dist/components/table/index.vue.d.ts +17246 -0
- package/dist/components/table/props.d.ts +26 -0
- package/dist/components/table/useColumn.d.ts +15 -0
- package/dist/components/upload/enum.d.ts +18 -0
- package/dist/components/upload/index.d.ts +4 -0
- package/dist/components/upload/index.vue.d.ts +17 -0
- package/dist/components/upload/props.d.ts +7 -0
- package/dist/const/defaults.d.ts +7 -0
- package/dist/const/index.d.ts +2 -0
- package/dist/const/types.d.ts +134 -0
- package/dist/context/color.d.ts +13 -0
- package/dist/context/common.d.ts +117 -0
- package/dist/context/index.d.ts +41 -0
- package/dist/context/layout.d.ts +52 -0
- package/dist/context/loading-bar.d.ts +14 -0
- package/dist/context/locale.d.ts +143 -0
- package/dist/context/menu.d.ts +212 -0
- package/dist/context/message.d.ts +14 -0
- package/dist/context/notification.d.ts +14 -0
- package/dist/context/table.d.ts +917 -0
- package/dist/context/theme.d.ts +20 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/useAdmin.d.ts +0 -0
- package/dist/hooks/useForm.d.ts +54 -0
- package/dist/hooks/useLayout.d.ts +116 -0
- package/dist/hooks/useProviderContext.d.ts +17 -0
- package/dist/hooks/useTable.d.ts +66 -0
- package/dist/hooks/useThemeOverrides.d.ts +8 -0
- package/dist/hooks/useUpload.d.ts +22 -0
- package/dist/index.css +36 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +6771 -0
- package/dist/share/compact.d.ts +16 -0
- package/dist/share/index.d.ts +2 -0
- package/dist/share/menu.d.ts +0 -0
- package/dist/share/route.d.ts +0 -0
- package/dist/share/slot.d.ts +6 -0
- package/dist/utils/form.d.ts +0 -0
- package/dist/utils/index.d.ts +0 -0
- package/dist/utils/transformRoutes.d.ts +67 -0
- package/dist/utils/tree.d.ts +6 -0
- package/package.json +53 -0
- package/src/auto-imports.d.ts +73 -0
- package/src/components/breadcrumb/index.ts +0 -0
- package/src/components/breadcrumb/index.vue +0 -0
- package/src/components/breadcrumb/props.ts +0 -0
- package/src/components/button/action/index.ts +4 -0
- package/src/components/button/action/index.vue +313 -0
- package/src/components/button/action/props.ts +78 -0
- package/src/components/button/action/utils.ts +122 -0
- package/src/components/button/base/index.ts +4 -0
- package/src/components/button/base/index.vue +156 -0
- package/src/components/button/base/props.ts +29 -0
- package/src/components/button/index.ts +4 -0
- package/src/components/button/types.ts +2 -0
- package/src/components/form/helper.ts +73 -0
- package/src/components/form/index.ts +5 -0
- package/src/components/form/index.vue +243 -0
- package/src/components/form/props.ts +75 -0
- package/src/components/icon/IconPicker.vue +255 -0
- package/src/components/icon/iconify.ts +80 -0
- package/src/components/icon/index.ts +7 -0
- package/src/components/icon/index.vue +23 -0
- package/src/components/layout/const.ts +102 -0
- package/src/components/layout/context.ts +189 -0
- package/src/components/layout/index.ts +8 -0
- package/src/components/layout/index.vue +64 -0
- package/src/components/layout/layout-parts/AppBreadcrumb.vue +108 -0
- package/src/components/layout/layout-parts/AppFooter.vue +26 -0
- package/src/components/layout/layout-parts/AppHeader.vue +112 -0
- package/src/components/layout/layout-parts/AppLeftLogoInfo.vue +30 -0
- package/src/components/layout/layout-parts/AppMain.vue +34 -0
- package/src/components/layout/layout-parts/AppSidebar.vue +174 -0
- package/src/components/layout/layout-parts/LayoutTransition.vue +366 -0
- package/src/components/layout/mode.ts +0 -0
- package/src/components/layout/props.ts +36 -0
- package/src/components/layout/types.ts +79 -0
- package/src/components/layout/utils.ts +201 -0
- package/src/components/provider/index.ts +5 -0
- package/src/components/provider/index.vue +69 -0
- package/src/components/provider/props.ts +45 -0
- package/src/components/search-bar/index.ts +5 -0
- package/src/components/search-bar/index.vue +282 -0
- package/src/components/search-bar/props.ts +26 -0
- package/src/components/table/TableSetting.vue +253 -0
- package/src/components/table/index.ts +14 -0
- package/src/components/table/index.vue +179 -0
- package/src/components/table/props.ts +29 -0
- package/src/components/table/useColumn.ts +104 -0
- package/src/components/upload/enum.ts +21 -0
- package/src/components/upload/index.ts +9 -0
- package/src/components/upload/index.vue +267 -0
- package/src/components/upload/props.ts +8 -0
- package/src/components.d.ts +154 -0
- package/src/const/defaults.ts +94 -0
- package/src/const/index.ts +2 -0
- package/src/const/types.ts +139 -0
- package/src/context/color.ts +53 -0
- package/src/context/common.ts +27 -0
- package/src/context/index.ts +141 -0
- package/src/context/layout.ts +34 -0
- package/src/context/loading-bar.ts +26 -0
- package/src/context/locale.ts +22 -0
- package/src/context/menu.ts +26 -0
- package/src/context/message.ts +30 -0
- package/src/context/notification.ts +29 -0
- package/src/context/table.ts +32 -0
- package/src/context/theme.ts +35 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useAdmin.ts +0 -0
- package/src/hooks/useForm.ts +272 -0
- package/src/hooks/useLayout.ts +300 -0
- package/src/hooks/useProviderContext.ts +47 -0
- package/src/hooks/useTable.ts +241 -0
- package/src/hooks/useThemeOverrides.ts +18 -0
- package/src/hooks/useUpload.ts +82 -0
- package/src/index.ts +59 -0
- package/src/share/compact.ts +35 -0
- package/src/share/index.ts +2 -0
- package/src/share/menu.ts +0 -0
- package/src/share/route.ts +0 -0
- package/src/share/slot.ts +29 -0
- package/src/utils/form.ts +0 -0
- package/src/utils/index.ts +0 -0
- package/src/utils/transformRoutes.ts +163 -0
- package/src/utils/tree.ts +31 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { ButtonProps } from 'naive-ui'
|
|
2
|
+
import type { BaseButtonProps } from '../base/props'
|
|
3
|
+
import type { ActionButtonMode, ActionItem } from './props'
|
|
4
|
+
import { unref } from 'vue'
|
|
5
|
+
import { DEFAULT_ACTION_ICONS } from './props'
|
|
6
|
+
|
|
7
|
+
const DEFAULT_ICON = 'carbon:unknown'
|
|
8
|
+
|
|
9
|
+
// 获取图标
|
|
10
|
+
export function getIcon(item: ActionItem) {
|
|
11
|
+
if (item.icon)
|
|
12
|
+
return item.icon
|
|
13
|
+
return DEFAULT_ACTION_ICONS[item.key as string] || DEFAULT_ICON
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 判断是否禁用
|
|
17
|
+
export function isDisabled(item: ActionItem) {
|
|
18
|
+
if (item.disabled === undefined)
|
|
19
|
+
return false
|
|
20
|
+
if (typeof item.disabled === 'function')
|
|
21
|
+
return item.disabled()
|
|
22
|
+
return unref(item.disabled)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getPopconfirmProps(item: ActionItem) {
|
|
26
|
+
if (!item.popconfirm)
|
|
27
|
+
return undefined
|
|
28
|
+
|
|
29
|
+
if (typeof item.popconfirm === 'object') {
|
|
30
|
+
const { onPositiveClick: _, ...rest } = item.popconfirm as any
|
|
31
|
+
return rest
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return item.popconfirm
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 解析按钮属性
|
|
38
|
+
export function resolveButtonProps(
|
|
39
|
+
item: ActionItem,
|
|
40
|
+
mode: ActionButtonMode = 'text',
|
|
41
|
+
defaultButtonProps?: ButtonProps
|
|
42
|
+
): Omit<Partial<BaseButtonProps>, 'onClick'> {
|
|
43
|
+
const { onClick: _1, ...restDefaultProps } = defaultButtonProps || {}
|
|
44
|
+
const { onClick: _2, ...restItemProps } = item.props || {}
|
|
45
|
+
|
|
46
|
+
const baseProps: Partial<BaseButtonProps> = {
|
|
47
|
+
...restDefaultProps,
|
|
48
|
+
...restItemProps,
|
|
49
|
+
disabled: isDisabled(item),
|
|
50
|
+
size: item.size || defaultButtonProps?.size,
|
|
51
|
+
popconfirm: getPopconfirmProps(item),
|
|
52
|
+
popText: item.popText,
|
|
53
|
+
positiveText: item.positiveText,
|
|
54
|
+
negativeText: item.negativeText,
|
|
55
|
+
permission: item.permission
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Icon
|
|
59
|
+
if (mode !== 'text') {
|
|
60
|
+
baseProps.icon = getIcon(item)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Label & Tooltip
|
|
64
|
+
if (mode === 'icon') {
|
|
65
|
+
baseProps.tooltip = item.tooltip || item.label
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// label is handled in template via slot
|
|
69
|
+
if (item.tooltip) {
|
|
70
|
+
baseProps.tooltip = item.tooltip
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Styling
|
|
75
|
+
if (mode === 'button') {
|
|
76
|
+
baseProps.text = false
|
|
77
|
+
}
|
|
78
|
+
else if (mode === 'secondary') {
|
|
79
|
+
baseProps.text = false
|
|
80
|
+
baseProps.secondary = true
|
|
81
|
+
baseProps.type = 'primary'
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
baseProps.text = true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return baseProps as Omit<Partial<BaseButtonProps>, 'onClick'>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 解析更多按钮属性
|
|
91
|
+
export function resolveMoreButtonProps(
|
|
92
|
+
mode: ActionButtonMode = 'text',
|
|
93
|
+
defaultButtonProps?: ButtonProps
|
|
94
|
+
): Omit<Partial<BaseButtonProps>, 'onClick'> {
|
|
95
|
+
const { onClick: _, ...restDefaultProps } = defaultButtonProps || {}
|
|
96
|
+
|
|
97
|
+
const baseProps: Partial<BaseButtonProps> = {
|
|
98
|
+
...restDefaultProps
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (mode !== 'text') {
|
|
102
|
+
baseProps.icon = DEFAULT_ACTION_ICONS.more
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (mode !== 'icon') {
|
|
106
|
+
// label is handled in template via slot
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (mode === 'button') {
|
|
110
|
+
baseProps.text = false
|
|
111
|
+
}
|
|
112
|
+
else if (mode === 'secondary') {
|
|
113
|
+
baseProps.text = false
|
|
114
|
+
baseProps.secondary = true
|
|
115
|
+
baseProps.type = 'primary'
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
baseProps.text = true
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return baseProps as Omit<Partial<BaseButtonProps>, 'onClick'>
|
|
122
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ButtonProps, PopconfirmProps } from 'naive-ui'
|
|
3
|
+
import type { BaseButtonProps } from './props'
|
|
4
|
+
import { QuiIcon } from '../../icon'
|
|
5
|
+
|
|
6
|
+
defineOptions({
|
|
7
|
+
inheritAttrs: false
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(defineProps<BaseButtonProps>(), {
|
|
11
|
+
type: 'default',
|
|
12
|
+
size: 'small',
|
|
13
|
+
text: false,
|
|
14
|
+
secondary: false,
|
|
15
|
+
disabled: undefined
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits<{
|
|
19
|
+
click: [e: MouseEvent]
|
|
20
|
+
positiveClick: [e: MouseEvent]
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const computedButtonProps = computed<ButtonProps>(() => {
|
|
24
|
+
const { icon: _1, tooltip: _2, popconfirm: _3, popText: _4, positiveText: _5, negativeText: _6, permission: _7, disabled: _8, ...others } = props
|
|
25
|
+
return others as ButtonProps
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const permissionCheck = computed(() => {
|
|
29
|
+
if (!props.permission) {
|
|
30
|
+
return { visible: true, disabled: false }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { group, disabled } = props.permission
|
|
34
|
+
const [codes, code] = group
|
|
35
|
+
|
|
36
|
+
// 如果权限组为空或当前权限为空,认为有权限(或者根据业务逻辑,这里假设必须都有值)
|
|
37
|
+
// 按照通常逻辑,如果没传 code,就不校验
|
|
38
|
+
if (!codes || !code) {
|
|
39
|
+
return { visible: true, disabled: false }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const hasPermission = codes.includes(code)
|
|
43
|
+
|
|
44
|
+
if (hasPermission) {
|
|
45
|
+
return { visible: true, disabled: false }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 无权限
|
|
49
|
+
if (disabled) {
|
|
50
|
+
return { visible: true, disabled: true }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 默认隐藏
|
|
54
|
+
return { visible: false, disabled: false }
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const finalDisabled = computed(() => {
|
|
58
|
+
if (props.disabled !== undefined) {
|
|
59
|
+
return props.disabled
|
|
60
|
+
}
|
|
61
|
+
return permissionCheck.value.disabled
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const computedPopconfirm = computed<PopconfirmProps | undefined>(() => {
|
|
65
|
+
const { popconfirm, positiveText, negativeText, popText } = props
|
|
66
|
+
if (!popconfirm && !positiveText && !negativeText && !popText)
|
|
67
|
+
return undefined
|
|
68
|
+
|
|
69
|
+
let basePopconfirm: PopconfirmProps = {}
|
|
70
|
+
|
|
71
|
+
if (popconfirm) {
|
|
72
|
+
basePopconfirm = { ...popconfirm }
|
|
73
|
+
}
|
|
74
|
+
else if (popText) {
|
|
75
|
+
basePopconfirm.showIcon = true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (positiveText) {
|
|
79
|
+
basePopconfirm.positiveText = positiveText
|
|
80
|
+
}
|
|
81
|
+
if (negativeText) {
|
|
82
|
+
basePopconfirm.negativeText = negativeText
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return Object.keys(basePopconfirm).length > 0 ? basePopconfirm : undefined
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const popconfirmTitle = computed(() => {
|
|
89
|
+
const { popText } = props
|
|
90
|
+
if (popText)
|
|
91
|
+
return popText
|
|
92
|
+
return '确认进行此操作?'
|
|
93
|
+
})
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<template>
|
|
97
|
+
<template v-if="permissionCheck.visible">
|
|
98
|
+
<!-- 带气泡确认的按钮 -->
|
|
99
|
+
<NPopconfirm
|
|
100
|
+
v-if="computedPopconfirm || $slots.popconfirm"
|
|
101
|
+
v-bind="computedPopconfirm"
|
|
102
|
+
@positive-click="emit('positiveClick', $event)"
|
|
103
|
+
>
|
|
104
|
+
<template #trigger>
|
|
105
|
+
<NButton
|
|
106
|
+
v-bind="{ ...computedButtonProps, ...$attrs }"
|
|
107
|
+
:disabled="finalDisabled"
|
|
108
|
+
@click="emit('click', $event)"
|
|
109
|
+
>
|
|
110
|
+
<template v-if="icon" #icon>
|
|
111
|
+
<QuiIcon :icon="icon" />
|
|
112
|
+
</template>
|
|
113
|
+
<slot />
|
|
114
|
+
</NButton>
|
|
115
|
+
</template>
|
|
116
|
+
<slot name="popconfirm">
|
|
117
|
+
{{ popconfirmTitle }}
|
|
118
|
+
</slot>
|
|
119
|
+
</NPopconfirm>
|
|
120
|
+
|
|
121
|
+
<!-- 普通按钮 -->
|
|
122
|
+
<template v-else>
|
|
123
|
+
<!-- 带提示的按钮 -->
|
|
124
|
+
<NTooltip v-if="tooltip || $slots.tooltip" trigger="hover">
|
|
125
|
+
<template #trigger>
|
|
126
|
+
<NButton
|
|
127
|
+
v-bind="{ ...computedButtonProps, ...$attrs }"
|
|
128
|
+
:disabled="finalDisabled"
|
|
129
|
+
@click="emit('click', $event)"
|
|
130
|
+
>
|
|
131
|
+
<template v-if="icon" #icon>
|
|
132
|
+
<QuiIcon :icon="icon" />
|
|
133
|
+
</template>
|
|
134
|
+
<slot />
|
|
135
|
+
</NButton>
|
|
136
|
+
</template>
|
|
137
|
+
<slot name="tooltip">
|
|
138
|
+
{{ tooltip }}
|
|
139
|
+
</slot>
|
|
140
|
+
</NTooltip>
|
|
141
|
+
|
|
142
|
+
<!-- 仅按钮 -->
|
|
143
|
+
<NButton
|
|
144
|
+
v-else
|
|
145
|
+
v-bind="{ ...computedButtonProps, ...$attrs }"
|
|
146
|
+
:disabled="finalDisabled"
|
|
147
|
+
@click="emit('click', $event)"
|
|
148
|
+
>
|
|
149
|
+
<template v-if="icon" #icon>
|
|
150
|
+
<QuiIcon :icon="icon" />
|
|
151
|
+
</template>
|
|
152
|
+
<slot />
|
|
153
|
+
</NButton>
|
|
154
|
+
</template>
|
|
155
|
+
</template>
|
|
156
|
+
</template>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ButtonProps, PopconfirmProps } from 'naive-ui'
|
|
2
|
+
|
|
3
|
+
export interface ButtonPermission {
|
|
4
|
+
/** 权限组:[拥有的权限列表, 需要的权限] */
|
|
5
|
+
group: [string[], string]
|
|
6
|
+
/** 是否禁用 */
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
/** 是否显示 */
|
|
9
|
+
show?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BaseButtonProps extends /* @vue-ignore */ ButtonProps {
|
|
13
|
+
/** 图标名称 */
|
|
14
|
+
icon?: string
|
|
15
|
+
/** 提示文本 */
|
|
16
|
+
tooltip?: string
|
|
17
|
+
/** 是否为文本按钮 */
|
|
18
|
+
text?: boolean
|
|
19
|
+
/** Popconfirm 配置 */
|
|
20
|
+
popconfirm?: PopconfirmProps
|
|
21
|
+
/** Popconfirm 内容文本 */
|
|
22
|
+
popText?: string
|
|
23
|
+
/** Popconfirm 确认按钮文本 */
|
|
24
|
+
positiveText?: string
|
|
25
|
+
/** Popconfirm 取消按钮文本 */
|
|
26
|
+
negativeText?: string
|
|
27
|
+
/** 权限 */
|
|
28
|
+
permission?: ButtonPermission
|
|
29
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Recordable } from '../../const'
|
|
2
|
+
import type { FormSchema } from './props'
|
|
3
|
+
import { isArray, isBoolean, isFunction, isNullOrUnDef, isObject, isString } from '@quiteer/is'
|
|
4
|
+
import {
|
|
5
|
+
NCascader,
|
|
6
|
+
NDatePicker,
|
|
7
|
+
NDynamicInput,
|
|
8
|
+
NDynamicTags,
|
|
9
|
+
NInput,
|
|
10
|
+
NInputNumber,
|
|
11
|
+
NSelect,
|
|
12
|
+
NSwitch,
|
|
13
|
+
NTimePicker
|
|
14
|
+
} from 'naive-ui'
|
|
15
|
+
import { QuiUpload } from '../upload'
|
|
16
|
+
|
|
17
|
+
// 加工 form values
|
|
18
|
+
export function handleFormValues(values: Recordable) {
|
|
19
|
+
if (!isObject(values)) {
|
|
20
|
+
return {}
|
|
21
|
+
}
|
|
22
|
+
const res: Recordable = {}
|
|
23
|
+
for (const item of Object.entries(values)) {
|
|
24
|
+
let [key, value] = item
|
|
25
|
+
if (!key || (isArray(value) && value.length === 0) || isFunction(value) || isNullOrUnDef(value)) {
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
// 删除空格
|
|
29
|
+
if (isString(value)) {
|
|
30
|
+
value = value.trim()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Reflect.set(res, key, value)
|
|
34
|
+
}
|
|
35
|
+
return res
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getComponentProps(schema: FormSchema, disabled: boolean) {
|
|
39
|
+
const compProps = schema.componentProps ?? {}
|
|
40
|
+
|
|
41
|
+
const isReadonly = isBoolean(compProps.disabled) ? compProps.disabled : disabled
|
|
42
|
+
|
|
43
|
+
const props = {
|
|
44
|
+
clearable: !isReadonly,
|
|
45
|
+
...compProps
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (schema.component === 'NInput')
|
|
49
|
+
return { ...props, readonly: isReadonly }
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
disabled: isReadonly,
|
|
53
|
+
...props
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 获取对应组件
|
|
58
|
+
export function getComponent(schema: FormSchema) {
|
|
59
|
+
const components = {
|
|
60
|
+
NInput,
|
|
61
|
+
NInputNumber,
|
|
62
|
+
NSelect,
|
|
63
|
+
NSwitch,
|
|
64
|
+
NDatePicker,
|
|
65
|
+
NTimePicker,
|
|
66
|
+
NCascader,
|
|
67
|
+
NDynamicInput,
|
|
68
|
+
NDynamicTags,
|
|
69
|
+
NUpload: QuiUpload
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return Reflect.get(components, schema.component ?? '')
|
|
73
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { FormProps, FormRules, GridProps, NForm } from 'naive-ui'
|
|
3
|
+
import type { Recordable } from '../../const'
|
|
4
|
+
import type { Props } from './props'
|
|
5
|
+
import { isArray, isBoolean, isNullOrUnDef } from '@quiteer/is'
|
|
6
|
+
import { computed, nextTick, onMounted, reactive, ref, toRaw, unref, useAttrs } from 'vue'
|
|
7
|
+
import { getComponent, getComponentProps, handleFormValues } from './helper'
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
10
|
+
disabled: false,
|
|
11
|
+
labelWidth: 80,
|
|
12
|
+
labelPlacement: 'top',
|
|
13
|
+
layout: 'inline',
|
|
14
|
+
inline: false,
|
|
15
|
+
size: 'medium',
|
|
16
|
+
isFull: true,
|
|
17
|
+
gridProps: undefined
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
reset: []
|
|
22
|
+
}>()
|
|
23
|
+
const attrs = useAttrs()
|
|
24
|
+
const formProps = computed((): FormProps => {
|
|
25
|
+
const rules: FormRules = {}
|
|
26
|
+
|
|
27
|
+
props.schemas.forEach((item) => {
|
|
28
|
+
if (item.rules && isArray(item.rules)) {
|
|
29
|
+
rules[item.field as string] = item.rules
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
labelWidth: props.labelWidth,
|
|
35
|
+
inline: props.inline,
|
|
36
|
+
size: props.size,
|
|
37
|
+
labelPlacement: props.labelPlacement,
|
|
38
|
+
labelAlign: 'left',
|
|
39
|
+
rules,
|
|
40
|
+
...unref(attrs)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const getGrid = computed((): GridProps => {
|
|
45
|
+
return {
|
|
46
|
+
cols: props.inline ? '1 s:2 m:4 l:5 2xl:6' : '1',
|
|
47
|
+
xGap: 10,
|
|
48
|
+
responsive: 'screen',
|
|
49
|
+
...props.gridProps
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const formElRef = ref<InstanceType<typeof NForm>>()
|
|
54
|
+
const formModel = reactive<Recordable>({})
|
|
55
|
+
const defaultFormModel = ref<Recordable>({})
|
|
56
|
+
|
|
57
|
+
// 初始化默认值
|
|
58
|
+
function initDefault() {
|
|
59
|
+
const obj: Record<string, string | number> = {}
|
|
60
|
+
props.schemas.forEach((item) => {
|
|
61
|
+
const { defaultValue } = item
|
|
62
|
+
if (!isNullOrUnDef(defaultValue)) {
|
|
63
|
+
obj[item.field as string] = defaultValue
|
|
64
|
+
formModel[item.field as string] = defaultValue
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
defaultFormModel.value = obj
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
onMounted(() => {
|
|
71
|
+
initDefault()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// 验证
|
|
75
|
+
/**
|
|
76
|
+
* 表单校验函数:
|
|
77
|
+
* 统一返回 Promise<void>,避免类型推断依赖 async-validator 的内部路径。
|
|
78
|
+
*/
|
|
79
|
+
function validate(): Promise<void> {
|
|
80
|
+
const form = unref(formElRef)
|
|
81
|
+
if (!form) {
|
|
82
|
+
return Promise.resolve()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return form.validate().then(() => {})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 清空校验状态:
|
|
90
|
+
* 当表单未挂载时直接返回已完成的 Promise,保证返回类型稳定。
|
|
91
|
+
*/
|
|
92
|
+
async function clearValidate(): Promise<void> {
|
|
93
|
+
const form = unref(formElRef)
|
|
94
|
+
if (!form) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
await form.restoreValidation()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 重置
|
|
101
|
+
async function resetFields(): Promise<void> {
|
|
102
|
+
const formEl = unref(formElRef)
|
|
103
|
+
if (!formEl)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
Object.keys(formModel).forEach((key) => {
|
|
107
|
+
formModel[key] = unref(defaultFormModel)[key] || null
|
|
108
|
+
})
|
|
109
|
+
await clearValidate()
|
|
110
|
+
emit('reset')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 获取表单值
|
|
114
|
+
function getFieldsValue() {
|
|
115
|
+
const form = handleFormValues(toRaw(formModel))
|
|
116
|
+
return form
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const isSetFieldsValue = ref(false)
|
|
120
|
+
|
|
121
|
+
defineExpose({
|
|
122
|
+
// 验证
|
|
123
|
+
validate,
|
|
124
|
+
// 重置
|
|
125
|
+
resetFields,
|
|
126
|
+
// 清空校验
|
|
127
|
+
clearValidate,
|
|
128
|
+
// 获取表单值
|
|
129
|
+
getFieldsValue,
|
|
130
|
+
// 设置表单字段值
|
|
131
|
+
setFieldsValue(values: Recordable) {
|
|
132
|
+
isSetFieldsValue.value = true
|
|
133
|
+
const fields = unref(props.schemas)
|
|
134
|
+
?.map(item => item.field)
|
|
135
|
+
?.filter(Boolean)
|
|
136
|
+
|
|
137
|
+
Object.keys(values).forEach((key) => {
|
|
138
|
+
const value = values[key]
|
|
139
|
+
if (value && fields.includes(key)) {
|
|
140
|
+
formModel[key] = value
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
nextTick(() => {
|
|
144
|
+
isSetFieldsValue.value = false
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
</script>
|
|
149
|
+
|
|
150
|
+
<template>
|
|
151
|
+
<NForm v-bind="formProps" id="__AI_FORM__" ref="formElRef" :model="formModel">
|
|
152
|
+
<NGrid v-bind="getGrid" :style="gridStyle">
|
|
153
|
+
<NFormItemGi
|
|
154
|
+
v-for="schema in schemas"
|
|
155
|
+
v-bind="schema.giProps ? schema.giProps : giProps"
|
|
156
|
+
:key="schema.field"
|
|
157
|
+
:label="schema.label"
|
|
158
|
+
:path="schema.field"
|
|
159
|
+
>
|
|
160
|
+
<!-- 标签名右侧温馨提示 -->
|
|
161
|
+
<template v-if="schema.labelMessage" #label>
|
|
162
|
+
<span class="inline-flex items-center">
|
|
163
|
+
{{ schema.label }}
|
|
164
|
+
<NTooltip trigger="hover" :style="schema.labelMessageStyle">
|
|
165
|
+
<template #trigger>
|
|
166
|
+
<i class="i-material-symbols-info-outline ml-1 text-4 text-amber" />
|
|
167
|
+
</template>
|
|
168
|
+
{{ schema.labelMessage }}
|
|
169
|
+
</NTooltip>
|
|
170
|
+
</span>
|
|
171
|
+
</template>
|
|
172
|
+
|
|
173
|
+
<!-- 判断插槽 -->
|
|
174
|
+
<template v-if="schema.slot">
|
|
175
|
+
<slot :name="schema.slot" :model="formModel" :field="schema.field" :value="formModel[schema.field]" />
|
|
176
|
+
</template>
|
|
177
|
+
|
|
178
|
+
<!-- NCheckbox -->
|
|
179
|
+
<template v-else-if="schema.component === 'NCheckbox' && schema.componentProps">
|
|
180
|
+
<NCheckboxGroup
|
|
181
|
+
v-model:value="formModel[schema.field]"
|
|
182
|
+
:disabled="isBoolean(schema.componentProps.disabled) ? schema.componentProps.disabled : disabled"
|
|
183
|
+
>
|
|
184
|
+
<NSpace>
|
|
185
|
+
<NCheckbox
|
|
186
|
+
v-for="item in schema.componentProps.options"
|
|
187
|
+
:key="item.value"
|
|
188
|
+
:value="item.value"
|
|
189
|
+
:label="item.label"
|
|
190
|
+
/>
|
|
191
|
+
</NSpace>
|
|
192
|
+
</NCheckboxGroup>
|
|
193
|
+
</template>
|
|
194
|
+
|
|
195
|
+
<!-- NRadioGroup -->
|
|
196
|
+
<template v-else-if="schema.component === 'NRadioGroup' && schema.componentProps">
|
|
197
|
+
<NRadioGroup v-model:value="formModel[schema.field]" v-bind="getComponentProps(schema, disabled)">
|
|
198
|
+
<NSpace>
|
|
199
|
+
<NRadio v-for="item in schema.componentProps.options" :key="item.value" :value="item.value">
|
|
200
|
+
{{ item.label }}
|
|
201
|
+
</NRadio>
|
|
202
|
+
</NSpace>
|
|
203
|
+
</NRadioGroup>
|
|
204
|
+
</template>
|
|
205
|
+
|
|
206
|
+
<!-- NSwitch -->
|
|
207
|
+
<template v-else-if="schema.component === 'NSwitch' && schema.componentProps">
|
|
208
|
+
<NSwitch v-model:value="formModel[schema.field]" v-bind="getComponentProps(schema, disabled)">
|
|
209
|
+
<template #checked>
|
|
210
|
+
{{ schema.componentProps?.checkedText }}
|
|
211
|
+
</template>
|
|
212
|
+
<template #unchecked>
|
|
213
|
+
{{ schema.componentProps?.uncheckedText }}
|
|
214
|
+
</template>
|
|
215
|
+
</NSwitch>
|
|
216
|
+
</template>
|
|
217
|
+
|
|
218
|
+
<!-- 动态渲染表单组件 -->
|
|
219
|
+
<component
|
|
220
|
+
v-bind="getComponentProps(schema, disabled)"
|
|
221
|
+
:is="getComponent(schema)"
|
|
222
|
+
v-else
|
|
223
|
+
v-model:value="formModel[schema.field]"
|
|
224
|
+
:is-set-fields-value="isSetFieldsValue"
|
|
225
|
+
:class="{ 'w-full': schema.isFull !== false && isFull }"
|
|
226
|
+
/>
|
|
227
|
+
<!-- 组件后面的内容 -->
|
|
228
|
+
<template v-if="schema.suffix">
|
|
229
|
+
<slot :name="schema.suffix" :model="formModel" :field="schema.field" :value="formModel[schema.field]" />
|
|
230
|
+
</template>
|
|
231
|
+
</NFormItemGi>
|
|
232
|
+
|
|
233
|
+
<!-- 提交 重置 展开 收起 按钮 -->
|
|
234
|
+
<NGi :span="inline ? '' : 24" :suffix="inline ? true : false">
|
|
235
|
+
<NSpace align="center" justify="end" class="h-full">
|
|
236
|
+
<slot name="action-button" :model="formModel" />
|
|
237
|
+
</NSpace>
|
|
238
|
+
</NGi>
|
|
239
|
+
</NGrid>
|
|
240
|
+
</NForm>
|
|
241
|
+
</template>
|
|
242
|
+
|
|
243
|
+
<style lang="scss" scoped></style>
|