refactor(viewmodel): 将 searchFoodList/getCookFoodList 迁移至 StateFlow,DishListFragment 改用 combine 并行合并数据并修复分页本地数据混入问题

This commit is contained in:
2026-04-23 09:54:34 +08:00
parent 16fe64e9fe
commit 9e99e6ef6d
9 changed files with 246 additions and 269 deletions
@@ -6,6 +6,9 @@ import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.entity.SeasoningEntity
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
/**
@@ -42,16 +45,19 @@ class NetViewModel(
}
}
/**
* 搜索菜品列表的 UI 状态流,UI 层通过 collect 监听
*/
private val _searchFoodState = MutableStateFlow<UiState<MutableList<FoodRecord>?>>(UiState.Idle)
val searchFoodState: StateFlow<UiState<MutableList<FoodRecord>?>> = _searchFoodState.asStateFlow()
/**
* 搜索菜品列表
*/
fun searchFoodList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) {
fun searchFoodList(param: MutableMap<String, Any>) {
viewModelScope.launch {
repository.searchFoodList(param, onSuccess, onFailure)
_searchFoodState.value = UiState.Loading
_searchFoodState.value = repository.searchFoodList(param)
}
}
@@ -46,18 +46,18 @@ class RemoteRepository {
/**
* 搜索菜品列表
* @param param 查询参数
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
* @return UiState 包装的结果,Success 携带列表数据,Error 携带错误信息
*/
suspend fun searchFoodList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.searchFoodList(param = param) },
onSuccess = onSuccess,
onFailure = onFailure
)
suspend fun searchFoodList(param: MutableMap<String, Any>): UiState<MutableList<FoodRecord>?> {
return try {
val resp = apiService.searchFoodList(param = param)
if (resp.isSuccess()) UiState.Success(resp.data)
else UiState.Error(resp.code, resp.msg ?: "")
} catch (e: Exception) {
val ex = getApiException(e)
UiState.Error("-1", ex.errorMsg)
}
}
/**
* 查询采样数据列表
@@ -0,0 +1,15 @@
package com.shuwei.dish.match.net
/**
* 统一 UI 状态模型,用于 ViewModel 向 UI 层传递请求状态
* Idle — 初始状态,未发起请求
* Loading — 请求进行中
* Success — 请求成功,携带数据
* Error — 请求失败,携带错误码和错误信息
*/
sealed class UiState<out T> {
object Idle : UiState<Nothing>()
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val code: String, val msg: String) : UiState<Nothing>()
}
@@ -107,8 +107,8 @@ class DishSamplingActivity : BaseActivity() {
ViewModelProvider(this, factory)[AppViewModel::class.java]
}
fun getCookFoodList(action: (MutableList<CookFoodEntity>?) -> Unit) {
appViewModel.getCookFoodList(cookMode = 1) { action(it) }
fun getCookFoodList() {
appViewModel.getCookFoodList(cookMode = 1)
}
/**
@@ -4,6 +4,9 @@ import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.FoodRecordAdapter
@@ -12,6 +15,7 @@ import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.ActivityFoodSearchBinding
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.utils.KeyboardUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
import com.shuwei.dish.match.utils.ext.gone
@@ -19,6 +23,7 @@ import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.utils.ext.visible
import java.io.Serializable
import kotlinx.coroutines.launch
class FoodSearchActivity : BaseActivity() {
@@ -83,6 +88,31 @@ class FoodSearchActivity : BaseActivity() {
adapter = recordAdapter
}
addViewListener()
initObserver()
}
/**
* 收集 searchFoodState,统一处理 Loading / Success / Error 状态
*/
private fun initObserver() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
netViewModel.searchFoodState.collect { state ->
when (state) {
is UiState.Loading -> showLoading()
is UiState.Success -> handleSearchResult(state.data)
is UiState.Error -> {
delayDismissLoading()
finishRefresh()
binding.refreshLayout.setEnableRefresh(true)
toast(state.msg)
if (pageNo == 1) loadEmptyView()
}
is UiState.Idle -> {}
}
}
}
}
}
private fun addViewListener() {
@@ -140,61 +170,6 @@ class FoodSearchActivity : BaseActivity() {
@SuppressLint("NotifyDataSetChanged")
private fun queryListInfo(input: String) {
showLoading()
// val map = mutableMapOf(
// "foodName" to input.trim(),
// "pageNo" to "$pageNo",
// "pageSize" to "$pageSize",
// "canteenId" to BaseApp.canteenId
// "dinnerType" to getDinnerTypeText()
// )
// val sb = StringBuilder(UrlConfig.QUERY_FOOD_LIST).apply {
// append("?")
// map.forEach { (key, value) -> append("$key=$value&") }
// }
// sb.deleteCharAt(sb.length - 1)
// HttpUtil.get(
// url = sb.toString(),
// doSuccess = {
// delayDismissLoading()
// finishRefresh()
// binding.refreshLayout.run {
// setEnableRefresh(true)
// }
// val json = it.toJsonString()
// val recordBean: FoodRecordBean? = json.toObject<FoodRecordBean>()
// if (recordBean == null || recordBean.records.isNullOrEmpty()) {
//// toast("暂未搜索到相关菜品信息")
// if (pageNo == 1) {
// loadEmptyView()
// }
// return@get
// }
// val records = recordBean.records
// if (pageNo == 1) {
// list.clear()
// }
// list.addAll(records!!)
// recordAdapter.notifyDataSetChanged()
// val isLoadMoreEnable = records.size >= pageSize
// binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
// if (isLoadMoreEnable) {
// pageNo++
// }
// },
// doFailure = { code, msg ->
// binding.refreshLayout.run {
// setEnableRefresh(true)
// }
// toast(msg)
// delayDismissLoading()
// finishRefresh()
// if (pageNo == 1) {
// loadEmptyView()
// }
// }
// )
val map = mutableMapOf<String, Any>(
"foodName" to input.trim(),
"pageNum" to pageNo,
@@ -202,40 +177,27 @@ class FoodSearchActivity : BaseActivity() {
"placeId" to BaseApp.canteenId,
"dinnerType" to getDinnerTypeText()
)
netViewModel.searchFoodList(
param = map,
onSuccess = {
delayDismissLoading()
finishRefresh()
binding.refreshLayout.setEnableRefresh(true)
if (it.isNullOrEmpty()) {
// toast("暂未搜索到相关菜品信息")
if (pageNo == 1) {
loadEmptyView()
}
return@searchFoodList
}
if (pageNo == 1) {
list.clear()
}
list.addAll(it)
recordAdapter.notifyDataSetChanged()
val isLoadMoreEnable = it.size >= pageSize
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
if (isLoadMoreEnable) {
pageNo++
}
}, onFailure = { code, msg ->
binding.refreshLayout.run {
setEnableRefresh(true)
}
toast(msg)
delayDismissLoading()
finishRefresh()
if (pageNo == 1) {
loadEmptyView()
}
})
netViewModel.searchFoodList(param = map)
}
/**
* 处理搜索结果,填充列表或展示空视图
*/
@SuppressLint("NotifyDataSetChanged")
private fun handleSearchResult(records: MutableList<FoodRecord>?) {
delayDismissLoading()
finishRefresh()
binding.refreshLayout.setEnableRefresh(true)
if (records.isNullOrEmpty()) {
if (pageNo == 1) loadEmptyView()
return
}
if (pageNo == 1) list.clear()
list.addAll(records)
recordAdapter.notifyDataSetChanged()
val isLoadMoreEnable = records.size >= pageSize
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
if (isLoadMoreEnable) pageNo++
}
private fun finishRefresh() {
@@ -5,7 +5,10 @@ import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import androidx.activity.addCallback
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.dish.match.R
@@ -27,6 +30,7 @@ import com.shuwei.dish.match.utils.ext.visible
import com.shuwei.dish.match.viewmodel.AppViewModel
import com.shuwei.dish.match.viewmodel.factory.AppFactory
import java.io.Serializable
import kotlinx.coroutines.launch
class SamplingListActivity : BaseActivity() {
@@ -47,10 +51,45 @@ class SamplingListActivity : BaseActivity() {
initViewModel()
initRecyclerView()
addViewListener()
initObserver()
requestData(true)
onBackPressedDispatcher.addCallback(this) { }
}
/**
* 收集 cookFoodListState,将本地烹饪中数据渲染到列表
*/
private fun initObserver() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
appViewModel.cookFoodListState.collect { cookFoodEntities ->
delayDismissLoading()
if (cookFoodEntities.isNullOrEmpty()) {
loadEmptyView()
return@collect
}
val tempList = mutableListOf<FoodRecord>()
cookFoodEntities.sortedByDescending { DateTimeUtil.convert(dateStr = it.createTime) }
.forEach { entity ->
tempList.add(FoodRecord().apply {
foodId = entity.foodId
foodName = entity.foodName
cookMode = 1
isCooking = true
})
}
list.clear()
list.addAll(tempList)
dishAdapter.notifyDataSetChanged()
binding.refreshLayout.run {
setEnableRefresh(false)
setEnableLoadMore(false)
}
}
}
}
}
private fun setTitleBar() {
setTitleBar(titleBarAction = {
@@ -78,8 +117,8 @@ class SamplingListActivity : BaseActivity() {
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
fun getCookFoodList(action: (MutableList<CookFoodEntity>?) -> Unit) {
appViewModel.getCookFoodList(cookMode = 1) { action(it) }
fun getCookFoodList() {
appViewModel.getCookFoodList(cookMode = 1)
}
fun deleteCookFoodAndGoods(foodId: String, action: () -> Unit) {
@@ -298,32 +337,7 @@ class SamplingListActivity : BaseActivity() {
@SuppressLint("NotifyDataSetChanged")
fun refreshLocalData() {
showLoading()
//查询本地数据
getCookFoodList { cookFoodEntities ->
delayDismissLoading()
if (cookFoodEntities.isNullOrEmpty()) {
loadEmptyView()
return@getCookFoodList
}
val tempList = mutableListOf<FoodRecord>()
cookFoodEntities.sortedByDescending { DateTimeUtil.convert(dateStr = it.createTime) }
.forEach { entity ->
tempList.add(FoodRecord().apply {
foodId = entity.foodId
foodName = entity.foodName
cookMode = 1
isCooking = true
})
}
list.clear()
list.addAll(tempList)
dishAdapter.notifyDataSetChanged()
binding.refreshLayout.run {
setEnableRefresh(false)
setEnableLoadMore(false)
}
}
getCookFoodList()
}
private fun requestData(isCooking: Boolean) {
@@ -10,8 +10,6 @@ import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.ActivitySelectDishBinding
import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.dialog.CommonDialog
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.ui.fragment.DishListFragment
import com.shuwei.dish.match.utils.KeyboardUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
@@ -32,7 +30,8 @@ class SelectDishActivity : BaseActivity() {
private lateinit var binding: ActivitySelectDishBinding
private lateinit var appViewModel: AppViewModel
lateinit var appViewModel: AppViewModel
private set
private var dinnerType: String = "1"
@@ -72,8 +71,8 @@ class SelectDishActivity : BaseActivity() {
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
fun getCookFoodList(action: (MutableList<CookFoodEntity>?) -> Unit) {
appViewModel.getCookFoodList(cookMode = 0, dinnerType = dinnerType) { action(it) }
fun getCookFoodList() {
appViewModel.getCookFoodList(cookMode = 0, dinnerType = dinnerType)
}
private var configFinished = false
@@ -189,16 +188,8 @@ class SelectDishActivity : BaseActivity() {
isVisible = false
}
public fun getFoodList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) {
netViewModel.searchFoodList(
param = param,
onSuccess = onSuccess,
onFailure = onFailure
)
public fun getFoodList(param: MutableMap<String, Any>) {
netViewModel.searchFoodList(param = param)
}
}
@@ -5,6 +5,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.chad.library.adapter4.util.setOnDebouncedItemClick
@@ -13,7 +16,9 @@ import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.base.BaseFragment
import com.shuwei.dish.match.databinding.FragmentDishListBinding
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.ui.PrepareCookActivity
import com.shuwei.dish.match.ui.SelectDishActivity
import com.shuwei.dish.match.ui.SubmitFoodActivity
@@ -21,6 +26,8 @@ import com.shuwei.dish.match.utils.SwipeCallback
import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toast
import java.io.Serializable
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
class DishListFragment : BaseFragment<FragmentDishListBinding>() {
@@ -108,6 +115,7 @@ class DishListFragment : BaseFragment<FragmentDishListBinding>() {
itemTouchHelper.attachToRecyclerView(this)
}
addViewListener()
initObserver()
pageNo = 1
activity.showLoading()
@@ -115,6 +123,39 @@ class DishListFragment : BaseFragment<FragmentDishListBinding>() {
binding.refreshLayout.setEnableRefresh(true)
}
/**
* 用 combine 同时监听网络结果与本地数据,网络成功时统一合并渲染
* pageNo > 1(加载更多)时 cookFoodListState 不参与合并,直接追加网络数据
*/
private fun initObserver() {
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
combine(
activity.netViewModel.searchFoodState,
activity.appViewModel.cookFoodListState
) { networkState, localList -> Pair(networkState, localList) }
.collect { (networkState, localList) ->
when (networkState) {
is UiState.Loading -> activity.showLoading()
is UiState.Success -> {
activity.delayDismissLoading()
finishRefresh()
loadAndMergeDishList(networkState.data, localList)
}
is UiState.Error -> {
if (isAdded.not()) return@collect
toast(networkState.msg)
finishRefresh()
if (pageNo == 1) loadEmptyView()
activity.delayDismissLoading()
}
is UiState.Idle -> {}
}
}
}
}
}
private fun addViewListener() {
binding.refreshLayout.run {
setEnableRefresh(true)
@@ -147,72 +188,15 @@ class DishListFragment : BaseFragment<FragmentDishListBinding>() {
@SuppressLint("NotifyDataSetChanged")
@Suppress("unchecked_cast")
fun getDishList() {
activity.showLoading()
activity.getFoodList(
param = mutableMapOf(
"pageNum" to pageNo,
"pageSize" to pageSize,
"placeId" to BaseApp.canteenId,
"dinnerType" to getDinnerTypeText()
),
onSuccess = { records ->
activity.delayDismissLoading()
finishRefresh()
loadDishList(records)
},
onFailure = { code, msg ->
if (isAdded.not()) {
return@getFoodList
}
toast(msg)
finishRefresh()
if (pageNo == 1) {
loadEmptyView()
}
activity.delayDismissLoading()
}
val param = mutableMapOf<String, Any>(
"pageNum" to pageNo,
"pageSize" to pageSize,
"placeId" to BaseApp.canteenId,
"dinnerType" to getDinnerTypeText()
)
// val map = mapOf(
//// "foodName" to input,
// "dinnerType" to getDinnerTypeText(),
// "pageNo" to "$pageNo",
// "pageSize" to "$pageSize",
// "canteenId" to BaseApp.canteenId
// )
// val sb = StringBuilder(UrlConfig.QUERY_FOOD_LIST).apply {
// append("?")
// map.forEach { (key, value) -> append("$key=$value&") }
// }
// sb.deleteCharAt(sb.length - 1)
// HttpUtil.get(
// url = sb.toString(),
// doSuccess = { data ->
// binding.refreshLayout.run {
// setEnableRefresh(true)
// }
// activity.delayDismissLoading()
// finishRefresh()
// val json = data.toJsonString()
// val recordBean: FoodRecordBean? = json.toObject<FoodRecordBean>()
// loadDishList(recordBean)
// }, doFailure = { code, msg ->
// try {
// if (isAdded.not()) {
// return@get
// }
// binding.refreshLayout.run {
// setEnableRefresh(true)
// }
// toast(msg)
// finishRefresh()
// if (pageNo == 1) {
// loadEmptyView()
// }
// activity.delayDismissLoading()
// } catch (e: Exception) {
// e.printStackTrace()
// }
// })
// 网络与本地并行触发,pageNo > 1 时不重复查询本地数据
activity.getFoodList(param = param)
if (pageNo == 1) activity.getCookFoodList()
}
private var emptyViewBinding: LayoutEmptyViewBinding? = null
@@ -248,64 +232,65 @@ class DishListFragment : BaseFragment<FragmentDishListBinding>() {
}
}
/**
* 将网络数据与本地烹饪中数据合并后渲染列表
* pageNo > 1 时忽略本地数据,直接追加网络数据,避免 combine 带入旧的本地状态
*/
@SuppressLint("NotifyDataSetChanged")
private fun refreshLocalData(list: MutableList<FoodRecord>) {
activity.getCookFoodList { cookFoodEntities ->
if (cookFoodEntities.isNullOrEmpty()) {
return@getCookFoodList
}
val tempList = mutableListOf<FoodRecord>()
cookFoodEntities.forEachIndexed { index, entity ->
val food = list.firstOrNull { it.foodId == entity.foodId }
if (food != null) {
food.isCooking = true
food.sort = index
food.dinnerType = entity.dinnerType ?: "0"
tempList.add(food)
} else {
//当前查询到的list不包括本地数据id,可能数据在很多页以后了,暂时只能直接构造数据
tempList.add(
FoodRecord(
foodId = entity.foodId,
foodName = entity.foodName,
sort = index,
dinnerType = entity.dinnerType ?: "0",
isCooking = true
)
)
}
}
list.removeAll(tempList)
list.addAll(0, tempList)
dishAdapter.notifyDataSetChanged()
}
}
@SuppressLint("NotifyDataSetChanged")
private fun loadDishList(records: MutableList<FoodRecord>?) {
private fun loadAndMergeDishList(
records: MutableList<FoodRecord>?,
localList: MutableList<CookFoodEntity>?
) {
try {
if (isAdded.not()) {
if (isAdded.not()) return
// pageNo > 1 时不参与本地合并,避免 combine 带入旧的本地状态
val effectiveLocalList = if (pageNo == 1) localList else null
if (records.isNullOrEmpty() && effectiveLocalList.isNullOrEmpty()) {
if (pageNo == 1) loadEmptyView()
return
}
if (records.isNullOrEmpty()) {
//toast("暂未搜索到相关菜品信息")
if (pageNo == 1) {
loadEmptyView()
if (pageNo == 1) list.clear()
val mergedList = records?.toMutableList() ?: mutableListOf()
// 仅 pageNo == 1 时才有本地数据参与合并
if (!effectiveLocalList.isNullOrEmpty()) {
val cookingItems = mutableListOf<FoodRecord>()
effectiveLocalList.forEachIndexed { index, entity ->
val food = mergedList.firstOrNull { it.foodId == entity.foodId }
if (food != null) {
mergedList.remove(food)
food.isCooking = true
food.sort = index
food.dinnerType = entity.dinnerType ?: "0"
cookingItems.add(food)
} else {
// 当前网络数据不包含该本地记录,直接构造
cookingItems.add(
FoodRecord(
foodId = entity.foodId,
foodName = entity.foodName,
sort = index,
dinnerType = entity.dinnerType ?: "0",
isCooking = true
)
)
}
}
return
mergedList.addAll(0, cookingItems)
}
if (pageNo == 1) {
list.clear()
if (pageNo >= 2 && !localList.isNullOrEmpty()) {
//第2页起,从mergedList中移除localList中的同foodId数据
val localIds = localList.map { it.foodId }.toHashSet()
mergedList.removeAll { it.foodId in localIds }
}
list.addAll(records)
list.addAll(mergedList)
dishAdapter.notifyDataSetChanged()
val isLoadMoreEnable = records.size >= pageSize
val isLoadMoreEnable = (records?.size ?: 0) >= pageSize
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
if (isLoadMoreEnable) {
pageNo++
}
refreshLocalData(list)
if (isLoadMoreEnable) pageNo++
} catch (e: Exception) {
e.printStackTrace()
}
@@ -6,6 +6,9 @@ import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.SeasoningEntity
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -26,14 +29,15 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() {
}
}
private val _cookFoodListState = MutableStateFlow<MutableList<CookFoodEntity>?>(null)
val cookFoodListState: StateFlow<MutableList<CookFoodEntity>?> = _cookFoodListState.asStateFlow()
fun getCookFoodList(
cookMode: Int,
dinnerType: String = "0",
action: (MutableList<CookFoodEntity>?) -> Unit
dinnerType: String = "0"
) {
viewModelScope.launch {
val data = rep.getCookFoodList(cookMode, dinnerType)
action(data)
_cookFoodListState.value = rep.getCookFoodList(cookMode, dinnerType)
}
}