Compare commits

...

19 Commits

Author SHA1 Message Date
ea298ec322 feat: 开始计重新增托盘信息并自动计算运抵重量
国际出港开始计重页面新增托盘数量、托盘自重输入框(自重默认18可编辑),
运抵重量自动按“地磅称重 -(托盘数量 × 托盘自重)”计算;
分托计重与完成计重提交均携带 trayNumber、trayTotalWeight;
“托盘车号”“托盘车自重”标签改为“平板车”“平板车自重”。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 13:11:17 +08:00
b57a3cd133 fix: 计重明细托盘自重改取托盘重量并支持编辑联动
托盘自重原误取托盘车重(carWeight),改为托盘重量(trayTotalWeight)。
编辑模式下托盘自重可编辑,重量按编辑前后差值同步浮动,底部统计随之更新。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 11:59:13 +08:00
4e6e654558 feat: 国际出港运抵列表新增运抵状态筛选
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 11:34:46 +08:00
b65e61595b feat: 国际进港舱单编辑支持交接图片新增与删除
编辑/新增表单页增加交接图片区(选图、上传、长按删除),
保存时随舱单提交 pic/originalPic/picNumber 三字段,
编辑模式回填已有图片且不重复上传。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 11:23:43 +08:00
86f77353c0 feat: 国际出港板箱过磅通道号默认沿用上一次选择
称重提交成功后本地记住通道号,后续新增称重自动默认选中;
重置时不再清空通道号,编辑模式仍按记录自带通道号回填。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 10:44:31 +08:00
39b5cc3689 feat: 国际出港板箱过磅挂签支持多选批量打印
多选时逐张串行发送,张间节流留出出纸时间,写入失败即中止
并提示已打印张数与失败的 ULD 号;单选与详情页打印路径不变。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 10:44:25 +08:00
a1b55f656f feat: 航班管理列表新增增删改功能
- 列表页右上角新增加号入口,新增/编辑共用表单页(HbFlightEditActivity)
- 列表项支持侧滑编辑、删除,删除使用 ConfirmDialogModel 二次确认
- 接入 /flt/saveFlight、/flt/updateFlight、/flt/delete 接口
- 编辑回填增加枚举中文名转代码处理;FlightBean 地区类型兼容中文值显示

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 10:13:37 +08:00
13c9c6f5dc feat: 国际出港运抵计重状态改为海关放行
- 主单列表项第二行「计重状态」改为「海关放行」,取运抵分页接口
  返回的 cmdStatus,按普通文本显示,与查询详情页「海关指令」一致
- 展开的分单列表在「申报状态」与「品名(中)」之间新增「海关放行」列
- GjcMaWb 补充 cmdStatus 字段,此前仅 GjcHaWb 有该字段

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 17:27:31 +08:00
de62b903d4 feat: 国际出港板箱过磅新增耗材重量并计入货重
- 过磅表单新增只读的耗材重量,数据随列表项 Bean 带入,扫码新增时
  取 queryUsingUldByUld 返回的 consumeWeight
- 货重公式由「装机重 - ULD自重」改为「装机重 - ULD自重 - 耗材自重」
- queryUsingUldByUld 返回后补触发重量计算,此前该接口异步返回时
  耗材重量不会参与计算
- 表单保持每行三列:第三列自上而下为总重、装机重、耗材重量、货重,
  目的港单独占一行三分之一宽,备注换行整行撑满

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 17:12:21 +08:00
2d42ce9cc3 feat: 国际出港重量回填页改名并新增耗材重量
- 页面标题由「国际出港修改组装重量」改为「国际出港重量回填」
- ULD 信息卡片在货重之后新增耗材重量,取组装分页接口返回的
  consumeWeight 字段,数据随列表页传入的 Bean 一并带过来

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 16:46:01 +08:00
861ce8ca1e feat: 国际出港交接复磅状态改为 IMP 代码
列表项第二行第三列由「复磅状态」改为「IMP 代码」,取交接分页接口
返回的 dgrCode 字段;值非空且不等于 ELI、ELM 时以红色显示,
空值与 ELI、ELM 保持常规颜色。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 16:31:27 +08:00
f312ef866c feat: 国际进港舱单新增开始理货按钮
- 底部「货物发放」改名为「理货完成」,「分拣理货」改名为「装机单」
- 新增蓝色「开始理货」按钮,选中唯一一条运单后弹出修改库位对话框,
  提交 /IntImpManifest/modifyStorage
- 弹窗默认回填该运单已有的理货库位:舱单列表接口不返回 locationTally,
  改为先按 fid + wbNo 查一次装机单记录取出库位再回填
- GjjManifest 补充 locationTallyId 字段
- 装机单列表项运单号、品名列 completeSpace 调整为 5 以对齐

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 16:19:47 +08:00
6f27c15d9a fix: 修复国际进港出库运单号搜索不生效
运单号原本传给了 no 参数,但接口中 no 是不含前缀的主单号
(如 33362265),wbNo 才是完整 11 位运单号,导致条件被忽略。
同模块提取记录、进港查询用的均为 wbNo,此处保持一致。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 15:08:04 +08:00
ded57554fa feat: 国际出港查询新增日期类型与航班航次统计
- 合计栏在总重量后追加航班航次(取统计接口 flightNumber)
- 日期范围前新增日期类型下拉(入库/离港),对应接口 dateState 参数
- 航班号从筛选弹框移至列表上方,出库状态移入筛选弹框
- dateState、outState 按接口文档改为整型传参

原航班号查询无效的原因并非参数名有误(fno 与文档一致),
而是缺少 dateState 导致后端按默认日期口径过滤,补齐后已实测生效。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 14:49:46 +08:00
2f3ae2a814 feat: 国际进港查询统计新增航班航次并调整筛选条件布局
- 底部统计栏在总重量后新增航班航次显示
- 航班号从筛选面板移至页面上方搜索区,出库状态移入筛选面板
- 筛选红点提示与重置逻辑同步调整字段归属

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 17:08:47 +08:00
19d02700b8 feat: 国际出港仓库清仓弹窗增加备注输入框
清仓操作弹窗在「清仓正常」下拉基础上新增选填备注,提交时随 clearRemark 字段传给 updateClear 接口

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 16:41:49 +08:00
aa6cc43bfa feat: 国际事故签证新增支持运单号联想并自动带出舱单信息
- 运单号输入启用自动联想(1条自动填充、多条弹窗选择)
- 运单号满11位自动查询进港舱单,带出航班日期、航班号、始发/目的站、总件数、总重量、品名及交接图片
- 仅新增模式触发带出,避免编辑模式覆盖已保存数据和图片

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 16:31:16 +08:00
dcb335ecdf feat: ULD新增编辑表单增加来源、入库时间、所在港字段
- 编辑表单新增来源(空运/陆运)、入库时间、所在港(默认HFE)三个字段
- 列表项同步显示来源、所在港、位置、入库时间,去掉备注展示并收窄首列
- PadDataLayoutNew 新增 DATETIME 类型,支持年月日时分秒选择

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-27 15:30:23 +08:00
567b76014b fix: 国内进港卸机相关页面标题去掉"国内"二字
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-06 10:35:36 +08:00
62 changed files with 1798 additions and 190 deletions

View File

@@ -19,7 +19,7 @@ class GnjUnloadAddActivity :
override fun viewModelClass() = GnjUnloadAddViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国内进港开始卸机")
setBackArrow("进港开始卸机")
binding.viewModel = viewModel
viewModel.rv = binding.rv

View File

@@ -24,7 +24,7 @@ class GnjUnloadListActivity :
}
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国内进港卸机")
setBackArrow("进港卸机")
binding.viewModel = viewModel
viewModel.pageModel.bindSmartRefreshLayout(
binding.srl,

View File

@@ -30,6 +30,14 @@ class UldEditViewModel : BaseViewModel() {
)
)
// 来源下拉列表
val sourceList = MutableLiveData<List<KeyValue>>(
listOf(
KeyValue("空运", "空运"),
KeyValue("陆运", "陆运"),
)
)
// 所属航司下拉列表数据源POST /typeCode/intCarrier
val carrierList = MutableLiveData<List<KeyValue>>(emptyList())
@@ -40,7 +48,8 @@ class UldEditViewModel : BaseViewModel() {
else if (modify) DetailsPageType.Modify else DetailsPageType.Details
if (id.isEmpty()) {
// 新增:直接加载航司列表
// 新增:所在港默认 HFE直接加载航司列表
uldBean.value = ULDBean(nowPort = "HFE")
loadCarrierList(null)
} else {
getData()
@@ -79,7 +88,7 @@ class UldEditViewModel : BaseViewModel() {
}) {
onSuccess = {
showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功"))
if (isAdd) uldBean.value = ULDBean() else getData()
if (isAdd) uldBean.value = ULDBean(nowPort = "HFE") else getData()
}
}
}

View File

@@ -121,6 +121,49 @@
</LinearLayout>
<!-- Row 3: 来源 / 入库时间 / 所在港 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请选择来源"}'
list="@{viewModel.sourceList}"
title='@{"来源"}'
titleLength="@{7}"
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.uldBean.source}' />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请选择入库时间"}'
title='@{"入库时间"}'
titleLength="@{5}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.uldBean.inStoreTime}' />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
enable="@{viewModel.pageType != DetailsPageType.Details}"
hint='@{"请输入所在港"}'
title='@{"所在港"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.uldBean.nowPort}' />
</LinearLayout>
<!-- 备注(全行,多行输入) -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:layout_width="match_parent"

View File

@@ -55,7 +55,7 @@
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:layout_weight="1.0"
android:gravity="center_vertical">
<TextView
@@ -109,7 +109,7 @@
android:layout_height="wrap_content"
android:text="自重:"
android:textSize="15sp"
completeSpace="@{3}" />
completeSpace="@{4}" />
<TextView
android:layout_width="wrap_content"
@@ -165,7 +165,7 @@
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 第二行:最大载重 | 备注 -->
<!-- 第二行:最大载重 | 来源 | 所在港 | 位置 | 入库时间 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -175,7 +175,7 @@
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:layout_weight="1.0"
android:gravity="center_vertical">
<TextView
@@ -193,24 +193,90 @@
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 备注 -->
<!-- 来源 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3.6"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="备注"
android:text="来源"
android:textSize="15sp"
completeSpace="@{3}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.remark}"
android:text="@{bean.source}"
android:textSize="15sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 所在港 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="所在港:"
android:textSize="15sp"
completeSpace="@{4}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.nowPort}"
android:textSize="15sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 位置 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="位置:"
android:textSize="15sp"
completeSpace="@{5}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.nowPosition}"
android:textSize="15sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 入库时间 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.2"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="入库时间:"
android:textSize="15sp"
completeSpace="@{7}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.inStoreTime}"
android:textSize="15sp" />
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@@ -234,7 +234,10 @@ class FlightBean : ICheck {
return when (countryType) {
"0" -> "国内"
"1" -> "国际"
else -> ""
"2" -> "地区"
"3" -> "混合"
// 部分接口直接返回中文名,原样显示
else -> countryType
}
}

View File

@@ -22,7 +22,9 @@ data class GjcCheckInRecord(
var weight: Double = 0.0, // 运抵重量
var volume: Double = 0.0, // 运抵体积
var whId: Long = 0, // GJC_WAREHOUSE.ID
var carWeight: String = "" // 托盘
var carWeight: String = "", // 托盘
var trayNumber: Int = 0, // 托盘数量
var trayTotalWeight: Double = 0.0 // 托盘重量(托盘自重)
) : BaseObservable() {
// 数据变化回调
@@ -53,4 +55,28 @@ data class GjcCheckInRecord(
onDataChanged?.invoke()
}
}
// 托盘自重编辑过程中的原始输入串(保持回显与输入一致,避免光标跳位)
// 不作为构造参数data class copy 备份/恢复时自动丢弃,回退到 trayTotalWeight 显示
// @Transient仅界面内部状态不参与 Gson 序列化提交
@Transient
private var trayTotalWeightInput: String? = null
// 托盘自重的字符串表示(用于双向绑定)
// 业务规则:重量包含托盘自重,托盘自重编辑后,重量同步增减相同差值
@get:Bindable
var trayTotalWeightStr: String
get() = trayTotalWeightInput
?: if (trayTotalWeight == 0.0) "" else trayTotalWeight.toString()
set(value) {
trayTotalWeightInput = value
val newTrayWeight = value.toDoubleOrNull() ?: 0.0
if (trayTotalWeight != newTrayWeight) {
weight = Math.round((weight + newTrayWeight - trayTotalWeight) * 100) / 100.0
trayTotalWeight = newTrayWeight
notifyPropertyChanged(BR.trayTotalWeightStr)
notifyPropertyChanged(BR.weightStr)
onDataChanged?.invoke()
}
}
}

View File

