refactor(entity): 新增 GoodsItem 统一物品数据模型,合并查询接口并迁移 UI 状态字段

- 新增 GoodsItem 纯数据类(DTO),整合接口字段与原 CookFoodGoodsEntity @Ignore UI 状态字段
- ApiService/RemoteRepository/NetViewModel 合并 queryGoodsList 与 querySeasoningList 为统一接口
- GoodsInfoSearchAdapter/FoodSearchAdapter/DishPartAdapter 实体类改为 GoodsItem
- FoodSearchDialog/FoodRecognizeDialog/FoodRecognizeActivity/VectorCollectionFragment 改用 GoodsItem
- PrepareFoodActivity.list 改为 GoodsItem,loadDishDetail 加 toGoodsItem() 转换,采集模式传 SubmitFoodActivity 前转回 CookFoodGoodsEntity
- CookFoodGoodsEntity 删除所有 @Ignore 字段,新增 toGoodsItem() 转换方法

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 15:00:32 +08:00
co-authored by Claude Sonnet 4.6
parent 05063ee9de
commit e5fc0c265d
38 changed files with 846 additions and 819 deletions
@@ -5,13 +5,16 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.databinding.ListItemDishRecordBinding
import com.shuwei.dish.match.databinding.ListItemFood2Binding
import com.shuwei.dish.match.entity.FoodRecord
class FoodRecordAdapter(list: MutableList<FoodRecord>) :
BaseQuickAdapter<FoodRecord, FoodRecordAdapter.VH>(list) {
/**
* 菜品搜索页面的适配器
*/
class Food2Adapter(list: MutableList<FoodRecord>) :
BaseQuickAdapter<FoodRecord, Food2Adapter.VH>(list) {
inner class VH(var binding: ListItemDishRecordBinding) : QuickViewHolder(binding.root)
inner class VH(var binding: ListItemFood2Binding) : QuickViewHolder(binding.root)
override fun onBindViewHolder(
holder: VH,
@@ -27,7 +30,7 @@ class FoodRecordAdapter(list: MutableList<FoodRecord>) :
viewType: Int
): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemDishRecordBinding.inflate(inflater, parent, false)
val binding = ListItemFood2Binding.inflate(inflater, parent, false)
return VH(binding)
}
}
@@ -0,0 +1,40 @@
package com.shuwei.dish.match.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.databinding.ListItemFood3Binding
import com.shuwei.dish.match.entity.GoodsItem
/**
* 向量采集页面的适配器
*/
class Food3Adapter(list: MutableList<GoodsItem>) :
BaseQuickAdapter<GoodsItem, Food3Adapter.VH>(list) {
override fun onBindViewHolder(
holder: VH,
position: Int,
item: GoodsItem?
) {
item ?: return
holder.binding.tvFoodName.run {
text = item.goodsName
isChecked = item.isClicked
}
}
override fun onCreateViewHolder(
context: Context,
parent: ViewGroup,
viewType: Int
): VH {
val binding = ListItemFood3Binding.inflate(LayoutInflater.from(context), parent, false)
return VH(binding)
}
inner class VH(var binding: ListItemFood3Binding) : QuickViewHolder(binding.root)
}
@@ -7,41 +7,45 @@ import androidx.core.content.ContextCompat
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.databinding.ListItemFoodBinding
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.roundedDecimalPlace
import com.shuwei.dish.match.utils.ext.visible
class GoodsInfoSearchAdapter(private var list: MutableList<CookFoodGoodsEntity>) :
BaseQuickAdapter<CookFoodGoodsEntity, GoodsInfoSearchAdapter.VH>(list) {
/**
* 识别页面和搜索弹窗的适配器
*/
class FoodAdapter(list: MutableList<GoodsItem>) :
BaseQuickAdapter<GoodsItem, FoodAdapter.VH>(list) {
override fun onBindViewHolder(
holder: VH,
position: Int,
item: CookFoodGoodsEntity?
item: GoodsItem?
) {
item ?: return
holder.binding.tvGoodsInfo.run {
text = item.goodsName
setBackgroundResource(
if (item.isClicked == true) R.drawable.shape_green_stroke
if (item.isClicked) R.drawable.shape_green_stroke
else R.drawable.shape_white_12_corners
)
setTextColor(
ContextCompat.getColor(
context,
if (item.isClicked == true) R.color.dish_green
if (item.isClicked) R.color.dish_green
else R.color.black666
)
)
}
if (item.foodScore == -1){
if (item.foodScore == -1) {
holder.binding.tvFoodScore.gone()
} else {
holder.binding.tvFoodScore.let {
it.visible()
it.text = "${(item.foodScore / 100.0).roundedDecimalPlace(2)}%"
val foodScoreText = "${(item.foodScore / 100.0).roundedDecimalPlace(2)}%"
it.text = foodScoreText
}
}
}
@@ -52,11 +56,10 @@ class GoodsInfoSearchAdapter(private var list: MutableList<CookFoodGoodsEntity>)
viewType: Int
): VH {
val binding =
ListItemSearchGoodsInfoBinding.inflate(LayoutInflater.from(context), parent, false)
ListItemFoodBinding.inflate(LayoutInflater.from(context), parent, false)
return VH(binding)
}
inner class VH(var binding: ListItemSearchGoodsInfoBinding) : QuickViewHolder(binding.root)
inner class VH(var binding: ListItemFoodBinding) : QuickViewHolder(binding.root)
}
@@ -7,27 +7,27 @@ import androidx.core.content.ContextCompat
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemDishCookBinding
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.databinding.ListItemFoodMaterialBinding
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
import com.shuwei.dish.match.utils.ext.visible
class DishPartAdapter(list: MutableList<CookFoodGoodsEntity>) :
BaseQuickAdapter<CookFoodGoodsEntity, DishPartAdapter.VH>(list) {
class FoodMaterialAdapter(list: MutableList<GoodsItem>) :
BaseQuickAdapter<GoodsItem, FoodMaterialAdapter.VH>(list) {
/** item 主体点击回调 */
var onItemClick: ((position: Int) -> Unit)? = null
inner class VH(var binding: ListItemDishCookBinding) : QuickViewHolder(binding.root)
inner class VH(var binding: ListItemFoodMaterialBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemDishCookBinding.inflate(inflater, parent, false)
val binding = ListItemFoodMaterialBinding.inflate(inflater, parent, false)
return VH(binding)
}
override fun onBindViewHolder(holder: VH, position: Int, item: CookFoodGoodsEntity?) {
override fun onBindViewHolder(holder: VH, position: Int, item: GoodsItem?) {
holder.binding.run {
val data = item ?: return
@@ -1,43 +0,0 @@
package com.shuwei.dish.match.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemSearchFoodBinding
import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.roundedDecimalPlace
import com.shuwei.dish.match.utils.ext.visible
class FoodSearchAdapter(private var list: MutableList<CookFoodGoodsEntity>) :
BaseQuickAdapter<CookFoodGoodsEntity, FoodSearchAdapter.VH>(list) {
override fun onBindViewHolder(
holder: VH,
position: Int,
item: CookFoodGoodsEntity?
) {
item ?: return
holder.binding.tvFoodName.run {
text = item.goodsName
isChecked = item.isClicked
}
}
override fun onCreateViewHolder(
context: Context,
parent: ViewGroup,
viewType: Int
): VH {
val binding = ListItemSearchFoodBinding.inflate(LayoutInflater.from(context), parent, false)
return VH(binding)
}
inner class VH(var binding: ListItemSearchFoodBinding) : QuickViewHolder(binding.root)
}
@@ -7,9 +7,12 @@ import androidx.core.content.ContextCompat
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding
import com.shuwei.dish.match.databinding.ListItemFoodBinding
import com.shuwei.dish.match.entity.SeasoningEntity
/**
* 调料搜索弹窗适配器
*/
class SeasoningSearchAdapter(private var list: MutableList<SeasoningEntity>) :
BaseQuickAdapter<SeasoningEntity, SeasoningSearchAdapter.VH>(list) {
@@ -40,10 +43,10 @@ class SeasoningSearchAdapter(private var list: MutableList<SeasoningEntity>) :
viewType: Int
): VH {
val binding =
ListItemSearchGoodsInfoBinding.inflate(LayoutInflater.from(context), parent, false)
ListItemFoodBinding.inflate(LayoutInflater.from(context), parent, false)
return VH(binding)
}
inner class VH(var binding: ListItemSearchGoodsInfoBinding) : QuickViewHolder(binding.root)
inner class VH(var binding: ListItemFoodBinding) : QuickViewHolder(binding.root)
}
@@ -1,136 +1,136 @@
package com.shuwei.dish.match.adapter
import android.graphics.Typeface
import android.icu.text.DecimalFormat
import android.text.SpannableStringBuilder
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import android.text.style.LineHeightSpan
import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.graphics.toColorInt
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemCellBinding
import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.utils.ext.appendText
import com.shuwei.dish.match.utils.ext.buildSpannableString
import com.shuwei.dish.match.utils.ext.clickWithDebounce
import com.shuwei.dish.match.utils.ext.dp
import com.shuwei.dish.match.utils.ext.invisible
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
import com.shuwei.dish.match.utils.ext.visible
class TextCellAdapter(var list: MutableList<SeasoningEntity>) :
RecyclerView.Adapter<TextCellAdapter.VH>() {
inner class VH(var binding: ListItemCellBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(parent.context)
val binding = ListItemCellBinding.inflate(inflater, parent, false)
return VH(binding)
}
override fun onBindViewHolder(holder: VH, position: Int) {
val item = list[position]
holder.binding.run {
if (position == 10 || position == 11 || position == 13 || position == 14) {
root.invisible()
} else {
root.visible()
}
loadCell(tvCell, item)
//root.setBackgroundResource(R.color.dish_green)
(root.layoutParams as RecyclerView.LayoutParams).let {
// it.bottomMargin = 15.dp
it.width = 226.dp
it.height = 120.dp
// it.rightMargin = 0
// it.leftMargin = if (position % 3 == 0) 0 else 15.dp
it.leftMargin = 7.dp
it.rightMargin = 8.dp
it.topMargin = 7.dp
it.bottomMargin = 8.dp
}
if (item.pageType == 1) {
root.clickWithDebounce { onCellClick?.invoke(position) }
}
}
}
override fun getItemCount() = list.size
var onCellClick: ((positon: Int) -> Unit)? = null
companion object {
fun loadLayout(frameLayout: FrameLayout, item: SeasoningEntity) {
val child: TextView = frameLayout.getChildAt(0) as TextView
child.text = getTextSpan(item)
}
fun loadCell(tvCell: TextView, item: SeasoningEntity) {
tvCell.text = getTextSpan(item)
}
// fun loadCell2(tvCell: TextView, item: SeasoningEntity) {
// if (item.goodsName.isNullOrBlank()) {
// tvCell.run {
// text = buildSpannableString {
//package com.shuwei.dish.match.adapter
//
//import android.graphics.Typeface
//import android.icu.text.DecimalFormat
//import android.text.SpannableStringBuilder
//import android.text.style.AbsoluteSizeSpan
//import android.text.style.ForegroundColorSpan
//import android.text.style.LineHeightSpan
//import android.text.style.StyleSpan
//import android.view.LayoutInflater
//import android.view.ViewGroup
//import android.widget.FrameLayout
//import android.widget.TextView
//import androidx.core.content.ContextCompat
//import androidx.core.graphics.toColorInt
//import androidx.recyclerview.widget.RecyclerView
//import com.chad.library.adapter4.viewholder.QuickViewHolder
//import com.shuwei.dish.match.R
//import com.shuwei.dish.match.databinding.ListItemCellBinding
//import com.shuwei.dish.match.entity.SeasoningEntity
//import com.shuwei.dish.match.utils.ext.appendText
//import com.shuwei.dish.match.utils.ext.buildSpannableString
//import com.shuwei.dish.match.utils.ext.clickWithDebounce
//import com.shuwei.dish.match.utils.ext.dp
//import com.shuwei.dish.match.utils.ext.invisible
//import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
//import com.shuwei.dish.match.utils.ext.visible
//
//class TextCellAdapter(var list: MutableList<SeasoningEntity>) :
// RecyclerView.Adapter<TextCellAdapter.VH>() {
//
// inner class VH(var binding: ListItemCellBinding) : QuickViewHolder(binding.root)
//
// override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
// val inflater = LayoutInflater.from(parent.context)
// val binding = ListItemCellBinding.inflate(inflater, parent, false)
// return VH(binding)
// }
//
// override fun onBindViewHolder(holder: VH, position: Int) {
// val item = list[position]
// holder.binding.run {
// if (position == 10 || position == 11 || position == 13 || position == 14) {
// root.invisible()
// } else {
// root.visible()
// }
// loadCell(tvCell, item)
// //root.setBackgroundResource(R.color.dish_green)
// (root.layoutParams as RecyclerView.LayoutParams).let {
//// it.bottomMargin = 15.dp
// it.width = 226.dp
// it.height = 120.dp
//// it.rightMargin = 0
//// it.leftMargin = if (position % 3 == 0) 0 else 15.dp
//
// it.leftMargin = 7.dp
// it.rightMargin = 8.dp
// it.topMargin = 7.dp
// it.bottomMargin = 8.dp
// }
// if (item.pageType == 1) {
// root.clickWithDebounce { onCellClick?.invoke(position) }
// }
// }
// }
//
// override fun getItemCount() = list.size
//
// var onCellClick: ((positon: Int) -> Unit)? = null
//
// companion object {
// fun loadLayout(frameLayout: FrameLayout, item: SeasoningEntity) {
// val child: TextView = frameLayout.getChildAt(0) as TextView
// child.text = getTextSpan(item)
// }
// fun loadCell(tvCell: TextView, item: SeasoningEntity) {
// tvCell.text = getTextSpan(item)
// }
//// fun loadCell2(tvCell: TextView, item: SeasoningEntity) {
//// if (item.goodsName.isNullOrBlank()) {
//// tvCell.run {
//// text = buildSpannableString {
//// appendText(
//// "未配置",
//// ForegroundColorSpan("#999999".toColorInt()),
//// StyleSpan(Typeface.BOLD),
//// AbsoluteSizeSpan(28, true),
//// LineHeightSpan { text, start, end, spanstartv, v, fm ->
//// fm.descent += 10.dp // 增加行间距
//// }
//// )}
//// //setTextColor(ContextCompat.getColor(context, R.color.black999))
//// }
//// } else {
////// if (item.pageType == 1) {
////// tvCell.run {
////// text = item.goodsName
////// setTextColor(ContextCompat.getColor(context, R.color.black))
////// }
////// } else {
//// tvCell.text = getTextSpan(item)
////// }
//// }
//// }
//
// fun getTextSpan(item: SeasoningEntity): SpannableStringBuilder {
// val endIndex = if (item.sort == 9) 10 else 6
// var name = if (item.goodsName.isNullOrBlank()) "未配置" else item.goodsName!!
// if (name.length > endIndex) {
// name = "${name.substring(0, endIndex)}…"
// }
// return buildSpannableString {
// appendText(
// "未配置",
// ForegroundColorSpan("#999999".toColorInt()),
// name,
// ForegroundColorSpan(if (item.goodsName.isNullOrBlank()) "#999999".toColorInt() else "#000000".toColorInt()),
// StyleSpan(Typeface.BOLD),
// AbsoluteSizeSpan(28, true),
// LineHeightSpan { text, start, end, spanstartv, v, fm ->
// fm.descent += 10.dp // 增加行间距
// }
// )}
// //setTextColor(ContextCompat.getColor(context, R.color.black999))
// }
// } else {
//// if (item.pageType == 1) {
//// tvCell.run {
//// text = item.goodsName
//// setTextColor(ContextCompat.getColor(context, R.color.black))
//// }
//// } else {
// tvCell.text = getTextSpan(item)
//// }
// )
// append("\n")
// val weight = item.useWeight ?: 0.toDouble()
// val weightColor = if (weight == 0.toDouble()) "#999999" else "#00BC71"
// appendText(
//// "${DecimalFormat("#").format(weight)}g",
// "${weight.roundedOneDecimalPlace()}g",
// ForegroundColorSpan(weightColor.toColorInt()),
// AbsoluteSizeSpan(30, true)
// )
// }
// }
fun getTextSpan(item: SeasoningEntity): SpannableStringBuilder {
val endIndex = if (item.sort == 9) 10 else 6
var name = if (item.goodsName.isNullOrBlank()) "未配置" else item.goodsName!!
if (name.length > endIndex) {
name = "${name.substring(0, endIndex)}"
}
return buildSpannableString {
appendText(
name,
ForegroundColorSpan(if (item.goodsName.isNullOrBlank()) "#999999".toColorInt() else "#000000".toColorInt()),
StyleSpan(Typeface.BOLD),
AbsoluteSizeSpan(28, true),
LineHeightSpan { text, start, end, spanstartv, v, fm ->
fm.descent += 10.dp // 增加行间距
}
)
append("\n")
val weight = item.useWeight ?: 0.toDouble()
val weightColor = if (weight == 0.toDouble()) "#999999" else "#00BC71"
appendText(
// "${DecimalFormat("#").format(weight)}g",
"${weight.roundedOneDecimalPlace()}g",
ForegroundColorSpan(weightColor.toColorInt()),
AbsoluteSizeSpan(30, true)
)
}
}
}
}
// }
//
//}
@@ -25,7 +25,7 @@ import com.shuwei.dish.match.entity.SeasoningSlotEntity
CookFoodGoodsEntity::class,
SeasoningSlotEntity::class,
],
version = 8,
version = 9,
exportSchema = true
)
abstract class AppDatabase : RoomDatabase() {
@@ -214,6 +214,16 @@ val MIGRATION_6_7 = MigrationImpl(6, 7) {db ->
}
}
/**
* 版本8→9:为 dm_cook_food_goods 和 dm_seasoning 表新增 zjmCode、materId 字段
*/
val MIGRATION_8_9 = MigrationImpl(8, 9) { db ->
db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN zjmCode TEXT DEFAULT ''")
db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN materId TEXT DEFAULT ''")
db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN zjmCode TEXT DEFAULT ''")
db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN materId TEXT DEFAULT ''")
}
class DatabaseProvider(private val context: Context) {
// 单例模式确保全局唯一实例‌
val instance: AppDatabase by lazy {
@@ -233,6 +243,7 @@ class DatabaseProvider(private val context: Context) {
// .addMigrations(MIGRATION_5_6)
// .addMigrations(MIGRATION_6_7)
.addMigrations(MIGRATION_7_8)
.addMigrations(MIGRATION_8_9)
.build()
}
}
@@ -1,62 +1,62 @@
package com.shuwei.dish.match.dialog
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.DialogFoodRecognizeBinding
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
/**
* 菜品识别结果弹窗,继承 BottomSheetDialog 确保只初始化一次
* @param activity 宿主 Activity
* @param onDismiss 弹窗关闭时的额外回调(可选)
* @param onItemSelected 用户点击某一菜品时的回调
*/
class FoodRecognizeDialog(
private val activity: BaseActivity,
private val onDismiss: (() -> Unit)? = null,
private val onItemSelected: (item: CookFoodGoodsEntity) -> Unit
) : BottomSheetDialog(activity, R.style.BottomSheet) {
private val binding = DialogFoodRecognizeBinding.inflate(LayoutInflater.from(activity))
private val list = mutableListOf<CookFoodGoodsEntity>()
private val adapter = GoodsInfoSearchAdapter(list).apply {
setOnItemClickListener { _, _, position ->
list[position].isClicked = true
notifyItemChanged(position)
Handler(Looper.getMainLooper()).postDelayed({
onItemSelected(list[position])
dismiss()
}, 300)
}
}
init {
setContentView(binding.root)
setCancelable(true)
behavior.skipCollapsed = false
setOnDismissListener { activity.hideStatusBar(); onDismiss?.invoke() }
binding.recyclerView.run {
layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
adapter = this@FoodRecognizeDialog.adapter
}
}
/**
* 加载识别结果数据并显示弹窗
* @param items 识别到的菜品列表
*/
@SuppressLint("NotifyDataSetChanged")
fun loadData(items: List<CookFoodGoodsEntity>) {
list.clear()
list.addAll(items)
adapter.notifyDataSetChanged()
if (!isShowing) show()
}
}
//package com.shuwei.dish.match.dialog
//
//import android.annotation.SuppressLint
//import android.os.Handler
//import android.os.Looper
//import android.view.LayoutInflater
//import androidx.recyclerview.widget.GridLayoutManager
//import com.google.android.material.bottomsheet.BottomSheetDialog
//import com.shuwei.dish.match.R
//import com.shuwei.dish.match.adapter.FoodAdapter
//import com.shuwei.dish.match.base.BaseActivity
//import com.shuwei.dish.match.databinding.DialogFoodRecognizeBinding
//import com.shuwei.dish.match.entity.GoodsItem
//
///**
// * 菜品识别结果弹窗,继承 BottomSheetDialog 确保只初始化一次
// * @param activity 宿主 Activity
// * @param onDismiss 弹窗关闭时的额外回调(可选)
// * @param onItemSelected 用户点击某一菜品时的回调
// */
//class FoodRecognizeDialog(
// private val activity: BaseActivity,
// private val onDismiss: (() -> Unit)? = null,
// private val onItemSelected: (item: GoodsItem) -> Unit
//) : BottomSheetDialog(activity, R.style.BottomSheet) {
//
// private val binding = DialogFoodRecognizeBinding.inflate(LayoutInflater.from(activity))
// private val list = mutableListOf<GoodsItem>()
// private val adapter = FoodAdapter(list).apply {
// setOnItemClickListener { _, _, position ->
// list[position].isClicked = true
// notifyItemChanged(position)
// Handler(Looper.getMainLooper()).postDelayed({
// onItemSelected(list[position])
// dismiss()
// }, 300)
// }
// }
//
// init {
// setContentView(binding.root)
// setCancelable(true)
// behavior.skipCollapsed = false
// setOnDismissListener { activity.hideStatusBar(); onDismiss?.invoke() }
// binding.recyclerView.run {
// layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
// adapter = this@FoodRecognizeDialog.adapter
// }
// }
//
// /**
// * 加载识别结果数据并显示弹窗
// * @param items 识别到的菜品列表
// */
// @SuppressLint("NotifyDataSetChanged")
// fun loadData(items: List<GoodsItem>) {
// list.clear()
// list.addAll(items)
// adapter.notifyDataSetChanged()
// if (!isShowing) show()
// }
//}
@@ -1,8 +1,6 @@
package com.shuwei.dish.match.dialog
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.view.LayoutInflater
import android.view.View
@@ -15,16 +13,15 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter
import com.shuwei.dish.match.adapter.FoodAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.DialogFoodSearchBinding
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.utils.KeyboardUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
import com.shuwei.dish.match.utils.ext.toast
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
/**
@@ -36,24 +33,24 @@ import kotlinx.coroutines.launch
class FoodSearchDialog(
private val activity: BaseActivity,
private val defGoodsName: String? = null,
private val onItemSelected: (item: CookFoodGoodsEntity) -> Unit
private val onItemSelected: (item: GoodsItem) -> Unit
) : BottomSheetDialog(activity, R.style.BottomSheet) {
private val binding = DialogFoodSearchBinding.inflate(LayoutInflater.from(activity))
private val list = mutableListOf<CookFoodGoodsEntity>().apply {
add(CookFoodGoodsEntity(goodsId = "200001", goodsName = "土豆丝"))
add(CookFoodGoodsEntity(goodsId = "200002", goodsName = "土豆片"))
add(CookFoodGoodsEntity(goodsId = "200003", goodsName = "土豆丁"))
add(CookFoodGoodsEntity(goodsId = "200004", goodsName = "胡萝卜丝"))
add(CookFoodGoodsEntity(goodsId = "200005", goodsName = "胡萝卜片"))
add(CookFoodGoodsEntity(goodsId = "200006", goodsName = "胡萝卜丁"))
add(CookFoodGoodsEntity(goodsId = "200007", goodsName = "黄瓜丝"))
add(CookFoodGoodsEntity(goodsId = "200008", goodsName = "黄瓜片"))
add(CookFoodGoodsEntity(goodsId = "200009", goodsName = "黄瓜丁"))
add(CookFoodGoodsEntity(goodsId = "200010", goodsName = "洋葱丝"))
add(CookFoodGoodsEntity(goodsId = "200011", goodsName = "洋葱丁"))
private val list = mutableListOf<GoodsItem>().apply {
add(GoodsItem(goodsId = "200001", goodsName = "土豆丝"))
add(GoodsItem(goodsId = "200002", goodsName = "土豆片"))
add(GoodsItem(goodsId = "200003", goodsName = "土豆丁"))
add(GoodsItem(goodsId = "200004", goodsName = "胡萝卜丝"))
add(GoodsItem(goodsId = "200005", goodsName = "胡萝卜片"))
add(GoodsItem(goodsId = "200006", goodsName = "胡萝卜丁"))
add(GoodsItem(goodsId = "200007", goodsName = "黄瓜丝"))
add(GoodsItem(goodsId = "200008", goodsName = "黄瓜片"))
add(GoodsItem(goodsId = "200009", goodsName = "黄瓜丁"))
add(GoodsItem(goodsId = "200010", goodsName = "洋葱丝"))
add(GoodsItem(goodsId = "200011", goodsName = "洋葱丁"))
}
private val adapter = GoodsInfoSearchAdapter(list).apply {
private val adapter = FoodAdapter(list).apply {
setOnItemClickListener { _, _, position ->
list[position].isClicked = true
notifyItemChanged(position)
@@ -118,10 +115,12 @@ class FoodSearchDialog(
initObserver()
// 若有默认食材名称,自动填充并触发搜索
if (!defGoodsName.isNullOrBlank()) {
//if (!defGoodsName.isNullOrBlank()) {
binding.etSheetInput.setText(defGoodsName)
searchGoods(binding.etSheetInput)
}
pageNo = 1
getGoodsList()
KeyboardUtil.hideKeyboard(binding.root)
//}
}
/**
@@ -177,7 +176,7 @@ class FoodSearchDialog(
* 将返回数据填充到列表
*/
@SuppressLint("NotifyDataSetChanged")
private fun loadGoodsList(records: MutableList<CookFoodGoodsEntity>?) {
private fun loadGoodsList(records: MutableList<GoodsItem>?) {
finishRefresh()
if (records.isNullOrEmpty()) {
activity.toast("暂未搜索到食材信息")
@@ -1,259 +1,259 @@
package com.shuwei.dish.match.dialog
import android.annotation.SuppressLint
import android.graphics.Typeface
import android.os.Handler
import android.os.Looper
import android.text.SpannableStringBuilder
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import android.text.style.LineHeightSpan
import android.text.style.StyleSpan
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.core.graphics.toColorInt
import androidx.core.view.isEmpty
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.DialogSeasoningSearchBinding
import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.utils.KeyboardUtil
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
import com.shuwei.dish.match.utils.ext.appendText
import com.shuwei.dish.match.utils.ext.buildSpannableString
import com.shuwei.dish.match.utils.ext.dp
import com.shuwei.dish.match.utils.ext.roundedDecimalPlace
import com.shuwei.dish.match.utils.ext.toast
import kotlinx.coroutines.launch
/**
* 调料搜索弹窗,继承 BottomSheetDialog 确保只初始化一次
* @param activity 宿主 Activity
* @param weighIndex 秤的索引
* @param weighAddress 秤的地址,用于监听重量和去皮
* @param clickName 默认填充的调料名称(可选)
* @param onItemSelected 用户点击某一调料时的回调,携带当前重量
*/
class SeasoningSearchDialog(
private val activity: BaseActivity,
private val weighIndex: Int,
private val weighAddress: Int,
private val clickName: String? = null,
private val onItemSelected: (item: SeasoningEntity) -> Unit
) : BottomSheetDialog(activity, R.style.BottomSheet) {
companion object {
const val TAG = "BottomDialog2"
}
private val binding = DialogSeasoningSearchBinding.inflate(LayoutInflater.from(activity))
private val list = mutableListOf<SeasoningEntity>()
private val adapter = SeasoningSearchAdapter(list).apply {
setOnItemClickListener { _, _, position ->
list[position].isClicked = true
notifyItemChanged(position)
Handler(Looper.getMainLooper()).postDelayed({
onItemSelected(list[position].also { it.useWeight = currentWeight })
dismiss()
}, 300)
}
}
private var goodsName: String? = null
private var currentWeight = 0.0
private var pageNo = 1
private val pageSize = 50
init {
setContentView(binding.root)
setCancelable(true)
behavior.skipCollapsed = false
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
window?.setGravity(Gravity.BOTTOM)
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
window?.setWindowAnimations(R.style.DialogSoftInputAnimation)
window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
setOnDismissListener {
// 弹窗关闭时移除重量监听,避免内存泄漏
WeightUtil.removeWeightListener(TAG)
activity.hideStatusBar()
}
// 输入框:清空时重置列表
binding.etSheetInput.run {
hint = "输入调料名称"
addTextChangedListener(
onTextChanged = { text, _, _, _ -> goodsName = text.toString() },
afterTextChanged = {
if (it.isNullOrBlank()) {
list.clear()
adapter.notifyDataSetChanged()
}
}
)
addOnActionSearchListener { searchGoods(this) }
}
// RecyclerView 初始化及滑动冲突处理
binding.recyclerView.let {
it.layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
it.adapter = adapter
it.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(rv, dx, dy)
// 解决 RecyclerView 与 SmartRefreshLayout 滑动冲突
val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top
binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0)
}
})
}
binding.refreshLayout.run {
setEnableRefresh(false)
setEnableLoadMore(false)
setOnRefreshListener { pageNo = 1; getGoodsList() }
setOnLoadMoreListener { getGoodsList() }
}
binding.ivSearch.setOnClickListener { searchGoods(it) }
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
// 去皮按钮
binding.tvClear.setOnClickListener {
Log.d(TAG, "tareTwo: weighAddress=$weighAddress")
WeightUtil.tareTwo(weighAddress)
}
// 监听对应秤的重量变化
WeightUtil.addWeightListener(
weightKey = TAG,
getWeight = { address, _, weight ->
if (weighAddress == address) {
currentWeight = weight.toDouble()
binding.tvWeight.text = getTextSpan(weight)
}
}
)
binding.tvWeight.text = getTextSpan(0.0)
initObserver()
// 若有默认调料名称,自动填充并触发搜索
if (!clickName.isNullOrBlank()) {
binding.etSheetInput.setText(clickName.trim())
pageNo = 1
getGoodsList()
}
}
/**
* 收集 seasoningListState,统一处理 Loading / Success / Error 状态
*/
private fun initObserver() {
activity.lifecycleScope.launch {
activity.repeatOnLifecycle(Lifecycle.State.STARTED) {
activity.netViewModel.seasoningListState.collect { state ->
when (state) {
is UiState.Success -> loadGoodsList(state.data)
is UiState.Error -> {
activity.toast(state.msg)
finishRefresh()
}
else -> {}
}
}
}
}
}
/**
* 触发搜索:校验输入、隐藏键盘、发起请求
*/
private fun searchGoods(v: View) {
if (goodsName.isNullOrBlank()) {
activity.toast("${binding.etSheetInput.hint}")
return
}
pageNo = 1
getGoodsList()
KeyboardUtil.hideKeyboard(v)
}
/**
* 请求调料列表
*/
private fun getGoodsList() {
val param = mutableMapOf<String, Any>(
"goodsType" to "1",
"placeId" to BaseApp.canteenId,
"pageNum" to pageNo,
"pageSize" to pageSize
)
if (!goodsName.isNullOrBlank()) {
param["goodsName"] = goodsName!!
}
activity.netViewModel.querySeasoningList(param)
}
/**
* 将返回数据填充到列表
*/
@SuppressLint("NotifyDataSetChanged")
private fun loadGoodsList(records: MutableList<SeasoningEntity>?) {
finishRefresh()
if (records.isNullOrEmpty()) {
activity.toast("暂未搜索到调料信息")
return
}
if (pageNo == 1) list.clear()
list.addAll(records)
adapter.notifyDataSetChanged()
val isLoadMoreEnable = records.size >= pageSize
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
if (isLoadMoreEnable) pageNo++
}
private fun finishRefresh() {
if (pageNo == 1) binding.refreshLayout.finishRefresh()
else binding.refreshLayout.finishLoadMore()
}
/**
* 生成重量显示的富文本:数值大字加粗,单位小字灰色
*/
fun getTextSpan(weight: Double): SpannableStringBuilder {
val topWeight = if (weight >= 1000) "${(weight / 1000).roundedDecimalPlace(3)}" else "$weight"
val bottomUnit = if (weight >= 1000) "千克" else ""
return buildSpannableString {
appendText(
topWeight,
ForegroundColorSpan("#000000".toColorInt()),
StyleSpan(Typeface.BOLD),
AbsoluteSizeSpan(72, true),
LineHeightSpan { _, _, _, _, _, fm -> fm.descent += 10.dp }
)
append("\n")
appendText(
bottomUnit,
ForegroundColorSpan("#999999".toColorInt()),
AbsoluteSizeSpan(30, true)
)
}
}
}
//package com.shuwei.dish.match.dialog
//
//import android.annotation.SuppressLint
//import android.graphics.Typeface
//import android.os.Handler
//import android.os.Looper
//import android.text.SpannableStringBuilder
//import android.text.style.AbsoluteSizeSpan
//import android.text.style.ForegroundColorSpan
//import android.text.style.LineHeightSpan
//import android.text.style.StyleSpan
//import android.util.Log
//import android.view.Gravity
//import android.view.LayoutInflater
//import android.view.View
//import android.view.ViewGroup
//import android.view.WindowManager
//import androidx.core.graphics.toColorInt
//import androidx.core.view.isEmpty
//import androidx.core.widget.addTextChangedListener
//import androidx.lifecycle.Lifecycle
//import androidx.lifecycle.lifecycleScope
//import androidx.lifecycle.repeatOnLifecycle
//import androidx.recyclerview.widget.GridLayoutManager
//import androidx.recyclerview.widget.RecyclerView
//import com.google.android.material.bottomsheet.BottomSheetDialog
//import com.shuwei.dish.match.R
//import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
//import com.shuwei.dish.match.base.BaseActivity
//import com.shuwei.dish.match.base.BaseApp
//import com.shuwei.dish.match.databinding.DialogSeasoningSearchBinding
//import com.shuwei.dish.match.entity.SeasoningEntity
//import com.shuwei.dish.match.net.UiState
//import com.shuwei.dish.match.utils.KeyboardUtil
//import com.shuwei.dish.match.utils.WeightUtil
//import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
//import com.shuwei.dish.match.utils.ext.appendText
//import com.shuwei.dish.match.utils.ext.buildSpannableString
//import com.shuwei.dish.match.utils.ext.dp
//import com.shuwei.dish.match.utils.ext.roundedDecimalPlace
//import com.shuwei.dish.match.utils.ext.toast
//import kotlinx.coroutines.launch
//
///**
// * 调料搜索弹窗,继承 BottomSheetDialog 确保只初始化一次
// * @param activity 宿主 Activity
// * @param weighIndex 秤的索引
// * @param weighAddress 秤的地址,用于监听重量和去皮
// * @param clickName 默认填充的调料名称(可选)
// * @param onItemSelected 用户点击某一调料时的回调,携带当前重量
// */
//class SeasoningSearchDialog(
// private val activity: BaseActivity,
// private val weighIndex: Int,
// private val weighAddress: Int,
// private val clickName: String? = null,
// private val onItemSelected: (item: SeasoningEntity) -> Unit
//) : BottomSheetDialog(activity, R.style.BottomSheet) {
//
// companion object {
// const val TAG = "BottomDialog2"
// }
//
// private val binding = DialogSeasoningSearchBinding.inflate(LayoutInflater.from(activity))
// private val list = mutableListOf<SeasoningEntity>()
// private val adapter = SeasoningSearchAdapter(list).apply {
// setOnItemClickListener { _, _, position ->
// list[position].isClicked = true
// notifyItemChanged(position)
// Handler(Looper.getMainLooper()).postDelayed({
// onItemSelected(list[position].also { it.useWeight = currentWeight })
// dismiss()
// }, 300)
// }
// }
//
// private var goodsName: String? = null
// private var currentWeight = 0.0
// private var pageNo = 1
// private val pageSize = 50
//
// init {
// setContentView(binding.root)
// setCancelable(true)
// behavior.skipCollapsed = false
// window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
// window?.setGravity(Gravity.BOTTOM)
// window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
// window?.setWindowAnimations(R.style.DialogSoftInputAnimation)
// window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
//
// setOnDismissListener {
// // 弹窗关闭时移除重量监听,避免内存泄漏
// WeightUtil.removeWeightListener(TAG)
// activity.hideStatusBar()
// }
//
// // 输入框:清空时重置列表
// binding.etSheetInput.run {
// hint = "输入调料名称"
// addTextChangedListener(
// onTextChanged = { text, _, _, _ -> goodsName = text.toString() },
// afterTextChanged = {
// if (it.isNullOrBlank()) {
// list.clear()
// adapter.notifyDataSetChanged()
// }
// }
// )
// addOnActionSearchListener { searchGoods(this) }
// }
//
// // RecyclerView 初始化及滑动冲突处理
// binding.recyclerView.let {
// it.layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
// it.adapter = adapter
// it.addOnScrollListener(object : RecyclerView.OnScrollListener() {
// override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
// super.onScrolled(rv, dx, dy)
// // 解决 RecyclerView 与 SmartRefreshLayout 滑动冲突
// val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top
// binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0)
// }
// })
// }
//
// binding.refreshLayout.run {
// setEnableRefresh(false)
// setEnableLoadMore(false)
// setOnRefreshListener { pageNo = 1; getGoodsList() }
// setOnLoadMoreListener { getGoodsList() }
// }
//
// binding.ivSearch.setOnClickListener { searchGoods(it) }
// binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
//
// // 去皮按钮
// binding.tvClear.setOnClickListener {
// Log.d(TAG, "tareTwo: weighAddress=$weighAddress")
// WeightUtil.tareTwo(weighAddress)
// }
//
// // 监听对应秤的重量变化
// WeightUtil.addWeightListener(
// weightKey = TAG,
// getWeight = { address, _, weight ->
// if (weighAddress == address) {
// currentWeight = weight.toDouble()
// binding.tvWeight.text = getTextSpan(weight)
// }
// }
// )
// binding.tvWeight.text = getTextSpan(0.0)
//
// initObserver()
//
// // 若有默认调料名称,自动填充并触发搜索
// if (!clickName.isNullOrBlank()) {
// binding.etSheetInput.setText(clickName.trim())
// pageNo = 1
// getGoodsList()
// }
// }
//
// /**
// * 收集 seasoningListState,统一处理 Loading / Success / Error 状态
// */
// private fun initObserver() {
// activity.lifecycleScope.launch {
// activity.repeatOnLifecycle(Lifecycle.State.STARTED) {
// activity.netViewModel.seasoningListState.collect { state ->
// when (state) {
// is UiState.Success -> loadGoodsList(state.data)
// is UiState.Error -> {
// activity.toast(state.msg)
// finishRefresh()
// }
// else -> {}
// }
// }
// }
// }
// }
//
// /**
// * 触发搜索:校验输入、隐藏键盘、发起请求
// */
// private fun searchGoods(v: View) {
// if (goodsName.isNullOrBlank()) {
// activity.toast("请${binding.etSheetInput.hint}")
// return
// }
// pageNo = 1
// getGoodsList()
// KeyboardUtil.hideKeyboard(v)
// }
//
// /**
// * 请求调料列表
// */
// private fun getGoodsList() {
// val param = mutableMapOf<String, Any>(
// "goodsType" to "1",
// "placeId" to BaseApp.canteenId,
// "pageNum" to pageNo,
// "pageSize" to pageSize
// )
// if (!goodsName.isNullOrBlank()) {
// param["goodsName"] = goodsName!!
// }
// activity.netViewModel.querySeasoningList(param)
// }
//
// /**
// * 将返回数据填充到列表
// */
// @SuppressLint("NotifyDataSetChanged")
// private fun loadGoodsList(records: MutableList<SeasoningEntity>?) {
// finishRefresh()
// if (records.isNullOrEmpty()) {
// activity.toast("暂未搜索到调料信息")
// return
// }
// if (pageNo == 1) list.clear()
// list.addAll(records)
// adapter.notifyDataSetChanged()
// val isLoadMoreEnable = records.size >= pageSize
// binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
// if (isLoadMoreEnable) pageNo++
// }
//
// private fun finishRefresh() {
// if (pageNo == 1) binding.refreshLayout.finishRefresh()
// else binding.refreshLayout.finishLoadMore()
// }
//
// /**
// * 生成重量显示的富文本:数值大字加粗,单位小字灰色
// */
// fun getTextSpan(weight: Double): SpannableStringBuilder {
// val topWeight = if (weight >= 1000) "${(weight / 1000).roundedDecimalPlace(3)}" else "$weight"
// val bottomUnit = if (weight >= 1000) "千克" else "克"
// return buildSpannableString {
// appendText(
// topWeight,
// ForegroundColorSpan("#000000".toColorInt()),
// StyleSpan(Typeface.BOLD),
// AbsoluteSizeSpan(72, true),
// LineHeightSpan { _, _, _, _, _, fm -> fm.descent += 10.dp }
// )
// append("\n")
// appendText(
// bottomUnit,
// ForegroundColorSpan("#999999".toColorInt()),
// AbsoluteSizeSpan(30, true)
// )
// }
// }
//}
@@ -5,28 +5,35 @@ import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.KeyboardShortcutGroup
import android.view.LayoutInflater
import android.view.Menu
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.DialogSeasoningSelectBinding
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.utils.KeyboardUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
import com.shuwei.dish.match.utils.ext.toast
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
/**
* 调料选择弹窗,不含重量显示,继承 BottomSheetDialog 确保只初始化一次
* @param activity 宿主 Activity
* @param clickIndex 点击的调料槽位索引
* @param onItemSelected 用户点击某一调料时的回调
*/
class SeasoningSelectDialog(
@@ -54,6 +61,7 @@ class SeasoningSelectDialog(
private var goodsName: String? = null
private var pageNo = 1
private val pageSize = 50
private var collectJob: Job? = null
init {
setContentView(binding.root)
@@ -66,6 +74,7 @@ class SeasoningSelectDialog(
window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
setOnDismissListener {
collectJob?.cancel()
activity.hideStatusBar()
}
@@ -104,6 +113,20 @@ class SeasoningSelectDialog(
/** 弹窗显示时自动加载默认调料列表,并在 Window.Callback 层提前拦截触摸以解决滑动冲突 */
override fun show() {
super.show()
// 启动 StateFlow 收集,弹窗关闭时由 setOnDismissListener 取消
collectJob = activity.lifecycleScope.launch {
activity.netViewModel.goodsListState.collect { state ->
when (state) {
is UiState.Loading -> {}
is UiState.Success -> loadGoodsList(state.data)
is UiState.Error -> {
activity.toast(state.msg)
finishRefresh()
}
is UiState.Idle -> {}
}
}
}
pageNo = 1
getGoodsList()
// Window.Callback.dispatchTouchEvent 在整个 View 树的 onInterceptTouchEvent 之前执行,
@@ -126,6 +149,18 @@ class SeasoningSelectDialog(
}
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)
}
}
}
@@ -143,50 +178,33 @@ class SeasoningSelectDialog(
}
/**
* 请求调料列表(临时使用本地模拟数据,正式联调时替换为真实接口)
* 请求调料列表
*/
private fun getGoodsList() {
// TODO: 联调时注释掉下方模拟数据,取消注释真实接口调用
val nameList = listOf(
"", "白砂糖","冰糖", "鸡精", "味精", "陈醋", "老抽", "生抽", "番茄酱", "胡椒粉", "孜然", "十三香",
"料酒", "白醋", "蚝油", "辣椒面", "辣椒酱", "豆瓣酱", "淀粉", "", "", "", "香菜", ""
val param = mutableMapOf<String, Any>(
"goodsType" to "1",
"placeId" to BaseApp.canteenId,
"pageNum" to pageNo,
"pageSize" to pageSize
)
val mockData = mutableListOf<SeasoningEntity>()
nameList.forEachIndexed { index, name ->
val padString = "${index + 1}".padStart(2, '0')
mockData.add(SeasoningEntity(goodsId = "1000$padString", goodsName = name))
if (!goodsName.isNullOrBlank()) {
param["goodsName"] = goodsName!!
}
loadGoodsList(mockData)
// val param = mutableMapOf<String, Any>(
// "goodsType" to "1",
// "placeId" to BaseApp.canteenId,
// "pageNum" to pageNo,
// "pageSize" to pageSize
// )
// if (!goodsName.isNullOrBlank()) {
// param["goodsName"] = goodsName!!
// }
// activity.querySeasoningList(param = param, onSuccess = {
// loadGoodsList(it)
// }, onFailure = { _, msg ->
// activity.toast(msg)
// finishRefresh()
// })
activity.netViewModel.queryGoodsList(param)
}
/**
* 将返回数据填充到列表
* 将返回数据转换并填充到列表
*/
@SuppressLint("NotifyDataSetChanged")
private fun loadGoodsList(records: MutableList<SeasoningEntity>?) {
private fun loadGoodsList(records: MutableList<GoodsItem>?) {
finishRefresh()
if (records.isNullOrEmpty()) {
activity.toast("暂未搜索到调料信息")
return
}
if (pageNo == 1) list.clear()
list.addAll(records)
list.addAll(records.map { it.toSeasoningEntity() })
adapter.notifyDataSetChanged()
val isLoadMoreEnable = records.size >= pageSize
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
@@ -1,7 +1,6 @@
package com.shuwei.dish.match.entity
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import com.shuwei.dish.match.db.BaseEntity
import com.shuwei.dish.match.utils.DateTimeUtil
@@ -63,30 +62,28 @@ class CookFoodGoodsEntity(
var relateionType_dictText: String? = "",
var zjmCode: String? = "",
var materId: String? = "",
var isDel: Int = 0,
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now())
) : BaseEntity, Serializable {
constructor() : this(goodsId = "") // 必需的空构造,
constructor() : this(goodsId = "") // 必需的空构造
@Ignore
var isSetFinished: Boolean = false
@Ignore
var isItemClicked: Boolean = false
@Ignore
var isSamplingPage: Boolean = false
@Ignore
var isNewDishType: Boolean = false
@Ignore
var isClicked: Boolean = false
//true-原始数据,false-非原始数据
@Ignore
var isOriginalData: Boolean = true
@Ignore
var foodScore: Int = -1
/**
* 转换为 GoodsItem,用于 UI 层(DishPartAdapter、PrepareFoodActivity 等)
*/
fun toGoodsItem(): GoodsItem = GoodsItem(
goodsId = goodsId,
goodsName = goodsName,
popularName = popularName,
zjmCode = zjmCode,
materId = materId,
materialType = materialType,
useWeight = useWeight,
relateionType = relateionType,
allEdible = allEdible,
goodsOrRelationCode = goodsOrRelationCode
)
}
@@ -0,0 +1,62 @@
package com.shuwei.dish.match.entity
import java.io.Serializable
/**
* 物品信息接口返回的数据类(DTO),不依赖 Room,同时承载 UI 状态字段
* 对应接口:queryGoodsInfoList,食材(goodsType=0)和调料(goodsType=1)共用此结构
*/
data class GoodsItem(
// 接口返回字段
val goodsId: String = "",
val goodsName: String? = null,
val popularName: String? = null,
val zjmCode: String? = null,
val materId: String? = null,
// 从 CookFoodGoodsEntity 迁移的数据字段(DishPartAdapter 显示及转换用)
var materialType: Int = 0,
var useWeight: Double? = 0.0,
var relateionType: Int = 0,
var allEdible: Boolean = true,
var goodsOrRelationCode: String? = "",
// 从 CookFoodGoodsEntity @Ignore 迁移的 UI 状态字段
var isSetFinished: Boolean = false,
var isItemClicked: Boolean = false,
var isSamplingPage: Boolean = false,
var isNewDishType: Boolean = false,
var isClicked: Boolean = false,
// true-原始数据,false-非原始数据(识图新增)
var isOriginalData: Boolean = true,
// 识图匹配分数,-1 表示不显示
var foodScore: Int = -1
) : Serializable {
/**
* 转换为 CookFoodGoodsEntity,用于写入数据库或传给 SubmitFoodActivity
*/
fun toCookFoodGoodsEntity(): CookFoodGoodsEntity = CookFoodGoodsEntity(
goodsId = goodsId,
goodsName = goodsName,
popularName = popularName,
zjmCode = zjmCode,
materId = materId,
materialType = materialType,
useWeight = useWeight,
relateionType = relateionType,
allEdible = allEdible,
goodsOrRelationCode = goodsOrRelationCode
)
/**
* 转换为 SeasoningEntity,用于调料选择弹窗
*/
fun toSeasoningEntity(): SeasoningEntity = SeasoningEntity(
goodsId = goodsId,
goodsName = goodsName,
popularName = popularName,
zjmCode = zjmCode,
materId = materId
)
}
@@ -55,6 +55,10 @@ data class SeasoningEntity(
var relateionType_dictText: String? = "",
var zjmCode: String? = "",
var materId: String? = "",
var sort: Int = 9999,
/**
* isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据
@@ -2,9 +2,8 @@ package com.shuwei.dish.match.net
import com.shuwei.dish.match.base.GlobalData
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.entity.GoodsItem
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
@@ -51,21 +50,12 @@ interface ApiService {
): ApiResponse<MutableList<FoodRecord>?>
/**
* 物品信息
* 物品信息(食材 goodsType=0,调料 goodsType=1 共用此接口)
*/
@POST
suspend fun queryGoodsList(
@Url url:String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/queryGoodsInfoList",
@Body param: MutableMap<String, Any>
): ApiResponse<MutableList<CookFoodGoodsEntity>?>
/**
* 调料信息
*/
@POST
suspend fun querySeasoningList(
@Url url:String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/queryGoodsInfoList",
@Body param: MutableMap<String, Any>
): ApiResponse<MutableList<SeasoningEntity>?>
): ApiResponse<MutableList<GoodsItem>?>
}
@@ -3,9 +3,8 @@ package com.shuwei.dish.match.net
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.entity.GoodsItem
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -87,13 +86,13 @@ class NetViewModel(
}
/**
* 查询物品信息列表的 UI 状态流,UI 层通过 collect 监听
* 查询物品信息列表的 UI 状态流(食材/调料共用)UI 层通过 collect 监听
*/
private val _goodsListState = MutableStateFlow<UiState<MutableList<CookFoodGoodsEntity>?>>(UiState.Idle)
val goodsListState: StateFlow<UiState<MutableList<CookFoodGoodsEntity>?>> = _goodsListState.asStateFlow()
private val _goodsListState = MutableStateFlow<UiState<MutableList<GoodsItem>?>>(UiState.Idle)
val goodsListState: StateFlow<UiState<MutableList<GoodsItem>?>> = _goodsListState.asStateFlow()
/**
* 查询物品信息列表
* 查询物品信息列表(食材 goodsType=0,调料 goodsType=1 共用)
*/
fun queryGoodsList(param: MutableMap<String, Any>) {
viewModelScope.launch {
@@ -101,20 +100,4 @@ class NetViewModel(
_goodsListState.value = repository.queryGoodsList(param)
}
}
/**
* 查询调料信息列表的 UI 状态流,UI 层通过 collect 监听
*/
private val _seasoningListState = MutableStateFlow<UiState<MutableList<SeasoningEntity>?>>(UiState.Idle)
val seasoningListState: StateFlow<UiState<MutableList<SeasoningEntity>?>> = _seasoningListState.asStateFlow()
/**
* 查询调料信息列表
*/
fun querySeasoningList(param: MutableMap<String, Any>) {
viewModelScope.launch {
_seasoningListState.value = UiState.Loading
_seasoningListState.value = repository.querySeasoningList(param)
}
}
}
@@ -1,9 +1,8 @@
package com.shuwei.dish.match.net
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.entity.GoodsItem
/**
* 网络数据仓库,封装所有 ApiService 调用
@@ -76,11 +75,11 @@ class RemoteRepository {
}
/**
* 查询物品信息列表
* 查询物品信息列表(食材 goodsType=0,调料 goodsType=1 共用)
* @param param 查询参数
* @return UiState 包装的结果,Success 携带列表数据,Error 携带错误信息
*/
suspend fun queryGoodsList(param: MutableMap<String, Any>): UiState<MutableList<CookFoodGoodsEntity>?> {
suspend fun queryGoodsList(param: MutableMap<String, Any>): UiState<MutableList<GoodsItem>?> {
return try {
val resp = apiService.queryGoodsList(param = param)
if (resp.isSuccess()) UiState.Success(resp.data)
@@ -91,21 +90,4 @@ class RemoteRepository {
}
}
/**
* 查询调料信息列表
* @param param 查询参数
* @return UiState 包装的结果,Success 携带列表数据,Error 携带错误信息
*/
suspend fun querySeasoningList(param: MutableMap<String, Any>): UiState<MutableList<SeasoningEntity>?> {
return try {
val resp = apiService.querySeasoningList(param = param)
if (resp.isSuccess()) UiState.Success(resp.data)
else UiState.Error(resp.code, resp.msg ?: "")
} catch (e: Exception) {
val ex = getApiException(e)
UiState.Error("-1", ex.errorMsg)
}
}
}
@@ -9,7 +9,7 @@ import com.shuwei.dish.match.R
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityCookingModeBinding
import com.shuwei.dish.match.dialog.CommonDialog
import com.shuwei.dish.match.ui.fragment.DishListFragment
import com.shuwei.dish.match.ui.fragment.FoodListFragment
import com.shuwei.dish.match.utils.KeyboardUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
import com.shuwei.dish.match.utils.ext.gone
@@ -32,10 +32,10 @@ class CookingModeActivity : BaseActivity() {
private var dinnerType: String = "1"
private val fragmentList = mutableListOf<DishListFragment>().apply {
add(DishListFragment.instance("1"))
add(DishListFragment.instance("2"))
add(DishListFragment.instance("3"))
private val fragmentList = mutableListOf<FoodListFragment>().apply {
add(FoodListFragment.instance("1"))
add(FoodListFragment.instance("2"))
add(FoodListFragment.instance("3"))
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -7,11 +7,11 @@ import androidx.activity.addCallback
import androidx.core.content.IntentCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter
import com.shuwei.dish.match.adapter.FoodAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityFoodRecognizeBinding
import com.shuwei.dish.match.dialog.FoodSearchDialog
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.utils.ActivityManager
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.clickWithDebounce
@@ -35,7 +35,7 @@ class FoodRecognizeActivity : BaseActivity() {
const val IS_MANUAL_CANCEL = "isManualCancel"
/** 内存缓存食材列表,避免通过 Intent 序列化传递大数据 */
private var pendingGoodsList: ArrayList<CookFoodGoodsEntity>? = null
private var pendingGoodsList: ArrayList<GoodsItem>? = null
/**
* 启动本页面,通过 onResult 回调返回用户选中的食材
@@ -48,8 +48,8 @@ class FoodRecognizeActivity : BaseActivity() {
activity: BaseActivity,
imageUri: String? = null,
currentWeight: Double = 0.0,
goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(),
finishCallback: (Boolean, CookFoodGoodsEntity?) -> Unit = { _, _ -> }
goodsList: ArrayList<GoodsItem> = arrayListOf(),
finishCallback: (Boolean, GoodsItem?) -> Unit = { _, _ -> }
) {
pendingGoodsList = goodsList
val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply {
@@ -62,7 +62,7 @@ class FoodRecognizeActivity : BaseActivity() {
IntentCompat.getSerializableExtra(
it,
EXTRA_SELECTED_ITEM,
CookFoodGoodsEntity::class.java)
GoodsItem::class.java)
}
finishCallback(isManualCancel, entity)
}
@@ -82,7 +82,7 @@ class FoodRecognizeActivity : BaseActivity() {
private lateinit var binding: ActivityFoodRecognizeBinding
private val list = mutableListOf<CookFoodGoodsEntity>()
private val list = mutableListOf<GoodsItem>()
/** 当前选中的列表位置,-1 表示无选中 */
private var selectedPosition = -1
@@ -108,7 +108,7 @@ class FoodRecognizeActivity : BaseActivity() {
}
}
private val adapter = GoodsInfoSearchAdapter(list).apply {
private val adapter = FoodAdapter(list).apply {
setOnItemClickListener { _, _, position ->
list.forEachIndexed { i, item -> item.isClicked = (i == position) }
// 点击选择时记录当前秤重量
@@ -281,7 +281,7 @@ class FoodRecognizeActivity : BaseActivity() {
* 用新食材替换当前选中项(或追加到列表首位)
*/
@SuppressLint("NotifyDataSetChanged")
private fun updateSelectedFood(item: CookFoodGoodsEntity) {
private fun updateSelectedFood(item: GoodsItem) {
val existIndex = list.indexOfFirst { it.goodsId == item.goodsId }
if (existIndex >= 0) {
list.forEachIndexed { i, e -> e.isClicked = (i == existIndex) }
@@ -9,7 +9,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.FoodRecordAdapter
import com.shuwei.dish.match.adapter.Food2Adapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.ActivityFoodSearchBinding
@@ -44,7 +44,7 @@ class FoodSearchActivity : BaseActivity() {
private var pageType = 0
private var dinnerType = "1"
private val recordAdapter by lazy {
FoodRecordAdapter(list).apply {
Food2Adapter(list).apply {
isStateViewEnable = true
setOnItemClickListener { adapter, view, position ->
if (pageType == 1) {
@@ -13,7 +13,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.DishPartAdapter
import com.shuwei.dish.match.adapter.FoodMaterialAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityPrepareFoodBinding
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
@@ -21,6 +21,7 @@ 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
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.objbox.FoodModule
import com.shuwei.dish.match.utils.AddressUtil
@@ -72,8 +73,6 @@ class PrepareFoodActivity : BaseActivity() {
private var pageFrom: String? = null
private var goodsList: MutableList<CookFoodGoodsEntity>? = null
@Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
@@ -169,8 +168,10 @@ class PrepareFoodActivity : BaseActivity() {
foodRecord.foodName = showFoodName
foodRecord.cookMode = 1
Log.d(TAG, "goToSubmit: goodsList:${list.toJsonString()}")
// 采集模式传给 SubmitFoodActivity 时转换为 CookFoodGoodsEntity
val submitList = list.map { it.toCookFoodGoodsEntity() }.toMutableList()
startActivity<SubmitFoodActivity> {
putExtra(SubmitFoodActivity.GOODS_LIST, list as Serializable)
putExtra(SubmitFoodActivity.GOODS_LIST, submitList as Serializable)
putExtra(SubmitFoodActivity.FOOD_ITEM, foodRecord as Serializable)
}
} else {
@@ -227,14 +228,14 @@ class PrepareFoodActivity : BaseActivity() {
* - list 中不含该 goodsId:直接新增,isNewDishType = true
* - list 中已含该 goodsId:累加 useWeight
*/
private val foodSelectCallback: (CookFoodGoodsEntity) -> Unit = { entity ->
private val foodSelectCallback: (GoodsItem) -> Unit = { entity ->
val existing = list.firstOrNull { it.goodsId == entity.goodsId }
if (existing != null) {
existing.useWeight = (existing.useWeight ?: 0.0) + (entity.useWeight ?: 0.0)
existing.isSetFinished = true
dishPartAdapter.notifyItemChanged(list.indexOf(existing))
materialAdapter.notifyItemChanged(list.indexOf(existing))
} else {
dishPartAdapter.add(entity.also {
materialAdapter.add(entity.also {
it.isNewDishType = true
it.isSetFinished = true
})
@@ -260,16 +261,10 @@ class PrepareFoodActivity : BaseActivity() {
if (goodsList == null) {
goodsList = mutableListOf()
}
// 将 GoodsItem 转换为 CookFoodGoodsEntity 并补充菜品相关字段
list.filter { it.isSetFinished }.forEach {
goodsList?.add(CookFoodGoodsEntity().apply {
goodsList?.add(it.toCookFoodGoodsEntity().apply {
foodId = food!!.foodId
goodsId = it.goodsId
relateionType = it.relateionType
allEdible = it.allEdible
goodsName = it.goodsName
materialType = it.materialType
useWeight = it.useWeight ?: 0.toDouble()
goodsOrRelationCode = it.goodsOrRelationCode
})
}
}
@@ -311,23 +306,6 @@ class PrepareFoodActivity : BaseActivity() {
}
private fun getDishDetail() {
// //649
// val url = "${UrlConfig.DISH_DETAIL}?foodId=${food?.foodId}"
// HttpUtil.get(
// url = url,
// doSuccess = {
// val json = it.toJsonString()
// Log.d(TAG, "getDishDetail: json:$json")
// val detail: CookFoodEntity? = json.toObject<CookFoodEntity>()
// if (detail == null) {
// toast("查询菜品信息为空")
// return@get
// }
// loadDishDetail(detail)
// }) { code, msg ->
// toast(msg)
// }
netViewModel.getFoodDetail(food?.foodId ?: "")
}
@@ -338,22 +316,20 @@ class PrepareFoodActivity : BaseActivity() {
if (voList.isNullOrEmpty()) {
return
}
//筛选出主材和辅材
// 筛选出主材和辅材,转换为 GoodsItemuseWeight 置零)
val tempData = voList.filter { it.materialType == 1 || it.materialType == 2 }
.apply {
forEach { it.useWeight = 0.toDouble() }
}
.map { it.toGoodsItem().also { item -> item.useWeight = 0.0 } }
list.clear()
firstReqSize = list.size
list.addAll(tempData)
dishPartAdapter.notifyDataSetChanged()
materialAdapter.notifyDataSetChanged()
}
private var firstReqSize = 0
private var isDetailLoaded = false
private val list = mutableListOf<CookFoodGoodsEntity>()
private val dishPartAdapter by lazy {
DishPartAdapter(list = list).apply {
private val list = mutableListOf<GoodsItem>()
private val materialAdapter by lazy {
FoodMaterialAdapter(list = list).apply {
val adapter = this
onItemClick = { positon ->
list.forEachIndexed { index, entity ->
@@ -399,15 +375,15 @@ class PrepareFoodActivity : BaseActivity() {
.setContent("确定删除食材「${item.goodsName}」吗?")
.setNegativeButton("取消")
.setPositiveButton("删除") {
dishPartAdapter.removeAt(position)
materialAdapter.removeAt(position)
toast("已删除")
}.show()
}
// item 点击事件
setOnItemClickListener { _, position ->
dishPartAdapter.onItemClick?.invoke(position)
materialAdapter.onItemClick?.invoke(position)
}
adapter = dishPartAdapter
adapter = materialAdapter
}
}
@@ -536,7 +512,7 @@ class PrepareFoodActivity : BaseActivity() {
private fun queryFood(list: List<FoodModule.IdNameScore>) {
Log.d(TAG, "takePhoto queryFood, list=$list")
//调用接口成功,返回食材列表设置isTakingPhoto.set(false),暂时写死数据
val foodList = mutableListOf<CookFoodGoodsEntity>()
val foodList = mutableListOf<GoodsItem>()
list.forEachIndexed { index, food ->
Log.d(TAG, "takePhoto queryFood, index=${index},food=$food")
var foodName = ""
@@ -550,10 +526,9 @@ class PrepareFoodActivity : BaseActivity() {
}
}
foodList.add(
CookFoodGoodsEntity(
GoodsItem(
goodsId = foodId,
goodsName = foodName,
foodId = index.toString()
goodsName = foodName
).also {
it.foodScore = ((1 - food.score) * 10000).toInt()
it.isOriginalData = false
@@ -570,7 +545,7 @@ class PrepareFoodActivity : BaseActivity() {
/**
* 加载识别结果页面
*/
private fun loadRecognizeResultPage(foodList: List<CookFoodGoodsEntity>) {
private fun loadRecognizeResultPage(foodList: List<GoodsItem>) {
if (currentWeight < WEIGHT_RECOGNIZE_VALUE) {
Log.d(TAG, "takePhoto queryFood, 已取消")
toast("已取消")
@@ -14,7 +14,7 @@ import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.FoodListAdapter
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.base.BaseFragment
import com.shuwei.dish.match.databinding.FragmentDishListBinding
import com.shuwei.dish.match.databinding.FragmentFoodListBinding
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
import com.shuwei.dish.match.dialog.CommonDialog
import com.shuwei.dish.match.entity.CookFoodEntity
@@ -31,14 +31,14 @@ import java.io.Serializable
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
class DishListFragment : BaseFragment<FragmentDishListBinding>() {
class FoodListFragment : BaseFragment<FragmentFoodListBinding>() {
companion object {
private const val TAG = "DishListFragment"
const val DINNER_TYPE = "dinnerType"
public fun instance(dinnerType: String): DishListFragment {
return DishListFragment().apply {
public fun instance(dinnerType: String): FoodListFragment {
return FoodListFragment().apply {
arguments = Bundle().apply {
putString(DINNER_TYPE, dinnerType)
}
@@ -85,8 +85,8 @@ class DishListFragment : BaseFragment<FragmentDishListBinding>() {
override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentDishListBinding {
return FragmentDishListBinding.inflate(inflater, container, false)
): FragmentFoodListBinding {
return FragmentFoodListBinding.inflate(inflater, container, false)
}
override fun initialize() {
@@ -17,13 +17,13 @@ import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.GridLayoutManager
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.VectorCollectionAdapter
import com.shuwei.dish.match.adapter.FoodSearchAdapter
import com.shuwei.dish.match.adapter.Food3Adapter
import com.shuwei.dish.match.base.BaseFragment
import com.shuwei.dish.match.databinding.FragmentVectorCollectionBinding
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
import com.shuwei.dish.match.dialog.Loading
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodCollectionBean
import com.shuwei.dish.match.entity.GoodsItem
import com.shuwei.dish.match.objbox.FoodModule
import com.shuwei.dish.match.ui.CollectedFoodActivity
import com.shuwei.dish.match.ui.SingleFragmentActivity
@@ -53,28 +53,28 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
}
}
private val searchList = mutableListOf<CookFoodGoodsEntity>().apply {
add(CookFoodGoodsEntity(goodsId = "200001", goodsName = "土豆丝"))
add(CookFoodGoodsEntity(goodsId = "200002", goodsName = "土豆片"))
add(CookFoodGoodsEntity(goodsId = "200003", goodsName = "土豆丁"))
add(CookFoodGoodsEntity(goodsId = "200004", goodsName = "胡萝卜丝"))
add(CookFoodGoodsEntity(goodsId = "200005", goodsName = "胡萝卜片"))
add(CookFoodGoodsEntity(goodsId = "200006", goodsName = "胡萝卜丁"))
add(CookFoodGoodsEntity(goodsId = "200007", goodsName = "黄瓜丝"))
add(CookFoodGoodsEntity(goodsId = "200008", goodsName = "黄瓜片"))
add(CookFoodGoodsEntity(goodsId = "200009", goodsName = "黄瓜丁"))
add(CookFoodGoodsEntity(goodsId = "200010", goodsName = "洋葱丝"))
add(CookFoodGoodsEntity(goodsId = "200011", goodsName = "洋葱丁"))
private val searchList = mutableListOf<GoodsItem>().apply {
add(GoodsItem(goodsId = "200001", goodsName = "土豆丝"))
add(GoodsItem(goodsId = "200002", goodsName = "土豆片"))
add(GoodsItem(goodsId = "200003", goodsName = "土豆丁"))
add(GoodsItem(goodsId = "200004", goodsName = "胡萝卜丝"))
add(GoodsItem(goodsId = "200005", goodsName = "胡萝卜片"))
add(GoodsItem(goodsId = "200006", goodsName = "胡萝卜丁"))
add(GoodsItem(goodsId = "200007", goodsName = "黄瓜丝"))
add(GoodsItem(goodsId = "200008", goodsName = "黄瓜片"))
add(GoodsItem(goodsId = "200009", goodsName = "黄瓜丁"))
add(GoodsItem(goodsId = "200010", goodsName = "洋葱丝"))
add(GoodsItem(goodsId = "200011", goodsName = "洋葱丁"))
}
private var checkedItem: CookFoodGoodsEntity? = null
private var checkedItem: GoodsItem? = null
private val searchAdapter by lazy {
FoodSearchAdapter(searchList).apply {
Food3Adapter(searchList).apply {
setOnItemClickListener { adapter, view, position ->
searchList.forEachIndexed { index, item -> item.isClicked = index == position }
checkedItem = searchList[position]
notifyDataSetChanged()
checkedItem?.let {
selectedFoodId = it.foodId
selectedFoodId = it.goodsId
selectedFoodName = it.goodsName
}
}
@@ -1,66 +1,66 @@
package com.shuwei.dish.match.utils
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.adapter.DishPartAdapter
import com.shuwei.dish.match.adapter.FoodListAdapter
class SwipeCallback(
private val adapter: BaseQuickAdapter<*,*>,
private val callback: (position: Int) -> Unit = {}
) :
ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false // 不允许拖动,只允许滑动删除
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.getBindingAdapterPosition()
callback(position)
}
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val position = viewHolder.getBindingAdapterPosition()
if (position < 0) {
return makeMovementFlags(0, 0)
}
// 默认的拖动和滑动方向
val dragFlags = 0
// ItemTouchHelper.UP or ItemTouchHelper.DOWN
var swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
if (adapter is FoodListAdapter) {
if (adapter.items.isEmpty()) {
return makeMovementFlags(0, 0)
}
if (!adapter.items[position].isCooking) {
swipeFlags = 0
}
} else if (adapter is DishPartAdapter) {
if (adapter.items.isEmpty()) {
return makeMovementFlags(0, 0)
}
if (!adapter.items[position].isNewDishType) {
swipeFlags = 0
}
}
// 根据viewHolder的逻辑判断是否允许滑动
val count = recyclerView.adapter?.itemCount ?:0
//BaseQuickAdapter中空布局使用的ViewHolder是这个import com.chad.library.adapter4.viewholder.StateLayoutVH
if (count == 1 && viewHolder !is QuickViewHolder) {
swipeFlags = 0 // 不允许滑动
}
return makeMovementFlags(dragFlags, swipeFlags)
}
}
//package com.shuwei.dish.match.utils
//
//import androidx.recyclerview.widget.ItemTouchHelper
//import androidx.recyclerview.widget.RecyclerView
//import com.chad.library.adapter4.BaseQuickAdapter
//import com.chad.library.adapter4.viewholder.QuickViewHolder
//import com.shuwei.dish.match.adapter.DishPartAdapter
//import com.shuwei.dish.match.adapter.FoodListAdapter
//
//class SwipeCallback(
// private val adapter: BaseQuickAdapter<*,*>,
// private val callback: (position: Int) -> Unit = {}
//) :
// ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
// override fun onMove(
// recyclerView: RecyclerView,
// viewHolder: RecyclerView.ViewHolder,
// target: RecyclerView.ViewHolder
// ): Boolean {
// return false // 不允许拖动,只允许滑动删除
// }
//
// override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// val position = viewHolder.getBindingAdapterPosition()
// callback(position)
// }
//
// override fun getMovementFlags(
// recyclerView: RecyclerView,
// viewHolder: RecyclerView.ViewHolder
// ): Int {
// val position = viewHolder.getBindingAdapterPosition()
// if (position < 0) {
// return makeMovementFlags(0, 0)
// }
// // 默认的拖动和滑动方向
// val dragFlags = 0
// // ItemTouchHelper.UP or ItemTouchHelper.DOWN
// var swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
//
// if (adapter is FoodListAdapter) {
// if (adapter.items.isEmpty()) {
// return makeMovementFlags(0, 0)
// }
// if (!adapter.items[position].isCooking) {
// swipeFlags = 0
// }
// } else if (adapter is DishPartAdapter) {
// if (adapter.items.isEmpty()) {
// return makeMovementFlags(0, 0)
// }
// if (!adapter.items[position].isNewDishType) {
// swipeFlags = 0
// }
// }
//
// // 根据viewHolder的逻辑判断是否允许滑动
// val count = recyclerView.adapter?.itemCount ?:0
// //BaseQuickAdapter中空布局使用的ViewHolder是这个import com.chad.library.adapter4.viewholder.StateLayoutVH
// if (count == 1 && viewHolder !is QuickViewHolder) {
// swipeFlags = 0 // 不允许滑动
// }
// return makeMovementFlags(dragFlags, swipeFlags)
// }
//
//}
@@ -46,7 +46,7 @@
app:spanCount="2"
tools:itemCount="4"
android:background="@drawable/shape_white_12_corners"
tools:listitem="@layout/list_item_search_goods_info"
tools:listitem="@layout/list_item_food"
tools:visibility="visible" />
<View
@@ -82,7 +82,7 @@
android:layout_marginBottom="20dp"
android:overScrollMode="never"
tools:itemCount="3"
tools:listitem="@layout/list_item_dish_record" />
tools:listitem="@layout/list_item_food2" />
<com.scwang.smart.refresh.footer.ClassicsFooter
android:layout_width="match_parent"
@@ -100,7 +100,7 @@
android:overScrollMode="never"
android:scrollbars="vertical"
tools:itemCount="3"
tools:listitem="@layout/list_item_dish_cook" />
tools:listitem="@layout/list_item_food_material" />
</LinearLayout>
@@ -39,6 +39,6 @@
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:itemCount="10"
tools:listitem="@layout/list_item_search_goods_info" />
tools:listitem="@layout/list_item_food" />
</LinearLayout>
@@ -89,7 +89,7 @@
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:itemCount="10"
tools:listitem="@layout/list_item_search_goods_info" />
tools:listitem="@layout/list_item_food" />
<com.scwang.smart.refresh.footer.ClassicsFooter
android:layout_width="match_parent"
@@ -122,7 +122,7 @@
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:itemCount="10"
tools:listitem="@layout/list_item_search_goods_info" />
tools:listitem="@layout/list_item_food" />
<com.scwang.smart.refresh.footer.ClassicsFooter
android:layout_width="match_parent"
@@ -88,7 +88,7 @@
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:itemCount="10"
tools:listitem="@layout/list_item_search_goods_info" />
tools:listitem="@layout/list_item_food" />
<com.scwang.smart.refresh.footer.ClassicsFooter
android:layout_width="match_parent"
@@ -189,7 +189,7 @@
tools:itemCount="10"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/list_item_search_food"/>
tools:listitem="@layout/list_item_food3"/>
<TextView
android:id="@+id/btnSave"