feat(pack): 优化菜品净菜包装界面和功能

- 调整布局间距,移除商品名称和材料类型显示
- 修改开始组配按钮文本为完成组配并设为可见
- 更新API服务接口支持可空参数类型
- 添加餐品净菜包装数据模型字段
- 实现包装数据收集和提交功能
- 添加时间工具类导入和设备码字段
- 优化适配器中材料分类显示逻辑
This commit is contained in:
2026-06-02 19:49:21 +08:00
parent 32ec1d9b74
commit 0cc1d9189c
9 changed files with 135 additions and 79 deletions
@@ -48,7 +48,7 @@ class TaskDetailAdapter(list: MutableList<NutComboTaskItemDetail>) :
1 -> "主材"
2 -> "辅材"
3 -> "调料"
else -> "未知"
else -> "-"
}
tvIngredientClass.text = classText
@@ -72,9 +72,11 @@ data class NutFoodOptionVO(
*/
@Parcelize
data class NutFoodComposition(
val materId: Long?,
val ingredientName: String?,
val isMain: Int?
var materId: Long?,
var ingredientName: String?,
var traceCode: String?,
var amount: Double?,
var isMain: Int?
) : Parcelable
/**
@@ -218,6 +220,7 @@ data class NutComboTaskDetailVO(
var portions: Int? = null,
var comboStatus: Int? = null,
var ownerName: String? = null,
var deviceCode: String? = null,
var items: List<NutComboTaskItemDetail>? = null,
) : Parcelable
@@ -133,7 +133,7 @@ interface ApiServiceV2 {
@POST
suspend fun addCleanPackage(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/clean-package/add",
@Body param: Map<String, @JvmSuppressWildcards Any>
@Body param: Map<String, @JvmSuppressWildcards Any?>
): ApiResponse<Unit>
// ========== 四、餐品净菜包装接口 ==========
@@ -144,7 +144,7 @@ interface ApiServiceV2 {
@POST
suspend fun addMealPackage(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/meal-package/add",
@Body param: Map<String, @JvmSuppressWildcards Any>
@Body param: Map<String, @JvmSuppressWildcards Any?>
): ApiResponse<Unit>
// ========== 五、组配任务接口 ==========
@@ -157,13 +157,11 @@ class NetViewModelV2(
is UiState.Success -> {
block(true, "")
}
is UiState.Error -> {
block(false, uiState.msg)
}
is UiState.Loading -> {}
UiState.Idle -> {}
is UiState.Idle -> {}
}
}
}
@@ -213,7 +211,7 @@ class NetViewModelV2(
private val _addCleanPackageState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
val addCleanPackageState: StateFlow<UiState<Unit?>> = _addCleanPackageState.asStateFlow()
fun addCleanPackage(param: Map<String, Any>) {
fun addCleanPackage(param: Map<String, Any?>) {
viewModelScope.launch {
_addCleanPackageState.value = UiState.Loading
_addCleanPackageState.value = repository.addCleanPackage(param)
@@ -225,6 +223,24 @@ class NetViewModelV2(
private val _addMealPackageState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
val addMealPackageState: StateFlow<UiState<Unit?>> = _addMealPackageState.asStateFlow()
fun addMealPackage(param: Map<String, Any?>, callback: (Boolean, String) -> Unit) {
viewModelScope.launch {
val uiState = repository.addMealPackage(param)
when (uiState) {
is UiState.Success -> {
callback(true, "")
}
is UiState.Error -> {
callback(false, uiState.msg)
}
is UiState.Loading -> {}
is UiState.Idle -> {}
}
}
}
fun addMealPackage(param: Map<String, Any>) {
viewModelScope.launch {
_addMealPackageState.value = UiState.Loading
@@ -85,12 +85,12 @@ class RemoteRepositoryV2 {
// ========== 单品净菜包装接口 ==========
suspend fun addCleanPackage(param: Map<String, Any>): UiState<Unit?> =
suspend fun addCleanPackage(param: Map<String, Any?>): UiState<Unit?> =
safeApiCall { apiService.addCleanPackage(param = param) }
// ========== 餐品净菜包装接口 ==========
suspend fun addMealPackage(param: Map<String, Any>): UiState<Unit?> =
suspend fun addMealPackage(param: Map<String, Any?>): UiState<Unit?> =
safeApiCall { apiService.addMealPackage(param = param) }
// ========== 组配任务接口 ==========
@@ -53,7 +53,9 @@ import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.abs
import com.shuwei.dish.match.model.NutFoodComposition
import com.shuwei.dish.match.net.NetViewModelV2
import com.shuwei.dish.match.utils.DateTimeUtil
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
import java.time.LocalDateTime
@SuppressLint("NotifyDataSetChanged")
class PackActivity : BaseActivity() {
@@ -96,8 +98,7 @@ class PackActivity : BaseActivity() {
}
}
val isSamplingMode = SpTool.cookMode == 1
val titleText = if (isSamplingMode) "菜品信息采集" else "菜品净菜包装"
val titleText = "菜品净菜包"
setTitleBar(titleBarAction = {
it.visible()
}, titleAction = {
@@ -115,7 +116,7 @@ class PackActivity : BaseActivity() {
binding.etInputDish.isFocusableInTouchMode = false
binding.ivDishSearch.gone()
binding.tvGoodsName.text = "食材名称"
// binding.tvGoodsName.text = "食材名称"
addViewClickListener()
addBackKeyListener()
@@ -229,7 +230,72 @@ class PackActivity : BaseActivity() {
}.show()
}
binding.btnSubmit.clickWithDebounce {
submit()
}
}
/**
* 收集字段提交值,根据 apiKey / extraApiKeys 规则汇总
* @return 提交参数 Mapkey 为接口参数名,value 为提交值
*/
private fun collectSubmitValues(): MutableMap<String, String> {
val params = mutableMapOf<String, String>()
for (field in fields) {
if (field.hidden) continue
// 跳过 CHILDREN 类型,暂不处理
if (field.type == FieldType.CHILDREN) continue
if (field.extraApiKeys.isNotEmpty()) {
// 下拉字段:直接读取 bindDropdown 已解析好的 extraValues
field.extraValues.forEach { (apiKey, value) ->
params[apiKey] = value
}
} else if (field.apiKey.isNotBlank()) {
// 普通字段
params[field.apiKey] = field.value
}
}
return params
}
/**
* 提交餐品净菜包装
*/
private fun submit() {
val food = mealItem ?: return
//包装方式、包装份数、保质期至、包装人、存放温度、终端设备
val values = collectSubmitValues()
val params = mutableMapOf<String, Any?>()
values.forEach { (key, value) ->
params[key] = value
}
//关联餐品id
params["foodId"] = food.foodId
//包装餐品名称
params["mealName"] = food.foodName
//包装日期
params["packageDate"] = DateTimeUtil.formatDateTime(LocalDateTime.now())
//操作类型
params["opType"] = "设备自动"
//食材明细列表
params["ingredients"] = list.map {
NutFoodComposition(
materId = it.goodsId.toLongOrNull(),
ingredientName = it.goodsName,
traceCode = it.materCode,
amount = it.useWeight,
isMain = if (it.materialType == 1) 1 else 0
)
}.toMutableList()
viewModelV2.addMealPackage(params) { state, msg ->
if (state.not()) {
toast("提交失败:$msg")
return@addMealPackage
}
toast("提交成功")
finish()
}
}
@@ -400,12 +466,12 @@ class PackActivity : BaseActivity() {
entity.isClicked = index == positon
}
notifyDataSetChanged()
binding.tvGoodsName.text = item.goodsName
binding.tvMaterialType.text = when (item.materialType) {
1 -> "主材"
2 -> "辅材"
else -> "未知"
}
// binding.tvGoodsName.text = item.goodsName
// binding.tvMaterialType.text = when (item.materialType) {
// 1 -> "主材"
// 2 -> "辅材"
// else -> "未知"
// }
}
addOnItemChildClickListener(R.id.ivClearIcon) { _, _, position ->
val item = list.getOrNull(position) ?: return@addOnItemChildClickListener
@@ -8,6 +8,7 @@ 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.base.GlobalData
import com.shuwei.dish.match.databinding.ActivityTaskDetailBinding
import com.shuwei.dish.match.dialog.CommonDialog
import com.shuwei.dish.match.dialog.InboundIngredientSearchDialog
@@ -152,14 +153,15 @@ class TaskDetailActivity : BaseActivity() {
binding.tvStatus.text = getStatusText(it.comboStatus)
// 仅待组配状态显示"开始组配"按钮
if (it.comboStatus == 0) {
binding.btnStart.visible()
// if (it.comboStatus == 0) {
// binding.btnStart.visible()
binding.btnStart.clickWithDebounce {
startComboTask(it.id ?: return@clickWithDebounce)
//val taskId = it.id ?: return@clickWithDebounce
startComboTask()
}
} else {
binding.btnStart.gone()
}
// } else {
// binding.btnStart.gone()
// }
}
binding.btnGetTraceCode.clickWithDebounce {
val item = itemAdapter.getItem(clickIndex) ?: return@clickWithDebounce
@@ -271,13 +273,14 @@ class TaskDetailActivity : BaseActivity() {
/**
* 开始组配
*/
private fun startComboTask(taskId: Long) {
private fun startComboTask() {
val task = taskDetailVO ?: return
val count = comboTaskList.count { it.traceCode.isNullOrBlank() }
if (count > 0) {
toast("存在未设置的溯源码")
return
}
task.deviceCode = GlobalData.deviceId
showLoading("正在开始组配……")
lifecycleScope.launch {
//viewModelV2.startComboTask(taskId)
+17 -49
View File
@@ -54,58 +54,26 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginHorizontal="20dp"
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"/>
android:layout_marginVertical="10dp"
android:overScrollMode="never" />
</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" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginTop="20dp"
android:background="@drawable/shape_white_30_corners"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
@@ -165,7 +133,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="30dp"
android:layout_marginTop="30dp"
android:layout_marginTop="20dp"
android:layout_weight="1"
android:background="@drawable/shape_white_30_corners"
android:orientation="vertical">
@@ -204,14 +172,14 @@
android:id="@+id/btnGetTraceCode"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginEnd="30dp"
android:paddingHorizontal="20dp"
android:layout_gravity="end|center_vertical"
android:text="获取溯源码"
android:textSize="18sp"
android:layout_marginEnd="30dp"
android:background="@drawable/ripple_effect_light"
android:gravity="center"
android:paddingHorizontal="20dp"
android:text="获取溯源码"
android:textColor="@color/dish_green"
android:background="@drawable/ripple_effect_light"/>
android:textSize="18sp" />
<FrameLayout
@@ -265,10 +265,10 @@
android:background="@drawable/shape_green_bg"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:text="开始组配"
android:text="完成组配"
android:textColor="@color/white"
android:textSize="32sp"
android:textStyle="bold"
android:visibility="gone" />
android:visibility="visible" />
</LinearLayout>