diff --git a/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt b/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt index d664213..22c1bcb 100644 --- a/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt @@ -173,7 +173,10 @@ abstract class GoodsListActivity : BaseActivity() { SensorScaleUtils.tare() } - userViewModel.getInitInfo() + + if (this !is StoreActivity) { + userViewModel.getInitInfo() + } } override fun onResume() { diff --git a/app/src/main/java/com/sw/inbound/activity/HomeActivity.kt b/app/src/main/java/com/sw/inbound/activity/HomeActivity.kt index 2a11c1c..90ee64e 100644 --- a/app/src/main/java/com/sw/inbound/activity/HomeActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/HomeActivity.kt @@ -77,36 +77,36 @@ class HomeActivity : BaseActivity() { */ private fun initViews() { // 标题点击:跳转菜品采集页面 - binding.tvTitle.setOnClickListener { - startActivity() - } +// binding.tvTitle.setOnClickListener { +// startActivity() +// } +// +// // 时间点击:打开环境切换弹窗 +// binding.tvTime.setOnClickListener { v -> +// EnvSwitchDialog(context = this, onEnvChanged = { +// Loading.show(this) +// lifecycleScope.launch { +// ObjectBox.removeAll() +// FoodModule.init(this@HomeActivity) +// v.postDelayed({ +// Loading.dismiss() +// toast("服务重置完成") +// }, 500) +// } +// }).show() +// } - // 时间点击:打开环境切换弹窗 - binding.tvTime.setOnClickListener { v -> - EnvSwitchDialog(context = this, onEnvChanged = { - Loading.show(this) - lifecycleScope.launch { - ObjectBox.removeAll() - FoodModule.init(this@HomeActivity) - v.postDelayed({ - Loading.dismiss() - toast("服务重置完成") - }, 500) - } - }).show() - } - - // 采购单入库入口 - binding.ivOrderPurchase.setOnClickListener { - startActivity() - } - - // 自采入库入口,传入非采购单标志 - binding.ivOrderSelfProcurement.setOnClickListener { - startActivity { - putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false) - } - } +// // 采购单入库入口 +// binding.ivOrderPurchase.setOnClickListener { +// startActivity() +// } +// +// // 自采入库入口,传入非采购单标志 +// binding.ivOrderSelfProcurement.setOnClickListener { +// startActivity { +// putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false) +// } +// } // 净菜按钮点击提示 binding.btnCleanDishStore.setOnClickListener { diff --git a/app/src/main/java/com/sw/inbound/activity/StoreActivity.kt b/app/src/main/java/com/sw/inbound/activity/StoreActivity.kt index 01d3bca..5efb5c3 100644 --- a/app/src/main/java/com/sw/inbound/activity/StoreActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/StoreActivity.kt @@ -4,7 +4,6 @@ import android.os.Bundle import androidx.activity.viewModels import com.sw.inbound.GlobalData import com.sw.inbound.dialog.InboundGoodsSearchDialog -import com.sw.inbound.dialog.InboundGoodsStoreDialog import com.sw.inbound.model.response.DictType import com.sw.inbound.utils.ext.gone import com.sw.inbound.viewmodel.StoreViewModel @@ -41,17 +40,11 @@ class StoreActivity : GoodsListActivity() { } } - /** 根据 storeType 弹出对应入库弹窗,dismiss 时关闭页面 */ + /** 进入页面统一打开检索弹窗,dismiss 时关闭页面 */ private fun showEntryDialog() { - if (storeType == 1) { - InboundGoodsStoreDialog(this, null).apply { - setOnDismissListener { finish() } - }.show() - } else { - InboundGoodsSearchDialog(this).apply { - setOnDismissListener { finish() } - }.show() - } + InboundGoodsSearchDialog(this).apply { + setOnDismissListener { finish() } + }.show() } /** 预加载供应商和仓库区域列表到 GlobalData,供弹窗直接使用 */ diff --git a/app/src/main/java/com/sw/inbound/adapter/FormFieldAdapter.kt b/app/src/main/java/com/sw/inbound/adapter/FormFieldAdapter.kt index 563ced3..efaf93c 100644 --- a/app/src/main/java/com/sw/inbound/adapter/FormFieldAdapter.kt +++ b/app/src/main/java/com/sw/inbound/adapter/FormFieldAdapter.kt @@ -38,7 +38,7 @@ class FormFieldAdapter( fields: MutableList ) : BaseQuickAdapter(fields) { - private val isoFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) + private val isoFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) private val displayDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) inner class VH(val binding: ListItemFormFieldBinding) : QuickViewHolder(binding.root) @@ -125,6 +125,17 @@ class FormFieldAdapter( 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 + } }.also { it.bgLayout = b.flDropdown it.showAsDropDown(v) diff --git a/app/src/main/java/com/sw/inbound/base/BaseActivity.kt b/app/src/main/java/com/sw/inbound/base/BaseActivity.kt index c5e04e3..51ea75d 100644 --- a/app/src/main/java/com/sw/inbound/base/BaseActivity.kt +++ b/app/src/main/java/com/sw/inbound/base/BaseActivity.kt @@ -1,7 +1,7 @@ package com.sw.inbound.base import android.os.Bundle -import androidx.activity.ComponentActivity +import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat @@ -12,7 +12,7 @@ import androidx.core.view.WindowInsetsControllerCompat * - 自动隐藏状态栏和导航栏,实现全屏沉浸式体验 * - 在屏幕底部上滑时可临时唤出导航栏,松手后自动收回 */ -abstract class BaseActivity : ComponentActivity() { +abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/sw/inbound/dialog/InboundGoodsSearchDialog.kt b/app/src/main/java/com/sw/inbound/dialog/InboundGoodsSearchDialog.kt index 612f4ee..2999da0 100644 --- a/app/src/main/java/com/sw/inbound/dialog/InboundGoodsSearchDialog.kt +++ b/app/src/main/java/com/sw/inbound/dialog/InboundGoodsSearchDialog.kt @@ -33,9 +33,15 @@ class InboundGoodsSearchDialog( override fun initView() { storeActivity = getReceiptActivity() as? StoreActivity + binding.tvDialogTitle.text = if (storeActivity?.storeType == 1) "毛菜检索" else "净菜检索" binding.ivClose.setOnClickListener { dismiss() } binding.ivGoodsSearch.setOnClickListener { searchGoods() } binding.etInputGoods.addOnActionSearchListener { searchGoods() } + binding.btnAddGoods.setOnClickListener { + InboundGoodsStoreDialog(context = context, goods = null) { + dismiss() + }.show() + } binding.btnConfirm.setOnClickListener { if (list.isEmpty()) { context.toast("请搜索物品") @@ -92,9 +98,10 @@ class InboundGoodsSearchDialog( if (emptyBinding == null) { emptyBinding = LayoutEmptySearchBinding.inflate(inflater, binding.rvSearch, false) } - emptyBinding?.root?.let { layout -> - layout.setOnClickListener { layout.hideKeyboard() } - searchAdapter.stateView = layout + emptyBinding?.run { + tvContent.text = "请检查溯源码或稍后重试" + root.setOnClickListener { root.hideKeyboard() } + searchAdapter.stateView = root } } @@ -109,7 +116,7 @@ class InboundGoodsSearchDialog( if (list[position].isSelected) return@setOnItemClickListener list.forEach { it.isSelected = false } list[position].isSelected = true - submitList(list.toList()) + notifyDataSetChanged() } } } diff --git a/app/src/main/java/com/sw/inbound/dialog/InboundGoodsStoreDialog.kt b/app/src/main/java/com/sw/inbound/dialog/InboundGoodsStoreDialog.kt index 9295906..42f3969 100644 --- a/app/src/main/java/com/sw/inbound/dialog/InboundGoodsStoreDialog.kt +++ b/app/src/main/java/com/sw/inbound/dialog/InboundGoodsStoreDialog.kt @@ -46,7 +46,7 @@ class InboundGoodsStoreDialog( private var realWeight: Int = 0 - private val isoFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) + private val isoFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) /** 当前渲染的表单字段列表 */ private lateinit var fields: MutableList @@ -85,7 +85,7 @@ class InboundGoodsStoreDialog( private fun buildRawFields(): MutableList { val supplierOptions = GlobalData.inboundSupplierList.mapNotNull { opt -> val id = opt.id?.toString() ?: return@mapNotNull null - DictType(id = id, value = opt.name) + DictType(id = id, value = opt.name, type = opt.type?.toString() ?: "") } val zoneOptions = GlobalData.inboundZoneList.mapNotNull { zone -> val id = zone.id?.toString() ?: return@mapNotNull null @@ -96,26 +96,23 @@ class InboundGoodsStoreDialog( "溯源码", FieldType.TEXT, apiKey = "traceCode", - value = "", + value = goods?.traceCode ?: "", required = true, hint = "请输入溯源码" ), - FormField( - "来源类型", FieldType.DROPDOWN, apiKey = "", options = listOf( - DictType("0", "外采"), DictType("1", "自产") - ) - ), FormField( "食材名称", - FieldType.DROPDOWN, + FieldType.TEXT, apiKey = "materialName", required = true, - value = "", - hint = "请选择食材名称" + value = goods?.materName ?:"", + hint = "请输入食材名称" ), FormField( - "供应商", FieldType.DROPDOWN, apiKey = "supplierId", required = true, - hint = "请选择供应商", options = supplierOptions, submitValueId = true + "供应商", FieldType.DROPDOWN, + extraApiKeys = mapOf("supplierId" to "id", "supplierType" to "type"), + required = true, + hint = "请选择供应商", options = supplierOptions ), FormField( "入库规格", @@ -140,13 +137,11 @@ class InboundGoodsStoreDialog( submitValueId = true ), FormField( - "存放区域", - FieldType.DROPDOWN, - apiKey = "zoneId", + "存放区域", FieldType.DROPDOWN, + extraApiKeys = mapOf("zoneId" to "id", "storageArea" to "value"), required = true, hint = "请选择存放区域", - options = zoneOptions, - submitValueId = true + options = zoneOptions ), FormField( "操作人", @@ -166,10 +161,6 @@ class InboundGoodsStoreDialog( * expiryDate, storageTemp */ private fun buildCleanFields(): MutableList { - val vegTypes = goods?.vegTypeList() ?: emptyList() - val cleanTypeOptions = vegTypes.mapIndexed { index, type -> - DictType(id = index.toString(), value = type) - } val zoneOptions = GlobalData.inboundZoneList.mapNotNull { zone -> val id = zone.id?.toString() ?: return@mapNotNull null DictType(id = id, value = zone.zoneName) @@ -185,30 +176,23 @@ class InboundGoodsStoreDialog( ), FormField( "食材名称", - FieldType.FIXED, + type = if (goods?.materName.isNullOrEmpty()) FieldType.TEXT else FieldType.FIXED, apiKey = "ingredientName", required = true, value = goods?.materName ?: "" ), FormField( "净菜类型", - FieldType.FIXED, - required = true, + type = if (goods?.materName.isNullOrEmpty()) FieldType.TEXT else FieldType.FIXED, apiKey = "cleanType", + value = goods?.vegTypes ?: "" ), FormField( "入库规格", - FieldType.DROPDOWN, + FieldType.TEXT, apiKey = "spec", required = true, - hint = "请选择入库规格", - options = listOf( - DictType("0", "袋装/500g"), - DictType("1", "袋装/300g"), - DictType("1", "瓶装/800ml"), - DictType("1", "瓶装/500ml"), - DictType("1", "其它"), - ), + hint = "请输入入库规格" ), FormField( "入库数量", @@ -217,6 +201,13 @@ class InboundGoodsStoreDialog( required = true, hint = "请输入入库数量" ), + FormField( + "操作时间", + FieldType.DATE_PICKER, + apiKey = "inboundTime", + hint = "请选择操作时间", + submitValueId = true + ), FormField( "保质期至", FieldType.DATE_PICKER, @@ -225,13 +216,11 @@ class InboundGoodsStoreDialog( submitValueId = true ), FormField( - "存放区域", - FieldType.DROPDOWN, - apiKey = "zoneId", + "存放区域", FieldType.DROPDOWN, + extraApiKeys = mapOf("zoneId" to "id", "storageArea" to "value"), required = true, hint = "请选择存放区域", - options = zoneOptions, - submitValueId = true + options = zoneOptions ), FormField( "存放温度(℃)", @@ -257,7 +246,10 @@ class InboundGoodsStoreDialog( } private fun setupButtons() { - binding.btnCancel.setOnClickListener { dismiss() } + binding.btnCancel.setOnClickListener { + dismiss() + if (goods == null) storeActivity?.finish() + } binding.btnConfirm.setOnClickListener { confirmInputData() } binding.tvTitle.run { text = if (storeType == 1) "毛菜入库" else "净菜入库" @@ -304,21 +296,25 @@ class InboundGoodsStoreDialog( } /** - * 将 fields 转为接口 Map,规则: - * - apiKey 为空的字段跳过 - * - submitValueId=true 时取 valueId,否则取 value - * - 空字符串跳过(可选字段无值不传) - * - quantity → Int,zoneId/supplierId → Long,storageTemp → Double,其余 → String + * 将 fields 转为接口 Map: + * - extraApiKeys 非空时从 extraValues 读取多字段值 + * - apiKey 非空时按原逻辑提交单字段值 + * - quantity → Int,storageTemp → Double,其余 → String */ private fun buildParamMap(): MutableMap { val map = mutableMapOf() for (field in fields) { + if (field.extraApiKeys.isNotEmpty()) { + field.extraValues.forEach { (apiKey, raw) -> + if (raw.isNotBlank()) map[apiKey] = raw + } + continue + } if (field.apiKey.isBlank()) continue val raw = if (field.submitValueId) field.valueId else field.value if (raw.isBlank()) continue val converted: Any = when (field.apiKey) { "quantity" -> raw.toIntOrNull() ?: continue - "zoneId", "supplierId" -> raw.toLongOrNull() ?: continue "storageTemp" -> raw.toDoubleOrNull() ?: continue else -> raw } @@ -331,10 +327,6 @@ class InboundGoodsStoreDialog( val param = buildParamMap() // weight 来自秤,不在表单中,额外注入 param["weight"] = realWeight / 1000.0 - // supplierType 从 GlobalData 查找,不在表单中 - val supplierId = param["supplierId"]?.toString() - val supplierOpt = GlobalData.inboundSupplierList.find { it.id?.toString() == supplierId } - param["supplierType"] = (supplierOpt?.type ?: 1) Loading.show(storeActivity ?: return) storeActivity?.storeViewModel?.submitRawInbound(param) { success, msg -> diff --git a/app/src/main/java/com/sw/inbound/model/FormField.kt b/app/src/main/java/com/sw/inbound/model/FormField.kt index 9a033af..22cfbdc 100644 --- a/app/src/main/java/com/sw/inbound/model/FormField.kt +++ b/app/src/main/java/com/sw/inbound/model/FormField.kt @@ -20,24 +20,28 @@ enum class FieldType { * 动态表单字段数据模型 * @param label 字段标签名称 * @param type 字段类型,决定渲染哪种输入控件 - * @param apiKey 对应接口的字段名,空字符串表示该字段不提交(如来源类型) + * @param apiKey 非下拉字段对应的接口参数名,空字符串表示不提交 + * @param extraApiKeys 下拉字段的多接口参数映射,key=接口参数名,value=DictType 字段名(id/value/type) * @param required 是否必填,提交时校验 * @param hidden 是否隐藏,hidden=true 时不渲染该字段 * @param hint 校验失败提示文案,同时作为输入框 hint * @param options 下拉框选项列表(DROPDOWN 类型使用) - * @param submitValueId 提交时使用 valueId 而非 value;DROPDOWN 选 id、DATE_PICKER 选 ISO 格式时设为 true + * @param submitValueId 提交时使用 valueId 而非 value;DATE_PICKER 选 ISO 格式时设为 true * @param value 当前显示值/输入值;DATE_PICKER 存 yyyy-MM-dd 显示格式 - * @param valueId 下拉框选中项的 id;DATE_PICKER 存 ISO 格式 yyyy-MM-dd'T'HH:mm:ss + * @param valueId DATE_PICKER 存 ISO 格式 yyyy-MM-dd'T'HH:mm:ss + * @param extraValues 下拉选中后按 extraApiKeys 映射存储的提交值,key=接口参数名 */ data class FormField( val label: String, val type: FieldType, val apiKey: String = "", + val extraApiKeys: Map = emptyMap(), val required: Boolean = false, val hidden: Boolean = false, val hint: String = "", val options: List = emptyList(), val submitValueId: Boolean = false, var value: String = "", - var valueId: String = "" + var valueId: String = "", + val extraValues: MutableMap = mutableMapOf() ) diff --git a/app/src/main/java/com/sw/inbound/model/response/DictType.kt b/app/src/main/java/com/sw/inbound/model/response/DictType.kt index ddf8557..ada2d13 100644 --- a/app/src/main/java/com/sw/inbound/model/response/DictType.kt +++ b/app/src/main/java/com/sw/inbound/model/response/DictType.kt @@ -8,6 +8,7 @@ import kotlinx.parcelize.Parcelize */ @Parcelize data class DictType( - val id: String?=null, - val value: String?=null + val id: String? = null, + val value: String? = null, + val type: String = "" ) : Parcelable diff --git a/app/src/main/java/com/sw/inbound/network/api/InboundApiService.kt b/app/src/main/java/com/sw/inbound/network/api/InboundApiService.kt index 10125e9..e87a669 100644 --- a/app/src/main/java/com/sw/inbound/network/api/InboundApiService.kt +++ b/app/src/main/java/com/sw/inbound/network/api/InboundApiService.kt @@ -39,12 +39,12 @@ interface InboundApiService { /** 毛菜入库提交,入参为 Map 以便动态组装字段 */ @POST("nutrition/neglect/inbound/raw-inbound") suspend fun submitRawInbound( - @Body param: Map + @Body param: Map ): InboundApiResponse /** 净菜入库提交,入参为 Map 以便动态组装字段 */ @POST("nutrition/neglect/inbound/clean-inbound") suspend fun submitCleanInbound( - @Body param: Map + @Body param: Map ): InboundApiResponse } diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 17791c3..eba773b 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -116,7 +116,7 @@ android:orientation="horizontal"> -