printInputOptions.ts 8.99 KB
import type {
  LabelTemplateData,
  SystemLabelTemplate,
  SystemTemplateElementBase,
} from '../print/types/printer'

/**
 * 打印/预览 printInputJson 的 key:与接口文档一致优先 inputKey,否则 elementName,最后兜底元素 id。
 * 兼容根字段与 config 内 InputKey(部分模板把键写在 config)。
 */
export function printInputJsonKeyForElement(el: SystemTemplateElementBase): string {
  const cfg = el.config || {}
  const top =
    el.inputKey ??
    (el as { InputKey?: string }).InputKey ??
    cfg.inputKey ??
    cfg.InputKey ??
    el.elementName ??
    (el as { ElementName?: string }).ElementName ??
    cfg.elementName ??
    cfg.ElementName
  const s = top != null ? String(top).trim() : ''
  if (s) return s
  return String(el.id ?? '').trim()
}

export function isPrintInputOptionsElement(el: SystemTemplateElementBase): boolean {
  const vst = String(el.valueSourceType || '').toUpperCase()
  if (vst !== 'PRINT_INPUT') return false
  const c = el.config || {}
  const it = String(c.inputType ?? c.InputType ?? '').toLowerCase()
  if (it === 'options') return true
  const mid = c.multipleOptionId ?? c.MultipleOptionId
  return typeof mid === 'string' && mid.trim() !== ''
}

/** PRINT_INPUT 且非多选项字典(无 effective multipleOptionId):需用户在页面上输入 */
export function isPrintInputFreeFieldElement(el: SystemTemplateElementBase): boolean {
  const vst = String(el.valueSourceType || '').toUpperCase()
  if (vst !== 'PRINT_INPUT') return false
  return !isPrintInputOptionsElement(el)
}

/** 从模板初始 config 读出已选(接口预览可能已带 selectedOptionValues) */
export function readSelectionsFromTemplate(template: SystemLabelTemplate): Record<string, string[]> {
  const out: Record<string, string[]> = {}
  for (const el of template.elements || []) {
    if (!isPrintInputOptionsElement(el)) continue
    const raw = el.config?.selectedOptionValues ?? el.config?.SelectedOptionValues
    if (Array.isArray(raw) && raw.length) {
      out[el.id] = raw.map((x: unknown) => String(x))
    }
  }
  return out
}

/**
 * 将多选结果写回 config(画布与打印用 text 展示「字典名: 值」或 prefix+值)
 */
/** 从预览模板 config 初始化自由输入(如 WEIGHT 的 value、DATE 已算好的 text) */
export function readFreeFieldValuesFromTemplate(template: SystemLabelTemplate): Record<string, string> {
  const out: Record<string, string> = {}
  for (const el of template.elements || []) {
    if (!isPrintInputFreeFieldElement(el)) continue
    const c = el.config || {}
    const type = String(el.type || '').toUpperCase()
    let initial = ''
    if (type === 'WEIGHT') {
      const v = c.value ?? c.Value
      if (v != null && String(v).trim() !== '') initial = String(v).trim()
    } else {
      const fmt = String(c.format ?? c.Format ?? '').trim()
      const t = String(c.text ?? c.Text ?? '').trim()
      if (t && (!fmt || t !== fmt)) initial = t
    }
    out[el.id] = initial
  }
  return out
}

export function ensureFreeFieldKeys(
  template: SystemLabelTemplate,
  existing: Record<string, string>
): Record<string, string> {
  const out = { ...existing }
  for (const el of template.elements || []) {
    if (!isPrintInputFreeFieldElement(el)) continue
    if (!(el.id in out)) out[el.id] = ''
  }
  return out
}

/**
 * 将自由输入写回 config:画布用 text 展示;若有 unit 则拼在数值后(如 500g)。
 */
export function mergePrintInputFreeFields(
  template: SystemLabelTemplate,
  values: Record<string, string>
): SystemLabelTemplate {
  return {
    ...template,
    elements: (template.elements || []).map((el) => {
      if (!isPrintInputFreeFieldElement(el)) return el
      const cfg = { ...el.config }
      const raw = String(values[el.id] ?? '').trim()
      const unit = String(cfg.unit ?? cfg.Unit ?? '').trim()
      const type = String(el.type || '').toUpperCase()

      if (!raw) {
        if (type === 'DATE' || type === 'TIME') {
          const fmt = String(cfg.format ?? cfg.Format ?? '')
          return { ...el, config: { ...cfg, text: fmt } }
        }
        if (type === 'WEIGHT') {
          return { ...el, config: { ...cfg, text: '', value: '' } }
        }
        const ph = String(
          cfg.placeholder ?? cfg.Placeholder ?? cfg.defaultValue ?? cfg.DefaultValue ?? ''
        ).trim()
        return { ...el, config: { ...cfg, text: ph } }
      }

      const display = unit && !raw.endsWith(unit) ? `${raw}${unit}` : raw
      const next = { ...cfg, text: display } as Record<string, any>
      if (type === 'WEIGHT') {
        next.value = raw
      }
      return { ...el, config: next }
    }),
  }
}

