feat: 国际进港-原始舱

This commit is contained in:
2025-12-26 23:20:38 +08:00
parent f291a73b5b
commit a672e3927c
23 changed files with 2203 additions and 1 deletions

View File

@@ -0,0 +1,67 @@
package com.lukouguoji.gjj.activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.databinding.ActivityIntArrAirManifestBinding
import com.lukouguoji.gjj.viewModel.IntArrAirManifestViewModel
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.addOnItemClickListener
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际进港原始舱单
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_ARR_AIR_MANIFEST)
class IntArrAirManifestActivity :
BaseBindingActivity<ActivityIntArrAirManifestBinding, IntArrAirManifestViewModel>() {
override fun layoutId() = R.layout.activity_int_arr_air_manifest
override fun viewModelClass() = IntArrAirManifestViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际进港原始舱单")
binding.viewModel = viewModel
// 观察全选状态,更新图标透明度
viewModel.isAllChecked.observe(this) { isAllChecked ->
binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f
}
// 绑定分页
viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this)
// 设置item点击监听
binding.rv.addOnItemClickListener(viewModel)
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
// 初始加载数据
viewModel.refresh()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
Constant.RequestCode.WAYBILL -> {
viewModel.waybillNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
}
Constant.RequestCode.HNO -> {
viewModel.houseNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
}
}
}
}
}

View File

@@ -0,0 +1,41 @@
package com.lukouguoji.gjj.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.databinding.ActivityIntArrAirManifestDetailsBinding
import com.lukouguoji.gjj.viewModel.IntArrAirManifestDetailsViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.GjjImportManifest
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际进港原始舱单详情
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_ARR_AIR_MANIFEST_DETAILS)
class IntArrAirManifestDetailsActivity :
BaseBindingActivity<ActivityIntArrAirManifestDetailsBinding, IntArrAirManifestDetailsViewModel>() {
override fun layoutId() = R.layout.activity_int_arr_air_manifest_details
override fun viewModelClass() = IntArrAirManifestDetailsViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("原始舱单详情")
binding.viewModel = viewModel
// 初始化数据
viewModel.initOnCreated(intent)
}
companion object {
@JvmStatic
fun start(context: Context, manifest: GjjImportManifest) {
val starter = Intent(context, IntArrAirManifestDetailsActivity::class.java)
.putExtra(Constant.Key.DATA, manifest)
context.startActivity(starter)
}
}
}

View File

@@ -0,0 +1,41 @@
package com.lukouguoji.gjj.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.databinding.ActivityIntArrSupplementInfoBinding
import com.lukouguoji.gjj.viewModel.IntArrSupplementInfoViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.GjjImportManifest
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际进港补充信息(收发货人信息)
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_ARR_SUPPLEMENT_INFO)
class IntArrSupplementInfoActivity :
BaseBindingActivity<ActivityIntArrSupplementInfoBinding, IntArrSupplementInfoViewModel>() {
override fun layoutId() = R.layout.activity_int_arr_supplement_info
override fun viewModelClass() = IntArrSupplementInfoViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("收发货人信息")
binding.viewModel = viewModel
// 初始化数据
viewModel.initOnCreated(intent)
}
companion object {
@JvmStatic
fun start(context: Context, manifest: GjjImportManifest) {
val starter = Intent(context, IntArrSupplementInfoActivity::class.java)
.putExtra(Constant.Key.DATA, manifest)
context.startActivity(starter)
}
}
}

View File

@@ -0,0 +1,58 @@
package com.lukouguoji.gjj.dialog
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.databinding.DialogIntArrManifestDeleteBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue
/**
* 国际进港原始舱单 - 删除申报对话框
*/
class IntArrManifestDeleteDialogModel(
val deleteReasonList: List<KeyValue>, // 删除原因列表
private val callback: (IntArrManifestDeleteDialogModel) -> Unit
) : BaseDialogModel<DialogIntArrManifestDeleteBinding>(DIALOG_TYPE_CENTER) {
// 删除原因代码(存储的是 code)
val deleteReason = MutableLiveData("")
// 联系人姓名
val contactName = MutableLiveData("")
// 联系人电话
val contactPhone = MutableLiveData("")
override fun layoutId(): Int {
return R.layout.dialog_int_arr_manifest_delete
}
override fun onDialogCreated(context: Context) {
binding.model = this
}
/**
* 确认按钮点击
*/
fun onConfirmClick() {
// 验证删除原因
if (deleteReason.value.verifyNullOrEmpty("请选择删除原因")) {
return
}
// 验证联系人姓名
if (contactName.value.verifyNullOrEmpty("请输入联系人姓名")) {
return
}
// 验证联系人电话
if (contactPhone.value.verifyNullOrEmpty("请输入联系人电话")) {
return
}
dismiss()
callback(this)
}
}

View File

