feat: 完成出港查询页面5.5寸手机端适配
- 列表/详情/运单追踪三页同名布局双变体(平板移 layout-sw600dp/,Kotlin 零设备分支) - 列表:搜索行 + 三格统计(总票数/已入库/已离港,已加载数据客户端计数)+ 状态标签卡片 (已离港/已入库/未入库,前端按 fclose/opDate 推断)+ 9 项底部筛选弹层(特码改下拉) - 详情:3 Tab 复用 ViewPager2+Fragment,PhoneKvItem 网格;入库件数/重量按 warehouseList 求和 - 运单追踪:公共页 LogDetailActivity 双变体首例,手机竖排时间线动态构建,平板横向步骤条不动 - 修复 PhoneFilterPanel 筛选项超屏时底部按钮被挤出屏幕(内容插槽加 ScrollView) - PhoneKvItem 新增 tagColor 绿色高亮(海关放行),占位符不套标签底 - 手机首页国际 Tab 加出港查询入口;后端缺口 #21/#22 登记问题清单 - 双端实机验证通过(内网真实数据 + Proxyman Map Local 补验未入库兜底态) - skill 新增 references/proxyman.md:抓包/直调 A/B/Mock 与 api-doc 配合规范 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,10 @@ python3 $S/design_shots.py --html "…/出库交接.html" --out /tmp/design \
|
||||
返回同一批数据,一度误判为「后端不支持」;后查 api-doc 才发现正确参数名是 `handoverState`——
|
||||
后端对不认识的参数是**静默忽略**,不报错,实测只能证明「这个名字不对」,证不出「后端没这能力」。
|
||||
|
||||
「参数是否真的生效」的 A/B 验证优先走 Proxyman MCP(`create_compose_http_from_flow` 复制
|
||||
真实请求改参重发,token 自动带上),分工与流程见 `references/proxyman.md`:
|
||||
**api-doc 管契约(该有什么),Proxyman 管事实(实际发/回了什么)**。
|
||||
|
||||
核对完把结论落两处(模板与格式见 `references/api-doc.md`):
|
||||
|
||||
- 后端真实缺口(缺入参/缺出参/数据不回填)→ 记入 `documents/后端接口对接问题清单.xlsx`,
|
||||
@@ -232,9 +236,12 @@ $P <phone> tap-text "已交接" # 按文字点击,比手算坐标可靠
|
||||
$P <phone> texts # 用文字变化断言点击确实生效(如底部条应消失)
|
||||
$P <phone> shot /tmp/p1.png
|
||||
$P <phone> crash # 必须为 0
|
||||
$P <phone> req pageQuery # 核对请求体参数确实带上了新字段
|
||||
```
|
||||
|
||||
核对请求/响应体优先用 Proxyman MCP(`filter_flows` + `get_flow_detail`,比 `req` 的
|
||||
logcat 抓包完整可靠;模拟器代理一行命令接入,见 `references/proxyman.md`);
|
||||
Proxyman 不可用时兜底 `$P <phone> req pageQuery`。
|
||||
|
||||
逐项走查:列表/卡片两种形态、详情页、筛选弹层(重置+确认)、Tab 切换、单选与全选联动、
|
||||
下拉真实取数、扫码入口。每步 `crash` 都要为 0。
|
||||
|
||||
@@ -253,7 +260,12 @@ $P <phone> req pageQuery # 核对请求体参数确实带上了新字段
|
||||
**空值兜底**(真实数据空字段很常见——出库交接就出现过「库位」为空时橙色高亮标签只剩一个孤零零的
|
||||
小色块,需要空值时不套标签底)。
|
||||
|
||||
服务端当天没数据时(列表空)先放宽条件重查;仍为空就临时在 Activity 里注入样例 Bean 截图比对:
|
||||
服务端当天没数据时(列表空)先放宽条件重查;仍为空、或要覆盖真实数据凑不出的形态
|
||||
(空字段、超长文本、罕见状态标签),**首选 Proxyman Map Local 造 mock**——零代码改动、
|
||||
零重构建,见 `references/proxyman.md` 场景 3(2026-07-31 出港查询用它一次验完
|
||||
已离港/已入库/未入库三种卡片形态)。验证完 `delete_rule` + 清模拟器代理。
|
||||
|
||||
Proxyman 不可用时才退回老办法:Activity 里临时注入样例 Bean 截图比对:
|
||||
|
||||
```kotlin
|
||||
// import com.lukouguoji.module_base.ktx.commonAdapter
|
||||
@@ -287,6 +299,7 @@ binding.rv.postDelayed({
|
||||
## 参考文件
|
||||
|
||||
- `references/api-doc.md` — api-doc MCP 连接与逐接口核对流程、后端缺口记录规范、已核对字段语义(阶段 2 前读)
|
||||
- `references/proxyman.md` — Proxyman MCP 抓包/直调 A/B/Map Local mock 的接入与四个场景(阶段 2、5、6 用;与 api-doc 的分工:契约 vs 事实)
|
||||
- `references/components.md` — 7 个手机版公共组件的属性表、用法与封装新组件的规矩(写布局前读)
|
||||
- `references/pad-safety.md` — 布局变体约束、Pad 零破坏模式、易崩点(写代码前读)
|
||||
- `references/verification.md` — 双端 adb 命令、登录与直启、已知环境坑(验证前读)
|
||||
|
||||
@@ -77,6 +77,14 @@ curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||||
分单补充收发货人信息**后端完全没有对应接口**(`search_endpoints` 对 `IntExpArrive`/`HaWb`
|
||||
关键字均搜不到分单更新接口,只有国际进港的 `IntImpAirManifest/complete`),
|
||||
实机调用拟定路径 `IntExpArrive/haWb/supplement` 确认 404(缺口 #19)
|
||||
- `IntExpSearch`(出港查询,2026-07-31 内网实测):查询入参 `outState`/`fno`/`dest`/`spCode`/`awbType`/
|
||||
`businessType`/`goodsCn`/`beginDate`/`endDate`/`agentCode`/`wbNo` 全部真实生效(平板既有口径);
|
||||
文档定义的 `isFclose`(筛选航班是否关闭)**pageQuery 与 pageQueryTotal 均静默忽略**(A/B 对照
|
||||
全量 72 票与单日两组,加参前后结果完全一致,缺口 #21);`pageQueryTotal` 出参无已入库/已离港
|
||||
分项计数(#21);`detail` 出参 Map 无 `lockStatus`(锁定状态)与计费重量字段(#22);
|
||||
详情「入库件数/入库重量」= `warehouseList` 各批次 pc/weight 求和(mock 与实测均吻合);
|
||||
卡片状态推断口径:`fclose` 非空=已离港、否则 `opDate` 非空=已入库;
|
||||
特码字典沿用 `DictUtils.getSpecialCodeList(flag=1, ieFlag="")`(ieFlag 必须空串)
|
||||
- `IntExpCheckIn`(出港计重,2026-07-29 内网实测):`likeNo` **并非任意模糊**——按 8 位 `no`
|
||||
匹配(11 位 wbNo 或部分号均 0 条),11 位全号必须走 `wbNo`;`checkIn`/`checkInList`
|
||||
文档有定义但 `pageQuery`/`pageQueryTotal` **均静默忽略**(缺口 #12,待计重/计重中计数依赖);
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
| `PhoneFilterPanel` | 底部筛选弹层(遮罩 + 面板 + 重置/确认) | `title`、`panelVisible`、`setOnResetClick`、`setOnConfirmClick`、`setOnDismissClick` |
|
||||
| `PhoneBottomBar` | 底部操作条(全选 + 已选统计 + 主按钮) | `allChecked`、`countText`、`actionText`、`setOnAllCheckClick`、`setOnActionClick` |
|
||||
| `PhoneStatBox` | 卡片内 2~4 列统计框,未传 label 的列自动隐藏 | `label1..4`、`value1..4` |
|
||||
| `PhoneKvItem` | 详情页字段项(灰标签 + 数值) | `title`、`value`、`tag`(true → 橙色高亮标签) |
|
||||
| `PhoneKvItem` | 详情页字段项(灰标签 + 数值) | `title`、`value`、`tag`(true → 高亮标签)、`tagColor`("orange" 默认 / "green" 放行正常态);占位符 "-"/"--" 不套标签底 |
|
||||
| `PhoneFormRow` | 行式表单项(左标签 minWidth 110dp + 右值 + 1px 分隔线),覆盖新增/修改/详情三态表单 | `title`、`hint`、`type`(`PhoneFormRowType.INPUT`/`SELECT`/`DATE`/`TEXT`)、`value`(双向)、`list`(SELECT 选项)、`required`、`enable`、`numeric`、`setRefreshCallBack` |
|
||||
|
||||
`PhoneFormRow` 要点:SELECT 内置 XPopup 底部选择面板(点击整行弹出,无需页面代码);TEXT 为只读右对齐、
|
||||
空值自动占位 "--"(详情态);type 用三元按 `pageType` 切换即可一套布局覆盖三态页面(参考
|
||||
`app/res/layout/activity_uld_edit.xml`)。`PhoneBottomBar` 的 `actionDanger="@{true}"` 切浅红删除按钮。
|
||||
|
||||
`PhoneFilterPanel` 内容插槽已内置 ScrollView(2026-07-31 加):筛选项少时行为不变;
|
||||
筛选项多到超屏(出港查询 9 项)时内容区收缩滚动,底部「重置/确认」按钮始终可见——
|
||||
此前无滚动时按钮会被挤出屏幕外且毫无报错,只表现为"点不到确认"。
|
||||
|
||||
⚠️ 两个已踩坑:
|
||||
- **FAB 盖住弹层**:浮动按钮带 elevation 时会浮在 `PhoneFilterPanel` 之上,弹层实例要加
|
||||
`android:elevation="8dp"`(> FAB 的 6dp)
|
||||
@@ -129,6 +133,8 @@ android.util.Log.d("DBG", "value '$field' -> '$v'", Throwable("trace"))
|
||||
`ic_phone_chevron_right/down` `ic_phone_calendar` `ic_phone_close`
|
||||
`ic_phone_check_round_checked/unchecked`(圆形复选框)
|
||||
`ic_phone_clipboard`(剪贴板,白色 FAB 用)`ic_phone_weight`(砝码,可 tint)
|
||||
`ic_phone_package`(件数)`ic_phone_tag`(特码/标签)
|
||||
- 圆点:`bg_phone_dot_green/blue/gray`(状态指示点;蓝=出港查询卡片头,灰=时间线未达节点)
|
||||
- 复用 Pad 的矢量图:`img_search` `img_scan` `img_filter`(可 `app:tint` 染色)
|
||||
|
||||
设计稿里的 MDI 图标若缺失,直接写 24×24 的 vector(用 MDI 官方 path),比找位图靠谱且能染色。
|
||||
|
||||
76
.claude/skills/phone-adapt/references/proxyman.md
Normal file
76
.claude/skills/phone-adapt/references/proxyman.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Proxyman MCP:抓包 / 直调 / Mock(阶段 2、5、6 的调试利器)
|
||||
|
||||
Proxyman 桌面端 + `mcp__proxyman__*` 工具(会话内直接可用,缺 schema 时用 ToolSearch
|
||||
`select:mcp__proxyman__get_flows,...` 加载)。**2026-07-31 已在出港查询页全链路实测**:
|
||||
抓包核对请求体、Map Local mock 三种卡片形态(含真实数据不存在的「未入库」兜底态)一次跑通。
|
||||
|
||||
## 与 api-doc 的分工(先契约后事实)
|
||||
|
||||
| | api-doc MCP | Proxyman MCP |
|
||||
|---|---|---|
|
||||
| 回答什么 | **契约**:接口该有什么入参/出参、参数名叫什么 | **事实**:App 实际发了什么、后端实际回了什么 |
|
||||
| 用在何时 | 阶段 2 开工前逐接口核对 | 阶段 5/6 实机验证与 corner case 补测 |
|
||||
|
||||
标准配合流程:
|
||||
api-doc 拿权威参数名 → 前端实现 → Proxyman flow 核对请求体确实带上了 →
|
||||
compose 改参 A/B 验证后端认不认 → 不认 → 记问题清单(附 `export_flow_curl` 证据)→
|
||||
map local 造 mock 补 UI corner case 验证。
|
||||
|
||||
## 接入(一次性,实测步骤)
|
||||
|
||||
```bash
|
||||
# 1) 确认 Proxyman 在跑(返回 Recording: Active + 端口,默认 9090)
|
||||
# mcp__proxyman__get_proxy_status
|
||||
# 2) 模拟器全局代理指向宿主机(模拟器视角宿主机 = 10.0.2.2)
|
||||
adb -s <serial> shell settings put global http_proxy 10.0.2.2:9090
|
||||
# 3) 验证完【必须】清掉,否则 Proxyman 一关模拟器就断网,下次验证莫名全挂
|
||||
adb -s <serial> shell settings put global http_proxy :0
|
||||
```
|
||||
|
||||
本项目内网服务是**纯 HTTP**(192.168.1.250:8093)→ 不需要装证书,明文直接可见。
|
||||
(若未来切 HTTPS,才需要 `install_certificate` + `enable_ssl_proxying`。)
|
||||
|
||||
## 场景 1:核对请求/响应(替代 `ui_probe req`)
|
||||
|
||||
logcat 抓 OkHttp 多行 pretty JSON 脆弱且要人肉拼接;Proxyman 一步到位:
|
||||
|
||||
- `filter_flows`(key=url, value="IntExpSearch")→ 拿 flow ID 列表
|
||||
- `get_flow_detail`(flow_id)→ 完整请求体 + 响应体 + headers(Authorization 自动脱敏)
|
||||
|
||||
**出参无文档定义时(如 `IntExpSearch/detail` 返回裸 Map),用真实 flow 的 responseBody
|
||||
直接盘点字段清单**——判定「锁定状态/计费重量是否存在」这类问题的最快路径。
|
||||
|
||||
## 场景 2:直调后端 A/B 验证(替代 run-as 提 token + 手写 curl)
|
||||
|
||||
`create_compose_http_from_flow`(从真实 flow 复制请求,**token 自动带上,不用碰
|
||||
shared_prefs**)→ `update_compose_http` 改一个参数 → `send_compose_http` 看结果对比。
|
||||
isFclose 这类「参数是否生效」的 A/B 实验用这条链。给后端提缺口时用
|
||||
`export_flow_curl` 导出可复现的 curl 附进问题清单。
|
||||
|
||||
## 场景 3:Map Local 造 mock 验证 corner case(替代代码注入样例 Bean)
|
||||
|
||||
老流程「Activity 临时注入 Bean → 截图 → 删码 → 重构建」彻底淘汰:**零代码改动、零重构建**。
|
||||
|
||||
- `create_map_local`:url 精确到接口路径,method=POST,response_body 直接写目标 JSON
|
||||
(构造空字段/超长文本/罕见状态,比如出港查询的 fclose+opDate 双空「未入库」兜底态)
|
||||
- 改真实响应个别字段更省事时用 `create_map_local_from_flow`
|
||||
- 验证完 `delete_rule`(rule_type=maplocal)+ 清代理,**规则残留会污染后续真实数据验证**
|
||||
|
||||
响应结构照抄真实 flow 的顶层结构(如 pageQuery 是裸 PageInfo:`{total, list, pages, ...}`,
|
||||
不要包 `{status, data}`)。
|
||||
|
||||
⚠️ 实测坑:mock 响应到达后 UI 渲染可能滞后(冷启动首帧 Davey 卡顿 2s+),
|
||||
**截图断言前先 `ui_probe texts` 确认数据已上屏**——这次就因截图太早误判过一轮「mock 没生效」,
|
||||
其实 `get_flow_detail` 里 `matchedTools: ["Map Local"]` 早已证明规则命中,UI 只是还没画完。
|
||||
|
||||
## 场景 4:环境模拟(按需)
|
||||
|
||||
- `create_network_condition`:弱网/高延迟,验证 loading 弹窗与超时提示
|
||||
- `create_breakpoint`:单次拦截改包(比 map local 更临时的一次性实验)
|
||||
- `create_map_remote`:把请求转发到另一环境(如后端修复验证用的测试实例)
|
||||
|
||||
## 验证收尾清单
|
||||
|
||||
- [ ] `list_rules` 确认无本次遗留规则(maplocal/breakpoint/network_condition)
|
||||
- [ ] 模拟器代理已清(`settings get global http_proxy` 应为 `:0` 或空)
|
||||
- [ ] 用真实数据再过一遍被 mock 过的页面,确认无假数据残影
|
||||
Reference in New Issue
Block a user