yyb
14 小时以前 6c324a234060820d031014ea657af5aa0b0d478e
src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
@@ -1,5 +1,21 @@
import dayjs from "dayjs";
import { APPROVAL_TYPE_OPTIONS, SUBMIT_TEMPLATES } from "../approve-list/approveListConstants.js";
import {
  TEMPLATE_TYPE_CUSTOM,
  TEMPLATE_TYPE_OPTIONS,
} from "@/api/officeProcessAutomation/approvalTemplate.js";
import { APPROVAL_TYPE_OPTIONS } from "../approve-list/approveListConstants.js";
import {
  buildFormConfigJson,
  createEmptyFormConfigData,
  parseFormConfigToData,
  validateFormConfigData,
} from "./formConfigUtils.js";
export { TEMPLATE_TYPE_OPTIONS };
export function templateTypeLabel(type) {
  return TEMPLATE_TYPE_OPTIONS.find((x) => x.value === type)?.label || "—";
}
/** 节点内审批方式:会签 / 或签 */
export const NODE_SIGN_MODE_OPTIONS = [
@@ -7,18 +23,206 @@
  { value: "or_sign", label: "或签", desc: "本节点任一审批人通过即可" },
];
export const STORAGE_KEY = "oa_approve_template_custom_v1";
function parseFormConfig(formConfig) {
  if (!formConfig) return {};
  if (typeof formConfig === "object") return formConfig;
  try {
    return JSON.parse(formConfig);
  } catch {
    return {};
  }
}
/** 系统内置常用审批(只读展示,来源于审批列表提交模板) */
export function getBuiltinTemplates() {
  return Object.entries(SUBMIT_TEMPLATES).map(([key, tpl]) => ({
    key,
    approvalType: tpl.approvalType,
    label: tpl.label,
    summary: tpl.summaryPlaceholder || "系统预置填报字段",
    fieldCount: (tpl.fields || []).length,
    defaultMode: tpl.approvalMode,
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;
}
/** 分页列表项 → 页面行数据(主表 + 节点) */
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,
    formConfig: row.formConfig,
    formConfigData: parseFormConfigToData(row.formConfig),
    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 ?? TEMPLATE_TYPE_CUSTOM,
    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;
  return dto;
}
/** 构建分页查询参数 */
export function buildApprovalTemplateListParams({ page, searchForm }) {
  const params = {
    current: page.current,
    size: page.size,
  };
  if (searchForm?.templateType != null && searchForm.templateType !== "") {
    params.templateType = searchForm.templateType;
  }
  const kw = (searchForm?.keyword || "").trim();
  if (kw) params.templateName = kw;
  if (searchForm?.enabledOnly) params.enabled = "1";
  return params;
}
export function nodeSignModeLabel(mode) {
@@ -42,6 +246,9 @@
    id: "",
    templateName: "",
    description: "",
    templateType: TEMPLATE_TYPE_CUSTOM,
    formConfig: "",
    formConfigData: createEmptyFormConfigData(),
    enabled: true,
    flowNodes: [createEmptyNode(1)],
  };
@@ -50,11 +257,16 @@
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 || "",
      })),
@@ -71,6 +283,8 @@
      return { ok: false, message: `请为第 ${i + 1} 个节点选择至少一名审批人` };
    }
  }
  const cfgCheck = validateFormConfigData(form.formConfigData);
  if (!cfgCheck.ok) return cfgCheck;
  return { ok: true, nodes, name };
}
@@ -83,78 +297,4 @@
      return `节点${i + 1}(${nodeSignModeLabel(n.signMode)}:${names})`;
    })
    .join(" → ");
}
export function createInitialMockTemplates() {
  const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
  return [
    {
      id: "tpl_demo_1",
      templateName: "项目立项审批",
      description: "跨部门项目立项,需技术、财务依次会签",
      enabled: true,
      createTime: dayjs().subtract(5, "day").format("YYYY-MM-DD HH:mm:ss"),
      updateTime: now,
      flowNodes: [
        {
          nodeOrder: 1,
          signMode: "countersign",
          approvers: [
            { approverId: "mock_tech_lead", approverName: "技术负责人" },
            { approverId: "mock_pm", approverName: "项目经理" },
          ],
        },
        {
          nodeOrder: 2,
          signMode: "or_sign",
          approvers: [
            { approverId: "mock_finance", approverName: "财务主管" },
            { approverId: "mock_cfo", approverName: "财务总监" },
          ],
        },
      ],
    },
    {
      id: "tpl_demo_2",
      templateName: "合同用印申请",
      description: "法务与行政或签后,总经理终审",
      enabled: true,
      createTime: dayjs().subtract(12, "day").format("YYYY-MM-DD HH:mm:ss"),
      updateTime: dayjs().subtract(2, "day").format("YYYY-MM-DD HH:mm:ss"),
      flowNodes: [
        {
          nodeOrder: 1,
          signMode: "or_sign",
          approvers: [
            { approverId: "mock_legal", approverName: "法务专员" },
            { approverId: "mock_admin", approverName: "行政主管" },
          ],
        },
        {
          nodeOrder: 2,
          signMode: "countersign",
          approvers: [{ approverId: "mock_ceo", approverName: "总经理" }],
        },
      ],
    },
  ];
}
export function loadStoredTemplates() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? parsed : null;
  } catch {
    return null;
  }
}
export function saveStoredTemplates(rows) {
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(rows));
  } catch {
    /* ignore */
  }
}