| 项 | 内容 |
|---|---|
| 功能名称 | 车载香氛系统(智能香氛远程控制) |
| 所属模块 | Robotaxi 接驾界面(begin-route / 行程开始页) |
| 目标分支 | rt/feature/v2.0_ui_xj_730 |
| 文档状态 | 概要设计(需求已对齐,待评审后进入编码) |
| 关联能力 | 空调温控(temperature-control.tsx)—— 本功能与之左右并排,复用同一套远控范式 |
Robotaxi 接驾阶段(车辆即将到达,TripStatus.VehicleApproaching)已支持空调温控、车门/后备箱远程控制。本次新增 车载香氛 远程控制能力,让乘客在上车前即可通过小程序开启/关闭车内香氛并选择香型,提升乘车体验。
香氛能力与空调对齐:
- 同为「接驾态可操作」的车机远控
- 同样抽取为**独立组件**,挂载在接驾卡片区
- UI 上与空调卡片**左右并排**(左空调、右香氛)
接驾卡片区采用**左右双卡片**布局:
┌─────────────┬─────────────┐
│ 空调温控 │ 智能香氛 │
│ (左卡片) │ (右卡片) │
│ │ 主标题:智能香氛 │
│ │ 副标题:木质雪松 / 已关闭 │
└─────────────┴─────────────┘
switchOn + currentScent 映射:switchOn=false → 已关闭switchOn=true → 当前香型名称(如 香杉影木)车载香氛·畅享出行),右下角箭头 >面板以底部弹窗形式呈现,覆盖接驾界面下半部分。**面板无总开关 Switch、无「应用到车辆」按钮**,改为「点击香型即时下发」:
┌───────────────────────────────┐
│ 车内香氛 [ × ]│ ← 顶部:标题居中 + 关闭
├───────────────────────────────┤
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ 香杉 │ │ 白色 │ │ 琥珀 │ │ ← 香型卡片(竖排名 + 香调描述)
│ │ 影木 │ │ 清幽 │ │ 光橘 │ │
│ │沉稳·笃定│ │清新·淡雅│ │愉悦·活力│ │
│ └──────┘ └──────┘ └──────┘ │
└───────────────────────────────┘
香型选择区展示逻辑:
- 车端返回的 availableScents:正常显示、可点击
- 车端未返回的香型:不展示
- availableScents 为空:显示占位文案「当前车辆不支持香型选择」
- 香调描述(如「沉稳·笃定」):接口 desc 优先,缺失时按 id 走前端本地兜底表
当前生效态:
- switchOn=true 时,currentScent 命中的香型卡片高亮(米金底 + 金边)
- switchOn=false 时,无高亮
用户**点击香型卡片即触发下发**,无需二次确认:
switchOn=true + channelId)switchOn=false,channelId 可不传)applying 锁 + 香型卡片置灰)设置已应用至车辆 / 关闭 已关闭香氛已关闭)网络异常,请重试关闭方式:① 点击已选中香型关闭香氛功能;② 点击关闭 X 或蒙层收起面板;③ 再次点击入口卡片收起面板。
服务域 vehicleConvoy,路径前缀 _ROBOTAXI_HOST。注意本功能接口为 base 段(vehicleConvoy/base/v1/...),与空调(vehicleConvoy/api/v1/...)不同。鉴权:needLogin=true、**needSign=false**(对齐空调,不做签名)。
响应遵循项目统一两层结构(见
src/utils/request.ts):{ code, msg, data },code===CODE_SUCCESS(200/0)视为成功,request默认解包返回第二层data。
GET /vehicleConvoy/base/v1/mini-program/cabin/fragrance?orderId={orderId}
请求参数(query):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | string | 是 | 订单 ID |
响应示例:
{
"code": 200,
"msg": null,
"data": {
"fragrance": {
"switchOn": true,
"currentScent": "SANDALWOOD",
"availableScents": [
{ "channelId": "1", "id": "SANDALWOOD", "name": "香杉影木" },
{ "channelId": "2", "id": "WHITE", "name": "白色清幽" },
{ "channelId": "3", "id": "AMBER", "name": "琥珀光橘" }
]
}
}
}
data 字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| fragrance.switchOn | boolean | 香氛总开关的真实状态(决定 Switch 初始态) |
| fragrance.currentScent | string | 当前香型的 id(用于匹配默认选中项);关闭时可为空 |
| fragrance.availableScents | Scent[] | 车端可用香型列表;空数组 = 不支持香型选择 |
| Scent.channelId | string | 香型通道 ID,**下发时使用此字段** |
| Scent.id | string | 香型业务标识(与 currentScent 对应) |
| Scent.name | string | 香型展示名称 |
POST /vehicleConvoy/base/v1/mini-program/cabin/fragrance
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | string/number | 是 | 订单 ID |
| switchOn | boolean | 是 | 目标开关状态 |
| channelId | string | 否 | 目标香型通道 ID;switchOn=true 时必传,switchOn=false 时可不传 |
请求示例:
{ "orderId": 123456, "channelId": "1", "switchOn": true }
响应示例:
{ "code": 200, "msg": null, "data": null }
成功判定:code===CODE_SUCCESS(HTTP 200 且业务码成功)即视为下发成功,data 可为 null。
进入接驾态 (VehicleApproaching)
│
├─ 查询香氛状态 GET .../cabin/fragrance
│ ├─ 成功 → 卡片主标题按 switchOn+currentScent 映射
│ │ (关→"已关闭" / 开→香型名);生效香型高亮
│ └─ 失败 → 按"已关闭"兜底(见 8.异常处理)
│
用户点击「智能香氛」卡片
│
├─ 底部弹窗滑入(复用 use-bottom-popup-animation)
├─ 香型区渲染 availableScents;生效香型高亮
│
用户点击某香型卡片(即时下发):
│
├─ 点非生效香型 → nextSwitchOn=true, channelId=该香型
├─ 点已生效(高亮)香型 → nextSwitchOn=false(关闭香氛)
├─ 下发期间置灰防重复点击
├─ POST .../cabin/fragrance
│ payload: { orderId, switchOn, channelId? }
│
├─ 成功 → 高亮态更新 + 卡片主标题更新
│ + Toast(开:"设置已应用至车辆" / 关:"已关闭香氛");面板保持
└─ 失败 → 状态回滚 + Toast"网络异常,请重试";面板保持
| 车端返回 | 展示 |
|---|---|
| availableScents 非空 | 渲染各香型,可点击选择 |
| availableScents 为空 | 占位文案「当前车辆不支持香型选择」,隐藏香型项 |
| 未在 availableScents 中的香型 | 不展示 |
| 点击对象 | 下发 payload | 结果 |
|---|---|---|
| 非当前生效香型 | { switchOn: true, channelId } |
开启香氛并切换到该香型,该卡片高亮 |
| 当前生效(高亮)香型 | { switchOn: false } |
关闭香氛,取消高亮 |
| 下发进行中(applying) | — | 忽略点击(置灰防重复) |
[香型卡片] --点击--> [applying(置灰,禁点)]
│
┌─────────┴─────────┐
成功 失败
│ │
高亮/标题更新+Toast 状态回滚+Toast"网络异常,请重试"
(开:"设置已应用至车辆" (面板保持)
关:"已关闭香氛")
面板保持
| 数据来源 | 主标题文案 |
|---|---|
| switchOn=false | 已关闭 |
| switchOn=true | 当前生效香型 name(如 香杉影木) |
| 查询失败/无数据 | 按「已关闭」兜底,主标题展示 已关闭(见 8) |
对齐空调组件(temperature-control.tsx)的独立组件模式,香氛抽取为**独立可复用组件**。
src/robotaxi/pages/begin-route/components/calling-car-modules/
├── temperature-control.tsx (现有:空调,左卡片)
├── fragrance-control/ (新增:香氛,右卡片)
│ └── index.tsx 主组件(卡片入口 + 面板 + 即时下发编排)
└── ...
src/robotaxi/pages/begin-route/components/css-folder/
└── fragrance-control.less (新增:香氛样式)
| 组件/文件 | 职责 |
|---|---|
FragranceControl(index) |
卡片入口渲染、主标题状态、面板开合、进入接驾态查询、点击香型即时下发 |
use-bottom-popup-animation |
复用现有底部弹窗滑入/滑出动画 |
type Props = {
tripStatus: TripStatus; // 仅 VehicleApproaching 展示
orderId?: string;
pageDidShowVersion?: number; // 页面 didShow 版本,触发重查
hasPreviousOrder?: boolean; // 连环单时不展示/不下发
}
在 calling-car.tsx 车辆信息卡片区,将空调与香氛**左右并排**:
- 左:<TemperatureControl />(现有)
- 右:<FragranceControl />(新增)
布局改造需同步调整
css-folder/calling-car.less的卡片容器为左右布局。可参考旁支75689edaa的temperature-fragrance-control左右双卡片样式作为视觉参照(该旁支未合入,仅作参考)。
在 src/robotaxi/api/ 下新增香氛接口(建议 api/car-control.ts 追加,或新建 api/fragrance.ts):
queryCabinFragrance(orderId) // GET .../cabin/fragrance
setCabinFragrance(payload) // POST .../cabin/fragrance
类型定义追加至 api/types/begin-route.ts(或新建 api/types/fragrance.ts):CabinFragranceResponse、FragranceScent、SetFragrancePayload。
沿用项目 xpReport 双系统(this.$app.xpReportSend({ pageId, buttonId, para })),接驾页 pageId 沿用 begin-route 现有值,buttonId 待与埋点侧确认,建议:
| 事件 | 时机 | 建议 para |
|---|---|---|
| 香氛卡片点击 | 打开/关闭面板 | { switchOn } |
| 香型点击下发 | 点击某香型卡片即时下发 | { channelId, switchOn } |
| 下发结果 | 成功/失败回调 | { switchOn, channelId, success } |
具体 pageId / buttonId 数值需与埋点管理侧对齐后补充。
| 场景 | 处理 |
|---|---|
| 查询香氛状态失败 | 按「已关闭」兜底:卡片主标题展示 已关闭,无高亮;不阻塞其他卡片 |
| availableScents 为空 | 香型区展示「当前车辆不支持香型选择」,无可点击香型 |
| currentScent 不在 availableScents | 开启态回退取列表第一项作为生效香型 |
| 下发接口失败/超时 | 状态回滚到下发前 + Toast「网络异常,请重试」+ 面板保持 |
| 连环单(hasPreviousOrder=true) | 不展示香氛卡片、不查询、不下发(对齐空调,避免影响上一单乘客) |
| 非接驾态(tripStatus≠VehicleApproaching) | 组件返回 null,不展示 |
| 下发进行中重复点击 | applying 期间香型卡片置灰禁点,防重复下发 |
| 面板打开期间订单切换 | 按 orderId 重新查询并刷新状态,避免串单 |
| 文件 | 类型 | 说明 | 落地 |
|---|---|---|---|
src/robotaxi/api/fragrance.ts |
新增 | queryCabinFragrance / setCabinFragrance |
✅ |
src/robotaxi/api/types/fragrance.ts |
新增 | 香氛请求/响应类型 | ✅ |
.../calling-car-modules/fragrance-control/index.tsx |
新增 | 香氛卡片入口 + 底部弹窗面板主组件 | ✅ |
.../css-folder/fragrance-control.less |
新增 | 香氛卡片与面板样式 | ✅ |
.../components/calling-car.tsx |
改 | 挂载 <FragranceControl />,与空调左右并排 |
✅ |
.../css-folder/calling-car.less |
改 | 新增 .car-card-comfort 左右布局容器 |
✅ |
src/robotaxi/components/demo/index.tsx |
改(可选) | Demo 页增加香氛卡片渲染,便于联调 | 待定 |
面板与卡片入口合并在单一
index.tsx(面板结构简单,未单拆fragrance-panel.tsx)。
channelId(非 scentId/id)。请求体 { orderId, channelId, switchOn }。availableScents 第一项。switchOn=false,channelId 可不传。已关闭,开→当前香型名。temperature-control 模式),便于复用与挂载。needLogin=true、needSign=false(对齐空调,不做签名)。已关闭,面板 Switch 关闭态。