feat(form): 新增净菜包装功能并优化表单字段类型

- 添加 CleanPackActivity 页面用于净菜包装表单
- 将 NUMBER 字段类型拆分为 INTEGER 和 DECIMAL 类型
- 更新 FormFieldAdapter 适配新的字段类型
- 在 HomeV2Activity 中添加跳转到净菜包装页面的功能
- 创建 ApiServiceV2、NetViewModelV2 和 RemoteRepositoryV2 处理新接口
- 添加营养相关模型类和分页组件
- 在 AndroidManifest.xml 中注册新 Activity
This commit is contained in:
2026-06-01 11:21:04 +08:00
parent d7a1b75fef
commit c4a2105737
11 changed files with 829 additions and 6 deletions
+5
View File
@@ -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>