Compare commits

..

20 Commits

Author SHA1 Message Date
YANG JIANKUAN
42647c2136 docs: 后端反馈联调结论入库——问题清单 P 列结论 + 新增 #35~#41,更新 api-doc 口径与 CHANGELOG
- documents/后端接口对接问题清单.xlsx 基于后端返回版本(保留其解决方案列与截图),
  新增「前端联调结论(2026-10-09)」列逐条记录 #1~#34 结论,追加联调新发现 #35~#41
- api-doc.md 追加 2026-10-09 最新字段口径;CLAUDE.md 更新待办与新坑(数字输入框光标跳位)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:44:57 -07:00
YANG JIANKUAN
5e8fe7d69d fix(ULD管理): 新增/修改补充最大装载容积、最大载重为可编辑必填(问题清单 #36)
- 联调发现 saveUld 缺 maxVolume 或 maxWeight 时后端直接 500,手机新增表单(原仅详情展示两项)必然失败
- 按平板表单口径改为新增/修改可编辑(数字键盘,新增必填)并前端校验
- 内网实测:PMC12349JG 新增成功,source/inStoreTime(补 00:00:00)/nowPort/maxVolume/maxWeight 均落库,测试数据已清理

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:44:57 -07:00
YANG JIANKUAN
6f813f9cff feat(进港移库): 移库人取 opUserName,已移库照片经 movedDetail 回填(问题清单 #32~#34)
- #32 fdate/fno/spCode 后端已实装(search/searchMoved),手机端原有传参零改动生效
- #33 searchMoved 已回填 opId/opDate 并新增 opUserName:详情「移库人」改绑姓名(回退 opId)
- #34 新增 GET /DomImpMove/movedDetail:已移库运单(列表无 mawbId)拍照页按 moveId 回填照片
- 已移库运单 searchMoved/movedDetail 均无 mawbId,而 modify 以 mawbId 定位(实测返回「未能获取到运单id」),
  提交前给出明确提示,不再发出必失败请求;缺口已登记问题清单
- 内网实测:fno=MU2222 待移库 0 / 已移库 2 与直调一致;详情移库人「张三」

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:34:59 -07:00
YANG JIANKUAN
68e6c7c52b feat(进港查询): 三格统计改取 pageQueryTotal 分项 storedNumber/departedNumber(问题清单 #30)
- 已入库/已出库由「已加载数据客户端计数」改为服务端全量口径,删除 phoneLoadedList 累计逻辑
- 内网实测:全量 123/119/4,outState=0/1 分别 119/0 与 4/4 口径自洽;当日 1/1/0 与 App 一致

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:33:43 -07:00
YANG JIANKUAN
cca62aae29 feat(提取出库): 筛选对齐平板参数,出库人改绑 refUserName,移除无出参占位(问题清单 #28/#29)
- #28 后端不新增入参/出参:筛选弹层改为平板同款 缴费日期起止(beginDate/endDate)/代理人/特码,
  移除航班日期/航班号;卡片移除「提货车辆」,详情移除「目的港」「业务类型」占位
- 运单号入参 no → wbNo(与主干平板端修复一致,no 为 8 位编号)
- #29 Tab 继续按 outState 过滤(后端确认生效);「出库人」改绑新增出参 refUserName(dlvName 是提货人)
- 内网:请求体已确认携带 wbNo/outState;测试环境该接口暂无数据(total=0),卡片展示待有数据后复核

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:33:27 -07:00
YANG JIANKUAN
1229ecc920 feat(进港仓库): 筛选对齐平板、统计取 notClearNumber/clearNumber、详情按平板字段(问题清单 #25~#27)
- #25 后端不新增入参:筛选弹层改为平板同款 航班日期/航班号/库位/清仓结果,
  移除始发站/代理/特码/运单类型/业务类型/品名
- #26 未清仓/已清仓取 pageQueryTotal 出参 notClearNumber/clearNumber,统计请求 3 次 → 1 次
- #27 进港详情按平板:航程 range 替代 始发站/中转站/目的站(无经停站),入库时间(opDate 恒空)
  改为平板同款 出库时间 dlvTime
- 内网实测:48/42/5;详情 航程 LAX-HFE / UN编号 / 锁定状态 正常显示
- 注意:IntImpStorage/pageQuery 当前对所有过滤条件(含平板自用的 wbNo/fno/fdate/location/clearNormal)
  均不生效,属后端缺陷,已登记问题清单

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:32:47 -07:00
YANG JIANKUAN
d2340d46fb feat(出港查询): 三格统计取服务端分项,筛选补日期类型,详情移除锁定状态/计费重量(问题清单 #21/#22)
- #21 已入库/已离港改取 pageQueryTotal 出参 storedNumber/departedNumber(全量口径),删除客户端计数
- #21「起始时间参数无意义」:内网实测 beginDate/endDate 仅在携带 dateState 时生效,
  手机筛选新增「日期类型」(入库/离港,默认入库,与主干平板一致),有起止日期时才携带 dateState
- #22 后端确认无需展示:运单信息移除「锁定状态」「计费重量」,件重区改两列对齐
- 内网实测:当日入库 2/2/0;切换离港 0/0/0,与直调一致

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:31:04 -07:00
YANG JIANKUAN
a431c09bd0 feat(出港仓库): 筛选对齐平板查询参数,三格统计改取 notClearNumber/clearNumber(问题清单 #23/#24)
- #23 后端不新增入参:筛选弹层改为平板同款 航班日期/航班号/库位/清仓结果,
  移除代理/特码/目的站/运单类型/业务类型/品名(内网实测 agentCode/spCode/dest/awbType/goodsCn 均被忽略)
- #24 未清仓/已清仓直接取 pageQueryTotal 出参 notClearNumber/clearNumber,
  手机端统计请求由 3 次降为 1 次(与平板相同)
- 内网实测:全量 7/7/0;fno=MU2222 时 3/3/0 且列表同步过滤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:47:41 -07:00
YANG JIANKUAN
b522efa6b9 feat(出港运抵): Tab 改服务端 arrivalStatus 过滤,移除补充信息页(问题清单 #19/#20)
- #20 待运抵/已运抵 Tab 改为入参 arrivalStatus=0/1 服务端过滤(与平板运抵状态筛选一致),
  角标为同条件 pageQueryTotal 全量票数;删除客户端拆分逻辑与 GjcMaWb.isPending
  (口径变化:申报状态 E 等异常态归入「待运抵」,以后端 arrivalStatus 为准)
- #19 后端确认目前无需「补充信息」页:删除 IntExpArriveSupplementActivity/ViewModel/布局与清单注册,
  待运抵卡片不再弹长按浮层(已运抵保留 重置/回执)
- 筛选移除「代理」:内网实测 IntExpArrive/pageQuery 忽略 agentCode,与平板查询参数对齐
- 内网实测:全量 待运抵 6 / 已运抵 2,与 pageQuery total 一致

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:45:27 -07:00
YANG JIANKUAN
40edbe7c67 feat(出港计重): 对接后端统计分项/计重人/通道号/托盘字段,筛选对齐平板(问题清单 #12~#18)
- #12 三格统计改取 pageQueryTotal 出参 waitCheckInNumber/checkingNumber(一次请求),
  不再依赖 checkIn 入参过滤(内网实测 pageQuery 的 checkIn 过滤仍被忽略,统计出参正确)
- #13 待计重筛选对齐平板:航班日期/代理人/特码(移除通道号/承运人/运单类型)
- #14 计重记录筛选对齐平板:航班日期/代理人/特码(移除入库日期起止;dest/opId 实测被忽略一并移除)
- #15 运单详情「通道号」绑 checked/pageQuery 出参 passageWay
- #16 计重明细/修改记录「计重人」绑 listRecordByWh 出参 opUserName(回退 opId)
- #17 arriveFlag 已回填,「已提前运抵」置灰态生效(无需改代码,实机确认)
- #18 托盘改用平板同款 trayNumber + trayTotalWeight(= 数量 × 自重,自重默认 18kg);
  记录卡片/修改记录托盘自重绑 trayTotalWeight;修复托盘输入框 Double 回写致光标跳位(输 18 存成 81)
- 内网实测:三格 6/5/1;分托计重 trayNumber=2/trayTotalWeight=36 落库;修改记录保存 18.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:45:18 -07:00
YANG JIANKUAN
415b685a85 feat(ULD管理): 对接后端调整——两态状态/来源/inStoreTime/批量删除接口(问题清单 #7~#11)
- #7 状态维持两态:移除「占用」预留映射与橙色标签
- #8 来源 source(空运/陆运)出参与保存入参已支持;查询入参不支持 source,筛选弹层移除来源
- #9 查询参数与平板一致(uld/status/uldSuffix):筛选弹层改为 状态 + 所属航司,移除入库时间/进港航班号
- #10 批量删除改为一次调用 /eqm/uld/deleteUldBatch(请求体 ULD 编号数组)
- #11 入库时间改绑 inStoreTime;所在港改绑 nowPort(与平板端一致);手机日期行只产出 yyyy-MM-dd,
  保存时补齐 00:00:00(仅日期后端返回 400,内网实测)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:14:04 -07:00
YANG JIANKUAN
b3b884504d feat(出库交接): 筛选对齐平板查询参数,Tab 角标改用 uldNumber(问题清单 #4/#6)
- 后端不提供交接人查询入参:手机筛选弹层「交接人」替换为平板同款「目的站」(fdest),移除 hoId 与交接人下拉
- pageQueryTotal 已修复且新增 uldNumber(板箱数),Tab 角标改取 uldNumber
- ManifestTotalDto 补齐后端统一统计 DTO 的分项字段(uldNumber/storedNumber/departedNumber/
  notClearNumber/clearNumber/waitCheckInNumber/checkingNumber/flightNumber)
- 内网实测:无条件 已交接=2(列表 total 同为 2),fdest=PEK 时列表与角标均为 0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:14:04 -07:00
YANG JIANKUAN
f6b3842882 feat(出港移库): 对接后端新增出参 flight/businessName/opUserName(问题清单 #1~#3)
- 卡片航班信息绑 flight(航班日期/航班号),不再占位
- 详情页业务类型绑 businessName、移库人绑 opUserName(均回退编码)
- 内网实测:已移库运单 opId/opdate/opUserName 均已回填

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:14:04 -07:00
YANG JIANKUAN
18e438550c chore: 本地版本号同步线上 1.8.7(versionCode 87),避免联调时触发升级弹框
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:14:04 -07:00
0d9406af13 chore: SDK 版本管理由 vfox 迁移至 mise 原生配置
- 新增 mise.toml(java = 17,vendor 由全局 shorthand_vendor 控制)
- 删除 vfox 配置 .tool-versions / .vfox.toml
- 删除指向旧机器 vfox npx 路径的过时 MCP 定义 .claude/mcp.json
- 清理 .gitignore 与 settings.local.json 中的 vfox 残留

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-02 12:09:41 +08:00
c595a3046b chore: 引入 mobile-mcp 项目级 MCP 并沉淀 ADB×MCP 工具选型纪要
mobile-mcp 固定 1.0.7 版本;verification.md 新增 2026-10-02 工具选型纪要
(ADB/ui_probe.sh 与 Mobile MCP 的分工规则、实测踩坑、本机环境更新)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-02 12:09:11 +08:00
f90c421d44 feat: update claude md 2026-08-06 10:26:23 +08:00
49f11ef8e6 fix: 进港移库归属更正——删除误建IntImpMove,重新适配到国内进港DomImpMove
设计稿11「进港移库」经确认归属国内进港移库(DomImpMove后端8接口齐全),
此前误判为国际进港并预接了不存在的IntImpMove模块(问题清单#31作废)。

- 删除module_gjj的IntImpMove全套实现(3页面+Bean+Api+菜单+路由+权限串)
- GnjMoveStashListActivity双形态适配:待移库/已移库Tab走search/searchMoved
  双端点,Tab角标、筛选弹层4项、全选/批量移库复用平板既有链路
- 新增手机专属页:运单详情(Intent传bean零请求)+拍照上传(detail回填
  已有照片,modify最小体三字段提交,与平板移交编辑页同链路)
- 手机首页入口改挂国内Tab(GnViewModel补通用route跳转,权限串AppDomImpMove)
- 内网真实数据双端验证通过;Proxyman A/B:wbNo/awbType生效,
  fdate/fno/spCode被后端静默忽略(#32)、opId/opDate恒null(#33)
- 问题清单更正#31+新增#32~#34,CLAUDE.md/CHANGELOG/api-doc.md同步

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 12:17:10 +08:00
eb0655d3fc fix: 11模块设计稿全量走查修正——时间线状态pill/手机短标题/ULD字段顺序/计重卡片图标
设计稿逐态渲染对照走查(01~11 全模块)后的修正:
- 运单追踪竖排时间线为已发生节点补状态pill(绿底展示该环节日志content,超长省略)
- 11 页手机端标题按设计稿改短标题(DeviceUtil.isPhone 分支,平板长标题零变化):
  出库交接/出港待计重/计重记录/计重明细/开始计重/出港运抵/出港移库/出港查询/
  出港货物/进港查询/进港货物
- ULD 编辑表单「来源」移至「所在港」之后(设计稿顺序)
- 出港计重列表卡片与计重记录卡片补字段小图标(airplane/location/person/tag)

接口缺口台账复核:#31 进港移库整模块缺失成立(api-doc 全模块清单+报价单双重印证);
#19 修正定性——IntExpArrive/declare 入参已含收发货人全套字段,改登记为待确认提交口径;
#10 成立(单条 deleteUld 在、批量无)。

双端抽验:手机短标题三页生效(出港待计重/出库交接/进港货物),平板保持长标题,零崩溃。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 23:12:14 +08:00
a33776d959 feat: 完成进港查询与进港移库页面5.5寸手机端适配(设计稿11页全部完成)
进港查询(10,06出港查询的进港镜像):
- IntImpQueryActivity 手机布局:搜索行 + 三格统计(总票数/已入库/已出库客户端计数)
  + 状态标签卡片(dlvTime/inDate 前端推断)+ 筛选弹层9项(入参全部经api-doc核对存在)
- 详情页与运单追踪复用08/06已适配页零新增;平板侧滑筛选面板判空守卫,Pad端零变化
- 修复代理人空值兜底:转换器将JSON null归一为空串致DataBinding `??` 失效,
  改用Bean计算属性 agentDisplay(06出港查询同步修正)

进港移库(11,首个后端整模块缺失的设计稿页):
- 全新手机专属3页:列表(双Tab+角标/筛选4项/多选批量移库/卡片长按拍照浮层)
  + 运单详情(三卡片,Intent传bean零请求)+ 拍照上传(复用ImageSelectNewViewHolder,
  pic/originalPic/picNumber三字段规范)
- 后端无IntImpMove模块(api-doc核对),按IntExpMove同名约定预接四接口
  + 预定义权限串AppIntImpMove,后端交付后前端零改动生效(缺口#31)

其他:手机首页国际Tab新增进港查询/进港移库入口;新增资源bg_phone_btn_green/
bg_phone_card_header_orange/bg_phone_photo_add/phone_amber;问题清单登记#30~#31;
双端模拟器验证零崩溃(手机Map Local mock全交互链路,平板回归零变化)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:35:56 +08:00
108 changed files with 4795 additions and 3093 deletions

View File

@@ -1,11 +0,0 @@
{
"mcpServers": {
"api-doc": {
"type": "http",
"url": "https://www.agentfoxapp.com/mcp/976caff7-5f98-4487-bccf-3aa20c92cf1f",
"headers": {
"Authorization": "Bearer afk_G_VKmHTdXUi3GjQdg5AeHeH-0BSYQEPP"
}
}
}
}

View File

@@ -96,8 +96,6 @@
"Read(//Library/Java/JavaVirtualMachines/**)", "Read(//Library/Java/JavaVirtualMachines/**)",
"Read(//usr/local/**)", "Read(//usr/local/**)",
"Read(//opt/homebrew/opt/**)", "Read(//opt/homebrew/opt/**)",
"Bash(/Users/kid/.vfox/sdks/java/bin/java -version 2>&1)",
"Bash(export JAVA_HOME=/Users/kid/.vfox/sdks/java)",
"mcp__apifox__read_project_oas_kcl8s7", "mcp__apifox__read_project_oas_kcl8s7",
"mcp__apifox__refresh_project_oas_kcl8s7", "mcp__apifox__refresh_project_oas_kcl8s7",
"mcp__apifox__read_project_oas_ref_resources_kcl8s7", "mcp__apifox__read_project_oas_ref_resources_kcl8s7",
@@ -156,10 +154,5 @@
"ask": [] "ask": []
}, },
"enableAllProjectMcpServers": true, "enableAllProjectMcpServers": true,
"enabledMcpjsonServers": [ "enabledMcpjsonServers": []
"空港集团 - API 文档",
"apifox",
"aerologic-app",
"apidoc"
]
} }

View File

@@ -109,6 +109,28 @@ curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
运单号筛选参数名是 **`no`**(平板既有口径);`chargeName`/`chargeTime` = 柜台办理人/缴费(提取)时间, 运单号筛选参数名是 **`no`**(平板既有口径);`chargeName`/`chargeTime` = 柜台办理人/缴费(提取)时间,
`dlvTime` = 出库时间,`dlvName` 按提货人语义推断作「出库人」展示(待确认 #29); `dlvTime` = 出库时间,`dlvName` 按提货人语义推断作「出库人」展示(待确认 #29);
确认出库接口 `pickUpOut` 文档注明**需将全字段传至后端**(平板 `selectedItems.toRequestBody()` 已满足) 确认出库接口 `pickUpOut` 文档注明**需将全字段传至后端**(平板 `selectedItems.toRequestBody()` 已满足)
- `IntImpSearch`(进港查询,2026-07-31 文档核对):`pageQuery`/`pageQueryTotal` 分页参数名是
**`page`/`limit`**(不是 pageNum/pageSize);手机筛选 9 项入参全部真实存在:`beginDate`/`endDate`/
`fno`/`agentCode`/`spCode`/`origin`(始发站)/`awbType`/`businessType`/`goods`(品名,**不是 goodsCn**);
`outState`(0 未出库/1 已出库)文档为 integer,前端传字符串靠 Jackson 隐式转换;`pageQueryTotal`
出参无已入库/已出库分项计数(缺口 #30,手机三格统计为客户端计数口径:dlvTime 非空=已出库、
inDate 非空=已入库);出参含 `agentName`(中文名)+ `agentCode`,agentName 可为 null
- 「进港移库」(设计稿 11)**归属更正(2026-08-03 用户确认):属国内进港移库 `DomImpMove`**,
不是国际进港移库——「后端模块缺失」的旧结论(原缺口 #31)作废,原按 IntExpMove 约定预接的
IntImpMove 前端实现已整体删除。溯源:api-doc 53 模块中移库仅 `DomImpMove`(国内进港,8 接口)与
`IntExpMove`(国际出港,4 接口),移库按转关业务前缀划分(DomImpMove 处理 `CI**`、IntExpMove
处理 `IO**`),设计稿详情卡的「国际进港」红标签实为 `awbType` 值(如「国际进港(国内中转)」),
不代表页面归属
- `DomImpMove`(国内进港移库,2026-08-03 内网实测):手机 Tab 双端点——待移库 `search` /
已移库 `searchMoved`(同一套入参 schema,分页参数名 **`page`/`limit`**);入参 A/B 实测:
**`wbNo`、`awbType`(值为 CICO/CIII/CIIO 转关码)过滤生效;`fdate`/`fno`/`spCode` 文档有定义但
被静默忽略**(缺口 #32);`searchMoved`/`detail` 出参 **`opId`/`opDate` 恒 null** 且无操作人姓名
字段(缺口 #33,移库人/移库时间显示"-"),`moveId` 形如 `CICO240617155016` 疑似内嵌移库时间戳但无
文档语义;`searchMoved` 出参**无 pic 三字段**(search 有)——拍照上传页统一先调 `GET detail`
回填已有照片(#34 前端已规避);照片提交走 `modify` 最小体
`{mawbId, remark, picNumber, pic, originalPic}`(平板移交编辑页同链路,removeEmptyOrNull);
批量移库 `move` 入参 `{fid:"0", ids:[mawbId]}`;国内进港特码字典
`DictUtils.getSpecialCodeList(flag=0, ieFlag="I")` 实测取数正常
- `IntExpCheckIn`(出港计重,2026-07-29 内网实测):`likeNo` **并非任意模糊**——按 8 位 `no` - `IntExpCheckIn`(出港计重,2026-07-29 内网实测):`likeNo` **并非任意模糊**——按 8 位 `no`
匹配(11 位 wbNo 或部分号均 0 条),11 位全号必须走 `wbNo`;`checkIn`/`checkInList` 匹配(11 位 wbNo 或部分号均 0 条),11 位全号必须走 `wbNo`;`checkIn`/`checkInList`
文档有定义但 `pageQuery`/`pageQueryTotal` **均静默忽略**(缺口 #12,待计重/计重中计数依赖); 文档有定义但 `pageQuery`/`pageQueryTotal` **均静默忽略**(缺口 #12,待计重/计重中计数依赖);
@@ -136,3 +158,30 @@ curl -s -X POST "http://192.168.1.250:8093/IntExpOutHandover/pageQuery" \
用它做「参数是否生效」的 A/B 对比(同条件只改一个参数看 total 变化), 用它做「参数是否生效」的 A/B 对比(同条件只改一个参数看 total 变化),
再回 App UI 走一遍确认端到端链路。UI 里的滚轮日期选择器可用 再回 App UI 走一遍确认端到端链路。UI 里的滚轮日期选择器可用
`input swipe`(慢速 500ms、每次 3 格 ≈ 396px)驱动,滑完截图校准。 `input swipe`(慢速 500ms、每次 3 格 ≈ 396px)驱动,滑完截图校准。
## 2026-10-09 后端反馈联调后的最新口径(与上文冲突时以本节为准)
后端在问题清单补「解决方案」后逐项内网实测的结论。**通用规则:后端对手机端新增筛选维度一律答复
「与平板端查询参数保持一致」——手机筛选只放平板已用的入参;文档里定义了但平板没用的入参,
多数实测也被忽略(agentCode/spCode/dest/opId 等),别再按文档加筛选。**
- **统一统计 DTO**:各 `*/pageQueryTotal` 出参现含 `uldNumber`/`storedNumber`/`departedNumber`/
`notClearNumber`/`clearNumber`/`waitCheckInNumber`/`checkingNumber`/`flightNumber`(`ManifestTotalDto`
已补齐)。分项统计一律取出参,**不要再按状态入参多调几次 total**
- `IntExpOutHandover/pageQueryTotal` 已修复(与列表一致);Tab 角标取 `uldNumber`(板箱数);无交接人入参
- `IntExpMove/pageQuery` 出参新增 `flight`("20260924/MU2222")/`businessName`/`opUserName`;已移库 `opId`/`opdate` 已回填
- `eqm/uld`:状态维持两态;`source`(空运/陆运) 出参与保存入参支持、**查询入参不支持**;入库时间 = `inStoreTime`
(date-time,**只传日期 400**)、所在港 = `nowPort`;批删 `deleteUldBatch`(Body 为字符串数组);
⚠️ `pageQuery` 带 `uld` 即 500(#35);`saveUld` 缺 `maxVolume`/`maxWeight` 即 500(#36)
- `IntExpCheckIn`:`listRecordByWh` 出参 `opUserName`/`trayNumber`/`trayTotalWeight`;
`checked/pageQuery` 出参 `passageWay`;`splitCheckIn`/`completeCheckIn` 托盘入参 = `trayNumber` +
`trayTotalWeight`(数量×自重);`arriveFlag` 已回填("0" 正常/"1" 提前运抵);
⚠️ `pageQuery` 的 `checkIn` 过滤仍被忽略(#40,统计用分项出参);`checked/pageQuery` 的 `dest`/`opId` 被忽略
- `IntExpArrive`:入参 `arrivalStatus`("0" 未运抵/"1" 已运抵)服务端过滤生效;`agentCode` 被忽略;补充信息页已删除
- `IntExpSearch`:**`beginDate`/`endDate` 只有携带 `dateState`(0 入库/1 离港)才生效**;`outState` 生效
- `IntExpStorageUse`:仅 `fdate`/`fno`/`wbNo`/`location`/`clearNormal` 有意义;⚠️ 列表 total 与统计 wbNumber 不一致(#38)
- `IntImpStorage`:⚠️ `pageQuery` 当前**所有**过滤条件均失效(#37),`pageQueryTotal` 正常
- `IntImpSearch/detail`:按平板展示 航程 `range`(无经停站)/`unNumber`/`lockState`/出库时间 `dlvTime`
- `IntImpPickUpDlv`:运单号入参用 `wbNo`;出参新增 `refUserName`(出库人姓名,`dlvName` 是提货人);测试环境暂无数据
- `DomImpMove`:`fdate`/`fno`/`spCode` 已生效;`searchMoved` 已回填 `opId`/`opDate`/`opUserName`;
新增 `GET movedDetail?moveId=`(含 pic 三字段);⚠️ 已移库运单无 `mawbId`,`modify` 无法补传照片(#39)

View File

@@ -6,12 +6,12 @@
## 环境 ## 环境
```bash ```bash
~/Library/Android/sdk/emulator/emulator -list-avds # Medium_Phone_API_36.1(手机) / Aerologic_Tablet(平板) /opt/homebrew/share/android-commandlinetools/emulator/emulator -list-avds # 本机现仅 AeroLogic_Phone(手机)
adb devices -l adb devices -l
adb -s <serial> shell wm size && adb -s <serial> shell wm density adb -s <serial> shell wm size && adb -s <serial> shell wm density
``` ```
判定形态:手机 1080×2400@420dpi → `PHONE(sw=411dp)`;平板 1280×800@160dpi → `TABLET(sw=800dp)`。 判定形态:手机 720×1440@292dpi → `PHONE(sw≈394dp)`;平板 1280×800@160dpi → `TABLET(sw=800dp)`(本机平板 AVD 待建,见文末工具选型纪要)。
`ui_probe.sh` 封装了常用操作,比手写 adb 省事且更可靠: `ui_probe.sh` 封装了常用操作,比手写 adb 省事且更可靠:
@@ -46,7 +46,7 @@ adb -s <serial> install -r -d app/build/outputs/apk/debug/app-debug.apk
adb -s <serial> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1 adb -s <serial> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1
``` ```
测试环境账号在记忆文件 `test-env-login.md`(ADMIN / 用户已授权保存),可自主登录。 测试环境账号在记忆文件 `aerologic-test-account.md`(本机,admin / 用户已授权保存;旧机器文件名为 `test-env-login.md`),可自主登录。
填表用 `input text`,**`#` 不需要转义**——新版 adb 的 input text 参数不经远端 shell 解析, 填表用 `input text`,**`#` 不需要转义**——新版 adb 的 input text 参数不经远端 shell 解析,
写 `\#` 反而会把 `\` 也输进去(实测多出一位)。 写 `\#` 反而会把 `\` 也输进去(实测多出一位)。
@@ -179,3 +179,88 @@ adb -s <serial> logcat -d -s DBG | grep -A14 "'旧值' -> ''" | grep "at com.luk
本次靠这招定位到 `TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged`, 本次靠这招定位到 `TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged`,
即复合自定义 View 的 id 串档(见 `components.md`)。**排完删日志。** 即复合自定义 View 的 id 串档(见 `components.md`)。**排完删日志。**
---
## 工具选型纪要:ADB(ui_probe.sh) × Mobile MCP 分工(2026-10-02)
背景:`mobile-mcp`(`@mobilenext/mobile-mcp`)已装入项目级 `.mcp.json`,与本手册的
ADB/ui_probe.sh 流程并存。以下基于 2026-10-02 在 `AeroLogic_Phone` 模拟器上的实测对比
(登录流程实测到“填表”一步,因内网不通放弃后续),给出后期开发的工具选择规则。
### 能力对照(只列有差异的维度)
| 维度 | ADB + ui_probe.sh | Mobile MCP |
|------|-------------------|------------|
| 元素定位 | `tap-text` 按文字点,**每次点击前重新 dump**,天然抗布局变化;同名多处可指定序号 | 按 ref 点,键盘弹出/页面导航后 **ref 失效**,必须重新 list(实测踩过) |
| 元素读取 | `texts` 只出可见文字,token 便宜,配 grep 断言(`texts \| grep -c "全选"`) | 完整控件树(text/id/类型/focused/disabled/checked/坐标),信息全但 **token 重**(登录页 54 行) |
| 文本输入 | `input text` + `keycombination`(CTRL+A/DEL 清空——登录 bcrypt 回填坑的标准解法) | `type_keys` 只能输入,**无清空原语**(实测需回落 adb) |
| 请求核对 | `req <关键字>`(OkHttp 多行 pretty JSON 按关键字截取,为本项目定制) | `get_device_logs` 可过滤 tag/进程,多行报文截取不如 `req` 顺手 |
| 崩溃检查 | `crash` | `list_crashes` / `get_crash`(等价) |
| 前台断言 | `dumpsys activity`(手写 grep) | `get_foreground_app` 一次调用 |
| 录屏 | `adb shell screenrecord`(手拉文件) | `start/stop_screen_recording` 直接回传路径,更省事 |
| 批量操作 | 脚本串联,每条一次往返 | `mobile_batch_commands` 一次往返多步(**页面稳定时**才高效) |
| iOS / 云真机 | 无 | 有(本项目 Android-only,暂无用武之地) |
| 依赖 | 零依赖(本机 adb 即可) | npx 每会话冷启动、Node 20+、`@latest` 有版本漂移风险(建议固定 `@1.0.7`) |
### 选型规则(后期开发按此执行)
**默认走 ADB + ui_probe.sh**。本手册全部流程不变:构建安装、冷启动/直启、登录、
wait-text 就绪等待、tap-text 交互、texts 断言、req 抓包、crash 归零、方向采样、Pad 回归、
样例 Bean 注入。理由:为本项目的坑定制(bcrypt 回填、Loading 吞点击、参数名 A/B)、
token 成本低、零外部依赖,且 tap-text 每次重新 dump,比 ref 更抗布局变化。
**以下四种场景切 Mobile MCP**:
1. **控件级语义断言**——要看 disabled/checked/focused 状态、控件 id、输入框变换后的值
(如“密码框 9 个点”“角色 Spinner 是否禁用”“用户名框自动大写成 ADMIN”)。元素树比
截图肉眼判读可靠,比 `texts` 信息全。实测:登录页一次拿到 `inputPass text="•••••••••"`、
角色 Spinner `disabled`,这类断言 `texts` 做不到(密码框无明文文字)
2. **页面稳定时的多步连续输入**——list 一次拿 ref,`batch_commands` 一次往返完成
tap→type→tap→type;比逐条 adb 省 round-trip。前提:中间不弹键盘收不起/不导航
(弹了就见下方回落规则)
3. **录屏取证**——复现“先横屏闪 1 秒”类**时序 bug**,mp4 归档进问题清单,比连拍截图省力
4. **前台应用/安装卸载等状态核查**——`get_foreground_app`、`install/uninstall/launch_app`
一步到位,省得记 dumpsys/am 语法
**回落规则(两者随时可混用,操作同一设备无状态冲突)**:
- mobile-mcp ref 失效(键盘弹出/导航后点击落空)→ 重新 `list_elements`,或直接换
`ui_probe.sh tap-text`(按文字,永远新鲜)
- 需要清空输入框(登录 bcrypt 回填)→ 只能 adb:`input keycombination 113 29` + DEL
- 多行请求/响应体核对 → `req`,不用 mobile-mcp 的日志过滤硬凑
### 实测踩坑(Mobile MCP,2026-10-02)
- ⚠️ **键盘弹出会平移布局**:之前 list 出的 ref/坐标失效。batch 里连续 click+type 会把
两段文本打进同一个框(用户名框变成 `ADMINAA123456#`)。规避:每步输入完先
`KEYCODE_BACK` 收键盘再点下一个目标;或点完先确认焦点再 type
- 无“清空字段”原语;`type_keys` 输入即所得,不会帮你选中文本
- 元素树 token 开销大,断言优先 `texts | grep`,确实要看状态/id 才上 list_elements
### 本机环境更新(2026-10-02)
- SDK 根目录:`/opt/homebrew/share/android-commandlinetools`(brew 安装,emulator 包已装)
- 手机 AVD:`AeroLogic_Phone`——720×1440、5.51 寸 292dpi(sw≈394dp,落手机桶 360~411dp)、
Android 12 / API 31(与 targetSdk 一致)、arm64 纯 64 位(本项目无 abiFilters,唯一含
.so 的 fimage.aar 自带 arm64-v8a,兼容)、无边框(`showDeviceFrame=no`)
- ⚠️ **旧文档里的 `Aerologic_Phone_5.5_API31`(300dpi/sw=384dp)、`Medium_Phone_API_36.1`、
`Aerologic_Tablet` 均为旧机器 AVD,本机不存在**。平板回归前需先建平板 AVD
(1280×800@160dpi,`avdmanager create avd` 后照本文「Pad 端回归」执行)
- 启动:`/opt/homebrew/share/android-commandlinetools/emulator/emulator -avd AeroLogic_Phone`
(Quick Boot 快照已存在,二次启动数秒就绪)
- 模拟器 NAT 出网,**内网服务不可达**(本次登录实测“服务器不通”而放弃)——需要真实接口
联调时先确认宿主机网络可达内网;不可达时按上文「无数据时怎么核对 UI」注入样例 Bean
- `.mcp.json` 已加 `mobile-mcp` 条目(npx stdio),**建议把 `@latest` 固定为 `@1.0.7`**;
该文件 git 跟踪,改动需提交后队友才可用
### 推荐配合模式(单页验证的完整动线)
adb 骨架 + mobile-mcp 点睛,一条龙:
1. **adb**:构建安装 → 冷启动/直启(`am start`)→ 按登录节填表(含 bcrypt 清空顺序)
2. **ui_probe**:`wait-text` 就绪 → `tap-text` 交互 → `texts | grep` 断言 → `req` 抓包 → `crash` 归零
3. `texts` 断言不够(要看状态/id/变换值)或要连续多步输入 → **mobile-mcp**:
`list_elements` → `batch_commands`(tap/type)
4. 时序类 bug(闪屏/动画/异步错位)→ **mobile-mcp 录屏**取证,问题清单贴 mp4 + 截图
5. 全程崩溃检查两家等价(`crash` / `list_crashes`),任选

2
.gitignore vendored
View File

@@ -191,8 +191,6 @@ fabric.properties
!/gradle/wrapper/gradle-wrapper.jar !/gradle/wrapper/gradle-wrapper.jar
# End of https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,java,kotlin # End of https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,java,kotlin
.vfox/
.vfox/sdks/java
# Auto Claude data directory # Auto Claude data directory
.auto-claude/ .auto-claude/

View File

@@ -1,5 +1,9 @@
{ {
"mcpServers": { "mcpServers": {
"mobile-mcp": {
"command": "npx",
"args": ["-y", "@mobilenext/mobile-mcp@1.0.7"]
},
"api-doc": { "api-doc": {
"type": "http", "type": "http",
"url": "https://www.agentfoxapp.com/mcp/976caff7-5f98-4487-bccf-3aa20c92cf1f", "url": "https://www.agentfoxapp.com/mcp/976caff7-5f98-4487-bccf-3aa20c92cf1f",

View File

@@ -1 +0,0 @@
java 17.0.16+8-amzn

View File

@@ -1,2 +0,0 @@
[tools]
java = "17+35-amzn"

View File

@@ -7,7 +7,91 @@
## [Unreleased] ## [Unreleased]
### 变更 (Changed)
- 【后端反馈联调 · 2026-10-09】后端在《问题清单》补「解决方案/备注」后,逐项(#1~#34)api-doc 核对 +
内网直调 A/B + AeroLogic_Phone 模拟器端到端验证,并按后端意见调整前端:
① 后端已补字段/接口 → 前端对接:出港移库 flight/businessName/opUserName;出库交接 uldNumber 角标;
ULD source/inStoreTime/nowPort + `deleteUldBatch` 一次批删;出港计重 waitCheckInNumber/checkingNumber
统计、passageWay、opUserName、trayNumber/trayTotalWeight;运抵 arrivalStatus 服务端 Tab;
出港/进港查询 storedNumber/departedNumber;出港/进港仓库 notClearNumber/clearNumber(统计请求 3→1);
提取出库 refUserName;国内进港移库 opUserName + `movedDetail` 照片回填。
`ManifestTotalDto` 补齐后端统一统计 DTO 分项字段。
② 后端「按平板端调整/设计如此」→ 前端对齐:出库交接/ULD/计重/记录/出港仓库/进港仓库/提取出库/运抵
的手机筛选全部收敛为平板同款查询参数;ULD 状态回归两态;删除运抵「补充信息」页;出港详情移除
锁定状态/计费重量;进港详情按平板改 航程/出库时间(无经停站);提取出库移除无出参占位。
③ 联调新发现记入问题清单 #35~#41(ULD uld 搜索 500、saveUld 缺 maxVolume/maxWeight 500、
IntImpStorage/pageQuery 过滤全失效、出港仓库列表与统计口径不一致、已移库无 mawbId 无法补传照片等)。
- 【进港移库归属更正 · 2026-08-03】设计稿 11「进港移库」经确认归属**国内进港移库
(DomImpMove,后端 8 接口齐全,Pad 首页「国内进港→进港移库」既有页面)**,此前误判为
国际进港并按 IntExpMove 约定预接不存在的 IntImpMove 模块(原问题清单 #31 作废)。处置:
① 删除 module_gjj 的 IntImpMove 全套前端实现(3 Activity + 3 ViewModel + Holder + 4 布局 +
`IntImpMoveBean` + Api 4 方法 + 国际 Tab 菜单项 + 权限串/路由/双 Manifest 注册);
② 按 phone-adapt 流程重新适配到 `GnjMoveStashListActivity`(app 模块):主列表双形态
(Pad 布局移 layout-sw600dp 零改动;手机版 = PhoneSearchBar + 待移库/已移库 Tab
(`DomImpMove/search`/`searchMoved` 双端点,`searchMoved` 无移库标识按端点打 `isMoved`)+
角标(双端点 limit=1 取 total,仅手机发起)+ 筛选弹层 4 项 + 全选/已选计数 + 批量移库
(复用平板 `move` 链路)+ 卡片长按「拍照」浮层);新增手机专属页
`GnjMoveStashDetailActivity`(Intent 传 bean 零请求,三卡片)与
`GnjMoveStashPhotoActivity`(先调 `detail` 回填已有照片,提交走 `modify` 最小体
三字段规范,与平板移交编辑页同链路);`MoveStashBean` 增 `agentName`/`isMoved`/
`actionMenuVisible`/`agentDisplay`/`orDash`;手机首页入口改挂**国内 Tab**(`GnViewModel`
补通用 route 跳转,权限串 `GnjYiKu=AppDomImpMove` 与 Pad 一致,新路由
`/app/GnjMoveStashListActivity`)。
2026-08-03 内网真实数据双端验证通过(登录→国内 Tab→列表/Tab/详情/筛选/全选/长按拍照全链路
crash=0;平板回归原行为零变化);Proxyman A/B:`wbNo`/`awbType` 过滤生效,
`fdate`/`fno`/`spCode` 被后端静默忽略(新缺口 #32)、`searchMoved`/`detail` 出参
`opId`/`opDate` 恒 null 且无姓名字段(#33)、`searchMoved` 无 pic 三字段(#34,前端已规避)
### 修复 (Fixed)
- 出港计重「修改记录」托盘自重输入框:Double 回写导致光标跳回开头(输入 18 存成 81),改为保留输入原文
- ULD 新增:入库时间日期行仅产出 yyyy-MM-dd(后端 400)→ 保存时补 00:00:00;补充最大装载容积/最大载重
可编辑必填(缺失时 saveUld 500)
- 提取出库运单号入参 no → wbNo(与主干平板端修复一致)
### 新增 (Added) ### 新增 (Added)
- 「进港查询」5.5 寸手机端适配(设计稿 10_进港查询,06 出港查询的进港镜像;详情页与运单追踪
复用 08/06 已适配页零新增):列表 `IntImpQueryActivity`(module_gjj,首页菜单实际跳转页;
旧版 `GjjQueryListActivity` 路由已注释弃用)手机布局 = PhoneSearchBar(运单号+扫码+筛选)+
三格统计(总票数/已入库/已出库,已入库/已出库为已加载数据客户端计数,`pageQueryTotal` 无分项
出参,缺口 #30)+ 状态标签卡片(前端推断:`dlvTime` 非空=已出库绿 / `inDate` 非空=已入库橙 /
兜底未入库灰)+ 卡片字段件数(inPc)/重量(inWeight)/代理人/特码;筛选弹层 9 项
(航班日期起止/航班号/代理/特码/始发站/运单类型/业务类型/品名中,**全部经 api-doc 核对为
pageQuery 真实入参**,无缺口);平板侧滑筛选面板代码判空守卫,Pad 端行为零变化
- 「进港移库」5.5 寸手机端全新页面(设计稿 11_进港移库;**首个后端整模块缺失的适配页**:
api-doc 确认无 IntImpMove 模块、权限体系无对应 key,缺口 #31):
`IntImpMoveActivity`(待移库/已移库 Tab + 角标、搜索/扫码、筛选弹层 4 项(航班日期/航班号/
特码/运单类型)、多选 + 全选 + 批量移库(ConfirmDialogModel 确认)、卡片长按浮层「拍照」)+
`IntImpMoveDetailActivity`(运单详情三卡片:蓝头基本信息/绿头移库信息(仅已移库)/橙头异常照片,
Intent 传 bean 零请求)+ `IntImpMovePhotoActivity`(拍照上传:图片网格复用 module_base
`ImageSelectNewViewHolder`,提交走 pic/originalPic/picNumber 三字段规范);
接口按 IntExpMove 同名约定预接四个(pageQuery/pageQueryTotal/move/uploadPic),权限串
`AppIntImpMove` 预定义,后端交付后前端零改动生效(权限串下发前手机首页无入口,debug 直启验证)
- 新增 `IntImpMoveBean`(镜像 GjcMove + 航班/照片字段 + isMoved/flightInfo/orDash 计算属性);
`IntImpQueryBean` 新增 isQueryOutbound/isQueryStored/orDash/agentDisplay 计算属性;
手机首页「国际」Tab 增加「进港查询」(图标 gjj_query_icon)与「进港移库」(图标复用
gjc_yi_ku_icon)入口
- 新增手机版资源:`bg_phone_btn_green`(绿色主按钮)、`bg_phone_card_header_orange`(橙色详情卡
表头)、`bg_phone_photo_add`(虚线加号槽位)、色值 `phone_amber`
### 修复 (Fixed)
- 【11 模块全量设计稿走查】修正 4 类差异(2026-07-31,3 个并行审计 + 逐态渲染比对):
① 运单追踪手机竖排时间线补齐已发生节点的状态 pill(绿底标签展示该环节日志 content,
如「放行状态:01」,超长省略,设计稿 06/08 运单追踪均有此元素);
② 11 个页面手机端标题对齐设计稿短标题(出库交接/出港待计重/计重记录/计重明细/开始计重/
出港运抵/出港移库/出港查询/出港货物/进港查询/进港货物),平板保持原长标题零变化;
③ ULD 编辑表单「来源」字段移至「所在港」之后(设计稿顺序);
④ 出港计重列表卡片(日期/航程/代理/特码)与计重记录卡片(代理)补齐字段小图标
(复用 ic_phone_airplane/location/person/tag,与其余模块卡片风格统一)
- 出港查询/进港查询卡片「代理人」空值兜底失效:转换器把 JSON null 归一成空串,DataBinding
`??` 只判 null 不判空串导致 agentCode 回退永不生效;改用 Bean 计算属性 `agentDisplay`
(isNullOrEmpty 双兜底),已同步修正 06 出港查询(`item_gjc_query.xml`)
### 验证 (Verified)
- 进港查询手机端:Map Local mock 验证三格统计计数、三种状态标签、空值 "--" 兜底、筛选弹层
9 项收缩滚动;平板端回归横排搜索条/侧滑筛选面板/底部统计栏零变化;全程崩溃数 0
- 进港移库手机端:Map Local mock 验证双 Tab 角标、双形态卡片、单选/全选联动计数、空选拦截、
移库确认弹框、长按拍照浮层、运单详情、拍照上传页;全程崩溃数 0(后端模块缺失,接口联调待
后端交付,见问题清单 #31)
- 「提取出库」5.5 寸手机端适配(设计稿 09_提取出库):列表 `IntImpPickUpDLVActivity`(module_gjj) - 「提取出库」5.5 寸手机端适配(设计稿 09_提取出库):列表 `IntImpPickUpDLVActivity`(module_gjj)
手机布局 = PhoneSearchBar(运单号+扫码+筛选)+ **PhoneStatusTab 待出库/已出库**(`outState` 手机布局 = PhoneSearchBar(运单号+扫码+筛选)+ **PhoneStatusTab 待出库/已出库**(`outState`
为 pageQuery 文档定义入参 0/1,Tab 真实过滤 + 角标双 pageQueryTotal,抓包证实携带)+ 为 pageQuery 文档定义入参 0/1,Tab 真实过滤 + 角标双 pageQueryTotal,抓包证实携带)+

View File

@@ -1066,6 +1066,8 @@ DataBinding 适配器统一放在 `PhoneWidgetKtx.kt`(同名属性按控件类
9 个筛选项曾把「重置/确认」挤出屏幕)。 9 个筛选项曾把「重置/确认」挤出屏幕)。
出港仓库追加:`bg_phone_btn_disabled`(灰底禁用按钮,与 `bg_phone_btn_weak` 同 10dp 圆角, 出港仓库追加:`bg_phone_btn_disabled`(灰底禁用按钮,与 `bg_phone_btn_weak` 同 10dp 圆角,
卡片操作按钮禁用态配 `phone_tag_gray_text` 文字)。 卡片操作按钮禁用态配 `phone_tag_gray_text` 文字)。
进港移库追加:`bg_phone_btn_green`(绿色主按钮,拍照上传「确认提交」)、`bg_phone_card_header_orange`
(橙色详情卡表头,异常照片)、`bg_phone_photo_add`(虚线加号槽位)、色值 `phone_amber`。
⚠️ **弹层与浮动按钮的层级**:`PhoneFilterPanel` 默认无 elevation,页面里有带 elevation 的浮动按钮(FAB)时 ⚠️ **弹层与浮动按钮的层级**:`PhoneFilterPanel` 默认无 elevation,页面里有带 elevation 的浮动按钮(FAB)时
会穿透到弹层之上——给弹层实例加 `android:elevation="8dp"`(大于 FAB 的 6dp)。 会穿透到弹层之上——给弹层实例加 `android:elevation="8dp"`(大于 FAB 的 6dp)。
@@ -1141,6 +1143,28 @@ override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) =
缺口 #28:无 fdate 入参、出参无车牌(提货车辆)/目的港/业务类型。 缺口 #28:无 fdate 入参、出参无车牌(提货车辆)/目的港/业务类型。
⚠️ 踩坑:`checkAllClick` 全选后底部「已选 N 项」不更新——批量勾选不走 FlowBus 单卡事件, ⚠️ 踩坑:`checkAllClick` 全选后底部「已选 N 项」不更新——批量勾选不走 FlowBus 单卡事件,
必须在 checkAllClick 里同步 selectedCountText) 必须在 checkAllClick 里同步 selectedCountText)
- 「进港查询」`IntImpQueryActivity`(module_gjj;06 出港查询的进港镜像:三格统计
总票数/已入库/已出库为已加载数据客户端计数(`pageQueryTotal` 无分项出参,缺口 #30),
卡片状态前端推断:`dlvTime` 非空=已出库(绿)/`inDate` 非空=已入库(橙)/兜底未入库(灰);
筛选弹层 9 项**入参全部经 api-doc 核对真实存在**(分页参数名 `page`/`limit`,品名参数名
`goods`);详情页与运单追踪复用 08/06 已适配页零新增;平板侧滑筛选面板代码已判空守卫。
⚠️ 踩坑:代理人绑定不能写 `bean.agentName ?? bean.agentCode`——转换器把 JSON null 归一成
空串,`??` 只判 null 不判空串,须用 Bean 计算属性 `agentDisplay`(06 出港查询同步修正))
- 「进港移库」`GnjMoveStashListActivity`(app 模块,**2026-08-03 归属更正后重做**:设计稿 11
实际归属**国内进港移库 DomImpMove**(后端 8 接口齐全、Pad 首页「国内进港→进港移库」既有页面),
最初误判为国际进港并按 IntExpMove 约定预接了不存在的 IntImpMove 模块(原缺口 #31),该套
module_gjj 前端实现已整体删除。现行实现:主列表双形态(Pad 布局移 layout-sw600dp 零改动),
待移库/已移库 Tab 走 **`search`/`searchMoved` 双端点**(`searchMoved` 出参无移库标识,前端按
端点给 bean 打 `isMoved`),Tab 角标 = 两端点各调一次 limit=1 取分页 total(`initPhoneExtras`
开关,仅手机发起);手机专属新页 `GnjMoveStashDetailActivity`(Intent 传 bean 零请求,三卡片:
基本信息/移库信息(仅已移库)/异常照片)+ `GnjMoveStashPhotoActivity`(进入先调 `detail` 回填
已有照片——`searchMoved` 出参无 pic 三字段 #34,提交走 **`modify` 最小体**
`{mawbId,remark,picNumber,pic,originalPic}`,与平板移交编辑页同链路);批量移库复用平板
`moveStashClick`(`move`:fid+ids)。手机首页入口在**国内 Tab**(`GnViewModel`,权限串
`GnjYiKu=AppDomImpMove`,需补通用 route 跳转逻辑,图标从 module_gnj 复制);
GnjMoveStashListActivity 补 @Route `/app/GnjMoveStashListActivity`。
2026-08-03 内网真实数据双端验证通过;筛选 A/B:`wbNo`/`awbType` 生效,
`fdate`/`fno`/`spCode` 被后端静默忽略(#32)、`opId`/`opDate` 恒 null(#33))
- 「进港仓库」`IntImpStorageUseActivity`(module_gjj,07 出港仓库的进港镜像;**首页菜单实际跳转页, - 「进港仓库」`IntImpStorageUseActivity`(module_gjj,07 出港仓库的进港镜像;**首页菜单实际跳转页,
旧版 `GjjWareHouseActivity` 路由已注释弃用**。差异:卡片/筛选用始发站(fdep 入参)替代目的站; 旧版 `GjjWareHouseActivity` 路由已注释弃用**。差异:卡片/筛选用始发站(fdep 入参)替代目的站;
搜索框「运单号/航班号」分流(含字母→fno、其余→wbNo,抓包证实);筛选 5 项无入参(缺口 #25, 搜索框「运单号/航班号」分流(含字母→fno、其余→wbNo,抓包证实);筛选 5 项无入参(缺口 #25,
@@ -1165,10 +1189,15 @@ override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) =
双变体首例:手机竖排时间线由 Activity `buildVerticalSteps` 动态构建,平板横向步骤条不动; 双变体首例:手机竖排时间线由 Activity `buildVerticalSteps` 动态构建,平板横向步骤条不动;
手机标题按形态切「运单追踪」)。平板侧滑筛选面板代码已判空守卫(手机变体无 filter_panel 手机标题按形态切「运单追踪」)。平板侧滑筛选面板代码已判空守卫(手机变体无 filter_panel
include,Binding 字段为 @Nullable) include,Binding 字段为 @Nullable)
- ✅ **设计稿 11 个入口(01~11)已于 2026-07-31 全部适配完成**
- ⏳ **待办**: - ⏳ **待办**:
- `PhoneSearchBar` 接入 AutoQuery 联想(`AutoQueryManager` 目前只对接了 Pad 系组件) - `PhoneSearchBar` 接入 AutoQuery 联想(`AutoQueryManager` 目前只对接了 Pad 系组件)
- 出库交接 Tab 角标恒 0:`IntExpOutHandover/pageQueryTotal` 出参恒 0(后端缺陷,问题清单 #6), - 联调新发现的后端缺口 #35~#41(ULD uld 搜索 500、IntImpStorage 列表过滤全失效、已移库无 mawbId
后端修复后角标自动恢复(`handoverState` 过滤本身已于 2026-07-29 实机验证通过)生效 无法补传照片等)待后端处理,详见问题清单
- ✅ **2026-10-09 后端反馈联调完成**:问题清单 #1~#34 逐项内网直调 + 模拟器端到端验证,结论写入清单 P 列。
**后端口径:手机筛选一律与平板端查询参数保持一致**(不为手机新增入参);分项统计统一取
`pageQueryTotal` 出参(`ManifestTotalDto` 已补齐分项字段),不要再按状态多调 total。
最新字段语义见 `.claude/skills/phone-adapt/references/api-doc.md` 末节
- 📋 **接口对接**:适配中发现的后端接口缺口统一记录在 `documents/后端接口对接问题清单.xlsx` - 📋 **接口对接**:适配中发现的后端接口缺口统一记录在 `documents/后端接口对接问题清单.xlsx`
(按模块/页面/接口定义/接口地址索引,与后端对接的正式文档,每页适配完必须维护); (按模块/页面/接口定义/接口地址索引,与后端对接的正式文档,每页适配完必须维护);
接口入参/出参一律以 api-doc MCP 为准核对,流程见 接口入参/出参一律以 api-doc MCP 为准核对,流程见
@@ -1261,6 +1290,8 @@ adb shell am start -n com.lukouguoji.aerologic/com.lukouguoji.gjc.activity.IntEx
| 通过 `Intent.putExtra(Serializable)` 传递的 Bean,切换其 `checked`/`showMore` 等状态时崩溃 `NullPointerException: ... on a null object reference` | 这些字段标了 `@Transient`(= JVM `transient`,本意是避免 Gson 网络请求序列化 `ObservableBoolean`),但 Java 对象序列化(Intent 传递走的机制)会跳过 `transient` 字段的恢复,反序列化后是 null | 接收方 `initOnCreated` 里对列表做 `.map { it.copy() }`——`.copy()` 会重新走一遍类体初始化逻辑,用全新 `ObservableBoolean(false)` 补上 | | 通过 `Intent.putExtra(Serializable)` 传递的 Bean,切换其 `checked`/`showMore` 等状态时崩溃 `NullPointerException: ... on a null object reference` | 这些字段标了 `@Transient`(= JVM `transient`,本意是避免 Gson 网络请求序列化 `ObservableBoolean`),但 Java 对象序列化(Intent 传递走的机制)会跳过 `transient` 字段的恢复,反序列化后是 null | 接收方 `initOnCreated` 里对列表做 `.map { it.copy() }`——`.copy()` 会重新走一遍类体初始化逻辑,用全新 `ObservableBoolean(false)` 补上 |
| 布局构建报 `Error: The string "--" is not permitted within comments` | XML 注释中出现连续双横线(如写"兜底 \"--\""),XML 规范禁止 | 注释里改用文字描述(如「兜底双横线」),代码绑定表达式中的 `"--"` 不受影响 | | 布局构建报 `Error: The string "--" is not permitted within comments` | XML 注释中出现连续双横线(如写"兜底 \"--\""),XML 规范禁止 | 注释里改用文字描述(如「兜底双横线」),代码绑定表达式中的 `"--"` 不受影响 |
| 多选页手机版点「全选」后底部「已选 N 项」不更新(单卡勾选正常) | 批量勾选在 `checkAllClick` 里直接 set,不走 FlowBus 的单卡 EVENT_CHECK_CHANGED 事件,`selectedCountText` 只在事件回调里更新 | `checkAllClick` 末尾自行同步 `selectedCountText`(平板布局不绑定该字段,无影响) | | 多选页手机版点「全选」后底部「已选 N 项」不更新(单卡勾选正常) | 批量勾选在 `checkAllClick` 里直接 set,不走 FlowBus 的单卡 EVENT_CHECK_CHANGED 事件,`selectedCountText` 只在事件回调里更新 | `checkAllClick` 末尾自行同步 `selectedCountText`(平板布局不绑定该字段,无影响) |
| 手机版数字输入框(`PhoneFormRow` 双向绑 Double 的 `xxxStr`)输入 "18" 变成 "81" | getter 由 Double 反推字符串("1" → "1.0"),与输入框文本不等触发 `setText`,光标回到开头 | Bean 里保留输入原文(`@Transient` 私有 String),getter 优先返回原文(参考 `GjcCheckInRecord.trayTotalWeightStr`) |
| XML 里 `bean.a ?? bean.b` 兜底不生效,字段明明有值却显示占位符 | 网络层转换器把 JSON null 归一成空串 `""`,DataBinding 的 `??` 只判 null 不判空串,永远取到左侧的空串 | 在 Bean 上加计算属性做 `isNullOrEmpty` 双兜底(如 `agentDisplay`),XML 绑计算属性(进港查询/出港查询代理人已踩过) |
--- ---
@@ -1505,10 +1536,14 @@ Glide.with(itemView.context).load(glideUrl).into(binding.ivThumbnail)
### 双端验证命令 ### 双端验证命令
```bash ```bash
~/Library/Android/sdk/emulator/emulator -list-avds # 可用:Medium_Phone_API_36.1(手机)、Aerologic_Tablet(平板) ~/Library/Android/sdk/emulator/emulator -list-avds # 可用:Aerologic_Phone_5.5_API31(手机,首选)、Aerologic_Tablet(平板)、Medium_Phone_API_36.1
adb -s <设备> shell wm size && adb -s <设备> shell wm density # 确认屏幕参数 adb -s <设备> shell wm size && adb -s <设备> shell wm density # 确认屏幕参数
adb -s <设备> logcat -d -s BaseActivity:D # 查看形态识别结果 PHONE(sw=411dp) / TABLET(sw=800dp) adb -s <设备> logcat -d -s BaseActivity:D # 查看形态识别结果 PHONE(sw=384dp) / TABLET(sw=800dp)
adb -s <设备> exec-out screencap -p > /tmp/shot.png # 截图比对 adb -s <设备> exec-out screencap -p > /tmp/shot.png # 截图比对
``` ```
> 手机端验证**首选 `Aerologic_Phone_5.5_API31`**:按真实交付手持机 1:1 定制——720×1440、5.51 寸(300dpi,sw=384dp)、
> Android 12 / API 31(与 targetSdk 一致)、arm64-v8a,与实机的差异最小。`Medium_Phone_API_36.1`(1080×2400、API 36)
> 仅作兼容性参考,分辨率与系统版本都和交付机不符。
> 改动 `BaseActivity` / `BaseBindingActivity` / 公共组件后,**必须在平板上回归**,确认现有平板版行为不变。 > 改动 `BaseActivity` / `BaseBindingActivity` / 公共组件后,**必须在平板上回归**,确认现有平板版行为不变。

View File

@@ -26,8 +26,8 @@ android {
applicationId "com.lukouguoji.aerologic" applicationId "com.lukouguoji.aerologic"
minSdkVersion 24 minSdkVersion 24
targetSdkVersion 30 targetSdkVersion 30
versionCode 88 versionCode 87
versionName "1.8.8" versionName "1.8.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -98,12 +98,6 @@
android:exported="true" android:exported="true"
tools:replace="android:exported" /> tools:replace="android:exported" />
<!-- 出港运抵-补充信息(手机版适配验证页) -->
<activity
android:name="com.lukouguoji.gjc.activity.IntExpArriveSupplementActivity"
android:exported="true"
tools:replace="android:exported" />
<!-- 出港查询(手机版适配验证页) --> <!-- 出港查询(手机版适配验证页) -->
<activity <activity
android:name="com.lukouguoji.gjc.activity.GjcQueryActivity" android:name="com.lukouguoji.gjc.activity.GjcQueryActivity"
@@ -152,5 +146,29 @@
android:exported="true" android:exported="true"
tools:replace="android:exported" /> tools:replace="android:exported" />
<!-- 进港查询(手机版适配验证页) -->
<activity
android:name="com.lukouguoji.gjj.activity.IntImpQueryActivity"
android:exported="true"
tools:replace="android:exported" />
<!-- 进港移库(国内进港,手机版适配验证页) -->
<activity
android:name="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity"
android:exported="true"
tools:replace="android:exported" />
<!-- 进港移库-运单详情(手机版专属页) -->
<activity
android:name="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity"
android:exported="true"
tools:replace="android:exported" />
<!-- 进港移库-拍照上传(手机版专属页) -->
<activity
android:name="com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoActivity"
android:exported="true"
tools:replace="android:exported" />
</application> </application>
</manifest> </manifest>

View File

@@ -157,12 +157,6 @@
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:exported="false" android:exported="false"
android:screenOrientation="unspecified" /> android:screenOrientation="unspecified" />
<!-- 国际出港运抵-补充信息(5.5寸手机版专属页面) -->
<activity
android:name="com.lukouguoji.gjc.activity.IntExpArriveSupplementActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="unspecified" />
<!-- 国际出港仓库 --> <!-- 国际出港仓库 -->
<activity <activity
android:name="com.lukouguoji.gjc.activity.IntExpStorageUseActivity" android:name="com.lukouguoji.gjc.activity.IntExpStorageUseActivity"
@@ -375,6 +369,20 @@
android:name=".page.gnj.move.stash.list.GnjMoveStashListActivity" android:name=".page.gnj.move.stash.list.GnjMoveStashListActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:screenOrientation="unspecified" /> android:screenOrientation="unspecified" />
<!-- 国内进港移库-运单详情(5.5寸手机版专属) -->
<activity
android:name=".page.gnj.move.stash.detail.GnjMoveStashDetailActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="unspecified" />
<!-- 国内进港移库-拍照上传(5.5寸手机版专属) -->
<activity
android:name=".page.gnj.move.stash.photo.GnjMoveStashPhotoActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="unspecified" />
<activity <activity
android:name=".page.gnj.stash.list.GnjStashListActivity" android:name=".page.gnj.stash.list.GnjStashListActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"

View File

@@ -0,0 +1,49 @@
package com.lukouguoji.aerologic.page.gnj.move.stash.detail
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.google.gson.Gson
import com.lukouguoji.aerologic.R
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashDetailBinding
import com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoActivity
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.MoveStashBean
import com.lukouguoji.module_base.common.Constant
/**
* 国内进港移库「运单详情」(5.5 寸手持端专属页面,无平板变体)
*
* 由列表卡片的「查看详情」进入;「异常照片」卡片的加号槽位跳拍照上传页。
* 展示字段全部由列表项带入(Gson JSON),零新增请求。
*/
class GnjMoveStashDetailActivity :
BaseBindingActivity<ActivityGnjMoveStashDetailBinding, GnjMoveStashDetailViewModel>() {
override fun layoutId() = R.layout.activity_gnj_move_stash_detail
override fun viewModelClass() = GnjMoveStashDetailViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("运单详情")
binding.viewModel = viewModel
binding.activity = this
viewModel.initOnCreated(intent)
}
/** 「异常照片」加号槽位 → 拍照上传页 */
fun openPhoto() {
viewModel.dataBean.value?.let {
GnjMoveStashPhotoActivity.start(this, it.mawbId, it.wbNo, it.moveId)
}
}
companion object {
@JvmStatic
fun start(context: Context, bean: MoveStashBean) {
context.startActivity(
Intent(context, GnjMoveStashDetailActivity::class.java)
.putExtra(Constant.Key.DATA, Gson().toJson(bean))
)
}
}
}

View File

@@ -0,0 +1,25 @@
package com.lukouguoji.aerologic.page.gnj.move.stash.detail
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
import com.lukouguoji.module_base.base.BaseViewModel
import com.lukouguoji.module_base.bean.MoveStashBean
import com.lukouguoji.module_base.common.Constant
/**
* 国内进港移库「运单详情」ViewModel(5.5 寸手持端专属页面)
*
* 列表项数据已包含详情页所需全部字段(含 isMoved 端点打标),直接由列表带入,不额外请求。
*/
class GnjMoveStashDetailViewModel : BaseViewModel() {
val dataBean = MutableLiveData(MoveStashBean())
fun initOnCreated(intent: Intent) {
val json = intent.getStringExtra(Constant.Key.DATA).orEmpty()
if (json.isNotEmpty()) {
dataBean.value = Gson().fromJson(json, MoveStashBean::class.java)
}
}
}

View File

@@ -1,36 +1,104 @@
package com.lukouguoji.aerologic.page.gnj.move.stash.list package com.lukouguoji.aerologic.page.gnj.move.stash.list
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import com.lukouguoji.aerologic.R import androidx.databinding.ObservableBoolean
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashListBinding import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.aerologic.R
import com.lukouguoji.module_base.ktx.getLifecycleOwner import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashListBinding
import com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoActivity
class GnjMoveStashListActivity : import com.lukouguoji.module_base.base.BaseBindingActivity
BaseBindingActivity<ActivityGnjMoveStashListBinding, GnjMoveStashListViewModel>() { import com.lukouguoji.module_base.bean.MoveStashBean
override fun layoutId() = R.layout.activity_gnj_move_stash_list import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
override fun viewModelClass() = GnjMoveStashListViewModel::class.java import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.getLifecycleOwner
override fun initOnCreate(savedInstanceState: Bundle?) { import com.lukouguoji.module_base.router.ARouterConstants
setBackArrow("国内进港移库") import com.lukouguoji.module_base.util.DeviceUtil
binding.viewModel = viewModel @Route(path = ARouterConstants.ACTIVITY_URL_GNJ_MOVE_STASH_LIST)
class GnjMoveStashListActivity :
viewModel.pageModel BaseBindingActivity<ActivityGnjMoveStashListBinding, GnjMoveStashListViewModel>() {
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner()) override fun layoutId() = R.layout.activity_gnj_move_stash_list
viewModel.refresh() override fun viewModelClass() = GnjMoveStashListViewModel::class.java
}
/** 筛选弹层显隐(仅手机 UI 状态,平板布局不绑定) */
companion object { val filterPanelVisible = ObservableBoolean(false)
@JvmStatic
fun start(context: Context) { override fun initOnCreate(savedInstanceState: Bundle?) {
val starter = Intent(context, GnjMoveStashListActivity::class.java) // 设计稿手机端标题为「进港移库」,平板保持原标题
context.startActivity(starter) setBackArrow(if (DeviceUtil.isPhone()) "进港移库" else "国内进港移库")
}
} binding.viewModel = viewModel
binding.activity = this
}
viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
if (DeviceUtil.isPhone()) {
// 手机专属:Tab 角标 + 特码字典等额外请求
viewModel.initPhoneExtras()
// 卡片勾选变化 → 更新全选状态与已选计数
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) {
viewModel.updateCheckAllStatus()
}
}
// 拍照上传等子页面完成后刷新列表(平板无发射方,观察无副作用)
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
viewModel.refresh()
}
// ==================== 筛选弹层(仅手机) ====================
fun toggleFilterPanel() {
filterPanelVisible.set(!filterPanelVisible.get())
}
/** 筛选:重置(只清空弹层内条件,不影响 Tab 与顶部搜索框) */
fun resetFilter() {
viewModel.flightDate.value = ""
viewModel.flightNo.value = ""
viewModel.spCode.value = ""
viewModel.businessType.value = ""
}
fun confirmFilter() {
filterPanelVisible.set(false)
viewModel.refresh()
}
// ==================== 卡片长按「拍照」浮层(仅手机) ====================
/** 卡片长按:展开操作浮层(View.OnLongClickListener 要求返回 Boolean) */
fun showCardActionPhone(bean: MoveStashBean): Boolean {
bean.actionMenuVisible.set(true)
return true
}
/** 点浮层空白处关闭 */
fun hideCardActionPhone(bean: MoveStashBean) {
bean.actionMenuVisible.set(false)
}
/** 浮层「拍照」→ 拍照上传页 */
fun openPhoto(bean: MoveStashBean) {
bean.actionMenuVisible.set(false)
GnjMoveStashPhotoActivity.start(this, bean.mawbId, bean.wbNo, bean.moveId)
}
companion object {
@JvmStatic
fun start(context: Context) {
val starter = Intent(context, GnjMoveStashListActivity::class.java)
context.startActivity(starter)
}
}
}

View File

@@ -3,34 +3,64 @@ package com.lukouguoji.aerologic.page.gnj.move.stash.list
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import com.lukouguoji.aerologic.databinding.ItemGnjMoveStashListBinding import com.lukouguoji.aerologic.databinding.ItemGnjMoveStashListBinding
import com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity
import com.lukouguoji.gnj.page.yiku.handover.GnjYiKuHandoverActivity import com.lukouguoji.gnj.page.yiku.handover.GnjYiKuHandoverActivity
import com.lukouguoji.module_base.base.BaseViewHolder import com.lukouguoji.module_base.base.BaseViewHolder
import com.lukouguoji.module_base.bean.MoveStashBean import com.lukouguoji.module_base.bean.MoveStashBean
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
/**
* 国内进港移库列表项 ViewHolder(双形态共用)
*
* 平板/手机布局差异较大,单侧独有的控件 id 在 Binding 基类中为 @Nullable,
* 全部走判空调用(平板:swipe_menu/btn_edit/ll;手机:ll_detail)。
*/
class GnjMoveStashListViewHolder(view: View) : class GnjMoveStashListViewHolder(view: View) :
BaseViewHolder<MoveStashBean, ItemGnjMoveStashListBinding>(view) { BaseViewHolder<MoveStashBean, ItemGnjMoveStashListBinding>(view) {
override fun onBind(item: Any?, position: Int) { override fun onBind(item: Any?, position: Int) {
val bean = getItemBean(item)!! val bean = getItemBean(item)!!
binding.bean = bean binding.bean = bean
binding.position = position
binding.activity = itemView.context as? GnjMoveStashListActivity
// 勾选框(双形态共有):切换选中;手机端另行广播刷新全选/已选计数
binding.ivIcon.setOnClickListener { binding.ivIcon.setOnClickListener {
bean.checked.set(!bean.checked.get()) bean.checked.set(!bean.checked.get())
binding.executePendingBindings()
GlobalScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).emit("check_changed")
}
} }
// ===== 平板独有交互 =====
// 点击列表项进入移交详情 // 点击列表项进入移交详情
binding.ll.setOnClickListener { binding.ll?.setOnClickListener {
GnjYiKuHandoverActivity.startForDetails(itemView.context, bean.mawbId) GnjYiKuHandoverActivity.startForDetails(itemView.context, bean.mawbId)
} }
// 侧滑菜单 - 编辑按钮 // 侧滑菜单 - 编辑按钮
binding.btnEdit.setOnClickListener { binding.btnEdit?.setOnClickListener {
binding.swipeMenu.quickClose() binding.swipeMenu?.quickClose()
if (bean.pickFlag == "1") { if (bean.pickFlag == "1") {
Toast.makeText(itemView.context, "已出库,不可编辑", Toast.LENGTH_SHORT).show() Toast.makeText(itemView.context, "已出库,不可编辑", Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
GnjYiKuHandoverActivity.startForEdit(itemView.context, bean.mawbId) GnjYiKuHandoverActivity.startForEdit(itemView.context, bean.mawbId)
} }
// ===== 手机独有交互 =====
// 「查看详情」→ 手机版运单详情页
binding.llDetail?.setOnClickListener {
GnjMoveStashDetailActivity.start(itemView.context, bean)
}
binding.executePendingBindings()
} }
} }

View File

@@ -49,6 +49,41 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
val number = MutableLiveData("0") val number = MutableLiveData("0")
val weight = MutableLiveData("0") val weight = MutableLiveData("0")
// ========== 5.5 寸手持端专属字段(平板布局不绑定,默认值保证平板行为零变化) ==========
/** 移库状态 Tab:"0" 待移库(DomImpMove/search)/ "1" 已移库(DomImpMove/searchMoved) */
val moveState = MutableLiveData("0")
/** 手机筛选弹层:航班日期 / 航班号 / 特码(默认空 → 不进请求) */
val flightDate = MutableLiveData("")
val flightNo = MutableLiveData("")
val spCode = MutableLiveData("")
val spCodeList = MutableLiveData<List<KeyValue>>(emptyList())
val moveStateTabs = MutableLiveData(
listOf(KeyValue("待移库", "0"), KeyValue("已移库", "1"))
)
/** 状态 Tab 角标(与 [moveStateTabs] 等长,取两端点分页 total) */
val moveStateCounts = MutableLiveData(listOf("", ""))
/** 底部操作条「已选 N 项」 */
val selectedCount = MutableLiveData("0")
/** 全选状态(手机 PhoneBottomBar 勾选图标) */
val isAllChecked = MutableLiveData(false)
/** 手机专属的额外请求(Tab 角标、特码字典)开关,仅手机形态在 Activity 中开启 */
private var phoneExtrasEnabled = false
fun initPhoneExtras() {
phoneExtrasEnabled = true
// 特码字典:flag=0 国内、ieFlag=I 进港(是否生效待实测,见问题清单)
DictUtils.getSpecialCodeList(addAll = false, flag = 0, ieFlag = "I") { list ->
spCodeList.postValue(listOf(KeyValue("全部", "")) + list)
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// 方法区 // 方法区
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -95,25 +130,73 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
} }
/** 查询条件(分页参数除外),search / searchMoved / searchTotal 三处共用同一套入参 */
private fun buildFilterParams(): Map<String, Any?> = mapOf(
"awbType" to businessType.value!!.ifEmpty { null },
"wbNo" to waybill.value!!.ifEmpty { null },
"carrier" to carrier.value!!.ifEmpty { null },
// 以下为手机筛选弹层字段,平板恒为空串 → 恒传 null,行为不变
"fdate" to flightDate.value?.ifEmpty { null },
"fno" to flightNo.value?.ifEmpty { null },
"spCode" to spCode.value?.ifEmpty { null },
)
override fun getData() { override fun getData() {
val body = mapOf( val body = (buildFilterParams() + mapOf(
"page" to pageModel.page, "page" to pageModel.page,
"limit" to pageModel.limit, "limit" to pageModel.limit,
"awbType" to businessType.value!!.ifEmpty { null }, )).toRequestBody()
"wbNo" to waybill.value!!.ifEmpty { null }, val moved = moveState.value == "1"
"carrier" to carrier.value!!.ifEmpty { null },
).toRequestBody()
launchLoadingCollect({ launchLoadingCollect({
NetApply.api.getGnjMoveStashList( // 平板 moveState 恒为 "0" → 恒走 search(原行为);已移库 Tab 走 searchMoved
body if (moved) NetApply.api.getGnjMoveStashMovedList(body)
) else NetApply.api.getGnjMoveStashList(body)
}) { }) {
onSuccess = { onSuccess = {
pageModel.handleListBean(it) // searchMoved 出参无移库标识,按端点打标(卡片形态/详情移库信息卡依赖)
it.list?.forEach { bean -> bean.isMoved = moved }
pageModel.handleListBean(it) { updateCheckAllStatus() }
count.value = it.total count.value = it.total
} }
} }
getCountData(body) getCountData(body)
if (phoneExtrasEnabled) loadTabCounts()
}
/** Tab 角标:search / searchMoved 各以 limit=1 查一次取分页 total(仅手机形态发起) */
private fun loadTabCounts() {
val body = (buildFilterParams() + mapOf("page" to 1, "limit" to 1)).toRequestBody()
launchCollect({ NetApply.api.getGnjMoveStashList(body) }) {
onSuccess = { updateTabCount(0, it.total) }
}
launchCollect({ NetApply.api.getGnjMoveStashMovedList(body) }) {
onSuccess = { updateTabCount(1, it.total) }
}
}
private fun updateTabCount(index: Int, total: Int?) {
val current = moveStateCounts.value?.toMutableList() ?: mutableListOf("", "")
if (index < current.size) {
current[index] = (total ?: 0).toString()
moveStateCounts.value = current
}
}
// ========== 手机端全选 / 已选计数 ==========
fun toggleSelectAll() {
val list = pageModel.rv?.commonAdapter()?.items
?.filterIsInstance<MoveStashBean>() ?: return
CheckUtil.handleAllCheck(list)
updateCheckAllStatus()
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
}
fun updateCheckAllStatus() {
val list = pageModel.rv?.commonAdapter()?.items
?.filterIsInstance<MoveStashBean>() ?: return
isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() }
selectedCount.value = list.count { it.checked.get() }.toString()
} }
private fun getCountData(body: RequestBody) { private fun getCountData(body: RequestBody) {

View File

@@ -0,0 +1,49 @@
package com.lukouguoji.aerologic.page.gnj.move.stash.photo
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.lukouguoji.aerologic.R
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashPhotoBinding
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.ktx.addOnItemClickListener
/**
* 国内进港移库「拍照上传」(5.5 寸手持端专属页面,无平板变体)
*
* 由列表卡片长按浮层「拍照」或运单详情「异常照片」加号槽位进入。
* 已有照片经 DomImpMove/detail 回填,提交走 DomImpMove/modify
* (mawbId + pic/originalPic/picNumber 三字段规范,与平板移交编辑页同一链路)。
*/
class GnjMoveStashPhotoActivity :
BaseBindingActivity<ActivityGnjMoveStashPhotoBinding, GnjMoveStashPhotoViewModel>() {
override fun layoutId() = R.layout.activity_gnj_move_stash_photo
override fun viewModelClass() = GnjMoveStashPhotoViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("拍照上传")
binding.viewModel = viewModel
viewModel.rv = binding.rv
binding.rv.addOnItemClickListener(viewModel)
viewModel.initOnCreated(intent)
}
companion object {
/** 已移库运单的 moveId(movedDetail 回填照片用) */
const val EXTRA_MOVE_ID = "moveId"
@JvmStatic
fun start(context: Context, mawbId: String, wbNo: String, moveId: String = "") {
context.startActivity(
Intent(context, GnjMoveStashPhotoActivity::class.java)
.putExtra(Constant.Key.ID, mawbId)
.putExtra(Constant.Key.CODE, wbNo)
.putExtra(EXTRA_MOVE_ID, moveId)
)
}
}
}

View File

@@ -0,0 +1,188 @@
package com.lukouguoji.aerologic.page.gnj.move.stash.photo
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.recyclerview.widget.RecyclerView
import com.lukouguoji.module_base.base.BaseViewModel
import com.lukouguoji.module_base.bean.FileBean
import com.lukouguoji.module_base.bean.GnjYiKuBean
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.ImageSelectNewViewHolder
import com.lukouguoji.module_base.interfaces.IOnItemClickListener
import com.lukouguoji.module_base.ktx.commonAdapter
import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.util.MediaUtil
import com.lukouguoji.module_base.util.UploadUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
/**
* 国内进港移库「拍照上传」ViewModel(5.5 寸手持端专属页面)
*
* 图片选择/预览/删除复用 module_base 的 ImageSelectNewViewHolder(末位加号槽位)。
* 已有照片回填:待移库经 DomImpMove/detail(mawbId),已移库经 DomImpMove/movedDetail(moveId,
* searchMoved 出参不含 mawbId)。提交走 DomImpMove/modify 最小体
* (mawbId/remark/picNumber/pic/originalPic),与平板移交编辑页同一链路。
*/
class GnjMoveStashPhotoViewModel : BaseViewModel(), IOnItemClickListener {
var mawbId = ""
/** 已移库运单的移库记录 ID(searchMoved 出参),用于 movedDetail 回填 */
var moveId = ""
/** 运单号(副标题展示,由列表带入) */
val wbNo = MutableLiveData("")
/** detail 回填的完整 bean(提交时保留 remark 等原值) */
private var detailBean = GnjYiKuBean()
// 图片网格适配器配置(复用公共图片选择组件)
val itemViewHolder = ImageSelectNewViewHolder::class.java
val itemLayoutId = com.lukouguoji.module_base.R.layout.item_image_select_new
var rv: RecyclerView? = null
fun initOnCreated(intent: Intent) {
mawbId = intent.getStringExtra(Constant.Key.ID).orEmpty()
wbNo.value = intent.getStringExtra(Constant.Key.CODE).orEmpty()
moveId = intent.getStringExtra(GnjMoveStashPhotoActivity.EXTRA_MOVE_ID).orEmpty()
// 已有照片回填(pic 缩略图 + originalPic 原图),末位补加号槽位;
// 已移库运单无 mawbId,改按 moveId 走 movedDetail
launchCollect({
if (mawbId.isEmpty() && moveId.isNotEmpty()) NetApply.api.getGnjMoveStashMovedDetail(moveId)
else NetApply.api.getGnjMoveStashDetail(mawbId)
}) {
onSuccess = {
detailBean = it.data ?: GnjYiKuBean()
val picList = detailBean.pic.split(",").filter { p -> p.isNotEmpty() }
val originalList = detailBean.originalPic.split(",").filter { p -> p.isNotEmpty() }
val images = picList.mapIndexed { index, picUrl ->
FileBean(
path = MediaUtil.fillUrl(picUrl),
url = picUrl,
originalPic = MediaUtil.fillUrl(originalList.getOrElse(index) { picUrl })
)
}
rv?.post {
rv?.commonAdapter()?.refresh(images + FileBean())
}
}
onFailed = { _, _ ->
// 详情取不到也允许直接新增照片
rv?.post { rv?.commonAdapter()?.refresh(listOf(FileBean())) }
}
}
}
/**
* 确认提交:先补传未上传的本地图片,再按三字段规范提交 modify
*/
fun submitClick() {
// modify 以 mawbId 定位运单;已移库运单 searchMoved/movedDetail 均不返回 mawbId(后端缺口),无法提交
if (mawbId.isEmpty()) {
showToast("已移库运单缺少运单ID,暂不支持提交照片")
return
}
(rv?.commonAdapter()?.items ?: emptyList())
.asFlow()
.map { it as FileBean }
.filter { it.path.isNotEmpty() && it.url.isEmpty() }
.onEach {
val data = UploadUtil.upload(it.path).data
// UploadUtil 返回:newName=原图(较大),zipFileName=缩略图(较小)
it.url = data?.zipFileName ?: ""
it.originalPic = data?.newName ?: ""
}
.flowOn(Dispatchers.IO)
.onStart { showLoading() }
.catch {
showToast(it.message.noNull("上传图片失败"))
dismissLoading()
}
.onCompletion {
launchLoadingCollect({
val list = (rv?.commonAdapter()?.items ?: emptyList())
.filterIsInstance<FileBean>()
.filter { it.path.isNotEmpty() }
// 最小体提交:只更新图片三字段(remark 保留 detail 原值),与平板移交编辑页一致
val params = mapOf(
"mawbId" to mawbId,
"remark" to detailBean.remark,
"picNumber" to list.size.toString(),
"pic" to list.joinToString(",") { MediaUtil.removeUrl(it.url) },
"originalPic" to list.joinToString(",") { MediaUtil.removeUrl(it.originalPic) },
).toRequestBody(removeEmptyOrNull = true)
NetApply.api.modifyGnjMoveStash(params)
}) {
onSuccess = {
showToast("提交成功")
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}
getTopActivity().finish()
}
onFailed = { _, msg ->
showToast(msg.noNull("提交失败"))
}
}
}
.launchIn(viewModelScope)
}
/**
* 「拍照/上传」按钮:直接唤起图片选择(含拍照入口)
*/
fun pickClick() {
MediaUtil.pickImage(getTopActivity(), maxNum = 9) { results ->
val adapter = rv?.commonAdapter() ?: return@pickImage
results.forEach { result ->
// 在加号槽位(最后一项)之前插入
val insertPos = (adapter.items.size - 1).coerceAtLeast(0)
adapter.items.add(insertPos, FileBean(path = result.realPath))
adapter.notifyItemInserted(insertPos)
}
}
}
/**
* 图片网格点击回调(长按出删除角标 / 点删除角标移除)
*/
override fun onItemClick(position: Int, type: Int) {
val adapter = rv?.commonAdapter() ?: return
val bean = adapter.getItem(position) as? FileBean ?: return
when (type) {
com.lukouguoji.module_base.R.id.rl -> {
if (bean.path.isNotEmpty()) {
bean.canDelete.set(!bean.canDelete.get())
}
}
com.lukouguoji.module_base.R.id.iv_delete -> {
adapter.removeItem(position)
}
else -> {}
}
}
}

View File

@@ -91,7 +91,9 @@ class LogDetailActivity : BaseBindingActivity<ActivityLogDetailBinding, LogDetai
val step = steps[i] val step = steps[i]
val isActive = step.code in activeCodes val isActive = step.code in activeCodes
val isLast = i == steps.size - 1 val isLast = i == steps.size - 1
val time = logs.lastOrNull { it.status == step.code }?.opDate ?: "" val stepLog = logs.lastOrNull { it.status == step.code }
val time = stepLog?.opDate ?: ""
val statusContent = stepLog?.content ?: ""
val row = LinearLayout(this).apply { val row = LinearLayout(this).apply {
orientation = LinearLayout.HORIZONTAL orientation = LinearLayout.HORIZONTAL
@@ -141,12 +143,41 @@ class LogDetailActivity : BaseBindingActivity<ActivityLogDetailBinding, LogDetai
} }
setPadding(0, 0, 0, if (isLast) 0 else dp(24)) setPadding(0, 0, 0, if (isLast) 0 else dp(24))
} }
content.addView(TextView(this).apply { // 名称行:步骤名(左)+ 已发生节点的状态 pill(右,设计稿如「放行状态:01」)
val nameRow = LinearLayout(this).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
nameRow.addView(TextView(this).apply {
text = step.name text = step.name
setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f) setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
setTextColor(if (isActive) colorGreen else colorGray) setTextColor(if (isActive) colorGreen else colorGray)
if (isActive) setTypeface(typeface, android.graphics.Typeface.BOLD) if (isActive) setTypeface(typeface, android.graphics.Typeface.BOLD)
layoutParams = LinearLayout.LayoutParams(
0, LinearLayout.LayoutParams.WRAP_CONTENT
).apply { weight = 1f }
}) })
if (isActive && statusContent.isNotEmpty()) {
nameRow.addView(TextView(this).apply {
text = statusContent
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
setTextColor(colorGreen)
setBackgroundResource(com.lukouguoji.module_base.R.drawable.bg_phone_tag_green)
setPadding(dp(8), dp(3), dp(8), dp(3))
maxLines = 1
ellipsize = android.text.TextUtils.TruncateAt.END
maxWidth = dp(180)
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply { leftMargin = dp(8) }
})
}
content.addView(nameRow)
if (time.isNotEmpty()) { if (time.isNotEmpty()) {
content.addView(TextView(this).apply { content.addView(TextView(this).apply {
text = time text = time

View File

@@ -36,7 +36,7 @@ class UldEditViewModel : BaseViewModel() {
// ========== 手机版(5.5 寸手持端)专用 ========== // ========== 手机版(5.5 寸手持端)专用 ==========
/** 来源选项(本地枚举;后端字段暂不支持,需求已登记 documents/后端接口对接问题清单.xlsx) */ /** 来源选项(与平板端一致:空运 / 陆运) */
val sourceEditList = MutableLiveData<List<KeyValue>>( val sourceEditList = MutableLiveData<List<KeyValue>>(
listOf( listOf(
KeyValue("空运", "空运"), KeyValue("空运", "空运"),
@@ -45,8 +45,8 @@ class UldEditViewModel : BaseViewModel() {
) )
/** /**
* 手机版状态选项。设计稿为三态(可用/占用/维修),后端枚举当前仅 0/1, * 手机版状态选项。后端枚举维持两态(0 正常 / 1 故障,不新增「占用」),
* 按语义映射 可用=0 / 维修=1;「占用」待后端扩展枚举后放开(需求已登记清单)。 * 按设计稿文案映射 可用=0 / 维修=1。
*/ */
val statusPhoneList = listOf( val statusPhoneList = listOf(
KeyValue("可用", "0"), KeyValue("可用", "0"),
@@ -64,7 +64,7 @@ class UldEditViewModel : BaseViewModel() {
loadCarrierList(null) loadCarrierList(null)
// 手机版新增默认所在港 HFE(设计稿约定);平板无此预填,行为不变 // 手机版新增默认所在港 HFE(设计稿约定);平板无此预填,行为不变
if (DeviceUtil.isPhone()) { if (DeviceUtil.isPhone()) {
uldBean.value = uldBean.value?.copy(airport = "HFE") uldBean.value = uldBean.value?.copy(nowPort = "HFE")
} }
} else { } else {
getData() getData()
@@ -107,11 +107,24 @@ class UldEditViewModel : BaseViewModel() {
showToast("自重不能为空") showToast("自重不能为空")
return return
} }
if (bean.checkInDate.isBlank()) { // saveUld 缺最大装载容积/最大载重时后端直接 500(内网实测),提前拦截
if (bean.maxVolume.isBlank()) {
showToast("最大装载容积不能为空")
return
}
if (bean.maxWeight.isBlank()) {
showToast("最大载重不能为空")
return
}
if (bean.inStoreTime.isBlank()) {
showToast("入库时间不能为空") showToast("入库时间不能为空")
return return
} }
} }
// 手机版日期行只产出 yyyy-MM-dd,而 inStoreTime 为 date-time(仅日期后端返回 400),补齐时分秒
if (DeviceUtil.isPhone() && bean.inStoreTime.length == 10) {
bean.inStoreTime = "${bean.inStoreTime} 00:00:00"
}
val isAdd = pageType.value == DetailsPageType.Add val isAdd = pageType.value == DetailsPageType.Add
launchLoadingCollect({ launchLoadingCollect({
if (isAdd) NetApply.api.saveUld(bean.toRequestBody()) if (isAdd) NetApply.api.saveUld(bean.toRequestBody())
@@ -121,7 +134,7 @@ class UldEditViewModel : BaseViewModel() {
showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功")) showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功"))
if (isAdd) { if (isAdd) {
// 清空表单继续录入;手机版保持所在港默认 HFE(与进入页面时一致) // 清空表单继续录入;手机版保持所在港默认 HFE(与进入页面时一致)
uldBean.value = if (DeviceUtil.isPhone()) ULDBean(airport = "HFE") else ULDBean() uldBean.value = if (DeviceUtil.isPhone()) ULDBean(nowPort = "HFE") else ULDBean()
} else { } else {
getData() getData()
} }

View File

@@ -70,13 +70,11 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
/** /**
* 手机版筛选:重置 * 手机版筛选:重置
* *
* 只清空弹层内的四个条件(入库时间 / 进港航班号 / 所属航司 / 来源),不影响顶部搜索框。 * 只清空弹层内的两个条件(状态 / 所属航司),不影响顶部搜索框。
*/ */
fun resetFilter() { fun resetFilter() {
viewModel.checkInDateFilter.value = "" viewModel.status.value = ""
viewModel.ifNoFilter.value = ""
viewModel.uldSuffix.value = "" viewModel.uldSuffix.value = ""
viewModel.sourceFilter.value = ""
} }
/** 手机版筛选:确认 */ /** 手机版筛选:确认 */
@@ -85,7 +83,7 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
viewModel.refresh() viewModel.refresh()
} }
/** 手机版批量删除(二次确认后逐条调用单条删除接口) */ /** 手机版批量删除(二次确认后调用批量删除接口) */
fun showDeleteConfirmDialog() { fun showDeleteConfirmDialog() {
val selected = viewModel.getSelectedItems() val selected = viewModel.getSelectedItems()
if (selected.isEmpty()) { if (selected.isEmpty()) {

View File

@@ -27,20 +27,15 @@ class UldListViewModel : BasePageViewModel() {
val uldSuffix = MutableLiveData("") val uldSuffix = MutableLiveData("")
val uldNo = MutableLiveData("") val uldNo = MutableLiveData("")
// ========== 手机版(5.5 寸手持端)专用筛选条件 ========== // ========== 手机版(5.5 寸手持端)筛选 ==========
// 平板布局无对应控件,恒为空 → 不进请求参数,平板查询行为与改造前完全一致。 // 后端确认查询入参与平板端保持一致(uld / status / uldSuffix),手机筛选弹层只放 状态 + 所属航司。
// 三个入参后端暂不支持(api-doc 已核对),已登记 documents/后端接口对接问题清单.xlsx,
// 按「以设计为准」的约定照传(后端静默忽略无副作用),后端支持后自动生效。
val checkInDateFilter = MutableLiveData("") // 入库时间
val ifNoFilter = MutableLiveData("") // 进港航班号
val sourceFilter = MutableLiveData("") // 来源:空运 / 陆运
/** 来源下拉(本地枚举,后端字段需求已登记清单) */ /** 手机版状态下拉:文案与卡片标签一致(可用=0 / 维修=1) */
val sourceList = MutableLiveData<List<KeyValue>>( val statusPhoneFilterList = MutableLiveData<List<KeyValue>>(
listOf( listOf(
KeyValue("全部", ""), KeyValue("全部", ""),
KeyValue("空运", "空运"), KeyValue("可用", "0"),
KeyValue("陆运", "陆运"), KeyValue("维修", "1"),
) )
) )
@@ -82,10 +77,6 @@ class UldListViewModel : BasePageViewModel() {
"status" to status.value?.ifEmpty { null }, "status" to status.value?.ifEmpty { null },
"uldSuffix" to uldSuffix.value?.ifEmpty { null }, "uldSuffix" to uldSuffix.value?.ifEmpty { null },
"uld" to uldNo.value?.ifEmpty { null }, "uld" to uldNo.value?.ifEmpty { null },
// 手机版筛选维度(平板恒空不参与);后端暂不支持这三个入参,已登记清单
"checkInDate" to checkInDateFilter.value?.ifEmpty { null },
"ifNo" to ifNoFilter.value?.ifEmpty { null },
"source" to sourceFilter.value?.ifEmpty { null },
).toRequestBody() ).toRequestBody()
) )
}) { }) {
@@ -122,10 +113,7 @@ class UldListViewModel : BasePageViewModel() {
?.filter { it.checked.get() } ?: emptyList() ?.filter { it.checked.get() } ?: emptyList()
/** /**
* 批量删除选中的 ULD。 * 批量删除选中的 ULD(/eqm/uld/deleteUldBatch,请求体为 ULD 编号数组,一次调用)。
*
* 后端仅有单条删除接口(/eqm/uld/deleteUld,批量接口需求已登记清单),
* 此处按队列逐条串行调用,全部完成后统一刷新;中途失败即停并提示。
*/ */
fun deleteSelected() { fun deleteSelected() {
val ulds = getSelectedItems().map { it.uld } val ulds = getSelectedItems().map { it.uld }
@@ -133,19 +121,9 @@ class UldListViewModel : BasePageViewModel() {
showToast("请选择要删除的ULD") showToast("请选择要删除的ULD")
return return
} }
deleteQueue(ulds, 0) launchLoadingCollect({ NetApply.api.deleteUldBatch(ulds.toRequestBody()) }) {
} onSuccess = {
showToast("已删除 ${ulds.size} 条")
private fun deleteQueue(ulds: List<String>, index: Int) {
if (index >= ulds.size) {
showToast("已删除 ${ulds.size} 条")
refresh()
return
}
launchLoadingCollect({ NetApply.api.deleteUld(ulds[index]) }) {
onSuccess = { deleteQueue(ulds, index + 1) }
onFailed = { _, msg ->
showToast("删除 ${ulds[index]} 失败:${msg.noNull("未知错误")}")
refresh() refresh()
} }
} }

View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
<variable
name="viewModel"
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListViewModel" />
<!-- 平板不使用,仅为与手机变体保持 <variable> 一致(DataBinding 双变体强制约束) -->
<variable
name="activity"
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f2"
android:orientation="vertical">
<include layout="@layout/title_tool_bar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical">
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
hint='@{"请选择业务类型"}'
list="@{viewModel.businessTypeList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.businessType}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
hint='@{"请输入运单号"}'
icon="@{@drawable/img_scan}"
setInputWaybill="@{true}"
setOnIconClickListener="@{()-> viewModel.waybillScanClick()}"
setRefreshCallBack="@{viewModel::refresh}"
setSearchListRefresh="@{viewModel::getWayBillList}"
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.waybill}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
hint='@{"请输入承运人"}'
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.carrier}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
style="@style/iv_search_action"
android:onClick="@{()-> viewModel.refresh()}"
android:src="@drawable/img_search" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingTop="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:onClick="@{()->viewModel.checkAllClick()}"
android:text="全选" />
<ImageView
android:id="@+id/checkIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="5dp"
android:onClick="@{()->viewModel.checkAllClick()}"
android:src="@drawable/img_check_all" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
itemLayoutId="@{viewModel.itemLayoutId}"
viewHolder="@{viewModel.itemViewHolder}"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_gnj_move_stash_list" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/color_bottom_layout"
android:gravity="center_vertical"
android:paddingHorizontal="15dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text='@{"合计:"+viewModel.count+"条,总件数:"+viewModel.number+",总重量:"+viewModel.weight+"KG"}'
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
tools:text="合计:3条,总件数:212,总重量:342KG" />
<TextView
style="@style/tv_bottom_btn"
android:onClick="@{()-> viewModel.moveStashClick()}"
android:text="移库" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@@ -0,0 +1,296 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="bean"
type="com.lukouguoji.module_base.bean.MoveStashBean" />
<!-- 以下两个变量平板不使用,仅为与手机变体保持 <variable> 一致(DataBinding 双变体强制约束) -->
<variable
name="position"
type="Integer" />
<variable
name="activity"
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
</data>
<!-- 外层容器承载间距 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="5dp"
android:orientation="vertical">
<!-- 侧滑布局 -->
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
android:id="@+id/swipe_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 主列表项容器 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_item"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="@+id/iv_icon"
loadImage="@{bean.checked ? @drawable/img_plane_s : @drawable/img_plane}"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:src="@drawable/img_plane" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运单号:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.wbNo}"
android:textColor="@color/colorPrimary"
tools:text="789165431" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="件数:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{bean.pc}' />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重量:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.weight}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="代理人:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{bean.agentCode}' />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{5}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运单类型:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{bean.awbType}' />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="航班:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.flight}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="始发港:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.origin}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="起始站:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.dep}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="目的港:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.dest}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{5}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="品名:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.goods}" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</LinearLayout>
<!-- 右侧箭头 -->
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:src="@drawable/img_pda_right" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 侧滑菜单区域 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- 编辑按钮 -->
<TextView
android:id="@+id/btn_edit"
style="@style/tv_item_action"
android:background="@color/colorPrimary"
android:text="编辑" />
</androidx.appcompat.widget.LinearLayoutCompat>
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
</LinearLayout>
</layout>

View File

@@ -0,0 +1,332 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
国内进港移库「运单详情」—— 5.5 寸手持端专属页面(无平板变体)
对应设计稿 11_进港移库 的「运单详情」页:
· 卡片一:蓝色表头(运单号 + 绿点 + 操作时间)+ 类型标签行(红「运单类型」+ 货物类型)
+ 品名大字 + 两列字段网格(件数/重量/特码/代理/出运路径/承运人)
· 卡片二:绿色表头「移库信息」+ 移库人 / 移库时间(仅已移库显示)
· 卡片三:橙色表头「异常照片」+ 三个虚线加号槽位(点击跳拍照上传页)
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailViewModel" />
<variable
name="activity"
type="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/phone_page_bg"
android:orientation="vertical">
<include layout="@layout/title_tool_bar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- ==================== 卡片一:运单基本信息 ==================== -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card"
android:orientation="vertical">
<!-- 蓝色表头:运单号 + 绿点 + 操作时间 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card_header_blue"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{viewModel.dataBean.wbNo}"
android:textColor="@color/white"
android:textSize="17sp"
android:textStyle="bold" />
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginEnd="6dp"
android:background="@drawable/bg_phone_dot_green" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.opDate)}"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 类型标签行:运单类型(红标签) + 货物类型 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="22dp"
android:background="@drawable/bg_phone_tag_red"
android:gravity="center"
android:paddingHorizontal="8dp"
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.awbType)}"
android:textColor="@color/phone_tag_red_text"
android:textSize="12sp"
android:textStyle="bold" />
<View
android:layout_width="1dp"
android:layout_height="14dp"
android:layout_marginHorizontal="10dp"
android:background="@color/phone_divider" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.cargoType)}"
android:textColor="@color/phone_text_body"
android:textSize="14sp" />
</LinearLayout>
<!-- 品名(主字段大字) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.goods)}"
android:textColor="@color/phone_text_strong"
android:textSize="22sp"
android:textStyle="bold" />
<!-- 第 1 行:件数 / 重量 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
title='@{"件数"}'
value="@{viewModel.dataBean.orDash(viewModel.dataBean.pc)}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_weight="1"
title='@{"重量"}'
value='@{viewModel.dataBean.orDash(viewModel.dataBean.weight) + " kg"}' />
</LinearLayout>
<!-- 第 2 行:特码 / 代理 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
title='@{"特码"}'
value="@{viewModel.dataBean.spCode}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_weight="1"
title='@{"代理"}'
value="@{viewModel.dataBean.agentDisplay}" />
</LinearLayout>
<!-- 第 3 行:出运路径 / 承运人 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
title='@{"出运路径"}'
value="@{viewModel.dataBean.range}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_weight="1"
title='@{"承运人"}'
value="@{viewModel.dataBean.by1}" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- ==================== 卡片二:移库信息(仅已移库显示) ==================== -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/bg_phone_card"
android:orientation="vertical"
android:visibility="@{viewModel.dataBean.isMoved ? View.VISIBLE : View.GONE}">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card_header_green"
android:paddingHorizontal="16dp"
android:paddingVertical="10dp"
android:text="移库信息"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp">
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
title='@{"移库人"}'
value="@{viewModel.dataBean.opUserDisplay}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_weight="1"
title='@{"移库时间"}'
value="@{viewModel.dataBean.opDate}" />
</LinearLayout>
</LinearLayout>
<!-- ==================== 卡片三:异常照片 ==================== -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/bg_phone_card"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card_header_orange"
android:paddingHorizontal="16dp"
android:paddingVertical="10dp"
android:text="异常照片"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold" />
<!-- 三个虚线加号槽位(等宽正方形由 weight + 固定高实现),点击进拍照上传页 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="96dp"
android:layout_weight="1"
android:background="@drawable/bg_phone_photo_add"
android:gravity="center"
android:onClick="@{()-> activity.openPhoto()}"
android:text="+"
android:textColor="@color/phone_text_hint"
android:textSize="24sp" />
<TextView
android:layout_width="0dp"
android:layout_height="96dp"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:background="@drawable/bg_phone_photo_add"
android:gravity="center"
android:onClick="@{()-> activity.openPhoto()}"
android:text="+"
android:textColor="@color/phone_text_hint"
android:textSize="24sp" />
<TextView
android:layout_width="0dp"
android:layout_height="96dp"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:background="@drawable/bg_phone_photo_add"
android:gravity="center"
android:onClick="@{()-> activity.openPhoto()}"
android:text="+"
android:textColor="@color/phone_text_hint"
android:textSize="24sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</layout>

View File

@@ -1,148 +1,166 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <!--
xmlns:app="http://schemas.android.com/apk/res-auto" 国内进港移库 —— 5.5 寸手持端布局(平板变体见 layout-sw600dp/ 同名文件)
xmlns:tools="http://schemas.android.com/tools">
对应设计稿 documents/5.5寸手持端设计文件/11_进港移库/进港移库.html:
<data> · 顶部搜索框(运单号,含扫码)+ 筛选弹层(航班日期 / 航班号 / 特码 / 运单类型)
· 「待移库 / 已移库」Tab 绑定 viewModel.moveState("0"/"1"),
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" /> 分别走 DomImpMove/search 与 DomImpMove/searchMoved 两个端点
· 已移库 Tab 下隐藏底部操作条(与设计稿一致)
<variable · 卡片长按出「拍照」浮层(见 item_gnj_move_stash_list.xml)
name="viewModel" -->
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListViewModel" /> <layout xmlns:android="http://schemas.android.com/apk/res/android"
</data> xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent" <data>
android:layout_height="match_parent"
android:background="@color/color_f2" <import type="android.view.View" />
android:orientation="vertical">
<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType" />
<include layout="@layout/title_tool_bar" />
<variable
<LinearLayout name="viewModel"
android:layout_width="match_parent" type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListViewModel" />
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp" <variable
android:layout_marginTop="10dp" name="activity"
android:gravity="center_vertical"> type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
</data>
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
hint='@{"请选择业务类型"}' <FrameLayout
list="@{viewModel.businessTypeList}" android:layout_width="match_parent"
type="@{SearchLayoutType.SPINNER}" android:layout_height="match_parent"
value="@={viewModel.businessType}" android:background="@color/phone_page_bg">
android:layout_width="0dp"
android:layout_height="wrap_content" <LinearLayout
android:layout_weight="1" /> android:layout_width="match_parent"
android:layout_height="match_parent"
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout android:orientation="vertical">
hint='@{"请输入运单号"}'
icon="@{@drawable/img_scan}" <include layout="@layout/title_tool_bar" />
setInputWaybill="@{true}"
setOnIconClickListener="@{()-> viewModel.waybillScanClick()}" <!-- ==================== 搜索行 ==================== -->
setRefreshCallBack="@{viewModel::refresh}" <LinearLayout
setSearchListRefresh="@{viewModel::getWayBillList}" android:layout_width="match_parent"
type="@{SearchLayoutType.INPUT}" android:layout_height="wrap_content"
value="@={viewModel.waybill}" android:background="@color/white"
android:layout_width="0dp" android:orientation="vertical"
android:layout_height="wrap_content" android:paddingHorizontal="16dp"
android:layout_weight="1" /> android:paddingTop="16dp"
android:paddingBottom="12dp">
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
hint='@{"请输入承运人"}' <com.lukouguoji.module_base.ui.weight.phone.PhoneSearchBar
type="@{SearchLayoutType.INPUT}" android:layout_width="match_parent"
value="@={viewModel.carrier}" android:layout_height="wrap_content"
android:layout_width="0dp" hint='@{"搜索运单号"}'
android:layout_height="wrap_content" setOnFilterClickListener="@{(v)-> activity.toggleFilterPanel()}"
android:layout_weight="1" /> setOnScanClickListener="@{(v)-> viewModel.waybillScanClick()}"
setRefreshCallBack="@{viewModel::refresh}"
<LinearLayout upperCase="@{true}"
android:layout_width="0dp" value="@={viewModel.waybill}" />
android:layout_height="wrap_content"
android:layout_weight="1"> </LinearLayout>
<ImageView <!-- ==================== 待移库 / 已移库 Tab ==================== -->
style="@style/iv_search_action" <com.lukouguoji.module_base.ui.weight.phone.PhoneStatusTab
android:onClick="@{()-> viewModel.refresh()}" android:layout_width="match_parent"
android:src="@drawable/img_search" /> android:layout_height="wrap_content"
counts="@{viewModel.moveStateCounts}"
</LinearLayout> setOnTabChanged="@{viewModel::refresh}"
tabs="@{viewModel.moveStateTabs}"
<View value="@={viewModel.moveState}" />
android:layout_width="0dp"
android:layout_height="0dp" <View
android:layout_weight="1" /> android:layout_width="match_parent"
android:layout_height="1px"
<View android:background="@color/phone_divider" />
android:layout_width="0dp"
android:layout_height="0dp" <!-- ==================== 列表 ==================== -->
android:layout_weight="1" /> <com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/srl"
</LinearLayout> android:layout_width="match_parent"
<androidx.appcompat.widget.LinearLayoutCompat android:layout_height="0dp"
android:layout_width="match_parent" android:layout_weight="1">
android:layout_height="wrap_content"
android:gravity="center_vertical" <androidx.recyclerview.widget.RecyclerView
android:paddingTop="10dp"> android:id="@+id/rv"
android:layout_width="match_parent"
<TextView android:layout_height="match_parent"
android:layout_width="wrap_content" android:clipToPadding="false"
android:layout_height="wrap_content" android:overScrollMode="never"
android:layout_marginLeft="15dp" android:paddingBottom="12dp"
android:onClick="@{()->viewModel.checkAllClick()}" itemLayoutId="@{viewModel.itemLayoutId}"
android:text="全选" /> viewHolder="@{viewModel.itemViewHolder}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
<ImageView tools:itemCount="3"
android:id="@+id/checkIcon" tools:listitem="@layout/item_gnj_move_stash_list" />
android:layout_width="30dp"
android:layout_height="30dp" </com.scwang.smart.refresh.layout.SmartRefreshLayout>
android:layout_marginLeft="5dp"
android:onClick="@{()->viewModel.checkAllClick()}" <!-- ==================== 底部操作条(已移库 Tab 下隐藏) ==================== -->
android:src="@drawable/img_check_all" /> <com.lukouguoji.module_base.ui.weight.phone.PhoneBottomBar
android:layout_width="match_parent"
</androidx.appcompat.widget.LinearLayoutCompat> android:layout_height="wrap_content"
actionText='@{"移库"}'
<com.scwang.smart.refresh.layout.SmartRefreshLayout allChecked="@{viewModel.isAllChecked}"
android:id="@+id/srl" countText='@{"已选 " + viewModel.selectedCount + " 项"}'
android:layout_width="match_parent" setOnActionClick="@{(v)-> viewModel.moveStashClick()}"
android:layout_height="0dp" setOnAllCheckClick="@{(v)-> viewModel.toggleSelectAll()}"
android:layout_weight="1"> android:visibility="@{`1`.equals(viewModel.moveState) ? View.GONE : View.VISIBLE}" />
<androidx.recyclerview.widget.RecyclerView </LinearLayout>
android:id="@+id/rv"
itemLayoutId="@{viewModel.itemLayoutId}" <!-- ==================== 筛选弹层 ==================== -->
viewHolder="@{viewModel.itemViewHolder}" <com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" panelVisible="@{activity.filterPanelVisible}"
tools:listitem="@layout/item_gnj_move_stash_list" /> setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}"
</com.scwang.smart.refresh.layout.SmartRefreshLayout> setOnResetClick="@{(v)-> activity.resetFilter()}"
title='@{"筛选条件"}'>
<LinearLayout
android:layout_width="match_parent" <!-- 航班日期(入参 fdate) -->
android:layout_height="50dp" <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:background="@color/color_bottom_layout" android:layout_width="match_parent"
android:gravity="center_vertical" android:layout_height="wrap_content"
android:paddingHorizontal="15dp"> hint='@{"请选择航班日期"}'
title='@{"航班日期"}'
<TextView type="@{PhoneDataLayoutType.DATE}"
android:layout_width="0dp" value="@={viewModel.flightDate}" />
android:layout_height="wrap_content"
android:layout_weight="1" <!-- 航班号(入参 fno) -->
android:text='@{"合计:"+viewModel.count+"条,总件数:"+viewModel.number+",总重量:"+viewModel.weight+"KG"}' <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:textColor="@color/white" android:layout_width="match_parent"
android:textSize="18sp" android:layout_height="wrap_content"
android:textStyle="bold" hint='@{"请输入航班号"}'
tools:text="合计:3条,总件数:212,总重量:342KG" /> title='@{"航班号"}'
type="@{PhoneDataLayoutType.INPUT}"
<TextView value="@={viewModel.flightNo}" />
style="@style/tv_bottom_btn"
android:onClick="@{()-> viewModel.moveStashClick()}" <!-- 特码(入参 spCode) -->
android:text="移库" /> <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
</LinearLayout> android:layout_height="wrap_content"
hint='@{"请选择特码"}'
</LinearLayout> list="@{viewModel.spCodeList}"
</layout> title='@{"特码"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.spCode}" />
<!-- 运单类型(入参 awbType,复用平板既有 businessType 字段与字典) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择运单类型"}'
list="@{viewModel.businessTypeList}"
title='@{"运单类型"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.businessType}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout>
</layout>

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
国内进港移库「拍照上传」—— 5.5 寸手持端专属页面(无平板变体)
对应设计稿 11_进港移库/photo.html:
· 白卡「运单照片上传」+ 图片预览网格(复用公共 ImageSelectNewViewHolder,末位加号槽位)
· 底部两个按钮:拍照/上传(蓝) + 确认提交(绿)
提交走 DomImpMove/modify(pic/originalPic/picNumber 三字段规范)。
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/phone_page_bg"
android:orientation="vertical">
<include layout="@layout/title_tool_bar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card"
android:orientation="vertical"
android:padding="16dp">
<!-- 标题:运单照片上传(副行展示运单号) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运单照片上传"
android:textColor="@color/phone_text_strong"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@{viewModel.wbNo}"
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="78133363573" />
<!-- 图片预览网格(末位加号槽位,长按出删除角标) -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:overScrollMode="never"
itemLayoutId="@{viewModel.itemLayoutId}"
viewHolder="@{viewModel.itemViewHolder}"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="3"
tools:itemCount="3" />
<!-- 底部按钮:拍照/上传(蓝) + 确认提交(绿) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_weight="1"
android:background="@drawable/bg_phone_btn_primary"
android:gravity="center"
android:onClick="@{()-> viewModel.pickClick()}"
android:text="拍照/上传"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:background="@drawable/bg_phone_btn_green"
android:gravity="center"
android:onClick="@{()-> viewModel.submitClick()}"
android:text="确认提交"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</layout>

View File

@@ -14,8 +14,8 @@
· ULD修改(Modify):编号只读,其余可编辑 + 底部保存 · ULD修改(Modify):编号只读,其余可编辑 + 底部保存
· ULD详情(Details):全只读右对齐 + 顶栏右侧铅笔进入修改 + 额外展示最大装载容积/最大载重 · ULD详情(Details):全只读右对齐 + 顶栏右侧铅笔进入修改 + 额外展示最大装载容积/最大载重
「来源」「状态三态(占用)」为后端暂不支持的设计稿要求, 字段与平板端一致:来源 source(空运/陆运)、入库时间 inStoreTime、所在港 nowPort;
已登记 documents/后端接口对接问题清单.xlsx,UI 先行、后端补齐后自动生效。 状态维持两态(0 可用 / 1 维修,后端不新增「占用」)。
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
@@ -119,11 +119,11 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="22dp" android:layout_height="22dp"
android:background="@{viewModel.uldBean.status.empty ? null : (viewModel.uldBean.isStatusAvailable ? @drawable/bg_phone_tag_green : (viewModel.uldBean.isStatusOccupied ? @drawable/bg_phone_tag_orange : @drawable/bg_phone_tag_red_light))}" android:background="@{viewModel.uldBean.status.empty ? null : (viewModel.uldBean.isStatusAvailable ? @drawable/bg_phone_tag_green : @drawable/bg_phone_tag_red_light)}"
android:gravity="center" android:gravity="center"
android:paddingHorizontal="8dp" android:paddingHorizontal="8dp"
android:text='@{viewModel.uldBean.status.empty ? "请选择状态" : viewModel.uldBean.statusPhoneName}' android:text='@{viewModel.uldBean.status.empty ? "请选择状态" : viewModel.uldBean.statusPhoneName}'
android:textColor="@{viewModel.uldBean.status.empty ? @color/phone_text_hint : (viewModel.uldBean.isStatusAvailable ? @color/phone_tag_green_text : (viewModel.uldBean.isStatusOccupied ? @color/phone_tag_orange_text : @color/phone_danger_text))}" android:textColor="@{viewModel.uldBean.status.empty ? @color/phone_text_hint : (viewModel.uldBean.isStatusAvailable ? @color/phone_tag_green_text : @color/phone_danger_text)}"
android:textSize="12sp" /> android:textSize="12sp" />
<!-- 编辑态右箭头 --> <!-- 编辑态右箭头 -->
@@ -160,17 +160,6 @@
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
value="@={viewModel.uldBean.carrier}" /> value="@={viewModel.uldBean.carrier}" />
<!-- 来源(后端字段暂不支持,已登记清单;提交后端忽略) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请选择来源"}'
list="@{viewModel.sourceEditList}"
title='@{"来源"}'
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
value="@={viewModel.uldBean.source}" />
<!-- 自重(kg) --> <!-- 自重(kg) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -183,25 +172,31 @@
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@={viewModel.uldBean.uldWeight}" /> value="@={viewModel.uldBean.uldWeight}" />
<!-- 最大装载容积(仅详情展示,与设计稿一致) --> <!-- 最大装载容积:新增/修改可编辑(与平板一致;saveUld 缺该字段后端 500,内网实测) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请输入最大装载容积"}'
numeric="@{true}"
required="@{viewModel.pageType == DetailsPageType.Add}"
title='@{"最大装载容积(m³)"}' title='@{"最大装载容积(m³)"}'
type="@{PhoneFormRowType.TEXT}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@{viewModel.uldBean.maxVolume}" value="@={viewModel.uldBean.maxVolume}" />
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
<!-- 最大载重(仅详情展示,与设计稿一致) --> <!-- 最大载重:新增/修改可编辑(与平板一致;saveUld 缺该字段后端 500,内网实测) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请输入最大载重"}'
numeric="@{true}"
required="@{viewModel.pageType == DetailsPageType.Add}"
title='@{"最大载重(kg)"}' title='@{"最大载重(kg)"}'
type="@{PhoneFormRowType.TEXT}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@{viewModel.uldBean.maxWeight}" value="@={viewModel.uldBean.maxWeight}" />
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
<!-- 进港航班号(绑 ifNo,语义待后端确认,已登记清单) --> <!-- 进港航班号(ifNo) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -211,7 +206,7 @@
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@={viewModel.uldBean.ifNo}" /> value="@={viewModel.uldBean.ifNo}" />
<!-- 所在港(新增默认 HFE,由 ViewModel 手机开关预填) --> <!-- 所在港(nowPort,与平板端一致;新增默认 HFE,由 ViewModel 手机开关预填) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -219,9 +214,20 @@
hint='@{"请输入所在港"}' hint='@{"请输入所在港"}'
title='@{"所在港"}' title='@{"所在港"}'
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@={viewModel.uldBean.airport}" /> value="@={viewModel.uldBean.nowPort}" />
<!-- 入库时间(绑 checkInDate,筛选/格式语义待后端确认,已登记清单) --> <!-- 来源(source:空运/陆运,设计稿顺序位于所在港之后) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请选择来源"}'
list="@{viewModel.sourceEditList}"
title='@{"来源"}'
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
value="@={viewModel.uldBean.source}" />
<!-- 入库时间(inStoreTime,与平板端一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -230,7 +236,7 @@
required="@{viewModel.pageType == DetailsPageType.Add}" required="@{viewModel.pageType == DetailsPageType.Add}"
title='@{"入库时间"}' title='@{"入库时间"}'
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.DATE}" type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.DATE}"
value="@={viewModel.uldBean.checkInDate}" /> value="@={viewModel.uldBean.inStoreTime}" />
<!-- 备注 --> <!-- 备注 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow

View File

@@ -130,23 +130,15 @@
setOnResetClick="@{(v)-> activity.resetFilter()}" setOnResetClick="@{(v)-> activity.resetFilter()}"
title='@{"筛选条件"}'> title='@{"筛选条件"}'>
<!-- 入库时间(后端筛选入参暂不支持,已登记清单) --> <!-- 状态(后端入参 status,与平板端查询参数一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请输入入库时间"}' hint='@{"请选择状态"}'
title='@{"入库时间"}' list="@{viewModel.statusPhoneFilterList}"
type="@{PhoneDataLayoutType.DATE}" title='@{"状态"}'
value="@={viewModel.checkInDateFilter}" /> type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.status}" />
<!-- 进港航班号(后端筛选入参暂不支持,已登记清单) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入进港航班号"}'
title='@{"进港航班号"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.ifNoFilter}" />
<!-- 所属航司(后端入参 uldSuffix,真实生效) --> <!-- 所属航司(后端入参 uldSuffix,真实生效) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
@@ -158,16 +150,6 @@
type="@{PhoneDataLayoutType.SPINNER}" type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.uldSuffix}" /> value="@={viewModel.uldSuffix}" />
<!-- 来源(后端字段暂不支持,已登记清单) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择来源"}'
list="@{viewModel.sourceList}"
title='@{"来源"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.sourceFilter}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel> </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout> </FrameLayout>

View File

@@ -1,287 +1,227 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
国内进港移库 —— 列表项 5.5 寸手持端布局(平板变体见 layout-sw600dp/ 同名文件)
一套布局覆盖设计稿两种卡片形态,按 bean.isMoved 切换(载入时按端点打标):
· 待移库:圆形勾选框 + 蓝色运单号 + 橙色「待移库」标签
· 已移库:无勾选框 + 深色运单号 + 绿色对勾「已移库」
长按卡片出「拍照」浮层(跳拍照上传页),点浮层空白处收起。
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<import type="android.view.View" />
<variable <variable
name="bean" name="bean"
type="com.lukouguoji.module_base.bean.MoveStashBean" /> type="com.lukouguoji.module_base.bean.MoveStashBean" />
<variable
name="position"
type="Integer" />
<variable
name="activity"
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
</data> </data>
<!-- 外层容器承载间距 --> <FrameLayout
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp" android:layout_marginHorizontal="12dp"
android:layout_marginVertical="5dp" android:layout_marginTop="12dp">
android:orientation="vertical">
<!-- 侧滑布局 --> <LinearLayout
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
android:id="@+id/swipe_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card"
android:onLongClick="@{()-> activity.showCardActionPhone(bean)}"
android:orientation="vertical"
android:padding="16dp">
<!-- 主列表项容器 --> <!-- ==================== 卡片头部:勾选框 + 运单号 + 状态 ==================== -->
<androidx.appcompat.widget.LinearLayoutCompat <LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_item" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal">
android:padding="10dp">
<!-- 圆形勾选框(已移库的记录不可勾选,隐藏) -->
<ImageView <ImageView
android:id="@+id/iv_icon" android:id="@+id/iv_icon"
loadImage="@{bean.checked ? @drawable/img_plane_s : @drawable/img_plane}" android:layout_width="20dp"
android:layout_width="40dp" android:layout_height="20dp"
android:layout_height="40dp" android:layout_marginEnd="8dp"
android:layout_gravity="center" android:src="@drawable/ic_phone_check_round_unchecked"
android:src="@drawable/img_plane" /> android:visibility="@{bean.isMoved ? View.GONE : View.VISIBLE}"
loadImage="@{bean.checked.get() ? @drawable/ic_phone_check_round_checked : @drawable/ic_phone_check_round_unchecked}" />
<LinearLayout <!-- 运单号 -->
<TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:text="@{bean.wbNo}"
android:textColor="@{bean.isMoved ? @color/phone_text_title : @color/phone_primary}"
android:textSize="15sp"
android:textStyle="bold" />
<androidx.appcompat.widget.LinearLayoutCompat <!-- 待移库:橙色状态标签 -->
android:layout_width="match_parent" <TextView
android:layout_height="wrap_content"> android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:background="@drawable/bg_phone_tag_orange"
android:gravity="center"
android:paddingHorizontal="8dp"
android:text="待移库"
android:textColor="@color/phone_tag_orange_text"
android:textSize="11sp"
android:visibility="@{bean.isMoved ? View.GONE : View.VISIBLE}" />
<androidx.appcompat.widget.LinearLayoutCompat <!-- 已移库:对勾 + 状态文字 -->
android:layout_width="0dp" <LinearLayout
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_weight="1.1" android:layout_height="wrap_content"
android:gravity="center_vertical"> android:layout_marginStart="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="@{bean.isMoved ? View.VISIBLE : View.GONE}">
<TextView <ImageView
completeSpace="@{4}" android:layout_width="16dp"
android:layout_width="wrap_content" android:layout_height="16dp"
android:layout_height="wrap_content" android:src="@drawable/ic_phone_check_circle" />
android:text="运单号:" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.wbNo}"
android:textColor="@color/colorPrimary"
tools:text="789165431" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="件数:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{bean.pc}' />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重量:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.weight}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="代理人:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{bean.agentCode}' />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{5}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运单类型:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{bean.awbType}' />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"> android:layout_marginStart="4dp"
android:text="已移库"
<androidx.appcompat.widget.LinearLayoutCompat android:textColor="@color/phone_text_title"
android:layout_width="0dp" android:textSize="13sp" />
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="航班:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.flight}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="始发港:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.origin}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="起始站:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.dep}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
completeSpace="@{4}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="目的港:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.dest}" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:gravity="center_vertical">
<TextView
completeSpace="@{5}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="品名:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.goods}" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</LinearLayout> </LinearLayout>
<!-- 右侧箭头 --> </LinearLayout>
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:src="@drawable/img_pda_right" />
</androidx.appcompat.widget.LinearLayoutCompat> <!-- ==================== 航班信息行 ==================== -->
<LinearLayout
<!-- 侧滑菜单区域 --> android:layout_width="match_parent"
<androidx.appcompat.widget.LinearLayoutCompat android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_marginTop="12dp"
android:layout_height="match_parent" android:layout_marginBottom="12dp"
android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<!-- 编辑按钮 --> <ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_airplane" />
<TextView <TextView
android:id="@+id/btn_edit" android:layout_width="0dp"
style="@style/tv_item_action" android:layout_height="wrap_content"
android:background="@color/colorPrimary" android:layout_marginStart="4dp"
android:text="编辑" /> android:layout_weight="1"
android:text='@{"航班信息:" + bean.orDash(bean.flight)}'
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="航班信息:20260709/MU2311" />
</androidx.appcompat.widget.LinearLayoutCompat> </LinearLayout>
</com.mcxtzhang.swipemenulib.SwipeMenuLayout> <!-- ==================== 统计框:特码 / 件数 / 重量 ==================== -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneStatBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
label1='@{"特码"}'
label2='@{"件数"}'
label3='@{"重量"}'
value1="@{bean.orDash(bean.spCode)}"
value2='@{bean.orDash(bean.pc) + "件"}'
value3='@{bean.orDash(bean.weight) + "kg"}' />
</LinearLayout> <!-- ==================== 出运路径 ==================== -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_location" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_weight="1"
android:text='@{"出运路径:" + bean.orDash(bean.range)}'
android:textColor="@color/phone_text_body"
android:textSize="13sp" />
</LinearLayout>
<!-- ==================== 查看详情 ==================== -->
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginTop="16dp"
android:background="@color/phone_stat_bg" />
<LinearLayout
android:id="@+id/ll_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|end"
android:orientation="horizontal"
android:paddingTop="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查看详情"
android:textColor="@color/phone_primary"
android:textSize="14sp" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_phone_chevron_right" />
</LinearLayout>
</LinearLayout>
<!-- ==================== 长按操作浮层:拍照 ==================== -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#66000000"
android:clickable="true"
android:onClick="@{()-> activity.hideCardActionPhone(bean)}"
android:visibility="@{bean.actionMenuVisible ? View.VISIBLE : View.GONE}">
<TextView
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:background="@drawable/bg_phone_fab_purple"
android:gravity="center"
android:onClick="@{()-> activity.openPhoto(bean)}"
android:text="拍照"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold" />
</FrameLayout>
</FrameLayout>
</layout> </layout>

View File

@@ -11,8 +11,7 @@
· 平板独有 id(tv_modify / tv_delete / iv_icon)与手机独有 id(iv_check) · 平板独有 id(tv_modify / tv_delete / iv_icon)与手机独有 id(iv_check)
在 Binding 基类中自动为 @Nullable,ViewHolder 判空使用 在 Binding 基类中自动为 @Nullable,ViewHolder 判空使用
状态标签三色(设计稿):可用=浅绿 / 占用=浅橙 / 维修=浅红。 状态标签:可用=浅绿 / 维修=浅红(后端枚举维持两态 0/1,不含「占用」),见 ULDBean.statusPhoneName。
后端当前仅两态(0 可用 / 1 维修),「占用」为后端扩展枚举后的预留,见 ULDBean.statusPhoneName。
「来源」字段后端暂无(恒空显示 "-"),已登记 documents/后端接口对接问题清单.xlsx。 「来源」字段后端暂无(恒空显示 "-"),已登记 documents/后端接口对接问题清单.xlsx。
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -61,16 +60,16 @@
android:textSize="15sp" android:textSize="15sp"
android:textStyle="bold" /> android:textStyle="bold" />
<!-- 状态标签:可用(绿) / 占用(橙) / 维修(红,浅红底红字) --> <!-- 状态标签:可用(绿) / 维修(红,浅红底红字) -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="20dp" android:layout_height="20dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:background="@{bean.isStatusAvailable ? @drawable/bg_phone_tag_green : (bean.isStatusOccupied ? @drawable/bg_phone_tag_orange : @drawable/bg_phone_tag_red_light)}" android:background="@{bean.isStatusAvailable ? @drawable/bg_phone_tag_green : @drawable/bg_phone_tag_red_light}"
android:gravity="center" android:gravity="center"
android:paddingHorizontal="8dp" android:paddingHorizontal="8dp"
android:text="@{bean.statusPhoneName}" android:text="@{bean.statusPhoneName}"
android:textColor="@{bean.isStatusAvailable ? @color/phone_tag_green_text : (bean.isStatusOccupied ? @color/phone_tag_orange_text : @color/phone_danger_text)}" android:textColor="@{bean.isStatusAvailable ? @color/phone_tag_green_text : @color/phone_danger_text}"
android:textSize="11sp" /> android:textSize="11sp" />
<ImageView <ImageView
@@ -137,7 +136,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
android:layout_weight="1" android:layout_weight="1"
android:text='@{"入库时间:" + (bean.checkInDate.empty ? "-" : bean.checkInDate)}' android:text='@{"入库时间:" + (bean.inStoreTime.empty ? "-" : bean.inStoreTime)}'
android:textColor="@color/phone_text_body" android:textColor="@color/phone_text_body"
android:textSize="13sp" /> android:textSize="13sp" />

2
mise.toml Normal file
View File

@@ -0,0 +1,2 @@
[tools]
java = "17"

View File

@@ -22,9 +22,16 @@ data class GjcCheckInRecord(
var weight: Double = 0.0, // 运抵重量 var weight: Double = 0.0, // 运抵重量
var volume: Double = 0.0, // 运抵体积 var volume: Double = 0.0, // 运抵体积
var whId: Long = 0, // GJC_WAREHOUSE.ID var whId: Long = 0, // GJC_WAREHOUSE.ID
var carWeight: String = "" // 托盘自重 var carWeight: String = "", // 托盘车重(平板托盘车号反查值)
var trayNumber: Int = 0, // 托盘数量
var trayTotalWeight: Double = 0.0, // 托盘重量(托盘数量 × 托盘自重,与平板端一致)
var opUserName: String = "" // 计重人姓名(opId 关联查询)
) : BaseObservable() { ) : BaseObservable() {
/** 计重人:优先姓名,为空回退操作员编码 */
val opUserDisplay: String
get() = opUserName.ifEmpty { opId }
// 数据变化回调 // 数据变化回调
var onDataChanged: (() -> Unit)? = null var onDataChanged: (() -> Unit)? = null
@@ -54,14 +61,23 @@ data class GjcCheckInRecord(
} }
} }
// 托盘自重的字符串表示(用于双向绑定,5.5寸手机版「修改记录」覆层) /**
* 托盘重量输入原文(手机「修改记录」覆层)。保留原文而非由 Double 反推:
* 输入 "1" 时若 getter 返回 "1.0",输入框会被重设、光标回到开头,再输 "8" 就成了 "81"(实机踩过)。
* transient:不参与 Gson 提交序列化。
*/
@Transient
private var trayTotalWeightInput: String? = null
// 托盘重量的字符串表示(用于双向绑定,5.5寸手机版「修改记录」覆层)
@get:Bindable @get:Bindable
var carWeightStr: String var trayTotalWeightStr: String
get() = carWeight get() = trayTotalWeightInput ?: if (trayTotalWeight == 0.0) "" else trayTotalWeight.toString()
set(value) { set(value) {
if (carWeight != value) { if (trayTotalWeightStr != value) {
carWeight = value trayTotalWeightInput = value
notifyPropertyChanged(BR.carWeightStr) trayTotalWeight = value.toDoubleOrNull() ?: 0.0
notifyPropertyChanged(BR.trayTotalWeightStr)
onDataChanged?.invoke() onDataChanged?.invoke()
} }
} }

View File

@@ -76,8 +76,8 @@ data class GjcMaWb(
var carNumber: String? = null, // 车牌号 var carNumber: String? = null, // 车牌号
var passageWay: String? = null, // 通道号 var passageWay: String? = null, // 通道号
var passageWayId: String? = null, var passageWayId: String? = null,
var palletNumber: String? = null, // 托盘数量(5.5寸手机版开始计重页,后端暂无该入参,见接口问题清单) var trayNumber: Int? = null, // 托盘数量(splitCheckIn / completeCheckIn 入参,与平板端一致)
var carWeight: String? = null, // 托盘自重(5.5寸手机版手输,后端暂无该入参,见接口问题清单) var trayTotalWeight: Double? = null, // 托盘重量 = 托盘数量 × 托盘自重
// ==================== 状态信息 ==================== // ==================== 状态信息 ====================
var checkIn: String? = null, // 收运状态。0:待收运,1:已收运,2:收运中 var checkIn: String? = null, // 收运状态。0:待收运,1:已收运,2:收运中
@@ -131,13 +131,6 @@ data class GjcMaWb(
get() = checked.get() get() = checked.get()
set(value) = checked.set(value) set(value) = checked.set(value)
/**
* 5.5寸手机版:是否处于「待运抵」Tab(申报状态为空)。
* 接口无按申报状态区分的查询参数,Tab 由客户端对已加载数据拆分。
*/
val isPending: Boolean
get() = declareStatus.isNullOrEmpty()
/** /**
* 5.5寸手机版:申报状态三色标签的着色分类(01=绿/W=靛蓝/其余非空=琥珀)。 * 5.5寸手机版:申报状态三色标签的着色分类(01=绿/W=靛蓝/其余非空=琥珀)。
*/ */
@@ -162,6 +155,13 @@ data class GjcMaWb(
/** 5.5寸手机版:空值统一占位 "--"(空串与 null 都兜底,布局绑定用) */ /** 5.5寸手机版:空值统一占位 "--"(空串与 null 都兜底,布局绑定用) */
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "--" else value fun orDash(value: String?): String = if (value.isNullOrEmpty()) "--" else value
/**
* 5.5寸手机版:代理人展示值(名称优先,空则回退代码)。
* 不能在 XML 里用 `??` 兜底——转换器会把 JSON null 归一成空串,`??` 只判 null 不判空串。
*/
val agentDisplay: String?
get() = if (agentName.isNullOrEmpty()) agentCode else agentName
/** /**
* 5.5寸手机版-出港仓库:卡片状态标签(已清仓=绿 / 未清仓=橙,clearNormal 为空视作未清仓)。 * 5.5寸手机版-出港仓库:卡片状态标签(已清仓=绿 / 未清仓=橙,clearNormal 为空视作未清仓)。
* 已清仓时卡片「清仓」按钮置灰不可点。 * 已清仓时卡片「清仓」按钮置灰不可点。

View File

@@ -44,6 +44,9 @@ data class GjcMove(
var likeNo: String = "", // 部分运单号no(模糊查询) var likeNo: String = "", // 部分运单号no(模糊查询)
var range: String = "", var range: String = "",
var flight: String = "", // 航班信息(航班日期/航班号,如 20260924/MU2222)
var businessName: String = "", // 业务类型名称(中)
var opUserName: String = "", // 操作人(移库人)姓名
// UI扩展字段 - 使用ObservableBoolean实现自动UI更新 // UI扩展字段 - 使用ObservableBoolean实现自动UI更新
val checked: ObservableBoolean = ObservableBoolean(false) val checked: ObservableBoolean = ObservableBoolean(false)
@@ -67,6 +70,14 @@ data class GjcMove(
val goodsName: String val goodsName: String
get() = goodsCn.ifEmpty { goods } get() = goodsCn.ifEmpty { goods }
/** 业务类型:优先中文名称,为空回退编码 */
val businessDisplay: String
get() = businessName.ifEmpty { businessType }
/** 移库人:优先姓名,为空回退操作人ID */
val opUserDisplay: String
get() = opUserName.ifEmpty { opId }
/** /**
* 详情页字段统一空值占位。 * 详情页字段统一空值占位。
* 接口对 businessType / opdate / opId 等字段常返回 null, * 接口对 businessType / opdate / opId 等字段常返回 null,

View File

@@ -73,6 +73,7 @@ class GjcWeighingRecordBean : Serializable {
var paperTime: String = "" // 单据时间 var paperTime: String = "" // 单据时间
var userName: String = "" // 收运人 var userName: String = "" // 收运人
var passageWay: String = "" // 通道号(计重时选定的通道号名称)
var userId: String = "" // 收运人ID var userId: String = "" // 收运人ID
var remark: String = "" // 备注 var remark: String = "" // 备注

View File

@@ -10,4 +10,6 @@ class GjcWeighingStatisticsBean {
var totalPc: Long = 0 // 总件数 var totalPc: Long = 0 // 总件数
var totalWeight: Double = 0.0 // 总重量 var totalWeight: Double = 0.0 // 总重量
var cargoWeight: Double = 0.0 // 总货重 var cargoWeight: Double = 0.0 // 总货重
var waitCheckInNumber: Int = 0 // 待计重票数(IntExpCheckIn/pageQueryTotal)
var checkingNumber: Int = 0 // 计重中票数(IntExpCheckIn/pageQueryTotal)
} }

View File

@@ -46,6 +46,7 @@ class IntImpPickUpDLVBean : Serializable {
var dlvId: String = "" var dlvId: String = ""
var dlvIdType: String = "" var dlvIdType: String = ""
var dlvName: String = "" var dlvName: String = ""
var refUserName: String = "" // 出库人姓名(refId 关联查询)
var dlvPhone: String = "" var dlvPhone: String = ""
var locFlag: String = "" var locFlag: String = ""
var origin: String = "" var origin: String = ""

View File

@@ -54,4 +54,26 @@ data class IntImpQueryBean(
// ==================== 其他 ==================== // ==================== 其他 ====================
var remark: String? = null, // 备注 var remark: String? = null, // 备注
var activeId: Long? = null // 有效值 var activeId: Long? = null // 有效值
) ) {
/**
* 5.5寸手机版-进港查询:卡片状态标签。
* 已出库 = 出库时间(dlvTime)非空;已入库 = 入库时间(inDate)非空;都为空 = 未入库(灰)。
* 接口无独立状态出参,此判定为前端推断(与平板列表「入库时间/出库时间」两列同源)。
*/
val isQueryOutbound: Boolean
get() = !dlvTime.isNullOrEmpty()
val isQueryStored: Boolean
get() = !inDate.isNullOrEmpty()
/** 5.5寸手机版:空值统一占位 "--"(空串与 null 都兜底,布局绑定用) */
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "--" else value
/**
* 5.5寸手机版:代理人展示值(名称优先,空则回退代码)。
* 不能在 XML 里用 `??` 兜底——转换器会把 JSON null 归一成空串,`??` 只判 null 不判空串。
*/
val agentDisplay: String?
get() = if (agentName.isNullOrEmpty()) agentCode else agentName
}

View File

@@ -8,5 +8,15 @@ data class ManifestTotalDto(
var wbNumber: Int = 0, // 运单票数/总票数 var wbNumber: Int = 0, // 运单票数/总票数
var totalPc: Long = 0, // 总件数 var totalPc: Long = 0, // 总件数
var totalWeight: Double = 0.0, // 总重量 var totalWeight: Double = 0.0, // 总重量
var cargoWeight: Double = 0.0 // 总货重 var cargoWeight: Double = 0.0, // 总货重
// ===== 后端统一统计 DTO 的分项计数(2026-09 后端补齐,按接口各取所需) =====
var flightNumber: Int = 0, // 航班数
var uldNumber: Int = 0, // 板箱数(出库交接 Tab 角标)
var storedNumber: Int = 0, // 已入库票数(出港/进港查询)
var departedNumber: Int = 0, // 已离港(出港)/ 已出库(进港)票数
var notClearNumber: Int = 0, // 未清仓票数(出港/进港仓库)
var clearNumber: Int = 0, // 已清仓票数(出港/进港仓库)
var waitCheckInNumber: Int = 0, // 待计重票数(出港计重)
var checkingNumber: Int = 0 // 计重中票数(出港计重)
) )

View File

@@ -6,6 +6,7 @@ import com.lukouguoji.module_base.interfaces.ICheck
class MoveStashBean : ICheck { class MoveStashBean : ICheck {
var activeId: String = "" // null var activeId: String = "" // null
var agentCode: String = "" // HFEXB var agentCode: String = "" // HFEXB
var agentName: String = "" // 代理人名称(search 出参有定义)
var awbType: String = "" // 国际进港(国内中转) var awbType: String = "" // 国际进港(国内中转)
var businessType: String = "" // null var businessType: String = "" // null
var by1: String = "" // null var by1: String = "" // null
@@ -26,7 +27,9 @@ class MoveStashBean : ICheck {
var flight: String = "" // 20240711/MU7766 var flight: String = "" // 20240711/MU7766
var fno: String = "" // MU7766 var fno: String = "" // MU7766
var goods: String = "" // 国际货 var goods: String = "" // 国际货
var mawbId: String = "" // 996422 var mawbId: String = "" // 996422(searchMoved 出参不含此字段)
var moveId: String = "" // CIII260930141150(已移库,movedDetail 入参)
var opUserName: String = "" // 移库人姓名(searchMoved / movedDetail 出参)
var no: String = "" // 44445074 var no: String = "" // 44445074
var opDate: String = "" // null var opDate: String = "" // null
var opId: String = "" // null var opId: String = "" // null
@@ -49,4 +52,23 @@ class MoveStashBean : ICheck {
override fun getCheckObservable(): ObservableBoolean { override fun getCheckObservable(): ObservableBoolean {
return checked return checked
} }
// ========== 5.5 寸手持端展示辅助字段(仅前端使用,不参与提交) ==========
/** 是否已移库:由列表加载端点决定(search=待移库 / searchMoved=已移库),载入时打标 */
var isMoved: Boolean = false
/** 卡片长按「拍照」操作浮层显隐(仅 UI 状态) */
val actionMenuVisible = ObservableBoolean(false)
/** 代理显示:网络层会把 null 归一成空串,须 isNullOrEmpty 双兜底(沿用进港/出港查询口径) */
val agentDisplay: String
get() = if (agentName.isNotEmpty()) agentName else agentCode
/** 移库人:优先姓名,为空回退操作人 ID */
val opUserDisplay: String
get() = opUserName.ifEmpty { opId }
/** 详情页/卡片字段统一空值占位 */
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "-" else value
} }

View File

@@ -19,18 +19,21 @@ data class ULDBean(
var status: String = "", var status: String = "",
// 备注 // 备注
var remark: String = "", var remark: String = "",
// ========== 以下字段接口出参已有(api-doc /eqm/uld/pageQuery),此前 Bean 未解析 ========== // ========== 以下字段接口出参已有(api-doc /eqm/uld/pageQuery) ==========
// 进港航班号(语义待后端确认,见 documents/后端接口对接问题清单.xlsx) // 进港航班号
var ifNo: String = "", var ifNo: String = "",
// 出港航班号 // 出港航班号
var efNo: String = "", var efNo: String = "",
// 入库时间
var checkInDate: String = "", var checkInDate: String = "",
// 出库时间
var checkOutDate: String = "", var checkOutDate: String = "",
// ========== 后端暂无对应字段,前端按设计稿预留(提交后端忽略,无副作用) ========== // 来源(空运、陆运)
// 来源:空运 / 陆运(后端字段需求已登记清单)
var source: String = "", var source: String = "",
// 入库时间 yyyy-MM-dd HH:mm:ss(后端确认「入库时间」取 inStoreTime,与平板端一致)
var inStoreTime: String = "",
// 所在港
var nowPort: String = "",
// 位置信息
var nowPosition: String = "",
){ ){
val checked = ObservableBoolean(false) val checked = ObservableBoolean(false)
@@ -40,22 +43,18 @@ data class ULDBean(
else -> status else -> status
} }
// ========== 手机版(5.5 寸手持端)三态状态展示 ========== // ========== 手机版(5.5 寸手持端)状态展示 ==========
// 设计稿状态为 可用(绿)/占用(橙)/维修(红) 三态;后端当前仅两态(0/1), // 后端确认状态枚举维持两态(0 正常 / 1 故障),不新增「占用」;
// 按语义映射 0→可用、1→维修,"2"→占用为后端扩展枚举后的预留(需求已登记清单)。 // 手机版按设计稿文案映射 0→可用(绿)、1→维修(红)。
/** 手机版状态文案 */ /** 手机版状态文案 */
val statusPhoneName: String val statusPhoneName: String
get() = when (status) { get() = when (status) {
"0" -> "可用" "0" -> "可用"
"1" -> "维修" "1" -> "维修"
"2" -> "占用"
else -> statusName() else -> statusName()
} }
/** 可用(绿色标签) */ /** 可用(绿色标签) */
val isStatusAvailable: Boolean get() = status == "0" val isStatusAvailable: Boolean get() = status == "0"
/** 占用(橙色标签,后端扩展枚举后的预留) */
val isStatusOccupied: Boolean get() = status == "2"
} }

View File

@@ -272,6 +272,8 @@ interface Constant {
const val IntImpTally = "AppIntImpTally" //理货报告 const val IntImpTally = "AppIntImpTally" //理货报告
const val IntImpPickUpRecord = "AppIntImpPickUpRecord" //提取记录 const val IntImpPickUpRecord = "AppIntImpPickUpRecord" //提取记录
const val IntImpPickUpDLV = "AppIntImpPickUpDLV" //提取出库 const val IntImpPickUpDLV = "AppIntImpPickUpDLV" //提取出库
// 进港移库:后端暂无该模块与权限串(2026-07-31 api-doc 核对),按 AppIntImp* 命名约定预定义,
// 权限串下发前手机首页菜单不会显示该入口(已登记后端接口对接问题清单)
const val IntImpAccidentVisa = "AppIntImpAccidentVisa" //事故签证 const val IntImpAccidentVisa = "AppIntImpAccidentVisa" //事故签证
const val GjjManifestListActivity = "AppIntExpManifest" //舱单 const val GjjManifestListActivity = "AppIntExpManifest" //舱单
const val GjjTallyListActivity = "AppIntExpTally" //理货 const val GjjTallyListActivity = "AppIntExpTally" //理货

View File

@@ -1766,6 +1766,12 @@ interface Api {
@POST("DomImpMove/search") @POST("DomImpMove/search")
suspend fun getGnjMoveStashList(@Body data: RequestBody): BaseListBean<MoveStashBean> suspend fun getGnjMoveStashList(@Body data: RequestBody): BaseListBean<MoveStashBean>
/**
* 获取-国内进港移库-已移库列表(移库完成分页搜索,5.5 寸手机端「已移库」Tab)
*/
@POST("DomImpMove/searchMoved")
suspend fun getGnjMoveStashMovedList(@Body data: RequestBody): BaseListBean<MoveStashBean>
/** /**
* 获取-国内进港移库-列表--运单列表 * 获取-国内进港移库-列表--运单列表
*/ */
@@ -1790,6 +1796,13 @@ interface Api {
@GET("DomImpMove/detail") @GET("DomImpMove/detail")
suspend fun getGnjMoveStashDetail(@Query("mawbId") mawbId: String): BaseResultBean<GnjYiKuBean> suspend fun getGnjMoveStashDetail(@Query("mawbId") mawbId: String): BaseResultBean<GnjYiKuBean>
/**
* 国内进港移库-移库完成详情(已移库运单无 mawbId,按 moveId 查,含 pic 三字段与移库人)
* 接口路径: /DomImpMove/movedDetail
*/
@GET("DomImpMove/movedDetail")
suspend fun getGnjMoveStashMovedDetail(@Query("moveId") moveId: String): BaseResultBean<GnjYiKuBean>
/** /**
* 编辑-国内进港移库(新接口) * 编辑-国内进港移库(新接口)
*/ */
@@ -2085,6 +2098,13 @@ interface Api {
@POST("eqm/uld/deleteUld") @POST("eqm/uld/deleteUld")
suspend fun deleteUld(@Query("uld") uld: String): BaseResultBean<Any> suspend fun deleteUld(@Query("uld") uld: String): BaseResultBean<Any>
/**
* 批量删除-ULD-信息(请求体为 ULD 编号字符串数组)
* 接口路径: /eqm/uld/deleteUldBatch
*/
@POST("eqm/uld/deleteUldBatch")
suspend fun deleteUldBatch(@Body data: RequestBody): BaseResultBean<Any>
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// 冷库登记 // 冷库登记
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -104,6 +104,7 @@ object ARouterConstants {
// 国内进港移库(新版本) // 国内进港移库(新版本)
const val ACTIVITY_URL_GNJ_YIKU_LIST = "/gnj/GnjYiKuListActivity" //国内进港 移库列表 const val ACTIVITY_URL_GNJ_YIKU_LIST = "/gnj/GnjYiKuListActivity" //国内进港 移库列表
const val ACTIVITY_URL_GNJ_MOVE_STASH_LIST = "/app/GnjMoveStashListActivity" //国内进港 移库(首页菜单实际跳转页,双形态)
const val ACTIVITY_URL_GNJ_YIKU_DETAILS = "/gnj/GnjYiKuDetailsActivity" //国内进港 移库详情 const val ACTIVITY_URL_GNJ_YIKU_DETAILS = "/gnj/GnjYiKuDetailsActivity" //国内进港 移库详情
const val ACTIVITY_URL_GNJ_YIKU_EDIT = "/gnj/GnjYiKuEditActivity" //国内进港 移库编辑 const val ACTIVITY_URL_GNJ_YIKU_EDIT = "/gnj/GnjYiKuEditActivity" //国内进港 移库编辑
const val ACTIVITY_URL_GNJ_YIKU_HANDOVER = "/gnj/GnjYiKuHandoverActivity" //国内进港 移交编辑/详情 const val ACTIVITY_URL_GNJ_YIKU_HANDOVER = "/gnj/GnjYiKuHandoverActivity" //国内进港 移交编辑/详情

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 手机版绿色操作按钮:完成态绿实底 + 10dp 圆角(进港移库-拍照上传「确认提交」) -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#0E9F6E" />
<corners android:radius="10dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="@color/phone_green" />
<corners android:radius="10dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 手机版详情卡片表头:橙色底 + 顶部 12dp 圆角(进港移库-异常照片卡片) -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/phone_amber" />
<corners
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
</shape>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 手机版照片加号槽位:灰色虚线边框 + 8dp 圆角(进港移库-异常照片) -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<corners android:radius="8dp" />
<stroke
android:width="1dp"
android:color="@color/phone_divider"
android:dashGap="4dp"
android:dashWidth="4dp" />
</shape>

View File

@@ -79,5 +79,6 @@
<color name="phone_tag_indigo_text">#6366F1</color> <color name="phone_tag_indigo_text">#6366F1</color>
<color name="phone_tag_amber_bg">#FEF3C7</color> <!-- 状态标签-琥珀底(出港运抵申报状态 E) --> <color name="phone_tag_amber_bg">#FEF3C7</color> <!-- 状态标签-琥珀底(出港运抵申报状态 E) -->
<color name="phone_tag_amber_text">#D97706</color> <color name="phone_tag_amber_text">#D97706</color>
<color name="phone_amber">#F59E0B</color> <!-- 橙色信息卡表头(进港移库-异常照片) -->
</resources> </resources>

View File

@@ -32,7 +32,7 @@ class GjcQueryActivity :
override fun viewModelClass() = GjcQueryViewModel::class.java override fun viewModelClass() = GjcQueryViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港查询") setBackArrow(if (DeviceUtil.isPhone()) "出港查询" else "国际出港查询")
binding.viewModel = viewModel binding.viewModel = viewModel

View File

@@ -1,90 +1,91 @@
package com.lukouguoji.gjc.activity package com.lukouguoji.gjc.activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Gravity import android.view.Gravity
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter import com.alibaba.android.arouter.launcher.ARouter
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcQueryDetailsBinding import com.lukouguoji.gjc.databinding.ActivityGjcQueryDetailsBinding
import com.lukouguoji.gjc.viewModel.GjcQueryDetailsViewModel import com.lukouguoji.gjc.viewModel.GjcQueryDetailsViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.base.CustomVP2Adapter import com.lukouguoji.module_base.base.CustomVP2Adapter
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil
/**
* 国际出港查询详情页面 /**
*/ * 国际出港查询详情页面
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_DETAILS) */
class GjcQueryDetailsActivity : @Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_DETAILS)
BaseBindingActivity<ActivityGjcQueryDetailsBinding, GjcQueryDetailsViewModel>() { class GjcQueryDetailsActivity :
BaseBindingActivity<ActivityGjcQueryDetailsBinding, GjcQueryDetailsViewModel>() {
override fun layoutId() = R.layout.activity_gjc_query_details
override fun viewModelClass() = GjcQueryDetailsViewModel::class.java override fun layoutId() = R.layout.activity_gjc_query_details
override fun viewModelClass() = GjcQueryDetailsViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港查询详情") override fun initOnCreate(savedInstanceState: Bundle?) {
binding.viewModel = viewModel setBackArrow(if (DeviceUtil.isPhone()) "出港货物" else "国际出港查询详情")
binding.viewModel = viewModel
// 初始化ViewModel(传入maWbId)
viewModel.initOnCreated(intent) // 初始化ViewModel(传入maWbId)
viewModel.initOnCreated(intent)
// 配置ViewPager2
binding.vp.adapter = CustomVP2Adapter( // 配置ViewPager2
viewModel.fragmentList, binding.vp.adapter = CustomVP2Adapter(
supportFragmentManager, viewModel.fragmentList,
lifecycle supportFragmentManager,
) lifecycle
binding.vp.isUserInputEnabled = false // 禁用滑动 )
binding.vp.offscreenPageLimit = 3 // 预加载3个Fragment binding.vp.isUserInputEnabled = false // 禁用滑动
binding.vp.offscreenPageLimit = 3 // 预加载3个Fragment
// 监听Tab索引变化,切换Fragment
viewModel.currentTab.observe(this) { // 监听Tab索引变化,切换Fragment
binding.vp.setCurrentItem(it, false) // false:无动画 viewModel.currentTab.observe(this) {
} binding.vp.setCurrentItem(it, false) // false:无动画
}
// 标题栏右侧添加日志查询图标
val toolbar = findViewById<Toolbar>(com.lukouguoji.module_base.R.id.toolbar) // 标题栏右侧添加日志查询图标
val logIcon = ImageView(this).apply { val toolbar = findViewById<Toolbar>(com.lukouguoji.module_base.R.id.toolbar)
setImageResource(com.lukouguoji.module_base.R.drawable.img_log) val logIcon = ImageView(this).apply {
layoutParams = Toolbar.LayoutParams( setImageResource(com.lukouguoji.module_base.R.drawable.img_log)
LinearLayout.LayoutParams.WRAP_CONTENT, layoutParams = Toolbar.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT LinearLayout.LayoutParams.WRAP_CONTENT,
).apply { LinearLayout.LayoutParams.WRAP_CONTENT
gravity = Gravity.END or Gravity.CENTER_VERTICAL ).apply {
marginEnd = 15 gravity = Gravity.END or Gravity.CENTER_VERTICAL
width = 30.dp marginEnd = 15
height = 30.dp width = 30.dp
} height = 30.dp
setOnClickListener { }
val wbNo = viewModel.maWbData.value?.get("wbNo") as? String ?: "" setOnClickListener {
ARouter.getInstance() val wbNo = viewModel.maWbData.value?.get("wbNo") as? String ?: ""
.build(ARouterConstants.ACTIVITY_URL_LOG_DETAIL) ARouter.getInstance()
.withString(Constant.Key.KEY, wbNo) .build(ARouterConstants.ACTIVITY_URL_LOG_DETAIL)
.withString(Constant.Key.AWB_TYPE, "IO") .withString(Constant.Key.KEY, wbNo)
.navigation() .withString(Constant.Key.AWB_TYPE, "IO")
} .navigation()
} }
toolbar.addView(logIcon) }
toolbar.addView(logIcon)
// 加载详情数据
viewModel.loadDetails() // 加载详情数据
} viewModel.loadDetails()
}
private val Int.dp: Int
get() = (this * resources!!.displayMetrics.density).toInt() private val Int.dp: Int
get() = (this * resources!!.displayMetrics.density).toInt()
companion object {
@JvmStatic companion object {
fun start(context: Context, maWbId: Long?) { @JvmStatic
val starter = Intent(context, GjcQueryDetailsActivity::class.java) fun start(context: Context, maWbId: Long?) {
.putExtra(Constant.Key.ID, maWbId?.toString() ?: "") val starter = Intent(context, GjcQueryDetailsActivity::class.java)
context.startActivity(starter) .putExtra(Constant.Key.ID, maWbId?.toString() ?: "")
} context.startActivity(starter)
} }
} }
}

View File

@@ -1,105 +1,104 @@
package com.lukouguoji.gjc.activity package com.lukouguoji.gjc.activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcWeighingListBinding import com.lukouguoji.gjc.databinding.ActivityGjcWeighingListBinding
import com.lukouguoji.gjc.viewModel.GjcWeighingViewModel import com.lukouguoji.gjc.viewModel.GjcWeighingViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.GjcWeighingBean import com.lukouguoji.module_base.bean.GjcWeighingBean
import com.lukouguoji.module_base.common.ConstantEvent import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.getLifecycleOwner import com.lukouguoji.module_base.ktx.getLifecycleOwner
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil import com.lukouguoji.module_base.util.DeviceUtil
/** /**
* 国际出港计重列表页 * 国际出港计重列表页
* *
* 布局 id 不区分设备,平板/手机由资源限定符自动选择: * 布局 id 不区分设备,平板/手机由资源限定符自动选择:
* layout-sw600dp/activity_gjc_weighing_list.xml(平板) * layout-sw600dp/activity_gjc_weighing_list.xml(平板)
* layout/activity_gjc_weighing_list.xml(手机) * layout/activity_gjc_weighing_list.xml(手机)
*/ */
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_LIST) @Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_LIST)
class GjcWeighingListActivity : class GjcWeighingListActivity :
BaseBindingActivity<ActivityGjcWeighingListBinding, GjcWeighingViewModel>() { BaseBindingActivity<ActivityGjcWeighingListBinding, GjcWeighingViewModel>() {
override fun layoutId() = R.layout.activity_gjc_weighing_list override fun layoutId() = R.layout.activity_gjc_weighing_list
override fun viewModelClass() = GjcWeighingViewModel::class.java override fun viewModelClass() = GjcWeighingViewModel::class.java
/** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */ /** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */
val filterPanelVisible = ObservableBoolean(false) val filterPanelVisible = ObservableBoolean(false)
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港待计重") setBackArrow(if (DeviceUtil.isPhone()) "出港待计重" else "国际出港待计重")
binding.viewModel = viewModel binding.viewModel = viewModel
binding.activity = this binding.activity = this
// 手机版专属数据:放开默认日期、加载筛选弹层字典、启用三格统计 // 手机版专属数据:放开默认日期、加载筛选弹层字典、启用三格统计
// 平板端不调用,查询链路与请求次数与改造前完全一致 // 平板端不调用,查询链路与请求次数与改造前完全一致
if (DeviceUtil.isPhone()) { if (DeviceUtil.isPhone()) {
viewModel.initPhoneExtras() viewModel.initPhoneExtras()
} }
// 初始化代理人列表(从API获取) // 初始化代理人列表(从API获取)
viewModel.initAgentList() viewModel.initAgentList()
// 初始化特码列表(从API获取) // 初始化特码列表(从API获取)
viewModel.initSpecialCodeList() viewModel.initSpecialCodeList()
// 绑定分页逻辑 // 绑定分页逻辑
viewModel.pageModel viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner()) .bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
// 监听刷新事件 // 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { viewModel.onItemCheckChanged() } FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { viewModel.onItemCheckChanged() }
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH) FlowBus.with<String>(ConstantEvent.EVENT_REFRESH)
.observe(this) { .observe(this) {
viewModel.refresh() viewModel.refresh()
} }
// 初始加载 // 初始加载
viewModel.refresh() viewModel.refresh()
} }
// ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段) ==================== // ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段) ====================
/** 手机版筛选弹层显隐切换 */ /** 手机版筛选弹层显隐切换 */
fun toggleFilterPanel() { fun toggleFilterPanel() {
filterPanelVisible.set(!filterPanelVisible.get()) filterPanelVisible.set(!filterPanelVisible.get())
} }
/** 手机版筛选:重置(只清弹层内条件,不影响顶部搜索框) */ /** 手机版筛选:重置(只清弹层内条件,不影响顶部搜索框) */
fun resetFilter() { fun resetFilter() {
viewModel.channelFilter.value = "" viewModel.flightDate.value = ""
viewModel.carrierFilter.value = "" viewModel.agentCode.value = ""
viewModel.awbTypeFilter.value = "" viewModel.spCode.value = ""
viewModel.agentCode.value = "" }
}
/** 手机版筛选:确认 */
/** 手机版筛选:确认 */ fun confirmFilter() {
fun confirmFilter() { filterPanelVisible.set(false)
filterPanelVisible.set(false) viewModel.searchClick()
viewModel.searchClick() }
}
/** 手机版卡片内「提前运抵」(单票,由 ViewHolder 调用) */
/** 手机版卡片内「提前运抵」(单票,由 ViewHolder 调用) */ fun preArriveClick(bean: GjcWeighingBean) {
fun preArriveClick(bean: GjcWeighingBean) { viewModel.arrivedAheadSingle(bean)
viewModel.arrivedAheadSingle(bean) }
}
companion object {
companion object { @JvmStatic
@JvmStatic fun start(context: Context) {
fun start(context: Context) { val starter = Intent(context, GjcWeighingListActivity::class.java)
val starter = Intent(context, GjcWeighingListActivity::class.java) context.startActivity(starter)
context.startActivity(starter) }
} }
} }
}

View File

@@ -1,103 +1,104 @@
package com.lukouguoji.gjc.activity package com.lukouguoji.gjc.activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcWeighingRecordDetailsBinding import com.lukouguoji.gjc.databinding.ActivityGjcWeighingRecordDetailsBinding
import com.lukouguoji.gjc.holder.GjcCheckInRecordViewHolder import com.lukouguoji.gjc.holder.GjcCheckInRecordViewHolder
import com.lukouguoji.gjc.viewModel.GjcWeighingRecordDetailsViewModel import com.lukouguoji.gjc.viewModel.GjcWeighingRecordDetailsViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.base.CommonAdapter import com.lukouguoji.module_base.base.CommonAdapter
import com.lukouguoji.module_base.bean.GjcCheckInRecord import com.lukouguoji.module_base.bean.GjcCheckInRecord
import com.lukouguoji.module_base.bean.GjcWeighingRecordBean import com.lukouguoji.module_base.bean.GjcWeighingRecordBean
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil
/**
* 国际出港计重明细页 /**
* * 国际出港计重明细页
* 布局 id 不区分设备,平板/手机由资源限定符自动选择: *
* layout-sw600dp/activity_gjc_weighing_record_details.xml(平板,列表内联编辑) * 布局 id 不区分设备,平板/手机由资源限定符自动选择:
* layout/activity_gjc_weighing_record_details.xml(手机,单条记录经全屏「修改记录」覆层编辑) * layout-sw600dp/activity_gjc_weighing_record_details.xml(平板,列表内联编辑)
*/ * layout/activity_gjc_weighing_record_details.xml(手机,单条记录经全屏「修改记录」覆层编辑)
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_DETAILS) */
class GjcWeighingRecordDetailsActivity : @Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_DETAILS)
BaseBindingActivity<ActivityGjcWeighingRecordDetailsBinding, GjcWeighingRecordDetailsViewModel>() { class GjcWeighingRecordDetailsActivity :
BaseBindingActivity<ActivityGjcWeighingRecordDetailsBinding, GjcWeighingRecordDetailsViewModel>() {
private lateinit var adapter: CommonAdapter
private lateinit var adapter: CommonAdapter
// 当前编辑模式(供ViewHolder获取)
var currentEditMode = false // 当前编辑模式(供ViewHolder获取)
var currentEditMode = false
override fun layoutId() = R.layout.activity_gjc_weighing_record_details
override fun layoutId() = R.layout.activity_gjc_weighing_record_details
override fun viewModelClass() = GjcWeighingRecordDetailsViewModel::class.java
override fun viewModelClass() = GjcWeighingRecordDetailsViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港计重明细") override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow(if (DeviceUtil.isPhone()) "计重明细" else "国际出港计重明细")
binding.viewModel = viewModel
binding.viewModel = viewModel
// 初始化RecyclerView
val layoutManager = LinearLayoutManager(this) // 初始化RecyclerView
binding.recyclerView.layoutManager = layoutManager val layoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = layoutManager
// 添加分割线
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) // 添加分割线
binding.recyclerView.addItemDecoration(divider) val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
binding.recyclerView.addItemDecoration(divider)
adapter = CommonAdapter(
this, adapter = CommonAdapter(
R.layout.item_gjc_check_in_record, this,
GjcCheckInRecordViewHolder::class.java R.layout.item_gjc_check_in_record,
) GjcCheckInRecordViewHolder::class.java
binding.recyclerView.adapter = adapter )
binding.recyclerView.adapter = adapter
// 监听数据变化更新RecyclerView
viewModel.recordList.observe(this) { records -> // 监听数据变化更新RecyclerView
adapter.refresh(records) viewModel.recordList.observe(this) { records ->
} adapter.refresh(records)
}
// 监听编辑模式变化,更新当前编辑模式并刷新列表
viewModel.isEditMode.observe(this) { isEditMode -> // 监听编辑模式变化,更新当前编辑模式并刷新列表
// 更新当前编辑模式状态 viewModel.isEditMode.observe(this) { isEditMode ->
currentEditMode = isEditMode // 更新当前编辑模式状态
currentEditMode = isEditMode
// 刷新所有可见的 ViewHolder
adapter.notifyDataSetChanged() // 刷新所有可见的 ViewHolder
} adapter.notifyDataSetChanged()
}
// 初始化数据
viewModel.initOnCreated(intent) // 初始化数据
} viewModel.initOnCreated(intent)
}
// ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 编辑链路) ====================
// ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 编辑链路) ====================
/**
* 手机版:打开「修改记录」覆层编辑指定记录。 /**
* 复用 [GjcWeighingRecordDetailsViewModel.onModifyClick] 的备份/编辑态逻辑, * 手机版:打开「修改记录」覆层编辑指定记录。
* 覆层显隐由 isEditMode 驱动(平板端该状态驱动列表内联编辑,互不影响)。 * 复用 [GjcWeighingRecordDetailsViewModel.onModifyClick] 的备份/编辑态逻辑,
*/ * 覆层显隐由 isEditMode 驱动(平板端该状态驱动列表内联编辑,互不影响)。
fun openPhoneEdit(record: GjcCheckInRecord) { */
viewModel.onModifyClick() fun openPhoneEdit(record: GjcCheckInRecord) {
binding.editRecord = record viewModel.onModifyClick()
} binding.editRecord = record
}
/** 手机版:取消编辑(恢复备份并收起覆层) */
fun closePhoneEdit() { /** 手机版:取消编辑(恢复备份并收起覆层) */
viewModel.onCancelClick() fun closePhoneEdit() {
} viewModel.onCancelClick()
}
companion object {
@JvmStatic companion object {
fun start(context: Context, bean: GjcWeighingRecordBean) { @JvmStatic
val starter = Intent(context, GjcWeighingRecordDetailsActivity::class.java) fun start(context: Context, bean: GjcWeighingRecordBean) {
.putExtra(Constant.Key.BEAN, bean) val starter = Intent(context, GjcWeighingRecordDetailsActivity::class.java)
context.startActivity(starter) .putExtra(Constant.Key.BEAN, bean)
} context.startActivity(starter)
} }
} }
}

View File

@@ -1,99 +1,96 @@
package com.lukouguoji.gjc.activity package com.lukouguoji.gjc.activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcWeighingRecordListBinding import com.lukouguoji.gjc.databinding.ActivityGjcWeighingRecordListBinding
import com.lukouguoji.gjc.viewModel.GjcWeighingRecordViewModel import com.lukouguoji.gjc.viewModel.GjcWeighingRecordViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.ConstantEvent import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.getLifecycleOwner import com.lukouguoji.module_base.ktx.getLifecycleOwner
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil import com.lukouguoji.module_base.util.DeviceUtil
/** /**
* 国际出港计重记录列表页 * 国际出港计重记录列表页
* *
* 布局 id 不区分设备,平板/手机由资源限定符自动选择: * 布局 id 不区分设备,平板/手机由资源限定符自动选择:
* layout-sw600dp/activity_gjc_weighing_record_list.xml(平板) * layout-sw600dp/activity_gjc_weighing_record_list.xml(平板)
* layout/activity_gjc_weighing_record_list.xml(手机) * layout/activity_gjc_weighing_record_list.xml(手机)
*/ */
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_LIST) @Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_LIST)
class GjcWeighingRecordListActivity : class GjcWeighingRecordListActivity :
BaseBindingActivity<ActivityGjcWeighingRecordListBinding, GjcWeighingRecordViewModel>() { BaseBindingActivity<ActivityGjcWeighingRecordListBinding, GjcWeighingRecordViewModel>() {
override fun layoutId() = R.layout.activity_gjc_weighing_record_list override fun layoutId() = R.layout.activity_gjc_weighing_record_list
override fun viewModelClass() = GjcWeighingRecordViewModel::class.java override fun viewModelClass() = GjcWeighingRecordViewModel::class.java
/** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */ /** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */
val filterPanelVisible = ObservableBoolean(false) val filterPanelVisible = ObservableBoolean(false)
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港计重记录") setBackArrow(if (DeviceUtil.isPhone()) "计重记录" else "国际出港计重记录")
binding.viewModel = viewModel binding.viewModel = viewModel
binding.activity = this binding.activity = this
// 手机版专属数据:放开默认日期、加载计重人下拉 // 手机版专属数据:放开默认日期、加载计重人下拉
// 平板端不调用,查询链路与请求次数与改造前完全一致 // 平板端不调用,查询链路与请求次数与改造前完全一致
if (DeviceUtil.isPhone()) { if (DeviceUtil.isPhone()) {
viewModel.initPhoneExtras() viewModel.initPhoneExtras()
} }
// 初始化代理人列表 // 初始化代理人列表
viewModel.initAgentList() viewModel.initAgentList()
// 初始化特码列表 // 初始化特码列表
viewModel.initSpecialCodeList() viewModel.initSpecialCodeList()
// 绑定分页逻辑 // 绑定分页逻辑
viewModel.pageModel viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner()) .bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
// 监听刷新事件 // 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH) FlowBus.with<String>(ConstantEvent.EVENT_REFRESH)
.observe(this) { .observe(this) {
viewModel.refresh() viewModel.refresh()
} }
// 初始加载 // 初始加载
viewModel.refresh() viewModel.refresh()
} }
// ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段) ==================== // ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段) ====================
/** 手机版筛选弹层显隐切换 */ /** 手机版筛选弹层显隐切换 */
fun toggleFilterPanel() { fun toggleFilterPanel() {
filterPanelVisible.set(!filterPanelVisible.get()) filterPanelVisible.set(!filterPanelVisible.get())
} }
/** 手机版筛选:重置(只清弹层内条件,不影响顶部搜索框) */ /** 手机版筛选:重置(只清弹层内条件,不影响顶部搜索框) */
fun resetFilter() { fun resetFilter() {
viewModel.checkInDateStart.value = "" viewModel.flightDate.value = ""
viewModel.checkInDateEnd.value = "" viewModel.agentCode.value = ""
viewModel.agentCode.value = "" viewModel.spCode.value = ""
viewModel.spCode.value = "" }
viewModel.destFilter.value = ""
viewModel.opUserFilter.value = "" /** 手机版筛选:确认 */
} fun confirmFilter() {
filterPanelVisible.set(false)
/** 手机版筛选:确认 */ viewModel.searchClick()
fun confirmFilter() { }
filterPanelVisible.set(false)
viewModel.searchClick() companion object {
} @JvmStatic
fun start(context: Context) {
companion object { val starter = Intent(context, GjcWeighingRecordListActivity::class.java)
@JvmStatic context.startActivity(starter)
fun start(context: Context) { }
val starter = Intent(context, GjcWeighingRecordListActivity::class.java) }
context.startActivity(starter) }
}
}
}

View File

@@ -1,221 +1,221 @@
package com.lukouguoji.gjc.activity package com.lukouguoji.gjc.activity
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet import android.animation.AnimatorSet
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcWeighingStartBinding import com.lukouguoji.gjc.databinding.ActivityGjcWeighingStartBinding
import com.lukouguoji.gjc.viewModel.GjcWeighingStartViewModel import com.lukouguoji.gjc.viewModel.GjcWeighingStartViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil import com.lukouguoji.module_base.util.DeviceUtil
/** /**
* 国际出港计重-开始计重页面 * 国际出港计重-开始计重页面
*/ */
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_START) @Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_START)
class GjcWeighingStartActivity : class GjcWeighingStartActivity :
BaseBindingActivity<ActivityGjcWeighingStartBinding, GjcWeighingStartViewModel>() { BaseBindingActivity<ActivityGjcWeighingStartBinding, GjcWeighingStartViewModel>() {
private var isExpanded = false private var isExpanded = false
override fun layoutId() = R.layout.activity_gjc_weighing_start override fun layoutId() = R.layout.activity_gjc_weighing_start
override fun viewModelClass() = GjcWeighingStartViewModel::class.java override fun viewModelClass() = GjcWeighingStartViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港开始计重") setBackArrow(if (DeviceUtil.isPhone()) "开始计重" else "国际出港开始计重")
binding.viewModel = viewModel binding.viewModel = viewModel
// 手机版专属:提交体携带托盘数量/托盘自重(平板不调用,请求体与改造前完全一致) // 手机版专属:提交体携带托盘数量/托盘自重(平板不调用,请求体与改造前完全一致)
if (DeviceUtil.isPhone()) { if (DeviceUtil.isPhone()) {
viewModel.initPhoneExtras() viewModel.initPhoneExtras()
} }
viewModel.initOnCreated(this, intent) viewModel.initOnCreated(this, intent)
setupFloatButtons() setupFloatButtons()
} }
private fun setupFloatButtons() { private fun setupFloatButtons() {
// 浮动按钮组仅平板布局存在(layout-sw600dp),手机布局无这些 id,直接跳过 // 浮动按钮组仅平板布局存在(layout-sw600dp),手机布局无这些 id,直接跳过
val mainFloatButton = binding.mainFloatButton ?: return val mainFloatButton = binding.mainFloatButton ?: return
val maskView = binding.maskView ?: return val maskView = binding.maskView ?: return
val subButton1 = binding.subButton1 ?: return val subButton1 = binding.subButton1 ?: return
val subButton2 = binding.subButton2 ?: return val subButton2 = binding.subButton2 ?: return
// 主按钮点击事件 // 主按钮点击事件
mainFloatButton.setOnClickListener { mainFloatButton.setOnClickListener {
if (!isExpanded) { if (!isExpanded) {
expandButtons() expandButtons()
} }
} }
// 遮罩层点击事件 // 遮罩层点击事件
maskView.setOnClickListener { maskView.setOnClickListener {
collapseButtons() collapseButtons()
} }
// 子按钮1点击事件 - 跳转到出港运抵页面 // 子按钮1点击事件 - 跳转到出港运抵页面
subButton1.setOnClickListener { subButton1.setOnClickListener {
com.alibaba.android.arouter.launcher.ARouter.getInstance() com.alibaba.android.arouter.launcher.ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE) .build(ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
.navigation() .navigation()
collapseButtons() collapseButtons()
} }
// 子按钮2点击事件 - 跳转到计重记录 // 子按钮2点击事件 - 跳转到计重记录
subButton2.setOnClickListener { subButton2.setOnClickListener {
com.alibaba.android.arouter.launcher.ARouter.getInstance() com.alibaba.android.arouter.launcher.ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_LIST) .build(ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_LIST)
.navigation() .navigation()
collapseButtons() collapseButtons()
} }
} }
/** /**
* 展开子按钮(仅平板,手机布局无浮动按钮组) * 展开子按钮(仅平板,手机布局无浮动按钮组)
*/ */
private fun expandButtons() { private fun expandButtons() {
val mainFloatButton = binding.mainFloatButton ?: return val mainFloatButton = binding.mainFloatButton ?: return
val maskView = binding.maskView ?: return val maskView = binding.maskView ?: return
val subButton1 = binding.subButton1 ?: return val subButton1 = binding.subButton1 ?: return
val subButton2 = binding.subButton2 ?: return val subButton2 = binding.subButton2 ?: return
isExpanded = true isExpanded = true
// 显示遮罩层 // 显示遮罩层
maskView.visibility = View.VISIBLE maskView.visibility = View.VISIBLE
maskView.alpha = 0f maskView.alpha = 0f
maskView.animate().alpha(0.3f).setDuration(200).start() maskView.animate().alpha(0.3f).setDuration(200).start()
// 显示子按钮 // 显示子按钮
subButton1.visibility = View.VISIBLE subButton1.visibility = View.VISIBLE
subButton2.visibility = View.VISIBLE subButton2.visibility = View.VISIBLE
// 子按钮1动画(向左上方移动: 左移15dp, 上移55dp) // 子按钮1动画(向左上方移动: 左移15dp, 上移55dp)
val sub1TransX = ObjectAnimator.ofFloat(subButton1, "translationX", 0f, -5f) val sub1TransX = ObjectAnimator.ofFloat(subButton1, "translationX", 0f, -5f)
val sub1TransY = ObjectAnimator.ofFloat(subButton1, "translationY", 0f, -25f) val sub1TransY = ObjectAnimator.ofFloat(subButton1, "translationY", 0f, -25f)
val sub1Alpha = ObjectAnimator.ofFloat(subButton1, "alpha", 0f, 1f) val sub1Alpha = ObjectAnimator.ofFloat(subButton1, "alpha", 0f, 1f)
val sub1Scale = ObjectAnimator.ofFloat(subButton1, "scaleX", 0f, 1f) val sub1Scale = ObjectAnimator.ofFloat(subButton1, "scaleX", 0f, 1f)
val sub1ScaleY = ObjectAnimator.ofFloat(subButton1, "scaleY", 0f, 1f) val sub1ScaleY = ObjectAnimator.ofFloat(subButton1, "scaleY", 0f, 1f)
val animSet1 = AnimatorSet() val animSet1 = AnimatorSet()
animSet1.playTogether(sub1TransX, sub1TransY, sub1Alpha, sub1Scale, sub1ScaleY) animSet1.playTogether(sub1TransX, sub1TransY, sub1Alpha, sub1Scale, sub1ScaleY)
animSet1.duration = 200 animSet1.duration = 200
// 子按钮2动画(向左下方移动: 左移55dp, 下移15dp) // 子按钮2动画(向左下方移动: 左移55dp, 下移15dp)
val sub2TransX = ObjectAnimator.ofFloat(subButton2, "translationX", 0f, -25f) val sub2TransX = ObjectAnimator.ofFloat(subButton2, "translationX", 0f, -25f)
val sub2TransY = ObjectAnimator.ofFloat(subButton2, "translationY", 0f, -5f) val sub2TransY = ObjectAnimator.ofFloat(subButton2, "translationY", 0f, -5f)
val sub2Alpha = ObjectAnimator.ofFloat(subButton2, "alpha", 0f, 1f) val sub2Alpha = ObjectAnimator.ofFloat(subButton2, "alpha", 0f, 1f)
val sub2Scale = ObjectAnimator.ofFloat(subButton2, "scaleX", 0f, 1f) val sub2Scale = ObjectAnimator.ofFloat(subButton2, "scaleX", 0f, 1f)
val sub2ScaleY = ObjectAnimator.ofFloat(subButton2, "scaleY", 0f, 1f) val sub2ScaleY = ObjectAnimator.ofFloat(subButton2, "scaleY", 0f, 1f)
val animSet2 = AnimatorSet() val animSet2 = AnimatorSet()
animSet2.playTogether(sub2TransX, sub2TransY, sub2Alpha, sub2Scale, sub2ScaleY) animSet2.playTogether(sub2TransX, sub2TransY, sub2Alpha, sub2Scale, sub2ScaleY)
animSet2.duration = 200 animSet2.duration = 200
animSet2.startDelay = 50 animSet2.startDelay = 50
// 主按钮隐藏动画 // 主按钮隐藏动画
mainFloatButton.animate() mainFloatButton.animate()
.scaleX(0f) .scaleX(0f)
.scaleY(0f) .scaleY(0f)
.alpha(0f) .alpha(0f)
.setDuration(150) .setDuration(150)
.setListener(object : AnimatorListenerAdapter() { .setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
mainFloatButton.visibility = View.GONE mainFloatButton.visibility = View.GONE
} }
}) })
.start() .start()
animSet1.start() animSet1.start()
animSet2.start() animSet2.start()
} }
/** /**
* 收起子按钮(仅平板,手机布局无浮动按钮组) * 收起子按钮(仅平板,手机布局无浮动按钮组)
*/ */
private fun collapseButtons() { private fun collapseButtons() {
val mainFloatButton = binding.mainFloatButton ?: return val mainFloatButton = binding.mainFloatButton ?: return
val maskView = binding.maskView ?: return val maskView = binding.maskView ?: return
val subButton1 = binding.subButton1 ?: return val subButton1 = binding.subButton1 ?: return
val subButton2 = binding.subButton2 ?: return val subButton2 = binding.subButton2 ?: return
isExpanded = false isExpanded = false
// 隐藏遮罩层 // 隐藏遮罩层
maskView.animate().alpha(0f).setDuration(200).withEndAction { maskView.animate().alpha(0f).setDuration(200).withEndAction {
maskView.visibility = View.GONE maskView.visibility = View.GONE
}.start() }.start()
// 子按钮1动画(回到原位) // 子按钮1动画(回到原位)
val sub1TransX = ObjectAnimator.ofFloat(subButton1, "translationX", subButton1.translationX, 0f) val sub1TransX = ObjectAnimator.ofFloat(subButton1, "translationX", subButton1.translationX, 0f)
val sub1TransY = ObjectAnimator.ofFloat(subButton1, "translationY", subButton1.translationY, 0f) val sub1TransY = ObjectAnimator.ofFloat(subButton1, "translationY", subButton1.translationY, 0f)
val sub1Alpha = ObjectAnimator.ofFloat(subButton1, "alpha", 1f, 0f) val sub1Alpha = ObjectAnimator.ofFloat(subButton1, "alpha", 1f, 0f)
val sub1Scale = ObjectAnimator.ofFloat(subButton1, "scaleX", 1f, 0f) val sub1Scale = ObjectAnimator.ofFloat(subButton1, "scaleX", 1f, 0f)
val sub1ScaleY = ObjectAnimator.ofFloat(subButton1, "scaleY", 1f, 0f) val sub1ScaleY = ObjectAnimator.ofFloat(subButton1, "scaleY", 1f, 0f)
val animSet1 = AnimatorSet() val animSet1 = AnimatorSet()
animSet1.playTogether(sub1TransX, sub1TransY, sub1Alpha, sub1Scale, sub1ScaleY) animSet1.playTogether(sub1TransX, sub1TransY, sub1Alpha, sub1Scale, sub1ScaleY)
animSet1.duration = 200 animSet1.duration = 200
// 子按钮2动画(回到原位) // 子按钮2动画(回到原位)
val sub2TransX = ObjectAnimator.ofFloat(subButton2, "translationX", subButton2.translationX, 0f) val sub2TransX = ObjectAnimator.ofFloat(subButton2, "translationX", subButton2.translationX, 0f)
val sub2TransY = ObjectAnimator.ofFloat(subButton2, "translationY", subButton2.translationY, 0f) val sub2TransY = ObjectAnimator.ofFloat(subButton2, "translationY", subButton2.translationY, 0f)
val sub2Alpha = ObjectAnimator.ofFloat(subButton2, "alpha", 1f, 0f) val sub2Alpha = ObjectAnimator.ofFloat(subButton2, "alpha", 1f, 0f)
val sub2Scale = ObjectAnimator.ofFloat(subButton2, "scaleX", 1f, 0f) val sub2Scale = ObjectAnimator.ofFloat(subButton2, "scaleX", 1f, 0f)
val sub2ScaleY = ObjectAnimator.ofFloat(subButton2, "scaleY", 1f, 0f) val sub2ScaleY = ObjectAnimator.ofFloat(subButton2, "scaleY", 1f, 0f)
val animSet2 = AnimatorSet() val animSet2 = AnimatorSet()
animSet2.playTogether(sub2TransX, sub2TransY, sub2Alpha, sub2Scale, sub2ScaleY) animSet2.playTogether(sub2TransX, sub2TransY, sub2Alpha, sub2Scale, sub2ScaleY)
animSet2.duration = 200 animSet2.duration = 200
animSet1.addListener(object : AnimatorListenerAdapter() { animSet1.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
subButton1.visibility = View.GONE subButton1.visibility = View.GONE
} }
}) })
animSet2.addListener(object : AnimatorListenerAdapter() { animSet2.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
subButton2.visibility = View.GONE subButton2.visibility = View.GONE
} }
}) })
// 主按钮显示动画 // 主按钮显示动画
mainFloatButton.visibility = View.VISIBLE mainFloatButton.visibility = View.VISIBLE
mainFloatButton.animate() mainFloatButton.animate()
.scaleX(1f) .scaleX(1f)
.scaleY(1f) .scaleY(1f)
.alpha(1f) .alpha(1f)
.setDuration(200) .setDuration(200)
.setStartDelay(100) .setStartDelay(100)
.setListener(null) .setListener(null)
.start() .start()
animSet1.start() animSet1.start()
animSet2.start() animSet2.start()
} }
companion object { companion object {
/** /**
* 启动开始计重页面 * 启动开始计重页面
* @param context 上下文 * @param context 上下文
* @param maWbId 运单ID,默认为0(新增模式) * @param maWbId 运单ID,默认为0(新增模式)
*/ */
@JvmStatic @JvmStatic
fun startForAdd(context: Context, maWbId: Long = 0) { fun startForAdd(context: Context, maWbId: Long = 0) {
val starter = Intent(context, GjcWeighingStartActivity::class.java) val starter = Intent(context, GjcWeighingStartActivity::class.java)
.putExtra(Constant.Key.MAWB_ID, maWbId) .putExtra(Constant.Key.MAWB_ID, maWbId)
context.startActivity(starter) context.startActivity(starter)
} }
} }
} }

