commit
build / build (push) Successful in 9m55s

This commit is contained in:
wanghu
2026-10-10 15:58:02 +08:00
parent 6faa332d2b
commit 2e36d7a9c8
1842 changed files with 70537 additions and 0 deletions
+210
View File
@@ -0,0 +1,210 @@
// 预览专用:把页面会调用的 /api/* 接口就地 mock 掉。
//
// 为什么需要它:业务 dev 端口(8081/8078/8079)的 /api 会经 vite 代理打到真实后端,
// 未登录时后端返回 {code:302,urlToRedirectTo},而 alova 的 onSuccess 对该 code 会执行
// `window.location.href = urlToRedirectTo`(见 src/utils/alova/alova-core.ts),整页被
// 跳去 SSO —— 预览页什么都看不到。
//
// 因此在预览入口里用 fetch 拦截替换掉网络层,使该 harness 在任意端口(含 8081)
// 都能独立渲染,不再依赖 mock-server 或真实后端。
const LEAVE_TYPES = [
{ k: "sj", v: "事假" },
{ k: "bj", v: "病假" },
{ k: "tx", v: "调休" },
{ k: "hq", v: "婚假" },
{ k: "cq", v: "产假" },
{ k: "sq", v: "丧假" },
];
const TYPE_NAMES: Record<string, string> = Object.fromEntries(LEAVE_TYPES.map((t) => [t.k, t.v]));
const STATUS_NAMES = ["审批中", "已通过", "已拒绝", "已销假", "已撤回"];
const SEED = [
{ type: "sj", start: "2026-10-22 08:00:00", end: "2026-10-22 12:00:00", hours: 4, reason: "子女家长会", status: 0 },
{ type: "sj", start: "2026-10-21 08:00:00", end: "2026-10-21 12:00:00", hours: 4, reason: "家中有事需处理", status: 4 },
{ type: "tx", start: "2026-10-20 08:00:00", end: "2026-10-20 12:00:00", hours: 4, reason: "上月加班调休", status: 0 },
{ type: "cq", start: "2026-10-09 08:00:00", end: "2026-10-18 17:00:00", hours: 80, reason: "产假", status: 1 },
{ type: "bj", start: "2026-09-15 13:00:00", end: "2026-09-15 17:00:00", hours: 4, reason: "感冒发热,需居家休息", status: 1 },
{ type: "hq", start: "2026-08-03 08:00:00", end: "2026-08-07 17:00:00", hours: 40, reason: "婚假(含往返)", status: 2 },
{ type: "sq", start: "2026-06-11 08:00:00", end: "2026-06-13 17:00:00", hours: 24, reason: "家中长辈丧事", status: 3 },
{ type: "sj", start: "2026-05-06 13:00:00", end: "2026-05-06 17:00:00", hours: 4, reason: "子女家长会", status: 1 },
];
const NAMES = ["王雅静", "李国栋", "张明远", "陈思彤", "周建华", "孙丽娟", "吴强", "郑小满"];
/** 复刻 LeaveVo */
function leaveRows(scope: string) {
return SEED.map((s, i) => ({
id: i + 1,
userId: 1,
userName: scope === "user" ? "王雅静" : NAMES[i % NAMES.length],
leaveType: s.type,
leaveTypeName: TYPE_NAMES[s.type],
startTime: s.start,
endTime: s.end,
totalHours: s.hours,
reason: s.reason,
status: s.status,
statusName: STATUS_NAMES[s.status],
applyTime: `2026-10-0${(i % 9) + 1} 0${(i % 9) + 1}:3${i % 10}:1${i}`,
ccTime: scope === "cc" ? `2026-10-0${(i % 9) + 1} 10:0${i}:00` : undefined,
canEdit: s.status === 0 || s.status === 4,
canWithdraw: s.status === 0,
canCancel: s.status === 1,
canDelete: s.status !== 1,
}));
}
/** 复刻 TodoVo */
function todoRows() {
return SEED.slice(0, 5).map((s, i) => ({
taskId: 900 + i,
instanceId: 500 + i,
leaveId: i + 1,
userName: NAMES[i % NAMES.length],
leaveType: s.type,
leaveTypeName: TYPE_NAMES[s.type],
startTime: s.start,
endTime: s.end,
totalHours: s.hours,
reason: s.reason,
status: 0,
statusName: "审批中",
applyTime: `2026-10-0${i + 1} 09:0${i}:00`,
}));
}
/** 复刻 UserTimeRecordVo */
function recordRows() {
return SEED.map((s, i) => ({
id: i + 1,
userId: 1,
userName: NAMES[i % NAMES.length],
leaveId: i + 1,
leaveType: s.type,
leaveTypeName: TYPE_NAMES[s.type],
editHours: [-8, 8, -4, 4, 2, -2, 1, -1][i],
oldHours: 16 + i,
newHours: 16 + i + [-8, 8, -4, 4, 2, -2, 1, -1][i],
describeText: i % 2 ? "请假扣减" : "管理员调增",
operatorName: "系统",
occurTime: s.end,
}));
}
/** 复刻 LeaveStatsVo */
function stats() {
const types = LEAVE_TYPES.map((t, i) => ({
type: t.k,
typeName: t.v,
count: 3 + i,
hours: 12 + i * 6,
}));
const months = Array.from({ length: 6 }, (_, i) => ({
month: `${i + 5}月`,
count: 2 + (i % 4),
hours: 8 + i * 5,
}));
return {
totalCount: 18,
totalHours: 176.5,
totalDays: 22,
topTypeName: "事假",
typeStats: types,
monthStats: months,
};
}
/** 复刻 LeaveDetailVo(打印预览用;含流程 steps 与抄送人) */
function detailRow(id: number) {
const s = SEED[(id - 1 + SEED.length) % SEED.length];
return {
id,
userId: 1,
userName: "王雅静",
leaveType: s.type,
leaveTypeName: TYPE_NAMES[s.type],
startTime: s.start,
endTime: s.end,
totalHours: s.hours,
reason: s.reason,
courseArrange: 2,
courseArrangeName: "教务安排",
status: s.status,
statusName: STATUS_NAMES[s.status],
applyTime: "2026-10-09 19:28:00",
files: [],
ccUsers: [
{ userId: 2, userName: "李国栋" },
{ userId: 3, userName: "张明远" },
],
progress: {
instanceId: 500,
flowName: "教职工请假流程",
currentNodeName: "校长审批",
submitterName: "王雅静",
startTime: "2026-10-09 19:28:00",
steps: [
{ nodeName: "发起申请", assigneeName: "王雅静", action: "submit", opinion: "", time: "2026-10-09 19:28:00", finished: true },
{ nodeName: "部门审核", assigneeName: "李国栋", action: "approve", opinion: "情况属实,同意", time: "2026-10-09 20:15:00", finished: true },
{ nodeName: "校长审批", assigneeName: "周建华", action: "approve", opinion: "同意", time: "2026-10-09 21:02:00", finished: true },
],
},
canCancel: s.status === 1,
};
}
function page<T>(rows: T[], url: string) {
const q = new URL(url, "http://x").searchParams;
const pageIndex = Number(q.get("pageIndex") || 1);
const pageSize = Number(q.get("pageSize") || 10);
return { content: rows.slice((pageIndex - 1) * pageSize, pageIndex * pageSize), total: rows.length };
}
/** 按 URL 解析出 mock 数据;返回 undefined 表示「无数据」 */
export function resolveMock(rawUrl: string): any {
const url = rawUrl;
const q = new URL(url, "http://x").searchParams;
if (/leave\/todoList/i.test(url)) return page(todoRows(), url);
if (/leave\/auditedList/i.test(url)) return page(leaveRows("all").filter((r) => r.status !== 0), url);
if (/leave\/list/i.test(url)) {
const scope = q.get("scope") || "user";
let rows = leaveRows(scope);
const status = q.get("status");
const kw = (q.get("keyword") || "").trim();
const type = q.get("leaveType");
if (status !== null && 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));
return page(rows, url);
}
if (/leave\/time\/records/i.test(url)) return page(recordRows(), url);
if (/leave\/detail/i.test(url)) return detailRow(Number(q.get("leaveId") || 4));
if (/leave\/setting\/getoption/i.test(url)) return { reasonMust: 1, showExplain: 1, enableCourse: 1, allowExchange: 0, timeLimitEnabled: 0, timeManagedTypes: "sj,bj", calcWay: 1 };
if (/leave\/time\/sum/i.test(url)) return { addHours: 24, remainHours: 16.5 };
if (/leave\/statis/i.test(url)) return stats();
if (/dict\/list/i.test(url)) return LEAVE_TYPES;
if (/base\/getuserinfo/i.test(url)) return { id: 1, name: "王雅静", profilePicture: "" };
return undefined;
}
/** 安装 fetch 拦截:仅接管 /api/* ,其余原样放行 */
export function installApiStub() {
const w = window as any;
if (w.__apiStubInstalled) return;
const original = w.fetch.bind(window);
w.__apiStubHit = 0;
w.fetch = async (input: any, init?: any) => {
const url: string = typeof input === "string" ? input : input?.url || "";
if (!/\/api\//.test(url)) return original(input, init);
w.__apiStubHit++;
const data = resolveMock(url);
return new Response(JSON.stringify({ code: 0, msg: "ok", data: data === undefined ? null : data }), {
status: 200,
headers: { "Content-Type": "application/json;charset=utf-8" },
});
};
w.__apiStubInstalled = true;
}