feat(ULD管理): 对接后端调整——两态状态/来源/inStoreTime/批量删除接口(问题清单 #7~#11)

- #7 状态维持两态:移除「占用」预留映射与橙色标签
- #8 来源 source(空运/陆运)出参与保存入参已支持;查询入参不支持 source,筛选弹层移除来源
- #9 查询参数与平板一致(uld/status/uldSuffix):筛选弹层改为 状态 + 所属航司,移除入库时间/进港航班号
- #10 批量删除改为一次调用 /eqm/uld/deleteUldBatch(请求体 ULD 编号数组)
- #11 入库时间改绑 inStoreTime;所在港改绑 nowPort(与平板端一致);手机日期行只产出 yyyy-MM-dd,
  保存时补齐 00:00:00(仅日期后端返回 400,内网实测)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-10-09 03:14:04 -07:00
parent b3b884504d
commit 415b685a85
8 changed files with 63 additions and 96 deletions

View File

@@ -36,7 +36,7 @@ class UldEditViewModel : BaseViewModel() {
// ========== 手机版(5.5 寸手持端)专用 ==========
/** 来源选项(本地枚举;后端字段暂不支持,需求已登记 documents/后端接口对接问题清单.xlsx) */
/** 来源选项(与平板端一致:空运 / 陆运) */
val sourceEditList = MutableLiveData<List<KeyValue>>(
listOf(
KeyValue("空运", "空运"),
@@ -45,8 +45,8 @@ class UldEditViewModel : BaseViewModel() {
)
/**
* 手机版状态选项。设计稿为三态(可用/占用/维修),后端枚举当前仅 0/1,
* 按语义映射 可用=0 / 维修=1;「占用」待后端扩展枚举后放开(需求已登记清单)。
* 手机版状态选项。后端枚举维持两态(0 正常 / 1 故障,不新增「占用」),
* 按设计稿文案映射 可用=0 / 维修=1。
*/
val statusPhoneList = listOf(
KeyValue("可用", "0"),
@@ -64,7 +64,7 @@ class UldEditViewModel : BaseViewModel() {
loadCarrierList(null)
// 手机版新增默认所在港 HFE(设计稿约定);平板无此预填,行为不变
if (DeviceUtil.isPhone()) {
uldBean.value = uldBean.value?.copy(airport = "HFE")
uldBean.value = uldBean.value?.copy(nowPort = "HFE")
}
} else {
getData()
@@ -107,11 +107,15 @@ class UldEditViewModel : BaseViewModel() {
showToast("自重不能为空")
return
}
if (bean.checkInDate.isBlank()) {
if (bean.inStoreTime.isBlank()) {
showToast("入库时间不能为空")
return
}
}
// 手机版日期行只产出 yyyy-MM-dd,而 inStoreTime 为 date-time(仅日期后端返回 400),补齐时分秒
if (DeviceUtil.isPhone() && bean.inStoreTime.length == 10) {
bean.inStoreTime = "${bean.inStoreTime} 00:00:00"
}
val isAdd = pageType.value == DetailsPageType.Add
launchLoadingCollect({
if (isAdd) NetApply.api.saveUld(bean.toRequestBody())
@@ -121,7 +125,7 @@ class UldEditViewModel : BaseViewModel() {
showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功"))
if (isAdd) {
// 清空表单继续录入;手机版保持所在港默认 HFE(与进入页面时一致)
uldBean.value = if (DeviceUtil.isPhone()) ULDBean(airport = "HFE") else ULDBean()
uldBean.value = if (DeviceUtil.isPhone()) ULDBean(nowPort = "HFE") else ULDBean()
} else {
getData()
}

View File

@@ -70,13 +70,11 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
/**
* 手机版筛选:重置
*
* 只清空弹层内的四个条件(入库时间 / 进港航班号 / 所属航司 / 来源),不影响顶部搜索框。
* 只清空弹层内的两个条件(状态 / 所属航司),不影响顶部搜索框。
*/
fun resetFilter() {
viewModel.checkInDateFilter.value = ""
viewModel.ifNoFilter.value = ""
viewModel.status.value = ""
viewModel.uldSuffix.value = ""
viewModel.sourceFilter.value = ""
}
/** 手机版筛选:确认 */
@@ -85,7 +83,7 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
viewModel.refresh()
}
/** 手机版批量删除(二次确认后逐条调用单条删除接口) */
/** 手机版批量删除(二次确认后调用批量删除接口) */
fun showDeleteConfirmDialog() {
val selected = viewModel.getSelectedItems()
if (selected.isEmpty()) {

View File

@@ -27,20 +27,15 @@ class UldListViewModel : BasePageViewModel() {
val uldSuffix = MutableLiveData("")
val uldNo = MutableLiveData("")
// ========== 手机版(5.5 寸手持端)专用筛选条件 ==========
// 平板布局无对应控件,恒为空 → 不进请求参数,平板查询行为与改造前完全一致。
// 三个入参后端暂不支持(api-doc 已核对),已登记 documents/后端接口对接问题清单.xlsx,
// 按「以设计为准」的约定照传(后端静默忽略无副作用),后端支持后自动生效。
val checkInDateFilter = MutableLiveData("") // 入库时间
val ifNoFilter = MutableLiveData("") // 进港航班号
val sourceFilter = MutableLiveData("") // 来源:空运 / 陆运
// ========== 手机版(5.5 寸手持端)筛选 ==========
// 后端确认查询入参与平板端保持一致(uld / status / uldSuffix),手机筛选弹层只放 状态 + 所属航司。
/** 来源下拉(本地枚举,后端字段需求已登记清单) */
val sourceList = MutableLiveData<List<KeyValue>>(
/** 手机版状态下拉:文案与卡片标签一致(可用=0 / 维修=1) */
val statusPhoneFilterList = MutableLiveData<List<KeyValue>>(
listOf(
KeyValue("全部", ""),
KeyValue("空运", "空运"),
KeyValue("陆运", "陆运"),
KeyValue("可用", "0"),
KeyValue("维修", "1"),
)
)
@@ -82,10 +77,6 @@ class UldListViewModel : BasePageViewModel() {
"status" to status.value?.ifEmpty { null },
"uldSuffix" to uldSuffix.value?.ifEmpty { null },
"uld" to uldNo.value?.ifEmpty { null },
// 手机版筛选维度(平板恒空不参与);后端暂不支持这三个入参,已登记清单
"checkInDate" to checkInDateFilter.value?.ifEmpty { null },
"ifNo" to ifNoFilter.value?.ifEmpty { null },
"source" to sourceFilter.value?.ifEmpty { null },
).toRequestBody()
)
}) {
@@ -122,10 +113,7 @@ class UldListViewModel : BasePageViewModel() {
?.filter { it.checked.get() } ?: emptyList()
/**
* 批量删除选中的 ULD。
*
* 后端仅有单条删除接口(/eqm/uld/deleteUld,批量接口需求已登记清单),
* 此处按队列逐条串行调用,全部完成后统一刷新;中途失败即停并提示。
* 批量删除选中的 ULD(/eqm/uld/deleteUldBatch,请求体为 ULD 编号数组,一次调用)。
*/
fun deleteSelected() {
val ulds = getSelectedItems().map { it.uld }
@@ -133,20 +121,10 @@ class UldListViewModel : BasePageViewModel() {
showToast("请选择要删除的ULD")
return
}
deleteQueue(ulds, 0)
}
private fun deleteQueue(ulds: List<String>, index: Int) {
if (index >= ulds.size) {
launchLoadingCollect({ NetApply.api.deleteUldBatch(ulds.toRequestBody()) }) {
onSuccess = {
showToast("已删除 ${ulds.size} 条")
refresh()
return
}
launchLoadingCollect({ NetApply.api.deleteUld(ulds[index]) }) {
onSuccess = { deleteQueue(ulds, index + 1) }
onFailed = { _, msg ->
showToast("删除 ${ulds[index]} 失败:${msg.noNull("未知错误")}")
refresh()
}
}
}

View File

@@ -14,8 +14,8 @@
· ULD修改(Modify):编号只读,其余可编辑 + 底部保存
· ULD详情(Details):全只读右对齐 + 顶栏右侧铅笔进入修改 + 额外展示最大装载容积/最大载重
「来源」「状态三态(占用)」为后端暂不支持的设计稿要求,
已登记 documents/后端接口对接问题清单.xlsx,UI 先行、后端补齐后自动生效。
字段与平板端一致:来源 source(空运/陆运)、入库时间 inStoreTime、所在港 nowPort;
状态维持两态(0 可用 / 1 维修,后端不新增「占用」)。
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
@@ -119,11 +119,11 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="22dp"
android:background="@{viewModel.uldBean.status.empty ? null : (viewModel.uldBean.isStatusAvailable ? @drawable/bg_phone_tag_green : (viewModel.uldBean.isStatusOccupied ? @drawable/bg_phone_tag_orange : @drawable/bg_phone_tag_red_light))}"
android:background="@{viewModel.uldBean.status.empty ? null : (viewModel.uldBean.isStatusAvailable ? @drawable/bg_phone_tag_green : @drawable/bg_phone_tag_red_light)}"
android:gravity="center"
android:paddingHorizontal="8dp"
android:text='@{viewModel.uldBean.status.empty ? "请选择状态" : viewModel.uldBean.statusPhoneName}'
android:textColor="@{viewModel.uldBean.status.empty ? @color/phone_text_hint : (viewModel.uldBean.isStatusAvailable ? @color/phone_tag_green_text : (viewModel.uldBean.isStatusOccupied ? @color/phone_tag_orange_text : @color/phone_danger_text))}"
android:textColor="@{viewModel.uldBean.status.empty ? @color/phone_text_hint : (viewModel.uldBean.isStatusAvailable ? @color/phone_tag_green_text : @color/phone_danger_text)}"
android:textSize="12sp" />
<!-- 编辑态右箭头 -->
@@ -190,7 +190,7 @@
value="@{viewModel.uldBean.maxWeight}"
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
<!-- 进港航班号(绑 ifNo,语义待后端确认,已登记清单) -->
<!-- 进港航班号(ifNo) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -200,7 +200,7 @@
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@={viewModel.uldBean.ifNo}" />
<!-- 所在港(新增默认 HFE,由 ViewModel 手机开关预填) -->
<!-- 所在港(nowPort,与平板端一致;新增默认 HFE,由 ViewModel 手机开关预填) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -208,9 +208,9 @@
hint='@{"请输入所在港"}'
title='@{"所在港"}'
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
value="@={viewModel.uldBean.airport}" />
value="@={viewModel.uldBean.nowPort}" />
<!-- 来源(设计稿顺序位于所在港之后;后端字段暂不支持,已登记清单,提交后端忽略) -->
<!-- 来源(source:空运/陆运,设计稿顺序位于所在港之后) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -221,7 +221,7 @@
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
value="@={viewModel.uldBean.source}" />
<!-- 入库时间(绑 checkInDate,筛选/格式语义待后端确认,已登记清单) -->
<!-- 入库时间(inStoreTime,与平板端一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -230,7 +230,7 @@
required="@{viewModel.pageType == DetailsPageType.Add}"
title='@{"入库时间"}'
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.DATE}"
value="@={viewModel.uldBean.checkInDate}" />
value="@={viewModel.uldBean.inStoreTime}" />
<!-- 备注 -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow

View File

@@ -130,23 +130,15 @@
setOnResetClick="@{(v)-> activity.resetFilter()}"
title='@{"筛选条件"}'>
<!-- 入库时间(后端筛选入参暂不支持,已登记清单) -->
<!-- 状态(后端入参 status,与平板端查询参数一致) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入入库时间"}'
title='@{"入库时间"}'
type="@{PhoneDataLayoutType.DATE}"
value="@={viewModel.checkInDateFilter}" />
<!-- 进港航班号(后端筛选入参暂不支持,已登记清单) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请输入进港航班号"}'
title='@{"进港航班号"}'
type="@{PhoneDataLayoutType.INPUT}"
value="@={viewModel.ifNoFilter}" />
hint='@{"请选择状态"}'
list="@{viewModel.statusPhoneFilterList}"
title='@{"状态"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.status}" />
<!-- 所属航司(后端入参 uldSuffix,真实生效) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
@@ -158,16 +150,6 @@
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.uldSuffix}" />
<!-- 来源(后端字段暂不支持,已登记清单) -->
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
hint='@{"请选择来源"}'
list="@{viewModel.sourceList}"
title='@{"来源"}'
type="@{PhoneDataLayoutType.SPINNER}"
value="@={viewModel.sourceFilter}" />
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
</FrameLayout>

View File

@@ -11,8 +11,7 @@
· 平板独有 id(tv_modify / tv_delete / iv_icon)与手机独有 id(iv_check)
在 Binding 基类中自动为 @Nullable,ViewHolder 判空使用
状态标签三色(设计稿):可用=浅绿 / 占用=浅橙 / 维修=浅红。
后端当前仅两态(0 可用 / 1 维修),「占用」为后端扩展枚举后的预留,见 ULDBean.statusPhoneName。
状态标签:可用=浅绿 / 维修=浅红(后端枚举维持两态 0/1,不含「占用」),见 ULDBean.statusPhoneName。
「来源」字段后端暂无(恒空显示 "-"),已登记 documents/后端接口对接问题清单.xlsx。
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -61,16 +60,16 @@
android:textSize="15sp"
android:textStyle="bold" />
<!-- 状态标签:可用(绿) / 占用(橙) / 维修(红,浅红底红字) -->
<!-- 状态标签:可用(绿) / 维修(红,浅红底红字) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:background="@{bean.isStatusAvailable ? @drawable/bg_phone_tag_green : (bean.isStatusOccupied ? @drawable/bg_phone_tag_orange : @drawable/bg_phone_tag_red_light)}"
android:background="@{bean.isStatusAvailable ? @drawable/bg_phone_tag_green : @drawable/bg_phone_tag_red_light}"
android:gravity="center"
android:paddingHorizontal="8dp"
android:text="@{bean.statusPhoneName}"
android:textColor="@{bean.isStatusAvailable ? @color/phone_tag_green_text : (bean.isStatusOccupied ? @color/phone_tag_orange_text : @color/phone_danger_text)}"
android:textColor="@{bean.isStatusAvailable ? @color/phone_tag_green_text : @color/phone_danger_text}"
android:textSize="11sp" />
<ImageView
@@ -137,7 +136,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_weight="1"
android:text='@{"入库时间:" + (bean.checkInDate.empty ? "-" : bean.checkInDate)}'
android:text='@{"入库时间:" + (bean.inStoreTime.empty ? "-" : bean.inStoreTime)}'
android:textColor="@color/phone_text_body"
android:textSize="13sp" />

View File

@@ -19,18 +19,21 @@ data class ULDBean(
var status: String = "",
// 备注
var remark: String = "",
// ========== 以下字段接口出参已有(api-doc /eqm/uld/pageQuery),此前 Bean 未解析 ==========
// 进港航班号(语义待后端确认,见 documents/后端接口对接问题清单.xlsx)
// ========== 以下字段接口出参已有(api-doc /eqm/uld/pageQuery) ==========
// 进港航班号
var ifNo: String = "",
// 出港航班号
var efNo: String = "",
// 入库时间
var checkInDate: String = "",
// 出库时间
var checkOutDate: String = "",
// ========== 后端暂无对应字段,前端按设计稿预留(提交后端忽略,无副作用) ==========
// 来源:空运 / 陆运(后端字段需求已登记清单)
// 来源(空运、陆运)
var source: String = "",
// 入库时间 yyyy-MM-dd HH:mm:ss(后端确认「入库时间」取 inStoreTime,与平板端一致)
var inStoreTime: String = "",
// 所在港
var nowPort: String = "",
// 位置信息
var nowPosition: String = "",
){
val checked = ObservableBoolean(false)
@@ -40,22 +43,18 @@ data class ULDBean(
else -> status
}
// ========== 手机版(5.5 寸手持端)三态状态展示 ==========
// 设计稿状态为 可用(绿)/占用(橙)/维修(红) 三态;后端当前仅两态(0/1),
// 按语义映射 0→可用、1→维修,"2"→占用为后端扩展枚举后的预留(需求已登记清单)。
// ========== 手机版(5.5 寸手持端)状态展示 ==========
// 后端确认状态枚举维持两态(0 正常 / 1 故障),不新增「占用」;
// 手机版按设计稿文案映射 0→可用(绿)、1→维修(红)。
/** 手机版状态文案 */
val statusPhoneName: String
get() = when (status) {
"0" -> "可用"
"1" -> "维修"
"2" -> "占用"
else -> statusName()
}
/** 可用(绿色标签) */
val isStatusAvailable: Boolean get() = status == "0"
/** 占用(橙色标签,后端扩展枚举后的预留) */
val isStatusOccupied: Boolean get() = status == "2"
}

View File

@@ -2091,6 +2091,13 @@ interface Api {
@POST("eqm/uld/deleteUld")
suspend fun deleteUld(@Query("uld") uld: String): BaseResultBean<Any>
/**
* 批量删除-ULD-信息(请求体为 ULD 编号字符串数组)
* 接口路径: /eqm/uld/deleteUldBatch
*/
@POST("eqm/uld/deleteUldBatch")
suspend fun deleteUldBatch(@Body data: RequestBody): BaseResultBean<Any>
///////////////////////////////////////////////////////////////////////////
// 冷库登记
///////////////////////////////////////////////////////////////////////////