From 4d04734e13f6926c16aeee421feecb0d339534f0 Mon Sep 17 00:00:00 2001 From: kailong321200875 <321200875@qq.com> Date: Thu, 11 May 2023 15:52:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20SelectV2=E6=94=B9=E9=80=A0=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Form/src/Form.vue | 11 +- .../Form/src/components/useRenderSelect.tsx | 17 ++- src/components/Form/src/helper.ts | 48 +++++-- src/types/components.d.ts | 50 +++++++- src/types/form.d.ts | 4 +- src/utils/index.ts | 7 + src/views/Components/Form/DefaultForm.vue | 121 ++++++++++-------- 7 files changed, 183 insertions(+), 75 deletions(-) diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue index 88657bc..453696f 100644 --- a/src/components/Form/src/Form.vue +++ b/src/components/Form/src/Form.vue @@ -21,7 +21,7 @@ import { set } from 'lodash-es' import { FormProps } from './types' import { Icon } from '@/components/Icon' import { FormSchema, FormSetPropsType } from '@/types/form' -import { ComponentNameEnum, SelectComponentProps } from '@/types/components.d' +import { ComponentNameEnum, SelectComponentProps, SelectOption } from '@/types/components.d' const { getPrefixCls } = useDesign() @@ -188,6 +188,13 @@ export default defineComponent({ ) } } + + // 虚拟列表 + if (item.component === ComponentNameEnum.SELECT_V2 && componentSlots.default) { + slotsMap.default = ({ item }: any) => { + return componentSlots.default(item) + } + } // if ( // item?.component !== 'SelectV2' && // item?.component !== 'Cascader' && @@ -252,7 +259,7 @@ export default defineComponent({ const renderOptions = (item: FormSchema) => { switch (item.component) { case ComponentNameEnum.SELECT: - const { renderSelectOptions } = useRenderSelect(slots) + const { renderSelectOptions } = useRenderSelect() return renderSelectOptions(item) case 'Radio': case 'RadioButton': diff --git a/src/components/Form/src/components/useRenderSelect.tsx b/src/components/Form/src/components/useRenderSelect.tsx index 213ab9d..4f33f2a 100644 --- a/src/components/Form/src/components/useRenderSelect.tsx +++ b/src/components/Form/src/components/useRenderSelect.tsx @@ -1,22 +1,21 @@ import { ElOption, ElOptionGroup } from 'element-plus' -import { getSlot } from '@/utils/tsxHelper' -import { Slots } from 'vue' import { FormSchema } from '@/types/form' import { SelectComponentProps, SelectOption } from '@/types/components' -export const useRenderSelect = (slots: Slots) => { +export const useRenderSelect = () => { // 渲染 select options const renderSelectOptions = (item: FormSchema) => { const componentsProps = item.componentProps as SelectComponentProps const optionGroupDefaultSlot = componentsProps.slots?.optionGroupDefault // 如果有别名,就取别名 const labelAlias = componentsProps?.labelAlias + const keyAlias = componentsProps?.keyAlias return componentsProps?.options?.map((option) => { if (option?.options?.length) { return optionGroupDefaultSlot ? ( optionGroupDefaultSlot(option) ) : ( - + {{ default: () => option?.options?.map((v) => { @@ -37,15 +36,15 @@ export const useRenderSelect = (slots: Slots) => { const componentsProps = item.componentProps as SelectComponentProps const labelAlias = componentsProps?.labelAlias const valueAlias = componentsProps?.valueAlias + const keyAlias = componentsProps?.keyAlias const optionDefaultSlot = componentsProps.slots?.optionDefault - const { label, value, ...other } = option - return ( {{ default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined) diff --git a/src/components/Form/src/helper.ts b/src/components/Form/src/helper.ts index aaf4d03..8cbc98c 100644 --- a/src/components/Form/src/helper.ts +++ b/src/components/Form/src/helper.ts @@ -2,9 +2,10 @@ import { useI18n } from '@/hooks/web/useI18n' import { unref, type Slots } from 'vue' import { getSlot } from '@/utils/tsxHelper' import { PlaceholderMoel } from './types' -import { FormSchema } from '@/types/form' -import { ColProps } from '@/types/components' +import { FormSchema } from '@/types/form.d' +import { ColProps, ComponentNameEnum } from '@/types/components.d' import { isFunction } from '@/utils/is' +import { firstUpperCase } from '@/utils' const { t } = useI18n() @@ -15,14 +16,25 @@ const { t } = useI18n() * @description 用于自动设置placeholder */ export const setTextPlaceholder = (schema: FormSchema): PlaceholderMoel => { - const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword'] - const selectMap = ['Select', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect'] - if (textMap.includes(schema?.component as string)) { + const textMap = [ + ComponentNameEnum.INPUT, + ComponentNameEnum.AUTOCOMPLETE, + ComponentNameEnum.INPUT_NUMBER, + ComponentNameEnum.INPUT_PASSWORD + ] + const selectMap = [ + ComponentNameEnum.SELECT, + ComponentNameEnum.TIME_PICKER, + ComponentNameEnum.DATE_PICKER, + ComponentNameEnum.TIME_SELECT, + ComponentNameEnum.SELECT_V2 + ] + if (textMap.includes(schema?.component as ComponentNameEnum)) { return { placeholder: t('common.inputText') } } - if (selectMap.includes(schema?.component as string)) { + if (selectMap.includes(schema?.component as ComponentNameEnum)) { // 一些范围选择器 const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange'] if ( @@ -74,14 +86,30 @@ export const setGridProp = (col: ColProps = {}): ColProps => { */ export const setComponentProps = (item: FormSchema): Recordable => { // const notNeedClearable = ['ColorPicker'] + // 拆分事件并组合 + const onEvents = item?.componentProps?.on || {} + const newOnEvents: Recordable = {} + + for (const key in onEvents) { + if (onEvents[key]) { + newOnEvents[`on${firstUpperCase(key)}`] = (...args: any[]) => { + onEvents[key](...args) + } + } + } + const componentProps: Recordable = { clearable: true, - ...item.componentProps + ...item.componentProps, + ...newOnEvents } // 需要删除额外的属性 if (componentProps.slots) { delete componentProps.slots } + if (componentProps.on) { + delete componentProps.on + } return componentProps } @@ -95,8 +123,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] = (...args: any[]) => { + return slotsProps[key]?.(formModel, ...args) } } else { slotObj[key] = () => { @@ -124,7 +152,7 @@ export const initModel = (schema: FormSchema[], formModel: Recordable) => { } else if (v.component && v.component !== 'Divider') { const hasField = Reflect.has(model, v.field) // 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值 - model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : '' + model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : undefined } }) return model diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 7c93506..00994bf 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -105,7 +105,7 @@ export interface AutocompleteComponentProps { change?: (value: string | number) => void } slots?: { - default?: JSX.Element | null | ((formData: any) => JSX.Element | null) + default?: JSX.Element | null | ((formData: any, ...args: any[]) => JSX.Element | null) prefix?: JSX.Element | null | ((formData: any) => JSX.Element | null) suffix?: JSX.Element | null | ((formData: any) => JSX.Element | null) prepend?: JSX.Element | null | ((formData: any) => JSX.Element | null) @@ -150,7 +150,7 @@ interface SelectOption { } export interface SelectComponentProps { - value?: Array | string | number | boolean | Object + value?: string | number | boolean | Object multiple?: boolean disabled?: boolean valueKey?: string @@ -230,6 +230,52 @@ export interface SelectComponentProps { } options?: SelectOption[] style?: CSSProperties + style?: CSSProperties +} + +export interface SelectV2ComponentProps { + value?: string | number | boolean | Object + multiple?: boolean + disabled?: boolean + valueKey?: string + size?: InputNumberProps['size'] + clearable?: boolean + clearIcon?: string | JSX.Element | ((formData: any) => string | JSX.Element) + collapseTags?: boolean + multipleLimit?: number + name?: string + effect?: string + autocomplete?: string + placeholder?: string + filterable?: boolean + allowCreate?: boolean + reserveKeyword?: boolean + noDataText?: string + popperClass?: string + teleported?: boolean + persistent?: boolean + popperOptions?: any + automaticDropdown?: boolean + height?: number + scrollbarAlwaysOn?: boolean + remote?: boolean + remoteMethod?: (query: string) => void + validateEvent?: boolean + placement?: AutocompleteProps['placement'] + collapseTagsTooltip?: 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 + } + options?: SelectOption[] + slots?: { + default?: (option: SelectOption) => JSX.Element | null + } + style?: CSSProperties } export interface ColProps { diff --git a/src/types/form.d.ts b/src/types/form.d.ts index 5530c99..ca09bde 100644 --- a/src/types/form.d.ts +++ b/src/types/form.d.ts @@ -6,7 +6,8 @@ import { InputComponentProps, AutocompleteComponentProps, InputNumberComponentProps, - SelectComponentProps + SelectComponentProps, + SelectV2ComponentProps } from '@/types/components' import { FormValueType, FormValueType } from '@/types/form' import type { AxiosPromise } from 'axios' @@ -58,6 +59,7 @@ export interface FormSchema { | AutocompleteComponentProps | InputNumberComponentProps | SelectComponentProps + | SelectV2ComponentProps /** * formItem组件属性,具体可以查看element-plus文档 diff --git a/src/utils/index.ts b/src/utils/index.ts index 246de69..aa00c95 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -108,3 +108,10 @@ export function toAnyString() { }) return str } + +/** + * 首字母大写 + */ +export function firstUpperCase(str: string) { + return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase()) +} diff --git a/src/views/Components/Form/DefaultForm.vue b/src/views/Components/Form/DefaultForm.vue index 503400d..a559040 100644 --- a/src/views/Components/Form/DefaultForm.vue +++ b/src/views/Components/Form/DefaultForm.vue @@ -459,11 +459,11 @@ const schema = reactive([ select: handleSelect }, slots: { - default: (item: any) => { + default: (_, { item }) => { return ( <> -
{item.value}
- {item.link} +
{item?.value}
+ {item?.link} ) } @@ -689,56 +689,75 @@ const schema = reactive([ } } } + }, + { + field: 'field18', + label: `${t('formDemo.selectV2')}`, + component: 'Divider' + }, + { + field: 'field19', + label: t('formDemo.default'), + component: 'SelectV2', + componentProps: { + value: undefined, + options: options.value + } + }, + { + field: 'field20', + label: t('formDemo.slot'), + component: 'SelectV2', + componentProps: { + options: options.value, + slots: { + default: (option: SelectOption) => { + return ( + <> + {option?.label} + + {option?.value} + + + ) + } + } + } + }, + { + field: 'field21', + label: t('formDemo.selectGroup'), + component: 'SelectV2', + componentProps: { + options: options2.value + } + }, + { + field: 'field22', + label: `${t('formDemo.selectGroup')}${t('formDemo.slot')}`, + component: 'SelectV2', + componentProps: { + options: options2.value, + slots: { + default: (option: SelectOption) => { + return ( + <> + {option?.label} + + {option?.value} + + + ) + } + } + } + }, + { + field: 'field23', + label: t('formDemo.cascader'), + component: 'Divider' } // { - // field: 'field18', - // label: `${t('formDemo.selectV2')}`, - // component: 'Divider' - // }, - // { - // field: 'field19', - // label: t('formDemo.default'), - // component: 'SelectV2', - // componentProps: { - // options: options.value - // } - // }, - // { - // field: 'field20', - // label: t('formDemo.slot'), - // component: 'SelectV2', - // componentProps: { - // options: options.value, - // slots: { - // default: true - // } - // } - // }, - // { - // field: 'field21', - // label: t('formDemo.selectGroup'), - // component: 'SelectV2', - // componentProps: { - // options: options2.value - // } - // }, - // { - // field: 'field22', - // label: `${t('formDemo.selectGroup')}${t('formDemo.slot')}`, - // component: 'SelectV2', - // componentProps: { - // options: options2.value, - // slots: { - // default: true - // } - // } - // }, - // { - // field: 'field23', - // label: t('formDemo.cascader'), - // component: 'Divider' - // }, - // { // field: 'field24', // label: t('formDemo.default'), // component: 'Cascader',