当内容需要分组并在不同模块页面中展示,可使用 Tabs 标签栏对不同的组/页之间进行切换。
底层基于 reka-ui 无头组件,内置 ARIA 无障碍支持和键盘导航。
- 需要在不同模块页面之间切换展示内容时。
- 内容需要分组展示时。
- 节省页面空间,将相关内容组织在一起时。
import {
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@reglow/reui";
import type { TabItem } from "@reglow/reui";
适合标签数据来自接口或动态变化的场景:
<script setup>
import { ref } from "vue";
import { Tabs } from "@reglow/reui";
const activeKey = ref("1");
const tabList = [
{ itemKey: "1", tab: "文档" },
{ itemKey: "2", tab: "快速起步" },
{ itemKey: "3", tab: "帮助" },
];
</script>
<template>
<Tabs v-model:active-key="activeKey" :tab-list="tabList" type="line">
<div v-if="activeKey === '1'">文档内容</div>
<div v-if="activeKey === '2'">快速起步内容</div>
<div v-if="activeKey === '3'">帮助内容</div>
</Tabs>
</template>
适合自定义结构、内容就近放置的场景:
<script setup>
import { ref } from "vue";
import {
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@reglow/reui";
const activeKey = ref("1");
</script>
<template>
<Tabs v-model:active-key="activeKey" type="line">
<TabsList>
<TabsTrigger value="1">文档</TabsTrigger>
<TabsTrigger value="2">帮助</TabsTrigger>
</TabsList>
<TabsContent value="1">文档内容</TabsContent>
<TabsContent value="2">帮助内容</TabsContent>
</Tabs>
</template>
type 属性从 Tabs 通过 provide/inject 自动传递给 TabsList 和 TabsTrigger,无需在子组件上重复指定。
<!-- 线条式(默认) -->
<Tabs :tab-list="tabList" type="line">...</Tabs>
<!-- 按钮式 -->
<Tabs :tab-list="tabList" type="button">...</Tabs>
<!-- 卡片式 -->
<Tabs :tab-list="tabList" type="card">...</Tabs>
<!-- 斜线式 -->
<Tabs :tab-list="tabList" type="slash">...</Tabs>
<!-- 可编辑卡片式 -->
<Tabs
:tab-list="tabList"
type="editable-card"
@add="onAdd"
@close="onClose"
>...</Tabs>
<!-- 分段控制器式 -->
<Tabs :tab-list="tabList" type="segmented">...</Tabs>
segmented 类型的标签栏样式与 Segmented 组件一致,选中项有滑块背景动画。
<script setup>
import { ref } from "vue";
import { Tabs } from "@reglow/reui";
const activeKey = ref("1");
const tabList = [
{ itemKey: "1", tab: "日" },
{ itemKey: "2", tab: "周" },
{ itemKey: "3", tab: "月" },
{ itemKey: "4", tab: "年" },
];
</script>
<template>
<Tabs v-model:active-key="activeKey" :tab-list="tabList" type="segmented">
<div v-if="activeKey === '1'">按日查看</div>
<div v-if="activeKey === '2'">按周查看</div>
<div v-if="activeKey === '3'">按月查看</div>
<div v-if="activeKey === '4'">按年查看</div>
</Tabs>
</template>
editable-card 类型默认显示关闭按钮和新增按钮,适合动态标签页场景。
<script setup>
import { ref } from "vue";
import { Tabs } from "@reglow/reui";
const activeKey = ref("1");
const tabList = ref([
{ itemKey: "1", tab: "标签1" },
{ itemKey: "2", tab: "标签2" },
]);
let counter = 3;
function onAdd() {
const key = String(counter++);
tabList.value.push({ itemKey: key, tab: `标签${key}` });
activeKey.value = key;
}
function onClose(key) {
const idx = tabList.value.findIndex((t) => t.itemKey === key);
tabList.value = tabList.value.filter((t) => t.itemKey !== key);
if (activeKey.value === key && tabList.value.length > 0) {
activeKey.value = tabList.value[Math.max(0, idx - 1)].itemKey;
}
}
</script>
<template>
<Tabs
v-model:active-key="activeKey"
:tab-list="tabList"
type="editable-card"
@add="onAdd"
@close="onClose"
>
<div
v-for="item in tabList"
:key="item.itemKey"
v-show="activeKey === item.itemKey"
>
{{ item.tab }}内容
</div>
</Tabs>
</template>
可以通过 addable 和 editable 属性控制新增和关闭按钮的显示:
<!-- 禁用新增按钮 -->
<Tabs :tab-list="tabList" type="editable-card" :addable="false">...</Tabs>
<!-- 禁用关闭按钮 -->
<Tabs :tab-list="tabList" type="editable-card" :editable="false">...</Tabs>
复合组件模式下,TabsList 自动渲染新增按钮,TabsTrigger 自动渲染关闭按钮:
<Tabs
v-model:active-key="active"
type="editable-card"
@add="onAdd"
@close="onClose"
>
<TabsList>
<TabsTrigger v-for="t in tabs" :key="t.key" :value="t.key">
{{ t.label }}
</TabsTrigger>
</TabsList>
<TabsContent v-for="t in tabs" :key="t.key" :value="t.key">
{{ t.label }}内容
</TabsContent>
</Tabs>
通过 icon 属性添加图标(配置式):
<script setup>
import { h } from "vue";
import { File, Globe, HelpCircle } from "lucide-vue-next";
const tabList = [
{ itemKey: "1", tab: "文档", icon: h(File) },
{ itemKey: "2", tab: "快速起步", icon: h(Globe) },
{ itemKey: "3", tab: "帮助", icon: h(HelpCircle) },
];
</script>
<template>
<Tabs :tab-list="tabList" />
</template>
复合组件模式下直接在 TabsTrigger 内放置图标:
<TabsList>
<TabsTrigger value="1">
<File class="size-4" /> 文档
</TabsTrigger>
</TabsList>
设置 tabPosition 为 left 或 right。
<template>
<Tabs :tab-list="tabList" type="line" tab-position="left">
<div>内容</div>
</Tabs>
</template>
<script setup>
const tabList = [
{ itemKey: "1", tab: "文档" },
{ itemKey: "2", tab: "快速起步", disabled: true },
{ itemKey: "3", tab: "帮助" },
];
</script>
<template>
<Tabs :tab-list="tabList" default-active-key="1" />
</template>
通过 tabBarExtraContent 在标签栏添加附加操作(仅配置式模式)。
<script setup>
import { h } from "vue";
import { Button } from "@reglow/reui";
const extra = h(Button, { size: "sm" }, () => "额外操作");
</script>
<template>
<Tabs :tab-list="tabList" :tab-bar-extra-content="extra">
<div>内容</div>
</Tabs>
</template>
<template>
<Tabs :tab-list="tabList" size="small">小</Tabs>
<Tabs :tab-list="tabList" size="default">默认</Tabs>
<Tabs :tab-list="tabList" size="large">大</Tabs>
</template>
设置 closable 为 true(配置式)。
<script setup>
const tabList = [
{ itemKey: "1", tab: "标签1", closable: true },
{ itemKey: "2", tab: "标签2", closable: true },
];
</script>
<template>
<Tabs :tab-list="tabList" @close="(key) => console.log('关闭', key)" />
</template>
复合组件模式下,在 TabsTrigger 上设置 closable:
<TabsList>
<TabsTrigger value="1" closable>标签1</TabsTrigger>
</TabsList>
<script setup>
import { ref } from "vue";
const activeKey = ref("1");
const onChange = (key) => {
console.log("切换到:", key);
};
</script>
<template>
<Tabs v-model:active-key="activeKey" :tab-list="tabList" @change="onChange" />
</template>
设置 keepDOM 为 false 时只渲染当前面板(仅复合组件模式生效)。
<template>
<Tabs v-model:active-key="active" :keep-d-o-m="false" type="segmented">
<TabsList>
<TabsTrigger value="1">标签1</TabsTrigger>
<TabsTrigger value="2">标签2</TabsTrigger>
</TabsList>
<TabsContent value="1">内容1</TabsContent>
<TabsContent value="2">内容2</TabsContent>
</Tabs>
</template>
| 参数 | 说明 | 类型 | 默认值 |
|---|
| activeKey (v-model) | 当前激活的 key | string | - |
| defaultActiveKey | 默认激活的 key | string | 第一项 |
| type | 标签栏样式 | line | button | card | slash | editable-card | segmented | line |
| tabPosition | 标签栏位置 | top | left | right | bottom | top |
| tabList | 标签页列表(配置式) | TabItem[] | [] |
| keepDOM | 是否保持 DOM(复合组件模式) | boolean | false |
| size | 尺寸 | small | default | large | default |
| addable | 是否可新增标签(仅 editable-card) | boolean | true |
| editable | 是否可关闭标签(仅 editable-card) | boolean | true |
| tabBarExtraContent | 标签栏额外内容(仅配置式) | VNode | string | - |
| class | 附加类名 | string | - |
| style | 附加样式 | CSSProperties | - |
| 事件 | 说明 | 回调参数 |
|---|
| change | 激活项变化 | (key: string) |
| tabClick | 点击标签 | (key: string, e: MouseEvent) |
| close | 关闭标签 | (key: string) |
| add | 新增标签(editable-card) | () |
| 参数 | 说明 | 类型 | 默认值 |
|---|
| class | 附加类名 | string | - |
| 参数 | 说明 | 类型 | 默认值 |
|---|
| value | 标签页标识(必填) | string | - |
| disabled | 是否禁用 | boolean | false |
| closable | 是否可关闭 | boolean | false |
| class | 附加类名 | string | - |
| 参数 | 说明 | 类型 | 默认值 |
|---|
| value | 标签页标识(必填) | string | - |
| class | 附加类名 | string | - |
| 参数 | 说明 | 类型 | 默认值 |
|---|
| itemKey | 标签页标识(必填) | string | - |
| tab | 标签页标题 | string | VNode | - |
| disabled | 是否禁用 | boolean | false |
| icon | 图标 | VNode | - |
| closable | 是否可关闭 | boolean | false |
| type | 说明 |
|---|
line | 线条式,选中项有底部/侧边高亮线 |
button | 按钮式,选中项有背景色填充 |
card | 卡片式,选中项有边框连接 |
slash | 斜线式,标签倾斜显示 |
editable-card | 可编辑卡片式,默认可关闭和新增标签 |
segmented | 分段控制器式,选中项有滑块背景动画 |
底层基于 reka-ui,内置以下无障碍特性:
role="tablist" / role="tab" / role="tabpanel" ARIA 角色- 键盘导航:
←→↑↓ 切换标签,Home / End 跳转首尾 aria-selected / aria-controls / aria-labelledby 属性自动管理- 焦点管理:选中标签自动获得焦点