Files
aerologic-app/.claude/skills/phone-adapt/references/components.md
YANG JIANKUAN 1eb288a703 feat: 完成出港计重页面5.5寸手机端适配
- 待计重列表:三格统计、卡片单票提前运抵、筛选弹层(通道号/承运人/运单类型/代理人)
- 开始计重:预配统计卡 + PhoneFormRow 表单
- 计重记录:三格统计、筛选弹层(入库日期/代理人/特码/目的站/计重人)
- 计重明细:新增手机端全屏「修改记录」覆层,复用平板编辑链路
- 新增手机专属「运单详情」页
- 手机首页「国际」Tab 增加「出港计重」入口
- 登记后端接口缺口 7 项(#12~#18)至问题清单

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-29 20:09:55 +08:00

135 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 手机版公共组件(`module_base/.../ui/weight/phone/`
7 个组件覆盖设计稿反复出现的部件。**先复用,实在没有再新增**——每个新组件都要维护、都要踩一遍
状态恢复的坑,能少一个是一个。
所有属性都是**无命名空间写法**(与平板端 `PadSearchLayout` 一致DataBinding 适配器集中在
`PhoneWidgetKtx.kt`。同名属性(`hint`/`value`/`title`/`list`)按控件类型解析,与 Pad 系组件不冲突。
## 组件与属性
| 组件 | 用途 | 属性 |
|------|------|------|
| `PhoneSearchBar` | 顶部搜索行:搜索框 + 扫码 + 圆形筛选按钮 | `hint``value`(双向)、`showScan``showFilter``upperCase``setOnScanClickListener``setOnFilterClickListener``setRefreshCallBack` |
| `PhoneStatusTab` | 状态 Tab 条(文字 + 角标 + 选中下划线) | `tabs`(List\<KeyValue\>)、`counts`(List\<String\>)、`value`(双向)、`setOnTabChanged` |
| `PhoneDataLayout` | 表单/筛选项,三形态 | `title``hint``type`(`PhoneDataLayoutType.INPUT`/`DATE`/`SPINNER`)、`value`(双向)、`list``required``enable``setRefreshCallBack` |
| `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 → 橙色高亮标签) |
| `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}"` 切浅红删除按钮。
⚠️ 两个已踩坑:
- **FAB 盖住弹层**:浮动按钮带 elevation 时会浮在 `PhoneFilterPanel` 之上,弹层实例要加
`android:elevation="8dp"`> FAB 的 6dp
- **Spinner 选择回调是异步的**`onItemSelected` 由系统 post自动化测试里 tap 选项后立即 tap「确认」
可能抢在回值之前发出请求(表现为 UI 已显示选中但请求参数为 null——脚本里选完要 `sleep 1` 以上;
真人操作间隔足够,不受影响
`KeyValue``dev.utils.app.info.KeyValue`,字段是 **`key`(显示文案)+ `value`(业务值)**
不是 `name``PhoneStatusTab``tabs` 就按 `KeyValue("待交接","0")` 传。
## PhoneFilterPanel 的内容插槽
筛选项直接写在标签内,组件 `onFinishInflate` 会自动把它们移进面板中部并施加 20dp 项间距,
使用方不用关心内部层级、也不用逐个写 `marginTop`
```xml
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
android:layout_width="match_parent"
android:layout_height="match_parent"
title='@{"筛选条件"}'
panelVisible="@{activity.filterPanelVisible}"
setOnResetClick="@{(v)-> activity.resetFilter()}"
setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}">
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
title='@{"航班日期"}' hint='@{"请选择航班日期"}'
type="@{PhoneDataLayoutType.DATE}" value="@={viewModel.flightDate}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
```
记得在 `<data>``<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType" />`
## ⚠️ 新增复合组件必做:屏蔽子控件状态恢复
同一页面放多个同类组件时(比如筛选弹层里 3 个 `PhoneDataLayout`),它们的内部子控件
`et`/`tv`/`spinner`…)**共用同一套 id**。系统按 id 保存/恢复视图状态会跨实例串档。
实际后果出库交接踩过DATE 项的默认日期被邻近输入框恢复的空文本覆盖,
`EditText.setText("")` 触发 `doOnTextChanged`,再经双向绑定把 ViewModel 的航班日期也写成空——
**筛选条件静默丢失,界面上只表现为"日期没了",极难联想到状态恢复**
所以每个含内部 id 的组件都要写:
```kotlin
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) =
dispatchFreezeSelfOnly(container)
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) =
dispatchThawSelfOnly(container)
```
取值本来就由 ViewModel + DataBinding 持有,不需要视图级恢复,屏蔽掉没有副作用。
配套的第二道防线:多形态组件里,文本回写要限定形态,别让隐藏控件影响取值:
```kotlin
et.doOnTextChanged { text, _, _, _ ->
if (type == PhoneDataLayoutType.INPUT) value = text.toString()
}
```
### 遇到"值被神秘清空"怎么定位
在 setter 里打一条带堆栈的日志,一次就能看到真凶:
```kotlin
android.util.Log.d("DBG", "value '$field' -> '$v'", Throwable("trace"))
```
出库交接那次的栈是 `TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged`
指向非常明确。**排完记得删日志。**
## 新增组件的写法约定
照现有 7 个组件的模式来,保持一致:
1. 继承 `LinearLayout`/`FrameLayout``init``inflate(context, R.layout.layout_phone_xxx, this)`
2. 内部布局用 `<merge tools:parentTag="android.widget.LinearLayout">`,避免多一层嵌套
3. 属性做成 Kotlin `var` + setter 里立即生效;双向绑定值用
`onChangeListener: InverseBindingListener?`
4. BindingAdapter 统一加到 `PhoneWidgetKtx.kt`,函数名带组件名前缀避免顶层函数重名
5. 有互斥形态时,**`type` 要先于 `value` 生效**(把 type 放进同一个多属性 adapter 的第一个
`?.let`),否则 value 会写进错误的子控件
6. 加上上面那两个 `dispatch*SelfOnly` 覆写
7. 完成后同步更新 `CLAUDE.md` 的组件表和记忆文件 `phone-5.5inch-adaptation.md`
## 资源
配色一律用 `phone_*`,背景用 `bg_phone_*`,图标用 `ic_phone_*`,禁止写死色值。
已有的(不够再加,加完更新 CLAUDE.md
- 色:`phone_primary` `phone_page_bg` `phone_bg` `phone_stat_bg` `phone_text_title/body/hint/label/strong/weak_btn`
`phone_divider` `phone_badge_bg` `phone_green` `phone_check_border` `phone_tag_gray_*` `phone_tag_green_*`
`phone_tag_red_*` `phone_orange_bg/text`
- 背景:`bg_phone_card` `bg_phone_search` `bg_phone_input` `bg_phone_stat_box` `bg_phone_panel_top`
`bg_phone_icon_round` `bg_phone_btn_primary` `bg_phone_btn_outline` `bg_phone_btn_weak`(浅蓝弱按钮)
`bg_phone_badge` `bg_phone_tag_green/gray/red/orange` `bg_phone_card_header_blue/green`
`bg_phone_card_top_accent`(卡片顶部主色描边条,出港计重头部卡片)
- 图标:`ic_phone_airplane` `ic_phone_location` `ic_phone_person` `ic_phone_check_circle`
`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
- 复用 Pad 的矢量图:`img_search` `img_scan` `img_filter`(可 `app:tint` 染色)
设计稿里的 MDI 图标若缺失,直接写 24×24 的 vector用 MDI 官方 path比找位图靠谱且能染色。