View File

@@ -1,177 +1,170 @@
package com.lukouguoji.gjc.activity package com.lukouguoji.gjc.activity
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import com.alibaba.android.arouter.facade.annotation.Autowired import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter import com.alibaba.android.arouter.launcher.ARouter
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpArriveBinding import com.lukouguoji.gjc.databinding.ActivityIntExpArriveBinding
import com.lukouguoji.gjc.viewModel.IntExpArriveViewModel import com.lukouguoji.gjc.viewModel.IntExpArriveViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.GjcHaWb import com.lukouguoji.module_base.bean.GjcHaWb
import com.lukouguoji.module_base.bean.GjcMaWb import com.lukouguoji.module_base.bean.GjcMaWb
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.addOnItemClickListener import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.ktx.commonAdapter import com.lukouguoji.module_base.ktx.commonAdapter
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil import com.lukouguoji.module_base.util.DeviceUtil
import java.util.ArrayList import java.util.ArrayList
/** /**
* 国际出港-出港运抵页面 * 国际出港-出港运抵页面
* *
* 布局 id 不区分设备,平板/手机由资源限定符自动选择: * 布局 id 不区分设备,平板/手机由资源限定符自动选择:
* layout-sw600dp/activity_int_exp_arrive.xml(平板) * layout-sw600dp/activity_int_exp_arrive.xml(平板)
* layout/activity_int_exp_arrive.xml(手机) * layout/activity_int_exp_arrive.xml(手机)
*/ */
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE) @Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
class IntExpArriveActivity : class IntExpArriveActivity :
BaseBindingActivity<ActivityIntExpArriveBinding, IntExpArriveViewModel>() { BaseBindingActivity<ActivityIntExpArriveBinding, IntExpArriveViewModel>() {
@JvmField @JvmField
@Autowired @Autowired
var wbNoParam: String? = null var wbNoParam: String? = null
override fun layoutId() = R.layout.activity_int_exp_arrive override fun layoutId() = R.layout.activity_int_exp_arrive
override fun viewModelClass() = IntExpArriveViewModel::class.java override fun viewModelClass() = IntExpArriveViewModel::class.java
/** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */ /** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */
val filterPanelVisible = ObservableBoolean(false) val filterPanelVisible = ObservableBoolean(false)
/** 供 [com.lukouguoji.gjc.holder.IntExpArriveViewHolder] 绑定 item 布局的 viewModel 变量(父类 viewModel 为 protected) */ /** 供 [com.lukouguoji.gjc.holder.IntExpArriveViewHolder] 绑定 item 布局的 viewModel 变量(父类 viewModel 为 protected) */
val itemViewModel: IntExpArriveViewModel get() = viewModel val itemViewModel: IntExpArriveViewModel get() = viewModel
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
// 注入 ARouter 参数 // 注入 ARouter 参数
ARouter.getInstance().inject(this) ARouter.getInstance().inject(this)
setBackArrow("国际出港运抵") setBackArrow(if (DeviceUtil.isPhone()) "出港运抵" else "国际出港运抵")
binding.viewModel = viewModel binding.viewModel = viewModel
binding.activity = this binding.activity = this
// 手机版专属数据:加载「代理」筛选下拉 // 手机版专属数据:加载「代理」筛选下拉
// 平板端不调用,查询链路与请求次数与改造前完全一致 // 平板端不调用,查询链路与请求次数与改造前完全一致
if (DeviceUtil.isPhone()) { if (DeviceUtil.isPhone()) {
viewModel.initPhoneExtras() viewModel.initPhoneExtras()
} }
// 如果有传入运单号,自动填充并触发搜索 // 如果有传入运单号,自动填充并触发搜索
if (!wbNoParam.isNullOrEmpty()) { if (!wbNoParam.isNullOrEmpty()) {
viewModel.waybillNo.value = wbNoParam viewModel.waybillNo.value = wbNoParam
viewModel.searchClick() viewModel.searchClick()
} }
// 绑定分页 // 绑定分页
viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this) viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this)
// 设置item点击监听 // 设置item点击监听
binding.rv.addOnItemClickListener(viewModel) binding.rv.addOnItemClickListener(viewModel)
// 监听刷新事件 // 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { viewModel.onItemCheckChanged() } FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { viewModel.onItemCheckChanged() }
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) { FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh() viewModel.refresh()
} }
// 初始加载数据(如果没有传入运单号,才执行初始加载) // 初始加载数据(如果没有传入运单号,才执行初始加载)
if (wbNoParam.isNullOrEmpty()) { if (wbNoParam.isNullOrEmpty()) {
viewModel.refresh() viewModel.refresh()
} }
} }
// ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段与数据) ==================== // ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段与数据) ====================
/** /**
* 手机版 Tab 切换回调:PhoneStatusTab 已通过双向绑定写入 viewModel.currentTab, * 手机版 Tab 切换回调:PhoneStatusTab 已通过双向绑定写入 viewModel.currentTab,
* 这里触发 ViewModel 把适配器数据集替换为过滤后的子集(不发起新请求)。 * 这里触发 ViewModel 把适配器数据集替换为过滤后的子集(不发起新请求)。
*/ */
fun onArriveTabChanged() { fun onArriveTabChanged() {
viewModel.onTabChangedPhone() viewModel.onTabChangedPhone()
} }
/** 卡片长按:展开操作浮层(View.OnLongClickListener 要求返回 Boolean) */ /**
fun showCardActionPhone(bean: GjcMaWb): Boolean { * 卡片长按:展开操作浮层(View.OnLongClickListener 要求返回 Boolean)。
bean.actionMenuVisible.set(true) * 仅已运抵 Tab 有操作(重置 / 回执);待运抵 Tab 的「补充信息」页后端确认暂不需要,已移除。
return true */
} fun showCardActionPhone(bean: GjcMaWb): Boolean {
if (viewModel.currentTab.value != "shipped") return false
/** 点击浮层遮罩/空白处:收起操作浮层 */ bean.actionMenuVisible.set(true)
fun hideCardActionPhone(bean: GjcMaWb) { return true
bean.actionMenuVisible.set(false) }
}
/** 点击浮层遮罩/空白处:收起操作浮层 */
/** 选择圈勾选状态变化后回调(由 ViewHolder 调用) */ fun hideCardActionPhone(bean: GjcMaWb) {
fun onItemCheckChanged() { bean.actionMenuVisible.set(false)
viewModel.onItemCheckChanged() }
}
/** 选择圈勾选状态变化后回调(由 ViewHolder 调用) */
/** 手机版筛选弹层显隐切换 */ fun onItemCheckChanged() {
fun toggleFilterPanel() { viewModel.onItemCheckChanged()
filterPanelVisible.set(!filterPanelVisible.get()) }
}
/** 手机版筛选弹层显隐切换 */
/** 手机版筛选:重置 */ fun toggleFilterPanel() {
fun resetFilter() { filterPanelVisible.set(!filterPanelVisible.get())
viewModel.flightDate.value = "" }
viewModel.flightNo.value = ""
viewModel.agentCodePhone.value = "" /** 手机版筛选:重置 */
} fun resetFilter() {
viewModel.flightDate.value = ""
/** 手机版筛选:确认 */ viewModel.flightNo.value = ""
fun confirmFilter() { }
filterPanelVisible.set(false)
viewModel.searchClick() /** 手机版筛选:确认 */
} fun confirmFilter() {
filterPanelVisible.set(false)
/** viewModel.searchClick()
* 进入「分单明细」页:直接传递该运单已加载的 haWbList,零新增请求。 }
*/
fun openSubOrder(bean: GjcMaWb) { /**
val list = ArrayList<GjcHaWb>(bean.haWbList.orEmpty()) * 进入「分单明细」页:直接传递该运单已加载的 haWbList,零新增请求。
IntExpArriveSubOrderActivity.start(this, bean.wbNo.orEmpty(), list) */
} fun openSubOrder(bean: GjcMaWb) {
val list = ArrayList<GjcHaWb>(bean.haWbList.orEmpty())
/** IntExpArriveSubOrderActivity.start(this, bean.wbNo.orEmpty(), list)
* 进入「回执」页(手机专属只读页):复用 haWbList[].response + arrivalOpDate,零新增请求。 }
*/
fun openReceipt(bean: GjcMaWb) { /**
bean.actionMenuVisible.set(false) * 进入「回执」页(手机专属只读页):复用 haWbList[].response + arrivalOpDate,零新增请求。
val list = ArrayList<GjcHaWb>(bean.haWbList.orEmpty()) */
IntExpArriveReceiptActivity.start(this, bean.wbNo.orEmpty(), list) fun openReceipt(bean: GjcMaWb) {
} bean.actionMenuVisible.set(false)
val list = ArrayList<GjcHaWb>(bean.haWbList.orEmpty())
/** IntExpArriveReceiptActivity.start(this, bean.wbNo.orEmpty(), list)
* 进入「补充信息」页(手机专属新页):对该运单整个 haWbList 批量应用(参照 Pad 端国际进港 }
* IntArrSupplementInfoViewModel 的批量复制模式)。后端暂无保存接口,见接口问题清单。
*/ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
fun openSupplement(bean: GjcMaWb) { super.onActivityResult(requestCode, resultCode, data)
bean.actionMenuVisible.set(false) when (requestCode) {
val list = ArrayList<GjcHaWb>(bean.haWbList.orEmpty()) Constant.RequestCode.WAYBILL -> {
IntExpArriveSupplementActivity.start(this, bean.wbNo.orEmpty(), list) if (resultCode == Activity.RESULT_OK) {
} viewModel.waybillNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { }
super.onActivityResult(requestCode, resultCode, data) }
when (requestCode) { Constant.RequestCode.CODE -> {
Constant.RequestCode.WAYBILL -> { if (resultCode == Activity.RESULT_OK) {
if (resultCode == Activity.RESULT_OK) { viewModel.hno.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.waybillNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT) viewModel.searchClick()
viewModel.searchClick() }
} }
} }
Constant.RequestCode.CODE -> { }
if (resultCode == Activity.RESULT_OK) { }
viewModel.hno.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
}
}
}
}
}

