feat(activity): SubmitFoodActivity 支持制作中菜品调料历史数据加载与新增叠加

- SeasoningWeightAdapter 改继承 BaseQuickAdapter
- item_seasoning_weight 左右间距改为 32dp
- isCooking=true 时从数据库查询 goodsList 并预填充调料历史数据
- refreshAdapterByName 叠加历史用量,修复新增用量覆盖历史数据问题
- 返回键拦截,弹窗提示保存数据

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 11:47:13 +08:00
co-authored by Claude Sonnet 4.6
parent f25108360b
commit bce9675b0f
4 changed files with 107 additions and 53 deletions
@@ -1,8 +1,10 @@
package com.shuwei.dish.match.adapter package com.shuwei.dish.match.adapter
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.databinding.ItemSeasoningWeightBinding import com.shuwei.dish.match.databinding.ItemSeasoningWeightBinding
/** /**
@@ -10,7 +12,7 @@ import com.shuwei.dish.match.databinding.ItemSeasoningWeightBinding
* 用于 SubmitFoodActivity 展示各调料槽位的实时用量 * 用于 SubmitFoodActivity 展示各调料槽位的实时用量
* 同名调料已在外部聚合,adapter 按 goodsName 作为唯一标识 * 同名调料已在外部聚合,adapter 按 goodsName 作为唯一标识
*/ */
class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>() { class SeasoningWeightAdapter : BaseQuickAdapter<SeasoningWeightAdapter.Item, SeasoningWeightAdapter.VH>() {
/** 调料展示数据项,key = goodsName(同名调料已聚合) */ /** 调料展示数据项,key = goodsName(同名调料已聚合) */
data class Item( data class Item(
@@ -20,19 +22,15 @@ class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>()
val useWeight: Double val useWeight: Double
) )
val items = mutableListOf<Item>()
/** /**
* 更新单个 item,按 goodsName 匹配;不存在则追加 * 更新单个 item,按 goodsName 匹配;不存在则追加
*/ */
fun updateItem(item: Item) { fun updateItem(item: Item) {
val idx = items.indexOfFirst { it.goodsName == item.goodsName } val idx = items.indexOfFirst { it.goodsName == item.goodsName }
if (idx >= 0) { if (idx >= 0) {
items[idx] = item set(idx, item)
notifyItemChanged(idx)
} else { } else {
items.add(item) add(item)
notifyItemInserted(items.lastIndex)
} }
} }
@@ -41,29 +39,20 @@ class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>()
*/ */
fun removeItem(goodsName: String) { fun removeItem(goodsName: String) {
val idx = items.indexOfFirst { it.goodsName == goodsName } val idx = items.indexOfFirst { it.goodsName == goodsName }
if (idx >= 0) { if (idx >= 0) removeAt(idx)
items.removeAt(idx)
notifyItemRemoved(idx)
}
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val binding = ItemSeasoningWeightBinding.inflate( val binding = ItemSeasoningWeightBinding.inflate(LayoutInflater.from(context), parent, false)
LayoutInflater.from(parent.context), parent, false
)
return VH(binding) return VH(binding)
} }
override fun onBindViewHolder(holder: VH, position: Int) { override fun onBindViewHolder(holder: VH, position: Int, item: Item?) {
holder.bind(items[position]) item ?: return
holder.binding.tvSeasoningName.text = item.goodsName
holder.binding.tvSeasoningWeight.text = "${item.useWeight}g"
} }
override fun getItemCount() = items.size inner class VH(var binding: ItemSeasoningWeightBinding) : QuickViewHolder(binding.root)
class VH(private val b: ItemSeasoningWeightBinding) : RecyclerView.ViewHolder(b.root) {
fun bind(item: Item) {
b.tvSeasoningName.text = item.goodsName
b.tvSeasoningWeight.text = "${item.useWeight}g"
}
}
} }
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.activity.addCallback
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
@@ -13,6 +14,7 @@ import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.ActivitySubmitFoodBinding import com.shuwei.dish.match.databinding.ActivitySubmitFoodBinding
import com.shuwei.dish.match.db.AppRepository import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.dialog.CommonDialog
import com.shuwei.dish.match.entity.CookFoodEntity import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodRecord import com.shuwei.dish.match.entity.FoodRecord
@@ -23,6 +25,7 @@ import com.shuwei.dish.match.utils.AddressUtil
import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.clickWithDebounce
import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.roundedDecimalPlace
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toJsonString import com.shuwei.dish.match.utils.ext.toJsonString
@@ -83,6 +86,12 @@ class SubmitFoodActivity : BaseActivity() {
*/ */
private val rawWeightMap = mutableMapOf<String, Triple<String, String, Double>>() private val rawWeightMap = mutableMapOf<String, Triple<String, String, Double>>()
/**
* isCooking=true 时从 goodsList 预填充的历史用量,key = goodsNamevalue = goodsId to useWeight
* 用于 refreshAdapterByName 时叠加到秤新增用量上
*/
private val baseSeasoningMap = mutableMapOf<String, Pair<String, Double>>()
@Suppress("unchecked_cast", "DEPRECATION") @Suppress("unchecked_cast", "DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -93,8 +102,11 @@ class SubmitFoodActivity : BaseActivity() {
intent.extras?.apply { intent.extras?.apply {
food = getSerializable(FOOD_ITEM) as FoodRecord? food = getSerializable(FOOD_ITEM) as FoodRecord?
Log.d(TAG, "onCreate: cookMode=${food?.cookMode}") Log.d(TAG, "onCreate: cookMode=${food?.cookMode}")
// 非烹饪中,从菜品主辅材配置页面带入数据 // isCooking=false:从前一页面带入主辅材数据;isCooking=true:从数据库查询
if (food?.isCooking == false) {
goodsList = getSerializable(GOODS_LIST) as MutableList<CookFoodGoodsEntity>? goodsList = getSerializable(GOODS_LIST) as MutableList<CookFoodGoodsEntity>?
Log.d(TAG, "onCreate: goodsList=${goodsList.toJsonString()}")
}
} }
setTitleBar(titleBarAction = { setTitleBar(titleBarAction = {
it.visible() it.visible()
@@ -102,11 +114,17 @@ class SubmitFoodActivity : BaseActivity() {
it.text = food?.foodName it.text = food?.foodName
}, rightIconActon = { }, rightIconActon = {
it.gone() it.gone()
it.setImageResource(R.drawable.ic_setting) }, backAction = {
it.setOnClickListener {
remindSaveDataDialog()
}
}) })
setupRecyclerView() setupRecyclerView()
addViewListener() addViewListener()
loadSlotsThenObserveScales() loadSlotsThenObserveScales()
onBackPressedDispatcher.addCallback(this) {
remindSaveDataDialog()
}
} }
/** 初始化 RecyclerView2列 GridLayoutManager */ /** 初始化 RecyclerView2列 GridLayoutManager */
@@ -118,7 +136,7 @@ class SubmitFoodActivity : BaseActivity() {
/** /**
* 从 Room 加载槽位配置后,开始订阅子设备秤数据 * 从 Room 加载槽位配置后,开始订阅子设备秤数据
* 确保 slotMap 就绪后再处理秤数据,避免名称为空 * isCooking=true 时先从数据库查询 goodsList,筛选调料预填充 adapter,再开始观测
*/ */
private fun loadSlotsThenObserveScales() { private fun loadSlotsThenObserveScales() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
@@ -127,8 +145,30 @@ class SubmitFoodActivity : BaseActivity() {
slots.forEach { slot -> slots.forEach { slot ->
slotMap["${slot.deviceId}#${slot.address}"] = slot.goodsId to slot.goodsName slotMap["${slot.deviceId}#${slot.address}"] = slot.goodsId to slot.goodsName
} }
if (food?.isCooking == true) {
// 制作中:从数据库查询 goodsList,筛选调料数据预填充 adapter
val foodId = food?.foodId ?: return@withContext
val cookMode = food?.cookMode ?: return@withContext
appViewModel.getCookFoodGoodsList(foodId, cookMode) { list ->
goodsList = list
list.filter { it.materialType == 3 }.forEach { item ->
val name = item.goodsName ?: ""
val weight = item.useWeight ?: 0.0
baseSeasoningMap[name] = (item.goodsId) to weight
seasoningAdapter.updateItem(
SeasoningWeightAdapter.Item(
goodsId = item.goodsId,
goodsName = name,
useWeight = weight
)
)
}
observeScaleData() observeScaleData()
} }
} else {
observeScaleData()
}
}
} }
} }
@@ -174,33 +214,34 @@ class SubmitFoodActivity : BaseActivity() {
} }
/** /**
* 按 goodsName 聚合 rawWeightMap将同名调料的用量累加后推给 adapter * 按 goodsName 聚合 rawWeightMap叠加历史用量后推给 adapter
* 聚合后总量 < 0.5g 则从 adapter 移除 * isCooking=true 时只新增,不移除(历史调料数据不可减少)
*/ */
private fun refreshAdapterByName(goodsName: String) { private fun refreshAdapterByName(goodsName: String) {
val grouped = rawWeightMap.values.filter { it.second == goodsName } val grouped = rawWeightMap.values.filter { it.second == goodsName }
if (grouped.isEmpty()) { val newWeight = grouped.sumOf { it.third }.roundedOneDecimalPlace()
val (baseGoodsId, baseWeight) = baseSeasoningMap[goodsName] ?: ("" to 0.0)
val totalWeight = (newWeight + baseWeight).roundedOneDecimalPlace()
if (totalWeight < 0.5) {
// 制作中模式:已有历史数据,不允许移除
if (food?.isCooking != true) {
seasoningAdapter.removeItem(goodsName) seasoningAdapter.removeItem(goodsName)
}
return return
} }
val totalWeight = grouped.sumOf { it.third }.roundedOneDecimalPlace() val goodsId = grouped.firstOrNull()?.first?.takeIf { it.isNotEmpty() } ?: baseGoodsId
if (totalWeight < 0.5) {
seasoningAdapter.removeItem(goodsName)
} else {
seasoningAdapter.updateItem( seasoningAdapter.updateItem(
SeasoningWeightAdapter.Item( SeasoningWeightAdapter.Item(
goodsId = grouped.first().first, goodsId = goodsId,
goodsName = goodsName, goodsName = goodsName,
useWeight = totalWeight useWeight = totalWeight
) )
) )
} }
}
private fun addViewListener() { private fun addViewListener() {
binding.btnCook.clickWithDebounce { cook() } binding.btnCook.clickWithDebounce { cook() }
binding.btnWeightClear.clickWithDebounce { WeightUtil.tareTwo(2) }
binding.btnSubmit.clickWithDebounce { binding.btnSubmit.clickWithDebounce {
if (cookFoodEntity.foodWeight <= 0.toDouble()) { if (cookFoodEntity.foodWeight <= 0.toDouble()) {
toast("未识别到菜品熟重") toast("未识别到菜品熟重")
@@ -212,8 +253,14 @@ class SubmitFoodActivity : BaseActivity() {
// 监听主设备 2格秤的熟重 // 监听主设备 2格秤的熟重
WeightUtil.addWeightListener(TAG) { address, _, weight -> WeightUtil.addWeightListener(TAG) { address, _, weight ->
if (address == AddressUtil.TWO) { if (address == AddressUtil.TWO) {
cookFoodEntity.foodWeight = weight.toDouble() cookFoodEntity.foodWeight = weight
binding.tvTotalWeight.text = "${weight / 1000f}" if (weight > 1000) {
binding.tvTotalWeight.text = "${(weight / 1000f).roundedDecimalPlace(3)}"
binding.tvWeightUnit.text = "千克"
} else {
binding.tvTotalWeight.text = "$weight"
binding.tvWeightUnit.text = ""
}
} }
} }
} }
@@ -231,7 +278,7 @@ class SubmitFoodActivity : BaseActivity() {
showLoading() showLoading()
// 将调料数据转为 CookFoodGoodsEntity 追加到 goodsList // 将调料数据转为 CookFoodGoodsEntity 追加到 goodsList
val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {} // val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {}
val tempSeasoningList = seasoningData.map { item -> val tempSeasoningList = seasoningData.map { item ->
CookFoodGoodsEntity().also { entity -> CookFoodGoodsEntity().also { entity ->
entity.goodsId = item.goodsId entity.goodsId = item.goodsId
@@ -356,4 +403,18 @@ class SubmitFoodActivity : BaseActivity() {
WeightUtil.removeWeightListener(TAG) WeightUtil.removeWeightListener(TAG)
super.onDestroy() super.onDestroy()
} }
/**
* 未保存数据提醒dialog
*/
private fun remindSaveDataDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setContent("请确认是否存在未保存的数据?")
.setNegativeButton("取消,留在页面")
.setNeutralButton("返回上页,不保存") { finish() }
.setPositiveButton("保存为烹饪中菜品") { cook() }
.show()
}
} }
@@ -32,6 +32,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="660dp" android:layout_height="660dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginHorizontal="12dp"
android:overScrollMode="never" /> android:overScrollMode="never" />
</LinearLayout> </LinearLayout>
@@ -98,7 +100,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:includeFontPadding="false" android:includeFontPadding="false"
android:text="千克" tools:text="千克"
android:textColor="@color/black999" android:textColor="@color/black999"
android:textSize="32sp" /> android:textSize="32sp" />
</LinearLayout> </LinearLayout>
@@ -4,8 +4,10 @@
android:layout_height="60dp" android:layout_height="60dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="16dp" android:background="@drawable/shape_scale_cell"
android:paddingEnd="16dp"> android:paddingHorizontal="16dp"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="4dp">
<TextView <TextView
android:id="@+id/tvSeasoningName" android:id="@+id/tvSeasoningName"