---
nav:
  title: 组件
  order: 1
group:
  title: Navigation
  order: 1
title: PisellSuperTabs
order: 1
category: pro
---

# PisellSuperTabs 组件

PisellSuperTabs 是一个通用导航框架封装的全能型 Tab 组件，用于构建多种导航模式（如菜单、标签页、导航栏等）。组件采用组合设计模式，通过封装 PisellScrollView、PisellBasicGrid 和 PisellCards，提供了强大的布局、滚动和渲染能力。

## 何时使用

- 需要构建顶部横向导航栏时
- 需要构建侧边竖向菜单时
- 需要构建底部图标导航时
- 需要构建多级嵌套导航时
- 需要高度自定义的 Tab 渲染时

## 代码演示

### 基础用法

最简单的用法，配合 `PisellCards.TextCard` 使用。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const tabs = [
    { key: 'home', text: '首页' },
    { key: 'products', text: '产品' },
    { key: 'about', text: '关于' },
  ];

  return (
    <PisellSuperTabs
      dataSource={tabs}
      defaultValue="home"
      rowKey="key"
      gridProps={{
        direction: 'horizontal',
        gap: 16,
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          {...item}
          text={item.dataSource.text}
          active={{ value: active }}
        />
      )}
    />
  );
};
```

### 受控模式

通过 `value` 和 `onChange` 控制激活状态。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';
import { useState } from 'react';

export default () => {
  const [activeKey, setActiveKey] = useState('home');

  const tabs = [
    { key: 'home', label: '首页' },
    { key: 'products', label: '产品' },
    { key: 'about', label: '关于' },
    { key: 'contact', label: '联系' },
  ];

  return (
    <div>
      <div>当前激活: {activeKey}</div>
      <PisellSuperTabs
        dataSource={tabs}
        value={activeKey}
        rowKey="key"
        onChange={(key) => setActiveKey(key)}
        gridProps={{
          direction: 'horizontal',
          gap: 12,
        }}
        renderItem={(item, index, active) => (
          <PisellCards.TextCard
            {...item}
            text={item.dataSource.label}
            active={{ value: active }}
          />
        )}
      />
    </div>
  );
};
```

### 横向滚动导航

配置横向滚动，适用于 Tab 较多的场景。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const tabs = Array.from({ length: 10 }, (_, i) => ({
    key: `tab${i + 1}`,
    label: `Tab ${i + 1}`,
  }));

  return (
    <PisellSuperTabs
      dataSource={tabs}
      defaultValue="tab1"
      gridProps={{
        direction: 'horizontal',
        gap: 16,
      }}
      rowKey="key"
      scrollViewProps={{
        overflow: 'x',
        scrollbarStyle: 'thin',
        scrollActionsConfig: {
          show: true,
          type: 'lrArrows',
        },
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          {...item}
          text={item.dataSource.label}
          active={{ value: active }}
        />
      )}
    />
  );
};
```

### 竖向菜单

配置竖向布局，适用于侧边菜单。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const menus = [
    { key: 'dashboard', label: '控制台', icon: '📊' },
    { key: 'products', label: '产品管理', icon: '📦' },
    { key: 'orders', label: '订单管理', icon: '🛒' },
    { key: 'users', label: '用户管理', icon: '👥' },
    { key: 'settings', label: '系统设置', icon: '⚙️' },
  ];

  return (
    <div style={{ width: '200px' }}>
      <PisellSuperTabs
        dataSource={menus}
        defaultValue="dashboard"
        rowKey="key"
        gridProps={{
          direction: 'vertical',
          gap: 8,
        }}
        scrollViewProps={{
          height: '400px',
          overflow: 'y',
        }}
        renderItem={(item, index, active) => (
          <PisellCards.TextCard
            {...item}
            text={item.dataSource.label}
            active={{ value: active }}
            direction="horizontal"
          />
        )}
      />
    </div>
  );
};
```

### 底部导航（等分布局）

使用等分布局，适用于移动端底部导航。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const bottomTabs = [
    { key: 'home', label: '首页', icon: '🏠' },
    { key: 'category', label: '分类', icon: '📑' },
    { key: 'cart', label: '购物车', icon: '🛒' },
    { key: 'mine', label: '我的', icon: '👤' },
  ];

  return (
    <PisellSuperTabs
      dataSource={bottomTabs}
      defaultValue="home"
      rowKey="key"
      gridProps={{
        direction: 'horizontal',
        equalWidth: true,
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          {...item}
          text={item.dataSource.label}
          active={{ value: active }}
          direction="vertical"
          size="large"
        />
      )}
    />
  );
};
```

### 网格布局

使用 `columns` 配置每行显示个数。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const tabs = Array.from({ length: 12 }, (_, i) => ({
    key: `tab${i + 1}`,
    label: `分类 ${i + 1}`,
  }));

  return (
    <PisellSuperTabs
      dataSource={tabs}
      defaultValue="tab1"
      rowKey="key"
      gridProps={{
        columns: 4,
        wrap: 'wrap',
        gap: 16,
      }}
      scrollViewProps={{
        height: '300px',
        overflow: 'y',
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          {...item}
          text={item.dataSource.label}
          active={{ value: active }}
        />
      )}
    />
  );
};
```

