# PisellRecordBoard 记录看板

记录看板容器组件，提供统一的列表数据、分页、搜索/筛选/排序、多选等上下文，支持 Grid（表格）与 Card 两种布局。ToolBar
左侧为快速筛选（QuickFilter）+ 搜索（PisellFind），右侧为排序、筛选弹层、重置。

## 快速开始

```tsx
import PisellRecordBoard from '@pisell/materials';

// 受控：打散传入 data、pagination、searchParams、onSearch 等；多选通过 grid 传入
const value = {
    data: list,
    loading: false,
    total: 100,
    pagination: {pageNumber: 1, pageSize: 10},
    onPageChange: (num, size) => {
    },
    searchParams: {},
    onSearch: (params) => {
    },
    layoutType: 'grid',
    rowKey: 'id',
};

<PisellRecordBoard rowKey="id" {...value} grid={{columns, selectedKeys, selectedRows, onSelectionChange}}/>
<PisellRecordBoard.ShellFrame>
    <PisellRecordBoard.GridLayout/>
</PisellRecordBoard.ShellFrame>
</PisellRecordBoard>
```

通过 `grid.columns` 传入列配置，ToolBar 会根据 columns 自动派生筛选与排序；列上配置 `type` 时，表格会用对应
Pisell 组件只读渲染，筛选用编辑态。`grid` 支持 antd Table 其他 props 透传，如 `onRow`（行点击等）、`onHeaderRow`、`expandable`、
`rowClassName`、`summary` 等。

## 核心特性

- **统一上下文**：data、pagination、searchParams、onSearch、多选等由 Context 提供，子组件通过 `useRecordBoardContext()` 消费。
- **列驱动筛选/排序**：列配置（`grid.columns`）驱动 ToolBar 内筛选项与排序项，无需单独维护 filter 列表。
- **有 type / 无 type**：有 `type` 时表格/筛选/排序可自动派生；无 `type` 时需自行配置 `render`、`filter`、`sort`；有 type
  时也可传 `render`/`filter`/`sort` 覆盖。
- **快速筛选**：支持左侧内联 QuickFilter（如下单时间 RangePicker），右侧筛选弹层仅高级筛选。
- **多选**：支持表格/卡片多选，BatchActionBar 展示批量操作。
- **可选平面图**：在 `ShellFrame` 内并列声明 **`PisellRecordBoard.FloorMap {...}`**（与 `GridLayout`
  等同层）；仅表格、仅平面图或双视图（出现切换）由子节点决定。根级 `floorMap` 仍可透传（如 SalesGrid），与 `FloorMap` props
  合并且子优先；平面图模式下 **隐藏底部分页**。

## 表格无限滚动（`grid.infiniteScroll`）

置 `grid.infiniteScroll: true` 时：

- **ShellFrame 底部分页条不渲染**（含 `paginationRender` 的挂载位；需自行去掉业务里为替换分页而传的函数，避免无用代码）。
- **表体**在接近底部（约 48px）时调用 `onPageChange(pagination.pageNumber + 1, pagination.pageSize)`；触底停更条件为 `data.length < total`（且 `loading` 为 false）。
- **宿主数据**：
  - 推荐根级传入 **`data` 为已加载的累积行**（每加载一页就 `concat`），`total` 为服务端/逻辑总量；`pagination.pageNumber` 表示当前已请求到的页码（与「段数」语义一致亦可，由宿主自行统一）。
  - 若仅使用 **`dataSources` + `gridDataSourceKey`** 且未传 `data`：容器会对该 key 对应数组做 **`slice(0, pageNumber * pageSize)`** 的累积切片（与默认的「单页覆盖」切片不同），全量数组需在内存中随请求增长或由外部保持。

建议同时配置 `grid.scroll`（如 `autoCalc: true` 且父级有高度），否则表体可能无纵向滚动条、无法触底。

## 子组件

