---
title: 开发文档
order: 3
category: pro
---

# PisellBatchActionBar 开发文档

## 1. 组件基本信息

### 1.1 组件名称

- **中文名称**：批量操作栏
- **英文名称**：PisellBatchActionBar
- **组件编号**：pcp_da_batch_action_bar_V1.0

### 1.2 组件版本

- **当前版本**：1.0.0
- **更新日期**：2025-01-XX
- **版本说明**：首次发布

### 1.3 组件分类

- **类型**：Pro Components
- **分类**：Data Action（数据操作）
- **依赖**：PisellBasicGrid、GraphicTextCard、MultilevelCard、Ant Design Popconfirm、Badge

## 2. 组件描述

PisellBatchActionBar 是一个"选择感知型"的批量操作组件，用于承载基于选中对象的批量行为操作。该组件本身不负责数据选择，而是通过接收来自
Table / List / Card / Tree 等组件的 selection 状态，对外提供统一、可配置、可扩展的批量操作能力，支持在表格内、表格外、吸底、浮层等多种形态下复用。

### 2.1 核心能力

1. **选择感知能力**
  - 感知是否有选中项
  - 感知选中数量
  - 感知全选/半选状态
  - 感知选择范围（当前页/全部）

2. **批量操作编排能力**
  - 多操作并列展示
  - 主操作/次操作/更多操作分级
  - 操作顺序可配置
  - 基于 selection/权限的可见与禁用规则
  - 独立确认弹窗

3. **状态管理能力**
  - 无选中项状态
  - 存在选中项状态
  - 批量操作执行中（loading）
  - 无权限或不满足条件（disabled）
  - 操作项过多，折叠至更多

4. **布局能力**
  - 支持吸顶/吸底/悬浮/内嵌多种布局模式
  - 响应式适配
  - 操作按钮溢出自动折叠

### 2.2 适用场景

- **表格批量操作**：订单、用户、商品管理
- **列表/卡片批量操作**：内容、素材、SKU
- **管理后台**：商家/员工/角色批量操作
- **终端/Kiosk**：批量确认、提交

### 2.3 设计目的

1. **解耦选择与操作**：选择由上游组件负责，操作由 BatchActionBar 统一承载
2. **统一批量操作体验**：不同页面、不同数据载体的批量操作行为保持一致
3. **提高复用性**：一次定义，可用于表格、列表、卡片、树等多种容器
4. **支撑低代码/无代码**：操作项、顺序、显隐、文案可配置
5. **降低复杂度外溢**：将权限、确认、禁用等复杂逻辑集中处理

## 3. 组件需求点罗列

### 3.1 选择感知能力

- [x] 接收 selectedKeys（选中的 key 数组）
- [x] 接收 selectedRows（选中的行数据数组，可选）
- [x] 接收 total（总数据量，用于判断全选状态）
- [x] 接收 currentPageTotal（当前页数据量）
- [x] 计算选中数量
- [x] 判断是否有选中项
- [x] 判断是否全选当前页
- [x] 判断是否全选全部（跨分页）

### 3.2 默认操作按钮（固定四个）

- [x] 全选按钮（可配置显隐）
- [x] 反选按钮（可配置显隐）
- [x] 取消选择按钮（可配置显隐）
- [x] 删除按钮（可配置显隐，带确认弹窗）

**说明**：其他操作按钮（如导出、打印、分享、批量编辑等）通过 `actions` 数组传入，完全可自定义。

### 3.3 自定义操作按钮

- [x] 支持新增自定义操作按钮
- [x] 支持操作按钮组（分组展示）
- [x] 支持按钮排序
- [x] 支持按钮显隐控制（基于 selection/权限）
- [x] 支持按钮禁用控制（基于 selection/权限）
- [x] 支持按钮 loading 状态
- [x] 支持按钮自定义样式
- [x] 支持按钮自定义 icon 和文案

### 3.4 布局模式

- [x] 吸顶模式（sticky top）
- [x] 吸底模式（sticky bottom）
- [x] 悬浮模式（fixed，默认）
- [x] 内嵌模式（relative）

### 3.5 溢出处理

- [x] 操作按钮过多时自动折叠至"更多"下拉菜单
- [x] 支持自定义折叠阈值
- [x] 支持自定义"更多"按钮文案

