feat: 国际出港仓库入库/出库支持填写件数重量

- 入库弹窗新增件数、重量输入,默认取选中运单件数重量
- 出库改用自定义弹窗(替换 AlertDialog),新增件数、重量输入,默认取库位入库件数重量
- 出库接口请求体由数组改为对象,选中库位放入 storageUseList
- 库位子列表新增入库件数/重量、出库件数/重量四列

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-24 10:47:03 +08:00
parent aebf1b1090
commit 2dc86bcae5
10 changed files with 405 additions and 22 deletions

View File

@@ -1,6 +1,7 @@
package com.lukouguoji.module_base.bean package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import com.lukouguoji.module_base.ktx.toPlainText
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@@ -320,12 +321,21 @@ data class GjcStorageUse(
var outDate: String? = null, // 出库时间 var outDate: String? = null, // 出库时间
var outOpId: String? = null, // 出库人 var outOpId: String? = null, // 出库人
var outId: String? = null, // 出库人(兼容字段) var outId: String? = null, // 出库人(兼容字段)
var cargoStatus: String? = null // 货物状态 var cargoStatus: String? = null, // 货物状态
var inPc: Long? = null, // 入库件数
var inWeight: Double? = null, // 入库重量
var outPc: Long? = null, // 出库件数
var outWeight: Double? = null // 出库重量
) { ) {
// ==================== UI扩展字段 ==================== // ==================== UI扩展字段 ====================
@Transient @Transient
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态 val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
val inPcText: String get() = inPc?.toString() ?: ""
val inWeightText: String get() = inWeight?.toPlainText() ?: ""
val outPcText: String get() = outPc?.toString() ?: ""
val outWeightText: String get() = outWeight?.toPlainText() ?: ""
// 兼容现有API的isSelected属性 // 兼容现有API的isSelected属性
var isSelected: Boolean var isSelected: Boolean
get() = checked.get() get() = checked.get()

View File

@@ -14,3 +14,9 @@ fun Number.limit(count: Int = 2): String {
} }
return DecimalFormat(pattern.toString()).format(this) return DecimalFormat(pattern.toString()).format(this)
} }
/**
* 去掉无意义的小数位:12.0 -> "12",12.50 -> "12.5"
*/
fun Double.toPlainText(): String =
if (this % 1.0 == 0.0) toLong().toString() else toBigDecimal().stripTrailingZeros().toPlainString()

View File