View File

@@ -1,41 +0,0 @@
package com.lukouguoji.gjc.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpArriveSupplementBinding
import com.lukouguoji.gjc.viewModel.IntExpArriveSupplementViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.GjcHaWb
import com.lukouguoji.module_base.common.Constant
/**
* 国际出港运抵-补充信息页(5.5寸手机版专属新页,平板端无对应页面)
*
* 参照 Pad 端国际进港「收发货人信息」补充页的批量应用模式:对该运单整个 haWbList 批量应用
* 同一份收发货人表单。后端暂无保存接口,见 IntExpArriveSupplementViewModel 顶部注释。
* 对应设计稿 documents/5.5寸手持端设计文件/04_出港运抵/supplement.html。
*/
class IntExpArriveSupplementActivity :
BaseBindingActivity<ActivityIntExpArriveSupplementBinding, IntExpArriveSupplementViewModel>() {
override fun layoutId() = R.layout.activity_int_exp_arrive_supplement
override fun viewModelClass() = IntExpArriveSupplementViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("补充信息")
binding.viewModel = viewModel
viewModel.initOnCreated(intent)
}
companion object {
@JvmStatic
fun start(context: Context, wbNo: String, haWbList: ArrayList<GjcHaWb>) {
val starter = Intent(context, IntExpArriveSupplementActivity::class.java)
.putExtra(Constant.Key.CODE, wbNo)
.putExtra(Constant.Key.DATA, haWbList)
context.startActivity(starter)
}
}
}