@@ -0,0 +1,30 @@
package com.lukouguoji.gjj.holder
import android.view.View
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.databinding.ItemIntArrAirManifestBinding
import com.lukouguoji.module_base.base.BaseViewHolder
import com.lukouguoji.module_base.bean.GjjAirManifest
/**
* 国际进港原始舱单 ViewHolder
*/
class IntArrAirManifestViewHolder(view: View) :
BaseViewHolder<GjjAirManifest, ItemIntArrAirManifestBinding>(view) {
override fun onBind(item: Any?, position: Int) {
val bean = getItemBean(item) ?: return
val manifest = bean.getMainManifest()
binding.bean = bean
binding.manifest = manifest
binding.position = position
binding.executePendingBindings()
// 添加图标点击事件 - 切换选择状态
binding.ivIcon.setOnClickListener {
bean.checked.set(!bean.checked.get())
binding.executePendingBindings()
}
}
}

View File

@@ -0,0 +1,26 @@
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.GjjImportManifest
import com.lukouguoji.module_base.common.Constant
/**
* 国际进港原始舱单详情 ViewModel
*/
class IntArrAirManifestDetailsViewModel : BaseViewModel() {
// 舱单数据
val dataBean = MutableLiveData<GjjImportManifest>()
/**
* 初始化数据
*/
fun initOnCreated(intent: Intent) {
val manifest = intent.getSerializableExtra(Constant.Key.DATA) as? GjjImportManifest
if (manifest != null) {
dataBean.value = manifest
}
}
}

View File

@@ -0,0 +1,311 @@
package com.lukouguoji.gjj.viewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.holder.IntArrAirManifestViewHolder
import com.lukouguoji.module_base.base.BasePageViewModel
import com.lukouguoji.module_base.bean.GjjAirManifest
import com.lukouguoji.module_base.bean.GjjDeclareParam
import com.lukouguoji.module_base.bean.GjjImportManifest
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.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.model.ScanModel
import kotlinx.coroutines.launch
/**
* 国际进港原始舱单 ViewModel
*/
class IntArrAirManifestViewModel : BasePageViewModel() {
// ========== 搜索条件 ==========
val flightDate = MutableLiveData("")
val flightNo = MutableLiveData("")
val waybillNo = MutableLiveData("")
val houseNo = MutableLiveData("") // 分单号
// ========== 统计信息 ==========
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<GjjAirManifest> ?: return@observeForever
list.forEach { it.checked.set(checked) }
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
}
}
// ========== 适配器配置 ==========
val itemViewHolder = IntArrAirManifestViewHolder::class.java
val itemLayoutId = R.layout.item_int_arr_air_manifest
/**
* 搜索按钮点击
*/
fun searchClick() {
refresh()
}
/**
* 全选按钮点击
*/
fun checkAllClick() {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjAirManifest> ?: return
// 切换全选状态
val shouldCheckAll = !isAllChecked.value!!
list.forEach { it.checked.set(shouldCheckAll) }
isAllChecked.value = shouldCheckAll
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
}
/**
* 扫码运单号
*/
fun scanWaybill() {
ScanModel.startScan(getTopActivity(), Constant.RequestCode.WAYBILL)
}
/**
* 扫码分单号
*/
fun scanHouseNo() {
ScanModel.startScan(getTopActivity(), Constant.RequestCode.HNO)
}
/**
* 状态重置按钮点击
*/
fun resetStatusClick() {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjAirManifest> ?: return
val selectedItems = list.filter { it.isSelected }
if (selectedItems.isEmpty()) {
showToast("请选择要重置的舱单")
return
}
// 提取主单和分单
val maWbList = mutableListOf<GjjImportManifest>()
val haWbList = mutableListOf<GjjImportManifest>()
selectedItems.forEach { airManifest ->
airManifest.maWbList?.let { maWbList.add(it) }
airManifest.haWbList?.let { haWbList.addAll(it) }
}
val param = GjjDeclareParam(
maWbList = maWbList,
haWbList = haWbList,
restStatus = null, // 未申报状态
resetReason = "状态重置"
)
launchLoadingCollect({ NetApply.api.resetIntArrManifestStatus(param.toRequestBody()) }) {
onSuccess = {
showToast("状态重置成功")
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}
refresh()
}
}
}
/**
* 补充信息按钮点击
*/
fun supplementInfoClick() {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjAirManifest> ?: return
val selectedItems = list.filter { it.isSelected }
if (selectedItems.isEmpty()) {
showToast("请选择要补充信息的舱单")
return
}
// 获取第一个选中项的主单数据
val manifest = selectedItems.firstOrNull()?.maWbList ?: return
// 跳转到补充信息页面
com.lukouguoji.gjj.activity.IntArrSupplementInfoActivity.start(
getTopActivity(),
manifest
)
}
/**
* 删除申报按钮点击
*/
fun deleteDeclarationClick() {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjAirManifest> ?: return
val selectedItems = list.filter { it.isSelected }
if (selectedItems.isEmpty()) {
showToast("请选择要删除申报的舱单")
return
}
// 从接口获取删除原因列表
launchLoadingCollect({ NetApply.api.getDelReasonList() }) {
onSuccess = { result ->
val deleteReasonList = result.data?.map { it.toKeyValue() } ?: emptyList()
// 创建并显示弹框
val dialog = com.lukouguoji.gjj.dialog.IntArrManifestDeleteDialogModel(deleteReasonList) { dialogModel ->
// 弹框确认后的回调
// 提取主单和分单
val maWbList = mutableListOf<GjjImportManifest>()
val haWbList = mutableListOf<GjjImportManifest>()
selectedItems.forEach { airManifest ->
airManifest.maWbList?.let { maWbList.add(it) }
airManifest.haWbList?.let { haWbList.addAll(it) }
}
val param = GjjDeclareParam(
dcode = dialogModel.deleteReason.value ?: "",
dcontactsName = dialogModel.contactName.value ?: "",
dcontactsTel = dialogModel.contactPhone.value ?: "",
maWbList = maWbList,
haWbList = haWbList
)
val requestData = param.toRequestBody()
// 调用删除接口
launchLoadingCollect({ NetApply.api.deleteIntArrManifestDeclare(requestData) }) {
onSuccess = {
showToast("删除申报成功")
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}
refresh()
}
}
}
dialog.show()
}
}
}
/**
* 舱单申报按钮点击
*/
fun declareClick() {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjAirManifest> ?: return
val selectedItems = list.filter { it.isSelected }
if (selectedItems.isEmpty()) {
showToast("请选择要申报的舱单")
return
}
// 提取主单和分单
val maWbList = mutableListOf<GjjImportManifest>()
val haWbList = mutableListOf<GjjImportManifest>()
selectedItems.forEach { airManifest ->
airManifest.maWbList?.let { maWbList.add(it) }
airManifest.haWbList?.let { haWbList.addAll(it) }
}
val param = GjjDeclareParam(
maWbList = maWbList,
haWbList = haWbList
)
launchLoadingCollect({ NetApply.api.declareIntArrManifest(param.toRequestBody()) }) {
onSuccess = {
showToast("舱单申报成功")
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}
refresh()
}
}
}
/**
* 获取数据(重写BasePageViewModel)
*/
override fun getData() {
// 构建搜索条件
val filterParams = mapOf(
"fdate" to flightDate.value?.ifEmpty { null },
"fno" to flightNo.value?.ifEmpty { null },
"wbNo" to waybillNo.value?.ifEmpty { null },
"hno" to houseNo.value?.ifEmpty { null }
)
// 列表参数(含分页)
val listParams = (filterParams + mapOf(
"pageNum" to pageModel.page,
"pageSize" to pageModel.limit
)).toRequestBody()
// 统计参数(无分页)
val totalParams = filterParams.toRequestBody()
// 获取列表(带Loading)
launchLoadingCollect({ NetApply.api.getIntArrAirManifestList(listParams) }) {
onSuccess = { result ->
// 处理PageInfo结构
val pageInfo = result.data
if (pageInfo != null) {
val list = pageInfo.list ?: emptyList()
val pages = pageInfo.pages ?: 1
// 处理分页数据
pageModel.handleDataList(list)
pageModel.haveMore.postValue(pages > pageModel.page)
} else {
pageModel.handleDataList(emptyList())
pageModel.haveMore.postValue(false)
}
}
}
// 获取统计信息(后台请求,不阻塞列表)
launchCollect({ NetApply.api.getIntArrAirManifestTotal(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 onItemClick(position: Int, type: Int) {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjAirManifest> ?: return
val airManifest = list.getOrNull(position) ?: return
// 获取主单数据
val manifest = airManifest.maWbList ?: return
// 跳转到详情页
com.lukouguoji.gjj.activity.IntArrAirManifestDetailsActivity.start(
getTopActivity(),
manifest
)
}
}

View File

@@ -0,0 +1,44 @@
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.GjjImportManifest
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.ktx.showToast
/**
* 国际进港补充信息 ViewModel
*/
class IntArrSupplementInfoViewModel : BaseViewModel() {
// 数据对象
val dataBean = MutableLiveData<GjjImportManifest>()
/**
* 初始化数据
*/
fun initOnCreated(intent: Intent) {
val manifest = intent.getSerializableExtra(Constant.Key.DATA) as? GjjImportManifest
if (manifest != null) {
dataBean.value = manifest
} else {
dataBean.value = GjjImportManifest()
}
}
/**
* 取消按钮点击 - 暂不实现
*/
fun cancel() {
showToast("取消功能暂未实现")
getTopActivity().finish()
}
/**
* 保存按钮点击 - 暂不实现
*/
fun save() {
showToast("保存功能暂未实现")
}
}