From 49f11ef8e64bcb836fc3e3dea4c82e1fc542ce08 Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Mon, 3 Aug 2026 12:17:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BF=9B=E6=B8=AF=E7=A7=BB=E5=BA=93?= =?UTF-8?q?=E5=BD=92=E5=B1=9E=E6=9B=B4=E6=AD=A3=E2=80=94=E2=80=94=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E8=AF=AF=E5=BB=BAIntImpMove=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E9=80=82=E9=85=8D=E5=88=B0=E5=9B=BD=E5=86=85=E8=BF=9B?= =?UTF-8?q?=E6=B8=AFDomImpMove?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设计稿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 --- .../skills/phone-adapt/references/api-doc.md | 20 +- CHANGELOG.md | 22 + CLAUDE.md | 27 +- app/src/debug/AndroidManifest.xml | 16 +- app/src/main/AndroidManifest.xml | 35 +- .../detail/GnjMoveStashDetailActivity.kt | 49 ++ .../detail/GnjMoveStashDetailViewModel.kt | 25 + .../stash/list/GnjMoveStashListActivity.kt | 140 ++++-- .../stash/list/GnjMoveStashListViewHolder.kt | 36 +- .../stash/list/GnjMoveStashListViewModel.kt | 101 +++- .../stash/photo/GnjMoveStashPhotoActivity.kt | 45 ++ .../stash/photo/GnjMoveStashPhotoViewModel.kt | 84 ++-- .../activity_gnj_move_stash_list.xml | 153 +++++++ .../item_gnj_move_stash_list.xml | 296 ++++++++++++ .../layout/activity_gnj_move_stash_detail.xml | 30 +- .../layout/activity_gnj_move_stash_list.xml | 314 +++++++------ .../layout/activity_gnj_move_stash_photo.xml | 8 +- .../res/layout/item_gnj_move_stash_list.xml | 430 ++++++++---------- documents/后端接口对接问题清单.xlsx | Bin 23556 -> 24648 bytes .../module_base/bean/IntImpMoveBean.kt | 78 ---- .../module_base/bean/MoveStashBean.kt | 16 + .../lukouguoji/module_base/common/Constant.kt | 1 - .../lukouguoji/module_base/http/net/Api.kt | 39 +- .../module_base/router/ARouterConstants.kt | 2 +- .../gjj/activity/IntImpMoveActivity.kt | 147 ------ .../gjj/activity/IntImpMoveDetailActivity.kt | 46 -- .../gjj/activity/IntImpMovePhotoActivity.kt | 45 -- .../gjj/holder/IntImpMoveViewHolder.kt | 48 -- .../viewModel/IntImpMoveDetailViewModel.kt | 26 -- .../gjj/viewModel/IntImpMoveViewModel.kt | 187 -------- .../main/res/layout/activity_int_imp_move.xml | 166 ------- .../src/main/res/layout/item_int_imp_move.xml | 230 ---------- .../sixin/module_p/ui/enter/gj/GjViewModel.kt | 8 - .../sixin/module_p/ui/enter/gn/GnViewModel.kt | 16 + .../res/drawable-xxhdpi/gnj_yi_ku_icon.png | Bin 0 -> 26578 bytes 35 files changed, 1341 insertions(+), 1545 deletions(-) create mode 100644 app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/detail/GnjMoveStashDetailActivity.kt create mode 100644 app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/detail/GnjMoveStashDetailViewModel.kt create mode 100644 app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoActivity.kt rename module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMovePhotoViewModel.kt => app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoViewModel.kt (61%) create mode 100644 app/src/main/res/layout-sw600dp/activity_gnj_move_stash_list.xml create mode 100644 app/src/main/res/layout-sw600dp/item_gnj_move_stash_list.xml rename module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml => app/src/main/res/layout/activity_gnj_move_stash_detail.xml (93%) rename module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml => app/src/main/res/layout/activity_gnj_move_stash_photo.xml (93%) delete mode 100644 module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpMoveBean.kt delete mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveActivity.kt delete mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveDetailActivity.kt delete mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMovePhotoActivity.kt delete mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpMoveViewHolder.kt delete mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveDetailViewModel.kt delete mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveViewModel.kt delete mode 100644 module_gjj/src/main/res/layout/activity_int_imp_move.xml delete mode 100644 module_gjj/src/main/res/layout/item_int_imp_move.xml create mode 100644 module_p/src/main/res/drawable-xxhdpi/gnj_yi_ku_icon.png diff --git a/.claude/skills/phone-adapt/references/api-doc.md b/.claude/skills/phone-adapt/references/api-doc.md index eb54f1b..2354a46 100644 --- a/.claude/skills/phone-adapt/references/api-doc.md +++ b/.claude/skills/phone-adapt/references/api-doc.md @@ -115,10 +115,22 @@ curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \ `outState`(0 未出库/1 已出库)文档为 integer,前端传字符串靠 Jackson 隐式转换;`pageQueryTotal` 出参无已入库/已出库分项计数(缺口 #30,手机三格统计为客户端计数口径:dlvTime 非空=已出库、 inDate 非空=已入库);出参含 `agentName`(中文名)+ `agentCode`,agentName 可为 null -- `IntImpMove`(进港移库,2026-07-31 文档核对):**后端整个模块不存在**(api-doc 仅有国内进港 - `DomImpMove` 与国际出港 `IntExpMove`),权限串也没有对应 key(缺口 #31)。前端按 IntExpMove - 同名约定预接 `pageQuery`/`pageQueryTotal`/`move`/`uploadPic` 四接口 + 预定义权限串 - `AppIntImpMove`,后端交付后前端零改动生效 +- 「进港移库」(设计稿 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` 匹配(11 位 wbNo 或部分号均 0 条),11 位全号必须走 `wbNo`;`checkIn`/`checkInList` 文档有定义但 `pageQuery`/`pageQueryTotal` **均静默忽略**(缺口 #12,待计重/计重中计数依赖); diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea617b..8777990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ ## [Unreleased] +### 变更 (Changed) +- 【进港移库归属更正 · 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,前端已规避) + ### 新增 (Added) - 「进港查询」5.5 寸手机端适配(设计稿 10_进港查询,06 出港查询的进港镜像;详情页与运单追踪 复用 08/06 已适配页零新增):列表 `IntImpQueryActivity`(module_gjj,首页菜单实际跳转页; diff --git a/CLAUDE.md b/CLAUDE.md index fdefd92..c8fec9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1150,15 +1150,21 @@ override fun dispatchRestoreInstanceState(container: SparseArray) = `goods`);详情页与运单追踪复用 08/06 已适配页零新增;平板侧滑筛选面板代码已判空守卫。 ⚠️ 踩坑:代理人绑定不能写 `bean.agentName ?? bean.agentCode`——转换器把 JSON null 归一成 空串,`??` 只判 null 不判空串,须用 Bean 计算属性 `agentDisplay`(06 出港查询同步修正)) - - 「进港移库」`IntImpMoveActivity` + `IntImpMoveDetailActivity` + `IntImpMovePhotoActivity` - (module_gjj,**首个后端整模块缺失的设计稿页**:api-doc 确认无 IntImpMove 模块、权限串无 - 对应 key(缺口 #31)。前端按设计稿完整实现、按 IntExpMove 同名约定预接 - pageQuery/pageQueryTotal/move/uploadPic + 预定义权限串 `AppIntImpMove`,后端交付后零改动 - 生效;权限串下发前手机首页无入口,debug 直启验证。结构镜像 05 出港移库(Tab/批量移库/筛选 - 4 项),新增交互:卡片长按浮层「拍照」(复用 04 运抵的 actionMenuVisible 模式)→ 拍照上传页 - (图片网格复用 module_base `ImageSelectNewViewHolder`,提交走 pic/originalPic/picNumber - 三字段规范);详情页三卡片:基本信息/移库信息(仅已移库)/异常照片。手机专属新页无平板变体, - 布局只放 `res/layout/`;UI 与交互已用 Proxyman Map Local mock 全量验证) + - 「进港移库」`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 出港仓库的进港镜像;**首页菜单实际跳转页, 旧版 `GjjWareHouseActivity` 路由已注释弃用**。差异:卡片/筛选用始发站(fdep 入参)替代目的站; 搜索框「运单号/航班号」分流(含字母→fno、其余→wbNo,抓包证实);筛选 5 项无入参(缺口 #25, @@ -1186,7 +1192,8 @@ override fun dispatchRestoreInstanceState(container: SparseArray) = - ✅ **设计稿 11 个入口(01~11)已于 2026-07-31 全部适配完成** - ⏳ **待办**: - `PhoneSearchBar` 接入 AutoQuery 联想(`AutoQueryManager` 目前只对接了 Pad 系组件) - - 进港移库待后端交付 IntImpMove 模块(四接口 + 权限串 AppIntImpMove,缺口 #31)后联调 + - 进港移库筛选三项(航班日期/航班号/特码)待后端实装 `fdate`/`fno`/`spCode` 入参(缺口 #32)、 + 移库人/移库时间待后端回填 `opId`/`opDate` 并补姓名出参(缺口 #33)后自动生效 - 出库交接 Tab 角标恒 0:`IntExpOutHandover/pageQueryTotal` 出参恒 0(后端缺陷,问题清单 #6), 后端修复后角标自动恢复(`handoverState` 过滤本身已于 2026-07-29 实机验证通过)生效 - 📋 **接口对接**:适配中发现的后端接口缺口统一记录在 `documents/后端接口对接问题清单.xlsx` diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml index 8717d8c..cfe6128 100644 --- a/app/src/debug/AndroidManifest.xml +++ b/app/src/debug/AndroidManifest.xml @@ -158,9 +158,21 @@ android:exported="true" tools:replace="android:exported" /> - + + + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a9b492..c4bb10a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -375,6 +375,20 @@ android:name=".page.gnj.move.stash.list.GnjMoveStashListActivity" android:configChanges="orientation|keyboardHidden" android:screenOrientation="unspecified" /> + + + + + + - - - - - - - - - () { + + 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) + } + } + + companion object { + @JvmStatic + fun start(context: Context, bean: MoveStashBean) { + context.startActivity( + Intent(context, GnjMoveStashDetailActivity::class.java) + .putExtra(Constant.Key.DATA, Gson().toJson(bean)) + ) + } + } +} diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/detail/GnjMoveStashDetailViewModel.kt b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/detail/GnjMoveStashDetailViewModel.kt new file mode 100644 index 0000000..4cca875 --- /dev/null +++ b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/detail/GnjMoveStashDetailViewModel.kt @@ -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) + } + } +} diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListActivity.kt b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListActivity.kt index 86a24b3..3f1de6c 100644 --- a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListActivity.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListActivity.kt @@ -1,36 +1,104 @@ -package com.lukouguoji.aerologic.page.gnj.move.stash.list - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import com.lukouguoji.aerologic.R -import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashListBinding -import com.lukouguoji.module_base.base.BaseBindingActivity -import com.lukouguoji.module_base.ktx.getLifecycleOwner - -class GnjMoveStashListActivity : - BaseBindingActivity() { - override fun layoutId() = R.layout.activity_gnj_move_stash_list - - override fun viewModelClass() = GnjMoveStashListViewModel::class.java - - override fun initOnCreate(savedInstanceState: Bundle?) { - setBackArrow("国内进港移库") - - binding.viewModel = viewModel - - viewModel.pageModel - .bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner()) - - viewModel.refresh() - } - - companion object { - @JvmStatic - fun start(context: Context) { - val starter = Intent(context, GnjMoveStashListActivity::class.java) - context.startActivity(starter) - } - } - -} \ No newline at end of file +package com.lukouguoji.aerologic.page.gnj.move.stash.list + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.databinding.ObservableBoolean +import com.alibaba.android.arouter.facade.annotation.Route +import com.lukouguoji.aerologic.R +import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashListBinding +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.ConstantEvent +import com.lukouguoji.module_base.impl.FlowBus +import com.lukouguoji.module_base.impl.observe +import com.lukouguoji.module_base.ktx.getLifecycleOwner +import com.lukouguoji.module_base.router.ARouterConstants +import com.lukouguoji.module_base.util.DeviceUtil + +@Route(path = ARouterConstants.ACTIVITY_URL_GNJ_MOVE_STASH_LIST) +class GnjMoveStashListActivity : + BaseBindingActivity() { + override fun layoutId() = R.layout.activity_gnj_move_stash_list + + override fun viewModelClass() = GnjMoveStashListViewModel::class.java + + /** 筛选弹层显隐(仅手机 UI 状态,平板布局不绑定) */ + val filterPanelVisible = ObservableBoolean(false) + + override fun initOnCreate(savedInstanceState: Bundle?) { + // 设计稿手机端标题为「进港移库」,平板保持原标题 + 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(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { + viewModel.updateCheckAllStatus() + } + } + + // 拍照上传等子页面完成后刷新列表(平板无发射方,观察无副作用) + FlowBus.with(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) + } + + companion object { + @JvmStatic + fun start(context: Context) { + val starter = Intent(context, GnjMoveStashListActivity::class.java) + context.startActivity(starter) + } + } + +} diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewHolder.kt b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewHolder.kt index 0304c4d..f337635 100644 --- a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewHolder.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewHolder.kt @@ -3,34 +3,64 @@ package com.lukouguoji.aerologic.page.gnj.move.stash.list import android.view.View import android.widget.Toast 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.module_base.base.BaseViewHolder 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) : BaseViewHolder(view) { override fun onBind(item: Any?, position: Int) { val bean = getItemBean(item)!! binding.bean = bean + binding.position = position + binding.activity = itemView.context as? GnjMoveStashListActivity + // 勾选框(双形态共有):切换选中;手机端另行广播刷新全选/已选计数 binding.ivIcon.setOnClickListener { bean.checked.set(!bean.checked.get()) + binding.executePendingBindings() + + GlobalScope.launch { + FlowBus.with(ConstantEvent.EVENT_CHECK_CHANGED).emit("check_changed") + } } + // ===== 平板独有交互 ===== + // 点击列表项进入移交详情 - binding.ll.setOnClickListener { + binding.ll?.setOnClickListener { GnjYiKuHandoverActivity.startForDetails(itemView.context, bean.mawbId) } // 侧滑菜单 - 编辑按钮 - binding.btnEdit.setOnClickListener { - binding.swipeMenu.quickClose() + binding.btnEdit?.setOnClickListener { + binding.swipeMenu?.quickClose() if (bean.pickFlag == "1") { Toast.makeText(itemView.context, "已出库,不可编辑", Toast.LENGTH_SHORT).show() return@setOnClickListener } GnjYiKuHandoverActivity.startForEdit(itemView.context, bean.mawbId) } + + // ===== 手机独有交互 ===== + + // 「查看详情」→ 手机版运单详情页 + binding.llDetail?.setOnClickListener { + GnjMoveStashDetailActivity.start(itemView.context, bean) + } + + binding.executePendingBindings() } } diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewModel.kt b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewModel.kt index 9295d2c..a9b4b21 100644 --- a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewModel.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/list/GnjMoveStashListViewModel.kt @@ -49,6 +49,41 @@ class GnjMoveStashListViewModel : BasePageViewModel() { val number = 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>(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 = 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() { - val body = mapOf( + val body = (buildFilterParams() + mapOf( "page" to pageModel.page, "limit" to pageModel.limit, - "awbType" to businessType.value!!.ifEmpty { null }, - "wbNo" to waybill.value!!.ifEmpty { null }, - "carrier" to carrier.value!!.ifEmpty { null }, - ).toRequestBody() + )).toRequestBody() + val moved = moveState.value == "1" launchLoadingCollect({ - NetApply.api.getGnjMoveStashList( - body - ) + // 平板 moveState 恒为 "0" → 恒走 search(原行为);已移库 Tab 走 searchMoved + if (moved) NetApply.api.getGnjMoveStashMovedList(body) + else NetApply.api.getGnjMoveStashList(body) }) { onSuccess = { - pageModel.handleListBean(it) + // searchMoved 出参无移库标识,按端点打标(卡片形态/详情移库信息卡依赖) + it.list?.forEach { bean -> bean.isMoved = moved } + pageModel.handleListBean(it) { updateCheckAllStatus() } count.value = it.total } } 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() ?: return + CheckUtil.handleAllCheck(list) + updateCheckAllStatus() + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + + fun updateCheckAllStatus() { + val list = pageModel.rv?.commonAdapter()?.items + ?.filterIsInstance() ?: return + isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() } + selectedCount.value = list.count { it.checked.get() }.toString() } private fun getCountData(body: RequestBody) { diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoActivity.kt b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoActivity.kt new file mode 100644 index 0000000..971cb30 --- /dev/null +++ b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoActivity.kt @@ -0,0 +1,45 @@ +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() { + + 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 { + @JvmStatic + fun start(context: Context, mawbId: String, wbNo: String) { + context.startActivity( + Intent(context, GnjMoveStashPhotoActivity::class.java) + .putExtra(Constant.Key.ID, mawbId) + .putExtra(Constant.Key.CODE, wbNo) + ) + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMovePhotoViewModel.kt b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoViewModel.kt similarity index 61% rename from module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMovePhotoViewModel.kt rename to app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoViewModel.kt index 98a4cdd..9ad8f53 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMovePhotoViewModel.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/page/gnj/move/stash/photo/GnjMoveStashPhotoViewModel.kt @@ -1,19 +1,20 @@ -package com.lukouguoji.gjj.viewModel +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.google.gson.Gson -import com.lukouguoji.gjj.R import com.lukouguoji.module_base.base.BaseViewModel import com.lukouguoji.module_base.bean.FileBean -import com.lukouguoji.module_base.bean.IntImpMoveBean +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 @@ -30,17 +31,24 @@ 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 寸手持端专属页面) + * 国内进港移库「拍照上传」ViewModel(5.5 寸手持端专属页面) * * 图片选择/预览/删除复用 module_base 的 ImageSelectNewViewHolder(末位加号槽位)。 - * 提交走三字段规范(pic 缩略图 / originalPic 原图 / picNumber 数量)。 - * ⚠️ 后端暂无 IntImpMove 模块,提交接口按约定预接(问题清单已登记)。 + * 已有照片经 DomImpMove/detail 回填;提交走 DomImpMove/modify 最小体 + * (mawbId/remark/picNumber/pic/originalPic),与平板移交编辑页同一链路。 */ -class IntImpMovePhotoViewModel : BaseViewModel(), IOnItemClickListener { +class GnjMoveStashPhotoViewModel : BaseViewModel(), IOnItemClickListener { - val dataBean = MutableLiveData(IntImpMoveBean()) + var mawbId = "" + + /** 运单号(副标题展示,由列表带入) */ + val wbNo = MutableLiveData("") + + /** detail 回填的完整 bean(提交时保留 remark 等原值) */ + private var detailBean = GnjYiKuBean() // 图片网格适配器配置(复用公共图片选择组件) val itemViewHolder = ImageSelectNewViewHolder::class.java @@ -49,29 +57,35 @@ class IntImpMovePhotoViewModel : BaseViewModel(), IOnItemClickListener { var rv: RecyclerView? = null fun initOnCreated(intent: Intent) { - val json = intent.getStringExtra(Constant.Key.DATA).orEmpty() - if (json.isNotEmpty()) { - dataBean.value = Gson().fromJson(json, IntImpMoveBean::class.java) - } + mawbId = intent.getStringExtra(Constant.Key.ID).orEmpty() + wbNo.value = intent.getStringExtra(Constant.Key.CODE).orEmpty() // 已有照片回填(pic 缩略图 + originalPic 原图),末位补加号槽位 - val bean = dataBean.value ?: IntImpMoveBean() - val picList = bean.pic.split(",").filter { it.isNotEmpty() } - val originalList = bean.originalPic.split(",").filter { it.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()) + launchCollect({ 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() { (rv?.commonAdapter()?.items ?: emptyList()) @@ -95,15 +109,23 @@ class IntImpMovePhotoViewModel : BaseViewModel(), IOnItemClickListener { val list = (rv?.commonAdapter()?.items ?: emptyList()) .filterIsInstance() .filter { it.path.isNotEmpty() } - val bean = dataBean.value!! - bean.picNumber = list.size.toString() - bean.pic = list.joinToString(",") { MediaUtil.removeUrl(it.url) } - bean.originalPic = list.joinToString(",") { MediaUtil.removeUrl(it.originalPic) } - NetApply.api.submitIntImpMovePic(bean.toRequestBody()) + // 最小体提交:只更新图片三字段(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(ConstantEvent.EVENT_REFRESH).emit("refresh") + } getTopActivity().finish() } onFailed = { _, msg -> diff --git a/app/src/main/res/layout-sw600dp/activity_gnj_move_stash_list.xml b/app/src/main/res/layout-sw600dp/activity_gnj_move_stash_list.xml new file mode 100644 index 0000000..7582268 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/activity_gnj_move_stash_list.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/item_gnj_move_stash_list.xml b/app/src/main/res/layout-sw600dp/item_gnj_move_stash_list.xml new file mode 100644 index 0000000..b8a56ac --- /dev/null +++ b/app/src/main/res/layout-sw600dp/item_gnj_move_stash_list.xml @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml b/app/src/main/res/layout/activity_gnj_move_stash_detail.xml similarity index 93% rename from module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml rename to app/src/main/res/layout/activity_gnj_move_stash_detail.xml index 1f3d243..bd97b5a 100644 --- a/module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml +++ b/app/src/main/res/layout/activity_gnj_move_stash_detail.xml @@ -1,14 +1,12 @@ @@ -19,11 +17,11 @@ + type="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailViewModel" /> + type="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity" /> - + @@ -94,7 +92,7 @@ android:orientation="vertical" android:padding="16dp"> - + @@ -122,7 +120,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.businessName.empty ? viewModel.dataBean.businessType : viewModel.dataBean.businessName)}" + android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.cargoType)}" android:textColor="@color/phone_text_body" android:textSize="14sp" /> @@ -134,7 +132,7 @@ android:layout_height="wrap_content" android:layout_marginTop="12dp" android:layout_marginBottom="12dp" - android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.goodsName)}" + android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.goods)}" android:textColor="@color/phone_text_strong" android:textSize="22sp" android:textStyle="bold" /> @@ -150,7 +148,7 @@ android:layout_height="wrap_content" android:layout_weight="1" title='@{"件数"}' - value="@{String.valueOf(viewModel.dataBean.pc)}" /> + value="@{viewModel.dataBean.orDash(viewModel.dataBean.pc)}" /> + value='@{viewModel.dataBean.orDash(viewModel.dataBean.weight) + " kg"}' /> @@ -182,7 +180,7 @@ android:layout_marginStart="32dp" android:layout_weight="1" title='@{"代理"}' - value="@{viewModel.dataBean.agentName.empty ? viewModel.dataBean.agentCode : viewModel.dataBean.agentName}" /> + value="@{viewModel.dataBean.agentDisplay}" /> @@ -254,7 +252,7 @@ android:layout_marginStart="32dp" android:layout_weight="1" title='@{"移库时间"}' - value="@{viewModel.dataBean.opdate}" /> + value="@{viewModel.dataBean.opDate}" /> diff --git a/app/src/main/res/layout/activity_gnj_move_stash_list.xml b/app/src/main/res/layout/activity_gnj_move_stash_list.xml index 1e068ef..9b9a057 100644 --- a/app/src/main/res/layout/activity_gnj_move_stash_list.xml +++ b/app/src/main/res/layout/activity_gnj_move_stash_list.xml @@ -1,148 +1,166 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml b/app/src/main/res/layout/activity_gnj_move_stash_photo.xml similarity index 93% rename from module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml rename to app/src/main/res/layout/activity_gnj_move_stash_photo.xml index 2b6a422..67215c8 100644 --- a/module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml +++ b/app/src/main/res/layout/activity_gnj_move_stash_photo.xml @@ -1,12 +1,12 @@ + type="com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoViewModel" /> diff --git a/app/src/main/res/layout/item_gnj_move_stash_list.xml b/app/src/main/res/layout/item_gnj_move_stash_list.xml index 907131a..6b2f863 100644 --- a/app/src/main/res/layout/item_gnj_move_stash_list.xml +++ b/app/src/main/res/layout/item_gnj_move_stash_list.xml @@ -1,287 +1,227 @@ + + + + + + + - - + android:layout_marginHorizontal="12dp" + android:layout_marginTop="12dp"> - - + android:layout_height="wrap_content" + android:background="@drawable/bg_phone_card" + android:onLongClick="@{()-> activity.showCardActionPhone(bean)}" + android:orientation="vertical" + android:padding="16dp"> - - + + android:gravity="center_vertical" + android:orientation="horizontal"> + + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_marginEnd="8dp" + android:src="@drawable/ic_phone_check_round_unchecked" + android:visibility="@{bean.isMoved ? View.GONE : View.VISIBLE}" + loadImage="@{bean.checked.get() ? @drawable/ic_phone_check_round_checked : @drawable/ic_phone_check_round_unchecked}" /> - + + android:text="@{bean.wbNo}" + android:textColor="@{bean.isMoved ? @color/phone_text_title : @color/phone_primary}" + android:textSize="15sp" + android:textStyle="bold" /> - + + - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_marginStart="4dp" + android:text="已移库" + android:textColor="@color/phone_text_title" + android:textSize="13sp" /> - - + - - - - + - + + + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:layout_weight="1" + android:text='@{"航班信息:" + bean.orDash(bean.flight)}' + android:textColor="@color/phone_text_body" + android:textSize="13sp" + tools:text="航班信息:20260709/MU2311" /> - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/后端接口对接问题清单.xlsx b/documents/后端接口对接问题清单.xlsx index c6cab51de3835cb54fc351b84d9d04618f9a8227..fd2b823f3a6ef4d5a30cf65c40550e4840acb7cc 100644 GIT binary patch delta 13597 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 Rnbc0&&?bW**8Pv`{{TyMb2R_} delta 12501 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%0A@v+T#po6L4BMQ45jbma8Tfs(=M2pUE1h-qny8 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 Hga7{k|1)4- diff --git a/module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpMoveBean.kt b/module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpMoveBean.kt deleted file mode 100644 index e800406..0000000 --- a/module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpMoveBean.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.lukouguoji.module_base.bean - -import androidx.databinding.ObservableBoolean -import com.lukouguoji.module_base.interfaces.ICheck -import java.io.Serializable - -/** - * 国际进港移库 Bean(5.5 寸手持端专属页面) - * - * ⚠️ 后端目前**没有**「国际进港移库」模块(api-doc 仅有国内进港 DomImpMove 与国际出港 IntExpMove, - * 2026-07-31 核对),字段按 IntExpMove 出参约定 + 设计稿展示需求镜像定义, - * 已登记《后端接口对接问题清单》。后端按同名约定交付后前端自动生效。 - */ -data class IntImpMoveBean( - var no: String = "", // 运单号主体 - var prefix: String = "", // 运单前缀 - var wbNo: String = "", // 运单号 - var pc: Long = 0, // 件数 - var weight: Double = 0.0, // 重量 - - var fdate: String = "", // 航班日期 - var fno: String = "", // 航班号 - var flight: String = "", // 航班信息(日期/航班号 组合串) - var range: String = "", // 出运路径(如 HFE-NKG-LAX) - - var by1: String = "", // 承运人 - var awbType: String = "", // 运单类型编码 - var awbTypeName: String = "", // 运单类型名称 - var businessType: String = "", // 业务类型编码 - var businessName: String = "", // 业务类型名称 - var moveState: Int = 0, // 移库状态(0-待移库,1-已移库) - var goods: String = "", // 品名(英文) - var goodsCn: String = "", // 品名(中文) - var agentName: String = "", // 代理人 - var agentCode: String = "", // 代理代码 - var spCode: String = "", // 特码 - - var maWbId: Long = 0, // 主单主键ID - var opId: String = "", // 移库人 - var opdate: String = "", // 移库时间 - var remark: String = "", // 备注 - - // 异常照片(拍照上传,三字段规范:pic 缩略图 / originalPic 原图 / picNumber 数量) - var pic: String = "", - var originalPic: String = "", - var picNumber: String = "", - - // UI扩展字段 - 使用ObservableBoolean实现自动UI更新 - val checked: ObservableBoolean = ObservableBoolean(false), - // 卡片长按操作浮层显隐(仅 UI 状态) - val actionMenuVisible: ObservableBoolean = ObservableBoolean(false) -) : Serializable, ICheck { - - override fun getCheckObservable(): ObservableBoolean = checked - - var isSelected: Boolean - get() = checked.get() - set(value) = checked.set(value) - - // ========== 5.5 寸手持端展示辅助字段(仅用于手机版布局,不参与序列化) ========== - - /** 是否已移库(moveState:0-待移库,1-已移库) */ - val isMoved: Boolean - get() = moveState == 1 - - /** 品名:优先中文,为空回退英文 */ - val goodsName: String - get() = goodsCn.ifEmpty { goods } - - /** 航班信息展示:优先组合串,其次 日期/航班号 拼接 */ - val flightInfo: String - get() = flight.ifEmpty { - if (fdate.isEmpty() && fno.isEmpty()) "" else "$fdate/$fno" - } - - /** 详情页字段统一空值占位 */ - fun orDash(value: String?): String = if (value.isNullOrEmpty()) "-" else value -} diff --git a/module_base/src/main/java/com/lukouguoji/module_base/bean/MoveStashBean.kt b/module_base/src/main/java/com/lukouguoji/module_base/bean/MoveStashBean.kt index 8c151b9..34bdc63 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/bean/MoveStashBean.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/bean/MoveStashBean.kt @@ -6,6 +6,7 @@ import com.lukouguoji.module_base.interfaces.ICheck class MoveStashBean : ICheck { var activeId: String = "" // null var agentCode: String = "" // HFEXB + var agentName: String = "" // 代理人名称(search 出参有定义) var awbType: String = "" // 国际进港(国内中转) var businessType: String = "" // null var by1: String = "" // null @@ -49,4 +50,19 @@ class MoveStashBean : ICheck { override fun getCheckObservable(): ObservableBoolean { 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 + + /** 详情页/卡片字段统一空值占位 */ + fun orDash(value: String?): String = if (value.isNullOrEmpty()) "-" else value } \ No newline at end of file diff --git a/module_base/src/main/java/com/lukouguoji/module_base/common/Constant.kt b/module_base/src/main/java/com/lukouguoji/module_base/common/Constant.kt index 9e23ac2..d343bce 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/common/Constant.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/common/Constant.kt @@ -274,7 +274,6 @@ interface Constant { const val IntImpPickUpDLV = "AppIntImpPickUpDLV" //提取出库 // 进港移库:后端暂无该模块与权限串(2026-07-31 api-doc 核对),按 AppIntImp* 命名约定预定义, // 权限串下发前手机首页菜单不会显示该入口(已登记后端接口对接问题清单) - const val IntImpMove = "AppIntImpMove" //进港移库 const val IntImpAccidentVisa = "AppIntImpAccidentVisa" //事故签证 const val GjjManifestListActivity = "AppIntExpManifest" //舱单 const val GjjTallyListActivity = "AppIntExpTally" //理货 diff --git a/module_base/src/main/java/com/lukouguoji/module_base/http/net/Api.kt b/module_base/src/main/java/com/lukouguoji/module_base/http/net/Api.kt index ba750f0..00d6ad5 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/http/net/Api.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/http/net/Api.kt @@ -55,7 +55,6 @@ import com.lukouguoji.module_base.bean.GjAccidentVisaEditBean import com.lukouguoji.module_base.bean.IntImpAccidentVisaBean import com.lukouguoji.module_base.bean.IntImpPickUpDLVBean import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean -import com.lukouguoji.module_base.bean.IntImpMoveBean import com.lukouguoji.module_base.bean.IntImpQueryBean import com.lukouguoji.module_base.bean.GjjManifest import com.lukouguoji.module_base.bean.GjjManifestBean @@ -883,38 +882,6 @@ interface Api { @POST("IntExpMove/move") suspend fun submitIntExpMove(@Body data: RequestBody): BaseResultBean - /** - * 国际进港移库-分页查询 - * 接口路径: /IntImpMove/pageQuery - * ⚠️ 后端暂无该模块(2026-07-31 api-doc 核对),按 IntExpMove 命名约定预接,已登记问题清单 - */ - @POST("IntImpMove/pageQuery") - suspend fun getIntImpMoveList(@Body data: RequestBody): BaseListBean - - /** - * 国际进港移库-分页合计 - * 接口路径: /IntImpMove/pageQueryTotal - * ⚠️ 后端暂无该模块,同上 - */ - @POST("IntImpMove/pageQueryTotal") - suspend fun getIntImpMoveTotal(@Body data: RequestBody): BaseResultBean - - /** - * 国际进港移库-批量移库 - * 接口路径: /IntImpMove/move - * ⚠️ 后端暂无该模块,同上 - */ - @POST("IntImpMove/move") - suspend fun submitIntImpMove(@Body data: RequestBody): BaseResultBean - - /** - * 国际进港移库-异常照片上传(pic/originalPic/picNumber 三字段规范) - * 接口路径: /IntImpMove/uploadPic - * ⚠️ 后端暂无该模块,同上 - */ - @POST("IntImpMove/uploadPic") - suspend fun submitIntImpMovePic(@Body data: RequestBody): BaseResultBean - /** * 国际出港库位操作-清仓 * 接口路径: /IntExpStorageUse/updateClear @@ -1799,6 +1766,12 @@ interface Api { @POST("DomImpMove/search") suspend fun getGnjMoveStashList(@Body data: RequestBody): BaseListBean + /** + * 获取-国内进港移库-已移库列表(移库完成分页搜索,5.5 寸手机端「已移库」Tab) + */ + @POST("DomImpMove/searchMoved") + suspend fun getGnjMoveStashMovedList(@Body data: RequestBody): BaseListBean + /** * 获取-国内进港移库-列表--运单列表 */ diff --git a/module_base/src/main/java/com/lukouguoji/module_base/router/ARouterConstants.kt b/module_base/src/main/java/com/lukouguoji/module_base/router/ARouterConstants.kt index 1a87ea1..8dd2500 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/router/ARouterConstants.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/router/ARouterConstants.kt @@ -104,6 +104,7 @@ object ARouterConstants { // 国内进港移库(新版本) 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_EDIT = "/gnj/GnjYiKuEditActivity" //国内进港 移库编辑 const val ACTIVITY_URL_GNJ_YIKU_HANDOVER = "/gnj/GnjYiKuHandoverActivity" //国内进港 移交编辑/详情 @@ -182,7 +183,6 @@ object ARouterConstants { const val ACTIVITY_URL_INT_IMP_LOADING_LIST = "/gjj/IntImpLoadingListActivity" //国际进港 装机单(分拣理货) const val ACTIVITY_URL_INT_IMP_TALLY = "/gjj/IntImpTallyActivity" //国际进港 理货报告 const val ACTIVITY_URL_INT_IMP_PICK_UP_DLV = "/gjj/IntImpPickUpDLVActivity" //国际进港 提取出库 - const val ACTIVITY_URL_INT_IMP_MOVE = "/gjj/IntImpMoveActivity" //国际进港 进港移库(5.5寸手机端专属页) const val ACTIVITY_URL_INT_IMP_ACCIDENT_VISA = "/gjj/IntImpAccidentVisaActivity" //国际进港 事故签证 ///////////////// 航班查询模块 diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveActivity.kt deleted file mode 100644 index 6642ad0..0000000 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveActivity.kt +++ /dev/null @@ -1,147 +0,0 @@ -package com.lukouguoji.gjj.activity - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.os.Bundle -import androidx.databinding.ObservableBoolean -import com.alibaba.android.arouter.facade.annotation.Route -import com.lukouguoji.gjj.R -import com.lukouguoji.gjj.databinding.ActivityIntImpMoveBinding -import com.lukouguoji.gjj.viewModel.IntImpMoveViewModel -import com.lukouguoji.module_base.base.BaseBindingActivity -import com.lukouguoji.module_base.bean.IntImpMoveBean -import com.lukouguoji.module_base.common.Constant -import com.lukouguoji.module_base.common.ConstantEvent -import com.lukouguoji.module_base.impl.FlowBus -import com.lukouguoji.module_base.impl.observe -import com.lukouguoji.module_base.ktx.addOnItemClickListener -import com.lukouguoji.module_base.ktx.showToast -import com.lukouguoji.module_base.model.ConfirmDialogModel -import com.lukouguoji.module_base.model.ScanModel -import com.lukouguoji.module_base.router.ARouterConstants - -/** - * 国际进港-进港移库(5.5 寸手持端专属页面,无平板版本) - * - * 结构镜像 IntExpMoveActivity(国际出港移库),另有卡片长按「拍照」浮层(设计稿新增交互)。 - * ⚠️ 后端目前没有 IntImpMove 模块,接口按同名约定预接(问题清单已登记)。 - */ -@Route(path = ARouterConstants.ACTIVITY_URL_INT_IMP_MOVE) -class IntImpMoveActivity : BaseBindingActivity() { - - override fun layoutId() = R.layout.activity_int_imp_move - - override fun viewModelClass() = IntImpMoveViewModel::class.java - - /** 筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */ - val filterPanelVisible = ObservableBoolean(false) - - override fun initOnCreate(savedInstanceState: Bundle?) { - setBackArrow("进港移库") - binding.viewModel = viewModel - binding.activity = this - - viewModel.initDictLists() - - viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this) - binding.rv.addOnItemClickListener(viewModel) - - // 监听item选择变化事件,更新全选状态 - FlowBus.with(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) { - viewModel.onItemCheckChanged() - } - - // 监听刷新事件 - FlowBus.with(ConstantEvent.EVENT_REFRESH).observe(this) { - viewModel.refresh() - } - - viewModel.refresh() - } - - /** - * 扫码运单号 - */ - fun scanWaybill() { - ScanModel.startScan(this, Constant.RequestCode.WAYBILL) - } - - // ==================== 筛选弹层 ==================== - - fun toggleFilterPanel() { - filterPanelVisible.set(!filterPanelVisible.get()) - } - - /** - * 筛选:重置(只清空弹层内条件,不影响 Tab 与顶部搜索框) - */ - fun resetFilter() { - viewModel.flightDate.value = "" - viewModel.flightNo.value = "" - viewModel.spCode.value = "" - viewModel.awbType.value = "" - } - - fun confirmFilter() { - filterPanelVisible.set(false) - viewModel.searchClick() - } - - // ==================== 卡片长按「拍照」浮层 ==================== - - /** 卡片长按:展开操作浮层(View.OnLongClickListener 要求返回 Boolean) */ - fun showCardActionPhone(bean: IntImpMoveBean): Boolean { - bean.actionMenuVisible.set(true) - return true - } - - /** 点浮层空白处关闭 */ - fun hideCardActionPhone(bean: IntImpMoveBean) { - bean.actionMenuVisible.set(false) - } - - /** 浮层「拍照」→ 拍照上传页 */ - fun openPhoto(bean: IntImpMoveBean) { - bean.actionMenuVisible.set(false) - IntImpMovePhotoActivity.start(this, bean) - } - - /** - * 显示移库确认对话框(底部操作条 setOnActionClick 调用) - */ - fun showMoveConfirmDialog() { - val selectedItems = viewModel.getSelectedItems() - - if (selectedItems.isEmpty()) { - showToast("请至少选择一条运单") - return - } - - ConfirmDialogModel( - message = "确定要移库选中的 ${selectedItems.size} 条运单吗?", - title = "移库确认" - ) { - viewModel.submitMove() - }.show() - } - - /** - * 扫码回调 - */ - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == Constant.RequestCode.WAYBILL && resultCode == Activity.RESULT_OK) { - val code = data?.getStringExtra(Constant.Result.CODED_CONTENT) - viewModel.waybillNo.value = code - viewModel.searchClick() - } - } - - companion object { - @JvmStatic - fun start(context: Context) { - context.startActivity(Intent(context, IntImpMoveActivity::class.java)) - } - } -} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveDetailActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveDetailActivity.kt deleted file mode 100644 index 183ac90..0000000 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveDetailActivity.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.lukouguoji.gjj.activity - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import com.google.gson.Gson -import com.lukouguoji.gjj.R -import com.lukouguoji.gjj.databinding.ActivityIntImpMoveDetailBinding -import com.lukouguoji.gjj.viewModel.IntImpMoveDetailViewModel -import com.lukouguoji.module_base.base.BaseBindingActivity -import com.lukouguoji.module_base.bean.IntImpMoveBean -import com.lukouguoji.module_base.common.Constant - -/** - * 国际进港移库「运单详情」(5.5 寸手持端专属页面) - * - * 由列表卡片的「查看详情」进入;「异常照片」卡片的加号槽位跳拍照上传页。 - */ -class IntImpMoveDetailActivity : - BaseBindingActivity() { - - override fun layoutId() = R.layout.activity_int_imp_move_detail - override fun viewModelClass() = IntImpMoveDetailViewModel::class.java - - override fun initOnCreate(savedInstanceState: Bundle?) { - setBackArrow("运单详情") - binding.viewModel = viewModel - binding.activity = this - viewModel.initOnCreated(intent) - } - - /** 「异常照片」加号槽位 → 拍照上传页 */ - fun openPhoto() { - viewModel.dataBean.value?.let { IntImpMovePhotoActivity.start(this, it) } - } - - companion object { - @JvmStatic - fun start(context: Context, bean: IntImpMoveBean) { - context.startActivity( - Intent(context, IntImpMoveDetailActivity::class.java) - .putExtra(Constant.Key.DATA, Gson().toJson(bean)) - ) - } - } -} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMovePhotoActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMovePhotoActivity.kt deleted file mode 100644 index 40aba03..0000000 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMovePhotoActivity.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.lukouguoji.gjj.activity - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import com.google.gson.Gson -import com.lukouguoji.gjj.R -import com.lukouguoji.gjj.databinding.ActivityIntImpMovePhotoBinding -import com.lukouguoji.gjj.viewModel.IntImpMovePhotoViewModel -import com.lukouguoji.module_base.base.BaseBindingActivity -import com.lukouguoji.module_base.bean.IntImpMoveBean -import com.lukouguoji.module_base.common.Constant -import com.lukouguoji.module_base.ktx.addOnItemClickListener - -/** - * 国际进港移库「拍照上传」(5.5 寸手持端专属页面) - * - * 由列表卡片长按浮层「拍照」或运单详情「异常照片」加号槽位进入。 - */ -class IntImpMovePhotoActivity : - BaseBindingActivity() { - - override fun layoutId() = R.layout.activity_int_imp_move_photo - override fun viewModelClass() = IntImpMovePhotoViewModel::class.java - - override fun initOnCreate(savedInstanceState: Bundle?) { - setBackArrow("拍照上传") - binding.viewModel = viewModel - - viewModel.rv = binding.rv - binding.rv.addOnItemClickListener(viewModel) - - viewModel.initOnCreated(intent) - } - - companion object { - @JvmStatic - fun start(context: Context, bean: IntImpMoveBean) { - context.startActivity( - Intent(context, IntImpMovePhotoActivity::class.java) - .putExtra(Constant.Key.DATA, Gson().toJson(bean)) - ) - } - } -} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpMoveViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpMoveViewHolder.kt deleted file mode 100644 index 20aa602..0000000 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpMoveViewHolder.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.lukouguoji.gjj.holder - -import android.view.View -import com.lukouguoji.gjj.activity.IntImpMoveActivity -import com.lukouguoji.gjj.activity.IntImpMoveDetailActivity -import com.lukouguoji.gjj.databinding.ItemIntImpMoveBinding -import com.lukouguoji.module_base.base.BaseViewHolder -import com.lukouguoji.module_base.bean.IntImpMoveBean -import com.lukouguoji.module_base.common.ConstantEvent -import com.lukouguoji.module_base.impl.FlowBus -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch - -/** - * 国际进港移库列表项 ViewHolder(5.5 寸手持端专属页面) - * - * 卡片交互: - * · 勾选框点击 → 切换选中并通知 Activity 更新全选/已选计数 - * · 「查看详情」→ 运单详情页 - * · 卡片长按 → 「拍照」操作浮层(XML 绑定 activity 回调) - */ -class IntImpMoveViewHolder(view: View) : - BaseViewHolder(view) { - - override fun onBind(item: Any?, position: Int) { - val bean = getItemBean(item) ?: return - binding.bean = bean - binding.position = position - binding.activity = itemView.context as? IntImpMoveActivity - - // 「查看详情」→ 运单详情页 - binding.llDetail.setOnClickListener { - IntImpMoveDetailActivity.start(itemView.context, bean) - } - - // 勾选框点击 - 切换选择状态 - binding.ivIcon.setOnClickListener { - bean.checked.set(!bean.checked.get()) - binding.executePendingBindings() - - GlobalScope.launch { - FlowBus.with(ConstantEvent.EVENT_CHECK_CHANGED).emit("check_changed") - } - } - - binding.executePendingBindings() - } -} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveDetailViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveDetailViewModel.kt deleted file mode 100644 index 9154f69..0000000 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveDetailViewModel.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukouguoji.gjj.viewModel - -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.IntImpMoveBean -import com.lukouguoji.module_base.common.Constant - -/** - * 国际进港移库「运单详情」ViewModel(5.5 寸手持端专属页面) - * - * 列表项数据已包含详情页所需全部字段,故直接由列表带入,不额外请求接口 - * (后端暂无 IntImpMove 模块,也没有 detail 接口可查,问题清单已登记)。 - */ -class IntImpMoveDetailViewModel : BaseViewModel() { - - val dataBean = MutableLiveData(IntImpMoveBean()) - - fun initOnCreated(intent: Intent) { - val json = intent.getStringExtra(Constant.Key.DATA).orEmpty() - if (json.isNotEmpty()) { - dataBean.value = Gson().fromJson(json, IntImpMoveBean::class.java) - } - } -} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveViewModel.kt deleted file mode 100644 index e9bb2de..0000000 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveViewModel.kt +++ /dev/null @@ -1,187 +0,0 @@ -package com.lukouguoji.gjj.viewModel - -import androidx.lifecycle.MutableLiveData -import com.lukouguoji.gjj.R -import com.lukouguoji.gjj.holder.IntImpMoveViewHolder -import com.lukouguoji.module_base.base.BasePageViewModel -import com.lukouguoji.module_base.bean.IntImpMoveBean -import com.lukouguoji.module_base.http.net.NetApply -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.CheckUtil -import com.lukouguoji.module_base.util.DictUtils -import dev.utils.app.info.KeyValue - -/** - * 国际进港移库 ViewModel(5.5 寸手持端专属页面,无平板版本) - * - * 结构镜像 IntExpMoveViewModel(国际出港移库)。 - * ⚠️ 后端目前没有 IntImpMove 模块(2026-07-31 api-doc 核对),接口按同名约定预接, - * 实机会请求失败(问题清单已登记),后端交付后自动生效。 - */ -class IntImpMoveViewModel : BasePageViewModel(), IOnItemClickListener { - - // ========== 搜索字段(设计稿筛选弹层:航班日期 / 航班号 / 特码 / 运单类型) ========== - val flightDate = MutableLiveData("") // 航班日期 - val flightNo = MutableLiveData("") // 航班号 - val spCode = MutableLiveData("") // 特码 - val awbType = MutableLiveData("") // 运单类型 - val moveState = MutableLiveData("0") // 移库状态(默认「待移库」Tab) - val waybillNo = MutableLiveData("") // 运单号 - - // ========== 下拉数据 ========== - val spCodeList = MutableLiveData>(emptyList()) - val awbTypeList = MutableLiveData>(emptyList()) - - /** 状态 Tab 配置(设计稿只有两个 Tab,无「全部」) */ - val moveStateTabs = MutableLiveData( - listOf(KeyValue("待移库", "0"), KeyValue("已移库", "1")) - ) - - /** 状态 Tab 角标(与 [moveStateTabs] 等长) */ - val moveStateCounts = MutableLiveData(listOf("", "")) - - /** 已选数量(底部操作条「已选 N 项」) */ - val selectedCount = MutableLiveData("0") - - // ========== 全选状态 ========== - val isAllChecked = MutableLiveData(false) - - // ========== 适配器配置 ========== - val itemViewHolder = IntImpMoveViewHolder::class.java - val itemLayoutId = R.layout.item_int_imp_move - - /** - * 初始化下拉字典(特码 + 运单类型,type=II 国际进港) - */ - fun initDictLists() { - // flag=1 国际;ieFlag 必须留空——传值后端返回空数组(与既有国际页面口径一致) - DictUtils.getSpecialCodeList(addAll = false, flag = 1, ieFlag = "") { list -> - spCodeList.postValue(listOf(KeyValue("全部", "")) + list) - } - DictUtils.getWaybillTypeList(type = "II", addAll = true) { - awbTypeList.postValue(it) - } - } - - fun searchClick() { - refresh() - } - - /** - * 全选/取消全选 - */ - fun toggleSelectAll() { - val adapter = pageModel.rv?.commonAdapter() ?: return - val list = adapter.items.filterIsInstance() - - CheckUtil.handleAllCheck(list) - updateCheckAllStatus() - adapter.notifyDataSetChanged() - } - - /** - * 更新全选状态与已选计数 - */ - fun updateCheckAllStatus() { - val adapter = pageModel.rv?.commonAdapter() ?: return - val list = adapter.items.filterIsInstance() - - isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() } - selectedCount.value = list.count { it.checked.get() }.toString() - } - - /** - * 单个item选择变化时调用(从Activity接收事件) - */ - fun onItemCheckChanged() { - updateCheckAllStatus() - } - - /** - * 获取选中的运单 - */ - fun getSelectedItems(): List { - val adapter = pageModel.rv?.commonAdapter() ?: return emptyList() - return adapter.items.filterIsInstance().filter { it.checked.get() } - } - - /** - * 提交移库(在Activity中显示确认对话框后调用) - */ - fun submitMove() { - val selectedItems = getSelectedItems() - - if (selectedItems.isEmpty()) { - showToast("请至少选择一条运单") - return - } - - launchLoadingCollect({ NetApply.api.submitIntImpMove(selectedItems.toRequestBody()) }) { - onSuccess = { - showToast("移库成功") - refresh() - } - onFailed = { _, msg -> - showToast(msg.noNull("移库失败")) - } - } - } - - /** - * 获取列表数据 - */ - override fun getData() { - // 构建筛选参数(只传递非空值;参数名按 IntExpMove 同名约定,待后端交付核对) - val filterParams = mutableMapOf() - - flightDate.value?.takeIf { it.isNotEmpty() }?.let { filterParams["fdate"] = it } - flightNo.value?.takeIf { it.isNotEmpty() }?.let { filterParams["fno"] = it } - spCode.value?.takeIf { it.isNotEmpty() }?.let { filterParams["spCode"] = it } - awbType.value?.takeIf { it.isNotEmpty() }?.let { filterParams["awbType"] = it } - moveState.value?.takeIf { it.isNotEmpty() }?.let { filterParams["moveState"] = it } - waybillNo.value?.takeIf { it.isNotEmpty() }?.let { filterParams["wbNo"] = it } - - val listParams = (filterParams + mapOf( - "pageNum" to pageModel.page, - "pageSize" to pageModel.limit - )).toRequestBody() - - launchLoadingCollect({ NetApply.api.getIntImpMoveList(listParams) }) { - onSuccess = { - pageModel.handleListBean(it) { updateCheckAllStatus() } - } - } - - // Tab 角标计数(复用统计接口,moveState=0/1 各查一次) - loadTabCounts(filterParams) - } - - private fun loadTabCounts(filterParams: Map) { - listOf("0", "1").forEachIndexed { index, state -> - val params = (filterParams + mapOf("moveState" to state)).toRequestBody() - launchCollect({ NetApply.api.getIntImpMoveTotal(params) }) { - onSuccess = { result -> - val count = (result.data?.wbNumber ?: 0).toString() - val current = moveStateCounts.value?.toMutableList() ?: mutableListOf("", "") - if (index < current.size) { - current[index] = count - moveStateCounts.value = current - } - } - } - } - } - - /** - * Item点击事件处理(点击已在ViewHolder中处理) - */ - override fun onItemClick(position: Int, type: Int) { - // 卡片交互均在 ViewHolder / XML 中处理 - } -} diff --git a/module_gjj/src/main/res/layout/activity_int_imp_move.xml b/module_gjj/src/main/res/layout/activity_int_imp_move.xml deleted file mode 100644 index c650e7c..0000000 --- a/module_gjj/src/main/res/layout/activity_int_imp_move.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/module_gjj/src/main/res/layout/item_int_imp_move.xml b/module_gjj/src/main/res/layout/item_int_imp_move.xml deleted file mode 100644 index 38d6bdb..0000000 --- a/module_gjj/src/main/res/layout/item_int_imp_move.xml +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/module_p/src/main/java/com/sixin/module_p/ui/enter/gj/GjViewModel.kt b/module_p/src/main/java/com/sixin/module_p/ui/enter/gj/GjViewModel.kt index 2348421..45b2972 100644 --- a/module_p/src/main/java/com/sixin/module_p/ui/enter/gj/GjViewModel.kt +++ b/module_p/src/main/java/com/sixin/module_p/ui/enter/gj/GjViewModel.kt @@ -78,14 +78,6 @@ class GjViewModel : BaseViewModel() { Constant.AuthName.GjjWareHouseActivity, ARouterConstants.ACTIVITY_URL_GJJ_WARE_HOUSE ), - // 进港移库:后端暂无该模块与权限串 AppIntImpMove(问题清单已登记), - // 权限串下发前此入口不会显示;实机验证走 debug 直启 - ActionBean( - "进港移库", - R.drawable.gjc_yi_ku_icon, // 复用出港移库图标(Pad 端无进港移库菜单) - Constant.AuthName.IntImpMove, - ARouterConstants.ACTIVITY_URL_INT_IMP_MOVE - ), ActionBean( "进港查询", R.drawable.gjj_query_icon, diff --git a/module_p/src/main/java/com/sixin/module_p/ui/enter/gn/GnViewModel.kt b/module_p/src/main/java/com/sixin/module_p/ui/enter/gn/GnViewModel.kt index c4873be..907c894 100644 --- a/module_p/src/main/java/com/sixin/module_p/ui/enter/gn/GnViewModel.kt +++ b/module_p/src/main/java/com/sixin/module_p/ui/enter/gn/GnViewModel.kt @@ -1,12 +1,14 @@ package com.sixin.module_p.ui.enter.gn import android.content.Intent +import com.alibaba.android.arouter.launcher.ARouter import com.lukouguoji.module_base.base.BaseViewModel import com.lukouguoji.module_base.bean.ActionBean import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.db.perference.SharedPreferenceUtil import com.lukouguoji.module_base.ktx.logd import com.lukouguoji.module_base.ktx.loge +import com.lukouguoji.module_base.router.ARouterConstants import com.sixin.module_p.R import com.sixin.module_p.ui.enter.gn.document.PdaDocumentHandoverActivity import com.sixin.module_p.ui.gnc.cunfang.list.PdaGncCunFangListActivity @@ -57,11 +59,25 @@ class GnViewModel : BaseViewModel() { R.mipmap.gnc_document_handover, Constant.AuthName.DocumentHandover ), + ActionBean( + "进港移库", + R.drawable.gnj_yi_ku_icon, // 图标复制自 module_gnj(module_p 看不到业务模块资源) + Constant.AuthName.GnjYiKu, // 权限串与 Pad 端菜单一致(AppDomImpMove) + ARouterConstants.ACTIVITY_URL_GNJ_MOVE_STASH_LIST // 与 Pad 首页菜单同一跳转页(app 模块,走路由) + ), ) .filter { SharedPreferenceUtil.getString(Constant.Share.authList).contains(it.key) } fun onItemClick(position: Int) { + val bean = actionList.getOrNull(position) ?: return + + // 配了路由的直接跳(5.5 寸适配页走这条,与 GjViewModel 同模式) + if (bean.route.isNotEmpty()) { + ARouter.getInstance().build(bean.route).navigation() + return + } + when (val title = actionList[position].title) { "出港收运" -> { GncUnShouYunActivity.start(DevUtils.getTopActivity()) diff --git a/module_p/src/main/res/drawable-xxhdpi/gnj_yi_ku_icon.png b/module_p/src/main/res/drawable-xxhdpi/gnj_yi_ku_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..26454c68eca0c13ef996fbcf26e45cc563be78fc GIT binary patch literal 26578 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