View File

@@ -35,7 +35,7 @@ class IntExpOutHandoverActivity :
val filterPanelVisible = ObservableBoolean(false) val filterPanelVisible = ObservableBoolean(false)
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港出库交接") setBackArrow(if (DeviceUtil.isPhone()) "出库交接" else "国际出港出库交接")
binding.viewModel = viewModel binding.viewModel = viewModel
binding.activity = this binding.activity = this
@@ -72,12 +72,12 @@ class IntExpOutHandoverActivity :
/** /**
* 手机版筛选:重置 * 手机版筛选:重置
* *
* 只清空弹层内的三个条件(航班日期 / 航班号 / 交接人),不影响 Tab 与顶部搜索框。 * 只清空弹层内的三个条件(航班日期 / 航班号 / 目的站),不影响 Tab 与顶部搜索框。
*/ */
fun resetFilter() { fun resetFilter() {
viewModel.flightDate.value = "" viewModel.flightDate.value = ""
viewModel.flightNo.value = "" viewModel.flightNo.value = ""
viewModel.hoId.value = "" viewModel.fdest.value = ""
} }
/** 手机版筛选:确认 */ /** 手机版筛选:确认 */

View File

@@ -1,171 +1,171 @@
package com.lukouguoji.gjc.page.move package com.lukouguoji.gjc.page.move
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpMoveBinding import com.lukouguoji.gjc.databinding.ActivityIntExpMoveBinding
import com.lukouguoji.gjc.viewModel.IntExpMoveViewModel import com.lukouguoji.gjc.viewModel.IntExpMoveViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.addOnItemClickListener import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.model.ConfirmDialogModel import com.lukouguoji.module_base.model.ConfirmDialogModel
import com.lukouguoji.module_base.model.ScanModel import com.lukouguoji.module_base.model.ScanModel
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil import com.lukouguoji.module_base.util.DeviceUtil
/** /**
* 国际出港-出港移库 * 国际出港-出港移库
*/ */
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_MOVE) @Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_MOVE)
class IntExpMoveActivity : BaseBindingActivity<ActivityIntExpMoveBinding, IntExpMoveViewModel>() { class IntExpMoveActivity : BaseBindingActivity<ActivityIntExpMoveBinding, IntExpMoveViewModel>() {
// 布局 id 不区分设备,平板/手机由资源限定符自动选择: // 布局 id 不区分设备,平板/手机由资源限定符自动选择:
// layout-sw600dp/activity_int_exp_move.xml(平板) / layout/activity_int_exp_move.xml(手机) // layout-sw600dp/activity_int_exp_move.xml(平板) / layout/activity_int_exp_move.xml(手机)
override fun layoutId() = R.layout.activity_int_exp_move override fun layoutId() = R.layout.activity_int_exp_move
override fun viewModelClass() = IntExpMoveViewModel::class.java override fun viewModelClass() = IntExpMoveViewModel::class.java
/** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */ /** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */
val filterPanelVisible = ObservableBoolean(false) val filterPanelVisible = ObservableBoolean(false)
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港移库") setBackArrow(if (DeviceUtil.isPhone()) "出港移库" else "国际出港移库")
binding.viewModel = viewModel binding.viewModel = viewModel
binding.activity = this binding.activity = this
// 手机版专属数据:默认「待移库」Tab、Tab 角标计数、特码下拉 // 手机版专属数据:默认「待移库」Tab、Tab 角标计数、特码下拉
// 平板端不调用,查询链路与请求次数与改造前完全一致 // 平板端不调用,查询链路与请求次数与改造前完全一致
if (DeviceUtil.isPhone()) { if (DeviceUtil.isPhone()) {
viewModel.initPhoneExtras() viewModel.initPhoneExtras()
} }
initRecyclerView() initRecyclerView()
initListeners() initListeners()
observeData() observeData()
} }
/** /**
* 初始化RecyclerView * 初始化RecyclerView
*/ */
private fun initRecyclerView() { private fun initRecyclerView() {
viewModel.pageModel.bindSmartRefreshLayout( viewModel.pageModel.bindSmartRefreshLayout(
binding.srl, binding.srl,
binding.rv, binding.rv,
viewModel, viewModel,
this this
) )
binding.rv.addOnItemClickListener(viewModel) binding.rv.addOnItemClickListener(viewModel)
} }
/** /**
* 初始化监听器 * 初始化监听器
*/ */
private fun initListeners() { private fun initListeners() {
// 移库按钮:btnMove 只存在于平板变体(手机版底部条走 setOnActionClick),故判空 // 移库按钮:btnMove 只存在于平板变体(手机版底部条走 setOnActionClick),故判空
binding.btnMove?.setOnClickListener { binding.btnMove?.setOnClickListener {
showMoveConfirmDialog() showMoveConfirmDialog()
} }
} }
/** /**
* 观察数据变化 * 观察数据变化
*/ */
private fun observeData() { private fun observeData() {
// 监听item选择变化事件,更新全选状态 // 监听item选择变化事件,更新全选状态
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) {
viewModel.onItemCheckChanged() viewModel.onItemCheckChanged()
} }
// 监听刷新事件 // 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) { FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh() viewModel.refresh()
} }
// 初始加载数据 // 初始加载数据
viewModel.refresh() viewModel.refresh()
} }
/** /**
* 扫码运单号 * 扫码运单号
*/ */
fun scanWaybill() { fun scanWaybill() {
ScanModel.startScan(this, Constant.RequestCode.WAYBILL) ScanModel.startScan(this, Constant.RequestCode.WAYBILL)
} }
// ==================== 手机版专用交互(复用既有 ViewModel 字段,无新增业务逻辑) ==================== // ==================== 手机版专用交互(复用既有 ViewModel 字段,无新增业务逻辑) ====================
/** /**
* 手机版筛选弹层显隐切换 * 手机版筛选弹层显隐切换
*/ */
fun toggleFilterPanel() { fun toggleFilterPanel() {
filterPanelVisible.set(!filterPanelVisible.get()) filterPanelVisible.set(!filterPanelVisible.get())
} }
/** /**
* 手机版筛选:重置 * 手机版筛选:重置
* *
* 只清空弹层内的三个条件(承运人 / 运单类型 / 特码),不影响 Tab 与顶部搜索框。 * 只清空弹层内的三个条件(承运人 / 运单类型 / 特码),不影响 Tab 与顶部搜索框。
*/ */
fun resetFilter() { fun resetFilter() {
viewModel.by1.value = "" viewModel.by1.value = ""
viewModel.awbType.value = "" viewModel.awbType.value = ""
viewModel.spCode.value = "" viewModel.spCode.value = ""
} }
/** /**
* 手机版筛选:确定 * 手机版筛选:确定
*/ */
fun confirmFilter() { fun confirmFilter() {
filterPanelVisible.set(false) filterPanelVisible.set(false)
viewModel.searchClick() viewModel.searchClick()
} }
/** /**
* 显示移库确认对话框 * 显示移库确认对话框
* *
* 手机版底部操作条通过 XML 的 setOnActionClick 调用,故为 public。 * 手机版底部操作条通过 XML 的 setOnActionClick 调用,故为 public。
*/ */
fun showMoveConfirmDialog() { fun showMoveConfirmDialog() {
val selectedItems = viewModel.getSelectedItems() val selectedItems = viewModel.getSelectedItems()
if (selectedItems.isEmpty()) { if (selectedItems.isEmpty()) {
showToast("请至少选择一条运单") showToast("请至少选择一条运单")
return return
} }
ConfirmDialogModel( ConfirmDialogModel(
message = "确定要移库选中的 ${selectedItems.size} 条运单吗?", message = "确定要移库选中的 ${selectedItems.size} 条运单吗?",
title = "移库确认" title = "移库确认"
) { ) {
viewModel.submitMove() viewModel.submitMove()
}.show() }.show()
} }
/** /**
* 扫码回调 * 扫码回调
*/ */
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.RequestCode.WAYBILL && resultCode == Activity.RESULT_OK) { if (requestCode == Constant.RequestCode.WAYBILL && resultCode == Activity.RESULT_OK) {
val code = data?.getStringExtra(Constant.Result.CODED_CONTENT) val code = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.waybillNo.value = code viewModel.waybillNo.value = code
viewModel.searchClick() viewModel.searchClick()
} }
} }
companion object { companion object {
@JvmStatic @JvmStatic
fun start(context: Context) { fun start(context: Context) {
val starter = Intent(context, IntExpMoveActivity::class.java) val starter = Intent(context, IntExpMoveActivity::class.java)
context.startActivity(starter) context.startActivity(starter)
} }
} }
} }

