diff --git a/.claude/skills/phone-adapt/references/api-doc.md b/.claude/skills/phone-adapt/references/api-doc.md index 93a3cb5..eb54f1b 100644 --- a/.claude/skills/phone-adapt/references/api-doc.md +++ b/.claude/skills/phone-adapt/references/api-doc.md @@ -109,6 +109,16 @@ curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \ 运单号筛选参数名是 **`no`**(平板既有口径);`chargeName`/`chargeTime` = 柜台办理人/缴费(提取)时间, `dlvTime` = 出库时间,`dlvName` 按提货人语义推断作「出库人」展示(待确认 #29); 确认出库接口 `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 +- `IntImpMove`(进港移库,2026-07-31 文档核对):**后端整个模块不存在**(api-doc 仅有国内进港 + `DomImpMove` 与国际出港 `IntExpMove`),权限串也没有对应 key(缺口 #31)。前端按 IntExpMove + 同名约定预接 `pageQuery`/`pageQueryTotal`/`move`/`uploadPic` 四接口 + 预定义权限串 + `AppIntImpMove`,后端交付后前端零改动生效 - `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 94db5db..a358d34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,41 @@ ## [Unreleased] ### 新增 (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) +- 出港查询/进港查询卡片「代理人」空值兜底失效:转换器把 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) 手机布局 = PhoneSearchBar(运单号+扫码+筛选)+ **PhoneStatusTab 待出库/已出库**(`outState` 为 pageQuery 文档定义入参 0/1,Tab 真实过滤 + 角标双 pageQueryTotal,抓包证实携带)+ diff --git a/CLAUDE.md b/CLAUDE.md index 64be4e6..fdefd92 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1066,6 +1066,8 @@ DataBinding 适配器统一放在 `PhoneWidgetKtx.kt`(同名属性按控件类 9 个筛选项曾把「重置/确认」挤出屏幕)。 出港仓库追加:`bg_phone_btn_disabled`(灰底禁用按钮,与 `bg_phone_btn_weak` 同 10dp 圆角, 卡片操作按钮禁用态配 `phone_tag_gray_text` 文字)。 +进港移库追加:`bg_phone_btn_green`(绿色主按钮,拍照上传「确认提交」)、`bg_phone_card_header_orange` +(橙色详情卡表头,异常照片)、`bg_phone_photo_add`(虚线加号槽位)、色值 `phone_amber`。 ⚠️ **弹层与浮动按钮的层级**:`PhoneFilterPanel` 默认无 elevation,页面里有带 elevation 的浮动按钮(FAB)时 会穿透到弹层之上——给弹层实例加 `android:elevation="8dp"`(大于 FAB 的 6dp)。 @@ -1141,6 +1143,22 @@ override fun dispatchRestoreInstanceState(container: SparseArray) = 缺口 #28:无 fdate 入参、出参无车牌(提货车辆)/目的港/业务类型。 ⚠️ 踩坑:`checkAllClick` 全选后底部「已选 N 项」不更新——批量勾选不走 FlowBus 单卡事件, 必须在 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 出港查询同步修正)) + - 「进港移库」`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 全量验证) - 「进港仓库」`IntImpStorageUseActivity`(module_gjj,07 出港仓库的进港镜像;**首页菜单实际跳转页, 旧版 `GjjWareHouseActivity` 路由已注释弃用**。差异:卡片/筛选用始发站(fdep 入参)替代目的站; 搜索框「运单号/航班号」分流(含字母→fno、其余→wbNo,抓包证实);筛选 5 项无入参(缺口 #25, @@ -1165,8 +1183,10 @@ override fun dispatchRestoreInstanceState(container: SparseArray) = 双变体首例:手机竖排时间线由 Activity `buildVerticalSteps` 动态构建,平板横向步骤条不动; 手机标题按形态切「运单追踪」)。平板侧滑筛选面板代码已判空守卫(手机变体无 filter_panel include,Binding 字段为 @Nullable) +- ✅ **设计稿 11 个入口(01~11)已于 2026-07-31 全部适配完成** - ⏳ **待办**: - `PhoneSearchBar` 接入 AutoQuery 联想(`AutoQueryManager` 目前只对接了 Pad 系组件) + - 进港移库待后端交付 IntImpMove 模块(四接口 + 权限串 AppIntImpMove,缺口 #31)后联调 - 出库交接 Tab 角标恒 0:`IntExpOutHandover/pageQueryTotal` 出参恒 0(后端缺陷,问题清单 #6), 后端修复后角标自动恢复(`handoverState` 过滤本身已于 2026-07-29 实机验证通过)生效 - 📋 **接口对接**:适配中发现的后端接口缺口统一记录在 `documents/后端接口对接问题清单.xlsx` @@ -1261,6 +1281,7 @@ 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)` 补上 | | 布局构建报 `Error: The string "--" is not permitted within comments` | XML 注释中出现连续双横线(如写"兜底 \"--\""),XML 规范禁止 | 注释里改用文字描述(如「兜底双横线」),代码绑定表达式中的 `"--"` 不受影响 | | 多选页手机版点「全选」后底部「已选 N 项」不更新(单卡勾选正常) | 批量勾选在 `checkAllClick` 里直接 set,不走 FlowBus 的单卡 EVENT_CHECK_CHANGED 事件,`selectedCountText` 只在事件回调里更新 | `checkAllClick` 末尾自行同步 `selectedCountText`(平板布局不绑定该字段,无影响) | +| XML 里 `bean.a ?? bean.b` 兜底不生效,字段明明有值却显示占位符 | 网络层转换器把 JSON null 归一成空串 `""`,DataBinding 的 `??` 只判 null 不判空串,永远取到左侧的空串 | 在 Bean 上加计算属性做 `isNullOrEmpty` 双兜底(如 `agentDisplay`),XML 绑计算属性(进港查询/出港查询代理人已踩过) | --- diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml index 543f109..8717d8c 100644 --- a/app/src/debug/AndroidManifest.xml +++ b/app/src/debug/AndroidManifest.xml @@ -152,5 +152,17 @@ android:exported="true" tools:replace="android:exported" /> + + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 00d91a6..2a9b492 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -565,6 +565,27 @@ android:exported="false" android:screenOrientation="unspecified" /> + + + + + + + + + + /** + * 国际进港移库-分页查询 + * 接口路径: /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 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 700b17f..1a87ea1 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 @@ -182,6 +182,7 @@ 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_base/src/main/res/drawable/bg_phone_btn_green.xml b/module_base/src/main/res/drawable/bg_phone_btn_green.xml new file mode 100644 index 0000000..d868fee --- /dev/null +++ b/module_base/src/main/res/drawable/bg_phone_btn_green.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/module_base/src/main/res/drawable/bg_phone_card_header_orange.xml b/module_base/src/main/res/drawable/bg_phone_card_header_orange.xml new file mode 100644 index 0000000..d9abdd0 --- /dev/null +++ b/module_base/src/main/res/drawable/bg_phone_card_header_orange.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/module_base/src/main/res/drawable/bg_phone_photo_add.xml b/module_base/src/main/res/drawable/bg_phone_photo_add.xml new file mode 100644 index 0000000..a8ff1e7 --- /dev/null +++ b/module_base/src/main/res/drawable/bg_phone_photo_add.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/module_base/src/main/res/values/colors.xml b/module_base/src/main/res/values/colors.xml index 074da37..a369cd7 100644 --- a/module_base/src/main/res/values/colors.xml +++ b/module_base/src/main/res/values/colors.xml @@ -79,5 +79,6 @@ #6366F1 #FEF3C7 #D97706 + #F59E0B \ No newline at end of file diff --git a/module_gjc/src/main/res/layout/item_gjc_query.xml b/module_gjc/src/main/res/layout/item_gjc_query.xml index def293b..f41cdc7 100644 --- a/module_gjc/src/main/res/layout/item_gjc_query.xml +++ b/module_gjc/src/main/res/layout/item_gjc_query.xml @@ -189,7 +189,7 @@ android:layout_weight="1" android:ellipsize="end" android:maxLines="1" - android:text='@{"代理人:" + bean.orDash(bean.agentName ?? bean.agentCode)}' + android:text='@{"代理人:" + bean.orDash(bean.agentDisplay)}' android:textColor="@color/phone_text_body" android:textSize="13sp" tools:text="代理人:中外运" /> 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 new file mode 100644 index 0000000..6642ad0 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveActivity.kt @@ -0,0 +1,147 @@ +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 new file mode 100644 index 0000000..183ac90 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMoveDetailActivity.kt @@ -0,0 +1,46 @@ +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 new file mode 100644 index 0000000..40aba03 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpMovePhotoActivity.kt @@ -0,0 +1,45 @@ +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/activity/IntImpQueryActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryActivity.kt index f10c084..5d5a28f 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryActivity.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryActivity.kt @@ -19,6 +19,7 @@ import com.lukouguoji.module_base.ktx.getLifecycleOwner import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter import com.lukouguoji.module_base.ktx.setUpperCaseLetterFilter import com.lukouguoji.module_base.router.ARouterConstants +import com.lukouguoji.module_base.util.DeviceUtil /** * 国际进港查询列表页 @@ -50,22 +51,35 @@ class IntImpQueryActivity : viewModel.initSpecialCodeList() viewModel.initFilterLists() - // 观察筛选面板显示状态 - viewModel.filterVisible.observe(this) { visible -> - if (visible) showFilterPanel() else hideFilterPanel() + // 平板变体才有侧滑筛选面板(filter_panel/filter_overlay 手机变体不存在,Binding 字段为 null) + val padFilterPanel = binding.filterPanel + 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() - // 始发港:仅大写字母 - binding.filterPanel.filterOrigin.et.setUpperCaseLetterFilter() + // 手机形态专属:已入库/已出库统计计数 + if (DeviceUtil.isPhone()) { + viewModel.initPhoneExtras() + } viewModel.refresh() } private fun showFilterPanel() { - val panel = binding.filterPanel.root - val overlay = binding.filterOverlay + val panel = binding.filterPanel?.root ?: return + val overlay = binding.filterOverlay ?: return val panelWidth = window.decorView.width / 3 panel.layoutParams = (panel.layoutParams as FrameLayout.LayoutParams).apply { @@ -87,8 +101,8 @@ class IntImpQueryActivity : } private fun hideFilterPanel() { - val panel = binding.filterPanel.root - val overlay = binding.filterOverlay + val panel = binding.filterPanel?.root ?: return + val overlay = binding.filterOverlay ?: return if (panel.visibility != View.VISIBLE) return 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 new file mode 100644 index 0000000..20aa602 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpMoveViewHolder.kt @@ -0,0 +1,48 @@ +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/holder/IntImpQueryViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpQueryViewHolder.kt index 5c618c7..67bd5dc 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpQueryViewHolder.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpQueryViewHolder.kt @@ -19,9 +19,9 @@ class IntImpQueryViewHolder(view: View) : // 注册整行点击事件 notifyItemClick(position, binding.ll) - // 侧滑菜单 - 修改按钮 - binding.btnEdit.setOnClickListener { - binding.swipeMenu.quickClose() + // 侧滑菜单 - 修改按钮(平板变体独有;手机变体无侧滑编辑入口,字段为 null) + binding.btnEdit?.setOnClickListener { + binding.swipeMenu?.quickClose() clickListener?.onItemClick(position, 2000) // type=2000表示修改操作 } } 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 new file mode 100644 index 0000000..9154f69 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveDetailViewModel.kt @@ -0,0 +1,26 @@ +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/IntImpMovePhotoViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMovePhotoViewModel.kt new file mode 100644 index 0000000..98a4cdd --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMovePhotoViewModel.kt @@ -0,0 +1,152 @@ +package com.lukouguoji.gjj.viewModel + +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.common.Constant +import com.lukouguoji.module_base.http.net.NetApply +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.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 + +/** + * 国际进港移库「拍照上传」ViewModel(5.5 寸手持端专属页面) + * + * 图片选择/预览/删除复用 module_base 的 ImageSelectNewViewHolder(末位加号槽位)。 + * 提交走三字段规范(pic 缩略图 / originalPic 原图 / picNumber 数量)。 + * ⚠️ 后端暂无 IntImpMove 模块,提交接口按约定预接(问题清单已登记)。 + */ +class IntImpMovePhotoViewModel : BaseViewModel(), IOnItemClickListener { + + val dataBean = MutableLiveData(IntImpMoveBean()) + + // 图片网格适配器配置(复用公共图片选择组件) + 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) { + val json = intent.getStringExtra(Constant.Key.DATA).orEmpty() + if (json.isNotEmpty()) { + dataBean.value = Gson().fromJson(json, IntImpMoveBean::class.java) + } + + // 已有照片回填(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()) + } + } + + /** + * 确认提交:先补传未上传的本地图片,再按三字段规范提交 + */ + fun submitClick() { + (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() + .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()) + }) { + onSuccess = { + showToast("提交成功") + 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 -> {} + } + } +} 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 new file mode 100644 index 0000000..e9bb2de --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMoveViewModel.kt @@ -0,0 +1,187 @@ +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/java/com/lukouguoji/gjj/viewModel/IntImpQueryViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpQueryViewModel.kt index c23da61..2dd1708 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpQueryViewModel.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpQueryViewModel.kt @@ -64,6 +64,24 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener { val awbTypeList = MutableLiveData>(emptyList()) val businessTypeList = MutableLiveData>(emptyList()) + // ==================== 5.5寸手机版专属 ==================== + // 三格统计:已入库 / 已出库(总票数复用 totalCount,为服务端全量口径) + // pageQueryTotal 无分项统计出参——已入库/已出库为「已加载数据」客户端计数口径, + // 与卡片状态标签同一判定(dlvTime / inDate,见 IntImpQueryBean) + val storedCount = MutableLiveData("0") + val outboundCount = MutableLiveData("0") + + private var phoneExtrasEnabled = false + private var phoneLoadedList: List = emptyList() + + /** + * 手机形态专属初始化(Activity 在 DeviceUtil.isPhone() 时调用)。 + * 平板端不调用,请求次数与行为零变化。 + */ + fun initPhoneExtras() { + phoneExtrasEnabled = true + } + // ==================== 筛选条件 ==================== val spCode = MutableLiveData("") val flightNo = MutableLiveData("") @@ -165,6 +183,14 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener { }) { onSuccess = { pageInfo -> pageModel.handleListBean(pageInfo.toBaseListBean()) + + // 手机三格统计:对已加载数据做客户端计数(平板不触发) + if (phoneExtrasEnabled) { + val pageItems = pageInfo.list ?: emptyList() + phoneLoadedList = + if (pageModel.page <= 1) pageItems else phoneLoadedList + pageItems + updatePhoneCounts() + } } } @@ -180,6 +206,16 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener { } } + /** + * 手机三格统计(已入库/已出库):与卡片状态标签同一判定口径(dlvTime / inDate), + * 统计范围为已加载的列表数据。 + */ + private fun updatePhoneCounts() { + outboundCount.value = phoneLoadedList.count { it.isQueryOutbound }.toString() + storedCount.value = + phoneLoadedList.count { !it.isQueryOutbound && it.isQueryStored }.toString() + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (resultCode == Activity.RESULT_OK && data != null) { when (requestCode) { diff --git a/module_gjj/src/main/res/layout-sw600dp/activity_int_imp_query.xml b/module_gjj/src/main/res/layout-sw600dp/activity_int_imp_query.xml new file mode 100644 index 0000000..ab0223b --- /dev/null +++ b/module_gjj/src/main/res/layout-sw600dp/activity_int_imp_query.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout-sw600dp/item_int_imp_query.xml b/module_gjj/src/main/res/layout-sw600dp/item_int_imp_query.xml new file mode 100644 index 0000000..5fd2b42 --- /dev/null +++ b/module_gjj/src/main/res/layout-sw600dp/item_int_imp_query.xml @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000..c650e7c --- /dev/null +++ b/module_gjj/src/main/res/layout/activity_int_imp_move.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml b/module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml new file mode 100644 index 0000000..1f3d243 --- /dev/null +++ b/module_gjj/src/main/res/layout/activity_int_imp_move_detail.xml @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml b/module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml new file mode 100644 index 0000000..2b6a422 --- /dev/null +++ b/module_gjj/src/main/res/layout/activity_int_imp_move_photo.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_query.xml b/module_gjj/src/main/res/layout/activity_int_imp_query.xml index ab0223b..093968d 100644 --- a/module_gjj/src/main/res/layout/activity_int_imp_query.xml +++ b/module_gjj/src/main/res/layout/activity_int_imp_query.xml @@ -1,205 +1,201 @@ + + - + + - + android:background="@color/phone_page_bg"> - - - - - + android:layout_height="match_parent" + android:orientation="vertical"> - + + + + android:layout_height="wrap_content" + android:background="@color/white" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingTop="16dp" + android:paddingBottom="12dp"> - - + + + + + + + + - - + - - - - - - - - - - - - - + android:orientation="vertical" + android:paddingHorizontal="12dp" + android:paddingVertical="10dp"> - + - - - - - - - + - - - - - - - - + + 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"> + + + android:orientation="vertical" + android:paddingHorizontal="12dp" + android:paddingVertical="10dp"> + android:text="已入库" + android:textColor="@color/phone_text_body" + android:textSize="12sp" /> + tools:text="2" /> + + + + + + + + + + + + + + tools:text="2" /> @@ -207,28 +203,131 @@ - - + + android:layout_height="0dp" + android:layout_weight="1"> - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 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 new file mode 100644 index 0000000..38d6bdb --- /dev/null +++ b/module_gjj/src/main/res/layout/item_int_imp_move.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/item_int_imp_query.xml b/module_gjj/src/main/res/layout/item_int_imp_query.xml index 5fd2b42..241864a 100644 --- a/module_gjj/src/main/res/layout/item_int_imp_query.xml +++ b/module_gjj/src/main/res/layout/item_int_imp_query.xml @@ -1,325 +1,226 @@ + + + + - - - - - - - - - - + android:layout_marginHorizontal="16dp" + android:layout_marginTop="12dp" + android:background="@drawable/bg_phone_card" + android:orientation="vertical" + android:padding="14dp"> + + android:gravity="center_vertical" + android:orientation="horizontal"> - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_weight="1" + android:text="@{bean.wbNo}" + android:textColor="@color/phone_text_title" + android:textSize="16sp" + android:textStyle="bold" + tools:text="78133363396" /> - - + + - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - - - - + + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="6dp" + android:text='@{"件数:" + (bean.inPc != null ? String.valueOf(bean.inPc) : "--")}' + android:textColor="@color/phone_text_body" + android:textSize="13sp" + tools:text="件数:444" /> - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 d6309af..2348421 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,6 +78,20 @@ 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, + Constant.AuthName.GjjQueryListActivity, + ARouterConstants.ACTIVITY_URL_GJJ_QUERY_LIST + ), ActionBean( "提取出库", R.drawable.gjj_chu_ku_icon, diff --git a/module_p/src/main/res/drawable-xxhdpi/gjj_query_icon.png b/module_p/src/main/res/drawable-xxhdpi/gjj_query_icon.png new file mode 100644 index 0000000..0e98155 Binary files /dev/null and b/module_p/src/main/res/drawable-xxhdpi/gjj_query_icon.png differ