You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
6.7 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const FONT_SIZE_MAP = {
1: '14px',
2: '18px',
3: '28px',
}
const escapeHtml = (value) => {
return String(value ?? '')
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
const replacePlaceholders = (content, data) => {
return String(content || '').replace(/\{\{(\w+)\}\}/g, (_, key) => {
const val = data[key]
return val !== undefined && val !== null ? String(val) : ''
})
}
export const PLACEHOLDER_TEXT_CONTENTS = ['{{title}}', '{{ticketNo}}']
export const isPlaceholderTextSection = (section) => {
if (section?.type !== 'text') return false
const content = String(section.content || '').trim()
return PLACEHOLDER_TEXT_CONTENTS.includes(content)
}
const normalizeTextSection = (section) => {
const normalized = {
type: 'text',
content: String(section.content || ''),
fontSize: Number(section.fontSize) || 1,
align: section.align || 'left',
}
if (section.bold) {
normalized.bold = true
}
return normalized
}
const normalizeFieldSection = (section) => {
const normalized = {
type: 'field',
key: String(section.key || ''),
label: String(section.label || ''),
show: section.show !== false,
}
if (section.suffix) {
normalized.suffix = String(section.suffix)
}
return normalized
}
const normalizeSection = (section) => {
if (!section || !section.type) return null
if (section.type === 'line') {
return { type: 'line' }
}
if (section.type === 'text') {
return normalizeTextSection(section)
}
if (section.type === 'field') {
return normalizeFieldSection(section)
}
return null
}
export const normalizePrintTemplate = (template) => {
const sections = Array.isArray(template?.sections) ? template.sections : []
return {
cutPaper: template?.cutPaper !== false,
sections: sections.map(normalizeSection).filter(Boolean),
}
}
export const parsePrintTemplate = (raw) => {
console.log('[print-template] parsePrintTemplate 输入:', raw)
if (raw === undefined || raw === null) {
console.log('[print-template] 输入为空')
return normalizePrintTemplate({ sections: [], cutPaper: true })
}
if (typeof raw === 'string') {
const text = raw.trim()
if (!text) {
return normalizePrintTemplate({ sections: [], cutPaper: true })
}
try {
return parsePrintTemplate(JSON.parse(text))
} catch (error) {
console.log('[print-template] JSON 字符串解析失败:', error, text)
return normalizePrintTemplate({ sections: [], cutPaper: true })
}
}
if (Array.isArray(raw.sections)) {
const result = normalizePrintTemplate(raw)
console.log('[print-template] 直接解析 sections数量:', result.sections.length)
return result
}
if (typeof raw.msg === 'string' && raw.msg.trim()) {
try {
const parsed = JSON.parse(raw.msg)
if (parsed && Array.isArray(parsed.sections)) {
const result = normalizePrintTemplate(parsed)
console.log('[print-template] 从 msg 解析sections 数量:', result.sections.length)
return result
}
} catch (error) {
console.log('[print-template] msg JSON 解析失败:', error, raw.msg)
}
}
if (raw.data !== undefined && raw.data !== null) {
console.log('[print-template] 尝试解析 data 字段:', raw.data)
return parsePrintTemplate(raw.data)
}
console.log('[print-template] 未能识别模板结构,可用字段:', Object.keys(raw))
return normalizePrintTemplate({ sections: [], cutPaper: true })
}
// 保存/获取统一格式:{ cutPaper, sections }
export const buildPrintTemplateSaveBody = (template) => {
return normalizePrintTemplate(template)
}
// 极小模板,用于排查 sections 过大是否导致签名/网关异常
export const getMinimalPrintTemplateForTest = () =>
normalizePrintTemplate({
cutPaper: true,
sections: [
{ type: 'text', content: '测试', fontSize: 1, align: 'center' },
],
})
export const PRINT_SAMPLE_DATA = {
title: '国家税务总局',
ticketNo: 'A001',
businessType: '综合业务',
waitingCount: 5,
windowNumber: '3号窗口',
}
export const getDefaultPrintTemplate = () =>
normalizePrintTemplate({
cutPaper: true,
sections: [
{ type: 'text', content: '{{title}}', fontSize: 2, bold: true, align: 'center' },
{ type: 'line' },
{ type: 'text', content: '{{ticketNo}}', fontSize: 3, bold: true, align: 'center' },
{ type: 'line' },
{ type: 'field', key: 'businessType', label: '业务', show: true },
{ type: 'field', key: 'waitingCount', label: '等候', show: true, suffix: ' 人' },
{ type: 'field', key: 'windowNumber', label: '地点', show: true },
{ type: 'line' },
{ type: 'text', content: '请留意广播和显示屏叫号', fontSize: 1, align: 'center' },
],
})
export const buildPrintPreviewData = (template, context = {}) => {
return {
title: context.title ?? '',
ticketNo: context.ticketNo ?? '——',
businessType: context.businessType ?? context.businessName ?? '-',
waitingCount: context.waitingCount ?? 0,
windowNumber: context.windowNumber ?? '-',
rankUserName: context.rankUserName ?? '',
...context,
}
}
export const renderPrintTemplateHtml = (template, data = {}) => {
if (!template || !Array.isArray(template.sections)) {
return '<p style="text-align:center;color:#999;">暂无打印模板</p>'
}
const parts = [
'<div style="font-family:SimHei,monospace;padding:16px 12px;background:#fff;color:#111;">',
]
template.sections.forEach((section) => {
if (section?.show === false) return
if (section.type === 'line') {
parts.push('<div style="border-top:1px dashed #999;margin:10px 0;"></div>')
return
}
if (section.type === 'text') {
const text = replacePlaceholders(section.content, data)
const fontSize = FONT_SIZE_MAP[section.fontSize] || FONT_SIZE_MAP[1]
const fontWeight = section.bold ? 'bold' : 'normal'
const textAlign = section.align || 'left'
parts.push(
`<p style="margin:6px 0;font-size:${fontSize};font-weight:${fontWeight};text-align:${textAlign};line-height:1.4;">${escapeHtml(text)}</p>`
)
return
}
if (section.type === 'field') {
const value = data[section.key]
const display =
value !== undefined && value !== null && value !== '' ? String(value) : '-'
const suffix = section.suffix || ''
const label = section.label || ''
parts.push(
`<p style="margin:6px 0;font-size:14px;line-height:1.5;"><span>${escapeHtml(label)}</span><span>${escapeHtml(display)}${escapeHtml(suffix)}</span></p>`
)
}
})
parts.push('</div>')
return parts.join('')
}