Tabs标签页
选项卡切换组件。
何时使用
提供平级的区域将大块内容进行收纳和展现,保持界面整洁。
Ant Design 依次提供了三级选项卡,分别用于不同的场景。
卡片式的页签,提供可关闭的样式,常用于容器顶部。
既可用于容器顶部,也可用于容器内部,是最通用的 Tabs。
Radio.Button 可作为更次级的页签来使用。
代码演示

默认选中第一项。
import { Tabs } from 'antd';const { TabPane } = Tabs;function callback(key) {console.log(key);}const Demo = () => (<Tabs defaultActiveKey="1" onChange={callback}><TabPane tab="Tab 1" key="1">Content of Tab Pane 1</TabPane><TabPane tab="Tab 2" key="2">Content of Tab Pane 2</TabPane><TabPane tab="Tab 3" key="3">Content of Tab Pane 3</TabPane></Tabs>);ReactDOM.render(<Demo />, mountNode);

禁用某一项。
import { Tabs } from 'antd';const { TabPane } = Tabs;ReactDOM.render(<Tabs defaultActiveKey="1"><TabPane tab="Tab 1" key="1">Tab 1</TabPane><TabPane tab="Tab 2" disabled key="2">Tab 2</TabPane><TabPane tab="Tab 3" key="3">Tab 3</TabPane></Tabs>,mountNode,);

标签居中展示。
import { Tabs } from 'antd';const { TabPane } = Tabs;const Demo = () => (<Tabs defaultActiveKey="1" centered><TabPane tab="Tab 1" key="1">Content of Tab Pane 1</TabPane><TabPane tab="Tab 2" key="2">Content of Tab Pane 2</TabPane><TabPane tab="Tab 3" key="3">Content of Tab Pane 3</TabPane></Tabs>);ReactDOM.render(<Demo />, mountNode);

有图标的标签。
import { Tabs } from 'antd';import { AppleOutlined, AndroidOutlined } from '@ant-design/icons';const { TabPane } = Tabs;ReactDOM.render(<Tabs defaultActiveKey="2"><TabPanetab={<span><AppleOutlined />Tab 1</span>}key="1">Tab 1</TabPane><TabPanetab={<span><AndroidOutlined />Tab 2</span>}key="2">Tab 2</TabPane></Tabs>,mountNode,);

可以左右、上下滑动,容纳更多标签。
import { Tabs, Radio } from 'antd';const { TabPane } = Tabs;class SlidingTabsDemo extends React.Component {constructor(props) {super(props);this.state = {mode: 'top',};}handleModeChange = e => {const mode = e.target.value;this.setState({ mode });};render() {const { mode } = this.state;return (<div><Radio.Group onChange={this.handleModeChange} value={mode} style={{ marginBottom: 8 }}><Radio.Button value="top">Horizontal</Radio.Button><Radio.Button value="left">Vertical</Radio.Button></Radio.Group><Tabs defaultActiveKey="1" tabPosition={mode} style={{ height: 220 }}>{[...Array.from({ length: 30 }, (v, i) => i)].map(i => (<TabPane tab={`Tab-${i}`} key={i} disabled={i === 28}>Content of tab {i}</TabPane>))}</Tabs></div>);}}ReactDOM.render(<SlidingTabsDemo />, mountNode);

可以在页签两边添加附加操作。
import { Tabs, Button, Divider, Checkbox } from 'antd';const { TabPane } = Tabs;const CheckboxGroup = Checkbox.Group;const operations = <Button>Extra Action</Button>;const OperationsSlot = {left: <Button className="tabs-extra-demo-button">Left Extra Action</Button>,right: <Button>Right Extra Action</Button>,};const options = ['left', 'right'];const Demo = () => {const [position, setPosition] = React.useState(['left', 'right']);const slot = React.useMemo(() => {if (position.length === 0) return null;return position.reduce((acc, direction) => ({ ...acc, [direction]: OperationsSlot[direction] }),{},);}, [position]);return (<><Tabs tabBarExtraContent={operations}><TabPane tab="Tab 1" key="1">Content of tab 1</TabPane><TabPane tab="Tab 2" key="2">Content of tab 2</TabPane><TabPane tab="Tab 3" key="3">Content of tab 3</TabPane></Tabs><br /><br /><br /><div>You can also specify its direction or both side</div><Divider /><CheckboxGroupoptions={options}value={position}onChange={value => {setPosition(value);}}/><br /><br /><Tabs tabBarExtraContent={slot}><TabPane tab="Tab 1" key="1">Content of tab 1</TabPane><TabPane tab="Tab 2" key="2">Content of tab 2</TabPane><TabPane tab="Tab 3" key="3">Content of tab 3</TabPane></Tabs></>);};ReactDOM.render(<Demo />, mountNode);
.tabs-extra-demo-button {margin-right: 16px;}.ant-row-rtl .tabs-extra-demo-button {margin-right: 0;margin-left: 16px;}

