|
|
# 查价系统 · 宿主 postMessage 对接说明
|
|
|
|
|
|
**版本**:1.1
|
|
|
**更新日期**:2026-07-24
|
|
|
**适用对象**:在 iframe 中嵌入查价页的宿主系统(如 ShipRo / 元子侧适配方)
|
|
|
**嵌入页**:`/embed-demo`(须先按 `嵌入对接文档.md` 完成 API Key 直登)
|
|
|
|
|
|
> 原则:**由查价系统定义「要填什么 / 查完回什么」**;宿主按本协议发 `postMessage` 预填、收结果。字段与程序内部询价结构对齐,宿主无需再猜哪些字段可用。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 0. ShipRo 联调要点(v1.1)
|
|
|
|
|
|
### 0.1 URL 直进 MotherShip 登录后表单(方案 A)
|
|
|
|
|
|
```text
|
|
|
https://if.dev.51track.vip/embed-demo?login_type=api_key&api_key=<Key>&embed=1&module=MS_LOGGED_IN
|
|
|
```
|
|
|
|
|
|
等价:`entry=ms_logged_in`
|
|
|
|
|
|
登录成功后**跳过** MotherShip/Flock 卡片与「免账号/登录后」点选,直接进入「创建新货件」。须该客户已绑定 MotherShip 官网账密,否则停在账密面板。
|
|
|
|
|
|
### 0.2 fill 可晚到 / 可重复 / 先缓存再灌表(方案 B)
|
|
|
|
|
|
- 收到 `chajia:fill` **立刻缓存** `payload.form`(不要求当前已在表单页)。
|
|
|
- 切入 `module` 后,表单挂载时写入 UI;用户在表单页再点「填入数据」会**再次**灌表(`seq` 递增)。
|
|
|
- 先回 `chajia:fill-ack`(`ok: true`,缓存确认);表单真正写入后再回一次 ack(`ui_applied: true`)。
|
|
|
- **不依赖**宿主必须先等 `chajia:ready`(ready 仍会发,便于时序;晚到 fill 同样有效)。
|
|
|
|
|
|
### 0.3 `MS_LOGGED_IN` 地址可只传整段文本
|
|
|
|
|
|
宿主可不传 `place_id` / `mothership_option_id`。查价页把 `formatted_address` 或 `street` 写入提货/送货**搜索框**;用户须点选联想确认后才能「继续」。`cargo_lines` 写入货物行(inch/lb);空数组不改货物。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. 总览
|
|
|
|
|
|
```text
|
|
|
宿主页面
|
|
|
└─ iframe → .../embed-demo?...&embed=1[&module=MS_LOGGED_IN]
|
|
|
├─ iframe → 宿主:chajia:ready / chajia:fill-ack / chajia:quote-result / chajia:error
|
|
|
└─ 宿主 → iframe:chajia:select-module / chajia:fill / chajia:ping
|
|
|
```
|
|
|
|
|
|
| 方向 | 类型 | 作用 |
|
|
|
|------|------|------|
|
|
|
| iframe→宿主 | `chajia:ready` | 会话就绪(可开始发 fill;非强制前置) |
|
|
|
| 宿主→iframe | `chajia:select-module` | 切入四模块之一 |
|
|
|
| 宿主→iframe | `chajia:fill` | 按模块写入/缓存表单字段(**必带 module**,可重复) |
|
|
|
| 宿主→iframe | `chajia:scroll-quotes` | 请求 iframe 滚到「选择承运商」区(私卡出报价后) |
|
|
|
| iframe→宿主 | `chajia:fill-ack` | 已缓存;表单写入后再带 `ui_applied` |
|
|
|
| iframe→宿主 | `chajia:quote-result` | 查价结束(成功/失败/过期);**及** MS 侧栏点选承运商时再次推送(带 `selected`) |
|
|
|
| iframe→宿主 | `chajia:quote-save` | 用户点「保存本次询价记录」(可选;写快照) |
|
|
|
| iframe→宿主 | `chajia:scroll-top` | 进入二级详情等:请求宿主把查价 iframe 区域滚到视口顶部 |
|
|
|
| iframe→宿主 | `chajia:error` | 协议层错误(缺 module 等) |
|
|
|
| iframe→宿主 | `chajia:module-changed` | 用户或宿主切换了模块 |
|
|
|
|
|
|
信封统一格式:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"source": "chajia",
|
|
|
"version": 1,
|
|
|
"type": "chajia:fill",
|
|
|
"request_id": "宿主生成的 UUID,可选,用于配对",
|
|
|
"module": "MS_LOGGED_IN",
|
|
|
"payload": {}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
- **必须**校验 `source === "chajia"` 且 `version === 1`,忽略其它来源。
|
|
|
- `targetOrigin`:生产建议写成查价域名;联调可用 `*`。
|
|
|
- 仅当页面在 **iframe** 中时,查价页才会向 `parent` 回传。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2. 模块 ID(必须区分)
|
|
|
|
|
|
与 OpenAPI 路线一致,**四选一、互斥**:
|
|
|
|
|
|
| module | 含义 | 门户表现 |
|
|
|
|--------|------|----------|
|
|
|
| `MS_GUEST` | MotherShip 免账号 | MotherShip → 免账号 |
|
|
|
| `MS_LOGGED_IN` | MotherShip 登录后 | MotherShip → 登录后(须已绑官网账密) |
|
|
|
| `FLOCK_GUEST` | Flock 免账号 | Flock → 免账号 |
|
|
|
| `FLOCK_LOGGED_IN` | Flock 登录后 | Flock → 登录后(须已绑官网账密) |
|
|
|
|
|
|
`chajia:fill` **必须**带 `module`。不同模块的 `payload.form` 字段不同,不可混用。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 3. 宿主 → iframe:预填(按模块)
|
|
|
|
|
|
### 3.1 通用写法
|
|
|
|
|
|
```js
|
|
|
iframe.contentWindow.postMessage(
|
|
|
{
|
|
|
source: "chajia",
|
|
|
version: 1,
|
|
|
type: "chajia:fill",
|
|
|
request_id: crypto.randomUUID(),
|
|
|
module: "MS_LOGGED_IN",
|
|
|
payload: {
|
|
|
select_module: true,
|
|
|
form: { /* 见 3.3 */ }
|
|
|
},
|
|
|
},
|
|
|
"https://if.dev.51track.vip",
|
|
|
);
|
|
|
```
|
|
|
|
|
|
也可把 `form` 直接作为 `payload`(无 `select_module` 包装)。
|
|
|
|
|
|
### 3.2 `MS_GUEST`
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"pickup": { "street": "1234 Warehouse Blvd", "city": "Los Angeles", "state": "CA" },
|
|
|
"delivery": { "street": "5678 Distribution Dr", "city": "Dallas", "state": "TX" },
|
|
|
"weight": { "value": 500, "unit": "lb" },
|
|
|
"dimensions": { "length": 48, "width": 40, "height": 48, "unit": "in" },
|
|
|
"pallet_count": 2,
|
|
|
"cargo_type": "general_freight"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3.3 `MS_LOGGED_IN`(ShipRo 现网:整段文本 + cargo_lines)
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"pickup_address": {
|
|
|
"street": "整段发货地址原文",
|
|
|
"formatted_address": "整段发货地址原文"
|
|
|
},
|
|
|
"delivery_address": {
|
|
|
"street": "整段目的地址原文",
|
|
|
"formatted_address": "整段目的地址原文"
|
|
|
},
|
|
|
"cargo_lines": [
|
|
|
{
|
|
|
"cargo_type": "pallet",
|
|
|
"quantity": 1,
|
|
|
"weight_lb": 100,
|
|
|
"length_in": 48,
|
|
|
"width_in": 40,
|
|
|
"height_in": 48
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
| 字段 | 说明 |
|
|
|
|------|------|
|
|
|
| 地址 | `formatted_address` 或 `street` → 搜索框;可不传 place_id(用户点选联想) |
|
|
|
| cargo_lines | inch/lb;**空数组不改**货物 |
|
|
|
| ready_* / accessorials | 可选;不传用页默认 |
|
|
|
|
|
|
### 3.4 仅切模块
|
|
|
|
|
|
```json
|
|
|
{ "source": "chajia", "version": 1, "type": "chajia:select-module", "module": "MS_LOGGED_IN" }
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 4. 查价结果
|
|
|
|
|
|
`chajia:quote-result` → `payload.quotes[].carrier` / `final_total`;失败读 `error_message`。
|
|
|
宿主若需明确区分“官网原价”和“客户加价后展示价”,可直接读:
|
|
|
|
|
|
- `base_total_before_markup`:未加价原价
|
|
|
- `customer_markup_amount`:该客户加价金额
|
|
|
- `customer_final_total`:该客户最终展示价(等同当前 `final_total`)
|
|
|
- `is_customer_markup_applied`:是否已叠加客户加价
|
|
|
- `pricing_mode = "customer_final"`:宿主可直接按客户最终价展示
|
|
|
|
|
|
### 4.1 侧栏点选承运商即时同步(宿主锁价)
|
|
|
|
|
|
MotherShip 登录后侧栏(`MS_LOGGED_IN`):
|
|
|
|
|
|
1. 用户点选任意承运商卡片、默认选中出价、或切换保障导致生效费率变化时
|
|
|
2. iframe 再次发出 **`chajia:quote-result`**(**不依赖**绿色「保存本次询价记录」)
|
|
|
3. `payload.quotes[]` 中当前生效那条 **`selected: true`**,其余 `false`
|
|
|
4. 宿主取 `selected === true` 的档(无则兜底首条)即可锁定承运商与 `final_total` / `customer_final_total`
|
|
|
5. 绿色「保存」仍可选发 `chajia:quote-save`(`action: "save"` + 同样 `selected`),仅用于写快照;私卡询价跳转**不必**等保存
|
|
|
|
|
|
触发时机代码入口:`onSelectQuote` → `notifyHostQuoteSelection` → `reportQuoteResult`。
|
|
|
|
|
|
### 4.2 出报价后视野定位(需求3)
|
|
|
|
|
|
1. **查价页**:报价列表出现后自动 `scrollIntoView` 到侧栏「选择承运商」(`#ms-choose-carrier`);「已选」角标与承运商名同一行,不遮挡标题
|
|
|
2. **宿主**:收到 `quote-result` 后左侧滚到「当前选中报价」,并可向 iframe 发:
|
|
|
|
|
|
```json
|
|
|
{ "source": "chajia", "version": 1, "type": "chajia:scroll-quotes" }
|
|
|
```
|
|
|
|
|
|
iframe 收到后再次滚承运商区。需查价页与 cc-client **一起部署** 才完整。
|
|
|
|
|
|
### 4.3 「继续」进入二级后置顶
|
|
|
|
|
|
用户在一级点「继续」进入提货/送货详情时:
|
|
|
|
|
|
1. **查价页**:iframe 内 `scrollTo(0)` + 根节点 `scrollIntoView`
|
|
|
2. **同时**向宿主发:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"source": "chajia",
|
|
|
"version": 1,
|
|
|
"type": "chajia:scroll-top",
|
|
|
"module": "MS_LOGGED_IN",
|
|
|
"payload": { "reason": "logged-in-details" }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
3. **宿主**:收到后将私卡询价弹窗/iframe 容器滚到视口顶部
|
|
|
|
|
|
需查价页与 cc-client 一并更新。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 5. 联调顺序(ShipRo)
|
|
|
|
|
|
1. iframe:`...&embed=1&module=MS_LOGGED_IN`
|
|
|
2. 进「创建新货件」后点「填入数据」→ `chajia:fill`
|
|
|
3. 收 `fill-ack`(缓存)→ 再收 `ui_applied` ack
|
|
|
4. 用户点选地址联想 → 继续 → `chajia:quote-result`(出票)
|
|
|
5. 用户在侧栏点选承运商 → 再收 `chajia:quote-result`(带 `selected`)→ 宿主锁价后可「确认询价并新建集装箱」
|
|
|
|
|
|
亦可打开后立即 fill(先缓存,表单挂载后灌入)。
|
|
|
|
|
|
| 能力 | 状态 |
|
|
|
|------|------|
|
|
|
| URL `module` / `entry` 直进 | 已实现 |
|
|
|
| fill 缓存 + 可重复 + 晚到灌表 | 已实现 |
|
|
|
| MS_LOGGED_IN 搜索框 + cargo_lines | 已实现 |
|
|
|
| MS_GUEST 预填 / quote-result | 已实现 |
|
|
|
| 侧栏点选承运商 → quote-result + selected | 已实现 |
|
|
|
| 出报价后自动滚到「选择承运商」/ scroll-quotes | 已实现 |
|
|
|
| 「继续」进二级 → scroll-top 置顶 | 已实现 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 6. 文档关系
|
|
|
|
|
|
| 文档 | 用途 |
|
|
|
|------|------|
|
|
|
| `嵌入对接文档.md` | iframe URL、API Key、`module` |
|
|
|
| 本文 | postMessage |
|
|
|
| `api对接文档.md` | OpenAPI |
|
|
|
|
|
|
代码:`lib/embed/host-bridge.ts`、`parseEmbedEntryModule`(`lib/embed/sso-params.ts`)、`map-quotes-selection.ts`、`mothership-logged-in-quote-sidebar` / `embedded-quote-widget`。
|