Tabs 标签页
三种视觉变体的 tabs,支持受控与非受控、items 数组式 API、对齐方式可控。
基础用法
items 数组定义 tab 列表,v-model 绑定当前激活值。默认 variant="line",激活项底部带 accent 色横线。
背景 视口
在「账户」页面,可以修改昵称、头像、邮箱。
<script setup lang="ts">
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const tab = ref('account');
const items = [
{ value: 'account', label: '账户' },
{ value: 'security', label: '安全' },
{ value: 'notifications', label: '通知' },
{ value: 'billing', label: '账单', disabled: true },
];
</script>
<template>
<CfTabs v-model="tab" :items="items">
<template #default="{ active }">
<div v-if="active === 'account'">在「账户」页面,可以修改昵称、头像、邮箱。</div>
<div v-if="active === 'security'">在「安全」页面,可以重置密码、启用两步验证。</div>
<div v-if="active === 'notifications'">在「通知」页面,可以选择哪些事件推送邮件。</div>
</template>
</CfTabs>
</template> <script setup>
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const tab = ref('account');
const items = [
{ value: 'account', label: '账户' },
{ value: 'security', label: '安全' },
{ value: 'notifications', label: '通知' },
{ value: 'billing', label: '账单', disabled: true },
];
</script>
<template>
<CfTabs v-model="tab" :items="items">
<template #default="{ active }">
<div v-if="active === 'account'">在「账户」页面,可以修改昵称、头像、邮箱。</div>
<div v-if="active === 'security'">在「安全」页面,可以重置密码、启用两步验证。</div>
<div v-if="active === 'notifications'">在「通知」页面,可以选择哪些事件推送邮件。</div>
</template>
</CfTabs>
</template> import { useState } from 'react';
import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const [tab, setTab] = useState('account');
const items = [
{ value: 'account', label: '账户' },
{ value: 'security', label: '安全' },
{ value: 'notifications', label: '通知' },
{ value: 'billing', label: '账单', disabled: true },
];
return (
<>
<CfTabs value={tab} onChange={setTab} items={items}>
<div v-if="active === 'account'">在「账户」页面,可以修改昵称、头像、邮箱。</div>
<div v-if="active === 'security'">在「安全」页面,可以重置密码、启用两步验证。</div>
<div v-if="active === 'notifications'">在「通知」页面,可以选择哪些事件推送邮件。</div>
</>
);
} import { useState } from 'react';
import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const [tab, setTab] = useState('account');
const items = [
{ value: 'account', label: '账户' },
{ value: 'security', label: '安全' },
{ value: 'notifications', label: '通知' },
{ value: 'billing', label: '账单', disabled: true },
];
return (
<>
<CfTabs value={tab} onChange={setTab} items={items}>
<div v-if="active === 'account'">在「账户」页面,可以修改昵称、头像、邮箱。</div>
<div v-if="active === 'security'">在「安全」页面,可以重置密码、启用两步验证。</div>
<div v-if="active === 'notifications'">在「通知」页面,可以选择哪些事件推送邮件。</div>
</>
);
} 视觉变体
line 是默认下划线样式;segmented 是按钮组式分段控件;pill 是胶囊形单选式。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const a = ref('one');
const b = ref('one');
const c = ref('one');
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
</script>
<template>
<div class="demo-stack">
<CfTabs v-model="a" :items="items" variant="line" />
<CfTabs v-model="b" :items="items" variant="segmented" />
<CfTabs v-model="c" :items="items" variant="pill" />
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const a = ref('one');
const b = ref('one');
const c = ref('one');
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
</script>
<template>
<div class="demo-stack">
<CfTabs v-model="a" :items="items" variant="line" />
<CfTabs v-model="b" :items="items" variant="segmented" />
<CfTabs v-model="c" :items="items" variant="pill" />
</div>
</template> import { useState } from 'react';
import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const [a, setA] = useState('one');
const [a, setA] = useState('one');
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
const [b, setB] = useState('one');
const [b, setB] = useState('one');
const [c, setC] = useState('one');
const [c, setC] = useState('one');
return (
<>
<CfTabs value={a} onChange={setA} items={items} variant="line" />
<CfTabs value={b} onChange={setB} items={items} variant="segmented" />
<CfTabs value={c} onChange={setC} items={items} variant="pill" />
</>
);
} import { useState } from 'react';
import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const [a, setA] = useState('one');
const [a, setA] = useState('one');
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
const [b, setB] = useState('one');
const [b, setB] = useState('one');
const [c, setC] = useState('one');
const [c, setC] = useState('one');
return (
<>
<CfTabs value={a} onChange={setA} items={items} variant="line" />
<CfTabs value={b} onChange={setB} items={items} variant="segmented" />
<CfTabs value={c} onChange={setC} items={items} variant="pill" />
</>
);
} 尺寸
3 档尺寸跟随密度系统 --control-h-sm/md/lg。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const sm = ref('one');
const md = ref('one');
const lg = ref('one');
const items = [
{ value: 'one', label: 'Tab 1' },
{ value: 'two', label: 'Tab 2' },
{ value: 'three', label: 'Tab 3' },
];
</script>
<template>
<div class="demo-stack">
<CfTabs v-model="sm" :items="items" size="sm" />
<CfTabs v-model="md" :items="items" size="md" />
<CfTabs v-model="lg" :items="items" size="lg" />
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const sm = ref('one');
const md = ref('one');
const lg = ref('one');
const items = [
{ value: 'one', label: 'Tab 1' },
{ value: 'two', label: 'Tab 2' },
{ value: 'three', label: 'Tab 3' },
];
</script>
<template>
<div class="demo-stack">
<CfTabs v-model="sm" :items="items" size="sm" />
<CfTabs v-model="md" :items="items" size="md" />
<CfTabs v-model="lg" :items="items" size="lg" />
</div>
</template> import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const items = [
{ value: 'one', label: 'Tab 1' },
{ value: 'two', label: 'Tab 2' },
{ value: 'three', label: 'Tab 3' },
];
const sm = 'one';
const md = 'one';
const lg = 'one';
return (
<>
<CfTabs items={items} size="sm" defaultValue="one" />
<CfTabs items={items} size="md" defaultValue="one" />
<CfTabs items={items} size="lg" defaultValue="one" />
</>
);
} import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const items = [
{ value: 'one', label: 'Tab 1' },
{ value: 'two', label: 'Tab 2' },
{ value: 'three', label: 'Tab 3' },
];
const sm = 'one';
const md = 'one';
const lg = 'one';
return (
<>
<CfTabs items={items} size="sm" defaultValue="one" />
<CfTabs items={items} size="md" defaultValue="one" />
<CfTabs items={items} size="lg" defaultValue="one" />
</>
);
} 对齐方式
align 控制 tab 列表的水平对齐:start 靠左、center 居中、end 靠右、stretch 等分撑满整行。
对齐方式不影响内容区,只影响 tab 头部。
背景 视口
align = start(默认)
align = center
align = end
align = stretch
<script setup lang="ts">
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
const a = ref<string>('one');
const b = ref<string>('one');
const c = ref<string>('one');
const d = ref<string>('one');
</script>
<template>
<div class="demo-stack">
<div>
<p class="demo-hint">align = start(默认)</p>
<CfTabs v-model="a" :items="items" align="start" />
</div>
<div>
<p class="demo-hint">align = center</p>
<CfTabs v-model="b" :items="items" align="center" />
</div>
<div>
<p class="demo-hint">align = end</p>
<CfTabs v-model="c" :items="items" align="end" />
</div>
<div>
<p class="demo-hint">align = stretch</p>
<CfTabs v-model="d" :items="items" align="stretch" />
</div>
</div>
</template>
<style scoped>
.demo-hint { margin: 0 0 4px; color: var(--fg-3); font-size: var(--t-12); }
</style> <script setup>
import { ref } from 'vue';
import { CfTabs } from '@chufix-design/vue';
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
const a = ref<string>('one');
const b = ref<string>('one');
const c = ref<string>('one');
const d = ref<string>('one');
</script>
<template>
<div class="demo-stack">
<div>
<p class="demo-hint">align = start(默认)</p>
<CfTabs v-model="a" :items="items" align="start" />
</div>
<div>
<p class="demo-hint">align = center</p>
<CfTabs v-model="b" :items="items" align="center" />
</div>
<div>
<p class="demo-hint">align = end</p>
<CfTabs v-model="c" :items="items" align="end" />
</div>
<div>
<p class="demo-hint">align = stretch</p>
<CfTabs v-model="d" :items="items" align="stretch" />
</div>
</div>
</template>
<style scoped>
.demo-hint { margin: 0 0 4px; color: var(--fg-3); font-size: var(--t-12); }
</style> import { useState } from 'react';
import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const [a, setA] = useState('one');
const [a, setA] = useState('one');
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
const [b, setB] = useState('one');
const [b, setB] = useState('one');
const [c, setC] = useState('one');
const [c, setC] = useState('one');
const [d, setD] = useState('one');
const [d, setD] = useState('one');
return (
<>
<CfTabs value={a} onChange={setA} items={items} align="start" />
<CfTabs value={b} onChange={setB} items={items} align="center" />
<CfTabs value={c} onChange={setC} items={items} align="end" />
<CfTabs value={d} onChange={setD} items={items} align="stretch" />
</>
);
} import { useState } from 'react';
import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const [a, setA] = useState('one');
const [a, setA] = useState('one');
const items = [
{ value: 'one', label: '一' },
{ value: 'two', label: '二' },
{ value: 'three', label: '三' },
];
const [b, setB] = useState('one');
const [b, setB] = useState('one');
const [c, setC] = useState('one');
const [c, setC] = useState('one');
const [d, setD] = useState('one');
const [d, setD] = useState('one');
return (
<>
<CfTabs value={a} onChange={setA} items={items} align="start" />
<CfTabs value={b} onChange={setB} items={items} align="center" />
<CfTabs value={c} onChange={setC} items={items} align="end" />
<CfTabs value={d} onChange={setD} items={items} align="stretch" />
</>
);
} 非受控模式
不传 v-model 时组件内部自管 active 状态,默认激活 items[0]。
适合 tab 仅做局部展示切换、外部不需要观察当前激活值的场景。
背景 视口
概览页面 —— 默认激活第一项
<script setup lang="ts">
import { CfTabs } from '@chufix-design/vue';
const items = [
{ value: 'overview', label: '概览' },
{ value: 'activity', label: '动态' },
{ value: 'settings', label: '设置' },
];
</script>
<template>
<CfTabs :items="items">
<template #default="{ active }">
<div v-if="active === 'overview'">概览页面 —— 默认激活第一项</div>
<div v-if="active === 'activity'">动态页面</div>
<div v-if="active === 'settings'">设置页面</div>
</template>
</CfTabs>
</template> <script setup>
import { CfTabs } from '@chufix-design/vue';
const items = [
{ value: 'overview', label: '概览' },
{ value: 'activity', label: '动态' },
{ value: 'settings', label: '设置' },
];
</script>
<template>
<CfTabs :items="items">
<template #default="{ active }">
<div v-if="active === 'overview'">概览页面 —— 默认激活第一项</div>
<div v-if="active === 'activity'">动态页面</div>
<div v-if="active === 'settings'">设置页面</div>
</template>
</CfTabs>
</template> import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const items = [
{ value: 'overview', label: '概览' },
{ value: 'activity', label: '动态' },
{ value: 'settings', label: '设置' },
];
return (
<>
<CfTabs items={items}>
<div v-if="active === 'overview'">概览页面 —— 默认激活第一项</div>
<div v-if="active === 'activity'">动态页面</div>
<div v-if="active === 'settings'">设置页面</div>
</>
);
} import { CfTabs } from '@chufix-design/react';
export default function Demo() {
const items = [
{ value: 'overview', label: '概览' },
{ value: 'activity', label: '动态' },
{ value: 'settings', label: '设置' },
];
return (
<>
<CfTabs items={items}>
<div v-if="active === 'overview'">概览页面 —— 默认激活第一项</div>
<div v-if="active === 'activity'">动态页面</div>
<div v-if="active === 'settings'">设置页面</div>
</>
);
} API
Props · Tabs
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue (Vue) / value (React) | string | — | 当前激活 tab 的 value(受控) |
defaultValue (React) | string | — | 初始激活 tab 的 value(非受控;Vue 直接不传 v-model 即可) |
items | TabsItem[] | [] | tab 列表,每项 {value, label, disabled?} |
variant | 'line' | 'segmented' | 'pill' | 'line' | 视觉变体 |
size | 'sm' | 'md' | 'lg' | 'md' | 高度与字号 |
align | 'start' | 'center' | 'end' | 'stretch' | 'start' | tab 列表对齐方式 |
Props · TabPanel
TabPanel 是子组件,当 items prop 未提供时,会从子节点的 props 中自动推断 tab 列表。
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | — | panel 标识,与父 Tabs 的 active 匹配 |
label | string | — | tab 头部显示文案;省略时回退到 value |
disabled | boolean | false | 禁用此 tab(保留视觉,禁止切换) |
Events
| Vue 事件 | React 回调 | 载荷类型 | 说明 |
|---|---|---|---|
update:modelValue | — | string | v-model 隐式监听 |
change | onChange | string | 切换 active tab 时触发,与 update:modelValue 同步 |
Slots / Children
| Slot | Props | 说明 |
|---|---|---|
default | { active: string } | tab 内容区;Vue 用 scoped slot,React 用 children: ({ active }) => ReactNode |
反馈与讨论
Tabs 标签页 的讨论