| 子组件                                       | 说明                                                                                                      |
|-------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `PisellRecordBoard.ShellFrame`            | 外壳：ToolBar + BatchActionBar + children（Layout）+ Pagination；分页使用定制样式（见 shellFrame/Pagination/index.less） |
| `PisellRecordBoard.GridLayout`            | 表格布局（仅内容区 Grid；分页在 ShellFrame 底部）                                                                       |
| `PisellRecordBoard.CardLayout`            | 卡片列表布局                                                                                                  |
| `PisellRecordBoard.FloorMap`              | 平面图槽位（无 DOM），与 GridLayout 同写在 ShellFrame 内；props 对齐 PisellFloorMapLayout                                |
| `PisellRecordBoard.ToolBar`               | 工具栏（搜索、QuickFilter、Sort、Filter、列设置、Reset、slots）                                                         |
| `PisellRecordBoard.Search`                | 独立搜索槽（PisellFind）                                                                                       |
| `PisellRecordBoard.Pagination`            | 独立分页槽                                                                                                   |
| `PisellRecordBoard.Grid`                  | 独立表格视图（支持 columnVisibility 列显示隐藏）                                                                       |
| `PisellRecordBoard.CardList`              | 独立卡片列表视图                                                                                                |
| `PisellRecordBoard.BatchActionBar`        | 多选操作栏                                                                                                   |
| `PisellRecordBoard.useRecordBoardContext` | 消费 RecordBoard 上下文的 Hook                                                                                |

## 列配置逻辑（有 type / 无 type）

| 能力       | **有 type**（如 `singleLineText`）             | **无 type**                                                                                 |
|----------|--------------------------------------------|--------------------------------------------------------------------------------------------|
| **表格展示** | 自动用对应 Pisell 组件只读渲染；可传 `render` 覆盖         | 无 `render` 则纯文本；可自定义 `render`                                                              |
| **筛选**   | 自动用对应组件编辑态；可传 `filter`（type 或 component）覆盖 | 需配置 `filterable` + `filter`（`filter.type` 或 `filter.component`，component 需 value/onChange） |
| **排序**   | 自动从组件 SortOptions 派生；可传 `sort` 覆盖          | 需配置 `sortable` + `sort`（`sort.type` 或 `sort.getSortItems`）                                 |

### 支持的列 type（RecordBoardFieldType）

`singleLineText` | `longText` | `email` | `phone` | `url` | `number` | `currency` | `percent` | `rating` |
`singleSelect` | `multipleSelect` | `dateTime`

### 有 type 示例（options 或 getOptions）

列 type 为 singleSelect/multipleSelect 时，`fieldProps` 可传 `options` 或 `getOptions`（同步/异步返回选项列表）；筛选同理，
`filter.options` 或 `filter.getOptions`。

```tsx
const columns = [
    {title: '订单号', dataIndex: 'orderNo', key: 'orderNo', width: 160, type: 'singleLineText'},
    {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        type: 'singleSelect',
        fieldProps: {options: [{label: '全部', value: 'all'}, {label: '启用', value: 'enabled'}]},
    },
    {
        title: '下单时间',
        dataIndex: 'createTime',
        key: 'createTime',
        type: 'dateTime',
        fieldProps: {customFormat: 'YYYY-MM-DD HH:mm'},
        filterable: true,
        filter: {quickFilter: true},  // 放在左侧快筛
    },
];
```

### 无 type + 自定义 filter 示例

```tsx
const MyFilterInput = ({value, onChange}) => (
    <Input value={value} onChange={(e) => onChange?.(e.target.value)} placeholder="自定义"/>
);

const columns = [
    {
        title: '备注',
        dataIndex: 'remark',
        key: 'remark',
        filterable: true,
        filter: {component: MyFilterInput},
    },
    {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        sortable: true,
        sort: {getSortItems: (key) => [{key: `${key}:asc`, label: 'A-Z'}, {key: `${key}:desc`, label: 'Z-A'}]},
    },
];
```

