fix: 国际进港舱单编辑页下拉框回填及移除404接口
- 使用 DictUtils checkedValue 机制回填编辑模式下拉框选中项 - 移除已404的 searchCargoType 接口调用 - PadDataLayoutNew 增加 updateSpinnerSilently 防止 adapter 重建覆盖值 - CLAUDE.md 补充编辑表单 SPINNER 回填规范 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
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` 置顶排序)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 开发检查清单
|
## 开发检查清单
|
||||||
|
|
||||||
### 新页面开发必做
|
### 新页面开发必做
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class PadDataLayoutNew : FrameLayout {
|
|||||||
|
|
||||||
et.hint = value
|
et.hint = value
|
||||||
tv.hint = value
|
tv.hint = value
|
||||||
bindAdapter(spinner, list, hint)
|
updateSpinnerSilently { bindAdapter(spinner, list, hint) }
|
||||||
}
|
}
|
||||||
|
|
||||||
var required = false
|
var required = false
|
||||||
@@ -106,11 +106,29 @@ class PadDataLayoutNew : FrameLayout {
|
|||||||
tvM.visibility = if (value) VISIBLE else INVISIBLE
|
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>()
|
var list = emptyList<KeyValue>()
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
bindAdapter(spinner, value, hint)
|
updateSpinnerSilently { bindAdapter(spinner, value, hint) }
|
||||||
onValueSet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var icon: Any? = null
|
var icon: Any? = null
|
||||||
@@ -183,12 +201,7 @@ class PadDataLayoutNew : FrameLayout {
|
|||||||
et.doOnTextChanged { text, _, _, _ ->
|
et.doOnTextChanged { text, _, _, _ ->
|
||||||
value = text.toString()
|
value = text.toString()
|
||||||
}
|
}
|
||||||
bindOnSelected(spinner, object : IOnSpinnerSelected {
|
bindOnSelected(spinner, spinnerCallback)
|
||||||
override fun onSelected(position: Int) {
|
|
||||||
value = list.getOrNull(position)?.value ?: ""
|
|
||||||
refreshCallBack?.invoke()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 监听输入框焦点变化
|
// 监听输入框焦点变化
|
||||||
com.lukouguoji.module_base.adapter.setOnFocusChangeListener(
|
com.lukouguoji.module_base.adapter.setOnFocusChangeListener(
|
||||||
et, object : IOnFocusChangeListener {
|
et, object : IOnFocusChangeListener {
|
||||||
|
|||||||
@@ -153,51 +153,13 @@ class GjjManifestAddViewModel : BaseViewModel() {
|
|||||||
val specialCodeList = MutableLiveData<List<KeyValue>>()
|
val specialCodeList = MutableLiveData<List<KeyValue>>()
|
||||||
val specialCode = MutableLiveData("")
|
val specialCode = MutableLiveData("")
|
||||||
|
|
||||||
// 货物类型
|
// 货物类型(无下拉列表,仅用于编辑模式回传)
|
||||||
val goodsTypeList = MutableLiveData<List<KeyValue>>()
|
|
||||||
val goodsType = MutableLiveData("")
|
val goodsType = MutableLiveData("")
|
||||||
|
|
||||||
// 运单类型
|
// 运单类型
|
||||||
val waybillTypeList = MutableLiveData<List<KeyValue>>()
|
val waybillTypeList = MutableLiveData<List<KeyValue>>()
|
||||||
val waybillType = MutableLiveData("")
|
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获取参数)
|
* 初始化(从Intent获取参数)
|
||||||
*/
|
*/
|
||||||
@@ -235,6 +197,71 @@ class GjjManifestAddViewModel : BaseViewModel() {
|
|||||||
loadManifestFromImportBean(bean)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user