From 7413a8d15969a30832e35235293d9bf15ad2af37 Mon Sep 17 00:00:00 2001 From: YANGJIANKUAN Date: Sun, 8 Mar 2026 15:05:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BD=E9=99=85=E8=BF=9B=E6=B8=AF?= =?UTF-8?q?=E8=88=B1=E5=8D=95=E6=B7=BB=E5=8A=A0=E5=88=86=E5=8D=95=E5=AD=90?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=B1=95=E5=BC=80/=E6=94=B6=E8=B5=B7?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- .../lukouguoji/module_base/bean/GjjHaWb.kt | 39 + .../module_base/bean/GjjManifest.kt | 7 + .../lukouguoji/module_base/http/net/Api.kt | 7 + .../gjj/holder/IntImpManifestSubViewHolder.kt | 36 + .../gjj/holder/IntImpManifestViewHolder.kt | 27 +- .../gjj/viewModel/IntImpManifestViewModel.kt | 137 +++- .../res/layout/activity_int_imp_manifest.xml | 16 +- .../main/res/layout/item_int_imp_manifest.xml | 673 +++++++++++------- .../res/layout/item_int_imp_manifest_sub.xml | 112 +++ 9 files changed, 763 insertions(+), 291 deletions(-) create mode 100644 module_base/src/main/java/com/lukouguoji/module_base/bean/GjjHaWb.kt create mode 100644 module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestSubViewHolder.kt create mode 100644 module_gjj/src/main/res/layout/item_int_imp_manifest_sub.xml diff --git a/module_base/src/main/java/com/lukouguoji/module_base/bean/GjjHaWb.kt b/module_base/src/main/java/com/lukouguoji/module_base/bean/GjjHaWb.kt new file mode 100644 index 0000000..b5412b0 --- /dev/null +++ b/module_base/src/main/java/com/lukouguoji/module_base/bean/GjjHaWb.kt @@ -0,0 +1,39 @@ +package com.lukouguoji.module_base.bean + +import androidx.databinding.ObservableBoolean +import java.io.Serializable + +/** + * 国际进港舱单-分单Bean + */ +data class GjjHaWb( + var hawbId: Long = 0, + var hno: String = "", + var no: String = "", + var prefix: String = "", + var pc: Long = 0, + var weight: Double = 0.0, + var volume: Double = 0.0, + var cashWeight: Double = 0.0, + var goods: String = "", + var spCode: String = "", + var mftStatus: String = "", + var lastMftStatus: String = "", + var mftMsgId: String = "", + var lastMftMsgId: String = "", + var declareCount: Int = 0, + var checkInType: String = "", + var activeId: Long = 0, + var opId: String = "", + var opDate: String = "", + var billsNo: String = "", + var remark: String = "", + var response: String = "" +) : Serializable { + @Transient + 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/bean/GjjManifest.kt b/module_base/src/main/java/com/lukouguoji/module_base/bean/GjjManifest.kt index a70b966..3c2d122 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/bean/GjjManifest.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/bean/GjjManifest.kt @@ -56,6 +56,13 @@ data class GjjManifest( var unNumber: String = "", // 危险品编号 var activeId: Long = 0 // 活动ID ) : Serializable { + // 分单列表 + var haWbList: List? = null + + // 展开/收起状态 + @Transient + val showMore: ObservableBoolean = ObservableBoolean(false) + // 选中状态(用于多选功能)- 不参与序列化 @Transient val checked: ObservableBoolean = ObservableBoolean(false) 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 6857970..9524cea 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 @@ -44,6 +44,7 @@ import com.lukouguoji.module_base.bean.GjcWeighingBean import com.lukouguoji.module_base.bean.GjcWeighingRecordBean import com.lukouguoji.module_base.bean.GjcWeighingStatisticsBean import com.lukouguoji.module_base.bean.GjjAirManifest +import com.lukouguoji.module_base.bean.GjjHaWb import com.lukouguoji.module_base.bean.GjjGoodsBean import com.lukouguoji.module_base.bean.GjjGoodsDetailsBean import com.lukouguoji.module_base.bean.GjjGoodsTypeBean @@ -1775,6 +1776,12 @@ interface Api { @POST("IntImpManifest/pageQueryTotal") suspend fun getIntImpManifestTotal(@Body data: RequestBody): BaseResultBean + /** + * 国际进港舱单-获取主单下分单 + */ + @POST("IntImpManifest/listHaWbByManifest") + suspend fun getIntImpManifestHaWbList(@Body data: RequestBody): BaseResultBean> + /** * 国际进港舱单-分拣理货(装机单)-分页查询 */ diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestSubViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestSubViewHolder.kt new file mode 100644 index 0000000..fb354d3 --- /dev/null +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestSubViewHolder.kt @@ -0,0 +1,36 @@ +package com.lukouguoji.gjj.holder + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.lukouguoji.gjj.databinding.ItemIntImpManifestSubBinding +import com.lukouguoji.module_base.base.BaseViewHolder +import com.lukouguoji.module_base.bean.GjjHaWb +import com.lukouguoji.module_base.bean.GjjManifest + +/** + * 国际进港舱单 - 分单子列表 ViewHolder + */ +class IntImpManifestSubViewHolder(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() + + // 反向联动主列表项(勾选子项时自动勾选父项) + if (newCheckedState) { + val recyclerView = itemView.parent as? RecyclerView ?: return@setOnClickListener + val parentBean = recyclerView.tag as? GjjManifest ?: return@setOnClickListener + parentBean.checked.set(true) + } + } + } +} diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestViewHolder.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestViewHolder.kt index 1807e66..395ba27 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestViewHolder.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/holder/IntImpManifestViewHolder.kt @@ -1,9 +1,12 @@ package com.lukouguoji.gjj.holder import android.view.View +import com.lukouguoji.gjj.R import com.lukouguoji.gjj.databinding.ItemIntImpManifestBinding +import com.lukouguoji.module_base.adapter.setCommonAdapter import com.lukouguoji.module_base.base.BaseViewHolder import com.lukouguoji.module_base.bean.GjjManifest +import com.lukouguoji.module_base.ktx.refresh /** * 国际进港舱单 ViewHolder @@ -17,10 +20,14 @@ class IntImpManifestViewHolder(view: View) : binding.position = position binding.executePendingBindings() - // 选中图标点击 - 切换选择状态 + // 选中图标点击 - 切换选择状态(联动子列表) binding.ivIcon.setOnClickListener { - bean.checked.set(!bean.checked.get()) + val newCheckedState = !bean.checked.get() + bean.checked.set(newCheckedState) + // 联动子列表选中状态 + bean.haWbList?.forEach { sub -> sub.checked.set(newCheckedState) } binding.executePendingBindings() + binding.rvSub.adapter?.notifyDataSetChanged() } // 编辑按钮点击 @@ -32,5 +39,21 @@ class IntImpManifestViewHolder(view: View) : binding.btnDelete.setOnClickListener { clickListener?.onItemClick(position, 102) // 102=删除 } + + // 展开按钮点击事件 + binding.ivShow.setOnClickListener { + bean.showMore.set(!bean.showMore.get()) + } + + // 初始化分单子列表 RecyclerView + setCommonAdapter( + binding.rvSub, + IntImpManifestSubViewHolder::class.java, + R.layout.item_int_imp_manifest_sub + ) + + // 设置父Bean引用(用于子列表反向联动) + binding.rvSub.tag = bean + binding.rvSub.refresh(bean.haWbList ?: emptyList()) } } diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpManifestViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpManifestViewModel.kt index ab6a3f4..b687561 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpManifestViewModel.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/IntImpManifestViewModel.kt @@ -37,6 +37,70 @@ class IntImpManifestViewModel : BasePageViewModel() { val fdep = MutableLiveData("") // 目的站 val waybillNo = MutableLiveData("") // 运单号 + // ========== 航班级联查询 ========== + private var lastQueriedFlight = "" + private var fid = "" + + 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() + fdep.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 = "" + fdep.value = "" + sendAddressList.value = emptyList() + sendAddress.value = "" + showToast(it.msg.noNull("获取航班信息失败")) + } + } + + onFailed = { _, _ -> + fid = "" + fdep.value = "" + sendAddressList.value = emptyList() + sendAddress.value = "" + } + } + } + // ========== 统计信息 ========== val totalCount = MutableLiveData("0") // 合计票数 val totalPc = MutableLiveData("0") // 总件数 @@ -45,11 +109,17 @@ class IntImpManifestViewModel : BasePageViewModel() { // ========== 全选状态 ========== val isAllChecked = MutableLiveData(false) + // ========== 展开/收起 ========== + val isAllExpanded = MutableLiveData(false) + init { - // 监听全选状态,自动更新所有列表项 + // 监听全选状态,自动更新所有列表项(联动子列表) isAllChecked.observeForever { checked -> val list = pageModel.rv?.commonAdapter()?.items as? List ?: return@observeForever - list.forEach { it.checked.set(checked) } + list.forEach { + it.checked.set(checked) + it.haWbList?.forEach { sub -> sub.checked.set(checked) } + } pageModel.rv?.commonAdapter()?.notifyDataSetChanged() } } @@ -66,19 +136,37 @@ class IntImpManifestViewModel : BasePageViewModel() { } /** - * 全选按钮点击(切换全选状态) + * 全选按钮点击(切换全选状态,联动子列表) */ fun checkAllClick() { val list = pageModel.rv?.commonAdapter()?.items as? List ?: return // 切换全选状态 val shouldCheckAll = !isAllChecked.value!! - list.forEach { it.checked.set(shouldCheckAll) } + list.forEach { + it.checked.set(shouldCheckAll) + it.haWbList?.forEach { sub -> sub.checked.set(shouldCheckAll) } + } isAllChecked.value = shouldCheckAll pageModel.rv?.commonAdapter()?.notifyDataSetChanged() } + /** + * 全部展开/收起 + */ + fun toggleAllExpand() { + val shouldExpand = !isAllExpanded.value!! + isAllExpanded.value = shouldExpand + val list = pageModel.rv?.commonAdapter()?.items as? List ?: return + list.forEach { + if (!it.haWbList.isNullOrEmpty()) { + it.showMore.set(shouldExpand) + } + } + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + /** * 扫码运单号 */ @@ -90,25 +178,14 @@ class IntImpManifestViewModel : BasePageViewModel() { * 新增按钮点击 */ fun onAddClick() { - // 获取当前航班信息 - val fid = getCurrentFlightId() - GjjManifestAddActivity.start( context = getTopActivity(), fid = fid, - dep = fdep.value ?: "", - dest = "" // 目的站暂时留空 + dep = sendAddress.value ?: "", + dest = fdep.value ?: "" ) } - /** - * 获取当前航班ID(从列表第一项获取) - */ - private fun getCurrentFlightId(): String { - val firstItem = pageModel.rv?.commonAdapter()?.items?.firstOrNull() as? GjjManifest - return firstItem?.fid?.toString() ?: "" - } - /** * Item点击处理(侧滑按钮) */ @@ -217,6 +294,14 @@ class IntImpManifestViewModel : BasePageViewModel() { launchLoadingCollect({ NetApply.api.getIntImpManifestList(listParams) }) { onSuccess = { result -> pageModel.handleListBean(result.data?.toBaseListBean()) + + // 列表加载完成后,加载分单数据 + val items = pageModel.rv?.commonAdapter()?.items as? List + items?.forEach { bean -> + if (bean.haWbNum > 0) { + loadHaWbList(bean) + } + } } } @@ -230,4 +315,22 @@ class IntImpManifestViewModel : BasePageViewModel() { } } } + + /** + * 加载主单下的分单列表 + */ + private fun loadHaWbList(bean: GjjManifest) { + val params = mapOf( + "mfId" to bean.mfId + ).toRequestBody() + + launchCollect({ NetApply.api.getIntImpManifestHaWbList(params) }) { + onSuccess = { result -> + if (result.verifySuccess() && !result.data.isNullOrEmpty()) { + bean.haWbList = result.data + pageModel.rv?.commonAdapter()?.notifyDataSetChanged() + } + } + } + } } diff --git a/module_gjj/src/main/res/layout/activity_int_imp_manifest.xml b/module_gjj/src/main/res/layout/activity_int_imp_manifest.xml index da33709..071065c 100644 --- a/module_gjj/src/main/res/layout/activity_int_imp_manifest.xml +++ b/module_gjj/src/main/res/layout/activity_int_imp_manifest.xml @@ -33,6 +33,7 @@ - + + + + diff --git a/module_gjj/src/main/res/layout/item_int_imp_manifest.xml b/module_gjj/src/main/res/layout/item_int_imp_manifest.xml index 4155ba6..bd153b4 100644 --- a/module_gjj/src/main/res/layout/item_int_imp_manifest.xml +++ b/module_gjj/src/main/res/layout/item_int_imp_manifest.xml @@ -15,150 +15,47 @@ type="Integer" /> - + + android:layout_marginBottom="10dp" + android:orientation="vertical"> - - + android:layout_height="wrap_content"> - - - - + + android:background="@drawable/bg_white_radius_8" + android:gravity="center_vertical" + android:orientation="horizontal" + android:padding="15dp"> - + + + + + android:layout_marginLeft="15dp" + android:layout_weight="1" + android:orientation="vertical"> - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -166,161 +63,272 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" completeSpace="@{5}" - android:text="实到件数:" + android:text="运单号:" android:textColor="@color/text_normal" android:textSize="16sp" /> + android:text="@{bean.getWaybillNo()}" + android:textColor="@color/colorPrimary" + android:textSize="18sp" + android:textStyle="bold" /> - - - - - - + - + + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + + + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + - + - + + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + + + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + - + - + + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal"> - + - + - - - - - - - - - - - - - - - - - - - - + @@ -328,38 +336,163 @@ - + + - + + + + + + + + + + + + + + + android:id="@+id/iv_show" + android:layout_width="match_parent" + android:layout_height="20dp" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp" + android:padding="5dp" + android:src="@mipmap/img_down" + visible="@{bean.haWbList != null && !bean.haWbList.empty}" /> - + + visible="@{bean.showMore}" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:background="#e3f6e0" + android:orientation="vertical" + android:visibility="gone"> - - + + - - + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/module_gjj/src/main/res/layout/item_int_imp_manifest_sub.xml b/module_gjj/src/main/res/layout/item_int_imp_manifest_sub.xml new file mode 100644 index 0000000..cd6de00 --- /dev/null +++ b/module_gjj/src/main/res/layout/item_int_imp_manifest_sub.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +