/** * 与 Web 管理端 labelFormDatePreview 一致:templateProductDefaults 中日期/时间/时长存 JSON * `{"unit":"Days","value":"2"}`,预览与打印时按 BaseTime 解析为 config.text。 */ import type { SystemTemplateElementBase } from '../print/types/printer' const OFFSET_UNITS = new Set([ 'Minutes', 'Hours', 'Days', 'Weeks', 'Months (30 Day)', 'Years', ]) export function applyOffsetToDate (base: Date, amount: number, unit: string): Date { const d = new Date(base.getTime()) const u = String(unit ?? '').trim() switch (u) { case 'Minutes': d.setMinutes(d.getMinutes() + amount) break case 'Hours': d.setHours(d.getHours() + amount) break case 'Days': d.setDate(d.getDate() + amount) break case 'Weeks': d.setDate(d.getDate() + amount * 7) break case 'Months (30 Day)': d.setDate(d.getDate() + amount * 30) break case 'Years': d.setFullYear(d.getFullYear() + amount) break default: d.setDate(d.getDate() + amount) } return d } function formatDateByPreset (format: string, date: Date): string { const yyyy = String(date.getFullYear()) const yy = yyyy.slice(-2) const mm = String(date.getMonth() + 1).padStart(2, '0') const dd = String(date.getDate()).padStart(2, '0') const hh = String(date.getHours()).padStart(2, '0') const min = String(date.getMinutes()).padStart(2, '0') switch (format) { 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(format || '') .replace(/YYYY/g, yyyy) .replace(/YY/g, yy) .replace(/MM/g, mm) .replace(/DD/g, dd) .replace(/HH/g, hh) .replace(/mm/g, min) } } export function tryParsePrintInputOffsetStored ( raw: string | null | undefined, ): { unit: string; value: string } | null { const t = String(raw ?? '').trim() if (!t.startsWith('{')) return null try { const o = JSON.parse(t) as unknown if (o == null || typeof o !== 'object' || Array.isArray(o)) return null const rec = o as Record const unit = String(rec.unit ?? rec.Unit ?? '').trim() const value = String(rec.value ?? rec.Value ?? '') if (!unit || !OFFSET_UNITS.has(unit)) return null return { unit, value } } catch { return null } } /** 与 Web isDateTimeDataEntryField 对齐(App 侧元素无 typeAdd 时用命名兜底) */ export function isUniAppDateTimeOffsetField (el: SystemTemplateElementBase): boolean { const type = String(el.type || '').toUpperCase() const cfg = (el.config || {}) as Record if (type === 'TIME' || type === 'DURATION') return true if (type !== 'DATE') return false const it = String(cfg.inputType ?? cfg.InputType ?? '').toLowerCase() if (it === 'datetime' || it === 'date') return true const ta = String((el as { typeAdd?: string }).typeAdd ?? '').trim().toLowerCase().replace(/\s+/g, ' ') if (ta === 'label_duration date' || ta.includes('duration date')) return true const nameHint = `${el.elementName ?? ''} ${el.inputKey ?? ''}`.toLowerCase() if (/(durationdate|duration_date|duration\s*date)/.test(nameHint)) return true return false } /** * 将 templateProductDefaults 里单格字符串转为画布用的展示文案(相对 now)。 * 非 JSON 或无法解析时返回原串(兼容旧版已展开的日期文案)。 */ export function resolveTemplateDefaultValueForElement ( el: SystemTemplateElementBase, stored: string, now: Date, ): string { const s = String(stored ?? '').trim() if (!s) return '' if (!isUniAppDateTimeOffsetField(el)) return s const parsed = tryParsePrintInputOffsetStored(s) if (!parsed) return s const amount = Number(String(parsed.value).trim()) const unit = parsed.unit || 'Days' if (!Number.isFinite(amount) || String(parsed.value).trim() === '') return '' const type = String(el.type || '').toUpperCase() const cfg = (el.config || {}) as Record const d = applyOffsetToDate(now, amount, unit) if (type === 'DATE') { const it = String(cfg.inputType ?? cfg.InputType ?? '').toLowerCase() const format = (typeof cfg.format === 'string' && cfg.format.trim() ? cfg.format : typeof cfg.Format === 'string' && cfg.Format.trim() ? cfg.Format : it === 'datetime' ? 'YYYY-MM-DD HH:mm' : 'DD/MM/YYYY') || 'DD/MM/YYYY' return formatDateByPreset(format, d) } if (type === 'TIME') { return formatDateByPreset('HH:mm', d) } return `${amount} ${unit}` }