概要设计文档:Robotaxi 座椅通风加热

0. 文档信息

项 内容
功能名称 座椅通风 / 加热(在空调温控面板内扩展)
所属模块 Robotaxi 接驾界面(begin-route / 行程开始页)
目标分支 rt/feature/v2.0_ui_xj_730
文档状态 概要设计(需求已对齐,待评审后进入编码)
关联能力 空调温控(temperature-control.tsx)—— 复用其 setTemperature / airConditionerUnlock 及温度区间/防抖逻辑
参照文档 docs/车载香氛系统概要设计.md(同一套接驾态远控范式)

1. 需求背景

Robotaxi 接驾阶段(TripStatus.VehicleApproaching)已支持空调温控、车门/后备箱、车载香氛远控。本次在**空调能力基础上扩展座椅通风 / 加热**:

  • 交互稿将原空调卡片的内联温控,升级为「卡片展示 + 点击打开『温度调节』面板」
  • 「温度调节」面板内**同时承载**:座椅通风/加热(顶部)+ 空调温度调节与开关(底部)
  • 空调卡片上仍保留**空调总开关**,可直接在卡片上交互

采用**方案 A**:重构空调卡片为「展示态 + 面板」,温控迁入面板(滚动温度选择器 + 面板内开关),座椅区位于面板顶部。空调下发逻辑**复用现有接口**。


2. 需求概述

2.1 卡片入口(接驾界面 · 左卡片 = 空调)

┌─────────────┬─────────────┐
│  ❄ 空调  [开关]│  智能香氛     │
│  23.5 度      │ (右卡片)     │
│ 调节温度|通风加热 │             │
└─────────────┴─────────────┘
  • 左卡片主体展示当前温度(如「23.5 度」),副标题「调节温度|通风加热」
  • 卡片右上角保留**空调总开关 Switch**(复用现有开关下发逻辑,直接在卡片上可交互)
  • 点击卡片主体 → 打开「温度调节」底部弹窗面板

2.2 温度调节面板(底部弹窗)

┌───────────────────────────────┐
│           温度调节           [ × ]│  ← 顶部:标题居中 + 关闭
├───────────────────────────────┤
│   [车内俯视图 · 座椅区]           │
│   ┌──────────┐  ┌──────────┐    │
│   │第二排左侧座椅 │  │第二排右侧座椅 │   │
│   │ ❋通风  ⌇加热 │  │ ❋通风  ⌇加热 │  │  ← 每座椅两个档位按钮
│   └──────────┘  └──────────┘    │
├───────────────────────────────┤
│              空调               │
│      …23.5   [24]℃   24.5…       │  ← 横向温度滚动选择器
│           ——● 空调开关            │  ← 面板内空调开关
└───────────────────────────────┘

面板自上而下分两区:
1. 座椅区(本次新增):车内俯视图 + 按接口返回渲染的座椅,每座椅含「通风」「加热」两个档位按钮
2. 空调区(迁移自现有卡片):横向温度滚动选择器 + 空调开关

2.3 座椅区渲染逻辑

  • 按接口返回的 seats 数组渲染,兼容二排 / 三排(position 决定座位标签文案)
  • 每个座椅两个按钮:**通风**(风扇图标)、**加热**(热浪图标)
  • 能力可用性(按档数字段):
  • ventilationLevels === 0 → 该座椅**通风按钮置灰/不可点**(如三排不支持通风),仅保留加热按钮
  • heatingLevels === 0 → 加热按钮置灰/不可点
  • 按钮上展示当前档位(如「通风 2」/ 关闭态展示「通风」或档位 0 的视觉)

2.4 档位循环与互斥(点击即时下发)

档位循环:点击按钮在 0 → 1 → 2 → … → N → 0 间循环,上限 N 由该座椅对应的 ventilationLevels / heatingLevels 决定(0 = 关)。
- 例:ventilationLevels=3 → 循环 0→1→2→3→0
- 例:heatingLevels=2 → 循环 0→1→2→0

互斥规则:同一座椅通风与加热不能同时开启。
- 用户将**通风**调到非 0 档 → 该座椅**加热自动回退至 0**
- 反之,将**加热**调到非 0 档 → 该座椅**通风自动回退至 0**