View File

@@ -7,7 +7,6 @@ import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.holder.GjcQueryViewHolder import com.lukouguoji.gjc.holder.GjcQueryViewHolder
import com.lukouguoji.module_base.base.BasePageViewModel import com.lukouguoji.module_base.base.BasePageViewModel
import com.lukouguoji.module_base.bean.GjcMaWb
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.http.net.NetApply import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchCollect import com.lukouguoji.module_base.ktx.launchCollect
@@ -81,14 +80,17 @@ class GjcQueryViewModel : BasePageViewModel() {
// 特码下拉列表(设计稿筛选弹层为下拉;平板筛选为文本输入,不加载) // 特码下拉列表(设计稿筛选弹层为下拉;平板筛选为文本输入,不加载)
val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList()) val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList())
// 三格统计:已入库 / 已离港(总票数复用 totalCount,为服务端全量口径) // 三格统计:总票数 / 已入库 / 已离港,均取 pageQueryTotal 出参(wbNumber / storedNumber / departedNumber),
// 后端无分项统计出参,且文档定义的 isFclose 入参实测被 pageQuery/pageQueryTotal 静默忽略 // 与列表同条件、服务端全量口径
// (2026-07-31 内网 A/B 验证,见接口问题清单)——已入库/已离港暂为「已加载数据」客户端计数口径
val storedCount = MutableLiveData("0") val storedCount = MutableLiveData("0")
val departedCount = MutableLiveData("0") val departedCount = MutableLiveData("0")
// 日期类型(0:入库,1:离港),与主干平板端一致。后端 beginDate/endDate 仅在携带 dateState 时生效
// (内网实测:不带 dateState 时起止日期被忽略)。平板布局(本分支)不绑定,phoneExtras 关闭时不进请求
val dateState = MutableLiveData("0")
val dateStateList = MutableLiveData(listOf(KeyValue("入库", "0"), KeyValue("离港", "1")))
private var phoneExtrasEnabled = false private var phoneExtrasEnabled = false
private var phoneLoadedList: List<GjcMaWb> = emptyList()
/** /**
* 手机形态专属初始化(Activity 在 DeviceUtil.isPhone() 时调用)。 * 手机形态专属初始化(Activity 在 DeviceUtil.isPhone() 时调用)。
@@ -186,10 +188,15 @@ class GjcQueryViewModel : BasePageViewModel() {
* 核心方法:调用API获取数据并转换PageInfo为BaseListBean * 核心方法:调用API获取数据并转换PageInfo为BaseListBean
*/ */
override fun getData() { override fun getData() {
// 手机版:日期类型,仅在有起止日期时携带(平板 phoneExtrasEnabled 恒 false → null 不进请求)
val hasDate = !flightDateStart.value.isNullOrEmpty() || !flightDateEnd.value.isNullOrEmpty()
val dateStateParam = if (phoneExtrasEnabled && hasDate) dateState.value?.toIntOrNull() else null
// 构建查询参数(列表接口) // 构建查询参数(列表接口)
val listParams = mapOf( val listParams = mapOf(
"pageNum" to pageModel.page, "pageNum" to pageModel.page,
"pageSize" to pageModel.limit, "pageSize" to pageModel.limit,
"dateState" to dateStateParam,
"beginDate" to flightDateStart.value!!.ifEmpty { null }, "beginDate" to flightDateStart.value!!.ifEmpty { null },
"endDate" to flightDateEnd.value!!.ifEmpty { null }, "endDate" to flightDateEnd.value!!.ifEmpty { null },
"agentCode" to agentId.value!!.ifEmpty { null }, "agentCode" to agentId.value!!.ifEmpty { null },
@@ -206,6 +213,7 @@ class GjcQueryViewModel : BasePageViewModel() {
// 构建查询参数(统计接口 - 使用相同的搜索条件) // 构建查询参数(统计接口 - 使用相同的搜索条件)
val totalParams = mapOf( val totalParams = mapOf(
"dateState" to dateStateParam,
"beginDate" to flightDateStart.value!!.ifEmpty { null }, "beginDate" to flightDateStart.value!!.ifEmpty { null },
"endDate" to flightDateEnd.value!!.ifEmpty { null }, "endDate" to flightDateEnd.value!!.ifEmpty { null },
"agentCode" to agentId.value!!.ifEmpty { null }, "agentCode" to agentId.value!!.ifEmpty { null },
@@ -227,14 +235,6 @@ class GjcQueryViewModel : BasePageViewModel() {
onSuccess = { pageInfo -> onSuccess = { pageInfo ->
// ⚠️ 核心:使用toBaseListBean()转换PageInfo为BaseListBean // ⚠️ 核心:使用toBaseListBean()转换PageInfo为BaseListBean
pageModel.handleListBean(pageInfo.toBaseListBean()) pageModel.handleListBean(pageInfo.toBaseListBean())
// 手机三格统计:对已加载数据做客户端计数(平板不触发)
if (phoneExtrasEnabled) {
val pageItems = pageInfo.list ?: emptyList()
phoneLoadedList =
if (pageModel.page <= 1) pageItems else phoneLoadedList + pageItems
updatePhoneCounts()
}
} }
} }
@@ -247,20 +247,13 @@ class GjcQueryViewModel : BasePageViewModel() {
totalCount.value = (data?.wbNumber ?: 0).toString() totalCount.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString() totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString() totalWeight.value = (data?.totalWeight ?: 0.0).toString()
// 手机三格统计分项(平板布局不绑定,无影响)
storedCount.value = (data?.storedNumber ?: 0).toString()
departedCount.value = (data?.departedNumber ?: 0).toString()
} }
} }
} }
/**
* 手机三格统计(已入库/已离港):与卡片状态标签同一判定口径(fclose / opDate),
* 统计范围为已加载的列表数据。
*/
private fun updatePhoneCounts() {
departedCount.value = phoneLoadedList.count { it.isQueryDeparted }.toString()
storedCount.value =
phoneLoadedList.count { !it.isQueryDeparted && it.isQueryStored }.toString()
}
/** /**
* 处理扫码结果 * 处理扫码结果
*/ */

View File

@@ -145,7 +145,7 @@ class GjcWeighingRecordDetailsViewModel : BaseViewModel() {
totalWeight.value = String.format("%.2f", sumWeight) totalWeight.value = String.format("%.2f", sumWeight)
// 计算托盘总重(手机版顶部卡片) // 计算托盘总重(手机版顶部卡片)
val sumCarWeight = records.sumOf { it.carWeight.toDoubleOrNull() ?: 0.0 } val sumCarWeight = records.sumOf { it.trayTotalWeight }
totalCarWeight.value = String.format("%.2f", sumCarWeight) totalCarWeight.value = String.format("%.2f", sumCarWeight)
// 计算重量误差百分比 // 计算重量误差百分比

View File

@@ -48,28 +48,19 @@ class GjcWeighingRecordViewModel : BasePageViewModel() {
// ========== 5.5寸手机版专属字段(平板不调用 initPhoneExtras,行为零变化) ========== // ========== 5.5寸手机版专属字段(平板不调用 initPhoneExtras,行为零变化) ==========
/** 目的站(后端支持入参 dest) */ // 手机版筛选弹层与平板端查询参数保持一致(后端确认):航班日期 / 代理人 / 特码,
val destFilter = MutableLiveData("") // 运单号走顶部搜索框;dest / opId / 收运时间范围后端均不支持(dest、opId 内网实测被忽略)
/** 计重人(后端支持入参 opId,下拉复用仓库用户列表) */
val opUserFilter = MutableLiveData("")
val whsUserList = MutableLiveData<List<KeyValue>>(emptyList())
/** 入库日期起/止(后端暂无对应入参,传 opDateStart/opDateEnd 预留参数,见接口问题清单) */
val checkInDateStart = MutableLiveData("")
val checkInDateEnd = MutableLiveData("")
/** 由手机形态的 Activity 置 true,平板保持 false(不产生任何额外请求与参数) */ /** 由手机形态的 Activity 置 true,平板保持 false(不产生任何额外请求与参数) */
private var phoneExtrasEnabled = false private var phoneExtrasEnabled = false
/** /**
* 开启手机版专属数据:放开「今天」默认日期(设计稿筛选无航班日期)、加载计重人下拉。 * 开启手机版专属数据:放开「今天」默认日期(手机默认查全量,航班日期在筛选弹层可选)。
* 仅由手机形态的 Activity 调用。 * 仅由手机形态的 Activity 调用。
*/ */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneExtrasEnabled = true phoneExtrasEnabled = true
flightDate.value = "" flightDate.value = ""
DictUtils.getWHSUserList { whsUserList.postValue(it) }
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -173,11 +164,6 @@ class GjcWeighingRecordViewModel : BasePageViewModel() {
baseParams["wbNo"] = null baseParams["wbNo"] = null
baseParams["likeNo"] = key baseParams["likeNo"] = key
} }
baseParams["dest"] = destFilter.value?.ifEmpty { null }
baseParams["opId"] = opUserFilter.value?.ifEmpty { null }
// 入库日期起/止为后端预留参数(暂无对应入参,静默忽略,见接口问题清单)
baseParams["opDateStart"] = checkInDateStart.value?.ifEmpty { null }
baseParams["opDateEnd"] = checkInDateEnd.value?.ifEmpty { null }
} }
// 构建查询参数(列表接口) // 构建查询参数(列表接口)