@@ -3,13 +3,13 @@ package com.lukouguoji.gjc.activity
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpStorageUseBinding import com.lukouguoji.gjc.databinding.ActivityIntExpStorageUseBinding
import com.lukouguoji.gjc.dialog.IntExpMoveClearDialogModel import com.lukouguoji.gjc.dialog.IntExpMoveClearDialogModel
import com.lukouguoji.gjc.dialog.IntExpModifyStorageDialogModel import com.lukouguoji.gjc.dialog.IntExpModifyStorageDialogModel
import com.lukouguoji.gjc.dialog.IntExpInStorageDialogModel import com.lukouguoji.gjc.dialog.IntExpInStorageDialogModel
import com.lukouguoji.gjc.dialog.IntExpOutStorageDialogModel
import com.lukouguoji.gjc.viewModel.IntExpStorageUseViewModel import com.lukouguoji.gjc.viewModel.IntExpStorageUseViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.Constant
@@ -121,7 +121,7 @@ class IntExpStorageUseActivity :
} }
/** /**
* 显示出库二次确认对话框 * 显示出库操作对话框
*/ */
fun showOutStorageDialog() { fun showOutStorageDialog() {
val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return
@@ -139,16 +139,19 @@ class IntExpStorageUseActivity :
return return
} }
// 显示二次确认对话框 // 显示出库对话框,件数/重量默认取选中库位的入库件数/重量
AlertDialog.Builder(this) IntExpOutStorageDialogModel(
.setTitle("出库确认") storageCount = selectedStorageUseList.size,
.setMessage("确定要将选中的 ${selectedStorageUseList.size} 个库位执行出库操作吗?") defaultPc = selectedStorageUseList.mapNotNull { it.inPc }.takeIf { it.isNotEmpty() }?.sum(),
.setPositiveButton("确定") { _, _ -> defaultWeight = selectedStorageUseList.mapNotNull { it.inWeight }.takeIf { it.isNotEmpty() }?.sum()
// 用户确认后,执行出库操作 ) { dialog ->
viewModel.performOutStorage(selectedStorageUseList) // 用户点击确定后,执行出库操作
} viewModel.performOutStorage(
.setNegativeButton("取消", null) dialog.pc.value?.toLongOrNull(),
.show() dialog.weight.value?.toDoubleOrNull(),
selectedStorageUseList
)
}.show(this)
} }
/** /**
@@ -177,12 +180,19 @@ class IntExpStorageUseActivity :
return return
} }
// 显示入库对话框 // 显示入库对话框,件数/重量默认取选中运单的件数/重量
IntExpInStorageDialogModel { dialog -> IntExpInStorageDialogModel(
defaultPc = maWbListForInStorage.mapNotNull { it.pc }.takeIf { it.isNotEmpty() }?.sum(),
defaultWeight = maWbListForInStorage.mapNotNull { it.weight }.takeIf { it.isNotEmpty() }?.sum()
) { dialog ->
// 用户点击保存后,执行入库操作 // 用户点击保存后,执行入库操作
val locationName = dialog.locationName viewModel.performInStorage(
val locationId = dialog.locationId dialog.locationName,
viewModel.performInStorage(locationName, locationId, maWbListForInStorage) dialog.locationId,
dialog.pc.value?.toLongOrNull(),
dialog.weight.value?.toDoubleOrNull(),
maWbListForInStorage
)
}.show(this) }.show(this)
} }

View File

@@ -1,6 +1,7 @@
package com.lukouguoji.gjc.dialog package com.lukouguoji.gjc.dialog
import android.content.Context import android.content.Context
import android.text.InputType
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogIntExpInStorageBinding import com.lukouguoji.gjc.databinding.DialogIntExpInStorageBinding
@@ -8,13 +9,18 @@ import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.http.net.NetApply import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchCollect import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toPlainText
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue import dev.utils.app.info.KeyValue
/** /**
* 国际出港仓库 - 入库操作对话框 * 国际出港仓库 - 入库操作对话框
* @param defaultPc 默认件数(选中运单的件数)
* @param defaultWeight 默认重量(选中运单的重量)
*/ */
class IntExpInStorageDialogModel( class IntExpInStorageDialogModel(
defaultPc: Long?,
defaultWeight: Double?,
private val callback: (IntExpInStorageDialogModel) -> Unit private val callback: (IntExpInStorageDialogModel) -> Unit
) : BaseDialogModel<DialogIntExpInStorageBinding>(DIALOG_TYPE_CENTER) { ) : BaseDialogModel<DialogIntExpInStorageBinding>(DIALOG_TYPE_CENTER) {
@@ -30,12 +36,20 @@ class IntExpInStorageDialogModel(
// 库位名称 (后端需要的name) // 库位名称 (后端需要的name)
var locationName: String = "" var locationName: String = ""
// 入库件数
val pc = MutableLiveData(defaultPc?.toString() ?: "")
// 入库重量
val weight = MutableLiveData(defaultWeight?.toPlainText() ?: "")
override fun layoutId(): Int { override fun layoutId(): Int {
return R.layout.dialog_int_exp_in_storage return R.layout.dialog_int_exp_in_storage
} }
override fun onDialogCreated(context: Context) { override fun onDialogCreated(context: Context) {
binding.model = this binding.model = this
binding.pcInput.et.inputType = InputType.TYPE_CLASS_NUMBER
binding.weightInput.et.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
loadLocationList() loadLocationList()
// 监听选择变化,更新locationId和locationName // 监听选择变化,更新locationId和locationName
@@ -68,6 +82,14 @@ class IntExpInStorageDialogModel(
if (selectedLocationCode.value.verifyNullOrEmpty("请选择库位")) { if (selectedLocationCode.value.verifyNullOrEmpty("请选择库位")) {
return return
} }
if (pc.value?.toLongOrNull() == null) {
showToast("请输入正确的件数")
return
}
if (weight.value?.toDoubleOrNull() == null) {
showToast("请输入正确的重量")
return
}
dismiss() dismiss()
callback(this) callback(this)
} }

View File

@@ -0,0 +1,58 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import android.text.InputType
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogIntExpOutStorageBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toPlainText
/**
* 国际出港仓库 - 出库操作对话框
* @param storageCount 选中的库位数
* @param defaultPc 默认件数(选中库位的入库件数)
* @param defaultWeight 默认重量(选中库位的入库重量)
*/
class IntExpOutStorageDialogModel(
storageCount: Int,
defaultPc: Long?,
defaultWeight: Double?,
private val callback: (IntExpOutStorageDialogModel) -> Unit
) : BaseDialogModel<DialogIntExpOutStorageBinding>(DIALOG_TYPE_CENTER) {
val tip = "已选择 $storageCount 个库位执行出库操作"
// 出库件数
val pc = MutableLiveData(defaultPc?.toString() ?: "")
// 出库重量
val weight = MutableLiveData(defaultWeight?.toPlainText() ?: "")
override fun layoutId(): Int {
return R.layout.dialog_int_exp_out_storage
}
override fun onDialogCreated(context: Context) {
binding.model = this
binding.pcInput.et.inputType = InputType.TYPE_CLASS_NUMBER
binding.weightInput.et.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
}
/**
* 确定按钮点击
*/
fun onSaveClick() {
if (pc.value?.toLongOrNull() == null) {
showToast("请输入正确的件数")
return
}
if (weight.value?.toDoubleOrNull() == null) {
showToast("请输入正确的重量")
return
}
dismiss()
callback(this)
}
}

View File

@@ -225,16 +225,26 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
/** /**
* 执行出库操作 * 执行出库操作
* @param pc 出库件数
* @param weight 出库重量
* @param selectedStorageList 选中的子列表项(库位) * @param selectedStorageList 选中的子列表项(库位)
*/ */
fun performOutStorage(selectedStorageList: List<com.lukouguoji.module_base.bean.GjcStorageUse>) { fun performOutStorage(
pc: Long?,
weight: Double?,
selectedStorageList: List<com.lukouguoji.module_base.bean.GjcStorageUse>
) {
if (selectedStorageList.isEmpty()) { if (selectedStorageList.isEmpty()) {
showToast("请选择要出库的库位") showToast("请选择要出库的库位")
return return
} }
// 将选中的子列表项转换为RequestBody // 构建请求参数:出库件数 + 出库重量 + 选中的子列表项
val params = selectedStorageList.toRequestBody() val params = mapOf(
"pc" to pc,
"weight" to weight,
"storageUseList" to selectedStorageList
).toRequestBody()
launchLoadingCollect({ NetApply.api.outIntExpStorage(params) }) { launchLoadingCollect({ NetApply.api.outIntExpStorage(params) }) {
onSuccess = { onSuccess = {
@@ -261,11 +271,15 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
* 执行入库操作 * 执行入库操作
* @param locationName 库位名称 (后端的location字段) * @param locationName 库位名称 (后端的location字段)
* @param locationId 库位ID (后端的locationId字段) * @param locationId 库位ID (后端的locationId字段)
* @param pc 入库件数
* @param weight 入库重量
* @param maWbListForInStorage 包含选中子列表项的主列表数据 * @param maWbListForInStorage 包含选中子列表项的主列表数据
*/ */
fun performInStorage( fun performInStorage(
locationName: String, locationName: String,
locationId: String, locationId: String,
pc: Long?,
weight: Double?,
maWbListForInStorage: List<GjcMaWb> maWbListForInStorage: List<GjcMaWb>
) { ) {
if (maWbListForInStorage.isEmpty()) { if (maWbListForInStorage.isEmpty()) {
@@ -278,10 +292,12 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
return return
} }
// 构建请求参数:库位名称 + 库位ID + 完整的主子列表结构 // 构建请求参数:库位名称 + 库位ID + 入库件数 + 入库重量 + 完整的主子列表结构
val params = mapOf( val params = mapOf(
"location" to locationName, "location" to locationName,
"locationId" to locationId.toLongOrNull(), "locationId" to locationId.toLongOrNull(),
"pc" to pc,
"weight" to weight,
"maWbList" to maWbListForInStorage "maWbList" to maWbListForInStorage
).toRequestBody() ).toRequestBody()

View File

@@ -62,6 +62,60 @@
</LinearLayout> </LinearLayout>
<!-- 件数 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{5}"
android:text="件数:"
android:textColor="@color/text_normal"
android:textSize="16sp" />
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:id="@+id/pc_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
type="@{SearchLayoutType.INPUT}"
hint='@{"请输入件数"}'
value="@={model.pc}" />
</LinearLayout>
<!-- 重量 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{5}"
android:text="重量:"
android:textColor="@color/text_normal"
android:textSize="16sp" />
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:id="@+id/weight_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
type="@{SearchLayoutType.INPUT}"
hint='@{"请输入重量"}'
value="@={model.weight}" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<!-- 底部按钮 --> <!-- 底部按钮 -->

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType"/>
<variable
name="model"
type="com.lukouguoji.gjc.dialog.IntExpOutStorageDialogModel" />
</data>
<LinearLayout
android:layout_width="480dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_dialog_f2_radius_10"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- 标题栏 -->
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/bg_primary_radius_top_10"
android:gravity="center"
android:text="出库操作"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
<!-- 表单内容 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginTop="30dp"
android:orientation="vertical">
<!-- 提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{model.tip}"
android:textColor="@color/text_normal"
android:textSize="16sp" />
<!-- 件数 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{5}"
android:text="件数:"
android:textColor="@color/text_normal"
android:textSize="16sp" />
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:id="@+id/pc_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
type="@{SearchLayoutType.INPUT}"
hint='@{"请输入件数"}'
value="@={model.pc}" />
</LinearLayout>
<!-- 重量 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{5}"
android:text="重量:"
android:textColor="@color/text_normal"
android:textSize="16sp" />
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:id="@+id/weight_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
type="@{SearchLayoutType.INPUT}"
hint='@{"请输入重量"}'
value="@={model.weight}" />
</LinearLayout>
</LinearLayout>
<!-- 底部按钮 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginBottom="20dp">
<TextView
style="@style/tv_bottom_btn"
android:onClick="@{()->model.dismiss()}"
android:text="取消" />
<TextView
style="@style/tv_bottom_btn"
android:onClick="@{()->model.onSaveClick()}"
android:text="确定" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@@ -402,6 +402,26 @@
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center"
android:text="入库件数"
android:textColor="@color/text_normal"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center"
android:text="入库重量"
android:textColor="@color/text_normal"
android:textSize="14sp"
android:textStyle="bold" />
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -422,6 +442,26 @@
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center"
android:text="出库件数"
android:textColor="@color/text_normal"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center"
android:text="出库重量"
android:textColor="@color/text_normal"
android:textSize="14sp"
android:textStyle="bold" />
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -47,6 +47,28 @@
android:textColor="@color/text_normal" android:textColor="@color/text_normal"
android:textSize="14sp" /> android:textSize="14sp" />
<!-- 入库件数 -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="0.8"
android:gravity="center"
android:text="@{bean.inPcText}"
android:textColor="@color/text_normal"
android:textSize="14sp" />
<!-- 入库重量 -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="0.8"
android:gravity="center"
android:text="@{bean.inWeightText}"
android:textColor="@color/text_normal"
android:textSize="14sp" />
<!-- 入库人 --> <!-- 入库人 -->
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
@@ -69,6 +91,28 @@
android:textColor="@color/text_normal" android:textColor="@color/text_normal"
android:textSize="14sp" /> android:textSize="14sp" />
<!-- 出库件数 -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="0.8"
android:gravity="center"
android:text="@{bean.outPcText}"
android:textColor="@color/text_normal"
android:textSize="14sp" />
<!-- 出库重量 -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="0.8"
android:gravity="center"
android:text="@{bean.outWeightText}"
android:textColor="@color/text_normal"
android:textSize="14sp" />
<!-- 出库人 --> <!-- 出库人 -->
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"