feat: 添加打印机黑标检测开关和走纸长度设置
- 支持黑标检测开关,关闭时用走纸长度替代 - 打印机设置页面:黑标开关、走纸长度输入、测试入口 - SessionManager 持久化打印机配置 - 新增 PrinterTestScreen 测试页面 - VERSION_CODE 82 -> 90
This commit is contained in:
@@ -5,6 +5,7 @@ import androidx.datastore.core.DataStore
|
|||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import com.example.ops_android.data.model.Cookie
|
import com.example.ops_android.data.model.Cookie
|
||||||
@@ -25,6 +26,8 @@ class SessionManager(private val context: Context) {
|
|||||||
private val KEY_COOKIE_NAME = stringPreferencesKey("cookie_name")
|
private val KEY_COOKIE_NAME = stringPreferencesKey("cookie_name")
|
||||||
private val KEY_COOKIE_EXPIRES_AT = stringPreferencesKey("cookie_expires_at")
|
private val KEY_COOKIE_EXPIRES_AT = stringPreferencesKey("cookie_expires_at")
|
||||||
private val KEY_COOKIE_REMEMBER = booleanPreferencesKey("cookie_remember")
|
private val KEY_COOKIE_REMEMBER = booleanPreferencesKey("cookie_remember")
|
||||||
|
private val KEY_PRINTER_BLACK_MARK = booleanPreferencesKey("printer_black_mark")
|
||||||
|
private val KEY_PRINTER_UNWIND_MM = intPreferencesKey("printer_unwind_mm")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
@@ -35,6 +38,14 @@ class SessionManager(private val context: Context) {
|
|||||||
var cachedCookieValue: String = ""
|
var cachedCookieValue: String = ""
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var cachedBlackMarkEnabled: Boolean = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var cachedUnwindMm: Int = 40
|
||||||
|
private set
|
||||||
|
|
||||||
fun clearCookieCache() {
|
fun clearCookieCache() {
|
||||||
cachedCookieValue = ""
|
cachedCookieValue = ""
|
||||||
}
|
}
|
||||||
@@ -43,9 +54,11 @@ class SessionManager(private val context: Context) {
|
|||||||
val cookieValue: Flow<String> = context.dataStore.data.map { prefs -> prefs[KEY_COOKIE_VALUE] ?: "" }
|
val cookieValue: Flow<String> = context.dataStore.data.map { prefs -> prefs[KEY_COOKIE_VALUE] ?: "" }
|
||||||
|
|
||||||
suspend fun init() {
|
suspend fun init() {
|
||||||
cachedBaseUrl = context.dataStore.data.first()[KEY_API_BASE_URL] ?: ""
|
val prefs = context.dataStore.data.first()
|
||||||
val value = context.dataStore.data.first()[KEY_COOKIE_VALUE]
|
cachedBaseUrl = prefs[KEY_API_BASE_URL] ?: ""
|
||||||
cachedCookieValue = value ?: ""
|
cachedCookieValue = prefs[KEY_COOKIE_VALUE] ?: ""
|
||||||
|
cachedBlackMarkEnabled = prefs[KEY_PRINTER_BLACK_MARK] ?: true
|
||||||
|
cachedUnwindMm = prefs[KEY_PRINTER_UNWIND_MM] ?: 40
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getApiBaseUrl(): String = cachedBaseUrl.ifEmpty {
|
suspend fun getApiBaseUrl(): String = cachedBaseUrl.ifEmpty {
|
||||||
@@ -92,4 +105,14 @@ class SessionManager(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun logout() = clearCookie()
|
suspend fun logout() = clearCookie()
|
||||||
|
|
||||||
|
suspend fun setBlackMarkEnabled(enabled: Boolean) {
|
||||||
|
cachedBlackMarkEnabled = enabled
|
||||||
|
context.dataStore.edit { it[KEY_PRINTER_BLACK_MARK] = enabled }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setUnwindMm(mm: Int) {
|
||||||
|
cachedUnwindMm = mm
|
||||||
|
context.dataStore.edit { it[KEY_PRINTER_UNWIND_MM] = mm }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ object PrintHelper {
|
|||||||
|
|
||||||
suspend fun printOrderLabel(
|
suspend fun printOrderLabel(
|
||||||
printerManager: PrinterManager,
|
printerManager: PrinterManager,
|
||||||
order: PurchaseOrder
|
order: PurchaseOrder,
|
||||||
|
blackMarkEnabled: Boolean = true,
|
||||||
|
unwindMm: Int = 40
|
||||||
): Result<Unit> = printMutex.withLock {
|
): Result<Unit> = printMutex.withLock {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
@@ -28,8 +30,12 @@ object PrintHelper {
|
|||||||
if (order.id > 0) {
|
if (order.id > 0) {
|
||||||
printerManager.addBarcode("PO-${order.id}")
|
printerManager.addBarcode("PO-${order.id}")
|
||||||
}
|
}
|
||||||
printerManager.addLineFeed(2)
|
if (blackMarkEnabled) {
|
||||||
printerManager.enableMarkDetection(true)
|
printerManager.addLineFeed(2)
|
||||||
|
printerManager.enableMarkDetection(true)
|
||||||
|
} else {
|
||||||
|
printerManager.addLineFeed((unwindMm / 7).toInt().coerceAtLeast(2))
|
||||||
|
}
|
||||||
printerManager.commit().fold(
|
printerManager.commit().fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -46,7 +52,9 @@ object PrintHelper {
|
|||||||
|
|
||||||
suspend fun printWorkOrderLabel(
|
suspend fun printWorkOrderLabel(
|
||||||
printerManager: PrinterManager,
|
printerManager: PrinterManager,
|
||||||
workOrder: WorkOrder
|
workOrder: WorkOrder,
|
||||||
|
blackMarkEnabled: Boolean = true,
|
||||||
|
unwindMm: Int = 40
|
||||||
): Result<Unit> = printMutex.withLock {
|
): Result<Unit> = printMutex.withLock {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
@@ -59,8 +67,12 @@ object PrintHelper {
|
|||||||
if (workOrder.id > 0) {
|
if (workOrder.id > 0) {
|
||||||
printerManager.addBarcode("WO-${workOrder.id}")
|
printerManager.addBarcode("WO-${workOrder.id}")
|
||||||
}
|
}
|
||||||
printerManager.addLineFeed(2)
|
if (blackMarkEnabled) {
|
||||||
printerManager.enableMarkDetection(true)
|
printerManager.addLineFeed(2)
|
||||||
|
printerManager.enableMarkDetection(true)
|
||||||
|
} else {
|
||||||
|
printerManager.addLineFeed((unwindMm / 7).toInt().coerceAtLeast(2))
|
||||||
|
}
|
||||||
printerManager.commit().fold(
|
printerManager.commit().fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -77,7 +89,9 @@ object PrintHelper {
|
|||||||
|
|
||||||
suspend fun printItemLabel(
|
suspend fun printItemLabel(
|
||||||
printerManager: PrinterManager,
|
printerManager: PrinterManager,
|
||||||
item: Item
|
item: Item,
|
||||||
|
blackMarkEnabled: Boolean = true,
|
||||||
|
unwindMm: Int = 40
|
||||||
): Result<Unit> = printMutex.withLock {
|
): Result<Unit> = printMutex.withLock {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
@@ -91,8 +105,12 @@ object PrintHelper {
|
|||||||
if (item.id > 0) {
|
if (item.id > 0) {
|
||||||
printerManager.addBarcode("ITEM-${item.id}")
|
printerManager.addBarcode("ITEM-${item.id}")
|
||||||
}
|
}
|
||||||
printerManager.addLineFeed(2)
|
if (blackMarkEnabled) {
|
||||||
printerManager.enableMarkDetection(true)
|
printerManager.addLineFeed(2)
|
||||||
|
printerManager.enableMarkDetection(true)
|
||||||
|
} else {
|
||||||
|
printerManager.addLineFeed((unwindMm / 7).toInt().coerceAtLeast(2))
|
||||||
|
}
|
||||||
printerManager.commit().fold(
|
printerManager.commit().fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -110,7 +128,9 @@ object PrintHelper {
|
|||||||
suspend fun printContainerLabel(
|
suspend fun printContainerLabel(
|
||||||
printerManager: PrinterManager,
|
printerManager: PrinterManager,
|
||||||
containerName: String,
|
containerName: String,
|
||||||
barcode: String? = null
|
barcode: String? = null,
|
||||||
|
blackMarkEnabled: Boolean = true,
|
||||||
|
unwindMm: Int = 40
|
||||||
): Result<Unit> = printMutex.withLock {
|
): Result<Unit> = printMutex.withLock {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
@@ -123,8 +143,12 @@ object PrintHelper {
|
|||||||
printerManager.setBold(false)
|
printerManager.setBold(false)
|
||||||
printerManager.addBarcode(barcode)
|
printerManager.addBarcode(barcode)
|
||||||
}
|
}
|
||||||
printerManager.addLineFeed(2)
|
if (blackMarkEnabled) {
|
||||||
printerManager.enableMarkDetection(true)
|
printerManager.addLineFeed(2)
|
||||||
|
printerManager.enableMarkDetection(true)
|
||||||
|
} else {
|
||||||
|
printerManager.addLineFeed((unwindMm / 7).toInt().coerceAtLeast(2))
|
||||||
|
}
|
||||||
printerManager.commit().fold(
|
printerManager.commit().fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
|
|||||||
@@ -255,6 +255,20 @@ class PrinterManager(private val context: Context) {
|
|||||||
printManager!!.setUnwindPaperLen(len)
|
printManager!!.setUnwindPaperLen(len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUnwindPaperLenInt(): Int? = try {
|
||||||
|
printManager?.unwindPaperLen ?: printManager?.getUnwindPaperLen()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "getUnwindPaperLen failed", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFeedPaperSpaceInt(): Int? = try {
|
||||||
|
printManager?.feedPaperSpace ?: printManager?.getFeedPaperSpace()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "getFeedPaperSpace failed", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════
|
||||||
// 黑标 / 标签
|
// 黑标 / 标签
|
||||||
// ══════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.example.ops_android.ui.profile.ProfileScreen
|
|||||||
import com.example.ops_android.ui.schedule.ScheduleScreen
|
import com.example.ops_android.ui.schedule.ScheduleScreen
|
||||||
import com.example.ops_android.ui.search.SearchScreen
|
import com.example.ops_android.ui.search.SearchScreen
|
||||||
import com.example.ops_android.ui.settings.SettingsScreen
|
import com.example.ops_android.ui.settings.SettingsScreen
|
||||||
|
import com.example.ops_android.ui.settings.PrinterTestScreen
|
||||||
import com.example.ops_android.ui.warehouse.ItemDetailScreen
|
import com.example.ops_android.ui.warehouse.ItemDetailScreen
|
||||||
import com.example.ops_android.ui.warehouse.ItemFormScreen
|
import com.example.ops_android.ui.warehouse.ItemFormScreen
|
||||||
import com.example.ops_android.ui.warehouse.WarehouseScreen
|
import com.example.ops_android.ui.warehouse.WarehouseScreen
|
||||||
@@ -49,6 +50,7 @@ object Routes {
|
|||||||
const val ITEM_DETAIL = "item_detail/{id}"
|
const val ITEM_DETAIL = "item_detail/{id}"
|
||||||
const val ITEM_FORM = "item_form/{containerId}?editId={editId}"
|
const val ITEM_FORM = "item_form/{containerId}?editId={editId}"
|
||||||
const val CUSTOMER_LIST = "customer_list"
|
const val CUSTOMER_LIST = "customer_list"
|
||||||
|
const val PRINTER_TEST = "printer_test"
|
||||||
|
|
||||||
fun orderDetail(orderId: Int) = "order_detail/$orderId"
|
fun orderDetail(orderId: Int) = "order_detail/$orderId"
|
||||||
fun orderForm(editId: Int? = null) = if (editId != null) "order_form?editId=$editId" else "order_form"
|
fun orderForm(editId: Int? = null) = if (editId != null) "order_form?editId=$editId" else "order_form"
|
||||||
@@ -95,7 +97,13 @@ fun OPSNavGraph(isLoggedIn: Boolean) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(Routes.SETTINGS) {
|
composable(Routes.SETTINGS) {
|
||||||
SettingsScreen(onBack = { rootNavController.popBackStack() })
|
SettingsScreen(
|
||||||
|
onBack = { rootNavController.popBackStack() },
|
||||||
|
onPrinterTest = { rootNavController.navigate(Routes.PRINTER_TEST) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(Routes.PRINTER_TEST) {
|
||||||
|
PrinterTestScreen(onBack = { rootNavController.popBackStack() })
|
||||||
}
|
}
|
||||||
composable(Routes.SEARCH) {
|
composable(Routes.SEARCH) {
|
||||||
SearchScreen(
|
SearchScreen(
|
||||||
|
|||||||
@@ -58,9 +58,10 @@ fun OrderDetailScreen(
|
|||||||
order?.let { o ->
|
order?.let { o ->
|
||||||
val printerManager = LocalAppContainer.current.printerManager
|
val printerManager = LocalAppContainer.current.printerManager
|
||||||
if (printerManager != null) {
|
if (printerManager != null) {
|
||||||
|
val sm = LocalAppContainer.current.sessionManager
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
com.example.ops_android.printer.PrintHelper.printOrderLabel(printerManager, o)
|
com.example.ops_android.printer.PrintHelper.printOrderLabel(printerManager, o, sm.cachedBlackMarkEnabled, sm.cachedUnwindMm)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.Print, "打印", tint = Color.White)
|
Icon(Icons.Default.Print, "打印", tint = Color.White)
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
package com.example.ops_android.ui.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.ops_android.ui.LocalAppContainer
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun PrinterTestScreen(onBack: () -> Unit) {
|
||||||
|
val appContainer = LocalAppContainer.current
|
||||||
|
val printerManager = appContainer.printerManager
|
||||||
|
val sessionManager = appContainer.sessionManager
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
var isTesting by remember { mutableStateOf(false) }
|
||||||
|
var testResult by remember { mutableStateOf<String?>(null) }
|
||||||
|
var testSuccess by remember { mutableStateOf<Boolean?>(null) }
|
||||||
|
|
||||||
|
val isConnected = remember { derivedStateOf { printerManager?.isConnected() == true } }
|
||||||
|
val firmware = remember { derivedStateOf { printerManager?.getFirmwareVersion() } }
|
||||||
|
val unwindMm = remember { derivedStateOf { sessionManager.cachedUnwindMm } }
|
||||||
|
val blackMarkEnabled = remember { derivedStateOf { sessionManager.cachedBlackMarkEnabled } }
|
||||||
|
|
||||||
|
fun runTest(label: String, action: suspend () -> Unit) {
|
||||||
|
val pm = printerManager ?: return
|
||||||
|
isTesting = true
|
||||||
|
testResult = null
|
||||||
|
testSuccess = null
|
||||||
|
scope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
action()
|
||||||
|
testSuccess = true
|
||||||
|
testResult = "[$label] 执行完成"
|
||||||
|
} catch (e: Exception) {
|
||||||
|
testSuccess = false
|
||||||
|
testResult = "[$label] 失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isTesting = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text("打印机测试") },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onBack) {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "返回")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = Color(0xFF667EEA),
|
||||||
|
titleContentColor = Color.White,
|
||||||
|
navigationIconContentColor = Color.White
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(padding)
|
||||||
|
.padding(16.dp)
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text("连接状态", color = Color.Gray)
|
||||||
|
Text(
|
||||||
|
if (isConnected.value) "已连接" else "未连接",
|
||||||
|
color = if (isConnected.value) Color(0xFF4CAF50) else Color(0xFFF44336)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text("固件版本", color = Color.Gray)
|
||||||
|
Text(firmware.value ?: "未知")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Card {
|
||||||
|
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text("黑标检测", color = Color.Gray)
|
||||||
|
Text(if (blackMarkEnabled.value) "启用" else "禁用")
|
||||||
|
}
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text("走纸长度", color = Color.Gray)
|
||||||
|
Text("${unwindMm.value} mm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
Text("走纸测试", style = MaterialTheme.typography.titleMedium)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
runTest("空走纸") {
|
||||||
|
val pm = printerManager!!
|
||||||
|
pm.initDevice().getOrThrow()
|
||||||
|
pm.commit().getOrThrow()
|
||||||
|
delay(1000)
|
||||||
|
pm.closeDevice().getOrThrow()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = isConnected.value && !isTesting
|
||||||
|
) {
|
||||||
|
Text("测试A:空走纸(无内容,看默认基线)")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
runTest("空走纸 + 5行换行") {
|
||||||
|
val pm = printerManager!!
|
||||||
|
pm.initDevice().getOrThrow()
|
||||||
|
pm.addLineFeed(5)
|
||||||
|
pm.commit().getOrThrow()
|
||||||
|
delay(1000)
|
||||||
|
pm.closeDevice().getOrThrow()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = isConnected.value && !isTesting,
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF5C6BC0))
|
||||||
|
) {
|
||||||
|
Text("测试B:空走纸 + 5行换行")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
runTest("空走纸 + 10行换行") {
|
||||||
|
val pm = printerManager!!
|
||||||
|
pm.initDevice().getOrThrow()
|
||||||
|
pm.addLineFeed(10)
|
||||||
|
pm.commit().getOrThrow()
|
||||||
|
delay(1000)
|
||||||
|
pm.closeDevice().getOrThrow()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = isConnected.value && !isTesting,
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF8D6E63))
|
||||||
|
) {
|
||||||
|
Text("测试C:空走纸 + 10行换行")
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"目标:对比B(5行)和C(10行)的出纸长度,算出每行=多少mm,填入代码中校准",
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = Color.Gray
|
||||||
|
)
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
Text("SDK调试", style = MaterialTheme.typography.titleMedium)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
val pm = printerManager
|
||||||
|
if (pm != null) {
|
||||||
|
val unwindVal = pm.getUnwindPaperLenInt()
|
||||||
|
val feedSpaceVal = pm.getFeedPaperSpaceInt()
|
||||||
|
testSuccess = null
|
||||||
|
testResult = "getUnwindPaperLen = $unwindVal, getFeedPaperSpace = $feedSpaceVal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = isConnected.value && !isTesting
|
||||||
|
) {
|
||||||
|
Text("读取 SDK 内部走纸参数")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConnected.value && printerManager != null) {
|
||||||
|
Button(
|
||||||
|
onClick = { printerManager?.bindService() },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
|
||||||
|
) {
|
||||||
|
Text("绑定打印服务")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testResult?.let {
|
||||||
|
Card(
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = if (testSuccess == true) Color(0xFFE8F5E9) else if (testSuccess == false) Color(0xFFFFEBEE) else Color(0xFFF5F5F5)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
color = when (testSuccess) {
|
||||||
|
true -> Color(0xFF2E7D32)
|
||||||
|
false -> Color(0xFFC62828)
|
||||||
|
else -> Color.Gray
|
||||||
|
},
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import com.example.ops_android.ui.LocalAppContainer
|
|||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
|
onPrinterTest: () -> Unit = {},
|
||||||
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory(LocalAppContainer.current.sessionManager))
|
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory(LocalAppContainer.current.sessionManager))
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
@@ -121,6 +122,32 @@ fun SettingsScreen(
|
|||||||
|
|
||||||
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||||
|
|
||||||
|
Text("打印机设置", style = MaterialTheme.typography.titleMedium)
|
||||||
|
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text("黑标检测")
|
||||||
|
Switch(checked = uiState.blackMarkEnabled, onCheckedChange = viewModel::onBlackMarkChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uiState.blackMarkEnabled) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = uiState.unwindMm,
|
||||||
|
onValueChange = viewModel::onUnwindMmChange,
|
||||||
|
label = { Text("单次走纸长度 (mm)") },
|
||||||
|
suffix = { Text("mm") },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
Button(onClick = { viewModel.saveUnwindMm() }, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text("保存走纸长度")
|
||||||
|
}
|
||||||
|
Button(onClick = onPrinterTest, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary)) {
|
||||||
|
Text("测试打印机")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "关于",
|
text = "关于",
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ data class SettingsUiState(
|
|||||||
val isTesting: Boolean = false,
|
val isTesting: Boolean = false,
|
||||||
val testResult: String? = null,
|
val testResult: String? = null,
|
||||||
val testSuccess: Boolean = false,
|
val testSuccess: Boolean = false,
|
||||||
val message: String? = null
|
val message: String? = null,
|
||||||
|
val blackMarkEnabled: Boolean = true,
|
||||||
|
val unwindMm: String = "40"
|
||||||
)
|
)
|
||||||
|
|
||||||
class SettingsViewModel(
|
class SettingsViewModel(
|
||||||
@@ -41,6 +43,7 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
loadCurrentUrl()
|
loadCurrentUrl()
|
||||||
|
loadPrinterSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCurrentUrl() {
|
private fun loadCurrentUrl() {
|
||||||
@@ -50,10 +53,36 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadPrinterSettings() {
|
||||||
|
_uiState.value = _uiState.value.copy(
|
||||||
|
blackMarkEnabled = sessionManager.cachedBlackMarkEnabled,
|
||||||
|
unwindMm = sessionManager.cachedUnwindMm.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun onUrlChange(value: String) {
|
fun onUrlChange(value: String) {
|
||||||
_uiState.value = _uiState.value.copy(apiBaseUrl = value, message = null)
|
_uiState.value = _uiState.value.copy(apiBaseUrl = value, message = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onBlackMarkChange(enabled: Boolean) {
|
||||||
|
_uiState.value = _uiState.value.copy(blackMarkEnabled = enabled)
|
||||||
|
viewModelScope.launch { sessionManager.setBlackMarkEnabled(enabled) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUnwindMmChange(value: String) {
|
||||||
|
if (value.isEmpty() || value.all { it.isDigit() } && value.toIntOrNull()?.let { it in 1..200 } == true) {
|
||||||
|
_uiState.value = _uiState.value.copy(unwindMm = value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveUnwindMm() {
|
||||||
|
val mm = _uiState.value.unwindMm.toIntOrNull() ?: 40
|
||||||
|
viewModelScope.launch {
|
||||||
|
sessionManager.setUnwindMm(mm)
|
||||||
|
_uiState.value = _uiState.value.copy(message = "走纸长度已保存")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun saveUrl() {
|
fun saveUrl() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val url = _uiState.value.apiBaseUrl.trim()
|
val url = _uiState.value.apiBaseUrl.trim()
|
||||||
|
|||||||
@@ -67,8 +67,9 @@ fun WorkOrderDetailScreen(id: Int, onBack: () -> Unit) {
|
|||||||
wo?.let { wo_ ->
|
wo?.let { wo_ ->
|
||||||
val printerManager = com.example.ops_android.ui.LocalAppContainer.current.printerManager
|
val printerManager = com.example.ops_android.ui.LocalAppContainer.current.printerManager
|
||||||
if (printerManager != null) {
|
if (printerManager != null) {
|
||||||
|
val sm = com.example.ops_android.ui.LocalAppContainer.current.sessionManager
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
scope.launch { com.example.ops_android.printer.PrintHelper.printWorkOrderLabel(printerManager, wo_) }
|
scope.launch { com.example.ops_android.printer.PrintHelper.printWorkOrderLabel(printerManager, wo_, sm.cachedBlackMarkEnabled, sm.cachedUnwindMm) }
|
||||||
}) { Icon(Icons.Default.Print, "打印", tint = Color.White) }
|
}) { Icon(Icons.Default.Print, "打印", tint = Color.White) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#Thu Jun 25 15:33:48 CST 2026
|
#Thu Jun 25 17:53:49 CST 2026
|
||||||
VERSION_CODE=82
|
VERSION_CODE=90
|
||||||
VERSION_NAME=1.2
|
VERSION_NAME=1.2
|
||||||
|
|||||||
Reference in New Issue
Block a user