View File

@@ -72,17 +72,28 @@ class GjcWeighingStartViewModel : BaseViewModel() {
val spCodeList = MutableLiveData<List<KeyValue>>() // 特码列表 val spCodeList = MutableLiveData<List<KeyValue>>() // 特码列表
val businessTypeList = MutableLiveData<List<KeyValue>>() // 业务类型列表 val businessTypeList = MutableLiveData<List<KeyValue>>() // 业务类型列表
// 托盘车自重(平板:托盘车号查询回填只读;手机:手输,见下方 initPhoneExtras) // 托盘车自重(平板:托盘车号查询回填只读)
val carWeight = MutableLiveData("0") val carWeight = MutableLiveData("0")
// ========== 5.5寸手机版专属(平板不调用 initPhoneExtras,行为零变化) ========== // ========== 5.5寸手机版专属(平板不调用 initPhoneExtras,行为零变化) ==========
/** 托盘数量(设计稿字段,后端暂无对应入参,提交时传 palletNumber 预留参数,见接口问题清单) */ // 托盘信息(与平板端一致:托盘重量 trayTotalWeight = 托盘数量 × 托盘自重,自重默认 18kg 可编辑)
val palletNumber = MutableLiveData("") /** 托盘数量 → 入参 trayNumber */
val trayNumber = MutableLiveData("")
/** 手机形态标记:为 true 时提交体额外携带 palletNumber / carWeight(后端静默忽略) */ /** 单个托盘自重(默认 18kg)→ 与数量相乘得入参 trayTotalWeight */
val trayWeight = MutableLiveData("18")
/** 手机形态标记:为 true 时提交体携带 trayNumber / trayTotalWeight(平板保持原托盘车号链路) */
private var phoneMode = false private var phoneMode = false
/** 托盘重量 = 托盘数量 × 托盘自重 */
private fun getTrayTotalWeight(): Double {
val num = trayNumber.value?.toIntOrNull() ?: 0
val weight = trayWeight.value?.toDoubleOrNull() ?: 0.0
return num * weight
}
/** 仅由手机形态的 Activity 调用 */ /** 仅由手机形态的 Activity 调用 */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneMode = true phoneMode = true
@@ -406,10 +417,10 @@ class GjcWeighingStartViewModel : BaseViewModel() {
remark = pageRemark.value remark = pageRemark.value
// 手机版专属:托盘数量/托盘自重(后端暂无对应入参,静默忽略;平板 phoneMode 恒 false,字段为 null 不进请求体) // 手机版:托盘数量/托盘重量(平板 phoneMode 恒 false,字段为 null 不进请求体)
if (this@GjcWeighingStartViewModel.phoneMode) { if (this@GjcWeighingStartViewModel.phoneMode) {
palletNumber = this@GjcWeighingStartViewModel.palletNumber.value?.ifEmpty { null } trayNumber = this@GjcWeighingStartViewModel.trayNumber.value?.toIntOrNull()
carWeight = this@GjcWeighingStartViewModel.carWeight.value?.ifEmpty { null } trayTotalWeight = getTrayTotalWeight()
} }
} }
@@ -442,10 +453,12 @@ class GjcWeighingStartViewModel : BaseViewModel() {
maWbBean.value = maWbBean.value?.apply { maWbBean.value = maWbBean.value?.apply {
carId = null carId = null
remark = null remark = null
palletNumber = null trayNumber = null
trayTotalWeight = null
} }
carWeight.value = "0" carWeight.value = "0"
this@GjcWeighingStartViewModel.palletNumber.value = "" this@GjcWeighingStartViewModel.trayNumber.value = ""
trayWeight.value = "18"
} else { } else {
showToast(result.msg.noNull("分托计重失败")) showToast(result.msg.noNull("分托计重失败"))
} }
@@ -524,9 +537,9 @@ class GjcWeighingStartViewModel : BaseViewModel() {
"checkIn" to "1", // 收运状态设置为已收运 "checkIn" to "1", // 收运状态设置为已收运
"passageWayId" to channel.value, // 通道号参数 "passageWayId" to channel.value, // 通道号参数
"passageWay" to (channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: ""), "passageWay" to (channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: ""),
// 手机版专属预留参数(平板 phoneMode 恒 false → null → removeEmptyOrNull 移除,请求体不变) // 手机版托盘信息(平板 phoneMode 恒 false → null → removeEmptyOrNull 移除,请求体不变)
"palletNumber" to (if (phoneMode) palletNumber.value?.ifEmpty { null } else null), "trayNumber" to (if (phoneMode) trayNumber.value?.toIntOrNull() else null),
"carWeight" to (if (phoneMode) carWeight.value?.ifEmpty { null } else null) "trayTotalWeight" to (if (phoneMode) getTrayTotalWeight() else null)
).toRequestBody(removeEmptyOrNull = true) ).toRequestBody(removeEmptyOrNull = true)
launchLoadingCollect({ launchLoadingCollect({

View File

@@ -61,33 +61,23 @@ class GjcWeighingViewModel : BasePageViewModel() {
/** 手机版顶部搜索关键字(运单号/航班号混输,getData 内分流:含字母→fno,纯数字→likeNo 模糊) */ /** 手机版顶部搜索关键字(运单号/航班号混输,getData 内分流:含字母→fno,纯数字→likeNo 模糊) */
val searchKey = MutableLiveData("") val searchKey = MutableLiveData("")
/** 主页三格统计:待计重(checkIn=0)/ 计重中(checkIn=2)数量 */ /** 主页三格统计:待计重 / 计重中数量(取 pageQueryTotal 出参 waitCheckInNumber / checkingNumber) */
val pendingCount = MutableLiveData("0") val pendingCount = MutableLiveData("0")
val weighingCount = MutableLiveData("0") val weighingCount = MutableLiveData("0")
/** 手机版筛选弹层字段。通道号/承运人/运单类型后端暂无查询入参(见接口问题清单),传预留参数名 */ // 手机版筛选弹层与平板端查询参数保持一致(后端确认):航班日期 / 代理人 / 特码,
val channelFilter = MutableLiveData("") // 通道号 → passageWayId // 运单号与航班号走顶部搜索框,均复用上方既有字段
val carrierFilter = MutableLiveData("") // 承运人 → by0
val awbTypeFilter = MutableLiveData("") // 运单类型 → awbType
val channelList = MutableLiveData<List<KeyValue>>(emptyList())
val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList())
/** 由手机形态的 Activity 置 true,平板保持 false(不产生任何额外请求与参数) */ /** 由手机形态的 Activity 置 true,平板保持 false(不产生任何额外请求与参数) */
private var phoneExtrasEnabled = false private var phoneExtrasEnabled = false
/** /**
* 开启手机版专属数据:放开「今天」默认日期(设计稿主页无日期筛选)、加载筛选弹层字典。 * 开启手机版专属数据:放开「今天」默认日期(设计稿主页默认查全量,日期在筛选弹层可选)。
* 仅由手机形态的 Activity 调用。 * 仅由手机形态的 Activity 调用。
*/ */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneExtrasEnabled = true phoneExtrasEnabled = true
flightDate.value = "" flightDate.value = ""
DictUtils.getGjcChannelList { list ->
channelList.postValue(listOf(KeyValue("全部", "")) + list)
}
DictUtils.getWaybillTypeList2(ieFlag = "E", type = "IO") {
awbTypeList.postValue(it)
}
} }
/** /**
@@ -271,10 +261,6 @@ class GjcWeighingViewModel : BasePageViewModel() {
else -> baseParams["likeNo"] = key else -> baseParams["likeNo"] = key
} }
} }
// 以下三个为后端预留参数(暂无对应入参,后端静默忽略,见接口问题清单)
baseParams["passageWayId"] = channelFilter.value?.ifEmpty { null }
baseParams["by0"] = carrierFilter.value?.ifEmpty { null }
baseParams["awbType"] = awbTypeFilter.value?.ifEmpty { null }
} }
// 构建查询参数(列表接口) // 构建查询参数(列表接口)
@@ -304,21 +290,9 @@ class GjcWeighingViewModel : BasePageViewModel() {
totalCount.value = (data?.wbNumber ?: 0).toString() totalCount.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString() totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString() totalWeight.value = (data?.totalWeight ?: 0.0).toString()
} // 手机版三格统计:后端统计出参直接给出分项计数,无需额外请求
} pendingCount.value = (data?.waitCheckInNumber ?: 0).toString()
weighingCount.value = (data?.checkingNumber ?: 0).toString()
// 手机版三格统计:待计重 / 计重中(各带一次 checkIn 条件的统计请求,仅手机调用)
if (phoneExtrasEnabled) {
listOf("0" to pendingCount, "2" to weighingCount).forEach { (state, liveData) ->
launchCollect({
NetApply.api.getGjcWeighingStatistics(
(baseParams + mapOf("checkIn" to state)).toRequestBody()
)
}) {
onSuccess = { result ->
liveData.value = (result.data?.wbNumber ?: 0).toString()
}
}
} }
} }
} }

View File

@@ -1,119 +0,0 @@
package com.lukouguoji.gjc.viewModel
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.module_base.base.BaseViewModel
import com.lukouguoji.module_base.bean.GjcHaWb
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue
/**
* 国际出港运抵-补充信息 ViewModel(5.5寸手机版专属新页)
*
* 参照 Pad 端国际进港 [com.lukouguoji.gjj.viewModel.IntArrSupplementInfoViewModel] 的
* 「单一表单批量复制到选中列表」模式:对该运单的整个 haWbList 批量应用同一份收发货人信息。
*
* ⚠️ 后端暂无保存接口,见 [com.lukouguoji.module_base.http.net.Api.supplementArriveHaWb] 注释
* 与 documents/后端接口对接问题清单.xlsx。
*/
class IntExpArriveSupplementViewModel : BaseViewModel() {
// 表单编辑用数据对象(取首个分单填充,让用户看到已有数据)
val dataBean = MutableLiveData(GjcHaWb())
// 批量应用的目标分单列表
private var haWbList: List<GjcHaWb> = emptyList()
// 国家代码下拉列表
val countryCodeList = MutableLiveData<List<KeyValue>>(emptyList())
// 通讯方式下拉列表
val comTypeList = MutableLiveData<List<KeyValue>>(emptyList())
@Suppress("UNCHECKED_CAST")
fun initOnCreated(intent: Intent) {
val list = intent.getSerializableExtra(Constant.Key.DATA) as? ArrayList<GjcHaWb>
// .copy() 补上被 Java 序列化跳过的 @Transient 字段(checked 等),见分单明细页同款注释
haWbList = (list ?: emptyList()).map { it.copy() }
dataBean.value = haWbList.firstOrNull()?.copy() ?: GjcHaWb()
loadCountryCodeList()
loadComTypeList()
}
private fun loadCountryCodeList() {
launchCollect({ NetApply.api.getCountryCodeList() }) {
onSuccess = { result ->
countryCodeList.value = result.data?.mapNotNull { bean ->
if (bean.code != null && bean.name != null) KeyValue(bean.name, bean.code) else null
} ?: emptyList()
}
}
}
private fun loadComTypeList() {
launchCollect({ NetApply.api.getCommunicateTypeList() }) {
onSuccess = { result ->
comTypeList.value = result.data?.mapNotNull { bean ->
if (bean.code != null && bean.name != null) KeyValue(bean.name, bean.code) else null
} ?: emptyList()
}
}
}
fun cancel() {
getTopActivity().finish()
}
/**
* 保存按钮点击 - 将表单信息复制到该运单的所有分单,批量提交。
*/
fun save() {
val formBean = dataBean.value ?: return
if (formBean.consigneeName.verifyNullOrEmpty("收货人名称不能为空")) return
if (formBean.consigneeCountryCode.verifyNullOrEmpty("收货人国家代码不能为空")) return
if (formBean.consigneeComType.verifyNullOrEmpty("收货人通讯方式不能为空")) return
if (formBean.consigneePnum.verifyNullOrEmpty("收货人联系号码不能为空")) return
if (formBean.consignorName.verifyNullOrEmpty("发货人名称不能为空")) return
if (formBean.consignorCountryCode.verifyNullOrEmpty("发货人国家代码不能为空")) return
if (formBean.consignorComType.verifyNullOrEmpty("发货人通讯方式不能为空")) return
if (formBean.consignorPnum.verifyNullOrEmpty("发货人联系号码不能为空")) return
if (haWbList.isEmpty()) {
showToast("无可保存的分单数据")
return
}
val updatedList = haWbList.map { haWb ->
haWb.copy(
consigneeName = formBean.consigneeName,
consigneeCountryCode = formBean.consigneeCountryCode,
consigneeComType = formBean.consigneeComType,
consigneePnum = formBean.consigneePnum,
consigneeAddress = formBean.consigneeAddress,
consignorName = formBean.consignorName,
consignorCountryCode = formBean.consignorCountryCode,
consignorComType = formBean.consignorComType,
consignorPnum = formBean.consignorPnum,
consignorAddress = formBean.consignorAddress
)
}
launchLoadingCollect({ NetApply.api.supplementArriveHaWb(updatedList.toRequestBody()) }) {
onSuccess = {
showToast("保存成功")
getTopActivity().finish()
}
onFailed = { code, msg ->
showToast("保存失败:$msg(后端接口暂未支持,见接口问题清单)")
}
}
}
}

View File

@@ -56,7 +56,7 @@ class IntExpArriveViewModel : BasePageViewModel() {
// ========== 5.5寸手机版专属(平板布局不引用,零影响) ========== // ========== 5.5寸手机版专属(平板布局不引用,零影响) ==========
/** 当前 Tab:"pending"(待运抵) / "shipped"(已运抵)。接口无申报状态查询参数,Tab 由客户端对已加载数据拆分 */ /** 当前 Tab:"pending"(待运抵) / "shipped"(已运抵)。查询时映射为入参 arrivalStatus=0/1(与平板端运抵状态筛选一致) */
val currentTab = MutableLiveData("pending") val currentTab = MutableLiveData("pending")
/** Tab 配置 */ /** Tab 配置 */
@@ -64,7 +64,7 @@ class IntExpArriveViewModel : BasePageViewModel() {
listOf(KeyValue("待运抵", "pending"), KeyValue("已运抵", "shipped")) listOf(KeyValue("待运抵", "pending"), KeyValue("已运抵", "shipped"))
) )
/** 两个 Tab 的角标计数(当前已加载数据的客户端统计,非全量,见接口问题清单) */ /** 两个 Tab 的角标计数(同条件 + arrivalStatus=0/1 各调一次 pageQueryTotal 取 wbNumber,全量口径) */
val arriveTabCounts = MutableLiveData(listOf("0", "0")) val arriveTabCounts = MutableLiveData(listOf("0", "0"))
/** 手机版空态(当前 Tab 过滤后无数据时显示,对应设计稿 empty-pending) */ /** 手机版空态(当前 Tab 过滤后无数据时显示,对应设计稿 empty-pending) */
@@ -77,61 +77,51 @@ class IntExpArriveViewModel : BasePageViewModel() {
/** 手机版已选数量(底部操作条「已选 N 项」) */ /** 手机版已选数量(底部操作条「已选 N 项」) */
val selectedCountPhone = MutableLiveData("0") val selectedCountPhone = MutableLiveData("0")
/** 手机版筛选弹层「代理」(后端 pageQuery 支持 agentCode 入参,复用既有 agentCode 语义查询) */
val agentCodePhone = MutableLiveData("")
val agentListPhone = MutableLiveData<List<KeyValue>>(emptyList())
private var phoneExtrasEnabled = false private var phoneExtrasEnabled = false
/** /**
* 完整已加载列表(跨分页累计,未按 Tab 过滤)。 * 开启手机版专属数据:Tab(arrivalStatus)与角标统计。仅由手机形态的 Activity 调用。
* * 筛选弹层与平板端查询参数一致(航班日期/航班号),agentCode 内网实测被后端忽略,已移除「代理」。
* ⚠️ RecyclerView/LinearLayoutManager 不会像普通 ViewGroup 那样让 `View.GONE` 的子项
* 自动塌陷为 0 高度——它仍按子项的测量高度参与滚动区域计算,导致"隐藏"的卡片在列表里
* 留下一段空白(已实机截图验证)。因此 Tab 切换不能靠给 item 加 visibility 绑定,
* 必须真正地把适配器的数据集替换成过滤后的子集([applyTabFilterPhone])。
*/
private var allLoadedList: List<GjcMaWb> = emptyList()
/**
* 开启手机版专属数据:加载「代理」筛选下拉。仅由手机形态的 Activity 调用。
*/ */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneExtrasEnabled = true phoneExtrasEnabled = true
launchCollect({ NetApply.api.getIntExpAgentList() }) {
onSuccess = { result ->
agentListPhone.postValue(
listOf(KeyValue("全部", "")) + (result.data ?: emptyList()).map { KeyValue(it.name ?: "", it.code ?: "") }
)
}
}
} }
/** /**
* 手机版 Tab 切换:将适配器数据集替换为过滤后的子集(不重新请求)。 * 手机版 Tab 切换:按 arrivalStatus 重新查询(服务端过滤,分页与角标均为全量口径)。
* 由 Activity 在 [com.lukouguoji.module_base.ui.weight.phone.PhoneStatusTab] 的 * 由 Activity 在 [com.lukouguoji.module_base.ui.weight.phone.PhoneStatusTab] 的
* 双向绑定写入 currentTab 后调用。 * 双向绑定写入 currentTab 后调用。
*/ */
fun onTabChangedPhone() { fun onTabChangedPhone() {
applyTabFilterPhone() refresh()
} }
private fun applyTabFilterPhone() { /** 当前 Tab 对应的运抵状态入参:待运抵=0 / 已运抵=1 */
val isPendingTab = currentTab.value == "pending" private fun currentArrivalStatus(): String =
val filtered = allLoadedList.filter { it.isPending == isPendingTab } if (currentTab.value == "shipped") "1" else "0"
pageModel.rv?.commonAdapter()?.refresh(filtered)
emptyVisiblePhone.value = filtered.isEmpty() /** 列表加载完成后刷新空态文案(服务端已按 Tab 过滤,adapter 即当前 Tab 数据) */
private fun updateEmptyStatePhone() {
val isPendingTab = currentTab.value != "shipped"
emptyVisiblePhone.value = pageModel.rv?.commonAdapter()?.items.isNullOrEmpty()
emptyTextPhone.value = if (isPendingTab) "暂无待运抵的运单" else "暂无已运抵的运单" emptyTextPhone.value = if (isPendingTab) "暂无待运抵的运单" else "暂无已运抵的运单"
updateCheckAllStatusPhone()
} }
/** /**
* 根据完整已加载列表重新计算 Tab 角标计数(每次数据变化后调用)。 * 加载 Tab 角标:同条件 + arrivalStatus=0/1 各调一次 pageQueryTotal,取 wbNumber。
*/ */
private fun updatePhoneTabCounts() { private fun loadPhoneTabCounts(filterParams: Map<String, Any?>) {
val pending = allLoadedList.count { it.isPending }.toString() listOf("0", "1").forEachIndexed { index, status ->
val shipped = allLoadedList.count { !it.isPending }.toString() launchCollect({
arriveTabCounts.value = listOf(pending, shipped) NetApply.api.getIntExpArriveTotal((filterParams + mapOf("arrivalStatus" to status)).toRequestBody())
}) {
onSuccess = { result ->
val current = arriveTabCounts.value?.toMutableList() ?: mutableListOf("0", "0")
current[index] = (result.data?.wbNumber ?: 0).toString()
arriveTabCounts.value = current
}
}
}
} }
/** /**
@@ -439,33 +429,30 @@ class IntExpArriveViewModel : BasePageViewModel() {
filterParams["wbNo"] = null filterParams["wbNo"] = null
filterParams["likeNo"] = key filterParams["likeNo"] = key
} }
filterParams["agentCode"] = agentCodePhone.value?.ifEmpty { null }
} }
// 手机版 Tab 维度(运抵状态),角标需在不含该条件的基础上分别统计
val tabParams = if (phoneExtrasEnabled) {
filterParams + mapOf("arrivalStatus" to currentArrivalStatus())
} else filterParams
// 列表参数 (含分页) // 列表参数 (含分页)
val listParams = (filterParams + mapOf( val listParams = (tabParams + mapOf(
"pageNum" to pageModel.page, "pageNum" to pageModel.page,
"pageSize" to pageModel.limit "pageSize" to pageModel.limit
)).toRequestBody() )).toRequestBody()
// 统计参数 (无分页) // 统计参数 (无分页)
val totalParams = filterParams.toRequestBody() val totalParams = tabParams.toRequestBody()
// 获取列表 (带Loading) // 获取列表 (带Loading)
launchLoadingCollect({ NetApply.api.getIntExpArriveList(listParams) }) { launchLoadingCollect({ NetApply.api.getIntExpArriveList(listParams) }) {
onSuccess = { result -> onSuccess = { result ->
// 手机版:自行维护完整累计列表(不依赖 adapter.items 的历史状态,见 allLoadedList 注释)
if (phoneExtrasEnabled) {
val newPageItems = result.list ?: emptyList()
allLoadedList = if (pageModel.page <= 1) newPageItems else allLoadedList + newPageItems
}
pageModel.handleListBean(result) { pageModel.handleListBean(result) {
updateCheckAllStatus() updateCheckAllStatus()
if (phoneExtrasEnabled) { if (phoneExtrasEnabled) {
// 用完整列表强制覆盖 adapter 数据集为当前 Tab 的过滤子集 updateEmptyStatePhone()
applyTabFilterPhone() updateCheckAllStatusPhone()
updatePhoneTabCounts()
} }
} }
// 数据加载完成后,重置全局展开状态为收起 // 数据加载完成后,重置全局展开状态为收起
@@ -482,5 +469,10 @@ class IntExpArriveViewModel : BasePageViewModel() {
totalWeight.value = (data?.totalWeight ?: 0.0).toString() totalWeight.value = (data?.totalWeight ?: 0.0).toString()
} }
} }
// 手机版 Tab 角标(平板不触发)
if (phoneExtrasEnabled) {
loadPhoneTabCounts(filterParams)
}
} }
} }

View File

@@ -18,7 +18,6 @@ import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.model.ScanModel import com.lukouguoji.module_base.model.ScanModel
import com.lukouguoji.module_base.util.DictUtils
import dev.utils.app.info.KeyValue import dev.utils.app.info.KeyValue
import dev.utils.common.DateUtils import dev.utils.common.DateUtils
import com.lukouguoji.module_base.ktx.formatDate import com.lukouguoji.module_base.ktx.formatDate
@@ -36,12 +35,8 @@ class IntExpOutHandoverViewModel : BasePageViewModel() {
val uldNo = MutableLiveData("") // ULD编号 val uldNo = MutableLiveData("") // ULD编号
// ========== 手机版(5.5 寸手持端)专用筛选条件 ========== // ========== 手机版(5.5 寸手持端)专用筛选条件 ==========
// 平板布局不含对应控件,两者保持空值 → 不会进入请求参数,平板查询行为与改造前完全一致 // 平板布局不含对应控件,保持空值 → 不会进入请求参数,平板查询行为与改造前完全一致
val handoverState = MutableLiveData("") // 交接状态:"" 全部 / "0" 待交接 / "1" 已交接 val handoverState = MutableLiveData("") // 交接状态:"" 全部 / "0" 待交接 / "1" 已交接
val hoId = MutableLiveData("") // 交接人(仓管编码)
/** 交接人下拉数据(仅手机版筛选弹层使用,由 [initPhoneExtras] 触发加载) */
val hoUserList = MutableLiveData<List<KeyValue>>(emptyList())
/** 手机版状态 Tab 配置 */ /** 手机版状态 Tab 配置 */
val handoverStateTabs = MutableLiveData( val handoverStateTabs = MutableLiveData(
@@ -52,7 +47,7 @@ class IntExpOutHandoverViewModel : BasePageViewModel() {
val handoverStateCounts = MutableLiveData(listOf("", "")) val handoverStateCounts = MutableLiveData(listOf("", ""))
/** /**
* 是否启用手机版专属数据(Tab 角标计数、交接人下拉)。 * 是否启用手机版专属数据(Tab 角标计数)。
* *
* 由 Activity 在手机形态下置 true。平板端保持 false, * 由 Activity 在手机形态下置 true。平板端保持 false,
* 因此不会产生任何额外接口请求,平板行为零变化。 * 因此不会产生任何额外接口请求,平板行为零变化。
@@ -70,15 +65,12 @@ class IntExpOutHandoverViewModel : BasePageViewModel() {
val selectedCount = MutableLiveData("0") val selectedCount = MutableLiveData("0")
/** /**
* 开启手机版专属数据:默认停留「待交接」Tab、加载交接人下拉、启用 Tab 角标计数。 * 开启手机版专属数据:默认停留「待交接」Tab、启用 Tab 角标计数。
* 仅由手机形态的 Activity 调用。 * 仅由手机形态的 Activity 调用。
*/ */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneExtrasEnabled = true phoneExtrasEnabled = true
handoverState.value = "0" handoverState.value = "0"
DictUtils.getWHSUserList(addAll = false) { list ->
hoUserList.postValue(list)
}
} }
fun onItemCheckChanged() { fun onItemCheckChanged() {
@@ -218,11 +210,10 @@ class IntExpOutHandoverViewModel : BasePageViewModel() {
"fno" to flightNo.value?.ifEmpty { null }, "fno" to flightNo.value?.ifEmpty { null },
"fdest" to fdest.value?.ifEmpty { null }, "fdest" to fdest.value?.ifEmpty { null },
"uld" to uldNo.value?.ifEmpty { null }, "uld" to uldNo.value?.ifEmpty { null },
// 以下两项为手机版新增筛选维度,平板端保持空值故不参与查询 // 手机版 Tab 维度,平板端保持空值故不参与查询
// 参数名以 api-doc 为准:交接状态为 handoverState(integer,0 未交接 / 1 已交接)。 // 参数名以 api-doc 为准:交接状态为 handoverState(integer,0 未交接 / 1 已交接)。
// hoId(交接人)后端查询入参暂不支持,已记录到 documents/后端接口对接问题清单.xlsx // 其余筛选与平板端口径一致(fdate/fno/fdest/uld),后端不提供交接人查询入参
"handoverState" to handoverState.value?.ifEmpty { null }, "handoverState" to handoverState.value?.ifEmpty { null }
"hoId" to hoId.value?.ifEmpty { null }
) )
// 列表参数 (含分页) // 列表参数 (含分页)
@@ -257,15 +248,15 @@ class IntExpOutHandoverViewModel : BasePageViewModel() {
/** /**
* 加载「待交接 / 已交接」Tab 角标数量。 * 加载「待交接 / 已交接」Tab 角标数量。
* *
* 复用统计接口,分别以 handoverState=0 / 1 各查一次,且沿用当前其余筛选条件, * 复用统计接口,分别以 handoverState=0 / 1 各查一次,且沿用当前其余筛选条件;
* 保证角标与列表口径一致。仅手机版调用。 * 角标取 uldNumber(板箱数,与列表 pageQuery 的 total 同口径)。仅手机版调用。
*/ */
private fun loadTabCounts(filterParams: Map<String, Any?>) { private fun loadTabCounts(filterParams: Map<String, Any?>) {
listOf("0", "1").forEachIndexed { index, state -> listOf("0", "1").forEachIndexed { index, state ->
val params = (filterParams + mapOf("handoverState" to state)).toRequestBody() val params = (filterParams + mapOf("handoverState" to state)).toRequestBody()
launchCollect({ NetApply.api.getIntExpOutHandoverTotal(params) }) { launchCollect({ NetApply.api.getIntExpOutHandoverTotal(params) }) {
onSuccess = { result -> onSuccess = { result ->
val count = (result.data?.wbNumber ?: 0).toString() val count = (result.data?.uldNumber ?: 0).toString()
val current = handoverStateCounts.value?.toMutableList() val current = handoverStateCounts.value?.toMutableList()
?: mutableListOf("", "") ?: mutableListOf("", "")
if (index < current.size) { if (index < current.size) {

View File

@@ -17,7 +17,6 @@ import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.model.ScanModel import com.lukouguoji.module_base.model.ScanModel
import com.lukouguoji.module_base.util.DictUtils
import dev.utils.app.info.KeyValue import dev.utils.app.info.KeyValue
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -43,28 +42,11 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
// 筛选弹层显隐 // 筛选弹层显隐
val filterVisible = MutableLiveData(false) val filterVisible = MutableLiveData(false)
// 筛选条件(设计稿弹层 8 项中平板没有的 6 项;平板布局无对应控件 → 恒为空 → 不进请求) // 筛选弹层与平板端查询参数保持一致(后端确认):航班日期 / 航班号 / 库位 / 清仓结果,运单号走顶部搜索框。
val agentId = MutableLiveData("") // 代理(agentCode) // 代理/特码/目的站/运单类型/业务类型/品名 后端均不支持(内网实测 agentCode/spCode/dest 亦被忽略),已移除
val spCode = MutableLiveData("") // 特码
val dest = MutableLiveData("") // 目的站
val awbType = MutableLiveData("") // 运单类型(pageQuery 无此入参,预留,见接口问题清单)
val businessType = MutableLiveData("") // 业务类型(同上预留)
val goodsCn = MutableLiveData("") // 品名(中)(同上预留)
// 筛选下拉字典(initPhoneExtras 加载,平板不请求) // 三格统计:未清仓 / 已清仓(总票数复用 totalWbNumber),直接取 pageQueryTotal 出参
val agentList = MutableLiveData(listOf(KeyValue("全部", ""))) // notClearNumber / clearNumber(与总票数同一次请求、同条件)
val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList())
val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList())
val businessTypeList = MutableLiveData(
listOf(
KeyValue("全部", ""),
KeyValue("普货", "1"),
KeyValue("特货", "2")
)
)
// 三格统计:未清仓 / 已清仓(总票数复用 totalWbNumber,服务端全量口径)。
// 复用 pageQueryTotal 按 clearNormal=0/1 各查一次(入参 schema 含 clearNormal)。
val unclearedCount = MutableLiveData("0") val unclearedCount = MutableLiveData("0")
val clearedCount = MutableLiveData("0") val clearedCount = MutableLiveData("0")
@@ -76,22 +58,6 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
*/ */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneExtrasEnabled = true phoneExtrasEnabled = true
// 代理(与 06 出港查询同源接口)
launchCollect({ NetApply.api.getIntExpAgentList() }) {
onSuccess = { result ->
val list = mutableListOf(KeyValue("全部", ""))
result.data?.forEach { list.add(KeyValue(it.name ?: "", it.code ?: "")) }
agentList.value = list
}
}
// 特码:flag=1 国际;ieFlag 必须留空——传 "E" 后端返回空数组
DictUtils.getSpecialCodeList(addAll = false, flag = 1, ieFlag = "") { list ->
specialCodeList.postValue(listOf(KeyValue("全部", "")) + list)
}
// 运单类型(type=IO 国际出港)
DictUtils.getWaybillTypeList(type = Constant.businessType.IO, addAll = true) {
awbTypeList.postValue(it)
}
} }
/** 筛选按钮点击 - 打开筛选面板 */ /** 筛选按钮点击 - 打开筛选面板 */
@@ -108,12 +74,8 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
fun resetFilter() { fun resetFilter() {
flightDate.value = "" flightDate.value = ""
flightNo.value = "" flightNo.value = ""
agentId.value = "" location.value = ""
spCode.value = "" clearResult.value = ""
dest.value = ""
awbType.value = ""
businessType.value = ""
goodsCn.value = ""
} }
/** 确认筛选 - 关闭面板并刷新 */ /** 确认筛选 - 关闭面板并刷新 */
@@ -385,20 +347,13 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
* 获取数据 (重写BasePageViewModel) * 获取数据 (重写BasePageViewModel)
*/ */
override fun getData() { override fun getData() {
// 构建搜索条件(手机专属 6 项平板恒为空,不进请求;awbType/businessType/goodsCn // 构建搜索条件(与平板端一致)
// 为 pageQuery 未定义入参,后端静默忽略,补齐后自动生效,见接口问题清单)
val filterParams = mapOf( val filterParams = mapOf(
"fdate" to flightDate.value?.ifEmpty { null }, "fdate" to flightDate.value?.ifEmpty { null },
"fno" to flightNo.value?.ifEmpty { null }, "fno" to flightNo.value?.ifEmpty { null },
"wbNo" to wbNo.value?.ifEmpty { null }, "wbNo" to wbNo.value?.ifEmpty { null },
"location" to location.value?.ifEmpty { null }, "location" to location.value?.ifEmpty { null },
"clearNormal" to clearResult.value?.ifEmpty { null }, "clearNormal" to clearResult.value?.ifEmpty { null }
"agentCode" to agentId.value?.ifEmpty { null },
"spCode" to spCode.value?.ifEmpty { null },
"dest" to dest.value?.ifEmpty { null },
"awbType" to awbType.value?.ifEmpty { null },
"businessType" to businessType.value?.ifEmpty { null },
"goodsCn" to goodsCn.value?.ifEmpty { null }
) )
// 列表参数 (含分页) // 列表参数 (含分页)
@@ -428,18 +383,9 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
totalWbNumber.value = (data?.wbNumber ?: 0).toString() totalWbNumber.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString() totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString() totalWeight.value = (data?.totalWeight ?: 0.0).toString()
} // 手机三格统计分项(平板布局不绑定,无影响)
} unclearedCount.value = (data?.notClearNumber ?: 0).toString()
clearedCount.value = (data?.clearNumber ?: 0).toString()
// 手机专属:未清仓/已清仓分项统计(同条件 + clearNormal=0/1 各查一次,平板不发)
if (phoneExtrasEnabled) {
val unclearedParams = (filterParams + mapOf("clearNormal" to "0")).toRequestBody()
launchCollect({ NetApply.api.getIntExpStorageUseTotal(unclearedParams) }) {
onSuccess = { unclearedCount.value = (it.data?.wbNumber ?: 0).toString() }
}
val clearedParams = (filterParams + mapOf("clearNormal" to "1")).toRequestBody()
launchCollect({ NetApply.api.getIntExpStorageUseTotal(clearedParams) }) {
onSuccess = { clearedCount.value = (it.data?.wbNumber ?: 0).toString() }
} }
} }
} }

View File

@@ -239,21 +239,31 @@
setOnResetClick="@{(v)-> viewModel.resetFilter()}" setOnResetClick="@{(v)-> viewModel.resetFilter()}"
title='@{"筛选条件"}'> title='@{"筛选条件"}'>
<!-- 航班日期起(平板搜索行字段,手机移入弹层) --> <!-- 日期类型(dateState:入库/离港,与主干平板一致;后端起止日期仅在携带它时生效) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择航班日期"}' hint='@{"请选择日期类型"}'
title='@{"航班日期起"}' list="@{viewModel.dateStateList}"
title='@{"日期类型"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.dateState}" />
<!-- 开始日期(平板搜索行字段,手机移入弹层) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择开始日期"}'
title='@{"开始日期"}'
type="@{PhoneDataLayoutType.DATE}" type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.flightDateStart}" /> value="@={viewModel.flightDateStart}" />
<!-- 航班日期止 --> <!-- 结束日期 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择航班日期"}' hint='@{"请选择结束日期"}'
title='@{"航班日期止"}' title='@{"结束日期"}'
type="@{PhoneDataLayoutType.DATE}" type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.flightDateEnd}" /> value="@={viewModel.flightDateEnd}" />

View File

@@ -255,34 +255,16 @@
setOnResetClick="@{(v)-> activity.resetFilter()}" setOnResetClick="@{(v)-> activity.resetFilter()}"
title='@{"筛选条件"}'> title='@{"筛选条件"}'>
<!-- 通道号(后端暂无查询入参,预留,见接口问题清单) --> <!-- 筛选项与平板端查询参数保持一致:fdate / agentCode / spCode(运单号、航班号走顶部搜索框) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择通道号"}'
list="@{viewModel.channelList}"
title='@{"通道号"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.channelFilter}" />
<!-- 承运人(后端暂无查询入参,预留,见接口问题清单) --> <!-- 航班日期(复用既有 flightDate,手机默认空 = 全部) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请输入承运人"}' hint='@{"请选择航班日期"}'
title='@{"承运人"}' title='@{"航班日期"}'
type="@{PhoneDataLayoutType.INPUT}" type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.carrierFilter}" /> value="@={viewModel.flightDate}" />
<!-- 运单类型(后端暂无查询入参,预留,见接口问题清单) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择运单类型"}'
list="@{viewModel.awbTypeList}"
title='@{"运单类型"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.awbTypeFilter}" />
<!-- 代理人(复用既有 agentCode 查询字段) --> <!-- 代理人(复用既有 agentCode 查询字段) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
@@ -294,6 +276,16 @@
type="@{PhoneDataLayoutType.SPINNER}" type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.agentCode}" /> value="@={viewModel.agentCode}" />
<!-- 特码(复用既有 spCode 查询字段) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择特码"}'
list="@{viewModel.spCodeList}"
title='@{"特码"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.spCode}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel> </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout> </FrameLayout>

View File

@@ -6,7 +6,8 @@
变量 viewModel / activity / editRecord 与平板一致;保留 Activity 中引用的控件 id:recyclerView。 变量 viewModel / activity / editRecord 与平板一致;保留 Activity 中引用的控件 id:recyclerView。
交互差异(业务逻辑全部复用 GjcWeighingRecordDetailsViewModel): 交互差异(业务逻辑全部复用 GjcWeighingRecordDetailsViewModel):
· 顶部卡片:运单号 + 总件数/总重量/托盘总重(totalCarWeight 为手机专属统计字段) · 顶部卡片:运单号 + 总件数/总重量/托盘总重(totalCarWeight = 各记录 trayTotalWeight 之和,手机专属统计字段)
· 托盘自重绑 trayTotalWeight(与平板端一致,后端 listRecordByWh / updateRecordList 均支持)
· 每条记录卡片带「修改」→ 全屏「修改记录」覆层(isEditMode 驱动显隐, · 每条记录卡片带「修改」→ 全屏「修改记录」覆层(isEditMode 驱动显隐,
平板端该状态驱动列表内联编辑,互不影响);保存走 onPhoneSaveClick(成功后留在本页) 平板端该状态驱动列表内联编辑,互不影响);保存走 onPhoneSaveClick(成功后留在本页)
--> -->
@@ -217,15 +218,15 @@
required="@{true}" required="@{true}"
title='@{"托盘自重(kg)"}' title='@{"托盘自重(kg)"}'
type="@{PhoneFormRowType.INPUT}" type="@{PhoneFormRowType.INPUT}"
value="@={editRecord.carWeightStr}" /> value="@={editRecord.trayTotalWeightStr}" />
<!-- 计重人:接口出参仅有操作员编码(无姓名,后端缺口见接口问题清单) --> <!-- 计重人:opUserName 姓名,为空回退 opId -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
title='@{"计重人"}' title='@{"计重人"}'
type="@{PhoneFormRowType.TEXT}" type="@{PhoneFormRowType.TEXT}"
value="@{editRecord.opId}" /> value="@{editRecord.opUserDisplay}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -238,22 +238,14 @@
setOnResetClick="@{(v)-> activity.resetFilter()}" setOnResetClick="@{(v)-> activity.resetFilter()}"
title='@{"筛选条件"}'> title='@{"筛选条件"}'>
<!-- 入库日期起/止:后端暂无查询入参,预留(见接口问题清单) --> <!-- 航班日期(与平板端查询参数 fdate 一致;后端不提供入库日期范围入参) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择入库日期"}' hint='@{"请选择航班日期"}'
title='@{"入库日期起"}' title='@{"航班日期"}'
type="@{PhoneDataLayoutType.DATE}" type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.checkInDateStart}" /> value="@={viewModel.flightDate}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择入库日期"}'
title='@{"入库日期止"}'
type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.checkInDateEnd}" />
<!-- 代理人(复用既有 agentCode 查询字段) --> <!-- 代理人(复用既有 agentCode 查询字段) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
@@ -275,25 +267,6 @@
type="@{PhoneDataLayoutType.SPINNER}" type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.spCode}" /> value="@={viewModel.spCode}" />
<!-- 目的站(后端支持入参 dest) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入目的站"}'
title='@{"目的站"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.destFilter}" />
<!-- 计重人(后端支持入参 opId) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"全部"}'
list="@{viewModel.whsUserList}"
title='@{"计重人"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.opUserFilter}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel> </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout> </FrameLayout>

