Compare commits
7 Commits
67b2dc6d71
...
52171c94df
| Author | SHA1 | Date | |
|---|---|---|---|
| 52171c94df | |||
| 39934df970 | |||
| 624331ca68 | |||
| 3e5f185721 | |||
| 936af73ec0 | |||
| 43acf0a2de | |||
| c9625f6bfd |
@@ -116,7 +116,10 @@
|
||||
"Bash(echo \"exit:$?\")",
|
||||
"mcp__apifox__read_project_oas_j7j64k",
|
||||
"mcp__apifox__read_project_oas_ref_resources_j7j64k",
|
||||
"mcp__apifox__read_project_oas_ruugy8"
|
||||
"mcp__apifox__read_project_oas_ruugy8",
|
||||
"mcp__apifox__read_project_oas_ref_resources_ldmedm",
|
||||
"mcp__apifox__read_project_oas_ldmedm",
|
||||
"mcp__apifox__refresh_project_oas_ldmedm"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
53
CLAUDE.md
53
CLAUDE.md
@@ -861,6 +861,59 @@ adb logcat | grep "com.lukouguoji.aerologic" # 日志
|
||||
|
||||
---
|
||||
|
||||
## 编辑表单下拉框(SPINNER)回填规范
|
||||
|
||||
编辑页面(DetailsPageType.Modify)中,下拉框需要根据已有数据自动选中对应项。**必须使用 `DictUtils` 的 `checkedValue` 参数**,禁止依赖组件自动匹配 value。
|
||||
|
||||
### 原理
|
||||
|
||||
`DictUtils` 的 `handleCallBack` 会将 `checkedValue` 匹配的 `KeyValue` 置于列表首位。`PadDataLayoutNew` 的 SPINNER 默认显示列表第 0 项,因此匹配项自动成为选中项,无需额外设置 selectedIndex。
|
||||
|
||||
### 标准做法(参考 `GjjManifestDetailsViewModel`、`GjjManifestAddViewModel`)
|
||||
|
||||
1. **字典加载必须在编辑数据加载之后**(不能放在 `init` 中),确保 `checkedValue` 可用
|
||||
2. **编辑模式传入 `checkedValue`**,新增模式传 `null`
|
||||
3. **编辑模式不预置空 `KeyValue("", "")`**(否则空项会占据首位,覆盖 checkedValue 排序)
|
||||
|
||||
```kotlin
|
||||
fun initOnCreated(intent: Intent) {
|
||||
// 1. 先解析页面类型和编辑数据
|
||||
if (pageType.value == DetailsPageType.Modify) {
|
||||
loadManifestFromBean(bean) // 设置 agent.value、specialCode.value 等
|
||||
}
|
||||
// 2. 再加载字典列表(此时 checkedValue 已可用)
|
||||
loadDictLists()
|
||||
}
|
||||
|
||||
private fun loadDictLists() {
|
||||
val isModify = pageType.value == DetailsPageType.Modify
|
||||
|
||||
DictUtils.getXxxList(
|
||||
addAll = false,
|
||||
checkedValue = if (isModify) field.value else null // 编辑模式传值,新增传 null
|
||||
) {
|
||||
xxxList.postValue(if (isModify) it else listOf(KeyValue("", "")) + it)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### checkedValue 取值规则
|
||||
|
||||
提交时用的哪个字段值,`checkedValue` 就传哪个。对照 `toKeyValue()` 的 `value` 字段确认匹配:
|
||||
|
||||
| DictUtils 方法 | KeyValue.value 来源 | checkedValue 示例 |
|
||||
|---|---|---|
|
||||
| 通用(`handleCallBack`) | `DictBean.code` | `manifest.agentCode`(如 "SFINT") |
|
||||
| `getShouYunPackageTypeList` | `PackageBean.name` | `manifest.packageType`(如 "木框") |
|
||||
|
||||
### 禁止做法
|
||||
|
||||
- ❌ 在 `init` 中加载字典(编辑数据尚未可用,无法传 `checkedValue`)
|
||||
- ❌ 依赖 `PadDataLayoutNew` 的 `value` 属性自动匹配列表(Spinner adapter 重建时 `onItemSelected` 回调会覆盖已有值)
|
||||
- ❌ 编辑模式下在列表前添加 `KeyValue("", "")`(会干扰 `checkedValue` 置顶排序)
|
||||
|
||||
---
|
||||
|
||||
## 开发检查清单
|
||||
|
||||
### 新页面开发必做
|
||||
|
||||
@@ -44,7 +44,7 @@ data class GjjImportManifest(
|
||||
// 目的地
|
||||
var dest: String = "",
|
||||
// 危险品收货人通讯方式
|
||||
var dgrContactMame: String = "",
|
||||
var dgrContactName: String = "",
|
||||
// 危险品收货人通讯方式
|
||||
var dgrContactNumber: String = "",
|
||||
// 航班日期
|
||||
|
||||
@@ -28,6 +28,7 @@ data class GjjManifest(
|
||||
var awbType: String = "", // 运单类型
|
||||
var awbTypeName: String = "", // 运单类型(中)
|
||||
var businessType: String = "", // 业务类型
|
||||
var businessName: String = "", // 业务类型名称
|
||||
var cargoType: String = "", // 货物类型
|
||||
var spCode: String = "", // 特码
|
||||
var packageType: String = "", // 包装类型
|
||||
|
||||
@@ -10,6 +10,7 @@ class GjjManifestBean(
|
||||
var awbType: String = "",
|
||||
var awbpc: Int = 0,
|
||||
var businessType: String? = "",
|
||||
var businessName: String? = "",
|
||||
var cargoType: String? = "",
|
||||
var cashWeight: String? = "",
|
||||
var cneeCode: String? = "",
|
||||
@@ -73,7 +74,10 @@ class GjjManifestBean(
|
||||
var wbNo: String = "",
|
||||
var weight: String = "",
|
||||
var storageTime: String = "",
|
||||
var whslocation: String? = ""
|
||||
var whslocation: String? = "",
|
||||
var pic: String = "",
|
||||
var originalPic: String = "",
|
||||
var picNumber: String = ""
|
||||
) : BaseObservable(), ICheck {
|
||||
|
||||
// 展示逻辑
|
||||
|
||||
@@ -401,5 +401,8 @@ interface Constant {
|
||||
|
||||
// 运单类型
|
||||
const val AWB_TYPE = "awbType"
|
||||
|
||||
const val PIC = "pic"
|
||||
const val ORIGINAL_PIC = "originalPic"
|
||||
}
|
||||
}
|
||||
@@ -1280,6 +1280,18 @@ interface Api {
|
||||
@POST("IntImpManiFest/deleteFestList")
|
||||
suspend fun gjjManifestDeleteBatch(@Body data: RequestBody): BaseResultBean<Any>
|
||||
|
||||
/**
|
||||
* 删除舱单(主单)- 请求体为 GjjManifest 对象数组
|
||||
*/
|
||||
@POST("IntImpManifest/deleteManifest")
|
||||
suspend fun intImpManifestDeleteManifest(@Body data: RequestBody): BaseResultBean<Any>
|
||||
|
||||
/**
|
||||
* 删除分单 - 请求体为 GjjHaWb 对象数组
|
||||
*/
|
||||
@POST("IntImpManifest/deleteHawb")
|
||||
suspend fun intImpManifestDeleteHawb(@Body data: RequestBody): BaseResultBean<Any>
|
||||
|
||||
/**
|
||||
* 新增-国际进港舱单
|
||||
*/
|
||||
|
||||
@@ -97,7 +97,7 @@ class PadDataLayoutNew : FrameLayout {
|
||||
|
||||
et.hint = value
|
||||
tv.hint = value
|
||||
bindAdapter(spinner, list, hint)
|
||||
updateSpinnerSilently { bindAdapter(spinner, list, hint) }
|
||||
}
|
||||
|
||||
var required = false
|
||||
@@ -106,11 +106,29 @@ class PadDataLayoutNew : FrameLayout {
|
||||
tvM.visibility = if (value) VISIBLE else INVISIBLE
|
||||
}
|
||||
|
||||
private val spinnerCallback = object : IOnSpinnerSelected {
|
||||
override fun onSelected(position: Int) {
|
||||
value = list.getOrNull(position)?.value ?: ""
|
||||
refreshCallBack?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private val restoreListenerRunnable = Runnable {
|
||||
bindOnSelected(spinner, spinnerCallback)
|
||||
}
|
||||
|
||||
private fun updateSpinnerSilently(block: () -> Unit) {
|
||||
spinner.onItemSelectedListener = null
|
||||
spinner.removeCallbacks(restoreListenerRunnable)
|
||||
block()
|
||||
onValueSet()
|
||||
spinner.post(restoreListenerRunnable)
|
||||
}
|
||||
|
||||
var list = emptyList<KeyValue>()
|
||||
set(value) {
|
||||
field = value
|
||||
bindAdapter(spinner, value, hint)
|
||||
onValueSet()
|
||||
updateSpinnerSilently { bindAdapter(spinner, value, hint) }
|
||||
}
|
||||
|
||||
var icon: Any? = null
|
||||
@@ -183,12 +201,7 @@ class PadDataLayoutNew : FrameLayout {
|
||||
et.doOnTextChanged { text, _, _, _ ->
|
||||
value = text.toString()
|
||||
}
|
||||
bindOnSelected(spinner, object : IOnSpinnerSelected {
|
||||
override fun onSelected(position: Int) {
|
||||
value = list.getOrNull(position)?.value ?: ""
|
||||
refreshCallBack?.invoke()
|
||||
}
|
||||
})
|
||||
bindOnSelected(spinner, spinnerCallback)
|
||||
// 监听输入框焦点变化
|
||||
com.lukouguoji.module_base.adapter.setOnFocusChangeListener(
|
||||
et, object : IOnFocusChangeListener {
|
||||
|
||||
@@ -2,12 +2,14 @@ package com.lukouguoji.gjj.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.lukouguoji.gjj.R
|
||||
import com.lukouguoji.gjj.databinding.ActivityGjjManifestDetailsBinding
|
||||
import com.lukouguoji.gjj.holder.GjjManifestPicViewHolder
|
||||
import com.lukouguoji.gjj.viewModel.GjjManifestDetailsViewModel
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.base.CommonAdapter
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.ktx.noNull
|
||||
|
||||
@@ -21,17 +23,33 @@ class GjjManifestDetailsActivity :
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
setBackArrow("国际进港舱单详情")
|
||||
viewModel.id = intent.getStringExtra(Constant.Key.ID).noNull()
|
||||
viewModel.pic = intent.getStringExtra(Constant.Key.PIC).noNull()
|
||||
viewModel.originalPic = intent.getStringExtra(Constant.Key.ORIGINAL_PIC).noNull()
|
||||
|
||||
binding.viewModel = viewModel
|
||||
|
||||
val picAdapter = CommonAdapter(
|
||||
this,
|
||||
R.layout.item_gjj_manifest_pic,
|
||||
GjjManifestPicViewHolder::class.java
|
||||
)
|
||||
binding.rvPic.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
binding.rvPic.adapter = picAdapter
|
||||
|
||||
viewModel.picList.observe(this) { list ->
|
||||
picAdapter.refresh(list)
|
||||
}
|
||||
|
||||
viewModel.getData()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun start(context: Context, id: String) {
|
||||
fun start(context: Context, id: String, pic: String = "", originalPic: String = "") {
|
||||
val starter = Intent(context, GjjManifestDetailsActivity::class.java)
|
||||
.putExtra(Constant.Key.ID, id)
|
||||
.putExtra(Constant.Key.PIC, pic)
|
||||
.putExtra(Constant.Key.ORIGINAL_PIC, originalPic)
|
||||
context.startActivity(starter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.lukouguoji.gjj.activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import com.lukouguoji.gjj.R
|
||||
import com.lukouguoji.gjj.databinding.ActivityIntImpAccidentVisaEditBinding
|
||||
import com.lukouguoji.gjj.viewModel.IntImpAccidentVisaEditViewModel
|
||||
@@ -26,8 +27,8 @@ class IntImpAccidentVisaEditActivity :
|
||||
|
||||
// 航班号:大写字母+数字
|
||||
binding.fnoInput.et.setUpperCaseAlphanumericFilter()
|
||||
// 运单号:大写字母+数字
|
||||
binding.wbNoInput.et.setUpperCaseAlphanumericFilter()
|
||||
// 运单号:纯数字11位
|
||||
binding.wbNoInput.et.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
|
||||
viewModel.rv = binding.rv
|
||||
binding.rv.addOnItemClickListener(viewModel)
|
||||
|
||||
@@ -108,13 +108,11 @@ class IntImpLoadingListActivity :
|
||||
return
|
||||
}
|
||||
|
||||
// 如果所有选中项库位相同,则预选该库位
|
||||
val commonLocation = selectedItems.map { it.locationTally }.distinct().let {
|
||||
if (it.size == 1) it.first() else ""
|
||||
}
|
||||
// 单选时自动带出该项的库位号,多选不带出
|
||||
val presetLocation = if (selectedItems.size == 1) selectedItems.first().locationTally ?: "" else ""
|
||||
|
||||
IntImpModifyStorageDialogModel(
|
||||
currentLocationName = commonLocation
|
||||
currentLocationName = presetLocation
|
||||
) { dialog ->
|
||||
val locationName = dialog.locationName
|
||||
val locationId = dialog.locationId
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.lukouguoji.gjj.holder
|
||||
|
||||
import android.view.View
|
||||
import com.lukouguoji.gjj.databinding.ItemGjjManifestPicBinding
|
||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||
import com.lukouguoji.module_base.bean.FileBean
|
||||
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||
import com.lukouguoji.module_base.ui.page.preview.PreviewActivity
|
||||
|
||||
class GjjManifestPicViewHolder(view: View) :
|
||||
BaseViewHolder<FileBean, ItemGjjManifestPicBinding>(view) {
|
||||
|
||||
override fun onBind(item: Any?, position: Int) {
|
||||
val bean = getItemBean(item)!!
|
||||
binding.bean = bean
|
||||
|
||||
binding.ivThumbnail.setOnClickListener {
|
||||
val items = getRecyclerView()?.commonAdapter()?.items
|
||||
?.filterIsInstance<FileBean>() ?: listOf(bean)
|
||||
val originalList = items.map { fb ->
|
||||
FileBean(path = if (fb.originalPic.isNotEmpty()) fb.originalPic else fb.path)
|
||||
}
|
||||
PreviewActivity.start(itemView.context, originalList, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.lukouguoji.gjj.holder
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lukouguoji.gjj.databinding.ItemIntImpManifestSubBinding
|
||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||
import com.lukouguoji.module_base.bean.GjjHaWb
|
||||
import com.lukouguoji.module_base.bean.GjjManifest
|
||||
|
||||
/**
|
||||
* 国际进港舱单 - 分单子列表 ViewHolder
|
||||
@@ -24,13 +22,6 @@ class IntImpManifestSubViewHolder(view: View) :
|
||||
val newCheckedState = !bean.checked.get()
|
||||
bean.checked.set(newCheckedState)
|
||||
binding.executePendingBindings()
|
||||
|
||||
// 反向联动主列表项(勾选子项时自动勾选父项)
|
||||
if (newCheckedState) {
|
||||
val recyclerView = itemView.parent as? RecyclerView ?: return@setOnClickListener
|
||||
val parentBean = recyclerView.tag as? GjjManifest ?: return@setOnClickListener
|
||||
parentBean.checked.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,7 @@ class IntImpManifestViewHolder(view: View) :
|
||||
binding.ivIcon.setOnClickListener {
|
||||
val newCheckedState = !bean.checked.get()
|
||||
bean.checked.set(newCheckedState)
|
||||
// 联动子列表选中状态
|
||||
bean.haWbList?.forEach { sub -> sub.checked.set(newCheckedState) }
|
||||
binding.executePendingBindings()
|
||||
binding.rvSub.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
// 整卡点击 - 跳转详情页
|
||||
|
||||
@@ -153,51 +153,13 @@ class GjjManifestAddViewModel : BaseViewModel() {
|
||||
val specialCodeList = MutableLiveData<List<KeyValue>>()
|
||||
val specialCode = MutableLiveData("")
|
||||
|
||||
// 货物类型
|
||||
val goodsTypeList = MutableLiveData<List<KeyValue>>()
|
||||
// 货物类型(无下拉列表,仅用于编辑模式回传)
|
||||
val goodsType = MutableLiveData("")
|
||||
|
||||
// 运单类型
|
||||
val waybillTypeList = MutableLiveData<List<KeyValue>>()
|
||||
val waybillType = MutableLiveData("")
|
||||
|
||||
init {
|
||||
DictUtils.getIntImpAgentList(addAll = false) {
|
||||
agentList.postValue(listOf(KeyValue("", "")) + it)
|
||||
}
|
||||
DictUtils.getSpecialCodeList(addAll = false, flag = 1, ieFlag = "") {
|
||||
val list = arrayListOf<KeyValue>()
|
||||
it.find { b -> b.key.contains("普通货物") }?.let { b ->
|
||||
list.add(b)
|
||||
}
|
||||
list.addAll(it.filter { b -> !b.key.contains("普通货物") })
|
||||
specialCodeList.postValue(list)
|
||||
}
|
||||
DictUtils.getBusinessTypeList(addAll = false) {
|
||||
businessTypeList.postValue(it)
|
||||
// 新增模式下默认选中"普通货物运输"
|
||||
if (pageType.value == DetailsPageType.Add && businessType.value.isNullOrEmpty()) {
|
||||
it.find { b -> b.key.contains("普通货物运输") }?.let { b ->
|
||||
businessType.postValue(b.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
DictUtils.getShouYunPackageTypeList {
|
||||
packageTypeList.postValue(listOf(KeyValue("", "")) + it)
|
||||
}
|
||||
DictUtils.getGjjGoodsTypeList(addAll = false) {
|
||||
goodsTypeList.postValue(it)
|
||||
}
|
||||
DictUtils.getWaybillTypeList(type = "II", addAll = false) {
|
||||
val list = arrayListOf<KeyValue>()
|
||||
it.find { b -> b.key.contains("干线") }?.let { b ->
|
||||
list.add(b)
|
||||
}
|
||||
list.addAll(it.filter { b -> !b.key.contains("干线") })
|
||||
waybillTypeList.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化(从Intent获取参数)
|
||||
*/
|
||||
@@ -235,6 +197,71 @@ class GjjManifestAddViewModel : BaseViewModel() {
|
||||
loadManifestFromImportBean(bean)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载下拉列表(在编辑数据加载之后,以便使用 checkedValue 将选中项置顶)
|
||||
loadDictLists()
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载下拉列表数据
|
||||
* 编辑模式下传入 checkedValue,handleCallBack 会将匹配项置于列表首位,
|
||||
* Spinner 默认显示首项即完成回填
|
||||
*/
|
||||
private fun loadDictLists() {
|
||||
val isModify = pageType.value == DetailsPageType.Modify
|
||||
|
||||
DictUtils.getIntImpAgentList(
|
||||
addAll = false,
|
||||
checkedValue = if (isModify) agent.value else null
|
||||
) {
|
||||
agentList.postValue(if (isModify) it else listOf(KeyValue("", "")) + it)
|
||||
}
|
||||
|
||||
DictUtils.getSpecialCodeList(
|
||||
addAll = false, flag = 1, ieFlag = "",
|
||||
checkedValue = if (isModify) specialCode.value else null
|
||||
) {
|
||||
if (isModify) {
|
||||
specialCodeList.postValue(it)
|
||||
} else {
|
||||
val list = arrayListOf<KeyValue>()
|
||||
it.find { b -> b.key.contains("普通货物") }?.let { b -> list.add(b) }
|
||||
list.addAll(it.filter { b -> !b.key.contains("普通货物") })
|
||||
specialCodeList.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
DictUtils.getBusinessTypeList(
|
||||
addAll = false,
|
||||
checkedValue = if (isModify) businessType.value else null
|
||||
) {
|
||||
businessTypeList.postValue(it)
|
||||
if (!isModify && businessType.value.isNullOrEmpty()) {
|
||||
it.find { b -> b.key.contains("普通货物运输") }?.let { b ->
|
||||
businessType.postValue(b.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DictUtils.getShouYunPackageTypeList(
|
||||
checkedValue = if (isModify) packageType.value else null
|
||||
) {
|
||||
packageTypeList.postValue(if (isModify) it else listOf(KeyValue("", "")) + it)
|
||||
}
|
||||
|
||||
DictUtils.getWaybillTypeList(
|
||||
type = "II", addAll = false,
|
||||
checkedValue = if (isModify) waybillType.value else null
|
||||
) {
|
||||
if (isModify) {
|
||||
waybillTypeList.postValue(it)
|
||||
} else {
|
||||
val list = arrayListOf<KeyValue>()
|
||||
it.find { b -> b.key.contains("干线") }?.let { b -> list.add(b) }
|
||||
list.addAll(it.filter { b -> !b.key.contains("干线") })
|
||||
waybillTypeList.postValue(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.lukouguoji.gjj.viewModel
|
||||
import android.view.View
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.lukouguoji.module_base.base.BaseViewModel
|
||||
import com.lukouguoji.module_base.bean.FileBean
|
||||
import com.lukouguoji.module_base.http.net.NetApply
|
||||
import com.lukouguoji.module_base.interfaces.IGetData
|
||||
import com.lukouguoji.module_base.ktx.finish
|
||||
@@ -13,6 +14,7 @@ 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 dev.utils.app.info.KeyValue
|
||||
import dev.utils.common.DateUtils
|
||||
|
||||
@@ -20,6 +22,11 @@ class GjjManifestDetailsViewModel : BaseViewModel(), IGetData {
|
||||
|
||||
var id = ""
|
||||
var fid = ""
|
||||
var pic = ""
|
||||
var originalPic = ""
|
||||
|
||||
// 交接图片列表
|
||||
val picList = MutableLiveData<List<FileBean>>(emptyList())
|
||||
|
||||
// 是否修改状态
|
||||
var modifyAble = MutableLiveData(false)
|
||||
@@ -88,6 +95,7 @@ class GjjManifestDetailsViewModel : BaseViewModel(), IGetData {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
override fun getData() {
|
||||
parsePicList()
|
||||
showLoading()
|
||||
launchCollect({
|
||||
NetApply.api.getGjjManifestDetail(id)
|
||||
@@ -256,4 +264,21 @@ class GjjManifestDetailsViewModel : BaseViewModel(), IGetData {
|
||||
fun onCancelClick(view: View) {
|
||||
modifyAble.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 pic / originalPic 字符串为 FileBean 列表
|
||||
*/
|
||||
fun parsePicList() {
|
||||
val thumbUrls = pic.split(",").filter { it.isNotEmpty() }
|
||||
val originalUrls = originalPic.split(",").filter { it.isNotEmpty() }
|
||||
val list = thumbUrls.mapIndexed { index, thumbFile ->
|
||||
val originalFile = originalUrls.getOrElse(index) { thumbFile }
|
||||
FileBean(
|
||||
path = MediaUtil.fillUrl(thumbFile),
|
||||
url = thumbFile,
|
||||
originalPic = MediaUtil.fillUrl(originalFile)
|
||||
)
|
||||
}
|
||||
picList.value = list
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ class GjjManifestListViewModel : BasePageViewModel(), IOnItemClickListener {
|
||||
}
|
||||
|
||||
R.id.tv_details -> {
|
||||
GjjManifestDetailsActivity.start(DevUtils.getTopActivity(), bean.mfId)
|
||||
GjjManifestDetailsActivity.start(DevUtils.getTopActivity(), bean.mfId, bean.pic, bean.originalPic)
|
||||
}
|
||||
|
||||
R.id.tv_delete -> {
|
||||
|
||||
@@ -129,7 +129,7 @@ class IntArrSupplementInfoViewModel : BaseViewModel() {
|
||||
consignorPNum = formBean.consignorPNum,
|
||||
consignorAddress = formBean.consignorAddress,
|
||||
// 危险品信息
|
||||
dgrContactMame = formBean.dgrContactMame,
|
||||
dgrContactName = formBean.dgrContactName,
|
||||
dgrContactNumber = formBean.dgrContactNumber,
|
||||
unNumber = formBean.unNumber
|
||||
)
|
||||
|
||||
@@ -173,13 +173,23 @@ class IntImpAccidentVisaEditViewModel : BaseViewModel(), IOnItemClickListener {
|
||||
|
||||
fun onFlightDateInputComplete() {
|
||||
lastQueriedFlight = ""
|
||||
clearFlightInfo()
|
||||
queryFlightIfReady()
|
||||
}
|
||||
|
||||
fun onFlightNoInputComplete() {
|
||||
lastQueriedFlight = ""
|
||||
clearFlightInfo()
|
||||
queryFlightIfReady()
|
||||
}
|
||||
|
||||
private fun clearFlightInfo() {
|
||||
val b = dataBean.value ?: GjAccidentVisaEditBean()
|
||||
b.dep = ""
|
||||
b.dest = ""
|
||||
dataBean.value = b
|
||||
}
|
||||
|
||||
private fun queryFlightIfReady() {
|
||||
val bean = dataBean.value ?: return
|
||||
val fdate = bean.fdate
|
||||
@@ -228,11 +238,39 @@ class IntImpAccidentVisaEditViewModel : BaseViewModel(), IOnItemClickListener {
|
||||
// 保存
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 校验运单号格式
|
||||
* 规则:纯数字,固定11位,后8位中前7位 mod 7 == 最后一位
|
||||
* 返回 true 表示校验失败(有错误)
|
||||
*/
|
||||
private fun verifyWaybillNo(wbNo: String?): Boolean {
|
||||
if (wbNo.isNullOrEmpty()) return false
|
||||
if (wbNo.length != 11) {
|
||||
showToast("运单号必须为11位数字")
|
||||
return true
|
||||
}
|
||||
if (!wbNo.all { it.isDigit() }) {
|
||||
showToast("运单号必须为纯数字")
|
||||
return true
|
||||
}
|
||||
val last8 = wbNo.substring(3)
|
||||
val first7ofLast8 = last8.substring(0, 7).toLong()
|
||||
val lastDigit = last8.last().toString().toInt()
|
||||
if (first7ofLast8 % 7 != lastDigit.toLong()) {
|
||||
showToast("运单号校验位不正确")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun onSaveClick() {
|
||||
val bean = dataBean.value ?: return
|
||||
if (bean.fdate.verifyNullOrEmpty("请输入航班日期")) return
|
||||
if (bean.fno.verifyNullOrEmpty("请输入航班号")) return
|
||||
if (bean.wbNo.verifyNullOrEmpty("请输入运单号")) return
|
||||
if (verifyWaybillNo(bean.wbNo)) return
|
||||
if (bean.dep.verifyNullOrEmpty("请先填写航班信息(始发站不能为空)")) return
|
||||
if (bean.dest.verifyNullOrEmpty("请先填写航班信息(目的站不能为空)")) return
|
||||
|
||||
(rv?.commonAdapter()?.items ?: emptyList())
|
||||
.asFlow()
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.lukouguoji.gjj.activity.GjjManifestAddActivity
|
||||
import com.lukouguoji.gjj.activity.IntImpManifestSubEditActivity
|
||||
import com.lukouguoji.gjj.holder.IntImpManifestViewHolder
|
||||
import com.lukouguoji.module_base.base.BasePageViewModel
|
||||
import com.lukouguoji.module_base.bean.GjjHaWb
|
||||
import com.lukouguoji.module_base.bean.GjjManifest
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.common.ConstantEvent
|
||||
@@ -121,18 +122,6 @@ class IntImpManifestViewModel : BasePageViewModel() {
|
||||
// ========== 分单管理模式 ==========
|
||||
val isSubManagementMode = MutableLiveData(false)
|
||||
|
||||
init {
|
||||
// 监听全选状态,自动更新所有列表项(联动子列表)
|
||||
isAllChecked.observeForever { checked ->
|
||||
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjManifest> ?: return@observeForever
|
||||
list.forEach {
|
||||
it.checked.set(checked)
|
||||
it.haWbList?.forEach { sub -> sub.checked.set(checked) }
|
||||
}
|
||||
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 适配器配置 ==========
|
||||
val itemViewHolder = IntImpManifestViewHolder::class.java
|
||||
val itemLayoutId = R.layout.item_int_imp_manifest
|
||||
@@ -312,7 +301,7 @@ class IntImpManifestViewModel : BasePageViewModel() {
|
||||
message = "确定要删除运单号 ${bean.getWaybillNo()} 的舱单吗?",
|
||||
title = "提示"
|
||||
) {
|
||||
doDeleteByIds(listOf(bean.mfId))
|
||||
doDelete(listOf(bean), emptyList())
|
||||
}.show()
|
||||
}
|
||||
|
||||
@@ -321,37 +310,67 @@ class IntImpManifestViewModel : BasePageViewModel() {
|
||||
*/
|
||||
fun onDeleteClick() {
|
||||
val list = pageModel.rv?.commonAdapter()?.items as? List<GjjManifest> ?: return
|
||||
val selectedItems = list.filter { it.isSelected }
|
||||
|
||||
if (selectedItems.isEmpty()) {
|
||||
val selectedManifests = list.filter { it.isSelected }
|
||||
val selectedHawbs = list.flatMap { it.haWbList?.filter { hawb -> hawb.isSelected } ?: emptyList() }
|
||||
|
||||
if (selectedManifests.isEmpty() && selectedHawbs.isEmpty()) {
|
||||
showToast("请选择要删除的记录")
|
||||
return
|
||||
}
|
||||
|
||||
// 构建确认提示信息
|
||||
val msgParts = mutableListOf<String>()
|
||||
if (selectedManifests.isNotEmpty()) {
|
||||
msgParts.add("${selectedManifests.size} 条主单")
|
||||
}
|
||||
if (selectedHawbs.isNotEmpty()) {
|
||||
msgParts.add("${selectedHawbs.size} 条分单")
|
||||
}
|
||||
|
||||
ConfirmDialogModel(
|
||||
message = "确定要删除选中的 ${selectedItems.size} 条舱单吗?",
|
||||
message = "确定要删除选中的 ${msgParts.joinToString("和")} 吗?",
|
||||
title = "批量删除确认"
|
||||
) {
|
||||
doDeleteByIds(selectedItems.map { it.mfId })
|
||||
doDelete(selectedManifests, selectedHawbs)
|
||||
}.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行批量删除(统一接口,请求体为 mfId 数组)
|
||||
* 执行删除:先删分单,再删主单(串行执行)
|
||||
*/
|
||||
private fun doDeleteByIds(mfIds: List<Long>) {
|
||||
launchLoadingCollect({ NetApply.api.gjjManifestDeleteBatch(mfIds.toRequestBody()) }) {
|
||||
onSuccess = {
|
||||
if (it.verifySuccess()) {
|
||||
showToast("删除成功")
|
||||
isAllChecked.value = false
|
||||
viewModelScope.launch {
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
||||
private fun doDelete(manifests: List<GjjManifest>, hawbs: List<GjjHaWb>) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
showLoading()
|
||||
|
||||
// 第一步:删除分单(如果有选中的分单)
|
||||
if (hawbs.isNotEmpty()) {
|
||||
val hawbResult = NetApply.api.intImpManifestDeleteHawb(hawbs.toRequestBody())
|
||||
if (!hawbResult.verifySuccess()) {
|
||||
showToast(hawbResult.msg.noNull("分单删除失败"))
|
||||
refresh()
|
||||
return@launch
|
||||
}
|
||||
refresh()
|
||||
} else {
|
||||
showToast(it.msg.noNull("删除失败"))
|
||||
}
|
||||
|
||||
// 第二步:删除主单(分单删除成功后才执行)
|
||||
if (manifests.isNotEmpty()) {
|
||||
val manifestResult = NetApply.api.intImpManifestDeleteManifest(manifests.toRequestBody())
|
||||
if (!manifestResult.verifySuccess()) {
|
||||
showToast(manifestResult.msg.noNull("分单已删除,但主单删除失败"))
|
||||
refresh()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
showToast("删除成功")
|
||||
isAllChecked.value = false
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
||||
} catch (e: Exception) {
|
||||
showToast("删除失败:${e.message}")
|
||||
} finally {
|
||||
dismissLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.lukouguoji.gjj.viewModel
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.lukouguoji.gjj.R
|
||||
import com.lukouguoji.gjj.activity.IntImpQueryEditActivity
|
||||
@@ -71,6 +72,20 @@ class IntImpQueryViewModel : BasePageViewModel(), IOnItemClickListener {
|
||||
val businessType = MutableLiveData("")
|
||||
val goodsCn = MutableLiveData("")
|
||||
|
||||
// 是否有筛选条件(任意一个非空则为 true)
|
||||
val hasFilter: MediatorLiveData<Boolean> = MediatorLiveData<Boolean>().apply {
|
||||
val update = { _: Any? ->
|
||||
value = listOf(spCode, flightNo, origin, awbType, businessType, goodsCn)
|
||||
.any { !it.value.isNullOrEmpty() }
|
||||
}
|
||||
addSource(spCode, update)
|
||||
addSource(flightNo, update)
|
||||
addSource(origin, update)
|
||||
addSource(awbType, update)
|
||||
addSource(businessType, update)
|
||||
addSource(goodsCn, update)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 方法区
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
5
module_gjj/src/main/res/drawable/bg_red_dot.xml
Normal file
5
module_gjj/src/main/res/drawable/bg_red_dot.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="#F44336" />
|
||||
</shape>
|
||||
@@ -432,6 +432,25 @@
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/tv_manifest_details_label_no_mi"
|
||||
android:text="交接图片:" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_pic"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
title='@{"名称"}'
|
||||
titleLength="@{5}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.dataBean.dgrContactMame}' />
|
||||
value='@={viewModel.dataBean.dgrContactName}' />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||
android:layout_width="0dp"
|
||||
|
||||
@@ -323,6 +323,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
@@ -332,13 +333,14 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text='@{viewModel.isDetailMode ? "图片" : "上传图像"}'
|
||||
android:textColor="@color/text_gray"
|
||||
completeSpace="@{5}" />
|
||||
completeSpace="@{6}" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="10dp"
|
||||
android:overScrollMode="never"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
title='@{"业务类型"}'
|
||||
titleLength="@{5}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@{viewModel.dataBean.businessType}'
|
||||
value='@{viewModel.dataBean.businessName}'
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
|
||||
|
||||
<variable
|
||||
@@ -114,13 +115,26 @@
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/img_search" />
|
||||
|
||||
<ImageView
|
||||
<FrameLayout
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:onClick="@{()-> viewModel.filterClick()}"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/img_filter" />
|
||||
android:layout_marginLeft="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:onClick="@{()-> viewModel.filterClick()}"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/img_filter" />
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_gravity="top|end"
|
||||
android:background="@drawable/bg_red_dot"
|
||||
android:visibility="@{viewModel.hasFilter ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
20
module_gjj/src/main/res/layout/item_gjj_manifest_pic.xml
Normal file
20
module_gjj/src/main/res/layout/item_gjj_manifest_pic.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="bean"
|
||||
type="com.lukouguoji.module_base.bean.FileBean" />
|
||||
|
||||
</data>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_thumbnail"
|
||||
loadImage="@{bean.path}"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
</layout>
|
||||
Reference in New Issue
Block a user