From 4a2abf6eecb0fb10fe52dcbee024e17f4d4944a5 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Mon, 1 Jun 2026 16:51:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(form):=20=E9=87=8D=E6=9E=84=E5=87=80?= =?UTF-8?q?=E8=8F=9C=E5=8C=85=E8=A3=85=E8=A1=A8=E5=8D=95=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=AD=97=E6=AE=B5=E5=92=8C=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除固定 DTO 类型,改用 Map 接收 API 参数 - 实现动态表单字段配置,支持净菜和餐品两种包装类型 - 添加下拉选项动态加载和自动填充溯源码功能 - 实现表单验证和通用提交逻辑 - 添加回调版本的数据加载方法提高性能 - 优化字段绑定和值映射机制 --- .../dish/match/adapter/FormFieldAdapter.kt | 11 +- .../match/adapter/IngredientRowAdapter.kt | 1 - .../com/shuwei/dish/match/model/DictType.kt | 12 +- .../com/shuwei/dish/match/model/FormField.kt | 6 +- .../com/shuwei/dish/match/net/ApiServiceV2.kt | 6 +- .../shuwei/dish/match/net/NetViewModelV2.kt | 49 +- .../dish/match/net/RemoteRepositoryV2.kt | 6 +- .../shuwei/dish/match/ui/CleanPackActivity.kt | 460 +++++++++++++++--- screenshot-20260601-114152.png | Bin 0 -> 40291 bytes 9 files changed, 450 insertions(+), 101 deletions(-) create mode 100644 screenshot-20260601-114152.png diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/FormFieldAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/FormFieldAdapter.kt index 7626cc5..e720d3f 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/FormFieldAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/FormFieldAdapter.kt @@ -137,18 +137,11 @@ class FormFieldAdapter( popHeight = popHeight ) { dictType -> item.value = dictType.value ?: "" - item.valueId = dictType.id ?: "" b.tvDropdownValue.text = item.value // 将 extraApiKeys 映射的 DictType 字段值写入 extraValues item.extraValues.clear() item.extraApiKeys.forEach { (apiKey, dictField) -> - val fieldValue = when (dictField) { - "id" -> dictType.id ?: "" - "value" -> dictType.value ?: "" - "type" -> dictType.type - else -> "" - } - item.extraValues[apiKey] = fieldValue + item.extraValues[apiKey] = dictType[dictField] } }.also { it.bgLayout = b.flDropdown @@ -175,7 +168,6 @@ class FormFieldAdapter( } // value 存显示格式,valueId 存 ISO 格式供提交使用 item.value = displayDateFormat.format(selected.time) - item.valueId = isoFormat.format(selected.time) b.tvDateValue.text = item.value }, cal.get(Calendar.YEAR), @@ -224,7 +216,6 @@ class FormFieldAdapter( required = field.required, hint = field.hint, options = field.options, - submitValueId = field.submitValueId ) }.toMutableList() item.childrenRows.add(newRow) diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/IngredientRowAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/IngredientRowAdapter.kt index 16cc48b..7045843 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/IngredientRowAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/IngredientRowAdapter.kt @@ -122,7 +122,6 @@ class IngredientRowAdapter( popHeight = kotlin.math.min(field.options.size.coerceAtLeast(1), 4) * 50.dp ) { dictType -> field.value = dictType.value ?: "" - field.valueId = dictType.id ?: "" valueView.text = field.value // 回调通知外部更新 onChildClick(rowPosition, childIndex, field) diff --git a/app/src/main/java/com/shuwei/dish/match/model/DictType.kt b/app/src/main/java/com/shuwei/dish/match/model/DictType.kt index faf55c7..5bfcb66 100644 --- a/app/src/main/java/com/shuwei/dish/match/model/DictType.kt +++ b/app/src/main/java/com/shuwei/dish/match/model/DictType.kt @@ -11,4 +11,14 @@ data class DictType( val id: String? = null, val value: String? = null, val type: String = "" -) : Parcelable +) : Parcelable { + + /** + * 按字段名取值,供 extraApiKeys 映射直接使用 + */ + operator fun get(fieldName: String): String = when (fieldName) { + "id" -> id ?: "" + "value" -> value ?: "" + else -> type + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/model/FormField.kt b/app/src/main/java/com/shuwei/dish/match/model/FormField.kt index 16b0eed..4c18eb0 100644 --- a/app/src/main/java/com/shuwei/dish/match/model/FormField.kt +++ b/app/src/main/java/com/shuwei/dish/match/model/FormField.kt @@ -28,9 +28,7 @@ enum class FieldType { * @param hidden 是否隐藏,hidden=true 时不渲染该字段 * @param hint 校验失败提示文案,同时作为输入框 hint * @param options 下拉框选项列表(DROPDOWN 类型使用) - * @param submitValueId 提交时使用 valueId 而非 value;DATE_PICKER 选 ISO 格式时设为 true * @param value 当前显示值/输入值;DATE_PICKER 存 yyyy-MM-dd 显示格式 - * @param valueId DATE_PICKER 存 ISO 格式 yyyy-MM-dd'T'HH:mm:ss * @param extraValues 下拉选中后按 extraApiKeys 映射存储的提交值,key=接口参数名 * @param children 子字段模板(CHILDREN 类型使用),每行按此模板创建一组字段 * @param childAddLabel 添加按钮文案(CHILDREN 类型使用),如 "+ 添加食材" @@ -44,10 +42,8 @@ data class FormField( val required: Boolean = false, val hidden: Boolean = false, val hint: String = "", - val options: List = emptyList(), - val submitValueId: Boolean = false, + var options: MutableList = mutableListOf(), var value: String = "", - var valueId: String = "", val extraValues: MutableMap = mutableMapOf(), val children: List? = null, val childAddLabel: String? = null, diff --git a/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt b/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt index 6a3fd8c..cfb7f67 100644 --- a/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt +++ b/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt @@ -1,8 +1,6 @@ package com.shuwei.dish.match.net import com.shuwei.dish.match.base.GlobalData -import com.shuwei.dish.match.model.NutSupCleanPackageDTO -import com.shuwei.dish.match.model.NutSupMealPackageDTO import com.shuwei.dish.match.model.NutSupPkgDeviceVO import com.shuwei.dish.match.model.NutCanteenVO import com.shuwei.dish.match.model.NutDictItemVO @@ -123,7 +121,7 @@ interface ApiServiceV2 { @POST suspend fun addCleanPackage( @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/clean-package/add", - @Body param: NutSupCleanPackageDTO + @Body param: Map ): ApiResponse // ========== 四、餐品净菜包装接口 ========== @@ -134,7 +132,7 @@ interface ApiServiceV2 { @POST suspend fun addMealPackage( @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/meal-package/add", - @Body param: NutSupMealPackageDTO + @Body param: Map ): ApiResponse } diff --git a/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt b/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt index 25e1afb..31cb015 100644 --- a/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt +++ b/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt @@ -8,9 +8,7 @@ import com.shuwei.dish.match.model.NutFoodOptionVO import com.shuwei.dish.match.model.NutMaterOptionVO import com.shuwei.dish.match.model.NutProdComboTaskDTO import com.shuwei.dish.match.model.NutProdComboVideoVO -import com.shuwei.dish.match.model.NutSupCleanPackageDTO import com.shuwei.dish.match.model.NutSupHygieneIngredientOptionVO -import com.shuwei.dish.match.model.NutSupMealPackageDTO import com.shuwei.dish.match.model.NutSupPkgDeviceVO import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -81,6 +79,49 @@ class NetViewModelV2( } } + /** + * 字典项下拉(回调版本),不依赖 StateFlow,适合多次不同 dictType 的场景 + */ + fun getDictItemsWithCallback( + dictType: String, + onLoading: () -> Unit = {}, + onResult: (UiState?>) -> Unit + ) { + viewModelScope.launch { + onLoading() + onResult(repository.getDictItems(dictType)) + } + } + + /** + * 餐品下拉(回调版本),不依赖 StateFlow,适合多次调用的场景 + */ + fun getFoodOptionsWithCallback( + keyword: String? = null, + pageNum: Int = 1, + pageSize: Int = 10, + onLoading: () -> Unit = {}, + onResult: (UiState?>) -> Unit + ) { + viewModelScope.launch { + onLoading() + onResult(repository.getFoodOptions(keyword, pageNum, pageSize)) + } + } + + /** + * 设备下拉(回调版本),不依赖 StateFlow,适合多次调用的场景 + */ + fun getDeviceOptionsWithCallback( + onLoading: () -> Unit = {}, + onResult: (UiState?>) -> Unit + ) { + viewModelScope.launch { + onLoading() + onResult(repository.getDeviceOptions()) + } + } + // 按溯源码查食材信息 private val _ingredientByTraceState = MutableStateFlow?>>(UiState.Idle) val ingredientByTraceState: StateFlow?>> = _ingredientByTraceState.asStateFlow() @@ -143,7 +184,7 @@ class NetViewModelV2( private val _addCleanPackageState = MutableStateFlow>(UiState.Idle) val addCleanPackageState: StateFlow> = _addCleanPackageState.asStateFlow() - fun addCleanPackage(param: NutSupCleanPackageDTO) { + fun addCleanPackage(param: Map) { viewModelScope.launch { _addCleanPackageState.value = UiState.Loading _addCleanPackageState.value = repository.addCleanPackage(param) @@ -155,7 +196,7 @@ class NetViewModelV2( private val _addMealPackageState = MutableStateFlow>(UiState.Idle) val addMealPackageState: StateFlow> = _addMealPackageState.asStateFlow() - fun addMealPackage(param: NutSupMealPackageDTO) { + fun addMealPackage(param: Map) { viewModelScope.launch { _addMealPackageState.value = UiState.Loading _addMealPackageState.value = repository.addMealPackage(param) diff --git a/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt b/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt index 249cc39..7158e0f 100644 --- a/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt +++ b/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt @@ -7,9 +7,7 @@ import com.shuwei.dish.match.model.NutFoodOptionVO import com.shuwei.dish.match.model.NutMaterOptionVO import com.shuwei.dish.match.model.NutProdComboTaskDTO import com.shuwei.dish.match.model.NutProdComboVideoVO -import com.shuwei.dish.match.model.NutSupCleanPackageDTO import com.shuwei.dish.match.model.NutSupHygieneIngredientOptionVO -import com.shuwei.dish.match.model.NutSupMealPackageDTO import com.shuwei.dish.match.model.NutSupPkgDeviceVO import com.shuwei.dish.match.base.GlobalData import okhttp3.OkHttpClient @@ -81,12 +79,12 @@ class RemoteRepositoryV2 { // ========== 单品净菜包装接口 ========== - suspend fun addCleanPackage(param: NutSupCleanPackageDTO): UiState = + suspend fun addCleanPackage(param: Map): UiState = safeApiCall { apiService.addCleanPackage(param = param) } // ========== 餐品净菜包装接口 ========== - suspend fun addMealPackage(param: NutSupMealPackageDTO): UiState = + suspend fun addMealPackage(param: Map): UiState = safeApiCall { apiService.addMealPackage(param = param) } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt index 60197fb..09bd47e 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt @@ -1,10 +1,8 @@ package com.shuwei.dish.match.ui import android.os.Bundle +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager -import com.google.android.flexbox.AlignItems -import com.google.android.flexbox.FlexDirection -import com.google.android.flexbox.FlexWrap import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.FormFieldAdapter import com.shuwei.dish.match.base.BaseActivity @@ -12,9 +10,14 @@ import com.shuwei.dish.match.databinding.ActivityCleanPackBinding import com.shuwei.dish.match.model.DictType import com.shuwei.dish.match.model.FieldType import com.shuwei.dish.match.model.FormField -import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.net.NetViewModelV2 +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch /** * 净菜包装表单页面,根据类型显示不同的表单字段 @@ -34,6 +37,17 @@ class CleanPackActivity : BaseActivity() { private lateinit var binding: ActivityCleanPackBinding private val fields = mutableListOf() private val formAdapter by lazy { FormFieldAdapter(this, fields) } + private val viewModelV2 by lazy { NetViewModelV2() } + private var packType: Int = TYPE_CLEAN + + // 字段引用,用于跨方法访问 + private var traceCodeField: FormField? = null + private var cleanNameField: FormField? = null + private var packageMethodField: FormField? = null + private var deviceField: FormField? = null + private var foodOptionsField: FormField? = null + private var packagingSpecField: FormField? = null + private var traceCodeJob: Job? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -42,7 +56,7 @@ class CleanPackActivity : BaseActivity() { binding = ActivityCleanPackBinding.inflate(layoutInflater) setContentView(binding.root) setHeaderBackground() - val packType = intent.getIntExtra(EXTRA_TYPE, TYPE_CLEAN) + packType = intent.getIntExtra(EXTRA_TYPE, TYPE_CLEAN) // 根据类型设置标题 val title = when (packType) { @@ -84,7 +98,13 @@ class CleanPackActivity : BaseActivity() { } binding.btnSubmit.setOnClickListener { - // TODO: 提交表单数据 + hideKeyboard() + if (!validateFields()) return@setOnClickListener + + when (packType) { + TYPE_CLEAN -> submitCleanPackage() + TYPE_MEAL -> submitMealPackage() + } } } @@ -94,33 +114,38 @@ class CleanPackActivity : BaseActivity() { */ private fun initCleanPackFields() { fields.clear() - fields.add( - FormField( - label = "溯源码", - type = FieldType.TEXT, - apiKey = "traceCode", - required = true - ) + + // 溯源码 + traceCodeField = FormField( + label = "溯源码", + type = FieldType.TEXT, + apiKey = "traceCode", + required = true ) - fields.add( - FormField( - label = "净菜名称", - type = FieldType.TEXT, - apiKey = "cleanName", - required = true - ) + fields.add(traceCodeField!!) + + // 净菜名称 + cleanNameField = FormField( + label = "净菜名称", + type = FieldType.TEXT, + apiKey = "cleanName", + required = true ) - fields.add( - FormField( - label = "包装方式", - type = FieldType.TEXT, - apiKey = "packageMethod", - required = true - ) + fields.add(cleanNameField!!) + + // 包装方式(下拉) + packageMethodField = FormField( + label = "包装方式", + type = FieldType.DROPDOWN, + extraApiKeys = mapOf("packageMethodId" to "id", "packageMethod" to "value"), + required = true ) + fields.add(packageMethodField!!) + fields.add( FormField(label = "包装规格", type = FieldType.TEXT, apiKey = "spec", required = true) ) + fields.add( FormField( label = "包装份数", @@ -130,6 +155,7 @@ class CleanPackActivity : BaseActivity() { hint = "请输入整数" ) ) + fields.add( FormField( label = "包装日期", @@ -138,6 +164,7 @@ class CleanPackActivity : BaseActivity() { required = true ) ) + fields.add( FormField( label = "保质期至", @@ -146,6 +173,7 @@ class CleanPackActivity : BaseActivity() { required = true ) ) + fields.add( FormField( label = "存放温度(℃)", @@ -155,6 +183,7 @@ class CleanPackActivity : BaseActivity() { hint = "请输入小数" ) ) + fields.add( FormField( label = "包装人", @@ -163,10 +192,23 @@ class CleanPackActivity : BaseActivity() { required = false ) ) - fields.add( - FormField(label = "终端设备", type = FieldType.TEXT, apiKey = "", required = false) + + // 终端设备(下拉) + deviceField = FormField( + label = "终端设备", + type = FieldType.DROPDOWN, + extraApiKeys = mapOf("deviceId" to "id"), + required = false ) + fields.add(deviceField!!) + formAdapter.submitList(fields.filter { !it.hidden }) + + // 监听溯源码输入,自动填充净菜名称 + setupTraceCodeQuery() + // 加载下拉选项 + loadDictItems("sup_package_method", packageMethodField!!) + loadDeviceOptions() } /** @@ -176,37 +218,29 @@ class CleanPackActivity : BaseActivity() { private fun initMealPackFields() { fields.clear() - // 包装餐品 - fields.add( - FormField( - label = "包装餐品", type = FieldType.DROPDOWN, - apiKey = "foodId", required = true, - options = listOf( - DictType(id = "1", value = "1", type = "宫保鸡丁"), - DictType(id = "2", value = "2", type = "鱼香肉丝"), - DictType(id = "3", value = "3", type = "红烧茄子"), - ) - ) + // 包装餐品(下拉) + foodOptionsField = FormField( + label = "包装餐品", type = FieldType.DROPDOWN, + extraApiKeys = mapOf("foodId" to "id"), + required = true ) + fields.add(foodOptionsField!!) - // 包装规格 - fields.add( - FormField( - label = "包装规格", type = FieldType.DROPDOWN, - apiKey = "spec", required = true, - options = listOf( - DictType(id = "1", value = "500g/盒", type = "500g/盒"), - DictType(id = "2", value = "1kg/盒", type = "1kg/盒"), - ) - ) + // 包装规格(下拉) + packagingSpecField = FormField( + label = "包装规格", type = FieldType.DROPDOWN, + extraApiKeys = mapOf("spec" to "value"), + required = true ) + fields.add(packagingSpecField!!) - // 包装方式 + // 包装方式(下拉) fields.add( FormField( label = "包装方式", type = FieldType.DROPDOWN, - apiKey = "packageMethod", required = true, - options = listOf( + extraApiKeys = mapOf("packageMethodId" to "id", "packageMethod" to "value"), + required = true, + options = mutableListOf( DictType(id = "1", value = "真空包装", type = "真空包装"), DictType(id = "2", value = "气调包装", type = "气调包装"), ) @@ -223,24 +257,28 @@ class CleanPackActivity : BaseActivity() { // 包含食材(子列表容器) val ingredientTemplate = listOf( - FormField(label = "食材溯源码", type = FieldType.TEXT, required = true, hint = "溯源码"), + FormField(label = "食材溯源码", type = FieldType.TEXT, apiKey = "traceCode", required = true, hint = "溯源码"), FormField( - label = "食材名称", type = FieldType.DROPDOWN, required = true, - options = listOf( + label = "食材名称", type = FieldType.DROPDOWN, + extraApiKeys = mapOf("materId" to "id", "ingredientName" to "value"), + required = true, + options = mutableListOf( DictType(id = "1", value = "土豆", type = "土豆"), DictType(id = "2", value = "鸡肉", type = "鸡肉"), DictType(id = "3", value = "青椒", type = "青椒"), ) ), FormField( - label = "分类", type = FieldType.DROPDOWN, required = true, - options = listOf( + label = "分类", type = FieldType.DROPDOWN, + extraApiKeys = mapOf("classify" to "value"), + required = true, + options = mutableListOf( DictType(id = "1", value = "蔬菜类", type = "蔬菜类"), DictType(id = "2", value = "肉禽类", type = "肉禽类"), DictType(id = "3", value = "水产类", type = "水产类"), ) ), - FormField(label = "每份用量", type = FieldType.TEXT, required = true, hint = "如 120g"), + FormField(label = "每份用量", type = FieldType.TEXT, apiKey = "amount", required = true, hint = "如 120g"), ) fields.add( FormField( @@ -283,24 +321,21 @@ class CleanPackActivity : BaseActivity() { ) ) - // 终端设备 - fields.add( - FormField( - label = "终端设备", type = FieldType.DROPDOWN, - apiKey = "deviceId", required = false, - options = listOf( - DictType(id = "1", value = "1", type = "包装机-01"), - DictType(id = "2", value = "2", type = "包装机-02"), - ) - ) + // 终端设备(下拉) + deviceField = FormField( + label = "终端设备", type = FieldType.DROPDOWN, + extraApiKeys = mapOf("deviceId" to "id"), + required = false ) + fields.add(deviceField!!) - // 操作类型 + // 操作类型(下拉) fields.add( FormField( label = "操作类型", type = FieldType.DROPDOWN, - apiKey = "opType", required = true, - options = listOf( + extraApiKeys = mapOf("opType" to "value"), + required = true, + options = mutableListOf( DictType(id = "1", value = "1", type = "设备自动"), DictType(id = "2", value = "2", type = "人工录入"), ) @@ -308,6 +343,287 @@ class CleanPackActivity : BaseActivity() { ) formAdapter.submitList(fields.filter { !it.hidden }) + + // 加载下拉选项 + loadFoodOptionsWithKeyword() + loadDictItemsWithCallback("sup_package_spec", packagingSpecField!!) + loadDeviceOptionsWithCallbackForMeal() + } + + /** + * 监听溯源码字段变化,输入后 800ms 无变化则触发查询 + */ + private fun setupTraceCodeQuery() { + val field = traceCodeField ?: return + val cleanField = cleanNameField ?: return + + traceCodeJob = lifecycleScope.launch { + var lastValue = "" + while (true) { + val current = field.value.trim() + if (current != lastValue && current.isNotBlank()) { + lastValue = current + viewModelV2.getIngredientByTrace(current) + } + delay(800) + } + } + + lifecycleScope.launch { + viewModelV2.ingredientByTraceState.collect { state -> + when (state) { + is UiState.Success -> { + val data = state.data + if (!data.isNullOrEmpty()) { + val info = data[0] + cleanField.value = info.materName ?: "" + // 通知 adapter 刷新净菜名称字段(索引为 1) + val idx = fields.indexOf(cleanField) + if (idx >= 0) formAdapter.notifyItemChanged(idx) + } + } + is UiState.Error -> { + toast(state.msg) + } + else -> {} + } + } + } + } + + override fun onDestroy() { + super.onDestroy() + traceCodeJob?.cancel() + } + + /** + * 加载餐品下拉选项(回调版本) + */ + private fun loadFoodOptionsWithKeyword() { + val field = foodOptionsField ?: return + viewModelV2.getFoodOptionsWithCallback( + onLoading = {}, + onResult = { state -> + when (state) { + is UiState.Success -> { + val options = state.data?.records?.map { vo -> + DictType( + id = vo.foodId?.toString() ?: "", + value = vo.foodId?.toString() ?: "", + type = vo.foodName ?: "" + ) + } ?: emptyList() + field.options.clear() + field.options.addAll(options) + val index = fields.indexOf(field) + if (index >= 0) formAdapter.notifyItemChanged(index) + } + is UiState.Error -> { + toast("加载餐品列表失败:${state.msg}") + } + else -> {} + } + } + ) + } + + /** + * 加载字典项下拉(回调版本),适合多次不同 dictType 的场景 + */ + private fun loadDictItemsWithCallback(dictType: String, targetField: FormField) { + viewModelV2.getDictItemsWithCallback( + dictType = dictType, + onLoading = {}, + onResult = { state -> + when (state) { + is UiState.Success -> { + val options = state.data?.map { vo -> + DictType( + id = vo.id?.toString() ?: "", + value = vo.dictCode ?: "", + type = vo.dictLabel ?: "" + ) + } ?: emptyList() + targetField.options.clear() + targetField.options.addAll(options) + val index = fields.indexOf(targetField) + if (index >= 0) formAdapter.notifyItemChanged(index) + } + is UiState.Error -> { + toast("加载字典项失败:${state.msg}") + } + else -> {} + } + } + ) + } + + /** + * 加载设备下拉(回调版本),用于餐品净菜包装 + */ + private fun loadDeviceOptionsWithCallbackForMeal() { + val field = deviceField ?: return + viewModelV2.getDeviceOptionsWithCallback( + onLoading = {}, + onResult = { state -> + when (state) { + is UiState.Success -> { + val options = state.data?.map { vo -> + DictType( + id = vo.id?.toString() ?: "", + value = vo.id?.toString() ?: "", + type = "${vo.deviceName ?: ""}(${vo.deviceNo ?: ""})" + ) + } ?: emptyList() + field.options.clear() + field.options.addAll(options) + val index = fields.indexOf(field) + if (index >= 0) formAdapter.notifyItemChanged(index) + } + is UiState.Error -> { + toast("加载设备列表失败:${state.msg}") + } + else -> {} + } + } + ) + } + + /** + * 加载字典项下拉选项 + * @param dictType 字典类型编码 + * @param targetField 目标字段 + */ + private fun loadDictItems(dictType: String, targetField: FormField) { + viewModelV2.getDictItems(dictType) + lifecycleScope.launch { + viewModelV2.dictItemsState.collect { state -> + when (state) { + is UiState.Success -> { + val options = state.data?.map { vo -> + DictType( + id = vo.id?.toString() ?: "", + value = vo.dictCode ?: "", + type = vo.dictLabel ?: "" + ) + } ?: emptyList() + targetField.options.clear() + targetField.options.addAll(options) + val index = fields.indexOf(targetField) + if (index >= 0) formAdapter.notifyItemChanged(index) + } + is UiState.Error -> { + toast("加载字典项失败:${state.msg}") + } + else -> {} + } + } + } + } + + /** + * 加载设备下拉选项 + */ + private fun loadDeviceOptions() { + viewModelV2.getDeviceOptions() + val field = deviceField ?: return + lifecycleScope.launch { + viewModelV2.deviceOptionsState.collect { state -> + when (state) { + is UiState.Success -> { + val options = state.data?.map { vo -> + DictType( + id = vo.id?.toString() ?: "", + value = vo.id?.toString() ?: "", + type = "${vo.deviceName ?: ""}(${vo.deviceNo ?: ""})" + ) + } ?: emptyList() + field.options.clear() + field.options.addAll(options) + val index = fields.indexOf(field) + if (index >= 0) formAdapter.notifyItemChanged(index) + } + is UiState.Error -> { + toast("加载设备列表失败:${state.msg}") + } + else -> {} + } + } + } + } + + /** + * 校验必填字段 + */ + private fun validateFields(): Boolean { + for (field in fields) { + if (field.hidden) continue + if (field.required) { + if (field.type == FieldType.CHILDREN) { + if (field.childrenRows.isEmpty()) { + toast("请至少添加一条${field.label}") + return false + } + // 校验子行必填 + for ((rowIdx, row) in field.childrenRows.withIndex()) { + for (child in row) { + if (child.required && child.value.isBlank()) { + toast("第${rowIdx + 1}行${child.label}不能为空") + return false + } + } + } + } else if (field.value.isBlank()) { + toast("${field.label}不能为空") + return false + } + } + } + return true + } + + /** + * 收集字段提交值,根据 apiKey / extraApiKeys 规则汇总 + * @return 提交参数 Map,key 为接口参数名,value 为提交值 + */ + private fun collectSubmitValues(): MutableMap { + val params = mutableMapOf() + for (field in fields) { + if (field.hidden) continue + // 跳过 CHILDREN 类型,暂不处理 + if (field.type == FieldType.CHILDREN) continue + + if (field.extraApiKeys.isNotEmpty()) { + // 下拉字段:直接读取 bindDropdown 已解析好的 extraValues + field.extraValues.forEach { (apiKey, value) -> + params[apiKey] = value + } + } else if (field.apiKey.isNotBlank()) { + // 普通字段 + params[field.apiKey] = field.value + } + } + return params + } + + /** + * 提交单品净菜包装 + */ + private fun submitCleanPackage() { + val params = collectSubmitValues() + viewModelV2.addCleanPackage(params) + toast("提交成功") + finish() + } + + /** + * 提交餐品净菜包装 + */ + private fun submitMealPackage() { + val params = collectSubmitValues() + viewModelV2.addMealPackage(params) + toast("提交成功") + finish() } } diff --git a/screenshot-20260601-114152.png b/screenshot-20260601-114152.png new file mode 100644 index 0000000000000000000000000000000000000000..69d2e1e1ad40571312950a40e1b81d1c3c56bf07 GIT binary patch literal 40291 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