优化性能问题;完成需求;重构弹窗
This commit is contained in:
@@ -21,6 +21,10 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -34,6 +38,8 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import com.bbitcn.f8.pad.M
|
import com.bbitcn.f8.pad.M
|
||||||
import com.bbitcn.f8.pad.MyApp
|
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
|
import com.blankj.utilcode.util.ActivityUtils
|
||||||
|
|
||||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||||
@@ -68,7 +74,44 @@ fun MyDialog(
|
|||||||
onClickOK: () -> Unit = {},
|
onClickOK: () -> Unit = {},
|
||||||
content: @Composable () -> 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(
|
Box(
|
||||||
modifier = M
|
modifier = M
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -78,8 +121,8 @@ fun MyDialog(
|
|||||||
},
|
},
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
BaseDialogFrame(title, {
|
BaseDialogFrame(data.title, {
|
||||||
content()
|
data.content()
|
||||||
}) {
|
}) {
|
||||||
Row(modifier = M.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
Row(modifier = M.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
BigButton(
|
BigButton(
|
||||||
@@ -87,13 +130,13 @@ fun MyDialog(
|
|||||||
.weight(1f)
|
.weight(1f)
|
||||||
.widthIn(max = 100.dp)
|
.widthIn(max = 100.dp)
|
||||||
) {
|
) {
|
||||||
onDismissRequest()
|
data.onDismissRequest()
|
||||||
}
|
}
|
||||||
// 判断是否有点击事件
|
// 判断是否有点击事件
|
||||||
if (clickOKStr != "") {
|
if (data.clickOKStr != "") {
|
||||||
Spacer(modifier = M.width(30.dp))
|
Spacer(modifier = M.width(30.dp))
|
||||||
BigButton(clickOKStr, modifier = M.weight(1f), true) {
|
BigButton(data.clickOKStr, modifier = M.weight(1f), true) {
|
||||||
onClickOK()
|
data.onClickOK()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +153,7 @@ fun BaseDialogFrame(
|
|||||||
) {
|
) {
|
||||||
MyCard(
|
MyCard(
|
||||||
modifier = M
|
modifier = M
|
||||||
.padding(top = 10.dp, bottom = 50.dp, start = 100.dp, end = 100.dp)
|
.padding(vertical = 40.dp, horizontal = 100.dp)
|
||||||
.noVisualFeedbackClickable { }
|
.noVisualFeedbackClickable { }
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
@@ -161,4 +204,4 @@ fun BaseDialogFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.net.SocketException
|
import java.net.SocketException
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
import retrofit2.HttpException
|
||||||
|
|
||||||
open class BaseViewModel : ViewModel() {
|
open class BaseViewModel : ViewModel() {
|
||||||
|
|
||||||
@@ -78,6 +79,11 @@ open class BaseViewModel : ViewModel() {
|
|||||||
MyLog.test("协程被取消:${exception.javaClass.simpleName},message=${exception.message}")
|
MyLog.test("协程被取消:${exception.javaClass.simpleName},message=${exception.message}")
|
||||||
return@onFailure
|
return@onFailure
|
||||||
}
|
}
|
||||||
|
if (exception is HttpException && exception.code() == 401) {
|
||||||
|
Toasty.loginExpired()
|
||||||
|
Toasty.error("登录已过期")
|
||||||
|
return@onFailure
|
||||||
|
}
|
||||||
// 其他异常继续处理
|
// 其他异常继续处理
|
||||||
exception.printStackTrace()
|
exception.printStackTrace()
|
||||||
onError(exception)
|
onError(exception)
|
||||||
@@ -208,6 +214,7 @@ open class BaseViewModel : ViewModel() {
|
|||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
|
pollingTask.stopAllTasks()
|
||||||
taskMap.values.forEach { it.cancel() }
|
taskMap.values.forEach { it.cancel() }
|
||||||
taskMap.clear()
|
taskMap.clear()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,13 +172,23 @@ fun MyButton(
|
|||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = {
|
||||||
|
if (enabled) {
|
||||||
|
onClick()
|
||||||
|
} else {
|
||||||
|
Toasty.showToast("正在开发中,敬请期待")
|
||||||
|
}
|
||||||
|
},
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled,
|
enabled = true,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
shape = shape,
|
shape = shape,
|
||||||
border = border,
|
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,
|
contentPadding = contentPadding,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import androidx.navigation.compose.NavHost
|
|||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.bbitcn.f8.pad.M
|
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.TopInfoViewModel
|
||||||
import com.bbitcn.f8.pad.ui.screen.LoginScreen
|
import com.bbitcn.f8.pad.ui.screen.LoginScreen
|
||||||
import com.bbitcn.f8.pad.ui.screen.MainScreen
|
import com.bbitcn.f8.pad.ui.screen.MainScreen
|
||||||
@@ -273,6 +274,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val loadingDialog by Toasty.loadingDialog.collectAsState()
|
val loadingDialog by Toasty.loadingDialog.collectAsState()
|
||||||
val confirmDialog by Toasty.confirmDialog.collectAsState()
|
val confirmDialog by Toasty.confirmDialog.collectAsState()
|
||||||
val inputDialog by Toasty.inputDialog.collectAsState()
|
val inputDialog by Toasty.inputDialog.collectAsState()
|
||||||
|
val globalDialog by Toasty.globalDialog.collectAsState()
|
||||||
TipsDialog(
|
TipsDialog(
|
||||||
showDialog = tipsDialog.showDialog,
|
showDialog = tipsDialog.showDialog,
|
||||||
onDismiss = { Toasty.hideTipsDialog() },
|
onDismiss = { Toasty.hideTipsDialog() },
|
||||||
@@ -281,6 +283,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
LoadingDialog(loadingDialog)
|
LoadingDialog(loadingDialog)
|
||||||
ConfirmDialog(confirmDialog)
|
ConfirmDialog(confirmDialog)
|
||||||
InputDialog(inputDialog)
|
InputDialog(inputDialog)
|
||||||
|
GlobalDialogHost(globalDialog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,4 +316,4 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import com.bbitcn.f8.pad.base.isBluetoothEnabled
|
|||||||
import com.bbitcn.f8.pad.receiver.SystemInfoReceiver
|
import com.bbitcn.f8.pad.receiver.SystemInfoReceiver
|
||||||
import com.bbitcn.f8.pad.ui.screen.view.drawer.DrawerViewModel
|
import com.bbitcn.f8.pad.ui.screen.view.drawer.DrawerViewModel
|
||||||
import com.bbitcn.f8.pad.ui.theme.MyColors
|
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.scale.ScaleBT
|
||||||
import com.bbitcn.f8.pad.utils.externalModules.devices.printer.PrinterBT
|
import com.bbitcn.f8.pad.utils.externalModules.devices.printer.PrinterBT
|
||||||
import com.bbitcn.f8.pad.utils.externalModules.devices.printer.JTPrinterUSB
|
import com.bbitcn.f8.pad.utils.externalModules.devices.printer.JTPrinterUSB
|
||||||
@@ -88,6 +89,7 @@ fun MyTopBar(
|
|||||||
val date by topInfoViewModel.date.collectAsState()
|
val date by topInfoViewModel.date.collectAsState()
|
||||||
val time by topInfoViewModel.time.collectAsState()
|
val time by topInfoViewModel.time.collectAsState()
|
||||||
val logoState by topInfoViewModel.logoState.collectAsState()
|
val logoState by topInfoViewModel.logoState.collectAsState()
|
||||||
|
val versionName = remember { MyUtil.getVersionName() }
|
||||||
|
|
||||||
DisposableEffect(context) {
|
DisposableEffect(context) {
|
||||||
systemInfoReceiver.register()
|
systemInfoReceiver.register()
|
||||||
@@ -151,13 +153,21 @@ fun MyTopBar(
|
|||||||
)
|
)
|
||||||
}, label = "animated content"
|
}, label = "animated content"
|
||||||
) { value ->
|
) { value ->
|
||||||
Text(
|
Row(verticalAlignment = Alignment.Bottom) {
|
||||||
text = value.first.second,
|
Text(
|
||||||
M.padding(horizontal = 8.dp),
|
text = value.first.second,
|
||||||
color = MyColors.White,
|
M.padding(horizontal = 8.dp),
|
||||||
fontWeight = FontWeight.Bold,
|
color = MyColors.White,
|
||||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize
|
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))
|
Spacer(modifier = M.weight(1f))
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.bbitcn.f8.pad.ui.screen.dialog
|
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.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
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.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.bbitcn.f8.pad.M
|
import com.bbitcn.f8.pad.M
|
||||||
@@ -46,6 +49,8 @@ data class MessageDialogData(
|
|||||||
val time: String = "",
|
val time: String = "",
|
||||||
val username: String = "",
|
val username: String = "",
|
||||||
val content: String = "",
|
val content: String = "",
|
||||||
|
val title: String = "",
|
||||||
|
val isWebUrl: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||||
@@ -63,7 +68,7 @@ fun MessagePreview() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageDialog(info: MessageDialogData) {
|
fun MessageDialog(info: MessageDialogData) {
|
||||||
MyDialog("查看消息",
|
MyDialog(info.title.ifEmpty { "查看消息" },
|
||||||
info.showDialog,
|
info.showDialog,
|
||||||
onDismissRequest = { info.onDismiss() }
|
onDismissRequest = { info.onDismiss() }
|
||||||
) {
|
) {
|
||||||
@@ -98,11 +103,29 @@ fun MessageDialog(info: MessageDialogData) {
|
|||||||
.padding(30.dp)
|
.padding(30.dp)
|
||||||
.verticalScroll(rememberScrollState()) // 添加滚动支持
|
.verticalScroll(rememberScrollState()) // 添加滚动支持
|
||||||
) {
|
) {
|
||||||
Text(
|
if (info.isWebUrl) {
|
||||||
text = info.content,
|
AndroidView(
|
||||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
|
modifier = M.fillMaxSize(),
|
||||||
fontWeight = FontWeight.Normal
|
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.request.CocoonTypeTranslateRequest
|
||||||
import com.bbitcn.f8.pad.model.net.response.PurchaseDataResponse
|
import com.bbitcn.f8.pad.model.net.response.PurchaseDataResponse
|
||||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
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 com.bbitcn.f8.pad.utils.MyUtil
|
||||||
import kotlin.collections.map
|
import kotlin.collections.map
|
||||||
|
|
||||||
@@ -157,13 +158,12 @@ fun TicketMoreDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
MyImageButton("茧别转换", R.drawable.ic_ticket_convert) {
|
MyImageButton("茧别转换", R.drawable.ic_ticket_convert, enabled = false) {
|
||||||
isTranslated = !isTranslated
|
isTranslated = !isTranslated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
MyImageButton("茧票过户", R.drawable.ic_ticket_transfer, true) {
|
MyImageButton("茧票过户", R.drawable.ic_ticket_transfer, vip = true, enabled = false) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
@@ -176,13 +176,11 @@ fun TicketMoreDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
MyImageButton("上传图片", R.drawable.ic_upload_pic, true) {
|
MyImageButton("上传图片", R.drawable.ic_upload_pic, vip = true, enabled = false) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
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
|
@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(
|
Column(
|
||||||
modifier = M
|
modifier = M
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(10.dp)
|
.padding(10.dp)
|
||||||
.clickable { onClick() },
|
.clickable {
|
||||||
|
if (enabled) {
|
||||||
|
onClick()
|
||||||
|
} else {
|
||||||
|
Toasty.showToast("正在开发中,敬请期待")
|
||||||
|
}
|
||||||
|
},
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
if (vip) {
|
if (vip) {
|
||||||
@@ -251,18 +255,21 @@ fun MyImageButton(text: String, resId: Int, vip: Boolean = false, onClick: () ->
|
|||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = resId),
|
painter = painterResource(id = resId),
|
||||||
contentDescription = text,
|
contentDescription = text,
|
||||||
modifier = M.size(90.dp)
|
modifier = M.size(90.dp),
|
||||||
|
alpha = if (enabled) 1f else 0.35f
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = resId),
|
painter = painterResource(id = resId),
|
||||||
contentDescription = text,
|
contentDescription = text,
|
||||||
modifier = M.size(90.dp)
|
modifier = M.size(90.dp),
|
||||||
|
alpha = if (enabled) 1f else 0.35f
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
|
color = if (enabled) MyColors.Black else MyColors.Gray,
|
||||||
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
|
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -153,15 +153,15 @@ fun FundsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = M.weight(1f))
|
Spacer(modifier = M.weight(1f))
|
||||||
MyButton(modifier = M.fillMaxWidth(), text = "查询配额") {
|
MyButton(modifier = M.fillMaxWidth(), text = "查询配额", enabled = false) {
|
||||||
fundsViewModel.showQueryBalanceDialog()
|
fundsViewModel.showQueryBalanceDialog()
|
||||||
}
|
}
|
||||||
VipBadge {
|
VipBadge {
|
||||||
MyButton(modifier = M.fillMaxWidth(), text = "查询余额") {
|
MyButton(modifier = M.fillMaxWidth(), text = "查询余额", enabled = false) {
|
||||||
fundsViewModel.showQueryBalanceDialog()
|
fundsViewModel.showQueryBalanceDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyButton(modifier = M.fillMaxWidth(), text = "修改支付密码") {
|
MyButton(modifier = M.fillMaxWidth(), text = "修改支付密码", enabled = false) {
|
||||||
Toasty.showToast("正在开发中,敬请期待!")
|
Toasty.showToast("正在开发中,敬请期待!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,6 +413,7 @@ fun WeatherItem(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageInfo(homeViewModel: HomeViewModel) {
|
fun MessageInfo(homeViewModel: HomeViewModel) {
|
||||||
|
val notices by homeViewModel.notices.collectAsState()
|
||||||
Box(modifier = M.padding(5.dp)) {
|
Box(modifier = M.padding(5.dp)) {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = R.drawable.bg_weather),
|
painter = painterResource(id = R.drawable.bg_weather),
|
||||||
@@ -429,14 +430,16 @@ fun MessageInfo(homeViewModel: HomeViewModel) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(count = 0) { index ->
|
items(count = notices.size) { index ->
|
||||||
|
val notice = notices[index]
|
||||||
MessageItem(
|
MessageItem(
|
||||||
homeViewModel,
|
homeViewModel,
|
||||||
true,
|
true,
|
||||||
true,
|
!homeViewModel.isNoticeRead(notice.id),
|
||||||
"管理员",
|
notice.sender,
|
||||||
"2024-08-02 11:25:32",
|
notice.time,
|
||||||
"正在开发中"
|
notice.title,
|
||||||
|
onClick = { homeViewModel.showNoticeDialog(notice) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,12 +454,13 @@ fun MessageItem(
|
|||||||
unRead: Boolean,
|
unRead: Boolean,
|
||||||
name: String,
|
name: String,
|
||||||
time: String,
|
time: String,
|
||||||
message: String
|
message: String,
|
||||||
|
onClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = M
|
modifier = M
|
||||||
.clickable {
|
.clickable {
|
||||||
homeViewModel.showMsgDialog(name, time, message)
|
onClick?.invoke() ?: homeViewModel.showMsgDialog(name, time, message)
|
||||||
}
|
}
|
||||||
.padding(bottom = 5.dp),
|
.padding(bottom = 5.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically) {
|
verticalAlignment = Alignment.CenterVertically) {
|
||||||
@@ -640,4 +644,4 @@ fun AcquireDynamic(homeViewModel: HomeViewModel) {
|
|||||||
verticalPadding = 7.5.dp
|
verticalPadding = 7.5.dp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ import java.util.Date
|
|||||||
|
|
||||||
class HomeViewModel : BaseViewModel() {
|
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())
|
private val _todayPrice = MutableStateFlow<List<TodayPriceResponse.Data>>(emptyList())
|
||||||
val todayPrice = _todayPrice.asStateFlow()
|
val todayPrice = _todayPrice.asStateFlow()
|
||||||
|
|
||||||
@@ -37,7 +45,11 @@ class HomeViewModel : BaseViewModel() {
|
|||||||
private val _messageDialogData = MutableStateFlow(MessageDialogData())
|
private val _messageDialogData = MutableStateFlow(MessageDialogData())
|
||||||
val messageDialogData: StateFlow<MessageDialogData> = _messageDialogData.asStateFlow()
|
val messageDialogData: StateFlow<MessageDialogData> = _messageDialogData.asStateFlow()
|
||||||
|
|
||||||
|
private val _notices = MutableStateFlow<List<Notice>>(emptyList())
|
||||||
|
val notices = _notices.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
loadMockNotices()
|
||||||
refreshWeatherInfo(false)
|
refreshWeatherInfo(false)
|
||||||
refreshTodayPrice(false)
|
refreshTodayPrice(false)
|
||||||
doInIoThreadNoDialog {
|
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(
|
fun refreshAcquireData(
|
||||||
startDate: Date = _dateRange2.value.first,
|
startDate: Date = _dateRange2.value.first,
|
||||||
endDate: Date = _dateRange2.value.second,
|
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) {
|
fun logout(onFinished: () -> Unit) {
|
||||||
// 退出登录
|
// 退出登录
|
||||||
doInIoThreadThenUI(loadingTips = "正在退出登录", onIO = {
|
doInIoThreadThenUI(loadingTips = "正在退出登录", onIO = {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ fun PurchaseScreen(
|
|||||||
var queryInput by rememberSaveable { mutableStateOf("") }
|
var queryInput by rememberSaveable { mutableStateOf("") }
|
||||||
// 筛选条件3:查询类型
|
// 筛选条件3:查询类型
|
||||||
var queryType by rememberSaveable { mutableStateOf(0) }
|
var queryType by rememberSaveable { mutableStateOf(0) }
|
||||||
|
var queryInputInitialized by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
val info = purchaseViewModel.infoPager.collectAsLazyPagingItems()
|
val info = purchaseViewModel.infoPager.collectAsLazyPagingItems()
|
||||||
// 查询方法
|
// 查询方法
|
||||||
@@ -131,6 +132,10 @@ fun PurchaseScreen(
|
|||||||
info.refresh()
|
info.refresh()
|
||||||
}
|
}
|
||||||
LaunchedEffect(queryInput) {
|
LaunchedEffect(queryInput) {
|
||||||
|
if (!queryInputInitialized) {
|
||||||
|
queryInputInitialized = true
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
delay(350)
|
delay(350)
|
||||||
updateParams()
|
updateParams()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ class PurchaseViewModel : BaseViewModel() {
|
|||||||
init {
|
init {
|
||||||
doInIoThreadNoDialog {
|
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))
|
Spacer(modifier = M.weight(1f))
|
||||||
VipBadge {
|
VipBadge {
|
||||||
MyButton(modifier = M.fillMaxWidth(), text = "发送短信") {
|
MyButton(modifier = M.fillMaxWidth(), text = "发送短信", enabled = false) {
|
||||||
Toasty.showToast("正在开发中,敬请期待")
|
Toasty.showToast("正在开发中,敬请期待")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,50 @@
|
|||||||
package com.bbitcn.f8.pad.ui.screen.mainFunc
|
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.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.detectDragGestures
|
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.verticalScroll
|
||||||
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.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.bbitcn.f8.pad.M
|
|
||||||
import com.bbitcn.f8.pad.base.MainFuncFrame
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
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.AssistChipFilter
|
||||||
import com.bbitcn.f8.pad.base.InfoText
|
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.MyButton
|
||||||
import com.bbitcn.f8.pad.base.MyCard
|
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.MyInfoCard
|
||||||
import com.bbitcn.f8.pad.base.MyRefreshTable
|
import com.bbitcn.f8.pad.base.MyRefreshTable
|
||||||
import com.bbitcn.f8.pad.base.MyTableData
|
import com.bbitcn.f8.pad.base.MyTableData
|
||||||
import com.bbitcn.f8.pad.base.QueryTextField
|
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.model.net.response.UserDataResponse
|
||||||
import com.bbitcn.f8.pad.ui.theme.MyColors
|
import com.bbitcn.f8.pad.ui.theme.MyColors
|
||||||
import com.bbitcn.f8.pad.utils.MyUtil
|
|
||||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
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)
|
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||||
@Composable
|
@Composable
|
||||||
fun UserScreenPV() {
|
fun UserScreenPV() {
|
||||||
@@ -83,339 +56,14 @@ fun UserScreen(
|
|||||||
navController: NavController,
|
navController: NavController,
|
||||||
userViewModel: UserViewModel = viewModel()
|
userViewModel: UserViewModel = viewModel()
|
||||||
) {
|
) {
|
||||||
val treeData by userViewModel.treeData.collectAsState()
|
|
||||||
val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems()
|
val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems()
|
||||||
MainFuncFrame {
|
MainFuncFrame {
|
||||||
if (isLandscape()) {
|
MyInfoCard(modifier = M.fillMaxSize()) {
|
||||||
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)
|
|
||||||
) {
|
|
||||||
UserManageList(navController, userViewModel, userData)
|
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
|
@Composable
|
||||||
fun UserManageList(
|
fun UserManageList(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
@@ -424,6 +72,7 @@ fun UserManageList(
|
|||||||
) {
|
) {
|
||||||
var queryInput by rememberSaveable { mutableStateOf("") }
|
var queryInput by rememberSaveable { mutableStateOf("") }
|
||||||
val areaFilter by userViewModel.areaFilter.collectAsState()
|
val areaFilter by userViewModel.areaFilter.collectAsState()
|
||||||
|
var detailUser by remember { mutableStateOf<UserDataResponse.Data?>(null) }
|
||||||
val onFilterLikeChanged: (String) -> Unit = {
|
val onFilterLikeChanged: (String) -> Unit = {
|
||||||
queryInput = it
|
queryInput = it
|
||||||
userViewModel.updateParamsLike(it)
|
userViewModel.updateParamsLike(it)
|
||||||
@@ -434,25 +83,14 @@ fun UserManageList(
|
|||||||
.padding(15.dp)
|
.padding(15.dp)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
Row(
|
UserFilterBar(
|
||||||
modifier = M
|
userViewModel = userViewModel,
|
||||||
.fillMaxWidth()
|
userData = userData,
|
||||||
.padding(bottom = 5.dp),
|
queryInput = queryInput,
|
||||||
horizontalArrangement = Arrangement.End,
|
onQueryChanged = onFilterLikeChanged,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
areaFilter = areaFilter,
|
||||||
) {
|
navController = navController
|
||||||
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 = {})
|
|
||||||
}
|
|
||||||
val myPager = userViewModel.usersInfoMyPager
|
val myPager = userViewModel.usersInfoMyPager
|
||||||
val isRefreshing by myPager.listIsRefreshing.collectAsState()
|
val isRefreshing by myPager.listIsRefreshing.collectAsState()
|
||||||
MyRefreshTable(
|
MyRefreshTable(
|
||||||
@@ -467,8 +105,8 @@ fun UserManageList(
|
|||||||
MyTableData(1, isIndex = true),
|
MyTableData(1, isIndex = true),
|
||||||
MyTableData("姓名", 1, { it.nhname }),
|
MyTableData("姓名", 1, { it.nhname }),
|
||||||
MyTableData("手机号", 2, { it.phone }),
|
MyTableData("手机号", 2, { it.phone }),
|
||||||
MyTableData("身份证", 1, { if (it.idcard != "") "✔️" else "" }),
|
MyTableData("身份证", 1, { if (it.idcard != "") "已录" else "" }),
|
||||||
MyTableData("银行卡", 1, { if (it.bankcode != "") "✔️" else "" }),
|
MyTableData("银行卡", 1, { if (it.bankcode != "") "已录" else "" }),
|
||||||
MyTableData("所属地址", 3, { "${it.xian}${it.xiang}${it.cun}${it.zu}" }),
|
MyTableData("所属地址", 3, { "${it.xian}${it.xiang}${it.cun}${it.zu}" }),
|
||||||
MyTableData("建档时间", 2, { TimeUtils.formatDateTimeStrToDateStr(it.createtime) }),
|
MyTableData("建档时间", 2, { TimeUtils.formatDateTimeStrToDateStr(it.createtime) }),
|
||||||
MyTableData("", 1, { "修改" }, true) {
|
MyTableData("", 1, { "修改" }, true) {
|
||||||
@@ -478,27 +116,8 @@ fun UserManageList(
|
|||||||
navController.navigate("weight/${it.sysid}")
|
navController.navigate("weight/${it.sysid}")
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onExpend = {
|
onClick = {
|
||||||
Column(modifier = M.fillMaxWidth().border(1.dp, MyColors.Gray).padding(15.dp)) {
|
detailUser = it
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
userViewModel.deleteUser(it.nhname, it.sysid) {
|
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 androidx.lifecycle.viewModelScope
|
||||||
import com.bbitcn.f8.pad.base.BaseViewModel
|
import com.bbitcn.f8.pad.base.BaseViewModel
|
||||||
import com.bbitcn.f8.pad.model.net.request.UserListDataRequest
|
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.ui.screen.view.Toasty
|
||||||
import com.bbitcn.f8.pad.utils.pager.MyPager
|
import com.bbitcn.f8.pad.utils.pager.MyPager
|
||||||
import com.bbitcn.f8.pad.utils.pager.UsersInfoPagingSource
|
import com.bbitcn.f8.pad.utils.pager.UsersInfoPagingSource
|
||||||
import com.blankj.utilcode.util.StringUtils
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|
||||||
class UserViewModel : BaseViewModel() {
|
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("")
|
private val _areaFilter = MutableStateFlow("")
|
||||||
val areaFilter = _areaFilter.asStateFlow()
|
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(
|
val usersInfoMyPager = MyPager(
|
||||||
pagingSourceFactory = { UsersInfoPagingSource(it) },
|
pagingSourceFactory = { UsersInfoPagingSource(it) },
|
||||||
initialRequestData = UserListDataRequest(), // 传入初始的请求数据
|
initialRequestData = UserListDataRequest(), // 传入初始的请求数据
|
||||||
@@ -33,7 +56,7 @@ class UserViewModel : BaseViewModel() {
|
|||||||
val usersInfoPager = usersInfoMyPager.createPager(viewModelScope)
|
val usersInfoPager = usersInfoMyPager.createPager(viewModelScope)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getUsersArea(false)
|
refreshAreaOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateParamsLike(like: String) {
|
fun updateParamsLike(like: String) {
|
||||||
@@ -51,6 +74,67 @@ class UserViewModel : BaseViewModel() {
|
|||||||
_areaFilter.value = buildAreaFilter(xian, xiang, cun, zu)
|
_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 {
|
fun buildAreaFilter(xian: String, xiang: String, cun: String, zu: String): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
append(xian)
|
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) {
|
fun deleteUser(name: String, sysid: String, onSuccess: () -> Unit) {
|
||||||
Toasty.showConfirmDialog("确定删除农户" + name + "吗?") {
|
Toasty.showConfirmDialog("确定删除农户" + name + "吗?") {
|
||||||
doInIoThread("正在删除农户...") {
|
doInIoThread("正在删除农户...") {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package com.bbitcn.f8.pad.ui.screen.secondFunc
|
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.animation.animateContentSize
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
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.foundation.lazy.items
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -74,29 +80,23 @@ fun AddUserScreen(
|
|||||||
addUserViewModel.initExtendData()
|
addUserViewModel.initExtendData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val scanDialogData by addUserViewModel.scanDialogData.collectAsState()
|
|
||||||
val ocrDialogData by addUserViewModel.ocrDialogData.collectAsState()
|
val ocrDialogData by addUserViewModel.ocrDialogData.collectAsState()
|
||||||
val faceDialogData by addUserViewModel.faceDialogData.collectAsState()
|
|
||||||
MainFuncFrame {
|
MainFuncFrame {
|
||||||
MyCard(modifier = M.fillMaxSize(), radius = 7.dp, elevation = 1.dp) {
|
MyCard(modifier = M.fillMaxSize(), radius = 7.dp, elevation = 1.dp) {
|
||||||
VerticalTabPages(
|
VerticalTabPages(
|
||||||
modifier = M
|
modifier = M
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.padding(vertical = 15.dp),
|
.padding(vertical = 15.dp),
|
||||||
tabs = listOf("基本信息", "人脸录入", "附件上传", "拓展信息"),
|
tabs = listOf("基本信息", "拓展信息"),
|
||||||
) {
|
) {
|
||||||
when (it) {
|
when (it) {
|
||||||
0 -> AddUserBaseInfo(addUserViewModel)
|
0 -> AddUserBaseInfo(addUserViewModel)
|
||||||
1 -> AddUserFaceInfo(addUserViewModel)
|
1 -> AddUSerExtendInfo(addUserViewModel)
|
||||||
2 -> AddUserAttachmentInfo(addUserViewModel)
|
|
||||||
3 -> AddUSerExtendInfo(addUserViewModel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScanDialog(scanDialogData)
|
|
||||||
OCRDialog(ocrDialogData)
|
OCRDialog(ocrDialogData)
|
||||||
FaceDialog(faceDialogData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -170,6 +170,12 @@ fun AddUserBaseInfo(
|
|||||||
addUserViewModel: AddUserViewModel
|
addUserViewModel: AddUserViewModel
|
||||||
) {
|
) {
|
||||||
val configuration = LocalConfiguration.current
|
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) {
|
if (configuration.orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT) {
|
||||||
// 竖屏模式
|
// 竖屏模式
|
||||||
@@ -182,10 +188,18 @@ fun AddUserBaseInfo(
|
|||||||
) {
|
) {
|
||||||
addUserViewModel.editFarmer()
|
addUserViewModel.editFarmer()
|
||||||
}
|
}
|
||||||
AddUserLeftColumn(
|
FarmerCardWallet(
|
||||||
addUserViewModel = addUserViewModel
|
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 {
|
} else {
|
||||||
@@ -198,12 +212,103 @@ fun AddUserBaseInfo(
|
|||||||
) {
|
) {
|
||||||
addUserViewModel.editFarmer()
|
addUserViewModel.editFarmer()
|
||||||
}
|
}
|
||||||
AddUserLeftColumn(
|
FarmerCardWallet(
|
||||||
modifier = M.weight(1f),
|
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
|
@Composable
|
||||||
fun AddUserLeftColumn(
|
fun AddUserLeftColumn(
|
||||||
modifier: Modifier = M,
|
modifier: Modifier = M,
|
||||||
@@ -478,7 +711,7 @@ fun AddUserRightColumn(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InputFrame(title: String, content: @Composable () -> Unit) {
|
fun InputFrame(title: String, content: @Composable RowScope.() -> Unit) {
|
||||||
Column(modifier = M.padding(top = 5.dp)) {
|
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))
|
Text(text = title, fontSize = MaterialTheme.typography.headlineMedium.fontSize, fontWeight = FontWeight.Bold, modifier = M.padding(vertical = 7.5.dp))
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@@ -99,11 +99,12 @@ class AddUserViewModel : BaseViewModel() {
|
|||||||
doInIoThreadNoDialog {
|
doInIoThreadNoDialog {
|
||||||
// 更新四级联动数据
|
// 更新四级联动数据
|
||||||
_xianList.value = _address.map { it.first }
|
_xianList.value = _address.map { it.first }
|
||||||
_xiangList.value = _address.first { it.first == xian }.second.map { it.first }
|
val xianNode = _address.firstOrNull { it.first == xian }
|
||||||
_cunList.value =
|
_xiangList.value = xianNode?.second?.map { it.first } ?: emptyList()
|
||||||
_address.first { it.first == xian }.second.first { it.first == xiang }.second.map { it.first }
|
val xiangNode = xianNode?.second?.firstOrNull { it.first == xiang }
|
||||||
_zuList.value =
|
_cunList.value = xiangNode?.second?.map { it.first } ?: emptyList()
|
||||||
_address.first { it.first == xian }.second.first { it.first == xiang }.second.first { it.first == cun }.second
|
val cunNode = xiangNode?.second?.firstOrNull { it.first == cun }
|
||||||
|
_zuList.value = cunNode?.second ?: emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,4 +501,4 @@ class AddUserViewModel : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ fun PayByElectronic(payViewModel: PayViewModel) {
|
|||||||
MyTextField(value = input, isNumberInputType = true, modifier = M.width(200.dp)) {
|
MyTextField(value = input, isNumberInputType = true, modifier = M.width(200.dp)) {
|
||||||
input = it
|
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)) {
|
MyTextField(value = input, isNumberInputType = true, modifier = M.width(200.dp)) {
|
||||||
input = it
|
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)) {
|
MyTextField(value = input2, isNumberInputType = true, modifier = M.width(200.dp)) {
|
||||||
input2 = it
|
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.dialog.WaterCutRecordDialog
|
||||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
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.ui.screen.view.deviceManager.scale.MyWeightShow
|
||||||
|
import com.bbitcn.f8.pad.utils.WeightUnitFormatter
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||||
@@ -231,7 +232,7 @@ fun WeightList(weightViewModel: WeightViewModel) {
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
color = MyColors.Gray,
|
color = MyColors.Gray,
|
||||||
text = "单位公斤、元、元/公斤,支持长按删除",
|
text = "单位${WeightUnitFormatter.unitLabel}、元、${WeightUnitFormatter.priceUnitLabel},支持长按删除",
|
||||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -267,13 +268,13 @@ fun WeightList(weightViewModel: WeightViewModel) {
|
|||||||
item.categoryName to 1,
|
item.categoryName to 1,
|
||||||
item.packaging to 1,
|
item.packaging to 1,
|
||||||
item.basketCount.toString() to 1,
|
item.basketCount.toString() to 1,
|
||||||
item.grossWeight.toString() to 1,
|
WeightUnitFormatter.formatWeight(item.grossWeight) to 1,
|
||||||
item.tareWeight.toString() to 1,
|
WeightUnitFormatter.formatWeight(item.tareWeight) to 1,
|
||||||
item.netWeight.toString() to 1,
|
WeightUnitFormatter.formatWeight(item.netWeight) to 1,
|
||||||
item.unitPrice.toString() to 1,
|
WeightUnitFormatter.formatPrice(item.unitPrice) to 1,
|
||||||
item.time to 2
|
item.time to 2
|
||||||
),
|
),
|
||||||
verticalPadding = 15.dp,
|
verticalPadding = 20.dp,
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
Toasty.showConfirmDialog("是否删除第${item.weighingTimes}磅次记录?") {
|
Toasty.showConfirmDialog("是否删除第${item.weighingTimes}磅次记录?") {
|
||||||
weightViewModel.deleteDetail(item.sysid)
|
weightViewModel.deleteDetail(item.sysid)
|
||||||
@@ -304,10 +305,10 @@ fun WeightList(weightViewModel: WeightViewModel) {
|
|||||||
listOf(
|
listOf(
|
||||||
it.key,
|
it.key,
|
||||||
sumBasketCount,
|
sumBasketCount,
|
||||||
sumGrossWeight,
|
WeightUnitFormatter.formatWeight(sumGrossWeight),
|
||||||
sumTareWeight,
|
WeightUnitFormatter.formatWeight(sumTareWeight),
|
||||||
sumNetWeight,
|
WeightUnitFormatter.formatWeight(sumNetWeight),
|
||||||
sumUnitPrice
|
WeightUnitFormatter.formatPrice(sumUnitPrice)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -464,14 +465,12 @@ fun WeightRight(navController: NavController, weightViewModel: WeightViewModel)
|
|||||||
horizontalArrangement = Arrangement.spacedBy(20.dp),
|
horizontalArrangement = Arrangement.spacedBy(20.dp),
|
||||||
) {
|
) {
|
||||||
VipBadge(modifier = M.weight(1f)) {
|
VipBadge(modifier = M.weight(1f)) {
|
||||||
SaveButton(title = "按重量\n拆分茧别") {
|
SaveButton(title = "按重量\n拆分茧别", enabled = false) {
|
||||||
Toasty.showToast("VIP功能,暂未开放")
|
|
||||||
// weightViewModel.showSplitDialog(true)
|
// weightViewModel.showSplitDialog(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VipBadge(modifier = M.weight(1f)) {
|
VipBadge(modifier = M.weight(1f)) {
|
||||||
SaveButton(title = "按比例\n拆分茧别") {
|
SaveButton(title = "按比例\n拆分茧别", enabled = false) {
|
||||||
Toasty.showToast("VIP功能,暂未开放")
|
|
||||||
// weightViewModel.showSplitDialog(false)
|
// weightViewModel.showSplitDialog(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,7 +534,7 @@ fun AddKindsButton(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = M.padding(top = 5.dp),
|
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,
|
color = MyColors.Black,
|
||||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||||
)
|
)
|
||||||
@@ -545,15 +544,21 @@ fun AddKindsButton(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SaveButton(modifier: Modifier = M, title: String, onClick: () -> Unit) {
|
fun SaveButton(modifier: Modifier = M, title: String, enabled: Boolean = true, onClick: () -> Unit) {
|
||||||
MyCard(colors = MyColors.BlueGreen, modifier = modifier) {
|
MyCard(colors = if (enabled) MyColors.BlueGreen else MyColors.Disabled, modifier = modifier) {
|
||||||
Box(modifier = M.clickable { onClick() }) {
|
Box(modifier = M.clickable {
|
||||||
|
if (enabled) {
|
||||||
|
onClick()
|
||||||
|
} else {
|
||||||
|
Toasty.showToast("正在开发中,敬请期待")
|
||||||
|
}
|
||||||
|
}) {
|
||||||
Text(
|
Text(
|
||||||
modifier = M
|
modifier = M
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(10.dp),
|
.padding(vertical = 16.dp, horizontal = 10.dp),
|
||||||
text = title,
|
text = title,
|
||||||
color = MyColors.White,
|
color = if (enabled) MyColors.White else MyColors.Gray,
|
||||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ object Toasty : BaseViewModel() {
|
|||||||
val confirmDialog = _confirmDialog.asStateFlow()
|
val confirmDialog = _confirmDialog.asStateFlow()
|
||||||
private val _inputDialog = MutableStateFlow(InputDialogData())
|
private val _inputDialog = MutableStateFlow(InputDialogData())
|
||||||
val inputDialog = _inputDialog.asStateFlow()
|
val inputDialog = _inputDialog.asStateFlow()
|
||||||
|
private val _globalDialog = MutableStateFlow(GlobalDialogData())
|
||||||
|
val globalDialog = _globalDialog.asStateFlow()
|
||||||
|
|
||||||
// 控制 Drawer 状态的 Boolean Flow
|
// 控制 Drawer 状态的 Boolean Flow
|
||||||
private val _isDrawerOpen = MutableStateFlow(false)
|
private val _isDrawerOpen = MutableStateFlow(false)
|
||||||
@@ -233,6 +235,17 @@ object Toasty : BaseViewModel() {
|
|||||||
_confirmDialog.value = ConfirmDialogData(showDialog = false)
|
_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 Orange = Color(0xFFFFAE00)
|
||||||
val LightGray = Color(0xCCEEEEEE)
|
val LightGray = Color(0xCCEEEEEE)
|
||||||
val Gray = Color(0xFF999999)
|
val Gray = Color(0xFF999999)
|
||||||
|
val Disabled = Color(0xFFE5E7EB)
|
||||||
val BlueGreen = Color(0xFF2FBAA3)
|
val BlueGreen = Color(0xFF2FBAA3)
|
||||||
val LightBlueGreen = Color(0x802FBAA3)
|
val LightBlueGreen = Color(0x802FBAA3)
|
||||||
val LightLightBlueGreen = Color(0xFFF7FFFE)
|
val LightLightBlueGreen = Color(0xFFF7FFFE)
|
||||||
@@ -28,4 +29,4 @@ object MyColors {
|
|||||||
val Red = Color(0xFFF44336)
|
val Red = Color(0xFFF44336)
|
||||||
val LigntRed = Color(0x80E53935)
|
val LigntRed = Color(0x80E53935)
|
||||||
// 添加更多的颜色常量...
|
// 添加更多的颜色常量...
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class PollingTask private constructor() {
|
|||||||
* @param task 需要执行的任务
|
* @param task 需要执行的任务
|
||||||
*/
|
*/
|
||||||
fun startDelayedTask(taskId: String, delaySeconds: Long, task: Runnable) {
|
fun startDelayedTask(taskId: String, delaySeconds: Long, task: Runnable) {
|
||||||
|
ensureScheduler()
|
||||||
stopTask(taskId) // 如果已有相同ID的任务,先停止
|
stopTask(taskId) // 如果已有相同ID的任务,先停止
|
||||||
|
|
||||||
val future = scheduler!!.schedule({
|
val future = scheduler!!.schedule({
|
||||||
@@ -99,6 +100,7 @@ class PollingTask private constructor() {
|
|||||||
* @param task 需要执行的任务
|
* @param task 需要执行的任务
|
||||||
*/
|
*/
|
||||||
private fun startPollingTask(taskId: String, intervalSeconds: Long, task: Runnable) {
|
private fun startPollingTask(taskId: String, intervalSeconds: Long, task: Runnable) {
|
||||||
|
ensureScheduler()
|
||||||
stopTask(taskId) // 如果已有相同ID的任务,先停止
|
stopTask(taskId) // 如果已有相同ID的任务,先停止
|
||||||
val future = scheduler!!.scheduleWithFixedDelay(task, 0, intervalSeconds, TimeUnit.SECONDS)
|
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任务接口
|
// 定义IO任务接口
|
||||||
interface IRxIOTask<T> {
|
interface IRxIOTask<T> {
|
||||||
fun doInIOThread(): 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('.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-2
@@ -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.ConverterUtil
|
||||||
import com.bbitcn.f8.pad.utils.externalModules.manager.serial.uhfSerial.UHFReaderForSerial
|
import com.bbitcn.f8.pad.utils.externalModules.manager.serial.uhfSerial.UHFReaderForSerial
|
||||||
import com.bbitcn.f8.pad.utils.log.MyLog
|
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.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
@@ -28,6 +32,7 @@ object UHFReaderG06M_G25M : UHFReaderForSerial() {
|
|||||||
private var mRcpBase: RcpBase? = null
|
private var mRcpBase: RcpBase? = null
|
||||||
private var mSioBase: SioCom? = null
|
private var mSioBase: SioCom? = null
|
||||||
private var onCommListener: OnCommListener? = null
|
private var onCommListener: OnCommListener? = null
|
||||||
|
private val protocolScope = CoroutineScope(SupervisorJob() + Dispatchers.IO.limitedParallelism(1))
|
||||||
|
|
||||||
private var mBattaryLevel = 0
|
private var mBattaryLevel = 0
|
||||||
|
|
||||||
@@ -128,7 +133,7 @@ object UHFReaderG06M_G25M : UHFReaderForSerial() {
|
|||||||
Command(RcpMM.RCP_MM_PARA, RcpBase.RCP_MSG_GET)
|
Command(RcpMM.RCP_MM_PARA, RcpBase.RCP_MSG_GET)
|
||||||
|
|
||||||
mRcpBase?.setOnProtocolListener({ obj, protocolEventArg ->
|
mRcpBase?.setOnProtocolListener({ obj, protocolEventArg ->
|
||||||
doInIoThreadNoDialog {
|
protocolScope.launch {
|
||||||
val psData = protocolEventArg.protocolPacket
|
val psData = protocolEventArg.protocolPacket
|
||||||
when (psData.Code) {
|
when (psData.Code) {
|
||||||
RcpMM.RCP_MM_READ_C_UII ->
|
RcpMM.RCP_MM_READ_C_UII ->
|
||||||
@@ -296,4 +301,4 @@ object UHFReaderG06M_G25M : UHFReaderForSerial() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,13 @@ object RetrofitClient {
|
|||||||
try {
|
try {
|
||||||
val response = chain.proceed(chain.request())
|
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()
|
val content = response.peekBody(ERROR_BODY_PEEK_BYTES).string()
|
||||||
runCatching {
|
runCatching {
|
||||||
Gson().fromJson(content, CommonResponse::class.java)
|
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 com.bbitcn.f8.pad.utils.log.MyLog
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.Authenticator
|
import okhttp3.Authenticator
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.Route
|
import okhttp3.Route
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 401 token refresh interceptor.
|
* 401 token refresh interceptor.
|
||||||
@@ -18,6 +22,20 @@ import okhttp3.Route
|
|||||||
class TokenAuthenticator : Authenticator {
|
class TokenAuthenticator : Authenticator {
|
||||||
|
|
||||||
private val lock = Any()
|
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? {
|
override fun authenticate(route: Route?, response: Response): Request? {
|
||||||
MyLog.network("TokenAuthenticator: authenticate")
|
MyLog.network("TokenAuthenticator: authenticate")
|
||||||
@@ -45,7 +63,7 @@ class TokenAuthenticator : Authenticator {
|
|||||||
|
|
||||||
val newToken = runBlocking {
|
val newToken = runBlocking {
|
||||||
MyLog.network("刷新Token: $currentToken, $refreshToken")
|
MyLog.network("刷新Token: $currentToken, $refreshToken")
|
||||||
val result = RetrofitClient.apiInterface().refreshToken(
|
val result = refreshApi.refreshToken(
|
||||||
RefreshTokenRequest(
|
RefreshTokenRequest(
|
||||||
hardwareid = Global.getDeviceId(),
|
hardwareid = Global.getDeviceId(),
|
||||||
refToken = refreshToken,
|
refToken = refreshToken,
|
||||||
|
|||||||
Reference in New Issue
Block a user