• Tabs标签页
    • 何时使用
    • 代码演示
    • API
      • Tabs
      • Tabs.TabPane

    Tabs标签页

    选项卡切换组件。

    何时使用

    提供平级的区域将大块内容进行收纳和展现,保持界面整洁。

    Ant Design 依次提供了三级选项卡,分别用于不同的场景。

    • 卡片式的页签,提供可关闭的样式,常用于容器顶部。

    • 标准线条式页签,用于容器内部的主功能切换,这是最常用的 Tabs。

    • RadioButton 可作为更次级的页签来使用。

    代码演示

    Tabs 标签页 - 图1

    基本

    默认选中第一项。

    1. import { Tabs } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. function callback(key) {
    4. console.log(key);
    5. }
    6. ReactDOM.render(
    7. <Tabs defaultActiveKey="1" onChange={callback}>
    8. <TabPane tab="Tab 1" key="1">
    9. Content of Tab Pane 1
    10. </TabPane>
    11. <TabPane tab="Tab 2" key="2">
    12. Content of Tab Pane 2
    13. </TabPane>
    14. <TabPane tab="Tab 3" key="3">
    15. Content of Tab Pane 3
    16. </TabPane>
    17. </Tabs>,
    18. mountNode,
    19. );

    Tabs 标签页 - 图2

    禁用

    禁用某一项。

    1. import { Tabs } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. ReactDOM.render(
    4. <Tabs defaultActiveKey="1">
    5. <TabPane tab="Tab 1" key="1">
    6. Tab 1
    7. </TabPane>
    8. <TabPane tab="Tab 2" disabled key="2">
    9. Tab 2
    10. </TabPane>
    11. <TabPane tab="Tab 3" key="3">
    12. Tab 3
    13. </TabPane>
    14. </Tabs>,
    15. mountNode,
    16. );

    Tabs 标签页 - 图3

    图标

    有图标的标签。

    1. import { Tabs, Icon } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. ReactDOM.render(
    4. <Tabs defaultActiveKey="2">
    5. <TabPane
    6. tab={
    7. <span>
    8. <Icon type="apple" />
    9. Tab 1
    10. </span>
    11. }
    12. key="1"
    13. >
    14. Tab 1
    15. </TabPane>
    16. <TabPane
    17. tab={
    18. <span>
    19. <Icon type="android" />
    20. Tab 2
    21. </span>
    22. }
    23. key="2"
    24. >
    25. Tab 2
    26. </TabPane>
    27. </Tabs>,
    28. mountNode,
    29. );

    Tabs 标签页 - 图4

    滑动

    可以左右、上下滑动,容纳更多标签。

    1. import { Tabs, Radio } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. class SlidingTabsDemo extends React.Component {
    4. constructor(props) {
    5. super(props);
    6. this.state = {
    7. mode: 'top',
    8. };
    9. }
    10. handleModeChange = e => {
    11. const mode = e.target.value;
    12. this.setState({ mode });
    13. };
    14. render() {
    15. const { mode } = this.state;
    16. return (
    17. <div>
    18. <Radio.Group onChange={this.handleModeChange} value={mode} style={{ marginBottom: 8 }}>
    19. <Radio.Button value="top">Horizontal</Radio.Button>
    20. <Radio.Button value="left">Vertical</Radio.Button>
    21. </Radio.Group>
    22. <Tabs defaultActiveKey="1" tabPosition={mode} style={{ height: 220 }}>
    23. <TabPane tab="Tab 1" key="1">
    24. Content of tab 1
    25. </TabPane>
    26. <TabPane tab="Tab 2" key="2">
    27. Content of tab 2
    28. </TabPane>
    29. <TabPane tab="Tab 3" key="3">
    30. Content of tab 3
    31. </TabPane>
    32. <TabPane tab="Tab 4" key="4">
    33. Content of tab 4
    34. </TabPane>
    35. <TabPane tab="Tab 5" key="5">
    36. Content of tab 5
    37. </TabPane>
    38. <TabPane tab="Tab 6" key="6">
    39. Content of tab 6
    40. </TabPane>
    41. <TabPane tab="Tab 7" key="7">
    42. Content of tab 7
    43. </TabPane>
    44. <TabPane tab="Tab 8" key="8">
    45. Content of tab 8
    46. </TabPane>
    47. <TabPane tab="Tab 9" key="9">
    48. Content of tab 9
    49. </TabPane>
    50. <TabPane tab="Tab 10" key="10">
    51. Content of tab 10
    52. </TabPane>
    53. <TabPane tab="Tab 11" key="11">
    54. Content of tab 11
    55. </TabPane>
    56. </Tabs>
    57. </div>
    58. );
    59. }
    60. }
    61. ReactDOM.render(<SlidingTabsDemo />, mountNode);

    Tabs 标签页 - 图5

    附加内容

    可以在页签右边添加附加操作。

    1. import { Tabs, Button } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. const operations = <Button>Extra Action</Button>;
    4. ReactDOM.render(
    5. <Tabs tabBarExtraContent={operations}>
    6. <TabPane tab="Tab 1" key="1">
    7. Content of tab 1
    8. </TabPane>
    9. <TabPane tab="Tab 2" key="2">
    10. Content of tab 2
    11. </TabPane>
    12. <TabPane tab="Tab 3" key="3">
    13. Content of tab 3
    14. </TabPane>
    15. </Tabs>,
    16. mountNode,
    17. );

    Tabs 标签页 - 图6

    大小

    大号页签用在页头区域,小号用在弹出框等较狭窄的容器内。

    1. import { Tabs, Radio } from 'antd';
    2. const { TabPane } = Tabs;
    3. class Demo extends React.Component {
    4. state = { size: 'small' };
    5. onChange = e => {
    6. this.setState({ size: e.target.value });
    7. };
    8. render() {
    9. const { size } = this.state;
    10. return (
    11. <div>
    12. <Radio.Group value={size} onChange={this.onChange} style={{ marginBottom: 16 }}>
    13. <Radio.Button value="small">Small</Radio.Button>
    14. <Radio.Button value="default">Default</Radio.Button>
    15. <Radio.Button value="large">Large</Radio.Button>
    16. </Radio.Group>
    17. <Tabs defaultActiveKey="1" size={size}>
    18. <TabPane tab="Tab 1" key="1">
    19. Content of tab 1
    20. </TabPane>
    21. <TabPane tab="Tab 2" key="2">
    22. Content of tab 2
    23. </TabPane>
    24. <TabPane tab="Tab 3" key="3">
    25. Content of tab 3
    26. </TabPane>
    27. </Tabs>
    28. </div>
    29. );
    30. }
    31. }
    32. ReactDOM.render(<Demo />, mountNode);

    Tabs 标签页 - 图7

    位置

    有四个位置,tabPosition="left|right|top|bottom"

    1. import { Tabs, Select } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. const Option = Select.Option;
    4. class Demo extends React.Component {
    5. state = {
    6. tabPosition: 'top',
    7. };
    8. changeTabPosition = tabPosition => {
    9. this.setState({ tabPosition });
    10. };
    11. render() {
    12. return (
    13. <div>
    14. <div style={{ marginBottom: 16 }}>
    15. Tab position
    16. <Select
    17. value={this.state.tabPosition}
    18. onChange={this.changeTabPosition}
    19. dropdownMatchSelectWidth={false}
    20. >
    21. <Option value="top">top</Option>
    22. <Option value="bottom">bottom</Option>
    23. <Option value="left">left</Option>
    24. <Option value="right">right</Option>
    25. </Select>
    26. </div>
    27. <Tabs tabPosition={this.state.tabPosition}>
    28. <TabPane tab="Tab 1" key="1">
    29. Content of Tab 1
    30. </TabPane>
    31. <TabPane tab="Tab 2" key="2">
    32. Content of Tab 2
    33. </TabPane>
    34. <TabPane tab="Tab 3" key="3">
    35. Content of Tab 3
    36. </TabPane>
    37. </Tabs>
    38. </div>
    39. );
    40. }
    41. }
    42. ReactDOM.render(<Demo />, mountNode);

    Tabs 标签页 - 图8

    卡片式页签

    另一种样式的页签,不提供对应的垂直样式。

    1. import { Tabs } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. function callback(key) {
    4. console.log(key);
    5. }
    6. ReactDOM.render(
    7. <Tabs onChange={callback} type="card">
    8. <TabPane tab="Tab 1" key="1">
    9. Content of Tab Pane 1
    10. </TabPane>
    11. <TabPane tab="Tab 2" key="2">
    12. Content of Tab Pane 2
    13. </TabPane>
    14. <TabPane tab="Tab 3" key="3">
    15. Content of Tab Pane 3
    16. </TabPane>
    17. </Tabs>,
    18. mountNode,
    19. );

    Tabs 标签页 - 图9

    新增和关闭页签

    只有卡片样式的页签支持新增和关闭选项。使用 closable={false} 禁止关闭。

    1. import { Tabs } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. class Demo extends React.Component {
    4. constructor(props) {
    5. super(props);
    6. this.newTabIndex = 0;
    7. const panes = [
    8. { title: 'Tab 1', content: 'Content of Tab 1', key: '1' },
    9. { title: 'Tab 2', content: 'Content of Tab 2', key: '2' },
    10. {
    11. title: 'Tab 3',
    12. content: 'Content of Tab 3',
    13. key: '3',
    14. closable: false,
    15. },
    16. ];
    17. this.state = {
    18. activeKey: panes[0].key,
    19. panes,
    20. };
    21. }
    22. onChange = activeKey => {
    23. this.setState({ activeKey });
    24. };
    25. onEdit = (targetKey, action) => {
    26. this[action](targetKey);
    27. };
    28. add = () => {
    29. const panes = this.state.panes;
    30. const activeKey = `newTab${this.newTabIndex++}`;
    31. panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey });
    32. this.setState({ panes, activeKey });
    33. };
    34. remove = targetKey => {
    35. let activeKey = this.state.activeKey;
    36. let lastIndex;
    37. this.state.panes.forEach((pane, i) => {
    38. if (pane.key === targetKey) {
    39. lastIndex = i - 1;
    40. }
    41. });
    42. const panes = this.state.panes.filter(pane => pane.key !== targetKey);
    43. if (panes.length && activeKey === targetKey) {
    44. if (lastIndex >= 0) {
    45. activeKey = panes[lastIndex].key;
    46. } else {
    47. activeKey = panes[0].key;
    48. }
    49. }
    50. this.setState({ panes, activeKey });
    51. };
    52. render() {
    53. return (
    54. <Tabs
    55. onChange={this.onChange}
    56. activeKey={this.state.activeKey}
    57. type="editable-card"
    58. onEdit={this.onEdit}
    59. >
    60. {this.state.panes.map(pane => (
    61. <TabPane tab={pane.title} key={pane.key} closable={pane.closable}>
    62. {pane.content}
    63. </TabPane>
    64. ))}
    65. </Tabs>
    66. );
    67. }
    68. }
    69. ReactDOM.render(<Demo />, mountNode);

    Tabs 标签页 - 图10

    卡片式页签容器

    用于容器顶部,需要一点额外的样式覆盖。

    1. import { Tabs } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. ReactDOM.render(
    4. <div className="card-container">
    5. <Tabs type="card">
    6. <TabPane tab="Tab Title 1" key="1">
    7. <p>Content of Tab Pane 1</p>
    8. <p>Content of Tab Pane 1</p>
    9. <p>Content of Tab Pane 1</p>
    10. </TabPane>
    11. <TabPane tab="Tab Title 2" key="2">
    12. <p>Content of Tab Pane 2</p>
    13. <p>Content of Tab Pane 2</p>
    14. <p>Content of Tab Pane 2</p>
    15. </TabPane>
    16. <TabPane tab="Tab Title 3" key="3">
    17. <p>Content of Tab Pane 3</p>
    18. <p>Content of Tab Pane 3</p>
    19. <p>Content of Tab Pane 3</p>
    20. </TabPane>
    21. </Tabs>
    22. </div>,
    23. mountNode,
    24. );
    1. .card-container > .ant-tabs-card > .ant-tabs-content {
    2. height: 120px;
    3. margin-top: -16px;
    4. }
    5. .card-container > .ant-tabs-card > .ant-tabs-content > .ant-tabs-tabpane {
    6. background: #fff;
    7. padding: 16px;
    8. }
    9. .card-container > .ant-tabs-card > .ant-tabs-bar {
    10. border-color: #fff;
    11. }
    12. .card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab {
    13. border-color: transparent;
    14. background: transparent;
    15. }
    16. .card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active {
    17. border-color: #fff;
    18. background: #fff;
    19. }

    Tabs 标签页 - 图11

    自定义新增页签触发器

    隐藏默认的页签增加图标,给自定义触发器绑定事件。

    1. import { Tabs, Button } from 'antd';
    2. const TabPane = Tabs.TabPane;
    3. class Demo extends React.Component {
    4. constructor(props) {
    5. super(props);
    6. this.newTabIndex = 0;
    7. const panes = [
    8. { title: 'Tab 1', content: 'Content of Tab Pane 1', key: '1' },
    9. { title: 'Tab 2', content: 'Content of Tab Pane 2', key: '2' },
    10. ];
    11. this.state = {
    12. activeKey: panes[0].key,
    13. panes,
    14. };
    15. }
    16. onChange = activeKey => {
    17. this.setState({ activeKey });
    18. };
    19. onEdit = (targetKey, action) => {
    20. this[action](targetKey);
    21. };
    22. add = () => {
    23. const panes = this.state.panes;
    24. const activeKey = `newTab${this.newTabIndex++}`;
    25. panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey });
    26. this.setState({ panes, activeKey });
    27. };
    28. remove = targetKey => {
    29. let activeKey = this.state.activeKey;
    30. let lastIndex;
    31. this.state.panes.forEach((pane, i) => {
    32. if (pane.key === targetKey) {
    33. lastIndex = i - 1;
    34. }
    35. });
    36. const panes = this.state.panes.filter(pane => pane.key !== targetKey);
    37. if (panes.length && activeKey === targetKey) {
    38. if (lastIndex >= 0) {
    39. activeKey = panes[lastIndex].key;
    40. } else {
    41. activeKey = panes[0].key;
    42. }
    43. }
    44. this.setState({ panes, activeKey });
    45. };
    46. render() {
    47. return (
    48. <div>
    49. <div style={{ marginBottom: 16 }}>
    50. <Button onClick={this.add}>ADD</Button>
    51. </div>
    52. <Tabs
    53. hideAdd
    54. onChange={this.onChange}
    55. activeKey={this.state.activeKey}
    56. type="editable-card"
    57. onEdit={this.onEdit}
    58. >
    59. {this.state.panes.map(pane => (
    60. <TabPane tab={pane.title} key={pane.key}>
    61. {pane.content}
    62. </TabPane>
    63. ))}
    64. </Tabs>
    65. </div>
    66. );
    67. }
    68. }
    69. ReactDOM.render(<Demo />, mountNode);

    Tabs 标签页 - 图12

    自定义页签头

    使用 react-sticky 组件实现吸顶效果。

    1. import { Tabs } from 'antd';
    2. import { StickyContainer, Sticky } from 'react-sticky';
    3. const TabPane = Tabs.TabPane;
    4. const renderTabBar = (props, DefaultTabBar) => (
    5. <Sticky bottomOffset={80}>
    6. {({ style }) => (
    7. <DefaultTabBar {...props} style={{ ...style, zIndex: 1, background: '#fff' }} />
    8. )}
    9. </Sticky>
    10. );
    11. ReactDOM.render(
    12. <StickyContainer>
    13. <Tabs defaultActiveKey="1" renderTabBar={renderTabBar}>
    14. <TabPane tab="Tab 1" key="1" style={{ height: 200 }}>
    15. Content of Tab Pane 1
    16. </TabPane>
    17. <TabPane tab="Tab 2" key="2">
    18. Content of Tab Pane 2
    19. </TabPane>
    20. <TabPane tab="Tab 3" key="3">
    21. Content of Tab Pane 3
    22. </TabPane>
    23. </Tabs>
    24. </StickyContainer>,
    25. mountNode,
    26. );

    Tabs 标签页 - 图13

    可拖拽标签

    使用 react-dnd 实现标签可拖拽。

    1. import { Tabs } from 'antd';
    2. import { DragDropContextProvider, DragSource, DropTarget } from 'react-dnd';
    3. import HTML5Backend from 'react-dnd-html5-backend';
    4. const TabPane = Tabs.TabPane;
    5. // Drag & Drop node
    6. class TabNode extends React.Component {
    7. render() {
    8. const { connectDragSource, connectDropTarget, children } = this.props;
    9. return connectDragSource(connectDropTarget(children));
    10. }
    11. }
    12. const cardTarget = {
    13. drop(props, monitor) {
    14. const dragKey = monitor.getItem().index;
    15. const hoverKey = props.index;
    16. if (dragKey === hoverKey) {
    17. return;
    18. }
    19. props.moveTabNode(dragKey, hoverKey);
    20. monitor.getItem().index = hoverKey;
    21. },
    22. };
    23. const cardSource = {
    24. beginDrag(props) {
    25. return {
    26. id: props.id,
    27. index: props.index,
    28. };
    29. },
    30. };
    31. const WrapTabNode = DropTarget('DND_NODE', cardTarget, connect => ({
    32. connectDropTarget: connect.dropTarget(),
    33. }))(
    34. DragSource('DND_NODE', cardSource, (connect, monitor) => ({
    35. connectDragSource: connect.dragSource(),
    36. isDragging: monitor.isDragging(),
    37. }))(TabNode),
    38. );
    39. class DraggableTabs extends React.Component {
    40. state = {
    41. order: [],
    42. };
    43. moveTabNode = (dragKey, hoverKey) => {
    44. const newOrder = this.state.order.slice();
    45. const { children } = this.props;
    46. React.Children.forEach(children, c => {
    47. if (newOrder.indexOf(c.key) === -1) {
    48. newOrder.push(c.key);
    49. }
    50. });
    51. const dragIndex = newOrder.indexOf(dragKey);
    52. const hoverIndex = newOrder.indexOf(hoverKey);
    53. newOrder.splice(dragIndex, 1);
    54. newOrder.splice(hoverIndex, 0, dragKey);
    55. this.setState({
    56. order: newOrder,
    57. });
    58. };
    59. renderTabBar = (props, DefaultTabBar) => (
    60. <DefaultTabBar {...props}>
    61. {node => (
    62. <WrapTabNode key={node.key} index={node.key} moveTabNode={this.moveTabNode}>
    63. {node}
    64. </WrapTabNode>
    65. )}
    66. </DefaultTabBar>
    67. );
    68. render() {
    69. const { order } = this.state;
    70. const { children } = this.props;
    71. const tabs = [];
    72. React.Children.forEach(children, c => {
    73. tabs.push(c);
    74. });
    75. const orderTabs = tabs.slice().sort((a, b) => {
    76. const orderA = order.indexOf(a.key);
    77. const orderB = order.indexOf(b.key);
    78. if (orderA !== -1 && orderB !== -1) {
    79. return orderA - orderB;
    80. }
    81. if (orderA !== -1) {
    82. return -1;
    83. }
    84. if (orderB !== -1) {
    85. return 1;
    86. }
    87. const ia = tabs.indexOf(a);
    88. const ib = tabs.indexOf(b);
    89. return ia - ib;
    90. });
    91. return (
    92. <DragDropContextProvider backend={HTML5Backend}>
    93. <Tabs renderTabBar={this.renderTabBar} {...this.props}>
    94. {orderTabs}
    95. </Tabs>
    96. </DragDropContextProvider>
    97. );
    98. }
    99. }
    100. ReactDOM.render(
    101. <DraggableTabs>
    102. <TabPane tab="tab 1" key="1">
    103. Content of Tab Pane 1
    104. </TabPane>
    105. <TabPane tab="tab 2" key="2">
    106. Content of Tab Pane 2
    107. </TabPane>
    108. <TabPane tab="tab 3" key="3">
    109. Content of Tab Pane 3
    110. </TabPane>
    111. </DraggableTabs>,
    112. mountNode,
    113. );

    API

    Tabs

    参数说明类型默认值
    activeKey当前激活 tab 面板的 keystring
    animated是否使用动画切换 Tabs,在 tabPosition=top|bottom 时有效boolean | {inkBar:boolean, tabPane:boolean}true, 当 type="card" 时为 false
    renderTabBar替换 TabBar,用于二次封装标签头(props: DefaultTabBarProps, DefaultTabBar: React.ReactNode) => React.ReactNode
    defaultActiveKey初始化选中面板的 key,如果没有设置 activeKeystring第一个面板
    hideAdd是否隐藏加号图标,在 type="editable-card" 时有效booleanfalse
    size大小,提供 large defaultsmall 三种大小string'default'
    tabBarExtraContenttab bar 上额外的元素React.ReactNode
    tabBarGuttertabs 之间的间隙number
    tabBarStyletab bar 的样式对象object-
    tabPosition页签位置,可选值有 top right bottom leftstring'top'
    type页签的基本样式,可选 linecard editable-card 类型string'line'
    onChange切换面板的回调Function(activeKey) {}
    onEdit新增和删除页签的回调,在 type="editable-card" 时有效(targetKey, action): void
    onNextClicknext 按钮被点击的回调Function
    onPrevClickprev 按钮被点击的回调Function
    onTabClicktab 被点击的回调Function

    Tabs.TabPane

    参数说明类型默认值
    forceRender被隐藏时是否渲染 DOM 结构booleanfalse
    key对应 activeKeystring
    tab选项卡头显示文字string|ReactNode