refactor(viewmodel): 将 searchFoodList/getCookFoodList 迁移至 StateFlow,DishListFragment 改用 combine 并行合并数据并修复分页本地数据混入问题
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user