diff --git a/app/src/main/java/com/sw/inbound/ext/CommonExt.kt b/app/src/main/java/com/sw/inbound/ext/CommonExt.kt index 84599e2..15c6e0e 100644 --- a/app/src/main/java/com/sw/inbound/ext/CommonExt.kt +++ b/app/src/main/java/com/sw/inbound/ext/CommonExt.kt @@ -3,6 +3,7 @@ package com.sw.inbound.ext import java.math.BigDecimal import java.math.RoundingMode import kotlin.math.pow +import kotlin.math.round fun Float.toFormattedString(decimalPlaces: Int = 2): String { return when (this) { @@ -32,12 +33,12 @@ fun Double.toSafeBigDecimal(): BigDecimal { */ private fun Double.roundToDouble(decimalPlaces: Int): Double { val factor = 10.0.pow(decimalPlaces) - return kotlin.math.round(this * factor) / factor + return round(this * factor) / factor } private fun Float.roundToDouble(decimalPlaces: Int): Double { val factor = 10.0.pow(decimalPlaces) - return kotlin.math.round(this * factor) / factor + return round(this * factor) / factor } fun Float?.toSafeString(unitName: String?): String { @@ -101,4 +102,4 @@ fun BigDecimal.toFormattedString(): String { fun Int.toFormattedString(): String { return this.toString() -} +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt b/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt index 9831475..230373c 100644 --- a/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt @@ -16,15 +16,21 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -50,6 +56,9 @@ import com.sw.inbound.ui.theme.AppTypography.grayTextStyle import com.sw.inbound.ui.weight.BottomActionBar import com.sw.inbound.ui.weight.TopTitleBar import com.sw.inbound.viewmodel.PurchaseOrderViewModel +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import timber.log.Timber @Preview( widthDp = 1920, @@ -66,11 +75,32 @@ fun PurchaseOrderScreen( viewModel: PurchaseOrderViewModel = hiltViewModel() ) { val products by viewModel.supplierList.collectAsState() + val lazyListState = rememberLazyListState() + val canLoadMore by viewModel.canLoadMore.collectAsState() + val isMoreLoading by viewModel.isMoreLoading.collectAsState() + var pageNum by remember { mutableIntStateOf(1) } LaunchedEffect(Unit) { viewModel.getOrderList() } + // 检测是否滚动到底部 + LaunchedEffect(lazyListState) { + snapshotFlow { lazyListState.layoutInfo } + .map { layoutInfo -> + val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() + lastVisibleItem?.index == layoutInfo.totalItemsCount - 1 + } + .distinctUntilChanged() + .collect { reachedEnd -> + Timber.d("加载更多 reachedEnd = $reachedEnd, isMoreLoading = $isMoreLoading, canLoadMore = $canLoadMore") + if (reachedEnd && !isMoreLoading && canLoadMore) { + pageNum++ + viewModel.getOrderList(pageNum = pageNum) + } + } + } + Column { TopTitleBar() Box( @@ -101,6 +131,7 @@ fun PurchaseOrderScreen( LazyRow( modifier = modifier .padding(10.dp), + state = lazyListState, contentPadding = PaddingValues(16.dp), horizontalArrangement = Arrangement.spacedBy(30.dp) ) { @@ -114,6 +145,19 @@ fun PurchaseOrderScreen( ) } } + + if (isMoreLoading) { + item { + Box( + modifier = Modifier + .fillParentMaxHeight() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + } } } } diff --git a/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt b/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt index 1a7529a..8212242 100644 --- a/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt @@ -26,6 +26,10 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -34,6 +38,7 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -238,6 +243,11 @@ fun ContentRightView( @Composable fun ProductIdentification(viewModel: SelfProcurementViewModel) { val searchResultList by viewModel.searchListItems.collectAsState() + // 需要搜索的信息 + var searchInfo by remember { mutableStateOf("") } + var pageNum by remember { mutableIntStateOf(1) } + val isMoreLoading by viewModel.isMoreLoading.collectAsState() + val canLoadMore by viewModel.canLoadMore.collectAsState() Column( modifier = Modifier @@ -252,10 +262,18 @@ fun ProductIdentification(viewModel: SelfProcurementViewModel) { ) { // 菜品识别 IdentityView(list = searchResultList, showSearchView = true, onSearchClick = { + searchInfo = it viewModel.searchGoodsInfoList(it) }, onOptionSelected = { viewModel.updateSelectedItemWithSearch(it) - }) + }, onLoadMore = { + Timber.d("加载更多 canLoadMore = $canLoadMore") + if (!canLoadMore) { + return@IdentityView + } + pageNum++ + viewModel.searchGoodsInfoList(goodsName = searchInfo, pageNo = pageNum) + }, isMoreLoading = isMoreLoading, canLoadMore = canLoadMore) } Spacer(modifier = Modifier.height(10.dp)) Row( @@ -615,12 +633,16 @@ private fun SelfProductEditView( ) { Spacer(modifier = Modifier.height(10.dp)) Text( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 30.dp), textAlign = TextAlign.Center, text = selectedItem!!.goodsNameStr, + maxLines = 1, + overflow = TextOverflow.Ellipsis, style = AppTypography.black141428TextStyle.bold().withSize(30.sp) ) - Spacer(modifier = Modifier.height(20.dp)) + Spacer(modifier = Modifier.height(10.dp)) RowInputLayout( label = "仓库", @@ -757,7 +779,7 @@ private fun SelfProductEditView( ) } - Spacer(modifier = Modifier.height(40.dp)) + Spacer(modifier = Modifier.height(30.dp)) Row( horizontalArrangement = Arrangement.spacedBy(30.dp), modifier = Modifier diff --git a/app/src/main/java/com/sw/inbound/ui/weight/IdentityView.kt b/app/src/main/java/com/sw/inbound/ui/weight/IdentityView.kt index 15f376f..1158c0b 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/IdentityView.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/IdentityView.kt @@ -27,6 +27,9 @@ fun IdentityView( showSearchView: Boolean = false, onSearchClick: (String) -> Unit = {}, onOptionSelected: (SearchGoodsInfo.Record) -> Unit = {}, + onLoadMore: (Int) -> Unit = {}, + isMoreLoading: Boolean = false, + canLoadMore: Boolean = true ) { val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current @@ -59,7 +62,10 @@ fun IdentityView( modifier = Modifier .height(64.dp) .width(192.dp), options = list, - onOptionSelected = onOptionSelected + onOptionSelected = onOptionSelected, + onLoadMore = onLoadMore, + isMoreLoading = isMoreLoading, + canLoadMore = canLoadMore ) } else { SingleSelectButtonGroup( diff --git a/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt b/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt index a0528b9..73c138e 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt @@ -11,13 +11,17 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -31,6 +35,9 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.ui.theme.AppTypography +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import timber.log.Timber @Preview(showBackground = true) @@ -62,16 +69,43 @@ fun SingleSelectButtonGroup( onOptionSelected: (SearchGoodsInfo.Record) -> Unit, horizontalSpacing: Dp = 13.dp, verticalSpacing: Dp = 13.dp, - unSelectedTextStyle: TextStyle = AppTypography.gray96a0aaTextStyle + unSelectedTextStyle: TextStyle = AppTypography.gray96a0aaTextStyle, + onLoadMore: (Int) -> Unit = {}, + isMoreLoading: Boolean = false, + canLoadMore: Boolean = true ) { val context = LocalContext.current var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") } + val lazyGridState = rememberLazyGridState() +// val canLoadMore by viewModel.canLoadMore.collectAsState() +// val isMoreLoading by viewModel.isMoreLoading.collectAsState() + var pageNum by remember { mutableIntStateOf(1) } + + // 检测是否滚动到底部 + LaunchedEffect(lazyGridState) { + snapshotFlow { lazyGridState.layoutInfo } + .map { layoutInfo -> + val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() + lastVisibleItem?.index == layoutInfo.totalItemsCount - 1 + } + .distinctUntilChanged() + .collect { reachedEnd -> + Timber.d("加载更多 reachedEnd = $reachedEnd, isMoreLoading = $isMoreLoading, canLoadMore = $canLoadMore") + if (reachedEnd && !isMoreLoading && canLoadMore) { + pageNum++ + onLoadMore(pageNum) + } + } + } + // 定义颜色 val selectedColor = Color(0xFFD9E3F9) val unselectedColor = Color(0xFFDCDCF0) + LazyVerticalGrid( + state = lazyGridState, columns = GridCells.Fixed(2), // 每行2列 modifier = Modifier .fillMaxWidth(), diff --git a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt index 3ac08a2..c83267e 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt @@ -16,6 +16,10 @@ import java.io.IOException abstract class BaseViewModel( private val repository: RemoteRepository ) : ViewModel() { + + /** + * 带进度条的请求 + */ protected fun launchWithLoading(block: suspend () -> Unit) { viewModelScope.launch { try { @@ -30,6 +34,9 @@ abstract class BaseViewModel( } } + /** + * 不带进度条的请求 + */ protected fun launch(block: suspend () -> Unit) { viewModelScope.launch() { try { diff --git a/app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt index e7f8ffb..19fab4a 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt @@ -21,17 +21,45 @@ class PurchaseOrderViewModel @Inject constructor( private val _supplierList = MutableStateFlow>(emptyList()) val supplierList: StateFlow> = _supplierList + /** + * 是否可以加载更多 + */ + private val _canLoadMore = MutableStateFlow(true) + val canLoadMore: StateFlow = _canLoadMore - fun getOrderList(pageNum: Int = 0, pageSize: Int = 20) { - launchWithLoading { - val response = repository.getReceiveList(pageNum, pageSize) - if (response.isSuccess()) { - response.data?.records?.let { - _supplierList.value = it + /** + * 是否显示进度条 + */ + private val _isMoreLoading = MutableStateFlow(false) + val isMoreLoading = _isMoreLoading + + + /** + * 获取采购订单-供应商列表 + */ + fun getOrderList(pageNum: Int = 1, pageSize: Int = 5) { + if (pageNum != 1) { + launch { + _isMoreLoading.value = true + val response = repository.getReceiveList(pageNum, pageSize) + _isMoreLoading.value = false + if (response.isSuccess()) { + response.data?.records?.let { + _canLoadMore.value = it.size >= pageSize + _supplierList.value = _supplierList.value + it + } + } + } + } else { + launchWithLoading { + val response = repository.getReceiveList(pageNum, pageSize) + if (response.isSuccess()) { + response.data?.records?.let { + _supplierList.value = it + } } } } } - } diff --git a/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt index 3fc7146..eb6b130 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt @@ -77,6 +77,18 @@ class SelfProcurementViewModel @Inject constructor( */ private val _amountUserInput = MutableStateFlow(false) + /** + * 是否可以加载更多 + */ + private val _canLoadMore = MutableStateFlow(true) + val canLoadMore: StateFlow = _canLoadMore + + /** + * 是否显示进度条 + */ + private val _isMoreLoading = MutableStateFlow(false) + val isMoreLoading = _isMoreLoading + /** * 更新全局仓库 */ @@ -237,15 +249,24 @@ class SelfProcurementViewModel @Inject constructor( fun searchGoodsInfoList( goodsName: String, - pageNo: Int = 0, + pageNo: Int = 1, pageSize: Int = 10 ) { launch { + if (pageNo > 1) { + _isMoreLoading.value = true + } val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize) + _isMoreLoading.value = false if (response.isSuccess()) { val data = response.data - if (data != null) { - _searchListItems.value = data.records ?: emptyList() + data?.records?.let { + _canLoadMore.value = data.records.size >= pageSize + if (pageNo > 1) { + _searchListItems.value = _searchListItems.value + data.records + } else { + _searchListItems.value = data.records + } } } else { _searchListItems.value = emptyList()