### 自定义渲染

完全自定义 TabItem 的渲染。

```tsx
import { PisellSuperTabs } from '@pisell/materials';

export default () => {
  const tabs = [
    { key: 'tab1', label: 'Tab 1', count: 10 },
    { key: 'tab2', label: 'Tab 2', count: 20 },
    { key: 'tab3', label: 'Tab 3', count: 30 },
  ];

  return (
    <PisellSuperTabs
      dataSource={tabs}
      defaultValue="tab1"
      rowKey="key"
      gridProps={{
        direction: 'horizontal',
        gap: 16,
      }}
      renderItem={(item, index, active) => (
        <div
          style={{
            padding: '12px 24px',
            background: active ? '#1890ff' : '#f0f0f0',
            color: active ? '#fff' : '#000',
            borderRadius: '8px',
            cursor: 'pointer',
            transition: 'all 0.3s',
          }}
        >
          <div>{item.label}</div>
          <div style={{ fontSize: '12px', marginTop: '4px' }}>
            数量: {item.count}
          </div>
        </div>
      )}
    />
  );
};
```

### 带图标和徽章

使用 `PisellCards.TextCard` 的完整功能。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';
import {
  HomeOutlined,
  ShoppingOutlined,
  UserOutlined,
} from '@ant-design/icons';

export default () => {
  const tabs = [
    {
      key: 'home',
      label: '首页',
      icon: <HomeOutlined />,
    },
    {
      key: 'products',
      label: '产品',
      icon: <ShoppingOutlined />,
      badge: { count: 5 },
    },
    {
      key: 'profile',
      label: '我的',
      icon: <UserOutlined />,
      badge: { dot: true },
    },
  ];

  return (
    <PisellSuperTabs
      dataSource={tabs}
      defaultValue="home"
      gridProps={{
        direction: 'horizontal',
        gap: 16,
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          {...item}
          text={item.dataSource.label}
          active={{ value: active }}
        />
      )}
    />
  );
};
```

### 自定义 rowKey

使用自定义的 key 字段。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const tabs = [
    { id: '1', name: 'Tab 1' },
    { id: '2', name: 'Tab 2' },
    { id: '3', name: 'Tab 3' },
  ];

  return (
    <PisellSuperTabs
      dataSource={tabs}
      rowKey="id"
      defaultValue="1"
      gridProps={{
        direction: 'horizontal',
        gap: 16,
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          tabKey={item.id}
          text={item.dataSource.name}
          active={{ value: active }}
        />
      )}
    />
  );
};
```

### rowKey 函数模式

使用函数自定义 key 的生成逻辑。

```tsx
import { PisellSuperTabs, PisellCards } from '@pisell/materials';

export default () => {
  const tabs = [
    { type: 'home', title: '首页' },
    { type: 'about', title: '关于' },
    { type: 'contact', title: '联系' },
  ];

  return (
    <PisellSuperTabs
      dataSource={tabs}
      rowKey={(record, index) => `${record.type}-${index}`}
      defaultValue="home-0"
      gridProps={{
        direction: 'horizontal',
        gap: 16,
      }}
      renderItem={(item, index, active) => (
        <PisellCards.TextCard
          tabKey={`${item.type}-${index}`}
          text={item.dataSource.title}
          active={{ value: active }}
        />
      )}
    />
  );
};
```

## API

### PisellSuperTabsProps

| 参数            | 说明                         | 类型                                                     | 默认值  |
| --------------- | ---------------------------- | -------------------------------------------------------- | ------- |
| className       | 自定义类名                   | `string`                                                 | -       |
| style           | 自定义样式                   | `CSSProperties`                                          | -       |
| dataSource      | 数据源                       | `T[]`                                                    | `[]`    |
| rowKey          | 数据项的唯一标识字段         | `string \| ((record: T, index: number) => string)`       | `'key'` |
| value           | 当前激活的 Tab key（受控）   | `string`                                                 | -       |
| defaultValue    | 默认激活的 Tab key（非受控） | `string`                                                 | -       |
| onChange        | Tab 切换时的回调             | `(key: string, item: T) => void`                         | -       |
| renderItem      | 自定义 TabItem 渲染函数      | `(item: T, index: number, active: boolean) => ReactNode` | -       |
| gridProps       | PisellBasicGrid 组件的配置   | `PisellBasicGridProps`                                   | `{}`    |
| scrollViewProps | PisellScrollView 组件的配置  | `PisellScrollViewProps`                                  | `{}`    |

### gridProps 配置

详见 [PisellBasicGrid 组件文档](../PisellBasicGrid/docs/PisellBasicGrid.md)

常用配置：

| 参数       | 说明               | 类型                                                                       | 默认值         |
| ---------- | ------------------ | -------------------------------------------------------------------------- | -------------- |
| direction  | 排列方向           | `'horizontal' \| 'vertical' \| 'horizontal-reverse' \| 'vertical-reverse'` | `'horizontal'` |
| wrap       | 换行控制           | `'nowrap' \| 'wrap' \| 'wrap-reverse'`                                     | `'nowrap'`     |
| columns    | 一行显示的元素个数 | `number`                                                                   | -              |
| equalWidth | 是否等分容器宽度   | `boolean`                                                                  | `false`        |
| gap        | 间距               | `number \| string`                                                         | -              |
| justify    | 主轴对齐方式       | `'start' \| 'end' \| 'center' \| 'between' \| 'around' \| 'evenly'`        | `'start'`      |
| align      | 交叉轴对齐方式     | `'start' \| 'end' \| 'center' \| 'stretch' \| 'baseline'`                  | `'stretch'`    |

### scrollViewProps 配置

详见 [PisellScrollView 组件文档](../PisellScrollView/docs/PisellScrollView.md)

常用配置：

| 参数                | 说明         | 类型                                                                   | 默认值      |
| ------------------- | ------------ | ---------------------------------------------------------------------- | ----------- |
| overflow            | 滚动方向     | `'auto' \| 'scroll' \| 'hidden' \| 'x' \| 'y' \| 'both'`               | `'auto'`    |
| scrollbarStyle      | 滚动条样式   | `'default' \| 'thin' \| 'minimal' \| 'rounded' \| 'overlay' \| 'none'` | `'default'` |
| scrollActionsConfig | 滚动按钮配置 | `object`                                                               | -           |
| snapScroll          | 吸附滚动配置 | `object`                                                               | -           |
| width               | 容器宽度     | `number \| string`                                                     | -           |
| height              | 容器高度     | `number \| string`                                                     | -           |

### TabDataItem

建议的数据源结构：

```typescript
interface TabDataItem {
  key: string; // 唯一标识（必填）
  label?: string; // 标签文本
  icon?: ReactNode; // 图标
  badge?: {
    // 徽章配置
    count?: number;
    dot?: boolean;
    text?: string;
  };
  children?: TabDataItem[]; // 子级数据（多级嵌套）
  disabled?: boolean; // 是否禁用
  [key: string]: any; // 其他自定义字段
}
```

## 注意事项

1. **多级嵌套**：SuperTabs 只负责渲染第一级数据，`children` 的渲染由 Card 组件自己实现
2. **Props 分组**：`gridProps` 和 `scrollViewProps` 都是对象形式，分别透传给对应的底层组件
3. **Props 优先级**：
   - `style` 作用于外层容器
   - `gridProps.style` 作用于 Grid 组件
   - `scrollViewProps.style` 作用于 ScrollView 组件
4. **受控模式**：当传入 `value` 时，组件为受控模式，必须配合 `onChange` 使用
5. **rowKey 必填**：dataSource 中的每一项必须有唯一的 key 字段（默认为 'key'）

## 最佳实践

### 1. 选择合适的布局模式

- **横向导航**：使用 `direction: 'horizontal'` + `overflow: 'x'`
- **竖向菜单**：使用 `direction: 'vertical'` + `overflow: 'y'` + `height`
- **网格布局**：使用 `columns` + `wrap: 'wrap'`
- **等分布局**：使用 `equalWidth: true`

### 2. 配合 PisellCards 使用

优先使用 `PisellCards.TextCard`、`PisellCards.MenuCard` 等现有卡片组件，避免重复开发。

### 3. 性能优化

- 大数据量时，考虑使用虚拟滚动（后续版本支持）
- 使用 `React.memo` 优化 renderItem 中的组件
- 避免在 renderItem 中创建新的对象或函数

### 4. 可访问性

- 为 TabItem 添加合适的 ARIA 属性（预留）
- 支持键盘导航（预留）

## FAQ

### 如何实现多级嵌套导航？

多级嵌套的渲染逻辑由 Card 组件自己实现，SuperTabs 只负责渲染第一级数据。

```
const tabs = [
  {
    key: 'products',
    label: '产品',
    children: [
      { key: 'product-1', label: '产品 1' },
      { key: 'product-2', label: '产品 2' },
    ],
  },
];

// Card 组件内部处理 children 的渲染
<PisellSuperTabs
  dataSource={tabs}
  renderItem={(item) => <CustomCard {...item} />}
/>;
```

### 如何实现吸附滚动？

通过 `scrollViewProps.snapScroll` 配置：

```
<PisellSuperTabs
  scrollViewProps={{
    snapScroll: {
      enabled: true,
      type: 'mandatory',
      align: 'start',
    },
  }}
/>
```

### 如何自定义激活状态的样式？

在 renderItem 中根据 `active` 参数自定义样式：

```
<PisellSuperTabs
  renderItem={(item, index, active) => (
    <div
      style={{
        background: active ? '#1890ff' : '#fff',
        color: active ? '#fff' : '#000',
      }}
    >
      {item.label}
    </div>
  )}
/>
```
