@@ -0,0 +1,226 @@
|
||||
// 本地 mock 服务:仅为预览「教师时长」页视觉,供 preview-ut 专用 vite 代理使用
|
||||
// 用法:node preview-ut/mock-server.mjs8109
|
||||
import http from "node:http";
|
||||
|
||||
const USERS = [
|
||||
{ userId: 1, userName: "王雅静", usableHours: 24, remark: "语文组教研组长" },
|
||||
{ userId: 2, userName: "李国栋", usableHours: 16, remark: null },
|
||||
{ userId: 3, userName: "张明远", usableHours: 6, remark: null },
|
||||
{ userId: 4, userName: "陈思彤", usableHours: 0, remark: null },
|
||||
{ userId: 5, userName: "周建华", usableHours: 40, remark: null },
|
||||
{ userId: 6, userName: "孙丽娟", usableHours: 12.5, remark: null },
|
||||
{ userId: 7, userName: "吴强", usableHours: 3, remark: null },
|
||||
{ userId: 8, userName: "郑小满", usableHours: 32, remark: null },
|
||||
];
|
||||
const TYPES = [
|
||||
{ SerID: "1", CodeID: "sj", Name: "事假", Enable: 1 },
|
||||
{ SerID: "2", CodeID: "bj", Name: "病假", Enable: 1 },
|
||||
{ SerID: "3", CodeID: "tx", Name: "调休", Enable: 1 },
|
||||
{ SerID: "4", CodeID: "hq", Name: "婚假", Enable: 1 },
|
||||
{ SerID: "5", CodeID: "cq", Name: "产假", Enable: 1 },
|
||||
{ SerID: "6", CodeID: "sq", Name: "丧假", Enable: 1 },
|
||||
];
|
||||
// 复刻后端 UserCenterService.getSchoolAllTree 的扁平结构:
|
||||
// 组 type=1/isParent=true,教师 type=2,用 pid 指向父组
|
||||
const TREE = [
|
||||
{ id: 100, name: "云海中学", pid: 0, type: 1, isParent: true },
|
||||
{ id: 200, name: "语文组", pid: 100, type: 1, isParent: true },
|
||||
{ id: 201, name: "数学组", pid: 100, type: 1, isParent: true },
|
||||
{ id: 202, name: "英语组", pid: 100, type: 1, isParent: true },
|
||||
{ id: 203, name: "综合组", pid: 100, type: 1, isParent: true },
|
||||
{ id: 1, name: "王雅静", pid: 200, type: 2 },
|
||||
{ id: 2, name: "李国栋", pid: 200, type: 2 },
|
||||
{ id: 5, name: "周建华", pid: 200, type: 2 },
|
||||
{ id: 3, name: "张明远", pid: 201, type: 2 },
|
||||
{ id: 4, name: "陈思彤", pid: 201, type: 2 },
|
||||
{ id: 6, name: "孙丽娟", pid: 202, type: 2 },
|
||||
{ id: 7, name: "吴强", pid: 202, type: 2 },
|
||||
{ id: 8, name: "郑小满", pid: 203, type: 2 },
|
||||
];
|
||||
|
||||
const OPTION = { reasonMust: 1, showExplain: 1, timeLimitEnabled: 1, timeManagedTypes: "sj,bj,tx", enableCourse: 0, allowExchange: 0, calcWay: 1 };
|
||||
const TIMESET = { forenoon1: "08:00", forenoon2: "12:00", afternoon1: "13:00", afternoon2: "17:00", intervalMinutes: 10, minMinutes: 0 };
|
||||
|
||||
// 平台 GetUserMod 的真实返回(2026-10 环境):扁平一级菜单,id = modId = 97–105,
|
||||
// page 是历史路径。用于整壳验收侧栏「点击 → 跳转 → 高亮」链路(preview-app.html)。
|
||||
const USER_MOD = [
|
||||
{ id: 97, modId: 97, name: "我要请假", page: "/leave/", parentId: 0, level: 1, orderNumber: 100, usable: true, children: [] },
|
||||
{ id: 98, modId: 98, name: "我的请假", page: "/leave/myleave", parentId: 0, level: 1, orderNumber: 110, usable: true, children: [] },
|
||||
{ id: 99, modId: 99, name: "审批列表", page: "/leave/shenhe", parentId: 0, level: 1, orderNumber: 120, usable: true, children: [] },
|
||||
{ id: 100, modId: 100, name: "抄送列表", page: "/leave/leave_send", parentId: 0, level: 1, orderNumber: 130, usable: true, children: [] },
|
||||
{ id: 101, modId: 101, name: "全部请假", page: "/leave/totallist", parentId: 0, level: 1, orderNumber: 140, usable: true, children: [] },
|
||||
{ id: 102, modId: 102, name: "流程管理", page: "/leave/liucheng", parentId: 0, level: 1, orderNumber: 160, usable: true, children: [] },
|
||||
{ id: 103, modId: 103, name: "请假设置", page: "/leave/leave_timeSetting", parentId: 0, level: 1, orderNumber: 170, usable: true, children: [] },
|
||||
{ id: 104, modId: 104, name: "时长日志", page: "/leave/leave_amendant", parentId: 0, level: 1, orderNumber: 180, usable: true, children: [] },
|
||||
{ id: 105, modId: 105, name: "统计分析", page: "/leave/tongji", parentId: 0, level: 1, orderNumber: 190, usable: true, children: [] },
|
||||
];
|
||||
const USER_INFO = { id: 1, name: "王雅静", profilePicture: "" };
|
||||
|
||||
// ===== 流程管理(/api/leave/flow/*):内存态,可交互(开关/绑定即时生效)=====
|
||||
// 复刻流程管理页 FlowManageVo;通知流程默认「未启用」,演示 oapi 混入非本应用流程的场景
|
||||
const CENTER_FLOWS = [
|
||||
{ flowGuid: "35dc167902ef436e8767809eb6bf11ee", name: "设课(5天)", version: 6, nodeSummary: "审核中", enabled: true },
|
||||
{ flowGuid: "28bc3ca8acc24611809d7dd1b6b9a597", name: "产假(10天)", version: 5, nodeSummary: "审核中", enabled: true },
|
||||
{ flowGuid: "d2237a5b2b95492e845da9caeaa35590", name: "请假申请(小于3天或大于等于3天)", version: 4, nodeSummary: "审批节点—222", enabled: true },
|
||||
{ flowGuid: "fdb2c9046ede4db583b0a4899d065f75", name: "通知流程", version: 2, nodeSummary: "审批节点", enabled: false },
|
||||
];
|
||||
let BIND_SEQ = 3;
|
||||
const FLOW_BINDS = [
|
||||
{ id: 1, scope: 0, scopeKey: "-", scopeName: "全校默认", flowGuid: "d2237a5b2b95492e845da9caeaa35590", flowName: "请假申请(小于3天或大于等于3天)", flowVersion: 4, nodeSummary: "审批节点—222", existsInCenter: true },
|
||||
{ id: 2, scope: 1, scopeKey: "cq", scopeName: "产假", flowGuid: "28bc3ca8acc24611809d7dd1b6b9a597", flowName: "产假(10天)", flowVersion: 5, nodeSummary: "审核中", existsInCenter: true },
|
||||
];
|
||||
|
||||
function flowManageData() {
|
||||
const flows = CENTER_FLOWS.map((f) => {
|
||||
const typeKeys = FLOW_BINDS.filter((b) => b.scope === 1 && b.flowGuid === f.flowGuid).map((b) => b.scopeKey);
|
||||
return {
|
||||
...f,
|
||||
defaultBind: FLOW_BINDS.some((b) => b.scope === 0 && b.flowGuid === f.flowGuid),
|
||||
typeKeys,
|
||||
typeNames: typeKeys.map((k) => TYPES.find((t) => t.CodeID === k)?.Name || k),
|
||||
};
|
||||
});
|
||||
return {
|
||||
bizType: "leave",
|
||||
configFlowGuid: "d2237a5b2b95492e845da9caeaa35590",
|
||||
centerReachable: true,
|
||||
centerError: null,
|
||||
flows,
|
||||
binds: FLOW_BINDS.map((b) => ({ ...b })),
|
||||
leaveTypes: TYPES.map((t) => ({ key: t.CodeID, name: t.Name })),
|
||||
};
|
||||
}
|
||||
|
||||
/** 写操作:返回 true 表示已识别并处理(POST /api/leave/flow/*) */
|
||||
function flowMutate(url, body) {
|
||||
const guid = body?.flowGuid;
|
||||
if (/flow\/Enable/i.test(url)) {
|
||||
const f = CENTER_FLOWS.find((x) => x.flowGuid === guid);
|
||||
if (f) f.enabled = true;
|
||||
return true;
|
||||
}
|
||||
if (/flow\/Disable/i.test(url)) {
|
||||
const f = CENTER_FLOWS.find((x) => x.flowGuid === guid);
|
||||
if (f) f.enabled = false;
|
||||
for (let i = FLOW_BINDS.length - 1; i >= 0; i--) if (FLOW_BINDS[i].flowGuid === guid) FLOW_BINDS.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
if (/flow\/Bind/i.test(url)) {
|
||||
const scope = Number(body?.scope ?? 0);
|
||||
const scopeKey = scope === 1 ? String(body?.scopeKey || "") : "-";
|
||||
const f = CENTER_FLOWS.find((x) => x.flowGuid === guid);
|
||||
if (!f) return true;
|
||||
const hit = FLOW_BINDS.find((b) => b.scope === scope && b.scopeKey === scopeKey);
|
||||
const row = {
|
||||
id: hit ? hit.id : BIND_SEQ++,
|
||||
scope,
|
||||
scopeKey,
|
||||
scopeName: scope === 0 ? "全校默认" : TYPES.find((t) => t.CodeID === scopeKey)?.Name || scopeKey,
|
||||
flowGuid: f.flowGuid,
|
||||
flowName: f.name,
|
||||
flowVersion: f.version,
|
||||
nodeSummary: f.nodeSummary,
|
||||
existsInCenter: true,
|
||||
};
|
||||
if (hit) Object.assign(hit, row);
|
||||
else FLOW_BINDS.push(row);
|
||||
return true;
|
||||
}
|
||||
if (/flow\/Unbind/i.test(url)) {
|
||||
const scope = Number(body?.scope ?? 0);
|
||||
const scopeKey = scope === 1 ? String(body?.scopeKey || "") : "-";
|
||||
for (let i = FLOW_BINDS.length - 1; i >= 0; i--) {
|
||||
if (FLOW_BINDS[i].scope === scope && FLOW_BINDS[i].scopeKey === scopeKey) FLOW_BINDS.splice(i, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===== 请假列表(/api/leave/List):我的请假 / 全部请假 / 抄送列表 / 审批列表 共用一个接口 =====
|
||||
// 复刻 LeaveVo;状态 0审批中 1已通过 2已拒绝 3已销假 4已撤回
|
||||
const LEAVE_ROWS = [
|
||||
{ id: 1, leaveType: "sj", leaveTypeName: "事假", startTime: "2026-10-22 08:00:00", endTime: "2026-10-22 12:00:00", totalHours: 4, reason: "E2E扣减链路验证-事假", status: 0, canEdit: true, canWithdraw: true, canDelete: true },
|
||||
{ id: 2, leaveType: "sj", leaveTypeName: "事假", startTime: "2026-10-21 08:00:00", endTime: "2026-10-21 12:00:00", totalHours: 4, reason: "E2E扣减链路验证-事假", status: 4, canEdit: true, canDelete: true },
|
||||
{ id: 3, leaveType: "tx", leaveTypeName: "调休", startTime: "2026-10-20 08:00:00", endTime: "2026-10-20 12:00:00", totalHours: 4, reason: "E2E回调链路验证-调休", status: 0, canEdit: true, canWithdraw: true, canDelete: true },
|
||||
{ id: 4, leaveType: "cq", leaveTypeName: "产假", startTime: "2026-10-09 08:00:00", endTime: "2026-10-18 17:00:00", totalHours: 80, reason: "1111", status: 1, canCancel: true },
|
||||
{ id: 5, leaveType: "bj", leaveTypeName: "病假", startTime: "2026-09-15 13:00:00", endTime: "2026-09-15 17:00:00", totalHours: 4, reason: "感冒发热,需居家休息", status: 1, canCancel: true },
|
||||
{ id: 6, leaveType: "hq", leaveTypeName: "婚假", startTime: "2026-08-03 08:00:00", endTime: "2026-08-07 17:00:00", totalHours: 40, reason: "婚假(含往返)", status: 2 },
|
||||
{ id: 7, leaveType: "sq", leaveTypeName: "丧假", startTime: "2026-06-11 08:00:00", endTime: "2026-06-13 17:00:00", totalHours: 24, reason: "家中长辈丧事", status: 3 },
|
||||
{ id: 8, leaveType: "sj", leaveTypeName: "事假", startTime: "2026-05-06 13:00:00", endTime: "2026-05-06 17:00:00", totalHours: 4, reason: "子女家长会", status: 1, canCancel: true },
|
||||
];
|
||||
|
||||
function leaveList(url) {
|
||||
const q = new URL(url, "http://x").searchParams;
|
||||
const scope = q.get("scope") || "user";
|
||||
const status = q.get("status");
|
||||
const kw = (q.get("keyword") || "").trim();
|
||||
const type = q.get("leaveType");
|
||||
const pageIndex = Number(q.get("pageIndex") || 1);
|
||||
const pageSize = Number(q.get("pageSize") || 10);
|
||||
|
||||
let rows = LEAVE_ROWS.map((r, i) => ({
|
||||
...r,
|
||||
userId: 1,
|
||||
userName: scope === "user" ? "王雅静" : USERS[i % USERS.length].userName,
|
||||
statusName: ["审批中", "已通过", "已拒绝", "已销假", "已撤回"][r.status],
|
||||
applyTime: `2026-10-0${(i % 9) + 1} 0${(i % 9) + 1}:3${i % 10}:${10 + i}`,
|
||||
}));
|
||||
if (scope === "cc") rows = rows.map((r) => ({ ...r, ccTime: r.applyTime }));
|
||||
if (status !== null && status !== undefined && status !== "") rows = rows.filter((r) => String(r.status) === String(status));
|
||||
if (type) rows = rows.filter((r) => r.leaveType === type);
|
||||
if (kw) rows = rows.filter((r) => (r.reason || "").includes(kw) || r.leaveTypeName.includes(kw) || r.userName.includes(kw));
|
||||
const total = rows.length;
|
||||
const content = rows.slice((pageIndex - 1) * pageSize, pageIndex * pageSize);
|
||||
return { content, total, pageIndex, pageSize };
|
||||
}
|
||||
|
||||
function resolve(url) {
|
||||
// 应用外壳:用户信息 / 授权模块(整壳验收用)
|
||||
if (/Base\/GetUserMod|base\/GetUserMod/i.test(url)) return USER_MOD;
|
||||
if (/leave\/List/i.test(url)) return leaveList(url);
|
||||
if (/Base\/GetUserInfo|base\/GetUserInfo/i.test(url)) return USER_INFO;
|
||||
if (/GetSchoolAll/.test(url)) return TREE;
|
||||
if (/UserTimeList/.test(url)) {
|
||||
const kw = new URL(url, "http://x").searchParams.get("keyword");
|
||||
return kw ? USERS.filter((u) => u.userName.includes(kw)) : USERS;
|
||||
}
|
||||
// 下拉接口返回 { k, v }(与后端 DictService.List 一致);管理列表返回完整字段
|
||||
if (/Dict\/List/i.test(url)) return TYPES.map((t) => ({ k: t.CodeID, v: t.Name }));
|
||||
if (/\/dict|Dict/.test(url)) return TYPES;
|
||||
if (/Option/.test(url)) return OPTION;
|
||||
if (/TimeSet/.test(url)) return TIMESET;
|
||||
if (/Section/.test(url)) return [];
|
||||
if (/leave\/flow\/List/i.test(url)) return flowManageData();
|
||||
return null;
|
||||
}
|
||||
|
||||
http
|
||||
.createServer((req, res) => {
|
||||
console.log("[mock] ->", req.method, req.url);
|
||||
const chunks = [];
|
||||
req.on("data", (c) => chunks.push(c));
|
||||
req.on("end", () => {
|
||||
let body = null;
|
||||
const raw = Buffer.concat(chunks).toString("utf8");
|
||||
if (raw) {
|
||||
try {
|
||||
body = JSON.parse(raw);
|
||||
} catch {
|
||||
body = Object.fromEntries(new URLSearchParams(raw));
|
||||
}
|
||||
}
|
||||
const url = req.url || "";
|
||||
let data;
|
||||
if (req.method === "POST" && /leave\/flow\/(Enable|Disable|Bind|Unbind)/i.test(url)) {
|
||||
flowMutate(url, body);
|
||||
data = undefined; // 写接口返回 data:null
|
||||
} else {
|
||||
data = resolve(url);
|
||||
}
|
||||
res.setHeader("Content-Type", "application/json;charset=utf-8");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.end(JSON.stringify({ code: 0, msg: "ok", data: data === undefined ? null : data }));
|
||||
});
|
||||
})
|
||||
.listen(8109, () => console.log("mock server on http://localhost:8109"));
|
||||
Reference in New Issue
Block a user