feat: opt 板箱过磅 print

This commit is contained in:
2025-12-16 15:12:17 +08:00
parent eb1d356bd2
commit fafe092b3c
3 changed files with 229 additions and 34 deletions

View File

@@ -2,7 +2,6 @@ package com.lukouguoji.module_base.util
import android.Manifest import android.Manifest
import android.graphics.Typeface import android.graphics.Typeface
import android.util.Log
import com.gainscha.sdk2.ConnectType import com.gainscha.sdk2.ConnectType
import com.gainscha.sdk2.ConnectionListener import com.gainscha.sdk2.ConnectionListener
import com.gainscha.sdk2.Printer import com.gainscha.sdk2.Printer
@@ -18,23 +17,39 @@ import com.gprinter.io.PortManager
import com.gprinter.utils.CallbackListener import com.gprinter.utils.CallbackListener
import com.gprinter.utils.Command import com.gprinter.utils.Command
import com.gprinter.utils.ConnMethod import com.gprinter.utils.ConnMethod
import com.lukouguoji.module_base.bean.FlatcarBean import com.lukouguoji.module_base.bean.GjcUldUseBean
import com.lukouguoji.module_base.bean.GncFuBangBean import com.lukouguoji.module_base.bean.GncFuBangBean
import com.lukouguoji.module_base.bean.GncShouYunBean import com.lukouguoji.module_base.bean.GncShouYunBean
import com.lukouguoji.module_base.http.net.NetApply import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchCollect import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.loge import com.lukouguoji.module_base.ktx.loge
import com.lukouguoji.module_base.ktx.permission import com.lukouguoji.module_base.ktx.permission
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.model.LoadingModel import com.lukouguoji.module_base.model.LoadingModel
import dev.DevUtils import dev.DevUtils
import dev.utils.app.SizeUtils
import okio.internal.commonToUtf8String import okio.internal.commonToUtf8String
object PrinterUtils { object PrinterUtils {
/**
* 打印单元格数据
*/
private data class CellData(
val title: String, // 中文标签(大字)
val subtitle: String, // 英文标签(小字)
val value: String // 值(大字,垂直居中)
)
/**
* 打印表格行数据
*/
private data class GridRow(
val left: CellData? = null,
val right: CellData? = null,
val merged: CellData? = null // 合并单元格数据
)
private val loading by lazy { private val loading by lazy {
LoadingModel() LoadingModel()
} }
@@ -133,7 +148,8 @@ object PrinterUtils {
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION,
) { ) {
finder.searchPrinters(ConnectType.BLUETOOTH, finder.searchPrinters(
ConnectType.BLUETOOTH,
object : PrinterFinder.SimpleSearchPrinterResultListener() { object : PrinterFinder.SimpleSearchPrinterResultListener() {
override fun onSearchBluetoothPrinter(device: BluetoothPrinterDevice?) { override fun onSearchBluetoothPrinter(device: BluetoothPrinterDevice?) {
if (device == null) return if (device == null) return
@@ -218,28 +234,28 @@ object PrinterUtils {
var fDestinationCn = "" var fDestinationCn = ""
launchCollect({ launchCollect({
NetApply.api.getAirportByCode(bean.dest1) NetApply.api.getAirportByCode(bean.dest1)
}){ }) {
onSuccess = { onSuccess = {
destinationCn = it.data?.nameCn ?: "" destinationCn = it.data?.nameCn ?: ""
rowItem.add(listOf("日期", "航班号", "目的站")) rowItem.add(listOf("日期", "航班号", "目的站"))
rowItem.add(listOf( bean.fdate, bean.fno, bean.dest1 + destinationCn )) rowItem.add(listOf(bean.fdate, bean.fno, bean.dest1 + destinationCn))
rowItem.add(listOf("斗号", "计重", "仓管")) rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf( bean.location, bean.username, "")) rowItem.add(listOf(bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机")) rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf( "", "", "")) rowItem.add(listOf("", "", ""))
rowItem.add(listOf("监装监卸", "中转信息","备注")) rowItem.add(listOf("监装监卸", "中转信息", "备注"))
if (bean.dest2 != "") { if (bean.dest2 != "") {
launchCollect({ launchCollect({
NetApply.api.getAirportByCode(bean.dest2) NetApply.api.getAirportByCode(bean.dest2)
}){ }) {
onSuccess = { it2 -> onSuccess = { it2 ->
fDestinationCn = it2.data?.nameCn ?: "" fDestinationCn = it2.data?.nameCn ?: ""
rowItem.add(listOf( "", bean.dest2 + fDestinationCn, "")) rowItem.add(listOf("", bean.dest2 + fDestinationCn, ""))
printCommonGridNew(rowItem) printCommonGridNew(rowItem)
} }
} }
} else { } else {
rowItem.add(listOf( "", "", "")) rowItem.add(listOf("", "", ""))
printCommonGridNew(rowItem) printCommonGridNew(rowItem)
} }
} }
@@ -259,35 +275,35 @@ object PrinterUtils {
var fDestinationCn = "" var fDestinationCn = ""
launchCollect({ launchCollect({
NetApply.api.getAirportByCode(bean.fdest) NetApply.api.getAirportByCode(bean.fdest)
}){ }) {
onSuccess = { onSuccess = {
fDestinationCn = it.data?.nameCn ?: "" fDestinationCn = it.data?.nameCn ?: ""
if (bean.dest != "") { if (bean.dest != "") {
launchCollect({ launchCollect({
NetApply.api.getAirportByCode(bean.dest) NetApply.api.getAirportByCode(bean.dest)
}){ }) {
onSuccess = { it2 -> onSuccess = { it2 ->
destinationCn = it2.data?.nameCn ?: "" destinationCn = it2.data?.nameCn ?: ""
rowItem.add(listOf("日期", "航班号", "目的站")) rowItem.add(listOf("日期", "航班号", "目的站"))
rowItem.add(listOf( bean.fdate, bean.fno, bean.fdest + fDestinationCn )) rowItem.add(listOf(bean.fdate, bean.fno, bean.fdest + fDestinationCn))
rowItem.add(listOf("斗号", "计重", "仓管")) rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf( bean.location, bean.username, "")) rowItem.add(listOf(bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机")) rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf( "", "", "")) rowItem.add(listOf("", "", ""))
rowItem.add(listOf("监装监卸", "中转信息","备注")) rowItem.add(listOf("监装监卸", "中转信息", "备注"))
rowItem.add(listOf( "", bean.dest + destinationCn, "")) rowItem.add(listOf("", bean.dest + destinationCn, ""))
printCommonGridNew(rowItem) printCommonGridNew(rowItem)
} }
} }
} else { } else {
rowItem.add(listOf("日期", "航班号", "目的站")) rowItem.add(listOf("日期", "航班号", "目的站"))
rowItem.add(listOf( bean.fdate, bean.fno, bean.fdest + fDestinationCn )) rowItem.add(listOf(bean.fdate, bean.fno, bean.fdest + fDestinationCn))
rowItem.add(listOf("斗号", "计重", "仓管")) rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf( bean.location, bean.username, "")) rowItem.add(listOf(bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机")) rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf( "", "", "")) rowItem.add(listOf("", "", ""))
rowItem.add(listOf("监装监卸", "中转信息","备注")) rowItem.add(listOf("监装监卸", "中转信息", "备注"))
rowItem.add(listOf( "", "", "")) rowItem.add(listOf("", "", ""))
printCommonGridNew(rowItem) printCommonGridNew(rowItem)
} }
@@ -295,6 +311,43 @@ object PrinterUtils {
} }
} }
/**
* 打印国际出港板箱过磅挂签2列布局支持合并单元格
*/
fun printGjcBoxWeighing(bean: GjcUldUseBean) {
val rows = arrayListOf<GridRow>()
// 第1行日期 | 航班号
rows.add(GridRow(
left = CellData("日期:", "DATA:", bean.fdate),
right = CellData("航班号:", "FLIGHT NO.:", bean.fno)
))
// 第2行目的站 | 板型
rows.add(GridRow(
left = CellData("目的站:", "DESTINATION:", bean.fdest),
right = CellData("板型:", "PALLET TYPE:", bean.boardType)
))
// 第3行集装器编号合并两列
rows.add(GridRow(
merged = CellData("集装器编号:", "ULD NO.:", bean.uld)
))
// 第4行件数 | 重量
rows.add(GridRow(
left = CellData("件数:", "PIECES:", bean.pieces.ifEmpty { "0" }),
right = CellData("重量:", "GROSS WEIGHT:", bean.totalWeight.toInt().toString())
))
// 第5行备注合并两列
rows.add(GridRow(
merged = CellData("备注:", "NOTES:", bean.remark)
))
printMergedGrid(rows)
}
private fun printCommonGrid(rowItem: ArrayList<List<String>>) { private fun printCommonGrid(rowItem: ArrayList<List<String>>) {
val gridStartX = 80 val gridStartX = 80
val gridStartY = 300 val gridStartY = 300
@@ -370,10 +423,10 @@ object PrinterUtils {
if (index % 2 == 0) { if (index % 2 == 0) {
padding = 15 padding = 15
} }
if (str.length == 2){ if (str.length == 2) {
padding += 60 padding += 60
} }
if (str.length == 3){ if (str.length == 3) {
padding += 30 padding += 30
} }
if (str.length == 7) { if (str.length == 7) {
@@ -391,6 +444,127 @@ object PrinterUtils {
portManager?.writeDataImmediately(bytes) portManager?.writeDataImmediately(bytes)
} }
/**
* 打印支持合并单元格的表格
*/
private fun printMergedGrid(rows: ArrayList<GridRow>) {
val gridStartX = 30
val gridStartY = 250
val columnWidth = 570
val rowHeight = 240
val gridWidth = columnWidth * 2
val gridHeight = rowHeight * rows.size
val bytes = Tspl().apply {
addSize(100, 100)
addGap(3)
addCls()
addTextByBitmap(80, 80, 0, 130, "扬州泰州机场", Typeface.DEFAULT)
// 绘制表格横线
for (i in 0..rows.size) {
addBar(gridStartX, gridStartY + (i * rowHeight), gridWidth, 2)
}
// 绘制表格竖线(需要根据是否合并单元格决定)
rows.forEachIndexed { rowIndex, row ->
val yTop = gridStartY + (rowIndex * rowHeight)
// 左边线(总是绘制)
addBar(gridStartX, yTop, 2, rowHeight)
// 中间分隔线(非合并单元格才绘制)
if (row.merged == null) {
addBar(gridStartX + columnWidth, yTop, 2, rowHeight)
}
// 右边线(总是绘制)
if (rowIndex == rows.size - 1) {
addBar(gridStartX + gridWidth, yTop, 2, rowHeight)
}
}
// 填充单元格内容
rows.forEachIndexed { rowIndex, row ->
val yBase = gridStartY + (rowIndex * rowHeight)
if (row.merged != null) {
// 合并单元格
renderCell(
cell = row.merged,
x = gridStartX + 30,
y = yBase,
cellWidth = gridWidth,
cellHeight = rowHeight
)
} else {
// 左列单元格
row.left?.let { cell ->
renderCell(
cell = cell,
x = gridStartX + 30,
y = yBase,
cellWidth = columnWidth,
cellHeight = rowHeight
)
}
// 右列单元格
row.right?.let { cell ->
renderCell(
cell = cell,
x = gridStartX + columnWidth + 30,
y = yBase,
cellWidth = columnWidth,
cellHeight = rowHeight
)
}
}
}
addPrint(1)
}.bytes
portManager?.writeDataImmediately(bytes)
}
/**
* 渲染单元格内容
* @param cell 单元格数据
* @param x 起始X坐标
* @param y 起始Y坐标
* @param cellWidth 单元格宽度(用于计算右侧对齐)
* @param cellHeight 单元格高度(用于计算垂直居中)
*/
private fun Tspl.renderCell(
cell: CellData,
x: Int,
y: Int,
cellWidth: Int,
cellHeight: Int
) {
// 左上角:中文标签和英文标签
val titleY = y + 30 // 中文标签Y位置
val subtitleY = y + 80 // 英文标签Y位置中文下方
// 中文标签(大字,左上)
addText(x, titleY, Tspl.FONT_TSS24, 0, 3, 3, cell.title)
// 英文标签(小字,左上)
addText(x, subtitleY, Tspl.FONT_TSS24, 0, 2, 2, cell.subtitle)
// 右侧:值(大字,垂直居中)
// 计算垂直居中的Y坐标y + (cellHeight / 2) - (文字高度 / 2)
// 假设size=3的文字高度约为80像素
val valueY = y + (cellHeight / 2) - 40 // 垂直居中
// 计算右侧X坐标中文标签宽度 + 间距
// 假设"目的站:"宽度约为180像素"DESTINATION:"宽度约为240像素
val valueX = x + 280 // 右侧偏移量
// 值(大字,垂直居中,右侧)
addText(valueX, valueY, Tspl.FONT_TSS24, 0, 3, 3, cell.value)
}
fun printTest() { fun printTest() {
getConnectedPrinters().forEach { getConnectedPrinters().forEach {
val bytes = v(Instruction.TSC.toString(), null).bytes val bytes = v(Instruction.TSC.toString(), null).bytes

View File

@@ -21,9 +21,11 @@ import com.lukouguoji.module_base.ktx.noNull
import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody import com.lukouguoji.module_base.ktx.toRequestBody
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import com.lukouguoji.module_base.model.BluetoothDialogModel
import com.lukouguoji.module_base.model.ScanModel import com.lukouguoji.module_base.model.ScanModel
import com.lukouguoji.module_base.util.Common import com.lukouguoji.module_base.util.Common
import com.lukouguoji.module_base.util.DictUtils import com.lukouguoji.module_base.util.DictUtils
import com.lukouguoji.module_base.util.PrinterUtils
import dev.utils.app.info.KeyValue import dev.utils.app.info.KeyValue
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Calendar import java.util.Calendar
@@ -333,6 +335,12 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
onSuccess = { result -> onSuccess = { result ->
if (result.verifySuccess()) { if (result.verifySuccess()) {
showToast("添加成功") showToast("添加成功")
// 如果勾选了打印挂签,则执行打印
if (printTag.value == true) {
executePrint()
}
// 发送刷新事件 // 发送刷新事件
viewModelScope.launch { viewModelScope.launch {
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh") FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
@@ -345,6 +353,19 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
} }
} }
/**
* 执行打印
*/
private fun executePrint() {
val bean = dataBean.value ?: return
// 使用 BluetoothDialogModel 选择打印机并打印
BluetoothDialogModel()
.showCallBack {
PrinterUtils.printGjcBoxWeighing(bean)
}
}
/** /**
* 处理扫码结果 * 处理扫码结果
*/ */

View File

@@ -389,7 +389,7 @@
<CheckBox <CheckBox
android:id="@+id/cbPrint" android:id="@+id/cbPrint"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="48dp"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:layout_toStartOf="@+id/btnReset" android:layout_toStartOf="@+id/btnReset"