优化性能问题;完成需求;重构弹窗
This commit is contained in:
@@ -21,6 +21,10 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -34,6 +38,8 @@ import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.bbitcn.f8.pad.M
|
||||
import com.bbitcn.f8.pad.MyApp
|
||||
import com.bbitcn.f8.pad.ui.screen.view.GlobalDialogData
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
|
||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||
@@ -68,7 +74,44 @@ fun MyDialog(
|
||||
onClickOK: () -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MyAnimatedVisibility(showDialog) {
|
||||
val ownerKey = remember { Any() }
|
||||
val latestOnDismissRequest = rememberUpdatedState(onDismissRequest)
|
||||
val latestOnClickOK = rememberUpdatedState(onClickOK)
|
||||
val latestContent = rememberUpdatedState(content)
|
||||
|
||||
LaunchedEffect(showDialog, title, clickOKStr) {
|
||||
if (showDialog) {
|
||||
Toasty.showGlobalDialog(
|
||||
GlobalDialogData(
|
||||
ownerKey = ownerKey,
|
||||
title = title,
|
||||
onDismissRequest = {
|
||||
latestOnDismissRequest.value()
|
||||
},
|
||||
clickOKStr = clickOKStr,
|
||||
onClickOK = {
|
||||
latestOnClickOK.value()
|
||||
},
|
||||
content = {
|
||||
latestContent.value()
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Toasty.hideGlobalDialog(ownerKey)
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(ownerKey) {
|
||||
onDispose {
|
||||
Toasty.hideGlobalDialog(ownerKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GlobalDialogHost(data: GlobalDialogData) {
|
||||
MyAnimatedVisibility(data.showDialog) {
|
||||
Box(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
@@ -78,8 +121,8 @@ fun MyDialog(
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
BaseDialogFrame(title, {
|
||||
content()
|
||||
BaseDialogFrame(data.title, {
|
||||
data.content()
|
||||
}) {
|
||||
Row(modifier = M.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
BigButton(
|
||||
@@ -87,13 +130,13 @@ fun MyDialog(
|
||||
.weight(1f)
|
||||
.widthIn(max = 100.dp)
|
||||
) {
|
||||
onDismissRequest()
|
||||
data.onDismissRequest()
|
||||
}
|
||||
// 判断是否有点击事件
|
||||
if (clickOKStr != "") {
|
||||
if (data.clickOKStr != "") {
|
||||
Spacer(modifier = M.width(30.dp))
|
||||
BigButton(clickOKStr, modifier = M.weight(1f), true) {
|
||||
onClickOK()
|
||||
BigButton(data.clickOKStr, modifier = M.weight(1f), true) {
|
||||
data.onClickOK()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +153,7 @@ fun BaseDialogFrame(
|
||||
) {
|
||||
MyCard(
|
||||
modifier = M
|
||||
.padding(top = 10.dp, bottom = 50.dp, start = 100.dp, end = 100.dp)
|
||||
.padding(vertical = 40.dp, horizontal = 100.dp)
|
||||
.noVisualFeedbackClickable { }
|
||||
.fillMaxSize()
|
||||
) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.SocketException
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import retrofit2.HttpException
|
||||
|
||||
open class BaseViewModel : ViewModel() {
|
||||
|
||||
@@ -78,6 +79,11 @@ open class BaseViewModel : ViewModel() {
|
||||
MyLog.test("协程被取消:${exception.javaClass.simpleName},message=${exception.message}")
|
||||
return@onFailure
|
||||
}
|
||||
if (exception is HttpException && exception.code() == 401) {
|
||||
Toasty.loginExpired()
|
||||
Toasty.error("登录已过期")
|
||||
return@onFailure
|
||||
}
|
||||
// 其他异常继续处理
|
||||
exception.printStackTrace()
|
||||
onError(exception)
|
||||
@@ -208,6 +214,7 @@ open class BaseViewModel : ViewModel() {
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
pollingTask.stopAllTasks()
|
||||
taskMap.values.forEach { it.cancel() }
|
||||
taskMap.clear()
|
||||
}
|
||||
|
||||
@@ -172,13 +172,23 @@ fun MyButton(
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
onClick = {
|
||||
if (enabled) {
|
||||
onClick()
|
||||
} else {
|
||||
Toasty.showToast("正在开发中,敬请期待")
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
enabled = true,
|
||||
interactionSource = interactionSource,
|
||||
shape = shape,
|
||||
border = border,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colors),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = if (enabled) colors else MyColors.Disabled,
|
||||
disabledContainerColor = MyColors.Disabled,
|
||||
disabledContentColor = MyColors.Gray
|
||||
),
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
Text(
|
||||
|
||||
@@ -42,6 +42,7 @@ import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.bbitcn.f8.pad.M
|
||||
import com.bbitcn.f8.pad.base.GlobalDialogHost
|
||||
import com.bbitcn.f8.pad.ui.screen.TopInfoViewModel
|
||||
import com.bbitcn.f8.pad.ui.screen.LoginScreen
|
||||
import com.bbitcn.f8.pad.ui.screen.MainScreen
|
||||
@@ -273,6 +274,7 @@ class MainActivity : ComponentActivity() {
|
||||
val loadingDialog by Toasty.loadingDialog.collectAsState()
|
||||
val confirmDialog by Toasty.confirmDialog.collectAsState()
|
||||
val inputDialog by Toasty.inputDialog.collectAsState()
|
||||
val globalDialog by Toasty.globalDialog.collectAsState()
|
||||
TipsDialog(
|
||||
showDialog = tipsDialog.showDialog,
|
||||
onDismiss = { Toasty.hideTipsDialog() },
|
||||
@@ -281,6 +283,7 @@ class MainActivity : ComponentActivity() {
|
||||
LoadingDialog(loadingDialog)
|
||||
ConfirmDialog(confirmDialog)
|
||||
InputDialog(inputDialog)
|
||||
GlobalDialogHost(globalDialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.bbitcn.f8.pad.base.isBluetoothEnabled
|
||||
import com.bbitcn.f8.pad.receiver.SystemInfoReceiver
|
||||
import com.bbitcn.f8.pad.ui.screen.view.drawer.DrawerViewModel
|
||||
import com.bbitcn.f8.pad.ui.theme.MyColors
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.bbitcn.f8.pad.utils.externalModules.devices.scale.ScaleBT
|
||||
import com.bbitcn.f8.pad.utils.externalModules.devices.printer.PrinterBT
|
||||
import com.bbitcn.f8.pad.utils.externalModules.devices.printer.JTPrinterUSB
|
||||
@@ -88,6 +89,7 @@ fun MyTopBar(
|
||||
val date by topInfoViewModel.date.collectAsState()
|
||||
val time by topInfoViewModel.time.collectAsState()
|
||||
val logoState by topInfoViewModel.logoState.collectAsState()
|
||||
val versionName = remember { MyUtil.getVersionName() }
|
||||
|
||||
DisposableEffect(context) {
|
||||
systemInfoReceiver.register()
|
||||
@@ -151,13 +153,21 @@ fun MyTopBar(
|
||||
)
|
||||
}, label = "animated content"
|
||||
) { value ->
|
||||
Text(
|
||||
text = value.first.second,
|
||||
M.padding(horizontal = 8.dp),
|
||||
color = MyColors.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize
|
||||
)
|
||||
Row(verticalAlignment = Alignment.Bottom) {
|
||||
Text(
|
||||
text = value.first.second,
|
||||
M.padding(horizontal = 8.dp),
|
||||
color = MyColors.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize
|
||||
)
|
||||
Text(
|
||||
text = "v$versionName",
|
||||
M.padding(bottom = 2.dp),
|
||||
color = MyColors.LightGray,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Spacer(modifier = M.weight(1f))
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.bbitcn.f8.pad.ui.screen.dialog
|
||||
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -28,6 +30,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.bbitcn.f8.pad.M
|
||||
@@ -46,6 +49,8 @@ data class MessageDialogData(
|
||||
val time: String = "",
|
||||
val username: String = "",
|
||||
val content: String = "",
|
||||
val title: String = "",
|
||||
val isWebUrl: Boolean = false,
|
||||
)
|
||||
|
||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||
@@ -63,7 +68,7 @@ fun MessagePreview() {
|
||||
|
||||
@Composable
|
||||
fun MessageDialog(info: MessageDialogData) {
|
||||
MyDialog("查看消息",
|
||||
MyDialog(info.title.ifEmpty { "查看消息" },
|
||||
info.showDialog,
|
||||
onDismissRequest = { info.onDismiss() }
|
||||
) {
|
||||
@@ -98,11 +103,29 @@ fun MessageDialog(info: MessageDialogData) {
|
||||
.padding(30.dp)
|
||||
.verticalScroll(rememberScrollState()) // 添加滚动支持
|
||||
) {
|
||||
Text(
|
||||
text = info.content,
|
||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
if (info.isWebUrl) {
|
||||
AndroidView(
|
||||
modifier = M.fillMaxSize(),
|
||||
factory = { context ->
|
||||
WebView(context).apply {
|
||||
webViewClient = WebViewClient()
|
||||
settings.javaScriptEnabled = true
|
||||
loadUrl(info.content)
|
||||
}
|
||||
},
|
||||
update = { webView ->
|
||||
if (webView.url != info.content) {
|
||||
webView.loadUrl(info.content)
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = info.content,
|
||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.bbitcn.f8.pad.base.VipBadge
|
||||
import com.bbitcn.f8.pad.model.net.request.CocoonTypeTranslateRequest
|
||||
import com.bbitcn.f8.pad.model.net.response.PurchaseDataResponse
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.bbitcn.f8.pad.ui.theme.MyColors
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import kotlin.collections.map
|
||||
|
||||
@@ -157,13 +158,12 @@ fun TicketMoreDialog(
|
||||
}
|
||||
}
|
||||
item {
|
||||
MyImageButton("茧别转换", R.drawable.ic_ticket_convert) {
|
||||
MyImageButton("茧别转换", R.drawable.ic_ticket_convert, enabled = false) {
|
||||
isTranslated = !isTranslated
|
||||
}
|
||||
}
|
||||
item {
|
||||
MyImageButton("茧票过户", R.drawable.ic_ticket_transfer, true) {
|
||||
|
||||
MyImageButton("茧票过户", R.drawable.ic_ticket_transfer, vip = true, enabled = false) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
@@ -176,13 +176,11 @@ fun TicketMoreDialog(
|
||||
}
|
||||
}
|
||||
item {
|
||||
MyImageButton("上传图片", R.drawable.ic_upload_pic, true) {
|
||||
|
||||
MyImageButton("上传图片", R.drawable.ic_upload_pic, vip = true, enabled = false) {
|
||||
}
|
||||
}
|
||||
item {
|
||||
MyImageButton("隔日作废", R.drawable.ic_ticket_delete_next_day, true) {
|
||||
|
||||
MyImageButton("隔日作废", R.drawable.ic_ticket_delete_next_day, vip = true, enabled = false) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,12 +236,18 @@ fun CocoonTypeTranslate(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyImageButton(text: String, resId: Int, vip: Boolean = false, onClick: () -> Unit) {
|
||||
fun MyImageButton(text: String, resId: Int, vip: Boolean = false, enabled: Boolean = true, onClick: () -> Unit) {
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp)
|
||||
.clickable { onClick() },
|
||||
.clickable {
|
||||
if (enabled) {
|
||||
onClick()
|
||||
} else {
|
||||
Toasty.showToast("正在开发中,敬请期待")
|
||||
}
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (vip) {
|
||||
@@ -251,18 +255,21 @@ fun MyImageButton(text: String, resId: Int, vip: Boolean = false, onClick: () ->
|
||||
Image(
|
||||
painter = painterResource(id = resId),
|
||||
contentDescription = text,
|
||||
modifier = M.size(90.dp)
|
||||
modifier = M.size(90.dp),
|
||||
alpha = if (enabled) 1f else 0.35f
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(id = resId),
|
||||
contentDescription = text,
|
||||
modifier = M.size(90.dp)
|
||||
modifier = M.size(90.dp),
|
||||
alpha = if (enabled) 1f else 0.35f
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
color = if (enabled) MyColors.Black else MyColors.Gray,
|
||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
@@ -153,15 +153,15 @@ fun FundsScreen(
|
||||
)
|
||||
}
|
||||
Spacer(modifier = M.weight(1f))
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "查询配额") {
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "查询配额", enabled = false) {
|
||||
fundsViewModel.showQueryBalanceDialog()
|
||||
}
|
||||
VipBadge {
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "查询余额") {
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "查询余额", enabled = false) {
|
||||
fundsViewModel.showQueryBalanceDialog()
|
||||
}
|
||||
}
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "修改支付密码") {
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "修改支付密码", enabled = false) {
|
||||
Toasty.showToast("正在开发中,敬请期待!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +413,7 @@ fun WeatherItem(
|
||||
|
||||
@Composable
|
||||
fun MessageInfo(homeViewModel: HomeViewModel) {
|
||||
val notices by homeViewModel.notices.collectAsState()
|
||||
Box(modifier = M.padding(5.dp)) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.bg_weather),
|
||||
@@ -429,14 +430,16 @@ fun MessageInfo(homeViewModel: HomeViewModel) {
|
||||
})
|
||||
}
|
||||
LazyColumn {
|
||||
items(count = 0) { index ->
|
||||
items(count = notices.size) { index ->
|
||||
val notice = notices[index]
|
||||
MessageItem(
|
||||
homeViewModel,
|
||||
true,
|
||||
true,
|
||||
"管理员",
|
||||
"2024-08-02 11:25:32",
|
||||
"正在开发中"
|
||||
!homeViewModel.isNoticeRead(notice.id),
|
||||
notice.sender,
|
||||
notice.time,
|
||||
notice.title,
|
||||
onClick = { homeViewModel.showNoticeDialog(notice) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -451,12 +454,13 @@ fun MessageItem(
|
||||
unRead: Boolean,
|
||||
name: String,
|
||||
time: String,
|
||||
message: String
|
||||
message: String,
|
||||
onClick: (() -> Unit)? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = M
|
||||
.clickable {
|
||||
homeViewModel.showMsgDialog(name, time, message)
|
||||
onClick?.invoke() ?: homeViewModel.showMsgDialog(name, time, message)
|
||||
}
|
||||
.padding(bottom = 5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically) {
|
||||
|
||||
@@ -22,6 +22,14 @@ import java.util.Date
|
||||
|
||||
class HomeViewModel : BaseViewModel() {
|
||||
|
||||
data class Notice(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val sender: String,
|
||||
val time: String,
|
||||
val content: String
|
||||
)
|
||||
|
||||
private val _todayPrice = MutableStateFlow<List<TodayPriceResponse.Data>>(emptyList())
|
||||
val todayPrice = _todayPrice.asStateFlow()
|
||||
|
||||
@@ -37,7 +45,11 @@ class HomeViewModel : BaseViewModel() {
|
||||
private val _messageDialogData = MutableStateFlow(MessageDialogData())
|
||||
val messageDialogData: StateFlow<MessageDialogData> = _messageDialogData.asStateFlow()
|
||||
|
||||
private val _notices = MutableStateFlow<List<Notice>>(emptyList())
|
||||
val notices = _notices.asStateFlow()
|
||||
|
||||
init {
|
||||
loadMockNotices()
|
||||
refreshWeatherInfo(false)
|
||||
refreshTodayPrice(false)
|
||||
doInIoThreadNoDialog {
|
||||
@@ -58,6 +70,47 @@ class HomeViewModel : BaseViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMockNotices() {
|
||||
val readIds = getReadNoticeIds()
|
||||
_notices.value = listOf(
|
||||
Notice(
|
||||
id = "notice_001",
|
||||
title = "收购系统版本提示",
|
||||
sender = "系统通知",
|
||||
time = "2026-05-27 09:30:00",
|
||||
content = "本次前端已调整农户建档、通知展示和称重信息展示,后端接口将陆续配合。"
|
||||
),
|
||||
Notice(
|
||||
id = "notice_002",
|
||||
title = "操作指引",
|
||||
sender = "管理员",
|
||||
time = "2026-05-27 10:15:00",
|
||||
content = "https://www.bbitcn.com"
|
||||
),
|
||||
Notice(
|
||||
id = "notice_003",
|
||||
title = "设备巡检提醒",
|
||||
sender = "运维",
|
||||
time = "2026-05-27 11:00:00",
|
||||
content = "请在每日开秤前检查打印机、电子秤、读卡器连接状态。"
|
||||
)
|
||||
).map { it.copy(title = if (readIds.contains(it.id)) it.title else it.title) }
|
||||
}
|
||||
|
||||
fun isNoticeRead(id: String): Boolean = getReadNoticeIds().contains(id)
|
||||
|
||||
private fun getReadNoticeIds(): Set<String> {
|
||||
return MMKVUtil.get("HOME_NOTICE_READ_IDS", "")
|
||||
.split(",")
|
||||
.filter { it.isNotBlank() }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun markNoticeRead(id: String) {
|
||||
val ids = getReadNoticeIds() + id
|
||||
MMKVUtil.put("HOME_NOTICE_READ_IDS", ids.joinToString(","))
|
||||
}
|
||||
|
||||
fun refreshAcquireData(
|
||||
startDate: Date = _dateRange2.value.first,
|
||||
endDate: Date = _dateRange2.value.second,
|
||||
@@ -106,6 +159,22 @@ class HomeViewModel : BaseViewModel() {
|
||||
)
|
||||
}
|
||||
|
||||
fun showNoticeDialog(notice: Notice) {
|
||||
markNoticeRead(notice.id)
|
||||
_notices.update { it.toList() }
|
||||
_messageDialogData.value = MessageDialogData(
|
||||
showDialog = true,
|
||||
username = notice.sender,
|
||||
time = notice.time,
|
||||
content = notice.content,
|
||||
title = notice.title,
|
||||
isWebUrl = notice.content.startsWith("http://") || notice.content.startsWith("https://"),
|
||||
onDismiss = {
|
||||
_messageDialogData.update { it.copy(showDialog = false) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun logout(onFinished: () -> Unit) {
|
||||
// 退出登录
|
||||
doInIoThreadThenUI(loadingTips = "正在退出登录", onIO = {
|
||||
|
||||
@@ -118,6 +118,7 @@ fun PurchaseScreen(
|
||||
var queryInput by rememberSaveable { mutableStateOf("") }
|
||||
// 筛选条件3:查询类型
|
||||
var queryType by rememberSaveable { mutableStateOf(0) }
|
||||
var queryInputInitialized by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val info = purchaseViewModel.infoPager.collectAsLazyPagingItems()
|
||||
// 查询方法
|
||||
@@ -131,6 +132,10 @@ fun PurchaseScreen(
|
||||
info.refresh()
|
||||
}
|
||||
LaunchedEffect(queryInput) {
|
||||
if (!queryInputInitialized) {
|
||||
queryInputInitialized = true
|
||||
return@LaunchedEffect
|
||||
}
|
||||
delay(350)
|
||||
updateParams()
|
||||
}
|
||||
|
||||
@@ -65,7 +65,10 @@ class PurchaseViewModel : BaseViewModel() {
|
||||
init {
|
||||
doInIoThreadNoDialog {
|
||||
// 获取一个月前的日期
|
||||
updateParams(getRecentMonthsDate(1), Date(), "", 0)
|
||||
val start = getRecentMonthsDate(1)
|
||||
val end = Date()
|
||||
_dateRange.value = start to end
|
||||
updateParams(start, end, "", 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ fun StatisticsLeft(
|
||||
}
|
||||
Spacer(modifier = M.weight(1f))
|
||||
VipBadge {
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "发送短信") {
|
||||
MyButton(modifier = M.fillMaxWidth(), text = "发送短信", enabled = false) {
|
||||
Toasty.showToast("正在开发中,敬请期待")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +1,50 @@
|
||||
package com.bbitcn.f8.pad.ui.screen.mainFunc
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectDragGestures
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bbitcn.f8.pad.M
|
||||
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.M
|
||||
import com.bbitcn.f8.pad.base.AssistChipFilter
|
||||
import com.bbitcn.f8.pad.base.InfoText
|
||||
import com.bbitcn.f8.pad.base.MainFuncFrame
|
||||
import com.bbitcn.f8.pad.base.MyButton
|
||||
import com.bbitcn.f8.pad.base.MyCard
|
||||
import com.bbitcn.f8.pad.base.MyDialog
|
||||
import com.bbitcn.f8.pad.base.MyInfoCard
|
||||
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
|
||||
import com.blankj.utilcode.util.StringUtils
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description 主功能-预约售茧
|
||||
* @Author DuanKaiji
|
||||
* @CreateTime 2024年08月02日 11:25:32
|
||||
*/
|
||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||
@Composable
|
||||
fun UserScreenPV() {
|
||||
@@ -83,339 +56,14 @@ fun UserScreen(
|
||||
navController: NavController,
|
||||
userViewModel: UserViewModel = viewModel()
|
||||
) {
|
||||
val treeData by userViewModel.treeData.collectAsState()
|
||||
val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems()
|
||||
MainFuncFrame {
|
||||
if (isLandscape()) {
|
||||
UserScreenInLandscape(navController, userViewModel, treeData, userData)
|
||||
} else {
|
||||
UserScreenInPortrait(navController, userViewModel, treeData, userData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserScreenInPortrait(
|
||||
navController: NavController,
|
||||
userViewModel: UserViewModel,
|
||||
treeData: List<Pair<Pair<String, Int>, Any>>,
|
||||
userData: LazyPagingItems<UserDataResponse.Data>
|
||||
) {
|
||||
Column(
|
||||
modifier = M.fillMaxSize()
|
||||
) {
|
||||
MyInfoCard(
|
||||
modifier = M
|
||||
.weight(2f)
|
||||
.padding(bottom = 15.dp)
|
||||
) {
|
||||
CollapsibleList(userViewModel, treeData, userData)
|
||||
}
|
||||
MyInfoCard(
|
||||
modifier = M.weight(8f)
|
||||
) {
|
||||
MyInfoCard(modifier = M.fillMaxSize()) {
|
||||
UserManageList(navController, userViewModel, userData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserScreenInLandscape(
|
||||
navController: NavController,
|
||||
userViewModel: UserViewModel,
|
||||
treeData: List<Pair<Pair<String, Int>, Any>>,
|
||||
userData: LazyPagingItems<UserDataResponse.Data>
|
||||
) {
|
||||
var leftWeight by rememberSaveable { mutableStateOf(2.5f) }
|
||||
var rightWeight by rememberSaveable { mutableStateOf(7.5f) }
|
||||
Row(
|
||||
modifier = M.fillMaxSize()
|
||||
) {
|
||||
MyInfoCard(
|
||||
modifier = M
|
||||
.weight(leftWeight) // 使用动态权重
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
CollapsibleList(userViewModel, treeData, userData)
|
||||
}
|
||||
Box(
|
||||
modifier = M
|
||||
.fillMaxHeight()
|
||||
.pointerInput(Unit) {
|
||||
detectDragGestures { change, dragAmount ->
|
||||
change.consume() // 消费掉手势事件
|
||||
val dragDelta = dragAmount.x // 获取横向拖动的距离
|
||||
leftWeight = (leftWeight + dragDelta * 0.01f).coerceIn(2.5f, 4.5f)
|
||||
rightWeight = 10f - leftWeight
|
||||
// rightWeight = (rightWeight - dragDelta * 0.01f).coerceIn(5f, 8f)
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
MyInfoCard(
|
||||
modifier = M
|
||||
.width(15.dp)
|
||||
.height(50.dp)
|
||||
.padding(horizontal = 5.dp)
|
||||
) {
|
||||
}
|
||||
}
|
||||
MyInfoCard(
|
||||
modifier = M
|
||||
.weight(rightWeight) // 使用动态权重
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
UserManageList(navController, userViewModel, userData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CollapsibleList(
|
||||
userViewModel: UserViewModel,
|
||||
listData: List<Pair<Pair<String, Int>, Any>>,
|
||||
pager: LazyPagingItems<UserDataResponse.Data>
|
||||
) {
|
||||
val queryInput by userViewModel.areaLike.collectAsState()
|
||||
Column {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.padding(15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
QueryTextField(M.weight(1f), queryInput) {
|
||||
userViewModel.updateAreaLike(it)
|
||||
userViewModel.getUsersArea(false)
|
||||
}
|
||||
Image(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
contentDescription = "Refresh",
|
||||
modifier = M.clickable {
|
||||
userViewModel.updateParams()
|
||||
userViewModel.getUsersArea(showLoading = true)
|
||||
pager.refresh()
|
||||
}
|
||||
)
|
||||
}
|
||||
LazyColumn {
|
||||
items(listData) { item ->
|
||||
CollapsibleItem(
|
||||
userViewModel,
|
||||
pager,
|
||||
item = item,
|
||||
currentLevel = 1,
|
||||
needExpand = queryInput != ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CollapsibleItem(
|
||||
userViewModel: UserViewModel,
|
||||
pager: LazyPagingItems<UserDataResponse.Data>,
|
||||
item: Pair<Pair<String, Int>, Any>,
|
||||
currentLevel: Int,
|
||||
xian: String = "",
|
||||
xiang: String = "",
|
||||
cun: String = "",
|
||||
needExpand: Boolean = false
|
||||
) {
|
||||
val titleAndCount = item.first
|
||||
if (StringUtils.isEmpty(titleAndCount.first)) {
|
||||
return
|
||||
}
|
||||
val subItems = item.second
|
||||
// 当前项的展开状态 0表示展开,-1表示折叠 1表示加载中
|
||||
var expandedIndex by rememberSaveable { mutableStateOf(if (needExpand) 0 else -1) }
|
||||
LaunchedEffect(needExpand) {
|
||||
if (needExpand != (expandedIndex == 0)) {
|
||||
expandedIndex = if (needExpand) 0 else -1
|
||||
}
|
||||
}
|
||||
// 被选中的项
|
||||
val xianTmp = if (currentLevel == 1) titleAndCount.first else xian
|
||||
val xiangTmp = if (currentLevel == 2) titleAndCount.first else xiang
|
||||
val cunTmp = if (currentLevel == 3) titleAndCount.first else cun
|
||||
|
||||
val expendListener = {
|
||||
if (expandedIndex == -1) {
|
||||
// 加载子项数据
|
||||
expandedIndex = 1
|
||||
userViewModel.loadArea(
|
||||
currentLevel,
|
||||
xian = xianTmp,
|
||||
xiang = xiangTmp,
|
||||
cun = cunTmp,
|
||||
) {
|
||||
expandedIndex = 0
|
||||
}
|
||||
} else {
|
||||
expandedIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
val titleIsSelect =
|
||||
userViewModel.areaFilter.collectAsState().value.contains(item.first.first)
|
||||
// 顶层项的显示和点击逻辑
|
||||
MyCard(radius = 20.dp, elevation = 0.dp, modifier = M.padding(1.5.dp)) {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.background(if (titleIsSelect) MyColors.BlueGreen else MyColors.White)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onDoubleTap = {
|
||||
expendListener()
|
||||
},
|
||||
onTap = {
|
||||
userViewModel.updateParams(xianTmp, xiangTmp, cunTmp)
|
||||
pager.refresh()
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding(horizontal = 15.dp, vertical = 5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Row(
|
||||
modifier = M.weight(1f),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = titleAndCount.first,
|
||||
color = if (titleIsSelect) MyColors.White else MyColors.Black,
|
||||
fontSize = if (currentLevel == 1) MaterialTheme.typography.titleLarge.fontSize
|
||||
else if (currentLevel == 2) MaterialTheme.typography.bodyLarge.fontSize
|
||||
else MaterialTheme.typography.bodyMedium.fontSize,
|
||||
)
|
||||
if (titleAndCount.second != -1) {
|
||||
Text(
|
||||
modifier = M.padding(end = 5.dp),
|
||||
text = "${titleAndCount.second}",
|
||||
color = if (titleIsSelect) MyColors.LightGray else MyColors.Gray,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
if (expandedIndex == 1) {
|
||||
CircularProgressIndicator(
|
||||
strokeWidth = 3.dp,
|
||||
modifier = M.size(20.dp),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
modifier = M
|
||||
.clickable {
|
||||
expendListener()
|
||||
},
|
||||
tint = if (titleIsSelect) MyColors.White else MyColors.Black,
|
||||
imageVector = if (expandedIndex == 0)
|
||||
Icons.Default.ExpandLess
|
||||
else
|
||||
Icons.Default.ExpandMore,
|
||||
contentDescription = if (expandedIndex != -1) "Collapse" else "Expand"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = expandedIndex != -1) {
|
||||
Column(modifier = M.padding(start = 10.dp)) {
|
||||
when (subItems) {
|
||||
// 如果子项是 Map 类型,表示还有嵌套项,需要递归渲染
|
||||
is Map<*, *> -> {
|
||||
subItems.forEach { subItem ->
|
||||
if (subItem is Map.Entry<*, *>) {
|
||||
val subPair = subItem.key to subItem.value
|
||||
CollapsibleItem(
|
||||
userViewModel,
|
||||
pager,
|
||||
subPair as Pair<Pair<String, Int>, Any>,
|
||||
currentLevel + 1,
|
||||
xianTmp,
|
||||
xiangTmp,
|
||||
cunTmp,
|
||||
needExpand
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果子项是 List 类型,表示这是最底层的组数据
|
||||
is List<*> -> {
|
||||
subItems.forEach { subItem ->
|
||||
if (subItem is Pair<*, *>) {
|
||||
if (StringUtils.isEmpty(subItem.first.toString())) {
|
||||
return@forEach
|
||||
}
|
||||
val isSelect = titleIsSelect
|
||||
&& userViewModel.areaFilter.collectAsState().value.contains(
|
||||
subItem.first.toString()
|
||||
)
|
||||
// 最底层的组
|
||||
MyCard(
|
||||
radius = 20.dp,
|
||||
elevation = 0.dp,
|
||||
modifier = M.padding(2.5.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
if (isSelect) MyColors.BlueGreen else MyColors.White
|
||||
)
|
||||
.clickable {
|
||||
userViewModel.updateParams(
|
||||
xianTmp,
|
||||
xiangTmp,
|
||||
cunTmp,
|
||||
subItem.first.toString()
|
||||
)
|
||||
pager.refresh()
|
||||
}
|
||||
.padding(10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = subItem.first.toString(),
|
||||
color = if (isSelect) MyColors.White else MyColors.Black,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
if (subItem.second != -1) {
|
||||
Text(
|
||||
modifier = M.padding(end = 33.dp),
|
||||
text = "${subItem.second}",
|
||||
color = if (isSelect) MyColors.White else MyColors.Gray,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 未来处理更多层级时,可以继续添加逻辑
|
||||
// else if (subItem is Pair<*, *>) {
|
||||
// // 递归处理更深层的结构
|
||||
// (subItem as? Pair<String, Any>)?.let { subItemPair ->
|
||||
// CollapsibleItem(userViewModel,subItemPair,
|
||||
// currentLevel + 1)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserManageList(
|
||||
navController: NavController,
|
||||
@@ -424,6 +72,7 @@ fun UserManageList(
|
||||
) {
|
||||
var queryInput by rememberSaveable { mutableStateOf("") }
|
||||
val areaFilter by userViewModel.areaFilter.collectAsState()
|
||||
var detailUser by remember { mutableStateOf<UserDataResponse.Data?>(null) }
|
||||
val onFilterLikeChanged: (String) -> Unit = {
|
||||
queryInput = it
|
||||
userViewModel.updateParamsLike(it)
|
||||
@@ -434,25 +83,14 @@ fun UserManageList(
|
||||
.padding(15.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 5.dp),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MyButton(text = "新增", onClick = {
|
||||
navController.navigate("addUser")
|
||||
})
|
||||
AssistChipFilter("区域:", areaFilter, onClick = {
|
||||
userViewModel.updateParams()
|
||||
userData.refresh()
|
||||
})
|
||||
AssistChipFilter("筛选:", queryInput, onFilterLikeChanged)
|
||||
Spacer(modifier = M.weight(1f))
|
||||
QueryTextField(M.width(200.dp), queryInput, onValueChange = onFilterLikeChanged)
|
||||
// MyButton(text = "更多", modifier = M.padding(horizontal = 10.dp), onClick = {})
|
||||
}
|
||||
UserFilterBar(
|
||||
userViewModel = userViewModel,
|
||||
userData = userData,
|
||||
queryInput = queryInput,
|
||||
onQueryChanged = onFilterLikeChanged,
|
||||
areaFilter = areaFilter,
|
||||
navController = navController
|
||||
)
|
||||
val myPager = userViewModel.usersInfoMyPager
|
||||
val isRefreshing by myPager.listIsRefreshing.collectAsState()
|
||||
MyRefreshTable(
|
||||
@@ -467,8 +105,8 @@ fun UserManageList(
|
||||
MyTableData(1, isIndex = true),
|
||||
MyTableData("姓名", 1, { it.nhname }),
|
||||
MyTableData("手机号", 2, { it.phone }),
|
||||
MyTableData("身份证", 1, { if (it.idcard != "") "✔️" else "" }),
|
||||
MyTableData("银行卡", 1, { if (it.bankcode != "") "✔️" else "" }),
|
||||
MyTableData("身份证", 1, { if (it.idcard != "") "已录" else "" }),
|
||||
MyTableData("银行卡", 1, { if (it.bankcode != "") "已录" else "" }),
|
||||
MyTableData("所属地址", 3, { "${it.xian}${it.xiang}${it.cun}${it.zu}" }),
|
||||
MyTableData("建档时间", 2, { TimeUtils.formatDateTimeStrToDateStr(it.createtime) }),
|
||||
MyTableData("", 1, { "修改" }, true) {
|
||||
@@ -478,27 +116,8 @@ fun UserManageList(
|
||||
navController.navigate("weight/${it.sysid}")
|
||||
},
|
||||
),
|
||||
onExpend = {
|
||||
Column(modifier = M.fillMaxWidth().border(1.dp, MyColors.Gray).padding(15.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
InfoText("姓名", it.nhname,M.weight(2f),true)
|
||||
InfoText("电话", it.phone,M.weight(2f),true)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
InfoText("银行", it.bankname,M.weight(2f),true)
|
||||
InfoText("卡号", it.bankcode,M.weight(2f),true)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
InfoText("建档时间", it.createtime,M.weight(2f),true)
|
||||
InfoText("身份证", it.idcard,M.weight(2f),true)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
InfoText("县", it.xian,M.weight(1f),true)
|
||||
InfoText("乡", it.xiang,M.weight(1f),true)
|
||||
InfoText("村", it.cun,M.weight(1f),true)
|
||||
InfoText("组", it.zu,M.weight(1f),true)
|
||||
}
|
||||
}
|
||||
onClick = {
|
||||
detailUser = it
|
||||
},
|
||||
onLongClick = {
|
||||
userViewModel.deleteUser(it.nhname, it.sysid) {
|
||||
@@ -507,4 +126,130 @@ fun UserManageList(
|
||||
}
|
||||
)
|
||||
}
|
||||
UserDetailDialog(detailUser) {
|
||||
detailUser = null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserDetailDialog(
|
||||
user: UserDataResponse.Data?,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
MyDialog(
|
||||
title = "农户详情",
|
||||
showDialog = user != null,
|
||||
onDismissRequest = onDismiss
|
||||
) {
|
||||
if (user == null) {
|
||||
return@MyDialog
|
||||
}
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 24.dp, vertical = 8.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
DetailSection("基本信息") {
|
||||
DetailRow("姓名", user.nhname, "手机号", user.phone)
|
||||
DetailRow("身份证号", user.idcard, "建档时间", user.createtime)
|
||||
}
|
||||
DetailSection("银行卡") {
|
||||
DetailRow("银行卡号", user.bankcode, "银行名称", user.bankname)
|
||||
DetailRow("银行简称", user.bankshortname, "联行号", user.recbankcode)
|
||||
}
|
||||
DetailSection("地址") {
|
||||
DetailRow("县", user.xian, "乡", user.xiang)
|
||||
DetailRow("村", user.cun, "组", user.zu)
|
||||
DetailSingleRow("完整地址", "${user.xian}${user.xiang}${user.cun}${user.zu}")
|
||||
}
|
||||
DetailSection("系统信息") {
|
||||
DetailRow("系统ID", user.sysid, "状态标记", user.flag.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailSection(title: String, content: @Composable () -> Unit) {
|
||||
MyCard(elevation = 0.5.dp, radius = 6.dp, modifier = M.fillMaxWidth()) {
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.background(MyColors.LightGray)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
color = MyColors.BlueGreen,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailRow(label1: String, value1: String, label2: String, value2: String) {
|
||||
Row(modifier = M.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
DetailField(label1, value1, M.weight(1f))
|
||||
DetailField(label2, value2, M.weight(1f))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailSingleRow(label: String, value: String) {
|
||||
DetailField(label, value, M.fillMaxWidth())
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailField(label: String, value: String, modifier: androidx.compose.ui.Modifier = M) {
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = label,
|
||||
color = MyColors.Gray,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize
|
||||
)
|
||||
Text(
|
||||
text = value.ifBlank { "-" },
|
||||
color = MyColors.Black,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = MaterialTheme.typography.bodyLarge.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserFilterBar(
|
||||
userViewModel: UserViewModel,
|
||||
userData: LazyPagingItems<UserDataResponse.Data>,
|
||||
queryInput: String,
|
||||
onQueryChanged: (String) -> Unit,
|
||||
areaFilter: String,
|
||||
navController: NavController
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp), modifier = M.padding(bottom = 8.dp)) {
|
||||
Row(
|
||||
modifier = M.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MyButton(text = "新增", onClick = {
|
||||
navController.navigate("addUser")
|
||||
})
|
||||
Spacer(modifier = M.weight(1f))
|
||||
QueryTextField(M.width(200.dp), queryInput, onValueChange = onQueryChanged)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
AssistChipFilter("区域:", areaFilter, onClick = {
|
||||
userViewModel.clearAreaFilter()
|
||||
userData.refresh()
|
||||
})
|
||||
AssistChipFilter("筛选:", queryInput, onQueryChanged)
|
||||
Spacer(modifier = M.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,51 @@ package com.bbitcn.f8.pad.ui.screen.mainFunc;
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bbitcn.f8.pad.base.BaseViewModel
|
||||
import com.bbitcn.f8.pad.model.net.request.UserListDataRequest
|
||||
import com.bbitcn.f8.pad.model.net.response.UsersAreaResponse
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.bbitcn.f8.pad.utils.pager.MyPager
|
||||
import com.bbitcn.f8.pad.utils.pager.UsersInfoPagingSource
|
||||
import com.blankj.utilcode.util.StringUtils
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class UserViewModel : BaseViewModel() {
|
||||
|
||||
private val _areaLike = MutableStateFlow("")
|
||||
val areaLike = _areaLike.asStateFlow()
|
||||
|
||||
private val _treeData = MutableStateFlow<List<Pair<Pair<String, Int>, Any>>>(emptyList())
|
||||
val treeData: StateFlow<List<Pair<Pair<String, Int>, Any>>> = _treeData.asStateFlow()
|
||||
|
||||
private val _tempData: MutableList<UsersAreaResponse.Data> = mutableListOf()
|
||||
|
||||
private val _areaFilter = MutableStateFlow("")
|
||||
val areaFilter = _areaFilter.asStateFlow()
|
||||
|
||||
data class AreaOption(
|
||||
val xian: String,
|
||||
val xiang: String,
|
||||
val cun: String,
|
||||
val zu: String
|
||||
)
|
||||
|
||||
private val mockAreas = listOf(
|
||||
AreaOption("江安县", "怡乐镇", "公平村", "一组"),
|
||||
AreaOption("江安县", "怡乐镇", "公平村", "二组"),
|
||||
AreaOption("江安县", "大井镇", "友好村", "三组"),
|
||||
AreaOption("长宁县", "竹海镇", "联盟村", "一组"),
|
||||
AreaOption("长宁县", "梅硐镇", "石陇村", "五组"),
|
||||
AreaOption("兴文县", "古宋镇", "建设村", "二组")
|
||||
)
|
||||
|
||||
private val _xianList = MutableStateFlow<List<String>>(emptyList())
|
||||
val xianList = _xianList.asStateFlow()
|
||||
private val _xiangList = MutableStateFlow<List<String>>(emptyList())
|
||||
val xiangList = _xiangList.asStateFlow()
|
||||
private val _cunList = MutableStateFlow<List<String>>(emptyList())
|
||||
val cunList = _cunList.asStateFlow()
|
||||
private val _zuList = MutableStateFlow<List<String>>(emptyList())
|
||||
val zuList = _zuList.asStateFlow()
|
||||
|
||||
private val _selectedXian = MutableStateFlow("")
|
||||
val selectedXian = _selectedXian.asStateFlow()
|
||||
private val _selectedXiang = MutableStateFlow("")
|
||||
val selectedXiang = _selectedXiang.asStateFlow()
|
||||
private val _selectedCun = MutableStateFlow("")
|
||||
val selectedCun = _selectedCun.asStateFlow()
|
||||
private val _selectedZu = MutableStateFlow("")
|
||||
val selectedZu = _selectedZu.asStateFlow()
|
||||
|
||||
val usersInfoMyPager = MyPager(
|
||||
pagingSourceFactory = { UsersInfoPagingSource(it) },
|
||||
initialRequestData = UserListDataRequest(), // 传入初始的请求数据
|
||||
@@ -33,7 +56,7 @@ class UserViewModel : BaseViewModel() {
|
||||
val usersInfoPager = usersInfoMyPager.createPager(viewModelScope)
|
||||
|
||||
init {
|
||||
getUsersArea(false)
|
||||
refreshAreaOptions()
|
||||
}
|
||||
|
||||
fun updateParamsLike(like: String) {
|
||||
@@ -51,6 +74,67 @@ class UserViewModel : BaseViewModel() {
|
||||
_areaFilter.value = buildAreaFilter(xian, xiang, cun, zu)
|
||||
}
|
||||
|
||||
fun selectXian(value: String) {
|
||||
_selectedXian.value = value
|
||||
_selectedXiang.value = ""
|
||||
_selectedCun.value = ""
|
||||
_selectedZu.value = ""
|
||||
refreshAreaOptions()
|
||||
updateParams(value)
|
||||
}
|
||||
|
||||
fun selectXiang(value: String) {
|
||||
_selectedXiang.value = value
|
||||
_selectedCun.value = ""
|
||||
_selectedZu.value = ""
|
||||
refreshAreaOptions()
|
||||
updateParams(_selectedXian.value, value)
|
||||
}
|
||||
|
||||
fun selectCun(value: String) {
|
||||
_selectedCun.value = value
|
||||
_selectedZu.value = ""
|
||||
refreshAreaOptions()
|
||||
updateParams(_selectedXian.value, _selectedXiang.value, value)
|
||||
}
|
||||
|
||||
fun selectZu(value: String) {
|
||||
_selectedZu.value = value
|
||||
updateParams(_selectedXian.value, _selectedXiang.value, _selectedCun.value, value)
|
||||
}
|
||||
|
||||
fun clearAreaFilter() {
|
||||
_selectedXian.value = ""
|
||||
_selectedXiang.value = ""
|
||||
_selectedCun.value = ""
|
||||
_selectedZu.value = ""
|
||||
refreshAreaOptions()
|
||||
updateParams()
|
||||
}
|
||||
|
||||
private fun refreshAreaOptions() {
|
||||
val xian = _selectedXian.value
|
||||
val xiang = _selectedXiang.value
|
||||
val cun = _selectedCun.value
|
||||
_xianList.value = mockAreas.map { it.xian }.distinct()
|
||||
_xiangList.value = mockAreas
|
||||
.filter { xian.isEmpty() || it.xian == xian }
|
||||
.map { it.xiang }
|
||||
.distinct()
|
||||
_cunList.value = mockAreas
|
||||
.filter { (xian.isEmpty() || it.xian == xian) && (xiang.isEmpty() || it.xiang == xiang) }
|
||||
.map { it.cun }
|
||||
.distinct()
|
||||
_zuList.value = mockAreas
|
||||
.filter {
|
||||
(xian.isEmpty() || it.xian == xian) &&
|
||||
(xiang.isEmpty() || it.xiang == xiang) &&
|
||||
(cun.isEmpty() || it.cun == cun)
|
||||
}
|
||||
.map { it.zu }
|
||||
.distinct()
|
||||
}
|
||||
|
||||
fun buildAreaFilter(xian: String, xiang: String, cun: String, zu: String): String {
|
||||
return buildString {
|
||||
append(xian)
|
||||
@@ -60,154 +144,6 @@ class UserViewModel : BaseViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAreaLike(like: String) {
|
||||
_areaLike.value = like
|
||||
}
|
||||
|
||||
fun getUsersArea(showLoading: Boolean) {
|
||||
doInIoThreadWith(showLoading, "正在加载区域数据...") {
|
||||
_treeData.value = emptyList()
|
||||
val like = _areaLike.value
|
||||
if (StringUtils.isEmpty(like)) {
|
||||
val response = apiService.getUsersAreaForXian()
|
||||
if (response.code == 1) {
|
||||
response.data.forEach {
|
||||
_tempData.add(UsersAreaResponse.Data(xian = it.xian, count = it.count))
|
||||
}
|
||||
_treeData.value = convertToNestedStructure(_tempData)
|
||||
}
|
||||
} else {
|
||||
val response = apiService.getUsersArea(like)
|
||||
if (response.code == 1) {
|
||||
_treeData.value = convertToNestedStructure(response.data)
|
||||
} else {
|
||||
Toasty.showTipsDialog(response.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadArea(
|
||||
currentLevel: Int,
|
||||
xian: String,
|
||||
xiang: String,
|
||||
cun: String,
|
||||
onFinish: () -> Unit
|
||||
) {
|
||||
// 只有在搜索信息为空的时候才逐级加载
|
||||
if (StringUtils.isEmpty(_areaLike.value)) {
|
||||
doInIoThreadNoDialog {
|
||||
if (currentLevel == 1) {
|
||||
// 级别为1时,加载乡
|
||||
val list = apiService.getUsersAreaForXiang(xian = xian)
|
||||
if (list.code == 1) {
|
||||
list.data.forEach {
|
||||
_tempData.add(
|
||||
UsersAreaResponse.Data(
|
||||
xian = xian,
|
||||
xiang = it.xiang,
|
||||
count = it.count
|
||||
)
|
||||
)
|
||||
}
|
||||
_treeData.value = convertToNestedStructure(_tempData)
|
||||
} else {
|
||||
Toasty.showTipsDialog(list.msg)
|
||||
}
|
||||
} else if (currentLevel == 2) {
|
||||
// 级别为2时,加载村
|
||||
val list = apiService.getUsersAreaForCun(xian = xian, xiang = xiang)
|
||||
if (list.code == 1) {
|
||||
list.data.forEach {
|
||||
_tempData.add(
|
||||
UsersAreaResponse.Data(
|
||||
xian = xian,
|
||||
xiang = xiang,
|
||||
cun = it.cun,
|
||||
count = it.count
|
||||
)
|
||||
)
|
||||
}
|
||||
_treeData.value = convertToNestedStructure(_tempData)
|
||||
} else {
|
||||
Toasty.showTipsDialog(list.msg)
|
||||
}
|
||||
} else if (currentLevel == 3) {
|
||||
// 级别为3时,加载组
|
||||
val list = apiService.getUsersAreaForZu(xian = xian, xiang = xiang, cun = cun)
|
||||
if (list.code == 1) {
|
||||
list.data.forEach {
|
||||
_tempData.add(
|
||||
UsersAreaResponse.Data(
|
||||
xian = xian,
|
||||
xiang = xiang,
|
||||
cun = cun,
|
||||
zu = it.zu,
|
||||
count = it.count
|
||||
)
|
||||
)
|
||||
}
|
||||
_treeData.value = convertToNestedStructure(_tempData)
|
||||
} else {
|
||||
Toasty.showTipsDialog(list.msg)
|
||||
}
|
||||
}
|
||||
onFinish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun convertToNestedStructure(data: List<UsersAreaResponse.Data>): List<Pair<Pair<String, Int>, Any>> {
|
||||
val result = mutableListOf<Pair<Pair<String, Int>, Any>>()
|
||||
// val result = mutableListOf<Pair<Pair<String, Int>, Pair<Pair<String, Int>, Pair<Pair<String, Int>, Pair<Pair<String, Int>, String>>>>>()
|
||||
// 按县(Xian)分组
|
||||
data.groupBy { it.xian }.forEach { (xian, xiangList) ->
|
||||
// 按乡(Xiang)分组
|
||||
val xiangGroups = xiangList.groupBy { it.xiang }.map { (xiang, cunList) ->
|
||||
// 按村(Cun)分组
|
||||
val cunGroups = cunList.groupBy { it.cun }.map { (cun, zuList) ->
|
||||
// 按组(Zu)分组
|
||||
val zuListWithCount = zuList.map { it.zu to it.count }
|
||||
Pair(cun to getCount(data, xian, xiang, cun), zuListWithCount)
|
||||
}.toMap()
|
||||
// 返回乡及其村的分组
|
||||
Pair(xiang to getCount(data, xian, xiang), cunGroups)
|
||||
}.toMap()
|
||||
result.add(Pair(xian to getCount(data, xian), xiangGroups))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun getCount(
|
||||
list: List<UsersAreaResponse.Data>,
|
||||
xian: String, xiang: String? = null, cun: String? = null, zu: String? = null
|
||||
): Int {
|
||||
for (item in list) {
|
||||
// 找县的数量
|
||||
if (xiang == null) {
|
||||
if (item.xian == xian && item.xiang == "" && item.cun == "" && item.zu == "") {
|
||||
return item.count
|
||||
}
|
||||
} else if (cun == null) {
|
||||
// 找乡的数量
|
||||
if (item.xian == xian && item.xiang == xiang && item.cun == "" && item.zu == "") {
|
||||
return item.count
|
||||
}
|
||||
} else if (zu == null) {
|
||||
// 找村的数量
|
||||
if (item.xian == xian && item.xiang == xiang && item.cun == cun && item.zu == "") {
|
||||
return item.count
|
||||
}
|
||||
} else {
|
||||
// 找组的数量
|
||||
if (item.xian == xian && item.xiang == xiang && item.cun == cun && item.zu == zu) {
|
||||
return item.count
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun deleteUser(name: String, sysid: String, onSuccess: () -> Unit) {
|
||||
Toasty.showConfirmDialog("确定删除农户" + name + "吗?") {
|
||||
doInIoThread("正在删除农户...") {
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.bbitcn.f8.pad.ui.screen.secondFunc
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -26,8 +30,10 @@ import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -74,29 +80,23 @@ fun AddUserScreen(
|
||||
addUserViewModel.initExtendData()
|
||||
}
|
||||
}
|
||||
val scanDialogData by addUserViewModel.scanDialogData.collectAsState()
|
||||
val ocrDialogData by addUserViewModel.ocrDialogData.collectAsState()
|
||||
val faceDialogData by addUserViewModel.faceDialogData.collectAsState()
|
||||
MainFuncFrame {
|
||||
MyCard(modifier = M.fillMaxSize(), radius = 7.dp, elevation = 1.dp) {
|
||||
VerticalTabPages(
|
||||
modifier = M
|
||||
.fillMaxHeight()
|
||||
.padding(vertical = 15.dp),
|
||||
tabs = listOf("基本信息", "人脸录入", "附件上传", "拓展信息"),
|
||||
tabs = listOf("基本信息", "拓展信息"),
|
||||
) {
|
||||
when (it) {
|
||||
0 -> AddUserBaseInfo(addUserViewModel)
|
||||
1 -> AddUserFaceInfo(addUserViewModel)
|
||||
2 -> AddUserAttachmentInfo(addUserViewModel)
|
||||
3 -> AddUSerExtendInfo(addUserViewModel)
|
||||
1 -> AddUSerExtendInfo(addUserViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ScanDialog(scanDialogData)
|
||||
OCRDialog(ocrDialogData)
|
||||
FaceDialog(faceDialogData)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -170,6 +170,12 @@ fun AddUserBaseInfo(
|
||||
addUserViewModel: AddUserViewModel
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
var selectedCard by rememberSaveable { mutableStateOf("id") }
|
||||
var extraBankCount by rememberSaveable { mutableStateOf(0) }
|
||||
var avatar by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val cameraLauncher = rememberLauncherForActivityResult(ActivityResultContracts.TakePicturePreview()) {
|
||||
avatar = it
|
||||
}
|
||||
// 判断设备是否为竖屏
|
||||
if (configuration.orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT) {
|
||||
// 竖屏模式
|
||||
@@ -182,10 +188,18 @@ fun AddUserBaseInfo(
|
||||
) {
|
||||
addUserViewModel.editFarmer()
|
||||
}
|
||||
AddUserLeftColumn(
|
||||
addUserViewModel = addUserViewModel
|
||||
FarmerCardWallet(
|
||||
selectedCard = selectedCard,
|
||||
extraBankCount = extraBankCount,
|
||||
avatar = avatar,
|
||||
onSelect = { selectedCard = it },
|
||||
onAddBank = {
|
||||
extraBankCount += 1
|
||||
selectedCard = "bank_extra_$extraBankCount"
|
||||
},
|
||||
onAvatarClick = { cameraLauncher.launch(null) }
|
||||
)
|
||||
AddUserRightColumn(addUserViewModel = addUserViewModel)
|
||||
FarmerCardEditor(selectedCard, addUserViewModel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -198,12 +212,103 @@ fun AddUserBaseInfo(
|
||||
) {
|
||||
addUserViewModel.editFarmer()
|
||||
}
|
||||
AddUserLeftColumn(
|
||||
FarmerCardWallet(
|
||||
modifier = M.weight(1f),
|
||||
addUserViewModel,
|
||||
selectedCard = selectedCard,
|
||||
extraBankCount = extraBankCount,
|
||||
avatar = avatar,
|
||||
onSelect = { selectedCard = it },
|
||||
onAddBank = {
|
||||
extraBankCount += 1
|
||||
selectedCard = "bank_extra_$extraBankCount"
|
||||
},
|
||||
onAvatarClick = { cameraLauncher.launch(null) }
|
||||
)
|
||||
}
|
||||
AddUserRightColumn(M.weight(1f), addUserViewModel)
|
||||
FarmerCardEditor(selectedCard, addUserViewModel, M.weight(1.6f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FarmerCardWallet(
|
||||
modifier: Modifier = M,
|
||||
selectedCard: String,
|
||||
extraBankCount: Int,
|
||||
avatar: Bitmap?,
|
||||
onSelect: (String) -> Unit,
|
||||
onAddBank: () -> Unit,
|
||||
onAvatarClick: () -> Unit,
|
||||
) {
|
||||
Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
MyCard(
|
||||
radius = 8.dp,
|
||||
elevation = 0.5.dp,
|
||||
modifier = M.fillMaxWidth().clickable(onClick = onAvatarClick)
|
||||
) {
|
||||
Row(M.fillMaxWidth().padding(12.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
if (avatar == null) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.icon_user),
|
||||
contentDescription = null,
|
||||
modifier = M.size(54.dp)
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
bitmap = avatar.asImageBitmap(),
|
||||
contentDescription = null,
|
||||
modifier = M.size(54.dp)
|
||||
)
|
||||
}
|
||||
Column(M.padding(start = 12.dp)) {
|
||||
Text("头像", fontWeight = FontWeight.Bold)
|
||||
Text("点击拍照", fontSize = MaterialTheme.typography.bodySmall.fontSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
WalletCard("id", "身份证", "基础身份信息", selectedCard == "id", onSelect)
|
||||
WalletCard("bank_main", "银行卡", "主银行卡", selectedCard == "bank_main", onSelect)
|
||||
repeat(extraBankCount) { index ->
|
||||
val id = "bank_extra_${index + 1}"
|
||||
WalletCard(id, "银行卡 ${index + 2}", "备用银行卡", selectedCard == id, onSelect)
|
||||
}
|
||||
MyButton(text = "新增银行卡", modifier = M.fillMaxWidth()) {
|
||||
onAddBank()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletCard(
|
||||
id: String,
|
||||
title: String,
|
||||
subTitle: String,
|
||||
selected: Boolean,
|
||||
onSelect: (String) -> Unit
|
||||
) {
|
||||
MyCard(
|
||||
radius = 8.dp,
|
||||
elevation = if (selected) 1.5.dp else 0.3.dp,
|
||||
modifier = M.fillMaxWidth().clickable { onSelect(id) }
|
||||
) {
|
||||
Column(M.fillMaxWidth().padding(14.dp)) {
|
||||
Text(title, fontWeight = FontWeight.Bold)
|
||||
Text(subTitle, fontSize = MaterialTheme.typography.bodySmall.fontSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FarmerCardEditor(
|
||||
selectedCard: String,
|
||||
addUserViewModel: AddUserViewModel,
|
||||
modifier: Modifier = M
|
||||
) {
|
||||
Column(modifier, verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||
when {
|
||||
selectedCard == "id" -> AddUserIdentityEditor(addUserViewModel)
|
||||
selectedCard == "bank_main" -> AddUserBankEditor(addUserViewModel, true, "主银行卡")
|
||||
selectedCard.startsWith("bank_extra_") -> AddUserBankEditor(addUserViewModel, false, "备用银行卡")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,6 +404,134 @@ fun AddUSerExtendInfo(addUserViewModel: AddUserViewModel) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddUserIdentityEditor(addUserViewModel: AddUserViewModel) {
|
||||
val userTypeList by addUserViewModel.userTypeList.collectAsState()
|
||||
val userLabelList by addUserViewModel.userLabelList.collectAsState()
|
||||
val xianList by addUserViewModel.xianList.collectAsState()
|
||||
val xiangList by addUserViewModel.xiangList.collectAsState()
|
||||
val cunList by addUserViewModel.cunList.collectAsState()
|
||||
val zuList by addUserViewModel.zuList.collectAsState()
|
||||
|
||||
InputFrame("基本信息") {
|
||||
MyTextField(modifier = M.weight(1f), hint = "姓名", value = addUserViewModel.idName) {
|
||||
addUserViewModel.idName = it
|
||||
}
|
||||
CombinedDropdownMenu(M.weight(1f), listOf("男", "女"), "性别", addUserViewModel.idGender) {
|
||||
addUserViewModel.idGender = it
|
||||
}
|
||||
CombinedDropdownMenu(M.weight(1f), userTypeList.map { it.name }, "农户类别", addUserViewModel.userType) {
|
||||
addUserViewModel.userType = it
|
||||
}
|
||||
}
|
||||
InputFrame("农户标签") {
|
||||
SelectableChipGroup(M.weight(1f), userLabelList) {
|
||||
addUserViewModel.switchUserLabel(it)
|
||||
}
|
||||
}
|
||||
InputFrame("手机号码") {
|
||||
MyTextField(
|
||||
modifier = M.weight(1f),
|
||||
hint = "请输入",
|
||||
value = addUserViewModel.userPhone,
|
||||
isNumberInputType = true,
|
||||
) {
|
||||
addUserViewModel.userPhone = it
|
||||
}
|
||||
}
|
||||
InputFrame("身份证号") {
|
||||
MyTextField(
|
||||
modifier = M.weight(1f),
|
||||
hint = "请输入",
|
||||
value = addUserViewModel.idCardNumber,
|
||||
isNumberInputType = true,
|
||||
) {
|
||||
addUserViewModel.idCardNumber = it
|
||||
}
|
||||
VipBadge {
|
||||
MyButton(text = "拍照识别") {
|
||||
addUserViewModel.recognizeIdCard({ name, gender, idCard, address ->
|
||||
addUserViewModel.idName = name
|
||||
addUserViewModel.idGender = gender
|
||||
addUserViewModel.idCardNumber = idCard
|
||||
addUserViewModel.idCardAddress = address
|
||||
}) { xian, xiang, cun, zu ->
|
||||
addUserViewModel.userXian = xian
|
||||
addUserViewModel.userXiang = xiang
|
||||
addUserViewModel.userCun = cun
|
||||
addUserViewModel.userZu = zu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
InputFrame("身份证地址") {
|
||||
MyTextField(
|
||||
modifier = M.weight(1f),
|
||||
hint = "请输入",
|
||||
readOnly = true,
|
||||
value = addUserViewModel.idCardAddress
|
||||
)
|
||||
}
|
||||
InputFrame("地址") {
|
||||
CombinedDropdownMenu(M.weight(1f), xianList, "县", addUserViewModel.userXian, true) {
|
||||
addUserViewModel.userXian = it
|
||||
addUserViewModel.onAddressSelected()
|
||||
}
|
||||
CombinedDropdownMenu(M.weight(1f), xiangList, "乡", addUserViewModel.userXiang, true) {
|
||||
addUserViewModel.userXiang = it
|
||||
addUserViewModel.onAddressSelected()
|
||||
}
|
||||
CombinedDropdownMenu(M.weight(1f), cunList, "村", addUserViewModel.userCun, true) {
|
||||
addUserViewModel.userCun = it
|
||||
addUserViewModel.onAddressSelected()
|
||||
}
|
||||
CombinedDropdownMenu(M.weight(1f), zuList, "组", addUserViewModel.userZu, true) {
|
||||
addUserViewModel.userZu = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddUserBankEditor(
|
||||
addUserViewModel: AddUserViewModel,
|
||||
isMainCard: Boolean,
|
||||
title: String
|
||||
) {
|
||||
val cardInfo by if (isMainCard) {
|
||||
addUserViewModel.mainCardInfo.collectAsState()
|
||||
} else {
|
||||
addUserViewModel.subCardInfo.collectAsState()
|
||||
}
|
||||
InputFrame(title) {
|
||||
MyTextField(
|
||||
modifier = M.weight(1f),
|
||||
hint = "请输入",
|
||||
value = cardInfo.first,
|
||||
isNumberInputType = true,
|
||||
) {
|
||||
addUserViewModel.clearBankCardInfo(isMainCard)
|
||||
addUserViewModel.updateBankCardCode(isMainCard, it)
|
||||
}
|
||||
VipBadge {
|
||||
MyButton(text = "拍照识别") {
|
||||
addUserViewModel.recognizeBankCard(isMainCard)
|
||||
}
|
||||
}
|
||||
}
|
||||
InputFrame("开户银行") {
|
||||
MyTextField(
|
||||
modifier = M.weight(1f),
|
||||
hint = "请输入",
|
||||
value = cardInfo.second.bankName,
|
||||
readOnly = true,
|
||||
isNumberInputType = true,
|
||||
)
|
||||
MyButton(text = "查询归属行") {
|
||||
addUserViewModel.analysisBankCard(cardInfo.first, isMainCard)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddUserLeftColumn(
|
||||
modifier: Modifier = M,
|
||||
@@ -478,7 +711,7 @@ fun AddUserRightColumn(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InputFrame(title: String, content: @Composable () -> Unit) {
|
||||
fun InputFrame(title: String, content: @Composable RowScope.() -> Unit) {
|
||||
Column(modifier = M.padding(top = 5.dp)) {
|
||||
Text(text = title, fontSize = MaterialTheme.typography.headlineMedium.fontSize, fontWeight = FontWeight.Bold, modifier = M.padding(vertical = 7.5.dp))
|
||||
Row(
|
||||
|
||||
@@ -99,11 +99,12 @@ class AddUserViewModel : BaseViewModel() {
|
||||
doInIoThreadNoDialog {
|
||||
// 更新四级联动数据
|
||||
_xianList.value = _address.map { it.first }
|
||||
_xiangList.value = _address.first { it.first == xian }.second.map { it.first }
|
||||
_cunList.value =
|
||||
_address.first { it.first == xian }.second.first { it.first == xiang }.second.map { it.first }
|
||||
_zuList.value =
|
||||
_address.first { it.first == xian }.second.first { it.first == xiang }.second.first { it.first == cun }.second
|
||||
val xianNode = _address.firstOrNull { it.first == xian }
|
||||
_xiangList.value = xianNode?.second?.map { it.first } ?: emptyList()
|
||||
val xiangNode = xianNode?.second?.firstOrNull { it.first == xiang }
|
||||
_cunList.value = xiangNode?.second?.map { it.first } ?: emptyList()
|
||||
val cunNode = xiangNode?.second?.firstOrNull { it.first == cun }
|
||||
_zuList.value = cunNode?.second ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ fun PayByElectronic(payViewModel: PayViewModel) {
|
||||
MyTextField(value = input, isNumberInputType = true, modifier = M.width(200.dp)) {
|
||||
input = it
|
||||
}
|
||||
MyButton(modifier = M.padding(horizontal = 10.dp), text = "电子支付") { }
|
||||
MyButton(modifier = M.padding(horizontal = 10.dp), text = "电子支付", enabled = false) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ fun PayByCash(payViewModel: PayViewModel) {
|
||||
MyTextField(value = input, isNumberInputType = true, modifier = M.width(200.dp)) {
|
||||
input = it
|
||||
}
|
||||
MyButton(modifier = M.padding(horizontal = 10.dp), text = "现金支付") { }
|
||||
MyButton(modifier = M.padding(horizontal = 10.dp), text = "现金支付", enabled = false) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +208,6 @@ fun PayByMix(payViewModel: PayViewModel) {
|
||||
MyTextField(value = input2, isNumberInputType = true, modifier = M.width(200.dp)) {
|
||||
input2 = it
|
||||
}
|
||||
MyButton(modifier = M.padding(horizontal = 10.dp), text = "开始支付") { }
|
||||
MyButton(modifier = M.padding(horizontal = 10.dp), text = "开始支付", enabled = false) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ import com.bbitcn.f8.pad.ui.screen.dialog.SplitDialog
|
||||
import com.bbitcn.f8.pad.ui.screen.dialog.WaterCutRecordDialog
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.bbitcn.f8.pad.ui.screen.view.deviceManager.scale.MyWeightShow
|
||||
import com.bbitcn.f8.pad.utils.WeightUnitFormatter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||
@@ -231,7 +232,7 @@ fun WeightList(weightViewModel: WeightViewModel) {
|
||||
)
|
||||
Text(
|
||||
color = MyColors.Gray,
|
||||
text = "单位公斤、元、元/公斤,支持长按删除",
|
||||
text = "单位${WeightUnitFormatter.unitLabel}、元、${WeightUnitFormatter.priceUnitLabel},支持长按删除",
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
}
|
||||
@@ -267,13 +268,13 @@ fun WeightList(weightViewModel: WeightViewModel) {
|
||||
item.categoryName to 1,
|
||||
item.packaging to 1,
|
||||
item.basketCount.toString() to 1,
|
||||
item.grossWeight.toString() to 1,
|
||||
item.tareWeight.toString() to 1,
|
||||
item.netWeight.toString() to 1,
|
||||
item.unitPrice.toString() to 1,
|
||||
WeightUnitFormatter.formatWeight(item.grossWeight) to 1,
|
||||
WeightUnitFormatter.formatWeight(item.tareWeight) to 1,
|
||||
WeightUnitFormatter.formatWeight(item.netWeight) to 1,
|
||||
WeightUnitFormatter.formatPrice(item.unitPrice) to 1,
|
||||
item.time to 2
|
||||
),
|
||||
verticalPadding = 15.dp,
|
||||
verticalPadding = 20.dp,
|
||||
onLongClick = {
|
||||
Toasty.showConfirmDialog("是否删除第${item.weighingTimes}磅次记录?") {
|
||||
weightViewModel.deleteDetail(item.sysid)
|
||||
@@ -304,10 +305,10 @@ fun WeightList(weightViewModel: WeightViewModel) {
|
||||
listOf(
|
||||
it.key,
|
||||
sumBasketCount,
|
||||
sumGrossWeight,
|
||||
sumTareWeight,
|
||||
sumNetWeight,
|
||||
sumUnitPrice
|
||||
WeightUnitFormatter.formatWeight(sumGrossWeight),
|
||||
WeightUnitFormatter.formatWeight(sumTareWeight),
|
||||
WeightUnitFormatter.formatWeight(sumNetWeight),
|
||||
WeightUnitFormatter.formatPrice(sumUnitPrice)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -464,14 +465,12 @@ fun WeightRight(navController: NavController, weightViewModel: WeightViewModel)
|
||||
horizontalArrangement = Arrangement.spacedBy(20.dp),
|
||||
) {
|
||||
VipBadge(modifier = M.weight(1f)) {
|
||||
SaveButton(title = "按重量\n拆分茧别") {
|
||||
Toasty.showToast("VIP功能,暂未开放")
|
||||
SaveButton(title = "按重量\n拆分茧别", enabled = false) {
|
||||
// weightViewModel.showSplitDialog(true)
|
||||
}
|
||||
}
|
||||
VipBadge(modifier = M.weight(1f)) {
|
||||
SaveButton(title = "按比例\n拆分茧别") {
|
||||
Toasty.showToast("VIP功能,暂未开放")
|
||||
SaveButton(title = "按比例\n拆分茧别", enabled = false) {
|
||||
// weightViewModel.showSplitDialog(false)
|
||||
}
|
||||
}
|
||||
@@ -535,7 +534,7 @@ fun AddKindsButton(
|
||||
)
|
||||
Text(
|
||||
modifier = M.padding(top = 5.dp),
|
||||
text = "${info.minPrice}-${info.maxPrice}元/公斤",
|
||||
text = "${WeightUnitFormatter.formatPrice(info.minPrice)}-${WeightUnitFormatter.formatPrice(info.maxPrice)}${WeightUnitFormatter.priceUnitLabel}",
|
||||
color = MyColors.Black,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
@@ -545,15 +544,21 @@ fun AddKindsButton(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SaveButton(modifier: Modifier = M, title: String, onClick: () -> Unit) {
|
||||
MyCard(colors = MyColors.BlueGreen, modifier = modifier) {
|
||||
Box(modifier = M.clickable { onClick() }) {
|
||||
fun SaveButton(modifier: Modifier = M, title: String, enabled: Boolean = true, onClick: () -> Unit) {
|
||||
MyCard(colors = if (enabled) MyColors.BlueGreen else MyColors.Disabled, modifier = modifier) {
|
||||
Box(modifier = M.clickable {
|
||||
if (enabled) {
|
||||
onClick()
|
||||
} else {
|
||||
Toasty.showToast("正在开发中,敬请期待")
|
||||
}
|
||||
}) {
|
||||
Text(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp),
|
||||
.padding(vertical = 16.dp, horizontal = 10.dp),
|
||||
text = title,
|
||||
color = MyColors.White,
|
||||
color = if (enabled) MyColors.White else MyColors.Gray,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center
|
||||
|
||||
@@ -51,6 +51,8 @@ object Toasty : BaseViewModel() {
|
||||
val confirmDialog = _confirmDialog.asStateFlow()
|
||||
private val _inputDialog = MutableStateFlow(InputDialogData())
|
||||
val inputDialog = _inputDialog.asStateFlow()
|
||||
private val _globalDialog = MutableStateFlow(GlobalDialogData())
|
||||
val globalDialog = _globalDialog.asStateFlow()
|
||||
|
||||
// 控制 Drawer 状态的 Boolean Flow
|
||||
private val _isDrawerOpen = MutableStateFlow(false)
|
||||
@@ -233,6 +235,17 @@ object Toasty : BaseViewModel() {
|
||||
_confirmDialog.value = ConfirmDialogData(showDialog = false)
|
||||
}
|
||||
|
||||
fun showGlobalDialog(data: GlobalDialogData) {
|
||||
_globalDialog.value = data.copy(showDialog = true)
|
||||
}
|
||||
|
||||
fun hideGlobalDialog(ownerKey: Any? = null) {
|
||||
val current = _globalDialog.value
|
||||
if (ownerKey == null || current.ownerKey == ownerKey) {
|
||||
_globalDialog.value = GlobalDialogData(showDialog = false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录过期事件
|
||||
*/
|
||||
@@ -254,3 +267,13 @@ object Toasty : BaseViewModel() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class GlobalDialogData(
|
||||
val ownerKey: Any? = null,
|
||||
val title: String = "",
|
||||
val showDialog: Boolean = false,
|
||||
val onDismissRequest: () -> Unit = {},
|
||||
val clickOKStr: String = "",
|
||||
val onClickOK: () -> Unit = {},
|
||||
val content: @Composable () -> Unit = {}
|
||||
)
|
||||
|
||||
@@ -17,6 +17,7 @@ object MyColors {
|
||||
val Orange = Color(0xFFFFAE00)
|
||||
val LightGray = Color(0xCCEEEEEE)
|
||||
val Gray = Color(0xFF999999)
|
||||
val Disabled = Color(0xFFE5E7EB)
|
||||
val BlueGreen = Color(0xFF2FBAA3)
|
||||
val LightBlueGreen = Color(0x802FBAA3)
|
||||
val LightLightBlueGreen = Color(0xFFF7FFFE)
|
||||
|
||||
@@ -33,6 +33,7 @@ class PollingTask private constructor() {
|
||||
* @param task 需要执行的任务
|
||||
*/
|
||||
fun startDelayedTask(taskId: String, delaySeconds: Long, task: Runnable) {
|
||||
ensureScheduler()
|
||||
stopTask(taskId) // 如果已有相同ID的任务,先停止
|
||||
|
||||
val future = scheduler!!.schedule({
|
||||
@@ -99,6 +100,7 @@ class PollingTask private constructor() {
|
||||
* @param task 需要执行的任务
|
||||
*/
|
||||
private fun startPollingTask(taskId: String, intervalSeconds: Long, task: Runnable) {
|
||||
ensureScheduler()
|
||||
stopTask(taskId) // 如果已有相同ID的任务,先停止
|
||||
val future = scheduler!!.scheduleWithFixedDelay(task, 0, intervalSeconds, TimeUnit.SECONDS)
|
||||
|
||||
@@ -132,6 +134,12 @@ class PollingTask private constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureScheduler() {
|
||||
if (scheduler == null || scheduler!!.isShutdown || scheduler!!.isTerminated) {
|
||||
createNewScheduler()
|
||||
}
|
||||
}
|
||||
|
||||
// 定义IO任务接口
|
||||
interface IRxIOTask<T> {
|
||||
fun doInIOThread(): T
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.bbitcn.f8.pad.utils
|
||||
|
||||
object WeightUnitFormatter {
|
||||
private const val KEY = "GLOBAL_WEIGHT_UNIT"
|
||||
const val UNIT_KG = "公斤"
|
||||
const val UNIT_JIN = "斤"
|
||||
|
||||
var displayUnit: String
|
||||
get() = MMKVUtil.get(KEY, UNIT_KG)
|
||||
set(value) {
|
||||
MMKVUtil.put(KEY, if (value == UNIT_JIN) UNIT_JIN else UNIT_KG)
|
||||
}
|
||||
|
||||
val unitLabel: String
|
||||
get() = displayUnit
|
||||
|
||||
val priceUnitLabel: String
|
||||
get() = "元/$unitLabel"
|
||||
|
||||
fun weight(kg: Double): Double = if (displayUnit == UNIT_JIN) kg * 2 else kg
|
||||
|
||||
fun price(pricePerKg: Double): Double = if (displayUnit == UNIT_JIN) pricePerKg / 2 else pricePerKg
|
||||
|
||||
fun formatWeight(kg: Double): String = trim(weight(kg))
|
||||
|
||||
fun formatPrice(pricePerKg: Double): String = trim(price(pricePerKg))
|
||||
|
||||
private fun trim(value: Double): String {
|
||||
return if (value % 1.0 == 0.0) {
|
||||
value.toInt().toString()
|
||||
} else {
|
||||
"%.2f".format(value).trimEnd('0').trimEnd('.')
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -15,10 +15,14 @@ import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.bbitcn.f8.pad.utils.ConverterUtil
|
||||
import com.bbitcn.f8.pad.utils.externalModules.manager.serial.uhfSerial.UHFReaderForSerial
|
||||
import com.bbitcn.f8.pad.utils.log.MyLog
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.UUID
|
||||
import kotlin.experimental.xor
|
||||
@@ -28,6 +32,7 @@ object UHFReaderG06M_G25M : UHFReaderForSerial() {
|
||||
private var mRcpBase: RcpBase? = null
|
||||
private var mSioBase: SioCom? = null
|
||||
private var onCommListener: OnCommListener? = null
|
||||
private val protocolScope = CoroutineScope(SupervisorJob() + Dispatchers.IO.limitedParallelism(1))
|
||||
|
||||
private var mBattaryLevel = 0
|
||||
|
||||
@@ -128,7 +133,7 @@ object UHFReaderG06M_G25M : UHFReaderForSerial() {
|
||||
Command(RcpMM.RCP_MM_PARA, RcpBase.RCP_MSG_GET)
|
||||
|
||||
mRcpBase?.setOnProtocolListener({ obj, protocolEventArg ->
|
||||
doInIoThreadNoDialog {
|
||||
protocolScope.launch {
|
||||
val psData = protocolEventArg.protocolPacket
|
||||
when (psData.Code) {
|
||||
RcpMM.RCP_MM_READ_C_UII ->
|
||||
|
||||
@@ -45,7 +45,13 @@ object RetrofitClient {
|
||||
try {
|
||||
val response = chain.proceed(chain.request())
|
||||
|
||||
if (response.code !in listOf(200, 401)) {
|
||||
if (response.code == 401) {
|
||||
Toasty.loginExpired()
|
||||
Toasty.error("登录已过期")
|
||||
return@Interceptor response
|
||||
}
|
||||
|
||||
if (response.code != 200) {
|
||||
val content = response.peekBody(ERROR_BODY_PEEK_BYTES).string()
|
||||
runCatching {
|
||||
Gson().fromJson(content, CommonResponse::class.java)
|
||||
|
||||
@@ -8,9 +8,13 @@ import com.bbitcn.f8.pad.utils.global.RxTag
|
||||
import com.bbitcn.f8.pad.utils.log.MyLog
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.Authenticator
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.Route
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* 401 token refresh interceptor.
|
||||
@@ -18,6 +22,20 @@ import okhttp3.Route
|
||||
class TokenAuthenticator : Authenticator {
|
||||
|
||||
private val lock = Any()
|
||||
private val refreshApi: ApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(RetrofitClient.BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(
|
||||
OkHttpClient.Builder()
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(20, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
.create(ApiService::class.java)
|
||||
}
|
||||
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
MyLog.network("TokenAuthenticator: authenticate")
|
||||
@@ -45,7 +63,7 @@ class TokenAuthenticator : Authenticator {
|
||||
|
||||
val newToken = runBlocking {
|
||||
MyLog.network("刷新Token: $currentToken, $refreshToken")
|
||||
val result = RetrofitClient.apiInterface().refreshToken(
|
||||
val result = refreshApi.refreshToken(
|
||||
RefreshTokenRequest(
|
||||
hardwareid = Global.getDeviceId(),
|
||||
refToken = refreshToken,
|
||||
|
||||
Reference in New Issue
Block a user