This commit is contained in:
zxj
2025-07-08 15:40:39 +08:00
parent d2698ed60f
commit 4501b6d5fc
132 changed files with 8998 additions and 0 deletions
@@ -0,0 +1,159 @@
package com.sw.inbound.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.sw.inbound.GlobalKey
import com.sw.inbound.R
import com.sw.inbound.ui.page.HomeScreen
import com.sw.inbound.ui.page.LoginScreen
import com.sw.inbound.ui.page.PurchaseOrderScreen
import com.sw.inbound.ui.page.ReceiptProductScreen
import com.sw.inbound.ui.page.SelfProcurementScreen
import com.sw.inbound.ui.page.TestScreen
import com.sw.inbound.ui.weight.GlobalLoading
import com.sw.inbound.utils.SPUtil
import com.sw.inbound.utils.ToastUtils
@Composable
fun AppScreen(
onBackRequest: () -> Unit
) {
val navController = rememberNavController()
val currentBackStackEntry by navController.currentBackStackEntryAsState()
var canBack by remember { mutableStateOf(false) }
// 拦截物理返回键
// BackHandler(
// onBack = {
// val currentRoute = currentBackStackEntry?.destination?.route
// Timber.d("BackHandler currentRouter = ${currentRoute}")
// if (currentRoute == Screen.Home.route && !canBack) {
// ToastUtils.showToast("再次点击退出")
// canBack = true
// } else if (currentRoute == Screen.Login.route) {
// onBackRequest()
// } else {
// navController.popBackStack()
// }
// }
// )
//
// BackHandler(canBack) {
// Timber.d("canBack = $canBack")
// if (canBack) {
// onBackRequest()
// }
// }
Scaffold(
modifier = Modifier
.fillMaxSize()
.paint(painter = painterResource(R.mipmap.bg)),
containerColor = Color.Transparent,
content = { padding ->
Box {
// Image(
// painter = painterResource(R.mipmap.bg),
// contentDescription = null,
// modifier = Modifier.fillMaxSize(),
// contentScale = ContentScale.Crop
// )
NavHost(padding, navController, onBackRequest)
}
}
)
ToastUtils.ToastComposable()
GlobalLoading() // 全局Loading
}
@Composable
private fun NavHost(
padding: PaddingValues,
navController: NavHostController,
onBackRequest: () -> Unit = {}
) {
val spUtil = SPUtil.getInstance()
val token = spUtil.get(GlobalKey.KEY_TOKEN, "")!!
NavHost(
modifier = Modifier
.fillMaxSize()
.padding(padding),
navController = navController,
startDestination = if (token.isEmpty()) Screen.Login.route else Screen.Home.route
) {
composable(Screen.Test.route) {
TestScreen(modifier = Modifier, navController)
}
// 首页
composable(Screen.Home.route) {
HomeScreen(modifier = Modifier, navController, onBackRequest = onBackRequest)
}
// 登录
composable(Screen.Login.route) {
LoginScreen(modifier = Modifier, navController)
}
// 采购单入库
composable(Screen.PurchaseOrder.route) {
PurchaseOrderScreen(modifier = Modifier.fillMaxSize(), navController)
}
// 自采单
composable(Screen.SelfProcurement.route) {
SelfProcurementScreen(modifier = Modifier, navController)
}
// 收货
composable(
Screen.ReceiptProduct.route,
arguments = listOf(
navArgument(name = "id", builder = { type = NavType.IntType }),
navArgument(name = "supplierId", builder = { type = NavType.IntType })
)
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("id") ?: 0
val supplierId = backStackEntry.arguments?.getInt("supplierId") ?: 0
ReceiptProductScreen(modifier = Modifier, navController, id, supplierId)
}
}
}
sealed class Screen(val route: String) {
data object Home : Screen("home")
data object Login : Screen("login")
// 采购单入库
data object PurchaseOrder : Screen("purchase_order")
// 自采单入库
data object SelfProcurement : Screen("self_procurement")
// 收货
data object ReceiptProduct : Screen("receipt_product?id={id}&supplierId={supplierId}") {
fun createRoute(id: Int, supplierId: Int) =
"receipt_product?id=${id}&supplierId=${supplierId}"
}
// 添加物品
data object AddProduct : Screen("addProduct")
data object Test : Screen("Test")
}
@@ -0,0 +1,89 @@
package com.sw.inbound.ui.page
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.sw.inbound.R
import com.sw.inbound.ui.Screen
import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.viewmodel.UserViewModel
import timber.log.Timber
@Composable
fun HomeScreen(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
viewModel: UserViewModel = hiltViewModel<UserViewModel>(),
onBackRequest: () -> Unit = {}
) {
val user by viewModel.user.collectAsState()
// 避免每次进入都获取字典类型
LaunchedEffect(Unit) {
Timber.d("LaunchedEffect(Unit)")
viewModel.getInitInfo()
}
BackHandler {
onBackRequest()
}
Column(modifier = Modifier.fillMaxSize()) {
TopTitleBar(user = user, onLogoutClick = {
viewModel.logout()
navController.navigate(Screen.Login.route) {
popUpTo(Screen.Home.route) {
inclusive = true
}
}
})
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.weight(1f)
.fillMaxWidth()
) {
Image(
modifier = Modifier
.width(595.dp)
.height(477.dp)
.clickable() {
navController.navigate(Screen.PurchaseOrder.route)
},
painter = painterResource(R.mipmap.ic_order_purchase),
contentDescription = "采购单入库"
)
Spacer(modifier = Modifier.width(15.dp))
Image(
modifier = Modifier
.width(595.dp)
.height(477.dp)
.clickable {
navController.navigate(Screen.SelfProcurement.route)
},
painter = painterResource(R.mipmap.ic_order_self_procurement),
contentDescription = "自采入库"
)
}
}
}
@@ -0,0 +1,169 @@
package com.sw.inbound.ui.page
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.sw.inbound.GlobalData
import com.sw.inbound.R
import com.sw.inbound.ext.bold
import com.sw.inbound.ext.textAlign
import com.sw.inbound.ext.withSize
import com.sw.inbound.ui.Screen
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.viewmodel.UserViewModel
import timber.log.Timber
@Composable
fun LoginScreen(
modifier: Modifier = Modifier,
controller: NavController = rememberNavController(),
viewModel: UserViewModel = hiltViewModel<UserViewModel>()
) {
var username by remember { mutableStateOf<String>("padAdmin") }
var password by remember { mutableStateOf<String>("123123") }
// val user by viewModel.user.collectAsState()
LaunchedEffect(Unit) {
Timber.d(" user =")
viewModel.user.collect {
Timber.d(" user collect = ${it}")
if (it != null) {
GlobalData.user = it
controller.navigate(Screen.Home.route) {
Timber.d("登录成功,跳转到主界面")
controller.popBackStack()
}
}
}
// if (user != null){
// controller.navigate(Screen.Home.route) {
// controller.popBackStack()
// }
// }
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "出入库管理", style = AppTypography.black141428TextStyle.withSize(60.sp).bold())
Spacer(modifier = Modifier.height(91.dp))
UserInputView(value = username, placeholderValue = "请输入用户名", onValueChange = {
username = it
})
Spacer(modifier = Modifier.height(30.dp))
UserInputView(value = password, placeholderValue = "请输入密码", onValueChange = {
password = it
}, isPassword = true)
Spacer(modifier = Modifier.height(90.dp))
CustomButton(
modifier = modifier
.width(885.dp)
.height(120.dp),
text = "登录",
onClick = {
if (username.isEmpty() || password.isEmpty()) {
ToastUtils.showToast("用户名或密码不能为空")
return@CustomButton
}
viewModel.login(username, password)
// viewModel.test()
},
borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white),
fontSize = 36.sp
)
}
}
@Composable
private fun UserInputView(
value: String,
placeholderValue: String,
onValueChange: (String) -> Unit,
isPassword: Boolean = false,
isShowPassword: Boolean = false
) {
val backgroundColor = Color.White.copy(alpha = 0.37f)
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier
.height(120.dp)
.width(885.dp)
.border(
width = 2.dp,
color = backgroundColor,
shape = RoundedCornerShape(10.dp)
),
colors = TextFieldDefaults.colors(
focusedContainerColor = backgroundColor,
unfocusedContainerColor = backgroundColor,
disabledContainerColor = backgroundColor,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
// 密码输入相关设置
visualTransformation = if (isPassword && !isShowPassword) {
PasswordVisualTransformation()
} else {
VisualTransformation.None
},
keyboardOptions = KeyboardOptions(
keyboardType = if (isPassword) KeyboardType.Password else KeyboardType.Text,
imeAction = if (isPassword) ImeAction.Done else ImeAction.Next
),
shape = RoundedCornerShape(10.dp),
textStyle = AppTypography.gray96a0aaTextStyle.withSize(30.sp).textAlign(TextAlign.Center),
placeholder = {
Text(
text = placeholderValue,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center, // 占位符水平居中
style = AppTypography.gray96a0aaTextStyle.textAlign(TextAlign.Center)
)
},
singleLine = true // 确保单行输入(避免换行导致高度变化)
)
}
@@ -0,0 +1,283 @@
package com.sw.inbound.ui.page
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.sw.inbound.R
import com.sw.inbound.ext.dashedBorder
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.model.response.SupplierInfo
import com.sw.inbound.ui.Screen
import com.sw.inbound.ui.theme.AppTypography.blackTextStyle
import com.sw.inbound.ui.theme.AppTypography.grayTextStyle
import com.sw.inbound.ui.weight.BottomActionBar
import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.viewmodel.ProductViewModel
@Preview(
widthDp = 1920,
heightDp = 1080,
showBackground = true
)
@Composable
fun PurchaseOrderScreen(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
viewModel: ProductViewModel = hiltViewModel<ProductViewModel>()
) {
val products by viewModel.supplierList.collectAsState()
LaunchedEffect(Unit) {
viewModel.getOrderList()
}
Column {
TopTitleBar()
Box(
modifier = Modifier
.weight(1f)
.padding(horizontal = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.bg_listview),
contentDescription = "背景",
modifier = Modifier
.width(1900.dp)
.height(880.dp),
contentScale = ContentScale.FillBounds
)
Box(
modifier = Modifier.padding(
start = 30.dp,
end = 30.dp,
top = 30.dp,
bottom = 90.dp
)
) {
if (products.isEmpty()) {
PurchaseEmptyItem()
} else {
LazyRow(
modifier = modifier
// .paint(painterResource(R.mipmap.bg_listview))
// .fillMaxSize()
// .background(
// color = Color(0x80FFFFFF),
// shape = RoundedCornerShape(12.dp) // 圆角背景
// )
.padding(10.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp)
) {
items(items = products, key = { it!!.id }) {
PurchaseOrderItem(it!!) {
navController.navigate(
Screen.ReceiptProduct.createRoute(
it.id,
it.supplierId
)
)
}
}
}
}
}
}
BottomActionBar(
modifier = Modifier, onLeftButtonClick = {
navController.popBackStack()
}, onRight2ButtonClick = {
navController.navigate(Screen.SelfProcurement.route)
})
}
}
@Composable
fun PurchaseEmptyItem() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(painter = painterResource(R.mipmap.ic_empty), contentDescription = "暂无数据")
Text(
text = "暂无数据", style = TextStyle(
fontSize = 30.sp,
fontWeight = FontWeight.Bold,
color = colorResource(R.color.title)
)
)
}
}
@Composable
fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) {
val (receiptStatusStyle, statusLabel) = when (product.receiveStatus) {
1 -> blackTextStyle to "已关闭"
2 -> blackTextStyle to "已完成"
3 -> blackTextStyle.copy(
color = colorResource(R.color.red)
) to "未收货"
4 -> blackTextStyle.copy(
color = colorResource(R.color.origin)
) to "部分收货"
else -> blackTextStyle to "已关闭"
}
val itemSpace = 29.dp
Box(
modifier = Modifier
.background(
color = Color.White.copy(alpha = 0.37f),
shape = RoundedCornerShape(10.dp) // 圆角背景
)
.clickable {
onItemClick(product)
}
) {
Column(
modifier = Modifier
.width(580.dp)
.padding(horizontal = 30.dp, vertical = 30.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(0.dp)
) {
Text(
text = product.supplierName,
modifier = Modifier.padding(top = 25.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 30.sp,
color = colorResource(R.color.black)
)
)
Spacer(modifier = Modifier.height(itemSpace))
Text(
text = "单号 ${product.receiveCode}",
maxLines = 1,
style = grayTextStyle
)
Spacer(modifier = Modifier.height(itemSpace))
HorizontalDivider(
thickness = 1.dp,
color = colorResource(R.color.divider)
)
Spacer(modifier = Modifier.height(40.dp))
Text(
text = "物品(项)",
style = grayTextStyle
)
Spacer(modifier = Modifier.height(22.dp))
Text(
text = product.goodCount.toFormattedString(),
style = TextStyle(
color = colorResource(R.color.black),
fontSize = 90.sp,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = Modifier.height(22.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.dashedBorder(
strokeWidth = 2.dp,
color = Color(0xFFBDC5CE),
cornerRadiusDp = 10.dp
)
.padding(horizontal = 72.dp, vertical = 27.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "采购日期", style = grayTextStyle)
Spacer(modifier = Modifier.height(8.dp))
Text(text = product.purchaseDate ?: "", style = blackTextStyle)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "到货日期", style = grayTextStyle)
Spacer(modifier = Modifier.height(8.dp))
Text(text = product.receiveDate ?: "-", style = blackTextStyle)
}
}
Spacer(modifier = Modifier.height(28.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Row {
Text("收货状态:", style = grayTextStyle)
Spacer(Modifier.width(8.dp))
Text(statusLabel, style = receiptStatusStyle)
}
Row {
Text("采购人:", style = grayTextStyle)
Spacer(Modifier.width(8.dp))
Text(product.receiveUser, style = blackTextStyle)
}
}
Spacer(modifier = Modifier.height(itemSpace))
Button(
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF009632).copy(alpha = 1f),
contentColor = colorResource(R.color.white)
), onClick = {
onItemClick(product)
}) {
Text(
if (product.receiveStatus == 4) "部分收货" else "收货",
style = TextStyle(fontWeight = FontWeight.Bold, fontSize = 30.sp)
)
}
}
}
}
@@ -0,0 +1,613 @@
package com.sw.inbound.ui.page
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.sw.inbound.GlobalData
import com.sw.inbound.R
import com.sw.inbound.ext.bold
import com.sw.inbound.ext.toSafeFloat
import com.sw.inbound.ext.withSize
import com.sw.inbound.model.request.UploadInfo
import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.AppTypography.black141428TextStyle
import com.sw.inbound.ui.theme.AppTypography.gray96a0aaTextStyle
import com.sw.inbound.ui.weight.BottomActionBar
import com.sw.inbound.ui.weight.CustomDropdownTextField
import com.sw.inbound.ui.weight.CustomSpinner
import com.sw.inbound.ui.weight.CustomTextField
import com.sw.inbound.ui.weight.IdentityView
import com.sw.inbound.ui.weight.InputType
import com.sw.inbound.ui.weight.ProductListView
import com.sw.inbound.ui.weight.RowInputLayout
import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.ui.weight.dialog.ReceiptTipDialog
import com.sw.inbound.viewmodel.ReceiptViewModel
import timber.log.Timber
@Composable
fun ReceiptProductScreen(
modifier: Modifier = Modifier,
controller: NavController = rememberNavController(),
id: Int,
supplierId: Int,
viewModel: ReceiptViewModel = hiltViewModel<ReceiptViewModel>()
) {
val adjustState by viewModel.adjustState.collectAsState()
val showDialog by viewModel.showReceiptDialog.collectAsState()
val orders by viewModel.orders.collectAsState()
val receiptResult by viewModel.receiptResult.collectAsState()
LaunchedEffect(receiptResult) {
if (receiptResult) {
controller.popBackStack()
}
}
Column(modifier = modifier) {
TopTitleBar()
Row(
modifier = modifier
.padding(30.dp)
.weight(1f)
) {
Row(
modifier = modifier
.background(
color = Color(0x80FFFFFF),
shape = RoundedCornerShape(12.dp)
)
.fillMaxWidth()
.padding(30.dp)
) {
// 左侧列表
ReceiptLeftView(modifier.weight(1f), viewModel, id)
// 未调整,显示右侧界面
if (!adjustState) {
Spacer(modifier = Modifier.width(30.dp))
// 右侧编辑
ReceiptRightView(modifier.width(580.dp), viewModel)
}
}
}
BottomActionBar(
modifier = Modifier,
right2ButtonText = "确认收货",
onLeftButtonClick = {
controller.popBackStack()
},
onRight2ButtonClick = {
viewModel.updateReceiptDialog(true)
})
}
if (showDialog) {
ReceiptTipDialog(isWarn = viewModel.hasWrongCount(), onCancelClick = {
Timber.d("返回")
viewModel.updateReceiptDialog(false)
}, onConfirmClick = { isFull ->
Timber.d(if (isFull) "确认收货" else "部分收货")
viewModel.updateReceiptDialog(false)
val uploadInfo = UploadInfo(id = id, receiveGoodsInfos = orders)
if (isFull) {
viewModel.confirmReceipt(uploadInfo)
} else {
uploadInfo.supplierId = supplierId
viewModel.partialReceipt(uploadInfo)
}
})
}
}
@Composable
fun ReceiptLeftView(
modifier: Modifier,
viewModel: ReceiptViewModel,
id: Int
) {
val warehouseTypeList = GlobalData.warehouseTypeList // receiptViewModel.getStoreList()
val currentPurchaseInfo by viewModel.currentPurchaseInfo.collectAsState()
LaunchedEffect(id) {
viewModel.getReceiveDetail(id)
}
currentPurchaseInfo?.let {
Column(modifier) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column {
Text(
"供应商:${currentPurchaseInfo?.supplierName}",
style = black141428TextStyle.bold()
)
Spacer(modifier = Modifier.height(18.dp))
Row {
Text(
"采购单号:${currentPurchaseInfo?.purCode}",
style = gray96a0aaTextStyle.copy(fontSize = 20.sp)
)
Spacer(modifier = Modifier.width(120.dp))
Text(
"收货单号:${currentPurchaseInfo?.receiveCode}",
style = gray96a0aaTextStyle.copy(fontSize = 20.sp)
)
}
}
CustomSpinner(
items = warehouseTypeList,
selectedItem = currentPurchaseInfo!!.warehouseName,
onItemSelected = { value ->
val currentPurchaseInfo1 = currentPurchaseInfo!!.copy()
currentPurchaseInfo1.warehouseName = value.value
viewModel.updateCurrentPurchaseInfo(currentPurchaseInfo1)
viewModel.updateAllPurchaseStore(store = value)
})
}
Spacer(modifier = Modifier.height(30.dp))
TabScreen(viewModel)
// LazyColumn(
// verticalArrangement = Arrangement.spacedBy(30.dp)
// ) {
// items(items = purchaseOrderList, key = { it.id }) {
// ReceiptItem(it, modifier)
// }
// }
}
}
}
@Composable
fun TabScreen(viewModel: ReceiptViewModel) {
var selectedTabIndex by remember { mutableIntStateOf(0) }
val purchaseUnadjustedList by viewModel.unadjustedOrders.collectAsState()
val purchaseAdjustList by viewModel.adjustedOrders.collectAsState()
val tabs =
listOf("未调整(${purchaseUnadjustedList.size})", "已调整(${purchaseAdjustList.size})")
Column(
modifier = Modifier
.background(
color = Color.White.copy(alpha = 0.37f),
shape = RoundedCornerShape(10.dp) // 圆角背景
)
.fillMaxSize()
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 30.dp), // 左上角对齐
horizontalArrangement = Arrangement.Start,
) {
tabs.forEachIndexed { index, title ->
val selected = selectedTabIndex == index
Tab(
selected = selected,
onClick = { selectedTabIndex = index },
modifier = Modifier
.width(150.dp)
// .wrapContentWidth()
.padding(start = if (index == 1) 30.dp else 0.dp),
selectedContentColor = Color(0xFF0032C8),
unselectedContentColor = Color(0xFF14141E)
) {
Text(
text = title,
style = if (selected) AppTypography.BlueTextStyle else AppTypography.black141428TextStyle.bold(),
textAlign = TextAlign.Center,
modifier = Modifier.padding(vertical = 12.dp)
)
}
}
}
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
thickness = 1.dp,
color = colorResource(R.color.divider)
)
when (selectedTabIndex) {
// 未调整列表
0 -> {
UnadjustedView(purchaseUnadjustedList, viewModel)
viewModel.updateAdjustState(false)
}
// 已调整列表
1 -> {
AdjustedView(viewModel)
viewModel.updateAdjustState(true)
}
}
}
}
@Composable
fun UnadjustedView(purchaseOrderList: List<GoodsInfo>, viewModel: ReceiptViewModel) {
val checkedItem by viewModel.selectedItem.collectAsState()
// 未调整view
ProductListView(
modifier = Modifier.padding(horizontal = 30.dp),
productList = purchaseOrderList,
checkedItem = checkedItem,
onItemCheckedClick = {
viewModel.updateSelectedItem(it)
})
}
@Composable
fun AdjustedView(viewModel: ReceiptViewModel) {
// 已调整view
val purchaseAdjustList by viewModel.adjustedOrders.collectAsState()
LazyColumn(
verticalArrangement = Arrangement.spacedBy(0.dp)
) {
items(items = purchaseAdjustList, key = { it.goodId!! }) {
// ReceiptItem(it)
AdjustedViewItem(it, viewModel)
}
}
}
@Composable
private fun AdjustedViewItem(purchaseOrder: GoodsInfo, viewModel: ReceiptViewModel) {
val warehouseTypeList = GlobalData.warehouseTypeList // viewModel.getStoreList()
Column(modifier = Modifier.padding(horizontal = 30.dp)) {
Spacer(modifier = Modifier.height(30.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(purchaseOrder.goodNameStr, style = AppTypography.blackTextStyle)
Row {
Text("采购量:", style = gray96a0aaTextStyle)
Text(
"${purchaseOrder.receiveCountStr}${purchaseOrder.unitNameStr}",
style = black141428TextStyle
)
}
}
Spacer(modifier = Modifier.height(30.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp)
) {
AdjustedRowInputLayout(
label = "单价", value = purchaseOrder.recUnitPriceTaxInStr,
onValueChange = {
viewModel.updatePurchaseItem(
purchaseOrder = purchaseOrder.copy(
recUnitPriceTaxIn = it.toSafeFloat()
)
)
}, inputType = InputType.Decimal
)
Spacer(modifier = Modifier.width(12.dp))
Text(text = "", style = AppTypography.gray96a0aaTextStyle)
Spacer(modifier = Modifier.width(50.dp))
Text(text = "x", style = AppTypography.black141428TextStyle.bold())
Spacer(modifier = Modifier.width(50.dp))
AdjustedRowInputLayout(
label = "实收", value = purchaseOrder.receivedNumStr,
onValueChange = {
viewModel.updatePurchaseItem(
purchaseOrder = purchaseOrder.copy(
receivedNum = it.toSafeFloat()
)
)
}, inputType = InputType.Decimal
)
Spacer(modifier = Modifier.width(12.dp))
Text(text = purchaseOrder.unitNameStr, style = AppTypography.gray96a0aaTextStyle)
Spacer(modifier = Modifier.width(50.dp))
Text(text = "=", style = AppTypography.black141428TextStyle.bold())
Spacer(modifier = Modifier.width(50.dp))
AdjustedRowInputLayout(
label = "金额", value = purchaseOrder.recPriceExItemStr,
onValueChange = {
viewModel.updatePurchaseItem(
purchaseOrder = purchaseOrder.copy(
recPriceExItem = it.toSafeFloat()
)
)
}, inputType = InputType.Decimal
)
Spacer(modifier = Modifier.width(12.dp))
Text(text = "", style = AppTypography.gray96a0aaTextStyle)
Spacer(modifier = Modifier.weight(1f))
AdjustedRowInputLayout(
label = "仓库",
value = purchaseOrder.warehouseNameStr,
dropdownItems = warehouseTypeList,
onValueChange = { value ->
purchaseOrder.let {
viewModel.updatePurchaseItem(
purchaseOrder.copy(
warehouseName = value,
warehouseId = warehouseTypeList.find { it.value == value }?.id ?: 0
)
)
}
})
}
Spacer(modifier = Modifier.height(30.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
}
}
@Composable
private fun AdjustedRowInputLayout(
label: String,
value: String,
onValueChange: (String) -> Unit,
dropdownItems: List<DictType> = emptyList(),
textAlign: TextAlign = TextAlign.Center,
spinnerTextAlign: TextAlign = TextAlign.Start,
trailingLabel: String? = null,
inputType: InputType = InputType.Text
) {
Row(
modifier = Modifier
.height(60.dp)
// .fillMaxWidth()
// .padding(horizontal = 30.dp)
,
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(label, style = AppTypography.gray96a0aaTextStyle)
Spacer(modifier = Modifier.width(12.dp))
if (dropdownItems.isEmpty()) {
CustomTextField(
modifier = Modifier.width(180.dp),
value = value,
onValueChange = onValueChange,
textAlign = textAlign,
trailingLabel = trailingLabel, inputType = inputType
)
} else {
CustomDropdownTextField(
modifier = Modifier.width(300.dp),
value = value,
dropdownItems = dropdownItems,
onValueChange = onValueChange,
textAlign = spinnerTextAlign
)
}
}
}
@Composable
fun ReceiptRightView(modifier: Modifier = Modifier, viewModel: ReceiptViewModel) {
val searchResultList by viewModel.searchListItems.collectAsState()
val selectedItem by viewModel.selectedItem.collectAsState()
Column(
modifier = modifier
.fillMaxSize()
.background(
color = Color.White.copy(alpha = 0.37f),
shape = RoundedCornerShape(10.dp) // 圆角背景
)
.padding(horizontal = 30.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (selectedItem == null) {
Spacer(modifier = Modifier.height(30.dp))
IdentityView(list = searchResultList, showSearchView = false, onOptionSelected = {})
} else {
ReceiptProductEditView(viewModel)
}
}
}
/**
* 收货界面右侧编辑
*/
@Composable
private fun ReceiptProductEditView(
viewModel: ReceiptViewModel,
onConfirmClick: () -> Unit = {},
onCancelClick: () -> Unit = {}
) {
val selectedItem by viewModel.selectedItem.collectAsState()
val warehouseTypeList = GlobalData.warehouseTypeList
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
Timber.d("开始传感器采集")
viewModel.startSensorScale()
onDispose {
Timber.d("停止传感器采集")
viewModel.stopSensorScale()
}
}
selectedItem?.let {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(top = 20.dp, bottom = 20.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Spacer(modifier = Modifier.height(10.dp))
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = selectedItem?.goodName ?: "-",
style = AppTypography.black141428TextStyle.bold().withSize(30.sp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Spacer(modifier = Modifier.height(20.dp))
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(),
thickness = 1.dp,
color = colorResource(R.color.divider)
)
RowInputLayout(
label = "仓库",
value = selectedItem!!.warehouseNameStr,
dropdownItems = warehouseTypeList,
onValueChange = { value ->
selectedItem?.let {
viewModel.updateSelectedItem(
selectedItem!!.copy(
warehouseName = value,
warehouseId =
warehouseTypeList.find { it.value == value }?.id ?: 0
// storeList.indexOf(value)
)
)
}
})
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(),
thickness = 1.dp,
color = colorResource(R.color.divider)
)
RowInputLayout(
label = "收货数量",
value = selectedItem!!.receivedNumStr,
onValueChange = { value ->
viewModel.updateCountInputState(value.isNotEmpty())
selectedItem?.let {
viewModel.updateSelectedItem(selectedItem!!.copy(receivedNum = value.toSafeFloat()))
}
},
trailingLabel = selectedItem?.unitName,
inputType = InputType.Decimal,
isInitUpdate = true
)
RowInputLayout(
label = "收货单价",
value = selectedItem!!.recUnitPriceTaxInStr,
trailingLabel = "",
onValueChange = { value ->
selectedItem?.let {
viewModel.updateSelectedItem(selectedItem!!.copy(recUnitPriceTaxIn = value.toSafeFloat()))
}
}, inputType = InputType.Decimal
)
RowInputLayout(
label = "收货金额",
value = selectedItem!!.recPriceExItemStr,
trailingLabel = "",
onValueChange = { value ->
selectedItem?.let {
viewModel.updateSelectedItem(selectedItem!!.copy(recPriceExItem = value.toSafeFloat()))
}
}, inputType = InputType.Decimal
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = "物品重量", style = AppTypography.black141428TextStyle)
CustomTextField(
modifier = Modifier
.width(290.dp)
.height(60.dp),
value = selectedItem!!.goodsWeightStr, onValueChange = {},
enabled = false,
isInitUpdate = true,
textAlign = TextAlign.End,
trailingLabel = selectedItem!!.goodsWeightUnitStr,
inputType = InputType.Decimal
)
}
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(),
thickness = 1.dp,
color = colorResource(R.color.divider)
)
Spacer(modifier = Modifier.height(30.dp))
Row(
horizontalArrangement = Arrangement.SpaceAround,
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
) {
Image(modifier = Modifier.clickable {
viewModel.updateSelectedItem(null)
}, painter = painterResource(R.mipmap.ic_btn_back), contentDescription = "返回")
Image(modifier = Modifier.clickable {
viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true))
viewModel.updateSelectedItem(null)
}, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定")
}
}
}
}
@@ -0,0 +1,758 @@
package com.sw.inbound.ui.page
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.sw.inbound.GlobalData
import com.sw.inbound.R
import com.sw.inbound.ext.bold
import com.sw.inbound.ext.dashedBorder
import com.sw.inbound.ext.isValidAmount
import com.sw.inbound.ext.medium
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.ext.toSafeFloat
import com.sw.inbound.ext.withColor
import com.sw.inbound.ext.withSize
import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.model.response.DictType
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Black_141428
import com.sw.inbound.ui.weight.BottomActionBar
import com.sw.inbound.ui.weight.CameraCaptureLayout
import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.ui.weight.CustomDropdownTextField
import com.sw.inbound.ui.weight.CustomOutlinedButton
import com.sw.inbound.ui.weight.CustomSpinner
import com.sw.inbound.ui.weight.CustomTextField
import com.sw.inbound.ui.weight.IdentityView
import com.sw.inbound.ui.weight.InputType
import com.sw.inbound.ui.weight.RowInputLayout
import com.sw.inbound.ui.weight.SelfProcurementListItem
import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.viewmodel.SelfProcurementViewModel
import timber.log.Timber
@Preview(
widthDp = 1920,
heightDp = 1080,
showBackground = true
)
@Composable
fun SelfProcurementScreen(
modifier: Modifier = Modifier,
controller: NavController = rememberNavController(),
viewModel: SelfProcurementViewModel = hiltViewModel<SelfProcurementViewModel>()
) {
val showDialog by viewModel.showAddProductDialog.collectAsState()
val addToWarehouseResult by viewModel.addToWarehouseResult.collectAsState()
LaunchedEffect(addToWarehouseResult) {
if (addToWarehouseResult) {
controller.popBackStack()
}
}
Column(modifier = modifier) {
TopTitleBar(title = "自采入库")
Box(
modifier = Modifier
.weight(1f)
.padding(horizontal = 20.dp)
) {
Image(
painter = painterResource(id = R.mipmap.bg_listview),
contentDescription = "背景",
modifier = Modifier
.width(1900.dp)
.height(880.dp),
contentScale = ContentScale.FillBounds
)
ContentView(viewModel)
}
BottomActionBar(onLeftButtonClick = {
controller.popBackStack()
}, showRight1Button = true, right1ButtonText = "清空物品", onRight1ButtonClick = {
viewModel.clearPurchaseOrders()
}, right2ButtonText = "提交入库", onRight2ButtonClick = {
viewModel.addToWarehouse()
})
}
if (showDialog) {
AddProductDialog(
onDismiss = { viewModel.updateAddProductDialog(false) },
modifier = Modifier,
onCancelClick = {
viewModel.updateAddProductDialog(false)
viewModel.cleanGoodsAddParam()
},
onConfirmClick = {
viewModel.addGoodsInfo()
}
) {
// 弹窗内容
DialogContentView(modifier, viewModel)
}
}
}
@Composable
private fun ContentView(
viewModel: SelfProcurementViewModel,
) {
Row(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
ContentLeftView(viewModel)
ContentRightView(modifier = Modifier.weight(1f), viewModel)
}
}
@Composable
private fun ContentLeftView(
viewModel: SelfProcurementViewModel
) {
val warehouseList = GlobalData.warehouseTypeList // productViewModel.getStoreList()
val productList by viewModel.purchaseList.collectAsState()
val globalWarehouseName by viewModel.globalWarehouse.collectAsState() // remember { mutableStateOf<String>("选择仓库") }
Column(
modifier = Modifier
.width(793.dp)
.padding(30.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = "物品(项)", style = AppTypography.black141428TextStyle.bold())
CustomSpinner(
modifier = Modifier
.height(70.dp)
.width(240.dp),
items = warehouseList,
selectedItem = globalWarehouseName.value,
onItemSelected = {
// globalWarehouseName = it
viewModel.updateGlobalWarehouse(it)
})
}
Spacer(modifier = Modifier.height(30.dp))
SelfProcurementListItem(
modifier = Modifier
.fillMaxHeight()
.background(
color = Color.White.copy(alpha = 0.37f),
shape = RoundedCornerShape(10.dp) // 圆角背景
), productList = productList, showClose = true, onCloseClick = {
viewModel.removePurchaseOrder(it.goodsId)
})
}
}
@Composable
fun ContentRightView(
modifier: Modifier,
viewModel: SelfProcurementViewModel
) {
Row(
modifier = modifier
.width(1037.dp)
.fillMaxHeight()
.padding(top = 30.dp, end = 30.dp, bottom = 30.dp)
.background(
color = Color.White.copy(alpha = 0.37f),
shape = RoundedCornerShape(10.dp) // 圆角背景
)
) {
// 商品识别
ProductIdentification(viewModel)
Image(
painter = painterResource(R.mipmap.bg_divider),
modifier = Modifier
.fillMaxHeight()
.width(30.dp),
contentDescription = ""
)
SelfProductEditView(viewModel)
}
}
@Composable
fun ProductIdentification(viewModel: SelfProcurementViewModel) {
val searchResultList by viewModel.searchListItems.collectAsState()
val selectItem by viewModel.selectedItem.collectAsState()
Column(
modifier = Modifier
.width(457.dp)
.fillMaxHeight()
.padding(30.dp)
) {
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
) {
// 菜品识别
IdentityView(list = searchResultList, showSearchView = true, onSearchClick = {
viewModel.searchGoodsInfoList(it)
}, onOptionSelected = {
viewModel.updateSelectedItemWithSearch(it)
})
}
Spacer(modifier = Modifier.height(10.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.clickable {
viewModel.updateAddProductDialog(true)
},
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Image(painter = painterResource(R.mipmap.ic_add), contentDescription = "添加")
Spacer(modifier = Modifier.width(11.dp))
Text(text = "快速添加", style = AppTypography.BlueTextStyle.medium())
}
}
}
@Composable
private fun DialogContentView(
modifier: Modifier,
viewModel: SelfProcurementViewModel
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier.fillMaxSize()
) {
Text(
text = "新增物品", style = TextStyle().bold().withSize(36.sp).withColor(
Black_141428
)
)
Spacer(modifier = Modifier.height(56.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
Spacer(modifier = Modifier.height(58.dp))
Row(modifier = Modifier.fillMaxWidth()) {
// 采集窗口
CameraCaptureLayout(modifier = Modifier.width(428.dp), onCancelClick = {
}, onConfirmClick = {
})
Spacer(modifier = Modifier.width(60.dp))
// 右侧输入窗口
DialogRightEditView(viewModel)
}
Spacer(modifier = Modifier.height(60.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
}
}
@Composable
private fun DialogRightEditView(viewModel: SelfProcurementViewModel) {
val itemTypeList = GlobalData.goodsTypeList
val unitTypeList = GlobalData.unitTypeList
val storageTypeList = GlobalData.storageTypeList
val goodsAddParam by viewModel.goodsAddParam.collectAsState()
Column(
modifier = Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(29.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(60.dp)
) {
ColumnInputText(
modifier = Modifier
.width(474.dp),
label = "物品名称",
value = goodsAddParam.goodNameSr,
onValueChange = {
viewModel.updateGoodsAddParam(goodsAddParam.copy(goodName = it))
})
ColumnInputText(
modifier = Modifier.width(474.dp),
label = "物品编码(服务生成)",
value = "",
onValueChange = {
// viewModel.updateFormState(formState.copy(goodCode = it))
})
}
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(60.dp)
) {
ColumnInputText(
modifier = Modifier
.weight(1f)
.width(474.dp),
label = "物品类型",
dropdownItems = itemTypeList,
value = goodsAddParam.goodsTypeStr,
onValueChange = { value ->
viewModel.updateGoodsAddParam(
goodsAddParam.copy(
goodType =
itemTypeList.find { it.value == value }?.id ?: 0
)
)
}
)
ColumnInputText(
modifier = Modifier.width(474.dp),
label = "储存方式",
dropdownItems = storageTypeList,
value = goodsAddParam.storageTypeStr,
onValueChange = { value ->
viewModel.updateGoodsAddParam(
goodsAddParam.copy(
storageType = storageTypeList.find { it.value == value }?.id ?: 0
)
)
})
}
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(60.dp)
) {
ColumnInputText(
modifier = Modifier
.weight(1f)
.width(474.dp),
label = "净材率",
value = goodsAddParam.netRateStr,
inputType = InputType.Decimal,
onValueChange = {
viewModel.updateGoodsAddParam(goodsAddParam.copy(netRate = it.toSafeFloat()))
})
ColumnInputText(
modifier = Modifier.width(474.dp),
value = goodsAddParam.unitIdStr,
label = "库存单位",
dropdownItems = unitTypeList,
onValueChange = { value ->
viewModel.updateGoodsAddParam(
goodsAddParam.copy(
unitId = unitTypeList.find { it.value == value }?.id ?: 0
)
)
},
)
}
Column {
Text(text = "采购单位", style = AppTypography.black141428TextStyle)
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier
.height(92.dp)
.fillMaxWidth()
.dashedBorder()
.padding(vertical = 16.dp, horizontal = 26.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("1", style = AppTypography.black141428TextStyle.bold())
Spacer(modifier = Modifier.width(24.dp))
CustomDropdownTextField(
modifier = Modifier.width(200.dp),
value = goodsAddParam.purchaseUnitStr,
dropdownItems = unitTypeList,
placeholderValue = "请选择",
onValueChange = { value ->
viewModel.updateGoodsAddParam(
goodsAddParam.copy(
purchaseUnit = unitTypeList.find { it.value == value }?.id
?: 0
)
)
})
Spacer(modifier = Modifier.width(24.dp))
Text(text = "=", style = AppTypography.gray96a0aaTextStyle)
Spacer(modifier = Modifier.width(24.dp))
CustomTextField(
modifier = Modifier.width(200.dp),
value = goodsAddParam.purchaseValue.toFormattedString(),
placeholderValue = "请录入",
onValueChange = {
viewModel.updateGoodsAddParam(goodsAddParam.copy(purchaseValue = it.toSafeFloat()))
}, inputType = InputType.Decimal
)
Spacer(modifier = Modifier.width(24.dp))
Text(goodsAddParam.unitIdStr, style = AppTypography.gray96a0aaTextStyle)
Spacer(modifier = Modifier.width(111.dp))
Text("单价", style = AppTypography.black141428TextStyle)
Spacer(modifier = Modifier.width(24.dp))
CustomTextField(
modifier = Modifier.width(200.dp),
placeholderValue = "请录入",
value = goodsAddParam.purchasePriceStr, onValueChange = {
if (it.isValidAmount()) {
viewModel.updateGoodsAddParam(goodsAddParam.copy(purchasePrice = it.toSafeBigDecimal()))
}
}, inputType = InputType.Decimal, hasNext = false
)
Spacer(modifier = Modifier.width(24.dp))
Text("", style = AppTypography.gray96a0aaTextStyle)
}
}
}
}
@Composable
fun ColumnInputText(
modifier: Modifier = Modifier,
label: String,
value: String,
onValueChange: (String) -> Unit = {},
dropdownItems: List<DictType> = emptyList(),
trailingLabel: String? = null, inputType: InputType = InputType.Text
) {
val placeholder = if (dropdownItems.isEmpty()) "请录入" else "请选择"
Column(modifier = modifier) {
Text(label, style = AppTypography.black141428TextStyle)
Spacer(modifier = Modifier.height(20.dp))
if (dropdownItems.isEmpty()) {
CustomTextField(
value = value,
onValueChange = onValueChange,
placeholderValue = placeholder,
trailingLabel = trailingLabel,
inputType = inputType
)
} else {
CustomDropdownTextField(
value = value,
onValueChange = onValueChange,
dropdownItems = dropdownItems,
placeholderValue = placeholder,
)
}
}
}
@Composable
fun AddProductDialog(
modifier: Modifier = Modifier,
onCancelClick: () -> Unit,
onConfirmClick: () -> Unit,
onDismiss: () -> Unit,
content: @Composable () -> Unit
) {
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
usePlatformDefaultWidth = false, // 不使用平台默认宽度
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
)
) {
Surface(
modifier = Modifier
.padding(start = 60.dp, end = 60.dp, bottom = 40.dp) // 设置弹窗距离边框60dp
.wrapContentSize(),
shape = RoundedCornerShape(30.dp),
color = Color.White,
shadowElevation = 0.dp
) {
ToastUtils.ToastComposable()
Column(
modifier = Modifier
.fillMaxSize()
.padding(start = 152.dp, end = 152.dp, top = 56.dp, bottom = 30.dp),
) {
// 主要内容区域
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
content()
}
Spacer(modifier = Modifier.height(30.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
CustomOutlinedButton(
modifier = modifier
.width(300.dp)
.height(100.dp),
text = "取消",
fontSize = 36.sp,
onClick = onCancelClick
)
Spacer(modifier = Modifier.width(20.dp))
CustomButton(
modifier = modifier
.width(300.dp)
.height(100.dp),
text = "确定",
onClick = onConfirmClick,
borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white),
fontSize = 36.sp
)
}
}
}
}
}
/**
* 商品编辑
*/
@Composable
private fun SelfProductEditView(
viewModel: SelfProcurementViewModel,
) {
val selectedItem by viewModel.selectedItem.collectAsState()
val storeList = GlobalData.warehouseTypeList
var unitTypeList = emptyList<DictType>() // 界面中会重新获取
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(selectedItem) {
Timber.d("界面显示")
if (selectedItem == null) {
viewModel.updateSelectedItem(purchaseOrder = PurchaseWarehouseParam())
}
}
DisposableEffect(lifecycleOwner) {
Timber.d("开始传感器采集")
viewModel.startSensorScale()
onDispose {
Timber.d("停止传感器采集")
viewModel.stopSensorScale()
}
}
selectedItem?.let {
unitTypeList = selectedItem!!.unitDictTypeList
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(top = 20.dp, bottom = 20.dp, end = 20.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Spacer(modifier = Modifier.height(10.dp))
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = selectedItem!!.goodsNameStr,
style = AppTypography.black141428TextStyle.bold().withSize(30.sp)
)
Spacer(modifier = Modifier.height(20.dp))
RowInputLayout(
label = "仓库",
value = selectedItem!!.warehouseNameStr,
dropdownItems = storeList,
onValueChange = { value ->
selectedItem.let {
viewModel.updateSelectedItem(
selectedItem!!.copy(
// warehouseName = value,
warehouseId = storeList.find { it.value == value }?.id ?: 0
)
)
}
})
RowInputLayout(
label = "采购单位",
value = selectedItem!!.unitNameStr,
dropdownItems = unitTypeList,
onValueChange = { value ->
val selectUnitType =
selectedItem!!.unitList!!.find { it.purchaseUnitName == value }
if (selectUnitType != null) {
viewModel.updateSelectedItem(
selectedItem!!.copy(
unitName = value,
buyToInventoryValue = selectUnitType.buyToInventoryValue ?: "",
goodPurId = selectUnitType.businessUnitId?.toInt() ?: 0,
selectUnitType = selectUnitType
)
)
}
})
RowInputLayout(
label = "采购数量",
value = selectedItem!!.goodsCountStr,
inputType = InputType.Decimal,
isInitUpdate = true,
onValueChange = { value ->
Timber.d("采购数量 onValueChange value = $value")
viewModel.updateCountInputState(value.isNotEmpty())
selectedItem.let {
viewModel.updateSelectedItem(selectedItem!!.copy(goodsCount = value.toSafeDouble()))
}
},
trailingLabel = selectedItem!!.unitName,
)
RowInputLayout(
label = "采购单价",
value = selectedItem!!.goodsUnitPriceStr,
inputType = InputType.Decimal,
trailingLabel = "",
onValueChange = { value ->
selectedItem.let {
viewModel.updateSelectedItem(selectedItem!!.copy(goodsUnitPrice = value.toSafeDouble()))
}
})
RowInputLayout(
label = "采购金额",
value = selectedItem!!.goodsPriceStr,
inputType = InputType.Decimal,
trailingLabel = "",
onValueChange = { value ->
selectedItem.let {
viewModel.updateSelectedItem(selectedItem!!.copy(goodsPrice = value.toSafeDouble()))
}
})
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = "物品重量", style = AppTypography.black141428TextStyle)
CustomTextField(
modifier = Modifier
.width(290.dp)
.height(60.dp),
value = selectedItem!!.goodsWeightStr,
onValueChange = { },
enabled = false,
isInitUpdate = true,
textAlign = TextAlign.End,
trailingLabel = selectedItem!!.goodsWeightUnitStr,
leadingIcon = {
Button(
onClick = {},
modifier = Modifier
.padding(0.dp)
.fillMaxHeight(),
shape = RoundedCornerShape(10.dp),
border = BorderStroke(2.dp, color = Color.White),
colors = ButtonDefaults.buttonColors(
containerColor = Color.White,
contentColor = Black_141428
),
) { Text(text = "累计", style = AppTypography.black141428TextStyle) }
}
)
}
Spacer(modifier = Modifier.height(40.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(30.dp),
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
) {
CustomOutlinedButton(
modifier = Modifier
.width(245.dp)
.height(80.dp),
text = "取消",
fontSize = 30.sp,
textColor = colorResource(R.color.green),
borderColor = colorResource(R.color.green),
onClick = {
viewModel.updateSelectedItem(null)
})
CustomButton(
modifier = Modifier
.width(245.dp)
.height(80.dp),
text = "确定",
fontSize = 30.sp,
borderColor = colorResource(R.color.green),
onClick = {
if (selectedItem == null) return@CustomButton
val errInfo = selectedItem!!.hasNullField()
if (errInfo != null) {
ToastUtils.showToast(errInfo)
return@CustomButton
}
viewModel.addPurchaseItem(selectedItem!!)
})
}
}
}
}
@@ -0,0 +1,15 @@
package com.sw.inbound.ui.page
import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun TestScreen(modifier: Modifier.Companion, navController: NavHostController) {
}
@@ -0,0 +1,49 @@
package com.sw.inbound.ui.theme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.sw.inbound.R
object AppTypography {
val gray96a0aaTextStyle: TextStyle
@Composable get() = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF96A0AA)
)
val black141428TextStyle: TextStyle
@Composable get() = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = Black_141428
)
val grayTextStyle: TextStyle
@Composable
get() = TextStyle(
color = colorResource(R.color.gray),
fontSize = 24.sp,
fontWeight = FontWeight.Medium
)
val blackTextStyle: TextStyle
@Composable
get() = TextStyle(
color = colorResource(R.color.black),
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
val BlueTextStyle: TextStyle
@Composable
get() = TextStyle(
color = colorResource(R.color.blue),
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
}
@@ -0,0 +1,21 @@
package com.sw.inbound.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
val Black_141428 = Color(0xFF141428)
val Gray_A0A0B4 = Color(0XffA0A0B4)
val Gray_DCDCF0 = Color(0xFFDCDCF0)
val Gray_96A0AA = Color(0xFF96A0AA)
val GrayDivider = Color(0XFFDCDCF0)
val Green_009632 = Color(0xFF009632)
val Blue_0032C8 = Color(0xFF0032C8)
@@ -0,0 +1,57 @@
package com.sw.inbound.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun InboundTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content,
)
}
@@ -0,0 +1,34 @@
package com.sw.inbound.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
@@ -0,0 +1,91 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.R
@Composable
fun BottomActionBar(
leftText: String = "返回",
right1ButtonText: String = "清空物品",
right2ButtonText: String = "新增收货",
showRight1Button: Boolean = false,
onLeftButtonClick: () -> Unit,
onRight1ButtonClick: () -> Unit = {},
onRight2ButtonClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
.height(100.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
// 左侧图标+文字
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable {
onLeftButtonClick()
}
) {
Image(
painter = painterResource(R.mipmap.ic_home),
contentDescription = "返回",
modifier = Modifier.size(80.dp)
)
Spacer(modifier = Modifier.width(17.dp))
Text(
text = leftText,
style = TextStyle(
color = colorResource(R.color.black),
fontWeight = FontWeight.Bold,
fontSize = 30.sp
)
)
}
Row {
if (showRight1Button) {
CustomOutlinedButton(
modifier = modifier
.width(300.dp)
.height(100.dp),
text = right1ButtonText,
fontSize = 36.sp,
onClick = onRight1ButtonClick
)
Spacer(modifier = Modifier.width(20.dp))
}
CustomButton(
modifier = modifier
.width(300.dp)
.height(100.dp),
text = right2ButtonText,
onClick = onRight2ButtonClick,
borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white),
fontSize = 36.sp
)
}
}
}
@@ -0,0 +1,264 @@
package com.sw.inbound.ui.weight
import android.net.Uri
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.Toast
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.LocalLifecycleOwner
import coil.compose.AsyncImage
import com.sw.inbound.GlobalData
import com.sw.inbound.R
import com.sw.inbound.ext.dashedBorder
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.utils.FileUtils
import com.sw.inbound.utils.ToastUtils
import timber.log.Timber
import java.io.File
@Composable
fun CameraCaptureLayout(
modifier: Modifier = Modifier,
onCancelClick: () -> Unit = {},
onConfirmClick: () -> Unit = {}
) {
val context = LocalContext.current
var showCamera by remember { mutableStateOf(false) }
val lifecycleOwner = LocalLifecycleOwner.current
var photoUri by remember { mutableStateOf<Uri?>(null) }
// CameraX 控制器
val cameraController = remember {
LifecycleCameraController(context).apply {
setEnabledUseCases(
CameraController.IMAGE_CAPTURE or
CameraController.VIDEO_CAPTURE
)
}
}
Column(
modifier = modifier
.width(428.dp)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
if (showCamera) {
// CameraPreview()
// 摄像头预览
CameraPreview(
modifier = Modifier
.width(428.dp)
.height(321.dp), controller = cameraController
)
} else {
if (photoUri == null) {
// 图片预览区域
Column(
modifier = Modifier
.fillMaxWidth()
.height(321.dp)
.dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp)
.clickable {
showCamera = true
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(R.mipmap.ic_camera),
contentDescription = "默认图片",
modifier = Modifier
.width(96.dp)
.height(76.dp)
)
Spacer(modifier = Modifier.height(24.dp))
Text(
"图片采集",
style = AppTypography.grayTextStyle.copy(color = Color(0xFFB4B4C8))
)
}
} else {
Column(
modifier = Modifier
.fillMaxWidth()
.height(321.dp)
.dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
AsyncImage(
model = photoUri,
contentDescription = "照片",
modifier = Modifier.fillMaxSize()
)
}
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
CustomOutlinedButton(
modifier = Modifier
.weight(1f)
.height(80.dp),
text = "取消",
fontSize = 30.sp,
borderColor = colorResource(R.color.green),
textColor = colorResource(R.color.green),
onClick = {
Timber.d("点击取消")
showCamera = true
photoUri?.let {
FileUtils.deleteFileWithUri(context, photoUri!!)
}
photoUri = null
}
)
Spacer(modifier = Modifier.width(20.dp))
CustomButton(
modifier = Modifier
.weight(1f)
.height(80.dp),
text = "采集",
onClick = {
Timber.d("点击采集")
if (!showCamera) {
ToastUtils.showToast("请先开启图片预览")
return@CustomButton
}
val executor = ContextCompat.getMainExecutor(context)
val cacheDir = context.cacheDir
val photoFile = File.createTempFile(
"IMG_${System.currentTimeMillis()}",
".jpg",
cacheDir
)
val cacheOutputOptions =
ImageCapture.OutputFileOptions.Builder(photoFile).build()
cameraController.takePicture(
cacheOutputOptions,
executor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile)
photoUri = savedUri
Timber.d("photoUri = $photoUri")
Toast.makeText(context, "照片已保存", Toast.LENGTH_SHORT).show()
showCamera = false
GlobalData.imageUri = photoUri
}
override fun onError(exception: ImageCaptureException) {
GlobalData.imageUri = null
Toast.makeText(
context,
"拍照失败: ${exception.message}",
Toast.LENGTH_SHORT
).show()
}
})
},
borderColor = colorResource(R.color.green),
textColor = colorResource(R.color.white),
fontSize = 30.sp,
showButtonIcon = true
)
}
}
// 生命周期管理
DisposableEffect(lifecycleOwner) {
Timber.d("cameraController 释放")
cameraController.bindToLifecycle(lifecycleOwner)
onDispose { }
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CameraContent(modifier: Modifier, cameraController: LifecycleCameraController) {
val lifecycleOwner = LocalLifecycleOwner.current
Box(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
//在Compose中使用View系统中的PreviewView
AndroidView(
modifier = Modifier
.fillMaxSize(),
factory = { context ->
PreviewView(context).apply {
//设置布局宽度和高度占据全屏
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
//设置背景颜色
setBackgroundColor(android.graphics.Color.BLACK)
//设置渲染的实现模式
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
//设置缩放方式
scaleType = PreviewView.ScaleType.FILL_START
}.also {
it.controller = cameraController
cameraController.bindToLifecycle(lifecycleOwner)
}
},
onReset = {},
onRelease = {
Timber.d("cameraController.unbind()")
cameraController.unbind()
}
)
Image(
modifier = Modifier
.fillMaxSize()
.padding(30.dp),
painter = painterResource(R.mipmap.ic_scan),
contentDescription = "扫描"
)
}
}
@@ -0,0 +1,43 @@
package com.sw.inbound.ui.weight
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.sw.inbound.R
@Composable
fun CameraPreview(
controller: LifecycleCameraController,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
Box(modifier = modifier) {
AndroidView(
factory = { ctx ->
PreviewView(ctx).apply {
this.controller = controller
scaleType = PreviewView.ScaleType.FILL_CENTER // 控制预览缩放
}
},
modifier = Modifier.fillMaxSize()
)
Image(
modifier = Modifier
.fillMaxSize()
.padding(30.dp),
painter = painterResource(R.mipmap.ic_scan),
contentDescription = "扫描"
)
}
}
@@ -0,0 +1,60 @@
package com.sw.inbound.ui.weight
import android.Manifest
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
@OptIn(ExperimentalPermissionsApi::class)
@Preview
@Composable
fun CameraScreen() {
val cameraPermissionState =
rememberPermissionState(permission = Manifest.permission.CAMERA)
LaunchedEffect(key1 = Unit) {
if (!cameraPermissionState.status.isGranted && !cameraPermissionState.status.shouldShowRationale) {
cameraPermissionState.launchPermissionRequest()
}
}
if (cameraPermissionState.status.isGranted) {
//接受拍照的授权
// CameraContent()
} else {
//未授权,显示未授权的界面
NoPermissionScreen(cameraPermissionState)
}
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NoPermissionScreen(cameraPermissionState: PermissionState) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
val message = if (cameraPermissionState.status.shouldShowRationale) {
"未获取照相机权限导致无法使用照相机功能"
} else {
"请授权照相机的权限"
}
Text(message)
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
cameraPermissionState.launchPermissionRequest()
}) {
Text("请求授权")
}
}
}
@@ -0,0 +1,102 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.R
@Composable
fun CustomOutlinedButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
textColor: Color = colorResource(R.color.blue),
borderColor: Color = Color.Blue,
cornerRadius: Dp = 8.dp,
borderWidth: Dp = 1.dp,
fontWeight: FontWeight = FontWeight.Bold,
fontSize: TextUnit = 24.sp
) {
OutlinedButton(
onClick = onClick,
modifier = modifier,
shape = RoundedCornerShape(cornerRadius),
border = BorderStroke(borderWidth, color = borderColor),
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = textColor
)
) {
Text(
text = text,
style = TextStyle(
fontWeight = fontWeight,
fontSize = fontSize,
color = textColor
)
)
}
}
@Composable
fun CustomButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
textColor: Color = colorResource(R.color.white),
borderColor: Color = colorResource(R.color.green),
cornerRadius: Dp = 8.dp,
borderWidth: Dp = 1.dp,
fontWeight: FontWeight = FontWeight.Bold,
fontSize: TextUnit = 24.sp,
showButtonIcon: Boolean = false
) {
Button(
onClick = onClick,
modifier = modifier,
shape = RoundedCornerShape(cornerRadius),
border = BorderStroke(borderWidth, color = borderColor),
colors = ButtonDefaults.buttonColors(
containerColor = borderColor,
contentColor = textColor
)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (showButtonIcon) {
Image(
painter = painterResource(R.mipmap.ic_camera_small),
contentDescription = "采集"
)
Spacer(modifier = Modifier.width(13.dp))
}
Text(
text = text,
style = TextStyle(
fontWeight = fontWeight,
fontSize = fontSize,
color = textColor
)
)
}
}
}
@@ -0,0 +1,109 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MenuAnchorType.Companion.PrimaryNotEditable
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.R
import com.sw.inbound.model.response.DictType
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Black_141428
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomDropdownTextField(
value: String,
onValueChange: (String) -> Unit,
dropdownItems: List<DictType>,
modifier: Modifier = Modifier,
placeholderValue: String = "请选择",
textAlign: TextAlign = TextAlign.Start,
textStyle: TextStyle? = null,
) {
var expanded by remember { mutableStateOf(false) }
var newTextStyle = textStyle
?: LocalTextStyle.current.copy(
color = Black_141428,
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textAlign = textAlign
)
ExposedDropdownMenuBox(
modifier = modifier,
expanded = expanded,
onExpandedChange = { expanded = it },
) {
TextField(
value = value,
onValueChange = onValueChange,
readOnly = true,
textStyle = newTextStyle,
shape = RoundedCornerShape(10.dp),
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
},
colors = ExposedDropdownMenuDefaults.textFieldColors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
placeholder = {
Text(
modifier = modifier.fillMaxWidth(),
text = placeholderValue, style = AppTypography.gray96a0aaTextStyle.copy(
textAlign = textAlign
)
)
},
modifier = Modifier
.fillMaxWidth()
.background(Color.Transparent)
.border(
width = 2.dp,
color = colorResource(R.color.border_line),
shape = RoundedCornerShape(10.dp)
)
.menuAnchor(PrimaryNotEditable, true)
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
dropdownItems.forEach { item ->
DropdownMenuItem(
text = { Text(text = item.value, style = AppTypography.black141428TextStyle) },
onClick = {
onValueChange(item.value)
expanded = false
}
)
}
}
}
}
@@ -0,0 +1,95 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.sw.inbound.R
import com.sw.inbound.ui.theme.AppTypography
import timber.log.Timber
@Composable
fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) {
var searchText by remember { mutableStateOf("") }
val borderColor = Color(0xFFDCDCF0)
val focusManager = LocalFocusManager.current
// 搜索框
Box(
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.border(
width = 2.dp,
color = borderColor,
shape = RoundedCornerShape(10.dp) // 圆角边框
)
.background(Color.Transparent) // 透明背景
) {
TextField(
value = searchText,
onValueChange = {
searchText = it
onValueChange
},
modifier = Modifier
.fillMaxWidth()
.padding(end = 25.dp), // 为图标留出空间
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
placeholder = {
Text("输入物品名称", style = AppTypography.gray96a0aaTextStyle)
},
singleLine = true,
textStyle = AppTypography.black141428TextStyle,
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Search
),
keyboardActions = KeyboardActions(onSearch = {
focusManager.clearFocus()
onSearchClick(searchText)
}),
trailingIcon = {
Image(
painter = painterResource(R.mipmap.ic_search),
contentDescription = "搜索",
modifier = Modifier
.width(32.dp)
.height(32.dp)
.clickable {
Timber.d("搜索点击")
onSearchClick(searchText)
}
)
},
)
}
}
@@ -0,0 +1,102 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.background
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MenuAnchorType.Companion.PrimaryNotEditable
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.R
import com.sw.inbound.model.response.DictType
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomSpinner(
items: List<DictType>,
selectedItem: String,
onItemSelected: (DictType) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
modifier = modifier.background(
color = Color(0x80FFFFFF),
shape = RoundedCornerShape(10.dp) // 圆角背景
)
) {
TextField(
value = selectedItem,
onValueChange = {},
readOnly = true,
textStyle = TextStyle(
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
),
shape = RoundedCornerShape(10.dp),
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
},
colors = ExposedDropdownMenuDefaults.textFieldColors(
focusedContainerColor = colorResource(R.color.blue),
unfocusedContainerColor = colorResource(R.color.blue),
disabledContainerColor = colorResource(R.color.blue),
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
cursorColor = Color.White,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
focusedLabelColor = Color.White.copy(alpha = 0.8f),
unfocusedLabelColor = Color.White.copy(alpha = 0.8f),
focusedTrailingIconColor = Color.White,
unfocusedTrailingIconColor = Color.White
),
modifier = Modifier.menuAnchor(PrimaryNotEditable, true)
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(colorResource(R.color.blue))
.exposedDropdownSize(matchTextFieldWidth = true)
) {
items.forEach { item ->
DropdownMenuItem(
text = {
Text(
text = item.value,
style = TextStyle(
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
),
)
},
onClick = {
onItemSelected(item)
expanded = false
}
)
}
}
}
}
@@ -0,0 +1,210 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.ext.isValidFloat
import com.sw.inbound.ext.isValidNumber
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Black_141428
import com.sw.inbound.ui.theme.Gray_DCDCF0
import timber.log.Timber
//@Preview(showBackground = true)
//@Composable
//fun testTextField() {
// Column(verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(20.dp)) {
// CustomTextField(value = "123", onValueChange = {}, textAlign = TextAlign.Start)
// CustomTextField(value = "", onValueChange = {}, textAlign = TextAlign.Start)
// CustomTextField(value = "", onValueChange = {}, textAlign = TextAlign.End)
// CustomTextField(
// value = "123",
// onValueChange = {},
// textAlign = TextAlign.End,
// trailingLabel = "克"
// )
// CustomTextField(
// enabled = false,
// modifier = Modifier
// .height(60.dp),
// value = "123",
// onValueChange = {},
// textAlign = TextAlign.End,
// leadingIcon =
// {
// Button(
// onClick = {},
// modifier = Modifier
// .padding(0.dp)
// .fillMaxHeight(),
// shape = RoundedCornerShape(10.dp),
// border = BorderStroke(2.dp, color = Color.White),
// colors = ButtonDefaults.buttonColors(
// containerColor = Color.White,
// contentColor = Black_141428
// ),
// ) { Text(text = "累计", style = AppTypography.black141428TextStyle) }
// }
// )
// }
//
//}
enum class InputType {
Text, // 字符串
Number, // 数字
Decimal // 浮点
}
@Composable
fun CustomTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier.height(60.dp),
placeholderValue: String = "请录入",
trailingLabel: String? = null,
textAlign: TextAlign = TextAlign.Start,
textStyle: TextStyle? = null,
leadingIcon: @Composable (() -> Unit)? = null,
enabled: Boolean = true,
inputType: InputType = InputType.Text,
hasNext: Boolean = true,
isInitUpdate: Boolean = false,
keyboardOptions: KeyboardOptions? = null,
keyboardActions: KeyboardActions? = null
) {
val containerColor = if (enabled) Color.Transparent else Gray_DCDCF0
// var inputValue by remember { mutableStateOf(value) }
var isUserInput by remember { mutableStateOf(false) }
var inputValue by remember(if (isInitUpdate && !isUserInput) value else null) {
mutableStateOf(value)
}
val focusManager = LocalFocusManager.current
fun onEditingComplete() {
Timber.d("onEditingComplete inputValue = $inputValue")
onValueChange(inputValue)
}
var newTextStyle = textStyle
?: LocalTextStyle.current.copy(
color = Black_141428,
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textAlign = textAlign
)
Box(modifier = modifier) {
TextField(
enabled = enabled,
value = inputValue,
textStyle = newTextStyle,
onValueChange = { newValue ->
Timber.d("onValueChange newValue = $newValue")
isUserInput = newValue.isNotEmpty()
when (inputType) {
InputType.Number -> {
if (newValue.isEmpty() || newValue.isValidNumber()) {
inputValue = newValue
onEditingComplete()
}
}
InputType.Decimal -> {
if (newValue.isEmpty() || newValue.isValidFloat()) {
inputValue = newValue
onEditingComplete()
}
}
else -> {
inputValue = newValue
onEditingComplete()
}
}
},
modifier = Modifier
.fillMaxWidth()
.background(Color.Transparent)
.border(
width = 2.dp,
color = Gray_DCDCF0,
shape = RoundedCornerShape(10.dp)
)
.onFocusChanged(onFocusChanged = { focusState ->
{
onEditingComplete()
}
}),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = containerColor,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(10.dp),
placeholder = {
Text(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.wrapContentHeight(Alignment.CenterVertically),
text = placeholderValue,
textAlign = textAlign,
style = AppTypography.gray96a0aaTextStyle
)
},
leadingIcon = leadingIcon,
trailingIcon = trailingLabel?.let { label ->
{
Text(
text = trailingLabel,
modifier = Modifier
.fillMaxHeight()
.wrapContentHeight(Alignment.CenterVertically),
style = AppTypography.gray96a0aaTextStyle
)
}
},
keyboardOptions = keyboardOptions
?: KeyboardOptions.Default.copy(imeAction = if (hasNext) ImeAction.Next else ImeAction.Done),
keyboardActions = keyboardActions ?: KeyboardActions(onNext = {
focusManager.moveFocus(focusDirection = FocusDirection.Next)
onEditingComplete()
}, onDone = {
focusManager.clearFocus()
onEditingComplete()
})
)
}
}
@@ -0,0 +1,32 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.network.LoadingState
@Composable
fun GlobalLoading() {
if (LoadingState.isLoading) {
Dialog(
onDismissRequest = {},
properties = DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false
)
) {
Column {
CircularProgressIndicator(modifier = Modifier)
Spacer(modifier = Modifier.height(20.dp))
Text(text = "请稍等...")
}
}
}
}
@@ -0,0 +1,77 @@
package com.sw.inbound.ui.weight
import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.sw.inbound.ext.medium
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.ui.theme.AppTypography
/**
* 菜品识别组件
*/
@Composable
fun IdentityView(
list: List<SearchGoodsInfo.Record>,
showSearchView: Boolean = false,
onSearchClick: (String) -> Unit = {},
onOptionSelected: (SearchGoodsInfo.Record) -> Unit = {},
) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
// CameraX 控制器
val cameraController = remember {
LifecycleCameraController(context).apply {
setEnabledUseCases(CameraController.IMAGE_CAPTURE)
bindToLifecycle(lifecycleOwner)
}
}
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
CameraPreview(
modifier = Modifier
.width(397.dp)
.height(298.dp),
controller = cameraController
)
if (showSearchView) {
Spacer(modifier = Modifier.height(30.dp))
CustomSearchView(onSearchClick = onSearchClick)
}
Spacer(modifier = Modifier.height(30.dp))
if (showSearchView) {
SingleSelectButtonGroup(
modifier = Modifier
.height(64.dp)
.width(192.dp), options = list,
onOptionSelected = onOptionSelected
)
} else {
SingleSelectButtonGroup(
modifier = Modifier
.height(70.dp)
.width(215.dp),
options = list,
horizontalSpacing = 30.dp,
verticalSpacing = 20.dp,
onOptionSelected = onOptionSelected,
unSelectedTextStyle = AppTypography.blackTextStyle.medium()
)
}
}
}
@@ -0,0 +1,70 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.sw.inbound.model.response.DictType
import com.sw.inbound.ui.theme.AppTypography
/**
* 横向输入框 包含左侧文件+右侧输入框
*/
@Composable
fun RowInputLayout(
label: String,
value: String,
onValueChange: (String) -> Unit,
dropdownItems: List<DictType>? = null,
hasNext: Boolean = true,
inputType: InputType = InputType.Text,
trailingLabel: String? = null,
keyboardOptions: KeyboardOptions? = null,
keyboardActions: KeyboardActions? = null,
isInitUpdate: Boolean = false
) {
Row(
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.padding(horizontal = 30.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(label, style = AppTypography.black141428TextStyle)
// Spacer(modifier = Modifier.width(67.dp))
Spacer(modifier = Modifier.weight(1f))
if (dropdownItems == null) {
CustomTextField(
modifier = Modifier.width(290.dp),
value = value, onValueChange = onValueChange,
textAlign = TextAlign.End,
trailingLabel = trailingLabel,
hasNext = hasNext,
inputType = inputType,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
isInitUpdate = isInitUpdate
)
} else {
CustomDropdownTextField(
modifier = Modifier.width(290.dp),
value = value,
dropdownItems = dropdownItems,
onValueChange = onValueChange,
textAlign = TextAlign.End
)
}
}
}
@@ -0,0 +1,161 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.sw.inbound.R
import com.sw.inbound.ext.bold
import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Gray_DCDCF0
@Composable
fun ProductListView(
modifier: Modifier = Modifier,
productList: List<GoodsInfo>,
checkedItem: GoodsInfo? = null,
onItemCheckedClick: (GoodsInfo) -> Unit = {},
showClose: Boolean = false,
onCloseClick: (GoodsInfo) -> Unit = {},
) {
Column(
modifier = modifier
// .padding(horizontal = 30.dp)
) {
// 左侧列表标题
Row(
modifier = Modifier
.fillMaxWidth()
.padding(30.dp)
) {
CustomSingleRightText(
modifier = Modifier.weight(1f),
text = "名称",
style = AppTypography.gray96a0aaTextStyle.bold(),
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "单价(元)",
style = AppTypography.gray96a0aaTextStyle.bold()
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "数量",
style = AppTypography.gray96a0aaTextStyle.bold()
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(110.dp),
text = "金额(元)",
style = AppTypography.gray96a0aaTextStyle.bold()
)
if (showClose) {
Spacer(modifier = Modifier.width(78.dp))
}
}
// Spacer(modifier = Modifier.height(31.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
// Spacer(modifier = Modifier.height(31.dp))
// 左侧列表
LazyColumn() {
items(items = productList, key = { it.goodId!! }) { it ->
val checkedBg =
if (it.goodId == checkedItem?.goodId) Gray_DCDCF0 else Color.Transparent
Row(
modifier = Modifier
.background(color = checkedBg)
.padding(30.dp)
.clickable {
onItemCheckedClick(it)
}, verticalAlignment = Alignment.CenterVertically
) {
CustomSingleRightText(
modifier = Modifier.weight(1f),
text = it.goodNameStr,
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = it.recUnitPriceTaxInStr,
style = AppTypography.blackTextStyle
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "${it.receiveCountStr}${it.unitNameStr}",
style = AppTypography.blackTextStyle
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(110.dp),
text = it.recPriceExItemStr,
style = AppTypography.blackTextStyle
)
if (showClose) {
Spacer(modifier = Modifier.width(30.dp))
Image(
modifier = Modifier
.size(48.dp)
.clickable {
onCloseClick(it)
},
painter = painterResource(R.mipmap.ic_delete),
contentDescription = "删除"
)
}
}
// Spacer(modifier = Modifier.height(33.dp))
// if (productList.indexOf(it) != productList.lastIndex)
HorizontalDivider(
thickness = 1.dp,
color = colorResource(R.color.divider)
)
// Spacer(modifier = Modifier.height(33.dp))
}
}
}
}
@Composable
private fun CustomSingleRightText(
modifier: Modifier,
text: String,
style: TextStyle = AppTypography.blackTextStyle,
textAlign: TextAlign = TextAlign.End
) {
Text(
modifier = modifier,
text = text,
textAlign = textAlign,
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -0,0 +1,161 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.sw.inbound.R
import com.sw.inbound.ext.bold
import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Gray_DCDCF0
@Composable
fun SelfProcurementListItem(
modifier: Modifier = Modifier,
productList: List<PurchaseWarehouseParam>,
checkedItem: PurchaseWarehouseParam? = null,
onItemCheckedClick: (PurchaseWarehouseParam) -> Unit = {},
showClose: Boolean = false,
onCloseClick: (PurchaseWarehouseParam) -> Unit = {},
) {
Column(
modifier = modifier
// .padding(horizontal = 30.dp)
) {
// 左侧列表标题
Row(
modifier = Modifier
.fillMaxWidth()
.padding(30.dp)
) {
CustomSingleRightText(
modifier = Modifier.weight(1f),
text = "名称",
style = AppTypography.gray96a0aaTextStyle.bold(),
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "单价(元)",
style = AppTypography.gray96a0aaTextStyle.bold()
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "数量",
style = AppTypography.gray96a0aaTextStyle.bold()
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(110.dp),
text = "金额(元)",
style = AppTypography.gray96a0aaTextStyle.bold()
)
if (showClose) {
Spacer(modifier = Modifier.width(78.dp))
}
}
// Spacer(modifier = Modifier.height(31.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
// Spacer(modifier = Modifier.height(31.dp))
// 左侧列表
LazyColumn() {
items(items = productList, key = { it.goodsId }) { it ->
val checkedBg =
if (it.goodsId == checkedItem?.goodsId) Gray_DCDCF0 else Color.Transparent
Row(
modifier = Modifier
.background(color = checkedBg)
.padding(30.dp)
.clickable {
onItemCheckedClick(it)
}, verticalAlignment = Alignment.CenterVertically
) {
CustomSingleRightText(
modifier = Modifier.weight(1f),
text = it.goodsNameStr,
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = it.goodsUnitPriceStr,
style = AppTypography.blackTextStyle
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "${it.goodsCountStr}${it.unitNameStr}",
style = AppTypography.blackTextStyle
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(110.dp),
text = it.goodsPriceStr,
style = AppTypography.blackTextStyle
)
if (showClose) {
Spacer(modifier = Modifier.width(30.dp))
Image(
modifier = Modifier
.size(48.dp)
.clickable {
onCloseClick(it)
},
painter = painterResource(R.mipmap.ic_delete),
contentDescription = "删除"
)
}
}
// Spacer(modifier = Modifier.height(33.dp))
// if (productList.indexOf(it) != productList.lastIndex)
HorizontalDivider(
thickness = 1.dp,
color = colorResource(R.color.divider)
)
// Spacer(modifier = Modifier.height(33.dp))
}
}
}
}
@Composable
private fun CustomSingleRightText(
modifier: Modifier,
text: String,
style: TextStyle = AppTypography.blackTextStyle,
textAlign: TextAlign = TextAlign.End
) {
Text(
modifier = modifier,
text = text,
textAlign = textAlign,
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -0,0 +1,112 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.ui.theme.AppTypography
@Preview(showBackground = true)
@Composable
fun testSingleGroup() {
val list = mutableListOf<SearchGoodsInfo.Record>()
list.add(SearchGoodsInfo.Record(goodsName = "胶东大白菜胶东大白菜胶东大白菜胶东大白菜"))
for (i in 1..20) {
list.add(SearchGoodsInfo.Record(goodsName = "胶东大白菜$i"))
}
// val list = arrayListOf<String>(
// "胶东大白菜",
// "玉田尖白菜1",
// "玉田尖白菜2",
// "玉田尖白菜3",
// "玉田尖白菜4"
// )
// SingleSelectButtonGroup(list)
}
@Composable
fun SingleSelectButtonGroup(
options: List<SearchGoodsInfo.Record>,
modifier: Modifier = Modifier,
onOptionSelected: (SearchGoodsInfo.Record) -> Unit,
horizontalSpacing: Dp = 13.dp,
verticalSpacing: Dp = 13.dp,
unSelectedTextStyle: TextStyle = AppTypography.gray96a0aaTextStyle
) {
val context = LocalContext.current
var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") }
// 定义颜色
val selectedColor = Color(0xFFD9E3F9) // 选中颜色 #D9E3F9
val unselectedColor = Color(0xFFDCDCF0) // 未选中颜色 #DCDCF0
LazyVerticalGrid(
columns = GridCells.Fixed(2), // 每行2列
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing), // 水平间距13dp
verticalArrangement = Arrangement.spacedBy(verticalSpacing) // 垂直间距13dp
) {
items(items = options) { option ->
Box(
modifier = modifier
.border(
width = 2.dp,
shape = RoundedCornerShape(10.dp),
color = if (option == selectedOption) selectedColor else unselectedColor,
)
.clip(RoundedCornerShape(10.dp))
.background(if (option == selectedOption) selectedColor else Color.Transparent)
.clickable {
selectedOption = option
onOptionSelected(option)
},
contentAlignment = Alignment.Center
// .padding(vertical = 20.dp) // 垂直内边距
) {
Text(
text = option.goodsNameStr,
style = if (option == selectedOption) AppTypography.BlueTextStyle else unSelectedTextStyle,
modifier = Modifier
// .width(192.dp)
// .height(64.dp)
.wrapContentWidth()
.padding(horizontal = 10.dp), // 水平内边距
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
softWrap = false
)
}
}
}
}
@@ -0,0 +1,112 @@
package com.sw.inbound.ui.weight
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.R
import com.sw.inbound.ext.medium
import com.sw.inbound.model.response.User
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.utils.DateTimeUtils
import kotlinx.coroutines.delay
@OptIn(ExperimentalFoundationApi::class)
@Preview(
widthDp = 1920,
heightDp = 1080,
showBackground = true
)
@Composable
fun TopTitleBar(
modifier: Modifier = Modifier,
title: String = "采购单入库",
user: User? = null,
onLogoutClick: () -> Unit = {}
) {
var currentTime by remember { mutableStateOf(DateTimeUtils.getChineseDateString()) }
// 每秒更新一次时间
LaunchedEffect(Unit) {
while (true) {
delay(1000) // 1秒间隔
currentTime = DateTimeUtils.getChineseDateString()
}
}
Row(
modifier = modifier
.fillMaxWidth()
.padding(start = 60.dp, end = 60.dp, top = 22.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = title,
style = TextStyle(
fontWeight = FontWeight.Bold,
color = colorResource(R.color.title),
fontSize = 36.sp
),
modifier = Modifier.weight(1f)
)
if (user != null) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(60.dp)
.clickable {
onLogoutClick()
}) {
Text(text = user.name ?: "用户", style = AppTypography.blackTextStyle.medium())
Spacer(modifier = Modifier.width(21.dp))
Image(
modifier = Modifier.size(60.dp),
painter = painterResource(R.mipmap.ic_logout),
contentDescription = "退出"
)
}
} else {
Text(
modifier = Modifier.combinedClickable(
onClick = {},
onLongClick = {
onLogoutClick()
}
),
text = currentTime,
style = TextStyle(
fontWeight = FontWeight.Medium,
color = colorResource(R.color.black),
fontSize = 24.sp
)
)
}
}
}
@@ -0,0 +1,142 @@
package com.sw.inbound.ui.weight.dialog
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.R
import com.sw.inbound.ext.bold
import com.sw.inbound.ext.withColor
import com.sw.inbound.ext.withSize
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.ui.weight.CustomOutlinedButton
@Composable
fun ReceiptTipDialog(
modifier: Modifier = Modifier,
isWarn: Boolean = false,
onCancelClick: () -> Unit,
onConfirmClick: (Boolean) -> Unit,
onDismiss: () -> Unit = {},
) {
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
usePlatformDefaultWidth = false, // 不使用平台默认宽度
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
)
) {
Surface(
modifier = Modifier
.width(1190.dp)
.height(840.dp)
.wrapContentSize(),
shape = RoundedCornerShape(30.dp),
color = Color.White,
shadowElevation = 0.dp
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(30.dp),
) {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Image(
painter = if (isWarn) painterResource(R.mipmap.ic_tip_warn) else painterResource(
R.mipmap.ic_tip_success
), contentDescription = "提示"
)
Spacer(modifier = Modifier.height(60.dp))
Text(
text = if (isWarn) "收货数量与采购量不一致" else "核对无误,确认收货",
style = AppTypography.black141428TextStyle.bold().withSize(48.sp)
)
if (isWarn) {
Spacer(modifier = Modifier.height(29.dp))
Text(
text = "请确认实际收货数量,或选择部分收货处理",
style = AppTypography.black141428TextStyle.withColor(
color = Color(0xFF999999)
)
)
}
}
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
Spacer(modifier = Modifier.height(30.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
CustomOutlinedButton(
modifier = modifier
.width(180.dp)
.height(90.dp),
text = "返回",
fontSize = 36.sp,
onClick = onCancelClick
)
Spacer(modifier = Modifier.width(12.dp))
if (isWarn) {
CustomButton(
modifier = modifier
.width(260.dp)
.height(90.dp),
text = "部分收货",
onClick = {
onConfirmClick(false)
},
borderColor = colorResource(R.color.green),
textColor = colorResource(R.color.white),
fontSize = 36.sp
)
Spacer(modifier = Modifier.width(12.dp))
}
CustomButton(
modifier = modifier
.width(260.dp)
.height(90.dp),
text = "确定收货",
onClick = {
onConfirmClick(true)
},
borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white),
fontSize = 36.sp
)
}
}
}
}
}