125 lines
5.0 KiB
TypeScript
125 lines
5.0 KiB
TypeScript
import type { Plugin } from "vite";
|
||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||
|
||
/*
|
||
* 开发用 mock 中间件:后端未就绪时拦截招生辅助数据接口,返回 Admin.NET 统一响应结构。
|
||
* 仅在 dev serve 生效(apply: "serve"),由 vite.config 根据 VITE_USE_MOCK 决定是否启用。
|
||
* 后端就绪后把 .env 的 VITE_USE_MOCK 置空/false 即可。
|
||
*/
|
||
|
||
// value 为后端整数枚举(见 docs/api.md 10.8),前端按整数判断学段。
|
||
const CONSULT_STAGES = [
|
||
{ label: "高中", value: 1, sort: 1, disabled: false },
|
||
{ label: "职教高考", value: 2, sort: 2, disabled: false },
|
||
{ label: "综合高中", value: 3, sort: 3, disabled: false },
|
||
{ label: "小学", value: 4, sort: 4, disabled: false },
|
||
{ label: "初中", value: 5, sort: 5, disabled: false },
|
||
{ label: "国际/出国", value: 6, sort: 6, disabled: false },
|
||
{ label: "复读", value: 7, sort: 7, disabled: false },
|
||
];
|
||
|
||
const REGION_SCHOOLS: Record<string, string[]> = {
|
||
历城区: ["济南市历城第二中学", "济南稼轩学校", "济南市历城区洪家楼高级中学"],
|
||
市中区: [
|
||
"济南协和双语实验学校(初中部)",
|
||
"济南市安生学校(初中部)",
|
||
"济南高新区海川中学(民办)",
|
||
"济南高新区瀚阳学校(初中部)",
|
||
"济南高新区凤凰路学校",
|
||
"济南高新区奥体中路学校",
|
||
"济南高新区伯乐实验学校",
|
||
"济南市育英中学",
|
||
"山东省济南第三中学",
|
||
],
|
||
槐荫区: ["山东省济南第九中学", "济南市槐荫中学", "济南市济微中学"],
|
||
天桥区: ["山东省济南第十一中学", "济南市天桥区泺口实验学校"],
|
||
平阴县: ["山东省平阴县第一中学", "平阴县实验高级中学"],
|
||
长清区: ["济南市长清第一中学", "济南市长清中学"],
|
||
章丘区: ["山东省章丘第四中学", "济南市章丘区实验中学"],
|
||
济阳区: ["山东省济北中学", "济阳区第一中学"],
|
||
};
|
||
|
||
// 给「市中区」补一些学校,方便测试 VanList 滚动分页(超过 1 页)。
|
||
for (let i = 1; i <= 15; i++) {
|
||
REGION_SCHOOLS["市中区"].push(`济南市中区实验学校(分校${i})`);
|
||
}
|
||
|
||
const REGION_ORDER = Object.keys(REGION_SCHOOLS);
|
||
|
||
const REGIONS = REGION_ORDER.map((region, index) => ({
|
||
region,
|
||
label: region,
|
||
schoolCount: REGION_SCHOOLS[region].length,
|
||
sort: index + 1,
|
||
}));
|
||
|
||
let autoId = 0;
|
||
const ALL_SCHOOLS = REGION_ORDER.flatMap((region) => REGION_SCHOOLS[region].map((schoolName) => ({ id: ++autoId, region, schoolName })));
|
||
|
||
function sendResult(res: ServerResponse, result: unknown): void {
|
||
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
||
res.statusCode = 200;
|
||
res.end(JSON.stringify({ code: 200, type: "success", message: "", result, extras: null, time: new Date().toISOString() }));
|
||
}
|
||
|
||
function readJson(req: IncomingMessage): Promise<Record<string, unknown>> {
|
||
return new Promise((resolve) => {
|
||
let raw = "";
|
||
req.on("data", (chunk) => (raw += chunk));
|
||
req.on("end", () => {
|
||
try {
|
||
resolve(raw ? JSON.parse(raw) : {});
|
||
} catch {
|
||
resolve({});
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
const delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
|
||
|
||
export function mockServer(): Plugin {
|
||
return {
|
||
name: "admission-mock-server",
|
||
apply: "serve",
|
||
configureServer(server) {
|
||
server.middlewares.use(async (req, res, next) => {
|
||
const path = (req.url || "").split("?")[0];
|
||
const method = req.method || "GET";
|
||
|
||
const handler =
|
||
method === "GET" && path === "/api/admission/options/consult-stages"
|
||
? () => sendResult(res, CONSULT_STAGES)
|
||
: method === "GET" && path === "/api/admission/school/regions"
|
||
? () => sendResult(res, REGIONS)
|
||
: method === "POST" && path === "/api/admission/school/search"
|
||
? async () => {
|
||
const body = await readJson(req);
|
||
const page = Math.max(1, Number(body.page) || 1);
|
||
const pageSize = Math.min(100, Math.max(1, Number(body.pageSize) || 20));
|
||
let list = ALL_SCHOOLS;
|
||
if (body.region) {
|
||
list = list.filter((school) => school.region === body.region);
|
||
}
|
||
if (body.keyword) {
|
||
list = list.filter((school) => school.schoolName.includes(String(body.keyword)));
|
||
}
|
||
const total = list.length;
|
||
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
||
const items = list.slice((page - 1) * pageSize, page * pageSize);
|
||
sendResult(res, { page, pageSize, total, totalPages, items, hasPrevPage: page > 1, hasNextPage: page < totalPages });
|
||
}
|
||
: null;
|
||
|
||
if (!handler) {
|
||
next();
|
||
return;
|
||
}
|
||
|
||
await delay(250); // 模拟网络延迟
|
||
await handler();
|
||
});
|
||
},
|
||
};
|
||
}
|