diff --git a/src/components/Form/src/Form copy.vue b/src/components/Form/src/Form copy.vue
new file mode 100644
index 0000000..94fe848
--- /dev/null
+++ b/src/components/Form/src/Form copy.vue
@@ -0,0 +1,302 @@
+
+
+
diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue
index 5686668..0ce1af2 100644
--- a/src/components/Form/src/Form.vue
+++ b/src/components/Form/src/Form.vue
@@ -21,6 +21,7 @@ import { set } from 'lodash-es'
import { FormProps } from './types'
import { Icon } from '@/components/Icon'
import { FormSchema, FormSetPropsType } from '@/types/form'
+import { ComponentNameEnum } from '@/types/components.d'
const { getPrefixCls } = useDesign()
@@ -172,46 +173,55 @@ export default defineComponent({
// 渲染formItem
const renderFormItem = (item: FormSchema) => {
// 单独给只有options属性的组件做判断
- const notRenderOptions = ['SelectV2', 'Cascader', 'Transfer']
+ // const notRenderOptions = ['SelectV2', 'Cascader', 'Transfer']
+ const componentSlots = (item?.componentProps as any)?.slots || {}
const slotsMap: Recordable = {
- ...setItemComponentSlots(unref(formModel), item?.componentProps?.slots)
+ ...setItemComponentSlots(unref(formModel), componentSlots)
}
- if (
- item?.component !== 'SelectV2' &&
- item?.component !== 'Cascader' &&
- item?.componentProps?.options
- ) {
- slotsMap.default = () => renderOptions(item)
+ // 如果是select组件,并且没有自定义模板,自动渲染options
+ if (item.component === ComponentNameEnum.SELECT) {
+ slotsMap.default = !componentSlots.default
+ ? () => renderOptions(item)
+ : (option: any) => {
+ console.log(option)
+ return componentSlots.default(option)
+ }
}
+ // if (
+ // item?.component !== 'SelectV2' &&
+ // item?.component !== 'Cascader' &&
+ // item?.componentProps?.options
+ // ) {
+ // slotsMap.default = () => renderOptions(item)
+ // }
- const formItemSlots: Recordable = setFormItemSlots(slots, item.field)
+ // const formItemSlots: Recordable = setFormItemSlots(slots, item.field)
// 如果有 labelMessage,自动使用插槽渲染
- if (item?.labelMessage) {
- formItemSlots.label = () => {
- return (
- <>
- {item.label}
-
- {{
- content: () => ,
- default: () => (
-
- )
- }}
-
- >
- )
- }
- }
+ // if (item?.labelMessage) {
+ // formItemSlots.label = () => {
+ // return (
+ // <>
+ // {item.label}
+ //
+ // {{
+ // content: () => ,
+ // default: () => (
+ //
+ // )
+ // }}
+ //
+ // >
+ // )
+ // }
+ // }
return (
{{
- ...formItemSlots,
default: () => {
const Com = componentMap[item.component as string] as ReturnType<
typeof defineComponent
@@ -227,10 +237,6 @@ export default defineComponent({
{...(autoSetPlaceholder && setTextPlaceholder(item))}
{...setComponentProps(item)}
style={item.componentProps?.style}
- {...(notRenderOptions.includes(item?.component as string) &&
- item?.componentProps?.options
- ? { options: item?.componentProps?.options || [] }
- : {})}
>
{{ ...slotsMap }}
@@ -244,7 +250,7 @@ export default defineComponent({
// 渲染options
const renderOptions = (item: FormSchema) => {
switch (item.component) {
- case 'Select':
+ case ComponentNameEnum.SELECT:
const { renderSelectOptions } = useRenderSelect(slots)
return renderSelectOptions(item)
case 'Radio':
diff --git a/src/components/Form/src/components/useRenderSelect.tsx b/src/components/Form/src/components/useRenderSelect.tsx
index e4b5a26..9421c0e 100644
--- a/src/components/Form/src/components/useRenderSelect.tsx
+++ b/src/components/Form/src/components/useRenderSelect.tsx
@@ -2,14 +2,15 @@ import { ElOption, ElOptionGroup } from 'element-plus'
import { getSlot } from '@/utils/tsxHelper'
import { Slots } from 'vue'
import { FormSchema } from '@/types/form'
-import { ComponentOptions } from '@/types/components'
+import { SelectComponentProps, SelectOption } from '@/types/components'
export const useRenderSelect = (slots: Slots) => {
// 渲染 select options
const renderSelectOptions = (item: FormSchema) => {
+ const componentsProps = item.componentProps as SelectComponentProps
// 如果有别名,就取别名
- const labelAlias = item?.componentProps?.optionsAlias?.labelField
- return item?.componentProps?.options?.map((option) => {
+ const labelAlias = componentsProps?.labelAlias
+ return componentsProps?.options?.map((option) => {
if (option?.options?.length) {
return (
@@ -27,10 +28,11 @@ export const useRenderSelect = (slots: Slots) => {
}
// 渲染 select option item
- const renderSelectOptionItem = (item: FormSchema, option: ComponentOptions) => {
+ const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => {
// 如果有别名,就取别名
- const labelAlias = item?.componentProps?.optionsAlias?.labelField
- const valueAlias = item?.componentProps?.optionsAlias?.valueField
+ const componentsProps = item.componentProps as SelectComponentProps
+ const labelAlias = componentsProps?.labelAlias
+ const valueAlias = componentsProps?.valueAlias
const { label, value, ...other } = option
@@ -43,7 +45,7 @@ export const useRenderSelect = (slots: Slots) => {
{{
default: () =>
// option 插槽名规则,{field}-option
- item?.componentProps?.optionsSlot
+ componentsProps?.optionsSlot
? getSlot(slots, `${item.field}-option`, { item: option })
: undefined
}}
diff --git a/src/components/Form/src/helper.ts b/src/components/Form/src/helper.ts
index f58b7f1..f8c3a64 100644
--- a/src/components/Form/src/helper.ts
+++ b/src/components/Form/src/helper.ts
@@ -1,5 +1,5 @@
import { useI18n } from '@/hooks/web/useI18n'
-import type { Slots } from 'vue'
+import { unref, type Slots } from 'vue'
import { getSlot } from '@/utils/tsxHelper'
import { PlaceholderMoel } from './types'
import { FormSchema } from '@/types/form'
@@ -74,12 +74,14 @@ export const setGridProp = (col: ColProps = {}): ColProps => {
*/
export const setComponentProps = (item: FormSchema): Recordable => {
// const notNeedClearable = ['ColorPicker']
- const componentProps = {
+ const componentProps: Recordable = {
clearable: true,
...item.componentProps
}
// 需要删除额外的属性
- delete componentProps?.slots
+ if (componentProps.slots) {
+ delete componentProps.slots
+ }
return componentProps
}
@@ -93,8 +95,8 @@ export const setItemComponentSlots = (formModel: any, slotsProps: Recordable = {
for (const key in slotsProps) {
if (slotsProps[key]) {
if (isFunction(slotsProps[key])) {
- slotObj[key] = () => {
- return slotsProps[key]?.(formModel)
+ slotObj[key] = (item: any) => {
+ return slotsProps[key]?.(unref(item?.item) || undefined, formModel)
}
} else {
slotObj[key] = () => {
diff --git a/src/locales/en.ts b/src/locales/en.ts
index 6f097b6..26a9a02 100644
--- a/src/locales/en.ts
+++ b/src/locales/en.ts
@@ -214,7 +214,9 @@ export default {
default: 'Default',
icon: 'Icon',
mixed: 'Mixed',
+ password: 'Password',
textarea: 'Textarea',
+ remoteSearch: 'Remote search',
slot: 'Slot',
position: 'Position',
autocomplete: 'Autocomplete',
diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts
index 6fa0531..165e268 100644
--- a/src/locales/zh-CN.ts
+++ b/src/locales/zh-CN.ts
@@ -214,7 +214,9 @@ export default {
default: '默认',
icon: '图标',
mixed: '复合型',
+ password: '密码框',
textarea: '多行文本',
+ remoteSearch: '远程搜索',
slot: '插槽',
position: '位置',
autocomplete: '自动补全',
diff --git a/src/types/components.d.ts b/src/types/components.d.ts
index fbc7c49..e132fad 100644
--- a/src/types/components.d.ts
+++ b/src/types/components.d.ts
@@ -1,5 +1,5 @@
import { CSSProperties } from 'vue'
-import { InputProps } from 'element-plus'
+import { InputProps, AutocompleteProps, InputNumberProps } from 'element-plus'
export enum ComponentNameEnum {
RADIO = 'Radio',
@@ -25,6 +25,16 @@ export enum ComponentNameEnum {
EDITOR = 'Editor'
}
+type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K
+ ? K extends string
+ ? K extends `${infer A}_${infer B}`
+ ? `${Capitalize>}${Capitalize>}`
+ : Capitalize>
+ : never
+ : never
+
+export type ComponentName = CamelCaseComponentName
+
export interface InputComponentProps {
value?: string | number
maxlength?: number | string
@@ -37,8 +47,8 @@ export interface InputComponentProps {
showPassword?: boolean
disabled?: boolean
size?: InputProps['size']
- prefixIcon?: string | JSX.Element | ((data: T | any) => string | JSX.Element)
- suffixIcon?: string | JSX.Element | ((data: T | any) => string | JSX.Element)
+ prefixIcon?: string | JSX.Element | ((item: any, data: any) => string | JSX.Element)
+ suffixIcon?: string | JSX.Element | ((item: any, data: any) => string | JSX.Element)
type?: InputProps['type']
rows?: number
autosize?: boolean | { Pows?: numer; maxRows?: number }
@@ -63,22 +73,167 @@ export interface InputComponentProps {
input?: (value: string | number) => void
}
slots?: {
- prefix?: JSX.Element | ((data: T | any) => JSX.Element)
- suffix?: JSX.Element | ((data: T | any) => JSX.Element)
- prepend?: JSX.Element | ((data: T | any) => JSX.Element)
- append?: JSX.Element | ((data: T | any) => JSX.Element)
+ prefix?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ suffix?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ prepend?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ append?: JSX.Element | ((item: any, data: any) => JSX.Element)
}
+ style?: CSSProperties
}
-type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K
- ? K extends string
- ? K extends `${infer A}_${infer B}`
- ? `${Capitalize>}${Capitalize>}`
- : Capitalize>
- : never
- : never
+export interface AutocompleteComponentProps {
+ value?: string
+ placeholder?: string
+ clearable?: boolean
+ disabled?: boolean
+ valueKey?: string
+ debounce?: number
+ placement?: AutocompleteProps['placement']
+ fetchSuggestions?: (queryString: string, callback: (data: string[]) => void) => void
+ triggerOnFocus?: boolean
+ selectWhenUnmatched?: boolean
+ name?: string
+ label?: string
+ hideLoading?: boolean
+ popperClass?: string
+ popperAppendToBody?: boolean
+ teleported?: boolean
+ highlightFirstItem?: boolean
+ fitInputWidth?: boolean
+ on?: {
+ select?: (item: any) => void
+ change?: (value: string | number) => void
+ }
+ slots?: {
+ default?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ prefix?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ suffix?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ prepend?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ append?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ }
+ style?: CSSProperties
+}
-export type ComponentName = CamelCaseComponentName
+export interface InputNumberComponentProps {
+ value?: number
+ min?: number
+ max?: number
+ step?: number
+ stepStrictly?: boolean
+ precision?: number
+ size?: InputNumberProps['size']
+ readonly?: boolean
+ disabled?: boolean
+ controls?: boolean
+ controlsPosition?: InputNumberProps['controlsPosition']
+ name?: string
+ label?: string
+ placeholder?: string
+ id?: string
+ valueOnClear?: number | null | 'min' | 'max'
+ validateEvent?: boolean
+ on?: {
+ change?: (currentValue: number | undefined, oldValue: number | undefined) => void
+ blur?: (event: FocusEvent) => void
+ focus?: (event: FocusEvent) => void
+ }
+ style?: CSSProperties
+}
+
+interface SelectOption {
+ label?: string
+ disabled?: boolean
+ value?: any
+ key?: string | number
+ options?: SelectOption[]
+ [key: string]: any
+}
+
+export interface SelectComponentProps {
+ value?: Array | string | number | boolean | Object
+ multiple?: boolean
+ disabled?: boolean
+ valueKey?: string
+ size?: InputNumberProps['size']
+ clearable?: boolean
+ collapseTags?: boolean
+ collapseTagsTooltip?: number
+ multipleLimit?: number
+ name?: string
+ effect?: string
+ autocomplete?: string
+ placeholder?: string
+ filterable?: boolean
+ allowCreate?: boolean
+ filterMethod?: (query: string, item: any) => boolean
+ remote?: boolean
+ remoteMethod?: (query: string) => void
+ remoteShowSuffix?: boolean
+ loading?: boolean
+ loadingText?: string
+ noMatchText?: string
+ noDataText?: string
+ popperClass?: string
+ reserveKeyword?: boolean
+ defaultFirstOption?: boolean
+ popperAppendToBody?: boolean
+ teleported?: boolean
+ persistent?: boolean
+ automaticDropdown?: boolean
+ clearIcon?: string | JSX.Element | ((item: any, data: any) => string | JSX.Element)
+ fitInputWidth?: boolean
+ suffixIcon?: string | JSX.Element | ((item: any, data: any) => string | JSX.Element)
+ tagType?: 'success' | 'info' | 'warning' | 'danger'
+ validateEvent?: boolean
+ placement?:
+ | 'top'
+ | 'top-start'
+ | 'top-end'
+ | 'bottom'
+ | 'bottom-start'
+ | 'bottom-end'
+ | 'left'
+ | 'left-start'
+ | 'left-end'
+ | 'right'
+ | 'right-start'
+ | 'right-end'
+ maxCollapseTags?: number
+ /**
+ * label别名
+ */
+ labelAlias?: string
+
+ /**
+ * value别名
+ */
+ valueAlias?: string
+
+ /**
+ * key别名
+ */
+ keyAlias?: string
+
+ /**
+ * option是否禁用的统一拦截
+ */
+ optionDisabled?: (optin: any, data: any) => boolean
+ on?: {
+ change?: (value: string | number | boolean | Object) => void
+ visibleChange?: (visible: boolean) => void
+ removeTag?: (tag: any) => void
+ clear?: () => void
+ blur?: (event: FocusEvent) => void
+ focus?: (event: FocusEvent) => void
+ }
+ slots?: {
+ default?: (item: any) => JSX.Element
+ prefix?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ empty?: JSX.Element | ((item: any, data: any) => JSX.Element)
+ }
+ options?: SelectOption[]
+ style?: CSSProperties
+}
export interface ColProps {
span?: number
@@ -92,7 +247,7 @@ export interface ColProps {
export interface ComponentOptions extends Recordable {
label?: string
- value?: FormValueType
+ value?: any
disabled?: boolean
key?: string | number
children?: ComponentOptions[]
diff --git a/src/types/form.d.ts b/src/types/form.d.ts
index ee38385..5530c99 100644
--- a/src/types/form.d.ts
+++ b/src/types/form.d.ts
@@ -3,8 +3,10 @@ import {
ColProps,
ComponentProps,
ComponentName,
- ComponentNameEnum,
- InputComponentProps
+ InputComponentProps,
+ AutocompleteComponentProps,
+ InputNumberComponentProps,
+ SelectComponentProps
} from '@/types/components'
import { FormValueType, FormValueType } from '@/types/form'
import type { AxiosPromise } from 'axios'
@@ -28,29 +30,57 @@ export type FormItemProps = {
}
export interface FormSchema {
- // 唯一值
+ /**
+ * 唯一标识
+ */
field: string
- // 标题
- label?: string
- // 提示
- labelMessage?: string
- // col组件属性
- colProps?: ColProps
- // 表单组件属性,slots对应的是表单组件的插槽,规则:${field}-xxx,具体可以查看element-plus文档
- // componentProps?: { slots?: Recordable } & ComponentProps
/**
- * 表单组件属性,slots对应的是表单组件的插槽,规则:${field}-xxx,具体可以查看element-plus文档
+ * 标题
+ */
+ label?: string
+
+ /**
+ * 提示信息
+ */
+ labelMessage?: string
+
+ /**
+ * col组件属性
+ */
+ colProps?: ColProps
+
+ /**
+ * 表单组件属性,具体可以查看element-plus文档
+ */
+ componentProps?:
+ | InputComponentProps
+ | AutocompleteComponentProps
+ | InputNumberComponentProps
+ | SelectComponentProps
+
+ /**
+ * formItem组件属性,具体可以查看element-plus文档
*/
- componentProps?: InputComponentProps
- // formItem组件属性
formItemProps?: FormItemProps
- // 渲染的组件
+
+ /**
+ * 渲染的组件名称
+ */
component?: ComponentName
- // 初始值
+
+ /**
+ * 初始值
+ */
value?: FormValueType
- // 是否隐藏
+
+ /**
+ * 是否隐藏
+ */
hidden?: boolean
- // 远程加载下拉项
+
+ /**
+ * @returns 远程加载下拉项
+ */
api?: () => AxiosPromise
}
diff --git a/src/views/Components/Form/DefaultForm.vue b/src/views/Components/Form/DefaultForm.vue
index 7415bb3..3902705 100644
--- a/src/views/Components/Form/DefaultForm.vue
+++ b/src/views/Components/Form/DefaultForm.vue
@@ -1,4 +1,4 @@
-