import dayjs from "dayjs";
|
import { getTypeEnums } from "@/api/basicData/enum.js";
|
import { 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 [];
|
}
|
}
|
|
/** 节点内审批方式:会签 / 或签 */
|
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;
|
}
|
|
/** 分页列表项 → 页面行数据(主表 + 节点) */
|
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),
|
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: 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;
|
return dto;
|
}
|
|
export function buildApprovalTemplateListParams({ page, searchForm, templateType = TEMPLATE_TYPE_CUSTOM }) {
|
const params = {
|
current: page.current,
|
size: page.size,
|
templateType: searchForm?.templateType != null && searchForm.templateType !== ""
|
? searchForm.templateType
|
: templateType,
|
};
|
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,
|
businessType: "",
|
formConfig: "",
|
formConfigData: createEmptyFormConfigData(),
|
enabled: true,
|
flowNodes: [createEmptyNode(1)],
|
};
|
}
|
|
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(" → ");
|
}
|