@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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/dist/components/breadcrumb/index.d.ts +0 -0
  4. package/dist/components/breadcrumb/index.vue.d.ts +3 -0
  5. package/dist/components/breadcrumb/props.d.ts +0 -0
  6. package/dist/components/button/action/index.d.ts +3 -0
  7. package/dist/components/button/action/index.vue.d.ts +118 -0
  8. package/dist/components/button/action/props.d.ts +63 -0
  9. package/dist/components/button/action/utils.d.ts +8 -0
  10. package/dist/components/button/base/index.d.ts +3 -0
  11. package/dist/components/button/base/index.vue.d.ts +36 -0
  12. package/dist/components/button/base/props.d.ts +27 -0
  13. package/dist/components/button/index.d.ts +4 -0
  14. package/dist/components/button/types.d.ts +2 -0
  15. package/dist/components/form/helper.d.ts +11 -0
  16. package/dist/components/form/index.d.ts +3 -0
  17. package/dist/components/form/index.vue.d.ts +642 -0
  18. package/dist/components/form/props.d.ts +34 -0
  19. package/dist/components/icon/IconPicker.vue.d.ts +13 -0
  20. package/dist/components/icon/iconify.d.ts +25 -0
  21. package/dist/components/icon/index.d.ts +3 -0
  22. package/dist/components/icon/index.vue.d.ts +12 -0
  23. package/dist/components/layout/const.d.ts +22 -0
  24. package/dist/components/layout/context.d.ts +77 -0
  25. package/dist/components/layout/index.d.ts +5 -0
  26. package/dist/components/layout/index.vue.d.ts +80 -0
  27. package/dist/components/layout/layout-parts/AppBreadcrumb.vue.d.ts +3 -0
  28. package/dist/components/layout/layout-parts/AppFooter.vue.d.ts +18 -0
  29. package/dist/components/layout/layout-parts/AppHeader.vue.d.ts +18 -0
  30. package/dist/components/layout/layout-parts/AppLeftLogoInfo.vue.d.ts +3 -0
  31. package/dist/components/layout/layout-parts/AppMain.vue.d.ts +18 -0
  32. package/dist/components/layout/layout-parts/AppSidebar.vue.d.ts +4067 -0
  33. package/dist/components/layout/layout-parts/LayoutTransition.vue.d.ts +58 -0
  34. package/dist/components/layout/mode.d.ts +0 -0
  35. package/dist/components/layout/props.d.ts +35 -0
  36. package/dist/components/layout/types.d.ts +59 -0
  37. package/dist/components/layout/utils.d.ts +97 -0
  38. package/dist/components/provider/index.d.ts +3 -0
  39. package/dist/components/provider/index.vue.d.ts +19 -0
  40. package/dist/components/provider/props.d.ts +33 -0
  41. package/dist/components/search-bar/index.d.ts +3 -0
  42. package/dist/components/search-bar/index.vue.d.ts +1288 -0
  43. package/dist/components/search-bar/props.d.ts +15 -0
  44. package/dist/components/table/TableSetting.vue.d.ts +15 -0
  45. package/dist/components/table/index.d.ts +4 -0
  46. package/dist/components/table/index.vue.d.ts +17246 -0
  47. package/dist/components/table/props.d.ts +26 -0
  48. package/dist/components/table/useColumn.d.ts +15 -0
  49. package/dist/components/upload/enum.d.ts +18 -0
  50. package/dist/components/upload/index.d.ts +4 -0
  51. package/dist/components/upload/index.vue.d.ts +17 -0
  52. package/dist/components/upload/props.d.ts +7 -0
  53. package/dist/const/defaults.d.ts +7 -0
  54. package/dist/const/index.d.ts +2 -0
  55. package/dist/const/types.d.ts +134 -0
  56. package/dist/context/color.d.ts +13 -0
  57. package/dist/context/common.d.ts +117 -0
  58. package/dist/context/index.d.ts +41 -0
  59. package/dist/context/layout.d.ts +52 -0
  60. package/dist/context/loading-bar.d.ts +14 -0
  61. package/dist/context/locale.d.ts +143 -0
  62. package/dist/context/menu.d.ts +212 -0
  63. package/dist/context/message.d.ts +14 -0
  64. package/dist/context/notification.d.ts +14 -0
  65. package/dist/context/table.d.ts +917 -0
  66. package/dist/context/theme.d.ts +20 -0
  67. package/dist/hooks/index.d.ts +6 -0
  68. package/dist/hooks/useAdmin.d.ts +0 -0
  69. package/dist/hooks/useForm.d.ts +54 -0
  70. package/dist/hooks/useLayout.d.ts +116 -0
  71. package/dist/hooks/useProviderContext.d.ts +17 -0
  72. package/dist/hooks/useTable.d.ts +66 -0
  73. package/dist/hooks/useThemeOverrides.d.ts +8 -0
  74. package/dist/hooks/useUpload.d.ts +22 -0
  75. package/dist/index.css +36 -0
  76. package/dist/index.d.ts +29 -0
  77. package/dist/index.js +6771 -0
  78. package/dist/share/compact.d.ts +16 -0
  79. package/dist/share/index.d.ts +2 -0
  80. package/dist/share/menu.d.ts +0 -0
  81. package/dist/share/route.d.ts +0 -0
  82. package/dist/share/slot.d.ts +6 -0
  83. package/dist/utils/form.d.ts +0 -0
  84. package/dist/utils/index.d.ts +0 -0
  85. package/dist/utils/transformRoutes.d.ts +67 -0
  86. package/dist/utils/tree.d.ts +6 -0
  87. package/package.json +53 -0
  88. package/src/auto-imports.d.ts +73 -0
  89. package/src/components/breadcrumb/index.ts +0 -0
  90. package/src/components/breadcrumb/index.vue +0 -0
  91. package/src/components/breadcrumb/props.ts +0 -0
  92. package/src/components/button/action/index.ts +4 -0
  93. package/src/components/button/action/index.vue +313 -0
  94. package/src/components/button/action/props.ts +78 -0
  95. package/src/components/button/action/utils.ts +122 -0
  96. package/src/components/button/base/index.ts +4 -0
  97. package/src/components/button/base/index.vue +156 -0
  98. package/src/components/button/base/props.ts +29 -0
  99. package/src/components/button/index.ts +4 -0
  100. package/src/components/button/types.ts +2 -0
  101. package/src/components/form/helper.ts +73 -0
  102. package/src/components/form/index.ts +5 -0
  103. package/src/components/form/index.vue +243 -0
  104. package/src/components/form/props.ts +75 -0
  105. package/src/components/icon/IconPicker.vue +255 -0
  106. package/src/components/icon/iconify.ts +80 -0
  107. package/src/components/icon/index.ts +7 -0
  108. package/src/components/icon/index.vue +23 -0
  109. package/src/components/layout/const.ts +102 -0
  110. package/src/components/layout/context.ts +189 -0
  111. package/src/components/layout/index.ts +8 -0
  112. package/src/components/layout/index.vue +64 -0
  113. package/src/components/layout/layout-parts/AppBreadcrumb.vue +108 -0
  114. package/src/components/layout/layout-parts/AppFooter.vue +26 -0
  115. package/src/components/layout/layout-parts/AppHeader.vue +112 -0
  116. package/src/components/layout/layout-parts/AppLeftLogoInfo.vue +30 -0
  117. package/src/components/layout/layout-parts/AppMain.vue +34 -0
  118. package/src/components/layout/layout-parts/AppSidebar.vue +174 -0
  119. package/src/components/layout/layout-parts/LayoutTransition.vue +366 -0
  120. package/src/components/layout/mode.ts +0 -0
  121. package/src/components/layout/props.ts +36 -0
  122. package/src/components/layout/types.ts +79 -0
  123. package/src/components/layout/utils.ts +201 -0
  124. package/src/components/provider/index.ts +5 -0
  125. package/src/components/provider/index.vue +69 -0
  126. package/src/components/provider/props.ts +45 -0
  127. package/src/components/search-bar/index.ts +5 -0
  128. package/src/components/search-bar/index.vue +282 -0
  129. package/src/components/search-bar/props.ts +26 -0
  130. package/src/components/table/TableSetting.vue +253 -0
  131. package/src/components/table/index.ts +14 -0
  132. package/src/components/table/index.vue +179 -0
  133. package/src/components/table/props.ts +29 -0
  134. package/src/components/table/useColumn.ts +104 -0
  135. package/src/components/upload/enum.ts +21 -0
  136. package/src/components/upload/index.ts +9 -0
  137. package/src/components/upload/index.vue +267 -0
  138. package/src/components/upload/props.ts +8 -0
  139. package/src/components.d.ts +154 -0
  140. package/src/const/defaults.ts +94 -0
  141. package/src/const/index.ts +2 -0
  142. package/src/const/types.ts +139 -0
  143. package/src/context/color.ts +53 -0
  144. package/src/context/common.ts +27 -0
  145. package/src/context/index.ts +141 -0
  146. package/src/context/layout.ts +34 -0
  147. package/src/context/loading-bar.ts +26 -0
  148. package/src/context/locale.ts +22 -0
  149. package/src/context/menu.ts +26 -0
  150. package/src/context/message.ts +30 -0
  151. package/src/context/notification.ts +29 -0
  152. package/src/context/table.ts +32 -0
  153. package/src/context/theme.ts +35 -0
  154. package/src/hooks/index.ts +6 -0
  155. package/src/hooks/useAdmin.ts +0 -0
  156. package/src/hooks/useForm.ts +272 -0
  157. package/src/hooks/useLayout.ts +300 -0
  158. package/src/hooks/useProviderContext.ts +47 -0
  159. package/src/hooks/useTable.ts +241 -0
  160. package/src/hooks/useThemeOverrides.ts +18 -0
  161. package/src/hooks/useUpload.ts +82 -0
  162. package/src/index.ts +59 -0
  163. package/src/share/compact.ts +35 -0
  164. package/src/share/index.ts +2 -0
  165. package/src/share/menu.ts +0 -0
  166. package/src/share/route.ts +0 -0
  167. package/src/share/slot.ts +29 -0
  168. package/src/utils/form.ts +0 -0
  169. package/src/utils/index.ts +0 -0
  170. package/src/utils/transformRoutes.ts +163 -0
  171. 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,4 @@
1
+ import QuiBaseButton from './index.vue'
2
+
3
+ export * from './props'
4
+ export { QuiBaseButton }
@@ -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,4 @@
1
+ export { QuiActionButton } from './action'
2
+ export type { ActionItem } from './action'
3
+ export { QuiBaseButton } from './base'
4
+ export type { BaseButtonProps } from './base'
@@ -0,0 +1,2 @@
1
+ export type { ActionButtonMode, ActionItem } from './action'
2
+ export type { BaseButtonProps } from './base'
@@ -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,5 @@
1
+ import QuiForm from './index.vue'
2
+
3
+ export { QuiForm }
4
+
5
+ export type { CustomSwitchProps, Props as FormProps, FormSchema } from './props'
@@ -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>