### 3.6 样式自定义

- [x] 容器样式自定义（背景色、边框、padding、gap、阴影）
- [x] 按钮样式自定义
- [x] 文本和 icon 样式自定义

## 4. 组件 API 设计

### 4.1 PisellBatchActionBarProps

```
interface PisellBatchActionBarProps {
  /* ==================== 基础属性 ==================== */

  /**
   * 自定义类名
   */
  className?: string;

  /**
   * 自定义样式
   */
  style?: CSSProperties;

  /* ==================== 选择状态 ==================== */

  /**
   * 选中的 key 数组
   * @description 必填，用于感知选择状态
   */
  selectedKeys: React.Key[];

  /**
   * 选中的行数据数组
   * @description 可选，用于传递给操作回调
   */
  selectedRows?: any[];

  /**
   * 总数据量
   * @description 用于判断全选状态
   */
  total?: number;

  /**
   * 当前页数据量
   * @description 用于判断全选当前页状态
   */
  currentPageTotal?: number;

  /**
   * 当前页数据 key 数组
   * @description 用于判断全选当前页状态
   */
  currentPageKeys?: React.Key[];

  /* ==================== 布局模式 ==================== */

  /**
   * 布局模式
   * @default 'float'
   */
  position?: 'sticky-top' | 'sticky-bottom' | 'float' | 'inline';

  /**
   * 吸顶/吸底时的偏移量
   * @default 0
   */
  offset?: number;

  /* ==================== 默认操作按钮配置 ==================== */

  /**
   * 是否显示选中数量提示
   * @default true
   */
  showSelectedCount?: boolean;

  /**
   * 选中数量提示文案
   * @default '已选择 {count} 项'
   */
  selectedCountText?: string | ((count: number) => string);

  /**
   * 默认操作按钮配置
   * @description 固定四个基础操作按钮：全选、反选、取消选择、删除，其他操作按钮通过 actions 传入
   */
  defaultActions?: {
    /**
     * 是否显示全选/取消全选按钮
     * @default false
     */
    selectAll?: boolean;

    /**
     * 是否显示清空选择按钮
     * @default true
     */
    clear?: boolean;

    /**
     * 是否显示全选当前页按钮
     * @default false
     */
    selectCurrentPage?: boolean;
  };

  /* ==================== 自定义操作按钮 ==================== */

  /**
   * 自定义操作按钮配置
   */
  actions?: BatchActionItem[];

  /**
   * 操作按钮排序
   * @description 通过 key 数组控制按钮顺序
   */
  actionOrder?: string[];

  /* ==================== 溢出处理 ==================== */

  /**
   * 是否启用溢出折叠
   * @default true
   */
  enableOverflow?: boolean;

  /**
   * 溢出折叠阈值（按钮数量）
   * @default 5
   */
  overflowThreshold?: number;

  /**
   * "更多"按钮文案
   * @default '更多'
   */
  moreText?: string;

  /* ==================== 事件回调 ==================== */

  /**
   * 全选回调
   */
  onSelectAll?: () => void;

  /**
   * 取消全选回调
   */
  onDeselectAll?: () => void;

  /**
   * 清空选择回调
   */
  onClear?: () => void;

  /**
   * 全选当前页回调
   */
  onSelectCurrentPage?: () => void;

  /**
   * 自定义操作按钮点击回调
   * @description 当 actions 中的按钮被点击时触发，通过 action.key 区分不同的操作
   */
  onActionClick?: (key: string, selectedKeys: React.Key[], selectedRows?: any[]) => void | Promise<void>;
}
```

### 4.2 BatchActionItem（自定义操作按钮配置）

