---
nav:
  title: Plus组件
  order: 3
group:
  title: 业务组件
  order: 3
title: PisellReservation
order: 6
category: plus
---

- 店铺端需要与 Sales Monitor 预约列表**同一套筛排与列**的预约页。
- 需要 **表格 ⟷ 平面图** 双视图，且平面图桌位与列表数据对齐（默认同页 booking 映射为 `tables`）。
- 需要 **按营业日 + 时刻** 拉取列表（平面图视图下切换日程会合并 `booking_time_start_between`）。

## 数据模式

| 模式       | 条件        | 行为                                                                                                                |
|----------|-----------|-------------------------------------------------------------------------------------------------------------------|
| **内置请求** | 不传 `data` | 使用引擎注入的 `request` 请求 booking 列表；分页、筛选、重置由内部 `usePisellReservationBookingData` 管理。                                 |
| **受控列表** | 传入 `data` | 不再请求 booking；需自行传入 `total`、`pagination`、`onPageChange`、`loading`，以及 `onSearch` / `onReset` / `searchParams`（若需要）。 |

**平面图桌位**：`dataSources[gridDataSourceKey]`（默认 key 为 `tables`）若已有数据则优先使用；否则在**内置请求**模式下由当前列表行经
`bookingListToReservationTableRows` 映射；受控模式下由 `data` 映射。

## 视图与时间轴

- **defaultBodyView**：`grid` | `floorMap`，与 RecordBoard 一致。
- **顶栏时间轴 + 换日条**仅在 **bodyView === 'floorMap'** 时显示；表格视图下工具栏 `tabs` 仅展示你传入的额外 tab。
- 时间轴默认范围为锚定日 **02:00 — 次日 02:00**（跨日 24h），与列表请求窗口一致；可通过 `timeNavigatorProps` 覆盖。
- **跟随当前**（工具栏右侧开关）：开启后 `schedule` 的日期与时刻按系统时间更新（约每 30 秒 tick），并合并 *
  *PisellTimeNavigator** 的只读与隐藏 Now、**PisellReservationSchedule** 的 `navigationLocked`。可通过 `scheduleEndSlot`
  完全自定义右侧区域以去掉该开关。

## 平面图与店铺配置

- **floorPlanId**：传 `null` 时**不请求**店铺平面图接口，仅用本地/传入的 `floorMap.floorMapConfig`（适合 Story、纯前端联调）。未传或为
  `undefined` 时使用内置默认 id 尝试 **GET /shop/schedule/floor-plan/code/{code}** 合并远程 `layout`。
- **floorPlanCode**：默认 `pisell_reservation`，与接口 `code` 对齐。
- 保存：在具备有效服务端 `id` 时，`onSave` 会经 `saveViewConfigToShopFloorPlan` 写回；可传 `putFloorPlan` 自定义 PUT 实现，
  `onFloorPlanPersisted` 在成功后回调。
- **编辑平面图**：未受控 `floorMap.mode` 时，平面图视图下提供「编辑平面图 / 完成」切换内部 `edit/read`；切回表格会自动退出编辑态。

## 平面图只读点击（预约详情）

- `**floorMapBookingClickMode`**（默认 `detailModal`）：`detailModal` 时打开包内 **预约详情弹窗**（纯展示，无结账按钮）；
  `hostDrawer` 时与历史一致，调用引擎 `pisell1.handleOpenHostCheckoutDrawer`。
- `**onFloorMapBookingClick`**：若返回 `true`，表示业务侧已处理点击，不再执行上述内置逻辑。

## 代码演示

### 内置列表 + 关闭远程平面图（Story 常用）

依赖低代码/应用注入 `**appHelper.utils.request**`（与 `packages/private-materials` 内 `request` 单例一致）。
`floorPlanId={null}` 跳过店铺平面图 GET。

```tsx
import React, {useMemo, useState} from 'react';
import {
    PisellReservation,
    defaultReservationTableRows,
    getDefaultReservationFloorMapConfig,
} from '@pisell/private-materials';

export default function Demo() {
    const [floorMapConfig, setFloorMapConfig] = useState(() => {
        const base = getDefaultReservationFloorMapConfig();
        return {
            ...base,
            sceneElements: base.sceneElements.map((el) => ({...el})),
        };
    });
    const [rows, setRows] = useState(() => [...defaultReservationTableRows]);

    const floorMap = useMemo(
        () => ({
            floorMapConfig,
            onSave: (config: unknown) => {
                setFloorMapConfig(config as typeof floorMapConfig);
            },
            onDataSourceRecordSave: (
                _dataSourceKey: string,
                id: string,
                newData: Record<string, unknown>
            ) => {
                setRows((prev) =>
                    prev.map((r) => (r.id === id ? {...r, ...newData} : r))
                );
            },
        }),
        [floorMapConfig]
    );

    return (
        <div style={{height: 560}}>
            <PisellReservation
                floorPlanId={null}
                dataSources={{tables: rows}}
                floorMap={floorMap}
                defaultBodyView="grid"
                onNewReservation={() => {
                }}
            />
        </div>
    );
}
```

### 仅默认形态（依赖环境请求）

```tsx
import React from 'react';
import {PisellReservation} from '@pisell/private-materials';

export default () => (
    <div style={{height: 520}}>
        <PisellReservation floorPlanId={null} defaultBodyView="grid"/>
    </div>
);
```

### 受控顶栏日程

