feat: 开始组装 opt

This commit is contained in:
2025-12-19 11:42:21 +08:00
parent b8fbc304f1
commit c6fe8f5310
5 changed files with 280 additions and 266 deletions

View File

@@ -24,6 +24,14 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
// ========== 搜索条件 ==========
val searchText = MutableLiveData("")
val uldSearchText = MutableLiveData("") // ULD搜索框
// ========== 组装参数(从选中运单自动提取)==========
val assembleFlightNo = MutableLiveData("") // 航班号
val assembleFlightDate = MutableLiveData("") // 航班日期
// ========== 防抖标记 ==========
private var lastQueriedAssembledParams = "" // 避免重复查询
// ========== 左侧组装信息列表 ==========
val assembleInfoList = MutableLiveData<MutableList<AssembleInfoBean>>()
@@ -55,27 +63,6 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
// ========== 标记位,避免重复查询 ==========
private var lastQueriedUldNo = ""
// ========== 本地存储(页面生命周期内)==========
/**
* 已组装货物的本地存储
* Key: ULD编号
* Value: 该ULD下的运单列表
*/
private val assembledCargoMap = mutableMapOf<String, MutableList<AssembleWaybillBean>>()
/**
* 扁平化的组装信息列表(包含一级和二级行)
* 用于RecyclerView显示
*/
private val flatAssembleInfoList = mutableListOf<AssembleInfoBean>()
/**
* 编辑模式标记
* null: 新增模式
* 非null: 编辑模式存储原始ULD编号和运单号
*/
private var editMode: Pair<String, String>? = null // Pair<ULD编号, 运单号>
/**
* ULD编号锁定状态
*/
@@ -122,6 +109,9 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
// 刷新列表
assemblePositionList.value = list
// 触发组装信息列表查询
loadAssembledList()
}
/**
@@ -143,6 +133,10 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
if (selectedWaybill != null) {
selectedWaybill.isSelected.set(true)
// 自动提取航班号和航班日期
assembleFlightNo.value = selectedWaybill.fno
assembleFlightDate.value = selectedWaybill.fdate
// 保存当前的组装人(始终保留)
val previousOperator = waybillInfo.value?.operator ?: ""
@@ -155,11 +149,8 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
previousAssembleCount = ""
previousAssembleWeight = ""
// 如果在编辑模式,退出编辑模式并解锁 ULD
if (editMode != null) {
editMode = null
isUldNoLocked.value = false
}
// 解锁ULD编号
isUldNoLocked.value = false
} else {
// 同一个运单:保留组装件数和组装重量
previousAssembleCount = waybillInfo.value?.assembleCount ?: ""
@@ -179,6 +170,12 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
// 刷新列表
waybillList.value = list
// 清除防抖标记,强制重新查询组装信息列表
lastQueriedAssembledParams = ""
// 触发组装信息列表查询
loadAssembledList()
}
/**
@@ -226,14 +223,11 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
if (currentWaybillNo.isNotEmpty()) {
val existsInNewList = waybillBeanList.any { it.waybillNo == currentWaybillNo }
if (!existsInNewList) {
// 当前运单不在新列表中,清空表单并退出编辑模式
// 当前运单不在新列表中,清空表单
val previousOperator = waybillInfo.value?.operator ?: ""
waybillInfo.value = WaybillInfoBean().apply {
operator = previousOperator
}
if (editMode != null) {
editMode = null
}
}
}
@@ -278,14 +272,11 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
if (currentWaybillNo.isNotEmpty()) {
val existsInNewList = waybillBeanList.any { it.waybillNo == currentWaybillNo }
if (!existsInNewList) {
// 当前运单不在新列表中,清空表单并退出编辑模式
// 当前运单不在新列表中,清空表单
val previousOperator = waybillInfo.value?.operator ?: ""
waybillInfo.value = WaybillInfoBean().apply {
operator = previousOperator
}
if (editMode != null) {
editMode = null
}
}
}
@@ -429,34 +420,17 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
}
// 2. 获取或构建运单Bean
val selectedWaybill: AssembleWaybillBean = if (editMode != null) {
// 编辑模式:从本地存储中获取运单数据
val uldNoInEdit = editMode!!.first
val waybillNoInEdit = editMode!!.second
val storedList = assembledCargoMap[uldNoInEdit]
val storedWaybill = storedList?.firstOrNull { it.waybillNo == waybillNoInEdit }
// 从运单列表中获取选中的运单
val currentWaybillList = waybillList.value
if (currentWaybillList == null) {
showToast("运单列表为空")
return
}
if (storedWaybill == null) {
showToast("未找到运单数据")
return
}
// 使用存储的运单数据(包含原始件数/重量和其他字段)
storedWaybill
} else {
// 新增模式:从运单列表中获取选中的运单
val currentWaybillList = waybillList.value
if (currentWaybillList == null) {
showToast("运单列表为空")
return
}
val selected = currentWaybillList.firstOrNull { it.isSelected.get() }
if (selected == null) {
showToast("请选择运单")
return
}
selected
val selectedWaybill = currentWaybillList.firstOrNull { it.isSelected.get() }
if (selectedWaybill == null) {
showToast("请选择运单")
return
}
// 3. 构建useInfoULD信息
@@ -472,13 +446,13 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
)
// 4. 构建wbInfo运单信息
// 如果是编辑模式,使用原始运单件数/重量否则使用当前件数/重量
val waybillPc = if (editMode != null && selectedWaybill.originalPieces.isNotEmpty()) {
// 使用原始运单件数/重量(如果有),否则使用当前件数/重量
val waybillPc = if (selectedWaybill.originalPieces.isNotEmpty()) {
selectedWaybill.originalPieces.toLongOrNull()
} else {
selectedWaybill.pieces.toLongOrNull()
}
val waybillWeight = if (editMode != null && selectedWaybill.originalWeight.isNotEmpty()) {
val waybillWeight = if (selectedWaybill.originalWeight.isNotEmpty()) {
selectedWaybill.originalWeight.toDoubleOrNull()
} else {
selectedWaybill.weight.toDoubleOrNull()
@@ -505,12 +479,9 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
"userId" to SharedPreferenceUtil.getString(Constant.Share.account)
).toRequestBody()
// 6. 立即同步到组装信息列表(不等接口成功
// 6. 调用接口带Loading等待接口返回
val operationName = if (isLoad) "装货" else "卸货"
handleOperationSuccess(operationName, isLoad, uldNo, selectedWaybill, assembleCount, assembleWeight)
// 7. 调用接口后台执行不阻塞UI
launchCollect({
launchLoadingCollect({
if (isLoad) {
NetApply.api.assembleLoadCargo(params)
} else {
@@ -518,10 +489,11 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
}
}) {
onSuccess = { result ->
// 接口成功,无需额外操作(已经同步到本地)
// 接口成功后才显示成功提示并刷新列表
handleOperationSuccess(operationName, isLoad, uldNo, selectedWaybill, assembleCount, assembleWeight)
}
onFailed = { code, message ->
showToast("${operationName}接口调用失败: $message(数据已保存到本地)")
showToast("${operationName}失败: $message")
}
}
}
@@ -539,37 +511,8 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
) {
showToast("${operationName}成功")
// 创建一个包含本次操作件数和重量的运单Bean
val assembleWaybill = AssembleWaybillBean()
assembleWaybill.waybillNo = selectedWaybill.waybillNo
assembleWaybill.pieces = assembleCount // 本次操作的件数
assembleWaybill.weight = assembleWeight // 本次操作的重量
assembleWaybill.flight = selectedWaybill.flight
assembleWaybill.fno = selectedWaybill.fno
assembleWaybill.fdate = selectedWaybill.fdate
assembleWaybill.whId = selectedWaybill.whId
assembleWaybill.isMarked = selectedWaybill.isMarked
// 保存原始运单件数/重量(用于编辑模式回显)
assembleWaybill.originalPieces = selectedWaybill.originalPieces.ifEmpty { selectedWaybill.pieces }
assembleWaybill.originalWeight = selectedWaybill.originalWeight.ifEmpty { selectedWaybill.weight }
// 判断是新增还是编辑模式
if (editMode != null) {
// 编辑模式:更新本地存储中的对应运单
updateAssembledCargoInLocal(
uldNo = editMode!!.first,
oldWaybillNo = editMode!!.second,
newWaybillBean = assembleWaybill,
isLoad = isLoad
)
} else {
// 新增模式:添加到本地存储
addAssembledCargoToLocal(uldNo, assembleWaybill, isLoad)
}
// 同步刷新组装信息列表
syncAssembleInfoListFromLocal()
// 重新查询组装信息列表(刷新数据)
loadAssembledList()
// 刷新运单列表
loadInitialWaitingAssemble()
@@ -578,96 +521,30 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
clearForm()
}
/**
* 从本地存储同步组装信息列表
* 1. 遍历 assembledCargoMap为每个 ULD 生成一级行
* 2. 计算总件数/总重量(求和子运单)
* 3. 根据展开状态决定是否添加二级运单行
* 4. 更新 LiveData 触发 UI 刷新
*/
private fun syncAssembleInfoListFromLocal() {
// 记录当前展开状态(避免刷新后展开状态丢失)
val expandedUlds = flatAssembleInfoList
.filter { it.itemType == AssembleInfoBean.ItemType.ULD_HEADER && it.isExpanded.get() }
.map { it.uldNo }
.toSet()
flatAssembleInfoList.clear()
// 遍历每个 ULD带序号
var uldIndex = 1
assembledCargoMap.forEach { (uldNo, waybillList) ->
// 计算总件数和总重量
var totalPieces = 0
var totalWeight = 0.0
waybillList.forEach { waybill ->
totalPieces += waybill.pieces.toIntOrNull() ?: 0
totalWeight += waybill.weight.toDoubleOrNull() ?: 0.0
}
// 创建一级 ULD 行
val uldHeader = AssembleInfoBean().apply {
itemType = AssembleInfoBean.ItemType.ULD_HEADER
this.uldNo = uldNo
this.uldIndex = uldIndex
this.totalPieces = totalPieces
this.totalWeight = totalWeight
hasArrow = true
showIndex = true // 显示序号
// 恢复展开状态
isExpanded.set(expandedUlds.contains(uldNo))
// 保存子运单(用于展开/折叠)
waybillChildren = waybillList.map { waybill ->
AssembleInfoBean().apply {
itemType = AssembleInfoBean.ItemType.WAYBILL_DETAIL
parentUldNo = uldNo
wbNo = waybill.waybillNo
waybillPieces = waybill.pieces.toIntOrNull() ?: 0
waybillWeight = waybill.weight.toDoubleOrNull() ?: 0.0
waybillData = waybill
showIndent = true
showIndex = false
}
}.toMutableList()
}
// 添加一级行
flatAssembleInfoList.add(uldHeader)
// 如果展开,添加二级运单行
if (uldHeader.isExpanded.get()) {
flatAssembleInfoList.addAll(uldHeader.waybillChildren)
}
// 递增序号
uldIndex++
}
// 更新 LiveData
assembleInfoList.value = flatAssembleInfoList.toMutableList()
}
/**
* 组装信息列表点击事件
* 1. 点击一级 ULD 行:切换展开/折叠状态
* 2. 点击二级运单行:进入编辑模式,填充表单
* 1. 点击一级ULD行展开/折叠(按需加载运单列表)
* 2. 点击二级运单行:填充表单(编辑模式)
*/
fun onAssembleInfoItemClick(position: Int) {
val list = flatAssembleInfoList
val list = assembleInfoList.value ?: return
if (position !in list.indices) return
val clickedItem = list[position]
when (clickedItem.itemType) {
AssembleInfoBean.ItemType.ULD_HEADER -> {
// 一级 ULD 行:切换展开/折叠
// 一级ULD行切换展开/折叠
val newExpandState = !clickedItem.isExpanded.get()
clickedItem.isExpanded.set(newExpandState)
// 重新生成扁平化列表
syncAssembleInfoListFromLocal()
if (newExpandState) {
// 展开查询该ULD的运单列表
loadWaybillsByUld(position, clickedItem)
} else {
// 折叠:移除二级运单行
removeWaybillRows(position)
}
}
AssembleInfoBean.ItemType.WAYBILL_DETAIL -> {
@@ -677,20 +554,111 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
}
}
/**
* 加载指定ULD的运单列表
*/
private fun loadWaybillsByUld(uldPosition: Int, uldItem: AssembleInfoBean) {
val uldBean = uldItem.tag as? GjcUldUseBean ?: return
// 检查是否已缓存
if (uldBean.waybillDetails != null) {
// 已缓存,直接展开
insertWaybillRows(uldPosition, uldBean.waybillDetails!!)
return
}
// 未缓存,调用接口查询
val params = uldBean.toRequestBody()
launchLoadingCollect({ NetApply.api.getAssembledWaybillsByUld(params) }) {
onSuccess = { result ->
val waybillList = result.data ?: emptyList()
if (waybillList.isEmpty()) {
showToast("该ULD暂无运单记录")
// 折叠回去
uldItem.isExpanded.set(false)
} else {
// 缓存到GjcUldUseBean
uldBean.waybillDetails = waybillList.toMutableList()
// 插入二级运单行
insertWaybillRows(uldPosition, waybillList)
}
}
onFailed = { code, message ->
showToast("查询失败: $message")
// 折叠回去
uldItem.isExpanded.set(false)
}
}
}
/**
* 在指定位置后插入二级运单行
*/
private fun insertWaybillRows(uldPosition: Int, waybillList: List<GjcWarehouse>) {
val list = assembleInfoList.value?.toMutableList() ?: return
val uldItem = list[uldPosition]
// 生成二级运单行
val waybillRows = waybillList.map { warehouse ->
AssembleInfoBean().apply {
itemType = AssembleInfoBean.ItemType.WAYBILL_DETAIL
parentUldNo = uldItem.uldNo
wbNo = warehouse.wbNo
waybillPieces = warehouse.pc.toInt()
waybillWeight = warehouse.weight
showIndent = true
showIndex = false
// 转换为AssembleWaybillBean用于填充表单
waybillData = AssembleWaybillBean().apply {
waybillNo = warehouse.wbNo
pieces = warehouse.pc.toString()
weight = String.format("%.1f", warehouse.weight)
flight = warehouse.flight
fno = warehouse.fno
fdate = warehouse.fdate
whId = warehouse.whId
}
}
}
// 插入到ULD行后面
list.addAll(uldPosition + 1, waybillRows)
assembleInfoList.value = list
}
/**
* 移除指定ULD后的二级运单行
*/
private fun removeWaybillRows(uldPosition: Int) {
val list = assembleInfoList.value?.toMutableList() ?: return
val uldItem = list[uldPosition]
// 移除该ULD的所有二级行
list.removeAll {
it.itemType == AssembleInfoBean.ItemType.WAYBILL_DETAIL &&
it.parentUldNo == uldItem.uldNo
}
assembleInfoList.value = list
}
/**
* 从二级运单行填充表单(编辑模式)
* 1. 填充 ULD 信息(并锁定 ULD 编号)
* 1. 填充ULD信息并锁定ULD编号
* 2. 填充运单信息(使用原始运单件数/重量,只读)
* 3. 保留组装件数、组装重量、组装人为可编辑
* 4. 标记编辑模式
*/
private fun fillFormFromWaybillDetail(item: AssembleInfoBean) {
val waybill = item.waybillData ?: return
// 1. 填充 ULD 信息
// 1. 填充ULD信息
uldInfo.value = uldInfo.value?.apply {
uldNo = item.parentUldNo
// 耗材重量、ULD 状态保持不变
// 耗材重量、ULD状态保持不变
}
// 保存当前的组装人(在创建新对象前)
@@ -707,109 +675,121 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
operator = previousOperator // 保留之前选择的组装人
}
// 3. 锁定 ULD 编号
// 3. 锁定ULD编号
isUldNoLocked.value = true
// 4. 标记编辑模式(存储原始 ULD 编号和运单号
editMode = Pair(item.parentUldNo, waybill.waybillNo)
// 5. 查询 ULD 信息(如果需要)
// 4. 查询ULD信息如果需要
if (item.parentUldNo != lastQueriedUldNo) {
lastQueriedUldNo = item.parentUldNo
queryUldInfo(item.parentUldNo)
}
}
/**
* 新增模式:添加已组装货物到本地存储
* @param isLoad true-装货false-卸货(减)
* 查询已组装的ULD列表
* 参数来源:
* - fno/fdate从当前选中的运单自动提取
* - loadArea从当前选中的组装位置获取
* - uld从ULD搜索框获取可选
*/
private fun addAssembledCargoToLocal(uldNo: String, waybill: AssembleWaybillBean, isLoad: Boolean) {
val list = assembledCargoMap.getOrPut(uldNo) { mutableListOf() }
fun loadAssembledList() {
// 1. 参数验证
val fno = assembleFlightNo.value ?: ""
val fdate = assembleFlightDate.value ?: ""
val loadArea = selectedPosition.value?.positionName ?: ""
val uldSearch = uldSearchText.value ?: ""
// 检查是否已存在
val existingIndex = list.indexOfFirst { it.waybillNo == waybill.waybillNo }
if (existingIndex >= 0) {
// 已存在:累加或减去件数和重量
val existing = list[existingIndex]
val existingPieces = existing.pieces.toIntOrNull() ?: 0
val existingWeight = existing.weight.toDoubleOrNull() ?: 0.0
val newPieces = waybill.pieces.toIntOrNull() ?: 0
val newWeight = waybill.weight.toDoubleOrNull() ?: 0.0
// 必填参数检查
if (fno.isEmpty() || fdate.isEmpty() || loadArea.isEmpty()) {
// 参数缺失,清空列表(静默处理)
assembleInfoList.value = mutableListOf()
return
}
if (isLoad) {
// 装货:累加
existing.pieces = (existingPieces + newPieces).toString()
existing.weight = String.format("%.1f", existingWeight + newWeight)
} else {
// 卸货:减去
val resultPieces = existingPieces - newPieces
val resultWeight = existingWeight - newWeight
// 2. 防抖检查(避免重复查询)
val currentParams = "$fno|$fdate|$loadArea|$uldSearch"
if (currentParams == lastQueriedAssembledParams) {
return
}
lastQueriedAssembledParams = currentParams
if (resultPieces <= 0 || resultWeight <= 0) {
// 减到0或负数从列表中移除
list.removeAt(existingIndex)
// 如果该ULD下没有运单了删除整个ULD
if (list.isEmpty()) {
assembledCargoMap.remove(uldNo)
}
// 3. 构建请求参数
val params = mapOf(
"fno" to fno,
"fdate" to fdate,
"loadArea" to loadArea,
"uld" to uldSearch.ifEmpty { null } // 空值时传null
).toRequestBody()
// 4. 调用接口
launchLoadingCollect({ NetApply.api.getAssembledList(params) }) {
onSuccess = { result ->
val uldList = result.data ?: emptyList()
if (uldList.isEmpty()) {
showToast("暂无已组装记录")
assembleInfoList.value = mutableListOf()
} else {
existing.pieces = resultPieces.toString()
existing.weight = String.format("%.1f", resultWeight)
// 转换为AssembleInfoBean列表
convertToAssembleInfoBeans(uldList)
}
}
} else {
// 不存在:直接新增(只有装货时才会走到这里)
if (isLoad) {
list.add(waybill)
onFailed = { code, message ->
showToast("查询失败: $message")
assembleInfoList.value = mutableListOf()
}
}
}
/**
* 编辑模式:更新本地存储中的运单数据
* @param isLoad true-装货false-卸货(减)
* 数据转换: List<GjcAssembled> → List<AssembleInfoBean>
* 只生成一级ULD行不包含二级运单行
* 同时将运单列表缓存到uldBean.waybillDetails
*/
private fun updateAssembledCargoInLocal(
uldNo: String,
oldWaybillNo: String,
newWaybillBean: AssembleWaybillBean,
isLoad: Boolean
) {
val list = assembledCargoMap[uldNo] ?: return
private fun convertToAssembleInfoBeans(assembledList: List<GjcAssembled>) {
val infoList = assembledList.mapIndexed { index, assembled ->
val uldBean = assembled.uldUse
val warehouseList = assembled.warehouseList
// 查找并更新
val index = list.indexOfFirst { it.waybillNo == oldWaybillNo }
if (index >= 0) {
val existing = list[index]
val existingPieces = existing.pieces.toIntOrNull() ?: 0
val existingWeight = existing.weight.toDoubleOrNull() ?: 0.0
val newPieces = newWaybillBean.pieces.toIntOrNull() ?: 0
val newWeight = newWaybillBean.weight.toDoubleOrNull() ?: 0.0
if (isLoad) {
// 装货:累加
existing.pieces = (existingPieces + newPieces).toString()
existing.weight = String.format("%.1f", existingWeight + newWeight)
} else {
// 卸货:减去
val resultPieces = existingPieces - newPieces
val resultWeight = existingWeight - newWeight
if (resultPieces <= 0 || resultWeight <= 0) {
// 减到0或负数从列表中移除
list.removeAt(index)
// 如果该ULD下没有运单了删除整个ULD
if (list.isEmpty()) {
assembledCargoMap.remove(uldNo)
}
} else {
existing.pieces = resultPieces.toString()
existing.weight = String.format("%.1f", resultWeight)
}
if (uldBean == null) {
return@mapIndexed null
}
}
// 缓存运单列表到uldBean展开时不需要再次查询
if (warehouseList != null && warehouseList.isNotEmpty()) {
uldBean.waybillDetails = warehouseList
}
// 计算总件数和总重量(从运单列表求和)
val calculatedPieces = warehouseList?.sumOf { it.pc.toInt() } ?: 0
val calculatedWeight = warehouseList?.sumOf { it.weight } ?: 0.0
AssembleInfoBean().apply {
itemType = AssembleInfoBean.ItemType.ULD_HEADER
uldNo = uldBean.uld
uldIndex = index + 1
// 使用计算的总件数和总重量
totalPieces = calculatedPieces
totalWeight = calculatedWeight
hasArrow = true
showIndex = true
isExpanded.set(false) // 默认折叠
// 关联原始ULD数据用于点击展开时查询
tag = uldBean // 使用tag存储GjcUldUseBean对象
}
}.filterNotNull().toMutableList()
assembleInfoList.value = infoList
}
/**
* ULD搜索框输入完成回调
*/
fun onUldSearchInputComplete() {
loadAssembledList()
}
/**
@@ -823,6 +803,5 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
operator = previousOperator // 恢复组装人
}
isUldNoLocked.value = false
editMode = null
}
}

View File

@@ -46,11 +46,13 @@
android:layout_weight="3"
android:background="@drawable/bg_white_radius_8"
android:orientation="vertical">
<!-- 搜索框 -->
<!-- ULD搜索框 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayoutNew
hint='@{"请输入ULD编"}'
hint='@{"请输入ULD编"}'
icon="@{@drawable/img_search}"
setRefreshCallBack="@{viewModel::onUldSearchInputComplete}"
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.searchText}"
value="@={viewModel.uldSearchText}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp" />