```
interface BatchActionItem {
  /**
   * 操作唯一标识
   */
  key: string;

  /**
   * 按钮文案
   */
  label: React.ReactNode;

  /**
   * 按钮图标
   */
  icon?: React.ReactNode;

  /**
   * 按钮类型
   * @default 'default'
   */
  type?: 'primary' | 'default' | 'dashed' | 'link' | 'text' | 'danger';

  /**
   * 按钮尺寸
   * @default 'middle'
   */
  size?: 'small' | 'middle' | 'large';

  /**
   * 是否禁用
   * @description 支持函数形式，基于 selection 动态判断
   */
  disabled?: boolean | ((selectedKeys: React.Key[], selectedRows?: any[]) => boolean);

  /**
   * 是否显示
   * @description 支持函数形式，基于 selection 动态判断
   * @default true
   */
  visible?: boolean | ((selectedKeys: React.Key[], selectedRows?: any[]) => boolean);

  /**
   * 是否显示 loading 状态
   */
  loading?: boolean;

  /**
   * 是否需要确认弹窗
   * @default false
   */
  confirm?: boolean | {
    title?: string;
    description?: string;
    okText?: string;
    cancelText?: string;
  };

  /**
   * 自定义样式
   */
  style?: CSSProperties;

  /**
   * 自定义类名
   */
  className?: string;

  /**
   * 按钮组标识
   * @description 相同 group 的按钮会分组展示
   */
  group?: string;

  /**
   * 按钮点击回调
   * @description 如果配置了此回调，会优先使用此回调，否则使用组件级别的 onActionClick
   */
  onClick?: (selectedKeys: React.Key[], selectedRows?: any[]) => void | Promise<void>;
}
```

## 5. 组件模块拆解

### 5.1 组件结构图

```
PisellBatchActionBar
├── Container (容器组件)
│   ├── SelectedCount (选中数量提示)
│   └── PisellBasicGrid (按钮布局容器)
│       ├── DefaultActions (默认操作按钮组，固定四个)
│       │   ├── GraphicTextCard (全选按钮)
│       │   ├── GraphicTextCard (反选按钮)
│       │   ├── GraphicTextCard (取消选择按钮)
│       │   ├── GraphicTextCard (删除按钮，带确认弹窗)
│       │   └── GraphicTextCard (全选当前页)
│       ├── CustomActions (自定义操作按钮组)
│       │   └── GraphicTextCard[] (通过 actions 数组传入的所有自定义按钮)
│       └── MultilevelCard (更多操作下拉菜单)
│           ├── card: GraphicTextCard (更多按钮)
│           └── panel: GraphicTextCard[] (溢出的操作按钮)
└── hooks/
    ├── useSelectionState (选择状态计算)
    ├── useOverflowActions (溢出处理)
    └── useActionVisibility (按钮显隐控制)
```

### 5.2 子组件说明

#### 5.2.1 Container（容器组件）

- **职责**：管理整体布局、定位模式、样式
- **实现**：根据 `position` 属性设置不同的定位方式
- **样式**：支持自定义容器样式

#### 5.2.2 SelectedCount（选中数量提示）

- **职责**：显示选中数量信息
- **实现**：根据 `selectedKeys.length` 显示数量，支持自定义文案

#### 5.2.3 DefaultActions（默认操作按钮组）

- **职责**：渲染固定的四个基础操作按钮
- **实现**：根据 `defaultActions` 配置决定显示哪些按钮
- **按钮类型**：
  - 全选/取消全选：根据全选状态切换，触发 `onSelectAll` 或 `onDeselectAll`
  - 清空选择：清空 selectedKeys，触发 `onClear`
  - 全选当前页：选中当前页所有数据，触发 `onSelectCurrentPage`

**说明**：其他操作按钮（如全选全部、反选、导出、打印、删除、批量编辑等）通过 `actions` 数组传入，在 CustomActions 中渲染。

#### 5.2.4 CustomActions（自定义操作按钮组）

- **职责**：渲染自定义操作按钮
- **实现**：遍历 `actions` 数组，根据配置渲染按钮
- **特性**：
  - 支持按钮分组（group）
  - 支持动态显隐（visible）
  - 支持动态禁用（disabled）
  - 支持 loading 状态
  - 支持确认弹窗

#### 5.2.5 MoreMultilevelCard（更多操作下拉菜单）

- **职责**：收纳溢出的操作按钮
- **实现**：使用 MultilevelCard 组件，主卡片显示"更多"按钮，panel 中展示溢出的操作按钮
- **触发条件**：操作按钮数量超过 `overflowThreshold`

### 5.3 Hooks 说明

#### 5.3.1 useSelectionState

