feat: 重构工单和物品打印模板为左右排布,支持二维码和条码
- 工单模板:左侧二维码+客户,右侧工单ID/标题/描述 - 物品模板:左侧二维码,右侧物品ID/名称/数量/位置/客户,底部条码+序列号 - 打印标签设置:新增标签宽度/高度/打印前回退长度设置 - QR码JSON增加type=ops_sys字段区分系统来源 - WorkOrder/Item模型增加linkedCustomers等关联数据 - 使用Canvas绘制Bitmap实现自定义打印布局
This commit is contained in:
@@ -28,6 +28,9 @@ class SessionManager(private val context: Context) {
|
||||
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")
|
||||
private val KEY_LABEL_WIDTH_MM = intPreferencesKey("label_width_mm")
|
||||
private val KEY_LABEL_HEIGHT_MM = intPreferencesKey("label_height_mm")
|
||||
private val KEY_PRINT_RETRACT_MM = intPreferencesKey("print_retract_mm")
|
||||
}
|
||||
|
||||
@Volatile
|
||||
@@ -46,6 +49,18 @@ class SessionManager(private val context: Context) {
|
||||
var cachedUnwindMm: Int = 40
|
||||
private set
|
||||
|
||||
@Volatile
|
||||
var cachedLabelWidthMm: Int = 53
|
||||
private set
|
||||
|
||||
@Volatile
|
||||
var cachedLabelHeightMm: Int = 40
|
||||
private set
|
||||
|
||||
@Volatile
|
||||
var cachedPrintRetractMm: Int = 10
|
||||
private set
|
||||
|
||||
fun clearCookieCache() {
|
||||
cachedCookieValue = ""
|
||||
}
|
||||
@@ -59,6 +74,9 @@ class SessionManager(private val context: Context) {
|
||||
cachedCookieValue = prefs[KEY_COOKIE_VALUE] ?: ""
|
||||
cachedBlackMarkEnabled = prefs[KEY_PRINTER_BLACK_MARK] ?: true
|
||||
cachedUnwindMm = prefs[KEY_PRINTER_UNWIND_MM] ?: 40
|
||||
cachedLabelWidthMm = prefs[KEY_LABEL_WIDTH_MM] ?: 53
|
||||
cachedLabelHeightMm = prefs[KEY_LABEL_HEIGHT_MM] ?: 40
|
||||
cachedPrintRetractMm = prefs[KEY_PRINT_RETRACT_MM] ?: 10
|
||||
}
|
||||
|
||||
suspend fun getApiBaseUrl(): String = cachedBaseUrl.ifEmpty {
|
||||
@@ -115,4 +133,19 @@ class SessionManager(private val context: Context) {
|
||||
cachedUnwindMm = mm
|
||||
context.dataStore.edit { it[KEY_PRINTER_UNWIND_MM] = mm }
|
||||
}
|
||||
|
||||
suspend fun setLabelWidthMm(mm: Int) {
|
||||
cachedLabelWidthMm = mm
|
||||
context.dataStore.edit { it[KEY_LABEL_WIDTH_MM] = mm }
|
||||
}
|
||||
|
||||
suspend fun setLabelHeightMm(mm: Int) {
|
||||
cachedLabelHeightMm = mm
|
||||
context.dataStore.edit { it[KEY_LABEL_HEIGHT_MM] = mm }
|
||||
}
|
||||
|
||||
suspend fun setPrintRetractMm(mm: Int) {
|
||||
cachedPrintRetractMm = mm
|
||||
context.dataStore.edit { it[KEY_PRINT_RETRACT_MM] = mm }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.example.ops_android.data.model
|
||||
|
||||
import com.example.ops_android.data.remote.api.LinkedCustomerInfo
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Item(
|
||||
@@ -14,7 +15,10 @@ data class Item(
|
||||
@SerializedName("CreatorID") val user_id: Int? = null,
|
||||
@SerializedName("barcode") private val _barcode: String? = null,
|
||||
@SerializedName("spec") private val _spec: String? = null,
|
||||
@SerializedName("move_history") private val _move_history: List<MoveHistory>? = null
|
||||
@SerializedName("move_history") private val _move_history: List<MoveHistory>? = null,
|
||||
// Filled from detail API
|
||||
@kotlin.jvm.Transient val linkedCustomers: List<LinkedCustomerInfo>? = null,
|
||||
@kotlin.jvm.Transient val containerBreadcrumb: String? = null
|
||||
) {
|
||||
val barcode: String? get() = _barcode
|
||||
val spec: String? get() = _spec
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.example.ops_android.data.model
|
||||
|
||||
import com.example.ops_android.data.remote.api.LinkedCustomer
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class WorkOrder(
|
||||
@@ -11,7 +12,8 @@ data class WorkOrder(
|
||||
@SerializedName("UpdatedAt") val updated_at: String? = null,
|
||||
@SerializedName("UserID") val user_id: Int? = null,
|
||||
// Filled from detail API
|
||||
@kotlin.jvm.Transient val commits: List<Commit>? = null
|
||||
@kotlin.jvm.Transient val commits: List<Commit>? = null,
|
||||
@kotlin.jvm.Transient val linkedCustomers: List<LinkedCustomer>? = null
|
||||
)
|
||||
|
||||
data class WorkOrderCommit(
|
||||
|
||||
@@ -101,7 +101,12 @@ class WarehouseRepository(
|
||||
return try {
|
||||
val response = warehouseApi.getItem(GetItemRequest(id))
|
||||
if (response.errCode == 0 && response.data != null) {
|
||||
Result.success(response.data)
|
||||
val resp = response.data
|
||||
val itm = resp.item?.copy(
|
||||
linkedCustomers = resp.customers,
|
||||
containerBreadcrumb = resp.container_breadcrumb
|
||||
)
|
||||
Result.success(resp.copy(item = itm))
|
||||
} else {
|
||||
Result.failure(Exception("获取物品详情失败"))
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ class WorkOrderRepository(
|
||||
return try {
|
||||
val response = workOrderApi.get(WorkOrderGetRequest(id))
|
||||
if (response.errCode == 0 && response.data?.order != null) {
|
||||
Result.success(response.data.order)
|
||||
val detail = response.data
|
||||
val wo = detail.order.copy(
|
||||
linkedCustomers = detail.linkedCustomers
|
||||
)
|
||||
Result.success(wo)
|
||||
} else {
|
||||
Result.failure(Exception("获取工单详情失败"))
|
||||
}
|
||||
|
||||
@@ -1,16 +1,148 @@
|
||||
package com.example.ops_android.printer
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import com.example.ops_android.data.model.Item
|
||||
import com.example.ops_android.data.model.PurchaseOrder
|
||||
import com.example.ops_android.data.model.WorkOrder
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.oned.Code128Writer
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
object PrintHelper {
|
||||
|
||||
private val printMutex = Mutex()
|
||||
private const val DOTS_PER_MM = 8
|
||||
|
||||
private fun mmToDots(mm: Int): Int = mm * DOTS_PER_MM
|
||||
|
||||
private fun buildQrContent(module: String, id: Int): String {
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||
return JSONObject().apply {
|
||||
put("v", "v1")
|
||||
put("module", module)
|
||||
put("id", id)
|
||||
put("printedAt", sdf.format(Date()))
|
||||
put("type", "ops_sys")
|
||||
}.toString()
|
||||
}
|
||||
|
||||
private fun generateQRBitmap(content: String, size: Int): Bitmap {
|
||||
val hints = mapOf(EncodeHintType.MARGIN to 1)
|
||||
val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, size, size, hints)
|
||||
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
|
||||
for (y in 0 until size) {
|
||||
for (x in 0 until size) {
|
||||
bitmap.setPixel(x, y, if (bitMatrix[x, y]) Color.BLACK else Color.WHITE)
|
||||
}
|
||||
}
|
||||
return bitmap
|
||||
}
|
||||
|
||||
private fun drawTextBlock(
|
||||
canvas: Canvas,
|
||||
label: String,
|
||||
value: String,
|
||||
x: Float,
|
||||
y: Float,
|
||||
maxWidth: Float,
|
||||
maxLines: Int = 1,
|
||||
textSize: Float = 22f,
|
||||
bold: Boolean = false,
|
||||
lineSpacingExtra: Float = 4f
|
||||
): Float {
|
||||
val text = "$label$value"
|
||||
val paint = TextPaint().apply {
|
||||
color = Color.BLACK
|
||||
this.textSize = textSize
|
||||
isAntiAlias = true
|
||||
isFakeBoldText = bold
|
||||
}
|
||||
|
||||
val builder = StaticLayout.Builder.obtain(text, 0, text.length, paint, maxWidth.toInt())
|
||||
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||
.setMaxLines(maxLines)
|
||||
.setEllipsize(android.text.TextUtils.TruncateAt.END)
|
||||
.setLineSpacing(lineSpacingExtra, 1f)
|
||||
|
||||
val layout = builder.build()
|
||||
canvas.save()
|
||||
canvas.translate(x, y)
|
||||
layout.draw(canvas)
|
||||
canvas.restore()
|
||||
|
||||
return y + layout.height
|
||||
}
|
||||
|
||||
private fun buildWorkOrderLabelBitmap(wo: WorkOrder, widthDots: Int, heightDots: Int): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(widthDots, heightDots, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
canvas.drawColor(Color.WHITE)
|
||||
|
||||
val padding = 12
|
||||
val dividerX = (widthDots * 0.48).toInt()
|
||||
val leftInnerWidth = dividerX - padding * 2
|
||||
val qrSize = (leftInnerWidth * 0.95).toInt().coerceAtLeast(140)
|
||||
val qrX = (dividerX - qrSize) / 2
|
||||
val qrY = padding
|
||||
|
||||
val qrContent = buildQrContent("workorder", wo.id)
|
||||
val qrBitmap = generateQRBitmap(qrContent, qrSize)
|
||||
canvas.drawBitmap(qrBitmap, qrX.toFloat(), qrY.toFloat(), null)
|
||||
|
||||
val sepPaint = Paint().apply {
|
||||
color = Color.BLACK
|
||||
strokeWidth = 1.5f
|
||||
isAntiAlias = true
|
||||
}
|
||||
canvas.drawLine(dividerX.toFloat(), padding.toFloat(), dividerX.toFloat(), (heightDots - padding).toFloat(), sepPaint)
|
||||
|
||||
val textX = (dividerX + padding).toFloat()
|
||||
val textMaxWidth = (widthDots - dividerX - padding * 2).toFloat()
|
||||
var textY = (padding - 2).toFloat()
|
||||
val maxTextY = (heightDots - padding).toFloat()
|
||||
|
||||
textY = drawTextBlock(canvas, "", "工单:${wo.id}", textX, textY, textMaxWidth, textSize = 32f, bold = true, lineSpacingExtra = 6f)
|
||||
|
||||
if (textY < maxTextY) {
|
||||
textY = drawTextBlock(canvas, "", "标题:${wo.title ?: "-"}", textX, textY, textMaxWidth, maxLines = 3, textSize = 28f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
if (textY < maxTextY) {
|
||||
textY = drawTextBlock(canvas, "", "描述:${wo.description ?: "-"}", textX, textY, textMaxWidth, maxLines = 6, textSize = 28f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
val customerNames = wo.linkedCustomers
|
||||
?.mapNotNull { c ->
|
||||
listOfNotNull(c.first_name, c.last_name)
|
||||
.joinToString(" ")
|
||||
.ifBlank { null }
|
||||
}
|
||||
?.joinToString(", ")
|
||||
?: "-"
|
||||
val customerText = "客户:$customerNames"
|
||||
val customerY = qrY + qrSize + 4
|
||||
val leftTextMaxWidth = (dividerX - padding * 2).toFloat()
|
||||
if (customerY < heightDots - padding) {
|
||||
drawTextBlock(canvas, "", customerText, padding.toFloat(), customerY.toFloat(), leftTextMaxWidth, maxLines = 2, textSize = 28f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
suspend fun printOrderLabel(
|
||||
printerManager: PrinterManager,
|
||||
@@ -45,20 +177,22 @@ object PrintHelper {
|
||||
suspend fun printWorkOrderLabel(
|
||||
printerManager: PrinterManager,
|
||||
workOrder: WorkOrder,
|
||||
labelWidthMm: Int = 53,
|
||||
labelHeightMm: Int = 40,
|
||||
blackMarkEnabled: Boolean = true,
|
||||
unwindMm: Int = 40
|
||||
unwindMm: Int = 40,
|
||||
printRetractMm: Int = 10
|
||||
): Result<Unit> = printMutex.withLock {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
printerManager.initDevice().onFailure { return@withContext Result.failure(it) }
|
||||
printerManager.setFontSize(24)
|
||||
printerManager.setBold(true)
|
||||
printerManager.addText("工单: ${workOrder.title ?: "-"}\n")
|
||||
printerManager.setFontSize(20)
|
||||
printerManager.setBold(false)
|
||||
if (workOrder.id > 0) {
|
||||
printerManager.addBarcode("WO-${workOrder.id}")
|
||||
if (printRetractMm > 0) {
|
||||
printerManager.setUnwindLength(mmToDots(printRetractMm))
|
||||
}
|
||||
val widthDots = mmToDots(labelWidthMm)
|
||||
val heightDots = mmToDots(labelHeightMm)
|
||||
val bitmap = buildWorkOrderLabelBitmap(workOrder, widthDots, heightDots)
|
||||
printerManager.printImage(bitmap, 1)
|
||||
if (blackMarkEnabled) {
|
||||
printerManager.addLineFeed(2)
|
||||
printerManager.enableMarkDetection(true)
|
||||
@@ -72,24 +206,109 @@ object PrintHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildItemLabelBitmap(item: Item, widthDots: Int, heightDots: Int): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(widthDots, heightDots, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
canvas.drawColor(Color.WHITE)
|
||||
|
||||
val padding = 12
|
||||
val barcodeReserve = if (!item.serial_number.isNullOrBlank()) 70 else 0
|
||||
val dividerX = (widthDots * 0.48).toInt()
|
||||
val leftInnerWidth = dividerX - padding * 2
|
||||
val qrSize = (leftInnerWidth * 0.95).toInt().coerceAtLeast(140)
|
||||
val qrX = (dividerX - qrSize) / 2
|
||||
val qrY = padding
|
||||
|
||||
val qrContent = buildQrContent("item", item.id)
|
||||
val qrBitmap = generateQRBitmap(qrContent, qrSize)
|
||||
canvas.drawBitmap(qrBitmap, qrX.toFloat(), qrY.toFloat(), null)
|
||||
|
||||
val sepPaint = Paint().apply {
|
||||
color = Color.BLACK
|
||||
strokeWidth = 1.5f
|
||||
isAntiAlias = true
|
||||
}
|
||||
|
||||
val textX = (dividerX + padding).toFloat()
|
||||
val textMaxWidth = (widthDots - dividerX - padding * 2).toFloat()
|
||||
var textY = (padding - 2).toFloat()
|
||||
val maxTextY = heightDots - padding - barcodeReserve
|
||||
|
||||
textY = drawTextBlock(canvas, "", "物品:${item.id}", textX, textY, textMaxWidth, textSize = 32f, bold = true, lineSpacingExtra = 6f)
|
||||
|
||||
if (textY < maxTextY) {
|
||||
textY = drawTextBlock(canvas, "", "名称:${item.name ?: "-"}", textX, textY, textMaxWidth, maxLines = 3, textSize = 25f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
if (textY < maxTextY && item.quantity != 1) {
|
||||
textY = drawTextBlock(canvas, "", "数量:${item.quantity}", textX, textY, textMaxWidth, textSize = 25f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
if (textY < maxTextY && !item.containerBreadcrumb.isNullOrBlank()) {
|
||||
textY = drawTextBlock(canvas, "", "位置:${item.containerBreadcrumb}", textX, textY, textMaxWidth, maxLines = 4, textSize = 25f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
if (textY < maxTextY && !item.linkedCustomers.isNullOrEmpty()) {
|
||||
val customerNames = item.linkedCustomers
|
||||
.mapNotNull { c ->
|
||||
listOfNotNull(c.first_name, c.last_name)
|
||||
.joinToString(" ")
|
||||
.ifBlank { null }
|
||||
}
|
||||
.joinToString(", ")
|
||||
textY = drawTextBlock(canvas, "", "客户:$customerNames", textX, textY, textMaxWidth, maxLines = 2, textSize = 25f, lineSpacingExtra = 4f)
|
||||
}
|
||||
|
||||
canvas.drawLine(dividerX.toFloat(), padding.toFloat(), dividerX.toFloat(), maxTextY.toFloat(), sepPaint)
|
||||
|
||||
if (!item.serial_number.isNullOrBlank()) {
|
||||
canvas.drawLine(padding.toFloat(), maxTextY.toFloat(), (widthDots - padding).toFloat(), maxTextY.toFloat(), sepPaint)
|
||||
|
||||
val snTextY = maxTextY + 4
|
||||
val snTextHeight = drawTextBlock(canvas, "", item.serial_number ?: "",
|
||||
padding.toFloat(), snTextY.toFloat(),
|
||||
(widthDots - padding * 2).toFloat(),
|
||||
maxLines = 1, textSize = 22f, lineSpacingExtra = 0f
|
||||
) - snTextY
|
||||
|
||||
val barcodeWidth = widthDots - padding * 4
|
||||
val barcodeHeight = 35
|
||||
val barcodeX = (widthDots - barcodeWidth) / 2
|
||||
val barcodeY = snTextY + snTextHeight + 4
|
||||
|
||||
val code128 = Code128Writer()
|
||||
val barcodeMatrix = code128.encode(item.serial_number, BarcodeFormat.CODE_128, barcodeWidth, barcodeHeight)
|
||||
val barcodeBitmap = Bitmap.createBitmap(barcodeWidth, barcodeHeight, Bitmap.Config.ARGB_8888)
|
||||
for (y in 0 until barcodeHeight) {
|
||||
for (x in 0 until barcodeWidth) {
|
||||
barcodeBitmap.setPixel(x, y, if (barcodeMatrix[x, y]) Color.BLACK else Color.WHITE)
|
||||
}
|
||||
}
|
||||
canvas.drawBitmap(barcodeBitmap, barcodeX.toFloat(), barcodeY.toFloat(), null)
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
suspend fun printItemLabel(
|
||||
printerManager: PrinterManager,
|
||||
item: Item,
|
||||
labelWidthMm: Int = 53,
|
||||
labelHeightMm: Int = 40,
|
||||
blackMarkEnabled: Boolean = true,
|
||||
unwindMm: Int = 40
|
||||
unwindMm: Int = 40,
|
||||
printRetractMm: Int = 10
|
||||
): Result<Unit> = printMutex.withLock {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
printerManager.initDevice().onFailure { return@withContext Result.failure(it) }
|
||||
printerManager.setFontSize(24)
|
||||
printerManager.setBold(true)
|
||||
printerManager.addText("${item.name ?: "-"}\n")
|
||||
printerManager.setFontSize(18)
|
||||
printerManager.setBold(false)
|
||||
item.serial_number?.let { printerManager.addText("编号: $it\n") }
|
||||
if (item.id > 0) {
|
||||
printerManager.addBarcode("ITEM-${item.id}")
|
||||
if (printRetractMm > 0) {
|
||||
printerManager.setUnwindLength(mmToDots(printRetractMm))
|
||||
}
|
||||
val widthDots = mmToDots(labelWidthMm)
|
||||
val heightDots = mmToDots(labelHeightMm)
|
||||
val bitmap = buildItemLabelBitmap(item, widthDots, heightDots)
|
||||
printerManager.printImage(bitmap, 1)
|
||||
if (blackMarkEnabled) {
|
||||
printerManager.addLineFeed(2)
|
||||
printerManager.enableMarkDetection(true)
|
||||
|
||||
@@ -148,6 +148,49 @@ fun SettingsScreen(
|
||||
|
||||
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
|
||||
Text("标签尺寸", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
OutlinedTextField(
|
||||
value = uiState.labelWidthMm,
|
||||
onValueChange = viewModel::onLabelWidthChange,
|
||||
label = { Text("标签宽度 (mm)") },
|
||||
suffix = { Text("mm") },
|
||||
singleLine = true,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = uiState.labelHeightMm,
|
||||
onValueChange = viewModel::onLabelHeightChange,
|
||||
label = { Text("标签高度 (mm)") },
|
||||
suffix = { Text("mm") },
|
||||
singleLine = true,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Button(onClick = { viewModel.saveLabelSize() }, modifier = Modifier.fillMaxWidth()) {
|
||||
Text("保存标签尺寸")
|
||||
}
|
||||
|
||||
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
|
||||
Text("打印校准", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
OutlinedTextField(
|
||||
value = uiState.printRetractMm,
|
||||
onValueChange = viewModel::onPrintRetractMmChange,
|
||||
label = { Text("打印前回退长度 (mm)") },
|
||||
suffix = { Text("mm") },
|
||||
singleLine = true,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Button(onClick = { viewModel.savePrintRetractMm() }, modifier = Modifier.fillMaxWidth()) {
|
||||
Text("保存回退长度")
|
||||
}
|
||||
|
||||
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
|
||||
Text(
|
||||
text = "关于",
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
|
||||
@@ -25,7 +25,10 @@ data class SettingsUiState(
|
||||
val testSuccess: Boolean = false,
|
||||
val message: String? = null,
|
||||
val blackMarkEnabled: Boolean = true,
|
||||
val unwindMm: String = "40"
|
||||
val unwindMm: String = "40",
|
||||
val labelWidthMm: String = "53",
|
||||
val labelHeightMm: String = "40",
|
||||
val printRetractMm: String = "10"
|
||||
)
|
||||
|
||||
class SettingsViewModel(
|
||||
@@ -56,7 +59,10 @@ class SettingsViewModel(
|
||||
private fun loadPrinterSettings() {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
blackMarkEnabled = sessionManager.cachedBlackMarkEnabled,
|
||||
unwindMm = sessionManager.cachedUnwindMm.toString()
|
||||
unwindMm = sessionManager.cachedUnwindMm.toString(),
|
||||
labelWidthMm = sessionManager.cachedLabelWidthMm.toString(),
|
||||
labelHeightMm = sessionManager.cachedLabelHeightMm.toString(),
|
||||
printRetractMm = sessionManager.cachedPrintRetractMm.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -83,6 +89,42 @@ class SettingsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun onLabelWidthChange(value: String) {
|
||||
if (value.isEmpty() || value.all { it.isDigit() } && value.toIntOrNull()?.let { it in 10..200 } == true) {
|
||||
_uiState.value = _uiState.value.copy(labelWidthMm = value)
|
||||
}
|
||||
}
|
||||
|
||||
fun onLabelHeightChange(value: String) {
|
||||
if (value.isEmpty() || value.all { it.isDigit() } && value.toIntOrNull()?.let { it in 10..200 } == true) {
|
||||
_uiState.value = _uiState.value.copy(labelHeightMm = value)
|
||||
}
|
||||
}
|
||||
|
||||
fun saveLabelSize() {
|
||||
val width = _uiState.value.labelWidthMm.toIntOrNull() ?: 53
|
||||
val height = _uiState.value.labelHeightMm.toIntOrNull() ?: 40
|
||||
viewModelScope.launch {
|
||||
sessionManager.setLabelWidthMm(width)
|
||||
sessionManager.setLabelHeightMm(height)
|
||||
_uiState.value = _uiState.value.copy(message = "标签尺寸已保存")
|
||||
}
|
||||
}
|
||||
|
||||
fun onPrintRetractMmChange(value: String) {
|
||||
if (value.isEmpty() || value.all { it.isDigit() } && value.toIntOrNull()?.let { it in 0..50 } == true) {
|
||||
_uiState.value = _uiState.value.copy(printRetractMm = value)
|
||||
}
|
||||
}
|
||||
|
||||
fun savePrintRetractMm() {
|
||||
val mm = _uiState.value.printRetractMm.toIntOrNull() ?: 10
|
||||
viewModelScope.launch {
|
||||
sessionManager.setPrintRetractMm(mm)
|
||||
_uiState.value = _uiState.value.copy(message = "回退长度已保存")
|
||||
}
|
||||
}
|
||||
|
||||
fun saveUrl() {
|
||||
viewModelScope.launch {
|
||||
val url = _uiState.value.apiBaseUrl.trim()
|
||||
|
||||
@@ -129,9 +129,7 @@ fun ItemDetailScreen(id: Int, onBack: () -> Unit, onCreateWorkOrder: (Int) -> Un
|
||||
val printerManager = app.printerManager
|
||||
if (printerManager != null) {
|
||||
IconButton(onClick = {
|
||||
scope.launch {
|
||||
PrintHelper.printItemLabel(printerManager, itm, app.sessionManager.cachedBlackMarkEnabled, app.sessionManager.cachedUnwindMm)
|
||||
}
|
||||
scope.launch { PrintHelper.printItemLabel(printerManager, itm, labelWidthMm = app.sessionManager.cachedLabelWidthMm, labelHeightMm = app.sessionManager.cachedLabelHeightMm, blackMarkEnabled = app.sessionManager.cachedBlackMarkEnabled, unwindMm = app.sessionManager.cachedUnwindMm, printRetractMm = app.sessionManager.cachedPrintRetractMm) }
|
||||
}) {
|
||||
Icon(Icons.Default.Print, "打印", tint = Color.White)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ fun WorkOrderDetailScreen(id: Int, onBack: () -> Unit) {
|
||||
if (printerManager != null) {
|
||||
val sm = com.example.ops_android.ui.LocalAppContainer.current.sessionManager
|
||||
IconButton(onClick = {
|
||||
scope.launch { com.example.ops_android.printer.PrintHelper.printWorkOrderLabel(printerManager, wo_, sm.cachedBlackMarkEnabled, sm.cachedUnwindMm) }
|
||||
scope.launch { com.example.ops_android.printer.PrintHelper.printWorkOrderLabel(printerManager, wo_, labelWidthMm = sm.cachedLabelWidthMm, labelHeightMm = sm.cachedLabelHeightMm, blackMarkEnabled = sm.cachedBlackMarkEnabled, unwindMm = sm.cachedUnwindMm, printRetractMm = sm.cachedPrintRetractMm) }
|
||||
}) { Icon(Icons.Default.Print, "打印", tint = Color.White) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user