feat(ui): 添加用料明细预览功能和清空数据确认对话框
- 在 SubmitFoodActivity 中添加右侧图标显示并实现点击打开用料明细预览弹窗 - 新增 GoodsPreviewDialog 用于展示底部弹出的用料明细列表 - 创建 GoodsPreviewAdapter 和相关布局文件实现用料列表展示 - 添加 ic_list_view 图标用于右上角按钮 - 在 VectorCollectionFragment 中为清空数据按钮添加确认对话框 - 优化 WeightUtil 工具类添加可用性检查避免异常调用
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
package com.shuwei.dish.match.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.shuwei.dish.match.databinding.ItemGoodsPreviewBinding
|
||||||
|
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用料明细预览列表适配器
|
||||||
|
* 显示字段:foodId、goodsId、goodsCode、goodsName、materialType、useWeight
|
||||||
|
*/
|
||||||
|
class GoodsPreviewAdapter(
|
||||||
|
private val list: List<CookFoodGoodsEntity>
|
||||||
|
) : RecyclerView.Adapter<GoodsPreviewAdapter.VH>() {
|
||||||
|
|
||||||
|
inner class VH(val binding: ItemGoodsPreviewBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
|
val binding = ItemGoodsPreviewBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context), parent, false
|
||||||
|
)
|
||||||
|
return VH(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||||
|
val item = list[position]
|
||||||
|
holder.binding.run {
|
||||||
|
tvGoodsId.text = item.goodsId
|
||||||
|
tvGoodsCode.text = item.goodsCode.orEmpty()
|
||||||
|
tvGoodsName.text = item.goodsName.orEmpty()
|
||||||
|
// materialType:1主料 2辅料 3调料
|
||||||
|
tvMaterialType.text = when (item.materialType) {
|
||||||
|
1 -> "主料"
|
||||||
|
2 -> "辅料"
|
||||||
|
3 -> "调料"
|
||||||
|
else -> "${item.materialType}"
|
||||||
|
}
|
||||||
|
tvUseWeight.text = "${item.useWeight ?: 0.0}g"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = list.size
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.shuwei.dish.match.dialog
|
||||||
|
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.view.KeyboardShortcutGroup
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.shuwei.dish.match.R
|
||||||
|
import com.shuwei.dish.match.adapter.GoodsPreviewAdapter
|
||||||
|
import com.shuwei.dish.match.databinding.DialogGoodsPreviewBinding
|
||||||
|
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
||||||
|
import com.shuwei.dish.match.utils.ext.gone
|
||||||
|
import com.shuwei.dish.match.utils.ext.visible
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用料明细预览弹窗,以 BottomSheetDialog 形式展示当前 goodsList
|
||||||
|
* @param context 宿主 Context(Activity)
|
||||||
|
* @param goodsList 需要展示的物品列表
|
||||||
|
*/
|
||||||
|
class GoodsPreviewDialog(
|
||||||
|
context: android.content.Context,
|
||||||
|
private val goodsList: List<CookFoodGoodsEntity>
|
||||||
|
) : BottomSheetDialog(context, R.style.BottomSheet) {
|
||||||
|
|
||||||
|
private val binding = DialogGoodsPreviewBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
init {
|
||||||
|
setContentView(binding.root)
|
||||||
|
setCancelable(true)
|
||||||
|
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
|
||||||
|
binding.rvGoodsList.run {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = GoodsPreviewAdapter(goodsList)
|
||||||
|
}
|
||||||
|
// foodId 对同一菜品所有条目相同,统一显示在列表上方
|
||||||
|
val foodId = goodsList.firstOrNull()?.foodId.orEmpty()
|
||||||
|
binding.tvFoodId.text = "菜品ID:${foodId.ifEmpty { "-" }}"
|
||||||
|
if (goodsList.isEmpty()) {
|
||||||
|
binding.rvGoodsList.gone()
|
||||||
|
binding.tvEmpty.visible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 弹出时注入 Window.Callback,解决 RecyclerView 与 BottomSheet 拖拽的滑动冲突 */
|
||||||
|
override fun show() {
|
||||||
|
super.show()
|
||||||
|
val originalCallback = window?.callback ?: return
|
||||||
|
window?.callback = object : android.view.Window.Callback by originalCallback {
|
||||||
|
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||||
|
when (event.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
val rect = Rect()
|
||||||
|
binding.rvGoodsList.getGlobalVisibleRect(rect)
|
||||||
|
behavior.isDraggable = !rect.contains(event.rawX.toInt(), event.rawY.toInt())
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
|
behavior.isDraggable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalCallback.dispatchTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPointerCaptureChanged(hasCapture: Boolean) {
|
||||||
|
originalCallback.onPointerCaptureChanged(hasCapture)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProvideKeyboardShortcuts(
|
||||||
|
data: List<KeyboardShortcutGroup?>?,
|
||||||
|
menu: Menu?,
|
||||||
|
deviceId: Int
|
||||||
|
) {
|
||||||
|
originalCallback.onProvideKeyboardShortcuts(data, menu, deviceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,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.dialog.CommonDialog
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
|
import com.shuwei.dish.match.dialog.GoodsPreviewDialog
|
||||||
import com.shuwei.dish.match.db.entity.CookFoodEntity
|
import com.shuwei.dish.match.db.entity.CookFoodEntity
|
||||||
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
||||||
import com.shuwei.dish.match.model.FoodRecord
|
import com.shuwei.dish.match.model.FoodRecord
|
||||||
@@ -30,7 +31,6 @@ import com.shuwei.dish.match.utils.WeightUtil
|
|||||||
import com.shuwei.dish.match.utils.ext.appendText
|
import com.shuwei.dish.match.utils.ext.appendText
|
||||||
import com.shuwei.dish.match.utils.ext.buildSpannableString
|
import com.shuwei.dish.match.utils.ext.buildSpannableString
|
||||||
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.roundedDecimalPlace
|
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
|
||||||
@@ -128,7 +128,14 @@ class SubmitFoodActivity : BaseActivity() {
|
|||||||
}, titleAction = {
|
}, titleAction = {
|
||||||
it.text = food?.foodName
|
it.text = food?.foodName
|
||||||
}, rightIconAction = {
|
}, rightIconAction = {
|
||||||
it.gone()
|
it.visible()
|
||||||
|
it.setImageResource(R.drawable.ic_list_view)
|
||||||
|
it.setOnClickListener {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val list = buildPreviewGoodsList()
|
||||||
|
GoodsPreviewDialog(this@SubmitFoodActivity, list).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}, backAction = {
|
}, backAction = {
|
||||||
it.setOnClickListener {
|
it.setOnClickListener {
|
||||||
remindSaveDataDialog()
|
remindSaveDataDialog()
|
||||||
@@ -492,6 +499,19 @@ class SubmitFoodActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建用于预览弹窗的 goodsList:
|
||||||
|
* 取 goodsList 中的主辅材,再用 adapter 中最新调料覆盖原有调料数据(materialType==3)
|
||||||
|
* 调料的完整字段(含 goodsCode)通过 buildSeasoningEntity 从 DB 补全
|
||||||
|
*/
|
||||||
|
private suspend fun buildPreviewGoodsList(): List<CookFoodGoodsEntity> {
|
||||||
|
val base = goodsList?.filter { it.materialType != 3 }?.toMutableList() ?: mutableListOf()
|
||||||
|
seasoningAdapter.items
|
||||||
|
.filter { it.useWeight > 0.0 }
|
||||||
|
.forEach { base.add(buildSeasoningEntity(it)) }
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
WeightUtil.removeWeightListener(TAG)
|
WeightUtil.removeWeightListener(TAG)
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.shuwei.dish.match.databinding.FragmentVectorCollectionBinding
|
|||||||
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
||||||
import com.shuwei.dish.match.dialog.Loading
|
import com.shuwei.dish.match.dialog.Loading
|
||||||
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.model.FoodCollectionBean
|
import com.shuwei.dish.match.model.FoodCollectionBean
|
||||||
import com.shuwei.dish.match.model.GoodsItem
|
import com.shuwei.dish.match.model.GoodsItem
|
||||||
import com.shuwei.dish.match.net.UiState
|
import com.shuwei.dish.match.net.UiState
|
||||||
@@ -299,7 +300,14 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
}
|
}
|
||||||
takePhoto()
|
takePhoto()
|
||||||
}
|
}
|
||||||
binding.btnClearData.setOnClickListener { clearData() }
|
binding.btnClearData.setOnClickListener {
|
||||||
|
CommonDialog(requireContext())
|
||||||
|
.setTitle("清空确认")
|
||||||
|
.setContent("确认清空所有已采集的图片数据?")
|
||||||
|
.setNegativeButton("取消")
|
||||||
|
.setPositiveButton("确认") { clearData() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
binding.rvSearchFood.let {
|
binding.rvSearchFood.let {
|
||||||
it.layoutManager = GridLayoutManager(context, 2)
|
it.layoutManager = GridLayoutManager(context, 2)
|
||||||
it.adapter = searchAdapter
|
it.adapter = searchAdapter
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ object WeightUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getWeight() {
|
fun getWeight() {
|
||||||
|
if (!isAvailable) return
|
||||||
try {
|
try {
|
||||||
Weigher2.getWeight()
|
Weigher2.getWeight()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -95,6 +96,7 @@ object WeightUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tareTwo(address: Int) {
|
fun tareTwo(address: Int) {
|
||||||
|
if (!isAvailable) return
|
||||||
try {
|
try {
|
||||||
Log.d(TAG, "tareTwo,执行清零操作: address=$address")
|
Log.d(TAG, "tareTwo,执行清零操作: address=$address")
|
||||||
// SDK 内部自动管理总线,直接发送清零命令
|
// SDK 内部自动管理总线,直接发送清零命令
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M3,5 h18 v2 h-18 Z
|
||||||
|
M3,11 h18 v2 h-18 Z
|
||||||
|
M3,17 h18 v2 h-18 Z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_rounded_top"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 顶部拖拽指示条 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="90dp"
|
||||||
|
android:layout_height="10dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:background="@drawable/shape_gray_dc_5" />
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:text="用料明细"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="36sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- 菜品ID -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvFoodId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:textColor="@color/black666"
|
||||||
|
android:textSize="26sp" />
|
||||||
|
|
||||||
|
<!-- 表头 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/white_f6"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="40dp"
|
||||||
|
android:paddingEnd="40dp"
|
||||||
|
android:paddingTop="20dp"
|
||||||
|
android:paddingBottom="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:text="物品ID"
|
||||||
|
android:textColor="@color/black999"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:text="物品名称"
|
||||||
|
android:textColor="@color/black999"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:text="物料编码"
|
||||||
|
android:textColor="@color/black999"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="物料类型"
|
||||||
|
android:textColor="@color/black999"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="用量(g)"
|
||||||
|
android:textColor="@color/black999"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvGoodsList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:maxHeight="700dp"
|
||||||
|
android:nestedScrollingEnabled="true"
|
||||||
|
android:overScrollMode="never" />
|
||||||
|
|
||||||
|
<!-- 空布局 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEmpty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="暂无用料数据"
|
||||||
|
android:textColor="@color/gray_c8"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_bottom_line"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="40dp"
|
||||||
|
android:paddingEnd="40dp"
|
||||||
|
android:paddingTop="24dp"
|
||||||
|
android:paddingBottom="24dp">
|
||||||
|
|
||||||
|
<!-- goodsId -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvGoodsId"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/black666"
|
||||||
|
android:textSize="26sp" />
|
||||||
|
|
||||||
|
<!-- goodsName -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvGoodsName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="26sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- goodsCode -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvGoodsCode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="26sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- materialType -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMaterialType"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/dish_green"
|
||||||
|
android:textSize="26sp" />
|
||||||
|
|
||||||
|
<!-- useWeight -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvUseWeight"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="26sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user