export function mergePrintOptionSelections(
  template: SystemLabelTemplate,
  selections: Record<string, string[]>,
  dictNames: Record<string, string>
): SystemLabelTemplate {
  return {
    ...template,
    elements: (template.elements || []).map((el) => {
      if (!isPrintInputOptionsElement(el)) return el
      const sel = selections[el.id]
      if (!sel || sel.length === 0) return el
      const cfg = { ...el.config }
      cfg.selectedOptionValues = sel
      const dictLabel = dictNames[el.id] || 'Options'
      const prefix = String(cfg.prefix ?? cfg.Prefix ?? '').trim()
      const answers = sel.join(', ')
      cfg.text = prefix ? `${prefix}${answers}` : `${dictLabel}: ${answers}`
      return { ...el, config: cfg }
    }),
  }
}

export function printInputJsonFromSelections(
  template: SystemLabelTemplate,
  selections: Record<string, string[]>
): Record<string, unknown> {
  const out: Record<string, unknown> = {}
  for (const el of template.elements || []) {
    if (!isPrintInputOptionsElement(el)) continue
    const sel = selections[el.id]
    if (!sel || !sel.length) continue
    const key = printInputJsonKeyForElement(el)
    if (!key) continue
    out[key] = sel.length === 1 ? sel[0] : [...sel]
  }
  return out
}

export function printInputJsonFromFreeFields(
  template: SystemLabelTemplate,
  values: Record<string, string>
): Record<string, unknown> {
  const out: Record<string, unknown> = {}
  for (const el of template.elements || []) {
    if (!isPrintInputFreeFieldElement(el)) continue
    const key = printInputJsonKeyForElement(el)
    if (!key) continue
    const v = String(values[el.id] ?? '').trim()
    if (v === '') continue
    out[key] = v
  }
  return out
}

export function buildPrintInputJson(
  template: SystemLabelTemplate,
  optionSelections: Record<string, string[]>,
  freeFieldValues: Record<string, string>
): Record<string, unknown> {
  return {
    ...printInputJsonFromSelections(template, optionSelections),
    ...printInputJsonFromFreeFields(template, freeFieldValues),
  }
}

/**
 * 供接口 9 落库 `printInputJson`:在**纯 JSON 克隆**上再合并多选/自由输入,避免 Vue Proxy 嵌套 config
 * 在部分环境下 `JSON.stringify` 丢字段,导致服务端存下的 RenderDataJson 仍是设计器默认值(无日期、无多选)。
 */
export function buildPrintPersistTemplateSnapshot(
  base: SystemLabelTemplate,
  optionSelections: Record<string, string[]>,
  freeFieldValues: Record<string, string>,
  dictNames: Record<string, string>
): SystemLabelTemplate {
  const cloned = JSON.parse(JSON.stringify(base)) as SystemLabelTemplate
  let m = mergePrintOptionSelections(cloned, optionSelections, dictNames)
  m = mergePrintInputFreeFields(m, freeFieldValues)
  return m
}

/** 本地打印机 / dataJson:与 printInputJson 同键,供模板 {{key}} 或 native 层合并 */
export function printInputJsonToLabelTemplateData(pj: Record<string, unknown>): LabelTemplateData {
  const out: LabelTemplateData = {}
  for (const k of Object.keys(pj)) {
    const v = pj[k]
    if (v == null) continue
    if (typeof v === 'string' || typeof v === 'number') {
      out[k] = v
    } else if (Array.isArray(v)) {
      out[k] = v.map((x) => String(x)).join(', ')
    } else {
      out[k] = JSON.stringify(v)
    }
  }
  return out
}

/**
 * PRINT_INPUT 且为多选项字典:未至少选一项则不可打印。
 */
export function validatePrintInputOptionsBeforePrint(
  template: SystemLabelTemplate,
  selections: Record<string, string[]>,
  dictLabels: Record<string, string>
): string | null {
  for (const el of template.elements || []) {
    if (!isPrintInputOptionsElement(el)) continue
    const sel = selections[el.id]
    if (!sel || sel.length === 0) {
      const name =
        dictLabels[el.id] ||
        el.elementName ||
        el.inputKey ||
        'options'
      return `Please select “${name}” before printing.`
    }
  }
  return null
}

/**
 * PRINT_INPUT 自由字段:未填写则不可打印。
 */
export function validatePrintInputFreeFieldsBeforePrint(
  template: SystemLabelTemplate,
  values: Record<string, string>
): string | null {
  for (const el of template.elements || []) {
    if (!isPrintInputFreeFieldElement(el)) continue
    const raw = String(values[el.id] ?? '').trim()
    if (raw === '') {
      const name = el.elementName || el.inputKey || 'field'
      return `Please fill in “${name}” before printing.`
    }
  }
  return null
}