- **职责**：计算选择状态
- **返回值**：
  ```
  {
    selectedCount: number;           // 选中数量
    hasSelected: boolean;             // 是否有选中项
    isAllSelected: boolean;           // 是否全选
    isCurrentPageAllSelected: boolean; // 是否全选当前页
    isAllPagesSelected: boolean;      // 是否全选全部
  }
  ```

#### 5.3.2 useOverflowActions

- **职责**：处理操作按钮溢出
- **实现**：参考 PisellProcedure 的 useOverflowActions
- **返回值**：
  ```
  {
    visibleActions: ReactNode[];     // 可见的操作按钮
    overflowActions: ReactNode[];     // 溢出的操作按钮
    hasOverflow: boolean;             // 是否有溢出
    moreMenuItems: MenuProps['items']; // 更多菜单项
  }
  ```

#### 5.3.3 useActionVisibility

- **职责**：控制按钮显隐和禁用状态
- **实现**：根据 `visible` 和 `disabled` 配置（支持函数形式）计算状态

## 6. 组件实现方案

### 6.1 技术选型

- **UI 组件库**：
  - PisellBasicGrid：按钮布局和间距控制
  - GraphicTextCard：操作按钮
  - MultilevelCard：更多操作下拉菜单
  - Ant Design Popconfirm：确认弹窗
  - Ant Design Badge：选中数量徽标（可选）
- **状态管理**：React Hooks（useState、useMemo、useCallback）
- **样式方案**：Less + CSS Modules
- **类型定义**：TypeScript

### 6.2 核心实现逻辑

#### 6.2.1 选择状态计算

```
const useSelectionState = (props: {
  selectedKeys: React.Key[];
  total?: number;
  currentPageTotal?: number;
  currentPageKeys?: React.Key[];
}) => {
  const selectedCount = selectedKeys.length;
  const hasSelected = selectedCount > 0;
  
  const isAllSelected = useMemo(() => {
    if (!total) return false;
    return selectedCount === total;
  }, [selectedCount, total]);
  
  const isCurrentPageAllSelected = useMemo(() => {
    if (!currentPageKeys || !currentPageTotal) return false;
    return currentPageKeys.every(key => selectedKeys.includes(key));
  }, [selectedKeys, currentPageKeys, currentPageTotal]);
  
  return {
    selectedCount,
    hasSelected,
    isAllSelected,
    isCurrentPageAllSelected,
  };
};
```

#### 6.2.2 默认操作按钮渲染

```
const renderDefaultActions = () => {
  const actions: ReactNode[] = [];
  
  if (defaultActions?.selectAll) {
    actions.push(
      <GraphicTextCard
        key="select-all"
        text={isAllSelected ? '取消全选' : '全选'}
        variant="horizontal"
        size="small"
        clickable={true}
        onClick={() => {
          if (isAllSelected) {
            onDeselectAll?.();
          } else {
            onSelectAll?.();
          }
        }}
      />
    );
  }
  
  if (defaultActions?.clear && hasSelected) {
    actions.push(
      <GraphicTextCard
        key="clear"
        text="清空选择"
        variant="horizontal"
        size="small"
        clickable={true}
        onClick={onClear}
      />
    );
  }
  
  if (defaultActions?.selectCurrentPage) {
    actions.push(
      <GraphicTextCard
        key="select-current-page"
        text="全选当前页"
        variant="horizontal"
        size="small"
        clickable={true}
        onClick={onSelectCurrentPage}
      />
    );
  }
  
  return actions;
};
```

#### 6.2.3 自定义操作按钮渲染

