From 0a9230860b91c081abaab36a4291cc27cd4987f4 Mon Sep 17 00:00:00 2001 From: YANGJIANKUAN Date: Thu, 5 Mar 2026 15:15:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BD=E9=99=85=E8=BF=9B=E6=B8=AF?= =?UTF-8?q?=E7=94=B5=E6=8A=A5=E8=A7=A3=E6=9E=90=E6=8E=A5=E5=8F=A3=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=E5=8F=8A=E4=BB=93=E5=BA=93/=E6=8F=90=E5=8F=96?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 电报解析:航班级联查询(自动填充目的站、始发站下拉) - 电报解析:修复接口返回格式(PageInfo 非 BaseResultBean) - 电报解析:报文类型必选校验 - 新增进港仓库、提取记录页面及菜单入口 - 修复 Spinner 空列表时 hint 不显示的问题 Co-Authored-By: Claude Opus 4.6 --- .claude/settings.local.json | 5 +- app/src/main/AndroidManifest.xml | 21 + .../aerologic/ui/fragment/HomeFragment.kt | 26 ++ .../lukouguoji/module_base/LoginActivity.kt | 2 +- .../module_base/adapter/SpinnerAdapter.kt | 20 + .../bean/IntImpPickUpRecordBean.kt | 37 ++ .../lukouguoji/module_base/common/Constant.kt | 1 + .../lukouguoji/module_base/http/net/Api.kt | 79 +++- .../module_base/router/ARouterConstants.kt | 1 + .../gjj/activity/GjjWareHouseActivity.kt | 2 +- .../activity/IntImpPickUpRecordActivity.kt | 88 ++++ .../IntImpPickUpRecordDetailsActivity.kt | 35 ++ .../gjj/activity/IntImpStorageUseActivity.kt | 178 ++++++++ .../gjj/dialog/IntImpInStorageDialogModel.kt | 74 ++++ .../dialog/IntImpModifyStorageDialogModel.kt | 74 ++++ .../gjj/dialog/IntImpMoveClearDialogModel.kt | 47 ++ .../holder/IntImpPickUpRecordViewHolder.kt | 32 ++ .../holder/IntImpStorageUseSubViewHolder.kt | 45 ++ .../gjj/holder/IntImpStorageUseViewHolder.kt | 54 +++ .../gjj/viewModel/IntImpMsgParseViewModel.kt | 101 ++++- .../IntImpPickUpRecordDetailsViewModel.kt | 42 ++ .../viewModel/IntImpPickUpRecordViewModel.kt | 191 ++++++++ .../viewModel/IntImpStorageUseViewModel.kt | 287 ++++++++++++ .../res/layout/activity_int_imp_msg_parse.xml | 9 +- .../activity_int_imp_pick_up_record.xml | 200 +++++++++ ...ctivity_int_imp_pick_up_record_details.xml | 274 ++++++++++++ .../layout/activity_int_imp_storage_use.xml | 252 +++++++++++ .../res/layout/dialog_int_imp_in_storage.xml | 87 ++++ .../layout/dialog_int_imp_modify_storage.xml | 87 ++++ .../res/layout/dialog_int_imp_move_clear.xml | 86 ++++ .../layout/item_int_imp_pick_up_record.xml | 304 +++++++++++++ .../res/layout/item_int_imp_storage_use.xml | 411 ++++++++++++++++++ .../layout/item_int_imp_storage_use_sub.xml | 90 ++++ 33 files changed, 3226 insertions(+), 16 deletions(-) create mode 100644 module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpPickUpRecordBean.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordActivity.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordDetailsActivity.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpStorageUseActivity.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpInStorageDialogModel.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpModifyStorageDialogModel.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpMoveClearDialogModel.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpPickUpRecordViewHolder.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseSubViewHolder.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseViewHolder.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordDetailsViewModel.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordViewModel.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpStorageUseViewModel.kt create mode 100644 module_gjj/src/main/res/layout/activity_int_imp_pick_up_record.xml create mode 100644 module_gjj/src/main/res/layout/activity_int_imp_pick_up_record_details.xml create mode 100644 module_gjj/src/main/res/layout/activity_int_imp_storage_use.xml create mode 100644 module_gjj/src/main/res/layout/dialog_int_imp_in_storage.xml create mode 100644 module_gjj/src/main/res/layout/dialog_int_imp_modify_storage.xml create mode 100644 module_gjj/src/main/res/layout/dialog_int_imp_move_clear.xml create mode 100644 module_gjj/src/main/res/layout/item_int_imp_pick_up_record.xml create mode 100644 module_gjj/src/main/res/layout/item_int_imp_storage_use.xml create mode 100644 module_gjj/src/main/res/layout/item_int_imp_storage_use_sub.xml diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6d60244..1f396c9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -55,7 +55,10 @@ "Bash(xargs:*)", "Bash(unzip:*)", "WebFetch(domain:gainscha.github.io)", - "WebFetch(domain:m.gainscha.com)" + "WebFetch(domain:m.gainscha.com)", + "Bash(git add:*)", + "Bash(git commit:*)", + "WebFetch(domain:support.claude.com)" ], "deny": [], "ask": [] diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b4ac4f6..853ae5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -419,6 +419,27 @@ android:exported="false" android:screenOrientation="userLandscape" /> + + + + + + + + + diff --git a/app/src/main/java/com/lukouguoji/aerologic/ui/fragment/HomeFragment.kt b/app/src/main/java/com/lukouguoji/aerologic/ui/fragment/HomeFragment.kt index 8eb01b8..8608ae8 100644 --- a/app/src/main/java/com/lukouguoji/aerologic/ui/fragment/HomeFragment.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/ui/fragment/HomeFragment.kt @@ -451,6 +451,18 @@ class HomeFragment : Fragment() { .navigation() } + // 进港仓库 + Constant.AuthName.GjjWareHouseActivity -> { + ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_GJJ_WARE_HOUSE) + .navigation() + } + + // 提取记录 + Constant.AuthName.IntImpPickUpRecord -> { + ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_INT_IMP_PICK_UP_RECORD) + .navigation() + } + /** * 航班查询 */ @@ -800,6 +812,20 @@ class HomeFragment : Fragment() { "理货报告" ) ) + list.add( + RightMenu( + Constant.AuthName.GjjWareHouseActivity, + R.mipmap.gnj_cang_ku_icon, + "进港仓库" + ) + ) + list.add( + RightMenu( + Constant.AuthName.IntImpPickUpRecord, + R.mipmap.gnj_cang_ku_icon, + "提取记录" + ) + ) } Constant.AuthName.Flight -> { diff --git a/module_base/src/main/java/com/lukouguoji/module_base/LoginActivity.kt b/module_base/src/main/java/com/lukouguoji/module_base/LoginActivity.kt index bb22afa..0d6b880 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/LoginActivity.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/LoginActivity.kt @@ -41,7 +41,7 @@ import me.jessyan.autosize.internal.CustomAdapt * ========== 开发调试开关 ========== * TODO: 正式发布前务必设置为 false */ -private const val DEV_AUTO_LOGIN = false // 自动登录开关 +private const val DEV_AUTO_LOGIN = true // 自动登录开关 @Route(path = ARouterConstants.ACTIVITY_URL_LOGIN) class LoginActivity : BaseActivity(), diff --git a/module_base/src/main/java/com/lukouguoji/module_base/adapter/SpinnerAdapter.kt b/module_base/src/main/java/com/lukouguoji/module_base/adapter/SpinnerAdapter.kt index 0add0f7..f5f9eba 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/adapter/SpinnerAdapter.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/adapter/SpinnerAdapter.kt @@ -49,6 +49,26 @@ fun bindAdapter( if (hint.isNullOrEmpty()) { val adapter = ArrayAdapter(spinner.context, layoutId, list) spinner.adapter = adapter + } else if (list.isEmpty()) { + // 列表为空时:控件上显示 hint 占位文字(灰色),下拉列表隐藏 + // 不能用 SpinnerHintAdapter,其 getCount()=0 会导致 Spinner 不渲染 + val adapter = object : ArrayAdapter(spinner.context, layoutId, listOf(hint)) { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val view = super.getView(position, convertView, parent) + (view as TextView).setTextColor(context.resources.getColor(R.color.text_gray_l)) + return view + } + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + // 下拉列表中隐藏 hint 项 + val view = super.getDropDownView(position, convertView, parent) + view.visibility = View.GONE + view.layoutParams = ViewGroup.LayoutParams(0, 0) + return view + } + } + spinner.adapter = adapter + spinner.setSelection(0) } else { val containHintList = list + hint val adapter = SpinnerHintAdapter(spinner.context, layoutId, containHintList) diff --git a/module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpPickUpRecordBean.kt b/module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpPickUpRecordBean.kt new file mode 100644 index 0000000..59a16d6 --- /dev/null +++ b/module_base/src/main/java/com/lukouguoji/module_base/bean/IntImpPickUpRecordBean.kt @@ -0,0 +1,37 @@ +package com.lukouguoji.module_base.bean + +import androidx.databinding.ObservableBoolean +import java.io.Serializable + +/** + * 国际进港提取记录-列表数据Bean + * 对应API: IntImpPickUpRecord/pageQuery + */ +class IntImpPickUpRecordBean : Serializable { + var id: Long = 0 // 主键ID + var wbNo: String = "" // 运单号 + var pc: Int = 0 // 件数 + var weight: Double = 0.0 // 重量 + var checkWeight: Double = 0.0 // 计重重量 + var agentCode: String = "" // 代理人 + var spCode: String = "" // 特码 + var serviceFee: Double = 0.0 // 服务费 + var storageFee: Double = 0.0 // 仓储费 + var totalAmount: Double = 0.0 // 总金额 + var pickUpTime: String = "" // 提取时间 + var pickUpNo: String = "" // 提货编号 + var infoFee: Double = 0.0 // 信息费 + var drawFee: Double = 0.0 // 抽单费 + var coldFee: Double = 0.0 // 冷藏费 + var forkliftFee: Double = 0.0 // 铲车费 + var tallyFee: Double = 0.0 // 理货费 + var operator: String = "" // 办理人 + var outTime: String = "" // 出库时间 + + // ========== UI扩展字段 ========== + val checked: ObservableBoolean = ObservableBoolean(false) + + var isSelected: Boolean + get() = checked.get() + set(value) = checked.set(value) +} 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 0a0d4d8..29d3206 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 @@ -270,6 +270,7 @@ interface Constant { const val IntArrAirManifest = "AppIntArrAirManifest" //原始舱单 const val IntImpManifest = "AppIntImpManifest" //进港舱单 const val IntImpTally = "AppIntImpTally" //理货报告 + const val IntImpPickUpRecord = "AppIntImpPickUpRecord" //提取记录 const val GjjManifestListActivity = "AppIntExpManifest" //舱单 const val GjjTallyListActivity = "AppIntExpTally" //理货 const val GjjGoodsListActivity = "AppIntExpGjjGoods" //货物交接 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 81b5c97..7d44dcc 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 @@ -49,6 +49,7 @@ import com.lukouguoji.module_base.bean.GjjGoodsDetailsBean import com.lukouguoji.module_base.bean.GjjGoodsTypeBean import com.lukouguoji.module_base.bean.GjjHandoverRecordBean import com.lukouguoji.module_base.bean.GjjImportTally +import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean import com.lukouguoji.module_base.bean.GjjManifest import com.lukouguoji.module_base.bean.GjjManifestBean import com.lukouguoji.module_base.bean.GjjPackTypeBean @@ -227,6 +228,12 @@ interface Api { @POST("typeCode/intExp/agentCode") suspend fun getIntExpAgentList(): DictListBean + /** + * 获取代理人-国际进港-下拉框 + */ + @POST("typeCode/intImp/agentCode") + suspend fun getIntImpAgentList(): DictListBean + /** * 获取业务类型---CI:国内进港,CO:国内出港,II:国际进港,IO:国际出港 下拉框 */ @@ -891,6 +898,76 @@ interface Api { @GET("typeCode/locationByFlag") suspend fun getLocationList(@Query("flag") flag: Int): BaseResultBean> + /** + * 国际进港仓库-分页查询 + * 接口路径: /IntImpStorageUse/pageQuery + */ + @POST("IntImpStorageUse/pageQuery") + suspend fun getIntImpStorageUseList(@Body data: RequestBody): PageInfo + + /** + * 国际进港仓库-分页合计 + * 接口路径: /IntImpStorageUse/pageQueryTotal + */ + @POST("IntImpStorageUse/pageQueryTotal") + suspend fun getIntImpStorageUseTotal(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港库位操作-清仓 + * 接口路径: /IntImpStorageUse/updateClear + */ + @POST("IntImpStorageUse/updateClear") + suspend fun clearIntImpStorage(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港库位操作-修改库位 + * 接口路径: /IntImpStorageUse/modifyStorage + */ + @POST("IntImpStorageUse/modifyStorage") + suspend fun modifyIntImpStorage(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港库位操作-出库 + * 接口路径: /IntImpStorageUse/outStorage + */ + @POST("IntImpStorageUse/outStorage") + suspend fun outIntImpStorage(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港库位操作-入库 + * 接口路径: /IntImpStorageUse/inStorage + */ + @POST("IntImpStorageUse/inStorage") + suspend fun inIntImpStorage(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港提取记录-分页查询 + * 接口路径: /IntImpPickUpRecord/pageQuery + */ + @POST("IntImpPickUpRecord/pageQuery") + suspend fun getIntImpPickUpRecordList(@Body data: RequestBody): PageInfo + + /** + * 国际进港提取记录-分页合计 + * 接口路径: /IntImpPickUpRecord/pageQueryTotal + */ + @POST("IntImpPickUpRecord/pageQueryTotal") + suspend fun getIntImpPickUpRecordTotal(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港提取记录-清除提货 + * 接口路径: /IntImpPickUpRecord/clearPickUp + */ + @POST("IntImpPickUpRecord/clearPickUp") + suspend fun clearIntImpPickUp(@Body data: RequestBody): BaseResultBean + + /** + * 国际进港提取记录-详情 + * 接口路径: /IntImpPickUpRecord/getDetails + */ + @POST("IntImpPickUpRecord/getDetails") + suspend fun getIntImpPickUpRecordDetails(@Body data: RequestBody): BaseResultBean + /** * 国际出港待计重-分页搜索 * 接口路径: /IntExpCheckIn/pageQuery @@ -1634,7 +1711,7 @@ interface Api { * 分页查询国际进港电报 */ @POST("IntImpMsg/pageQuery") - suspend fun getIntImpMsgList(@Body data: RequestBody): BaseResultBean> + suspend fun getIntImpMsgList(@Body data: RequestBody): PageInfo /** * 批量生成电报 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 6bfea93..f893b95 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 @@ -160,6 +160,7 @@ object ARouterConstants { const val ACTIVITY_URL_GJJ_WARE_HOUSE_INFO = "/gjj/GjjWareHouseInfoActivity" //国际进港模块 仓库详情 const val ACTIVITY_URL_GJJ_CHU_KU_LIST = "/gjj/GjjChuKuListActivity" //国际进港 出库 + const val ACTIVITY_URL_INT_IMP_PICK_UP_RECORD = "/gjj/IntImpPickUpRecordActivity" //国际进港 提取记录 const val ACTIVITY_URL_GJJ_QUERY_LIST = "/gjj/GjjQueryListActivity" //国际进港 查询 列表 const val ACTIVITY_URL_GJJ_QUERY_INFO = "/gjj/GnjQueryInfoActivity" //国际进港 查询 详情 diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjWareHouseActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjWareHouseActivity.kt index d531c2a..6152f91 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjWareHouseActivity.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjWareHouseActivity.kt @@ -29,7 +29,7 @@ import com.scwang.smart.refresh.layout.api.RefreshLayout import java.text.SimpleDateFormat import java.util.* -@Route(path = ARouterConstants.ACTIVITY_URL_GJJ_WARE_HOUSE) +//@Route(path = ARouterConstants.ACTIVITY_URL_GJJ_WARE_HOUSE) class GjjWareHouseActivity : BaseActivity(), View.OnClickListener { private lateinit var viewModel: GjjWareHouseListViewModel private val currentTitleName = "国际进港仓库管理" diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordActivity.kt new file mode 100644 index 0000000..6ab6a71 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordActivity.kt @@ -0,0 +1,88 @@ +package com.lukouguoji.gjj.activity + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import com.alibaba.android.arouter.facade.annotation.Route +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.ActivityIntImpPickUpRecordBinding +import com.lukouguoji.gjj.viewModel.IntImpPickUpRecordViewModel +import com.lukouguoji.module_base.base.BaseBindingActivity +import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean +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.commonAdapter +import com.lukouguoji.module_base.ktx.showToast +import com.lukouguoji.module_base.router.ARouterConstants + +/** + * 国际进港-提取记录 + */ +@Route(path = ARouterConstants.ACTIVITY_URL_INT_IMP_PICK_UP_RECORD) +class IntImpPickUpRecordActivity : + BaseBindingActivity() { + + override fun layoutId() = R.layout.activity_int_imp_pick_up_record + override fun viewModelClass() = IntImpPickUpRecordViewModel::class.java + + override fun initOnCreate(savedInstanceState: Bundle?) { + setBackArrow("国际进港提取记录") + binding.viewModel = viewModel + binding.activity = this + + // 观察全选状态,更新图标透明度 + viewModel.isAllChecked.observe(this) { isAllChecked -> + binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f + } + + // 绑定分页 + viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this) + + // 监听刷新事件 + FlowBus.with(ConstantEvent.EVENT_REFRESH).observe(this) { + viewModel.refresh() + } + + // 初始化下拉列表 + viewModel.initAgentList() + viewModel.initSpecialCodeList() + + // 初始加载数据 + viewModel.refresh() + } + + /** + * 清除提货操作 + */ + fun clearPickUp() { + val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return + val allItems = list.filterIsInstance() + + val selectedItems = allItems.filter { it.isSelected } + + if (selectedItems.isEmpty()) { + showToast("请选择要清除提货的记录") + return + } + + AlertDialog.Builder(this) + .setTitle("清除提货确认") + .setMessage("确定要清除选中的 ${selectedItems.size} 条提货记录吗?") + .setPositiveButton("确定") { _, _ -> + viewModel.clearPickUp(selectedItems) + } + .setNegativeButton("取消", null) + .show() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == Constant.RequestCode.WAYBILL && resultCode == Activity.RESULT_OK) { + viewModel.wbNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT) + viewModel.searchClick() + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordDetailsActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordDetailsActivity.kt new file mode 100644 index 0000000..0b9344b --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpPickUpRecordDetailsActivity.kt @@ -0,0 +1,35 @@ +package com.lukouguoji.gjj.activity + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.ActivityIntImpPickUpRecordDetailsBinding +import com.lukouguoji.gjj.viewModel.IntImpPickUpRecordDetailsViewModel +import com.lukouguoji.module_base.base.BaseBindingActivity +import com.lukouguoji.module_base.common.Constant + +/** + * 国际进港-提取详情 + */ +class IntImpPickUpRecordDetailsActivity : + BaseBindingActivity() { + + override fun layoutId() = R.layout.activity_int_imp_pick_up_record_details + override fun viewModelClass() = IntImpPickUpRecordDetailsViewModel::class.java + + override fun initOnCreate(savedInstanceState: Bundle?) { + setBackArrow("国际进港提取详情") + binding.viewModel = viewModel + viewModel.initOnCreated(intent) + } + + companion object { + @JvmStatic + fun start(context: Context, id: Long) { + val starter = Intent(context, IntImpPickUpRecordDetailsActivity::class.java) + .putExtra(Constant.Key.ID, id) + context.startActivity(starter) + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpStorageUseActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpStorageUseActivity.kt new file mode 100644 index 0000000..4c467d1 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpStorageUseActivity.kt @@ -0,0 +1,178 @@ +package com.lukouguoji.gjj.activity + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import com.alibaba.android.arouter.facade.annotation.Route +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.ActivityIntImpStorageUseBinding +import com.lukouguoji.gjj.dialog.IntImpMoveClearDialogModel +import com.lukouguoji.gjj.dialog.IntImpModifyStorageDialogModel +import com.lukouguoji.gjj.dialog.IntImpInStorageDialogModel +import com.lukouguoji.gjj.viewModel.IntImpStorageUseViewModel +import com.lukouguoji.module_base.base.BaseBindingActivity +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.commonAdapter +import com.lukouguoji.module_base.ktx.showToast +import com.lukouguoji.module_base.router.ARouterConstants + +/** + * 国际进港-仓库 + */ +@Route(path = ARouterConstants.ACTIVITY_URL_GJJ_WARE_HOUSE) +class IntImpStorageUseActivity : + BaseBindingActivity() { + + override fun layoutId() = R.layout.activity_int_imp_storage_use + override fun viewModelClass() = IntImpStorageUseViewModel::class.java + + override fun initOnCreate(savedInstanceState: Bundle?) { + setBackArrow("国际进港仓库") + binding.viewModel = viewModel + binding.activity = this + + // 观察全选状态,更新图标透明度 + viewModel.isAllChecked.observe(this) { isAllChecked -> + binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f + } + + // 绑定分页 + viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this) + + // 监听刷新事件 + FlowBus.with(ConstantEvent.EVENT_REFRESH).observe(this) { + viewModel.refresh() + } + + // 初始加载数据 + viewModel.refresh() + } + + /** + * 显示清仓操作对话框 + */ + fun showClearDialog() { + val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return + val allItems = list.filterIsInstance() + + val maWbListForClear = allItems.mapNotNull { maWb -> + val selectedStorageList = maWb.storageUseList?.filter { it.isSelected } ?: emptyList() + + if (selectedStorageList.isNotEmpty() || maWb.isSelected) { + maWb.copy(storageUseList = selectedStorageList) + } else { + null + } + } + + if (maWbListForClear.isEmpty()) { + showToast("请至少选择一个库位") + return + } + + IntImpMoveClearDialogModel { dialog -> + val clearNormal = dialog.clearNormal.value ?: "" + viewModel.performClear(clearNormal, maWbListForClear) + }.show(this) + } + + /** + * 显示修改库位对话框 + */ + fun showModifyStorageDialog() { + val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return + val allItems = list.filterIsInstance() + + val selectedStorageUseList = mutableListOf() + allItems.forEach { maWb -> + maWb.storageUseList?.filter { it.isSelected }?.let { selectedStorageUseList.addAll(it) } + } + + when { + selectedStorageUseList.isEmpty() -> { + showToast("请选择要修改的库位") + return + } + selectedStorageUseList.size > 1 -> { + showToast("只能选择一个库位进行修改") + return + } + } + + val selectedStorage = selectedStorageUseList[0] + + IntImpModifyStorageDialogModel { dialog -> + val locationName = dialog.locationName + val locationId = dialog.locationId + viewModel.performModifyStorage(locationName, locationId, selectedStorage) + }.show(this) + } + + /** + * 显示出库二次确认对话框 + */ + fun showOutStorageDialog() { + val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return + val allItems = list.filterIsInstance() + + val selectedStorageUseList = mutableListOf() + allItems.forEach { maWb -> + maWb.storageUseList?.filter { it.isSelected }?.let { selectedStorageUseList.addAll(it) } + } + + if (selectedStorageUseList.isEmpty()) { + showToast("请选择要出库的库位") + return + } + + AlertDialog.Builder(this) + .setTitle("出库确认") + .setMessage("确定要将选中的 ${selectedStorageUseList.size} 个库位执行出库操作吗?") + .setPositiveButton("确定") { _, _ -> + viewModel.performOutStorage(selectedStorageUseList) + } + .setNegativeButton("取消", null) + .show() + } + + /** + * 显示入库操作对话框 + */ + fun showInStorageDialog() { + val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return + val allItems = list.filterIsInstance() + + val maWbListForInStorage = allItems.mapNotNull { maWb -> + val selectedStorageList = maWb.storageUseList?.filter { it.isSelected } ?: emptyList() + + if (selectedStorageList.isNotEmpty() || maWb.isSelected) { + maWb.copy(storageUseList = selectedStorageList) + } else { + null + } + } + + if (maWbListForInStorage.isEmpty()) { + showToast("请至少选择一个单据") + return + } + + IntImpInStorageDialogModel { dialog -> + val locationName = dialog.locationName + val locationId = dialog.locationId + viewModel.performInStorage(locationName, locationId, maWbListForInStorage) + }.show(this) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == Constant.RequestCode.WAYBILL && resultCode == Activity.RESULT_OK) { + viewModel.wbNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT) + viewModel.searchClick() + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpInStorageDialogModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpInStorageDialogModel.kt new file mode 100644 index 0000000..a6211db --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpInStorageDialogModel.kt @@ -0,0 +1,74 @@ +package com.lukouguoji.gjj.dialog + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.DialogIntImpInStorageBinding +import com.lukouguoji.module_base.base.BaseDialogModel +import com.lukouguoji.module_base.http.net.NetApply +import com.lukouguoji.module_base.ktx.launchCollect +import com.lukouguoji.module_base.ktx.showToast +import com.lukouguoji.module_base.ktx.verifyNullOrEmpty +import dev.utils.app.info.KeyValue + +/** + * 国际进港仓库 - 入库操作对话框 + */ +class IntImpInStorageDialogModel( + private val callback: (IntImpInStorageDialogModel) -> Unit +) : BaseDialogModel(DIALOG_TYPE_CENTER) { + + // 库位列表 + val locationList = MutableLiveData>() + + // 选中的库位(存储的是code) + val selectedLocationCode = MutableLiveData("") + + // 库位ID (后端需要的code) + var locationId: String = "" + + // 库位名称 (后端需要的name) + var locationName: String = "" + + override fun layoutId(): Int { + return R.layout.dialog_int_imp_in_storage + } + + override fun onDialogCreated(context: Context) { + binding.model = this + loadLocationList() + + // 监听选择变化,更新locationId和locationName + selectedLocationCode.observeForever { code -> + val selectedItem = locationList.value?.find { it.value == code } + locationId = selectedItem?.value ?: "" + locationName = selectedItem?.key ?: "" + } + } + + /** + * 加载库位列表 + */ + private fun loadLocationList() { + launchCollect({ NetApply.api.getLocationList(flag = 4) }) { + onSuccess = { result -> + val list = result.data?.map { it.toKeyValue() } ?: emptyList() + locationList.value = list + } + onFailed = { _, msg -> + showToast(msg ?: "加载库位列表失败") + } + } + } + + /** + * 保存按钮点击 + */ + fun onSaveClick() { + if (selectedLocationCode.value.verifyNullOrEmpty("请选择库位")) { + return + } + dismiss() + callback(this) + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpModifyStorageDialogModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpModifyStorageDialogModel.kt new file mode 100644 index 0000000..092c156 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpModifyStorageDialogModel.kt @@ -0,0 +1,74 @@ +package com.lukouguoji.gjj.dialog + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.DialogIntImpModifyStorageBinding +import com.lukouguoji.module_base.base.BaseDialogModel +import com.lukouguoji.module_base.http.net.NetApply +import com.lukouguoji.module_base.ktx.launchCollect +import com.lukouguoji.module_base.ktx.showToast +import com.lukouguoji.module_base.ktx.verifyNullOrEmpty +import dev.utils.app.info.KeyValue + +/** + * 国际进港 - 修改库位对话框 + */ +class IntImpModifyStorageDialogModel( + private val callback: (IntImpModifyStorageDialogModel) -> Unit +) : BaseDialogModel(DIALOG_TYPE_CENTER) { + + // 库位列表 + val locationList = MutableLiveData>() + + // 选中的库位(存储的是code) + val selectedLocationCode = MutableLiveData("") + + // 库位ID (后端需要的code) + var locationId: String = "" + + // 库位名称 (后端需要的name) + var locationName: String = "" + + override fun layoutId(): Int { + return R.layout.dialog_int_imp_modify_storage + } + + override fun onDialogCreated(context: Context) { + binding.model = this + loadLocationList() + + // 监听选择变化,更新locationId和locationName + selectedLocationCode.observeForever { code -> + val selectedItem = locationList.value?.find { it.value == code } + locationId = selectedItem?.value ?: "" + locationName = selectedItem?.key ?: "" + } + } + + /** + * 加载库位列表 + */ + private fun loadLocationList() { + launchCollect({ NetApply.api.getLocationList(flag = 4) }) { + onSuccess = { result -> + val list = result.data?.map { it.toKeyValue() } ?: emptyList() + locationList.value = list + } + onFailed = { _, msg -> + showToast(msg ?: "加载库位列表失败") + } + } + } + + /** + * 保存按钮点击 + */ + fun onSaveClick() { + if (selectedLocationCode.value.verifyNullOrEmpty("请选择库位")) { + return + } + dismiss() + callback(this) + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpMoveClearDialogModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpMoveClearDialogModel.kt new file mode 100644 index 0000000..a5e5528 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/dialog/IntImpMoveClearDialogModel.kt @@ -0,0 +1,47 @@ +package com.lukouguoji.gjj.dialog + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.DialogIntImpMoveClearBinding +import com.lukouguoji.module_base.base.BaseDialogModel +import com.lukouguoji.module_base.ktx.verifyNullOrEmpty +import dev.utils.app.info.KeyValue + +/** + * 国际进港移库 - 清仓操作对话框 + */ +class IntImpMoveClearDialogModel( + private val callback: (IntImpMoveClearDialogModel) -> Unit +) : BaseDialogModel(DIALOG_TYPE_CENTER) { + + // 清仓正常(存储的是 code:"0" 或 "1") + val clearNormal = MutableLiveData("") + + // 清仓正常选项列表 + val clearNormalList = MutableLiveData>().apply { + value = listOf( + KeyValue("是", "1"), + KeyValue("否", "0") + ) + } + + override fun layoutId(): Int { + return R.layout.dialog_int_imp_move_clear + } + + override fun onDialogCreated(context: Context) { + binding.model = this + } + + /** + * 保存按钮点击 + */ + fun onSaveClick() { + if (clearNormal.value.verifyNullOrEmpty("请选择清仓正常")) { + return + } + dismiss() + callback(this) + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpPickUpRecordViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpPickUpRecordViewHolder.kt new file mode 100644 index 0000000..cbaa252 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpPickUpRecordViewHolder.kt @@ -0,0 +1,32 @@ +package com.lukouguoji.gjj.holder + +import android.view.View +import com.lukouguoji.gjj.activity.IntImpPickUpRecordDetailsActivity +import com.lukouguoji.gjj.databinding.ItemIntImpPickUpRecordBinding +import com.lukouguoji.module_base.base.BaseViewHolder +import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean + +/** + * 国际进港-提取记录 ViewHolder + */ +class IntImpPickUpRecordViewHolder(view: View) : + BaseViewHolder(view) { + + override fun onBind(item: Any?, position: Int) { + val bean = getItemBean(item) ?: return + binding.bean = bean + binding.position = position + binding.executePendingBindings() + + // 图标点击切换选择状态 + binding.ivIcon.setOnClickListener { + bean.checked.set(!bean.checked.get()) + binding.executePendingBindings() + } + + // 列表项点击进入提取详情 + itemView.setOnClickListener { + IntImpPickUpRecordDetailsActivity.start(itemView.context, bean.id) + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseSubViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseSubViewHolder.kt new file mode 100644 index 0000000..934ee41 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseSubViewHolder.kt @@ -0,0 +1,45 @@ +package com.lukouguoji.gjj.holder + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.lukouguoji.gjj.databinding.ItemIntImpStorageUseSubBinding +import com.lukouguoji.module_base.base.BaseViewHolder +import com.lukouguoji.module_base.bean.GjcMaWb +import com.lukouguoji.module_base.bean.GjcStorageUse + +/** + * 国际进港-仓库 库位明细行 ViewHolder + */ +class IntImpStorageUseSubViewHolder(view: View) : + BaseViewHolder(view) { + + override fun onBind(item: Any?, position: Int) { + val bean = getItemBean(item) ?: return + binding.bean = bean + binding.position = position + binding.executePendingBindings() + + // 单选框点击切换选择状态(反向联动主列表) + binding.ivCheckbox.setOnClickListener { + // 切换子列表项的选择状态 + val newCheckedState = !bean.checked.get() + bean.checked.set(newCheckedState) + binding.executePendingBindings() + + // 反向联动主列表项(仅在勾选时联动) + updateParentCheckState(newCheckedState) + } + } + + /** + * 更新父列表项的选择状态 + */ + private fun updateParentCheckState(newCheckedState: Boolean) { + val recyclerView = itemView.parent as? RecyclerView ?: return + val parentBean = recyclerView.tag as? GjcMaWb ?: return + + if (newCheckedState) { + parentBean.checked.set(true) + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseViewHolder.kt new file mode 100644 index 0000000..280cff1 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpStorageUseViewHolder.kt @@ -0,0 +1,54 @@ +package com.lukouguoji.gjj.holder + +import android.view.View +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.databinding.ItemIntImpStorageUseBinding +import com.lukouguoji.module_base.adapter.setCommonAdapter +import com.lukouguoji.module_base.base.BaseViewHolder +import com.lukouguoji.module_base.bean.GjcMaWb +import com.lukouguoji.module_base.ktx.refresh + +/** + * 国际进港-仓库 ViewHolder + */ +class IntImpStorageUseViewHolder(view: View) : + BaseViewHolder(view) { + + override fun onBind(item: Any?, position: Int) { + val bean = getItemBean(item) ?: return + binding.bean = bean + binding.position = position + binding.executePendingBindings() + + // 图标点击切换选择状态(联动子列表) + binding.ivIcon.setOnClickListener { + val newCheckedState = !bean.checked.get() + bean.checked.set(newCheckedState) + + // 联动勾选/取消所有子列表项 + bean.storageUseList?.forEach { storageUse -> + storageUse.checked.set(newCheckedState) + } + + binding.executePendingBindings() + binding.rvSub.adapter?.notifyDataSetChanged() + } + + // 展开按钮点击事件 + binding.ivShow.setOnClickListener { + bean.showMore.set(!bean.showMore.get()) + } + + // 初始化库位明细子列表 RecyclerView + setCommonAdapter( + binding.rvSub, + IntImpStorageUseSubViewHolder::class.java, + R.layout.item_int_imp_storage_use_sub + ) + + // 刷新库位明细数据(传递父Bean引用) + val storageUseList = bean.storageUseList ?: emptyList() + binding.rvSub.tag = bean + binding.rvSub.refresh(storageUseList) + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMsgParseViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMsgParseViewModel.kt index 4707b8d..ad84834 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMsgParseViewModel.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpMsgParseViewModel.kt @@ -7,12 +7,15 @@ import dev.utils.common.DateUtils import com.lukouguoji.module_base.ktx.formatDate import com.lukouguoji.gjj.holder.IntImpMsgParseViewHolder import com.lukouguoji.module_base.base.BasePageViewModel +import com.lukouguoji.module_base.bean.FlightBean import com.lukouguoji.module_base.bean.MsgReceivePool 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.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 dev.utils.app.info.KeyValue @@ -28,10 +31,14 @@ class IntImpMsgParseViewModel : BasePageViewModel() { val flightNo = MutableLiveData("") // 航班号 val sendAddress = MutableLiveData("") // 发站(择始发站) val sendAddressList = MutableLiveData>(emptyList()) - val receiveAddress = MutableLiveData("HFE") // 收报地址(目的站) + val receiveAddress = MutableLiveData("") // 收报地址(目的站,由航班查询返回) val msgType = MutableLiveData("") // 报文类型 val msgTypeList = MutableLiveData>(emptyList()) + // ========== 航班查询 ========== + private var fid: String = "" // 航班ID + private var lastQueriedFlight = "" // 避免重复查询 + // ========== 统计信息 ========== val totalCount = MutableLiveData("0") // 合计条数 @@ -39,6 +46,12 @@ class IntImpMsgParseViewModel : BasePageViewModel() { val isAllChecked = MutableLiveData(false) init { + // 初始化报文类型静态数据 + msgTypeList.value = listOf( + KeyValue("FFM", "FFM"), + KeyValue("FWB/FHL", "FWB/FHL") + ) + // 监听全选状态,自动更新所有列表项 isAllChecked.observeForever { checked -> val list = pageModel.rv?.commonAdapter()?.items as? List ?: return@observeForever @@ -47,6 +60,68 @@ class IntImpMsgParseViewModel : BasePageViewModel() { } } + // ========== 航班级联查询 ========== + + fun onFlightDateInputComplete() { + lastQueriedFlight = "" + queryFlightIfReady() + } + + fun onFlightNoInputComplete() { + queryFlightIfReady() + } + + private fun queryFlightIfReady() { + val fdate = flightDate.value + val fno = flightNo.value + if (fdate.isNullOrEmpty() || fno.isNullOrEmpty()) return + + val key = "$fdate-$fno" + if (key == lastQueriedFlight) return + lastQueriedFlight = key + + launchCollect({ + NetApply.api.getGjFlightBean( + mapOf( + "fdate" to fdate, + "fno" to fno, + "ieFlag" to "I", + ).toRequestBody() + ) + }) { + onSuccess = { + if (it.verifySuccess() && it.data != null) { + val flight = it.data!! + fid = flight.fid.noNull() + receiveAddress.value = flight.fdest.noNull() + + // 构建始发站下拉列表:fdep + jtz(经停港) + val list = mutableListOf( + KeyValue(flight.fdep.noNull(), flight.fdep.noNull()), + ) + if (!flight.jtz.isNullOrEmpty()) { + list.add(KeyValue(flight.jtz.noNull(), flight.jtz.noNull())) + } + sendAddressList.value = list + sendAddress.value = flight.fdep.noNull() + } else { + fid = "" + receiveAddress.value = "" + sendAddressList.value = emptyList() + sendAddress.value = "" + showToast(it.msg.noNull("获取航班信息失败")) + } + } + + onFailed = { _, _ -> + fid = "" + receiveAddress.value = "" + sendAddressList.value = listOf(KeyValue("全部", "")) + sendAddress.value = "" + } + } + } + // ========== 适配器配置 ========== val itemViewHolder = IntImpMsgParseViewHolder::class.java val itemLayoutId = R.layout.item_int_imp_msg_parse @@ -55,6 +130,10 @@ class IntImpMsgParseViewModel : BasePageViewModel() { * 搜索按钮点击 */ fun searchClick() { + if (msgType.value.isNullOrEmpty()) { + showToast("请选择报文类型") + return + } refresh() } @@ -103,13 +182,18 @@ class IntImpMsgParseViewModel : BasePageViewModel() { * 获取数据(重写BasePageViewModel) */ override fun getData() { - // 构建搜索条件 - val filterParams = mutableMapOf( - "fdate" to flightDate.value?.ifEmpty { null }, - "fno" to flightNo.value?.ifEmpty { null }, + // 构建搜索条件:优先用 fid,否则用 fdate + fno + val filterParams = mutableMapOf( "sendAddress" to sendAddress.value?.ifEmpty { null }, + "receiveAddress" to receiveAddress.value?.ifEmpty { null }, "msgType" to msgType.value?.ifEmpty { null } ) + if (fid.isNotEmpty()) { + filterParams["fid"] = fid + } else { + filterParams["fdate"] = flightDate.value?.ifEmpty { null } + filterParams["fno"] = flightNo.value?.ifEmpty { null } + } // 列表参数(含分页) val listParams = (filterParams + mapOf( @@ -119,10 +203,9 @@ class IntImpMsgParseViewModel : BasePageViewModel() { // 获取列表(带Loading) launchLoadingCollect({ NetApply.api.getIntImpMsgList(listParams) }) { - onSuccess = { result -> - val pageInfo = result.data - pageModel.handleListBean(pageInfo?.toBaseListBean()) - totalCount.value = (pageInfo?.total ?: 0).toString() + onSuccess = { pageInfo -> + pageModel.handleListBean(pageInfo.toBaseListBean()) + totalCount.value = (pageInfo.total ?: 0).toString() } } } diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordDetailsViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordDetailsViewModel.kt new file mode 100644 index 0000000..0ed31e4 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordDetailsViewModel.kt @@ -0,0 +1,42 @@ +package com.lukouguoji.gjj.viewModel + +import android.content.Intent +import androidx.lifecycle.MutableLiveData +import com.lukouguoji.module_base.base.BaseViewModel +import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean +import com.lukouguoji.module_base.common.Constant +import com.lukouguoji.module_base.http.net.NetApply +import com.lukouguoji.module_base.ktx.launchLoadingCollect +import com.lukouguoji.module_base.ktx.showToast +import com.lukouguoji.module_base.ktx.toRequestBody + +/** + * 国际进港提取详情 ViewModel + */ +class IntImpPickUpRecordDetailsViewModel : BaseViewModel() { + + var recordId: Long = 0 + + val dataBean = MutableLiveData(IntImpPickUpRecordBean()) + + fun initOnCreated(intent: Intent) { + recordId = intent.getLongExtra(Constant.Key.ID, 0) + if (recordId > 0) { + getData() + } else { + showToast("参数错误") + getTopActivity().finish() + } + } + + private fun getData() { + val params = mapOf("id" to recordId).toRequestBody() + launchLoadingCollect({ + NetApply.api.getIntImpPickUpRecordDetails(params) + }) { + onSuccess = { + dataBean.value = it.data ?: IntImpPickUpRecordBean() + } + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordViewModel.kt new file mode 100644 index 0000000..c491364 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpPickUpRecordViewModel.kt @@ -0,0 +1,191 @@ +package com.lukouguoji.gjj.viewModel + +import android.app.Activity +import android.content.Intent +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.holder.IntImpPickUpRecordViewHolder +import com.lukouguoji.module_base.base.BasePageViewModel +import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean +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.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.model.ScanModel +import com.lukouguoji.module_base.util.DictUtils +import dev.utils.app.info.KeyValue +import kotlinx.coroutines.launch + +/** + * 国际进港-提取记录 ViewModel + */ +class IntImpPickUpRecordViewModel : BasePageViewModel() { + + // ========== 筛选条件 ========== + val paymentDateStart = MutableLiveData("") // 缴费日期起 + val paymentDateEnd = MutableLiveData("") // 缴费日期止 + val agentCode = MutableLiveData("") // 代理人 + val spCode = MutableLiveData("") // 特码 + val wbNo = MutableLiveData("") // 运单号 + + // ========== 下拉列表数据源 ========== + val agentList = MutableLiveData(listOf(KeyValue("全部", ""))) + val spCodeList = MutableLiveData>(emptyList()) + + // ========== 统计信息 ========== + val totalCount = MutableLiveData("0") // 合计票数 + val totalPc = MutableLiveData("0") // 总件数 + val totalWeight = MutableLiveData("0") // 总重量 + + // ========== 全选状态 ========== + val isAllChecked = MutableLiveData(false) + + init { + isAllChecked.observeForever { checked -> + val list = pageModel.rv?.commonAdapter()?.items as? List + ?: return@observeForever + list.forEach { it.checked.set(checked) } + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + } + + // ========== 适配器配置 ========== + val itemViewHolder = IntImpPickUpRecordViewHolder::class.java + val itemLayoutId = R.layout.item_int_imp_pick_up_record + + /** + * 初始化代理人列表 + */ + fun initAgentList() { + launchCollect({ NetApply.api.getIntImpAgentList() }) { + onSuccess = { result -> + val list = mutableListOf(KeyValue("全部", "")) + result.data?.forEach { + list.add(KeyValue(it.name ?: "", it.code ?: "")) + } + agentList.value = list + } + } + } + + /** + * 初始化特码列表 + */ + fun initSpecialCodeList() { + DictUtils.getSpecialCodeList( + flag = 1, // 国际 + ieFlag = "I", // 进港 + parentcode = "" + ) { + spCodeList.value = it + } + } + + /** + * 搜索按钮点击 + */ + fun searchClick() { + refresh() + } + + /** + * 扫码运单号 + */ + fun scanWbNo() { + ScanModel.startScan(getTopActivity(), Constant.RequestCode.WAYBILL) + } + + /** + * 全选按钮点击 + */ + fun checkAllClick() { + val list = pageModel.rv?.commonAdapter()?.items as? List ?: return + + val shouldCheckAll = !isAllChecked.value!! + list.forEach { it.checked.set(shouldCheckAll) } + isAllChecked.value = shouldCheckAll + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + + /** + * 清除提货操作 + */ + fun clearPickUp(selectedItems: List) { + if (selectedItems.isEmpty()) { + showToast("请选择要清除提货的记录") + return + } + + val params = selectedItems.toRequestBody() + + launchLoadingCollect({ NetApply.api.clearIntImpPickUp(params) }) { + onSuccess = { + showToast("清除提货成功") + viewModelScope.launch { + FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + } + refresh() + } + onFailed = { _, msg -> + showToast(msg.noNull("清除提货失败")) + } + } + } + + /** + * 获取列表数据 + */ + override fun getData() { + val filterParams = mapOf( + "paymentDateStart" to paymentDateStart.value?.ifEmpty { null }, + "paymentDateEnd" to paymentDateEnd.value?.ifEmpty { null }, + "agentCode" to agentCode.value?.ifEmpty { null }, + "spCode" to spCode.value?.ifEmpty { null }, + "wbNo" to wbNo.value?.ifEmpty { null } + ) + + val listParams = (filterParams + mapOf( + "pageNum" to pageModel.page, + "pageSize" to pageModel.limit + )).toRequestBody() + + val totalParams = filterParams.toRequestBody() + + launchLoadingCollect({ NetApply.api.getIntImpPickUpRecordList(listParams) }) { + onSuccess = { result -> + pageModel.handleDataList(result.list) + pageModel.haveMore.postValue((result.pages) > pageModel.page) + } + } + + launchCollect({ NetApply.api.getIntImpPickUpRecordTotal(totalParams) }) { + onSuccess = { result -> + val data = result.data + totalCount.value = (data?.wbNumber ?: 0).toString() + totalPc.value = (data?.totalPc ?: 0).toString() + totalWeight.value = (data?.totalWeight ?: 0.0).toString() + } + } + } + + /** + * 处理扫码结果 + */ + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (resultCode == Activity.RESULT_OK && data != null) { + when (requestCode) { + Constant.RequestCode.WAYBILL -> { + wbNo.value = data.getStringExtra(Constant.Result.CODED_CONTENT) + refresh() + } + } + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpStorageUseViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpStorageUseViewModel.kt new file mode 100644 index 0000000..64719bc --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpStorageUseViewModel.kt @@ -0,0 +1,287 @@ +package com.lukouguoji.gjj.viewModel + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.lukouguoji.gjj.R +import com.lukouguoji.gjj.holder.IntImpStorageUseViewHolder +import com.lukouguoji.module_base.base.BasePageViewModel +import com.lukouguoji.module_base.bean.GjcMaWb +import com.lukouguoji.module_base.common.Constant +import com.lukouguoji.module_base.common.ConstantEvent +import com.lukouguoji.module_base.http.net.NetApply +import com.lukouguoji.module_base.impl.FlowBus +import com.lukouguoji.module_base.ktx.commonAdapter +import com.lukouguoji.module_base.ktx.formatDate +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.model.ScanModel +import dev.utils.app.info.KeyValue +import kotlinx.coroutines.launch +import java.util.Date + +/** + * 国际进港-仓库 ViewModel + */ +class IntImpStorageUseViewModel : BasePageViewModel() { + + // ========== 筛选条件 ========== + val flightDate = MutableLiveData(Date().formatDate()) // 航班日期,默认今天 + val flightNo = MutableLiveData("") // 航班号 + val clearResult = MutableLiveData("") // 清仓综合结果 + val clearResultList = MutableLiveData>() // 清仓综合结果列表 + val wbNo = MutableLiveData("") // 运单号 + val location = MutableLiveData("") // 库位号 + + // ========== 统计信息 ========== + val totalWbNumber = MutableLiveData("0") // 总票数 + val totalPc = MutableLiveData("0") // 总件数 + val totalWeight = MutableLiveData("0") // 总重量 + + // ========== 全选状态 ========== + val isAllChecked = MutableLiveData(false) + + // ========== 全局展开状态 ========== + val isAllExpanded = MutableLiveData(false) + + init { + clearResultList.value = listOf( + KeyValue("全部", ""), + KeyValue("正常", "0"), + KeyValue("异常", "1") + ) + + isAllChecked.observeForever { checked -> + val list = + pageModel.rv?.commonAdapter()?.items as? List ?: return@observeForever + list.forEach { it.checked.set(checked) } + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + } + + // ========== 适配器配置 ========== + val itemViewHolder = IntImpStorageUseViewHolder::class.java + val itemLayoutId = R.layout.item_int_imp_storage_use + + fun searchClick() { + refresh() + } + + /** + * 全选按钮点击(联动勾选所有子列表项) + */ + fun checkAllClick() { + val list = pageModel.rv?.commonAdapter()?.items as? List ?: return + + val shouldCheckAll = !isAllChecked.value!! + + list.forEach { maWb -> + maWb.checked.set(shouldCheckAll) + maWb.storageUseList?.forEach { storageUse -> + storageUse.checked.set(shouldCheckAll) + } + } + + isAllChecked.value = shouldCheckAll + + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + + /** + * 切换全局展开/收起状态 + */ + fun toggleAllExpand() { + val list = pageModel.rv?.commonAdapter()?.items as? List ?: return + + val shouldExpand = !isAllExpanded.value!! + isAllExpanded.value = shouldExpand + + list.forEach { bean -> + if (!bean.storageUseList.isNullOrEmpty()) { + bean.showMore.set(shouldExpand) + } + } + + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + + fun scanWbNo() { + ScanModel.startScan(getTopActivity(), Constant.RequestCode.WAYBILL) + } + + fun clearStorage() { + // 由Activity显示对话框 + } + + /** + * 执行清仓操作 + */ + fun performClear(clearNormal: String, maWbListForClear: List) { + if (maWbListForClear.isEmpty()) { + showToast("请至少选择一个库位") + return + } + + val params = mapOf( + "clearNormal" to clearNormal, + "maWbList" to maWbListForClear + ).toRequestBody() + + launchLoadingCollect({ NetApply.api.clearIntImpStorage(params) }) { + onSuccess = { + showToast("清仓成功") + viewModelScope.launch { + FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + } + refresh() + } + onFailed = { _, msg -> + showToast(msg.noNull("清仓失败")) + } + } + } + + fun modifyStorage() { + // 由Activity显示对话框 + } + + /** + * 执行修改库位操作 + */ + fun performModifyStorage( + locationName: String, + locationId: String, + storageUse: com.lukouguoji.module_base.bean.GjcStorageUse + ) { + if (locationName.isEmpty() || locationId.isEmpty()) { + showToast("请选择库位") + return + } + + val updatedStorage = storageUse.copy( + location = locationName, + locationId = locationId.toLongOrNull() ?: 0 + ) + + val params = updatedStorage.toRequestBody() + + launchLoadingCollect({ NetApply.api.modifyIntImpStorage(params) }) { + onSuccess = { + showToast("修改库位成功") + viewModelScope.launch { + FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + } + refresh() + } + onFailed = { _, msg -> + showToast(msg.noNull("修改库位失败")) + } + } + } + + fun outStorage() { + // 由Activity显示二次确认对话框 + } + + /** + * 执行出库操作 + */ + fun performOutStorage(selectedStorageList: List) { + if (selectedStorageList.isEmpty()) { + showToast("请选择要出库的库位") + return + } + + val params = selectedStorageList.toRequestBody() + + launchLoadingCollect({ NetApply.api.outIntImpStorage(params) }) { + onSuccess = { + showToast("出库成功") + viewModelScope.launch { + FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + } + refresh() + } + onFailed = { _, msg -> + showToast(msg.noNull("出库失败")) + } + } + } + + fun inStorage() { + // 由Activity显示对话框 + } + + /** + * 执行入库操作 + */ + fun performInStorage( + locationName: String, + locationId: String, + maWbListForInStorage: List + ) { + if (maWbListForInStorage.isEmpty()) { + showToast("请至少选择一个单据") + return + } + + if (locationName.isEmpty() || locationId.isEmpty()) { + showToast("请选择库位") + return + } + + val params = mapOf( + "location" to locationName, + "locationId" to locationId.toLongOrNull(), + "maWbList" to maWbListForInStorage + ).toRequestBody() + + launchLoadingCollect({ NetApply.api.inIntImpStorage(params) }) { + onSuccess = { + showToast("入库成功") + viewModelScope.launch { + FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + } + refresh() + } + onFailed = { _, msg -> + showToast(msg.noNull("入库失败")) + } + } + } + + override fun getData() { + val filterParams = mapOf( + "fdate" to flightDate.value?.ifEmpty { null }, + "fno" to flightNo.value?.ifEmpty { null }, + "wbNo" to wbNo.value?.ifEmpty { null }, + "location" to location.value?.ifEmpty { null } + ) + + val listParams = (filterParams + mapOf( + "pageNum" to pageModel.page, + "pageSize" to pageModel.limit + )).toRequestBody() + + val totalParams = filterParams.toRequestBody() + + launchLoadingCollect({ NetApply.api.getIntImpStorageUseList(listParams) }) { + onSuccess = { result -> + pageModel.handleDataList(result.list) + pageModel.haveMore.postValue((result.pages) > pageModel.page) + isAllExpanded.value = false + } + } + + launchCollect({ NetApply.api.getIntImpStorageUseTotal(totalParams) }) { + onSuccess = { result -> + val data = result.data + totalWbNumber.value = (data?.wbNumber ?: 0).toString() + totalPc.value = (data?.totalPc ?: 0).toString() + totalWeight.value = (data?.totalWeight ?: 0.0).toString() + } + } + } +} diff --git a/module_gjj/src/main/res/layout/activity_int_imp_msg_parse.xml b/module_gjj/src/main/res/layout/activity_int_imp_msg_parse.xml index e95e1a8..ddb0ad0 100644 --- a/module_gjj/src/main/res/layout/activity_int_imp_msg_parse.xml +++ b/module_gjj/src/main/res/layout/activity_int_imp_msg_parse.xml @@ -33,6 +33,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_pick_up_record_details.xml b/module_gjj/src/main/res/layout/activity_int_imp_pick_up_record_details.xml new file mode 100644 index 0000000..54ffcf5 --- /dev/null +++ b/module_gjj/src/main/res/layout/activity_int_imp_pick_up_record_details.xml @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/activity_int_imp_storage_use.xml b/module_gjj/src/main/res/layout/activity_int_imp_storage_use.xml new file mode 100644 index 0000000..92168b0 --- /dev/null +++ b/module_gjj/src/main/res/layout/activity_int_imp_storage_use.xml @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/dialog_int_imp_in_storage.xml b/module_gjj/src/main/res/layout/dialog_int_imp_in_storage.xml new file mode 100644 index 0000000..48c5bce --- /dev/null +++ b/module_gjj/src/main/res/layout/dialog_int_imp_in_storage.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/dialog_int_imp_modify_storage.xml b/module_gjj/src/main/res/layout/dialog_int_imp_modify_storage.xml new file mode 100644 index 0000000..f4fcb1f --- /dev/null +++ b/module_gjj/src/main/res/layout/dialog_int_imp_modify_storage.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/dialog_int_imp_move_clear.xml b/module_gjj/src/main/res/layout/dialog_int_imp_move_clear.xml new file mode 100644 index 0000000..6832dc3 --- /dev/null +++ b/module_gjj/src/main/res/layout/dialog_int_imp_move_clear.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/item_int_imp_pick_up_record.xml b/module_gjj/src/main/res/layout/item_int_imp_pick_up_record.xml new file mode 100644 index 0000000..3018ec5 --- /dev/null +++ b/module_gjj/src/main/res/layout/item_int_imp_pick_up_record.xml @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/item_int_imp_storage_use.xml b/module_gjj/src/main/res/layout/item_int_imp_storage_use.xml new file mode 100644 index 0000000..3a221c7 --- /dev/null +++ b/module_gjj/src/main/res/layout/item_int_imp_storage_use.xml @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_gjj/src/main/res/layout/item_int_imp_storage_use_sub.xml b/module_gjj/src/main/res/layout/item_int_imp_storage_use_sub.xml new file mode 100644 index 0000000..21e5f28 --- /dev/null +++ b/module_gjj/src/main/res/layout/item_int_imp_storage_use_sub.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +