From 22eeac6c62a2e02062993d8f3f0de1cb26db3662 Mon Sep 17 00:00:00 2001 From: BBIT-Kai <2911862937@qq.com> Date: Wed, 27 May 2026 15:10:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../java/com/bbitcn/f8/pad/base/BaseList.kt | 15 ++-- .../bbitcn/f8/pad/base/BasePagingSource.kt | 15 ++-- .../f8/pad/base/CustomMaterialComponents.kt | 9 +-- .../f8/pad/ui/screen/TopInfoViewModel.kt | 10 ++- .../pad/ui/screen/dialog/AddEditUserDialog.kt | 2 +- .../pad/ui/screen/dialog/AddWeightDialog.kt | 2 +- .../f8/pad/ui/screen/dialog/ChatDialog.kt | 2 +- .../f8/pad/ui/screen/dialog/PriceDialog.kt | 2 +- .../ui/screen/dialog/WaterCutRecordDialog.kt | 2 +- .../dialog/drycocoon/DryCocoonFilterDialog.kt | 4 +- .../dialog/drycocoon/DryCocoonLossDialog.kt | 4 +- .../drycocoon/DryCocoonLossDialogInOut.kt | 2 +- .../pad/ui/screen/mainFunc/PurchaseScreen.kt | 51 +++++++++--- .../ui/screen/mainFunc/StatisticsScreen.kt | 6 +- .../f8/pad/ui/screen/mainFunc/UserScreen.kt | 39 ++++++--- .../pad/ui/screen/secondFunc/AddUserScreen.kt | 4 +- .../pad/ui/screen/secondFunc/WeightScreen.kt | 5 +- .../f8/pad/ui/screen/view/drawer/SetScreen.kt | 4 +- .../screen/view/drawer/TicketForPurchase.kt | 2 +- .../f8/pad/utils/network/RetrofitClient.kt | 81 +++++++++---------- .../f8/pad/utils/network/RetrofitClientAI.kt | 10 ++- .../pad/utils/network/RetrofitClientFace.kt | 10 ++- .../f8/pad/utils/network/RetrofitClientIOT.kt | 10 ++- .../pad/utils/network/TokenAuthenticator.kt | 67 ++++++++------- .../com/bbitcn/f8/pad/utils/pager/MyPager.kt | 2 +- 26 files changed, 204 insertions(+), 157 deletions(-) diff --git a/.gitignore b/.gitignore index 9478034..68d0cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ local.properties .idea/ app/release/ app/debug/ +.kotlin/ diff --git a/app/src/main/java/com/bbitcn/f8/pad/base/BaseList.kt b/app/src/main/java/com/bbitcn/f8/pad/base/BaseList.kt index 8a741dc..e8ff9fc 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/base/BaseList.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/base/BaseList.kt @@ -210,8 +210,7 @@ fun MyTable( TableContent( verticalPadding = verticalPadding, modifier = M - .fillMaxWidth() - .animateItem(), + .fillMaxWidth(), backgroundDeepColor = itIndex % 2 == 0, list = items[itIndex].map { it.toString() }.zip(ratio.map { it.toInt() }), ) @@ -274,9 +273,10 @@ fun MyRefreshTableForCard( verticalArrangement = Arrangement.spacedBy(10.dp) ) { items(count = info.itemCount, key = info.itemKey { key(it) }) { index -> - val lineItem = info[index]!! - Box(modifier = M.animateItem()) { - item(lineItem, index) + info[index]?.let { lineItem -> + Box { + item(lineItem, index) + } } } item { @@ -318,7 +318,6 @@ fun MyAnyTable( val lineItem = info[index] Card( modifier = M - .animateItem() .pointerInput(Unit) { detectTapGestures( onLongPress = { @@ -422,7 +421,6 @@ fun MyTable2( Card( modifier = MD - .animateItem() .pointerInput(Unit) { detectTapGestures( onLongPress = { @@ -549,10 +547,9 @@ fun MyRefreshTable( LazyColumn(state = listState) { items(count = info.itemCount, key = info.itemKey { key(it) }) { index -> // // 每行 - val lineItem = info[index]!! + val lineItem = info[index] ?: return@items Card( modifier = MD - .animateItem() .pointerInput(Unit) { detectTapGestures( onLongPress = { diff --git a/app/src/main/java/com/bbitcn/f8/pad/base/BasePagingSource.kt b/app/src/main/java/com/bbitcn/f8/pad/base/BasePagingSource.kt index 5831376..2f6cb0f 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/base/BasePagingSource.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/base/BasePagingSource.kt @@ -11,19 +11,18 @@ abstract class BasePagingSource( val apiService = RetrofitClient.apiInterface() - abstract suspend fun fetchData(pageInfoJsonStr: String, requestData: R): List // 返回 T 类型的列表 + abstract suspend fun fetchData(pageInfoJsonStr: String, requestData: R): List override suspend fun load(params: LoadParams): LoadResult { return try { val page = params.key ?: 1 val pageSize = params.loadSize val response = fetchData(PageInfo(page, pageSize).toJson(), requestData) - // 计算上一页和下一页的 key val prevKey = if (page > 1) page - 1 else null val nextKey = if (response.size < pageSize) null else page + 1 LoadResult.Page( - data = response, // 直接返回数据列表 + data = response, prevKey = prevKey, nextKey = nextKey ) @@ -32,10 +31,10 @@ abstract class BasePagingSource( } } - /** - * 这里可以根据当前的分页位置来返回刷新key - */ - override fun getRefreshKey(state: PagingState): Int { - return 1 // 默认返回1,具体实现可根据需求调整 + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) + ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) + } } } diff --git a/app/src/main/java/com/bbitcn/f8/pad/base/CustomMaterialComponents.kt b/app/src/main/java/com/bbitcn/f8/pad/base/CustomMaterialComponents.kt index fa53111..7faf1c0 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/base/CustomMaterialComponents.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/base/CustomMaterialComponents.kt @@ -246,10 +246,8 @@ fun MainFuncFrame( .fillMaxSize() .background(Color.White) ) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(R.drawable.bg) - .build(), + Image( + painter = painterResource(R.drawable.bg), contentDescription = "Background", contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize() @@ -568,7 +566,6 @@ fun VerticalTabs( tabs.forEachIndexed() { index, tab -> Row( modifier = M - .animateItem() .clickable { onTabSelected(index) } @@ -930,4 +927,4 @@ fun AssistChipFilter( }, ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/TopInfoViewModel.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/TopInfoViewModel.kt index 21e43bc..f0b631f 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/TopInfoViewModel.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/TopInfoViewModel.kt @@ -37,7 +37,9 @@ class TopInfoViewModel : BaseViewModel() { refreshBatteryInfo() refreshTitle() //循环更新TopBar UI - pollingTask.startPollingTaskOnIOThread(RxTag.BAR_UI, 5) { + val dateFormat = SimpleDateFormat("yyyy/MM/dd \n") + val timeFormat = SimpleDateFormat("HH:mm") + pollingTask.startPollingTaskOnIOThread(RxTag.BAR_UI, 60) { val calendar = Calendar.getInstance() val lunarCalender: LunarCalendar = LunarCalendar.obtainCalendar( calendar[Calendar.YEAR], @@ -45,11 +47,11 @@ class TopInfoViewModel : BaseViewModel() { calendar[Calendar.DAY_OF_MONTH] ) _date.update { - SimpleDateFormat("yyyy/MM/dd \n").format(Date()) + + dateFormat.format(Date()) + (lunarCalender.getLunarMonth() + "月" + lunarCalender.getLunarDay()) } _time.update { - SimpleDateFormat("HH:mm").format(Date()) + timeFormat.format(Date()) } } } @@ -91,4 +93,4 @@ class TopInfoViewModel : BaseViewModel() { } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddEditUserDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddEditUserDialog.kt index 12eba74..949e3bf 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddEditUserDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddEditUserDialog.kt @@ -107,7 +107,7 @@ fun AddEditUserDialog(info: AddEditUserDialogData) { LazyColumn(modifier = M.fillMaxSize()) { item { Row( - modifier = M.fillMaxWidth().animateItem(), + modifier = M.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp) ) { Column(modifier = M.weight(1f)) { diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddWeightDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddWeightDialog.kt index 48ae01d..e0d2f1f 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddWeightDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/AddWeightDialog.kt @@ -99,7 +99,7 @@ fun AddWeightDialog(info: AddWeightDialogData) { LazyColumn { item { Column( - modifier = M.fillMaxSize().animateItem(), + modifier = M.fillMaxSize(), horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(10.dp) ) { diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/ChatDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/ChatDialog.kt index e794731..145e303 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/ChatDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/ChatDialog.kt @@ -203,7 +203,7 @@ fun Messages( ) { items(chatRecords) { Message( - modifier = M.animateItem(), + modifier = M, msg = it, isEndMessageByAuthor = true, isNewMessageByAuthor = true diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/PriceDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/PriceDialog.kt index 5cb50af..13e009c 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/PriceDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/PriceDialog.kt @@ -133,7 +133,7 @@ fun PriceDialog(info: PriceDialogData) { var priceT by rememberSaveable { mutableStateOf(item.price.toString()) } val moneySum = ((priceT.toDoubleOrNull() ?: 0.0) * item.jweightSum) PriceTableContent( - M.animateItem(), + M, index % 2 == 0, item.sgTypeName, item.weightCount.toString(), diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/WaterCutRecordDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/WaterCutRecordDialog.kt index 4676d2a..3a59782 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/WaterCutRecordDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/WaterCutRecordDialog.kt @@ -113,7 +113,7 @@ fun WaterCutRecordDialog(info: WaterCutRecordDialogData) { LazyColumn { items(count = items.size) { it -> TableContent( - modifier = M.fillMaxWidth().animateItem(), + modifier = M.fillMaxWidth(), backgroundDeepColor = it % 2 == 0, list = items[it].map { it.toString() }.zip(ratio.map { it.toInt() }), verticalPadding = 15.dp, diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonFilterDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonFilterDialog.kt index f8d6ebc..fddf31a 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonFilterDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonFilterDialog.kt @@ -94,7 +94,7 @@ fun DryCocoonFilterDialog( } } items(tagIds.toList()) { - PackageLossInfo(M.animateItem(), it) + PackageLossInfo(M, it) } } VerticalDivider(modifier = M.padding(horizontal = 5.dp)) @@ -123,7 +123,7 @@ fun DryCocoonFilterDialog( HorizontalDivider(modifier = M.padding(vertical = 5.dp)) } items(forceItems.toList()) { - PackageLossInfo(M.animateItem(), it) + PackageLossInfo(M, it) } } } diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialog.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialog.kt index f41d612..a5226f4 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialog.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialog.kt @@ -138,7 +138,7 @@ fun DryCocoonLossDialog( } } items(tagIds, key = { it }) { tag -> - PackageLossInfo(M.animateItem(),tag) + PackageLossInfo(M,tag) } } } @@ -171,4 +171,4 @@ fun PackageLossInfo(modifier: Modifier, rfid: String) { } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialogInOut.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialogInOut.kt index 7ac5a45..bcaddd4 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialogInOut.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/dialog/drycocoon/DryCocoonLossDialogInOut.kt @@ -120,7 +120,7 @@ fun DryCocoonLossDialogInOut( } } items(tagIds, key = { it }) { tag -> - PackageLossInfo(M.animateItem(), tag) + PackageLossInfo(M, tag) } } } diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/PurchaseScreen.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/PurchaseScreen.kt index 760ffbf..81c270e 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/PurchaseScreen.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/PurchaseScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.DatePickerDefaults @@ -32,9 +31,11 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.rememberDateRangePickerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -51,6 +52,7 @@ import com.bbitcn.f8.pad.base.MainFuncFrame import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController +import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.bbitcn.f8.pad.R import com.bbitcn.f8.pad.base.DateRangePickTextFiled @@ -75,6 +77,7 @@ import com.bbitcn.f8.pad.ui.screen.view.drawer.DrawerViewModel import com.bbitcn.f8.pad.ui.screen.view.drawer.StateChartsOnclick import com.bbitcn.f8.pad.ui.theme.MyColors import com.bbitcn.f8.pad.utils.TimeUtils +import kotlinx.coroutines.delay import java.util.Date /** @@ -127,6 +130,10 @@ fun PurchaseScreen( ) info.refresh() } + LaunchedEffect(queryInput) { + delay(350) + updateParams() + } MainFuncFrame { MyCard { Column(modifier = M.padding(15.dp)) { @@ -170,8 +177,14 @@ fun PurchaseScreen( Spacer(modifier = M.weight(1f)) } DateRangePickTextFiled(dateRange = queryDateRange) { - purchaseViewModel.showDateRangeSelectDialog{ - updateParams() + purchaseViewModel.showDateRangeSelectDialog { range -> + purchaseViewModel.updateParams( + range.first, + range.second, + queryInput, + queryType + ) + info.refresh() } } QueryTextField( @@ -181,7 +194,6 @@ fun PurchaseScreen( text = queryInput ) { queryInput = it - updateParams() } } if (!isLandscape()) { @@ -216,7 +228,7 @@ fun PurchaseScreen( } } } - InfoList(purchaseViewModel) { + InfoList(purchaseViewModel, info) { drawerViewModel.openPurchaseDetailDrawer( info = it, StateChartsOnclick(pricing = { @@ -311,6 +323,7 @@ fun DateRangePickerModal( @Composable fun InfoList( purchaseViewModel: PurchaseViewModel, + info: LazyPagingItems, onClick: (info: PurchaseDataResponse.Data) -> Unit ) { // 获取当前设备的屏幕配置,比如方向 @@ -322,10 +335,9 @@ fun InfoList( } else { 4 // 横屏显示四列 } - val info = purchaseViewModel.infoPager.collectAsLazyPagingItems() val myPager = purchaseViewModel.infoMyPager val isRefreshing by myPager.listIsRefreshing.collectAsState() - var selectIndex by rememberSaveable { mutableStateOf(-1) } + var selectId by rememberSaveable { mutableStateOf("") } MyRefreshTableForCard( modifier = M .fillMaxWidth() @@ -338,9 +350,9 @@ fun InfoList( }, columns = columns, item = { data, index -> - InfoItem(isSelect = index == selectIndex, data = data) { + InfoItem(isSelect = data.czSysid == selectId, data = data) { onClick(data) - selectIndex = index + selectId = data.czSysid } } ) @@ -352,6 +364,10 @@ fun InfoItem( data: PurchaseDataResponse.Data, onClick: () -> Unit ) { + val visibleItems = remember(data.chengZhongItemSumList) { + data.chengZhongItemSumList.take(2) + } + val hiddenCount = data.chengZhongItemSumList.size - visibleItems.size MyCard( radius = 7.dp, elevation = 0.dp, @@ -401,11 +417,10 @@ fun InfoItem( Pair("净重", 1), Pair("单价", 1) ), isSelect ) - LazyColumn(modifier = M.height(60.dp)) { - items(count = data.chengZhongItemSumList.size) { index -> - val item = data.chengZhongItemSumList[index] + Column(modifier = M.height(60.dp)) { + visibleItems.forEach { item -> TableContent( - M.fillMaxWidth().animateItem(), isSelect, + M.fillMaxWidth(), isSelect, listOf( Pair(item.sgTypeName, 1), Pair(item.mweightSum.toString(), 1), @@ -416,6 +431,16 @@ fun InfoItem( ), 3.5.dp ) } + if (hiddenCount > 0) { + Text( + text = "+$hiddenCount", + modifier = M + .fillMaxWidth() + .padding(top = 2.dp, end = 8.dp), + color = if (isSelect) MyColors.BlueGreen else MyColors.Gray, + fontSize = MaterialTheme.typography.labelSmall.fontSize + ) + } } StateList( data.ispPicing, data.isKouPiing, diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/StatisticsScreen.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/StatisticsScreen.kt index 4cbcaf9..a87996d 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/StatisticsScreen.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/StatisticsScreen.kt @@ -78,7 +78,7 @@ fun StatisticsScreen( .fillMaxSize() .padding(10.dp) ) { - StatisticsLeft(modifier = M.weight(2f), statisticsViewModel, curType) { + StatisticsLeft(modifier = M.weight(2f), statisticsViewModel, curType, pager) { curType = it statisticsViewModel.updateParamsCocoonType(it.sgtypesysid) pager.refresh() @@ -114,11 +114,11 @@ fun StatisticsLeft( modifier: Modifier, statisticsViewModel: StatisticsViewModel, selectedTabIndex: StatisticsResponse.Data, + pager: LazyPagingItems, onFilterSelectedTabChanged: (StatisticsResponse.Data) -> Unit ) { val daysInfo by statisticsViewModel.daysInfo.collectAsState() val statistics by statisticsViewModel.statistics.collectAsState() - val pager = statisticsViewModel.statisticsPager.collectAsLazyPagingItems() Column( modifier = modifier @@ -246,4 +246,4 @@ fun StatisticsMain( } ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/UserScreen.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/UserScreen.kt index c55d3e7..1028991 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/UserScreen.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/mainFunc/UserScreen.kt @@ -47,6 +47,7 @@ import com.bbitcn.f8.pad.base.MainFuncFrame import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController +import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.bbitcn.f8.pad.R import com.bbitcn.f8.pad.base.AssistChipFilter @@ -58,6 +59,7 @@ import com.bbitcn.f8.pad.base.MyRefreshTable import com.bbitcn.f8.pad.base.MyTableData import com.bbitcn.f8.pad.base.QueryTextField import com.bbitcn.f8.pad.base.isLandscape +import com.bbitcn.f8.pad.model.net.response.UserDataResponse import com.bbitcn.f8.pad.ui.theme.MyColors import com.bbitcn.f8.pad.utils.MyUtil import com.bbitcn.f8.pad.utils.TimeUtils @@ -82,11 +84,12 @@ fun UserScreen( userViewModel: UserViewModel = viewModel() ) { val treeData by userViewModel.treeData.collectAsState() + val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems() MainFuncFrame { if (isLandscape()) { - UserScreenInLandscape(navController, userViewModel, treeData) + UserScreenInLandscape(navController, userViewModel, treeData, userData) } else { - UserScreenInPortrait(navController, userViewModel, treeData) + UserScreenInPortrait(navController, userViewModel, treeData, userData) } } } @@ -95,7 +98,8 @@ fun UserScreen( fun UserScreenInPortrait( navController: NavController, userViewModel: UserViewModel, - treeData: List, Any>> + treeData: List, Any>>, + userData: LazyPagingItems ) { Column( modifier = M.fillMaxSize() @@ -105,12 +109,12 @@ fun UserScreenInPortrait( .weight(2f) .padding(bottom = 15.dp) ) { - CollapsibleList(userViewModel, treeData) + CollapsibleList(userViewModel, treeData, userData) } MyInfoCard( modifier = M.weight(8f) ) { - UserManageList(navController, userViewModel) + UserManageList(navController, userViewModel, userData) } } } @@ -119,7 +123,8 @@ fun UserScreenInPortrait( fun UserScreenInLandscape( navController: NavController, userViewModel: UserViewModel, - treeData: List, Any>> + treeData: List, Any>>, + userData: LazyPagingItems ) { var leftWeight by rememberSaveable { mutableStateOf(2.5f) } var rightWeight by rememberSaveable { mutableStateOf(7.5f) } @@ -131,7 +136,7 @@ fun UserScreenInLandscape( .weight(leftWeight) // 使用动态权重 .fillMaxHeight() ) { - CollapsibleList(userViewModel, treeData) + CollapsibleList(userViewModel, treeData, userData) } Box( modifier = M @@ -160,15 +165,18 @@ fun UserScreenInLandscape( .weight(rightWeight) // 使用动态权重 .fillMaxHeight() ) { - UserManageList(navController, userViewModel) + UserManageList(navController, userViewModel, userData) } } } @Composable -fun CollapsibleList(userViewModel: UserViewModel, listData: List, Any>>) { +fun CollapsibleList( + userViewModel: UserViewModel, + listData: List, Any>>, + pager: LazyPagingItems +) { val queryInput by userViewModel.areaLike.collectAsState() - val pager = userViewModel.usersInfoPager.collectAsLazyPagingItems() Column { Row( modifier = M @@ -194,6 +202,7 @@ fun CollapsibleList(userViewModel: UserViewModel, listData: List CollapsibleItem( userViewModel, + pager, item = item, currentLevel = 1, needExpand = queryInput != "" @@ -206,6 +215,7 @@ fun CollapsibleList(userViewModel: UserViewModel, listData: List, item: Pair, Any>, currentLevel: Int, xian: String = "", @@ -225,7 +235,6 @@ fun CollapsibleItem( expandedIndex = if (needExpand) 0 else -1 } } - val pager = userViewModel.usersInfoPager.collectAsLazyPagingItems() // 被选中的项 val xianTmp = if (currentLevel == 1) titleAndCount.first else xian val xiangTmp = if (currentLevel == 2) titleAndCount.first else xiang @@ -327,6 +336,7 @@ fun CollapsibleItem( val subPair = subItem.key to subItem.value CollapsibleItem( userViewModel, + pager, subPair as Pair, Any>, currentLevel + 1, xianTmp, @@ -407,10 +417,13 @@ fun CollapsibleItem( } @Composable -fun UserManageList(navController: NavController, userViewModel: UserViewModel) { +fun UserManageList( + navController: NavController, + userViewModel: UserViewModel, + userData: LazyPagingItems +) { var queryInput by rememberSaveable { mutableStateOf("") } val areaFilter by userViewModel.areaFilter.collectAsState() - val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems() val onFilterLikeChanged: (String) -> Unit = { queryInput = it userViewModel.updateParamsLike(it) diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/AddUserScreen.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/AddUserScreen.kt index 7ce376b..db0bd7f 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/AddUserScreen.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/AddUserScreen.kt @@ -176,7 +176,7 @@ fun AddUserBaseInfo( LazyColumn(modifier = M.padding(10.dp)) { item { BigButton( - modifier = M.padding(top = 15.dp).animateItem(), + modifier = M.padding(top = 15.dp), text = "保存", isLight = true ) { @@ -213,7 +213,7 @@ fun AddUSerExtendInfo(addUserViewModel: AddUserViewModel) { val userExtendList by addUserViewModel.userExtendList.collectAsState() LazyColumn { item { - Column(modifier = M.padding(top = 5.dp).animateItem()) { + Column(modifier = M.padding(top = 5.dp)) { Row( horizontalArrangement = Arrangement.spacedBy(5.dp), verticalAlignment = Alignment.CenterVertically diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/WeightScreen.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/WeightScreen.kt index 20f58d4..8062eb7 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/WeightScreen.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/secondFunc/WeightScreen.kt @@ -260,8 +260,7 @@ fun WeightList(weightViewModel: WeightViewModel) { val item = detailList[it] TableContent( modifier = M - .fillMaxWidth() - .animateItem(), + .fillMaxWidth(), backgroundDeepColor = it % 2 == 0, list = listOf( item.weighingTimes.toString() to 1, @@ -457,7 +456,7 @@ fun WeightRight(navController: NavController, weightViewModel: WeightViewModel) verticalArrangement = Arrangement.spacedBy(10.dp) ) { items(kindsInfo) { - AddKindsButton(M.animateItem(), curWeight, weightViewModel, it, errMsg) + AddKindsButton(M, curWeight, weightViewModel, it, errMsg) } } Row( diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/SetScreen.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/SetScreen.kt index 18e3321..a063b3b 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/SetScreen.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/SetScreen.kt @@ -389,7 +389,6 @@ fun ReaderSerialPort( items(labelList) { tag -> Text( modifier = M - .animateItem() .fillMaxWidth() .padding(10.dp), text = tag, @@ -565,7 +564,6 @@ fun ConnectBluetooth(bluetooth: MyBlueTooth) { println("bluetooth.device.value?.address: ${bluetooth.device.value?.address}") Row( modifier = M - .animateItem() .fillMaxWidth() .clickable { bluetooth.connect(device.address) }, ) { @@ -651,4 +649,4 @@ fun AutoConnectOnStartUp(device: DeviceController) { } }) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/TicketForPurchase.kt b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/TicketForPurchase.kt index e4a2405..5385969 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/TicketForPurchase.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/ui/screen/view/drawer/TicketForPurchase.kt @@ -141,7 +141,7 @@ fun TicketForPurchase( ) LazyColumn(modifier = M.weight(1f)) { items(info.chengZhongItemSumList) { - SilkKindCard(M.animateItem(),it) + SilkKindCard(M,it) } } Column { diff --git a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClient.kt b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClient.kt index 180b7cd..7c093ff 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClient.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClient.kt @@ -1,11 +1,8 @@ package com.bbitcn.f8.pad.utils.network -import com.bbitcn.f8.pad.R import com.bbitcn.f8.pad.model.net.response.CommonResponse import com.bbitcn.f8.pad.ui.screen.view.Toasty -import com.bbitcn.f8.pad.utils.AudioPlayer import com.bbitcn.f8.pad.utils.MMKVUtil -import com.bbitcn.f8.pad.utils.TTSManager import com.bbitcn.f8.pad.utils.global.RxTag import com.bbitcn.f8.pad.utils.log.MyLog import com.google.gson.Gson @@ -14,7 +11,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import okhttp3.Interceptor -import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Protocol @@ -23,7 +19,6 @@ import okhttp3.ResponseBody import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -import java.io.IOException import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException @@ -34,50 +29,43 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) object RetrofitClient { - const val BASE_URL = "http://f8.api.app.bbitcn.cn/" // 生产环境 -// const val BASE_URL = "http://172.20.50.11:5291/" //何 工厂 -// const val BASE_URL = "http://10.0.4.30:5291/" //何 -// const val BASE_URL = "http://10.0.4.50:5291/" //罗 -// const val BASE_URL = "http://10.0.4.68:5291/" //孔 + const val BASE_URL = "http://f8.api.app.bbitcn.cn/" + private const val ERROR_BODY_PEEK_BYTES = 64 * 1024L + private const val ENABLE_BODY_LOG = false val loggingInterceptor = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY + level = if (ENABLE_BODY_LOG) { + HttpLoggingInterceptor.Level.BODY + } else { + HttpLoggingInterceptor.Level.NONE + } } + val errorInterceptor = Interceptor { chain -> - // 获取原始的响应 try { val response = chain.proceed(chain.request()) - // 先读取响应体的内容,但不消耗流 - val responseBody = response.body - val content = responseBody?.string() // 这里消耗了响应体,但我们要重新创建响应体 - - // 将响应体内容保存到新的 ResponseBody - val newResponseBody = ResponseBody.create(responseBody?.contentType(), content ?: "") - - // 重新构造响应,避免影响后续的流操作 - val newResponse = response.newBuilder() - .body(newResponseBody) // 替换成新的响应体 - .build() - - // 如果响应码不是 200 或 401,显示错误消息 - if (newResponse.code !in listOf(200, 401)) { - // todo 如果断网 这里会出现 com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ - // 暂定是服务器的断点导致的 - val commonResponse = Gson().fromJson(content, CommonResponse::class.java) - Toasty.showTipsDialog("网络请求错误:${commonResponse.msg}") - if (commonResponse.code == 401 && MMKVUtil.get(RxTag.REFRESH_TOKEN).isEmpty()) { - Toasty.loginExpired() + if (response.code !in listOf(200, 401)) { + val content = response.peekBody(ERROR_BODY_PEEK_BYTES).string() + runCatching { + Gson().fromJson(content, CommonResponse::class.java) + }.onSuccess { commonResponse -> + Toasty.showTipsDialog("网络请求错误:${commonResponse.msg}") + if (commonResponse.code == 401 && MMKVUtil.get(RxTag.REFRESH_TOKEN).isEmpty()) { + Toasty.loginExpired() + } + }.onFailure { + Toasty.showTipsDialog(response.message) } } - // 返回新的响应 - return@Interceptor newResponse + + return@Interceptor response } catch (e: Exception) { e.printStackTrace() MyLog.networkError("请求网络时发生异常:${e.message}") MyLog.networkError("错误类型:${e.javaClass.simpleName},错误信息:${e.message}") val responseBody = - ResponseBody.create("application/json".toMediaTypeOrNull(), "{}") // 空的 JSON + ResponseBody.create("application/json".toMediaTypeOrNull(), "{}") val fakeResponse: Response = if (e is UnknownHostException || e is SocketTimeoutException || e is ConnectException) { Response.Builder() @@ -100,13 +88,18 @@ object RetrofitClient { } } + private val retrofit: Retrofit by lazy { createRetrofit() } + private val apiService: ApiService by lazy { retrofit.create(ApiService::class.java) } + @Provides @Singleton - fun myRetrofit(): Retrofit { + fun myRetrofit(): Retrofit = retrofit + + private fun createRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) - .client(// 设置 OkHttpClient + .client( OkHttpClient.Builder() .addInterceptor { chain -> val request = chain.request().newBuilder() @@ -114,10 +107,10 @@ object RetrofitClient { .build() chain.proceed(request) } - .connectTimeout(20, TimeUnit.SECONDS) // 设置连接超时时间 - .readTimeout(20, TimeUnit.SECONDS) // 设置读取超时时间 - .writeTimeout(20, TimeUnit.SECONDS) // 设置写入超时时间 - .authenticator(TokenAuthenticator()) // 添加401拦截器 + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) + .authenticator(TokenAuthenticator()) .addInterceptor(loggingInterceptor) .addInterceptor(errorInterceptor) .build() @@ -127,7 +120,5 @@ object RetrofitClient { @Provides @Singleton - fun apiInterface(): ApiService = - RetrofitClient_MyRetrofitFactory.myRetrofit().create(ApiService::class.java) - -} \ No newline at end of file + fun apiInterface(): ApiService = apiService +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientAI.kt b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientAI.kt index 0d77032..bea0c82 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientAI.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientAI.kt @@ -16,10 +16,14 @@ import javax.inject.Singleton object RetrofitClientAI { const val AI_BASE_URL = "http://171.212.101.199:13010/" + private val retrofit: Retrofit by lazy { createRetrofit() } + private val apiService: AIApiService by lazy { retrofit.create(AIApiService::class.java) } @Provides @Singleton - fun myAIRetrofit(): Retrofit { + fun myAIRetrofit(): Retrofit = retrofit + + private fun createRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl(AI_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) @@ -41,6 +45,6 @@ object RetrofitClientAI { @Provides @Singleton - fun aiApiInterface(): AIApiService = myAIRetrofit().create(AIApiService::class.java) + fun aiApiInterface(): AIApiService = apiService -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientFace.kt b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientFace.kt index 3fa1f9a..756fb79 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientFace.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientFace.kt @@ -16,10 +16,14 @@ import javax.inject.Singleton object RetrofitClientFace { const val Face_BASE_URL = "https://aip.baidubce.com/" + private val retrofit: Retrofit by lazy { createRetrofit() } + private val apiService: AIApiService by lazy { retrofit.create(AIApiService::class.java) } @Provides @Singleton - fun myAIRetrofit(): Retrofit { + fun myAIRetrofit(): Retrofit = retrofit + + private fun createRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl(Face_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) @@ -40,6 +44,6 @@ object RetrofitClientFace { @Provides @Singleton - fun faceApiInterface(): AIApiService = myAIRetrofit().create(AIApiService::class.java) + fun faceApiInterface(): AIApiService = apiService -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientIOT.kt b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientIOT.kt index d682fbb..fd1b990 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientIOT.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/utils/network/RetrofitClientIOT.kt @@ -16,10 +16,14 @@ import javax.inject.Singleton object RetrofitClientIOT { const val AI_BASE_URL = "https://iot.api.bbitcn.net/" + private val retrofit: Retrofit by lazy { createRetrofit() } + private val apiService: ApiServiceIOT by lazy { retrofit.create(ApiServiceIOT::class.java) } @Provides @Singleton - fun myAIRetrofit(): Retrofit { + fun myAIRetrofit(): Retrofit = retrofit + + private fun createRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl(AI_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) @@ -41,6 +45,6 @@ object RetrofitClientIOT { @Provides @Singleton - fun apiInterface(): ApiServiceIOT = myAIRetrofit().create(ApiServiceIOT::class.java) + fun apiInterface(): ApiServiceIOT = apiService -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bbitcn/f8/pad/utils/network/TokenAuthenticator.kt b/app/src/main/java/com/bbitcn/f8/pad/utils/network/TokenAuthenticator.kt index eda10e3..e5e3775 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/utils/network/TokenAuthenticator.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/utils/network/TokenAuthenticator.kt @@ -13,59 +13,72 @@ import okhttp3.Response import okhttp3.Route /** - * 401 拦截器 + * 401 token refresh interceptor. */ -class TokenAuthenticator() : Authenticator { +class TokenAuthenticator : Authenticator { - private val lock = Any() // 用于同步刷新 token 的操作 - private var isRefreshing = false // 标志:是否正在刷新 token + private val lock = Any() override fun authenticate(route: Route?, response: Response): Request? { MyLog.network("TokenAuthenticator: authenticate") - if (response.code == 401) { - MyLog.network("401TokenAuthenticator: authenticate") - if (MMKVUtil.get(RxTag.REFRESH_TOKEN).isEmpty()) { + if (response.code != 401 || responseCount(response) >= 2) { + return null + } + + if (response.request.url.encodedPath.contains("RefreshToken", ignoreCase = true)) { + Toasty.loginExpired() + return null + } + + synchronized(lock) { + val refreshToken = MMKVUtil.get(RxTag.REFRESH_TOKEN, "") + if (refreshToken.isEmpty()) { Toasty.loginExpired() + return null } - synchronized(lock) { - // 如果正在刷新 token,直接返回 null,等待刷新完成 - if (isRefreshing) - return null - isRefreshing = true + + val currentToken = MMKVUtil.get(RxTag.ACCESS_TOKEN, "") + val requestToken = response.request.header("Authorization")?.removePrefix("Bearer ") + if (currentToken.isNotEmpty() && requestToken != currentToken) { + return response.request.withToken(currentToken) } val newToken = runBlocking { - val accessToken = MMKVUtil.get(RxTag.ACCESS_TOKEN, "") - val refreshToken = MMKVUtil.get(RxTag.REFRESH_TOKEN, "") - MyLog.network("刷新Token: $accessToken, $refreshToken") + MyLog.network("刷新Token: $currentToken, $refreshToken") val result = RetrofitClient.apiInterface().refreshToken( RefreshTokenRequest( hardwareid = Global.getDeviceId(), refToken = refreshToken, - token = accessToken + token = currentToken ) ) if (result.code == 1) { MMKVUtil.put(RxTag.ACCESS_TOKEN, result.data.accessToken) result.data.accessToken } else { - // 刷新 token 失败,跳转到登录页面 Toasty.loginExpired() MyLog.networkError("刷新Token失败,跳转到登录页面: ${result.msg}") null } } - synchronized(lock) { - isRefreshing = false // 刷新完成,允许其他请求进行刷新 - } - - return newToken?.let { - response.request.newBuilder() - .header("Authorization", "Bearer $it") - .build() - } + return newToken?.let { response.request.withToken(it) } } - return null + } + + private fun Request.withToken(token: String): Request { + return newBuilder() + .header("Authorization", "Bearer $token") + .build() + } + + private fun responseCount(response: Response): Int { + var count = 1 + var prior = response.priorResponse + while (prior != null) { + count++ + prior = prior.priorResponse + } + return count } } diff --git a/app/src/main/java/com/bbitcn/f8/pad/utils/pager/MyPager.kt b/app/src/main/java/com/bbitcn/f8/pad/utils/pager/MyPager.kt index 97821f6..6000ce1 100644 --- a/app/src/main/java/com/bbitcn/f8/pad/utils/pager/MyPager.kt +++ b/app/src/main/java/com/bbitcn/f8/pad/utils/pager/MyPager.kt @@ -19,7 +19,7 @@ class MyPager( initialRequestData: R, // 初始的请求数据 private val initialLoadSize: Int = 12, private val pageSize: Int = 12, - private val enablePlaceholders: Boolean = true + private val enablePlaceholders: Boolean = false ) { // 内部的 MutableStateFlow,用来保存请求数据