labelTemplateService.ts 7.65 KB
import { createApiClient } from "../lib/apiClient";
import type {
  LabelElement,
  LabelTemplateCreateInput,
  LabelTemplateDto,
  LabelTemplateGetListInput,
  LabelTemplateProductDefaultDto,
  LabelTemplateUpdateInput,
  PagedResultDto,
} from "../types/labelTemplate";

const api = createApiClient({
  getToken: () => {
    try {
      return localStorage.getItem("access_token") ?? localStorage.getItem("token") ?? null;
    } catch {
      return null;
    }
  },
});

const PATH = "/label-template";

function normalizeTemplateCode(raw: unknown): string {
  const r = raw as Record<string, unknown> | null | undefined;
  if (!r || typeof r !== "object") return "";
  const id = r.id ?? r.templateCode ?? r.TemplateCode;
  return typeof id === "string" ? id.trim() : String(id ?? "").trim();
}

/** 详情/列表里的 elements 兼容 PascalCase(如 InputKey) */
function normalizeTemplateElements(list: unknown): LabelElement[] {
  if (!Array.isArray(list)) return [];
  return list.map((raw) => {
    const e = raw as Record<string, unknown> & {
      InputKey?: unknown;
      inputKey?: unknown;
      ElementName?: unknown;
      elementName?: unknown;
      LibraryCategory?: unknown;
      libraryCategory?: unknown;
    };
    const ik = e.inputKey ?? e.InputKey;
    const nameRaw = e.elementName ?? e.ElementName;
    const lcRaw = e.libraryCategory ?? e.LibraryCategory;
    let libraryCategory: LabelElement["libraryCategory"];
    if (typeof lcRaw === "string") {
      const t = lcRaw.trim();
      if (t) libraryCategory = t;
    }
    return {
      ...(e as object),
      elementName:
        typeof nameRaw === "string" ? nameRaw.trim() : undefined,
      inputKey: typeof ik === "string" ? ik : e.inputKey ?? null,
      libraryCategory,
      config: (e.config && typeof e.config === "object" ? e.config : {}) as LabelElement["config"],
    } as LabelElement;
  });
}

function normalizeDefaultValuesJson(raw: unknown): Record<string, string> {
  if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return {};
  const out: Record<string, string> = {};
  for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {
    if (v == null) out[k] = "";
    else if (typeof v === "string") out[k] = v;
    else if (typeof v === "number" || typeof v === "boolean") out[k] = String(v);
    else out[k] = JSON.stringify(v);
  }
  return out;
}

function normalizeTemplateProductDefaultsList(raw: unknown): LabelTemplateProductDefaultDto[] {
  if (!Array.isArray(raw)) return [];
  return raw.map((item, index) => {
    const o = item as Record<string, unknown>;
    const dv = o.defaultValues ?? o.DefaultValues ?? o.defaultValuesJson ?? o.DefaultValuesJson;
    return {
      productId: String(o.productId ?? o.ProductId ?? "").trim(),
      labelTypeId: String(o.labelTypeId ?? o.LabelTypeId ?? "").trim(),
      defaultValues: normalizeDefaultValuesJson(dv),
      orderNum: Number(o.orderNum ?? o.OrderNum ?? index + 1) || index + 1,
    };
  });
}

