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

# PisellFloorMapLayout 开发文档

## 1. 组件信息

| 项目   | 内容                                                                                                                                                                                |
|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 组件名称 | PisellFloorMapLayout                                                                                                                                                              |
| 组件版本 | 1.1.0（随平面图编辑、模块拆分迭代）                                                                                                                                                              |
| 组件分类 | Pro Components / Data Display                                                                                                                                                     |
| 描述   | 空间布局视图：基础模式由 `items` + `renderItem` 驱动；平面图模式由 `floorMapConfig` + `dataSources` 合并为画布图元，支持只读/编辑、侧栏与工具栏；可选 **`FloorMapLayoutProvider`** / **`FloorMapLayoutContextValue`** 注入配置与保存。 |

## 2. 仓库目录结构（与实现一致）

以下路径相对于 `packages/materials/src/components/pisellFloorMapLayout/`。

```
pisellFloorMapLayout/
├── index.tsx                          # 命名导出 PisellFloorMapLayout、FloorMapLayoutProvider、类型、FigmaTableCard 辅助方法等
├── PisellFloorMapLayout.tsx           # 主组件：组装 Transform、MapLayer、Item/EditableItemLayer、侧栏/工具栏/画布 Tab
├── PisellFloorMapLayout.less          # 根与布局级样式（编辑态双栏、侧栏、加载/错误）
├── PisellFloorMapLayout.stories.tsx   # Storybook
├── types.ts                           # Props、Ref、FloorMapViewConfig、FloorMapLayoutContextValue 等
├── context/
│   ├── FloorMapLayoutContext.tsx      # Provider、useFloorMapLayoutContext、EMPTY_FLOOR_MAP_LAYOUT_CONTEXT
│   └── mergeFloorMapLayoutContext.ts  # mergeFloorMapLayoutPropsFromContext
├── hooks/
│   ├── useFloorMapEditState.ts        # 平面图编辑：draftConfig、画布、选中、placeMode、落点、editPanelParams
│   └── useFloorMapTransformMetrics.ts # 视口尺寸、content 宽高/偏移、fitBounds、全屏、transform 记忆、拖拽图元时恢复视图
├── utils/
│   ├── layoutMeasurements.ts         # DEFAULT_CELL_SIZE、gapToPx、getContentSize、getPixelBoundingBox
│   ├── floorMapStageDefaults.ts       # STAGE_DECO_DEFAULT（舞台类装饰默认尺寸/名称）
│   ├── floorMapFallbackRender.tsx     # 无 renderItemByKind 时的占位渲染（含 stage、floorMapImage）
│   ├── floorMapConfigMerge.ts         # mergeCanvasItems：config → 当前画布 FloorMapMergedItem[]
│   └── snap.ts                        # 吸附（EditableItemLayer）
├── components/
│   ├── MapLayer.tsx                   # 底层背景、网格、padding
│   ├── ItemLayer.tsx                  # 阅读态：只读展示层
│   ├── EditableItemLayer.tsx          # 编辑态：选中、拖拽、缩放图元
│   ├── ViewControls.tsx               # 缩放/复位/fitBounds/全屏 按钮 UI
│   ├── ViewControlsWithZoom.tsx       # 在 TransformWrapper 内用 useControls 桥接 ViewControls
│   ├── CanvasTabs.tsx                 # 多画布（楼层）Tab、增删改名称
│   ├── FloorMapEditTopBar.tsx         # 编辑态单行顶栏：CanvasTabs + 保存/全屏/区域定位
│   ├── FloorMapToolbar.tsx            # 编辑态左侧：放置模式、数据源预览（保存可选）
│   ├── FloorMapEditPanel.tsx          # 编辑态右侧：右栏图元属性 + 数据源记录（绑定行、表单或自定义）
│   ├── FloorMapDataForm.tsx           # 按列配置或自动枚举字段的简单记录表单
│   ├── FloorMapImageElement.tsx       # 图片图元内层（URL / 失败占位）
│   ├── FigmaTableCard.tsx             # 方桌/圆桌卡片渲染辅助（供 renderItemByKind）
│   └── index.tsx                      # 子组件导出
└── docs/
    ├── pisellFloorMapLayout.md
    ├── pisellFloorMapLayout.$tab-design.md
    ├── pisellFloorMapLayout.$tab-dev.md
    └── pisellFloorMapLayout.$tab-test.md
```

### 2.1 主文件与 Hooks 分工

