59e51671
“wangming”
1
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
/**
* Android NativeTemplateCommandBuilder 仅处理:TEXT_*、QRCODE、BARCODE、IMAGE、
* 以及 border=line 的 BLANK;其余类型在原生路径下会被静默跳过(与画布预览不一致)。
*/
import type {
LabelTemplateData,
SystemLabelTemplate,
SystemTemplateElementBase,
} from './types/printer'
import { applyTemplateData } from './templateRenderer'
function isElementHandledByNativeFastPrinter (el: SystemTemplateElementBase): boolean {
const type = String(el.type || '').toUpperCase()
if (type.startsWith('TEXT_')) return true
if (type === 'NUTRITION') return true
if (type === 'QRCODE' || type === 'IMAGE') return true
if (type === 'BARCODE') return true
if (type === 'BLANK') return true
return false
}
/**
* 将 WEIGHT / DATE / TIME / DURATION 转为 TEXT_STATIC(展示文案与合并后的 config.text 一致),
* LOGO → IMAGE,使同一套模板可走 native printTemplate,避免仅因元素类型名而整页光栅(进度长期停在 ~12–14%)。
*/
export function normalizeTemplateForNativeFastJob (
template: SystemLabelTemplate,
data: LabelTemplateData
): SystemLabelTemplate {
const now = new Date()
const pad2 = (n: number): string => String(n).padStart(2, '0')
const formatDateByPreset = (fmt: string, d: Date): string => {
const yyyy = String(d.getFullYear())
const yy = yyyy.slice(-2)
const mm = pad2(d.getMonth() + 1)
const dd = pad2(d.getDate())
const hh = pad2(d.getHours())
const min = pad2(d.getMinutes())
switch (fmt) {
case 'DD/MM/YYYY': return `${dd}/${mm}/${yyyy}`
case 'MM/DD/YYYY': return `${mm}/${dd}/${yyyy}`
case 'DD/MM/YY': return `${dd}/${mm}/${yy}`
case 'MM/DD/YY': return `${mm}/${dd}/${yy}`
case 'MM/YY': return `${mm}/${yy}`
case 'MM/DD': return `${mm}/${dd}`
case 'MM': return mm
case 'DD': return dd
case 'YY': return yy
case 'YYYY-MM-DD': return `${yyyy}-${mm}-${dd}`
case 'YYYY-MM-DD HH:mm': return `${yyyy}-${mm}-${dd} ${hh}:${min}`
case 'HH:mm': return `${hh}:${min}`
default:
return String(fmt || '')
.replace('YYYY', yyyy)
.replace('YY', yy)
.replace('MM', mm)
.replace('DD', dd)
.replace('HH', hh)
.replace('mm', min)
}
}
const elements = (template.elements || []).map((el) => {
const type = String(el.type || '').toUpperCase()
const config = (el.config || {}) as Record<string, any>
if (type === 'LOGO') {
return { ...el, type: 'IMAGE' as typeof el.type }
}
if (type === 'WEIGHT' || type === 'DATE' || type === 'TIME' || type === 'DURATION') {
let text = ''
/** 与 renderLabelPreviewCanvas.previewTextForElement 一致:后端 AUTO_DB 的 DATE/TIME 常把算好的展示串写在 format 而非 text */
let raw = String(config.text ?? config.Text ?? '').trim()
const vst = String(el.valueSourceType || '').toUpperCase()
const inputType = String(config.inputType ?? config.InputType ?? '').toLowerCase()
if (type === 'DATE') {
if (raw) {
text = applyTemplateData(raw, data)
} else if (vst === 'PRINT_INPUT' && (inputType === 'date' || inputType === 'datetime')) {
const fmt = String(config.format ?? config.Format ?? (inputType === 'datetime' ? 'YYYY-MM-DD HH:mm' : 'DD/MM/YYYY')).trim()
text = fmt
} else {
const fmt = String(config.format ?? config.Format ?? 'DD/MM/YYYY').trim() || 'DD/MM/YYYY'
const offset = Number(config.offsetDays ?? config.OffsetDays ?? 0) || 0
const d = new Date(now.getTime())
d.setDate(d.getDate() + offset)
text = formatDateByPreset(fmt, d)
}
} else if (type === 'TIME') {
if (raw) {
text = applyTemplateData(raw, data)
} else if (vst === 'PRINT_INPUT') {
text = String(config.format ?? config.Format ?? 'HH:mm').trim() || 'HH:mm'
} else {
text = formatDateByPreset('HH:mm', now)
}
} else if (type === 'DURATION') {
if (raw) {
text = applyTemplateData(raw, data)
} else {
const unit = String(config.format ?? config.Format ?? 'Days').trim() || 'Days'
const rawV = config.durationValue ?? config.value ?? config.offsetDays ?? config.DurationValue ?? config.Value ?? config.OffsetDays
const val = Number.isFinite(Number(rawV)) ? Number(rawV) : 0
text = `${val} ${unit}`.trim()
}
} else if (type === 'WEIGHT') {
if (!raw) raw = String(config.value ?? config.Value ?? '').trim()
if (raw) text = applyTemplateData(raw, data)
}
if (!text && type === 'WEIGHT') {
const v = String(config.value ?? config.Value ?? '')
const u = String(config.unit ?? config.Unit ?? '')
if (v && u && !v.endsWith(u)) text = `${v}${u}`
else text = v || u
}
return {
...el,
type: 'TEXT_STATIC' as typeof el.type,
config: { ...config, text, nativeSourceType: type },
}
}
return el
})
return { ...template, elements }
}
/** 存在任一原生不支持的元素时,预览打印应走光栅,避免「成功但缺内容/不出纸」与画布不一致 */
export function templateHasUnsupportedNativeFastElements (template: SystemLabelTemplate): boolean {
for (const el of template.elements || []) {
if (!isElementHandledByNativeFastPrinter(el)) return true
}
return false
}
|