```
const renderCustomActions = () => {
  return actions
    .filter(action => {
      const visible = typeof action.visible === 'function'
        ? action.visible(selectedKeys, selectedRows)
        : action.visible !== false;
      return visible;
    })
    .map(action => {
      const disabled = typeof action.disabled === 'function'
        ? action.disabled(selectedKeys, selectedRows)
        : action.disabled;
      
      // 处理点击事件：优先使用 action.onClick，否则使用组件级别的 onActionClick
      const handleClick = () => {
        if (action.onClick) {
          action.onClick(selectedKeys, selectedRows);
        } else if (onActionClick) {
          onActionClick(action.key, selectedKeys, selectedRows);
        }
      };
      
      // 使用 GraphicTextCard 渲染按钮
      const card = (
        <GraphicTextCard
          key={action.key}
          text={action.label}
          icon={action.icon}
          variant="horizontal"
          size={action.size || 'small'}
          clickable={!disabled && !action.loading}
          disabled={{ value: disabled || false }}
          loading={action.loading ? { value: true } : undefined}
          onClick={handleClick}
          style={action.style}
          className={action.className}
        />
      );
      
      // 如果需要确认弹窗
      if (action.confirm) {
        return (
          <Popconfirm
            key={action.key}
            title={typeof action.confirm === 'object' ? action.confirm.title : '确认操作'}
            description={typeof action.confirm === 'object' ? action.confirm.description : undefined}
            onConfirm={handleClick}
          >
            {card}
          </Popconfirm>
        );
      }
      
      return card;
    });
};
```

#### 6.2.4 溢出处理

参考 `PisellProcedure/components/FooterActions.tsx` 的实现：

1. 使用隐藏的测量容器测量所有按钮宽度
2. 使用 ResizeObserver 监听容器宽度变化
3. 计算可见按钮和溢出按钮
4. 将溢出按钮放入 MultilevelCard 的 panel 中，使用 MultilevelCard 替代 Dropdown 菜单

```
const renderMoreActions = () => {
  if (!hasOverflow) return null;
  
  return (
    <MultilevelCard
      card={(props) => (
        <GraphicTextCard
          text={moreText || '更多'}
          variant="horizontal"
          size="small"
          clickable={true}
        />
      )}
      panel={(props) => (
        <PisellBasicGrid
          dataSource={overflowActions}
          renderItem={(item) => item}
          direction="vertical"
          gap={8}
        />
      )}
      trigger="click"
      panelWidthMode="auto"
    />
  );
};
```

#### 6.2.5 布局模式实现

```
// 使用 PisellBasicGrid 控制按钮布局和间距
const renderActionsContainer = () => {
  const allActions = [
    ...renderDefaultActions(),
    ...renderCustomActions(),
  ];
  
  return (
    <PisellBasicGrid
      dataSource={allActions}
      renderItem={(item) => item}
      direction="horizontal"
      wrap="nowrap"
      gap={12}
      justify="flex-start"
      align="center"
      className={actionsContainerClassName}
      style={actionsContainerStyle}
    />
  );
};

const getContainerStyle = (): CSSProperties => {
  const baseStyle: CSSProperties = {
    padding: '12px 16px',
    background: '#fff',
    border: '1px solid #d9d9d9',
    borderRadius: '4px',
    ...style,
  };
  
  switch (position) {
    case 'sticky-top':
      return {
        ...baseStyle,
        position: 'sticky',
        top: offset || 0,
        zIndex: 100,
      };
    case 'sticky-bottom':
      return {
        ...baseStyle,
        position: 'sticky',
        bottom: offset || 0,
        zIndex: 100,
      };
    case 'float':
      return {
        ...baseStyle,
        position: 'fixed',
        bottom: offset || 24,
        left: '50%',
        transform: 'translateX(-50%)',
        zIndex: 1000,
        boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
      };
    case 'inline':
    default:
      return baseStyle;
  }
};
```

### 6.3 组件组成部分和引用部分

#### 6.3.1 引用现有组件

- **PisellBasicGrid**：按钮布局和间距控制
- **GraphicTextCard**：操作按钮（使用 GraphicTextCard 组件替代 Button）
- **MultilevelCard**：更多操作下拉菜单（使用 MultilevelCard 组件替代 Dropdown）
- **Ant Design Popconfirm**：确认弹窗
- **Ant Design Badge**：选中数量徽标（可选）

#### 6.3.2 自定义组件

- **PisellBatchActionBar**：主组件
- **SelectedCount**：选中数量提示组件
- **DefaultActionButton**：默认操作按钮组件（可选，用于统一按钮样式）

#### 6.3.3 工具函数

- **calculateSelectionState**：计算选择状态（可选，提取为工具函数）
- **checkActionVisibility**：检查按钮显隐（可选，提取为工具函数）

### 6.4 样式设计

#### 6.4.1 容器样式

- 默认背景色：白色
- 默认边框：1px solid #d9d9d9
- 默认圆角：4px
- 默认内边距：12px 16px
- 按钮间距：通过 PisellBasicGrid 的 `gap` 属性控制，默认 12px

