gongchunyi
2026-05-28 19f2e3bdbe04e7ea79c6a0bdc8c7318d4837b189
src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,355 @@
import dayjs from "dayjs";
import { getTypeEnums } from "@/api/basicData/enum.js";
import {
  TEMPLATE_TYPE_BUILTIN,
  TEMPLATE_TYPE_CUSTOM,
} from "@/api/officeProcessAutomation/approvalTemplate.js";
import { APPROVAL_TYPE_OPTIONS } from "../approve-list/approveListConstants.js";
import {
  buildFormConfigJson,
  createEmptyFormConfigData,
  parseFormConfigToData,
  validateFormConfigData,
} from "./formConfigUtils.js";
export function unwrapEnumList(data) {
  if (Array.isArray(data)) return data;
  if (!data || typeof data !== "object") return [];
  if (Array.isArray(data.TypeEnums)) return data.TypeEnums;
  if (Array.isArray(data.typeEnums)) return data.typeEnums;
  const nested = Object.values(data).find((v) => Array.isArray(v));
  return nested || [];
}
export function normalizeBusinessTypeOptions(data) {
  return unwrapEnumList(data)
    .map((item) => {
      const rawValue = item?.value ?? item?.code ?? item?.businessType ?? item?.dictValue ?? item?.key;
      if (rawValue == null || rawValue === "") return null;
      const num = Number(rawValue);
      const value =
        typeof rawValue === "number" || (Number.isFinite(num) && String(rawValue).trim() !== "")
          ? num
          : rawValue;
      const label =
        item?.label ?? item?.name ?? item?.desc ?? item?.dictLabel ?? item?.text ?? String(value);
      return { label, value };
    })
    .filter(Boolean);
}
export async function fetchBusinessTypeOptions() {
  try {
    const res = await getTypeEnums();
    return normalizeBusinessTypeOptions(res?.data);
  } catch {
    return [];
  }
}
/** æ˜¯å¦ä¸ºç³»ç»Ÿå†…置模板(templateType === 0) */
export function isBuiltinTemplate(row) {
  return Number(row?.templateType) === TEMPLATE_TYPE_BUILTIN;
}
/** èŠ‚ç‚¹å†…å®¡æ‰¹æ–¹å¼ï¼šä¼šç­¾ / æˆ–ç­¾ */
export const NODE_SIGN_MODE_OPTIONS = [
  { value: "countersign", label: "会签", desc: "本节点所有审批人均需通过" },
  { value: "or_sign", label: "或签", desc: "本节点任一审批人通过即可" },
];
function parseFormConfig(formConfig) {
  if (!formConfig) return {};
  if (typeof formConfig === "object") return formConfig;
  try {
    return JSON.parse(formConfig);
  } catch {
    return {};
  }
}
function resolveDefaultMode(row, cfg, nodes) {
  let mode = cfg.approvalMode || cfg.defaultMode;
  if (!mode && nodes.length) {
    const t = String(nodes[0]?.approveType || "").toUpperCase();
    mode = t === "OR" ? "or_sign" : "parallel";
  }
  const m = String(mode || "").toLowerCase();
  if (m === "or" || m === "or_sign") return "or_sign";
  return "parallel";
}
/** å°†æŽ¥å£è¿”回的模板转为「系统常用审批」卡片数据 */
export function mapBuiltinCardFromApi(row) {
  const cfg = parseFormConfig(row?.formConfig);
  const fields = cfg.fields || cfg.formFields || [];
  const nodes = row?.nodes || row?.flowNodes || [];
  return {
    key: String(row?.id ?? row?.templateName ?? ""),
    id: row?.id,
    approvalType: cfg.approvalType || row?.approvalType || "",
    label: row?.templateName || row?.name || "—",
    summary: (row?.description || "").trim() || cfg.summaryPlaceholder || "系统预置填报字段",
    fieldCount: fields.length,
    defaultMode: resolveDefaultMode(row, cfg, nodes),
  };
}
export function unwrapTemplateList(payload) {
  const data = payload?.data ?? payload;
  if (Array.isArray(data)) return data;
  if (Array.isArray(data?.records)) return data.records;
  if (Array.isArray(data?.list)) return data.list;
  return [];
}
/** åŽç«¯ approveType â†’ é¡µé¢ signMode */
export function mapSignModeFromApi(approveType) {
  const t = String(approveType || "").toUpperCase();
  return t === "OR" ? "or_sign" : "countersign";
}
/** é¡µé¢ signMode â†’ åŽç«¯ approveType */
export function mapSignModeToApi(signMode) {
  return signMode === "or_sign" ? "OR" : "AND";
}
/** é¡µé¢ enabled â†’ åŽç«¯ enabled(1 å¯ç”¨ï¼Œ0 åœç”¨ï¼‰ */
export function mapEnabledToApi(enabled) {
  return enabled !== false ? "1" : "0";
}
/** åŽç«¯ nodes â†’ é¡µé¢ flowNodes(保留 id ä¾›ä¿®æ”¹æäº¤ï¼‰ */
export function mapNodesFromApi(nodes) {
  const list = Array.isArray(nodes) ? nodes : [];
  return list.map((n, i) => ({
    id: n.id,
    templateId: n.templateId,
    nodeOrder: n.levelNo ?? i + 1,
    signMode: mapSignModeFromApi(n.approveType ?? n.signMode),
    approvers: (n.approvers || [])
      .filter((a) => a?.approverId != null && a.approverId !== "")
      .map((a) => ({
        id: a.id,
        nodeId: a.nodeId,
        templateId: a.templateId,
        approverId: a.approverId,
        approverName: a.approverName || "",
      })),
  }));
}
/** enabled:1 å¯ç”¨ï¼Œ0 åœç”¨ */
export function mapEnabledFromApi(enabled) {
  return enabled === "1" || enabled === 1 || enabled === true;
}
/** å…¼å®¹å¤šç§åŽç«¯æ—¶é—´å­—段名并格式化展示 */
export function pickTemplateTimes(row) {
  const rawCreated =
    row?.createdTime ?? row?.createTime ?? row?.gmtCreate ?? row?.created_at ?? "";
  const rawUpdated =
    row?.updatedTime ?? row?.updateTime ?? row?.gmtModified ?? row?.modifyTime ?? row?.updated_at ?? "";
  const createdTime = normalizeTimeValue(rawCreated);
  const updatedTime = normalizeTimeValue(rawUpdated);
  return { createdTime, updatedTime, createTime: createdTime, updateTime: updatedTime };
}
function normalizeTimeValue(val) {
  if (val == null || val === "") return "";
  if (Array.isArray(val) && val.length >= 3) {
    const [y, m, d, h = 0, min = 0, s = 0] = val;
    return dayjs(new Date(y, m - 1, d, h, min, s)).format("YYYY-MM-DD HH:mm:ss");
  }
  if (typeof val === "number") {
    const d = val > 1e12 ? dayjs(val) : dayjs.unix(val);
    return d.isValid() ? d.format("YYYY-MM-DD HH:mm:ss") : "";
  }
  const s = String(val).trim();
  if (!s) return "";
  const parsed = dayjs(s.includes("T") ? s : s.replace(/-/g, "/"));
  return parsed.isValid() ? parsed.format("YYYY-MM-DD HH:mm:ss") : s;
}
export function formatDisplayTime(val) {
  const t = normalizeTimeValue(val);
  return t || "—";
}
/** è¯¦æƒ…接口 data è§£åŒ… */
export function unwrapTemplateDetail(res) {
  const data = res?.data ?? res;
  if (!data || typeof data !== "object") return {};
  if (data.templateName != null || data.id != null) return data;
  if (data.approvalTemplateVo) return data.approvalTemplateVo;
  if (data.records && data.records[0]) return data.records[0];
  return data;
}
/** åŽç«¯é™„件字段 â†’ é¡µé¢ storageBlobDTOs */
export function mapAttachmentsFromApi(row) {
  const list =
    row?.storageBlobDTOs ||
    row?.storageBlobDTOS ||
    row?.storageBlobVOS ||
    row?.storageBlobVOList ||
    row?.attachmentList ||
    [];
  return Array.isArray(list) ? list : [];
}
/** åˆ†é¡µåˆ—表项 â†’ é¡µé¢è¡Œæ•°æ®ï¼ˆä¸»è¡¨ + èŠ‚ç‚¹ï¼‰ */
export function mapTemplateFromApi(row) {
  if (!row) return {};
  const flowNodes = mapNodesFromApi(row.nodes || row.flowNodes);
  const times = pickTemplateTimes(row);
  return {
    id: row.id,
    templateName: row.templateName || "",
    description: row.description || "",
    enabled: mapEnabledFromApi(row.enabled),
    enabledRaw: row.enabled,
    templateType: row.templateType != null ? Number(row.templateType) : undefined,
    businessType: row.businessType ?? "",
    formConfig: row.formConfig,
    formConfigData: parseFormConfigToData(row.formConfig),
    storageBlobDTOs: mapAttachmentsFromApi(row),
    createdUser: row.createdUser,
    createdUserName: row.createdUserName,
    ...times,
    flowNodes,
    nodes: row.nodes || row.flowNodes,
  };
}
/** è¡¨å•数据 â†’ æäº¤ DTO(ApprovalTemplateDto) */
export function mapTemplateToApi(form) {
  const nodes = normalizeFlowNodes(form.flowNodes);
  const templateId = form.id || null;
  const dto = {
    templateName: (form.templateName || "").trim(),
    description: (form.description || "").trim(),
    enabled: mapEnabledToApi(form.enabled),
    templateType:
      form.templateType != null ? Number(form.templateType) : TEMPLATE_TYPE_CUSTOM,
    businessType: form.businessType ?? "",
    formConfig: buildFormConfigJson(form.formConfigData),
    nodes: nodes.map((n, i) => {
      const node = {
        levelNo: n.nodeOrder ?? i + 1,
        approveType: mapSignModeToApi(n.signMode),
        approvers: n.approvers.map((a, idx) => {
          const approver = {
            approverId: a.approverId,
            approverName: a.approverName || "",
            sortNo: idx + 1,
          };
          if (a.id != null) approver.id = a.id;
          if (a.nodeId != null) approver.nodeId = a.nodeId;
          if (a.templateId != null) approver.templateId = a.templateId;
          else if (templateId) approver.templateId = templateId;
          return approver;
        }),
      };
      if (n.id != null) node.id = n.id;
      if (n.templateId != null) node.templateId = n.templateId;
      else if (templateId) node.templateId = templateId;
      return node;
    }),
  };
  if (templateId) dto.id = templateId;
  const attachments = Array.isArray(form.storageBlobDTOs) ? form.storageBlobDTOs : [];
  if (attachments.length) dto.storageBlobDTOs = attachments;
  return dto;
}
export function buildApprovalTemplateListParams({ page, searchForm }) {
  const params = {
    current: page.current,
    size: page.size,
  };
  const kw = (searchForm?.keyword || "").trim();
  if (kw) params.templateName = kw;
  if (searchForm?.enabledOnly) params.enabled = "1";
  return params;
}
export function nodeSignModeLabel(mode) {
  return NODE_SIGN_MODE_OPTIONS.find((x) => x.value === mode)?.label || "—";
}
export function approvalTypeLabel(type) {
  return APPROVAL_TYPE_OPTIONS.find((x) => x.value === type)?.label || type || "—";
}
export function createEmptyNode(order = 1) {
  return {
    nodeOrder: order,
    signMode: "countersign",
    approvers: [],
  };
}
export function createEmptyTemplateForm() {
  return {
    id: "",
    templateName: "",
    description: "",
    templateType: TEMPLATE_TYPE_CUSTOM,
    lockedFormFieldUids: [],
    businessType: "",
    formConfig: "",
    formConfigData: createEmptyFormConfigData(),
    enabled: true,
    flowNodes: [createEmptyNode(1)],
    storageBlobDTOs: [],
  };
}
export function normalizeFlowNodes(nodes) {
  const list = Array.isArray(nodes) ? nodes : [];
  return list.map((n, i) => ({
    id: n.id,
    templateId: n.templateId,
    nodeOrder: i + 1,
    signMode: n.signMode === "or_sign" ? "or_sign" : "countersign",
    approvers: (n.approvers || [])
      .filter((a) => a?.approverId != null && a.approverId !== "")
      .map((a) => ({
        id: a.id,
        nodeId: a.nodeId,
        templateId: a.templateId,
        approverId: a.approverId,
        approverName: a.approverName || "",
      })),
  }));
}
export function validateTemplateForm(form) {
  const name = (form.templateName || "").trim();
  if (!name) return { ok: false, message: "请填写模板名称" };
  if (form.businessType == null || form.businessType === "") {
    return { ok: false, message: "请选择模板类型" };
  }
  const nodes = normalizeFlowNodes(form.flowNodes);
  if (!nodes.length) return { ok: false, message: "请至少配置一个审批节点" };
  for (let i = 0; i < nodes.length; i++) {
    if (!nodes[i].approvers.length) {
      return { ok: false, message: `请为第 ${i + 1} ä¸ªèŠ‚ç‚¹é€‰æ‹©è‡³å°‘ä¸€åå®¡æ‰¹äºº` };
    }
  }
  const cfgCheck = validateFormConfigData(form.formConfigData);
  if (!cfgCheck.ok) return cfgCheck;
  return { ok: true, nodes, name };
}
export function flowNodesSummary(nodes) {
  const list = normalizeFlowNodes(nodes);
  if (!list.length) return "—";
  return list
    .map((n, i) => {
      const names = n.approvers.map((a) => a.approverName || "未命名").join("、") || "未配置";
      return `节点${i + 1}(${nodeSignModeLabel(n.signMode)}:${names})`;
    })
    .join(" â†’ ");
}