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"
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||
|
||||
<activity android:name=".ui.CleanPackActivity"
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
android:screenOrientation="portrait"
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.CookingModeActivity"
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
|
||||
@@ -29,7 +29,7 @@ import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 动态表单字段 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 fields 表单字段列表(hidden=false 的字段,由外部过滤后传入)
|
||||
@@ -67,7 +67,8 @@ class FormFieldAdapter(
|
||||
|
||||
when (item.type) {
|
||||
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,
|
||||
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 {
|
||||
/** 普通文本输入 */
|
||||
TEXT,
|
||||
/** 数字输入(支持小数和负数) */
|
||||
NUMBER,
|
||||
/** 整数输入 */
|
||||
INTEGER,
|
||||
/** 小数输入 */
|
||||
DECIMAL,
|
||||
/** 下拉选择 */
|
||||
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 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<CleanPackActivity> {
|
||||
putExtra(CleanPackActivity.EXTRA_TYPE, CleanPackActivity.TYPE_CLEAN)
|
||||
}
|
||||
}
|
||||
|
||||
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