添加了供应商列表和搜索物品分页功能

This commit is contained in:
zxj
2025-07-11 15:53:17 +08:00
parent 7459bd205a
commit f1283ba11e
8 changed files with 182 additions and 19 deletions
@@ -3,6 +3,7 @@ package com.sw.inbound.ext
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.round
fun Float.toFormattedString(decimalPlaces: Int = 2): String { fun Float.toFormattedString(decimalPlaces: Int = 2): String {
return when (this) { return when (this) {
@@ -32,12 +33,12 @@ fun Double.toSafeBigDecimal(): BigDecimal {
*/ */
private fun Double.roundToDouble(decimalPlaces: Int): Double { private fun Double.roundToDouble(decimalPlaces: Int): Double {
val factor = 10.0.pow(decimalPlaces) 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 { private fun Float.roundToDouble(decimalPlaces: Int): Double {
val factor = 10.0.pow(decimalPlaces) val factor = 10.0.pow(decimalPlaces)
return kotlin.math.round(this * factor) / factor return round(this * factor) / factor
} }
fun Float?.toSafeString(unitName: String?): String { fun Float?.toSafeString(unitName: String?): String {
@@ -16,15 +16,21 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.BottomActionBar
import com.sw.inbound.ui.weight.TopTitleBar import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.viewmodel.PurchaseOrderViewModel import com.sw.inbound.viewmodel.PurchaseOrderViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import timber.log.Timber
@Preview( @Preview(
widthDp = 1920, widthDp = 1920,
@@ -66,11 +75,32 @@ fun PurchaseOrderScreen(
viewModel: PurchaseOrderViewModel = hiltViewModel<PurchaseOrderViewModel>() viewModel: PurchaseOrderViewModel = hiltViewModel<PurchaseOrderViewModel>()
) { ) {
val products by viewModel.supplierList.collectAsState() 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) { LaunchedEffect(Unit) {
viewModel.getOrderList() 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 { Column {
TopTitleBar() TopTitleBar()
Box( Box(
@@ -101,6 +131,7 @@ fun PurchaseOrderScreen(
LazyRow( LazyRow(
modifier = modifier modifier = modifier
.padding(10.dp), .padding(10.dp),
state = lazyListState,
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(30.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()
}
}
}
} }
} }
} }
@@ -26,6 +26,10 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.res.painterResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@@ -238,6 +243,11 @@ fun ContentRightView(
@Composable @Composable
fun ProductIdentification(viewModel: SelfProcurementViewModel) { fun ProductIdentification(viewModel: SelfProcurementViewModel) {
val searchResultList by viewModel.searchListItems.collectAsState() 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( Column(
modifier = Modifier modifier = Modifier
@@ -252,10 +262,18 @@ fun ProductIdentification(viewModel: SelfProcurementViewModel) {
) { ) {
// 菜品识别 // 菜品识别
IdentityView(list = searchResultList, showSearchView = true, onSearchClick = { IdentityView(list = searchResultList, showSearchView = true, onSearchClick = {
searchInfo = it
viewModel.searchGoodsInfoList(it) viewModel.searchGoodsInfoList(it)
}, onOptionSelected = { }, onOptionSelected = {
viewModel.updateSelectedItemWithSearch(it) 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)) Spacer(modifier = Modifier.height(10.dp))
Row( Row(
@@ -615,12 +633,16 @@ private fun SelfProductEditView(
) { ) {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Text( Text(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
text = selectedItem!!.goodsNameStr, text = selectedItem!!.goodsNameStr,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = AppTypography.black141428TextStyle.bold().withSize(30.sp) style = AppTypography.black141428TextStyle.bold().withSize(30.sp)
) )
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(10.dp))
RowInputLayout( RowInputLayout(
label = "仓库", label = "仓库",
@@ -757,7 +779,7 @@ private fun SelfProductEditView(
) )
} }
Spacer(modifier = Modifier.height(40.dp)) Spacer(modifier = Modifier.height(30.dp))
Row( Row(
horizontalArrangement = Arrangement.spacedBy(30.dp), horizontalArrangement = Arrangement.spacedBy(30.dp),
modifier = Modifier modifier = Modifier
@@ -27,6 +27,9 @@ fun IdentityView(
showSearchView: Boolean = false, showSearchView: Boolean = false,
onSearchClick: (String) -> Unit = {}, onSearchClick: (String) -> Unit = {},
onOptionSelected: (SearchGoodsInfo.Record) -> Unit = {}, onOptionSelected: (SearchGoodsInfo.Record) -> Unit = {},
onLoadMore: (Int) -> Unit = {},
isMoreLoading: Boolean = false,
canLoadMore: Boolean = true
) { ) {
val context = LocalContext.current val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
@@ -59,7 +62,10 @@ fun IdentityView(
modifier = Modifier modifier = Modifier
.height(64.dp) .height(64.dp)
.width(192.dp), options = list, .width(192.dp), options = list,
onOptionSelected = onOptionSelected onOptionSelected = onOptionSelected,
onLoadMore = onLoadMore,
isMoreLoading = isMoreLoading,
canLoadMore = canLoadMore
) )
} else { } else {
SingleSelectButtonGroup( SingleSelectButtonGroup(
@@ -11,13 +11,17 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -31,6 +35,9 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import timber.log.Timber
@Preview(showBackground = true) @Preview(showBackground = true)
@@ -62,16 +69,43 @@ fun SingleSelectButtonGroup(
onOptionSelected: (SearchGoodsInfo.Record) -> Unit, onOptionSelected: (SearchGoodsInfo.Record) -> Unit,
horizontalSpacing: Dp = 13.dp, horizontalSpacing: Dp = 13.dp,
verticalSpacing: 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 val context = LocalContext.current
var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") } 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 selectedColor = Color(0xFFD9E3F9)
val unselectedColor = Color(0xFFDCDCF0) val unselectedColor = Color(0xFFDCDCF0)
LazyVerticalGrid( LazyVerticalGrid(
state = lazyGridState,
columns = GridCells.Fixed(2), // 每行2列 columns = GridCells.Fixed(2), // 每行2列
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
@@ -16,6 +16,10 @@ import java.io.IOException
abstract class BaseViewModel( abstract class BaseViewModel(
private val repository: RemoteRepository private val repository: RemoteRepository
) : ViewModel() { ) : ViewModel() {
/**
* 带进度条的请求
*/
protected fun launchWithLoading(block: suspend () -> Unit) { protected fun launchWithLoading(block: suspend () -> Unit) {
viewModelScope.launch { viewModelScope.launch {
try { try {
@@ -30,6 +34,9 @@ abstract class BaseViewModel(
} }
} }
/**
* 不带进度条的请求
*/
protected fun launch(block: suspend () -> Unit) { protected fun launch(block: suspend () -> Unit) {
viewModelScope.launch() { viewModelScope.launch() {
try { try {
@@ -21,8 +21,36 @@ class PurchaseOrderViewModel @Inject constructor(
private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList()) private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList())
val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList
/**
* 是否可以加载更多
*/
private val _canLoadMore = MutableStateFlow(true)
val canLoadMore: StateFlow<Boolean> = _canLoadMore
fun getOrderList(pageNum: Int = 0, pageSize: Int = 20) { /**
* 是否显示进度条
*/
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 { launchWithLoading {
val response = repository.getReceiveList(pageNum, pageSize) val response = repository.getReceiveList(pageNum, pageSize)
if (response.isSuccess()) { if (response.isSuccess()) {
@@ -32,6 +60,6 @@ class PurchaseOrderViewModel @Inject constructor(
} }
} }
} }
}
} }
@@ -77,6 +77,18 @@ class SelfProcurementViewModel @Inject constructor(
*/ */
private val _amountUserInput = MutableStateFlow<Boolean>(false) private val _amountUserInput = MutableStateFlow<Boolean>(false)
/**
* 是否可以加载更多
*/
private val _canLoadMore = MutableStateFlow(true)
val canLoadMore: StateFlow<Boolean> = _canLoadMore
/**
* 是否显示进度条
*/
private val _isMoreLoading = MutableStateFlow(false)
val isMoreLoading = _isMoreLoading
/** /**
* 更新全局仓库 * 更新全局仓库
*/ */
@@ -237,15 +249,24 @@ class SelfProcurementViewModel @Inject constructor(
fun searchGoodsInfoList( fun searchGoodsInfoList(
goodsName: String, goodsName: String,
pageNo: Int = 0, pageNo: Int = 1,
pageSize: Int = 10 pageSize: Int = 10
) { ) {
launch { launch {
if (pageNo > 1) {
_isMoreLoading.value = true
}
val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize) val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize)
_isMoreLoading.value = false
if (response.isSuccess()) { if (response.isSuccess()) {
val data = response.data val data = response.data
if (data != null) { data?.records?.let {
_searchListItems.value = data.records ?: emptyList<SearchGoodsInfo.Record>() _canLoadMore.value = data.records.size >= pageSize
if (pageNo > 1) {
_searchListItems.value = _searchListItems.value + data.records
} else {
_searchListItems.value = data.records
}
} }
} else { } else {
_searchListItems.value = emptyList<SearchGoodsInfo.Record>() _searchListItems.value = emptyList<SearchGoodsInfo.Record>()