瀏覽代碼

出库任务Items查看

flower_mr 2 周之前
父節點
當前提交
a7b796de9a

+ 4 - 0
.kotlin/errors/errors-1748428190257.log

@@ -0,0 +1,4 @@
+kotlin version: 2.0.21
+error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
+    1. Kotlin compile daemon is ready
+

二進制
app/release/app-release.apk


二進制
app/release/baselineProfiles/0/app-release.dm


二進制
app/release/baselineProfiles/1/app-release.dm


+ 81 - 0
app/src/main/java/com/example/pda/model/ApiResponse.kt

@@ -0,0 +1,81 @@
+package com.example.pda.model
+
+import com.google.gson.annotations.SerializedName
+
+
+// 添加对应的数据类
+
+data class ApiResponse(
+    @SerializedName("code")
+    val code: Int,
+
+    @SerializedName("message")
+    val message: String,
+
+    @SerializedName("data")
+    val data: ContainerDetail
+)
+
+data class ContainerDetail(
+    @SerializedName("count")
+    val count: Int,
+
+    @SerializedName("results")
+    val results: List<MaterialResult>,
+
+    @SerializedName("batch_totals")
+    val batch_totals: List<BatchTotal>
+)
+
+data class MaterialResult(
+    @SerializedName("goods_code")
+    val goods_code: String,
+
+    @SerializedName("goods_desc")
+    val goods_desc: String,
+
+    @SerializedName("current_qty")
+    val current_qty: Int,
+
+    @SerializedName("group_qty")
+    val group_qty: Int,
+
+    @SerializedName("bound_number")
+    val bound_number: String,
+
+    @SerializedName("create_time")
+    val create_time: String
+
+)
+
+data class BatchTotal(
+    @SerializedName("bound_number")
+    val bound_number: String,
+
+    @SerializedName("total_batch_qty")
+    val total_batch_qty: Int
+)
+
+
+
+
+data class ContainerItemDetail(
+    @SerializedName("id")
+    val id:Int ,
+
+    @SerializedName("batch")
+    val batch_number:String,
+
+    @SerializedName("goods_code")
+    val goods_code: String,
+
+    @SerializedName("goods_desc")
+    val goods_desc: String,
+
+    @SerializedName("goods_qty")
+    val goods_in_qty: Int,
+
+    @SerializedName("out_goods_qty")
+    val goods_out_qty: Int
+
+)

+ 1 - 1
app/src/main/java/com/example/pda/model/BaseResponse.kt

@@ -11,4 +11,4 @@ data class BaseResponse<T>(
 
     @SerializedName("data")
     val data: T? = null
-)
+)

+ 2 - 0
app/src/main/java/com/example/pda/model/InventoryItem.kt

@@ -12,3 +12,5 @@ data class InventoryItem(
     @SerializedName("goods_qty")
     val specification: Int
 )
+
+

+ 56 - 41
app/src/main/java/com/example/pda/navigation/NavGraph.kt

@@ -6,81 +6,96 @@ import androidx.navigation.NavType
 import androidx.navigation.compose.composable
 import androidx.navigation.navArgument
 import androidx.navigation.navigation
-import com.example.pda.ui.DetailsScreen
-import com.example.pda.ui.MainScreen
-import com.example.pda.ui.ScanScreen
-import com.example.pda.ui.PingScreen
-import com.example.pda.ui.ContainerScreen
-import com.example.pda.ui.LoginScreen
-import com.example.pda.ui.SettingScreen
-import com.example.pda.ui.UpStationScreen
+import com.example.pda.ui.*
 
