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, default: undefined, }, defaultValue: { type: [String, Number, Boolean] as PropType, 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>, default: [], }, spaceProps: { type: Object as PropType>, 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 (
handleChange(opt[props.valueField], e)} // 内部统一处理点击 > {slots.radio({ data: opt, checked: _value.value === opt[props.valueField] })}
) } return ( {labelContent} ) }) } 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 () => ( {props.options.length > 0 ? renderOptions() : slots.default?.()} ) }, })