commit
build / build (push) Successful in 5m20s

This commit is contained in:
wanghu
2026-10-13 10:47:07 +08:00
parent 90bf259210
commit 15762faeac
518 changed files with 34857 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
import { Method } from "alova";
import { alovaInstance } from "@/utils/alova/alova-iview";
import conf from "@/assets/config/host.config";
import type {
ClassTreeNode,
EffectiveSettingVo,
ModNode,
MyClassVo,
MyTimetableVo,
NowYearTermVo,
SettingGetVo,
SubjectVo,
TemplateSaveRequest,
TimeSettingVo,
TimetableSaveRequest,
TimetableVo,
UpFileResultVo,
UserInfoVo,
} from "@/types/syllabus";
/**
* 课程表(AcaSyllabu)接口封装。
*
* 前缀说明:conf.serviceHost 本地为空(走 vite 同源代理),线上为基础平台域名;
* platform 固定使用 "api",与后端 Config.API_PATH 中的 api 匹配。
* 统一响应 {code,msg,data} 由 alova-iview 解包为 data(code==0),页面直接使用返回值。
*
* 约定:
* - postJSON 用于 @RequestBody 接口;postForm 用于 @RequestParam 接口(表单提交)。
* - postForm 同时把参数拼到 URL query:后端 @RequestParam 都收得到,且本地
* vite-plugin-mock 不解析 urlencoded body,靠 query 才能拿到参数(不影响线上)。
*/
const BASE = "/api/base";
const FILE = "/api/file";
const TIMETABLE = "/api/timetable";
const SETTING = "/api/setting";
/** GET 请求,T 为返回数据类型 */
function get<T = any>(url: string, params?: Record<string, any>): Method<T> {
return new Method<T>("GET", alovaInstance, url, { params });
}
/** POST JSON body(与后端 @RequestBody 对齐),T 为返回数据类型 */
function postJSON<T = any>(url: string, data?: Record<string, any> | any[]): Method<T> {
return new Method<T>(
"POST",
alovaInstance,
url,
{ headers: { "Content-Type": "application/json;charset=UTF-8" } },
data ? JSON.stringify(data) : undefined
);
}
/** POST 表单(与后端 @RequestParam 对齐,application/x-www-form-urlencoded),T 为返回数据类型 */
function postForm<T = any>(url: string, data?: Record<string, any>): Method<T> {
const pairs = data ? Object.entries(data).filter(([, v]) => v !== undefined && v !== null) : [];
const form = pairs.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
// B1 修复:mock 只解析 query,故仅 mock 模式把参数拼进 URL;真后端只发 body,
// 否则 Spring @RequestParam 会把 query 与 body 同名值以逗号合并(单值变 "X,X"、数字变 "1,1" 直接 500)。
const fullUrl = conf.isMock && form ? `${url}${url.includes("?") ? "&" : "?"}${form}` : url;
return new Method<T>(
"POST",
alovaInstance,
fullUrl,
{ headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" } },
form || undefined
);
}
/** 附件下载直链(浏览器 location.href 跳转) */
function fileDownUrl(fileId: number): string {
return `${conf.serviceHost}${FILE}/Down?id=${fileId}`;
}
/** 附件在线预览直链(照片课表展示用) */
export function filePreviewUrl(fileId: number): string {
return `${conf.serviceHost}${FILE}/Preview?id=${fileId}`;
}
/** 照片课表上传地址(FormData 字段名 file) */
export function fileUploadUrl(): string {
return `${conf.serviceHost}${FILE}/Upload`;
}
class BaseApi {
// ========== 用户信息 & 模块权限 ==========
/** 获取当前登录用户信息 */
getUserInfo() {
return get<UserInfoVo>(`${BASE}/GetUserInfo`);
}
/** 获取当前用户已授权的模块列表 */
getUserMod() {
return get<ModNode[]>(`${BASE}/GetUserMod`);
}
/** 当前学校组织树(部门→教师) */
getSchoolAll() {
return get<any[]>(`${BASE}/GetSchoolAll`);
}
/** 年级→班级树(叶子=班级,附学生数;课程表按班级维护) */
getClassTree() {
return get<ClassTreeNode[]>(`${BASE}/GetClassTree`);
}
/** 端分离入口:切换当前视角(teacher / parent) */
setRole(as: "teacher" | "parent") {
return postJSON<{ role: string }>(`${BASE}/SetRole`, { as });
}
}
class SyllabusApi {
// ========== 电子课程表 ==========
/** 查询班级课程表(无数据时返回空节次,前端补默认 10 节) */
get(classId: number) {
return get<TimetableVo>(`${TIMETABLE}/Get`, { classId });
}
/** 整表保存电子课程表(全量替换节次),返回课表ID */
save(data: TimetableSaveRequest) {
return postJSON<number>(`${TIMETABLE}/Save`, data);
}
// ========== 照片课程表 ==========
/** 绑定/替换照片课表(fileId 为已上传附件ID) */
savePhoto(classId: number, fileId: number) {
return postForm<void>(`${TIMETABLE}/SavePhoto`, { classId, fileId });
}
/** 移除照片课表 */
removePhoto(classId: number) {
return postForm<void>(`${TIMETABLE}/RemovePhoto`, { classId });
}
// ========== 学科 ==========
/** 本校学科列表(学科下拉数据源;不可达时返回空列表) */
subjects() {
return get<SubjectVo[]>(`${TIMETABLE}/Subjects`);
}
// ========== 学年学期 ==========
/** 当前学年学期(课表按学年学期维护,页头展示用;班级服务不可达时报错) */
nowYearTerm() {
return get<NowYearTermVo>(`${TIMETABLE}/NowYearTerm`);
}
// ========== 我的课表(按角色过滤的只读视图) ==========
/** 可查看班级列表(管理员/校领导=全部;班主任=所带;任教=任教班级) */
myClasses() {
return get<MyClassVo[]>(`${TIMETABLE}/MyClasses`);
}
/** 班级课表(权限校验;任教老师只返回任教科目格) */
myGet(classId: number) {
return get<MyTimetableVo>(`${TIMETABLE}/MyGet`, { classId });
}
// ========== 应用设置(时段判定 + 节次模板) ==========
/** 查询本校应用设置(时间设置 + 全部节次模板含行) */
getSetting() {
return get<SettingGetVo>(`${SETTING}/Get`);
}
/**
* 保存时段判定时间设置(四边界 HH:mm 依次递增)。
* 页面已无调用:启用时段与节次/课间时长自 2026-10-12 起随模板保存(见 saveTemplate),
* 界面也不再维护四个边界;保留此方法仅为后端接口仍然可用。
*/
saveTimeSetting(data: TimeSettingVo) {
return postJSON<void>(`${SETTING}/SaveTime`, data);
}
/** 新建/更新节次模板(全量替换行),返回模板ID */
saveTemplate(data: TemplateSaveRequest) {
return postJSON<number>(`${SETTING}/SaveTemplate`, data);
}
/** 删除节次模板(连带行) */
deleteTemplate(id: number) {
return postForm<void>(`${SETTING}/DeleteTemplate`, { id });
}
/** 课表编辑页生效设置:时间边界 + 当前学期下指定年级启用模板的行(term 1=上学期 2=下学期) */
effectiveForGrade(gradeName: string, term?: number) {
return get<EffectiveSettingVo>(`${SETTING}/ForGrade`, { gradeName, term });
}
// ========== 附件 ==========
fileDownUrl = fileDownUrl;
}
export const baseApi = new BaseApi();
export const syllabusApi = new SyllabusApi();
export type { UpFileResultVo };
export default syllabusApi;