即时下发(对齐香氛,无独立"应用"按钮):
1. 点击某座椅的通风/加热按钮 → 计算该座椅下一档位 + 互斥联动
2. 仅下发该被改动的座椅,ventilationLevel 与 heatingLevel 一起下发
3. 下发期间该座椅按钮置灰防重复点击
4. 成功:按钮档位更新,Toast 提示(文案见 §8)
5. 失败:该座椅状态回滚到下发前,Toast「网络异常,请重试」,面板保持

2.5 空调区(复用现有能力)

  • 温度调节:横向滚动选择器,区间 15.5 ~ 32.5℃、步进 0.5,**复用现有 setTemperature + 防抖(500ms)**
  • 空调开关:面板内开关与**卡片上开关状态同源**,复用 airConditionerUnlock
  • 温度/开关的初始化、连环单跳过、本地缓存等逻辑**沿用现有 temperature-control 实现**,本次不改其接口契约

3. 接口契约

服务域 vehicleConvoy,路径前缀 _ROBOTAXI_HOST,段为 base(vehicleConvoy/base/v1/...)。鉴权:needLogin=true、needSign=false(对齐香氛/空调)。

响应遵循项目统一两层结构(见 src/utils/request.ts):{ code, msg, data },code===CODE_SUCCESS 视为成功,request 默认解包返回第二层 data。

3.1 查询座椅通风/加热状态

GET  /vehicleConvoy/base/v1/mini-program/cabin/seat/hv?orderId={orderId}

请求参数(query):

字段 类型 必填 说明
orderId string 是 订单 ID

响应示例:

{
  "code": 200,
  "msg": null,
  "data": {
    "seats": [
      { "position": "SECOND_ROW_LEFT",  "ventilation": 1, "ventilationLevels": 3, "heating": 0, "heatingLevels": 3 },
      { "position": "SECOND_ROW_RIGHT", "ventilation": 0, "ventilationLevels": 3, "heating": 3, "heatingLevels": 3 },
      { "position": "THIRD_ROW_LEFT",   "ventilation": 0, "ventilationLevels": 0, "heating": 0, "heatingLevels": 3 },
      { "position": "THIRD_ROW_RIGHT",  "ventilation": 0, "ventilationLevels": 0, "heating": 0, "heatingLevels": 3 }
    ]
  }
}

data.seats[] 字段说明:

字段 类型 说明
position string 座位标识:SECOND_ROW_LEFT / SECOND_ROW_RIGHT(目前仅支持二排,前端兼容三排 THIRD_ROW_*)
ventilation int 通风当前档位 0~3,0=关
ventilationLevels int 通风支持的总档数(如 3 即可设 0~3);0 表示不支持通风
heating int 加热当前档位 0~3,0=关
heatingLevels int 加热支持的总档数(如 3 即可设 0~3);0 表示不支持加热

3.2 设置座椅通风/加热

POST /vehicleConvoy/base/v1/mini-program/cabin/seat/hv

请求体:

字段 类型 必填 说明
orderId string/number 是 订单 ID
seats SeatSet[] 是 本次改动的座椅集合(**仅传被改动座椅**)
SeatSet.position string 是 座位标识
SeatSet.ventilationLevel int 是 目标通风档位 0~N
SeatSet.heatingLevel int 是 目标加热档位 0~N(互斥:与 ventilationLevel 至多一个非 0)

请求示例(仅下发改动的座椅,通风与加热一起下发):

{
  "orderId": 123456,
  "seats": [
    { "position": "SECOND_ROW_LEFT",  "ventilationLevel": 2, "heatingLevel": 0 },
    { "position": "THIRD_ROW_RIGHT",  "ventilationLevel": 0, "heatingLevel": 3 }
  ]
}

响应示例:

{ "code": 200, "msg": null, "data": null }

成功判定:code===CODE_SUCCESS 即视为下发成功,data 可为 null。


4. 交互流程

4.1 主流程(进入接驾态 → 打开面板 → 点击座椅按钮即时下发)

进入接驾态 (VehicleApproaching)
   │
   ├─ 查询座椅状态 GET .../cabin/seat/hv(面板打开时或进入接驾态时)
   │     ├─ 成功 → 渲染座椅区,按 levels 决定档位上限与按钮可用性
   │     └─ 失败 → 座椅区兜底(见 8)
   ├─ 空调温度/开关:沿用现有 temperature-control 初始化
   │
用户点击「空调」卡片(主体) → 打开「温度调节」面板
   │