@@ -57,6 +57,8 @@ data class GjcMaWb(
var arriveWeight: Double? = null, // 运抵重量
var arriveVolume: Double? = null, // 运抵体积
var arriveFlag: String? = null, // 运抵状态0正常运抵1提前运抵
var trayNumber: Int? = null, // 托盘数量
var trayTotalWeight: Double? = null, // 托盘重量(托盘数量 × 托盘自重)
// ==================== 特码与收货人 ====================
var spCode: String? = null, // 特码
@@ -79,6 +81,7 @@ data class GjcMaWb(
// ==================== 状态信息 ====================
var checkIn: String? = null, // 收运状态。0待收运1已收运2收运中
var declareStatus: String? = null, // 申报状态
var cmdStatus: String? = null, // 海关放行(命令状态)
var reviewStatus: String? = null, // 审核状态0未审核1通过2退回
var tranFlag: String? = null, // 转运标志
var clearNormal: String? = null, // 清仓正常01

View File

@@ -12,6 +12,8 @@ data class GjjManifest(
var no: String = "", // 编号
var prefix: String = "", // 前缀
var wbNo: String = "", // 运单号
var fdate: String = "", // 航班日期
var fno: String = "", // 航班号
var origin: String = "", // 货源地(始发站)
var dest: String = "", // 目的站
var fdep: String = "", // 航班起始站
@@ -58,6 +60,7 @@ data class GjjManifest(
var unNumber: String = "", // 危险品编号
var activeId: Long = 0, // 活动ID
var locationTally: String = "", // 理货库位号
var locationTallyId: Long = 0, // 理货库位ID
var pic: String = "", // 交接图片缩略图路径
var originalPic: String = "", // 交接图片原图地址
var picNumber: String = "" // 交接图片数量

View File

@@ -8,5 +8,6 @@ data class ManifestTotalDto(
var wbNumber: Int = 0, // 运单票数/总票数
var totalPc: Long = 0, // 总件数
var totalWeight: Double = 0.0, // 总重量
var cargoWeight: Double = 0.0 // 总货重
var cargoWeight: Double = 0.0, // 总货重
var flightNumber: Int = 0 // 航班航次
)

View File

@@ -19,6 +19,14 @@ data class ULDBean(
var status: String = "",
// 备注
var remark: String = "",
// 来源(空运、陆运)
var source: String = "",
// 入库时间 yyyy-MM-dd HH:mm:ss
var inStoreTime: String = "",
// 所在港
var nowPort: String = "",
// 位置信息
var nowPosition: String = "",
){
val checked = ObservableBoolean(false)

View File

@@ -1391,6 +1391,24 @@ interface Api {
@POST("flt/listFlightDest")
suspend fun listFlightDest(@Body data: RequestBody): BaseResultBean<List<String>>
/**
* 航班管理-新增航班
*/
@POST("flt/saveFlight")
suspend fun saveFlight(@Body data: RequestBody): BaseResultBean<String>
/**
* 航班管理-修改航班
*/
@POST("flt/updateFlight")
suspend fun updateFlight(@Body data: RequestBody): BaseResultBean<String>
/**
* 航班管理-删除航班(请求体为航班对象数组)
*/
@POST("flt/delete")
suspend fun deleteFlightList(@Body data: RequestBody): BaseResultBean<String>
/**
* 新增或修航司屏蔽
*/
@@ -1891,6 +1909,14 @@ interface Api {
@POST("IntImpManifest/pageQueryTotal")
suspend fun getIntImpManifestTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际进港舱单-根据11位具体运单号查询运单信息
* 接口路径: /IntImpManifest/queryWbByNo
* @param wbNo 11位运单号
*/
@POST("IntImpManifest/queryWbByNo")
suspend fun getIntImpManifestByWbNo(@Query("wbNo") wbNo: String): BaseResultBean<GjjManifest>
/**
* 国际进港舱单-获取主单下分单
*/

View File

@@ -211,7 +211,7 @@ class PadDataLayout : FrameLayout {
setOnClickListener(null)
}
DataLayoutType.DATE -> {
DataLayoutType.DATE, DataLayoutType.DATETIME -> {
tv.visibility = VISIBLE
et.visibility = GONE
spinner.visibility = GONE
@@ -258,4 +258,5 @@ enum class DataLayoutType {
INPUT, // 文本
SPINNER, // 下拉选择
DATE, // 日期
DATETIME, // 日期时间yyyy-MM-dd HH:mm:ss
}

View File

@@ -25,6 +25,7 @@ import com.lukouguoji.module_base.adapter.visible
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.formatDateTime
import com.lukouguoji.module_base.ktx.getActivity
import com.lukouguoji.module_base.ktx.loge
import com.lukouguoji.module_base.util.Common
@@ -182,6 +183,18 @@ class PadDataLayoutNew : FrameLayout {
}
}
// 选择日期时间yyyy-MM-dd HH:mm:ss
private val dateTimeClick: (v: View) -> Unit = {
if (enable) {
Common.onYearMonthDayTime(context.getActivity(), value) { year, month, day, hour, minute, second ->
val calendar = Calendar.getInstance()
calendar.set(year, month - 1, day, hour, minute, second)
value = calendar.time.formatDateTime()
refreshCallBack?.invoke()
}
}
}
init {
initView()
}
@@ -252,7 +265,7 @@ class PadDataLayoutNew : FrameLayout {
setOnClickListener(null)
}
DataLayoutType.DATE -> {
DataLayoutType.DATE, DataLayoutType.DATETIME -> {
tv.visibility = VISIBLE
et.visibility = GONE
spinner.visibility = GONE
@@ -263,7 +276,7 @@ class PadDataLayoutNew : FrameLayout {
iv.layoutParams.width = dp20
iv.layoutParams.height = dp20
setOnClickListener(dateClick)
setOnClickListener(if (type == DataLayoutType.DATETIME) dateTimeClick else dateClick)
}
}
}

View File

@@ -27,7 +27,12 @@ import com.lukouguoji.module_base.ktx.permission
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.model.LoadingModel
import dev.DevUtils
import dev.utils.app.HandlerUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import okio.internal.commonToUtf8String
import java.io.IOException
object PrinterUtils {
@@ -315,6 +320,50 @@ object PrinterUtils {
* 打印国际出港板箱过磅挂签2列布局支持合并单元格
*/
fun printGjcBoxWeighing(bean: GjcUldUseBean) {
printMergedGrid(buildGjcBoxWeighingRows(bean))
}
/**
* 批量打印国际出港板箱过磅挂签
* 逐张串行发送,张间节流,任一张发送失败即中止并提示已完成张数
*/
suspend fun printGjcBoxWeighingBatch(beans: List<GjcUldUseBean>) = withContext(Dispatchers.IO) {
var printed = 0
loading.showLoading()
try {
for ((index, bean) in beans.withIndex()) {
// 连接断开(含上一张写入异常后 SDK 置为断开)则不再继续发送
if (portManager?.connectStatus != true) break
HandlerUtils.postRunnable {
loading.setMessage("正在打印 ${index + 1}/${beans.size}")
}
val bytes = buildMergedGridBytes(buildGjcBoxWeighingRows(bean))
val ok = try {
portManager?.writeDataImmediately(bytes) == true
} catch (e: IOException) {
showLog("批量打印第 ${index + 1} 张写入异常 : ${e.message}")
false
}
if (!ok) break
printed++
// 张间留出出纸时间,避免连续灌入导致打印机缓冲溢出
if (index < beans.lastIndex) delay(800)
}
} finally {
loading.dismissLoading()
}
if (printed == beans.size) {
showToast("${beans.size} 张挂签打印完成")
} else {
val failBean = beans[printed]
showToast("已打印 $printed 张,第 ${printed + 1}ULD ${failBean.uld})发送失败,请检查打印机后重试")
}
}
/**
* 组装板箱过磅挂签的表格行数据
*/
private fun buildGjcBoxWeighingRows(bean: GjcUldUseBean): ArrayList<GridRow> {
val rows = arrayListOf<GridRow>()
// 第1行日期 | 航班号
@@ -345,7 +394,7 @@ object PrinterUtils {
merged = CellData("备注:", "NOTES:", bean.remark)
))
printMergedGrid(rows)
return rows
}
private fun printCommonGrid(rowItem: ArrayList<List<String>>) {
@@ -456,6 +505,13 @@ object PrinterUtils {
* 打印支持合并单元格的表格
*/
private fun printMergedGrid(rows: ArrayList<GridRow>) {
portManager?.writeDataImmediately(buildMergedGridBytes(rows))
}
/**
* 生成支持合并单元格的表格标签字节流(自包含一张完整标签的 TSPL 指令)
*/
private fun buildMergedGridBytes(rows: ArrayList<GridRow>): ByteArray {
val gridStartX = 30
val titleTopMargin = 50 // 标题顶部间距
val titleFontSize = 130 // 标题字体大小
@@ -536,7 +592,7 @@ object PrinterUtils {
addPrint(1)
}.bytes
portManager?.writeDataImmediately(bytes)
return bytes
}
/**

View File

@@ -30,7 +30,7 @@ class GjcAssembleWeightEditActivity :
override fun viewModelClass() = GjcAssembleWeightEditViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港修改组装重量")
setBackArrow("国际出港重量回填")
binding.viewModel = viewModel

View File

@@ -54,7 +54,7 @@ class GjcQueryActivity :
}
// 航班号:大写字母+数字
binding.filterPanel.filterFlightNo.et.setUpperCaseAlphanumericFilter()
binding.flightNoSearch.et.setUpperCaseAlphanumericFilter()
// 目的港:仅大写字母
binding.filterPanel.filterDest.et.setUpperCaseLetterFilter()

View File

@@ -79,7 +79,8 @@ class IntExpStorageUseActivity :
IntExpMoveClearDialogModel { dialog ->
// 用户点击保存后,执行清仓操作
val clearNormal = dialog.clearNormal.value ?: ""
viewModel.performClear(clearNormal, maWbListForClear)
val clearRemark = dialog.clearRemark.value ?: ""
viewModel.performClear(clearNormal, clearRemark, maWbListForClear)
}.show(this)
}

View File

@@ -18,6 +18,9 @@ class IntExpMoveClearDialogModel(
// 清仓正常(存储的是 code"0" 或 "1"
val clearNormal = MutableLiveData("")
// 清仓备注(选填)
val clearRemark = MutableLiveData("")
// 清仓正常选项列表
val clearNormalList = MutableLiveData<List<KeyValue>>().apply {
value = listOf(

View File

@@ -38,6 +38,7 @@ class GjcAssembleWeightEditViewModel : BaseViewModel() {
val totalPc = MutableLiveData("") // 总件数
val totalWeight = MutableLiveData("") // 总重量
val cargoWeight = MutableLiveData("") // 货重
val consumeWeight = MutableLiveData("") // 耗材重量
///////////////////////////////////////////////////////////////////////////
// 底部统计字段
@@ -74,6 +75,7 @@ class GjcAssembleWeightEditViewModel : BaseViewModel() {
totalPc.value = it.pc.toString()
totalWeight.value = it.totalWeight.toString()
cargoWeight.value = it.cargoWeight.toString()
consumeWeight.value = it.consumeWeight.toString()
// 保存参数用于API调用
useId = it.useId

View File

@@ -20,6 +20,7 @@ import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import com.lukouguoji.module_base.db.perference.SharedPreferenceUtil
import com.lukouguoji.module_base.model.BluetoothDialogModel
import com.lukouguoji.module_base.model.ScanModel
import com.lukouguoji.module_base.util.Common
@@ -34,6 +35,11 @@ import java.util.Date
*/
class GjcBoxWeighingAddViewModel : BaseViewModel() {
companion object {
// 记录上一次称重完成时使用的通道号,后续称重默认选中
private const val KEY_LAST_PASSAGEWAY_ID = "gjc_box_weighing_last_passageway_id"
}
// 数据Bean
val dataBean = MutableLiveData(GjcUldUseBean())
@@ -51,6 +57,7 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
val totalWeight = MutableLiveData("0") // 总重(红色)
val netWeight = MutableLiveData("0") // 装机重(蓝色)
val cargoWeight = MutableLiveData("0") // 货重(绿色)
val consumeWeight = MutableLiveData("0") // 耗材重量(只读,随 ULD 记录带出)
// 需要自动查询的独立字段(用于监听输入变化)
val carId = MutableLiveData("") // 架子车号
@@ -116,7 +123,9 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
loadPassagewayList(editBean.passagewayId)
loadPlCloseList(editBean.plClose)
} else {
loadPassagewayList(null)
// 新增模式:默认选中上一次称重使用的通道号
val lastPassagewayId = SharedPreferenceUtil.getString(KEY_LAST_PASSAGEWAY_ID)
loadPassagewayList(lastPassagewayId.ifEmpty { null })
loadPlCloseList(null)
}
}
@@ -138,6 +147,7 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
totalWeight.value = bean.totalWeight.toString()
netWeight.value = bean.netWeight.toString()
cargoWeight.value = bean.cargoWeight.toString()
consumeWeight.value = bean.consumeWeight.toString()
diBangWeight.value = bean.totalWeight.toString()
// 通道号 Spinner 绑定到 channelKeyValue.value 即 passagewayId
@@ -273,15 +283,16 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
/**
* 计算重量字段
* 装机重 = 总重 - 架子车自重
* 货重 = 装机重 - ULD自重
* 货重 = 装机重 - ULD自重 - 耗材自重
*/
private fun calculateWeights() {
val total = totalWeight.value?.toDoubleOrNull() ?: 0.0
val carW = dataBean.value?.carWeight ?: 0.0
val uldW = dataBean.value?.uldWeight ?: 0.0
val consumeW = consumeWeight.value?.toDoubleOrNull() ?: 0.0
val net = total - carW
val cargo = net - uldW
val cargo = net - uldW - consumeW
netWeight.value = if (net > 0) net.toString() else "0"
cargoWeight.value = if (cargo > 0) cargo.toString() else "0"
@@ -334,7 +345,8 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
totalWeight.value = "0"
netWeight.value = "0"
cargoWeight.value = "0"
channel.value = ""
consumeWeight.value = "0"
// 通道号不清空:保留当前选择,与 Spinner 显示保持一致,后续称重沿用上一次的通道号
printTag.value = false
// 重置独立字段
@@ -437,6 +449,11 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
if (result.verifySuccess()) {
showToast("完成复磅")
// 记住本次使用的通道号,后续称重默认选中
if (bean.passagewayId.isNotEmpty()) {
SharedPreferenceUtil.addString(KEY_LAST_PASSAGEWAY_ID, bean.passagewayId)
}
// 如果勾选了打印挂签,则执行打印
if (printTag.value == true) {
executePrint()
@@ -519,6 +536,9 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
if (usingData != null && usingData.useId > 0) {
// 保存返回的数据,用于提交时传递
usingUldData = usingData
// 耗材重量随该记录带回,需要重新参与货重计算
consumeWeight.value = usingData.consumeWeight.toString()
calculateWeights()
} else {
// 没有 useId说明未组装不可复磅
usingUldData = null

View File

@@ -3,6 +3,7 @@ package com.lukouguoji.gjc.viewModel
import android.app.Activity
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.holder.GjcBoxWeighingViewHolder
import com.lukouguoji.module_base.base.BasePageViewModel
@@ -20,6 +21,7 @@ import com.lukouguoji.module_base.util.PrinterUtils
import dev.utils.app.info.KeyValue
import dev.utils.common.DateUtils
import com.lukouguoji.module_base.ktx.formatDate
import kotlinx.coroutines.launch
/**
* 国际出港板箱过磅 ViewModel
@@ -96,16 +98,19 @@ class GjcBoxWeighingViewModel : BasePageViewModel() {
return
}
// 校验多选
if (selectedItems.size > 1) {
showToast("只能选择一条记录进行打印")
return
}
// 执行打印
val bean = selectedItems.first()
BluetoothDialogModel().showCallBack {
PrinterUtils.printGjcBoxWeighing(bean)
if (selectedItems.size == 1) {
// 单条:保持原有打印路径
val bean = selectedItems.first()
BluetoothDialogModel().showCallBack {
PrinterUtils.printGjcBoxWeighing(bean)
}
} else {
// 多条:逐张串行批量打印
BluetoothDialogModel().showCallBack {
viewModelScope.launch {
PrinterUtils.printGjcBoxWeighingBatch(selectedItems)
}
}
}
}

View File

@@ -25,13 +25,22 @@ import com.lukouguoji.module_base.ktx.formatDate
class GjcQueryViewModel : BasePageViewModel() {
// ==================== 搜索条件 ====================
val dateState = MutableLiveData("0") // 日期类型0入库1离港
val flightDateStart = MutableLiveData<String>(DateUtils.getCurrentTime().formatDate()) // 航班日期起
val flightDateEnd = MutableLiveData("") // 航班日期止
val agentId = MutableLiveData("") // 代理ID
val outStatus = MutableLiveData("") // 出库状态
val flightNo = MutableLiveData("") // 航班号
val waybillNo = MutableLiveData("") // 运单号
// ==================== 下拉列表 ====================
// 日期类型下拉列表(对应接口 dateState
val dateStateList = MutableLiveData(
listOf(
KeyValue("入库", "0"),
KeyValue("离港", "1")
)
)
// 代理下拉列表从API获取
val agentList = MutableLiveData(listOf(KeyValue("全部", "")))
@@ -49,9 +58,10 @@ class GjcQueryViewModel : BasePageViewModel() {
val itemLayoutId = R.layout.item_gjc_query
// ==================== 统计数据 ====================
val totalCount = MutableLiveData("0") // 合计票数
val totalPc = MutableLiveData("0") // 总件数
val totalWeight = MutableLiveData("0") // 总重量
val totalCount = MutableLiveData("0") // 合计票数
val totalPc = MutableLiveData("0") // 总件数
val totalWeight = MutableLiveData("0") // 总重量
val totalFlightNumber = MutableLiveData("0") // 航班航次
// ==================== 筛选面板 ====================
val filterVisible = MutableLiveData(false)
@@ -70,7 +80,7 @@ class GjcQueryViewModel : BasePageViewModel() {
// ==================== 筛选条件 ====================
val spCode = MutableLiveData("") // 特码
val flightNo = MutableLiveData("") // 航班号
val outStatus = MutableLiveData("") // 出库状态
val dest = MutableLiveData("") // 目的港
val awbType = MutableLiveData("") // 运单类型
val businessType = MutableLiveData("") // 业务类型
@@ -79,11 +89,11 @@ class GjcQueryViewModel : BasePageViewModel() {
// 是否有筛选条件(任意一个非空则为 true用于筛选按钮红点
val hasFilter: MediatorLiveData<Boolean> = MediatorLiveData<Boolean>().apply {
val update = { _: Any? ->
value = listOf(spCode, flightNo, dest, awbType, businessType, goodsCn)
value = listOf(spCode, outStatus, dest, awbType, businessType, goodsCn)
.any { !it.value.isNullOrEmpty() }
}
addSource(spCode, update)
addSource(flightNo, update)
addSource(outStatus, update)
addSource(dest, update)
addSource(awbType, update)
addSource(businessType, update)
@@ -127,7 +137,7 @@ class GjcQueryViewModel : BasePageViewModel() {
*/
fun resetFilter() {
spCode.value = ""
flightNo.value = ""
outStatus.value = ""
dest.value = ""
awbType.value = ""
businessType.value = ""
@@ -160,39 +170,32 @@ class GjcQueryViewModel : BasePageViewModel() {
* 核心方法调用API获取数据并转换PageInfo为BaseListBean
*/
override fun getData() {
// 公共查询条件(列表与统计接口共用)
val commonParams = mapOf(
// 日期类型0入库1离港+ 日期范围
"dateState" to dateState.value?.ifEmpty { null }?.toIntOrNull(),
"beginDate" to flightDateStart.value?.ifEmpty { null },
"endDate" to flightDateEnd.value?.ifEmpty { null },
"agentCode" to agentId.value?.ifEmpty { null },
"fno" to flightNo.value?.ifEmpty { null },
"wbNo" to waybillNo.value?.ifEmpty { null },
// 筛选面板条件
"spCode" to spCode.value?.ifEmpty { null },
"outState" to outStatus.value?.ifEmpty { null }?.toIntOrNull(),
"dest" to dest.value?.ifEmpty { null },
"awbType" to awbType.value?.ifEmpty { null },
"businessType" to businessType.value?.ifEmpty { null },
"goodsCn" to goodsCn.value?.ifEmpty { null }
)
// 构建查询参数(列表接口)
val listParams = mapOf(
val listParams = (commonParams + mapOf(
"pageNum" to pageModel.page,
"pageSize" to pageModel.limit,
"beginDate" to flightDateStart.value!!.ifEmpty { null },
"endDate" to flightDateEnd.value!!.ifEmpty { null },
"agentCode" to agentId.value!!.ifEmpty { null },
"outState" to outStatus.value!!.ifEmpty { null },
"wbNo" to waybillNo.value!!.ifEmpty { null },
// 筛选条件(暂未使用)
"spCode" to spCode.value!!.ifEmpty { null },
"fno" to flightNo.value!!.ifEmpty { null },
"dest" to dest.value!!.ifEmpty { null },
"awbType" to awbType.value!!.ifEmpty { null },
"businessType" to businessType.value!!.ifEmpty { null },
"goodsCn" to goodsCn.value!!.ifEmpty { null }
).toRequestBody()
"pageSize" to pageModel.limit
)).toRequestBody()
// 构建查询参数(统计接口 - 使用相同的搜索条件)
val totalParams = mapOf(
"beginDate" to flightDateStart.value!!.ifEmpty { null },
"endDate" to flightDateEnd.value!!.ifEmpty { null },
"agentCode" to agentId.value!!.ifEmpty { null },
"outState" to outStatus.value!!.ifEmpty { null },
"wbNo" to waybillNo.value!!.ifEmpty { null },
// 筛选条件
"spCode" to spCode.value!!.ifEmpty { null },
"fno" to flightNo.value!!.ifEmpty { null },
"dest" to dest.value!!.ifEmpty { null },
"awbType" to awbType.value!!.ifEmpty { null },
"businessType" to businessType.value!!.ifEmpty { null },
"goodsCn" to goodsCn.value!!.ifEmpty { null }
).toRequestBody()
val totalParams = commonParams.toRequestBody()
// 获取列表数据显示loading
launchLoadingCollect({
@@ -213,6 +216,7 @@ class GjcQueryViewModel : BasePageViewModel() {
totalCount.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString()
totalFlightNumber.value = (data?.flightNumber ?: 0).toString()
}
}
}

View File

@@ -66,6 +66,10 @@ class GjcWeighingStartViewModel : BaseViewModel() {
val pageRemark = MutableLiveData("") // 备注
// 托盘信息(运抵重量 = 地磅称重 - 托盘数量 × 托盘自重)
val trayNumber = MutableLiveData("") // 托盘数量
val trayWeight = MutableLiveData("18") // 托盘自重默认18可编辑
// 下拉选择数据源
val channelList = MutableLiveData<List<KeyValue>>() // 通道号列表
val agentList = MutableLiveData<List<KeyValue>>() // 代理人列表
@@ -82,11 +86,10 @@ class GjcWeighingStartViewModel : BaseViewModel() {
// 获取传入的运单ID
maWbId = intent.getLongExtra(Constant.Key.MAWB_ID, 0)
// 监听地磅称重输入,单向同步到运抵重量
diBangWeight.observe(activity as LifecycleOwner) { weight ->
// 同步到运抵重量(单向,不做反向同步)
arriveWeight.value = weight ?: "0"
}
// 监听地磅称重、托盘数量、托盘自重输入,自动计算运抵重量
diBangWeight.observe(activity as LifecycleOwner) { recalculateArriveWeight() }
trayNumber.observe(activity as LifecycleOwner) { recalculateArriveWeight() }
trayWeight.observe(activity as LifecycleOwner) { recalculateArriveWeight() }
// 监听运抵重量变化,自动计算运抵体积
arriveWeight.observe(activity as LifecycleOwner) { weightStr ->
@@ -113,6 +116,32 @@ class GjcWeighingStartViewModel : BaseViewModel() {
}
}
/**
* 自动计算运抵重量:地磅称重 - (托盘数量 × 托盘自重)
*/
private fun recalculateArriveWeight() {
val gross = diBangWeight.value?.toDoubleOrNull() ?: 0.0
val trayTotal = getTrayTotalWeight()
val net = gross - trayTotal
arriveWeight.value = if (net > 0) formatWeight(net) else "0"
}
/**
* 托盘总重量 = 托盘数量 × 托盘自重
*/
private fun getTrayTotalWeight(): Double {
val num = trayNumber.value?.toIntOrNull() ?: 0
val weight = trayWeight.value?.toDoubleOrNull() ?: 0.0
return num * weight
}
/**
* 重量格式化最多保留两位小数去掉多余的0
*/
private fun formatWeight(value: Double): String {
return String.format("%.2f", value).trimEnd('0').trimEnd('.')
}
/**
* 加载通道号列表 (国际出港专用)
*/
@@ -391,6 +420,10 @@ class GjcWeighingStartViewModel : BaseViewModel() {
passageWay = this@GjcWeighingStartViewModel.channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: ""
passageWayId = this@GjcWeighingStartViewModel.channel.value
// 托盘信息
trayNumber = this@GjcWeighingStartViewModel.trayNumber.value?.toIntOrNull()
trayTotalWeight = getTrayTotalWeight()
remark = pageRemark.value
}
@@ -417,6 +450,8 @@ class GjcWeighingStartViewModel : BaseViewModel() {
arrivePc.value = ""
arriveWeight.value = ""
arriveVolume.value = ""
trayNumber.value = ""
trayWeight.value = "18"
pageRemark.value = ""
@@ -499,6 +534,8 @@ class GjcWeighingStartViewModel : BaseViewModel() {
"goodsCn" to bean.goodsCn,
"businessType" to bean.businessType,
"carId" to bean.carId,
"trayNumber" to trayNumber.value?.toIntOrNull(),
"trayTotalWeight" to getTrayTotalWeight(),
"remark" to pageRemark.value,
"checkIn" to "1", // 收运状态设置为已收运
"passageWayId" to channel.value, // 通道号参数

View File

@@ -20,6 +20,7 @@ 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 dev.utils.app.info.KeyValue
import dev.utils.common.DateUtils
import com.lukouguoji.module_base.ktx.formatDate
import kotlinx.coroutines.launch
@@ -34,6 +35,16 @@ class IntExpArriveViewModel : BasePageViewModel() {
val flightNo = MutableLiveData("") // 航班号
val waybillNo = MutableLiveData("") // 运单号
val hno = MutableLiveData("") // 分单号
val arrivalStatus = MutableLiveData("") // 运抵状态0未运抵1已运抵
// 运抵状态下拉列表
val arrivalStatusList = MutableLiveData(
listOf(
KeyValue("全部", ""),
KeyValue("已运抵", "1"),
KeyValue("未运抵", "0")
)
)
// ========== 统计信息 ==========
val totalCount = MutableLiveData("0") // 合计票数
@@ -301,7 +312,8 @@ class IntExpArriveViewModel : BasePageViewModel() {
"fdate" to flightDate.value?.ifEmpty { null },
"fno" to flightNo.value?.ifEmpty { null },
"wbNo" to waybillNo.value?.ifEmpty { null },
"hno" to hno.value?.ifEmpty { null }
"hno" to hno.value?.ifEmpty { null },
"arrivalStatus" to arrivalStatus.value?.ifEmpty { null }
)
// 列表参数 (含分页)

View File

@@ -140,9 +140,10 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
/**
* 执行清仓操作
* @param clearNormal 清仓正常("0"或"1"
* @param clearRemark 清仓备注(选填)
* @param maWbListForClear 包含选中子列表项的主列表数据
*/
fun performClear(clearNormal: String, maWbListForClear: List<GjcMaWb>) {
fun performClear(clearNormal: String, clearRemark: String, maWbListForClear: List<GjcMaWb>) {
if (maWbListForClear.isEmpty()) {
showToast("请至少选择一个库位")
return
@@ -151,6 +152,7 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
// 构建请求参数:完整的主子列表结构
val params = mapOf(
"clearNormal" to clearNormal,
"clearRemark" to clearRemark.ifEmpty { null },
"maWbList" to maWbListForClear
).toRequestBody()

View File

@@ -139,6 +139,29 @@
android:textSize="16sp" />
</LinearLayout>
<!-- 耗材重量 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="耗材重量:"
android:textColor="@color/text_gray"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.consumeWeight}"
android:textColor="@color/text_normal"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -259,7 +259,7 @@
</LinearLayout>
<!-- 第5行架子车自重、ULD自重、货重(绿色只读) -->
<!-- 第5行架子车自重、ULD自重、耗材重量(只读) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -289,10 +289,10 @@
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
enable="@{false}"
title='@{"货重"}'
title='@{"耗材重量"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.cargoWeight}'
value='@{viewModel.consumeWeight}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
@@ -300,7 +300,7 @@
</LinearLayout>
<!-- 第6行航班日期、航班号、目的港 -->
<!-- 第6行航班日期、航班号、货重(绿色只读) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -331,11 +331,11 @@
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入目的港"}'
title='@{"目的港"}'
enable="@{false}"
title='@{"货重"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.dataBean.fdest}'
value='@{viewModel.cargoWeight}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
@@ -343,7 +343,32 @@
</LinearLayout>
<!-- 第7行备注 -->
<!-- 第7行目的港(占 1/3 宽度) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入目的港"}'
title='@{"目的港"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.dataBean.fdest}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<!-- 占位:保持目的港与上方各行第一列同宽 -->
<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="2" />
</LinearLayout>
<!-- 第8行备注整行 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -42,6 +42,16 @@
android:gravity="center_vertical"
android:orientation="horizontal">
<!-- 日期类型(入库/离港) -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
hint='@{"请选择日期类型"}'
list="@{viewModel.dateStateList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.dateState}" />
<!-- 航班日期起 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:layout_width="0dp"
@@ -72,15 +82,15 @@
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.agentId}" />
<!-- 出库状态 -->
<!-- 航班号 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:id="@+id/flight_no_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择出库状态"}'
list="@{viewModel.outStatusList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.outStatus}" />
hint='@{"请输入航班号"}'
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.flightNo}" />
<!-- 运单号 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
@@ -204,6 +214,16 @@
android:textStyle="bold"
tools:text="总重量100" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text='@{`航班航次:` + viewModel.totalFlightNumber}'
android:textColor="@color/bottom_tool_tips_text_color"
android:textSize="16sp"
android:textStyle="bold"
tools:text="航班航次2" />
</LinearLayout>
</LinearLayout>

View File

@@ -313,7 +313,45 @@
</LinearLayout>
<!-- 第6行运抵件数、运抵重量、运抵体积 -->
<!-- 第6行托盘数量、托盘自重(用于自动计算运抵重量) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入托盘数量"}'
inputType="@{android.text.InputType.TYPE_CLASS_NUMBER}"
title='@{"托盘数量"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.trayNumber}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入托盘自重"}'
inputType="@{android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL}"
title='@{"托盘自重"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.trayWeight}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第7行运抵件数、运抵重量、运抵体积 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -356,7 +394,7 @@
</LinearLayout>
<!--7行:托盘车号、托盘车自重、业务类型 -->
<!--8行:平板车、平板车自重、业务类型 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -364,9 +402,9 @@
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入托盘车号"}'
hint='@{"请输入平板车号"}'
setRefreshCallBack="@{viewModel::onCarIdInputComplete}"
title='@{"托盘车号"}'
title='@{"平 板 车"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.maWbBean.carId}'
@@ -376,7 +414,7 @@
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
enable="@{false}"
title='@{"托盘车自重"}'
title='@{"平板车自重"}'
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.carWeight}'
@@ -399,7 +437,7 @@
</LinearLayout>
<!--8行:备注 -->
<!--9行:备注 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入备注"}'
inputHeight="@{80}"

View File

@@ -79,6 +79,16 @@
android:layout_height="wrap_content"
android:layout_weight="1" />
<!-- 运抵状态 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
hint='@{"请选择运抵状态"}'
list="@{viewModel.arrivalStatusList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.arrivalStatus}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<!-- 搜索按钮 -->
<LinearLayout
android:layout_width="0dp"

View File

@@ -61,6 +61,32 @@
</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:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请输入备注"}'
type="@{SearchLayoutType.INPUT}"
value="@={model.clearRemark}" />
</LinearLayout>
</LinearLayout>
<!-- 底部按钮 -->

View File

@@ -67,17 +67,19 @@
type="@{DataLayoutType.INPUT}"
value="@={record.weightStr}" />
<!-- 托盘自重字段(只读 -->
<!-- 托盘自重字段(取托盘重量 trayTotalWeight可编辑 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
enable="@{false}"
enable="@{isEditMode}"
hint="@{`托盘自重`}"
inputType="@{android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL}"
title="@{`托盘自重`}"
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value="@{record.carWeight}" />
value="@={record.trayTotalWeightStr}" />
<!-- 计重时间字段(只读) -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew

View File

@@ -198,7 +198,7 @@
</LinearLayout>
<!-- 第二行:航程、计重状态、运抵状态、运单类型、分单数 -->
<!-- 第二行:航程、海关放行、运抵状态、运单类型、分单数 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -231,7 +231,7 @@
</LinearLayout>
<!-- 计重状态 -->
<!-- 海关放行 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -243,20 +243,16 @@
completeSpace="@{5}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="计重状态"
android:text="海关放行"
android:textColor="@color/text_normal"
android:textSize="15sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@{`1`.equals(bean.checkIn) ? @drawable/tag_status_green : (`2`.equals(bean.checkIn) ? @drawable/tag_status_blue : @drawable/tag_status_gray)}"
android:paddingHorizontal="8dp"
android:paddingVertical="2dp"
android:text="@{bean.checkInText}"
android:textColor="@{`1`.equals(bean.checkIn) ? @color/text_green : (`2`.equals(bean.checkIn) ? @color/colorPrimary : @color/text_gray)}"
android:textSize="14sp"
android:visibility="@{bean.checkIn != null &amp;&amp; !bean.checkIn.isEmpty() ? View.VISIBLE : View.GONE}" />
android:text="@{bean.cmdStatus}"
android:textColor="@color/text_normal"
android:textSize="15sp" />
</LinearLayout>
@@ -439,6 +435,16 @@
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.9"
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"

View File

@@ -90,6 +90,16 @@
</LinearLayout>
<!-- 海关放行 -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.9"
android:gravity="center"
android:text="@{bean.cmdStatus ?? ``}"
android:textColor="@color/text_normal"
android:textSize="14sp" />
<!-- 品名(中) -->
<TextView
android:layout_width="0dp"

View File

@@ -167,7 +167,7 @@
</LinearLayout>
<!-- 第二行:目的港、库位号、复磅状态、交接人、交接时间 -->
<!-- 第二行:目的港、库位号、IMP代码、交接人、交接时间 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -221,7 +221,7 @@
</LinearLayout>
<!-- 复磅状态 -->
<!-- IMP代码非空且不是 ELI/ELM 时标红) -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -233,14 +233,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{5}"
android:text="复磅状态"
android:text="IMP代码"
android:textSize="15sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{bean.wtDate != null &amp;&amp; !bean.wtDate.isEmpty() ? `已复磅` : `未复磅`}"
android:textColor="@{bean.wtDate != null &amp;&amp; !bean.wtDate.isEmpty() ? @color/text_green : @color/text_normal}"
android:text="@{bean.dgrCode}"
android:textColor="@{bean.dgrCode != null &amp;&amp; !bean.dgrCode.isEmpty() &amp;&amp; !bean.dgrCode.equals(`ELI`) &amp;&amp; !bean.dgrCode.equals(`ELM`) ? @color/text_red : @color/text_normal}"
android:textSize="15sp" />
</LinearLayout>

View File

@@ -67,17 +67,17 @@
type="@{DataLayoutType.INPUT}"
value='@={viewModel.spCode}' />
<!-- 航班号 -->
<!-- 出库状态 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:id="@+id/filter_flight_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
hint='@{"请输入航班号"}'
title='@{"航班号"}'
hint='@{"请选择出库状态"}'
list="@{viewModel.outStatusList}"
title='@{"出库状态"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.flightNo}' />
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.outStatus}' />
<!-- 目的港 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew

View File

@@ -10,6 +10,7 @@ import com.lukouguoji.gjj.viewModel.GjjManifestAddViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.DetailsPageType
import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
@@ -31,6 +32,10 @@ class GjjManifestAddActivity :
binding.actualWeightInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
binding.billingWeightInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
// 交接图片列表
viewModel.rv = binding.rvPic
binding.rvPic.addOnItemClickListener(viewModel)
viewModel.initOnCreated(intent)
// 动态设置标题(必须在 initOnCreated 之后pageType 已从 Intent 解析)

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.InputType
import androidx.core.widget.doOnTextChanged
import com.lukouguoji.gjj.R
import com.lukouguoji.gjj.databinding.ActivityIntImpAccidentVisaEditBinding
import com.lukouguoji.gjj.viewModel.IntImpAccidentVisaEditViewModel
@@ -29,6 +30,10 @@ class IntImpAccidentVisaEditActivity :
binding.fnoInput.et.setUpperCaseAlphanumericFilter()
// 运单号纯数字11位
binding.wbNoInput.et.inputType = InputType.TYPE_CLASS_NUMBER
// 运单号满11位后自动查询舱单信息带出表单数据覆盖手输、联想填充、弹窗选择
binding.wbNoInput.et.doOnTextChanged { text, _, _, _ ->
viewModel.onWbNoChanged(text?.toString()?.trim() ?: "")
}
viewModel.rv = binding.rv
binding.rv.addOnItemClickListener(viewModel)

View File

@@ -55,8 +55,8 @@ class IntImpQueryActivity :
if (visible) showFilterPanel() else hideFilterPanel()
}
// 航班号:大写字母+数字
binding.filterPanel.filterFlightNo.et.setUpperCaseAlphanumericFilter()
// 航班号(顶部搜索区):大写字母+数字
binding.flightNoSearch.et.setUpperCaseAlphanumericFilter()
// 始发港:仅大写字母
binding.filterPanel.filterOrigin.et.setUpperCaseLetterFilter()

View File

@@ -5,26 +5,45 @@ import android.view.View
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.recyclerview.widget.RecyclerView
import com.lukouguoji.gjj.R
import com.lukouguoji.module_base.base.BaseViewModel
import com.lukouguoji.module_base.bean.FileBean
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.common.DetailsPageType
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.ImageSelectNewViewHolder
import com.lukouguoji.module_base.interfaces.IOnItemClickListener
import com.lukouguoji.module_base.ktx.commonAdapter
import com.lukouguoji.module_base.ktx.finish
import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.loadMore
import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import com.lukouguoji.module_base.util.DictUtils
import com.lukouguoji.module_base.util.MediaUtil
import com.lukouguoji.module_base.util.UploadUtil
import dev.utils.app.info.KeyValue
import dev.utils.common.DateUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
class GjjManifestAddViewModel : BaseViewModel() {
class GjjManifestAddViewModel : BaseViewModel(), IOnItemClickListener {
// 页面类型必须用LiveData
val pageType = MutableLiveData(DetailsPageType.Add)
@@ -167,6 +186,11 @@ class GjjManifestAddViewModel : BaseViewModel() {
val waybillTypeList = MutableLiveData<List<KeyValue>>()
val waybillType = MutableLiveData("")
// ========== 交接图片 ==========
val itemLayoutId = R.layout.item_image_select_new
val itemViewHolder = ImageSelectNewViewHolder::class.java
var rv: RecyclerView? = null
/**
* 初始化从Intent获取参数
*/
@@ -205,6 +229,9 @@ class GjjManifestAddViewModel : BaseViewModel() {
}
}
// 交接图片:末尾追加空白占位项作为添加按钮
rv?.post { rv?.commonAdapter()?.addItem(FileBean()) }
// 加载下拉列表(在编辑数据加载之后,以便使用 checkedValue 将选中项置顶)
loadDictLists()
}
@@ -302,6 +329,21 @@ class GjjManifestAddViewModel : BaseViewModel() {
businessType.value = manifest.businessType
goodsType.value = manifest.cargoType
waybillType.value = manifest.awbType
// 回填交接图片path 取原图 URL 确保预览清晰url 取缩略图用于提交
val picList = manifest.pic.noNull().split(",").filter { it.isNotEmpty() }
val originalList = manifest.originalPic.noNull().split(",").filter { it.isNotEmpty() }
if (picList.isNotEmpty()) {
val images = picList.mapIndexed { index, picUrl ->
val originalUrl = originalList.getOrElse(index) { picUrl }
FileBean(
path = MediaUtil.fillUrl(originalUrl),
url = picUrl,
originalPic = originalUrl
)
}
rv?.loadMore(images)
}
}
/**
@@ -411,30 +453,82 @@ class GjjManifestAddViewModel : BaseViewModel() {
paramsMap["prefix"] = prefix
}
val params = paramsMap.toRequestBody()
launchLoadingCollect({
if (isModify) {
NetApply.api.gjjManifestUpdate(params)
} else {
NetApply.api.gjjManifestInsert(params)
// 先上传本地新增的交接图片,全部完成后再组装三字段提交表单
var uploadFailed = false
(rv?.commonAdapter()?.items ?: emptyList())
.asFlow()
.map { it as FileBean }
.filter { it.path.isNotEmpty() && it.url.isEmpty() }
.onEach {
val data = UploadUtil.upload(it.path).data
// UploadUtil 返回newName=原图(较大)zipFileName=缩略图(较小)
// FileBean.url 用作缩略图标识FileBean.originalPic 用作原图标识
it.url = data?.zipFileName ?: ""
it.originalPic = data?.newName ?: ""
}
}) {
onSuccess = {
if (it.verifySuccess()) {
val successMsg = if (isModify) "修改成功" else "保存成功"
showToast(successMsg)
.flowOn(Dispatchers.IO)
.onStart { showLoading() }
.catch {
uploadFailed = true
showToast(it.message.noNull("上传图片失败"))
dismissLoading()
}
.onCompletion {
if (uploadFailed) return@onCompletion
// 发送刷新事件
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
// 交接图片三字段pic=缩略图、originalPic=原图、picNumber=数量
val picBeans = (rv?.commonAdapter()?.items ?: emptyList())
.filterIsInstance<FileBean>()
.filter { it.path.isNotEmpty() }
paramsMap["picNumber"] = picBeans.size.toString()
paramsMap["pic"] = picBeans.joinToString(",") { MediaUtil.removeUrl(it.url) }
paramsMap["originalPic"] =
picBeans.joinToString(",") { MediaUtil.removeUrl(it.originalPic) }
val params = paramsMap.toRequestBody()
launchLoadingCollect({
if (isModify) {
NetApply.api.gjjManifestUpdate(params)
} else {
NetApply.api.gjjManifestInsert(params)
}
}) {
onSuccess = {
if (it.verifySuccess()) {
val successMsg = if (isModify) "修改成功" else "保存成功"
showToast(successMsg)
view.context.finish()
} else {
showToast(it.msg.noNull("保存失败"))
// 发送刷新事件
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}
view.context.finish()
} else {
showToast(it.msg.noNull("保存失败"))
}
}
}
}
.launchIn(viewModelScope)
}
/**
* 交接图片列表点击回调
* 长按图片R.id.rl切换删除按钮显隐点击删除按钮R.id.iv_delete移除该图片
*/
override fun onItemClick(position: Int, type: Int) {
val adapter = rv?.commonAdapter() ?: return
val bean = adapter.getItem(position) as? FileBean ?: return
when (type) {
R.id.rl -> {
bean.canDelete.set(!bean.canDelete.get())
}
R.id.iv_delete -> {
adapter.removeItem(position)
}
else -> {}
}
}

View File

@@ -233,6 +233,65 @@ class IntImpAccidentVisaEditViewModel : BaseViewModel(), IOnItemClickListener {
}
}
///////////////////////////////////////////////////////////////////////////
// 运单号带出国际进港舱单信息
///////////////////////////////////////////////////////////////////////////
private var lastQueriedWbNo = ""
/**
* 运单号文本变化回调Activity 中监听输入框)
* 满11位且与上次查询不同时查询进港舱单信息并回填表单
* 仅新增模式生效:编辑模式下 loadDetail 回显运单号会触发本回调,
* 若不拦截会用舱单数据覆盖已保存的表单和图片列表
*/
fun onWbNoChanged(wbNo: String) {
if (pageType.value != DetailsPageType.Add) return
if (wbNo.length != 11) return
if (wbNo == lastQueriedWbNo) return
lastQueriedWbNo = wbNo
queryWbInfo(wbNo)
}
private fun queryWbInfo(wbNo: String) {
launchLoadingCollect({ NetApply.api.getIntImpManifestByWbNo(wbNo) }) {
onSuccess = {
val data = it.data
if (it.verifySuccess() && data != null) {
// 接口可能返回 null 字段Gson 反射注入会绕过 Kotlin 非空默认值),统一 noNull 防护
val b = dataBean.value ?: GjAccidentVisaEditBean()
b.fdate = data.fdate.noNull().take(10)
b.fno = data.fno.noNull()
b.dep = data.origin.noNull()
b.dest = data.dest.noNull()
b.pc = (if (data.totalPc > 0) data.totalPc else data.pc).toString()
b.weight = (if (data.totalWeight > 0.0) data.totalWeight else data.weight).toString()
b.goods = data.goods.noNull().ifEmpty { data.goodsCn.noNull() }
dataBean.value = b
// 与航班级联查询判重键同步,避免航班输入框失焦后重复查询覆盖带出的始发/目的站
lastQueriedFlight = "${b.fdate}-${b.fno}"
// 回填交接图片path 取原图完整 URL 用于预览url 取缩略图相对路径用于提交
val picList = data.pic.noNull().split(",").filter { p -> p.isNotEmpty() }
val originalList = data.originalPic.noNull().split(",").filter { p -> p.isNotEmpty() }
val images = picList.mapIndexed { index, picUrl ->
val originalUrl = originalList.getOrElse(index) { picUrl }
FileBean(
path = MediaUtil.fillUrl(originalUrl),
url = picUrl,
originalPic = originalUrl
)
}
// 重置图片区:舱单图片 + 末尾空白添加占位
rv?.commonAdapter()?.refresh(images + FileBean())
} else {
showToast(it.msg.noNull("未查询到该运单的进港舱单信息"))
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// 保存
///////////////////////////////////////////////////////////////////////////

View File

@@ -481,6 +481,77 @@ class IntImpManifestViewModel : BasePageViewModel() {
.navigation()
}
/**
* 开始理货(弹出修改库位对话框,提交理货库位)
*/
fun startTallyClick() {
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjManifest> ?: return
val selectedItems = list.filter { it.isSelected }
if (selectedItems.isEmpty()) {
showToast("请选择要理货的运单")
return
}
if (selectedItems.size > 1) {
showToast("只能选择一个运单")
return
}
// 舱单列表接口不返回理货库位,需先查装机单记录取出该运单已有的库位再弹窗回填
val selectedItem = selectedItems.first()
val queryParams = mapOf(
"fid" to fid.ifEmpty { null },
"fdep" to fdep.ifEmpty { null },
"wbNo" to selectedItem.wbNo.ifEmpty { null },
"pageNum" to 1,
"pageSize" to 1
).toRequestBody()
launchLoadingCollect({ NetApply.api.getIntImpLoadingList(queryParams) }) {
onSuccess = { result ->
val tallyRecord = result.list?.firstOrNull()
showModifyStorageDialog(
selectedItems = selectedItems,
currentLocationId = tallyRecord?.locationTallyId?.takeIf { it > 0 }?.toString() ?: "",
currentLocationName = tallyRecord?.locationTally ?: ""
)
}
// 查询失败不阻断操作,直接弹出空白库位的对话框
onFailed = { _, _ -> showModifyStorageDialog(selectedItems, "", "") }
}
}
/**
* 弹出修改库位对话框并提交currentLocation* 用于回填该运单已有的理货库位)
*/
private fun showModifyStorageDialog(
selectedItems: List<GjjManifest>,
currentLocationId: String,
currentLocationName: String
) {
IntImpModifyStorageDialogModel(
currentLocationId = currentLocationId,
currentLocationName = currentLocationName
) { dialog ->
val params = mapOf(
"location" to dialog.locationName,
"locationId" to dialog.locationId.toLongOrNull(),
"manifestList" to selectedItems
).toRequestBody()
launchLoadingCollect({ NetApply.api.modifyIntImpLoadingStorage(params) }) {
onSuccess = {
if (it.verifySuccess()) {
showToast(it.msg.noNull("修改库位成功"))
refresh()
} else {
showToast(it.msg.noNull("修改库位失败"))
}
}
}
}.show()
}
/**
* 货物发放
*/

View File

@@ -143,7 +143,7 @@ class IntImpPickUpDLVViewModel : BasePageViewModel() {
"beginDate" to paymentDateStart.value?.ifEmpty { null },
"endDate" to paymentDateEnd.value?.ifEmpty { null },
"agentCode" to agentCode.value?.ifEmpty { null },
"no" to wbNo.value?.ifEmpty { null },
"wbNo" to wbNo.value?.ifEmpty { null },
"spCode" to spCode.value?.ifEmpty { null }
)

View File

@@ -33,7 +33,7 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
val flightDateStart = MutableLiveData<String>(DateUtils.getCurrentTime().formatDate())
val flightDateEnd = MutableLiveData("")
val agentId = MutableLiveData("")
val outStatus = MutableLiveData("")
val flightNo = MutableLiveData("")
val waybillNo = MutableLiveData("")
// ==================== 下拉列表 ====================
@@ -55,6 +55,7 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
val totalCount = MutableLiveData("0")
val totalPc = MutableLiveData("0")
val totalWeight = MutableLiveData("0")
val totalFlightNumber = MutableLiveData("0")
// ==================== 特码下拉 ====================
val spCodeList = MutableLiveData<List<KeyValue>>(emptyList())
@@ -66,7 +67,7 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
// ==================== 筛选条件 ====================
val spCode = MutableLiveData("")
val flightNo = MutableLiveData("")
val outStatus = MutableLiveData("")
val origin = MutableLiveData("")
val awbType = MutableLiveData("")
val businessType = MutableLiveData("")
@@ -75,11 +76,11 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
// 是否有筛选条件(任意一个非空则为 true
val hasFilter: MediatorLiveData<Boolean> = MediatorLiveData<Boolean>().apply {
val update = { _: Any? ->
value = listOf(spCode, flightNo, origin, awbType, businessType, goodsCn)
value = listOf(spCode, outStatus, origin, awbType, businessType, goodsCn)
.any { !it.value.isNullOrEmpty() }
}
addSource(spCode, update)
addSource(flightNo, update)
addSource(outStatus, update)
addSource(origin, update)
addSource(awbType, update)
addSource(businessType, update)
@@ -108,7 +109,7 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
fun resetFilter() {
spCode.value = ""
flightNo.value = ""
outStatus.value = ""
origin.value = ""
awbType.value = ""
businessType.value = ""
@@ -176,6 +177,7 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
totalCount.value = (data?.wbNumber ?: 0).toString()
totalPc.value = (data?.totalPc ?: 0).toString()
totalWeight.value = (data?.totalWeight ?: 0.0).toString()
totalFlightNumber.value = (data?.flightNumber ?: 0).toString()
}
}
}

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
@@ -356,7 +357,37 @@
android:layout_weight="0.605" />
</LinearLayout>
<!-- -->
<!-- 第8行交接图片 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="15dp"
android:orientation="horizontal">
<TextView
completeSpace="@{5}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="交接图片"
android:textColor="@color/text_gray" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_pic"
itemLayoutId="@{viewModel.itemLayoutId}"
viewHolder="@{viewModel.itemViewHolder}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="9"
tools:listitem="@layout/item_image_select_new" />
</LinearLayout>
</LinearLayout>

View File

@@ -118,6 +118,12 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
autoQueryEnabled="@{true}"
autoQueryMaxLength="@{8}"
autoQueryMinLength="@{8}"
autoQueryParamKey="@{`wbNo`}"
autoQueryTitle="@{`选择运单号`}"
autoQueryUrl="@{`/IntImpManifest/pageQueryWbNoList`}"
enable="@{!viewModel.isDetailMode}"
hint='@{"请输入运单号"}'
title='@{"运单号"}'

View File

@@ -274,9 +274,15 @@
style="@style/tv_bottom_btn"
android:layout_marginEnd="15dp"
android:onClick="@{()-> viewModel.sortingTallyClick()}"
android:text="分拣理货" />
android:text="装机单" />
<!-- 货物发放按钮 (红色) -->
<TextView
style="@style/tv_bottom_btn"
android:layout_marginEnd="15dp"
android:onClick="@{()-> viewModel.startTallyClick()}"
android:text="开始理货" />
<!-- 理货完成按钮 (红色) -->
<TextView
android:layout_width="100dp"
android:layout_height="40dp"
@@ -284,7 +290,7 @@
android:background="@drawable/bg_red_btn_bottom"
android:gravity="center"
android:onClick="@{()-> viewModel.cargoReleaseClick()}"
android:text="货物发放"
android:text="理货完成"
android:textColor="@color/white"
android:textSize="18sp" />

View File

@@ -72,15 +72,15 @@
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.agentId}" />
<!-- 出库状态 -->
<!-- 航班号 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
android:id="@+id/flight_no_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
hint='@{"请选择出库状态"}'
list="@{viewModel.outStatusList}"
type="@{SearchLayoutType.SPINNER}"
value="@={viewModel.outStatus}" />
hint='@{"请输入航班号"}'
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.flightNo}" />
<!-- 运单号 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
@@ -201,6 +201,16 @@
android:textStyle="bold"
tools:text="总重量100" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text='@{`航班航次:` + viewModel.totalFlightNumber}'
android:textColor="@color/bottom_tool_tips_text_color"
android:textSize="16sp"
android:textStyle="bold"
tools:text="航班航次2" />
</LinearLayout>
</LinearLayout>

View File

@@ -67,7 +67,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{4}"
completeSpace="@{5}"
android:text="运单号:"
android:textSize="15sp" />
@@ -194,7 +194,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
completeSpace="@{4}"
completeSpace="@{5}"
android:text="品名(中)"
android:textSize="15sp" />

View File

@@ -68,17 +68,17 @@
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.spCode}' />
<!-- 航班号 -->
<!-- 出库状态 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:id="@+id/filter_flight_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
hint='@{"请输入航班号"}'
title='@{"航班号"}'
hint='@{"请选择出库状态"}'
list="@{viewModel.outStatusList}"
title='@{"出库状态"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.flightNo}' />
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.outStatus}' />
<!-- 始发港 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew

View File

@@ -0,0 +1,53 @@
package com.lukouguoji.hangban.page.edit
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.lukouguoji.hangban.R
import com.lukouguoji.hangban.databinding.ActivityHbFlightEditBinding
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.DetailsPageType
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
class HbFlightEditActivity :
BaseBindingActivity<ActivityHbFlightEditBinding, HbFlightEditViewModel>() {
override fun layoutId() = R.layout.activity_hb_flight_edit
override fun viewModelClass() = HbFlightEditViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
binding.viewModel = viewModel
binding.flightNoInput.et.setUpperCaseAlphanumericFilter()
binding.fdepInput.et.setUpperCaseAlphanumericFilter()
binding.fdestInput.et.setUpperCaseAlphanumericFilter()
binding.registrationInput.et.setUpperCaseAlphanumericFilter()
viewModel.initOnCreated(intent)
// 动态设置标题(必须在 initOnCreated 之后pageType 已从 Intent 解析)
val title = when (viewModel.pageType.value) {
DetailsPageType.Modify -> "编辑航班"
else -> "新增航班"
}
setBackArrow(title)
}
companion object {
/**
* fid 为空表示新增,非空表示编辑
*/
@JvmStatic
fun start(context: Context, fid: String = "") {
val pageType =
if (fid.isEmpty()) DetailsPageType.Add.name else DetailsPageType.Modify.name
context.startActivity(
Intent(context, HbFlightEditActivity::class.java)
.putExtra(Constant.Key.PAGE_TYPE, pageType)
.putExtra(Constant.Key.ID, fid)
)
}
}
}

View File

@@ -0,0 +1,280 @@
package com.lukouguoji.hangban.page.edit
import android.content.Intent
import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.lukouguoji.module_base.base.BaseViewModel
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.common.DetailsPageType
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import com.lukouguoji.module_base.util.DictUtils
import dev.DevUtils
import dev.utils.app.info.KeyValue
import dev.utils.common.DateUtils
import kotlinx.coroutines.launch
class HbFlightEditViewModel : BaseViewModel() {
val pageType = MutableLiveData(DetailsPageType.Add)
// 航班主键(编辑模式)
private var fid = ""
///////////////////////////////////////////////////////////////////////////
// 表单字段
///////////////////////////////////////////////////////////////////////////
val flightDate = MutableLiveData("")
val flightNo = MutableLiveData("")
val aircraftCode = MutableLiveData("")
val fdep = MutableLiveData("")
val jtz = MutableLiveData("")
val fdest = MutableLiveData("")
val countryType = MutableLiveData("")
val serviceType = MutableLiveData("")
val status = MutableLiveData("")
val scheduledTackOff = MutableLiveData("")
val estimatedTakeOff = MutableLiveData("")
val actualTakeOff = MutableLiveData("")
val scheduledArrival = MutableLiveData("")
val estimatedArrival = MutableLiveData("")
val actualArrival = MutableLiveData("")
val standId = MutableLiveData("")
val registration = MutableLiveData("")
val prefix = MutableLiveData("")
val flightStatus = MutableLiveData("")
val cmFlag = MutableLiveData("")
val delayFreeText = MutableLiveData("")
///////////////////////////////////////////////////////////////////////////
// 下拉列表
///////////////////////////////////////////////////////////////////////////
val countryTypeList = MutableLiveData<List<KeyValue>>(emptyList())
val serviceTypeList = MutableLiveData<List<KeyValue>>(emptyList())
val statusList = MutableLiveData<List<KeyValue>>(emptyList())
val flightStatusList = MutableLiveData<List<KeyValue>>(emptyList())
val cmFlagList = MutableLiveData<List<KeyValue>>(emptyList())
///////////////////////////////////////////////////////////////////////////
// 选项定义(服务端入库/过滤用 code详情接口返回中文名回填时需反查
///////////////////////////////////////////////////////////////////////////
private val countryTypeOptions = listOf(
KeyValue("国内", "0"),
KeyValue("国际", "1"),
KeyValue("地区", "2"),
KeyValue("混合", "3"),
)
private val serviceTypeOptions = listOf(
KeyValue("客机", "0"),
KeyValue("货机", "1"),
KeyValue("卡车", "2"),
)
private val statusOptions = listOf(
KeyValue("出港", "0"),
KeyValue("进港", "1"),
)
private val flightStatusOptions = listOf(
KeyValue("正常", "0"),
KeyValue("登机", "1"),
KeyValue("登机结束", "2"),
KeyValue("起飞", "3"),
KeyValue("到达", "4"),
KeyValue("取消", "5"),
KeyValue("备降", "6"),
KeyValue("删除发布", "7"),
KeyValue("延误", "8"),
KeyValue("未知", "9"),
)
private val cmFlagOptions = listOf(
KeyValue("有货邮", "0"),
KeyValue("无货邮", "1"),
)
///////////////////////////////////////////////////////////////////////////
// 方法区
///////////////////////////////////////////////////////////////////////////
fun initOnCreated(intent: Intent) {
pageType.value =
if (intent.getStringExtra(Constant.Key.PAGE_TYPE) == DetailsPageType.Modify.name)
DetailsPageType.Modify else DetailsPageType.Add
fid = intent.getStringExtra(Constant.Key.ID) ?: ""
if (pageType.value == DetailsPageType.Modify && fid.isNotEmpty()) {
loadDetails()
} else {
flightDate.value = DateUtils.getCurrentTime().formatDate()
loadDictLists()
}
}
/**
* 编辑模式:加载航班详情
*/
private fun loadDetails() {
launchLoadingCollect({
NetApply.api.getFlightDetails(fid)
}) {
onSuccess = {
it.data?.let { bean ->
flightDate.value = bean.fdate
flightNo.value = bean.fno
aircraftCode.value = bean.aircraftCode
fdep.value = bean.fdep
jtz.value = bean.jtz
fdest.value = bean.fdest
countryType.value = toCode(countryTypeOptions, bean.countryType)
serviceType.value = toCode(serviceTypeOptions, bean.serviceType)
status.value = toCode(statusOptions, bean.status)
scheduledTackOff.value = bean.scheduledTackOff
estimatedTakeOff.value = bean.estimatedTakeOff
actualTakeOff.value = bean.actualTakeOff
scheduledArrival.value = bean.scheduledArrival
estimatedArrival.value = bean.estimatedArrival
actualArrival.value = bean.actualArrival
standId.value = bean.standId
registration.value = bean.registration
prefix.value = bean.prefix
flightStatus.value = toCode(flightStatusOptions, bean.flightStatus)
cmFlag.value = toCode(cmFlagOptions, bean.cmFlag)
delayFreeText.value = bean.delayFreeText
}
// 字典加载必须在编辑数据回填之后,保证 checkedValue 可用
loadDictLists()
}
}
}
/**
* 加载下拉列表(编辑模式传 checkedValue 置顶选中项)
*/
private fun loadDictLists() {
val isModify = pageType.value == DetailsPageType.Modify
DictUtils.getCountryTypeList(
addAll = false,
checkedValue = if (isModify) countryType.value else null
) {
countryTypeList.postValue(if (isModify) it else listOf(KeyValue("", "")) + it)
}
serviceTypeList.value =
buildStaticList(serviceTypeOptions, if (isModify) serviceType.value else null)
statusList.value =
buildStaticList(statusOptions, if (isModify) status.value else null)
flightStatusList.value =
buildStaticList(flightStatusOptions, if (isModify) flightStatus.value else null)
cmFlagList.value =
buildStaticList(cmFlagOptions, if (isModify) cmFlag.value else null)
}
/**
* 值转 code详情接口可能返回中文名如“国际”“客机”统一转为 code 用于回填与提交
*/
private fun toCode(options: List<KeyValue>, raw: String?): String {
if (raw.isNullOrEmpty()) return ""
return options.firstOrNull { it.value == raw }?.value
?: options.firstOrNull { it.key == raw }?.value
?: raw
}
/**
* 静态下拉列表:新增模式首位置空项;编辑模式将选中项置顶
*/
private fun buildStaticList(list: List<KeyValue>, checkedValue: String?): List<KeyValue> {
if (checkedValue.isNullOrEmpty()) {
return listOf(KeyValue("", "")) + list
}
return list.filter { it.value == checkedValue } + list.filter { it.value != checkedValue }
}
/**
* 保存 点击
*/
fun onSaveClick(view: View) {
if (flightDate.value.verifyNullOrEmpty("请选择航班日期")) return
if (flightNo.value.verifyNullOrEmpty("请输入航班号")) return
if (fdep.value.verifyNullOrEmpty("请输入始发港")) return
if (fdest.value.verifyNullOrEmpty("请输入目的港")) return
val isModify = pageType.value == DetailsPageType.Modify
val dep = fdep.value?.trim().noNull()
val dest = fdest.value?.trim().noNull()
val params = mutableMapOf<String, Any?>(
"fdate" to flightDate.value,
"fno" to flightNo.value?.trim(),
"aircraftCode" to aircraftCode.value?.trim(),
"fdep" to dep,
"jtz" to jtz.value?.trim(),
"fdest" to dest,
"range" to "$dep-$dest",
"countryType" to countryType.value,
"countryName" to countryTypeList.value
?.firstOrNull { it.value == countryType.value }?.key,
"serviceType" to serviceType.value,
"status" to status.value,
"scheduledTackOff" to scheduledTackOff.value,
"estimatedTakeOff" to estimatedTakeOff.value,
"actualTakeOff" to actualTakeOff.value,
"scheduledArrival" to scheduledArrival.value,
"estimatedArrival" to estimatedArrival.value,
"actualArrival" to actualArrival.value,
"standId" to standId.value?.trim(),
"registration" to registration.value?.trim(),
"prefix" to prefix.value?.trim(),
"flightStatus" to flightStatus.value,
"cmFlag" to cmFlag.value,
"delayFreeText" to delayFreeText.value?.trim(),
)
if (isModify) {
params["fid"] = fid.toLongOrNull() ?: fid
}
launchLoadingCollect({
if (isModify) {
NetApply.api.updateFlight(params.toRequestBody())
} else {
NetApply.api.saveFlight(params.toRequestBody())
}
}) {
onSuccess = {
if (it.verifySuccess()) {
showToast(if (isModify) "修改成功" else "新增成功")
viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}
DevUtils.getTopActivity().finish()
} else {
showToast(it.msg.noNull("保存失败"))
}
}
}
}
/**
* 取消 点击
*/
fun onCancelClick(view: View) {
DevUtils.getTopActivity().finish()
}
}

View File

@@ -5,6 +5,9 @@ import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.hangban.R
import com.lukouguoji.hangban.databinding.ActivityHbQueryListBinding
import com.lukouguoji.module_base.base.BaseBindingActivity
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.ktx.getLifecycleOwner
import com.lukouguoji.module_base.router.ARouterConstants
@@ -26,6 +29,11 @@ class HbQueryListActivity :
binding.rv.addOnItemClickListener(viewModel)
// 新增、编辑保存后刷新列表
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
viewModel.refresh()
}

View File

@@ -28,6 +28,8 @@ class HbQueryListViewHolder(view: View) :
}
notifyItemClick(position, binding.ll)
notifyItemClick(position, binding.tvModify)
notifyItemClick(position, binding.tvDelete)
}
private fun setTextColorRecursive(view: View, color: Int) {

View File

@@ -3,6 +3,7 @@ package com.lukouguoji.hangban.page.query.list
import androidx.lifecycle.MutableLiveData
import com.alibaba.android.arouter.launcher.ARouter
import com.lukouguoji.hangban.R
import com.lukouguoji.hangban.page.edit.HbFlightEditActivity
import com.lukouguoji.module_base.base.BasePageViewModel
import com.lukouguoji.module_base.bean.FlightBean
import com.lukouguoji.module_base.common.Constant
@@ -10,9 +11,13 @@ import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.commonAdapter
import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.model.ConfirmDialogModel
import com.lukouguoji.module_base.router.ARouterConstants
import com.lukouguoji.module_base.util.DictUtils
import dev.DevUtils
import dev.utils.app.info.KeyValue
import dev.utils.common.DateUtils
@@ -80,11 +85,55 @@ class HbQueryListViewModel : BasePageViewModel() {
return "${d ?: ""}-${a ?: ""}"
}
/**
* 新增 点击
*/
fun addClick() {
HbFlightEditActivity.start(DevUtils.getTopActivity())
}
override fun onItemClick(position: Int, type: Int) {
val bean = pageModel.rv!!.commonAdapter()!!.getItem(position) as FlightBean
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_FLIGHT_QUERY_DETAILS)
.withString(Constant.Key.ID, bean.fid)
.navigation()
when (type) {
R.id.tv_modify -> {
HbFlightEditActivity.start(DevUtils.getTopActivity(), bean.fid)
}
R.id.tv_delete -> {
ConfirmDialogModel(
message = "是否确认删除航班【${bean.fno}】?",
) {
onDelete(bean)
}.show(DevUtils.getTopActivity())
}
else -> {
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_FLIGHT_QUERY_DETAILS)
.withString(Constant.Key.ID, bean.fid)
.navigation()
}
}
}
/**
* 删除航班
*/
private fun onDelete(bean: FlightBean) {
launchLoadingCollect({
NetApply.api.deleteFlightList(
listOf(mapOf("fid" to bean.fid)).toRequestBody()
)
}) {
onSuccess = {
if (it.verifySuccess()) {
// 服务端删除接口的 msg 为“修改成功”,此处固定提示删除成功
showToast("删除成功")
refresh()
} else {
showToast(it.msg.noNull("删除失败"))
}
}
}
}
}

View File

@@ -18,6 +18,11 @@
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="userLandscape"
android:exported="false" />
<activity
android:name=".page.edit.HbFlightEditActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="userLandscape"
android:exported="false" />
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden"

View File

@@ -0,0 +1,377 @@
<?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.data.layout.DataLayoutType" />
<variable
name="viewModel"
type="com.lukouguoji.hangban.page.edit.HbFlightEditViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f2"
android:orientation="vertical">
<include layout="@layout/title_tool_bar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_white_radius_8"
android:orientation="vertical"
android:padding="8dp">
<!-- 第1行航班日期、航班号、机型 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择航班日期"}'
required="@{true}"
title='@{"航班日期"}'
titleLength="@{4}"
type="@{DataLayoutType.DATE}"
value='@={viewModel.flightDate}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:id="@+id/flightNoInput"
hint='@{"请输入航班号"}'
required="@{true}"
title='@{"航班号"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.flightNo}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入机型"}'
title='@{"机型"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.aircraftCode}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第2行始发港、经停港、目的港 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:id="@+id/fdepInput"
hint='@{"请输入始发港"}'
required="@{true}"
title='@{"始发港"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.fdep}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"多个用英文逗号分隔"}'
title='@{"经停港"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.jtz}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:id="@+id/fdestInput"
hint='@{"请输入目的港"}'
required="@{true}"
title='@{"目的港"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.fdest}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第3行地区类型、服务类型、进出港 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择地区类型"}'
list="@{viewModel.countryTypeList}"
title='@{"地区类型"}'
titleLength="@{4}"
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.countryType}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择服务类型"}'
list="@{viewModel.serviceTypeList}"
title='@{"服务类型"}'
titleLength="@{4}"
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.serviceType}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择进出港"}'
list="@{viewModel.statusList}"
title='@{"进出港"}'
titleLength="@{4}"
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.status}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第4行计划起飞、预计起飞、实际起飞 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择计划起飞时间"}'
title='@{"计划起飞"}'
titleLength="@{4}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.scheduledTackOff}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择预计起飞时间"}'
title='@{"预计起飞"}'
titleLength="@{4}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.estimatedTakeOff}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择实际起飞时间"}'
title='@{"实际起飞"}'
titleLength="@{4}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.actualTakeOff}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第5行计划降落、预计降落、实际降落 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择计划降落时间"}'
title='@{"计划降落"}'
titleLength="@{4}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.scheduledArrival}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择预计降落时间"}'
title='@{"预计降落"}'
titleLength="@{4}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.estimatedArrival}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择实际降落时间"}'
title='@{"实际降落"}'
titleLength="@{4}"
type="@{DataLayoutType.DATETIME}"
value='@={viewModel.actualArrival}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第6行机位、机号、运单前缀 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入机位"}'
title='@{"机位"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.standId}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
android:id="@+id/registrationInput"
hint='@{"请输入机号"}'
title='@{"机号"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.registration}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入运单前缀"}'
title='@{"运单前缀"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.prefix}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
<!-- 第7行航班状态、货邮状态、延误原因 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择航班状态"}'
list="@{viewModel.flightStatusList}"
title='@{"航班状态"}'
titleLength="@{4}"
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.flightStatus}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请选择货邮状态"}'
list="@{viewModel.cmFlagList}"
title='@{"货邮状态"}'
titleLength="@{4}"
type="@{DataLayoutType.SPINNER}"
value='@={viewModel.cmFlag}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
hint='@{"请输入延误原因"}'
title='@{"延误原因"}'
titleLength="@{4}"
type="@{DataLayoutType.INPUT}"
value='@={viewModel.delayFreeText}'
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
<!-- 底部操作栏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="15dp">
<TextView
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginEnd="30dp"
android:background="@drawable/bg_primary_radius_4"
android:gravity="center"
android:onClick="@{viewModel::onCancelClick}"
android:text="取消"
android:textColor="@color/white"
android:textSize="18sp" />
<TextView
android:layout_width="150dp"
android:layout_height="50dp"
android:background="@drawable/bg_primary_radius_4"
android:gravity="center"
android:onClick="@{viewModel::onSaveClick}"
android:text="保存"
android:textColor="@color/white"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</layout>

View File

@@ -104,6 +104,14 @@
android:padding="2dp"
android:src="@drawable/img_search" />
<!-- 新增按钮 -->
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="16dp"
android:onClick="@{()-> viewModel.addClick()}"
android:src="@drawable/img_add" />
</LinearLayout>
</LinearLayout>

View File

@@ -10,16 +10,26 @@
type="com.lukouguoji.module_base.bean.FlightBean" />
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll"
<!-- 外层容器承载间距,使侧滑按钮紧贴卡片(参考 item_int_imp_pick_up_record -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="5dp"
android:background="@drawable/bg_item"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
android:orientation="vertical">
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_item"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<!-- 左侧飞机图标 -->
<ImageView
@@ -317,5 +327,28 @@
android:layout_marginLeft="10dp"
android:src="@drawable/img_pda_right" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- 侧滑操作按钮 -->
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_modify"
style="@style/tv_item_action"
android:background="@color/colorPrimary"
android:text="编辑" />
<TextView
android:id="@+id/tv_delete"
style="@style/tv_item_action"
android:background="#df253c"
android:text="删除" />
</androidx.appcompat.widget.LinearLayoutCompat>
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
</LinearLayout>
</layout>

View File

@@ -23,7 +23,7 @@ class PdaGnjXieJiAddActivity :
}
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国内进港开始卸机")
setBackArrow("进港开始卸机")
viewModel.rv = binding.rv
binding.viewModel = viewModel
binding.rv.addOnItemClickListener(viewModel)

View File

@@ -27,7 +27,7 @@ class PdaGnjXieJiListActivity :
}
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国内进港卸机")
setBackArrow("进港卸机")
binding.viewModel = viewModel
viewModel.pageModel.bindSmartRefreshLayout(