flower_mr 3 週間 前
コミット
4a3c919299

+ 10 - 2
app/src/main/java/com/example/pda/function/ResultCounter.kt

@@ -3,8 +3,16 @@ package com.example.pda.function
 // 顶层数据类声明
 data class ResultCount(
     val normalizedResult: String,
-    val occurrences: Int
-)
+    var occurrences: Int,
+    var selectedSpec: Spec = Spec.OPTION_30KG,
+    var customValue: String = ""
+) {
+    sealed class Spec(val display: String) {
+        object OPTION_30KG : Spec("30kg/组")
+        object OPTION_25KG : Spec("25kg/组")
+        object CUSTOM : Spec("自定义kg/组")
+    }
+}
 
 class ResultCounter {
     // 使用LinkedHashMap保持插入顺序(可选)

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

@@ -19,7 +19,7 @@ object AppPrefs {
 
     var token by StringPreference({ prefs }, "token", "")
     var ip_server by StringPreference({ prefs }, "ip_server", "")
-    var base_url by StringPreference({ prefs }, "base_url", "")
+    var base_url by StringPreference({ prefs }, "base_url", "http://192.168.18.200:8008/")
     var rememberMe by BooleanPreference({ prefs }, "remember_me", false)
     var isDarkMode by BooleanPreference({ prefs }, "dark_mode", false)
     var islogin by BooleanPreference({ prefs }, "islogin", false)

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

@@ -6,6 +6,9 @@ data class InventoryItem(
     @SerializedName("goods_code")
     val normalizedResult: String,
 
+    @SerializedName("goods_group")
+    val occurrences: Int,
+
     @SerializedName("goods_qty")
-    val occurrences: Int
+    val specification: Int
 )

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

@@ -21,9 +21,9 @@ object HttpClient {
     // 带动态 Header 的 OkHttpClient
     val instance: OkHttpClient by lazy {
         OkHttpClient.Builder()
-            .connectTimeout(1, TimeUnit.SECONDS)
-            .readTimeout(1, TimeUnit.SECONDS)
-            .writeTimeout(1, TimeUnit.SECONDS)
+            .connectTimeout(10, TimeUnit.SECONDS)
+            .readTimeout(10, TimeUnit.SECONDS)
+            .writeTimeout(10, TimeUnit.SECONDS)
             .addInterceptor(authInterceptor) // 先添加认证拦截器
             .addInterceptor(HttpLoggingInterceptor().apply {
                 level = HttpLoggingInterceptor.Level.BODY // 后添加日志拦截器

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

@@ -26,9 +26,9 @@ object HttpClientWCS {
     // 带动态equipmentNumber  的 OkHttpClient
     val instance: OkHttpClient by lazy {
         OkHttpClient.Builder()
-            .connectTimeout(1, TimeUnit.SECONDS)
-            .readTimeout(1, TimeUnit.SECONDS)
-            .writeTimeout(1, TimeUnit.SECONDS)
+            .connectTimeout(10, TimeUnit.SECONDS)
+            .readTimeout(10, TimeUnit.SECONDS)
+            .writeTimeout(10, TimeUnit.SECONDS)
             .addInterceptor(queryParamsInterceptor)
             .addInterceptor(HttpLoggingInterceptor().apply {
                 level = HttpLoggingInterceptor.Level.BODY // 后添加日志拦截器

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

@@ -51,7 +51,6 @@ object NetworkHelper {
     // 获取库存列表的方法
     fun getIdList(
         container_code: String,
-
         onSuccess: (List<InventoryItem>) -> Unit, // 成功后的回调函数,接受一个InventoryItem列表作为参数
         onError: (String) -> Unit // 失败后的回调函数
     ) {

+ 25 - 10
app/src/main/java/com/example/pda/network/RetrofitClient.kt

@@ -4,19 +4,34 @@ import retrofit2.Retrofit
 import retrofit2.converter.gson.GsonConverterFactory
 import com.example.pda.model.AppPrefs
 // RetrofitClient 对象用于创建和管理 Retrofit 实例
+// 添加重置方法
 object RetrofitClient {
-    // 私有常量 BASE_URL 定义了 API 的基础 URL
-    private const val BASE_URL = "http://192.168.18.69:8008/"
+    private var retrofitInstance: Retrofit? = null
+    private var apiService: ApiService? = null
 
+    fun reset() {
+        retrofitInstance = null
+        apiService = null
+    }
+
+    val instance: ApiService
+        get() {
+            if (apiService == null) {
+                retrofitInstance = Retrofit.Builder()
+                    .baseUrl(formatBaseUrl(AppPrefs.base_url))
+                    .client(HttpClient.instance)
+                    .addConverterFactory(GsonConverterFactory.create())
+                    .build()
+                apiService = retrofitInstance!!.create(ApiService::class.java)
+            }
+            return apiService!!
+        }
 
-    // val instance 通过 lazy 委托实现懒加载,创建 ApiService 的单例实例
-    val instance: ApiService by lazy {
-        Retrofit.Builder()
-            .baseUrl(BASE_URL)
-            .client(HttpClient.instance)
-            .addConverterFactory(GsonConverterFactory.create())
-            .build()
-            .create(ApiService::class.java)
+    private fun formatBaseUrl(url: String): String {
+        var formatted = url
+        if (!formatted.startsWith("http")) formatted = "http://$formatted"
+        if (!formatted.endsWith("/")) formatted += "/"
+        return formatted
     }
 }
 

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

@@ -17,8 +17,13 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+// 添加缺失的导入
+import androidx.compose.runtime.getValue
+
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
@@ -39,7 +44,6 @@ import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
-
 fun DetailsScreen(
     container: String,
     result: String,
@@ -67,27 +71,52 @@ fun DetailsScreen(
             resultCounter.addResult(it)
         }
         editableResults.clear()
-        editableResults.addAll(resultCounter.getStatistics())
+        editableResults.addAll(
+            resultCounter.getStatistics().map {
+                ResultCount(
+                    normalizedResult = it.normalizedResult,
+                    occurrences = it.occurrences,
+                    selectedSpec = ResultCount.Spec.OPTION_30KG
+                )
+            }
+        )
 
         // 初始化库存列表
         inventoryitems.clear()
         inventoryitems.addAll(
             editableResults.map {
-                InventoryItem(it.normalizedResult, it.occurrences)
+                InventoryItem(
+                    it.normalizedResult,
+                    it.occurrences,
+                    specification = when (it.selectedSpec) {
+                        is ResultCount.Spec.OPTION_30KG -> 30
+                        is ResultCount.Spec.OPTION_25KG -> 25
+                        is ResultCount.Spec.CUSTOM -> it.customValue.toIntOrNull() ?: 0
+                    }
+                )
             }
         )
     }
 
     // 更新文本输入时的数据同步
     fun updateInventoryItem(index: Int, newCount: Int) {
-        editableResults[index] = editableResults[index].copy(occurrences = newCount)
-        inventoryitems[index] = inventoryitems[index].copy(occurrences = newCount)
+        val newItem = editableResults[index].copy(occurrences = newCount)
+        editableResults[index] = newItem
+        inventoryitems[index] = InventoryItem(
+            newItem.normalizedResult,
+            newCount,
+            specification = when (newItem.selectedSpec) {
+                is ResultCount.Spec.OPTION_30KG -> 30
+                is ResultCount.Spec.OPTION_25KG -> 25
+                is ResultCount.Spec.CUSTOM -> newItem.customValue.toIntOrNull() ?: 0
+            }
+        )
     }
 
     // 提交按钮的统一处理
     fun onSubmit() {
         val items = editableResults.map {
-            InventoryItem(it.normalizedResult, it.occurrences)
+            InventoryItem(it.normalizedResult, it.occurrences,30)
         }
         inventoryitems.clear()
         inventoryitems.addAll(items)
@@ -118,7 +147,7 @@ fun DetailsScreen(
         snackbarHost = { SnackbarHost(snackbarHostState) },
         topBar = {
             TopAppBar(
-                title = { Text("扫描结果详情") },
+                title = { Text("扫描详情") },
                 navigationIcon = {
                     IconButton(onClick = onBack) {
                         Icon(
@@ -142,6 +171,7 @@ fun DetailsScreen(
             horizontalArrangement = Arrangement.SpaceEvenly,
 
             ) {
+                Spacer(Modifier.width(18.dp)) // 添加间距
                 ExtendedFloatingActionButton(
                     modifier = Modifier.weight(1f),
                     onClick = onMainScreen,
@@ -183,11 +213,11 @@ fun DetailsScreen(
                              } ,
                     icon = {
                         Icon(
-                            painter = painterResource(id = R.drawable.ic_scan),
+                            painter = painterResource(id = R.drawable.component),
                             contentDescription = "组盘入库"
                         )
                     },
-                    text = { Text("组盘入库") }
+                    text = { Text("组盘") }
                 )
             }
             }
@@ -200,7 +230,7 @@ fun DetailsScreen(
                 .padding(24.dp)
         ) {
             Text(
-                text = "扫描结果:",
+                text = "托盘编码:",
                 style = MaterialTheme.typography.titleLarge
             )
 
@@ -227,14 +257,20 @@ fun DetailsScreen(
             ) {
                 Text("条码", style = MaterialTheme.typography.titleMedium)
                 Text("数量", style = MaterialTheme.typography.titleMedium)
+                Text("规格", style = MaterialTheme.typography.titleMedium)
+
             }
 
+// 修改 LazyColumn 部分
             LazyColumn(
                 modifier = Modifier
                     .fillMaxWidth()
-                    .weight(1f)
+                    .padding(16.dp)
             ) {
                 itemsIndexed(editableResults) { index, item ->
+
+                    var expanded by remember { mutableStateOf(false) }
+                    var customDialogVisible by remember { mutableStateOf(false) }
                     Row(
                         modifier = Modifier
                             .fillMaxWidth()
@@ -242,31 +278,121 @@ fun DetailsScreen(
                         verticalAlignment = Alignment.CenterVertically,
                         horizontalArrangement = Arrangement.SpaceBetween
                     ) {
+                        // 第一列:条码
                         Text(
                             text = item.normalizedResult,
                             modifier = Modifier.weight(1f),
                             overflow = TextOverflow.Ellipsis,
-                            maxLines = 1
+                            maxLines = 2
                         )
 
-                        Spacer(modifier = Modifier.width(16.dp))
-
+                        // 第二列:数量输入
                         OutlinedTextField(
                             value = item.occurrences.toString(),
                             onValueChange = { newValue ->
                                 newValue.toIntOrNull()?.let { updateInventoryItem(index, it) }
-
                             },
-                            modifier = Modifier.width(100.dp),
+                            modifier = Modifier
+                                .width(70.dp)
+                                .padding(horizontal = 4.dp),
                             keyboardOptions = KeyboardOptions.Default.copy(
                                 keyboardType = KeyboardType.Number
                             )
                         )
+
+                        // 第三列:规格选择
+                        ExposedDropdownMenuBox(
+                            expanded = expanded,
+                            onExpandedChange = { expanded = expanded.not() },
+                            modifier = Modifier
+                                .weight(1f)
+                                .padding(start = 8.dp)
+                        ) {
+                            OutlinedTextField(
+                                value = when (item.selectedSpec) {
+                                    is ResultCount.Spec.OPTION_30KG -> "30kg/组"
+                                    is ResultCount.Spec.OPTION_25KG -> "25kg/组"
+                                    is ResultCount.Spec.CUSTOM ->
+                                        if (item.customValue.isEmpty()) "自定义kg/组"
+                                        else "${item.customValue}kg/组"
+                                },
+                                onValueChange = {},
+                                readOnly = true,
+                                trailingIcon = {
+                                    ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
+                                },
+                                modifier = Modifier
+                                    .menuAnchor()
+                                    .fillMaxWidth()
+                            )
+
+                            ExposedDropdownMenu(
+                                expanded = expanded,
+                                onDismissRequest = { expanded = false }
+                            ) {
+                                // 30kg选项
+                                DropdownMenuItem(
+                                    text = { Text("30kg/组") },
+                                    onClick = {
+                                        editableResults[index] = item.copy(
+                                            selectedSpec = ResultCount.Spec.OPTION_30KG
+                                        )
+                                        expanded = false
+                                    }
+                                )
+
+                                // 25kg选项
+                                DropdownMenuItem(
+                                    text = { Text("25kg/组") },
+                                    onClick = {
+                                        editableResults[index] = item.copy(
+                                            selectedSpec = ResultCount.Spec.OPTION_25KG
+                                        )
+                                        expanded = false
+                                    }
+                                )
+
+                                // 自定义选项
+                                DropdownMenuItem(
+                                    text = { Text("自定义kg/组") },
+                                    onClick = {
+                                        expanded = false
+                                        customDialogVisible = true
+                                    }
+                                )
+                            }
+                        }
+                    }
+
+                    // 自定义输入对话框
+                    if (customDialogVisible) {
+                        AlertDialog(
+                            onDismissRequest = { customDialogVisible = false },
+                            title = { Text("输入自定义规格") },
+                            text = {
+                                OutlinedTextField(
+                                    value = item.customValue,
+                                    onValueChange = {
+                                        editableResults[index] = item.copy(
+                                            selectedSpec = ResultCount.Spec.CUSTOM,
+                                            customValue = it
+                                        )
+                                    },
+                                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+                                    label = { Text("输入公斤数") }
+                                )
+                            },
+                            confirmButton = {
+                                TextButton(
+                                    onClick = { customDialogVisible = false }
+                                ) {
+                                    Text("确定")
+                                }
+                            }
+                        )
                     }
                 }
             }
-
-
         }
     }
 }

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

@@ -97,7 +97,7 @@ fun MainScreen(
                     containerColor = Color(0xFFFDECD0),
                     icon = {
                         Icon(
-                            painter = painterResource(id = R.drawable.ic_scan),
+                            painter = painterResource(id = R.drawable.ic_up_down),
                             contentDescription = "入库扫描"
                         )
                     },
@@ -115,7 +115,7 @@ fun MainScreen(
                     containerColor = Color(0xFF91BFBF),
                     icon = {
                         Icon(
-                            painter = painterResource(id = R.drawable.ic_scan),
+                            painter = painterResource(id = R.drawable.container),
                             contentDescription = "托盘扫描"
                         )
                     },

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

@@ -86,7 +86,7 @@ fun ScanScreen(
                             tint = MaterialTheme.colorScheme.surfaceTint
                         )
                         Spacer(Modifier.width(8.dp))
-                        Text("信泰PDA—入库扫描-组盘扫描")
+                        Text("入库扫描-组盘扫描")
                     }
                 },
                 colors = TopAppBarDefaults.topAppBarColors(

+ 57 - 11
app/src/main/java/com/example/pda/ui/SettingScreen.kt

@@ -1,26 +1,37 @@
 package com.example.pda.ui
 
+import android.widget.Toast
 import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.clickable
 import androidx.compose.material3.*
 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.foundation.clickable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.dp
-
-
 import com.example.pda.R
 import com.example.pda.model.AppPrefs
-import com.example.pda.network.HttpClient
+import com.example.pda.network.RetrofitClient
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun SettingScreen(
     onPingScreen: () -> Unit
 ) {
+    val context = LocalContext.current
+    var inputUrl by remember { mutableStateOf("") }
+
+    // 初始化时读取当前服务器地址
+    LaunchedEffect(Unit) {
+        inputUrl = AppPrefs.base_url
+    }
 
     Scaffold(
         topBar = {
@@ -38,7 +49,7 @@ fun SettingScreen(
                     }
                 },
                 colors = TopAppBarDefaults.topAppBarColors(
-                    containerColor = Color(0xFFBCD0C5), // 自定义定义
+                    containerColor = Color(0xFFBCD0C5),
                     titleContentColor = MaterialTheme.colorScheme.onPrimary,
                     actionIconContentColor = MaterialTheme.colorScheme.onPrimary
                 )
@@ -49,21 +60,56 @@ fun SettingScreen(
             modifier = Modifier
                 .padding(innerPadding)
                 .fillMaxSize()
+                .padding(16.dp)
         ) {
-            // 扫描提示区域
+            // 服务器地址设置
+            Text(
+                text = "服务器地址配置",
+                style = MaterialTheme.typography.titleMedium,
+                modifier = Modifier.padding(bottom = 8.dp)
+            )
+
+            TextField(
+                value = inputUrl,
+                onValueChange = { inputUrl = it },
+                label = { Text("请输入服务器地址") },
+                placeholder = { Text("示例: http://192.168.1.100:8080/") },
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .padding(bottom = 16.dp)
+            )
+
+            Button(
+                onClick = {
+                    // 保存配置并重置网络客户端
+                    AppPrefs.base_url = inputUrl
+                    RetrofitClient.reset()
+                    Toast.makeText(context, "服务器地址已更新", Toast.LENGTH_SHORT).show()
+                },
+                modifier = Modifier.fillMaxWidth()
+            ) {
+                Text("保存配置")
+            }
+
+            Spacer(Modifier.height(32.dp))
+
+            // 原有功能区域
             Box(
                 modifier = Modifier
                     .fillMaxWidth()
                     .clickable(enabled = true, onClick = { onPingScreen() }),
                 contentAlignment = Alignment.Center
             ) {
-                Text(text = "ping监测")
+                Text("PING监测", style = MaterialTheme.typography.titleMedium)
             }
-            Text(
 
-                text = "当前登录用户为:${AppPrefs.username}"
-            )
+            Spacer(Modifier.height(16.dp))
 
+            Text(
+                text = "当前登录用户: ${AppPrefs.username}",
+                style = MaterialTheme.typography.bodyMedium
+            )
         }
     }
 }
+

+ 2 - 2
app/src/main/java/com/example/pda/ui/UpStationScreen.kt

@@ -92,7 +92,7 @@ fun UpStationScreen(
                     },
                     text = {
                         Text(
-                            text = "出库提升",
+                            text = "203提升",
                             fontSize = 18.sp,
                             color = MaterialTheme.colorScheme.onSurface
                         )
@@ -113,7 +113,7 @@ fun UpStationScreen(
                     },
                     text = {
                         Text(
-                            text = "入库提升",
+                            text = "103提升",
                             fontSize = 20.sp,
                             color = MaterialTheme.colorScheme.onSurface
                         )

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

@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M21,5v6.5H9.33V5H21zM14.67,19v-6.5H9.33V19H14.67zM15.67,12.5V19H21v-6.5H15.67zM8.33,19V5H3v14H8.33z"/>
+    
+</vector>

+ 5 - 0
app/src/main/res/drawable/container.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="M22,9L22,7h-2L20,5c0,-1.1 -0.9,-2 -2,-2L4,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2v-2h-2L20,9h2zM18,19L4,19L4,5h14v14zM6,13h5v4L6,17zM12,7h4v3h-4zM6,7h5v5L6,12zM12,11h4v6h-4z"/>
+    
+</vector>

+ 5 - 0
app/src/main/res/drawable/ic_up_down.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="M2,7h4v10H2V7zM7,19h10V5H7V19zM18,7h4v10h-4V7z"/>
+    
+</vector>