first commit
This commit is contained in:
174
mock/appointment.ts
Normal file
174
mock/appointment.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
const allAppointments = [
|
||||
// 已预约
|
||||
{ id: 1, name: '能力提升面试', category: '模拟面试', time: '2025.08.10', session: '14:00:00-16:00:00', teacher: '李老师', location: '教室1', status: 'booked' },
|
||||
{ id: 2, name: '能力提升面试', category: '模拟面试', time: '2025.08.10', session: '14:00:00-16:00:00', teacher: '李老师', location: '教室1', status: 'booked' },
|
||||
{ id: 3, name: '能力提升面试', category: '模拟面试', time: '2025.08.10', session: '14:00:00-16:00:00', teacher: '李老师', location: '教室1', status: 'booked' },
|
||||
{ id: 4, name: '职业规划咨询', category: '职业咨询', time: '2025.08.12', session: '09:00:00-11:00:00', teacher: '王老师', location: '会议室A', status: 'booked' },
|
||||
{ id: 5, name: '简历修改指导', category: '简历指导', time: '2025.08.15', session: '10:00:00-12:00:00', teacher: '张老师', location: '教室3', status: 'booked' },
|
||||
// 预约待确认
|
||||
{ id: 6, name: '模拟群面练习', category: '模拟面试', time: '2025.08.18', session: '14:00:00-16:00:00', teacher: '赵老师', location: '教室2', status: 'pending' },
|
||||
{ id: 7, name: '行业分析讲座', category: '讲座', time: '2025.08.20', session: '09:00:00-11:00:00', teacher: '刘老师', location: '报告厅', status: 'pending' },
|
||||
{ id: 8, name: '职业规划咨询', category: '职业咨询', time: '2025.08.22', session: '14:00:00-16:00:00', teacher: '王老师', location: '会议室A', status: 'pending' },
|
||||
// 已完成
|
||||
{ id: 9, name: '能力提升面试', category: '模拟面试', time: '2025.07.20', session: '14:00:00-16:00:00', teacher: '李老师', location: '教室1', status: 'completed' },
|
||||
{ id: 10, name: '简历修改指导', category: '简历指导', time: '2025.07.15', session: '10:00:00-12:00:00', teacher: '张老师', location: '教室3', status: 'completed' },
|
||||
{ id: 11, name: '职业规划咨询', category: '职业咨询', time: '2025.07.10', session: '09:00:00-11:00:00', teacher: '王老师', location: '会议室A', status: 'completed' },
|
||||
{ id: 12, name: '模拟群面练习', category: '模拟面试', time: '2025.07.05', session: '14:00:00-16:00:00', teacher: '赵老师', location: '教室2', status: 'completed' },
|
||||
// 已取消
|
||||
{ id: 13, name: '行业分析讲座', category: '讲座', time: '2025.07.25', session: '09:00:00-11:00:00', teacher: '刘老师', location: '报告厅', status: 'cancelled' },
|
||||
{ id: 14, name: '能力提升面试', category: '模拟面试', time: '2025.07.22', session: '14:00:00-16:00:00', teacher: '李老师', location: '教室1', status: 'cancelled' },
|
||||
];
|
||||
|
||||
// 预约管理数据
|
||||
const specialManage = [
|
||||
{ id: 101, name: '能力提升面试', category: '模拟面试', startTime: '2025.09.01', endTime: '2026.10.01', remaining: 20, total: 300, sessions: 20, teacher: '李老师', status: 'ongoing' },
|
||||
{ id: 102, name: '能力提升面试', category: '模拟面试', startTime: '2025.09.01', endTime: '2026.10.01', remaining: 20, total: 300, sessions: 20, teacher: '李老师', status: 'ended' },
|
||||
{ id: 103, name: '能力提升面试', category: '模拟面试', startTime: '2025.09.01', endTime: '2026.10.01', remaining: 0, total: 300, sessions: 20, teacher: '李老师', status: 'ongoing' },
|
||||
];
|
||||
const categories = ['模拟面试', '职业咨询', '简历指导'];
|
||||
const teachers = ['李老师', '王老师', '张老师', '赵老师', '刘老师', '陈老师', '周老师', '吴老师'];
|
||||
const statuses = ['ongoing', 'notStarted', 'ongoing', 'ongoing', 'ongoing', 'ongoing', 'ended', 'ongoing'];
|
||||
const names = ['能力提升面试', '职业规划咨询', '简历修改指导', '模拟群面练习', '行业分析讲座', '求职技巧培训'];
|
||||
const manageAppointments = [
|
||||
...specialManage,
|
||||
...Array.from({ length: 97 }, (_, i) => ({
|
||||
id: 104 + i,
|
||||
name: names[i % names.length],
|
||||
category: categories[i % categories.length],
|
||||
startTime: '2025.09.01',
|
||||
endTime: '2026.10.01',
|
||||
remaining: 20 + (i % 50),
|
||||
total: 300,
|
||||
sessions: 10 + (i % 20),
|
||||
teacher: teachers[i % teachers.length],
|
||||
status: statuses[i % statuses.length],
|
||||
})),
|
||||
];
|
||||
|
||||
// 用户映射:userId=2 的用户没有任何预约数据
|
||||
const userAppointments: Record<number, number[]> = {
|
||||
1: allAppointments.map((a) => a.id), // 默认用户有所有数据
|
||||
2: [], // 空白用户
|
||||
};
|
||||
|
||||
export default {
|
||||
// 获取预约列表
|
||||
'GET /api/appointment/list': (req: any, res: any) => {
|
||||
const { status, name, student, date, page = 1, pageSize = 10, userId = 1 } = req.query;
|
||||
const uid = Number(userId);
|
||||
const userIds = userAppointments[uid] ?? userAppointments[1];
|
||||
|
||||
let filtered = allAppointments.filter((item) => userIds.includes(item.id));
|
||||
|
||||
// 按状态筛选
|
||||
if (status) {
|
||||
filtered = filtered.filter((item) => item.status === status);
|
||||
}
|
||||
|
||||
// 按预约名称模糊搜索
|
||||
if (name) {
|
||||
filtered = filtered.filter((item) => item.name.includes(name));
|
||||
}
|
||||
|
||||
// 按日期筛选
|
||||
if (date) {
|
||||
filtered = filtered.filter((item) => item.time === date);
|
||||
}
|
||||
|
||||
const total = filtered.length;
|
||||
const start = (Number(page) - 1) * Number(pageSize);
|
||||
const end = start + Number(pageSize);
|
||||
const list = filtered.slice(start, end);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
data: {
|
||||
list,
|
||||
total,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize),
|
||||
},
|
||||
message: 'ok',
|
||||
});
|
||||
},
|
||||
|
||||
// 预约管理列表
|
||||
'GET /api/appointment/manage': (req: any, res: any) => {
|
||||
const { name, category, teacher, status, page = 1, pageSize = 10 } = req.query;
|
||||
|
||||
let filtered = [...manageAppointments];
|
||||
|
||||
if (name) {
|
||||
filtered = filtered.filter((item) => item.name.includes(name));
|
||||
}
|
||||
if (category) {
|
||||
filtered = filtered.filter((item) => item.category === category);
|
||||
}
|
||||
if (teacher) {
|
||||
filtered = filtered.filter((item) => item.teacher.includes(teacher));
|
||||
}
|
||||
if (status) {
|
||||
filtered = filtered.filter((item) => item.status === status);
|
||||
}
|
||||
|
||||
const total = filtered.length;
|
||||
const start = (Number(page) - 1) * Number(pageSize);
|
||||
const end = start + Number(pageSize);
|
||||
const list = filtered.slice(start, end);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
data: { list, total, page: Number(page), pageSize: Number(pageSize) },
|
||||
message: 'ok',
|
||||
});
|
||||
},
|
||||
|
||||
// 预约操作
|
||||
'POST /api/appointment/book': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const item = manageAppointments.find((a) => a.id === Number(id));
|
||||
|
||||
if (!item) {
|
||||
res.json({ code: 404, message: '预约项目不存在' });
|
||||
return;
|
||||
}
|
||||
if (item.status === 'ended') {
|
||||
res.json({ code: 400, message: '该项目已结束,不可预约' });
|
||||
return;
|
||||
}
|
||||
if (item.remaining <= 0) {
|
||||
res.json({ code: 400, message: '名额已满,不可预约' });
|
||||
return;
|
||||
}
|
||||
|
||||
item.remaining -= 1;
|
||||
res.json({ code: 0, message: '预约成功' });
|
||||
},
|
||||
|
||||
// 取消预约
|
||||
'POST /api/appointment/cancel': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const appointment = allAppointments.find((a) => a.id === Number(id));
|
||||
|
||||
if (!appointment) {
|
||||
res.json({ code: 404, message: '预约不存在' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 已完成的不能取消
|
||||
if (appointment.status === 'completed') {
|
||||
res.json({ code: 400, message: '已结束的预约不能取消' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 已取消的不能重复取消
|
||||
if (appointment.status === 'cancelled') {
|
||||
res.json({ code: 400, message: '该预约已取消' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有 booked 和 pending 可以取消
|
||||
appointment.status = 'cancelled';
|
||||
res.json({ code: 0, message: '取消成功' });
|
||||
},
|
||||
};
|
||||
215
mock/college.ts
Normal file
215
mock/college.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
// Mock data for college management
|
||||
const collegeTree = [
|
||||
{
|
||||
id: '1',
|
||||
name: '设计学院',
|
||||
depts: [
|
||||
{
|
||||
id: '1-1',
|
||||
name: '视觉传达设计系',
|
||||
majors: [
|
||||
{
|
||||
id: '1-1-1',
|
||||
name: '平面设计',
|
||||
classes: [
|
||||
{ id: '1-1-1-1', name: '平面设计1班' },
|
||||
{ id: '1-1-1-2', name: '平面设计2班' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '1-1-2',
|
||||
name: '数字媒体设计',
|
||||
classes: [
|
||||
{ id: '1-1-2-1', name: '数字媒体1班' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '1-2',
|
||||
name: '环境设计系',
|
||||
majors: [
|
||||
{
|
||||
id: '1-2-1',
|
||||
name: '室内设计',
|
||||
classes: [
|
||||
{ id: '1-2-1-1', name: '室内设计1班' },
|
||||
{ id: '1-2-1-2', name: '室内设计2班' },
|
||||
{ id: '1-2-1-3', name: '室内设计3班' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '管理学院',
|
||||
depts: [
|
||||
{
|
||||
id: '2-1',
|
||||
name: '工商管理系',
|
||||
majors: [
|
||||
{
|
||||
id: '2-1-1',
|
||||
name: '工商管理',
|
||||
classes: [
|
||||
{ id: '2-1-1-1', name: '工商管理1班' },
|
||||
{ id: '2-1-1-2', name: '工商管理2班' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2-1-2',
|
||||
name: '市场营销',
|
||||
classes: [
|
||||
{ id: '2-1-2-1', name: '市场营销1班' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2-2',
|
||||
name: '会计系',
|
||||
majors: [
|
||||
{
|
||||
id: '2-2-1',
|
||||
name: '财务管理',
|
||||
classes: [
|
||||
{ id: '2-2-1-1', name: '财务管理1班' },
|
||||
{ id: '2-2-1-2', name: '财务管理2班' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2-2-2',
|
||||
name: '审计学',
|
||||
classes: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '艺术学院',
|
||||
depts: [
|
||||
{
|
||||
id: '3-1',
|
||||
name: '音乐系',
|
||||
majors: [
|
||||
{
|
||||
id: '3-1-1',
|
||||
name: '声乐表演',
|
||||
classes: [
|
||||
{ id: '3-1-1-1', name: '声乐表演1班' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '信息工程学院',
|
||||
depts: [
|
||||
{
|
||||
id: '4-1',
|
||||
name: '计算机系',
|
||||
majors: [
|
||||
{
|
||||
id: '4-1-1',
|
||||
name: '软件工程',
|
||||
classes: [
|
||||
{ id: '4-1-1-1', name: '软件工程1班' },
|
||||
{ id: '4-1-1-2', name: '软件工程2班' },
|
||||
{ id: '4-1-1-3', name: '软件工程3班' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4-1-2',
|
||||
name: '计算机科学与技术',
|
||||
classes: [
|
||||
{ id: '4-1-2-1', name: '计科1班' },
|
||||
{ id: '4-1-2-2', name: '计科2班' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4-1-3',
|
||||
name: '人工智能',
|
||||
classes: [
|
||||
{ id: '4-1-3-1', name: 'AI1班' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4-2',
|
||||
name: '电子信息系',
|
||||
majors: [
|
||||
{
|
||||
id: '4-2-1',
|
||||
name: '通信工程',
|
||||
classes: [
|
||||
{ id: '4-2-1-1', name: '通信工程1班' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: '外国语学院',
|
||||
depts: [],
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
'GET /api/admin/college': (_req: any, res: any) => {
|
||||
res.json({
|
||||
code: 0,
|
||||
data: collegeTree,
|
||||
message: 'ok',
|
||||
});
|
||||
},
|
||||
|
||||
'POST /api/admin/college': (req: any, res: any) => {
|
||||
const { data } = req.body;
|
||||
if (!data || !Array.isArray(data)) {
|
||||
return res.json({ code: 40000, data: null, message: '参数错误' });
|
||||
}
|
||||
|
||||
// Validate: all names must be non-empty
|
||||
const validate = (items: any[], level: string): string | null => {
|
||||
for (const item of items) {
|
||||
if (!item.name || !item.name.trim()) {
|
||||
return `${level}名称不能为空`;
|
||||
}
|
||||
if (item.depts) {
|
||||
const err = validate(item.depts, '系');
|
||||
if (err) return err;
|
||||
}
|
||||
if (item.majors) {
|
||||
const err = validate(item.majors, '专业');
|
||||
if (err) return err;
|
||||
}
|
||||
if (item.classes) {
|
||||
const err = validate(item.classes, '班级');
|
||||
if (err) return err;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const err = validate(data, '学院');
|
||||
if (err) {
|
||||
return res.json({ code: 40001, data: null, message: err });
|
||||
}
|
||||
|
||||
console.log('[Mock] 保存学校管理数据, 学院数量:', data.length);
|
||||
res.json({
|
||||
code: 0,
|
||||
data: null,
|
||||
message: '保存成功',
|
||||
});
|
||||
},
|
||||
};
|
||||
42
mock/home.ts
Normal file
42
mock/home.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export default {
|
||||
// 获取首页轮播 Banner 列表
|
||||
'GET /api/home/banners': {
|
||||
code: 0,
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
title: '武汉高校校园招聘会',
|
||||
highlightText: '校园',
|
||||
subtitle: '科技无限 · 海量职位等你来',
|
||||
date: '活动时间:2026/01/01',
|
||||
imageUrl: 'https://test02-1302947942.cos.ap-shanghai.myqcloud.com/%E6%B3%BD%E6%9E%97-banner/banner.png',
|
||||
linkUrl: '/jobs?event=1',
|
||||
sort: 1,
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'AI助力职业发展',
|
||||
highlightText: '职业',
|
||||
subtitle: '智能匹配 · 精准推荐好工作',
|
||||
date: '平台持续更新中',
|
||||
imageUrl: 'https://test02-1302947942.cos.ap-shanghai.myqcloud.com/%E6%B3%BD%E6%9E%97-banner/banner.png',
|
||||
linkUrl: '/jobs',
|
||||
sort: 2,
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '春季校园专场招聘',
|
||||
highlightText: '专场',
|
||||
subtitle: '名企云集 · 优质岗位等你来',
|
||||
date: '活动时间:2026/03/15',
|
||||
imageUrl: 'https://test02-1302947942.cos.ap-shanghai.myqcloud.com/%E6%B3%BD%E6%9E%97-banner/banner.png',
|
||||
linkUrl: '/jobs?event=3',
|
||||
sort: 3,
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
message: 'ok',
|
||||
},
|
||||
};
|
||||
41
mock/overview.ts
Normal file
41
mock/overview.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
// 模拟数据总览数据
|
||||
const mockOverviewData = {
|
||||
aiInterviewUsage: 900,
|
||||
aiResumeUsage: 900,
|
||||
currentTasks: 0,
|
||||
completedPersons: 150,
|
||||
todayInterviewUsage: 10,
|
||||
todayResumeUsage: 8,
|
||||
};
|
||||
|
||||
export default {
|
||||
// 获取数据总览
|
||||
'GET /api/overview/data': (req: Request, res: Response) => {
|
||||
setTimeout(() => {
|
||||
res.json({
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: mockOverviewData,
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
|
||||
// 更新数据总览
|
||||
'POST /api/overview/update': (req: Request, res: Response) => {
|
||||
const { type, value } = req.body;
|
||||
|
||||
if (mockOverviewData.hasOwnProperty(type)) {
|
||||
(mockOverviewData as any)[type] = value;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
res.json({
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: mockOverviewData,
|
||||
});
|
||||
}, 300);
|
||||
},
|
||||
};
|
||||
18
mock/resume.ts
Normal file
18
mock/resume.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 简历相关 Mock
|
||||
*
|
||||
* 以下接口已接入真实后端 192.168.0.55:8080,通过 proxy 转发:
|
||||
* - GET /api/resume/student/templates 模板列表
|
||||
* - POST /api/resume/student/ai/generate AI生成简历
|
||||
*
|
||||
* 以下仅保留尚未对接真实接口的 mock。
|
||||
*/
|
||||
|
||||
const generatedResumes: any[] = [];
|
||||
|
||||
export default {
|
||||
// 获取我的简历列表(暂无真实接口)
|
||||
'GET /api/resume/mine': (_req: any, res: any) => {
|
||||
res.json({ code: 0, data: generatedResumes, message: 'ok' });
|
||||
},
|
||||
};
|
||||
320
mock/role.ts
Normal file
320
mock/role.ts
Normal file
@@ -0,0 +1,320 @@
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
// 模拟角色数据
|
||||
const roleList = [
|
||||
{
|
||||
id: '1',
|
||||
name: '超级管理员',
|
||||
description: '拥有系统所有权限',
|
||||
permissions: ['user_manage', 'role_manage', 'system_config', 'data_view'],
|
||||
status: 'active',
|
||||
createTime: '2024-01-01 10:00:00',
|
||||
updateTime: '2024-01-01 10:00:00',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '学院管理员',
|
||||
description: '管理学院相关事务',
|
||||
permissions: ['student_manage', 'staff_manage', 'data_view'],
|
||||
status: 'active',
|
||||
createTime: '2024-01-02 10:00:00',
|
||||
updateTime: '2024-01-02 10:00:00',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '教师',
|
||||
description: '教师角色,可查看学生信息',
|
||||
permissions: ['student_view', 'data_view'],
|
||||
status: 'active',
|
||||
createTime: '2024-01-03 10:00:00',
|
||||
updateTime: '2024-01-03 10:00:00',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '学生',
|
||||
description: '学生角色,基础权限',
|
||||
permissions: ['profile_view'],
|
||||
status: 'inactive',
|
||||
createTime: '2024-01-04 10:00:00',
|
||||
updateTime: '2024-01-04 10:00:00',
|
||||
},
|
||||
];
|
||||
|
||||
// 权限树数据
|
||||
const permissionTree = [
|
||||
{
|
||||
id: 'user_manage',
|
||||
name: '用户管理',
|
||||
code: 'user_manage',
|
||||
type: 'menu',
|
||||
children: [
|
||||
{
|
||||
id: 'student_manage',
|
||||
name: '学生管理',
|
||||
code: 'student_manage',
|
||||
type: 'menu',
|
||||
parentId: 'user_manage',
|
||||
},
|
||||
{
|
||||
id: 'staff_manage',
|
||||
name: '教职工管理',
|
||||
code: 'staff_manage',
|
||||
type: 'menu',
|
||||
parentId: 'user_manage',
|
||||
},
|
||||
{
|
||||
id: 'student_view',
|
||||
name: '学生查看',
|
||||
code: 'student_view',
|
||||
type: 'button',
|
||||
parentId: 'user_manage',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'role_manage',
|
||||
name: '角色管理',
|
||||
code: 'role_manage',
|
||||
type: 'menu',
|
||||
},
|
||||
{
|
||||
id: 'system_config',
|
||||
name: '系统配置',
|
||||
code: 'system_config',
|
||||
type: 'menu',
|
||||
children: [
|
||||
{
|
||||
id: 'banner_manage',
|
||||
name: '轮播图管理',
|
||||
code: 'banner_manage',
|
||||
type: 'menu',
|
||||
parentId: 'system_config',
|
||||
children: [
|
||||
{
|
||||
id: 'banner_add',
|
||||
name: '新增轮播图',
|
||||
code: 'banner_add',
|
||||
type: 'button',
|
||||
parentId: 'banner_manage',
|
||||
},
|
||||
{
|
||||
id: 'banner_edit',
|
||||
name: '编辑轮播图',
|
||||
code: 'banner_edit',
|
||||
type: 'button',
|
||||
parentId: 'banner_manage',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'security_config',
|
||||
name: '安全配置',
|
||||
code: 'security_config',
|
||||
type: 'menu',
|
||||
parentId: 'system_config',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'data_view',
|
||||
name: '数据查看',
|
||||
code: 'data_view',
|
||||
type: 'menu',
|
||||
},
|
||||
{
|
||||
id: 'profile_view',
|
||||
name: '个人信息查看',
|
||||
code: 'profile_view',
|
||||
type: 'button',
|
||||
},
|
||||
];
|
||||
|
||||
// 组织架构数据
|
||||
const organizationData = [
|
||||
{
|
||||
id: 'school_1',
|
||||
name: '学校管理',
|
||||
type: 'school',
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
id: 'school_2',
|
||||
name: '学校管理',
|
||||
type: 'school',
|
||||
expanded: false,
|
||||
},
|
||||
{
|
||||
id: 'dept_1',
|
||||
name: '编辑',
|
||||
type: 'department',
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
id: 'major_1',
|
||||
name: '批量导入',
|
||||
type: 'major',
|
||||
expanded: false,
|
||||
children: [
|
||||
{
|
||||
id: 'class_1',
|
||||
name: '批量导入',
|
||||
type: 'class',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'major_2',
|
||||
name: '新增',
|
||||
type: 'major',
|
||||
expanded: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'school_3',
|
||||
name: '角色管理',
|
||||
type: 'school',
|
||||
expanded: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// 获取角色列表
|
||||
export default {
|
||||
'GET /api/admin/role': (req: Request, res: Response) => {
|
||||
const { page = 1, pageSize = 10, name } = req.query;
|
||||
|
||||
let filteredList = [...roleList];
|
||||
|
||||
// 按名称筛选
|
||||
if (name) {
|
||||
filteredList = filteredList.filter(item =>
|
||||
item.name.includes(name as string)
|
||||
);
|
||||
}
|
||||
|
||||
const start = (Number(page) - 1) * Number(pageSize);
|
||||
const end = start + Number(pageSize);
|
||||
const list = filteredList.slice(start, end);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
message: 'success',
|
||||
data: {
|
||||
list,
|
||||
total: filteredList.length,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获取组织架构数据
|
||||
'GET /api/admin/organization': (req: Request, res: Response) => {
|
||||
res.json({
|
||||
code: 0,
|
||||
message: 'success',
|
||||
data: organizationData,
|
||||
});
|
||||
},
|
||||
|
||||
// 获取权限树
|
||||
'GET /api/admin/role/permissions': (req: Request, res: Response) => {
|
||||
res.json({
|
||||
code: 0,
|
||||
message: 'success',
|
||||
data: permissionTree,
|
||||
});
|
||||
},
|
||||
|
||||
// 创建角色
|
||||
'POST /api/admin/role/create': (req: Request, res: Response) => {
|
||||
const { name, description, permissions } = req.body;
|
||||
|
||||
const newRole = {
|
||||
id: String(Date.now()),
|
||||
name,
|
||||
description: description || '',
|
||||
permissions: permissions || [],
|
||||
status: 'active',
|
||||
createTime: new Date().toLocaleString('zh-CN'),
|
||||
updateTime: new Date().toLocaleString('zh-CN'),
|
||||
};
|
||||
|
||||
roleList.push(newRole);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
message: '创建成功',
|
||||
data: newRole,
|
||||
});
|
||||
},
|
||||
|
||||
// 更新角色
|
||||
'POST /api/admin/role/update': (req: Request, res: Response) => {
|
||||
const { id, name, description, permissions } = req.body;
|
||||
|
||||
const index = roleList.findIndex(item => item.id === id);
|
||||
if (index === -1) {
|
||||
return res.json({
|
||||
code: 1,
|
||||
message: '角色不存在',
|
||||
});
|
||||
}
|
||||
|
||||
roleList[index] = {
|
||||
...roleList[index],
|
||||
name,
|
||||
description: description || '',
|
||||
permissions: permissions || [],
|
||||
updateTime: new Date().toLocaleString('zh-CN'),
|
||||
};
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
message: '更新成功',
|
||||
data: roleList[index],
|
||||
});
|
||||
},
|
||||
|
||||
// 切换角色状态
|
||||
'POST /api/admin/role/toggle-status': (req: Request, res: Response) => {
|
||||
const { id } = req.body;
|
||||
|
||||
const role = roleList.find(item => item.id === id);
|
||||
if (!role) {
|
||||
return res.json({
|
||||
code: 1,
|
||||
message: '角色不存在',
|
||||
});
|
||||
}
|
||||
|
||||
role.status = role.status === 'active' ? 'inactive' : 'active';
|
||||
role.updateTime = new Date().toLocaleString('zh-CN');
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
message: '状态更新成功',
|
||||
data: role,
|
||||
});
|
||||
},
|
||||
|
||||
// 删除角色
|
||||
'POST /api/admin/role/delete': (req: Request, res: Response) => {
|
||||
const { id } = req.body;
|
||||
|
||||
const index = roleList.findIndex(item => item.id === id);
|
||||
if (index === -1) {
|
||||
return res.json({
|
||||
code: 1,
|
||||
message: '角色不存在',
|
||||
});
|
||||
}
|
||||
|
||||
roleList.splice(index, 1);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
message: '删除成功',
|
||||
});
|
||||
},
|
||||
};
|
||||
150
mock/staff.ts
Normal file
150
mock/staff.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
const familyNames = ['赵', '冯', '钱', '刘', '孙', '李', '周', '吴', '郑', '王', '陈', '杨', '黄', '张', '林', '何', '高', '马', '罗', '梁'];
|
||||
const givenNames = ['玉岚', '艺莲', '若蓉', '思钰', '婷雅', '恩铭', '浩然', '子涵', '欣怡', '梓萱', '宇轩', '雨桐', '佳琪', '思远', '天乐', '晓峰', '文博', '嘉欣', '雅静', '明辉'];
|
||||
|
||||
const colleges = ['设计学院', '管理学院', '艺术学院'];
|
||||
const deptMap: Record<string, string[]> = {
|
||||
'设计学院': ['视觉传达设计系', '环境设计系'],
|
||||
'管理学院': ['工商管理系', '会计系', '人力资源系'],
|
||||
'艺术学院': ['音乐系', '美术系'],
|
||||
};
|
||||
|
||||
const classMap: Record<string, string[]> = {
|
||||
'工商管理系': ['工商管理1班', '工商管理2班'],
|
||||
'会计系': ['财务管理1班', '财务管理2班'],
|
||||
'人力资源系': ['人力资源1班'],
|
||||
'视觉传达设计系': ['平面设计1班', '平面设计2班'],
|
||||
'环境设计系': ['室内设计1班', '室内设计2班', '室内设计3班'],
|
||||
'音乐系': ['声乐表演1班'],
|
||||
'美术系': ['国画1班', '油画1班'],
|
||||
};
|
||||
|
||||
const statuses: Array<'normal' | 'disabled'> = ['normal', 'normal', 'normal', 'normal', 'disabled'];
|
||||
|
||||
function randomPick<T>(arr: T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function maskPhone(phone: string) {
|
||||
return phone.substring(0, 3) + '****' + phone.substring(7);
|
||||
}
|
||||
|
||||
// 生成100条教职工数据
|
||||
const staffList: any[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const college = randomPick(colleges);
|
||||
const depts = deptMap[college] || [];
|
||||
const dept = randomPick(depts);
|
||||
const classes = classMap[dept] || [];
|
||||
const cls = classes.length > 0 ? randomPick(classes) : '';
|
||||
const status = randomPick(statuses);
|
||||
const phone = `1${randomPick(['39', '56', '78', '38', '59'])}${String(Math.floor(Math.random() * 100000000)).padStart(8, '0')}`;
|
||||
|
||||
staffList.push({
|
||||
id: String(i + 1),
|
||||
name: randomPick(familyNames) + randomPick(givenNames),
|
||||
phone,
|
||||
maskedPhone: maskPhone(phone),
|
||||
status,
|
||||
college,
|
||||
dept,
|
||||
className: cls,
|
||||
});
|
||||
}
|
||||
|
||||
// 学院树结构(用于左侧导航)
|
||||
const collegeTree = colleges.map((c) => ({
|
||||
id: c,
|
||||
name: c,
|
||||
children: (deptMap[c] || []).map((d) => ({
|
||||
id: d,
|
||||
name: d,
|
||||
children: (classMap[d] || []).map((cls) => ({
|
||||
id: cls,
|
||||
name: cls,
|
||||
})),
|
||||
})),
|
||||
}));
|
||||
|
||||
export default {
|
||||
'GET /api/admin/staff': (req: any, res: any) => {
|
||||
const { page = '1', pageSize = '10', name, phone, college, dept, className, status } = req.query;
|
||||
let filtered = [...staffList];
|
||||
if (name) filtered = filtered.filter((s) => s.name.includes(name));
|
||||
if (phone) filtered = filtered.filter((s) => s.phone.includes(phone));
|
||||
if (college) filtered = filtered.filter((s) => s.college === college);
|
||||
if (dept) filtered = filtered.filter((s) => s.dept === dept);
|
||||
if (className) filtered = filtered.filter((s) => s.className === className);
|
||||
if (status) filtered = filtered.filter((s) => s.status === status);
|
||||
|
||||
const p = Number(page);
|
||||
const ps = Number(pageSize);
|
||||
const start = (p - 1) * ps;
|
||||
res.json({ code: 0, data: { list: filtered.slice(start, start + ps), total: filtered.length, page: p, pageSize: ps }, message: 'ok' });
|
||||
},
|
||||
|
||||
'GET /api/admin/staff/college-tree': (_req: any, res: any) => {
|
||||
res.json({ code: 0, data: collegeTree, message: 'ok' });
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/add': (req: any, res: any) => {
|
||||
const { name, phone, college, dept, className } = req.body;
|
||||
if (!name || !phone) { res.json({ code: 40000, message: '请填写完整信息' }); return; }
|
||||
const newStaff = {
|
||||
id: String(staffList.length + 1000),
|
||||
name, phone, maskedPhone: maskPhone(phone), status: 'normal' as const,
|
||||
college: college || '', dept: dept || '', className: className || '',
|
||||
};
|
||||
staffList.unshift(newStaff);
|
||||
res.json({ code: 0, data: newStaff, message: '新增成功' });
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/edit': (req: any, res: any) => {
|
||||
const { id, name, phone, college, dept, className } = req.body;
|
||||
const staff = staffList.find((s) => s.id === id);
|
||||
if (!staff) { res.json({ code: 40000, message: '教职工不存在' }); return; }
|
||||
Object.assign(staff, { name, phone, maskedPhone: maskPhone(phone), college, dept, className });
|
||||
res.json({ code: 0, data: staff, message: '编辑成功' });
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/toggle-status': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const staff = staffList.find((s) => s.id === id);
|
||||
if (staff) { staff.status = staff.status === 'normal' ? 'disabled' : 'normal'; res.json({ code: 0, message: '操作成功' }); }
|
||||
else { res.json({ code: 40000, message: '教职工不存在' }); }
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/delete': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const idx = staffList.findIndex((s) => s.id === id);
|
||||
if (idx > -1) { staffList.splice(idx, 1); res.json({ code: 0, message: '删除成功' }); }
|
||||
else { res.json({ code: 40000, message: '教职工不存在' }); }
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/batch-delete': (req: any, res: any) => {
|
||||
const { ids } = req.body;
|
||||
if (!ids || !Array.isArray(ids) || ids.length === 0) { res.json({ code: 40000, message: '请选择要删除的教职工' }); return; }
|
||||
let count = 0;
|
||||
for (let i = staffList.length - 1; i >= 0; i--) { if (ids.includes(staffList[i].id)) { staffList.splice(i, 1); count++; } }
|
||||
res.json({ code: 0, data: { count }, message: `成功删除${count}名教职工` });
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/batch-import': (_req: any, res: any) => {
|
||||
const imported: any[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const college = randomPick(colleges);
|
||||
const dept = randomPick(deptMap[college] || []);
|
||||
const phone = `1${randomPick(['39', '56', '78', '38', '59'])}${String(Math.floor(Math.random() * 100000000)).padStart(8, '0')}`;
|
||||
const s = { id: String(Date.now() + i), name: randomPick(familyNames) + randomPick(givenNames), phone, maskedPhone: maskPhone(phone), status: 'normal' as const, college, dept, className: '' };
|
||||
staffList.unshift(s);
|
||||
imported.push(s);
|
||||
}
|
||||
res.json({ code: 0, data: { count: imported.length }, message: `成功导入${imported.length}名教职工` });
|
||||
},
|
||||
|
||||
'POST /api/admin/staff/reset-password': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const staff = staffList.find((s) => s.id === id);
|
||||
if (staff) { res.json({ code: 0, message: '密码已重置为 123456' }); }
|
||||
else { res.json({ code: 40000, message: '教职工不存在' }); }
|
||||
},
|
||||
};
|
||||
379
mock/student.ts
Normal file
379
mock/student.ts
Normal file
@@ -0,0 +1,379 @@
|
||||
const familyNames = ['赵', '冯', '钱', '刘', '孙', '李', '周', '吴', '郑', '王', '陈', '杨', '黄', '张', '林', '何', '高', '马', '罗', '梁'];
|
||||
const givenNames = ['玉岚', '艺莲', '若蓉', '思钰', '婷雅', '恩铭', '浩然', '子涵', '欣怡', '梓萱', '宇轩', '雨桐', '佳琪', '思远', '天乐', '晓峰', '文博', '嘉欣', '雅静', '明辉'];
|
||||
const colleges = ['电气化学院', '管理学院', '设计学院', '信息工程学院', '艺术学院', '外国语学院'];
|
||||
const deptMap: Record<string, string[]> = {
|
||||
'电气化学院': ['电力工程系', '电气工程系', '自动化系'],
|
||||
'管理学院': ['工商管理系', '会计系', '人力资源系'],
|
||||
'设计学院': ['视觉传达设计系', '环境设计系'],
|
||||
'信息工程学院': ['计算机系', '电子信息系'],
|
||||
'艺术学院': ['音乐系', '美术系'],
|
||||
'外国语学院': ['英语系', '日语系'],
|
||||
};
|
||||
const majorMap: Record<string, string[]> = {
|
||||
'电力工程系': ['电气化', '电力工程'],
|
||||
'电气工程系': ['电气工程', '智能电网'],
|
||||
'自动化系': ['自动化', '机器人工程'],
|
||||
'工商管理系': ['工商管理', '市场营销'],
|
||||
'会计系': ['财务管理', '审计学'],
|
||||
'人力资源系': ['人力资源管理'],
|
||||
'视觉传达设计系': ['平面设计', '数字媒体设计'],
|
||||
'环境设计系': ['室内设计', '景观设计'],
|
||||
'计算机系': ['软件工程', '计算机科学与技术', '人工智能'],
|
||||
'电子信息系': ['通信工程', '电子信息'],
|
||||
'音乐系': ['声乐表演', '器乐演奏'],
|
||||
'美术系': ['国画', '油画'],
|
||||
'英语系': ['英语', '翻译'],
|
||||
'日语系': ['日语'],
|
||||
};
|
||||
|
||||
const statuses: Array<'normal' | 'disabled'> = ['normal', 'normal', 'normal', 'normal', 'disabled'];
|
||||
const roles = ['学生'];
|
||||
const grades = ['2023级', '2024级', '2025级'];
|
||||
const classes = ['1班', '2班', '3班', '4班', '5班'];
|
||||
|
||||
// 年级管理数据
|
||||
const gradeList: any[] = [
|
||||
{ id: '1', name: '2025级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '2', name: '2026级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '3', name: '2024级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '4', name: '2023级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '5', name: '2022级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '6', name: '2021级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '7', name: '2020级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '8', name: '2025级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '9', name: '2025级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
{ id: '10', name: '2025级', createTime: '2025.09.01 16:01:30', graduated: false },
|
||||
];
|
||||
|
||||
// 设置一条数据为已毕业状态,用于"还原"操作演示
|
||||
gradeList[1].graduated = true;
|
||||
|
||||
function randomPick<T>(arr: T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function maskPhone(phone: string) {
|
||||
return phone.substring(0, 3) + '****' + phone.substring(7);
|
||||
}
|
||||
|
||||
// 生成100条学生数据
|
||||
const students: any[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const college = randomPick(colleges);
|
||||
const depts = deptMap[college] || [];
|
||||
const dept = randomPick(depts);
|
||||
const majors = majorMap[dept] || [];
|
||||
const major = randomPick(majors);
|
||||
const status = randomPick(statuses);
|
||||
const phone = `1${randomPick(['39', '56', '78', '38', '59'])}${String(Math.floor(Math.random() * 100000000)).padStart(8, '0')}`;
|
||||
|
||||
students.push({
|
||||
id: String(i + 1),
|
||||
name: randomPick(familyNames) + randomPick(givenNames),
|
||||
studentNo: String(178000 + Math.floor(Math.random() * 1000)),
|
||||
college,
|
||||
dept,
|
||||
major,
|
||||
className: `${Math.ceil(Math.random() * 3)}班`,
|
||||
grade: randomPick(grades),
|
||||
phone,
|
||||
maskedPhone: maskPhone(phone),
|
||||
role: randomPick(roles),
|
||||
updateDate: '2025.09.01',
|
||||
status,
|
||||
graduated: false,
|
||||
});
|
||||
}
|
||||
|
||||
// 生成100条已毕业学生数据
|
||||
const graduatedStudents: any[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const college = randomPick(colleges);
|
||||
const depts = deptMap[college] || [];
|
||||
const dept = randomPick(depts);
|
||||
const majors = majorMap[dept] || [];
|
||||
const major = randomPick(majors);
|
||||
const status = randomPick(statuses);
|
||||
const phone = `1${randomPick(['39', '56', '78', '38', '59'])}${String(Math.floor(Math.random() * 100000000)).padStart(8, '0')}`;
|
||||
|
||||
graduatedStudents.push({
|
||||
id: String(i + 2000),
|
||||
name: randomPick(familyNames) + randomPick(givenNames),
|
||||
studentNo: String(178000 + Math.floor(Math.random() * 1000)),
|
||||
college,
|
||||
dept,
|
||||
major,
|
||||
className: `${Math.ceil(Math.random() * 3)}班`,
|
||||
grade: randomPick(grades),
|
||||
phone,
|
||||
maskedPhone: maskPhone(phone),
|
||||
role: randomPick(roles),
|
||||
updateDate: '2025.09.01',
|
||||
status,
|
||||
graduated: true,
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
'GET /api/admin/student': (req: any, res: any) => {
|
||||
const {
|
||||
page = '1',
|
||||
pageSize = '10',
|
||||
graduated,
|
||||
studentNo,
|
||||
name,
|
||||
college,
|
||||
dept,
|
||||
major,
|
||||
className,
|
||||
status,
|
||||
} = req.query;
|
||||
|
||||
const source = graduated === 'true' ? graduatedStudents : students;
|
||||
let filtered = [...source];
|
||||
|
||||
if (studentNo) {
|
||||
filtered = filtered.filter((s) => s.studentNo.includes(studentNo));
|
||||
}
|
||||
if (name) {
|
||||
filtered = filtered.filter((s) => s.name.includes(name));
|
||||
}
|
||||
if (college) {
|
||||
filtered = filtered.filter((s) => s.college === college);
|
||||
}
|
||||
if (dept) {
|
||||
filtered = filtered.filter((s) => s.dept === dept);
|
||||
}
|
||||
if (major) {
|
||||
filtered = filtered.filter((s) => s.major === major);
|
||||
}
|
||||
if (className) {
|
||||
filtered = filtered.filter((s) => s.className === className);
|
||||
}
|
||||
if (status) {
|
||||
filtered = filtered.filter((s) => s.status === status);
|
||||
}
|
||||
|
||||
const p = Number(page);
|
||||
const ps = Number(pageSize);
|
||||
const start = (p - 1) * ps;
|
||||
const list = filtered.slice(start, start + ps);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
data: {
|
||||
list,
|
||||
total: filtered.length,
|
||||
page: p,
|
||||
pageSize: ps,
|
||||
},
|
||||
message: 'ok',
|
||||
});
|
||||
},
|
||||
|
||||
'POST /api/admin/student/toggle-status': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const student = students.find((s) => s.id === id) || graduatedStudents.find((s) => s.id === id);
|
||||
if (student) {
|
||||
student.status = student.status === 'normal' ? 'disabled' : 'normal';
|
||||
res.json({ code: 0, data: null, message: '操作成功' });
|
||||
} else {
|
||||
res.json({ code: 40000, data: null, message: '学生不存在' });
|
||||
}
|
||||
},
|
||||
|
||||
'POST /api/admin/student/delete': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
let idx = students.findIndex((s) => s.id === id);
|
||||
if (idx > -1) {
|
||||
students.splice(idx, 1);
|
||||
res.json({ code: 0, data: null, message: '删除成功' });
|
||||
return;
|
||||
}
|
||||
idx = graduatedStudents.findIndex((s) => s.id === id);
|
||||
if (idx > -1) {
|
||||
graduatedStudents.splice(idx, 1);
|
||||
res.json({ code: 0, data: null, message: '删除成功' });
|
||||
return;
|
||||
}
|
||||
res.json({ code: 40000, data: null, message: '学生不存在' });
|
||||
},
|
||||
|
||||
'POST /api/admin/student/batch-delete': (req: any, res: any) => {
|
||||
const { ids, graduated } = req.body;
|
||||
if (!ids || !Array.isArray(ids) || ids.length === 0) {
|
||||
res.json({ code: 40000, data: null, message: '请选择要删除的学生' });
|
||||
return;
|
||||
}
|
||||
const source = graduated ? graduatedStudents : students;
|
||||
let count = 0;
|
||||
for (let i = source.length - 1; i >= 0; i--) {
|
||||
if (ids.includes(source[i].id)) {
|
||||
source.splice(i, 1);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
res.json({ code: 0, data: { count }, message: `成功删除${count}名学生` });
|
||||
},
|
||||
|
||||
'POST /api/admin/student/batch-import': (req: any, res: any) => {
|
||||
// 模拟批量导入5名学生
|
||||
const imported: any[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const college = randomPick(colleges);
|
||||
const depts = deptMap[college] || [];
|
||||
const dept = randomPick(depts);
|
||||
const majors = majorMap[dept] || [];
|
||||
const major = randomPick(majors);
|
||||
const phone = `1${randomPick(['39', '56', '78', '38', '59'])}${String(Math.floor(Math.random() * 100000000)).padStart(8, '0')}`;
|
||||
const newStudent = {
|
||||
id: String(Date.now() + i),
|
||||
name: randomPick(familyNames) + randomPick(givenNames),
|
||||
studentNo: String(178000 + Math.floor(Math.random() * 1000)),
|
||||
college,
|
||||
dept,
|
||||
major,
|
||||
className: `${Math.ceil(Math.random() * 3)}班`,
|
||||
grade: randomPick(grades),
|
||||
phone,
|
||||
maskedPhone: maskPhone(phone),
|
||||
role: '学生',
|
||||
updateDate: new Date().toISOString().slice(0, 10).replace(/-/g, '.'),
|
||||
status: 'normal' as const,
|
||||
graduated: false,
|
||||
};
|
||||
students.unshift(newStudent);
|
||||
imported.push(newStudent);
|
||||
}
|
||||
res.json({ code: 0, data: { count: imported.length }, message: `成功导入${imported.length}名学生` });
|
||||
},
|
||||
|
||||
'POST /api/admin/student/reset-password': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const student = students.find((s) => s.id === id) || graduatedStudents.find((s) => s.id === id);
|
||||
if (student) {
|
||||
console.log(`[Mock] 重置学生 ${student.name} 密码`);
|
||||
res.json({ code: 0, data: null, message: '密码已重置为 123456' });
|
||||
} else {
|
||||
res.json({ code: 40000, data: null, message: '学生不存在' });
|
||||
}
|
||||
},
|
||||
|
||||
'POST /api/admin/student/edit': (req: any, res: any) => {
|
||||
const { id, name, studentNo, role, college, dept, major, className, grade, phone } = req.body;
|
||||
const student = students.find((s) => s.id === id) || graduatedStudents.find((s) => s.id === id);
|
||||
if (!student) {
|
||||
res.json({ code: 40000, data: null, message: '学生不存在' });
|
||||
return;
|
||||
}
|
||||
if (!name || !studentNo || !role || !college || !dept || !major || !className || !grade) {
|
||||
res.json({ code: 40000, data: null, message: '请填写完整信息' });
|
||||
return;
|
||||
}
|
||||
Object.assign(student, {
|
||||
name, studentNo, role, college, dept, major, className, grade,
|
||||
phone: phone || '',
|
||||
maskedPhone: phone ? phone.substring(0, 3) + '****' + phone.substring(7) : '',
|
||||
updateDate: new Date().toISOString().slice(0, 10).replace(/-/g, '.'),
|
||||
});
|
||||
res.json({ code: 0, data: student, message: '编辑成功' });
|
||||
},
|
||||
|
||||
'POST /api/admin/student/add': (req: any, res: any) => {
|
||||
const { name, studentNo, role, college, dept, major, className, grade, phone } = req.body;
|
||||
if (!name || !studentNo || !role || !college || !dept || !major || !className || !grade) {
|
||||
res.json({ code: 40000, data: null, message: '请填写完整信息' });
|
||||
return;
|
||||
}
|
||||
const newStudent = {
|
||||
id: String(students.length + 1000),
|
||||
name,
|
||||
studentNo,
|
||||
college,
|
||||
dept,
|
||||
major,
|
||||
className,
|
||||
grade,
|
||||
phone: phone || '',
|
||||
maskedPhone: phone ? phone.substring(0, 3) + '****' + phone.substring(7) : '',
|
||||
role,
|
||||
updateDate: new Date().toISOString().slice(0, 10).replace(/-/g, '.'),
|
||||
status: 'normal' as const,
|
||||
};
|
||||
students.unshift(newStudent);
|
||||
res.json({ code: 0, data: newStudent, message: '新增成功' });
|
||||
},
|
||||
|
||||
'GET /api/admin/student/colleges': (_req: any, res: any) => {
|
||||
res.json({
|
||||
code: 0,
|
||||
data: {
|
||||
colleges,
|
||||
deptMap,
|
||||
majorMap,
|
||||
classes,
|
||||
grades,
|
||||
roles,
|
||||
},
|
||||
message: 'ok',
|
||||
});
|
||||
},
|
||||
|
||||
// 年级管理接口
|
||||
'GET /api/admin/grade': (req: any, res: any) => {
|
||||
const { page = '1', pageSize = '10', name } = req.query;
|
||||
let filtered = [...gradeList];
|
||||
if (name) {
|
||||
filtered = filtered.filter((g: any) => g.name.includes(name));
|
||||
}
|
||||
const p = Number(page);
|
||||
const ps = Number(pageSize);
|
||||
const start = (p - 1) * ps;
|
||||
const list = filtered.slice(start, start + ps);
|
||||
res.json({ code: 0, data: { list, total: filtered.length, page: p, pageSize: ps }, message: 'ok' });
|
||||
},
|
||||
|
||||
'POST /api/admin/grade/add': (req: any, res: any) => {
|
||||
const { name } = req.body;
|
||||
const newGrade = {
|
||||
id: String(gradeList.length + 1),
|
||||
name,
|
||||
createTime: new Date().toISOString().replace('T', ' ').slice(0, 19),
|
||||
};
|
||||
gradeList.unshift(newGrade);
|
||||
res.json({ code: 0, data: newGrade, message: '新增成功' });
|
||||
},
|
||||
|
||||
'POST /api/admin/grade/edit': (req: any, res: any) => {
|
||||
const { id, name } = req.body;
|
||||
const item = gradeList.find((g: any) => g.id === id);
|
||||
if (item) {
|
||||
item.name = name;
|
||||
res.json({ code: 0, data: item, message: '编辑成功' });
|
||||
} else {
|
||||
res.json({ code: 1, message: '未找到该年级' });
|
||||
}
|
||||
},
|
||||
|
||||
'POST /api/admin/grade/graduate': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const idx = gradeList.findIndex((g: any) => g.id === id);
|
||||
if (idx >= 0) {
|
||||
gradeList[idx].graduated = true;
|
||||
res.json({ code: 0, message: '转毕业成功' });
|
||||
} else {
|
||||
res.json({ code: 1, message: '未找到该年级' });
|
||||
}
|
||||
},
|
||||
|
||||
'POST /api/admin/grade/restore': (req: any, res: any) => {
|
||||
const { id } = req.body;
|
||||
const idx = gradeList.findIndex((g: any) => g.id === id);
|
||||
if (idx >= 0) {
|
||||
gradeList[idx].graduated = false;
|
||||
res.json({ code: 0, message: '还原成功' });
|
||||
} else {
|
||||
res.json({ code: 1, message: '未找到该年级' });
|
||||
}
|
||||
},
|
||||
};
|
||||
156
mock/user.ts
Normal file
156
mock/user.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 用户相关 Mock
|
||||
*
|
||||
* 认证接口(登录、验证码登录、发送验证码)已接入真实后端 192.168.0.55:8080,
|
||||
* 通过 .umirc.ts 的 proxy 代理转发,不再 mock。
|
||||
*
|
||||
* 以下仅保留尚未对接真实接口的 mock。
|
||||
*/
|
||||
|
||||
const mockUsers = [
|
||||
{
|
||||
userId: 1,
|
||||
username: 'admin',
|
||||
realName: '管理员',
|
||||
phone: '13800138000',
|
||||
avatar: null,
|
||||
userType: 1,
|
||||
password: '123456',
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
username: 'teacher001',
|
||||
realName: '李老师',
|
||||
phone: '13800138002',
|
||||
avatar: null,
|
||||
userType: 2,
|
||||
password: '123456',
|
||||
},
|
||||
{
|
||||
userId: 3,
|
||||
username: 'student001',
|
||||
realName: '张三',
|
||||
phone: '13800138001',
|
||||
avatar: null,
|
||||
userType: 3,
|
||||
password: '123456',
|
||||
},
|
||||
];
|
||||
|
||||
let nextUserId = 4;
|
||||
|
||||
const generateToken = () =>
|
||||
'eyJhbGciOiJIUzI1NiJ9.mock-' + Date.now() + '-' + Math.random().toString(36).slice(2);
|
||||
|
||||
const toUserData = (user: (typeof mockUsers)[0], token: string) => ({
|
||||
userId: user.userId,
|
||||
username: user.username,
|
||||
realName: user.realName,
|
||||
phone: user.phone,
|
||||
avatar: user.avatar,
|
||||
userType: user.userType,
|
||||
token,
|
||||
});
|
||||
|
||||
export default {
|
||||
// 验证重置密码验证码(暂无真实接口)
|
||||
'POST /api/auth/verifyResetCode': (req: any, res: any) => {
|
||||
const { phone, code } = req.body;
|
||||
|
||||
if (!phone || !code) {
|
||||
return res.json({ code: 40000, data: null, message: '请求参数错误' });
|
||||
}
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
return res.json({ code: 40000, data: null, message: '手机号格式不正确' });
|
||||
}
|
||||
|
||||
const user = mockUsers.find((u) => u.phone === phone);
|
||||
if (!user) {
|
||||
return res.json({ code: 40000, data: null, message: '该手机号未注册' });
|
||||
}
|
||||
|
||||
if (code !== '123456') {
|
||||
return res.json({ code: 40000, data: null, message: '验证码错误或已过期' });
|
||||
}
|
||||
|
||||
console.log(`[Mock] 验证码验证通过: ${phone}`);
|
||||
res.json({ code: 0, data: true, message: 'ok' });
|
||||
},
|
||||
|
||||
// 重置密码(暂无真实接口)
|
||||
'POST /api/auth/resetPassword': (req: any, res: any) => {
|
||||
const { phone, code, newPassword } = req.body;
|
||||
|
||||
if (!phone || !code || !newPassword) {
|
||||
return res.json({ code: 40000, data: null, message: '请求参数错误' });
|
||||
}
|
||||
|
||||
if (code !== '123456') {
|
||||
return res.json({ code: 40000, data: null, message: '验证码错误或已过期' });
|
||||
}
|
||||
|
||||
const user = mockUsers.find((u) => u.phone === phone);
|
||||
if (!user) {
|
||||
return res.json({ code: 40000, data: null, message: '该手机号未注册' });
|
||||
}
|
||||
|
||||
if (newPassword.length < 6 || newPassword.length > 20) {
|
||||
return res.json({ code: 40000, data: null, message: '密码长度需为6-20位' });
|
||||
}
|
||||
|
||||
user.password = newPassword;
|
||||
console.log(`[Mock] 用户 ${user.username} 密码已重置`);
|
||||
res.json({ code: 0, data: true, message: 'ok' });
|
||||
},
|
||||
|
||||
// 用户注册(暂无真实接口)
|
||||
'POST /api/auth/register': (req: any, res: any) => {
|
||||
const { username, password, confirmPassword, realName, phone, email, userType } = req.body;
|
||||
|
||||
if (!username || !password || !confirmPassword || !realName || !phone) {
|
||||
return res.json({ code: 40000, data: null, message: '请求参数错误' });
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_]{4,20}$/.test(username)) {
|
||||
return res.json({ code: 40000, data: null, message: '用户名只能包含字母、数字、下划线' });
|
||||
}
|
||||
|
||||
if (password.length < 6 || password.length > 20) {
|
||||
return res.json({ code: 40000, data: null, message: '密码长度需为6-20位' });
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
return res.json({ code: 40000, data: null, message: '两次密码不一致' });
|
||||
}
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
return res.json({ code: 40000, data: null, message: '手机号格式不正确' });
|
||||
}
|
||||
|
||||
if (mockUsers.find((u) => u.username === username)) {
|
||||
return res.json({ code: 40000, data: null, message: '用户名已存在' });
|
||||
}
|
||||
|
||||
if (mockUsers.find((u) => u.phone === phone)) {
|
||||
return res.json({ code: 40000, data: null, message: '手机号已注册' });
|
||||
}
|
||||
|
||||
const newUser = {
|
||||
userId: nextUserId++,
|
||||
username,
|
||||
realName,
|
||||
phone,
|
||||
avatar: null,
|
||||
userType: userType || 3,
|
||||
password,
|
||||
};
|
||||
mockUsers.push(newUser);
|
||||
|
||||
res.json({
|
||||
code: 0,
|
||||
data: toUserData(newUser, generateToken()),
|
||||
message: 'ok',
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user