labelFormDatePreview.ts 5.5 KB
import type { LabelElement } from "../types/labelTemplate";
import { canonicalElementType, isDateTimeDataEntryField } from "../types/labelTemplate";

/**
 * 标签创建/编辑表单:相对时间偏移 + 按模板 format 预览(与 LabelCanvas formatDateByPreset 对齐)
 */

export const LABEL_FORM_OFFSET_UNITS = [
  "Minutes",
  "Hours",
  "Days",
  "Weeks",
  "Months (30 Day)",
  "Years",
] as const;

export type LabelFormOffsetUnit = (typeof LABEL_FORM_OFFSET_UNITS)[number];

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;
}

/** 与 LabelCanvas 中 formatDateByPreset 一致 */
export 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");
  const monthLong = date.toLocaleString("en-US", { month: "long" }).toUpperCase();
  const dayLong = date.toLocaleString("en-US", { weekday: "long" }).toUpperCase();
  const dayShort = date.toLocaleString("en-US", { weekday: "short" }).toUpperCase();
  const monthShort = date.toLocaleString("en-US", { month: "short" }).toUpperCase();
  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 "FULLY DAY(WEDNESDAY)":
      return dayLong;
    case "DAY (WED)":
      return dayShort;
    case "MONTH (DECEMBER)":
      return monthLong;
    case "YEAR (2025)":
      return yyyy;
    case "DD MONTH YEAR (25 DECEMBER 2025)":
      return `${dd} ${monthLong} ${yyyy}`;
    default:
      return 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);
  }
}

const OFFSET_UNIT_SET = new Set<string>(LABEL_FORM_OFFSET_UNITS as unknown as string[]);

/**
 * 模板 productDefault 中日期/时间/时长录入:存 `{"unit":"Days","value":"2"}`,供 App 与打印按 BaseTime 解析。
 */
export function serializePrintInputOffset(unit: string, value: string): string {
  const u = String(unit ?? "").trim() || "Days";
  return JSON.stringify({ unit: u, value: String(value ?? "") });
}

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<string, unknown>;
    const unit = String(rec.unit ?? rec.Unit ?? "").trim();
    const value = String(rec.value ?? rec.Value ?? "");
    if (!unit || !OFFSET_UNIT_SET.has(unit)) return null;
    return { unit, value };
  } catch {
    return null;
  }
}

/** 批量录入格:无 JSON 时纯数字按 Days 预填,便于兼容旧 duration 整数 */
export function offsetFieldUiStateFromStored(stored: string | undefined): { unit: string; value: string } {
  const p = tryParsePrintInputOffsetStored(stored);
  if (p) return p;
  const t = String(stored ?? "").trim();
  if (/^\d+$/.test(t)) return { unit: "Days", value: t };
  return { unit: "Days", value: "" };
}

/**
 * 将落库的 JSON(或旧版纯文案)解析为画布/预览用的展示字符串(相对 base 时间)。
 */
export function resolveStoredPrintValueToDisplayText(
  el: LabelElement,
  stored: string,
  base: Date,
): string {
  const s = String(stored ?? "").trim();
  if (!s) return "";
  if (!isDateTimeDataEntryField(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 = canonicalElementType(el.type);
  const d = applyOffsetToDate(base, amount, unit);
  const cfg = el.config as Record<string, unknown>;
  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}`;
}