View File

@@ -9,8 +9,8 @@
交互差异(业务逻辑全部复用 GjcWeighingStartViewModel): 交互差异(业务逻辑全部复用 GjcWeighingStartViewModel):
· 手机版从待计重卡片进入(必带 maWbId),故不做运单号输入行 · 手机版从待计重卡片进入(必带 maWbId),故不做运单号输入行
· 平板「地磅称重」输入 → 手机直接编辑「运抵重量」(同一 arriveWeight 字段) · 平板「地磅称重」输入 → 手机直接编辑「运抵重量」(同一 arriveWeight 字段)
· 平板「托盘车号」→ 手机按设计稿为「托盘数量」(palletNumber 预留字段), · 平板「托盘车号」→ 手机按设计稿为「托盘数量」+「托盘自重」(默认 18kg),
「托盘自重」相应改为手输(carWeight),两者后端暂无入参,见接口问题清单 提交 trayNumber / trayTotalWeight(= 数量 × 自重),与平板端托盘口径一致
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
@@ -182,7 +182,7 @@
type="@{PhoneFormRowType.TEXT}" type="@{PhoneFormRowType.TEXT}"
value="@{viewModel.arriveVolume}" /> value="@{viewModel.arriveVolume}" />
<!-- 托盘数量:设计稿字段,后端暂无入参(预留 palletNumber,见接口问题清单) --> <!-- 托盘数量 → trayNumber -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -190,9 +190,9 @@
numeric="@{true}" numeric="@{true}"
title='@{"托盘数量"}' title='@{"托盘数量"}'
type="@{PhoneFormRowType.INPUT}" type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.palletNumber}" /> value="@={viewModel.trayNumber}" />
<!-- 托盘自重:手机版手输(平板为托盘车号查询回填) --> <!-- 托盘自重(单个,默认 18kg)× 数量 → trayTotalWeight -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -200,7 +200,7 @@
numeric="@{true}" numeric="@{true}"
title='@{"托盘自重"}' title='@{"托盘自重"}'
type="@{PhoneFormRowType.INPUT}" type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.carWeight}" /> value="@={viewModel.trayWeight}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -4,8 +4,7 @@
设计稿:documents/5.5寸手持端设计文件/03_出港计重/detail.html 设计稿:documents/5.5寸手持端设计文件/03_出港计重/detail.html
结构:白色卡片「基本信息」 + 两列灰底字段网格(PhoneKvItem 套 bg_phone_stat_box) 结构:白色卡片「基本信息」 + 两列灰底字段网格(PhoneKvItem 套 bg_phone_stat_box)
注:「通道号」在 /IntExpCheckIn/checked/pageQuery 出参中不存在(后端缺口,见接口问题清单), 「通道号」绑 /IntExpCheckIn/checked/pageQuery 出参 passageWay,空值由组件兜底显示 "-"。
此处绑定空串由组件兜底显示 "-"。
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <layout xmlns:android="http://schemas.android.com/apk/res/android">
@@ -196,7 +195,7 @@
title='@{"业务类型"}' title='@{"业务类型"}'
value="@{viewModel.bean.businessName.empty ? viewModel.bean.businessType : viewModel.bean.businessName}" /> value="@{viewModel.bean.businessName.empty ? viewModel.bean.businessType : viewModel.bean.businessName}" />
<!-- 通道号:接口出参无该字段(后端缺口),空值兜底 "-" --> <!-- 通道号:passageWay,空值兜底 "-" -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem <com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -205,7 +204,7 @@
android:background="@drawable/bg_phone_stat_box" android:background="@drawable/bg_phone_stat_box"
android:padding="12dp" android:padding="12dp"
title='@{"通道号"}' title='@{"通道号"}'
value='@{""}' /> value="@{viewModel.bean.passageWay}" />
</LinearLayout> </LinearLayout>

View File

@@ -177,15 +177,6 @@
type="@{com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType.INPUT}" type="@{com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType.INPUT}"
value="@={viewModel.flightNo}" /> value="@={viewModel.flightNo}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择代理"}'
list="@{viewModel.agentListPhone}"
title='@{"代理"}'
type="@{com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType.SPINNER}"
value="@={viewModel.agentCodePhone}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel> </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout> </FrameLayout>

View File

@@ -1,219 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
国际出港运抵-补充信息 —— 5.5 寸手机版专属新页(平板端无对应页面)
参照 Pad 端国际进港「收发货人信息」补充页(IntArrSupplementInfoActivity)的批量应用模式:
对该运单整个 haWbList 批量应用同一份表单。后端暂无保存接口,见 ViewModel 顶部注释与
documents/后端接口对接问题清单.xlsx。对应设计稿 04_出港运抵/supplement.html。
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneFormRowType" />
<variable
name="viewModel"
type="com.lukouguoji.gjc.viewModel.IntExpArriveSupplementViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/phone_page_bg"
android:orientation="vertical">
<include layout="@layout/title_tool_bar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 收货人信息 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_phone_card"
android:orientation="vertical"
android:padding="14dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<View
android:layout_width="4dp"
android:layout_height="16dp"
android:background="@color/phone_primary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="收货人信息"
android:textColor="@color/phone_text_strong"
android:textSize="15sp"
android:textStyle="bold" />
</LinearLayout>
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
hint='@{"请输入收货人名称"}'
required="@{true}"
title='@{"收货人名称"}'
type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.dataBean.consigneeName}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择国家代码"}'
list="@{viewModel.countryCodeList}"
required="@{true}"
title='@{"国家代码"}'
type="@{PhoneFormRowType.SELECT}"
value="@={viewModel.dataBean.consigneeCountryCode}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择"}'
list="@{viewModel.comTypeList}"
required="@{true}"
title='@{"通讯方式"}'
type="@{PhoneFormRowType.SELECT}"
value="@={viewModel.dataBean.consigneeComType}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入联系号码"}'
required="@{true}"
title='@{"联系号码"}'
type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.dataBean.consigneePnum}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入地址"}'
title='@{"地址"}'
type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.dataBean.consigneeAddress}" />
</LinearLayout>
<!-- 发货人信息 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:background="@drawable/bg_phone_card"
android:orientation="vertical"
android:padding="14dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<View
android:layout_width="4dp"
android:layout_height="16dp"
android:background="@color/phone_primary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="发货人信息"
android:textColor="@color/phone_text_strong"
android:textSize="15sp"
android:textStyle="bold" />
</LinearLayout>
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
hint='@{"请输入发货人名称"}'
required="@{true}"
title='@{"发货人名称"}'
type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.dataBean.consignorName}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择国家代码"}'
list="@{viewModel.countryCodeList}"
required="@{true}"
title='@{"国家代码"}'
type="@{PhoneFormRowType.SELECT}"
value="@={viewModel.dataBean.consignorCountryCode}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择"}'
list="@{viewModel.comTypeList}"
required="@{true}"
title='@{"通讯方式"}'
type="@{PhoneFormRowType.SELECT}"
value="@={viewModel.dataBean.consignorComType}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入联系号码"}'
required="@{true}"
title='@{"联系号码"}'
type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.dataBean.consignorPnum}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入地址"}'
title='@{"地址"}'
type="@{PhoneFormRowType.INPUT}"
value="@={viewModel.dataBean.consignorAddress}" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<!-- 确定 -->
<TextView
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_margin="16dp"
android:background="@drawable/bg_phone_btn_primary"
android:gravity="center"
android:onClick="@{()-> viewModel.save()}"
android:text="确定"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</layout>

View File

@@ -7,8 +7,8 @@
· 卡片二:绿色表头「移库信息」+ 移库人 / 移库时间(仅已移库显示) · 卡片二:绿色表头「移库信息」+ 移库人 / 移库时间(仅已移库显示)
字段全部由列表项带入,不额外请求接口。 字段全部由列表项带入,不额外请求接口。
接口 IntExpMove/pageQuery 目前对 businessType / opdate / opId 恒返回 null, 业务类型绑 businessName(中文名称),移库人绑 opUserName(姓名),均回退编码;
按约定占位 "-"(见 GjcMove.orDash),后端补齐后自动显示。 未移库运单 opdate / opId 为空,按约定占位 "-"(见 GjcMove.orDash)。
设计稿里红色标签写的是「国际进港」,但本页属国际出港,判断为设计稿复制粘贴笔误, 设计稿里红色标签写的是「国际进港」,但本页属国际出港,判断为设计稿复制粘贴笔误,
实际绑运单类型 awbTypeName(真实值形如「国际出港(国际直航)」)。 实际绑运单类型 awbTypeName(真实值形如「国际出港(国际直航)」)。
@@ -121,7 +121,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.businessType)}" android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.businessDisplay)}"
android:textColor="@color/phone_text_body" android:textColor="@color/phone_text_body"
android:textSize="14sp" /> android:textSize="14sp" />
@@ -245,7 +245,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
title='@{"移库人"}' title='@{"移库人"}'
value="@{viewModel.dataBean.opId}" /> value="@{viewModel.dataBean.opUserDisplay}" />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem <com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp" android:layout_width="0dp"

View File

@@ -145,15 +145,14 @@
type="@{PhoneDataLayoutType.INPUT}" type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.flightNo}" /> value="@={viewModel.flightNo}" />
<!-- 交接人 --> <!-- 目的站(与平板端查询参数 fdest 一致;后端不提供交接人查询入参) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择交接人"}' hint='@{"请输入目的站"}'
list="@{viewModel.hoUserList}" title='@{"目的站"}'
title='@{"交接人"}' type="@{PhoneDataLayoutType.INPUT}"
type="@{PhoneDataLayoutType.SPINNER}" value="@={viewModel.fdest}" />
value="@={viewModel.hoId}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel> </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>

View File

@@ -11,8 +11,8 @@
· 平板独有的 checkIcon(底部全选)手机版不提供,Binding 基类中自动变 @Nullable · 平板独有的 checkIcon(底部全选)手机版不提供,Binding 基类中自动变 @Nullable
交互差异(均复用既有 IntExpStorageUseViewModel 字段与接口): 交互差异(均复用既有 IntExpStorageUseViewModel 字段与接口):
· 平板 5 个横排搜索条 → 手机顶部搜索框(运单号)+ 底部筛选弹层 8 项 · 平板 5 个横排搜索条 → 手机顶部搜索框(运单号)+ 底部筛选弹层 4 项
(设计稿弹层无「库位号/清仓结果」两项,对应平板字段保持空值不参与请求) (航班日期/航班号/库位/清仓结果,后端确认与平板端查询参数保持一致)
· 三格统计(总票数/未清仓/已清仓),后两格为手机专属统计请求(initPhoneExtras 开启, · 三格统计(总票数/未清仓/已清仓),后两格为手机专属统计请求(initPhoneExtras 开启,
复用 pageQueryTotal 按 clearNormal=0/1 各查一次) 复用 pageQueryTotal 按 clearNormal=0/1 各查一次)
· 平板底部批量操作条 → 手机卡片级操作按钮(见 item 布局),无全选/批量 · 平板底部批量操作条 → 手机卡片级操作按钮(见 item 布局),无全选/批量
@@ -233,7 +233,7 @@
</LinearLayout> </LinearLayout>
<!-- ==================== 筛选弹层(8 项,对应设计稿 filterSheet) ==================== --> <!-- ==================== 筛选弹层(与平板端查询参数一致:航班日期/航班号/库位/清仓结果) ==================== -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel <com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -262,63 +262,24 @@
type="@{PhoneDataLayoutType.INPUT}" type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.flightNo}" /> value="@={viewModel.flightNo}" />
<!-- 代理(手机专属筛选,字典由 initPhoneExtras 加载) --> <!-- 库位(与平板端查询参数 location 一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择代理"}' hint='@{"请输入库位"}'
list="@{viewModel.agentList}" title='@{"库位"}'
title='@{"代理"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.agentId}" />
<!-- 特码 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择特码"}'
list="@{viewModel.specialCodeList}"
title='@{"特码"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.spCode}" />
<!-- 目的站 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入目的站"}'
title='@{"目的站"}'
type="@{PhoneDataLayoutType.INPUT}" type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.dest}" /> value="@={viewModel.location}" />
<!-- 运单类型(pageQuery 无此入参,后端补齐后自动生效,见接口问题清单) --> <!-- 清仓结果(与平板端查询参数 clearNormal 一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择运单类型"}' hint='@{"请选择清仓结果"}'
list="@{viewModel.awbTypeList}" list="@{viewModel.clearResultList}"
title='@{"运单类型"}' title='@{"清仓结果"}'
type="@{PhoneDataLayoutType.SPINNER}" type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.awbType}" /> value="@={viewModel.clearResult}" />
<!-- 业务类型(pageQuery 无此入参,同上) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择业务类型"}'
list="@{viewModel.businessTypeList}"
title='@{"业务类型"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.businessType}" />
<!-- 品名(中)(pageQuery 无此入参,同上) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入中文品名"}'
title='@{"品名(中)"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.goodsCn}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel> </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>

View File