大号页签用在页头区域,小号用在弹出框等较狭窄的容器内。
import { Tabs, Radio } from 'antd';const { TabPane } = Tabs;class Demo extends React.Component {state = { size: 'small' };onChange = e => {this.setState({ size: e.target.value });};render() {const { size } = this.state;return (<div><Radio.Group value={size} onChange={this.onChange} style={{ marginBottom: 16 }}><Radio.Button value="small">Small</Radio.Button><Radio.Button value="default">Default</Radio.Button><Radio.Button value="large">Large</Radio.Button></Radio.Group><Tabs defaultActiveKey="1" size={size} style={{ marginBottom: 32 }}><TabPane tab="Tab 1" key="1">Content of tab 1</TabPane><TabPane tab="Tab 2" key="2">Content of tab 2</TabPane><TabPane tab="Tab 3" key="3">Content of tab 3</TabPane></Tabs><Tabs defaultActiveKey="1" type="card" size={size}><TabPane tab="Card Tab 1" key="1">Content of card tab 1</TabPane><TabPane tab="Card Tab 2" key="2">Content of card tab 2</TabPane><TabPane tab="Card Tab 3" key="3">Content of card tab 3</TabPane></Tabs></div>);}}ReactDOM.render(<Demo />, mountNode);

有四个位置,tabPosition="left|right|top|bottom"。在移动端下,left|right 会自动切换成 top。
import { Tabs, Radio, Space } from 'antd';const { TabPane } = Tabs;class Demo extends React.Component {state = {tabPosition: 'left',};changeTabPosition = e => {this.setState({ tabPosition: e.target.value });};render() {const { tabPosition } = this.state;return (<><Space style={{ marginBottom: 24 }}>Tab position:<Radio.Group value={tabPosition} onChange={this.changeTabPosition}><Radio.Button value="top">top</Radio.Button><Radio.Button value="bottom">bottom</Radio.Button><Radio.Button value="left">left</Radio.Button><Radio.Button value="right">right</Radio.Button></Radio.Group></Space><Tabs tabPosition={tabPosition}><TabPane tab="Tab 1" key="1">Content of Tab 1</TabPane><TabPane tab="Tab 2" key="2">Content of Tab 2</TabPane><TabPane tab="Tab 3" key="3">Content of Tab 3</TabPane></Tabs></>);}}ReactDOM.render(<Demo />, mountNode);

另一种样式的页签,不提供对应的垂直样式。
import { Tabs } from 'antd';const { TabPane } = Tabs;function callback(key) {console.log(key);}ReactDOM.render(<Tabs onChange={callback} type="card"><TabPane tab="Tab 1" key="1">Content of Tab Pane 1</TabPane><TabPane tab="Tab 2" key="2">Content of Tab Pane 2</TabPane><TabPane tab="Tab 3" key="3">Content of Tab Pane 3</TabPane></Tabs>,mountNode,);

只有卡片样式的页签支持新增和关闭选项。使用 closable={false} 禁止关闭。
import { Tabs } from 'antd';const { TabPane } = Tabs;const initialPanes = [{ title: 'Tab 1', content: 'Content of Tab 1', key: '1' },{ title: 'Tab 2', content: 'Content of Tab 2', key: '2' },{title: 'Tab 3',content: 'Content of Tab 3',key: '3',closable: false,},];class Demo extends React.Component {newTabIndex = 0;state = {activeKey: initialPanes[0].key,panes: initialPanes,};onChange = activeKey => {this.setState({ activeKey });};onEdit = (targetKey, action) => {this[action](targetKey);};add = () => {const { panes } = this.state;const activeKey = `newTab${this.newTabIndex++}`;const newPanes = [...panes];newPanes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey });this.setState({panes: newPanes,activeKey,});};remove = targetKey => {const { panes, activeKey } = this.state;let newActiveKey = activeKey;let lastIndex;panes.forEach((pane, i) => {if (pane.key === targetKey) {lastIndex = i - 1;}});const newPanes = panes.filter(pane => pane.key !== targetKey);if (newPanes.length && newActiveKey === targetKey) {if (lastIndex >= 0) {newActiveKey = newPanes[lastIndex].key;} else {newActiveKey = newPanes[0].key;}}this.setState({panes: newPanes,activeKey: newActiveKey,});};render() {const { panes, activeKey } = this.state;return (<Tabstype="editable-card"onChange={this.onChange}activeKey={activeKey}onEdit={this.onEdit}>{panes.map(pane => (<TabPane tab={pane.title} key={pane.key} closable={pane.closable}>{pane.content}</TabPane>))}</Tabs>);}}ReactDOM.render(<Demo />, mountNode);

