Browse Source

feat: Radio改造

kailong321200875 1 year ago
parent
commit
83513d519d

+ 1 - 1
package.json

@@ -36,7 +36,7 @@
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
     "echarts-wordcloud": "^2.1.0",
-    "element-plus": "2.3.5",
+    "element-plus": "^2.3.5",
     "intro.js": "^7.0.1",
     "lodash-es": "^4.17.21",
     "mitt": "^3.0.0",

+ 70 - 36
src/components/Form/src/Form.vue

@@ -21,7 +21,16 @@ import { set } from 'lodash-es'
 import { FormProps } from './types'
 import { Icon } from '@/components/Icon'
 import { FormSchema, FormSetPropsType } from '@/types/form'
-import { ComponentNameEnum, SelectComponentProps, SelectOption } from '@/types/components.d'
+import {
+  ComponentNameEnum,
+  SelectComponentProps,
+  SelectOption,
+  RadioComponentProps
+} from '@/types/components.d'
+
+const { renderSelectOptions } = useRenderSelect()
+const { renderRadioOptions } = useRenderRadio()
+const { renderCheckboxOptions } = useRenderCheckbox()
 
 const { getPrefixCls } = useDesign()
 
@@ -181,7 +190,7 @@ export default defineComponent({
       // 如果是select组件,并且没有自定义模板,自动渲染options
       if (item.component === ComponentNameEnum.SELECT) {
         slotsMap.default = !componentSlots.default
-          ? () => renderOptions(item)
+          ? () => renderSelectOptions(item)
           : () => {
               return componentSlots.default(
                 unref((item?.componentProps as SelectComponentProps)?.options)
@@ -231,24 +240,52 @@ export default defineComponent({
         <ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label || ''}>
           {{
             default: () => {
-              const Com = componentMap[item.component as string] as ReturnType<
-                typeof defineComponent
-              >
-
-              const { autoSetPlaceholder } = unref(getProps)
-
-              return slots[item.field] ? (
-                getSlot(slots, item.field, formModel.value)
-              ) : (
-                <Com
-                  vModel={formModel.value[item.field]}
-                  {...(autoSetPlaceholder && setTextPlaceholder(item))}
-                  {...setComponentProps(item)}
-                  style={item.componentProps?.style}
+              if (slots[item.field]) {
+                return getSlot(slots, item.field, formModel.value)
+              } else {
+                const Com = componentMap[item.component as string] as ReturnType<
+                  typeof defineComponent
                 >
-                  {{ ...slotsMap }}
-                </Com>
-              )
+
+                const { autoSetPlaceholder } = unref(getProps)
+
+                // 需要特殊处理的组件
+                const specialComponents = [ComponentNameEnum.RADIO]
+
+                if (specialComponents.findIndex((v) => v === item.component) !== -1) {
+                  switch (item.component) {
+                    case ComponentNameEnum.RADIO:
+                      const componentProps = item.componentProps as RadioComponentProps
+                      const valueAlias = componentProps?.props?.value || 'value'
+                      const labelAlias = componentProps?.props?.label || 'label'
+                      const disabledAlias = componentProps?.props?.disabled || 'disabled'
+
+                      return componentProps?.options?.map((v) => {
+                        return (
+                          <Com
+                            vModel={formModel.value[item.field]}
+                            {...setComponentProps(item)}
+                            label={v[valueAlias]}
+                            disabled={v[disabledAlias]}
+                          >
+                            {v[labelAlias]}
+                          </Com>
+                        )
+                      })
+                  }
+                }
+
+                return (
+                  <Com
+                    vModel={formModel.value[item.field]}
+                    {...(autoSetPlaceholder && setTextPlaceholder(item))}
+                    {...setComponentProps(item)}
+                    style={item.componentProps?.style}
+                  >
+                    {{ ...slotsMap }}
+                  </Com>
+                )
+              }
             }
           }}
         </ElFormItem>
@@ -256,23 +293,20 @@ export default defineComponent({
     }
 
     // 渲染options
-    const renderOptions = (item: FormSchema) => {
-      switch (item.component) {
-        case ComponentNameEnum.SELECT:
-          const { renderSelectOptions } = useRenderSelect()
-          return renderSelectOptions(item)
-        case 'Radio':
-        case 'RadioButton':
-          const { renderRadioOptions } = useRenderRadio()
-          return renderRadioOptions(item)
-        case 'Checkbox':
-        case 'CheckboxButton':
-          const { renderCheckboxOptions } = useRenderCheckbox()
-          return renderCheckboxOptions(item)
-        default:
-          break
-      }
-    }
+    // const renderOptions = (item: FormSchema) => {
+    //   switch (item.component) {
+    //     case ComponentNameEnum.SELECT:
+    //       return renderSelectOptions(item)
+    //     case ComponentNameEnum.RADIO:
+    //     case 'RadioButton':
+    //       return renderRadioOptions(item)
+    //     case 'Checkbox':
+    //     case 'CheckboxButton':
+    //       return renderCheckboxOptions(item)
+    //     default:
+    //       break
+    //   }
+    // }
 
     // 过滤传入Form组件的属性
     const getFormBindValue = () => {

+ 4 - 2
src/components/Form/src/componentMap.ts

@@ -16,14 +16,16 @@ import {
   ElTimeSelect,
   ElTransfer,
   ElAutocomplete,
-  ElDivider
+  ElDivider,
+  ElRadio
 } from 'element-plus'
 import { InputPassword } from '@/components/InputPassword'
 import { Editor } from '@/components/Editor'
 import { ComponentName } from '@/types/components'
 
 const componentMap: Recordable<Component, ComponentName> = {
-  Radio: ElRadioGroup,
+  Radio: ElRadio,
+  // RadioGroup: ElRadioGroup,
   Checkbox: ElCheckboxGroup,
   CheckboxButton: ElCheckboxGroup,
   Input: ElInput,

+ 5 - 1
src/components/Form/src/components/useRenderRadio.tsx

@@ -1,9 +1,13 @@
 import { FormSchema } from '@/types/form'
 import { ElRadio, ElRadioButton } from 'element-plus'
 import { defineComponent } from 'vue'
+import { ComponentNameEnum } from '@/types/components.d'
 
 export const useRenderRadio = () => {
-  const renderRadioOptions = (item: FormSchema) => {
+  const renderRadioOptions = (
+    item: FormSchema,
+    type?: ComponentNameEnum.RADIO | ComponentNameEnum.RADIO_BUTTON = ComponentNameEnum.RADIO
+  ) => {
     // 如果有别名,就取别名
     const labelAlias = item?.componentProps?.optionsAlias?.labelField
     const valueAlias = item?.componentProps?.optionsAlias?.valueField

+ 5 - 5
src/components/Form/src/components/useRenderSelect.tsx

@@ -8,8 +8,8 @@ export const useRenderSelect = () => {
     const componentsProps = item.componentProps as SelectComponentProps
     const optionGroupDefaultSlot = componentsProps.slots?.optionGroupDefault
     // 如果有别名,就取别名
-    const labelAlias = componentsProps?.labelAlias
-    const keyAlias = componentsProps?.keyAlias
+    const labelAlias = componentsProps?.props?.label
+    const keyAlias = componentsProps?.props?.key
     return componentsProps?.options?.map((option) => {
       if (option?.options?.length) {
         return optionGroupDefaultSlot ? (
@@ -34,9 +34,9 @@ export const useRenderSelect = () => {
   const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => {
     // 如果有别名,就取别名
     const componentsProps = item.componentProps as SelectComponentProps
-    const labelAlias = componentsProps?.labelAlias
-    const valueAlias = componentsProps?.valueAlias
-    const keyAlias = componentsProps?.keyAlias
+    const labelAlias = componentsProps?.props?.label
+    const valueAlias = componentsProps?.props?.value
+    const keyAlias = componentsProps?.props?.key
     const optionDefaultSlot = componentsProps.slots?.optionDefault
 
     return (

+ 3 - 3
src/components/Form/src/helper.ts

@@ -5,7 +5,7 @@ import { PlaceholderMoel } from './types'
 import { FormSchema } from '@/types/form.d'
 import { ColProps, ComponentNameEnum } from '@/types/components.d'
 import { isFunction } from '@/utils/is'
-import { firstUpperCase } from '@/utils'
+import { firstUpperCase, humpToDash } from '@/utils'
 
 const { t } = useI18n()
 
@@ -123,11 +123,11 @@ export const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable =
   for (const key in slotsProps) {
     if (slotsProps[key]) {
       if (isFunction(slotsProps[key])) {
-        slotObj[key] = (...args: any[]) => {
+        slotObj[humpToDash(key)] = (...args: any[]) => {
           return slotsProps[key]?.(...args)
         }
       } else {
-        slotObj[key] = () => {
+        slotObj[humpToDash(key)] = () => {
           return slotsProps[key]
         }
       }

+ 49 - 17
src/types/components.d.ts

@@ -1,4 +1,4 @@
-import { CSSProperties } from 'vue'
+import { CSSProperties, VNodeProps, VNode } from 'vue'
 import {
   InputProps,
   AutocompleteProps,
@@ -148,7 +148,7 @@ export interface InputNumberComponentProps {
   style?: CSSProperties
 }
 
-interface SelectOption {
+export interface SelectOption {
   label?: string
   disabled?: boolean
   value?: any
@@ -208,19 +208,14 @@ export interface SelectComponentProps {
     | 'right-end'
   maxCollapseTags?: number
   /**
-   * label别名
+   * 数据源的字段别名
    */
-  labelAlias?: string
-
-  /**
-   * value别名
-   */
-  valueAlias?: string
-
-  /**
-   * key别名
-   */
-  keyAlias?: string
+  props?: {
+    key?: string
+    value?: string
+    label?: string
+    children?: string
+  }
   on?: {
     change?: (value: string | number | boolean | Object) => void
     visibleChange?: (visible: boolean) => void
@@ -390,14 +385,17 @@ export interface ColorPickerComponentProps {
 
 export interface TransferComponentProps {
   value?: any[]
-  data?: Array<{ key; label; disabled }>
+  data?: any[]
   filterable?: boolean
   filterPlaceholder?: string
   filterMethod?: (query: string, item: any) => boolean
   targetOrder?: string
   titles?: string[]
   buttonTexts?: string[]
-  renderContent?: (h: any, option: any) => JSX.Element
+  renderContent?: (
+    h: (type: string, props: VNodeProps | null, children?: string) => VNode,
+    option: any
+  ) => JSX.Element
   format?: {
     noChecked?: string
     hasChecked?: string
@@ -411,7 +409,11 @@ export interface TransferComponentProps {
   rightDefaultChecked?: any[]
   validateEvent?: boolean
   on?: {
-    change?: (value: any[]) => void
+    change?: (
+      value: number | string,
+      direction: 'left' | 'right',
+      movedKeys: string[] | number[]
+    ) => void
     leftCheckChange?: (value: any[]) => void
     rightCheckChange?: (value: any[]) => void
   }
@@ -423,6 +425,36 @@ export interface TransferComponentProps {
   style?: CSSProperties
 }
 
+export interface RadioOption {
+  label?: string
+  value?: string | number | boolean
+  disabled?: boolean
+  [key: string]: any
+}
+export interface RadioComponentProps {
+  value?: string | number | boolean
+  label?: string | number | boolean
+  disabled?: boolean
+  border?: boolean
+  size?: ElementPlusSize
+  options?: RadioOption[]
+  /**
+   * 数据源的字段别名
+   */
+  props: {
+    label?: string
+    value?: string
+    disabled?: string
+  }
+  name?: string
+  on?: {
+    change?: (value: string | number | boolean) => void
+  }
+  slots?: {
+    default?: (...args: any[]) => JSX.Element | null
+  }
+}
+
 export interface ColProps {
   span?: number
   xs?: number

+ 15 - 11
src/types/form.d.ts

@@ -12,7 +12,8 @@ import {
   SwitchComponentProps,
   RateComponentProps,
   ColorPickerComponentProps,
-  TransferComponentProps
+  TransferComponentProps,
+  RadioComponentProps
 } from '@/types/components'
 import { FormValueType, FormValueType } from '@/types/form'
 import type { AxiosPromise } from 'axios'
@@ -36,26 +37,28 @@ export type FormItemProps = {
 }
 
 // 定义联合类型和条件类型
-type ComponentPropsForComponent<T extends ComponentName> = T extends 'input'
+type ComponentPropsForComponent<T extends ComponentName> = T extends 'Input'
   ? InputComponentProps
-  : T extends 'autocomplete'
+  : T extends 'Autocomplete'
   ? AutocompleteComponentProps
-  : T extends 'inputNumber'
+  : T extends 'InputNumber'
   ? InputNumberComponentProps
-  : T extends 'select'
+  : T extends 'Select'
   ? SelectComponentProps
-  : T extends 'selectV2'
+  : T extends 'SelectV2'
   ? SelectV2ComponentProps
-  : T extends 'cascader'
+  : T extends 'Cascader'
   ? CascaderComponentProps
-  : T extends 'switch'
+  : T extends 'Switch'
   ? SwitchComponentProps
-  : T extends 'rate'
+  : T extends 'Rate'
   ? RateComponentProps
-  : T extends 'colorPicker'
+  : T extends 'ColorPicker'
   ? ColorPickerComponentProps
-  : T extends 'transfer'
+  : T extends 'Transfer'
   ? TransferComponentProps
+  : T extends 'Radio'
+  ? RadioComponentProps
   : any
 
 export interface FormSchema {
@@ -93,6 +96,7 @@ export interface FormSchema {
     | RateComponentProps
     | ColorPickerComponentProps
     | TransferComponentProps
+    | RadioComponentProps
 
   /**
    * formItem组件属性,具体可以查看element-plus文档

+ 7 - 0
src/utils/index.ts

@@ -36,6 +36,13 @@ export const underlineToHump = (str: string): string => {
   })
 }
 
+/**
+ * 驼峰转横杠
+ */
+export const humpToDash = (str: string): string => {
+  return str.replace(/([A-Z])/g, '-$1').toLowerCase()
+}
+
 export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
   dom.style.setProperty(prop, val)
 }

+ 115 - 85
src/views/Components/Form/DefaultForm.vue

@@ -8,7 +8,7 @@ import { useAppStore } from '@/store/modules/app'
 import { FormSchema } from '@/types/form'
 import { ComponentOptions, SelectOption, SelectComponentProps } from '@/types/components'
 import { useForm } from '@/hooks/web/useForm'
-import { ElOption, ElOptionGroup } from 'element-plus'
+import { ElOption, ElOptionGroup, ElButton } from 'element-plus'
 
 const appStore = useAppStore()
 
@@ -840,92 +840,117 @@ const schema = reactive<FormSchema[]>([
     field: 'field34',
     label: t('formDemo.transfer'),
     component: 'Divider'
+  },
+  {
+    field: 'field35',
+    label: t('formDemo.default'),
+    component: 'Transfer',
+    componentProps: {
+      props: {
+        key: 'value',
+        label: 'desc'
+      },
+      data: generateData()
+    },
+    value: [],
+    colProps: {
+      span: 24
+    }
+  },
+  {
+    field: 'field36',
+    label: t('formDemo.slot'),
+    component: 'Transfer',
+    componentProps: {
+      props: {
+        key: 'value',
+        label: 'desc'
+      },
+      filterable: true,
+      leftDefaultChecked: [2, 3],
+      rightDefaultChecked: [1],
+      titles: ['Source', 'Target'],
+      buttonTexts: ['To Left', 'To Right'],
+      format: {
+        noChecked: '${total}',
+        hasChecked: '${checked}/${total}'
+      },
+      data: generateData(),
+      slots: {
+        default: ({ option }) => {
+          return (
+            <span>
+              {option.value} - {option.desc}
+            </span>
+          )
+        },
+        leftFooter: () => {
+          return (
+            <ElButton class="transfer-footer" size="small">
+              Operation
+            </ElButton>
+          )
+        },
+        rightFooter: () => {
+          return (
+            <ElButton class="transfer-footer" size="small">
+              Operation
+            </ElButton>
+          )
+        }
+      }
+    },
+    value: [1],
+    colProps: {
+      span: 24
+    }
+  },
+  {
+    field: 'field37',
+    label: `${t('formDemo.render')}`,
+    component: 'Transfer',
+    componentProps: {
+      props: {
+        key: 'value',
+        label: 'desc',
+        disabled: 'disabled'
+      },
+      leftDefaultChecked: [2, 3],
+      rightDefaultChecked: [1],
+      data: generateData(),
+      renderContent: (h, option) => {
+        return h('span', null, `${option.value} - ${option.desc}`)
+      }
+    },
+    value: [1],
+    colProps: {
+      span: 24
+    }
+  },
+  {
+    field: 'field38',
+    label: t('formDemo.radio'),
+    component: 'Divider'
+  },
+  {
+    field: 'field39',
+    label: t('formDemo.default'),
+    component: 'Radio',
+    componentProps: {
+      options: [
+        {
+          // disabled: true,
+          label: 'option-1',
+          value: '1'
+        },
+        {
+          label: 'option-2',
+          value: '2'
+        }
+      ]
+    }
   }
   // {
-  //   field: 'field35',
-  //   label: t('formDemo.default'),
-  //   component: 'Transfer',
-  //   componentProps: {
-  //     props: {
-  //       key: 'value',
-  //       label: 'desc',
-  //       disabled: 'disabled'
-  //     },
-  //     data: generateData()
-  //   },
-  //   value: [],
-  //   colProps: {
-  //     span: 24
-  //   }
-  // },
-  // {
-  //   field: 'field36',
-  //   label: t('formDemo.slot'),
-  //   component: 'Transfer',
-  //   componentProps: {
-  //     props: {
-  //       key: 'value',
-  //       label: 'desc',
-  //       disabled: 'disabled'
-  //     },
-  //     leftDefaultChecked: [2, 3],
-  //     rightDefaultChecked: [1],
-  //     data: generateData(),
-  //     slots: {
-  //       default: true
-  //     }
-  //   },
-  //   value: [1],
-  //   colProps: {
-  //     span: 24
-  //   }
-  // },
-  // {
-  //   field: 'field37',
-  //   label: `${t('formDemo.render')}`,
-  //   component: 'Transfer',
-  //   componentProps: {
-  //     props: {
-  //       key: 'value',
-  //       label: 'desc',
-  //       disabled: 'disabled'
-  //     },
-  //     leftDefaultChecked: [2, 3],
-  //     rightDefaultChecked: [1],
-  //     data: generateData(),
-  //     renderContent: (h: Fn, option: Recordable) => {
-  //       return h('span', null, `${option.value} - ${option.desc}`)
-  //     }
-  //   },
-  //   value: [1],
-  //   colProps: {
-  //     span: 24
-  //   }
-  // },
-  // {
-  //   field: 'field38',
-  //   label: t('formDemo.radio'),
-  //   component: 'Divider'
-  // },
-  // {
-  //   field: 'field39',
-  //   label: t('formDemo.default'),
-  //   component: 'Radio',
-  //   componentProps: {
-  //     options: [
-  //       {
-  //         disabled: true,
-  //         label: 'option-1',
-  //         value: '1'
-  //       },
-  //       {
-  //         label: 'option-2',
-  //         value: '2'
-  //       }
-  //     ]
-  //   }
-  // },
-  // {
   //   field: 'field40',
   //   label: t('formDemo.button'),
   //   component: 'RadioButton',
@@ -1360,4 +1385,9 @@ const changeToggle = () => {
     transform: translateX(-50%);
   }
 }
+
+.transfer-footer {
+  margin-left: 15px;
+  padding: 6px 5px;
+}
 </style>