# 预约详情弹窗 · 数据来源与 UI 对应

与平面图 RoomCard 数据说明 同属「宿主行 → UI」对照文档。弹窗正文为单条 `floorMapSourceBookings` 项（点击哪条打开哪条，不做 `booking_id` 聚合）。Modal 使用 `footer=null`，无底部「应付」条。正文三块（粉头、操作格、商品）包在 `pisell-res-booking-detail-modal__booking-card` 内，样式见 `ReservationBookingDetailModal.less`。

## 数据从哪来

`PisellReservation` 在平面图「详情弹窗」模式下，将当前点击解析得到的单条记录写入 `bookingDetailRaw`，并传入 `ReservationBookingDetailModal` 的 `raw`；当前桌位合并行（与图元一致）传入 `tableRow`。

    平面图点击
      → resolveFloorMapHostCheckoutRecord（可选命中子卡片 data 属性）
      → setBookingDetailRaw(rec)   // rec 为 floorMapSourceBookings[] 中一条
      → <ReservationBookingDetailModal raw={bookingDetailRaw} tableRow={…} />

禁止使用 `order.products` 作为商品列表。商品区仅读取与 `order` 同级的顶层 `raw.products[]`（与接口示例一致）。

示例 JSON：`packages/private-materials/src/plus/pisellReservation/api/api-bookings-demo.json`（数组元素即一条 booking；约第 227 行起为顶层 `products`）。

## Modal 标题栏

固定文案 `pisell-reservation.booking-detail.modal-title`（中/英/繁：详情 / Details / 詳情；ja/pt 同步）。预约号、资源摘要、预约状态等均在下方组合卡片的粉头 `order-strip` 中展示。

## 组合卡片 · 粉头信息条（order-strip）

视图模型：`bookingDetailOrderStripFromRaw(raw)`。左上联系人：与平面图 RoomCard 同源 `resolveFloorRoomCardHolder(raw)`（`holder` → `contacts_info` → 有效 `customer_id` 时 `customer_name`+`phone`）。展示对齐 RoomCard `ContactInfo`：有姓名则显示姓名，有手机则并排显示脱敏尾号（`maskPisellReservationPhoneTail`）；若仅有手机无姓名，整行显示脱敏手机（与 `guestDisplayLine(holder, true)` 一致）。若解析不到任何联系人，则回退 `strip.eyebrow`。

UI | 数据路径 | 备注
--- | --- | ---
左上联系人 | `resolveFloorRoomCardHolder(raw)` → `guestHeadline`（姓名优先，否则脱敏手机）；有姓名且 `contactPhone` 时并排脱敏手机；无联系人时 `strip.eyebrow` | 与 RoomCard `ContactInfo` / `guestDisplayLine` 一致
右上（同一行） | 容器 `pisell-res-booking-detail-modal__order-strip-top-right`：flex 横向，右对齐。依次为支付胶囊（`order.payment_status` / `raw.payment_status`）与预约状态徽章（`raw.status` → `booking-detail.status-enum.*`，来自 `bookingDetailFromRaw`）。 | 原 Modal 标题内状态已迁入粉头
大号字（预约 ID） | `raw.booking_id` → `raw.id` → `order.shop_full_order_number` → `order.order_number` | `strip.stripBookingId`
时段一行 | 优先 `start_at`/`end_at`；否则 `start_date`+`start_time` 与 `end_date`+`end_time` | `buildStripScheduleLine`，失败回退 `buildTimeRange`
左下「n 件 + 订单合计」 | 件数 n：优先 `raw.products` 有效行数（与下方 ProductCard 列表条数一致）；若无数组再回退 `order.summary.product_quantity`。合计金额仍用 `order.summary.total_amount` + `order.currency_symbol` | 模板键 `strip-items-total`
右下 Due | `bookingDetailOrderStripFromRaw`：`dueWithSymbol` = `expect_amount` 或 `total_amount` 经 `moneyWithSymbol(order.currency_symbol, …)`。UI 上 `strip-due-label`（如「Due:」）与金额在 `pisell-res-booking-detail-modal__order-strip-due` 内同一行（flex-row + baseline）。 | 与左下「n 件 + 合计」同一行 flex 底对齐

## 组合卡片 · 操作按钮（仅 UI）

布局：`…__action-grid` 为 2 行 × 每行 3 个（`grid-template-columns: repeat(3,1fr)`）。每格 `…__action-cell` 为 横向 flex：图标在左（`@ant-design/icons`），文案在右（locales `booking-detail.action-edit` 等六键）。

交互：点击仅 `preventDefault` + `stopPropagation`，不接宿主业务。

## 组合卡片 · 商品列表（ProductCard）

遍历 `Array.isArray(raw.products) ? raw.products : []`，经 `bookingProductLineToProductCardData` 映射为 `@pisell/materials` 的 `ProductCard` `dataSource`。

UI / 组件字段 | 行对象字段（顶层 products[]） | 备注
--- | --- | ---
`name` | `product_title`（多语言对象，按 auto/zh-CN/en… 回退） | —
`product_option_string` | `product_sku.subtitle`（多语言对象） | —
`num` | `product_quantity` | —
`price` / `total` | `selling_price`、`payment_price` | 数值解析为 number
`_id`（列表 key） | `order_detail_id` → `booking_uid` → `product_id`+index | 稳定即可
`id` | `product_id` | —

货币符号：`ProductCard` 内部金额区读的是 `dataSource.symbol`；根上的 `symbol` prop 不会自动写入 `dataSource`。弹窗内写法为 JSX `dataSource={{ ...ds, symbol: currencySymbol }}`（`currencySymbol` = `orderCurrencySymbol(raw.order)`，空则 `$`）。

只读：`bookingProductLineToProductCardData` 已设 `isShowAction: false`、`isShowNote: false`；弹窗再传 `isShowImage`、`isShowHolder`、`isShowDelete`、`isShowPackageNote`、`isShowEditProduct` 均为 `false`。默认最多展示 3 条，超出显示 `see-all` 按钮展开；粉头件数/合计文案键为 `strip-items-total`。

## tableRow（可选）

类型 `PisellReservationTableRow`。当前弹窗内组合卡片未单独展示桌位行列；若后续扩展，可与 `bookingDetailFromRaw` 中 `roomTitleLabel` / `roomCapacityLabel` 同源（`code`、`main_field`、`capacityLabel`）。

## 相关源码

- `PisellReservation.tsx` — `bookingDetailRaw`、弹窗挂载
- `components/bookingDetailModal/ReservationBookingDetailModal.tsx`、`ReservationBookingDetailModal.less`
- `components/bookingDetailModal/bookingDetailFromRaw.ts`
- `components/bookingDetailModal/bookingProductToProductCard.ts`
- `floorMap/floorMapHostCheckout.ts` — `resolveFloorMapHostCheckoutRecord`