From c4a2105737233da8da14353f135d4ca10a7314fa Mon Sep 17 00:00:00 2001
From: lvmeng <848755140@qq.com>
Date: Mon, 1 Jun 2026 11:21:04 +0800
Subject: [PATCH] =?UTF-8?q?feat(form):=20=E6=96=B0=E5=A2=9E=E5=87=80?=
=?UTF-8?q?=E8=8F=9C=E5=8C=85=E8=A3=85=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E8=A1=A8=E5=8D=95=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 添加 CleanPackActivity 页面用于净菜包装表单
- 将 NUMBER 字段类型拆分为 INTEGER 和 DECIMAL 类型
- 更新 FormFieldAdapter 适配新的字段类型
- 在 HomeV2Activity 中添加跳转到净菜包装页面的功能
- 创建 ApiServiceV2、NetViewModelV2 和 RemoteRepositoryV2 处理新接口
- 添加营养相关模型类和分页组件
- 在 AndroidManifest.xml 中注册新 Activity
---
app/src/main/AndroidManifest.xml | 5 +
.../dish/match/adapter/FormFieldAdapter.kt | 5 +-
.../com/shuwei/dish/match/model/FormField.kt | 6 +-
.../dish/match/model/NutritionModels.kt | 190 ++++++++++++++++++
.../com/shuwei/dish/match/net/ApiServiceV2.kt | 140 +++++++++++++
.../shuwei/dish/match/net/NetViewModelV2.kt | 165 +++++++++++++++
.../java/com/shuwei/dish/match/net/Page.kt | 10 +
.../dish/match/net/RemoteRepositoryV2.kt | 92 +++++++++
.../shuwei/dish/match/ui/CleanPackActivity.kt | 181 +++++++++++++++++
.../shuwei/dish/match/ui/HomeV2Activity.kt | 9 +-
.../main/res/layout/activity_clean_pack.xml | 32 +++
11 files changed, 829 insertions(+), 6 deletions(-)
create mode 100644 app/src/main/java/com/shuwei/dish/match/model/NutritionModels.kt
create mode 100644 app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt
create mode 100644 app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt
create mode 100644 app/src/main/java/com/shuwei/dish/match/net/Page.kt
create mode 100644 app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt
create mode 100644 app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt
create mode 100644 app/src/main/res/layout/activity_clean_pack.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 62ba0ec..c6b3410 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -64,6 +64,11 @@
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
+
+
bindEditText(b, item, InputType.TYPE_CLASS_TEXT)
- FieldType.NUMBER -> bindEditText(
+ FieldType.INTEGER -> bindEditText(b, item, InputType.TYPE_CLASS_NUMBER)
+ FieldType.DECIMAL -> bindEditText(
b, item,
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
)
diff --git a/app/src/main/java/com/shuwei/dish/match/model/FormField.kt b/app/src/main/java/com/shuwei/dish/match/model/FormField.kt
index 5a62e8e..105eb86 100644
--- a/app/src/main/java/com/shuwei/dish/match/model/FormField.kt
+++ b/app/src/main/java/com/shuwei/dish/match/model/FormField.kt
@@ -4,8 +4,10 @@ package com.shuwei.dish.match.model
enum class FieldType {
/** 普通文本输入 */
TEXT,
- /** 数字输入(支持小数和负数) */
- NUMBER,
+ /** 整数输入 */
+ INTEGER,
+ /** 小数输入 */
+ DECIMAL,
/** 下拉选择 */
DROPDOWN,
/** 日期选择弹窗 */
diff --git a/app/src/main/java/com/shuwei/dish/match/model/NutritionModels.kt b/app/src/main/java/com/shuwei/dish/match/model/NutritionModels.kt
new file mode 100644
index 0000000..eba0780
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/model/NutritionModels.kt
@@ -0,0 +1,190 @@
+package com.shuwei.dish.match.model
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+/**
+ * 食堂VO - 食堂下拉列表响应
+ */
+@Parcelize
+data class NutCanteenVO(
+ val id: Long?,
+ val canteenCode: String?,
+ val canteenName: String?,
+ val orgCode: String?,
+ val orgName: String?,
+ val leader: String?,
+ val leaderPhone: String?,
+ val useTerminal: Short?,
+ val address: String?,
+ val longitude: String?,
+ val latitude: String?,
+ val status: Short?
+) : Parcelable
+
+/**
+ * 字典项VO - 字典项下拉响应
+ */
+@Parcelize
+data class NutDictItemVO(
+ val id: Long?,
+ val dictType: String?,
+ val dictCode: String?,
+ val dictLabel: String?,
+ val sortOrder: Int?,
+ val isBuiltin: Short?,
+ val status: Short?,
+ val remark: String?
+) : Parcelable
+
+/**
+ * 食材选项VO - 食材下拉响应
+ */
+@Parcelize
+data class NutMaterOptionVO(
+ val id: Long?,
+ val materClassName: String?
+) : Parcelable
+
+/**
+ * 包装设备VO - 包装设备下拉响应
+ */
+@Parcelize
+data class NutSupPkgDeviceVO(
+ val id: Long?,
+ val deviceNo: String?,
+ val deviceName: String?
+) : Parcelable
+
+/**
+ * 餐品选项VO - 餐品下拉搜索响应
+ */
+@Parcelize
+data class NutFoodOptionVO(
+ val foodId: Long?,
+ val foodCode: String?,
+ val foodName: String?,
+ val compositions: List?
+) : Parcelable
+
+/**
+ * 餐品食材构成
+ */
+@Parcelize
+data class NutFoodComposition(
+ val materId: Long?,
+ val ingredientName: String?
+) : Parcelable
+
+/**
+ * 溯源码查食材信息VO
+ */
+@Parcelize
+data class NutSupHygieneIngredientOptionVO(
+ val traceCode: String?,
+ val materId: Long?,
+ val materName: String?,
+ val vegTypes: String?,
+ val materClassId: Long?,
+ val materClassName: String?,
+ val testNo: String?
+) : Parcelable
+
+/**
+ * 组配任务视频监控VO
+ */
+@Parcelize
+data class NutProdComboVideoVO(
+ val cameraCode: String?,
+ val streamUrl: String?,
+ val snapshotUrl: String?,
+ val onlineStatus: Int?,
+ val recordType: Int?,
+ val startTime: String?,
+ val endTime: String?
+) : Parcelable
+
+/**
+ * 组配任务DTO - 上报组配任务请求体
+ */
+data class NutProdComboTaskDTO(
+ val planDate: String,
+ val canteenId: Long,
+ val mealType: Short?,
+ val foodId: Long?,
+ val targetDish: String,
+ val spec: String,
+ val portions: Int,
+ val taskDeadline: String?,
+ val taskSource: Short?,
+ val ownerName: String?,
+ val items: List?
+)
+
+/**
+ * 组配任务食材明细
+ */
+data class NutProdComboTaskItem(
+ val ingredientCode: String?,
+ val ingredientName: String,
+ val perPortionQty: String,
+ val unit: String? = "g",
+ val sortOrder: Int?
+)
+
+/**
+ * 单品净菜包装DTO - 上报单品净菜包装记录请求体
+ */
+data class NutSupCleanPackageDTO(
+ val traceCode: String,
+ val cleanInboundId: Long?,
+ val cleanName: String,
+ val packageMethod: String,
+ val spec: String,
+ val quantity: Int,
+ val packageDate: String,
+ val expiryDate: String,
+ val storageTemp: String?,
+ val relatedOrderNo: String?,
+ val operator: String?,
+ val deviceId: Long?,
+ val cameraCode: String?,
+ val videoUrl: String?
+)
+
+/**
+ * 餐品净菜包装DTO - 上报餐品净菜包装记录请求体
+ */
+data class NutSupMealPackageDTO(
+ val foodId: Long,
+ val mealName: String,
+ val spec: String,
+ val packageMethod: String,
+ val quantity: Int,
+ val packageDate: String,
+ val expiryDate: String,
+ val storageTemp: String?,
+ val relatedOrderNo: String?,
+ val operator: String?,
+ val deviceId: Long?,
+ val cameraCode: String?,
+ val videoUrl: String?,
+ val ingredients: List
+)
+
+/**
+ * 餐品净菜包装食材明细
+ */
+data class NutSupMealPackageIngredient(
+ val materId: Long,
+ val ingredientName: String,
+ val amount: String?,
+ val traceCode: String
+)
+
+/**
+ * ID通用DTO - 用于开始/完成组配等需要id参数的接口
+ */
+data class IdDTO(
+ val id: Long
+)
diff --git a/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt b/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt
new file mode 100644
index 0000000..6a3fd8c
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/net/ApiServiceV2.kt
@@ -0,0 +1,140 @@
+package com.shuwei.dish.match.net
+
+import com.shuwei.dish.match.base.GlobalData
+import com.shuwei.dish.match.model.NutSupCleanPackageDTO
+import com.shuwei.dish.match.model.NutSupMealPackageDTO
+import com.shuwei.dish.match.model.NutSupPkgDeviceVO
+import com.shuwei.dish.match.model.NutCanteenVO
+import com.shuwei.dish.match.model.NutDictItemVO
+import com.shuwei.dish.match.model.NutMaterOptionVO
+import com.shuwei.dish.match.model.NutFoodOptionVO
+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 retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.POST
+import retrofit2.http.Query
+import retrofit2.http.Url
+
+interface ApiServiceV2 {
+
+ // ========== 一、下拉选项接口 ==========
+
+ /**
+ * 食堂下拉列表,返回启用状态的食堂列表,支持关键字模糊搜索
+ */
+ @GET
+ suspend fun getCanteenOptions(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/canteen-options",
+ @Query("keyword") keyword: String? = null
+ ): ApiResponse>
+
+ /**
+ * 字典项下拉,按 dictType 查询字典项
+ */
+ @GET
+ suspend fun getDictItems(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/dict-items",
+ @Query("dictType") dictType: String
+ ): ApiResponse>
+
+ /**
+ * 食材下拉列表,支持关键字模糊搜索
+ */
+ @GET
+ suspend fun getMaterOptions(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/mater-options",
+ @Query("keyword") keyword: String? = null
+ ): ApiResponse>
+
+ /**
+ * 包装设备下拉(仅在线),返回在线设备的 id、编号、名称
+ */
+ @GET
+ suspend fun getDeviceOptions(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/device-options"
+ ): ApiResponse>
+
+ /**
+ * 餐品下拉搜索(含食材构成),按名称模糊搜索,分页返回
+ */
+ @GET
+ suspend fun getFoodOptions(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/food-options",
+ @Query("keyword") keyword: String? = null,
+ @Query("pageNum") pageNum: Int = 1,
+ @Query("pageSize") pageSize: Int = 10
+ ): ApiResponse>
+
+ /**
+ * 按溯源码查食材信息
+ */
+ @GET
+ suspend fun getIngredientByTrace(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/ingredient-by-trace",
+ @Query("traceCode") traceCode: String
+ ): ApiResponse>
+
+ // ========== 二、净菜组配接口 ==========
+
+ /**
+ * 上报组配任务(含食材明细)
+ */
+ @POST
+ suspend fun addComboTask(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/add",
+ @Body param: NutProdComboTaskDTO
+ ): ApiResponse
+
+ /**
+ * 开始组配,组配状态 0 -> 1
+ */
+ @POST
+ suspend fun startComboTask(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/start",
+ @Body param: IdDTO
+ ): ApiResponse
+
+ /**
+ * 完成组配,组配状态 1 -> 2
+ */
+ @POST
+ suspend fun finishComboTask(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/finish",
+ @Body param: IdDTO
+ ): ApiResponse
+
+ /**
+ * 获取组配任务视频监控地址
+ */
+ @GET
+ suspend fun getComboTaskVideo(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/video",
+ @Query("taskNo") taskNo: String
+ ): ApiResponse
+
+ // ========== 三、单品净菜包装接口 ==========
+
+ /**
+ * 上报单品净菜包装记录
+ */
+ @POST
+ suspend fun addCleanPackage(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/clean-package/add",
+ @Body param: NutSupCleanPackageDTO
+ ): ApiResponse
+
+ // ========== 四、餐品净菜包装接口 ==========
+
+ /**
+ * 上报餐品净菜包装记录
+ */
+ @POST
+ suspend fun addMealPackage(
+ @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/meal-package/add",
+ @Body param: NutSupMealPackageDTO
+ ): ApiResponse
+
+}
diff --git a/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt b/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt
new file mode 100644
index 0000000..25e1afb
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/net/NetViewModelV2.kt
@@ -0,0 +1,165 @@
+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.NutDictItemVO
+import com.shuwei.dish.match.model.NutFoodOptionVO
+import com.shuwei.dish.match.model.NutMaterOptionVO
+import com.shuwei.dish.match.model.NutProdComboTaskDTO
+import com.shuwei.dish.match.model.NutProdComboVideoVO
+import com.shuwei.dish.match.model.NutSupCleanPackageDTO
+import com.shuwei.dish.match.model.NutSupHygieneIngredientOptionVO
+import com.shuwei.dish.match.model.NutSupMealPackageDTO
+import com.shuwei.dish.match.model.NutSupPkgDeviceVO
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * 网络请求 ViewModel V2,所有网络操作通过 RemoteRepositoryV2 发起
+ */
+class NetViewModelV2(
+ private val repository: RemoteRepositoryV2 = RemoteRepositoryV2()
+) : ViewModel() {
+
+ // ========== 下拉选项接口 ==========
+
+ // 食堂下拉列表
+ private val _canteenOptionsState = MutableStateFlow?>>(UiState.Idle)
+ val canteenOptionsState: StateFlow?>> = _canteenOptionsState.asStateFlow()
+
+ fun getCanteenOptions(keyword: String? = null) {
+ viewModelScope.launch {
+ _canteenOptionsState.value = UiState.Loading
+ _canteenOptionsState.value = repository.getCanteenOptions(keyword)
+ }
+ }
+
+ // 字典项下拉
+ private val _dictItemsState = MutableStateFlow?>>(UiState.Idle)
+ val dictItemsState: StateFlow?>> = _dictItemsState.asStateFlow()
+
+ fun getDictItems(dictType: String) {
+ viewModelScope.launch {
+ _dictItemsState.value = UiState.Loading
+ _dictItemsState.value = repository.getDictItems(dictType)
+ }
+ }
+
+ // 食材下拉列表
+ private val _materOptionsState = MutableStateFlow?>>(UiState.Idle)
+ val materOptionsState: StateFlow?>> = _materOptionsState.asStateFlow()
+
+ fun getMaterOptions(keyword: String? = null) {
+ viewModelScope.launch {
+ _materOptionsState.value = UiState.Loading
+ _materOptionsState.value = repository.getMaterOptions(keyword)
+ }
+ }
+
+ // 包装设备下拉
+ private val _deviceOptionsState = MutableStateFlow?>>(UiState.Idle)
+ val deviceOptionsState: StateFlow?>> = _deviceOptionsState.asStateFlow()
+
+ fun getDeviceOptions() {
+ viewModelScope.launch {
+ _deviceOptionsState.value = UiState.Loading
+ _deviceOptionsState.value = repository.getDeviceOptions()
+ }
+ }
+
+ // 餐品下拉搜索
+ private val _foodOptionsState = MutableStateFlow?>>(UiState.Idle)
+ val foodOptionsState: StateFlow?>> = _foodOptionsState.asStateFlow()
+
+ fun getFoodOptions(keyword: String? = null, pageNum: Int = 1, pageSize: Int = 10) {
+ viewModelScope.launch {
+ _foodOptionsState.value = UiState.Loading
+ _foodOptionsState.value = repository.getFoodOptions(keyword, pageNum, pageSize)
+ }
+ }
+
+ // 按溯源码查食材信息
+ private val _ingredientByTraceState = MutableStateFlow?>>(UiState.Idle)
+ val ingredientByTraceState: StateFlow?>> = _ingredientByTraceState.asStateFlow()
+
+ fun getIngredientByTrace(traceCode: String) {
+ viewModelScope.launch {
+ _ingredientByTraceState.value = UiState.Loading
+ _ingredientByTraceState.value = repository.getIngredientByTrace(traceCode)
+ }
+ }
+
+ // ========== 净菜组配接口 ==========
+
+ // 上报组配任务
+ private val _addComboTaskState = MutableStateFlow>(UiState.Idle)
+ val addComboTaskState: StateFlow> = _addComboTaskState.asStateFlow()
+
+ fun addComboTask(param: NutProdComboTaskDTO) {
+ viewModelScope.launch {
+ _addComboTaskState.value = UiState.Loading
+ _addComboTaskState.value = repository.addComboTask(param)
+ }
+ }
+
+ // 开始组配
+ private val _startComboTaskState = MutableStateFlow>(UiState.Idle)
+ val startComboTaskState: StateFlow> = _startComboTaskState.asStateFlow()
+
+ fun startComboTask(id: Long) {
+ viewModelScope.launch {
+ _startComboTaskState.value = UiState.Loading
+ _startComboTaskState.value = repository.startComboTask(id)
+ }
+ }
+
+ // 完成组配
+ private val _finishComboTaskState = MutableStateFlow>(UiState.Idle)
+ val finishComboTaskState: StateFlow> = _finishComboTaskState.asStateFlow()
+
+ fun finishComboTask(id: Long) {
+ viewModelScope.launch {
+ _finishComboTaskState.value = UiState.Loading
+ _finishComboTaskState.value = repository.finishComboTask(id)
+ }
+ }
+
+ // 获取组配任务视频监控地址
+ private val _comboTaskVideoState = MutableStateFlow>(UiState.Idle)
+ val comboTaskVideoState: StateFlow> = _comboTaskVideoState.asStateFlow()
+
+ fun getComboTaskVideo(taskNo: String) {
+ viewModelScope.launch {
+ _comboTaskVideoState.value = UiState.Loading
+ _comboTaskVideoState.value = repository.getComboTaskVideo(taskNo)
+ }
+ }
+
+ // ========== 单品净菜包装接口 ==========
+
+ private val _addCleanPackageState = MutableStateFlow>(UiState.Idle)
+ val addCleanPackageState: StateFlow> = _addCleanPackageState.asStateFlow()
+
+ fun addCleanPackage(param: NutSupCleanPackageDTO) {
+ viewModelScope.launch {
+ _addCleanPackageState.value = UiState.Loading
+ _addCleanPackageState.value = repository.addCleanPackage(param)
+ }
+ }
+
+ // ========== 餐品净菜包装接口 ==========
+
+ private val _addMealPackageState = MutableStateFlow>(UiState.Idle)
+ val addMealPackageState: StateFlow> = _addMealPackageState.asStateFlow()
+
+ fun addMealPackage(param: NutSupMealPackageDTO) {
+ viewModelScope.launch {
+ _addMealPackageState.value = UiState.Loading
+ _addMealPackageState.value = repository.addMealPackage(param)
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/shuwei/dish/match/net/Page.kt b/app/src/main/java/com/shuwei/dish/match/net/Page.kt
new file mode 100644
index 0000000..9eaa095
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/net/Page.kt
@@ -0,0 +1,10 @@
+package com.shuwei.dish.match.net
+
+/** 通用分页响应包装 */
+data class Page(
+ val records: List?,
+ val total: Long?,
+ val size: Int?,
+ val current: Int?,
+ val pages: Int?
+)
diff --git a/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt b/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt
new file mode 100644
index 0000000..249cc39
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/net/RemoteRepositoryV2.kt
@@ -0,0 +1,92 @@
+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.NutDictItemVO
+import com.shuwei.dish.match.model.NutFoodOptionVO
+import com.shuwei.dish.match.model.NutMaterOptionVO
+import com.shuwei.dish.match.model.NutProdComboTaskDTO
+import com.shuwei.dish.match.model.NutProdComboVideoVO
+import com.shuwei.dish.match.model.NutSupCleanPackageDTO
+import com.shuwei.dish.match.model.NutSupHygieneIngredientOptionVO
+import com.shuwei.dish.match.model.NutSupMealPackageDTO
+import com.shuwei.dish.match.model.NutSupPkgDeviceVO
+import com.shuwei.dish.match.base.GlobalData
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+/**
+ * 网络数据仓库 V2,封装所有 ApiServiceV2 调用
+ */
+class RemoteRepositoryV2 {
+
+ private val apiService: ApiServiceV2 by lazy {
+ Retrofit.Builder()
+ .baseUrl(GlobalData.appBaseUrl)
+ .client(HttpManager.instance.client)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ .create(ApiServiceV2::class.java)
+ }
+
+ /** 统一封装 API 调用:处理响应映射和异常转换,返回 UiState */
+ private suspend fun safeApiCall(call: suspend () -> ApiResponse): UiState {
+ return try {
+ val resp = call()
+ if (resp.isSuccess()) UiState.Success(resp.data)
+ else UiState.Error(resp.code, resp.msg ?: "")
+ } catch (e: Exception) {
+ UiState.Error("-1", getApiException(e).errorMsg)
+ }
+ }
+
+ // ========== 下拉选项接口 ==========
+
+ suspend fun getCanteenOptions(keyword: String? = null): UiState?> =
+ safeApiCall { apiService.getCanteenOptions(keyword = keyword) }
+
+ suspend fun getDictItems(dictType: String): UiState?> =
+ safeApiCall { apiService.getDictItems(dictType = dictType) }
+
+ suspend fun getMaterOptions(keyword: String? = null): UiState?> =
+ safeApiCall { apiService.getMaterOptions(keyword = keyword) }
+
+ suspend fun getDeviceOptions(): UiState?> =
+ safeApiCall { apiService.getDeviceOptions() }
+
+ suspend fun getFoodOptions(
+ keyword: String? = null,
+ pageNum: Int = 1,
+ pageSize: Int = 10
+ ): UiState?> =
+ safeApiCall { apiService.getFoodOptions(keyword = keyword, pageNum = pageNum, pageSize = pageSize) }
+
+ suspend fun getIngredientByTrace(traceCode: String): UiState?> =
+ safeApiCall { apiService.getIngredientByTrace(traceCode = traceCode) }
+
+ // ========== 净菜组配接口 ==========
+
+ suspend fun addComboTask(param: NutProdComboTaskDTO): UiState =
+ safeApiCall { apiService.addComboTask(param = param) }
+
+ suspend fun startComboTask(id: Long): UiState =
+ safeApiCall { apiService.startComboTask(param = IdDTO(id)) }
+
+ suspend fun finishComboTask(id: Long): UiState =
+ safeApiCall { apiService.finishComboTask(param = IdDTO(id)) }
+
+ suspend fun getComboTaskVideo(taskNo: String): UiState =
+ safeApiCall { apiService.getComboTaskVideo(taskNo = taskNo) }
+
+ // ========== 单品净菜包装接口 ==========
+
+ suspend fun addCleanPackage(param: NutSupCleanPackageDTO): UiState =
+ safeApiCall { apiService.addCleanPackage(param = param) }
+
+ // ========== 餐品净菜包装接口 ==========
+
+ suspend fun addMealPackage(param: NutSupMealPackageDTO): UiState =
+ safeApiCall { apiService.addMealPackage(param = param) }
+
+}
diff --git a/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt
new file mode 100644
index 0000000..3bab141
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/ui/CleanPackActivity.kt
@@ -0,0 +1,181 @@
+package com.shuwei.dish.match.ui
+
+import android.os.Bundle
+import androidx.recyclerview.widget.GridLayoutManager
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexWrap
+import com.shuwei.dish.match.R
+import com.shuwei.dish.match.adapter.FormFieldAdapter
+import com.shuwei.dish.match.base.BaseActivity
+import com.shuwei.dish.match.databinding.ActivityCleanPackBinding
+import com.shuwei.dish.match.model.FieldType
+import com.shuwei.dish.match.model.FormField
+import com.shuwei.dish.match.utils.ext.dp
+import com.shuwei.dish.match.utils.ext.gone
+import com.shuwei.dish.match.utils.ext.visible
+
+/**
+ * 净菜包装表单页面,根据类型显示不同的表单字段
+ */
+class CleanPackActivity : BaseActivity() {
+
+ companion object {
+ /** 单品净菜包装 */
+ const val TYPE_CLEAN = 1
+
+ /** 餐品净菜包装 */
+ const val TYPE_MEAL = 2
+
+ const val EXTRA_TYPE = "extra_type"
+ }
+
+ private lateinit var binding: ActivityCleanPackBinding
+ private val fields = mutableListOf()
+ private val formAdapter by lazy { FormFieldAdapter(this, fields) }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ statusBarDarkFont(enable = true)
+
+ binding = ActivityCleanPackBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ setHeaderBackground()
+ val packType = intent.getIntExtra(EXTRA_TYPE, TYPE_CLEAN)
+
+ // 根据类型设置标题
+ val title = when (packType) {
+ TYPE_CLEAN -> "净菜包"
+ TYPE_MEAL -> "餐品净菜包"
+ else -> return
+ }
+
+ setTitleBar(titleBarAction = {
+ it.visible()
+ it.setOnClickListener { v ->
+ hideKeyboard()
+ }
+ }, titleAction = {
+ it.text = title
+ }, rightIconAction = {
+ it.gone()
+ }, backAction = {
+ it.visible()
+ it.setOnClickListener {
+ finish()
+ }
+ })
+
+ // 初始化 RecyclerView
+// val flexboxLayoutManager = FlexboxLayoutManager(this).apply {
+// flexDirection = FlexDirection.ROW
+// flexWrap = FlexWrap.WRAP
+// alignItems = AlignItems.STRETCH
+// }
+ val layoutManager = GridLayoutManager(this, 1).apply {}
+ binding.rvForm.layoutManager = layoutManager
+ binding.rvForm.adapter = formAdapter
+
+ // 根据类型初始化表单数据
+ when (packType) {
+ TYPE_CLEAN -> initCleanPackFields()
+ TYPE_MEAL -> initMealPackFields()
+ }
+
+ binding.btnSubmit.setOnClickListener {
+ // TODO: 提交表单数据
+ }
+ }
+
+ /**
+ * 初始化单品净菜包装表单字段
+ * 对应接口 NutSupCleanPackageDTO
+ */
+ private fun initCleanPackFields() {
+ fields.clear()
+ fields.add(
+ FormField(
+ label = "溯源码",
+ type = FieldType.TEXT,
+ apiKey = "traceCode",
+ required = true
+ )
+ )
+ fields.add(
+ FormField(
+ label = "净菜名称",
+ type = FieldType.TEXT,
+ apiKey = "cleanName",
+ required = true
+ )
+ )
+ fields.add(
+ FormField(
+ label = "包装方式",
+ type = FieldType.TEXT,
+ apiKey = "packageMethod",
+ required = true
+ )
+ )
+ fields.add(
+ FormField(label = "包装规格", type = FieldType.TEXT, apiKey = "spec", required = true)
+ )
+ 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
+ )
+ )
+ fields.add(
+ FormField(label = "终端设备", type = FieldType.TEXT, apiKey = "", required = false)
+ )
+ formAdapter.submitList(fields.filter { !it.hidden })
+ }
+
+ /**
+ * 初始化餐品净菜包装表单字段
+ * TODO: 由用户提供具体字段数据
+ */
+ private fun initMealPackFields() {
+ fields.clear()
+ // 待填充
+ formAdapter.submitList(fields)
+ }
+
+}
diff --git a/app/src/main/java/com/shuwei/dish/match/ui/HomeV2Activity.kt b/app/src/main/java/com/shuwei/dish/match/ui/HomeV2Activity.kt
index a093a1b..fa50503 100644
--- a/app/src/main/java/com/shuwei/dish/match/ui/HomeV2Activity.kt
+++ b/app/src/main/java/com/shuwei/dish/match/ui/HomeV2Activity.kt
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.ui
import android.os.Bundle
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityHomeV2Binding
+import com.shuwei.dish.match.utils.ext.startActivity
/**
* 主页V2 - 净菜包装页面
@@ -19,11 +20,15 @@ class HomeV2Activity : BaseActivity() {
setContentView(binding.root)
binding.btnCleanPack.setOnClickListener {
- // 净菜包点击
+ startActivity {
+ putExtra(CleanPackActivity.EXTRA_TYPE, CleanPackActivity.TYPE_CLEAN)
+ }
}
binding.btnMealPack.setOnClickListener {
- // 餐品净菜包点击
+ startActivity {
+ putExtra(CleanPackActivity.EXTRA_TYPE, CleanPackActivity.TYPE_MEAL)
+ }
}
}
}
diff --git a/app/src/main/res/layout/activity_clean_pack.xml b/app/src/main/res/layout/activity_clean_pack.xml
new file mode 100644
index 0000000..d5b321c
--- /dev/null
+++ b/app/src/main/res/layout/activity_clean_pack.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+