feat: Radio改造
This commit is contained in:
parent
64d436bf84
commit
83513d519d
|
@ -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",
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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文档
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue