From de69eeefd8bc4542718051d047a5e6838c01d3c3 Mon Sep 17 00:00:00 2001 From: YANGJIANKUAN Date: Mon, 23 Mar 2026 12:58:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=97=A5=E5=BF=97=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=E6=95=B0=E6=8D=AE=E9=A9=B1=E5=8A=A8=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=EF=BC=88=E6=B5=81=E8=BD=AC=E7=8A=B6=E6=80=81+=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E8=AF=A6=E6=83=85=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 入口页传递运单号和运单类型至日志详情页 - 区分国际出港(9步骤)/国际进港(6步骤)流转节点 - 用status字段匹配节点状态(蓝色/白色/绿色) - 修复API返回裸数组被拦截器包装导致解析失败的问题 - ScrollView改为NestedScrollView修复竖向列表不渲染 Co-Authored-By: Claude Opus 4.6 --- .../page/log/detail/LogDetailActivity.kt | 59 +++++--- .../page/log/detail/LogDetailViewModel.kt | 140 +++++++++++------- .../main/res/layout/activity_log_detail.xml | 8 +- .../lukouguoji/module_base/common/Constant.kt | 6 + .../gjc/activity/GjcQueryDetailsActivity.kt | 3 + .../activity/IntImpQueryDetailsActivity.kt | 4 + 6 files changed, 145 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailActivity.kt b/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailActivity.kt index 98dd34e..91b6efd 100644 --- a/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailActivity.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailActivity.kt @@ -38,10 +38,10 @@ class LogDetailActivity : BaseBindingActivity - buildStepProgressBar(viewModel.allSteps, index) - } + // 观察数据变化,重新构建步骤进度条 + viewModel.allSteps.observe(this) { rebuildSteps() } + viewModel.activeStepCodes.observe(this) { rebuildSteps() } + viewModel.latestStepCode.observe(this) { rebuildSteps() } viewModel.statusLogList.observe(this) { list -> timelineAdapter.setData(list) @@ -50,7 +50,19 @@ class LogDetailActivity : BaseBindingActivity, currentIndex: Int) { + private fun rebuildSteps() { + val steps = viewModel.allSteps.value ?: return + val activeCodes = viewModel.activeStepCodes.value ?: emptySet() + val latestCode = viewModel.latestStepCode.value ?: "" + if (steps.isEmpty()) return + buildStepProgressBar(steps, activeCodes, latestCode) + } + + private fun buildStepProgressBar( + steps: List, + activeCodes: Set, + latestCode: String + ) { val container = binding.llSteps container.removeAllViews() @@ -60,30 +72,38 @@ class LogDetailActivity : BaseBindingActivity= 0 && i <= latestIndex) colorBlue else colorGray val lineLeft = View(this).apply { - setBackgroundColor(if (isCompleted) colorBlue else colorGray) + setBackgroundColor(leftLineColor) layoutParams = FrameLayout.LayoutParams(0, lineHeight).apply { gravity = Gravity.CENTER_VERTICAL or Gravity.START } @@ -115,9 +137,10 @@ class LogDetailActivity : BaseBindingActivity= 0 && i < latestIndex) colorBlue else colorGray val lineRight = View(this).apply { setBackgroundColor(rightLineColor) layoutParams = FrameLayout.LayoutParams(0, lineHeight).apply { @@ -136,8 +159,8 @@ class LogDetailActivity : BaseBindingActivity R.drawable.bg_step_dot_green - isCompleted -> R.drawable.bg_step_dot_blue + isLatest -> R.drawable.bg_step_dot_green + isActive -> R.drawable.bg_step_dot_blue else -> R.drawable.bg_step_dot_gray } ) diff --git a/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailViewModel.kt b/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailViewModel.kt index 177f08a..3fbf7e7 100644 --- a/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailViewModel.kt +++ b/app/src/main/java/com/lukouguoji/aerologic/page/log/detail/LogDetailViewModel.kt @@ -2,14 +2,19 @@ package com.lukouguoji.aerologic.page.log.detail import android.content.Intent import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.lukouguoji.module_base.base.BaseViewModel import com.lukouguoji.module_base.bean.LogBean import com.lukouguoji.module_base.bean.StatusLogBean import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.http.net.NetApply -import com.lukouguoji.module_base.ktx.launchCollect import com.lukouguoji.module_base.ktx.toRequestBody +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +data class StepInfo(val code: String, val name: String) class LogDetailViewModel : BaseViewModel() { @@ -18,71 +23,100 @@ class LogDetailViewModel : BaseViewModel() { val statusLogList = MutableLiveData>(emptyList()) - // 流转状态步骤定义 - val allSteps = listOf( - "预录入", "完成收运", "运抵申报", "海关放行", - "完成组装", "完成复磅", "装载申报", "航班关闭", "理货申报" - ) + // 流转状态步骤(根据运单类型动态设置) + val allSteps = MutableLiveData>(emptyList()) - // 当前完成到哪一步(索引) + // 操作详情中出现的步骤 code 集合 + val activeStepCodes = MutableLiveData>(emptySet()) + + // 最新步骤的 code + val latestStepCode = MutableLiveData("") + + // 当前完成到哪一步(索引),用于线条着色 val currentStepIndex = MutableLiveData(-1) + private var awbType = "" + + // 国际出港步骤(来自 AwbGjcStatus.java) + private val gjcSteps = listOf( + StepInfo("1", "预录入"), StepInfo("2", "收运完成"), + StepInfo("3", "运抵申报"), StepInfo("4", "海关放行"), + StepInfo("5", "组装完成"), StepInfo("6", "复磅完成"), + StepInfo("7", "装载申报"), StepInfo("8", "航班关闭"), + StepInfo("9", "理货申报") + ) + + // 国际进港步骤(来自 AwbGjjStatus.java) + private val gjjSteps = listOf( + StepInfo("1", "原始舱单"), StepInfo("2", "分拣理货"), + StepInfo("3", "理货申报"), StepInfo("4", "海关放行"), + StepInfo("5", "柜台办结"), StepInfo("6", "提取出库") + ) + fun initOnCreated(intent: Intent) { - val json = intent.getStringExtra(Constant.Key.DATA) ?: "" - if (json.isNotEmpty()) { - val bean = Gson().fromJson(json, LogBean::class.java) - waybillNo.value = bean.key - waybillType.value = getAwbTypeName(bean.logType) - loadStatusList(bean.key, bean.logType) + // 优先从 ARouter 参数获取 + val key = intent.getStringExtra(Constant.Key.KEY) ?: "" + awbType = intent.getStringExtra(Constant.Key.AWB_TYPE) ?: "" + + // 如果没有 ARouter 参数,尝试从 LogBean 解析 + if (key.isEmpty()) { + val json = intent.getStringExtra(Constant.Key.DATA) ?: "" + if (json.isNotEmpty()) { + val bean = Gson().fromJson(json, LogBean::class.java) + waybillNo.value = bean.key + awbType = bean.logType + } + } else { + waybillNo.value = key + } + + waybillType.value = getAwbTypeName(awbType) + allSteps.value = if (awbType == "II") gjjSteps else gjcSteps + + if (waybillNo.value?.isNotEmpty() == true) { + loadStatusList(waybillNo.value!!, awbType) } } private fun loadStatusList(key: String, logType: String) { - if (key.isEmpty()) { - loadMockData() - return - } - launchCollect({ - NetApply.api.getLogStatusList( - mapOf( - "key" to key, - "awbType" to logType - ).toRequestBody() - ) - }) { - onSuccess = { - val list = it.data ?: emptyList() - if (list.isNotEmpty()) { - statusLogList.value = list - val lastStatus = list.last().content - val index = allSteps.indexOfFirst { step -> - lastStatus.contains(step) - } - currentStepIndex.value = if (index >= 0) index else list.size - 1 - } else { - loadMockData() + viewModelScope.launch { + try { + // 注意:SelfLoginInterceptor 会把裸数组 [...] 包装成 {"data": [...]} + // 所以 API 返回类型必须是 BaseResultBean,从 .data 取实际列表 + val result = withContext(Dispatchers.IO) { + NetApply.api.getLogStatusList( + mapOf( + "key" to key, + "awbType" to logType + ).toRequestBody() + ) } - } - onFailed = { _, _ -> - loadMockData() + + val list = result.data ?: emptyList() + statusLogList.value = list + + if (list.isNotEmpty()) { + // 提取所有出现的 status code(用 status 字段匹配) + val codes = list.mapNotNull { item -> + item.status.ifEmpty { null } + }.toSet() + activeStepCodes.value = codes + + // 最新节点 = 列表最后一条的 status + val latestCode = list.last().status + latestStepCode.value = latestCode + + // 计算最新节点在步骤列表中的索引 + val steps = allSteps.value ?: emptyList() + val index = steps.indexOfFirst { step -> step.code == latestCode } + currentStepIndex.value = if (index >= 0) index else -1 + } + } catch (e: Exception) { + e.printStackTrace() } } } - private fun loadMockData() { - // Mock 数据:模拟到"装载申报"步骤 - currentStepIndex.value = 6 // "装载申报" 在 allSteps 中的索引 - - statusLogList.value = listOf( - StatusLogBean(content = "托书录入", opDate = "2017-04-01 12:00:00"), - StatusLogBean(content = "完成收运", opDate = "2017-04-01 12:00:00"), - StatusLogBean(content = "完成组装", opDate = "2017-04-01 12:00:00"), - StatusLogBean(content = "已复磅", opDate = "2017-04-01 12:00:00"), - StatusLogBean(content = "海关已放行", opDate = "2017-04-01 12:00:00"), - StatusLogBean(content = "装载申报", opDate = "2017-04-01 12:00:00") - ) - } - private fun getAwbTypeName(logType: String): String { return when (logType) { "CI" -> "国内进港" diff --git a/app/src/main/res/layout/activity_log_detail.xml b/app/src/main/res/layout/activity_log_detail.xml index 2fae859..fcd7eec 100644 --- a/app/src/main/res/layout/activity_log_detail.xml +++ b/app/src/main/res/layout/activity_log_detail.xml @@ -17,7 +17,7 @@ - @@ -59,7 +59,7 @@ @@ -81,7 +81,7 @@ @@ -153,7 +153,7 @@ - + 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 b827f94..d5398f5 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 @@ -395,5 +395,11 @@ interface Constant { // Bean对象传递 const val BEAN = "bean" + + // 运单号(日志详情) + const val KEY = "key" + + // 运单类型 + const val AWB_TYPE = "awbType" } } \ No newline at end of file diff --git a/module_gjc/src/main/java/com/lukouguoji/gjc/activity/GjcQueryDetailsActivity.kt b/module_gjc/src/main/java/com/lukouguoji/gjc/activity/GjcQueryDetailsActivity.kt index d51fd89..eeaeb94 100644 --- a/module_gjc/src/main/java/com/lukouguoji/gjc/activity/GjcQueryDetailsActivity.kt +++ b/module_gjc/src/main/java/com/lukouguoji/gjc/activity/GjcQueryDetailsActivity.kt @@ -62,8 +62,11 @@ class GjcQueryDetailsActivity : height = 30.dp } setOnClickListener { + val wbNo = viewModel.maWbData.value?.get("wbNo") as? String ?: "" ARouter.getInstance() .build(ARouterConstants.ACTIVITY_URL_LOG_DETAIL) + .withString(Constant.Key.KEY, wbNo) + .withString(Constant.Key.AWB_TYPE, "IO") .navigation() } } diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryDetailsActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryDetailsActivity.kt index 44a49b1..d1437a1 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryDetailsActivity.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/IntImpQueryDetailsActivity.kt @@ -14,6 +14,7 @@ import com.lukouguoji.gjj.databinding.ActivityIntImpQueryDetailsBinding import com.lukouguoji.gjj.viewModel.IntImpQueryDetailsViewModel import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.CustomVP2Adapter +import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.router.ARouterConstants /** @@ -61,8 +62,11 @@ class IntImpQueryDetailsActivity : height = 30.dp } setOnClickListener { + val wbNo = viewModel.maWbData.value?.get("wbNo") as? String ?: "" ARouter.getInstance() .build(ARouterConstants.ACTIVITY_URL_LOG_DETAIL) + .withString(Constant.Key.KEY, wbNo) + .withString(Constant.Key.AWB_TYPE, "II") .navigation() } }