| 模块                              | 职责                                                                                                                                                                                                                                                                                                                                                                                        |
|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **PisellFloorMapLayout.tsx**    | `forwardRef` + `useImperativeHandle`；`mergeFloorMapLayoutPropsFromContext` + `useFloorMapLayoutContext` 合并后再解构；`contentOffsetRef` 注入双 Hook；`effectiveRenderItem` / `handleItemClick`；JSX 编排（`floorMapFullscreenMode` → `useFullTopChrome` / `editToggleFullscreen`；FloorMapEditTopBar 紧凑/全屏、Toolbar 保存与顶栏互斥、Transform、MapLayer、Item/EditableItemLayer、落点遮罩、侧栏拖拽接收层、右栏 FloorMapEditPanel）。 |
| **context/**                    | `FloorMapLayoutContext.tsx`：`FloorMapLayoutProvider`、`useFloorMapLayoutContext`、`EMPTY_FLOOR_MAP_LAYOUT_CONTEXT`；`mergeFloorMapLayoutContext.ts`：props 与 Context 字段合并。RecordBoard Shell 在平面图节点内置 Provider。                                                                                                                                                                                |
| **useFloorMapEditState**        | 同步 `floorMapConfig` → `draftConfig`；`mergedItems`（`mergeCanvasItems`）；`applyScenePlacementAtCoords`；画布 Tab 回调；`placeMode` 与画布点击落点；**`placeSceneElementFromPaletteDrop`**（侧栏拖入）；侧栏 `onUpdate` / `onDelete` / `onSaveRecord`；构造 `editPanelParams`（含 `dataSourceRecordForm` 透传）。                                                                                                             |
| **useFloorMapTransformMetrics** | `containerRef` / `mapAreaRef`；ResizeObserver；`contentWidth/Height` 与 `contentOffsetX/Y`（含 CONTENT_PADDING、编辑态不小于视口）；`scale`、`fitBounds`、`toggleFullscreen`、`lastTransformRef`、拖拽图元结束后的 `setTransform` 恢复。                                                                                                                                                                                 |

### 2.2 数据流（平面图模式）

1. **上游**持有 `FloorMapViewConfig`（画布列表、`sceneElements`、`elementKinds`、`elementKindCategories` 等）与
   `FloorMapDataSources`（可直接经 props 传入，或经 **`FloorMapLayoutProvider`** / RecordBoard **`floorMapLayoutContext`**
   注入后与 props **`??`** 合并）。
2. **编辑态**内组件维护 `draftConfig`；保存布局时通过 **`onSave(draftConfig)`** 交回上游持久化。
3. **当前画布**下，`mergeCanvasItems(canvasId, config, cellSize)` 将落位与装饰合并为 **`FloorMapMergedItem[]`**，再作为渲染用
   `items`（`itemUnit` 为 `pixel`）。
4. **业务行数据**变更：`FloorMapEditPanel` 内 **`onDataSourceRecordSave(dataSourceKey, id, newData)`**
   ，不由布局组件持久化，由业务合并回自己的数据源（如 RecordBoard 表格与 `dataSources` 同源）。

### 2.3 侧栏「数据源记录」表单策略

| 配置                                                                    | 行为                                                                                                                                   |
|-----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| 未配置 `dataSourceRecordForm` 且提供 `onDataSourceRecordSave`               | 使用 **`FloorMapDataForm`**：`dataSourceFormColumns` 指定列，否则按 `record` 字段自动枚举（排除 `id`）。                                                  |
| `dataSourceRecordForm[dataSourceKey]` 存在且非 `null`/`undefined`/`false` | **仅渲染自定义内容**（`ReactNode` 或 `(ctx) => ReactNode`），不渲染 `FloorMapDataForm`。`ctx` 类型见 **`FloorMapRecordFormRenderContext`**（`types.ts`）。 |
| 仅自定义表单、不传 `onDataSourceRecordSave`                                    | 「数据源记录」区块仍可展示；`ctx.onSaveRecord` 为 `undefined`，自定义 UI 需自行调接口。                                                                        |

「选择数据行」绑定仍由 **`FloorMapEditPanel`** 渲染，与是否自定义表单无关。

## 3. 需求点罗列（补充）

### 3.1 平面图编辑（floorMapConfig）

- 多画布：`FloorMapEditTopBar` 内 `CanvasTabs` 切换 `currentCanvasId`；可增删画布（受 `floorMapConfig.canvasUi.canEdit`
  等配置约束）。
- 顶栏：默认 **`floorMapFullscreenMode='toggle'`** — **非浏览器全屏**为 Tab + 进入全屏；**全屏**后 Tab + 保存 + 退出全屏 +
  区域定位（与 `controls` 显隐一致）。**`locked`**：始终「全屏顶栏」且无退出按钮。**`disabled`**：仅
  Tab，无全屏按钮，保存仍在侧栏。左下角不再重复全屏/fitBounds。
- 放置：`FloorMapToolbar` 点击图元类型与（可选）绑定数据行 → 画布点击落位；或**从侧栏拖拽图元卡片到画布** →
  `placeSceneElementFromPaletteDrop` 写入 `draftConfig`。
- 图元交互：`EditableItemLayer` 拖拽/缩放；选中后 `FloorMapEditPanel` 编辑 x/y/层级/宽高/name/画布及记录表单。
- **`layoutTransformLocked`**：为 true 时侧栏锁定位置与尺寸输入，且画布不可拖拽、不显示缩放点；可与 *
  *`onUpdate({ layoutTransformLocked })`** 切换。
- 图片图元：`FLOOR_MAP_IMAGE_ELEMENT_KIND`（`floorMapImage`），`sceneElement.imageUrl` 为位图地址；默认落点较大、`zIndex`
  偏负便于作底图；无 `renderItemByKind` 时由 `FloorMapImageElement` 占位/展示（`floorMapFallbackRender.tsx`）。
- 舞台类装饰：`FLOOR_MAP_STAGE_ELEMENT_KIND`（`stage`）与 `STAGE_DECO_DEFAULT`，占位见 `floorMapFallbackRender.tsx`。

### 3.2 与 RecordBoard 集成

- `RecordBoard` 的 `floorMap` 为 **`Partial<PisellFloorMapLayoutProps>`**，典型场景与表格共用同一 `dataSources`；
  `placement.id` 与行 `id` 对齐。
- 根级 **`floorMapLayoutContext?: FloorMapLayoutContextValue`** 写入 **`RecordBoardContextValue`**，Shell 在平面图节点用
  **`FloorMapLayoutProvider`** 包裹 **`PisellFloorMapLayout`**；**`useRecordBoardContext().floorMapLayoutContext`** 与之同源。

## 4. 组件 API 罗列（开发视角）

### 4.1 核心 Props（详见 types.ts）

- **基础**：`items`、`renderItem`、`mapLayer`、`zoom`、`pan`、`controls`、`itemUnit`、`cellSize`、`enableHover`、事件回调。
- **平面图**：`mode`、`floorMapConfig`、`dataSources`、`onSave`、`onDataSourceRecordSave`、`dataSourceLabels`、
  `dataSourceFormColumns`、**`dataSourceRecordForm`**、`renderItemByKind`、`loading`、`saveError`、`floorMapFullscreenMode`、
  `toolbarShowSave`、`onElementRenderError`。
- **Context 导出**：**`FloorMapLayoutProvider`**、**`useFloorMapLayoutContext`**、**`EMPTY_FLOOR_MAP_LAYOUT_CONTEXT`**、
  **`mergeFloorMapLayoutPropsFromContext`**、类型 **`FloorMapLayoutContextValue`** / **`FloorMapLayoutProviderProps`**。
- **Ref**：`zoomIn` / `zoomOut` / `reset` / `fitBounds`。

### 4.2 类型要点

- **`FloorMapMergedItem`**：`id` / `instanceId` 与 `sceneElements[].instanceId` 一致；**`_sceneElement`** 携带完整画布图元；
  `elementKind` 用于 `renderItemByKind`。
- **`FloorMapRecordFormRenderContext`**：自定义记录表单上下文；**`FloorMapDataSourceRecordFormContent`**：
  `ReactNode | ((ctx) => ReactNode)`。
- **`FloorMapLayoutContextValue`**：平面图注入上下文字段子集（见 `types.ts`），与 props 合并时 **props 优先**。

## 5. 实现要点摘要

- **缩放平移**：`react-zoom-pan-pinch`；`ViewControlsWithZoom` 必须在 `TransformWrapper` 子树内以使用 `useControls`。
- **内容区尺寸**：`useFloorMapTransformMetrics` 内调用 `getContentSize` / `getPixelBoundingBox`（`layoutMeasurements.ts`
  ）；编辑态保证画布不小于地图视口，避免 flex 子项 min-height 撑破外层布局。
- **类名前缀**：`pisell-floor-map-layout`；落点遮罩类名 `pisell-floor-map-layout-place-overlay`，与 `useFloorMapEditState`
  的 `layoutPrefix` 一致。

## 6. 依赖

- react、react-dom
- classnames
- react-zoom-pan-pinch
- antd、@ant-design/icons（视图控制图标；Story 与业务表单示例）
- 组件本身不强制业务方使用 antd Form；`FloorMapDataForm` 使用 antd Form。

## 7. 扩展与维护

- 新增装饰类型：优先在 `types.ts` 增加 kind 常量、`elementKinds` 默认项、`floorMapFallbackRender` 或 `renderItemByKind`。
- 复杂数据源表单：优先 **`dataSourceRecordForm`**，避免把业务规则塞进 `FloorMapDataForm`。
