ContainerItems.kt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. package com.example.pda.ui
  2. import androidx.compose.foundation.clickable
  3. import androidx.compose.runtime.*
  4. import androidx.compose.ui.platform.LocalConfiguration
  5. import androidx.compose.ui.Modifier
  6. import androidx.compose.foundation.layout.*
  7. import androidx.compose.foundation.lazy.LazyColumn
  8. import androidx.compose.foundation.lazy.itemsIndexed
  9. import androidx.compose.material.icons.Icons
  10. import androidx.compose.material.icons.filled.ArrowBack
  11. import androidx.compose.material.icons.filled.Clear
  12. import androidx.compose.material.icons.filled.Delete
  13. import androidx.compose.material3.*
  14. import androidx.compose.ui.graphics.Color
  15. import androidx.compose.ui.res.painterResource
  16. import androidx.compose.ui.unit.dp
  17. import androidx.compose.material.icons.filled.Search
  18. import com.example.pda.R
  19. import com.example.pda.ui.viewmodel.InventoryViewModel
  20. import androidx.compose.material3.SnackbarHostState
  21. import androidx.compose.material3.Scaffold
  22. import androidx.compose.runtime.Composable
  23. import androidx.compose.runtime.LaunchedEffect
  24. import androidx.compose.runtime.collectAsState
  25. import androidx.compose.runtime.mutableStateOf
  26. import androidx.compose.runtime.remember
  27. import androidx.compose.runtime.rememberCoroutineScope
  28. import androidx.compose.ui.Alignment
  29. import androidx.lifecycle.viewmodel.compose.viewModel
  30. import com.example.pda.model.ContainerItemDetail
  31. import kotlinx.coroutines.launch
  32. // 托盘详情界面 - 修复关键问题
  33. @OptIn(ExperimentalMaterial3Api::class)
  34. @Composable
  35. fun ContainerItems(
  36. container: String,
  37. onBack: () -> Unit,
  38. navToOutOperation: (String) -> Unit
  39. ) {
  40. val viewModel: InventoryViewModel = viewModel()
  41. val uiState by viewModel.uiState.collectAsState()
  42. val containerDetails by viewModel.containerItemsDetails.collectAsState()
  43. val snackbarHostState = remember { SnackbarHostState() }
  44. val coroutineScope = rememberCoroutineScope()
  45. val containerState = remember(container) { mutableStateOf(container) }
  46. var showInput by remember {
  47. mutableStateOf(container.isBlank())
  48. }
  49. val inputIp = remember { mutableStateOf("") }
  50. // 自动加载数据
  51. LaunchedEffect(container) {
  52. if (container.isNotBlank()) {
  53. viewModel.getContainerDetail(container)
  54. }
  55. }
  56. // 状态处理
  57. LaunchedEffect(uiState) {
  58. when (val currentState = uiState) {
  59. is InventoryViewModel.UiState.Success -> {
  60. coroutineScope.launch {
  61. snackbarHostState.showSnackbar(currentState.message)
  62. viewModel.resetState()
  63. }
  64. }
  65. is InventoryViewModel.UiState.Error -> {
  66. coroutineScope.launch {
  67. snackbarHostState.showSnackbar("错误:${currentState.message}")
  68. viewModel.resetState()
  69. }
  70. }
  71. else -> {}
  72. }
  73. }
  74. Scaffold(
  75. snackbarHost = { SnackbarHost(snackbarHostState) },
  76. topBar = {
  77. TopAppBar(
  78. navigationIcon = {
  79. IconButton(onClick = onBack) {
  80. Icon(Icons.Default.ArrowBack, "返回")
  81. }
  82. },
  83. title = {
  84. Row(verticalAlignment = Alignment.CenterVertically) {
  85. Icon(
  86. painter = painterResource(id = R.drawable.logo),
  87. contentDescription = "PDA Logo",
  88. modifier = Modifier.size(40.dp),
  89. tint = MaterialTheme.colorScheme.surfaceTint
  90. )
  91. Spacer(Modifier.width(8.dp))
  92. Text("信泰PDA—托盘详情")
  93. }
  94. },
  95. actions = {
  96. // 出库操作按钮
  97. IconButton(
  98. onClick = {
  99. if (containerDetails.isNotEmpty()) {
  100. navToOutOperation(containerState.value)
  101. } else {
  102. coroutineScope.launch {
  103. snackbarHostState.showSnackbar("没有可出库的批次")
  104. }
  105. }
  106. }
  107. ) {
  108. Icon(
  109. painter = painterResource(id = R.drawable.ic_out),
  110. contentDescription = "出库操作",
  111. tint = Color.White
  112. )
  113. }
  114. },
  115. colors = TopAppBarDefaults.topAppBarColors(
  116. containerColor = Color(0xFFBCD0C5),
  117. titleContentColor = MaterialTheme.colorScheme.onPrimary,
  118. actionIconContentColor = MaterialTheme.colorScheme.onPrimary
  119. )
  120. )
  121. }
  122. ) { innerPadding ->
  123. Column(
  124. modifier = Modifier
  125. .padding(innerPadding)
  126. .fillMaxSize()
  127. .padding(16.dp)
  128. ) {
  129. // 输入区域
  130. if (showInput || containerState.value.isBlank()) {
  131. Row(
  132. modifier = Modifier
  133. .fillMaxWidth()
  134. .padding(bottom = 16.dp),
  135. verticalAlignment = Alignment.CenterVertically
  136. ) {
  137. OutlinedTextField(
  138. value = inputIp.value,
  139. onValueChange = { inputIp.value = it },
  140. label = { Text("请输入托盘编码") },
  141. modifier = Modifier.weight(1f),
  142. trailingIcon = {
  143. IconButton(
  144. onClick = {
  145. if (inputIp.value.isNotBlank()) {
  146. containerState.value = inputIp.value
  147. viewModel.getContainerDetail(containerState.value)
  148. showInput = false
  149. }
  150. }
  151. ) {
  152. Icon(Icons.Default.Search, "搜索")
  153. }
  154. }
  155. )
  156. }
  157. }
  158. // 托盘编码显示与操作
  159. Row(
  160. modifier = Modifier
  161. .fillMaxWidth()
  162. .padding(bottom = 16.dp),
  163. verticalAlignment = Alignment.CenterVertically
  164. ) {
  165. Text(
  166. text = "托盘编码:${containerState.value}",
  167. style = MaterialTheme.typography.titleLarge,
  168. modifier = Modifier.weight(1f)
  169. )
  170. if (!showInput) {
  171. IconButton(
  172. onClick = {
  173. containerState.value = ""
  174. showInput = true
  175. },
  176. ) {
  177. Icon(Icons.Default.Clear, "清除托盘编码")
  178. }
  179. }
  180. }
  181. // 显示批次数据
  182. ContainerItemList(data = containerDetails)
  183. }
  184. }
  185. }
  186. // 批次列表显示 - 优化版
  187. @Composable
  188. private fun ContainerItemList(data: List<ContainerItemDetail>) {
  189. if (data.isEmpty()) {
  190. Box(
  191. modifier = Modifier
  192. .fillMaxSize()
  193. .padding(16.dp),
  194. contentAlignment = Alignment.Center
  195. ) {
  196. Text("没有找到批次数据", style = MaterialTheme.typography.titleMedium)
  197. }
  198. } else {
  199. LazyColumn(
  200. modifier = Modifier.fillMaxSize(),
  201. verticalArrangement = Arrangement.spacedBy(12.dp)
  202. ) {
  203. itemsIndexed(data) { index, item ->
  204. OutOperationItemCard(
  205. index = index,
  206. item = item
  207. )
  208. }
  209. }
  210. }
  211. }
  212. // 批次项卡片 - 提取为独立组件
  213. @Composable
  214. private fun OutOperationItemCard(
  215. index: Int,
  216. item: ContainerItemDetail
  217. ) {
  218. val remaining = item.goods_in_qty - item.goods_out_qty
  219. Card(
  220. modifier = Modifier.fillMaxWidth(),
  221. elevation = CardDefaults.cardElevation(4.dp)
  222. ) {
  223. Column(
  224. modifier = Modifier.padding(16.dp)
  225. ) {
  226. Row(
  227. modifier = Modifier.fillMaxWidth(),
  228. horizontalArrangement = Arrangement.SpaceBetween
  229. ) {
  230. Text("#${index + 1}", style = MaterialTheme.typography.labelMedium)
  231. Text("items_: ${item.id}", style = MaterialTheme.typography.labelMedium)
  232. }
  233. Spacer(Modifier.height(6.dp))
  234. Divider()
  235. Spacer(Modifier.height(6.dp))
  236. Text(
  237. "批次: ${item.batch_number}",
  238. style = MaterialTheme.typography.titleMedium
  239. )
  240. Spacer(Modifier.height(6.dp))
  241. Row(
  242. modifier = Modifier.fillMaxWidth(),
  243. horizontalArrangement = Arrangement.SpaceBetween
  244. ) {
  245. Column {
  246. Text("物料编码", style = MaterialTheme.typography.labelSmall)
  247. Text(item.goods_code, style = MaterialTheme.typography.bodyMedium)
  248. }
  249. Column {
  250. Text("物料名称", style = MaterialTheme.typography.labelSmall)
  251. Text(item.goods_desc, style = MaterialTheme.typography.bodyMedium)
  252. }
  253. }
  254. Spacer(Modifier.height(2.dp))
  255. Divider(thickness = 1.5.dp)
  256. // 数量信息网格
  257. Row(
  258. modifier = Modifier.fillMaxWidth(),
  259. horizontalArrangement = Arrangement.SpaceAround
  260. ) {
  261. QuantityInfoCard(title = "组盘", value = item.goods_in_qty.toString(), backgroundColor = Color(0xFFE3F2FD))
  262. QuantityInfoCard(title = "出库", value = item.goods_out_qty.toString(), backgroundColor = Color(0xFFFFF8E1))
  263. QuantityInfoCard(title = "剩余", value = remaining.toString(), backgroundColor = Color(0xFFE8F5E9))
  264. }
  265. }
  266. }
  267. }
  268. // 数量信息卡片组件
  269. @Composable
  270. private fun QuantityInfoCard(title: String, value: String, backgroundColor: Color) {
  271. Card(
  272. modifier = Modifier.size(60.dp),
  273. colors = CardDefaults.cardColors(containerColor = backgroundColor)
  274. ) {
  275. Column(
  276. modifier = Modifier.padding(8.dp),
  277. horizontalAlignment = Alignment.CenterHorizontally,
  278. verticalArrangement = Arrangement.Center
  279. ) {
  280. Text(title, style = MaterialTheme.typography.labelMedium)
  281. Spacer(Modifier.height(2.dp))
  282. Text(value, style = MaterialTheme.typography.titleMedium)
  283. }
  284. }
  285. }