yyb
7 小时以前 8bba0a2d08c7abc07604a0654661efc884e5d751
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import dayjs from "dayjs";
import { APPROVAL_TYPE_OPTIONS, SUBMIT_TEMPLATES } from "../approve-list/approveListConstants.js";
 
/** 节点内审批方式:会签 / 或签 */
export const NODE_SIGN_MODE_OPTIONS = [
  { value: "countersign", label: "会签", desc: "本节点所有审批人均需通过" },
  { value: "or_sign", label: "或签", desc: "本节点任一审批人通过即可" },
];
 
export const STORAGE_KEY = "oa_approve_template_custom_v1";
 
/** 系统内置常用审批(只读展示,来源于审批列表提交模板) */
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,
  }));
}
 
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: "",
    enabled: true,
    flowNodes: [createEmptyNode(1)],
  };
}
 
export function normalizeFlowNodes(nodes) {
  const list = Array.isArray(nodes) ? nodes : [];
  return list.map((n, i) => ({
    nodeOrder: i + 1,
    signMode: n.signMode === "or_sign" ? "or_sign" : "countersign",
    approvers: (n.approvers || [])
      .filter((a) => a?.approverId != null && a.approverId !== "")
      .map((a) => ({
        approverId: a.approverId,
        approverName: a.approverName || "",
      })),
  }));
}
 
export function validateTemplateForm(form) {
  const name = (form.templateName || "").trim();
  if (!name) 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} 个节点选择至少一名审批人` };
    }
  }
  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(" → ");
}
 
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 */
  }
}