#### 6.4.2 按钮样式

- 使用 GraphicTextCard 组件，variant="horizontal"，size="small"
- 支持通过 `action.style` 和 `action.className` 自定义
- 支持通过 GraphicTextCard 的 graphicConfig 自定义图标样式

#### 6.4.3 响应式设计

- 移动端：按钮自动换行或折叠至更多菜单
- 桌面端：按钮横向排列，溢出时折叠

## 7. 使用示例

### 7.1 基础用法

```
<PisellBatchActionBar
  selectedKeys={selectedKeys}
  selectedRows={selectedRows}
  total={total}
  defaultActions={{
    selectAll: true,
    clear: true,
    selectCurrentPage: true,
  }}
  onSelectAll={() => {
    // 全选逻辑
  }}
  onClear={() => {
    // 清空选择逻辑
  }}
  onSelectCurrentPage={() => {
    // 全选当前页逻辑
  }}
/>
```

### 7.2 自定义操作按钮

```
<PisellBatchActionBar
  selectedKeys={selectedKeys}
  selectedRows={selectedRows}
  defaultActions={{
    selectAll: true,
    clear: true,
  }}
  actions={[
    {
      key: 'select-all-pages',
      label: '全选全部',
      onClick: () => {
        // 全选全部逻辑
      },
    },
    {
      key: 'invert',
      label: '反选',
      onClick: () => {
        // 反选逻辑
      },
    },
    {
      key: 'export',
      label: '导出',
      icon: <ExportOutlined />,
      onClick: (keys, rows) => {
        // 导出逻辑
      },
    },
    {
      key: 'delete',
      label: '删除',
      type: 'danger',
      confirm: {
        title: '确认删除',
        description: '确定要删除选中的项目吗？',
      },
      onClick: async (keys, rows) => {
        await deleteItems(keys);
        message.success('删除成功');
      },
    },
    {
      key: 'batch-edit',
      label: '批量编辑',
      type: 'primary',
      onClick: (keys, rows) => {
        // 批量编辑逻辑
      },
    },
  ]}
  onActionClick={(key, keys, rows) => {
    // 统一处理所有自定义操作按钮的点击事件
    // 或者直接在 actions 中配置 onClick
  }}
/>
```

### 7.3 布局模式

```
// 吸底模式
<PisellBatchActionBar
  position="sticky-bottom"
  offset={0}
  selectedKeys={selectedKeys}
/>

// 悬浮模式（默认）
<PisellBatchActionBar
  position="float"
  offset={24}
  selectedKeys={selectedKeys}
/>
```

## 8. 注意事项

1. **选择状态管理**：组件不负责选择状态管理，需要外部传入 `selectedKeys`
2. **默认按钮设计**：固定提供四个基础操作按钮（全选、反选、取消选择、删除），可以通过 `defaultActions` 配置显隐，其他业务操作按钮通过
   `actions` 数组传入
3. **组件使用**：
  - 按钮布局使用 **PisellBasicGrid** 组件控制，通过 `gap` 属性设置按钮间距
  - 操作按钮使用 **GraphicTextCard** 组件，variant="horizontal"，size="small"
  - 更多操作使用 **MultilevelCard** 组件，主卡片显示"更多"按钮，panel 中展示溢出的操作按钮
4. **回调函数**：所有操作回调都需要外部实现，组件只负责触发
5. **按钮点击处理**：自定义操作按钮的点击事件优先使用 `action.onClick`，如果没有配置则使用组件级别的 `onActionClick`
6. **权限控制**：通过 `visible` 和 `disabled` 配置实现权限控制
7. **异步操作**：支持异步操作，通过 `loading` 状态显示加载中
8. **确认弹窗**：删除等危险操作建议使用确认弹窗
9. **性能优化**：大量选中项时，注意回调函数的性能优化

## 9. 后续扩展

1. **操作历史**：记录批量操作历史
2. **操作撤销**：支持撤销批量操作
3. **批量操作模板**：保存常用的批量操作配置
4. **操作统计**：统计批量操作的成功率、耗时等
5. **快捷键支持**：支持键盘快捷键触发批量操作