function normalizeLabelTemplateDto(raw: unknown): LabelTemplateDto {
  const r = raw as Record<string, unknown>;
  const ids =
    (Array.isArray(r.appliedLocationIds) ? r.appliedLocationIds : null) ??
    (Array.isArray(r.AppliedLocationIds) ? r.AppliedLocationIds : null) ??
    [];
  const appliedLocationIds = ids.map((x) => String(x));
  const id = normalizeTemplateCode(raw);

  const templateNameVo = r.templateName ?? r.TemplateName;
  const templateCodeVo = r.templateCode ?? r.TemplateCode;
  const locationTextVo = r.locationText ?? r.LocationText;
  const sizeTextVo = r.sizeText ?? r.SizeText;
  const ccRaw = r.contentsCount ?? r.ContentsCount;
  const contentsCountVo = typeof ccRaw === "number" ? ccRaw : undefined;
  const lastEditedVo = r.lastEdited ?? r.LastEdited;

  const nameFromList =
    (typeof r.name === "string" && r.name.trim() ? r.name : null) ??
    (typeof templateNameVo === "string" && String(templateNameVo).trim() ? String(templateNameVo) : null);

  return {
    ...(r as object),
    id,
    name: nameFromList ?? (r.name as LabelTemplateDto["name"]),
    templateName: (typeof templateNameVo === "string" ? templateNameVo : null) ?? (r.templateName as string | null),
    templateCode: (typeof templateCodeVo === "string" ? templateCodeVo : null) ?? (r.templateCode as string | null),
    locationText: (typeof locationTextVo === "string" ? locationTextVo : null) ?? (r.locationText as string | null),
    sizeText: (typeof sizeTextVo === "string" ? sizeTextVo : null) ?? (r.sizeText as string | null),
    contentsCount: contentsCountVo ?? (r.contentsCount as number | null),
    lastEdited: (typeof lastEditedVo === "string" ? lastEditedVo : null) ?? (r.lastEdited as string | null),
    appliedLocationIds,
    elements: normalizeTemplateElements(r.elements),
    templateProductDefaults: normalizeTemplateProductDefaultsList(
      r.templateProductDefaults ?? r.TemplateProductDefaults,
    ),
  } as LabelTemplateDto;
}

export async function getLabelTemplates(input: LabelTemplateGetListInput, signal?: AbortSignal): Promise<PagedResultDto<LabelTemplateDto>> {
  const res = await api.requestJson<PagedResultDto<LabelTemplateDto>>({
    path: PATH,
    method: "GET",
    query: {
      SkipCount: input.skipCount,
      MaxResultCount: input.maxResultCount,
      Sorting: input.sorting,
      Keyword: input.keyword,
      LocationId: input.locationId,
      LabelType: input.labelType,
      State: input.state,
    },
    signal,
  });
  const items = (res.items ?? []).map((x) => normalizeLabelTemplateDto(x));
  return { ...res, items };
}

export async function getLabelTemplate(templateCode: string, signal?: AbortSignal): Promise<LabelTemplateDto> {
  const raw = await api.requestJson<LabelTemplateDto>({
    path: `${PATH}/${encodeURIComponent(templateCode)}`,
    method: "GET",
    signal,
  });
  return normalizeLabelTemplateDto(raw);
}

export async function createLabelTemplate(input: LabelTemplateCreateInput): Promise<LabelTemplateDto> {
  const created = await api.requestJson<LabelTemplateDto>({
    path: PATH,
    method: "POST",
    body: {
      id: input.id,
      name: input.name,
      labelType: input.labelType,
      unit: input.unit,
      width: input.width,
      height: input.height,
      appliedLocation: input.appliedLocation,
      showRuler: input.showRuler ?? true,
      showGrid: input.showGrid ?? true,
      state: input.state ?? true,
      elements: input.elements,
      appliedLocationIds: input.appliedLocationIds ?? [],
    },
  });
  return normalizeLabelTemplateDto(created);
}

export async function updateLabelTemplate(templateCode: string, input: LabelTemplateUpdateInput): Promise<LabelTemplateDto> {
  const body: Record<string, unknown> = {
    id: input.id,
    name: input.name,
    labelType: input.labelType,
    unit: input.unit,
    width: input.width,
    height: input.height,
    appliedLocation: input.appliedLocation,
    showRuler: input.showRuler ?? true,
    showGrid: input.showGrid ?? true,
    state: input.state ?? true,
    elements: input.elements,
    appliedLocationIds: input.appliedLocationIds ?? [],
  };
  if (input.templateProductDefaults !== undefined) {
    body.templateProductDefaults = input.templateProductDefaults.map((row, i) => ({
      productId: row.productId,
      labelTypeId: row.labelTypeId,
      defaultValues: row.defaultValues,
      orderNum: row.orderNum ?? i + 1,
    }));
  }
  const updated = await api.requestJson<LabelTemplateDto>({
    path: `${PATH}/${encodeURIComponent(templateCode)}`,
    method: "PUT",
    body,
  });
  return normalizeLabelTemplateDto(updated);
}

export async function deleteLabelTemplate(templateCode: string): Promise<void> {
  await api.requestJson<unknown>({
    path: `${PATH}/${encodeURIComponent(templateCode)}`,
    method: "DELETE",
  });
}