import { createApiClient } from "../lib/apiClient"; import { stripLabelConfigPrefixes, normalizeTemplateBorder, normalizePrintOrientation, type LabelElement, type LabelTemplateCreateInput, type LabelTemplateDto, type LabelTemplateGetListInput, type LabelTemplateProductDefaultDto, type LabelTemplateUpdateInput, type 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 | 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 & { InputKey?: unknown; inputKey?: unknown; ElementName?: unknown; elementName?: unknown; TypeAdd?: unknown; typeAdd?: unknown; LibraryCategory?: unknown; libraryCategory?: unknown; }; const ik = e.inputKey ?? e.InputKey; const nameRaw = e.elementName ?? e.ElementName; const typeAddRaw = e.typeAdd ?? e.TypeAdd; const lcRaw = e.libraryCategory ?? e.LibraryCategory; let libraryCategory: LabelElement["libraryCategory"]; if (typeof lcRaw === "string") { const t = lcRaw.trim(); if (t) libraryCategory = t; } const rawCfg = e.config && typeof e.config === "object" && !Array.isArray(e.config) ? (e.config as Record) : {}; return { ...(e as object), elementName: typeof nameRaw === "string" ? nameRaw.trim() : undefined, typeAdd: typeof typeAddRaw === "string" ? typeAddRaw.trim() : undefined, inputKey: typeof ik === "string" ? ik : e.inputKey ?? null, libraryCategory, config: stripLabelConfigPrefixes(rawCfg) as LabelElement["config"], } as LabelElement; }); } function normalizeDefaultValuesJson(raw: unknown): Record { if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return {}; const out: Record = {}; for (const [k, v] of Object.entries(raw as Record)) { 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; 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 normalizeListContentItems(raw: unknown): string[] { const r = raw as Record | null | undefined; if (!r || typeof r !== "object") return []; const list = r.contentItems ?? r.items ?? r.Items ?? r.itemNames ?? r.ItemNames; /** 列表接口 items 常为后端拼好的逗号分隔字符串 */ if (typeof list === "string") { const t = list.trim(); return t ? [t] : []; } if (!Array.isArray(list)) return []; const out: string[] = []; for (const x of list) { if (typeof x === "string") { const t = x.trim(); if (t) out.push(t); continue; } if (x != null && typeof x === "object" && !Array.isArray(x)) { const o = x as Record; const name = String(o.elementName ?? o.ElementName ?? o.name ?? o.Name ?? "").trim(); if (name) out.push(name); } } return out; } /** 列表 Contents 展示文案:优先接口 items 字符串,否则数组 join */ function normalizeListContentsText(raw: unknown, contentItems: string[]): string | null { const r = raw as Record | null | undefined; if (!r || typeof r !== "object") return contentItems.length ? contentItems.join(", ") : null; const list = r.contentItems ?? r.items ?? r.Items ?? r.itemNames ?? r.ItemNames; if (typeof list === "string") { const t = list.trim(); return t || null; } if (contentItems.length) return contentItems.join(", "); return null; } function normalizeLabelTemplateDto(raw: unknown): LabelTemplateDto { const r = raw as Record; const ids = (Array.isArray(r.appliedLocationIds) ? r.appliedLocationIds : null) ?? (Array.isArray(r.AppliedLocationIds) ? r.AppliedLocationIds : null) ?? []; const appliedLocationIds = ids.map((x) => String(x)); const parseIdList = (v: unknown): string[] => { if (!Array.isArray(v)) return []; return [...new Set(v.map((x) => String(x).trim()).filter(Boolean))]; }; const regionIds = parseIdList(r.regionIds ?? r.RegionIds); const groupIds = parseIdList(r.groupIds ?? r.GroupIds); const locationIds = parseIdList(r.locationIds ?? r.LocationIds); const partnerIds = parseIdList(r.partnerIds ?? r.PartnerIds ?? r.companyIds ?? r.CompanyIds); const mergedRegionIds = [...new Set([...regionIds, ...groupIds])]; const mergedLocationIds = [ ...new Set([...locationIds, ...appliedLocationIds]), ]; const mergedPartnerIds = partnerIds; const appliedPartnerTypeRaw = r.appliedPartnerType ?? r.AppliedPartnerType; const appliedRegionTypeRaw = r.appliedRegionType ?? r.AppliedRegionType; const appliedLocationTypeRaw = r.appliedLocationType ?? r.AppliedLocationType ?? r.appliedLocation ?? r.AppliedLocation; const companyTextVo = r.company ?? r.Company; const regionTextVo = r.region ?? r.Region; const locationFieldVo = r.location ?? r.Location; 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); const contentItems = normalizeListContentItems(r); const contentsText = normalizeListContentsText(r, contentItems); 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), company: (typeof companyTextVo === "string" ? companyTextVo : null) ?? (r.company as string | null), region: (typeof regionTextVo === "string" ? regionTextVo : null) ?? (r.region as string | null), location: (typeof locationFieldVo === "string" ? locationFieldVo : null) ?? (r.location as string | null), appliedPartnerType: (typeof appliedPartnerTypeRaw === "string" ? appliedPartnerTypeRaw : null) ?? (r.appliedPartnerType as string | null), appliedRegionType: (typeof appliedRegionTypeRaw === "string" ? appliedRegionTypeRaw : null) ?? (r.appliedRegionType as string | null), appliedLocationType: (typeof appliedLocationTypeRaw === "string" ? appliedLocationTypeRaw : null) ?? (r.appliedLocationType as string | null), sizeText: (typeof sizeTextVo === "string" ? sizeTextVo : null) ?? (r.sizeText as string | null), contentsCount: contentsCountVo ?? (r.contentsCount as number | null), contentItems, contentsText, items: contentsText ?? (contentItems.length ? contentItems.join(", ") : null), lastEdited: (typeof lastEditedVo === "string" ? lastEditedVo : null) ?? (r.lastEdited as string | null), appliedLocationIds: mergedLocationIds.length ? mergedLocationIds : appliedLocationIds, partnerIds: mergedPartnerIds, companyIds: mergedPartnerIds, regionIds: mergedRegionIds, groupIds: mergedRegionIds, locationIds: mergedLocationIds, elements: normalizeTemplateElements(r.elements), templateProductDefaults: normalizeTemplateProductDefaultsList( r.templateProductDefaults ?? r.TemplateProductDefaults, ), border: normalizeTemplateBorder(r.border ?? r.BorderType ?? r.borderType), printOrientation: normalizePrintOrientation(r.printOrientation ?? r.PrintOrientation), } as LabelTemplateDto; } export async function getLabelTemplates(input: LabelTemplateGetListInput, signal?: AbortSignal): Promise> { const res = await api.requestJson>({ path: PATH, method: "GET", query: { SkipCount: input.skipCount, MaxResultCount: input.maxResultCount, Sorting: input.sorting, Keyword: input.keyword, PartnerId: input.partnerId, GroupId: input.groupId, 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 { const raw = await api.requestJson({ path: `${PATH}/${encodeURIComponent(templateCode)}`, method: "GET", signal, }); return normalizeLabelTemplateDto(raw); } function buildLabelTemplateScopeBody(input: LabelTemplateCreateInput): Record { const partnerIds = [ ...new Set( [...(input.partnerIds ?? []), ...(input.companyIds ?? [])] .map((x) => String(x).trim()) .filter(Boolean), ), ]; const regionIds = [ ...new Set( [...(input.regionIds ?? []), ...(input.groupIds ?? [])] .map((x) => String(x).trim()) .filter(Boolean), ), ]; const locationIds = [ ...new Set( [...(input.locationIds ?? []), ...(input.appliedLocationIds ?? [])] .map((x) => String(x).trim()) .filter(Boolean), ), ]; const appliedPartnerType = String(input.appliedPartnerType ?? "").trim().toUpperCase() || (partnerIds.length ? "SPECIFIED" : "ALL"); const appliedRegionType = String(input.appliedRegionType ?? "").trim().toUpperCase() || (regionIds.length ? "SPECIFIED" : "ALL"); const appliedLocation = String(input.appliedLocation ?? "").trim().toUpperCase() || (locationIds.length ? "SPECIFIED" : "ALL"); const body: Record = { id: input.id, name: input.name, labelType: input.labelType, unit: input.unit, width: input.width, height: input.height, appliedPartnerType, appliedRegionType, appliedLocation, showRuler: input.showRuler ?? true, showGrid: input.showGrid ?? true, border: normalizeTemplateBorder(input.border), printOrientation: normalizePrintOrientation(input.printOrientation), state: input.state ?? true, elements: input.elements, }; if (appliedPartnerType === "SPECIFIED" && partnerIds.length) { body.partnerIds = partnerIds; body.companyIds = partnerIds; } else { body.partnerIds = []; body.companyIds = []; } if (appliedRegionType === "SPECIFIED" && regionIds.length) { body.regionIds = regionIds; body.groupIds = regionIds; } else { body.regionIds = []; body.groupIds = []; } if (appliedLocation === "SPECIFIED" && locationIds.length) { body.locationIds = locationIds; body.appliedLocationIds = locationIds; } else { body.locationIds = []; body.appliedLocationIds = []; } return body; } export async function createLabelTemplate(input: LabelTemplateCreateInput): Promise { const created = await api.requestJson({ path: PATH, method: "POST", body: buildLabelTemplateScopeBody(input), }); return normalizeLabelTemplateDto(created); } export async function updateLabelTemplate(templateCode: string, input: LabelTemplateUpdateInput): Promise { const body = buildLabelTemplateScopeBody(input); 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({ path: `${PATH}/${encodeURIComponent(templateCode)}`, method: "PUT", body, }); return normalizeLabelTemplateDto(updated); } export async function deleteLabelTemplate(templateCode: string): Promise { await api.requestJson({ path: `${PATH}/${encodeURIComponent(templateCode)}`, method: "DELETE", }); }