Forráskód Böngészése

feat: SelectV2改造完成

kailong321200875 2 éve
szülő
commit
4d04734e13

+ 9 - 2
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':

+ 8 - 9
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)
         ) : (
-          <ElOptionGroup label={option[labelAlias || 'label']}>
+          <ElOptionGroup label={option[labelAlias || 'label']} key={option[keyAlias || 'key']}>
             {{
               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 (
       <ElOption
-        {...other}
-        label={labelAlias ? option[labelAlias] : label}
-        value={valueAlias ? option[valueAlias] : value}
+        {...option}
+        key={option[keyAlias || 'key']}
+        label={option[labelAlias || 'label']}
+        value={option[valueAlias || 'value']}
       >
         {{
           default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined)

+ 38 - 10
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

+ 48 - 2
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 {

+ 3 - 1
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文档

+ 7 - 0
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())
+}

+ 70 - 51
src/views/Components/Form/DefaultForm.vue

@@ -459,11 +459,11 @@ const schema = reactive<FormSchema[]>([
         select: handleSelect
       },
       slots: {
-        default: (item: any) => {
+        default: (_, { item }) => {
           return (
             <>
-              <div class="value">{item.value}</div>
-              <span class="link">{item.link}</span>
+              <div class="value">{item?.value}</div>
+              <span class="link">{item?.link}</span>
             </>
           )
         }
@@ -689,56 +689,75 @@ const schema = reactive<FormSchema[]>([
         }
       }
     }
+  },
+  {
+    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 (
+            <>
+              <span style="margin-right: 8px">{option?.label}</span>
+              <span style="color: var(--el-text-color-secondary); font-size: 13px">
+                {option?.value}
+              </span>
+            </>
+          )
+        }
+      }
+    }
+  },
+  {
+    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 (
+            <>
+              <span style="margin-right: 8px">{option?.label}</span>
+              <span style="color: var(--el-text-color-secondary); font-size: 13px">
+                {option?.value}
+              </span>
+            </>
+          )
+        }
+      }
+    }
+  },
+  {
+    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',