## Props

仅支持**打散传参**（有明确关系的成组，其余独立）。

**列表/分页/搜索**：`data`、`loading`、`total`、`pagination`、`onPageChange`、`searchParams`、`onSearch`、`onReset`、`rowKey`、
`layoutType`。**多选**：通过 `grid.selectedKeys`、`grid.selectedRows`、`grid.onSelectionChange` 传入。**列显示隐藏**
：由容器内部维护，不通过 props 传入，通过 context 提供给 ToolBar 列设置与 Grid。

**子组件配置**：`grid`、`toolBar`、`search`、`paginationConfig`、`batchActionBar`、`cardList`。

| 属性                                                                                                     | 类型                          | 说明                                                                                                                                                                                                           |
|--------------------------------------------------------------------------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `rowKey`                                                                                               | `string \| (record) => Key` | 行主键                                                                                                                                                                                                          |
| `layoutType`                                                                                           | `'grid' \| 'card'`          | 布局类型                                                                                                                                                                                                         |
| `data` / `loading` / `total` / `pagination` / `onPageChange` / `searchParams` / `onSearch` / `onReset` | 各自类型                        | 打散的数据与回调                                                                                                                                                                                                     |
| `grid` / `toolBar` / `search` / `paginationConfig` / `batchActionBar` / `cardList`                     | 各自类型                        | 打散的子组件配置。**grid** 类型为 RecordBoardGridProps（基于 antd TableProps 省略 dataSource/loading/pagination/columns/scroll/size/rowSelection），多选在 grid 内；支持 Table 透传（onRow、onHeaderRow、expandable、rowClassName、summary 等） |

## Context（RecordBoardContextValue）

- `data`、`loading`、`total`、`pagination`、`onPageChange`
- `searchParams`（含 keyword、filter、sort）、`onSearch`、`onReset`
- `layoutType`、`selectedKeys`、`selectedRows`、`onSelectionChange`、`rowKey`
- `columnVisibility`、`onColumnVisibilityChange`（由容器内部维护并注入 context，供 Grid/CardList 与 ToolBar 列设置使用；不通过
  props 传入）
- `childComponentProps`（由容器从打散 props 合并注入）

子组件通过 `PisellRecordBoard.useRecordBoardContext()` 获取。

## 文档

- [使用文档](./docs/PisellRecordBoard.md)（架构总览、Context、`searchParams`、平面图、Storybook、API）
- [Shell Frame](./docs/ShellFrame.md)
- [设计文档](./docs/PisellRecordBoard.$tab-design.md)
- [开发文档](./docs/PisellRecordBoard.$tab-dev.md)
- [测试文档](./docs/PisellRecordBoard.$tab-test.md)

## 目录结构

```
pisellRecordBoard/
├── index.tsx                 # 导出入口与子组件挂载
├── PisellRecordBoard.tsx     # 主容器与 Provider
├── types.ts                  # RecordBoardColumnType、RecordBoardContextValue 等
├── context/                  # RecordBoardContext、RecordBoardProvider
├── hooks/
│   └── useRecordBoardContext.ts
├── shellFrame/               # ShellFrame、FloorMap 槽、ShellBodyMetaContext、ToolBar、ColumnSetting…
├── layouts/
│   ├── GridLayout/           # 表格布局
│   └── CardLayout/           # 卡片布局
├── utils/
│   ├── recordBoardColumns.tsx  # processColumnsForGrid、deriveFilterFromColumns、deriveSortFromColumns
│   └── withRecordBoard.tsx
├── docs/
│   ├── PisellRecordBoard.md
│   ├── PisellRecordBoard.$tab-design.md
│   ├── PisellRecordBoard.$tab-dev.md
│   ├── PisellRecordBoard.$tab-test.md
│   └── ShellFrame.md
├── PisellRecordBoard.stories.tsx
└── README.md                 # 本文档
```

## License

MIT
