RadioButtonGroup 按钮单选
把一组互斥选项拼成整块的分段控件,选中的一段以语义色高亮。它是独立组件,内部自带隐藏原生 input 与同名互斥逻辑,不使用 MRadio / MRadioGroup,因此与单选框那套的 API、DOM 与样式互不牵连。数据由 options 数组驱动,选项可等宽(block)或按内容自适应。
代码演示
基础用法
options 描述选项,v-model 绑定选中值。加 block 后各组等宽平分父容器宽度,是视图切换最常见的形态。
modelValue 支持 string | number | boolean 三种类型。
<script setup lang="ts">
import { ref } from 'vue'
const view = ref<string | number | boolean>('list')
const options = [
{ value: 'list', label: '列表' },
{ value: 'grid', label: '网格' },
{ value: 'board', label: '看板' },
]
</script>
<template>
<div class="m-row-demo--stretch">
<MRadioButtonGroup v-model="view" :options="options" block aria-label="视图切换" />
<span>当前视图:{{ view }}</span>
</div>
</template>受控切换视图
组件完全是受控的:选中值变化即驱动外部内容切换,适合「列表 / 网格 / 看板」这类视图切换器。
<script setup lang="ts">
import { ref } from 'vue'
const view = ref<string | number | boolean>('list')
const options = [
{ value: 'list', label: '列表' },
{ value: 'grid', label: '网格' },
{ value: 'board', label: '看板' },
]
const hints: Record<string, string> = {
list: '每行一条记录,适合字段较少的列表。',
grid: '宫格平铺,适合封面 / 头像类内容。',
board: '按状态分列,适合拖拽流转。',
}
</script>
<template>
<div class="m-row-demo--col">
<MRadioButtonGroup v-model="view" :options="options" color="secondary" aria-label="切换内容视图" />
<span>{{ hints[String(view)] }}</span>
</div>
</template>禁用项
option.disabled 只禁用单个选项,组件的 disabled 禁用整组,两者可叠加。
<script setup lang="ts">
import { ref } from 'vue'
const value = ref<string | number | boolean>('grid')
const locked = ref<string | number | boolean>('list')
const options = [
{ value: 'list', label: '列表' },
{ value: 'grid', label: '网格' },
{ value: 'board', label: '看板', disabled: true },
]
</script>
<template>
<div class="m-row-demo--col">
<MRadioButtonGroup v-model="value" :options="options" aria-label="含禁用项的视图切换" />
<MRadioButtonGroup v-model="locked" :options="options" disabled aria-label="整体禁用" />
</div>
</template>尺寸
small / default / large 三档,高度取自全局令牌 --m-size-small(28px)、--m-size-default(36px)、--m-size-large(48px)。
<script setup lang="ts">
import { ref } from 'vue'
const small = ref<string | number | boolean>('a')
const normal = ref<string | number | boolean>('a')
const large = ref<string | number | boolean>('a')
const options = [
{ value: 'a', label: '选项一' },
{ value: 'b', label: '选项二' },
{ value: 'c', label: '选项三' },
]
</script>
<template>
<div class="m-row-demo--col">
<MRadioButtonGroup v-model="small" :options="options" size="small" aria-label="小尺寸" />
<MRadioButtonGroup v-model="normal" :options="options" size="default" aria-label="默认尺寸" />
<MRadioButtonGroup v-model="large" :options="options" size="large" aria-label="大尺寸" />
</div>
</template>图标与纵向排列
option.icon 传入 @mdi/js 的 SVG path(与 MIcon 同源)。只给 icon 不给 label 时,会用 String(value) 生成可访问名称。vertical 让选项纵向排列。
<script setup lang="ts">
import { ref } from 'vue'
import { mdiViewListOutline, mdiViewGridOutline, mdiViewDashboardOutline, mdiFormatAlignLeft, mdiFormatAlignCenter, mdiFormatAlignRight } from '@mdi/js'
const view = ref<string | number | boolean>('list')
const align = ref<string | number | boolean>('left')
const iconOptions = [
{ value: 'list', label: '列表', icon: mdiViewListOutline },
{ value: 'grid', label: '网格', icon: mdiViewGridOutline },
{ value: 'board', label: '看板', icon: mdiViewDashboardOutline },
]
const alignOptions = [
{ value: 'left', icon: mdiFormatAlignLeft },
{ value: 'center', icon: mdiFormatAlignCenter },
{ value: 'right', icon: mdiFormatAlignRight },
]
</script>
<template>
<div class="m-row-demo">
<MRadioButtonGroup v-model="view" :options="iconOptions" color="success" aria-label="带图标的视图切换" />
<MRadioButtonGroup v-model="align" :options="alignOptions" vertical aria-label="纵向对齐方式" />
</div>
</template>API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | string | number | boolean | undefined | 当前选中值(v-model) |
options | RadioButtonGroupOption[] | [] | 选项列表 |
name | string | undefined(自动生成 m-radio-button-group-{uid}) | 原生 input name:同名才互斥、方向键才在组内切换;缺省时自动生成 |
disabled | boolean | false | 整体禁用 |
color | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | 'primary' | 选中态颜色 |
size | 'small' | 'default' | 'large' | 'default' | 尺寸 |
block | boolean | false | 占满父容器宽度,各段等宽平分 |
vertical | boolean | false | 纵向排列(默认横向) |
ariaLabel | string | undefined | 组的可访问名称,写在 role="radiogroup" 容器上 |
Events
| 事件 | 参数 | 说明 |
|---|---|---|
update:modelValue | value: string | number | boolean | 选中变化(已选中项不重复抛出) |
change | value: string | number | boolean | 选中变化 |
Slots
无(纯 options 数据驱动)。
类型定义
type RadioValue = string | number | boolean
interface RadioButtonGroupOption {
/** 选项值:与 modelValue 比对判定选中(组内应唯一) */
value: RadioValue
/** 选项文本 */
label?: string
/** 图标:@mdi/js 的 SVG path,与 MIcon 的 path 同源 */
icon?: string
/** 单独禁用该选项 */
disabled?: boolean
}使用建议
与 MRadio 的分工
MRadio 用于表单里带文字说明的单选列表,MRadioButtonGroup 用于工具栏式的紧凑切换。两者互不依赖,可以同时出现在同一页面,不会共享样式或 name。
label 与 icon 至少给一项
只给 icon 时,组件会用 String(value) 兜底生成可访问名称(aria-label)。若两者都缺,屏幕阅读器读不出该选项的语义。
选项值比较是严格相等
选中判定用 option.value === modelValue,1 与 '1'、true 与 'true' 不会互相命中。切换 modelValue 类型时要同步改 options 里的 value 类型。
不传 name 也会自动生成
name 缺省时会用 useId() 生成唯一值,保证同名互斥与方向键切换正常工作。只有当你要把该组与外部表单的字段对齐时,才需要显式传 name。