feat(form): 新增净菜包装功能并优化表单字段类型
- 添加 CleanPackActivity 页面用于净菜包装表单 - 将 NUMBER 字段类型拆分为 INTEGER 和 DECIMAL 类型 - 更新 FormFieldAdapter 适配新的字段类型 - 在 HomeV2Activity 中添加跳转到净菜包装页面的功能 - 创建 ApiServiceV2、NetViewModelV2 和 RemoteRepositoryV2 处理新接口 - 添加营养相关模型类和分页组件 - 在 AndroidManifest.xml 中注册新 Activity
This commit is contained in:
@@ -64,6 +64,11 @@
|
|||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||||
|
|
||||||
|
<activity android:name=".ui.CleanPackActivity"
|
||||||
|
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.CookingModeActivity"
|
android:name=".ui.CookingModeActivity"
|
||||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import kotlin.math.min
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态表单字段 Adapter,配合 FlexboxLayoutManager 实现两列自动换行布局
|
* 动态表单字段 Adapter,配合 FlexboxLayoutManager 实现两列自动换行布局
|
||||||
* 单一 item 布局,通过 visibility 控制 TEXT/NUMBER/FIXED/DROPDOWN/DATE_PICKER 五种类型渲染
|
* 单一 item 布局,通过 visibility 控制 TEXT/INTEGER/DECIMAL/FIXED/DROPDOWN/DATE_PICKER 六种类型渲染
|
||||||
*
|
*
|
||||||
* @param activityContext Activity context,用于创建 DatePickerDialog
|
* @param activityContext Activity context,用于创建 DatePickerDialog
|
||||||
* @param fields 表单字段列表(hidden=false 的字段,由外部过滤后传入)
|
* @param fields 表单字段列表(hidden=false 的字段,由外部过滤后传入)
|
||||||
@@ -67,7 +67,8 @@ class FormFieldAdapter(
|
|||||||
|
|
||||||
when (item.type) {
|
when (item.type) {
|
||||||
FieldType.TEXT -> bindEditText(b, item, InputType.TYPE_CLASS_TEXT)
|
FieldType.TEXT -> bindEditText(b, item, InputType.TYPE_CLASS_TEXT)
|
||||||
FieldType.NUMBER -> bindEditText(
|
FieldType.INTEGER -> bindEditText(b, item, InputType.TYPE_CLASS_NUMBER)
|
||||||
|
FieldType.DECIMAL -> bindEditText(
|
||||||
b, item,
|
b, item,
|
||||||
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
|
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ package com.shuwei.dish.match.model
|
|||||||
enum class FieldType {
|
enum class FieldType {
|
||||||
/** 普通文本输入 */
|
/** 普通文本输入 */
|
||||||
TEXT,
|
TEXT,
|
||||||
/** 数字输入(支持小数和负数) */
|
/** 整数输入 */
|
||||||
NUMBER,
|
INTEGER,
|
||||||
|
/** 小数输入 */
|
||||||
|
DECIMAL,
|
||||||
/** 下拉选择 */
|
/** 下拉选择 */
|
||||||
DROPDOWN,
|
DROPDOWN,
|
||||||
/** 日期选择弹窗 */
|
/** 日期选择弹窗 */
|
||||||
|
|||||||
@@ -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<NutFoodComposition>?
|
||||||
|
) : 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<NutProdComboTaskItem>?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组配任务食材明细
|
||||||
|
*/
|
||||||
|
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<NutSupMealPackageIngredient>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 餐品净菜包装食材明细
|
||||||
|
*/
|
||||||
|
data class NutSupMealPackageIngredient(
|
||||||
|
val materId: Long,
|
||||||
|
val ingredientName: String,
|
||||||
|
val amount: String?,
|
||||||
|
val traceCode: String
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID通用DTO - 用于开始/完成组配等需要id参数的接口
|
||||||
|
*/
|
||||||
|
data class IdDTO(
|
||||||
|
val id: Long
|
||||||
|
)
|
||||||
@@ -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<List<NutCanteenVO>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典项下拉,按 dictType 查询字典项
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
suspend fun getDictItems(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/dict-items",
|
||||||
|
@Query("dictType") dictType: String
|
||||||
|
): ApiResponse<List<NutDictItemVO>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 食材下拉列表,支持关键字模糊搜索
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
suspend fun getMaterOptions(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/mater-options",
|
||||||
|
@Query("keyword") keyword: String? = null
|
||||||
|
): ApiResponse<List<NutMaterOptionVO>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装设备下拉(仅在线),返回在线设备的 id、编号、名称
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
suspend fun getDeviceOptions(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/device-options"
|
||||||
|
): ApiResponse<List<NutSupPkgDeviceVO>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 餐品下拉搜索(含食材构成),按名称模糊搜索,分页返回
|
||||||
|
*/
|
||||||
|
@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<Page<NutFoodOptionVO>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按溯源码查食材信息
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
suspend fun getIngredientByTrace(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/ingredient-by-trace",
|
||||||
|
@Query("traceCode") traceCode: String
|
||||||
|
): ApiResponse<List<NutSupHygieneIngredientOptionVO>>
|
||||||
|
|
||||||
|
// ========== 二、净菜组配接口 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报组配任务(含食材明细)
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun addComboTask(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/add",
|
||||||
|
@Body param: NutProdComboTaskDTO
|
||||||
|
): ApiResponse<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始组配,组配状态 0 -> 1
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun startComboTask(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/start",
|
||||||
|
@Body param: IdDTO
|
||||||
|
): ApiResponse<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成组配,组配状态 1 -> 2
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun finishComboTask(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/finish",
|
||||||
|
@Body param: IdDTO
|
||||||
|
): ApiResponse<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组配任务视频监控地址
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
suspend fun getComboTaskVideo(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/combo-task/video",
|
||||||
|
@Query("taskNo") taskNo: String
|
||||||
|
): ApiResponse<NutProdComboVideoVO>
|
||||||
|
|
||||||
|
// ========== 三、单品净菜包装接口 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报单品净菜包装记录
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun addCleanPackage(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/clean-package/add",
|
||||||
|
@Body param: NutSupCleanPackageDTO
|
||||||
|
): ApiResponse<Unit>
|
||||||
|
|
||||||
|
// ========== 四、餐品净菜包装接口 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报餐品净菜包装记录
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun addMealPackage(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/meal-package/add",
|
||||||
|
@Body param: NutSupMealPackageDTO
|
||||||
|
): ApiResponse<Unit>
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<List<NutCanteenVO>?>>(UiState.Idle)
|
||||||
|
val canteenOptionsState: StateFlow<UiState<List<NutCanteenVO>?>> = _canteenOptionsState.asStateFlow()
|
||||||
|
|
||||||
|
fun getCanteenOptions(keyword: String? = null) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_canteenOptionsState.value = UiState.Loading
|
||||||
|
_canteenOptionsState.value = repository.getCanteenOptions(keyword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字典项下拉
|
||||||
|
private val _dictItemsState = MutableStateFlow<UiState<List<NutDictItemVO>?>>(UiState.Idle)
|
||||||
|
val dictItemsState: StateFlow<UiState<List<NutDictItemVO>?>> = _dictItemsState.asStateFlow()
|
||||||
|
|
||||||
|
fun getDictItems(dictType: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_dictItemsState.value = UiState.Loading
|
||||||
|
_dictItemsState.value = repository.getDictItems(dictType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 食材下拉列表
|
||||||
|
private val _materOptionsState = MutableStateFlow<UiState<List<NutMaterOptionVO>?>>(UiState.Idle)
|
||||||
|
val materOptionsState: StateFlow<UiState<List<NutMaterOptionVO>?>> = _materOptionsState.asStateFlow()
|
||||||
|
|
||||||
|
fun getMaterOptions(keyword: String? = null) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_materOptionsState.value = UiState.Loading
|
||||||
|
_materOptionsState.value = repository.getMaterOptions(keyword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包装设备下拉
|
||||||
|
private val _deviceOptionsState = MutableStateFlow<UiState<List<NutSupPkgDeviceVO>?>>(UiState.Idle)
|
||||||
|
val deviceOptionsState: StateFlow<UiState<List<NutSupPkgDeviceVO>?>> = _deviceOptionsState.asStateFlow()
|
||||||
|
|
||||||
|
fun getDeviceOptions() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_deviceOptionsState.value = UiState.Loading
|
||||||
|
_deviceOptionsState.value = repository.getDeviceOptions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 餐品下拉搜索
|
||||||
|
private val _foodOptionsState = MutableStateFlow<UiState<Page<NutFoodOptionVO>?>>(UiState.Idle)
|
||||||
|
val foodOptionsState: StateFlow<UiState<Page<NutFoodOptionVO>?>> = _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<List<NutSupHygieneIngredientOptionVO>?>>(UiState.Idle)
|
||||||
|
val ingredientByTraceState: StateFlow<UiState<List<NutSupHygieneIngredientOptionVO>?>> = _ingredientByTraceState.asStateFlow()
|
||||||
|
|
||||||
|
fun getIngredientByTrace(traceCode: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_ingredientByTraceState.value = UiState.Loading
|
||||||
|
_ingredientByTraceState.value = repository.getIngredientByTrace(traceCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 净菜组配接口 ==========
|
||||||
|
|
||||||
|
// 上报组配任务
|
||||||
|
private val _addComboTaskState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||||
|
val addComboTaskState: StateFlow<UiState<Unit?>> = _addComboTaskState.asStateFlow()
|
||||||
|
|
||||||
|
fun addComboTask(param: NutProdComboTaskDTO) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_addComboTaskState.value = UiState.Loading
|
||||||
|
_addComboTaskState.value = repository.addComboTask(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始组配
|
||||||
|
private val _startComboTaskState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||||
|
val startComboTaskState: StateFlow<UiState<Unit?>> = _startComboTaskState.asStateFlow()
|
||||||
|
|
||||||
|
fun startComboTask(id: Long) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_startComboTaskState.value = UiState.Loading
|
||||||
|
_startComboTaskState.value = repository.startComboTask(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完成组配
|
||||||
|
private val _finishComboTaskState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||||
|
val finishComboTaskState: StateFlow<UiState<Unit?>> = _finishComboTaskState.asStateFlow()
|
||||||
|
|
||||||
|
fun finishComboTask(id: Long) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_finishComboTaskState.value = UiState.Loading
|
||||||
|
_finishComboTaskState.value = repository.finishComboTask(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取组配任务视频监控地址
|
||||||
|
private val _comboTaskVideoState = MutableStateFlow<UiState<NutProdComboVideoVO?>>(UiState.Idle)
|
||||||
|
val comboTaskVideoState: StateFlow<UiState<NutProdComboVideoVO?>> = _comboTaskVideoState.asStateFlow()
|
||||||
|
|
||||||
|
fun getComboTaskVideo(taskNo: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_comboTaskVideoState.value = UiState.Loading
|
||||||
|
_comboTaskVideoState.value = repository.getComboTaskVideo(taskNo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 单品净菜包装接口 ==========
|
||||||
|
|
||||||
|
private val _addCleanPackageState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||||
|
val addCleanPackageState: StateFlow<UiState<Unit?>> = _addCleanPackageState.asStateFlow()
|
||||||
|
|
||||||
|
fun addCleanPackage(param: NutSupCleanPackageDTO) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_addCleanPackageState.value = UiState.Loading
|
||||||
|
_addCleanPackageState.value = repository.addCleanPackage(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 餐品净菜包装接口 ==========
|
||||||
|
|
||||||
|
private val _addMealPackageState = MutableStateFlow<UiState<Unit?>>(UiState.Idle)
|
||||||
|
val addMealPackageState: StateFlow<UiState<Unit?>> = _addMealPackageState.asStateFlow()
|
||||||
|
|
||||||
|
fun addMealPackage(param: NutSupMealPackageDTO) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_addMealPackageState.value = UiState.Loading
|
||||||
|
_addMealPackageState.value = repository.addMealPackage(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.shuwei.dish.match.net
|
||||||
|
|
||||||
|
/** 通用分页响应包装 */
|
||||||
|
data class Page<T>(
|
||||||
|
val records: List<T>?,
|
||||||
|
val total: Long?,
|
||||||
|
val size: Int?,
|
||||||
|
val current: Int?,
|
||||||
|
val pages: Int?
|
||||||
|
)
|
||||||
@@ -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 <T> safeApiCall(call: suspend () -> ApiResponse<T>): UiState<T?> {
|
||||||
|
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<List<NutCanteenVO>?> =
|
||||||
|
safeApiCall { apiService.getCanteenOptions(keyword = keyword) }
|
||||||
|
|
||||||
|
suspend fun getDictItems(dictType: String): UiState<List<NutDictItemVO>?> =
|
||||||
|
safeApiCall { apiService.getDictItems(dictType = dictType) }
|
||||||
|
|
||||||
|
suspend fun getMaterOptions(keyword: String? = null): UiState<List<NutMaterOptionVO>?> =
|
||||||
|
safeApiCall { apiService.getMaterOptions(keyword = keyword) }
|
||||||
|
|
||||||
|
suspend fun getDeviceOptions(): UiState<List<NutSupPkgDeviceVO>?> =
|
||||||
|
safeApiCall { apiService.getDeviceOptions() }
|
||||||
|
|
||||||
|
suspend fun getFoodOptions(
|
||||||
|
keyword: String? = null,
|
||||||
|
pageNum: Int = 1,
|
||||||
|
pageSize: Int = 10
|
||||||
|
): UiState<Page<NutFoodOptionVO>?> =
|
||||||
|
safeApiCall { apiService.getFoodOptions(keyword = keyword, pageNum = pageNum, pageSize = pageSize) }
|
||||||
|
|
||||||
|
suspend fun getIngredientByTrace(traceCode: String): UiState<List<NutSupHygieneIngredientOptionVO>?> =
|
||||||
|
safeApiCall { apiService.getIngredientByTrace(traceCode = traceCode) }
|
||||||
|
|
||||||
|
// ========== 净菜组配接口 ==========
|
||||||
|
|
||||||
|
suspend fun addComboTask(param: NutProdComboTaskDTO): UiState<Unit?> =
|
||||||
|
safeApiCall { apiService.addComboTask(param = param) }
|
||||||
|
|
||||||
|
suspend fun startComboTask(id: Long): UiState<Unit?> =
|
||||||
|
safeApiCall { apiService.startComboTask(param = IdDTO(id)) }
|
||||||
|
|
||||||
|
suspend fun finishComboTask(id: Long): UiState<Unit?> =
|
||||||
|
safeApiCall { apiService.finishComboTask(param = IdDTO(id)) }
|
||||||
|
|
||||||
|
suspend fun getComboTaskVideo(taskNo: String): UiState<NutProdComboVideoVO?> =
|
||||||
|
safeApiCall { apiService.getComboTaskVideo(taskNo = taskNo) }
|
||||||
|
|
||||||
|
// ========== 单品净菜包装接口 ==========
|
||||||
|
|
||||||
|
suspend fun addCleanPackage(param: NutSupCleanPackageDTO): UiState<Unit?> =
|
||||||
|
safeApiCall { apiService.addCleanPackage(param = param) }
|
||||||
|
|
||||||
|
// ========== 餐品净菜包装接口 ==========
|
||||||
|
|
||||||
|
suspend fun addMealPackage(param: NutSupMealPackageDTO): UiState<Unit?> =
|
||||||
|
safeApiCall { apiService.addMealPackage(param = param) }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<FormField>()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.ui
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
import com.shuwei.dish.match.databinding.ActivityHomeV2Binding
|
import com.shuwei.dish.match.databinding.ActivityHomeV2Binding
|
||||||
|
import com.shuwei.dish.match.utils.ext.startActivity
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主页V2 - 净菜包装页面
|
* 主页V2 - 净菜包装页面
|
||||||
@@ -19,11 +20,15 @@ class HomeV2Activity : BaseActivity() {
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
binding.btnCleanPack.setOnClickListener {
|
binding.btnCleanPack.setOnClickListener {
|
||||||
// 净菜包点击
|
startActivity<CleanPackActivity> {
|
||||||
|
putExtra(CleanPackActivity.EXTRA_TYPE, CleanPackActivity.TYPE_CLEAN)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.btnMealPack.setOnClickListener {
|
binding.btnMealPack.setOnClickListener {
|
||||||
// 餐品净菜包点击
|
startActivity<CleanPackActivity> {
|
||||||
|
putExtra(CleanPackActivity.EXTRA_TYPE, CleanPackActivity.TYPE_MEAL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:background="@color/white"
|
||||||
|
tools:ignore="HardcodedText">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvForm"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:overScrollMode="never" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnSubmit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_margin="30dp"
|
||||||
|
android:background="@drawable/shape_green_bg"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="确认提交"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user