ButtonGroup 按钮组
把若干个 MButton 拼成一个视觉整体,适合「左中右」这类互斥或连续的操作用途。组本身不渲染任何按钮,只通过 provide/inject 向内部按钮统一下发 variant / color / size / disabled,因此组内按钮的外观天然一致。
代码演示
基础用法
MButtonGroup 必须搭配 MButton 使用,组只负责统一外观与圆角拼接。
组内按钮通过 provide/inject 拿到组下发的上下文,点击时各自抛出 click;组本身不代表某次选中状态,需要互斥选中请自行记录当前项。
<script setup lang="ts">
import { toast } from 'miao-design'
function handleClick(label: string) {
toast.success(`选中:${label}`)
}
</script>
<template>
<div class="m-row-demo">
<MButtonGroup variant="outlined" color="primary">
<MButton v-for="label in ['左', '中', '右']" :key="label" @click="handleClick(label)">
{{ label }}
</MButton>
</MButtonGroup>
</div>
</template>外观变体
组上的 variant 会覆盖内部每个按钮自己的 variant,即使子按钮显式传了 variant 也会被组改写。
非 outlined 变体下,组内按钮之间的分隔边框会转为透明,只保留拼接后的整体轮廓。
<script setup lang="ts">
const variants = ['outlined', 'contained', 'tonal', 'text'] as const
</script>
<template>
<div class="m-row-demo--col">
<MButtonGroup v-for="variant in variants" :key="variant" :variant="variant" color="primary">
<MButton>{{ variant }}</MButton>
<MButton>中间</MButton>
<MButton>右侧</MButton>
</MButtonGroup>
</div>
</template>尺寸
small / default / large 三档由组统一决定,子按钮传 size 无效。
size 未设置时组会在上下文里写入 'default',所以「不传」并不等于「子按钮自己决定」,而是统一按 default 渲染。
<script setup lang="ts">
const sizes = ['small', 'default', 'large'] as const
</script>
<template>
<div class="m-row-demo--col">
<MButtonGroup v-for="size in sizes" :key="size" variant="outlined" color="primary" :size="size">
<MButton>{{ size }}</MButton>
<MButton>中间</MButton>
<MButton>右侧</MButton>
</MButtonGroup>
</div>
</template>语义色与子级覆盖
color 是唯一「子优先」的属性:子按钮自己设置了 color 就用自己的,没设置才继承组的。
每组的最后一个按钮都显式写了 color="info",用于对比组色被覆盖的效果。
<script setup lang="ts">
const groups = ['primary', 'secondary', 'success', 'error'] as const
</script>
<template>
<div class="m-row-demo">
<MButtonGroup
v-for="color in groups"
:key="color"
variant="contained"
:color="color"
size="small"
>
<MButton>{{ color }}</MButton>
<MButton>中间</MButton>
<!-- 子按钮自带 color,组色不再生效 -->
<MButton color="info">info</MButton>
</MButtonGroup>
</div>
</template>禁用
disabled 取父子并集:组禁用则整组禁用,组不禁用时单个子按钮仍可自行禁用。
点击下方按钮可实时切换整组的禁用状态。
<script setup lang="ts">
import { ref } from 'vue'
const groupDisabled = ref(false)
</script>
<template>
<div class="m-row-demo--col">
<MButtonGroup variant="outlined" color="primary" :disabled="groupDisabled">
<MButton>左</MButton>
<MButton>中</MButton>
<MButton>右</MButton>
</MButtonGroup>
<MButtonGroup variant="outlined" color="primary">
<MButton>可用</MButton>
<!-- 组不禁用时,单个子按钮仍可自行禁用 -->
<MButton disabled>子级禁用</MButton>
<MButton>可用</MButton>
</MButtonGroup>
<MButton variant="contained" color="primary" @click="groupDisabled = !groupDisabled">
{{ groupDisabled ? '恢复整组' : '禁用整组' }}
</MButton>
</div>
</template>API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
variant | 'text' | 'outlined' | 'contained' | 'tonal' | 'circle' | 'outlined' | 组内按钮统一变体;始终覆盖子按钮自身的 variant |
color | 'primary' | 'secondary' | 'error' | 'success' | 'warning' | 'info' | undefined | 组内按钮统一语义色;子按钮自己设置了 color 时以子为准 |
size | 'small' | 'default' | 'large' | undefined(上下文回落 'default') | 组内按钮统一尺寸;始终覆盖子按钮自身的 size |
disabled | boolean | false | 是否整体禁用组内所有按钮(与子按钮的 disabled 取并集) |
Events
无。
Slots
| 插槽 | 参数 | 说明 |
|---|---|---|
default | 无 | 组内的 MButton 列表 |
组内上下文
组通过 provide/inject 把下面的对象下发给内部按钮(ButtonGroupContext),子按钮读取后按各自的优先级合并:
export interface ButtonGroupContext {
color?: ButtonColor
variant: ButtonVariant
size: 'small' | 'default' | 'large'
disabled: boolean
}合并规则:
| 属性 | 合并方式 | 结果 |
|---|---|---|
variant | 组优先 | 子按钮的 variant 被忽略 |
size | 组优先 | 子按钮的 size 被忽略(组未传时按 default 覆盖) |
color | 子优先 | 子按钮未设置时才取组的颜色 |
disabled | 取并集 | 组或子按钮任一为 true 即禁用 |
使用建议
组只统一外观,不管理选中状态
MButtonGroup 是纯样式与上下文容器,没有 modelValue 也不抛 change。需要「单选按钮组」这类互斥语义时,用业务里的 ref 记录当前项,再在按钮的 click 里更新即可。
子按钮的 variant 与 size 会被覆盖
这是刻意的设计:组内按钮外观必须一致,所以组的 variant / size 永远赢。只有 color 允许子按钮覆盖。如果你的按钮需要各自不同的变体,就不要把它们放进同一个组。
<MButtonGroup variant="contained" size="small">
<MButton variant="text" size="large">仍然按 contained + small 渲染</MButton>
</MButtonGroup>组内按钮的圆角与边框被改写
组会给首尾按钮加外圆角、给中间按钮去掉圆角,并把相邻按钮的 margin-left 设为 -1px 让边框重叠。如果按钮原本带了 shape="round" 或 shape="circle",拼接效果会和预期不同。