用户点击某座椅的 通风/加热 按钮:
   │
   ├─ 计算下一档位:curr → (curr+1) % (levels+1)
   ├─ 互斥联动:通风非0 ⇒ 加热=0;加热非0 ⇒ 通风=0
   ├─ 该座椅按钮置灰防重复
   ├─ POST .../cabin/seat/hv
   │     payload: { orderId, seats: [{ position, ventilationLevel, heatingLevel }] }
   │
   ├─ 成功 → 该座椅档位更新 + Toast
   └─ 失败 → 该座椅状态回滚 + Toast"网络异常,请重试";面板保持
   │
(空调温度/开关:复用现有下发逻辑,防抖 500ms)

4.2 座椅按钮可用性分支

接口字段 通风按钮 加热按钮
ventilationLevels>0 & heatingLevels>0 可点,循环 0~V 可点,循环 0~H
ventilationLevels=0 置灰/隐藏 可点
heatingLevels=0 可点 置灰/隐藏
seats 为空 座椅区占位提示(见 8) —

5. 状态机

5.1 单座椅档位循环

通风:0 → 1 → 2 → … → V → 0    (V = ventilationLevels)
加热:0 → 1 → 2 → … → H → 0    (H = heatingLevels)

5.2 单座椅互斥联动(点击后、下发前计算)

用户操作 联动结果 下发 payload(该座椅)
通风 0→非0 加热强制 0 { ventilationLevel: n, heatingLevel: 0 }
加热 0→非0 通风强制 0 { ventilationLevel: 0, heatingLevel: n }
通风 →0(关) 加热保持 0 { ventilationLevel: 0, heatingLevel: 0 }
加热 →0(关) 通风保持 0 { ventilationLevel: 0, heatingLevel: 0 }

5.3 即时下发流程(单座椅)

[座椅按钮] --点击--> [该座椅 applying(置灰,禁点)]
                          │
                ┌─────────┴─────────┐
              成功                  失败
                │                   │
        档位更新 + Toast        该座椅状态回滚 + Toast"网络异常,请重试"
        (面板保持)              (面板保持)

6. 组件结构

方案 A:新建独立座椅组件,空调卡片改造为「展示 + 面板」。座椅与空调温控同处「温度调节」面板。

6.1 目录结构(建议)

src/robotaxi/pages/begin-route/components/calling-car-modules/
├── temperature-control.tsx              (改造:卡片展示态 + 卡片开关 + 打开面板)
├── climate-panel/                       (新增:温度调节面板容器,可选)
│   └── index.tsx                        面板:座椅区 + 空调温度选择器 + 开关
├── seat-hv-control/                     (新增:座椅通风加热)
│   └── index.tsx                        座椅区渲染 + 档位循环 + 互斥 + 即时下发
└── ...
src/robotaxi/pages/begin-route/components/css-folder/
├── climate-panel.less                   (新增)
└── seat-hv-control.less                 (新增)

面板容器 climate-panel 是否单独拆分可在编码时按复杂度决定;核心是座椅逻辑独立在 seat-hv-control。

6.2 组件职责

组件/文件 职责
TemperatureControl(改造) 卡片展示当前温度 + 卡片空调开关 + 点击打开面板;温度/开关下发逻辑复用现有
「温度调节」面板 承载座椅区 + 空调温度滚动选择器 + 面板内开关;底部弹窗动画复用 use-bottom-popup-animation
SeatHvControl(新增) 查询座椅状态、渲染座椅、档位循环、互斥联动、点击即时下发(仅改动座椅)
use-bottom-popup-animation 复用现有底部弹窗滑入/滑出动画

6.3 Props 设计(对齐空调/香氛)

type Props = {
  tripStatus: TripStatus;          // 仅 VehicleApproaching 展示
  orderId?: string;
  pageDidShowVersion?: number;     // 页面 didShow 版本,触发重查
  hasPreviousOrder?: boolean;      // 连环单时不展示/不下发
}

6.4 API 层

新增 src/robotaxi/api/seat-hv.ts:

querySeatHv(orderId)      // GET  .../cabin/seat/hv
setSeatHv(payload)        // POST .../cabin/seat/hv(seats 仅含改动座椅)

类型新增 src/robotaxi/api/types/seat-hv.ts:SeatHvItem、QuerySeatHvResponse、SetSeatHvPayload、SeatSetItem、SeatPosition。

6.5 座椅区视觉

  • 车内俯视图:**纯 CSS 布局 + 占位背景图**,座椅按钮绝对定位叠放(设计稿未提供切图,先用占位图)
  • 座位标签文案按 position 映射:SECOND_ROW_LEFT→「第二排左侧座椅」等

