163 lines
4.4 KiB
TypeScript
163 lines
4.4 KiB
TypeScript
import type { PropType, RenderFunction } from 'vue'
|
|
import { computed, defineComponent, provide, reactive, ref, watch } from 'vue'
|
|
import { getPrefixCls } from '../_utils/getPrefixCls'
|
|
import Space from '../space/index'
|
|
import { radioGroupKey } from './context'
|
|
import Radio from './radio'
|
|
|
|
interface RadioOption {
|
|
label?: string | RenderFunction
|
|
value?: string | number
|
|
extra?: string | RenderFunction
|
|
disabled?: boolean
|
|
[key: string]: any
|
|
}
|
|
|
|
export default defineComponent({
|
|
name: 'RadioGroup',
|
|
props: {
|
|
modelValue: {
|
|
type: [String, Number, Boolean] as PropType<string | number | boolean>,
|
|
default: undefined,
|
|
},
|
|
defaultValue: {
|
|
type: [String, Number, Boolean] as PropType<string | number | boolean>,
|
|
default: '',
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
labelField: {
|
|
type: String,
|
|
default: 'label',
|
|
},
|
|
valueField: {
|
|
type: String,
|
|
default: 'value',
|
|
},
|
|
extraField: {
|
|
type: String,
|
|
default: 'extra',
|
|
},
|
|
options: {
|
|
type: Array as PropType<Array<string | number | RadioOption>>,
|
|
default: [],
|
|
},
|
|
spaceProps: {
|
|
type: Object as PropType<Record<string, any>>,
|
|
default: () => ({}),
|
|
},
|
|
type: {
|
|
type: String as PropType<'default' | 'filled' | 'primaryFilled' | 'card' | 'pureCard' | 'outline'>,
|
|
default: 'default',
|
|
},
|
|
size: {
|
|
type: String as PropType<'small' | 'medium' | 'large'>,
|
|
default: 'medium',
|
|
},
|
|
},
|
|
emits: ['update:modelValue', 'change'],
|
|
setup(props, { emit, slots }) {
|
|
const prefixCls = getPrefixCls('radio-group')
|
|
|
|
// 内部值,优先使用 v-model
|
|
const _value = ref(props.modelValue ?? props.defaultValue)
|
|
|
|
watch(() => props.modelValue, (newVal) => {
|
|
if (newVal !== undefined) {
|
|
_value.value = newVal
|
|
}
|
|
})
|
|
|
|
const handleChange = (val: string | number | boolean, e: Event) => {
|
|
_value.value = val
|
|
emit('update:modelValue', val)
|
|
emit('change', val, e)
|
|
}
|
|
const groupDisabled = computed(() => props.disabled)
|
|
const groupType = computed(() => props.type)
|
|
// 提供 context 给 Radio 使用
|
|
provide(radioGroupKey, reactive({
|
|
name: 'AoRadioGroup' as const,
|
|
value: _value,
|
|
disabled: groupDisabled,
|
|
type: groupType,
|
|
slots,
|
|
handleChange,
|
|
}))
|
|
|
|
const renderOptions = () => {
|
|
return props.options.map((option) => {
|
|
// 标准化 option
|
|
const opt: RadioOption
|
|
= typeof option === 'string' || typeof option === 'number'
|
|
? { [props.labelField]: String(option), [props.valueField]: option }
|
|
: option
|
|
|
|
const labelContent = slots.label
|
|
? slots.label({ data: opt })
|
|
: typeof opt[props.labelField] === 'function'
|
|
? opt[props.labelField]()
|
|
: opt[props.labelField]
|
|
|
|
const extraContent = slots.extra
|
|
? slots.extra({ data: opt })
|
|
: typeof opt[props.extraField] === 'function'
|
|
? opt[props.extraField]()
|
|
: opt[props.extraField]
|
|
|
|
if (slots.radio) {
|
|
return (
|
|
<div
|
|
key={opt[props.valueField]}
|
|
onClick={e => handleChange(opt[props.valueField], e)} // 内部统一处理点击
|
|
>
|
|
{slots.radio({ data: opt, checked: _value.value === opt[props.valueField] })}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Radio
|
|
key={opt[props.valueField]}
|
|
value={opt[props.valueField]}
|
|
disabled={opt.disabled ?? props.disabled}
|
|
modelValue={_value.value === opt[props.valueField]}
|
|
extra={extraContent}
|
|
type={props.type}
|
|
>
|
|
{labelContent}
|
|
</Radio>
|
|
)
|
|
})
|
|
}
|
|
|
|
const spaceSize
|
|
= ['default', 'card', 'pureCard'].includes(groupType.value)
|
|
? [6, 6]
|
|
: props.spaceProps.size ?? 0
|
|
|
|
const sizeClass = computed(() => {
|
|
if (['filled', 'primaryFilled', 'outline'].includes(groupType.value)) {
|
|
return `${prefixCls}-${props.size}`
|
|
}
|
|
return null
|
|
})
|
|
|
|
return () => (
|
|
<Space
|
|
align="start"
|
|
size={spaceSize}
|
|
{...props.spaceProps}
|
|
class={[
|
|
`${prefixCls}-${groupType.value}`,
|
|
sizeClass.value,
|
|
]}
|
|
>
|
|
{props.options.length > 0 ? renderOptions() : slots.default?.()}
|
|
</Space>
|
|
)
|
|
},
|
|
})
|