`scheduleValue` / `onScheduleChange` 的类型为 `ReservationScheduleBandValue`（`date`、`at` 为 `Dayjs`）。**不在示例中 import
dayjs**：首屏不传 `scheduleValue`，仅监听 `onScheduleChange`，在回调里拿到带 `Dayjs` 的 `next` 写入 state 后，再传入
`scheduleValue` 与 `onScheduleChange` 转为受控（写法与 @pisell/materials **PisellReservationScheduleBand** 文档「受控用法」一致）。

```tsx
import React, {useState} from 'react';
import type {ReservationScheduleBandValue} from '@pisell/materials';
import {PisellReservation} from '@pisell/private-materials';

export default function Demo() {
    const [schedule, setSchedule] = useState<
        ReservationScheduleBandValue | undefined
    >();

    return (
        <div style={{height: 520}}>
            <PisellReservation
                floorPlanId={null}
                defaultBodyView="floorMap"
                {...(schedule
                    ? {scheduleValue: schedule, onScheduleChange: setSchedule}
                    : {onScheduleChange: (next) => setSchedule(next)})}
            />
        </div>
    );
}
```

## API

本组件在 **RecordBoardProps** 基础上去除并由内部接管的字段包括：`toolBar`（改为合并写入）、`floorMap`（合并默认与远程）、
`dataSources`、`gridDataSourceKey`、`grid`、`search`、`batchActionBar`、`data`、`total`、`pagination`、`onPageChange`、
`onSearch`、`onReset`、`searchParams`、`loading`。其余 RecordBoard 属性（如 `columnVisibilityStorageKey`、`enablePagination`、
`paginationConfig`、`floorMapLayoutContext`、`rowKey` 等）会透传给 **PisellRecordBoard**。

### PisellReservationProps（摘要）

| 属性                                              | 说明                                               | 类型                                                             |
|-------------------------------------------------|--------------------------------------------------|----------------------------------------------------------------|
| data                                            | 传入则受控列表，不发 booking                               | `unknown[]`                                                    |
| total / pagination / onPageChange / loading     | 受控列表时使用                                          | 同 RecordBoard                                                  |
| searchParams / onSearch / onReset               | 受控列表筛排                                           | 同 RecordBoard                                                  |
| dataSources                                     | 平面图等多数据源；`tables` 可覆盖映射结果                        | `Record<string, PisellReservationTableRow[]>`                  |
| gridDataSourceKey                               | 表格推导用的 key，默认 `tables`                           | `string`                                                       |
| scheduleValue / onScheduleChange                | 顶栏日程受控                                           | `ReservationScheduleBandValue`                                 |
| timeNavigatorProps                              | 合并进 PisellTimeNavigator；跟随当前时合并只读与隐藏 Now         | `TimeNavigatorPassthroughProps`                                |
| scheduleProps                                   | 合并进 PisellReservationSchedule（不含 value/onChange） | 见 `@pisell/materials`                                          |
| toolBar                                         | 与预约视角工具栏合并；`tabs` 与时间轴并列                         | `Omit<RecordBoardToolBarProps, 'tabs'> & { tabs?: ReactNode }` |
| scheduleStartSlot / scheduleEndSlot             | 顶栏 band 左右插槽；不传 end 时内置「跟随当前 + 新建」               | `ReactNode`                                                    |
| onNewReservation                                | 点击内置「新建」                                         | `() => void`                                                   |
| fab                                             | 页面右下角悬浮区                                         | `ReactNode`                                                    |
| floorMap / defaultBodyView                      | 同 RecordBoard；内部合并 `renderItemByKind`、数据源标签等     | 同 RecordBoard                                                  |
| floorPlanId                                     | `null` 跳过远程平面图 GET；`undefined` 用默认 id            | `number                                                        | null | undefined`                                    |
| floorPlanCode                                   | GET/Persist 用 code，默认 `pisell_reservation`       | `string`                                                       |
| floorPlanCanvasWidth / floorPlanCanvasHeight    | 持久化画布尺寸                                          | `number`                                                       |
| floorPlanName / floorPlanSort / floorPlanStatus | PUT body 可选字段                                    | 见类型定义                                                          |
| onFloorPlanPersisted                            | 保存成功回调                                           | `() => void`                                                   |
| putFloorPlan                                    | 自定义 PUT                                          | `(id, body) => Promise<unknown>`                               |
| floorMapBookingClickMode                        | 只读平面图点击：`detailModal`（默认）                        | `hostDrawer`                                                   | `'hostDrawer' | 'detailModal'`                                 |
| onFloorMapBookingClick                          | 自定义点击；返回 `true` 则跳过内置抽屉/弹窗                       | 见 `FloorMapBookingClickArgs`                                   |
| className / style                               | 页面根容器                                            | `string` / `CSSProperties`                                     |

### PisellReservationTableRow

桌位/资源卡片数据形状（状态、客人、时段、进度、多段 slots 等），详见源码 `types.ts`，与 `getReservationRenderItemByKind`
的平面图渲染一致。

## 相关文档

- [PisellRecordBoard](https://www.npmjs.com/package/@pisell/materials)（@pisell/materials）
- 平面图店铺端接口说明：仓库内 `packages/materials/src/components/pisellFloorMapLayout/docs/api.md`
- 时间轴组合：@pisell/materials **PisellReservationScheduleBand**、**PisellTimeNavigator**
  正在修正文档：移除错误的受控示例，并添加 `index.ts` 的 mock 导出。

<｜tool▁calls▁begin｜><｜tool▁call▁begin｜>
StrReplace