用于容器顶部,需要一点额外的样式覆盖。
import { Tabs } from 'antd';const { TabPane } = Tabs;ReactDOM.render(<div className="card-container"><Tabs type="card"><TabPane tab="Tab Title 1" key="1"><p>Content of Tab Pane 1</p><p>Content of Tab Pane 1</p><p>Content of Tab Pane 1</p></TabPane><TabPane tab="Tab Title 2" key="2"><p>Content of Tab Pane 2</p><p>Content of Tab Pane 2</p><p>Content of Tab Pane 2</p></TabPane><TabPane tab="Tab Title 3" key="3"><p>Content of Tab Pane 3</p><p>Content of Tab Pane 3</p><p>Content of Tab Pane 3</p></TabPane></Tabs></div>,mountNode,);
.card-container p {margin: 0;}.card-container > .ant-tabs-card .ant-tabs-content {height: 120px;margin-top: -16px;}.card-container > .ant-tabs-card .ant-tabs-content > .ant-tabs-tabpane {padding: 16px;background: #fff;}.card-container > .ant-tabs-card > .ant-tabs-nav::before {display: none;}.card-container > .ant-tabs-card .ant-tabs-tab,[data-theme='compact'] .card-container > .ant-tabs-card .ant-tabs-tab {background: transparent;border-color: transparent;}.card-container > .ant-tabs-card .ant-tabs-tab-active,[data-theme='compact'] .card-container > .ant-tabs-card .ant-tabs-tab-active {background: #fff;border-color: #fff;}#components-tabs-demo-card-top .code-box-demo {padding: 24px;overflow: hidden;background: #f5f5f5;}[data-theme='compact'] .card-container > .ant-tabs-card .ant-tabs-content {height: 120px;margin-top: -8px;}[data-theme='dark'] .card-container > .ant-tabs-card .ant-tabs-tab {background: transparent;border-color: transparent;}[data-theme='dark'] #components-tabs-demo-card-top .code-box-demo {background: #000;}[data-theme='dark'] .card-container > .ant-tabs-card .ant-tabs-content > .ant-tabs-tabpane {background: #141414;}[data-theme='dark'] .card-container > .ant-tabs-card .ant-tabs-tab-active {background: #141414;border-color: #141414;}

隐藏默认的页签增加图标,给自定义触发器绑定事件。
import { Tabs, Button } from 'antd';const { TabPane } = Tabs;class Demo extends React.Component {constructor(props) {super(props);this.newTabIndex = 0;const panes = [{ title: 'Tab 1', content: 'Content of Tab Pane 1', key: '1' },{ title: 'Tab 2', content: 'Content of Tab Pane 2', key: '2' },];this.state = {activeKey: panes[0].key,panes,};}onChange = activeKey => {this.setState({ activeKey });};onEdit = (targetKey, action) => {this[action](targetKey);};add = () => {const { panes } = this.state;const activeKey = `newTab${this.newTabIndex++}`;panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey });this.setState({ panes, activeKey });};remove = targetKey => {let { activeKey } = this.state;let lastIndex;this.state.panes.forEach((pane, i) => {if (pane.key === targetKey) {lastIndex = i - 1;}});const panes = this.state.panes.filter(pane => pane.key !== targetKey);if (panes.length && activeKey === targetKey) {if (lastIndex >= 0) {activeKey = panes[lastIndex].key;} else {activeKey = panes[0].key;}}this.setState({ panes, activeKey });};render() {return (<div><div style={{ marginBottom: 16 }}><Button onClick={this.add}>ADD</Button></div><TabshideAddonChange={this.onChange}activeKey={this.state.activeKey}type="editable-card"onEdit={this.onEdit}>{this.state.panes.map(pane => (<TabPane tab={pane.title} key={pane.key}>{pane.content}</TabPane>))}</Tabs></div>);}}ReactDOM.render(<Demo />, mountNode);