@@ -11,8 +11,8 @@
数据全部来自既有 viewModel.maWbData(IntExpSearch/detail 的 maWb+maWbM 合并 Map), 数据全部来自既有 viewModel.maWbData(IntExpSearch/detail 的 maWb+maWbM 合并 Map),
取值统一走 viewModel.str()/dec() 帮助方法(空值兜底 "-")。 取值统一走 viewModel.str()/dec() 帮助方法(空值兜底 "-")。
按设计稿新增展示的字段(承运人/始发站/中转站/目的站/UN编号/锁定状态/计费重量), 按设计稿新增展示的字段(承运人/始发站/中转站/目的站/UN编号);
其中锁定状态(lockStatus)与计费重量(cashWeight)在接口文档中无定义,实机核对后如缺失记入问题清单。 锁定状态与计费重量后端确认无需展示,已移除(问题清单 #22)。
入库件数/入库重量为 warehouseList 各批次求和(viewModel.inPcTotal/inWeightTotal)。 入库件数/入库重量为 warehouseList 各批次求和(viewModel.inPcTotal/inWeightTotal)。
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <layout xmlns:android="http://schemas.android.com/apk/res/android">
@@ -167,20 +167,13 @@
</LinearLayout> </LinearLayout>
<!-- 锁定状态 | 包装类型 --> <!-- 包装类型(半宽) -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:orientation="horizontal"> android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
title='@{"锁定状态"}'
value='@{viewModel.str(viewModel.maWbData, "lockStatus")}' />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem <com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -188,6 +181,11 @@
title='@{"包装类型"}' title='@{"包装类型"}'
value='@{viewModel.str(viewModel.maWbData, "packageType")}' /> value='@{viewModel.str(viewModel.maWbData, "packageType")}' />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
<!-- 业务类型 | 入库时间 --> <!-- 业务类型 | 入库时间 -->
@@ -286,7 +284,7 @@
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:background="@color/phone_divider" /> android:background="@color/phone_divider" />
<!-- 运单件数 | 运单重量 | 计费重量 --> <!-- 运单件数 | 运单重量 -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -306,13 +304,6 @@
title='@{"运单重量"}' title='@{"运单重量"}'
value='@{viewModel.dec(viewModel.maWbData, "weight")}' /> value='@{viewModel.dec(viewModel.maWbData, "weight")}' />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
title='@{"计费重量"}'
value='@{viewModel.dec(viewModel.maWbData, "cashWeight")}' />
</LinearLayout> </LinearLayout>
<!-- 入库件数 | 入库重量 --> <!-- 入库件数 | 入库重量 -->
@@ -336,12 +327,6 @@
title='@{"入库重量"}' title='@{"入库重量"}'
value="@{viewModel.inWeightTotal}" /> value="@{viewModel.inWeightTotal}" />
<!-- 占位对齐第三列 -->
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -85,7 +85,7 @@
</LinearLayout> </LinearLayout>
<!-- 托盘自重(kg) --> <!-- 托盘自重(kg):trayTotalWeight(与平板端一致) -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -103,14 +103,14 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{record.carWeight}" android:text="@{String.valueOf(record.trayTotalWeight)}"
android:textColor="@color/phone_text_strong" android:textColor="@color/phone_text_strong"
android:textSize="14sp" android:textSize="14sp"
tools:text="18" /> tools:text="18" />
</LinearLayout> </LinearLayout>
<!-- 计重人(接口出参仅操作员编码,无姓名,见接口问题清单) --> <!-- 计重人:opUserName 姓名,为空回退 opId -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -128,7 +128,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{record.opId}" android:text="@{record.opUserDisplay}"
android:textColor="@color/phone_text_strong" android:textColor="@color/phone_text_strong"
android:textSize="14sp" android:textSize="14sp"
tools:text="张三" /> tools:text="张三" />

View File

@@ -189,7 +189,7 @@
android:layout_weight="1" android:layout_weight="1"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text='@{"代理人:" + bean.orDash(bean.agentName ?? bean.agentCode)}' android:text='@{"代理人:" + bean.orDash(bean.agentDisplay)}'
android:textColor="@color/phone_text_body" android:textColor="@color/phone_text_body"
android:textSize="13sp" android:textSize="13sp"
tools:text="代理人:中外运" /> tools:text="代理人:中外运" />

View File

@@ -75,23 +75,51 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text='@{"日期: " + bean.fdate}' android:gravity="center_vertical"
android:textColor="@color/phone_text_body" android:orientation="horizontal">
android:textSize="13sp"
tools:text="日期: 20260721" />
<TextView <ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_airplane" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text='@{"日期: " + bean.fdate}'
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="日期: 20260721" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text='@{"航程: " + bean.range}' android:gravity="center_vertical"
android:textColor="@color/phone_text_body" android:orientation="horizontal">
android:textSize="13sp"
tools:text="航程: HFE-LAX" /> <ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_location" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text='@{"航程: " + bean.range}'
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="航程: HFE-LAX" />
</LinearLayout>
</LinearLayout> </LinearLayout>
@@ -102,23 +130,54 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text='@{"代理: " + bean.agentName}' android:gravity="center_vertical"
android:textColor="@color/phone_text_body" android:orientation="horizontal">
android:textSize="13sp"
tools:text="代理: 中外运" />
<TextView <ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_person" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text='@{"代理: " + bean.agentName}'
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="代理: 中外运" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text='@{"特码: " + bean.spCode}' android:gravity="center_vertical"
android:textColor="@color/phone_text_body" android:orientation="horizontal">
android:textSize="13sp"
tools:text="特码: DGR" /> <ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_tag" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text='@{"特码: " + bean.spCode}'
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="特码: DGR" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -56,14 +56,28 @@
</LinearLayout> </LinearLayout>
<!-- 代理 --> <!-- 代理 -->
<TextView <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text='@{"代理: " + bean.agentName}' android:gravity="center_vertical"
android:textColor="@color/phone_text_body" android:orientation="horizontal">
android:textSize="13sp"
tools:text="代理: 马道" /> <ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/ic_phone_person" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text='@{"代理: " + bean.agentName}'
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="代理: 马道" />
</LinearLayout>
<!-- 运抵统计框 --> <!-- 运抵统计框 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneStatBox <com.lukouguoji.module_base.ui.weight.phone.PhoneStatBox

View File

@@ -217,19 +217,6 @@
android:gravity="center" android:gravity="center"
android:orientation="horizontal"> android:orientation="horizontal">
<!-- 补充(仅待运抵) -->
<TextView
android:layout_width="56dp"
android:layout_height="56dp"
android:background="@drawable/bg_phone_fab_purple"
android:gravity="center"
android:onClick="@{()-> activity.openSupplement(bean)}"
android:text="补充"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="@{`pending`.equals(viewModel.currentTab) ? View.VISIBLE : View.GONE}" />
<!-- 重置(仅已运抵) --> <!-- 重置(仅已运抵) -->
<TextView <TextView
android:layout_width="56dp" android:layout_width="56dp"

View File

@@ -108,7 +108,7 @@
</LinearLayout> </LinearLayout>
<!-- ==================== 航班信息行(后端暂无字段,占位 "-") ==================== --> <!-- ==================== 航班信息行(flight:航班日期/航班号) ==================== -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -127,7 +127,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_weight="1" android:layout_weight="1"
android:text="航班信息:-" android:text='@{"航班信息:" + bean.orDash(bean.flight)}'
android:textColor="@color/phone_text_body" android:textColor="@color/phone_text_body"
android:textSize="13sp" /> android:textSize="13sp" />

View File

@@ -19,6 +19,7 @@ import com.lukouguoji.module_base.ktx.getLifecycleOwner
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
import com.lukouguoji.module_base.ktx.setUpperCaseLetterFilter import com.lukouguoji.module_base.ktx.setUpperCaseLetterFilter
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil
/** /**
* 国际进港查询列表页 * 国际进港查询列表页
@@ -32,7 +33,7 @@ class IntImpQueryActivity :
override fun viewModelClass() = IntImpQueryViewModel::class.java override fun viewModelClass() = IntImpQueryViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际进港查询") setBackArrow(if (DeviceUtil.isPhone()) "进港查询" else "国际进港查询")
binding.viewModel = viewModel binding.viewModel = viewModel
@@ -50,22 +51,35 @@ class IntImpQueryActivity :
viewModel.initSpecialCodeList() viewModel.initSpecialCodeList()
viewModel.initFilterLists() viewModel.initFilterLists()
// 观察筛选面板显示状态 // 平板变体才有侧滑筛选面板(filter_panel/filter_overlay 手机变体不存在,Binding 字段为 null)
viewModel.filterVisible.observe(this) { visible -> val padFilterPanel = binding.filterPanel
if (visible) showFilterPanel() else hideFilterPanel() if (padFilterPanel != null) {
// 观察筛选面板显示状态(滑入/滑出动画)
viewModel.filterVisible.observe(this) { visible ->
if (visible) showFilterPanel() else hideFilterPanel()
}
// 航班号:大写字母+数字
padFilterPanel.filterFlightNo.et.setUpperCaseAlphanumericFilter()
// 始发港:仅大写字母
padFilterPanel.filterOrigin.et.setUpperCaseLetterFilter()
} else {
// 手机变体:PhoneFilterPanel 的显隐由 XML 直接绑定 viewModel.filterVisible,无需动画代码
binding.phoneFilterFlightNo?.et?.setUpperCaseAlphanumericFilter()
binding.phoneFilterOrigin?.et?.setUpperCaseLetterFilter()
} }
// 航班号:大写字母+数字 // 手机形态专属:已入库/已出库统计计数
binding.filterPanel.filterFlightNo.et.setUpperCaseAlphanumericFilter() if (DeviceUtil.isPhone()) {
// 始发港:仅大写字母 viewModel.initPhoneExtras()
binding.filterPanel.filterOrigin.et.setUpperCaseLetterFilter() }
viewModel.refresh() viewModel.refresh()
} }
private fun showFilterPanel() { private fun showFilterPanel() {
val panel = binding.filterPanel.root val panel = binding.filterPanel?.root ?: return
val overlay = binding.filterOverlay val overlay = binding.filterOverlay ?: return
val panelWidth = window.decorView.width / 3 val panelWidth = window.decorView.width / 3
panel.layoutParams = (panel.layoutParams as FrameLayout.LayoutParams).apply { panel.layoutParams = (panel.layoutParams as FrameLayout.LayoutParams).apply {
@@ -87,8 +101,8 @@ class IntImpQueryActivity :
} }
private fun hideFilterPanel() { private fun hideFilterPanel() {
val panel = binding.filterPanel.root val panel = binding.filterPanel?.root ?: return
val overlay = binding.filterOverlay val overlay = binding.filterOverlay ?: return
if (panel.visibility != View.VISIBLE) return if (panel.visibility != View.VISIBLE) return

View File

@@ -16,6 +16,7 @@ import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.base.CustomVP2Adapter import com.lukouguoji.module_base.base.CustomVP2Adapter
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DeviceUtil
/** /**
* 国际进港查询详情页面 * 国际进港查询详情页面
@@ -28,7 +29,7 @@ class IntImpQueryDetailsActivity :
override fun viewModelClass() = IntImpQueryDetailsViewModel::class.java override fun viewModelClass() = IntImpQueryDetailsViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) { override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际进港查询详情") setBackArrow(if (DeviceUtil.isPhone()) "进港货物" else "国际进港查询详情")
binding.viewModel = viewModel binding.viewModel = viewModel
// 初始化ViewModel(传入maWbId) // 初始化ViewModel(传入maWbId)

View File

@@ -19,9 +19,9 @@ class IntImpQueryViewHolder(view: View) :
// 注册整行点击事件 // 注册整行点击事件
notifyItemClick(position, binding.ll) notifyItemClick(position, binding.ll)
// 侧滑菜单 - 修改按钮 // 侧滑菜单 - 修改按钮(平板变体独有;手机变体无侧滑编辑入口,字段为 null)
binding.btnEdit.setOnClickListener { binding.btnEdit?.setOnClickListener {
binding.swipeMenu.quickClose() binding.swipeMenu?.quickClose()
clickListener?.onItemClick(position, 2000) // type=2000表示修改操作 clickListener?.onItemClick(position, 2000) // type=2000表示修改操作
} }
} }

View File

@@ -70,9 +70,7 @@ class IntImpPickUpDLVViewModel : BasePageViewModel() {
// 筛选弹层显隐 // 筛选弹层显隐
val filterVisible = MutableLiveData(false) val filterVisible = MutableLiveData(false)
// 筛选条件(flightDate 为 pageQuery 未定义入参 fdate,预留传参,见接口问题清单) // 筛选弹层与平板端查询参数保持一致(后端确认):缴费日期起止 / 代理人 / 特码,运单号走顶部搜索框
val flightDate = MutableLiveData("")
val flightNo = MutableLiveData("")
// 特码下拉字典(initPhoneExtras 加载,平板为扫码输入不加载) // 特码下拉字典(initPhoneExtras 加载,平板为扫码输入不加载)
val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList()) val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList())
@@ -107,8 +105,8 @@ class IntImpPickUpDLVViewModel : BasePageViewModel() {
/** 重置筛选条件 */ /** 重置筛选条件 */
fun resetFilter() { fun resetFilter() {
flightDate.value = "" paymentDateStart.value = ""
flightNo.value = "" paymentDateEnd.value = ""
agentCode.value = "" agentCode.value = ""
spCode.value = "" spCode.value = ""
} }
@@ -204,17 +202,15 @@ class IntImpPickUpDLVViewModel : BasePageViewModel() {
* 获取列表数据 * 获取列表数据
*/ */
override fun getData() { override fun getData() {
// 手机专属 3 项平板恒为空,不进请求;fdate 为 pageQuery 未定义入参 // 查询条件与平板端一致;outState 为手机 Tab 维度(平板恒空不进请求)。
//(后端静默忽略,补齐后自动生效,见接口问题清单) // 运单号入参用 wbNo(no 为 8 位编号,与主干平板端修复一致)
val filterParams = mapOf( val filterParams = mapOf(
"beginDate" to paymentDateStart.value?.ifEmpty { null }, "beginDate" to paymentDateStart.value?.ifEmpty { null },
"endDate" to paymentDateEnd.value?.ifEmpty { null }, "endDate" to paymentDateEnd.value?.ifEmpty { null },
"agentCode" to agentCode.value?.ifEmpty { null }, "agentCode" to agentCode.value?.ifEmpty { null },
"no" to wbNo.value?.ifEmpty { null }, "wbNo" to wbNo.value?.ifEmpty { null },
"spCode" to spCode.value?.ifEmpty { null }, "spCode" to spCode.value?.ifEmpty { null },
"outState" to outState.value?.ifEmpty { null }, "outState" to outState.value?.ifEmpty { null }
"fno" to flightNo.value?.ifEmpty { null },
"fdate" to flightDate.value?.ifEmpty { null }
) )
val listParams = (filterParams + mapOf( val listParams = (filterParams + mapOf(

View File

@@ -64,6 +64,22 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList()) val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList())
val businessTypeList = MutableLiveData<List<KeyValue>>(emptyList()) val businessTypeList = MutableLiveData<List<KeyValue>>(emptyList())
// ==================== 5.5寸手机版专属 ====================
// 三格统计:总票数 / 已入库 / 已出库,均取 pageQueryTotal 出参
// (wbNumber / storedNumber / departedNumber),服务端全量口径
val storedCount = MutableLiveData("0")
val outboundCount = MutableLiveData("0")
private var phoneExtrasEnabled = false
/**
* 手机形态专属初始化(Activity 在 DeviceUtil.isPhone() 时调用)。
* 平板端不调用,请求次数与行为零变化。
*/
fun initPhoneExtras() {
phoneExtrasEnabled = true
}
// ==================== 筛选条件 ==================== // ==================== 筛选条件 ====================
val spCode = MutableLiveData("") val spCode = MutableLiveData("")
val flightNo = MutableLiveData("") val flightNo = MutableLiveData("")
@@ -176,6 +192,9 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
totalCount.value = (data?.wbNumber ?: 0).toString() totalCount.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString() totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString() totalWeight.value = (data?.totalWeight ?: 0.0).toString()
// 手机三格统计分项(平板布局不绑定,无影响)
storedCount.value = (data?.storedNumber ?: 0).toString()
outboundCount.value = (data?.departedNumber ?: 0).toString()
} }
} }
} }

View File

@@ -18,7 +18,6 @@ import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.model.ScanModel import com.lukouguoji.module_base.model.ScanModel
import com.lukouguoji.module_base.util.DictUtils
import dev.utils.app.info.KeyValue import dev.utils.app.info.KeyValue
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -47,27 +46,10 @@ class IntImpStorageUseViewModel : BasePageViewModel() {
// 筛选弹层显隐 // 筛选弹层显隐
val filterVisible = MutableLiveData(false) val filterVisible = MutableLiveData(false)
// 筛选条件(fdep 为接口入参;其余 5 项为后端未定义入参,预留传参,见接口问题清单 #25) // 筛选弹层与平板端查询参数保持一致(后端确认):航班日期 / 航班号 / 库位 / 清仓结果,运单号/航班号走顶部搜索框。
val fdep = MutableLiveData("") // 始发站 // 始发站/代理/特码/运单类型/业务类型/品名 后端不支持(内网实测 fdep/agentCode 被忽略),已移除
val agentId = MutableLiveData("") // 代理(agentCode,预留)
val spCode = MutableLiveData("") // 特码(预留)
val awbType = MutableLiveData("") // 运单类型(预留)
val businessType = MutableLiveData("") // 业务类型(预留)
val goodsCn = MutableLiveData("") // 品名(中)(预留)
// 筛选下拉字典(initPhoneExtras 加载,平板不请求) // 三格统计:未清仓 / 已清仓,直接取 pageQueryTotal 出参 notClearNumber / clearNumber
val agentList = MutableLiveData(listOf(KeyValue("全部", "")))
val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList())
val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList())
val businessTypeList = MutableLiveData(
listOf(
KeyValue("全部", ""),
KeyValue("普货", "1"),
KeyValue("特货", "2")
)
)
// 三格统计:未清仓 / 已清仓(复用 pageQueryTotal 按 clearNormal=0/1 各查一次)
val unclearedCount = MutableLiveData("0") val unclearedCount = MutableLiveData("0")
val clearedCount = MutableLiveData("0") val clearedCount = MutableLiveData("0")
@@ -79,18 +61,6 @@ class IntImpStorageUseViewModel : BasePageViewModel() {
*/ */
fun initPhoneExtras() { fun initPhoneExtras() {
phoneExtrasEnabled = true phoneExtrasEnabled = true
// 代理(国际进港口径)
DictUtils.getIntImpAgentList(addAll = false) { list ->
agentList.postValue(listOf(KeyValue("全部", "")) + list)
}
// 特码:flag=1 国际;ieFlag 必须留空
DictUtils.getSpecialCodeList(addAll = false, flag = 1, ieFlag = "") { list ->
specialCodeList.postValue(listOf(KeyValue("全部", "")) + list)
}
// 运单类型(type=II 国际进港)
DictUtils.getWaybillTypeList(type = Constant.businessType.II, addAll = true) {
awbTypeList.postValue(it)
}
} }
/** /**
@@ -129,12 +99,8 @@ class IntImpStorageUseViewModel : BasePageViewModel() {
fun resetFilter() { fun resetFilter() {
flightDate.value = "" flightDate.value = ""
flightNo.value = "" flightNo.value = ""
fdep.value = "" location.value = ""
agentId.value = "" clearResult.value = ""
spCode.value = ""
awbType.value = ""
businessType.value = ""
goodsCn.value = ""
} }
/** 确认筛选 - 关闭面板并刷新 */ /** 确认筛选 - 关闭面板并刷新 */
@@ -354,20 +320,13 @@ class IntImpStorageUseViewModel : BasePageViewModel() {
} }
override fun getData() { override fun getData() {
// 手机专属 6 项平板恒为空,不进请求;agentCode/spCode/awbType/businessType/goodsCn // 查询条件与平板端一致
// 为 pageQuery 未定义入参(后端静默忽略,补齐后自动生效,见接口问题清单 #25)
val filterParams = mapOf( val filterParams = mapOf(
"fdate" to flightDate.value?.ifEmpty { null }, "fdate" to flightDate.value?.ifEmpty { null },
"fno" to flightNo.value?.ifEmpty { null }, "fno" to flightNo.value?.ifEmpty { null },
"wbNo" to wbNo.value?.ifEmpty { null }, "wbNo" to wbNo.value?.ifEmpty { null },
"location" to location.value?.ifEmpty { null }, "location" to location.value?.ifEmpty { null },
"clearNormal" to clearResult.value?.ifEmpty { null }, "clearNormal" to clearResult.value?.ifEmpty { null }
"fdep" to fdep.value?.ifEmpty { null },
"agentCode" to agentId.value?.ifEmpty { null },
"spCode" to spCode.value?.ifEmpty { null },
"awbType" to awbType.value?.ifEmpty { null },
"businessType" to businessType.value?.ifEmpty { null },
"goodsCn" to goodsCn.value?.ifEmpty { null }
) )
val listParams = (filterParams + mapOf( val listParams = (filterParams + mapOf(
@@ -391,18 +350,9 @@ class IntImpStorageUseViewModel : BasePageViewModel() {
totalWbNumber.value = (data?.wbNumber ?: 0).toString() totalWbNumber.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString() totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString() totalWeight.value = (data?.totalWeight ?: 0.0).toString()
} // 手机三格统计分项(平板布局不绑定,无影响)
} unclearedCount.value = (data?.notClearNumber ?: 0).toString()
clearedCount.value = (data?.clearNumber ?: 0).toString()
// 手机专属:未清仓/已清仓分项统计(同条件 + clearNormal=0/1 各查一次,平板不发)
if (phoneExtrasEnabled) {
val unclearedParams = (filterParams + mapOf("clearNormal" to "0")).toRequestBody()
launchCollect({ NetApply.api.getIntImpStorageUseTotal(unclearedParams) }) {
onSuccess = { unclearedCount.value = (it.data?.wbNumber ?: 0).toString() }
}
val clearedParams = (filterParams + mapOf("clearNormal" to "1")).toRequestBody()
launchCollect({ NetApply.api.getIntImpStorageUseTotal(clearedParams) }) {
onSuccess = { clearedCount.value = (it.data?.wbNumber ?: 0).toString() }
} }
} }
} }

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
<variable
name="viewModel"
type="com.lukouguoji.gjj.viewModel.IntImpQueryViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f2"
android:orientation="vertical">
<!-- 标题栏(始终在最上层,不被遮挡) -->
<include layout="@layout/title_tool_bar" />
<!-- 内容区域(FrameLayout 包裹,筛选面板和遮罩只在此区域内) -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- 原有内容 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 搜索区域 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<!-- 航班日期起 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择开始日期"}'
icon="@{@drawable/img_date}"
type="@{SearchLayoutType.DATE}"
value="@={viewModel.flightDateStart}" />
<!-- 航班日期止 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择结束航班日期"}'
icon="@{@drawable/img_date}"
type="@{SearchLayoutType.DATE}"
value="@={viewModel.flightDateEnd}" />
<!-- 代理人 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择代理"}'
list="@{viewModel.agentList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.agentId}" />
<!-- 出库状态 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择出库状态"}'
list="@{viewModel.outStatusList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.outStatus}" />
<!-- 运单号 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请输入运单号"}'
icon="@{@drawable/img_scan}"
setOnIconClickListener="@{()-> viewModel.waybillScanClick()}"
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.waybillNo}"
autoQueryEnabled="@{true}"
autoQueryUrl="@{`/IntImpSearch/queryWbNoList`}"
autoQueryParamKey="@{`wbNo`}"
autoQueryMinLength="@{4}"
autoQueryMaxLength="@{8}"
autoQueryTitle="@{`选择运单号`}" />
<!-- 搜索和筛选按钮 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:orientation="horizontal"
android:paddingHorizontal="24dp">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:onClick="@{()-> viewModel.searchClick()}"
android:padding="2dp"
android:src="@drawable/img_search" />
<FrameLayout
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginLeft="16dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="@{()-> viewModel.filterClick()}"
android:padding="5dp"
android:src="@drawable/img_filter" />
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_gravity="top|end"
android:background="@drawable/bg_red_dot"
android:visibility="@{viewModel.hasFilter ? View.VISIBLE : View.GONE}" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
<!-- 列表区域 -->
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
itemLayoutId="@{viewModel.itemLayoutId}"
viewHolder="@{viewModel.itemViewHolder}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_int_imp_query" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
<!-- 底部统计信息 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/white"
android:gravity="center_vertical"
android:paddingHorizontal="15dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{`合计:` + viewModel.totalCount + `票`}'
android:textColor="@color/bottom_tool_tips_text_color"
android:textSize="16sp"
android:textStyle="bold"
tools:text="合计:3票" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text='@{`总件数:` + viewModel.totalPc}'
android:textColor="@color/bottom_tool_tips_text_color"
android:textSize="16sp"
android:textStyle="bold"
tools:text="总件数:100" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text='@{`总重量:` + viewModel.totalWeight}'
android:textColor="@color/bottom_tool_tips_text_color"
android:textSize="16sp"
android:textStyle="bold"
tools:text="总重量:100" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- 遮罩层(只覆盖内容区域) -->
<View
android:id="@+id/filter_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
android:clickable="true"
android:focusable="true"
android:onClick="@{()-> viewModel.closeFilter()}"
android:visibility="gone" />
<!-- 筛选面板(从右侧滑入,只在内容区域内) -->
<include
android:id="@+id/filter_panel"
layout="@layout/layout_int_imp_query_filter"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:visibility="gone"
app:viewModel="@{viewModel}" />
</FrameLayout>
</LinearLayout>
</layout>

View File

@@ -0,0 +1,325 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="bean"
type="com.lukouguoji.module_base.bean.IntImpQueryBean" />
</data>
<!-- 外层容器承载间距 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="5dp"
android:orientation="vertical">
<!-- 侧滑布局 -->
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
android:id="@+id/swipe_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 主列表项容器 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_item"
android:orientation="horizontal"
android:paddingVertical="20dp"
android:paddingHorizontal="10dp">
<!-- 左侧图标 -->
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:src="@drawable/img_plane" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:orientation="vertical">
<!-- 第一行:运单号、件数、重量、代理人、航班信息 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 运单号 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{4}"
android:text="运单号:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@{bean.wbNo}"
android:textColor="@color/colorPrimary"
tools:text="78109081212" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 件数 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.6"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{3}"
android:text="件数:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{String.valueOf(bean.inPc)}'
tools:text="10" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 重量 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{5}"
android:text="重量:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{String.valueOf(bean.inWeight)}'
tools:text="200" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 代理人 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{5}"
android:text="代理人:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{bean.agentName}'
tools:text="SF" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 航班信息 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{5}"
android:text="航班信息:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{bean.flight}'
tools:text="20240216/MU2026" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 第二行:航程、特码、运单类型、入库时间、出库时间 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<!-- 航程 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{4}"
android:text="航程:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@{bean.range}"
tools:text="LAX-HFE" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 特码 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.6"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{3}"
android:text="特码:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@{bean.spCode}"
tools:text="NOR" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 运单类型 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{5}"
android:text="运单类型:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{bean.awbName}'
tools:text="国际进港" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 入库时间 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{5}"
android:text="入库时间:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{bean.inDate}'
tools:text="2024-05-13 17:10:22" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 出库时间 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
completeSpace="@{5}"
android:text="出库时间:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text='@{bean.dlvTime}'
tools:text="2024-05-13 17:10:22" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</LinearLayout>
<!-- 右侧箭头 -->
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:src="@drawable/img_pda_right" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 侧滑菜单按钮区 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- 修改按钮 -->
<TextView
android:id="@+id/btn_edit"
style="@style/tv_item_action"
android:background="@color/colorPrimary"
android:text="修改" />
</androidx.appcompat.widget.LinearLayoutCompat>
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
</LinearLayout>
</layout>

View File

@@ -3,10 +3,10 @@
国际进港提取出库-运单详情(5.5 寸手机版专属页面,无平板变体——平板端无入口) 国际进港提取出库-运单详情(5.5 寸手机版专属页面,无平板变体——平板端无入口)
对应设计稿 09_提取出库 的 page-detail: 对应设计稿 09_提取出库 的 page-detail:
蓝头卡(运单号 + 绿点提取时间)+ 红标签「国际进港」|业务类型 + 品名大标题 + 蓝头卡(运单号 + 绿点提取时间)+ 红标签「国际进港」+ 品名大标题 +
两列 KV(件数/重量/特码/代理/始发港/目的港/航班日期/航班号/库位橙标签/提货单号) 两列 KV(件数/重量/特码/代理/始发港/航班日期/航班号/库位橙标签/提货单号)
+ 绿头「提货信息」卡(柜台办理人/时间;已出库追加出库人/出库时间行)。 + 绿头「提货信息」卡(柜台办理人/时间;已出库追加出库人/出库时间行)。
「目的港/业务类型」为 pageQuery 出参缺失字段(见问题清单),空值兜底为双横线占位。 「目的港/业务类型」后端无对应出参且确认按平板端口径不提供,已移除;出库人绑 refUserName。
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
@@ -90,7 +90,7 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<!-- 红标签「国际进港」 | 业务类型 --> <!-- 红标签「国际进港」 -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -107,21 +107,6 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="11sp" /> android:textSize="11sp" />
<View
android:layout_width="1px"
android:layout_height="14dp"
android:layout_marginHorizontal="10dp"
android:background="@color/phone_divider" />
<!-- 业务类型(pageQuery 出参无此字段,见问题清单,兜底双横线) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="--"
android:textColor="@color/phone_text_body"
android:textSize="13sp"
tools:text="普通货物运输" />
</LinearLayout> </LinearLayout>
<!-- 品名大标题 --> <!-- 品名大标题 -->
@@ -181,7 +166,7 @@
</LinearLayout> </LinearLayout>
<!-- 始发港 | 目的港 --> <!-- 始发港(半宽) -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -195,13 +180,10 @@
title='@{"始发港"}' title='@{"始发港"}'
value='@{bean.origin.empty ? "--" : bean.origin}' /> value='@{bean.origin.empty ? "--" : bean.origin}' />
<!-- 目的港(pageQuery 出参无此字段,见问题清单,兜底双横线) --> <View
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1" />
title='@{"目的港"}'
value='@{"--"}' />
</LinearLayout> </LinearLayout>
@@ -316,7 +298,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
title='@{"出库人"}' title='@{"出库人"}'
value='@{bean.dlvName.empty ? "--" : bean.dlvName}' /> value='@{bean.refUserName.empty ? "--" : bean.refUserName}' />
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem <com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
android:layout_width="0dp" android:layout_width="0dp"

View File

@@ -121,23 +121,22 @@
setOnResetClick="@{(v)-> viewModel.resetFilter()}" setOnResetClick="@{(v)-> viewModel.resetFilter()}"
title='@{"筛选条件"}'> title='@{"筛选条件"}'>
<!-- 航班日期(pageQuery 无 fdate 入参,预留传参,见问题清单) --> <!-- 缴费日期起 / 止(与平板端查询参数 beginDate / endDate 一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请选择航班日期"}' hint='@{"请选择开始日期"}'
title='@{"航班日期"}' title='@{"缴费日期起"}'
type="@{PhoneDataLayoutType.DATE}" type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.flightDate}" /> value="@={viewModel.paymentDateStart}" />
<!-- 航班号 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
hint='@{"请输入航班号"}' hint='@{"请选择结束日期"}'
title='@{"航班号"}' title='@{"缴费日期止"}'
type="@{PhoneDataLayoutType.INPUT}" type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.flightNo}" /> value="@={viewModel.paymentDateEnd}" />
<!-- 代理人(复用平板既有 agentList) --> <!-- 代理人(复用平板既有 agentList) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout

View File

@@ -1,205 +1,201 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
国际进港查询 —— 手机版布局(5.5 寸手持端,最小宽度 < 600dp 时生效)
平板版为同名文件 layout-sw600dp/activity_int_imp_query.xml,两者是同一布局的资源变体,
DataBinding 因此生成同一个 ActivityIntImpQueryBinding 类。
变体之间必须满足:
· DataBinding 变量完全一致:viewModel
· 保留 Activity 中引用的控件 id:srl / rv
· 平板独有的 filter_overlay / filter_panel(侧滑筛选)手机版不提供,
Binding 基类中自动变 @Nullable,IntImpQueryActivity 已判空处理
交互差异(均复用既有 IntImpQueryViewModel 字段与接口):
· 平板 5 个横排搜索条 → 手机顶部搜索框(运单号)+ 底部筛选弹层
(航班日期起/止、代理从搜索行移入弹层;设计稿无「出库状态」项,未提供)
· 三格统计(总票数/已入库/已出库),后两格为手机专属客户端计数(initPhoneExtras 开启)
· 筛选弹层显隐直接绑既有 viewModel.filterVisible,无需平板的滑入动画代码
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<import type="android.view.View" /> <import type="android.view.View" />
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType" />
<variable <variable
name="viewModel" name="viewModel"
type="com.lukouguoji.gjj.viewModel.IntImpQueryViewModel" /> type="com.lukouguoji.gjj.viewModel.IntImpQueryViewModel" />
</data> </data>
<LinearLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/color_f2" android:background="@color/phone_page_bg">
android:orientation="vertical">
<!-- 标题栏(始终在最上层,不被遮挡) --> <LinearLayout
<include layout="@layout/title_tool_bar" />
<!-- 内容区域(FrameLayout 包裹,筛选面板和遮罩只在此区域内) -->
<FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="match_parent"
android:layout_weight="1"> android:orientation="vertical">
<!-- 原有内容 --> <include layout="@layout/title_tool_bar" />
<!-- ==================== 搜索行 ==================== -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical"> android:background="@color/white"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingTop="16dp"
android:paddingBottom="12dp">
<!-- 搜索区域 --> <com.lukouguoji.module_base.ui.weight.phone.PhoneSearchBar
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp" hint='@{"搜索运单号"}'
android:layout_marginTop="10dp" setOnFilterClickListener="@{(v)-> viewModel.filterClick()}"
android:gravity="center_vertical" setOnScanClickListener="@{(v)-> viewModel.waybillScanClick()}"
setRefreshCallBack="@{viewModel::searchClick}"
value="@={viewModel.waybillNo}" />
</LinearLayout>
<!-- ==================== 三格统计:总票数 / 已入库 / 已出库 ==================== -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<!-- 总票数(蓝) -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_phone_card"
android:orientation="horizontal"> android:orientation="horizontal">
<!-- 航班日期起 --> <View
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout android:layout_width="4dp"
android:layout_width="0dp" android:layout_height="match_parent"
android:layout_height="wrap_content" android:layout_marginVertical="12dp"
android:layout_weight="1" android:background="@color/phone_primary" />
hint='@{"请选择开始日期"}'
icon="@{@drawable/img_date}"
type="@{SearchLayoutType.DATE}"
value="@={viewModel.flightDateStart}" />
<!-- 航班日期止 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择结束航班日期"}'
icon="@{@drawable/img_date}"
type="@{SearchLayoutType.DATE}"
value="@={viewModel.flightDateEnd}" />
<!-- 代理人 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择代理"}'
list="@{viewModel.agentList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.agentId}" />
<!-- 出库状态 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择出库状态"}'
list="@{viewModel.outStatusList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.outStatus}" />
<!-- 运单号 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请输入运单号"}'
icon="@{@drawable/img_scan}"
setOnIconClickListener="@{()-> viewModel.waybillScanClick()}"
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.waybillNo}"
autoQueryEnabled="@{true}"
autoQueryUrl="@{`/IntImpSearch/queryWbNoList`}"
autoQueryParamKey="@{`wbNo`}"
autoQueryMinLength="@{4}"
autoQueryMaxLength="@{8}"
autoQueryTitle="@{`选择运单号`}" />
<!-- 搜索和筛选按钮 -->
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:orientation="vertical"
android:gravity="center_vertical|start" android:paddingHorizontal="12dp"
android:orientation="horizontal" android:paddingVertical="10dp">
android:paddingHorizontal="24dp">
<ImageView <TextView
android:layout_width="36dp" android:layout_width="wrap_content"
android:layout_height="36dp" android:layout_height="wrap_content"
android:onClick="@{()-> viewModel.searchClick()}" android:text="总票数"
android:padding="2dp" android:textColor="@color/phone_text_body"
android:src="@drawable/img_search" /> android:textSize="12sp" />
<FrameLayout <TextView
android:layout_width="36dp" android:layout_width="wrap_content"
android:layout_height="36dp" android:layout_height="wrap_content"
android:layout_marginLeft="16dp"> android:layout_marginTop="2dp"
android:text="@{viewModel.totalCount}"
<ImageView android:textColor="@color/phone_primary"
android:layout_width="match_parent" android:textSize="20sp"
android:layout_height="match_parent" android:textStyle="bold"
android:onClick="@{()-> viewModel.filterClick()}" tools:text="4" />
android:padding="5dp"
android:src="@drawable/img_filter" />
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_gravity="top|end"
android:background="@drawable/bg_red_dot"
android:visibility="@{viewModel.hasFilter ? View.VISIBLE : View.GONE}" />
</FrameLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<!-- 列表区域 --> <!-- 已入库(橙) -->
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
itemLayoutId="@{viewModel.itemLayoutId}"
viewHolder="@{viewModel.itemViewHolder}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_int_imp_query" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
<!-- 底部统计信息 -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="50dp" android:layout_height="wrap_content"
android:background="@color/white" android:layout_marginStart="10dp"
android:gravity="center_vertical" android:layout_weight="1"
android:paddingHorizontal="15dp"> android:background="@drawable/bg_phone_card"
android:orientation="horizontal">
<View
android:layout_width="4dp"
android:layout_height="match_parent"
android:layout_marginVertical="12dp"
android:background="@color/phone_tag_orange_text" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:orientation="vertical"
android:orientation="horizontal"> android:paddingHorizontal="12dp"
android:paddingVertical="10dp">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text='@{`合计:` + viewModel.totalCount + `票`}' android:text="已入库"
android:textColor="@color/bottom_tool_tips_text_color" android:textColor="@color/phone_text_body"
android:textSize="16sp" android:textSize="12sp" />
android:textStyle="bold"
tools:text="合计:3票" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="20dp" android:layout_marginTop="2dp"
android:text='@{`总件数:` + viewModel.totalPc}' android:text="@{viewModel.storedCount}"
android:textColor="@color/bottom_tool_tips_text_color" android:textColor="@color/phone_tag_orange_text"
android:textSize="16sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
tools:text="总件数:100" /> tools:text="2" />
</LinearLayout>
</LinearLayout>
<!-- 已出库(绿) -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:background="@drawable/bg_phone_card"
android:orientation="horizontal">
<View
android:layout_width="4dp"
android:layout_height="match_parent"
android:layout_marginVertical="12dp"
android:background="@color/phone_tag_green_text" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="12dp"
android:paddingVertical="10dp">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="20dp" android:text="已出库"
android:text='@{`总重量:` + viewModel.totalWeight}' android:textColor="@color/phone_text_body"
android:textColor="@color/bottom_tool_tips_text_color" android:textSize="12sp" />
android:textSize="16sp"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="@{viewModel.outboundCount}"
android:textColor="@color/phone_tag_green_text"
android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
tools:text="总重量:100" /> tools:text="2" />
</LinearLayout> </LinearLayout>
@@ -207,28 +203,131 @@
</LinearLayout> </LinearLayout>
<!-- 遮罩层(只覆盖内容区域) --> <!-- ==================== 列表 ==================== -->
<View <com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/filter_overlay" android:id="@+id/srl"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="0dp"
android:background="#80000000" android:layout_weight="1">
android:clickable="true"
android:focusable="true"
android:onClick="@{()-> viewModel.closeFilter()}"
android:visibility="gone" />
<!-- 筛选面板(从右侧滑入,只在内容区域内) --> <androidx.recyclerview.widget.RecyclerView
<include android:id="@+id/rv"
android:id="@+id/filter_panel" android:layout_width="match_parent"
layout="@layout/layout_int_imp_query_filter" android:layout_height="match_parent"
android:layout_width="wrap_content" android:clipToPadding="false"
android:layout_height="match_parent" android:overScrollMode="never"
android:layout_gravity="end" android:paddingTop="4dp"
android:visibility="gone" android:paddingBottom="24dp"
app:viewModel="@{viewModel}" /> itemLayoutId="@{viewModel.itemLayoutId}"
viewHolder="@{viewModel.itemViewHolder}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="3"
tools:listitem="@layout/item_int_imp_query" />
</FrameLayout> </com.scwang.smart.refresh.layout.SmartRefreshLayout>
</LinearLayout>
<!-- ==================== 筛选弹层(绑既有 filterVisible) ==================== -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="8dp"
panelVisible="@{viewModel.filterVisible}"
setOnConfirmClick="@{(v)-> viewModel.confirmFilter()}"
setOnDismissClick="@{(v)-> viewModel.closeFilter()}"
setOnResetClick="@{(v)-> viewModel.resetFilter()}"
title='@{"筛选条件"}'>
<!-- 航班日期起(平板搜索行字段,手机移入弹层) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择航班日期"}'
title='@{"航班日期起"}'
type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.flightDateStart}" />
<!-- 航班日期止 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择航班日期"}'
title='@{"航班日期止"}'
type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.flightDateEnd}" />
<!-- 航班号 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:id="@+id/phone_filter_flight_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入航班号"}'
title='@{"航班号"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.flightNo}" />
<!-- 代理(平板搜索行字段,手机移入弹层) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择代理"}'
list="@{viewModel.agentList}"
title='@{"代理"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.agentId}" />
<!-- 特码(复用平板筛选面板同一字典 spCodeList) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择特码"}'
list="@{viewModel.spCodeList}"
title='@{"特码"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.spCode}" />
<!-- 始发站 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:id="@+id/phone_filter_origin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入始发站"}'
title='@{"始发站"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.origin}" />
<!-- 运单类型 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择运单类型"}'
list="@{viewModel.awbTypeList}"
title='@{"运单类型"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.awbType}" />
<!-- 业务类型 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择业务类型"}'
list="@{viewModel.businessTypeList}"
title='@{"业务类型"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.businessType}" />
<!-- 品名(中) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入品名"}'
title='@{"品名(中)"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.goodsCn}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout>
</LinearLayout>
</layout> </layout>

Some files were not shown because too many files have changed in this diff Show More