7. 埋点

沿用项目 xpReport(this.$app.xpReportSend({ pageId, buttonId, para })),接驾页 pageId 沿用 begin-route 现有值,buttonId 待与埋点侧确认,建议:

事件 时机 建议 para
打开温度调节面板 点击空调卡片 {}
座椅通风点击下发 点击通风按钮 { position, ventilationLevel }
座椅加热点击下发 点击加热按钮 { position, heatingLevel }
座椅下发结果 成功/失败回调 { position, ventilationLevel, heatingLevel, success }

空调温度/开关埋点沿用现有实现,不在本次新增范围。


8. 异常处理与边界

场景 处理
查询座椅状态失败 座椅区兜底:展示占位提示(如「座椅调节暂不可用」)或空态;不阻塞空调温控
seats 为空 座椅区展示占位提示,不渲染座椅按钮
ventilationLevels=0 该座椅通风按钮置灰/隐藏,仅保留加热
heatingLevels=0 该座椅加热按钮置灰/隐藏,仅保留通风
下发接口失败/超时 该座椅状态回滚到下发前 + Toast「网络异常,请重试」+ 面板保持
下发进行中重复点击 该座椅 applying 期间按钮置灰禁点,防重复下发
连环单(hasPreviousOrder=true) 空调卡片与座椅区均不展示/不查询/不下发(对齐空调,避免影响上一单乘客)
非接驾态(tripStatus≠VehicleApproaching) 组件返回 null,不展示
面板打开期间订单切换 按 orderId 重新查询并刷新状态,避免串单

9. 落地文件清单

文件 类型 说明 落地
src/robotaxi/api/seat-hv.ts 新增 querySeatHv / setSeatHv ✅
src/robotaxi/api/types/seat-hv.ts 新增 座椅通风加热请求/响应类型 ✅
.../calling-car-modules/seat-hv-control/index.tsx 新增 入口 + 座椅面板 + 档位循环 + 互斥 + 即时下发 ✅
.../css-folder/seat-hv-control.less 新增 座椅区样式(俯视图 + 按钮) ✅
.../components/calling-car.tsx 改 挂载 <SeatHvControl />(空调卡片下方) ✅

本期实现说明:座椅通风加热作为**独立组件 SeatHvControl** 落地(含自带入口 + 底部弹窗面板),未改动已上线的 temperature-control.tsx,降低回归风险。温度选择器/空调开关合并进「温度调节」面板(方案 A 完整形态)作为**后续迭代**,待设计切图到位后再合并;当前空调仍沿用现有内联卡片,座椅入口以独立一行挂在其下。


10. 已决策事项(需求对齐结论)

  1. 方案 A:空调卡片改为「展示 + 点击打开『温度调节』面板」,卡片上保留空调总开关;座椅区在面板内。
  2. 座椅渲染:按接口返回渲染,兼容二/三排;ventilationLevels=0 通风置灰/隐藏仅留加热。
  3. 档位循环:0→1→…→N→0,上限 N 由该座椅 ventilationLevels/heatingLevels 决定。
  4. 互斥:同座椅通风与加热至多一个非 0;开一方则另一方自动归 0,一起下发。
  5. 下发范围:仅下发被改动的座椅(seats 数组含该座椅 ventilation+heating 两字段)。
  6. 下发时机:点击按钮即时下发,无独立"应用"按钮。
  7. 空调下发:复用现有 setTemperature / airConditionerUnlock 及温区/防抖逻辑。
  8. 座椅俯视图:纯 CSS 布局 + 占位背景图 + 绝对定位。
  9. 接口鉴权:needLogin=true、needSign=false。

11. 待确认项(进入编码前建议对齐)

  1. 埋点 pageId / buttonId 的具体数值(与埋点侧对齐)。
  2. 座椅区俯视图、通风/加热图标、档位视觉(不同档位的视觉呈现)的正式设计切图与色值。
  3. 下发成功后的 Toast 文案(如「已设置第二排左侧座椅通风2档」是否需要,或统一「设置已应用至车辆」)。
  4. 温度滚动选择器的具体交互(是否复用现有步进逻辑仅换 UI,还是全新滚动组件)——本期倾向复用现有下发逻辑,仅调整展示。
  5. 查询座椅状态失败时座椅区的确切占位文案。