使用 react-sticky 组件实现吸顶效果。
import { Tabs } from 'antd';import { StickyContainer, Sticky } from 'react-sticky';const { TabPane } = Tabs;const renderTabBar = (props, DefaultTabBar) => (<Sticky bottomOffset={80}>{({ style }) => (<DefaultTabBar {...props} className="site-custom-tab-bar" style={{ ...style }} />)}</Sticky>);ReactDOM.render(<StickyContainer><Tabs defaultActiveKey="1" renderTabBar={renderTabBar}><TabPane tab="Tab 1" key="1" style={{ height: 200 }}>Content of Tab Pane 1</TabPane><TabPane tab="Tab 2" key="2">Content of Tab Pane 2</TabPane><TabPane tab="Tab 3" key="3">Content of Tab Pane 3</TabPane></Tabs></StickyContainer>,mountNode,);
.site-custom-tab-bar {z-index: 1;background: #fff;}

使用 react-dnd 实现标签可拖拽。
import { Tabs } from 'antd';import { DndProvider, DragSource, DropTarget } from 'react-dnd';import { HTML5Backend } from 'react-dnd-html5-backend';const { TabPane } = Tabs;// Drag & Drop nodeclass TabNode extends React.Component {render() {const { connectDragSource, connectDropTarget, children } = this.props;return connectDragSource(connectDropTarget(children));}}const cardTarget = {drop(props, monitor) {const dragKey = monitor.getItem().index;const hoverKey = props.index;if (dragKey === hoverKey) {return;}props.moveTabNode(dragKey, hoverKey);monitor.getItem().index = hoverKey;},};const cardSource = {beginDrag(props) {return {id: props.id,index: props.index,};},};const WrapTabNode = DropTarget('DND_NODE', cardTarget, connect => ({connectDropTarget: connect.dropTarget(),}))(DragSource('DND_NODE', cardSource, (connect, monitor) => ({connectDragSource: connect.dragSource(),isDragging: monitor.isDragging(),}))(TabNode),);class DraggableTabs extends React.Component {state = {order: [],};moveTabNode = (dragKey, hoverKey) => {const newOrder = this.state.order.slice();const { children } = this.props;React.Children.forEach(children, c => {if (newOrder.indexOf(c.key) === -1) {newOrder.push(c.key);}});const dragIndex = newOrder.indexOf(dragKey);const hoverIndex = newOrder.indexOf(hoverKey);newOrder.splice(dragIndex, 1);newOrder.splice(hoverIndex, 0, dragKey);this.setState({order: newOrder,});};renderTabBar = (props, DefaultTabBar) => (<DefaultTabBar {...props}>{node => (<WrapTabNode key={node.key} index={node.key} moveTabNode={this.moveTabNode}>{node}</WrapTabNode>)}</DefaultTabBar>);render() {const { order } = this.state;const { children } = this.props;const tabs = [];React.Children.forEach(children, c => {tabs.push(c);});const orderTabs = tabs.slice().sort((a, b) => {const orderA = order.indexOf(a.key);const orderB = order.indexOf(b.key);if (orderA !== -1 && orderB !== -1) {return orderA - orderB;}if (orderA !== -1) {return -1;}if (orderB !== -1) {return 1;}const ia = tabs.indexOf(a);const ib = tabs.indexOf(b);return ia - ib;});return (<DndProvider backend={HTML5Backend}><Tabs renderTabBar={this.renderTabBar} {...this.props}>{orderTabs}</Tabs></DndProvider>);}}ReactDOM.render(<DraggableTabs><TabPane tab="tab 1" key="1">Content of Tab Pane 1</TabPane><TabPane tab="tab 2" key="2">Content of Tab Pane 2</TabPane><TabPane tab="tab 3" key="3">Content of Tab Pane 3</TabPane></DraggableTabs>,mountNode,);
API
Tabs
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| activeKey | 当前激活 tab 面板的 key | string | - | |
| addIcon | 自定义添加按钮 | ReactNode | - | 4.4.0 |
| animated | 是否使用动画切换 Tabs, 仅生效于 tabPosition=”top” | boolean | { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } | |
| centered | 标签居中展示 | boolean | false | 4.4.0 |
| defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | 第一个面板 | |
| hideAdd | 是否隐藏加号图标,在 type=”editable-card” 时有效 | boolean | false | |
| moreIcon | 自定义折叠 icon | ReactNode | <EllipsisOutlined /> | 4.14.0 |
| renderTabBar | 替换 TabBar,用于二次封装标签头 | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - | |
| size | 大小,提供 large default 和 small 三种大小 | string | default | |
| tabBarExtraContent | tab bar 上额外的元素 | ReactNode | {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 |
| tabBarGutter | tabs 之间的间隙 | number | - | |
| tabBarStyle | tab bar 的样式对象 | object | - | |
| tabPosition | 页签位置,可选值有 top right bottom left | string | top | |
| type | 页签的基本样式,可选 line、card editable-card 类型 | string | line | |
| onChange | 切换面板的回调 | function(activeKey) {} | - | |
| onEdit | 新增和删除页签的回调,在 type=”editable-card” 时有效 | (targetKey, action): void | - | |
| onTabClick | tab 被点击的回调 | function(key: string, event: MouseEvent) | - | |
| onTabScroll | tab 滚动时触发 | function({ direction: left | right | top | bottom }) | - | 4.3.0 |
Tabs.TabPane
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| closeIcon | 自定义关闭图标,在 type=”editable-card”时有效 | ReactNode | - |
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
| key | 对应 activeKey | string | - |
| tab | 选项卡头显示文字 | ReactNode | - |
当前内容版权归 Ant Design 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 Ant Design .