-// 定义应用程序的导航图
 fun NavGraphBuilder.appNavGraph(navController: NavController) {
-    // 创建一个导航图,起始目标为 "main",路由为 "root"
     navigation(startDestination = "login", route = "root") {
-        composable("login"){
-            LoginScreen (
-                onSetScreen = { navController.navigate("set")},
-                onMainScreen = {navController.navigate("main")}
+        composable("login") {
+            LoginScreen(
+                onSetScreen = { navController.navigate("set") },
+                onMainScreen = { navController.navigate("main") }
             )
         }
-        composable ("set"){
-            SettingScreen (
-                onPingScreen = { navController.navigate("ping") },
-
+        composable("set") {
+            SettingScreen(
+                onPingScreen = { navController.navigate("ping") }
             )
         }
-        // 定义 "main" 路由,显示主屏幕
         composable("main") {
             MainScreen(
-                onInventoryScan = { navController.navigate("scan") }, // 点击扫描按钮时导航到 "scan" 路由
-                onShelfScan = { navController.navigate("container") }, // 点击扫描按钮时导航到 "scan" 路由
-                onSetScreen = { navController.navigate("set") }, //
-                onUpStationScreen = { navController.navigate("upstation")},
+                onInventoryScan = { navController.navigate("scan") },
+                onShelfScan = { navController.navigate("container") },
+                onSetScreen = { navController.navigate("set") },
+                onUpStationScreen = { navController.navigate("upstation") },
+                onOutTaskScreen = { navController.navigate("outItems") }
             )
         }
         composable("upstation") {
-            UpStationScreen (
-                onBack = {navController.popBackStack()} ,
-                onSetScreen = { navController.navigate("set") }, //
-
+            UpStationScreen(
+                onBack = { navController.popBackStack() },
+                onSetScreen = { navController.navigate("set") }
             )
         }
         composable("ping") {
             PingScreen(
-                onMainScreen =  { navController.popBackStack() } // 点击返回按钮时弹出当前栈顶
+                onMainScreen = { navController.popBackStack() }
             )
         }
-        // 定义 "scan" 路由,显示扫描屏幕
         composable("scan") {
             ScanScreen(
-                onBack = { navController.popBackStack() }, // 点击返回按钮时弹出当前栈顶
+                onBack = { navController.popBackStack() },
                 onResult = { container, result ->
-                    navController.navigate("details/$container/$result") // 导航到 "details" 路由,传递结果参数
+                    navController.navigate("details/$container/$result")
                 }
             )
         }
         composable("container") {
-            ContainerScreen (
-                onBack = { navController.popBackStack() } // 点击返回按钮时弹出当前栈顶
+            ContainerScreen(
+                onBack = { navController.popBackStack() },
+                onContainerItems = { container ->
+                    navController.navigate("containerItems?container=$container")
+                }
+            )
+        }
+        // 这里使用 query 参数
+        composable(
+            route = "containerItems?container={container}",
+            arguments = listOf(
+                navArgument("container") {
+                    type = NavType.StringType
+                    defaultValue = ""
+                }
+            )
+        ) { backStackEntry ->
+            val container = backStackEntry.arguments?.getString("container") ?: ""
+            ContainerItems(
+                container = container,
+                onBack = { navController.popBackStack() }
             )
-
         }
-        // 定义 "details" 路由,显示详情屏幕,需要传递结果参数
+
         composable(
             route = "details/{container}/{result}",
             arguments = listOf(
-                navArgument("container") { type = NavType.StringType }, // 定义参数类型为字符串
-                navArgument("result") { type = NavType.StringType } // 定义参数类型为字符串
+                navArgument("container") { type = NavType.StringType },
+                navArgument("result") { type = NavType.StringType }
             )
         ) { backStackEntry ->
             DetailsScreen(
                 container = backStackEntry.arguments?.getString("container") ?: "",
-                result = backStackEntry.arguments?.getString("result") ?: "", // 获取并传递结果参数
-                onMainScreen = { navController.navigate("main") }, // 点击返回按钮时导航到 "main" 路由
-                onShelfScan = { navController.navigate("scan") }, // 点击上架按钮时导航到 "scan" 路由
-                onBack = { navController.popBackStack() } // 点击返回按钮时弹出当前栈顶
+                result = backStackEntry.arguments?.getString("result") ?: "",
+                onMainScreen = { navController.navigate("main") },
+                onShelfScan = { navController.navigate("scan") },
+                onBack = { navController.popBackStack() }
+            )
+        }
+
+        composable("outItems") {
+            OutTaskScreen(
+                onBack = { navController.popBackStack() },
+                onContainerItems = { container ->
+                    navController.navigate("containerItems?container=$container")
+                }
             )
         }
     }

+ 28 - 3
app/src/main/java/com/example/pda/network/ApiService.kt

@@ -1,14 +1,16 @@
 package com.example.pda.network
 
 import com.example.pda.model.BaseResponse
-import com.example.pda.model.InventoryItem
+import com.example.pda.model.ApiResponse
 import com.example.pda.model.loginItem
 import com.example.pda.model.loginSuccessResult
 import com.example.pda.model.ContainerData
+import com.example.pda.model.ContainerItemDetail
 import retrofit2.Response
 import retrofit2.http.Body
 import retrofit2.http.GET
 import retrofit2.http.POST
+import retrofit2.http.Query
 import retrofit2.http.QueryMap
 
 // 定义了一个接口 ApiService,用于与服务器进行库存相关的网络请求
@@ -26,9 +28,32 @@ interface ApiService {
     ): Response<BaseResponse<Unit>>
 
     // 从服务器获取库存列表的接口
-    @GET("asn/list/")
+    @GET("container/pdadetail/")
     suspend fun getInventoryList(
         @QueryMap params: Map<String, String>
-    ): Response<BaseResponse<List<InventoryItem>>>
+    ): Response<ApiResponse>
 
+    // PDA获取托盘items(未出库的)具体
+    @GET("container/pda/containerdetail/")
+    suspend fun getContainerItems(
+        @QueryMap params: Map<String, String>
+    ): Response<BaseResponse<List<ContainerItemDetail>>>
+
+    // 确认出库
+    @GET("container/pda/confirmdetail/")
+    suspend fun confirmOutItems(
+        @Query("container_code") container: String
+    ): Response<BaseResponse<Unit>>
+
+    // 取消出库
+    @GET("container/pda/canceldetail/")
+    suspend fun cancelOutItems(
+        @Query("container_code") container: String
+    ): Response<BaseResponse<Unit>>
+
+    // 获取出库详情
+    @GET("container/pda/outdetail/")
+    suspend fun getOutItems(
+        @Query("container_code") container: String
+    ): Response<BaseResponse<List<ContainerItemDetail>>>
 }

+ 141 - 20
app/src/main/java/com/example/pda/network/NetworkHelper.kt

@@ -1,14 +1,15 @@
 package com.example.pda.network
 
 import android.util.Log
-import com.example.pda.model.InventoryItem
+import com.example.pda.model.ApiResponse
+import com.example.pda.model.BaseResponse
 import com.example.pda.model.ContainerData
+import com.example.pda.model.ContainerItemDetail
 import com.example.pda.model.loginItem
 import com.example.pda.model.successItem
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
-import kotlin.collections.orEmpty
 
 // 网络请求助手对象,用于处理与服务器的交互
 object NetworkHelper {
@@ -33,7 +34,7 @@ object NetworkHelper {
                             onSuccess()
                         } else {
                             // 否则调用错误回调并传递错误信息
-                            onError(it.message ?: "Unknown error")
+                            onError(it.message)
                         }
                     }
                 } else {
@@ -49,34 +50,155 @@ object NetworkHelper {
     }
 
     // 获取库存列表的方法
+    // 修改后的 NetworkHelper 方法
     fun getIdList(
         container_code: String,
-        onSuccess: (List<InventoryItem>) -> Unit, // 成功后的回调函数,接受一个InventoryItem列表作为参数
-        onError: (String) -> Unit // 失败后的回调函数
+        onSuccess: (ApiResponse) -> Unit,  // 修改为接收完整响应对象
+        onError: (String) -> Unit
     ) {
-        val params = mapOf("code" to container_code)
-        // 在IO线程中启动协程以执行网络请求
+        val params = mapOf("container_code" to container_code)
         CoroutineScope(Dispatchers.IO).launch {
             try {
                 val response = RetrofitClient.instance.getInventoryList(params)
-                // 检查HTTP响应是否成功
                 if (response.isSuccessful) {
-                    // 如果响应体存在且code为200,则调用成功回调,并将数据传递给回调函数
+                    response.body()?.let { apiResponse ->
+                        if (apiResponse.code == 200) {
+                            onSuccess(apiResponse)  // 传递完整响应对象
+                        } else {
+                            onError(apiResponse.message)
+                        }
+                    }
+                } else {
+                    onError("提示: ${response.message()}")
 
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Get failed", e)
+                onError("Network error: ${e.localizedMessage}")
+            }
+        }
+    }
+
+    fun getItemsDetail(
+        container_code: String,
+        onSuccess: (List<ContainerItemDetail>) -> Unit,
+        onError: (String) -> Unit
+
+    ) {
+        val params = mapOf("container_code" to container_code)
+        CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val response = RetrofitClient.instance.getContainerItems(params)
+                if (response.isSuccessful) {
                     response.body()?.let {
                         if (it.code == 200) {
-                            onSuccess(it.data.orEmpty())
+                            if (it.data != null) {
+                                onSuccess(it.data)
+                            } else {
+                                onError(it.message)
+                            }
                         } else {
-                            // 否则调用错误回调并传递错误信息
-                            onError(it.message ?: "Unknown error")
+                            onError(it.message)
                         }
                     }
                 } else {
-                    // 如果HTTP响应不成功,则调用错误回调并传递HTTP错误代码
-                    onError("HTTP Error: ${response.code()}")
+                    onError("提示: ${response.message()}")
+
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Get failed", e)
+                onError("Network error: ${e.localizedMessage}")
+            }
+        }
+    }
+
+    fun confirmOutItems(
+        container_code: String,
+        onSuccess: (String) -> Unit,
+        onError: (String) -> Unit
+    ){
+        CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val response = RetrofitClient.instance.confirmOutItems(container_code)
+                if (response.isSuccessful) {
+                    response.body()?.let {
+                        if (it.code == 200) {
+                            if (it.data != null) {
+                                onSuccess(it.message)
+                            } else {
+                                onError(it.message)
+                            }
+                        } else {
+                            onError(it.message)
+                        }
+                    }
+                } else {
+                    onError("提示: ${response.message()}")
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Get failed", e)
+                onError("Network error: ${e.localizedMessage}")
+            }
+        }
+
+    }
+
+    fun cancelOutItems(
+        container_code: String,
+        onSuccess: (String) -> Unit,
+        onError: (String) -> Unit
+    ){
+        CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val response = RetrofitClient.instance.cancelOutItems(container_code)
+                if (response.isSuccessful) {
+                    response.body()?.let {
+                        if (it.code == 200) {
+                            if (it.data != null) {
+                                onSuccess(it.message)
+                            } else {
+                                onError(it.message)
+                            }
+                        } else {
+                            onError(it.message)
+                        }
+                    }
+                } else {
+                    onError("提示: ${response.message()}")
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Get failed", e)
+                onError("Network error: ${e.localizedMessage}")
+            }
+        }
+
+    }
+
+    fun getOutItemsDetail(
+        container_code: String,
+        onSuccess: (List<ContainerItemDetail>) -> Unit,
+        onError: (String) -> Unit
+
+    ) {
+        CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val response = RetrofitClient.instance.getOutItems(container_code)
+                if (response.isSuccessful) {
+                    response.body()?.let {
+                        if (it.code == 200) {
+                            if (it.data != null) {
+                                onSuccess(it.data)
+                            } else {
+                                onError(it.message)
+                            }
+                        } else {
+                            onError(it.message)
+                        }
+                    }
+                } else {
+                    onError("提示: ${response.message()}")
                 }
             } catch (e: Exception) {
-                // 捕获异常并记录日志,之后调用错误回调并传递异常本地化信息
                 Log.e(TAG, "Get failed", e)
                 onError("Network error: ${e.localizedMessage}")
             }
@@ -84,11 +206,10 @@ object NetworkHelper {
     }
 
     fun userlogin(
-        data : loginItem,
+        data: loginItem,
         onSuccess: (successItem) -> Unit,
         onError: (String) -> Unit
-    )
-    {
+    ) {
         CoroutineScope(Dispatchers.IO).launch {
             try {
                 val response = RetrofitClient.instance.login(data)
@@ -97,7 +218,7 @@ object NetworkHelper {
                         if (it.code == 200) {
                             onSuccess(it.data)
                         } else {
-                            onError(it.message ?: "Unknown error")
+                            onError(it.message)
                         }
                     }
                 } else {
@@ -107,7 +228,7 @@ object NetworkHelper {
                 Log.e(TAG, "Login failed", e)
                 onError("Network error: ${e.localizedMessage}")
             }
-            }
+        }
 
     }
 }

+ 1 - 1
app/src/main/java/com/example/pda/network/RetrofitWCS.kt

@@ -5,7 +5,7 @@ import retrofit2.converter.gson.GsonConverterFactory
 
 object RetrofitWCS {
     // 私有常量 BASE_URL 定义了 API 的基础 URL
-    private const val BASE_URL = "http://192.168.18.67:1616/"
+    private const val BASE_URL = "http://192.168.18.200:1616/"
     // val instance 通过 lazy 委托实现懒加载,创建 ApiService 的单例实例
     val instance: ApiWCS by lazy {
         Retrofit.Builder()

+ 225 - 0
app/src/main/java/com/example/pda/ui/ContainerItems.kt

@@ -0,0 +1,225 @@
+package com.example.pda.ui
+
+import androidx.compose.foundation.clickable
+import androidx.compose.runtime.*
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.Modifier
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material3.*
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.material.icons.filled.Search
+import com.example.pda.R
+import com.example.pda.ui.viewmodel.InventoryViewModel
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.pda.model.ContainerItemDetail
+import kotlinx.coroutines.launch
+
+// 托盘屏幕,用于托盘扫描和物料信息显示
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+
+fun ContainerItems(
+    container: String, // 新增参数
+    onBack: () -> Unit,
+) {
+    val viewModel: InventoryViewModel = viewModel()
+    val uiState = viewModel.uiState.collectAsState()
+    val snackbarHostState = remember { SnackbarHostState() }
+    val coroutineScope = rememberCoroutineScope()
+    val inputIp = remember { mutableStateOf("") }
+    val containerState = remember { mutableStateOf(container) }
+    var showInput by remember { mutableStateOf(container.isBlank()) }
+
+
+    // 自动加载数据(当传入的 container 非空时)
+    LaunchedEffect(container) {
+        if (container.isNotBlank()) {
+            viewModel.getContainerDetail(container)
+        }
+    }
+
+    // 根据 viewModel 状态显示 Snackbar(保持不变)
+    LaunchedEffect(uiState.value) {
+        when (val currentState = uiState.value) {
+            is InventoryViewModel.UiState.Success -> {
+                coroutineScope.launch {
+                    snackbarHostState.showSnackbar(currentState.message)
+                    viewModel.resetState()
+                }
+            }
+
+            is InventoryViewModel.UiState.Error -> {
+                coroutineScope.launch {
+                    snackbarHostState.showSnackbar("错误:${currentState.message}")
+                    viewModel.resetState()
+                }
+            }
+
+            else -> {}
+        }
+    }
+
+    Scaffold(
+        snackbarHost = { SnackbarHost(snackbarHostState) },
+        topBar = {
+            TopAppBar(
+                navigationIcon = {
+                    IconButton(onClick = onBack) {
+                        Icon(Icons.Default.ArrowBack, "返回")
+                    }
+                },
+                title = {
+                    Row(verticalAlignment = Alignment.CenterVertically) {
+                        Icon(
+                            painter = painterResource(id = R.drawable.logo),
+                            contentDescription = "PDA Logo",
+                            modifier = Modifier.size(40.dp),
+                            tint = MaterialTheme.colorScheme.surfaceTint
+                        )
+                        Spacer(Modifier.width(8.dp))
+                        Text("信泰PDA—托盘详情")
+                    }
+                },
+                colors = TopAppBarDefaults.topAppBarColors(
+                    containerColor = Color(0xFFBCD0C5),
+                    titleContentColor = MaterialTheme.colorScheme.onPrimary,
+                    actionIconContentColor = MaterialTheme.colorScheme.onPrimary
+                )
+            )
+        }
+    ) { innerPadding ->
+        Column(
+            modifier = Modifier
+                .padding(innerPadding)
+                .fillMaxSize()
+                .padding(24.dp)
+        ) {
+
+            // 输入区域(仅当需要时显示)
+            if (showInput || containerState.value.isBlank()) {
+                Row(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .padding(8.dp),
+                    verticalAlignment = Alignment.CenterVertically
+                ) {
+                    TextField(
+                        value = inputIp.value,
+                        onValueChange = { inputIp.value = it },
+                        label = { Text("请输入托盘编码") },
+                    )
+                    IconButton(
+                        onClick = {
+                            containerState.value = inputIp.value
+                            viewModel.getContainerDetail(containerState.value)
+                            showInput = false
+                        },
+                    ) {
+                        Icon(Icons.Default.Search, "确定")
+                    }
+
+                }
+            }
+
+            // 托盘编码显示与删除按钮
+            Row(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(8.dp),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                Text(
+                    text = "托盘编码:${containerState.value}",
+                    modifier = Modifier.weight(1f)
+                )
+                IconButton(
+                    onClick = {
+                        containerState.value = ""
+                        showInput = true
+                    },
+                ) {
+                    Icon(Icons.Default.Delete, "删除")
+                }
+            }
+
+            // 显示批次数据
+            ContainerItemList(data = viewModel.containerItemsDetails.collectAsState().value)
+        }
+    }
+}
+
+
+@Composable
+private fun ContainerItemList(data: List<ContainerItemDetail>) {
+    LazyColumn(modifier = Modifier.fillMaxSize()) {
+        itemsIndexed(data) { index, item ->
+            Card(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(2.dp),
+                elevation = CardDefaults.cardElevation(2.dp)
+            ) {
+                Column(modifier = Modifier.padding(4.dp)) {
+                    Text(
+                        "id:${index + 1}",
+                        style = MaterialTheme.typography.bodySmall
+                    )
+                    Spacer(Modifier.height(1.dp))
+                    Divider(thickness = 0.8.dp)
+                    Text(
+                        "物料批次:${item.batch_number}",
+                        style = MaterialTheme.typography.titleMedium
+                    )
+                    Spacer(Modifier.height(1.dp))
+                    Divider(thickness = 1.2.dp)
+                    Row(
+                        horizontalArrangement = Arrangement.SpaceBetween,
+                        modifier = Modifier.fillMaxWidth()
+                    ) {
+                        Text(
+                            "物料编码:${item.goods_code}",
+                            style = MaterialTheme.typography.bodyMedium
+                        )
+                        Text(
+                            "物料名称:${item.goods_desc}",
+                            style = MaterialTheme.typography.bodyMedium
+                        )
+                    }
+                    Spacer(Modifier.height(1.dp))
+                    Divider(thickness = 1.2.dp)
+                    Row(
+                        horizontalArrangement = Arrangement.SpaceBetween,
+                        modifier = Modifier.fillMaxWidth()
+                    ) {
+                        Text(
+                            "组盘数量:${item.goods_in_qty}",
+
+                            )
+                        Text(
+                            "已出库数量:${item.goods_out_qty}",
+
+                            )
+                    }
+
+                }
+            }
+        }
+    }
+}
+

+ 137 - 36
app/src/main/java/com/example/pda/ui/ContainerScreen.kt

@@ -18,12 +18,15 @@ import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.dp
 import android.media.AudioManager
 import android.media.ToneGenerator
+import androidx.compose.material.icons.filled.List
 import androidx.compose.material.icons.filled.Search
 import com.example.pda.R
 import com.example.pda.function.UBXScan
 import com.example.pda.ui.viewmodel.InventoryViewModel
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.Scaffold
+import androidx.compose.material3.TabRowDefaults
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
@@ -32,22 +35,23 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
 import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.pda.model.BatchTotal
+import com.example.pda.model.MaterialResult
 import kotlinx.coroutines.launch
 
-
-
+// 托盘屏幕,用于托盘扫描和物料信息显示
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun ContainerScreen(
     onBack: () -> Unit,
+    onContainerItems: (String) -> Unit // 新增回调参数
 ) {
     val viewModel: InventoryViewModel = viewModel()
     val uiState = viewModel.uiState.collectAsState()
     val snackbarHostState = remember { SnackbarHostState() }
     val coroutineScope = rememberCoroutineScope()
     // 新增输入状态
-    val inputIp = remember { mutableStateOf("12345678") }
-
+    val inputIp = remember { mutableStateOf("100") }
     val context = LocalContext.current
     val ubxScan = remember { UBXScan() }
     var isScanning by remember { mutableStateOf(true) }
@@ -57,11 +61,12 @@ fun ContainerScreen(
     var showContainer by remember { mutableStateOf(true) }
     val configuration = LocalConfiguration.current
     val screenHeight = configuration.screenHeightDp.dp
-    val boxHeight = screenHeight * 0.15f
-
+    val boxHeight = screenHeight * 0.05f
+    var selectedTab by remember { mutableStateOf(0) }
     // 创建 ToneGenerator
     val toneGenerator = remember { ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100) }
 
+    // 根据 viewModel 的状态显示 Snackbar
     LaunchedEffect(uiState.value) {
         when (val currentState = uiState.value) {
             is InventoryViewModel.UiState.Success -> {
@@ -70,32 +75,38 @@ fun ContainerScreen(
                     viewModel.resetState()
                 }
             }
+
             is InventoryViewModel.UiState.Error -> {
                 coroutineScope.launch {
                     snackbarHostState.showSnackbar("错误:${currentState.message}")
                     viewModel.resetState()
                 }
             }
+
             else -> {}
         }
     }
 
+    // 开始扫描托盘编码,并在扫描结果处理逻辑中更新托盘编码
     DisposableEffect(Unit) {
         startScanning(context, ubxScan) { result ->
             if (container.value.isEmpty()) {
                 toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP)
                 container.value = result
+                viewModel.getData(container.value)
                 showContainer = false
             }
         }
 
+        // 停止扫描并释放 ToneGenerator 资源
         onDispose {
             stopScanning(ubxScan)
             isScanning = false
             toneGenerator.release()
         }
     }
-    
+
+    // 创建 Scaffold 布局,包含顶部导航栏和主要内容区域
     Scaffold(
         snackbarHost = { SnackbarHost(snackbarHostState) },
         topBar = {
@@ -128,7 +139,7 @@ fun ContainerScreen(
             )
         },
 
-    ) { innerPadding ->
+        ) { innerPadding ->
         Column(
             modifier = Modifier
                 .padding(innerPadding)
@@ -151,19 +162,18 @@ fun ContainerScreen(
                     Text(text = "托盘条码扫描")
                 } else if (isScanning && showinput) {
                     Text(text = "输入条形码")
-                }
-                else{
+                } else {
                     Text(text = "物料信息")
                 }
             }
             if (showinput) {
-                Row (
+                Row(
                     modifier = Modifier
                         .fillMaxWidth()
                         .padding(8.dp),
                     verticalAlignment = Alignment.CenterVertically
 
-                ){
+                ) {
                     // 新增输入框
                     TextField(
                         value = inputIp.value,
@@ -175,7 +185,7 @@ fun ContainerScreen(
                         onClick = {
                             container.value = inputIp.value;
                             viewModel.getData(container.value)
-                                  },
+                        },
                     ) {
                         Icon(
                             imageVector = Icons.Default.Search,
@@ -199,38 +209,52 @@ fun ContainerScreen(
                     modifier = Modifier.weight(1f)
                 )
 
+                Spacer(Modifier.height(8.dp))
                 IconButton(
-                    onClick = { container.value = ""; showContainer = true;showinput=false },
+                    onClick = {
+                        onContainerItems(container.value)
+                    },
                 ) {
-                    Icon(
-                        imageVector = Icons.Default.Delete,
-                        contentDescription = "删除"
-                    )
+                    Icon(Icons.Default.List, "确定")
                 }
             }
 
             // 已扫描列表
-            LazyColumn(
-                modifier = Modifier
-                    .weight(1f)
-                    .padding(8.dp)
+            // 分页导航
+            TabRow(
+                selectedTabIndex = selectedTab,
+                indicator = { tabPositions ->
+                    TabRowDefaults.Indicator(
+                        modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTab]),
+                        height = 2.dp,
+                        color = MaterialTheme.colorScheme.primary
+                    )
+                }
             ) {
-                itemsIndexed(scannedItems) { index, item ->
-                    Row(
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .padding(8.dp),
-                        verticalAlignment = Alignment.CenterVertically
-                    ) {
-                        Text(
-                            text = "${index + 1}. $item",
-                            modifier = Modifier.weight(1f)
-                        )
-
-                    }
-                    Divider()
+                listOf("批次统计", "物料明细").forEachIndexed { index, title ->
+                    Tab(
+                        selected = selectedTab == index,
+                        onClick = { selectedTab = index },
+                        text = { Text(title) },
+                        selectedContentColor = MaterialTheme.colorScheme.primary,
+                        unselectedContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
+                    )
                 }
             }
+
+            // 内容区域
+            // Tab 内容区域添加权重
+            when (selectedTab) {
+                0 -> BatchTotalList(
+                    data = viewModel.batchTotals.collectAsState().value,
+
+                    )
+
+                1 -> MaterialResultList(
+                    data = viewModel.materialDetails.collectAsState().value,
+
+                    )
+            }
         }
     }
 }
@@ -246,6 +270,83 @@ private fun startScanning(
     }
 }
 
+// 停止扫描
 private fun stopScanning(ubxScan: UBXScan) {
     ubxScan.destroy()
 }
+
+@Composable
+private fun BatchTotalList(data: List<BatchTotal>) {
+    LazyColumn(modifier = Modifier.fillMaxSize()) {
+        itemsIndexed(data) { index, item ->
+            Card(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(8.dp),
+                elevation = CardDefaults.cardElevation(4.dp)
+            ) {
+                Column(modifier = Modifier.padding(16.dp)) {
+                    Text(
+                        "批次号:${item.bound_number}",
+                        style = MaterialTheme.typography.titleMedium
+                    )
+                    Spacer(Modifier.height(8.dp))
+                    Text(
+                        "总数量:${item.total_batch_qty}",
+                        style = MaterialTheme.typography.bodyLarge
+                    )
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun MaterialResultList(data: List<MaterialResult>) {
+    LazyColumn(modifier = Modifier.fillMaxSize()) {
+        itemsIndexed(data) { index, item ->
+            Column(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(16.dp)
+            ) {
+
+                Text(
+                    "所属批次:${item.bound_number}",
+                    style = MaterialTheme.typography.bodyLarge,
+                    color = MaterialTheme.colorScheme.primary
+                )
+
+                Spacer(Modifier.height(4.dp))
+                Text(
+                    "物料编码:${item.goods_code}",
+                    style = MaterialTheme.typography.bodyMedium
+                )
+
+                Text(
+                    "物料描述:${item.goods_desc}",
+                    style = MaterialTheme.typography.bodyMedium
+                )
+
+                Spacer(Modifier.height(8.dp))
+
+                Row(
+                    horizontalArrangement = Arrangement.SpaceBetween,
+                    modifier = Modifier.fillMaxWidth()
+                ) {
+                    Text("当前数量:${item.current_qty}")
+                    Text("✖:${item.group_qty}组数")
+                }
+
+                Spacer(Modifier.height(4.dp))
+
+                Text(
+                    "创建时间:${item.create_time}",
+                    style = MaterialTheme.typography.bodySmall,
+                    color = Color.Gray
+                )
+            }
+            Divider(thickness = 0.8.dp)
+        }
+    }
+}

+ 1 - 1
app/src/main/java/com/example/pda/ui/DetailsScreen.kt

@@ -229,7 +229,7 @@ fun DetailsScreen(
             }
         }
     ) { innerPadding ->
-        LazyColumn( // ✅ 使用单一滚动容器
+        LazyColumn( // ✅ 使用单一滚动托盘
             modifier = Modifier
                 .padding(innerPadding)
                 .fillMaxSize()

+ 24 - 5
app/src/main/java/com/example/pda/ui/MainScreen.kt

@@ -22,6 +22,7 @@ fun MainScreen(
     onShelfScan: () -> Unit,
     onSetScreen: () -> Unit,
     onUpStationScreen:() ->Unit,
+    onOutTaskScreen:() ->Unit
 ) {
     LaunchedEffect(Unit) {
         // 在登录成功后调用
@@ -71,7 +72,7 @@ fun MainScreen(
             
             Column(
                 horizontalAlignment = Alignment.CenterHorizontally,
-                verticalArrangement = Arrangement.spacedBy(60.dp)
+                verticalArrangement = Arrangement.spacedBy(50.dp)
             ) {
                 ExtendedFloatingActionButton(
                     onClick = onInventoryScan,
@@ -94,11 +95,11 @@ fun MainScreen(
                 ExtendedFloatingActionButton(
                     onClick = onUpStationScreen,
                     modifier = Modifier.size(220.dp, 70.dp),
-                    containerColor = Color(0xFFFDECD0),
+                    containerColor = Color(0xFF8FC6FC),
                     icon = {
                         Icon(
                             painter = painterResource(id = R.drawable.ic_up_down),
-                            contentDescription = "入库扫描"
+                            contentDescription = "落地提升"
                         )
                     },
                     text = {
@@ -116,12 +117,30 @@ fun MainScreen(
                     icon = {
                         Icon(
                             painter = painterResource(id = R.drawable.container),
-                            contentDescription = "托盘扫描"
+                            contentDescription = "托盘查询"
+                        )
+                    },
+                    text = {
+                        Text(
+                            text = "托盘查询",
+                            fontSize = 20.sp,
+                            color = MaterialTheme.colorScheme.onSurface
+                        )
+                    }
+                )
+                ExtendedFloatingActionButton(
+                    onClick = onOutTaskScreen,
+                    modifier = Modifier.size(220.dp, 70.dp),
+                    containerColor = Color(0xFF8F6206),
+                    icon = {
+                        Icon(
+                            painter = painterResource(id = R.drawable.out_task),
+                            contentDescription = "出库确认"
                         )
                     },
                     text = {
                         Text(
-                            text = "托盘扫描",
+                            text = "出库确认",
                             fontSize = 20.sp,
                             color = MaterialTheme.colorScheme.onSurface
                         )

+ 412 - 0
app/src/main/java/com/example/pda/ui/OutTaskScreen.kt

@@ -0,0 +1,412 @@
+package com.example.pda.ui
+
+import android.content.Context
+import androidx.compose.foundation.clickable
+import androidx.compose.runtime.*
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.Modifier
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material3.*
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import android.media.AudioManager
+import android.media.ToneGenerator
+import androidx.compose.material.icons.filled.List
+import androidx.compose.material.icons.filled.Search
+import com.example.pda.R
+import com.example.pda.function.UBXScan
+import com.example.pda.ui.viewmodel.InventoryViewModel
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.TabRowDefaults
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.pda.model.BatchTotal
+import com.example.pda.model.ContainerItemDetail
+import com.example.pda.model.MaterialResult
+import kotlinx.coroutines.launch
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+// 托盘屏幕,用于托盘扫描和物料信息显示
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun OutTaskScreen(
+    onBack: () -> Unit,
+    onContainerItems: (String) -> Unit // 新增回调参数
+) {
+    val viewModel: InventoryViewModel = viewModel()
+    val uiState = viewModel.uiState.collectAsState()
+    val snackbarHostState = remember { SnackbarHostState() }
+    val coroutineScope = rememberCoroutineScope()
+    // 新增输入状态
+    val inputIp = remember { mutableStateOf("100") }
+    val context = LocalContext.current
+    val ubxScan = remember { UBXScan() }
+    var isScanning by remember { mutableStateOf(true) }
+
+    val container = remember { mutableStateOf("") }
+    var showinput by remember { mutableStateOf(false) }
+    var showContainer by remember { mutableStateOf(true) }
+    val configuration = LocalConfiguration.current
+    val screenHeight = configuration.screenHeightDp.dp
+    val boxHeight = screenHeight * 0.05f
+    var selectedTab by remember { mutableStateOf(0) }
+
+    val toneGenerator = remember { ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100) }
+
+    // 根据 viewModel 的状态显示 Snackbar
+    LaunchedEffect(uiState.value) {
+        when (val currentState = uiState.value) {
+            is InventoryViewModel.UiState.Success -> {
+                coroutineScope.launch {
+                    snackbarHostState.showSnackbar(currentState.message)
+                    viewModel.resetState()
+                }
+            }
+
+            is InventoryViewModel.UiState.Error -> {
+                coroutineScope.launch {
+                    snackbarHostState.showSnackbar("错误:${currentState.message}")
+                    viewModel.resetState()
+                }
+            }
+
+            else -> {}
+        }
+    }
+
+    // 开始扫描托盘编码,并在扫描结果处理逻辑中更新托盘编码
+    DisposableEffect(Unit) {
+        startScanning(context, ubxScan) { result ->
+            if (container.value.isEmpty()) {
+                toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP)
+                container.value = result
+                viewModel.getOutItems(container.value)
+                showContainer = false
+            }
+        }
+
+        // 停止扫描并释放 ToneGenerator 资源
+        onDispose {
+            stopScanning(ubxScan)
+            isScanning = false
+            toneGenerator.release()
+        }
+    }
+
+    // 创建 Scaffold 布局,包含顶部导航栏和主要内容区域
+    Scaffold(
+        snackbarHost = { SnackbarHost(snackbarHostState) },
+        topBar = {
+            TopAppBar(
+                navigationIcon = {
+                    IconButton(onClick = onBack) {
+                        Icon(
+                            imageVector = Icons.Default.ArrowBack,
+                            contentDescription = "返回"
+                        )
+                    }
+                },
+                title = {
+                    Row(verticalAlignment = Alignment.CenterVertically) {
+                        Icon(
+                            painter = painterResource(id = R.drawable.logo),
+                            contentDescription = "PDA Logo",
+                            modifier = Modifier.size(40.dp),
+                            tint = MaterialTheme.colorScheme.surfaceTint
+                        )
+                        Spacer(Modifier.width(8.dp))
+                        Text("信泰PDA—出库确认")
+                    }
+                },
+                colors = TopAppBarDefaults.topAppBarColors(
+                    containerColor = Color(0xFFBCD0C5), // 自定义颜色
+                    titleContentColor = MaterialTheme.colorScheme.onPrimary,
+                    actionIconContentColor = MaterialTheme.colorScheme.onPrimary
+                )
+            )
+        },
+
+        ) { innerPadding ->
+        Column(
+            modifier = Modifier
+                .padding(innerPadding)
+                .fillMaxSize()
+                .padding(24.dp)
+        ) {
+            // 扫描提示区域
+            Box(
+                modifier = Modifier
+                    .height(boxHeight)
+                    .fillMaxWidth()
+                    .clickable(enabled = isScanning) {
+                        container.value = ""
+                        showinput = true
+                        showContainer = false
+                    },
+                contentAlignment = Alignment.Center
+            ) {
+                if (isScanning && showContainer) {
+                    Text(text = "托盘条码扫描")
+                } else if (isScanning && showinput) {
+                    Text(text = "输入条形码")
+                } else {
+                    Text(text = "物料信息")
+                }
+            }
+            if (showinput) {
+                Row(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .padding(8.dp),
+                    verticalAlignment = Alignment.CenterVertically
+
+                ) {
+                    // 新增输入框
+                    TextField(
+                        value = inputIp.value,
+                        onValueChange = { inputIp.value = it },
+                        label = { Text("请输入托盘编码") },
+                    )
+
+                    IconButton(
+                        onClick = {
+                            container.value = inputIp.value;
+                            viewModel.getOutItems(container.value)
+                        },
+                    ) {
+                        Icon(
+                            imageVector = Icons.Default.Search,
+                            contentDescription = "确定"
+                        )
+                    }
+
+                }
+
+            }
+
+            // 添加托盘扫描
+            Row(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(8.dp),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                Text(
+                    text = "托盘编码:${container.value}",
+                    modifier = Modifier.weight(1f)
+                )
+
+                Spacer(Modifier.height(8.dp))
+                IconButton(
+                    onClick = {
+                        onContainerItems(container.value)
+                    },
+                ) {
+                    Icon(Icons.Default.List, "确定")
+                }
+            }
+
+            // 已扫描列表
+            // 分页导航
+            TabRow(
+                selectedTabIndex = selectedTab,
+                indicator = { tabPositions ->
+                    TabRowDefaults.Indicator(
+                        modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTab]),
+                        height = 2.dp,
+                        color = MaterialTheme.colorScheme.primary
+                    )
+                }
+            ) {
+                listOf("批次统计", "物料明细").forEachIndexed { index, title ->
+                    Tab(
+                        selected = selectedTab == index,
+                        onClick = { selectedTab = index },
+                        text = { Text(title) },
+                        selectedContentColor = MaterialTheme.colorScheme.primary,
+                        unselectedContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
+                    )
+                }
+            }
+
+            // 内容区域
+            // Tab 内容区域添加权重
+            when (selectedTab) {
+                0 -> BatchTotalList(
+
+                    data = OutItemsToBatchTotal(viewModel.containerItemsDetails.collectAsState().value)
+
+                    )
+
+                1 -> MaterialResultList(
+                    data = OutItemsToMaterialResult(viewModel.containerItemsDetails.collectAsState().value)
+
+                    )
+            }
+        }
+    }
+}
+
+// (不再直接调用onResult)
+private fun startScanning(
+    context: Context,
+    ubxScan: UBXScan,
+    onScanned: (String) -> Unit
+) {
+    ubxScan.setOnScanListener(context) { result ->
+        onScanned(result)
+    }
+}
+
+// 停止扫描
+private fun stopScanning(ubxScan: UBXScan) {
+    ubxScan.destroy()
+}
+
+private fun OutItemsToBatchTotal(input: List<ContainerItemDetail>): List<BatchTotal> {
+    // 1. 按批次号分组并计算每个批次的总数量
+    val batchMap = mutableMapOf<String, Int>()
+
+    input.forEach { item ->
+        // 计算当前物料的数量(入库-出库)
+        val currentQty =item.goods_out_qty
+
+        // 累加到批次总数
+        batchMap[item.batch_number] = batchMap.getOrDefault(item.batch_number, 0) + currentQty
+    }
+
+    // 2. 转换为 BatchTotal 对象列表
+    return batchMap.map { (batchNumber, totalQty) ->
+        BatchTotal(
+            bound_number = batchNumber,
+            total_batch_qty = totalQty
+        )
+    }
+}
+
+
+
+private fun OutItemsToMaterialResult(input: List<ContainerItemDetail>): List<MaterialResult> {
+    // 1. 按(批次号, 出库数量)分组并统计每个分组的记录数
+    val materialGroupMap = mutableMapOf<Pair<String, Int>, Int>()
+
+    input.forEach { item ->
+        val key = Pair(item.batch_number, item.goods_out_qty)
+        materialGroupMap[key] = materialGroupMap.getOrDefault(key, 0) + 1
+    }
+
+    // 2. 获取当前时间格式化器
+    val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.getDefault())
+    val currentTime = dateFormat.format(Date())
+
+    // 3. 转换为 MaterialResult 对象列表
+    return materialGroupMap.map { (groupKey, count) ->
+        // 查找该分组中的第一个物料作为代表(或根据业务需求调整)
+        val representativeItem = input.firstOrNull {
+            it.batch_number == groupKey.first && it.goods_out_qty == groupKey.second
+        }
+
+        MaterialResult(
+            goods_code = representativeItem?.goods_code ?: "",
+            goods_desc = representativeItem?.goods_desc ?: "",
+            current_qty = groupKey.second, // 出库数量作为当前数量
+            group_qty = count, // 分组中的记录数
+            bound_number = groupKey.first, // 批次号
+            create_time = currentTime
+        )
+    }
+}
+
+
+@Composable
+private fun BatchTotalList(data: List<BatchTotal>) {
+    LazyColumn(modifier = Modifier.fillMaxSize()) {
+        itemsIndexed(data) { index, item ->
+            Card(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(8.dp),
+                elevation = CardDefaults.cardElevation(4.dp)
+            ) {
+                Column(modifier = Modifier.padding(16.dp)) {
+                    Text(
+                        "批次号:${item.bound_number}",
+                        style = MaterialTheme.typography.titleMedium
+                    )
+                    Spacer(Modifier.height(8.dp))
+                    Text(
+                        "总数量:${item.total_batch_qty}",
+                        style = MaterialTheme.typography.bodyLarge
+                    )
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun MaterialResultList(data: List<MaterialResult>) {
+    LazyColumn(modifier = Modifier.fillMaxSize()) {
+        itemsIndexed(data) { index, item ->
+            Column(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(16.dp)
+            ) {
+
+                Text(
+                    "所属批次:${item.bound_number}",
+                    style = MaterialTheme.typography.bodyLarge,
+                    color = MaterialTheme.colorScheme.primary
+                )
+
+                Spacer(Modifier.height(4.dp))
+                Text(
+                    "物料编码:${item.goods_code}",
+                    style = MaterialTheme.typography.bodyMedium
+                )
+
+                Text(
+                    "物料描述:${item.goods_desc}",
+                    style = MaterialTheme.typography.bodyMedium
+                )
+
+                Spacer(Modifier.height(8.dp))
+
+                Row(
+                    horizontalArrangement = Arrangement.SpaceBetween,
+                    modifier = Modifier.fillMaxWidth()
+                ) {
+                    Text("当前数量:${item.current_qty}")
+                    Text("✖:${item.group_qty}组数")
+                }
+
+                Spacer(Modifier.height(4.dp))
+
+                Text(
+                    "创建时间:${item.create_time}",
+                    style = MaterialTheme.typography.bodySmall,
+                    color = Color.Gray
+                )
+            }
+            Divider(thickness = 0.8.dp)
+        }
+    }
+}

+ 80 - 17
app/src/main/java/com/example/pda/ui/viewmodel/InventoryViewModel.kt

@@ -1,5 +1,6 @@
 package com.example.pda.ui.viewmodel
 
+import android.R
 import android.util.Log
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
@@ -8,13 +9,27 @@ import com.example.pda.network.NetworkHelper
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
-
+import com.example.pda.model.BatchTotal
+import com.example.pda.model.ContainerItemDetail
+import com.example.pda.model.MaterialResult
 // ViewModel 类用于管理库存相关的UI状态和数据操作
 class InventoryViewModel : ViewModel() {
     // 私有 MutableStateFlow 用于存储UI状态,初始状态为 Idle
     private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
     // 公开的 StateFlow 提供给UI层观察当前UI状态
     val uiState: StateFlow<UiState> = _uiState
+    // 批次统计数据
+    private val _batchTotals = MutableStateFlow<List<BatchTotal>>(emptyList())
+    val batchTotals: StateFlow<List<BatchTotal>> = _batchTotals
+
+    // 物料明细数据
+    private val _materialDetails = MutableStateFlow<List<MaterialResult>>(emptyList())
+    val materialDetails: StateFlow<List<MaterialResult>> = _materialDetails
+
+    // 未出库物料信息
+    private val _containerItemsDetails = MutableStateFlow<List<ContainerItemDetail>>(emptyList())
+    val containerItemsDetails: StateFlow<List<ContainerItemDetail>> = _containerItemsDetails
+
 
     // 密封类 UiState 用于表示不同的UI状态
     sealed class UiState {
@@ -52,32 +67,80 @@ class InventoryViewModel : ViewModel() {
             )
         }
     }
-    fun getData(container: String)
-    {
-        // 在 viewModelScope 中启动协程以提交数据
+
+
+
+    fun getData(container: String) {
         viewModelScope.launch {
-            // 设置UI状态为 Loading 表示正在加载
             _uiState.value = UiState.Loading
-            // 调用 NetworkHelper 提交数据
-            NetworkHelper.getIdList (
+            _batchTotals.value = emptyList()
+            _materialDetails.value = emptyList()
+            NetworkHelper.getIdList(
                 container_code = container,
-                // 成功回调,设置UI状态为 Success 并包含成功消息
-                onSuccess = {
-                    _uiState.value = UiState.Success("返回成功")
-                    
-                    // 打印成功日志
-                    Log.d("getcontainerdata", "返回成功")
+                onSuccess = { response ->
+                    // 解析批次数据
+                    _batchTotals.value = response.data.batch_totals.map {
+                        BatchTotal(
+                            bound_number = it.bound_number,
+                            total_batch_qty = it.total_batch_qty
+                        )
+                    }
+
+                    // 解析物料明细
+                    _materialDetails.value = response.data.results.map {
+                        MaterialResult(
+                            goods_code = it.goods_code,
+                            goods_desc = it.goods_desc,
+                            current_qty = it.current_qty,
+                            bound_number = it.bound_number,
+                            create_time = it.create_time,
+                            group_qty = it.group_qty
+                        )
+                    }
+                    _uiState.value = UiState.Success("数据加载成功")
                 },
-                // 错误回调,设置UI状态为 Error 并包含错误消息
                 onError = { error ->
-                    _uiState.value = UiState.Error(error)
-                    // 打印错误日志
-                    Log.e("InventoryViewModel", error)
+                    _uiState.value = UiState.Success(error)
                 }
             )
         }
 
     }
+
+
+    fun getContainerDetail(container: String) {
+        viewModelScope.launch {
+            _uiState.value = UiState.Loading
+            _containerItemsDetails.value = emptyList()
+            NetworkHelper.getItemsDetail(
+                container_code =  container,
+                onSuccess = { response ->
+                    _containerItemsDetails.value = response
+                    _uiState.value = UiState.Success("数据加载成功")
+                },
+                onError = {
+                    _uiState.value = UiState.Success(it)
+                }
+            )
+        }
+    }
+
+    fun getOutItems(container: String) {
+        viewModelScope.launch {
+            _uiState.value = UiState.Loading
+            _containerItemsDetails.value = emptyList()
+            NetworkHelper.getOutItemsDetail (
+                container_code =  container,
+                onSuccess = { response ->
+                    _containerItemsDetails.value = response
+                    _uiState.value = UiState.Success("数据加载成功")
+                },
+                onError = {
+                    _uiState.value = UiState.Success(it)
+                }
+            )
+        }
+    }
    
    fun resetState() {
         // 重置 UI 状态

+ 5 - 0
app/src/main/res/drawable/out_task.xml

@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M21,14v2H4V4H2V2h4v12H21zM5.99,17C4.89,17 4,17.9 4,19c0,1.1 0.89,2 1.99,2C7.1,21 8,20.1 8,19C8,17.9 7.1,17 5.99,17zM18.99,17c-1.1,0 -1.99,0.9 -1.99,2c0,1.1 0.89,2 1.99,2c1.1,0 2.01,-0.9 2.01,-2C21,17.9 20.1,17 18.99,17zM13,13H7V7h6V13zM20,13h-6V7h6V13z"/>
+    
+</vector>