This commit is contained in:
oneao committed 2026-10-08 22:38:34 +08:00
1 parent f572dce2f6
commit e99a9fb274
356 files changed
+28877 -1055

No files matched your search

@@ -0,0 +1,64 @@
---
title: ButtonGroup 按钮组
description: 把多个按钮贴合排列成一组,适用于工具条与互斥动作
---
# ButtonGroup 按钮组
按钮组把多个 `G3Button` 贴合排列:组内按钮去掉相邻圆角、边框重叠 1px,视觉上成为一整块。它只负责**布局**,每个按钮的 `type` / `size` / `disabled` 仍由自己决定。
## 何时使用
- 多个同层级操作成排出现(工具栏、卡片操作区)时,用按钮组把它们的关联关系表达出来;
- 一组图标动作(左对齐 / 居中 / 右对齐)时使用,避免按钮之间出现缝隙;
- 只是想给按钮之间加间距,请用 [Space 间距](/ui/general/space),不要用按钮组。
## 基础用法
默认水平排列,组内按钮共用一套圆角与边框。
:::demo{name="button-group/basic"}
:::
## 纵向排列
`orientation="vertical"` 时纵向贴合,组内按钮宽度一致,常作为侧边操作组。
:::demo{name="button-group/vertical"}
:::
## 组合不同变体与尺寸
组内按钮各自保留 `type` / `size`;混排时建议统一变体,否则视觉层次会混乱。
:::demo{name="button-group/variant"}
:::
## API
### Props(ButtonGroup)
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | 排列方向。`vertical` 时纵向贴合、按钮等宽 |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `default` | — | 组内的 `G3Button`。非按钮元素不会被索引到圆角规则里,请勿放置 |
### 类型定义
```ts
export interface ButtonGroupProps {
orientation?: 'horizontal' | 'vertical'
}
```
## 实现说明
- 组内按钮通过 `margin-left: -1px` 让相邻边框重叠,避免出现 2px 的双线;
- 首尾按钮单独恢复圆角,因此**中间的按钮不要插非按钮元素**(会被 CSS 的 `:first-child` / `:last-child` 规则错位);
- 分组用 `role="group"` 标注,屏幕阅读器会把它们视为一组操作;
- 按钮聚焦时会 `z-index: 1` 浮到上层,避免焦点环被相邻按钮挡住。
@@ -1,42 +1,191 @@
---
title: Button 按钮
description: 常用的操作按钮,支持多种视觉类型与状态
description: 触发一个操作,支持 8 种视觉变体、3 档尺寸与加载态
---
# Button 按钮
常用的操作按钮,支持 7 种视觉类型、3 种尺寸与加载、禁用等状态。
按钮用于触发一个即时操作。它承载「点击后发生什么」,因此同一区域里应当只有一个视觉上最重的按钮,其余操作降级表达。
## 何时使用
- 用户点击后即时触发一个动作(提交、保存、删除、导出)时使用;
- 表单、对话框、工具条的最终动作使用 `primary`(或危险动作使用 `danger`);
- 面板头部、表格行内的图标类动作使用 `type="icon"`;
- 只是页面跳转且不需要按钮外观时,直接用 `<a>`,或使用 `type="link"` 保持按钮语义。
## 基础用法
::demo{name="button/basic"}
::
不传 `type` 时使用 `primary`;按钮内容写在默认插槽里。
## 视觉类型
:::demo{name="button/basic"}
:::
语义化类型用于表达操作的风险等级。
按钮是原生 `<button>`,因此天然支持 `disabled`、键盘 `Enter / Space` 触发与表单语义。
::demo{name="button/type"}
::
## 视觉变体
`type` 决定按钮的视觉重量,请按「操作重要性」选择,而不是按颜色喜好选择。
:::demo{name="button/variant"}
:::
| 变体 | 使用场景 |
| --- | --- |
| `primary` | 页面 / 表单 / 弹窗中的**主操作**,一屏通常只有一个 |
| `secondary` | 次要操作,有底色但无主色,常用于「取消」「重置」 |
| `outline` | 与 `primary` 并排出现时的次级操作,或工具条中的常规动作 |
| `ghost` | 表格行、卡片内的低强调操作,hover 才出现底色 |
| `danger` | **不可逆**的危险操作,如删除、停用;建议配合二次确认 |
| `danger-outline` | 列表工具条中的危险动作:表达危险语义但不与 `primary` 抢视觉 |
| `link` | 视觉上是文字链接,但保留按钮的尺寸、禁用与键盘语义 |
| `icon` | 正方形描边图标按钮,用于工具条 / 面板头部动作位 |
## 尺寸
::demo{name="button/size"}
::
`size` 提供 3 档,默认 `medium`(`--g3-control-height`,32px)。
## 状态
:::demo{name="button/size"}
:::
::demo{name="button/state"}
::
尺寸只影响按钮自身的高度与字号;图标按钮(`type="icon"`)会同步变成正方形。
## 图标与撑满
## 图标
::demo{name="button/icon"}
::
使用 `#icon` 插槽放图标,图标大小跟随按钮字号(`1em`);纯图标按钮用 `type="icon"`,**必须**提供 `aria-label`,否则屏幕阅读器读不出来。
::demo{name="button/block"}
::
:::demo{name="button/icon"}
:::
`type="icon"` 的按钮没有文字,因此无障碍标签需要手动补:
```vue
<G3Button type="icon" aria-label="搜索">
<template #icon><Search /></template>
</G3Button>
```
## 加载态
`loading` 会同时做三件事:显示加载图标、进入原生 `disabled`、**拦截 `click` 事件**(不会触发 `click`),因此可以直接用它防止重复提交。
:::demo{name="button/loading"}
:::
需要注意的是:`loading` 时按钮是原生禁用状态,浏览器不会派发 `click`;组件内部也额外做了拦截,避免在「按下瞬间进入 loading」时重复触发。
## 禁用态
`disabled` 使用原生 `disabled` 属性,同时降低整体透明度并显示禁用光标。
:::demo{name="button/disabled"}
:::
禁用按钮的 `click` 不会触发,也不会被聚焦(原生行为),所以不要把「必填校验失败」的提交按钮设为禁用——用户会不知道原因。
## 撑满宽度
`block` 让按钮占满父容器宽度,常用于移动端、抽屉底部与卡片内。
:::demo{name="button/block"}
:::
## 表单中的提交与重置
`htmlType` 对应原生 `<button type>`,配合 G3Form 使用时写 `html-type="submit"` / `"reset"` 即可触发表单的提交与重置。
:::demo{name="button/html-type"}
:::
`htmlType` 默认值是 `button`,这样按钮放在表单里不会意外提交(原生 `<button>` 在表单内的默认行为是 `submit`)。
## 全局默认值
通过 `G3ConfigProvider` 的 `componentDefaults` 可以一次性修改某类组件的默认 props,组件内部的回落顺序是 **显式 prop > 全局默认值 > 组件内置默认**。
:::demo{name="button/global-defaults"}
:::
## click 事件
`@click` 回传原生 `MouseEvent`,可以直接读取修饰键或做 `preventDefault`。
:::demo{name="button/event"}
:::
## API
<ApiTable name="button" />
### Props
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `type` | `'primary' \| 'secondary' \| 'outline' \| 'ghost' \| 'danger' \| 'danger-outline' \| 'link' \| 'icon'` | `'primary'` | 视觉变体。`icon` 为正方形描边图标按钮(无文字,需自行补 `aria-label`) |
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸。对应高度 24px / 32px / 40px(`--g3-control-height-sm` / `--g3-control-height` / `--g3-control-height-lg`) |
| `htmlType` | `'button' \| 'submit' \| 'reset'` | `'button'` | 原生 `type` 属性。表单里需要提交时写 `submit` |
| `disabled` | `boolean` | `false` | 是否禁用。原生禁用,不派发 `click`,不参与焦点 |
| `loading` | `boolean` | `false` | 是否加载中。展示加载图标并**阻止 `click`**,用于防重复提交 |
| `block` | `boolean` | `false` | 是否撑满父容器宽度(`display: flex; width: 100%`) |
> `type` 与 `size` 的 prop 默认值语义上留空,以便 `ConfigProvider.componentDefaults.button` 生效;未配置全局值时回落到 `primary` / `medium`。
### Events
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `click` | `(event: MouseEvent)` | 点击按钮时触发。`disabled` 或 `loading` 时不会触发 |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `default` | — | 按钮文字。为空时不渲染内容容器 |
| `icon` | — | 按钮左侧图标。图标尺寸跟随字号(`1em`),加载中时会被加载图标顶到右侧 |
### 类型定义
```ts
export type ButtonType =
| 'primary'
| 'secondary'
| 'outline'
| 'ghost'
| 'danger'
| 'danger-outline'
| 'link'
| 'icon'
export type ButtonSize = 'small' | 'medium' | 'large'
export interface ButtonProps {
type?: ButtonType
size?: ButtonSize
htmlType?: 'button' | 'submit' | 'reset'
disabled?: boolean
loading?: boolean
block?: boolean
}
export interface ButtonGroupProps {
/** 排列方向,默认 horizontal */
orientation?: 'horizontal' | 'vertical'
}
```
### 样式变量
按钮只引用设计变量,换肤时覆盖同名变量即可,不需要改组件代码:
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `--g3-control-height` | `32px` | 默认高度(`small` / `large` 用 `--g3-control-height-sm / -lg`) |
| `--g3-radius` | `6px` | 圆角 |
| `--g3-color-primary` | `#18181b` | `primary` 底色与 `link` 文字色 |
| `--g3-color-primary-on` | `#fafafa` | `primary` 上的文字色 |
| `--g3-color-danger` | `#ec4246` | `danger` / `danger-outline` 的红 |
| `--g3-fill` / `--g3-fill-hover` | `rgba(0,0,0,.04 / .08)` | `secondary` / `ghost` / `icon` 的底色 |
## 设计说明
- **hover 去抖**:按钮的「离开 hover」过渡带 60ms 延迟,用来吸收鼠标在按钮边缘反复进出造成的背景闪烁;「进入 hover」延迟为 0,保证跟手。
- **按下反馈**:`:active` 时 `transform: scale(0.97)`,加载态会压掉缩放,避免「进行中」的按钮抖动。
- **禁用态**:整体 `opacity: .5`,保留原配色,不再改文字颜色——这样深浅主题下都能看出是同一个按钮。
@@ -0,0 +1,123 @@
---
title: Grid 栅格
description: 24 栅格布局,支持间距、偏移、位移与 flex 自适应
---
# Grid 栅格
栅格把一行分成 24 等份,用 `G3Row` 表示行、`G3Col` 表示列。它适合页面级 / 表单级的块状布局;元素之间的等距排列(按钮行、标签行)请用 [Space](/ui/general/space)。
## 何时使用
- 卡片、表单项、图表块的成行排布;
- 需要按比例分配宽度(12 / 6 / 8 …)或「一侧固定、一侧自适应」时;
- 需要控制主轴分布(居中、两端对齐)与交叉轴对齐时。
## 基础用法
`span` 表示占多少格(0-24);一行内累计超过 24 会自动换行。
:::demo{name="grid/basic"}
:::
## 间距
`gutter` 传数字表示水平与垂直同值,传 `[水平, 垂直]` 可分别控制。栅格间距用 `gap` 实现,子列不再需要自己写 margin。
:::demo{name="grid/gutter"}
:::
## 偏移与位移
- `offset`:左侧留空格(不参与排序,位置固定);
- `push` / `pull`:左右位移,常用于调整视觉顺序;
- `order`:flex 的排序值,最小排在最前。
:::demo{name="grid/offset"}
:::
## 对齐与分布
`align` 控制交叉轴(`align-items`),`justify` 控制主轴分布(`justify-content`)。需要行内有高度差时给 `Row` 一个固定高度。
:::demo{name="grid/align"}
:::
## flex 自适应
`flex` 传数字表示 `flex-grow` 的份数,传字符串则作为完整的 `flex` 简写使用。
:::demo{name="grid/flex"}
:::
## 不换行
`wrap=false` 时列不会换行,超出 24 格的部分会被压缩,适合「横向铺满、不允许折行」的工具条。
:::demo{name="grid/no-wrap"}
:::
## API
### Props(Row)
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `gutter` | `number \| [number, number]` | `0` | 栅格间距(px)。数组为 `[水平, 垂直]` |
| `align` | `'top' \| 'middle' \| 'bottom' \| 'stretch'` | `'top'` | 交叉轴对齐(`align-items`) |
| `justify` | `'start' \| 'end' \| 'center' \| 'space-around' \| 'space-between' \| 'space-evenly'` | `'start'` | 主轴分布(`justify-content`) |
| `wrap` | `boolean` | `true` | 是否换行(`flex-wrap`) |
### Props(Col)
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `span` | `number \| string` | — | 占位格数 0-24。不传时按 `flex` 或内容宽度 |
| `offset` | `number \| string` | `0` | 左侧偏移格数 |
| `order` | `number \| string` | — | 排序值(`order`) |
| `push` | `number \| string` | `0` | 向右移动格数 |
| `pull` | `number \| string` | `0` | 向左移动格数 |
| `flex` | `number \| string` | — | 数字按 `flex: n n auto` 处理;字符串作为完整 `flex` 简写,如 `'0 0 200px'` |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `default` | — | `Row` 内放 `Col`;`Col` 内放任意内容 |
### 类型定义
```ts
export type GridGutter = number | [number, number]
export type RowAlign = 'top' | 'middle' | 'bottom' | 'stretch'
export type RowJustify =
| 'start'
| 'end'
| 'center'
| 'space-around'
| 'space-between'
| 'space-evenly'
export interface RowProps {
gutter?: GridGutter
align?: RowAlign
justify?: RowJustify
wrap?: boolean
}
export interface ColProps {
span?: number | string
offset?: number | string
order?: number | string
push?: number | string
pull?: number | string
flex?: number | string
}
```
## 实现说明
- 栅格基于 flex:`Row` 是 `display: flex` + `flex-wrap`,`Col` 通过 `flex-basis: span/24 * 100%` 占位;
- 水平间距在 `Row` 上用负 margin + `Col` 上的 padding 抵消(首尾不外扩);垂直间距由 `row-gap` 承担;
- `Col` 的 `span`、`offset`、`push` / `pull` / `order` 都支持字符串,便于上层做动态计算(如设计器里按栅格配置渲染);
- 不做 `xs / sm / md` 断点:本库面向中后台工具,布局由容器宽度(如 `Space` / `Splitter`)控制,需要响应式时请在业务层用 CSS 媒体查询覆盖。
@@ -0,0 +1,123 @@
---
title: Segmented 分段控制器
description: 在一组互斥选项中切换,滑块跟随选中项移动
---
# Segmented 分段控制器
分段控制器把 2-5 个互斥选项并排展示,用一个会滑动的滑块指示当前选项。它比 [Tabs](/ui/data/tabs) 轻量:切换的是**同一区域的视图模式**,不是内容面板。
## 何时使用
- 视图模式切换:列表 / 看板 / 时间线;
- 粒度切换:日 / 周 / 月;
- 表单里的少量选项(2-5 项)时,比 [Select](/ui/form/select) 更快、比 [Radio](/ui/form/radio) 更紧凑;
- 选项超过 5 个、或需要多选时,请改用 Select / Checkbox。
## 基础用法
`options` 可以直接传原始值数组,选中值通过 `v-model` 双向绑定(支持字符串、数字、布尔)。
:::demo{name="segmented/basic"}
:::
## 对象选项
选项写成对象可以自定义显示文字、加图标、单独禁用某一项。
:::demo{name="segmented/options"}
:::
## 圆角形状
:::demo{name="segmented/shape"}
:::
## 纵向排列
`vertical` 时滑块上下移动,适合做侧边栏的视图切换。
:::demo{name="segmented/vertical"}
:::
## 撑满宽度
`block` 让各项等分父容器宽度(纵向时为高度),常用于移动端或表单顶部。
:::demo{name="segmented/block"}
:::
## 自定义每项内容
`#label` 插槽作用域为 `{ option, index, checked }`:`option` 是原始选项对象,`checked` 表示该项是否选中。
:::demo{name="segmented/custom-label"}
:::
## 非受控用法
不传 `modelValue` 时组件内部维护选中值(默认不选中),通过 `change` 把结果抛给业务。
:::demo{name="segmented/uncontrolled"}
:::
## API
### Props
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelValue` | `string \| number \| boolean` | — | 双向绑定的选中值。不传即非受控 |
| `options` | `Array<SegmentedOptionInput>` | `[]` | 选项数据源,元素可以是原始值或 `{ label, value, disabled, icon }` |
| `shape` | `'default' \| 'round'` | `'default'` | 圆角形状。`round` 为全圆角胶囊 |
| `vertical` | `boolean` | `false` | 纵向排列,滑块上下移动 |
| `block` | `boolean` | `false` | 撑满父容器并等分各项 |
| `disabled` | `boolean` | `false` | 禁用整组 |
| `name` | `string` | 自动生成 | 原生 radio 组名。不传时用 `useId` 生成,避免多组互斥冲突 |
### Events
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `update:modelValue` | `(value: string \| number \| boolean)` | 选中项变化时触发(受控与非受控都会触发) |
| `change` | `(value: string \| number \| boolean)` | 同上,便于只监听变化 |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `label` | `{ option: SegmentedOptionInput, index: number, checked: boolean }` | 自定义每项内容;不传时渲染 `icon` + `label` |
### 类型定义
```ts
export type SegmentedValue = string | number | boolean
export interface SegmentedOption {
label?: string
value: SegmentedValue
disabled?: boolean
icon?: Component | VNode
}
export type SegmentedOptionInput = SegmentedValue | SegmentedOption
export type SegmentedShape = 'default' | 'round'
```
### 样式变量
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `--g3-segmented-padding` | `2px` | 轨道内边距 |
| `--g3-radius` | `6px` | 轨道圆角(`round` 时为全圆角) |
| `--g3-fill` | `rgba(0,0,0,.04)` | 轨道底色 |
| `--g3-bg-container` | `#fff` | 滑块底色 |
| `--g3-shadow` | `0 2px 8px rgba(0,0,0,.15)` | 滑块投影 |
## 实现说明
- 底层是**原生 radio 组**(`input[type=radio]` + `<label>`),因此键盘方向键切换、屏幕阅读器分组语义都由浏览器原生提供;
- 滑块的位移与尺寸通过 `getBoundingClientRect` 测量并写入 CSS 变量,容器尺寸变化时用 `ResizeObserver` 重算,所以字体缩放、文案长度变化都能跟上;
- 首次渲染时滑块先隐藏(`thumbVisible`),测量完成后再淡入,避免从 `(0,0)` 位置滑过来的闪动;
- 选项里的图标会经过 `toRaw` 处理:`options` 常由 `ref` 提供,直接使用会被深层响应式包装,触发 Vue 的「组件被响应式化」告警;
- 与 Tabs 的区别:Segmented 没有面板,只负责"选择";Tabs 负责"选择 + 内容区 + 懒渲染"。
@@ -0,0 +1,107 @@
---
title: Space 间距
description: 为一组内联元素设置统一间距,并支持插入分隔符
---
# Space 间距
`Space` 给一组子元素之间加统一间距,省去给每个元素写 `margin` 的麻烦。
**Space 与 Grid 的分工**:`Space` 面向「一行内联元素的等距排列」(按钮行、标签行、操作区),会给每个子元素包一层包装以支持分隔符与空节点隐藏;整块页面布局请用 [Grid 栅格](/ui/general/grid)。
## 何时使用
- 一排按钮 / 标签 / 文字之间需要统一间距时;
- 需要在元素之间插入分隔符(`|`、`·`)时;
- 子元素数量不确定、其中有条件渲染的节点时(空节点不会占位)。
## 基础用法
默认水平排列、间距 `small`(8px)、交叉轴居中对齐。
:::demo{name="space/basic"}
:::
## 间距大小
`size` 支持三种写法:预设名 `small` / `middle` / `large`、数字(按 px 处理)、以及 `[水平, 垂直]` 数组。
:::demo{name="space/size"}
:::
| 写法 | 效果 |
| --- | --- |
| `size="small"` | 8px |
| `size="middle"` | 16px(默认档,常用于行内) |
| `size="large"` | 24px |
| `:size="12"` | 12px |
| `:size="[16, 8]"` | 列间距 16px、行间距 8px(配合 `wrap` 用) |
## 纵向排列
`vertical` 是简写;`orientation` 与它同时配置时,以 `orientation` 优先(这样上层组件透传 `orientation` 时不会被 `vertical` 覆盖)。
:::demo{name="space/vertical"}
:::
## 分隔符
`#separator` 插槽会在相邻子元素之间插入内容,**不会**在首尾添加。
:::demo{name="space/separator"}
:::
## 自动换行
`wrap` 只在水平方向生效;配合 `[水平, 垂直]` 形式的 `size` 可以分别控制列间距与行间距。
:::demo{name="space/wrap"}
:::
## 空节点不占位
`v-if` 关闭的节点、注释节点不会被计入间距,因此条件渲染不会留下空隙。
:::demo{name="space/empty-children"}
:::
## API
### Props
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `size` | `'small' \| 'middle' \| 'large' \| number \| string \| [SpaceSize, SpaceSize]` | `'small'` | 间距。数组按 `[水平, 垂直]` 解析;数字按 px;其他字符串原样透传给 `column-gap` / `row-gap` |
| `orientation` | `'horizontal' \| 'vertical'` | 由 `vertical` 决定 | 排列方向。优先级高于 `vertical` |
| `vertical` | `boolean` | `false` | 纵向排列的简写 |
| `align` | `'start' \| 'end' \| 'center' \| 'baseline'` | 水平方向 `'center'`,纵向不设置 | 交叉轴对齐(`align-items`) |
| `wrap` | `boolean` | `false` | 是否自动换行,仅水平方向生效 |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `default` | — | 间距容器内的元素。每个有效子节点会被包一层 `g3-space__item` |
| `separator` | — | 相邻元素之间插入的分隔内容 |
### 类型定义
```ts
export type SpaceSize = 'small' | 'middle' | 'large' | number | string
export type SpaceOrientation = 'horizontal' | 'vertical'
export type SpaceAlign = 'start' | 'end' | 'center' | 'baseline'
export interface SpaceProps {
size?: SpaceSize | [SpaceSize, SpaceSize]
orientation?: SpaceOrientation
vertical?: boolean
align?: SpaceAlign
wrap?: boolean
}
```
## 实现说明
- 间距用 CSS 的 `gap` 实现(`column-gap` / `row-gap`),因此**不依赖子元素是否有 margin**,也不会出现「相邻 margin 合并」的问题;
- 子元素会被包一层 `inline-flex` 的 `g3-space__item`,这让分隔符有落点,同时过滤掉注释 / 文本空节点;
- 纵向排列时默认不设置 `align`(即 `stretch`),这样「撑满宽度」的按钮才不会被压成内容宽度;需要居中时显式传 `align="center"`。
@@ -0,0 +1,111 @@
---
title: Tag 标签
description: 用于标记与分类的小型容器,支持多种变体、可关闭与链接
---
# Tag 标签
标签用于标记对象的状态、分类或属性,例如「已完成」「前端」「张三」。它体量小、不可点击(除非传 `href`),因此不要用标签承载操作。
## 何时使用
- 表达状态:`success` 表示通过、`danger` 表示失败、`outline` 表示中性;
- 表达多值属性:一行为一组标签;
- 表示可移除的筛选项 / 输入项时使用 `closable`;
- 需要点击跳转时传 `href`,组件会渲染成 `<a>`。
## 视觉变体
:::demo{name="tag/variant"}
:::
| 变体 | 语义 | 典型场景 |
| --- | --- | --- |
| `default` | 常规标记 | 分类、属性 |
| `secondary` | 次要标记 | 筛选项、非重点属性 |
| `outline` | 中性描边 | 需要与底色区分开的标记 |
| `success` | 成功 | 已完成、已通过、已上线 |
| `danger` | 失败 / 危险 | 失败、异常、已禁用 |
| `ghost` | 极低强调 | 占位、统计数字 |
## 可关闭
`closable` 会渲染关闭按钮(`role="button"`,支持 Enter / Space)。**点击关闭后标签会自己隐藏**,同时触发 `close` 事件——业务通常只需要在事件里同步数据源。
:::demo{name="tag/closable"}
:::
## 阻止自动关闭(受控)
如果标签的显示与否由业务数据决定,在 `close` 里调用 `event.preventDefault()` 即可阻止自动隐藏。
:::demo{name="tag/prevent-close"}
:::
## 图标
`#icon` 放前置图标,`#close-icon` 可以替换默认的关闭图标(例如换成文字 `×`)。
:::demo{name="tag/icon"}
:::
## 链接标签
传 `href` 后渲染成 `<a>`;`disabled` 时会去掉 `href` 并标记 `aria-disabled`,避免出现「看起来能点但点了没反应」。
:::demo{name="tag/link"}
:::
## API
### Props
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `variant` | `'default' \| 'secondary' \| 'outline' \| 'success' \| 'danger' \| 'ghost'` | `'default'` | 视觉变体 |
| `closable` | `boolean` | `false` | 是否显示关闭按钮。点击后触发 `close` 并自动隐藏(可用 `preventDefault` 阻止) |
| `disabled` | `boolean` | `false` | 禁用态。关闭按钮不可用、`href` 被移除 |
| `href` | `string` | — | 传入后渲染为 `<a>`;其余原生属性(如 `target`)会透传到该元素 |
### Events
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `close` | `(event: Event)` | 点击关闭按钮时触发。调用 `event.preventDefault()` 可阻止标签自动隐藏 |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `default` | — | 标签文本 |
| `icon` | — | 前置图标(尺寸跟随字号 `1em`) |
| `close-icon` | — | 自定义关闭图标,默认是 `X` 图标 |
### 类型定义
```ts
export type TagVariant = 'default' | 'secondary' | 'outline' | 'success' | 'danger' | 'ghost'
export interface TagProps {
variant?: TagVariant
closable?: boolean
disabled?: boolean
href?: string
}
```
### 样式变量
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `--g3-tag-height` | `20px` | 标签高度(可通过组件私有变量覆盖) |
| `--g3-font-size-sm` | `12px` | 字号 |
| `--g3-radius-sm` | `4px` | 圆角 |
| `--g3-color-success` / `--g3-color-danger` | `#45b114` / `#ec4246` | `success` / `danger` 的前景色 |
| `--g3-fill` | `rgba(0,0,0,.04)` | `secondary` / `default` 的底色 |
## 实现说明
- 标签渲染为 `<span>`(或 `<a>`),是可内联排列的 `inline-flex`,因此可以直接放在文字流或 `Space` 里;
- 关闭按钮是 `span[role="button"]` 而不是 `<button>`,避免在 `a` 形态下嵌套交互元素(HTML 不允许 `<a>` 内嵌 `<button>`);
- 关闭按钮的 `aria-label` 取自语言包(`locale.common.close`),可通过 `ConfigProvider.locale` 切换语言。