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:
@@ -1,8 +1,10 @@
|
||||
package com.shuwei.dish.match.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -10,7 +12,7 @@ import com.shuwei.dish.match.databinding.ItemSeasoningWeightBinding
|
||||
* 用于 SubmitFoodActivity 展示各调料槽位的实时用量
|
||||
* 同名调料已在外部聚合,adapter 按 goodsName 作为唯一标识
|
||||
*/
|
||||
class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>() {
|
||||
class SeasoningWeightAdapter : BaseQuickAdapter<SeasoningWeightAdapter.Item, SeasoningWeightAdapter.VH>() {
|
||||
|
||||
/** 调料展示数据项,key = goodsName(同名调料已聚合) */
|
||||
data class Item(
|
||||
@@ -20,19 +22,15 @@ class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>()
|
||||
val useWeight: Double
|
||||
)
|
||||
|
||||
val items = mutableListOf<Item>()
|
||||
|
||||
/**
|
||||
* 更新单个 item,按 goodsName 匹配;不存在则追加
|
||||
*/
|
||||
fun updateItem(item: Item) {
|
||||
val idx = items.indexOfFirst { it.goodsName == item.goodsName }
|
||||
if (idx >= 0) {
|
||||
items[idx] = item
|
||||
notifyItemChanged(idx)
|
||||
set(idx, item)
|
||||
} else {
|
||||
items.add(item)
|
||||
notifyItemInserted(items.lastIndex)
|
||||
add(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,29 +39,20 @@ class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>()
|
||||
*/
|
||||
fun removeItem(goodsName: String) {
|
||||
val idx = items.indexOfFirst { it.goodsName == goodsName }
|
||||
if (idx >= 0) {
|
||||
items.removeAt(idx)
|
||||
notifyItemRemoved(idx)
|
||||
}
|
||||
if (idx >= 0) removeAt(idx)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||
val binding = ItemSeasoningWeightBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val binding = ItemSeasoningWeightBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||
holder.bind(items[position])
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: Item?) {
|
||||
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.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.addCallback
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
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.databinding.ActivitySubmitFoodBinding
|
||||
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.CookFoodGoodsEntity
|
||||
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.ext.clickWithDebounce
|
||||
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.startActivity
|
||||
import com.shuwei.dish.match.utils.ext.toJsonString
|
||||
@@ -83,6 +86,12 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
*/
|
||||
private val rawWeightMap = mutableMapOf<String, Triple<String, String, Double>>()
|
||||
|
||||
/**
|
||||
* isCooking=true 时从 goodsList 预填充的历史用量,key = goodsName,value = goodsId to useWeight
|
||||
* 用于 refreshAdapterByName 时叠加到秤新增用量上
|
||||
*/
|
||||
private val baseSeasoningMap = mutableMapOf<String, Pair<String, Double>>()
|
||||
|
||||
@Suppress("unchecked_cast", "DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -93,8 +102,11 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
intent.extras?.apply {
|
||||
food = getSerializable(FOOD_ITEM) as FoodRecord?
|
||||
Log.d(TAG, "onCreate: cookMode=${food?.cookMode}")
|
||||
// 非烹饪中,从菜品主辅材配置页面带入数据
|
||||
goodsList = getSerializable(GOODS_LIST) as MutableList<CookFoodGoodsEntity>?
|
||||
// isCooking=false:从前一页面带入主辅材数据;isCooking=true:从数据库查询
|
||||
if (food?.isCooking == false) {
|
||||
goodsList = getSerializable(GOODS_LIST) as MutableList<CookFoodGoodsEntity>?
|
||||
Log.d(TAG, "onCreate: goodsList=${goodsList.toJsonString()}")
|
||||
}
|
||||
}
|
||||
setTitleBar(titleBarAction = {
|
||||
it.visible()
|
||||
@@ -102,11 +114,17 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
it.text = food?.foodName
|
||||
}, rightIconActon = {
|
||||
it.gone()
|
||||
it.setImageResource(R.drawable.ic_setting)
|
||||
}, backAction = {
|
||||
it.setOnClickListener {
|
||||
remindSaveDataDialog()
|
||||
}
|
||||
})
|
||||
setupRecyclerView()
|
||||
addViewListener()
|
||||
loadSlotsThenObserveScales()
|
||||
onBackPressedDispatcher.addCallback(this) {
|
||||
remindSaveDataDialog()
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 RecyclerView,2列 GridLayoutManager */
|
||||
@@ -118,7 +136,7 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
|
||||
/**
|
||||
* 从 Room 加载槽位配置后,开始订阅子设备秤数据
|
||||
* 确保 slotMap 就绪后再处理秤数据,避免名称为空
|
||||
* isCooking=true 时先从数据库查询 goodsList,筛选调料预填充 adapter,再开始观测
|
||||
*/
|
||||
private fun loadSlotsThenObserveScales() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
@@ -127,7 +145,29 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
slots.forEach { slot ->
|
||||
slotMap["${slot.deviceId}#${slot.address}"] = slot.goodsId to slot.goodsName
|
||||
}
|
||||
observeScaleData()
|
||||
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()
|
||||
}
|
||||
} else {
|
||||
observeScaleData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,33 +214,34 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 goodsName 聚合 rawWeightMap,将同名调料的用量累加后推给 adapter
|
||||
* 聚合后总量 < 0.5g 则从 adapter 移除
|
||||
* 按 goodsName 聚合 rawWeightMap,叠加历史用量后推给 adapter
|
||||
* isCooking=true 时只新增,不移除(历史调料数据不可减少)
|
||||
*/
|
||||
private fun refreshAdapterByName(goodsName: String) {
|
||||
val grouped = rawWeightMap.values.filter { it.second == goodsName }
|
||||
if (grouped.isEmpty()) {
|
||||
seasoningAdapter.removeItem(goodsName)
|
||||
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)
|
||||
}
|
||||
return
|
||||
}
|
||||
val totalWeight = grouped.sumOf { it.third }.roundedOneDecimalPlace()
|
||||
if (totalWeight < 0.5) {
|
||||
seasoningAdapter.removeItem(goodsName)
|
||||
} else {
|
||||
seasoningAdapter.updateItem(
|
||||
SeasoningWeightAdapter.Item(
|
||||
goodsId = grouped.first().first,
|
||||
goodsName = goodsName,
|
||||
useWeight = totalWeight
|
||||
)
|
||||
val goodsId = grouped.firstOrNull()?.first?.takeIf { it.isNotEmpty() } ?: baseGoodsId
|
||||
seasoningAdapter.updateItem(
|
||||
SeasoningWeightAdapter.Item(
|
||||
goodsId = goodsId,
|
||||
goodsName = goodsName,
|
||||
useWeight = totalWeight
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun addViewListener() {
|
||||
|
||||
binding.btnCook.clickWithDebounce { cook() }
|
||||
|
||||
binding.btnWeightClear.clickWithDebounce { WeightUtil.tareTwo(2) }
|
||||
binding.btnSubmit.clickWithDebounce {
|
||||
if (cookFoodEntity.foodWeight <= 0.toDouble()) {
|
||||
toast("未识别到菜品熟重")
|
||||
@@ -212,8 +253,14 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
// 监听主设备 2格秤的熟重
|
||||
WeightUtil.addWeightListener(TAG) { address, _, weight ->
|
||||
if (address == AddressUtil.TWO) {
|
||||
cookFoodEntity.foodWeight = weight.toDouble()
|
||||
binding.tvTotalWeight.text = "${weight / 1000f}"
|
||||
cookFoodEntity.foodWeight = weight
|
||||
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()
|
||||
|
||||
// 将调料数据转为 CookFoodGoodsEntity 追加到 goodsList
|
||||
val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {}
|
||||
// val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {}
|
||||
val tempSeasoningList = seasoningData.map { item ->
|
||||
CookFoodGoodsEntity().also { entity ->
|
||||
entity.goodsId = item.goodsId
|
||||
@@ -356,4 +403,18 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
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_height="660dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -98,7 +100,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:includeFontPadding="false"
|
||||
android:text="千克"
|
||||
tools:text="千克"
|
||||
android:textColor="@color/black999"
|
||||
android:textSize="32sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
android:background="@drawable/shape_scale_cell"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSeasoningName"
|
||||
|
||||
Reference in New Issue
Block a user