feat(pack): 添加净菜包装功能和食材追溯界面
- 在activity_pack.xml中新增rvFormList RecyclerView用于表单展示 - 在TaskDetailActivity中实现食材明细的增删改查功能 - 添加InboundIngredientSearchDialog用于食材溯源码搜索 - 更新NetViewModelV2中addComboTask方法参数类型 - 添加NutCleanInboundIngredientVO数据模型用于已入库净菜食材 - 在PackActivity中实现包装表单动态加载和设备选择功能 - 添加用量计算和重量识别相关UI组件 - 优化FormFieldAdapter适配器去除Activity上下文依赖 - 新增formatDecimal扩展函数用于格式化用量字符串 - 实现TaskDetailAdapter中食材项的选中和清除逻辑 - 添加组合约束的API接口定义和数据传输对象修改 - 重构表单字段配置为动态加载模式支持多种输入类型 - 添加删除确认对话框提升用户体验和数据安全性 - 优化布局文件结构调整控件间距和显示效果 - 添加设备选项加载回调机制确保数据实时更新 - 实现重量监听器的生命周期管理防止内存泄漏 - 更新界面跳转逻辑支持餐品选择和详情展示 - 修复多个布局和代码中的空格及格式问题 - 添加必要的依赖导入和常量定义完善功能模块
This commit is contained in:
@@ -32,11 +32,9 @@ import kotlin.math.min
|
||||
* 动态表单字段 Adapter,配合 FlexboxLayoutManager 实现两列自动换行布局
|
||||
* 单一 item 布局,通过 visibility 控制 TEXT/INTEGER/DECIMAL/FIXED/DROPDOWN/DATE_PICKER 六种类型渲染
|
||||
*
|
||||
* @param activityContext Activity context,用于创建 DatePickerDialog
|
||||
* @param fields 表单字段列表(hidden=false 的字段,由外部过滤后传入)
|
||||
*/
|
||||
class FormFieldAdapter(
|
||||
private val activityContext: Context,
|
||||
fields: MutableList<FormField>
|
||||
) : BaseQuickAdapter<FormField, FormFieldAdapter.VH>(fields) {
|
||||
|
||||
@@ -131,7 +129,7 @@ class FormFieldAdapter(
|
||||
v.hideKeyboard()
|
||||
val popHeight = min(item.options.size.coerceAtLeast(1), 4) * 71.dp
|
||||
DropdownPopup(
|
||||
context = activityContext,
|
||||
context = context,
|
||||
list = item.options.toMutableList(),
|
||||
popWidth = v.width,
|
||||
popHeight = popHeight
|
||||
@@ -162,7 +160,7 @@ class FormFieldAdapter(
|
||||
v.hideKeyboard()
|
||||
val cal = Calendar.getInstance()
|
||||
DatePickerDialog(
|
||||
activityContext,
|
||||
context,
|
||||
{ _, year, month, dayOfMonth ->
|
||||
val selected = Calendar.getInstance().apply {
|
||||
set(year, month, dayOfMonth, 0, 0, 0)
|
||||
|
||||
@@ -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.R
|
||||
import com.shuwei.dish.match.databinding.ListItemInboundIngredientBinding
|
||||
import com.shuwei.dish.match.model.NutCleanInboundIngredientVO
|
||||
import com.shuwei.dish.match.utils.ext.formatDecimal
|
||||
|
||||
/**
|
||||
* 已入库净菜食材列表适配器
|
||||
*/
|
||||
class InboundIngredientAdapter(list: MutableList<NutCleanInboundIngredientVO>) :
|
||||
BaseQuickAdapter<NutCleanInboundIngredientVO, InboundIngredientAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemInboundIngredientBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val binding = ListItemInboundIngredientBinding.inflate(
|
||||
LayoutInflater.from(context), parent, false
|
||||
)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: NutCleanInboundIngredientVO?) {
|
||||
item ?: return
|
||||
holder.binding.run {
|
||||
tvIngredientName.text = item.ingredientName ?: "-"
|
||||
|
||||
val traceCode = item.traceCode.takeUnless { it.isNullOrBlank() } ?: "-"
|
||||
tvTraceCode.text = "溯源码:$traceCode"
|
||||
|
||||
val stock = item.currentStock.takeUnless { it.isNullOrBlank() } ?: "0"
|
||||
tvStock.text = "库存:${stock.formatDecimal()}kg"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,50 @@
|
||||
package com.shuwei.dish.match.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.toColorInt
|
||||
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.ListItemTaskDetailBinding
|
||||
import com.shuwei.dish.match.model.NutComboTaskItemDetail
|
||||
import com.shuwei.dish.match.utils.ext.formatDecimal
|
||||
import com.shuwei.dish.match.utils.ext.gone
|
||||
import com.shuwei.dish.match.utils.ext.visible
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 组配任务详情食材列表适配器
|
||||
* Adapter for displaying ingredient list in task details
|
||||
*/
|
||||
class TaskDetailAdapter(list: MutableList<NutComboTaskItemDetail>) :
|
||||
BaseQuickAdapter<NutComboTaskItemDetail, TaskDetailAdapter.VH>(list) {
|
||||
|
||||
// ViewHolder class for holding the view bindings
|
||||
inner class VH(var binding: ListItemTaskDetailBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
/**
|
||||
* 格式化用量字符串,去掉小数点后末尾的0
|
||||
* Called by RecyclerView to display the item at the specified position.
|
||||
* This method updates the ViewHolder's contents with the item's data.
|
||||
*
|
||||
* @param holder The ViewHolder to be updated
|
||||
* @param position The position of the item in the adapter
|
||||
* @param item The NutComboTaskItemDetail to be displayed
|
||||
*/
|
||||
private fun formatQty(value: String?): String {
|
||||
if (value.isNullOrBlank()) return "0"
|
||||
return try {
|
||||
BigDecimal(value).stripTrailingZeros().toPlainString()
|
||||
} catch (e: Exception) {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: VH,
|
||||
position: Int,
|
||||
item: NutComboTaskItemDetail?
|
||||
) {
|
||||
item?:return
|
||||
holder.binding.apply {
|
||||
tvIngredientName.text = item?.ingredientName
|
||||
// Set ingredient name
|
||||
tvIngredientName.text = item.ingredientName
|
||||
|
||||
// 食材类别
|
||||
val classText = when (item?.ingredientClass) {
|
||||
val classText = when (item.ingredientClass) {
|
||||
1 -> "主材"
|
||||
2 -> "辅材"
|
||||
3 -> "调料"
|
||||
@@ -49,16 +52,32 @@ class TaskDetailAdapter(list: MutableList<NutComboTaskItemDetail>) :
|
||||
}
|
||||
tvIngredientClass.text = classText
|
||||
|
||||
val unit = item?.unit ?: "g"
|
||||
tvPerPortionQty.text = "每份用量:${formatQty(item?.perPortionQty)}${unit}"
|
||||
tvActualQty.text = "实际用量:${formatQty(item?.actualQty)}${unit}"
|
||||
tvTotalQty.text = "合计用量:${formatQty(item?.totalQty)}${unit}"
|
||||
val unit = item.unit ?: "g"
|
||||
tvPerPortionQty.text = "每份用量:${item.perPortionQty.formatDecimal()}${unit}"
|
||||
tvActualQty.run {
|
||||
if (item.actualQty.isNullOrBlank()) {
|
||||
setTextColor("#999999".toColorInt())
|
||||
text = "-"
|
||||
} else {
|
||||
setTextColor("#00BC71".toColorInt())
|
||||
text = item.actualQty.formatDecimal() + unit
|
||||
}
|
||||
}
|
||||
// tvTotalQty.text = "合计用量:${item?.totalQty.formatDecimal()}${unit}"
|
||||
|
||||
tvTraceCode.visible()
|
||||
tvTraceCode.text = "溯源码:${(item?.traceCode ?:"").ifBlank { "-" }}"
|
||||
tvTraceCode.text = "溯源码:${(item.traceCode ?:"").ifBlank { "-" }}"
|
||||
|
||||
// val hasWeight = item.actualQty.isNullOrBlank().not()
|
||||
// ivClearIcon.imageTintList = if (hasWeight) null else ColorStateList.valueOf("#BFBFBF".toColorInt())
|
||||
|
||||
ivOperateIcon.setImageResource(
|
||||
if (item.isSetFinished == true) R.drawable.ic_dish_selected
|
||||
else R.drawable.ic_dish_unselected
|
||||
)
|
||||
|
||||
root.setBackgroundResource(
|
||||
if (item?.isClicked == true) R.drawable.shape_item_cook_dish
|
||||
if (item.isClicked == true) R.drawable.shape_item_cook_dish
|
||||
else R.drawable.shape_white_fb_15_corners
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.shuwei.dish.match.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Rect
|
||||
import android.text.Editable
|
||||
import android.view.KeyboardShortcutGroup
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.chad.library.adapter4.util.setOnDebouncedItemClick
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.InboundIngredientAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.DialogInboundIngredientSearchBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
||||
import com.shuwei.dish.match.model.NutCleanInboundIngredientVO
|
||||
import com.shuwei.dish.match.net.NetViewModelV2
|
||||
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
|
||||
|
||||
/**
|
||||
* 已入库净菜食材搜索弹窗
|
||||
* @param activity 宿主 Activity
|
||||
* @param defIngredientName 默认填充的食材名称(可选)
|
||||
* @param onItemSelected 用户点击某项食材时的回调
|
||||
*/
|
||||
class InboundIngredientSearchDialog(
|
||||
private val activity: BaseActivity,
|
||||
private val viewModelV2: NetViewModelV2,
|
||||
private val defIngredientName: String? = null,
|
||||
private val onItemSelected: (item: NutCleanInboundIngredientVO) -> Unit
|
||||
) : BottomSheetDialog(activity, R.style.BottomSheet) {
|
||||
|
||||
private val binding = DialogInboundIngredientSearchBinding.inflate(LayoutInflater.from(activity))
|
||||
private val list = mutableListOf<NutCleanInboundIngredientVO>()
|
||||
private val adapter = InboundIngredientAdapter(list).apply {
|
||||
isStateViewEnable = true
|
||||
setOnDebouncedItemClick { _, _, position ->
|
||||
val item = list.getOrNull(position) ?: return@setOnDebouncedItemClick
|
||||
onItemSelected(item)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private var keyword: String? = null
|
||||
|
||||
init {
|
||||
setContentView(binding.root)
|
||||
setCancelable(true)
|
||||
behavior.skipCollapsed = false
|
||||
setOnDismissListener { activity.hideStatusBar() }
|
||||
|
||||
binding.tvSheetName.text = "已入库净菜检索"
|
||||
|
||||
// 输入框:清空时重置列表
|
||||
binding.etSheetInput.run {
|
||||
hint = "输入食材名称"
|
||||
addTextChangedListener(
|
||||
onTextChanged = { text, _, _, _ -> keyword = text.toString() },
|
||||
afterTextChanged = { it: Editable? ->
|
||||
if (it.isNullOrBlank()) {
|
||||
list.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
)
|
||||
addOnActionSearchListener { searchIngredient(this) }
|
||||
}
|
||||
|
||||
// RecyclerView 初始化
|
||||
binding.recyclerView.run {
|
||||
layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
|
||||
adapter = this@InboundIngredientSearchDialog.adapter
|
||||
}
|
||||
|
||||
binding.refreshLayout.run {
|
||||
setEnableRefresh(true)
|
||||
setEnableLoadMore(false)
|
||||
setOnRefreshListener { getIngredientList() }
|
||||
}
|
||||
|
||||
binding.ivSearch.setOnClickListener { searchIngredient(it) }
|
||||
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
|
||||
|
||||
// 若有默认食材名称,自动填充并触发搜索
|
||||
if (!defIngredientName.isNullOrBlank()) {
|
||||
binding.etSheetInput.setText(defIngredientName)
|
||||
getIngredientList()
|
||||
KeyboardUtil.hideKeyboard(binding.root)
|
||||
}
|
||||
}
|
||||
|
||||
/** 弹窗显示时在 Window.Callback 层提前拦截触摸,解决 BottomSheet 拖拽与列表滑动的手势冲突 */
|
||||
override fun show() {
|
||||
super.show()
|
||||
val originalCallback = window?.callback ?: return
|
||||
window?.callback = object : Window.Callback by originalCallback {
|
||||
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
val rect = Rect()
|
||||
binding.refreshLayout.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发搜索:校验输入、隐藏键盘、发起请求
|
||||
*/
|
||||
private fun searchIngredient(v: View) {
|
||||
if (keyword.isNullOrBlank()) {
|
||||
activity.toast("请输入食材名称")
|
||||
return
|
||||
}
|
||||
getIngredientList()
|
||||
KeyboardUtil.hideKeyboard(v)
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求食材列表,使用回调方式发起
|
||||
*/
|
||||
private fun getIngredientList() {
|
||||
viewModelV2.getInboundIngredientOptionsWithCallback(
|
||||
keyword = keyword.takeUnless { it.isNullOrBlank() },
|
||||
onLoading = {},
|
||||
onResult = { state ->
|
||||
when (state) {
|
||||
is UiState.Success -> loadIngredientList(state.data)
|
||||
is UiState.Error -> {
|
||||
activity.toast(state.msg)
|
||||
binding.refreshLayout.finishRefresh()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将返回数据填充到列表
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun loadIngredientList(records: List<NutCleanInboundIngredientVO>?) {
|
||||
binding.refreshLayout.finishRefresh()
|
||||
if (records.isNullOrEmpty()) {
|
||||
loadEmptyView()
|
||||
return
|
||||
}
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
|
||||
list.clear()
|
||||
list.addAll(records)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private var emptyViewBinding: LayoutEmptyViewBinding? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun loadEmptyView() {
|
||||
try {
|
||||
list.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
if (emptyViewBinding == null) {
|
||||
emptyViewBinding = LayoutEmptyViewBinding.inflate(
|
||||
LayoutInflater.from(activity),
|
||||
binding.recyclerView,
|
||||
false
|
||||
)
|
||||
}
|
||||
emptyViewBinding?.let {
|
||||
it.tvContent.text = "暂无数据"
|
||||
it.tvSubContent.text = "未查询到已入库净菜信息"
|
||||
it.root.layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
|
||||
binding.recyclerView.post {
|
||||
it.root.minimumHeight = binding.recyclerView.height
|
||||
adapter.stateView = it.root
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,18 +207,18 @@ data class NutComboTaskVO(
|
||||
*/
|
||||
@Parcelize
|
||||
data class NutComboTaskDetailVO(
|
||||
val id: Long?,
|
||||
val taskNo: String?,
|
||||
val planDate: String?,
|
||||
val canteenId: Long?,
|
||||
val mealType: Int?,
|
||||
val foodId: Long?,
|
||||
val targetDish: String?,
|
||||
val spec: String?,
|
||||
val portions: Int?,
|
||||
val comboStatus: Int?,
|
||||
val ownerName: String?,
|
||||
val items: List<NutComboTaskItemDetail>?
|
||||
var id: Long? = null,
|
||||
var taskNo: String? = null,
|
||||
var planDate: String? = null,
|
||||
var canteenId: Long? = null,
|
||||
var mealType: Int? = null,
|
||||
var foodId: Long? = null,
|
||||
var targetDish: String? = null,
|
||||
var spec: String? = null,
|
||||
var portions: Int? = null,
|
||||
var comboStatus: Int? = null,
|
||||
var ownerName: String? = null,
|
||||
var items: List<NutComboTaskItemDetail>? = null,
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
@@ -232,10 +232,23 @@ data class NutComboTaskItemDetail(
|
||||
val ingredientClass: Int?,
|
||||
var traceCode: String?,
|
||||
val perPortionQty: String?,
|
||||
val actualQty: String?,
|
||||
var actualQty: String?,
|
||||
val totalQty: String?,
|
||||
val unit: String?,
|
||||
var isClicked: Boolean? = false,
|
||||
var isSetFinished: Boolean? = false,
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* 已入库净菜食材VO - 按食材名称查询已入库有库存的净菜记录
|
||||
*/
|
||||
@Parcelize
|
||||
data class NutCleanInboundIngredientVO(
|
||||
val id: Long?,
|
||||
val materId: Long?,
|
||||
val ingredientName: String?,
|
||||
val traceCode: String?,
|
||||
val currentStock: String?
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.shuwei.dish.match.model.NutSupHygieneIngredientOptionVO
|
||||
import com.shuwei.dish.match.model.NutProdComboVideoVO
|
||||
import com.shuwei.dish.match.model.NutProdComboTaskDTO
|
||||
import com.shuwei.dish.match.model.IdDTO
|
||||
import com.shuwei.dish.match.model.NutCleanInboundIngredientVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskDetailVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskVO
|
||||
import retrofit2.http.Body
|
||||
@@ -77,6 +78,15 @@ interface ApiServiceV2 {
|
||||
@Query("traceCode") traceCode: String
|
||||
): ApiResponse<List<NutSupHygieneIngredientOptionVO>>
|
||||
|
||||
/**
|
||||
* 按食材名称查询已入库有库存的净菜记录
|
||||
*/
|
||||
@GET
|
||||
suspend fun getInboundIngredientOptions(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/inbound-ingredient-options",
|
||||
@Query("keyword") keyword: String? = null
|
||||
): ApiResponse<List<NutCleanInboundIngredientVO>>
|
||||
|
||||
// ========== 二、净菜组配接口 ==========
|
||||
|
||||
/**
|
||||
@@ -85,7 +95,7 @@ interface ApiServiceV2 {
|
||||
@POST
|
||||
suspend fun addComboTask(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/add",
|
||||
@Body param: NutProdComboTaskDTO
|
||||
@Body param: NutComboTaskDetailVO
|
||||
): ApiResponse<Unit>
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.net
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.shuwei.dish.match.model.NutCanteenVO
|
||||
import com.shuwei.dish.match.model.NutCleanInboundIngredientVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskDetailVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskVO
|
||||
import com.shuwei.dish.match.model.NutDictItemVO
|
||||
@@ -28,7 +29,8 @@ class NetViewModelV2(
|
||||
|
||||
// 食堂下拉列表
|
||||
private val _canteenOptionsState = MutableStateFlow<UiState<List<NutCanteenVO>?>>(UiState.Idle)
|
||||
val canteenOptionsState: StateFlow<UiState<List<NutCanteenVO>?>> = _canteenOptionsState.asStateFlow()
|
||||
val canteenOptionsState: StateFlow<UiState<List<NutCanteenVO>?>> =
|
||||
_canteenOptionsState.asStateFlow()
|
||||
|
||||
fun getCanteenOptions(keyword: String? = null) {
|
||||
viewModelScope.launch {
|
||||
@@ -49,8 +51,10 @@ class NetViewModelV2(
|
||||
// }
|
||||
|
||||
// 食材下拉列表
|
||||
private val _materOptionsState = MutableStateFlow<UiState<List<NutMaterOptionVO>?>>(UiState.Idle)
|
||||
val materOptionsState: StateFlow<UiState<List<NutMaterOptionVO>?>> = _materOptionsState.asStateFlow()
|
||||
private val _materOptionsState =
|
||||
MutableStateFlow<UiState<List<NutMaterOptionVO>?>>(UiState.Idle)
|
||||
val materOptionsState: StateFlow<UiState<List<NutMaterOptionVO>?>> =
|
||||
_materOptionsState.asStateFlow()
|
||||
|
||||
fun getMaterOptions(keyword: String? = null) {
|
||||
viewModelScope.launch {
|
||||
@@ -60,8 +64,10 @@ class NetViewModelV2(
|
||||
}
|
||||
|
||||
// 包装设备下拉
|
||||
private val _deviceOptionsState = MutableStateFlow<UiState<List<NutSupPkgDeviceVO>?>>(UiState.Idle)
|
||||
val deviceOptionsState: StateFlow<UiState<List<NutSupPkgDeviceVO>?>> = _deviceOptionsState.asStateFlow()
|
||||
private val _deviceOptionsState =
|
||||
MutableStateFlow<UiState<List<NutSupPkgDeviceVO>?>>(UiState.Idle)
|
||||
val deviceOptionsState: StateFlow<UiState<List<NutSupPkgDeviceVO>?>> =
|
||||
_deviceOptionsState.asStateFlow()
|
||||
|
||||
fun getDeviceOptions() {
|
||||
viewModelScope.launch {
|
||||
@@ -72,7 +78,8 @@ class NetViewModelV2(
|
||||
|
||||
// 餐品下拉搜索
|
||||
private val _foodOptionsState = MutableStateFlow<UiState<Page<NutFoodOptionVO>?>>(UiState.Idle)
|
||||
val foodOptionsState: StateFlow<UiState<Page<NutFoodOptionVO>?>> = _foodOptionsState.asStateFlow()
|
||||
val foodOptionsState: StateFlow<UiState<Page<NutFoodOptionVO>?>> =
|
||||
_foodOptionsState.asStateFlow()
|
||||
|
||||
// fun getFoodOptions(keyword: String? = null, pageNum: Int = 1, pageSize: Int = 10) {
|
||||
// viewModelScope.launch {
|
||||
@@ -125,8 +132,10 @@ class NetViewModelV2(
|
||||
}
|
||||
|
||||
// 按溯源码查食材信息
|
||||
private val _ingredientByTraceState = MutableStateFlow<UiState<List<NutSupHygieneIngredientOptionVO>?>>(UiState.Idle)
|
||||
val ingredientByTraceState: StateFlow<UiState<List<NutSupHygieneIngredientOptionVO>?>> = _ingredientByTraceState.asStateFlow()
|
||||
private val _ingredientByTraceState =
|
||||
MutableStateFlow<UiState<List<NutSupHygieneIngredientOptionVO>?>>(UiState.Idle)
|
||||
val ingredientByTraceState: StateFlow<UiState<List<NutSupHygieneIngredientOptionVO>?>> =
|
||||
_ingredientByTraceState.asStateFlow()
|
||||
|
||||
fun getIngredientByTrace(traceCode: String) {
|
||||
viewModelScope.launch {
|
||||
@@ -141,12 +150,29 @@ class NetViewModelV2(
|
||||
private val _addComboTaskState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||
val addComboTaskState: StateFlow<UiState<Unit?>> = _addComboTaskState.asStateFlow()
|
||||
|
||||
fun addComboTask(param: NutProdComboTaskDTO) {
|
||||
fun addComboTask(param: NutComboTaskDetailVO, block: (Boolean, String) -> Unit) {
|
||||
viewModelScope.launch {
|
||||
_addComboTaskState.value = UiState.Loading
|
||||
_addComboTaskState.value = repository.addComboTask(param)
|
||||
val uiState = repository.addComboTask(param)
|
||||
when (uiState) {
|
||||
is UiState.Success -> {
|
||||
block(true, "")
|
||||
}
|
||||
|
||||
is UiState.Error -> {
|
||||
block(false, uiState.msg)
|
||||
}
|
||||
|
||||
is UiState.Loading -> {}
|
||||
UiState.Idle -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fun addComboTask(param: NutProdComboTaskDTO) {
|
||||
// viewModelScope.launch {
|
||||
// _addComboTaskState.value = UiState.Loading
|
||||
// _addComboTaskState.value = repository.addComboTask(param)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 开始组配
|
||||
private val _startComboTaskState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||
@@ -172,7 +198,8 @@ class NetViewModelV2(
|
||||
|
||||
// 获取组配任务视频监控地址
|
||||
private val _comboTaskVideoState = MutableStateFlow<UiState<NutProdComboVideoVO?>>(UiState.Idle)
|
||||
val comboTaskVideoState: StateFlow<UiState<NutProdComboVideoVO?>> = _comboTaskVideoState.asStateFlow()
|
||||
val comboTaskVideoState: StateFlow<UiState<NutProdComboVideoVO?>> =
|
||||
_comboTaskVideoState.asStateFlow()
|
||||
|
||||
fun getComboTaskVideo(taskNo: String) {
|
||||
viewModelScope.launch {
|
||||
@@ -220,7 +247,14 @@ class NetViewModelV2(
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
onLoading()
|
||||
onResult(repository.getIncompleteComboTasks(deviceCode = deviceCode, keyword = keyword, pageNum = pageNum, pageSize = pageSize))
|
||||
onResult(
|
||||
repository.getIncompleteComboTasks(
|
||||
deviceCode = deviceCode,
|
||||
keyword = keyword,
|
||||
pageNum = pageNum,
|
||||
pageSize = pageSize
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,4 +272,20 @@ class NetViewModelV2(
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 已入库净菜食材接口 ==========
|
||||
|
||||
/**
|
||||
* 按食材名称查询已入库有库存的净菜记录(回调版本)
|
||||
*/
|
||||
fun getInboundIngredientOptionsWithCallback(
|
||||
keyword: String? = null,
|
||||
onLoading: () -> Unit = {},
|
||||
onResult: (UiState<List<NutCleanInboundIngredientVO>?>) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
onLoading()
|
||||
onResult(repository.getInboundIngredientOptions(keyword))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.shuwei.dish.match.net
|
||||
|
||||
import com.shuwei.dish.match.model.IdDTO
|
||||
import com.shuwei.dish.match.model.NutCanteenVO
|
||||
import com.shuwei.dish.match.model.NutCleanInboundIngredientVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskDetailVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskVO
|
||||
import com.shuwei.dish.match.model.NutDictItemVO
|
||||
@@ -65,9 +66,12 @@ class RemoteRepositoryV2 {
|
||||
suspend fun getIngredientByTrace(traceCode: String): UiState<List<NutSupHygieneIngredientOptionVO>?> =
|
||||
safeApiCall { apiService.getIngredientByTrace(traceCode = traceCode) }
|
||||
|
||||
suspend fun getInboundIngredientOptions(keyword: String? = null): UiState<List<NutCleanInboundIngredientVO>?> =
|
||||
safeApiCall { apiService.getInboundIngredientOptions(keyword = keyword) }
|
||||
|
||||
// ========== 净菜组配接口 ==========
|
||||
|
||||
suspend fun addComboTask(param: NutProdComboTaskDTO): UiState<Unit?> =
|
||||
suspend fun addComboTask(param: NutComboTaskDetailVO): UiState<Unit?> =
|
||||
safeApiCall { apiService.addComboTask(param = param) }
|
||||
|
||||
suspend fun startComboTask(id: Long): UiState<Unit?> =
|
||||
|
||||
@@ -37,7 +37,7 @@ class CleanPackActivity : BaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityCleanPackBinding
|
||||
private val fields = mutableListOf<FormField>()
|
||||
private val formAdapter by lazy { FormFieldAdapter(this, fields) }
|
||||
private val formAdapter by lazy { FormFieldAdapter(fields) }
|
||||
private val viewModelV2 by lazy { NetViewModelV2() }
|
||||
private var packType: Int = TYPE_CLEAN
|
||||
|
||||
@@ -241,17 +241,13 @@ class CleanPackActivity : BaseActivity() {
|
||||
fields.add(packagingSpecField!!)
|
||||
|
||||
// 包装方式(下拉)
|
||||
fields.add(
|
||||
packageMethodField =
|
||||
FormField(
|
||||
label = "包装方式", type = FieldType.DROPDOWN,
|
||||
extraApiKeys = mapOf("packageMethodId" to "id", "packageMethod" to "value"),
|
||||
required = true,
|
||||
options = mutableListOf(
|
||||
DictType(id = "1", value = "真空包装", type = "真空包装"),
|
||||
DictType(id = "2", value = "气调包装", type = "气调包装"),
|
||||
)
|
||||
required = true
|
||||
)
|
||||
)
|
||||
fields.add(packageMethodField!!)
|
||||
|
||||
// 包装份数
|
||||
fields.add(
|
||||
@@ -277,14 +273,15 @@ class CleanPackActivity : BaseActivity() {
|
||||
required = true
|
||||
),
|
||||
FormField(
|
||||
label = "分类", type = FieldType.DROPDOWN,
|
||||
extraApiKeys = mapOf("classify" to "value"),
|
||||
label = "分类", type = FieldType.FIXED,
|
||||
// extraApiKeys = mapOf("classify" to "value"),
|
||||
apiKey = "classify",
|
||||
required = true,
|
||||
options = mutableListOf(
|
||||
DictType(id = "1", value = "蔬菜类", type = "蔬菜类"),
|
||||
DictType(id = "2", value = "肉禽类", type = "肉禽类"),
|
||||
DictType(id = "3", value = "水产类", type = "水产类"),
|
||||
)
|
||||
// options = mutableListOf(
|
||||
// DictType(id = "1", value = "蔬菜类", type = "蔬菜类"),
|
||||
// DictType(id = "2", value = "肉禽类", type = "肉禽类"),
|
||||
// DictType(id = "3", value = "水产类", type = "水产类"),
|
||||
// )
|
||||
),
|
||||
FormField(
|
||||
label = "每份用量",
|
||||
@@ -401,6 +398,7 @@ class CleanPackActivity : BaseActivity() {
|
||||
|
||||
// 加载下拉选项
|
||||
loadFoodOptionsWithKeyword()
|
||||
loadDictItemsWithCallback("sup_package_method", packageMethodField!!)
|
||||
loadDictItemsWithCallback("sup_package_spec", packagingSpecField!!)
|
||||
loadDeviceOptionsWithCallback(deviceField)
|
||||
}
|
||||
|
||||
@@ -10,17 +10,23 @@ import android.view.ViewGroup
|
||||
import androidx.activity.addCallback
|
||||
import androidx.core.content.IntentCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.FoodMaterialAdapter
|
||||
import com.shuwei.dish.match.adapter.FormFieldAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.ActivityPackBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
||||
import com.shuwei.dish.match.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.model.CookFoodDTO
|
||||
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
||||
import com.shuwei.dish.match.dialog.InboundIngredientSearchDialog
|
||||
import com.shuwei.dish.match.model.DictType
|
||||
import com.shuwei.dish.match.model.FieldType
|
||||
import com.shuwei.dish.match.model.FoodRecord
|
||||
import com.shuwei.dish.match.model.FormField
|
||||
import com.shuwei.dish.match.model.GoodsItem
|
||||
import com.shuwei.dish.match.model.NutFoodOptionVO
|
||||
import com.shuwei.dish.match.net.UiState
|
||||
@@ -127,6 +133,7 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var currentWeight = 0.0
|
||||
private fun addViewClickListener() {
|
||||
// ========== 重量识别相关功能已注释 ==========
|
||||
@@ -198,7 +205,7 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
binding.ivAddWeight.clickWithDebounce {
|
||||
if(currentWeight <= 0.0) {
|
||||
if (currentWeight <= 0.0) {
|
||||
toast("当前秤重量无效")
|
||||
return@clickWithDebounce
|
||||
}
|
||||
@@ -209,8 +216,17 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
binding.btnGetTraceCode.clickWithDebounce {
|
||||
val item = list.getOrNull(clickIndex)?:return@clickWithDebounce
|
||||
//viewModelV2.getCode
|
||||
val item = list.getOrNull(clickIndex) ?: return@clickWithDebounce
|
||||
InboundIngredientSearchDialog(
|
||||
activity = this,
|
||||
viewModelV2 = viewModelV2,
|
||||
defIngredientName = item.goodsName
|
||||
) { vo ->
|
||||
materialAdapter.getItem(clickIndex)?.let {
|
||||
it.materCode = vo.traceCode
|
||||
materialAdapter.notifyItemChanged(clickIndex)
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
binding.btnSubmit.clickWithDebounce {
|
||||
|
||||
@@ -316,6 +332,7 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
loadDishDetail(detail)
|
||||
}
|
||||
|
||||
is UiState.Error -> toast(state.msg)
|
||||
else -> {}
|
||||
}
|
||||
@@ -367,6 +384,13 @@ class PackActivity : BaseActivity() {
|
||||
private var firstReqSize = 0
|
||||
private val list = mutableListOf<GoodsItem>()
|
||||
private var clickIndex = -1
|
||||
|
||||
private val fields = mutableListOf<FormField>()
|
||||
|
||||
private val formAdapter by lazy {
|
||||
FormFieldAdapter(fields)
|
||||
}
|
||||
|
||||
private val materialAdapter by lazy {
|
||||
FoodMaterialAdapter(list).apply {
|
||||
onItemClick = onItemClick@{ positon ->
|
||||
@@ -409,12 +433,153 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
private var packageMethodField: FormField? = null
|
||||
private var deviceField: FormField? = null
|
||||
private fun initFormList() {
|
||||
packageMethodField =
|
||||
FormField(
|
||||
label = "包装方式", type = FieldType.DROPDOWN,
|
||||
extraApiKeys = mapOf("packageMethodId" to "id", "packageMethod" to "value"),
|
||||
required = true
|
||||
)
|
||||
fields.add(packageMethodField!!)
|
||||
fields.add(
|
||||
FormField(
|
||||
label = "包装份数", type = FieldType.INTEGER,
|
||||
apiKey = "quantity", required = true, hint = "请输入份数"
|
||||
)
|
||||
)
|
||||
// fields.add(
|
||||
// FormField(
|
||||
// label = "包装日期",
|
||||
// type = FieldType.DATE_PICKER,
|
||||
// apiKey = "packageDate",
|
||||
// required = true
|
||||
// )
|
||||
// )
|
||||
fields.add(
|
||||
FormField(
|
||||
label = "保质期至",
|
||||
type = FieldType.DATE_PICKER,
|
||||
apiKey = "expiryDate",
|
||||
required = true
|
||||
)
|
||||
)
|
||||
|
||||
fields.add(
|
||||
FormField(
|
||||
label = "存放温度(℃)",
|
||||
type = FieldType.DECIMAL,
|
||||
apiKey = "storageTemp",
|
||||
required = false,
|
||||
hint = "请输入小数"
|
||||
)
|
||||
)
|
||||
|
||||
fields.add(
|
||||
FormField(
|
||||
label = "包装人",
|
||||
type = FieldType.TEXT,
|
||||
apiKey = "operator",
|
||||
required = false
|
||||
)
|
||||
)
|
||||
deviceField = FormField(
|
||||
label = "终端设备",
|
||||
type = FieldType.DROPDOWN,
|
||||
extraApiKeys = mapOf("deviceId" to "id"),
|
||||
required = false
|
||||
)
|
||||
fields.add(deviceField!!)
|
||||
// fields.add(
|
||||
// FormField(
|
||||
// label = "操作类型", type = FieldType.FIXED,
|
||||
// apiKey = "opType",
|
||||
// required = true,
|
||||
// value = "设备自动"
|
||||
// )
|
||||
// )
|
||||
loadDictItemsWithCallback("sup_package_method", packageMethodField!!)
|
||||
loadDeviceOptionsWithCallback(deviceField)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典项下拉(回调版本),适合多次不同 dictType 的场景
|
||||
*/
|
||||
private fun loadDictItemsWithCallback(dictType: String, targetField: FormField) {
|
||||
viewModelV2.getDictItemsWithCallback(
|
||||
dictType = dictType,
|
||||
onLoading = {},
|
||||
onResult = { state ->
|
||||
when (state) {
|
||||
is UiState.Success -> {
|
||||
val options = state.data?.map { vo ->
|
||||
DictType(
|
||||
id = vo.id?.toString() ?: "",
|
||||
value = vo.dictLabel ?: "",
|
||||
type = vo.dictCode ?: ""
|
||||
)
|
||||
} ?: emptyList()
|
||||
targetField.options.clear()
|
||||
targetField.options.addAll(options)
|
||||
val index = fields.indexOf(targetField)
|
||||
if (index >= 0) formAdapter.notifyItemChanged(index)
|
||||
}
|
||||
|
||||
is UiState.Error -> {
|
||||
toast("加载字典项失败:${state.msg}")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载设备下拉(回调版本),用于餐品净菜包装
|
||||
*/
|
||||
private fun loadDeviceOptionsWithCallback(deviceField: FormField? = null) {
|
||||
val field = deviceField ?: return
|
||||
viewModelV2.getDeviceOptionsWithCallback(
|
||||
onLoading = {},
|
||||
onResult = { state ->
|
||||
when (state) {
|
||||
is UiState.Success -> {
|
||||
val options = state.data?.map { vo ->
|
||||
DictType(
|
||||
id = vo.id?.toString() ?: "",
|
||||
value = vo.deviceName ?: "",
|
||||
type = vo.deviceNo ?: ""
|
||||
)
|
||||
} ?: emptyList()
|
||||
field.options.clear()
|
||||
field.options.addAll(options)
|
||||
val index = fields.indexOf(field)
|
||||
if (index >= 0) formAdapter.notifyItemChanged(index)
|
||||
}
|
||||
|
||||
is UiState.Error -> {
|
||||
toast("加载设备列表失败:${state.msg}")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* RecyclerView初始化
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun initRecyclerView() {
|
||||
initFormList()
|
||||
binding.rvFormList.let {
|
||||
it.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)
|
||||
it.adapter = formAdapter
|
||||
}
|
||||
|
||||
binding.rvMaterialList.let {
|
||||
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
// 仅 isNewDishType=true 的 item 显示侧滑删除菜单(必须在 setAdapter 之前调用)
|
||||
@@ -570,7 +735,11 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
startActivity(launchIntent) { resultIntent ->
|
||||
if (resultIntent == null) return@startActivity
|
||||
val food = IntentCompat.getSerializableExtra(resultIntent, FoodSearchActivity.FOOD_ITEM, FoodRecord::class.java)
|
||||
val food = IntentCompat.getSerializableExtra(
|
||||
resultIntent,
|
||||
FoodSearchActivity.FOOD_ITEM,
|
||||
FoodRecord::class.java
|
||||
)
|
||||
Log.d(TAG, "jumpSearch: record=$food")
|
||||
if (food == null) return@startActivity
|
||||
binding.etInputDish.run {
|
||||
@@ -583,13 +752,15 @@ class PackActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun handleRvTouchHideKeyboard() {
|
||||
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
hideKeyboard()
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding.rvMaterialList.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
|
||||
val gestureDetector =
|
||||
GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
hideKeyboard()
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding.rvMaterialList.addOnItemTouchListener(object :
|
||||
RecyclerView.SimpleOnItemTouchListener() {
|
||||
private var startX = 0f
|
||||
private var startY = 0f
|
||||
|
||||
@@ -599,6 +770,7 @@ class PackActivity : BaseActivity() {
|
||||
startX = e.x
|
||||
startY = e.y
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val dx = abs(e.x - startX)
|
||||
val dy = abs(e.y - startY)
|
||||
@@ -613,4 +785,9 @@ class PackActivity : BaseActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
package com.shuwei.dish.match.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.core.content.IntentCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.TaskDetailAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.ActivityTaskDetailBinding
|
||||
import com.shuwei.dish.match.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.dialog.InboundIngredientSearchDialog
|
||||
import com.shuwei.dish.match.model.NutComboTaskDetailVO
|
||||
import com.shuwei.dish.match.model.NutComboTaskItemDetail
|
||||
import com.shuwei.dish.match.model.NutComboTaskVO
|
||||
import com.shuwei.dish.match.net.NetViewModelV2
|
||||
import com.shuwei.dish.match.net.UiState
|
||||
import com.shuwei.dish.match.utils.AddressUtil
|
||||
import com.shuwei.dish.match.utils.SpTool
|
||||
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.roundedOneDecimalPlace
|
||||
import com.shuwei.dish.match.utils.ext.toJsonString
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
import com.shuwei.dish.match.utils.ext.visible
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -27,23 +37,60 @@ class TaskDetailActivity : BaseActivity() {
|
||||
const val TASK_ITEM = "taskItem"
|
||||
}
|
||||
|
||||
private var taskDetailVO: NutComboTaskDetailVO? = null
|
||||
private val binding by lazy { ActivityTaskDetailBinding.inflate(layoutInflater) }
|
||||
private val viewModelV2 by lazy { NetViewModelV2() }
|
||||
|
||||
private var taskItem: NutComboTaskVO? = null
|
||||
private val itemList = mutableListOf<com.shuwei.dish.match.model.NutComboTaskItemDetail>()
|
||||
private var clickIndex = -1
|
||||
private val comboTaskList = mutableListOf<NutComboTaskItemDetail>()
|
||||
private val itemAdapter by lazy {
|
||||
TaskDetailAdapter(itemList).apply {
|
||||
TaskDetailAdapter(comboTaskList).apply {
|
||||
setOnItemClickListener { adapter, view, position ->
|
||||
getItem(position) ?: return@setOnItemClickListener
|
||||
itemList.forEachIndexed { index, detail ->
|
||||
detail.isClicked = index == position
|
||||
clickItem(this, position)
|
||||
}
|
||||
addOnItemChildClickListener(R.id.ivClearIcon) { _, _, position ->
|
||||
val item = getItem(position) ?: return@addOnItemChildClickListener
|
||||
Log.d(PrepareFoodActivity.Companion.TAG, "onFoodItemClick: ${item.toJsonString()}")
|
||||
// 无重量信息时转为 item 整体点击(触发选中效果)
|
||||
if (item.actualQty.isNullOrBlank()) {
|
||||
clickItem(this, position)
|
||||
return@addOnItemChildClickListener
|
||||
}
|
||||
deleteRemindDialog("清除确认", "确定清除食材「${item.ingredientName}」添加的溯源码和重量吗?") {
|
||||
item.run {
|
||||
actualQty = null
|
||||
traceCode = null
|
||||
isSetFinished = false
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除提醒
|
||||
*/
|
||||
private fun deleteRemindDialog(title: String, content: String, action: () -> Unit) {
|
||||
CommonDialog(this)
|
||||
.setTitle(title)
|
||||
.setContent(content)
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("确认") {
|
||||
action()
|
||||
}.show()
|
||||
}
|
||||
|
||||
private fun clickItem(adapter: TaskDetailAdapter, position: Int) {
|
||||
adapter.getItem(position) ?: return
|
||||
comboTaskList.forEachIndexed { index, detail ->
|
||||
detail.isClicked = index == position
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
this@TaskDetailActivity.clickIndex = position
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
@@ -64,8 +111,31 @@ class TaskDetailActivity : BaseActivity() {
|
||||
})
|
||||
|
||||
initRecyclerView()
|
||||
addWeightListener()
|
||||
initView()
|
||||
loadDetail()
|
||||
|
||||
lifecycleScope.launch {
|
||||
viewModelV2.startComboTaskState.collect { state ->
|
||||
when (state) {
|
||||
is UiState.Success -> {
|
||||
dismissLoading()
|
||||
toast("已开始组配")
|
||||
// 刷新详情
|
||||
loadDetail()
|
||||
// 更新本地状态
|
||||
taskItem?.comboStatus = 1
|
||||
}
|
||||
|
||||
is UiState.Error -> {
|
||||
dismissLoading()
|
||||
toast(state.msg)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,8 +162,59 @@ class TaskDetailActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
binding.btnGetTraceCode.clickWithDebounce {
|
||||
|
||||
val item = itemAdapter.getItem(clickIndex) ?: return@clickWithDebounce
|
||||
InboundIngredientSearchDialog(
|
||||
activity = this,
|
||||
viewModelV2 = viewModelV2,
|
||||
defIngredientName = item.ingredientName
|
||||
) { vo ->
|
||||
itemAdapter.getItem(clickIndex)?.let {
|
||||
it.traceCode = vo.traceCode
|
||||
itemAdapter.notifyItemChanged(clickIndex)
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
binding.ivAddWeight.clickWithDebounce {
|
||||
if (currentWeight <= 0.0) {
|
||||
toast("当前秤重量无效")
|
||||
return@clickWithDebounce
|
||||
}
|
||||
itemAdapter.getItem(clickIndex)?.let {
|
||||
it.actualQty = currentWeight.toString()
|
||||
it.isSetFinished = true
|
||||
itemAdapter.notifyItemChanged(clickIndex)
|
||||
}
|
||||
}
|
||||
binding.ivClearWeight.clickWithDebounce {
|
||||
initialWeight = 0.0
|
||||
WeightUtil.tareTwo(1)
|
||||
}
|
||||
}
|
||||
|
||||
private var currentWeight: Double = 0.0
|
||||
|
||||
/** 初始秤读数,净重 = 秤读数 - initialWeight,从 SpTool.configWeight 读取 */
|
||||
private var initialWeight = SpTool.configWeight.toDouble()
|
||||
|
||||
private fun addWeightListener() {
|
||||
WeightUtil.addWeightListener(
|
||||
weightKey = TAG,
|
||||
getWeight = { address, state, weight ->
|
||||
if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
|
||||
runOnUiThread {
|
||||
currentWeight = weight - initialWeight
|
||||
binding.tvShowWeight.run {
|
||||
if (tag != currentWeight) {
|
||||
// text = "称重:${netWeight}g"
|
||||
val weightText = "${currentWeight.roundedOneDecimalPlace()}g"
|
||||
text = weightText
|
||||
tag = currentWeight
|
||||
}
|
||||
}
|
||||
//recognizeFood(netWeight)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,22 +239,22 @@ class TaskDetailActivity : BaseActivity() {
|
||||
dismissLoading()
|
||||
when (state) {
|
||||
is UiState.Success -> {
|
||||
val detail = state.data
|
||||
if (detail == null) {
|
||||
taskDetailVO = state.data
|
||||
if (taskDetailVO == null) {
|
||||
toast("详情为空")
|
||||
return@onResult
|
||||
}
|
||||
// 更新状态(可能已被其他设备修改)
|
||||
binding.tvStatus.text = getStatusText(detail.comboStatus)
|
||||
if (detail.comboStatus != 0) {
|
||||
binding.tvStatus.text = getStatusText(taskDetailVO!!.comboStatus)
|
||||
if (taskDetailVO!!.comboStatus != 0) {
|
||||
binding.btnStart.gone()
|
||||
}
|
||||
|
||||
// 加载食材列表
|
||||
val items = detail.items
|
||||
val items = taskDetailVO!!.items
|
||||
if (!items.isNullOrEmpty()) {
|
||||
itemList.clear()
|
||||
itemList.addAll(items)
|
||||
comboTaskList.clear()
|
||||
comboTaskList.addAll(items)
|
||||
itemAdapter.notifyDataSetChanged()
|
||||
} else {
|
||||
toast("该任务无食材信息")
|
||||
@@ -151,29 +272,23 @@ class TaskDetailActivity : BaseActivity() {
|
||||
* 开始组配
|
||||
*/
|
||||
private fun startComboTask(taskId: Long) {
|
||||
val task = taskDetailVO ?: return
|
||||
val count = comboTaskList.count { it.traceCode.isNullOrBlank() }
|
||||
if (count > 0) {
|
||||
toast("存在未设置的溯源码")
|
||||
return
|
||||
}
|
||||
showLoading("正在开始组配……")
|
||||
lifecycleScope.launch {
|
||||
viewModelV2.startComboTask(taskId)
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
viewModelV2.startComboTaskState.collect { state ->
|
||||
when (state) {
|
||||
is UiState.Success -> {
|
||||
dismissLoading()
|
||||
toast("已开始组配")
|
||||
// 刷新详情
|
||||
loadDetail()
|
||||
// 更新本地状态
|
||||
taskItem?.comboStatus = 1
|
||||
}
|
||||
|
||||
is UiState.Error -> {
|
||||
dismissLoading()
|
||||
toast(state.msg)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
//viewModelV2.startComboTask(taskId)
|
||||
task.items = comboTaskList
|
||||
viewModelV2.addComboTask(param = task) { state, msg ->
|
||||
dismissLoading()
|
||||
if (state.not()) {
|
||||
toast(msg)
|
||||
return@addComboTask
|
||||
}
|
||||
toast("上报成功")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,4 +304,10 @@ class TaskDetailActivity : BaseActivity() {
|
||||
else -> "未知"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -167,4 +167,16 @@ fun View.hideKeyboard() {
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(this.windowToken, 0)
|
||||
this.clearFocus() // 清除焦点避免键盘再次弹出
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化用量字符串,去掉小数点后末尾的0
|
||||
*/
|
||||
fun String?.formatDecimal(): String {
|
||||
if (this.isNullOrBlank()) return "0"
|
||||
return try {
|
||||
BigDecimal(this).stripTrailingZeros().toPlainString()
|
||||
} catch (e: Exception) {
|
||||
this
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,19 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginHorizontal="30dp"
|
||||
app:dividerColor="@color/gray_eb" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvFormList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:overScrollMode="never"/>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
|
||||
@@ -96,10 +96,121 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="30dp"
|
||||
android:background="@drawable/shape_white_30_corners"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- <LinearLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="90dp"-->
|
||||
<!-- android:gravity="center_vertical">-->
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tvGoodsName"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginStart="30dp"-->
|
||||
<!-- android:textColor="@color/black"-->
|
||||
<!-- android:textSize="30sp"-->
|
||||
<!-- android:maxLines="1"-->
|
||||
<!-- android:ellipsize="end"-->
|
||||
<!-- android:maxLength="12"-->
|
||||
<!-- android:textStyle="bold"-->
|
||||
<!-- tools:text="豆腐" />-->
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tvMaterialType"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginStart="30dp"-->
|
||||
<!-- android:layout_marginEnd="30dp"-->
|
||||
<!-- android:textColor="@color/black999"-->
|
||||
<!-- android:textSize="26sp"-->
|
||||
<!-- tools:text="主材" />-->
|
||||
|
||||
<!-- </LinearLayout>-->
|
||||
|
||||
<!-- <com.google.android.material.divider.MaterialDivider-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="1dp"-->
|
||||
<!-- android:layout_marginHorizontal="30dp"-->
|
||||
<!-- app:dividerColor="@color/gray_eb" />-->
|
||||
|
||||
<!-- <androidx.recyclerview.widget.RecyclerView-->
|
||||
<!-- android:id="@+id/rvFormList"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginHorizontal="20dp"-->
|
||||
<!-- android:overScrollMode="never"/>-->
|
||||
|
||||
<!-- <com.google.android.material.divider.MaterialDivider-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="1dp"-->
|
||||
<!-- android:layout_marginHorizontal="30dp"-->
|
||||
<!-- app:dividerColor="@color/gray_eb" />-->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="160dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
android:text="用量(克)"
|
||||
android:textColor="@color/black666"
|
||||
android:textSize="26sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvShowWeight"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/shape_white_f6_10_corners"
|
||||
android:gravity="center"
|
||||
android:text="-"
|
||||
android:textColor="@color/gray_d6"
|
||||
android:textSize="50sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivClearWeight"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="90dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:src="@drawable/ic_weight_clear" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddWeight"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="90dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:src="@drawable/ic_weight_add" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:text="在下方列表中选择食材计重"
|
||||
android:textColor="@color/black999"
|
||||
android:textSize="26sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="30dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/shape_white_30_corners"
|
||||
android:orientation="vertical">
|
||||
@@ -111,9 +222,9 @@
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_weight="1"
|
||||
android:padding="10dp"
|
||||
android:text="食材明细"
|
||||
android:textColor="@color/black666"
|
||||
@@ -131,7 +242,6 @@
|
||||
android:textColor="@color/dish_green"
|
||||
android:textSize="18sp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
@@ -140,6 +250,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:overScrollMode="never"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:scrollbars="vertical"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/list_item_task_detail" />
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
tools:background="@color/white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/viewLine"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:background="@drawable/shape_gray_dc_5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSheetName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="50dp"
|
||||
android:textColor="@color/black333"
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="已入库净菜检索" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="60dp"
|
||||
android:layout_marginTop="50dp"
|
||||
android:layout_marginEnd="60dp"
|
||||
android:background="@drawable/shape_gray_f8_15"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSheetInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="@null"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:imeOptions="actionSearch"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="30dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:textColor="@color/black666"
|
||||
android:textColorHint="@color/gray_c8"
|
||||
android:textSize="36sp"
|
||||
tools:hint="输入食材名称"
|
||||
tools:ignore="Autofill,LabelFor,TextFields" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivSearch"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_search_gray"
|
||||
tools:ignore="ContentDescription" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/refreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="700dp"
|
||||
android:layout_marginTop="10dp"
|
||||
app:srlEnableOverScrollDrag="false">
|
||||
|
||||
<com.scwang.smart.refresh.header.ClassicsHeader
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="45dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="45dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:minHeight="380dp"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="10"
|
||||
tools:listitem="@layout/list_item_inbound_ingredient" />
|
||||
|
||||
<com.scwang.smart.refresh.footer.ClassicsFooter
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -73,7 +73,7 @@
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:src="@drawable/ic_dish_clear"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/clBlock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="130dp"
|
||||
android:layout_margin="15dp"
|
||||
android:background="@drawable/shape_white_12_corners"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvIngredientName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/black333"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="土豆" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStock"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:textColor="@color/black999"
|
||||
android:textSize="20sp"
|
||||
tools:text="库存:12.5kg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTraceCode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/dish_green"
|
||||
android:textSize="20sp"
|
||||
tools:text="溯源码:TR20260101001" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,17 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:background="@drawable/shape_white_fb_15_corners"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
android:paddingVertical="15dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivClearIcon"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:src="@drawable/ic_dish_clear"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivOperateIcon"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivClearIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/ic_dish_selected" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvActualQty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:hint="-"
|
||||
android:textColor="@color/dish_green"
|
||||
android:textColorHint="@color/gray_d6"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivOperateIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="200g" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:id="@+id/llFoodInfo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginStart="20dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/tvActualQty"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvIngredientName"
|
||||
@@ -32,49 +77,28 @@
|
||||
tools:text="主材" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:id="@+id/tvPerPortionQty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPerPortionQty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/black666"
|
||||
android:textSize="20sp"
|
||||
tools:text="每份用量:200g" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvActualQty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:textColor="@color/black666"
|
||||
android:textSize="20sp"
|
||||
tools:text="实际用量:200g" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTotalQty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:textColor="@color/black666"
|
||||
android:textSize="20sp"
|
||||
tools:text="合计用量:80000g" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_marginVertical="8dp"
|
||||
android:textColor="@color/black666"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/tvTraceCode"
|
||||
app:layout_constraintStart_toStartOf="@id/llFoodInfo"
|
||||
app:layout_constraintTop_toBottomOf="@id/llFoodInfo"
|
||||
tools:text="每份用量:200g" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTraceCode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:textColor="@color/black999"
|
||||
android:textSize="18sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/llFoodInfo"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPerPortionQty"
|
||||
tools:text="溯源码:TRACE-20260601001"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
Reference in New Issue
Block a user