2026-03-23 10:48:09 +08:00
|
|
|
|
import { getReadingPlatformAPI } from "./generated";
|
|
|
|
|
|
import { axios, customMutator } from "./generated/mutator";
|
2026-03-13 13:48:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建 API 实例
|
|
|
|
|
|
const api = getReadingPlatformAPI();
|
2026-03-12 13:05:20 +08:00
|
|
|
|
|
2026-03-19 10:32:58 +08:00
|
|
|
|
// 封装 http 方法(兼容原有代码)
|
|
|
|
|
|
export const http = {
|
2026-03-23 10:48:09 +08:00
|
|
|
|
get: <T = any>(url: string, config?: any) =>
|
|
|
|
|
|
customMutator<T>({ url, method: "get", ...config }),
|
|
|
|
|
|
post: <T = any>(url: string, data?: any, config?: any) =>
|
|
|
|
|
|
customMutator<T>({ url, method: "post", data, ...config }),
|
|
|
|
|
|
put: <T = any>(url: string, data?: any, config?: any) =>
|
|
|
|
|
|
customMutator<T>({ url, method: "put", data, ...config }),
|
|
|
|
|
|
delete: <T = any>(url: string, config?: any) =>
|
|
|
|
|
|
customMutator<T>({ url, method: "delete", ...config }),
|
2026-03-19 10:32:58 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-12 13:05:20 +08:00
|
|
|
|
// ============= 类型定义(保持向后兼容) =============
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export interface CourseQueryParams {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
pageSize?: number;
|
|
|
|
|
|
grade?: string;
|
2026-03-23 10:48:09 +08:00
|
|
|
|
gradeTags?: string; // 年级筛选(逗号分隔,如 "小班,中班,大班")
|
2026-02-26 15:22:26 +08:00
|
|
|
|
status?: string;
|
|
|
|
|
|
keyword?: string;
|
2026-03-16 11:08:27 +08:00
|
|
|
|
/** 审核管理页专用:仅返回待审核和已驳回,排除已通过 */
|
|
|
|
|
|
reviewOnly?: boolean;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface Course {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
pictureBookName?: string;
|
|
|
|
|
|
grades: string[];
|
|
|
|
|
|
status: string;
|
|
|
|
|
|
version: string;
|
|
|
|
|
|
usageCount: number;
|
|
|
|
|
|
teacherCount: number;
|
|
|
|
|
|
avgRating: number;
|
|
|
|
|
|
createdAt: Date;
|
2026-02-28 16:41:39 +08:00
|
|
|
|
updatedAt: Date;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
submittedAt?: Date;
|
|
|
|
|
|
reviewedAt?: Date;
|
|
|
|
|
|
reviewComment?: string;
|
2026-02-28 16:41:39 +08:00
|
|
|
|
// 新增字段
|
|
|
|
|
|
themeId?: number;
|
|
|
|
|
|
theme?: { id: number; name: string };
|
2026-03-23 16:26:10 +08:00
|
|
|
|
themeName?: string;
|
2026-02-28 16:41:39 +08:00
|
|
|
|
coreContent?: string;
|
|
|
|
|
|
coverImagePath?: string;
|
|
|
|
|
|
domainTags?: string[];
|
|
|
|
|
|
gradeTags?: string[];
|
|
|
|
|
|
duration?: number;
|
|
|
|
|
|
// 课程介绍字段
|
|
|
|
|
|
introSummary?: string;
|
|
|
|
|
|
introHighlights?: string;
|
|
|
|
|
|
introGoals?: string;
|
|
|
|
|
|
introSchedule?: string;
|
|
|
|
|
|
introKeyPoints?: string;
|
|
|
|
|
|
introMethods?: string;
|
|
|
|
|
|
introEvaluation?: string;
|
|
|
|
|
|
introNotes?: string;
|
|
|
|
|
|
// 排课计划参考
|
|
|
|
|
|
scheduleRefData?: string;
|
|
|
|
|
|
// 环创建设
|
|
|
|
|
|
environmentConstruction?: string;
|
|
|
|
|
|
// 关联课程
|
|
|
|
|
|
courseLessons?: CourseLesson[];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface CourseLesson {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
courseId: number;
|
|
|
|
|
|
lessonType: string;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
duration: number;
|
|
|
|
|
|
videoPath?: string;
|
|
|
|
|
|
videoName?: string;
|
|
|
|
|
|
pptPath?: string;
|
|
|
|
|
|
pptName?: string;
|
|
|
|
|
|
pdfPath?: string;
|
|
|
|
|
|
pdfName?: string;
|
|
|
|
|
|
objectives?: string;
|
|
|
|
|
|
preparation?: string;
|
|
|
|
|
|
extension?: string;
|
|
|
|
|
|
reflection?: string;
|
|
|
|
|
|
assessmentData?: string;
|
|
|
|
|
|
useTemplate: boolean;
|
|
|
|
|
|
sortOrder: number;
|
|
|
|
|
|
steps?: LessonStep[];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface LessonStep {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
lessonId: number;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
content?: string;
|
|
|
|
|
|
duration: number;
|
|
|
|
|
|
objective?: string;
|
|
|
|
|
|
resourceIds?: string;
|
|
|
|
|
|
sortOrder: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface ValidationResult {
|
|
|
|
|
|
valid: boolean;
|
|
|
|
|
|
errors: ValidationError[];
|
|
|
|
|
|
warnings: ValidationWarning[];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface ValidationError {
|
|
|
|
|
|
field: string;
|
|
|
|
|
|
message: string;
|
|
|
|
|
|
code: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface ValidationWarning {
|
|
|
|
|
|
field: string;
|
|
|
|
|
|
message: string;
|
|
|
|
|
|
code: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 13:05:20 +08:00
|
|
|
|
// ============= API 函数(使用生成的客户端) =============
|
|
|
|
|
|
|
|
|
|
|
|
// 转换查询参数类型
|
2026-03-13 13:48:28 +08:00
|
|
|
|
const toFindAllParams = (params: CourseQueryParams): any => ({
|
2026-03-16 11:08:27 +08:00
|
|
|
|
pageNum: params.pageNum ?? 1,
|
|
|
|
|
|
pageSize: params.pageSize ?? 10,
|
2026-03-12 13:05:20 +08:00
|
|
|
|
keyword: params.keyword,
|
2026-03-16 11:08:27 +08:00
|
|
|
|
category: params.grade,
|
2026-03-23 10:48:09 +08:00
|
|
|
|
gradeTags: params.gradeTags, // 年级筛选(逗号分隔)
|
2026-03-16 11:08:27 +08:00
|
|
|
|
status: params.status || undefined,
|
|
|
|
|
|
reviewOnly: params.reviewOnly,
|
2026-03-12 13:05:20 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-16 11:08:27 +08:00
|
|
|
|
// 后端 PageResult 返回 list,前端统一转为 items
|
|
|
|
|
|
function normalizePageResult(raw: any): {
|
|
|
|
|
|
items: Course[];
|
|
|
|
|
|
total: number;
|
|
|
|
|
|
page: number;
|
|
|
|
|
|
pageSize: number;
|
|
|
|
|
|
} {
|
|
|
|
|
|
const list = raw?.list ?? raw?.items ?? [];
|
|
|
|
|
|
return {
|
|
|
|
|
|
items: Array.isArray(list) ? list : [],
|
|
|
|
|
|
total: raw?.total ?? 0,
|
|
|
|
|
|
page: raw?.pageNum ?? raw?.page ?? 1,
|
|
|
|
|
|
pageSize: raw?.pageSize ?? 10,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-19 09:34:54 +08:00
|
|
|
|
// 获取课程包列表(7 步流程创建的教学资源)
|
|
|
|
|
|
// 注意:这里的 Course 实际对应后端的 CoursePackage(课程包)
|
2026-03-23 10:48:09 +08:00
|
|
|
|
// 使用扁平 query 参数,确保 gradeTags(适用年级)正确传给后端
|
2026-02-26 15:22:26 +08:00
|
|
|
|
export function getCourses(params: CourseQueryParams): Promise<{
|
|
|
|
|
|
items: Course[];
|
|
|
|
|
|
total: number;
|
|
|
|
|
|
page: number;
|
|
|
|
|
|
pageSize: number;
|
|
|
|
|
|
}> {
|
2026-03-23 10:48:09 +08:00
|
|
|
|
const flatParams = toFindAllParams(params);
|
|
|
|
|
|
return http
|
|
|
|
|
|
.get<{ list?: Course[]; items?: Course[]; total?: number; pageNum?: number; pageSize?: number }>(
|
|
|
|
|
|
"/v1/admin/packages",
|
|
|
|
|
|
{ params: flatParams }
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(normalizePageResult) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 11:08:27 +08:00
|
|
|
|
// 获取审核列表(仅返回待审核和已驳回,不含已通过)
|
2026-02-26 15:22:26 +08:00
|
|
|
|
export function getReviewList(params: CourseQueryParams): Promise<{
|
|
|
|
|
|
items: Course[];
|
|
|
|
|
|
total: number;
|
|
|
|
|
|
page: number;
|
|
|
|
|
|
pageSize: number;
|
|
|
|
|
|
}> {
|
2026-03-23 10:48:09 +08:00
|
|
|
|
const flatParams = toFindAllParams({ ...params, reviewOnly: true });
|
|
|
|
|
|
return http
|
|
|
|
|
|
.get<{ list?: Course[]; items?: Course[]; total?: number; pageNum?: number; pageSize?: number }>(
|
|
|
|
|
|
"/v1/admin/packages",
|
|
|
|
|
|
{ params: flatParams }
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(normalizePageResult) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 14:13:59 +08:00
|
|
|
|
// 获取课程包详情(id 支持 number | string,避免大整数精度丢失)
|
|
|
|
|
|
export function getCourse(id: number | string): Promise<any> {
|
|
|
|
|
|
return api.getCourse1(id as number) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建课程包
|
|
|
|
|
|
export function createCourse(data: any): Promise<any> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return api.createCourse(data) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新课程包
|
2026-03-16 14:13:59 +08:00
|
|
|
|
export function updateCourse(id: number | string, data: any): Promise<any> {
|
|
|
|
|
|
return api.updateCourse(id as number, data) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除课程包
|
2026-03-16 14:13:59 +08:00
|
|
|
|
export function deleteCourse(id: number | string): Promise<any> {
|
|
|
|
|
|
return api.deleteCourse(id as number) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:48:28 +08:00
|
|
|
|
// 验证课程完整性 (暂时返回 true,后端可能没有此接口)
|
2026-03-19 09:34:54 +08:00
|
|
|
|
export function validateCourse(_id: number): Promise<ValidationResult> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return Promise.resolve({ valid: true, errors: [], warnings: [] });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提交审核
|
2026-03-19 09:34:54 +08:00
|
|
|
|
export function submitCourse(id: number | string): Promise<any> {
|
|
|
|
|
|
return http.post(`/v1/admin/packages/${id}/submit`).then((res: any) => {
|
|
|
|
|
|
const body = res;
|
2026-03-23 10:48:09 +08:00
|
|
|
|
if (
|
|
|
|
|
|
body &&
|
|
|
|
|
|
typeof body === "object" &&
|
|
|
|
|
|
"code" in body &&
|
|
|
|
|
|
body.code !== 200 &&
|
|
|
|
|
|
body.code !== 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
throw new Error(body.message || "提交失败");
|
2026-03-19 09:34:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
return body?.data;
|
|
|
|
|
|
});
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:48:28 +08:00
|
|
|
|
// 撤销审核 (暂时使用更新接口,需要确认后端是否有此功能)
|
2026-02-26 15:22:26 +08:00
|
|
|
|
export function withdrawCourse(id: number): Promise<any> {
|
2026-03-23 10:48:09 +08:00
|
|
|
|
return api.updateCourse(id, { status: "DRAFT" }) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 审核通过
|
2026-03-23 10:48:09 +08:00
|
|
|
|
export function approveCourse(
|
|
|
|
|
|
id: number,
|
|
|
|
|
|
_data: { checklist?: any; comment?: string },
|
|
|
|
|
|
): Promise<any> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return api.publishCourse(id) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-19 09:34:54 +08:00
|
|
|
|
// 审核驳回(课程专用,调用 POST /v1/admin/packages/{id}/reject)
|
2026-03-23 10:48:09 +08:00
|
|
|
|
export function rejectCourse(
|
|
|
|
|
|
id: number | string,
|
|
|
|
|
|
data: { comment: string },
|
|
|
|
|
|
): Promise<any> {
|
2026-03-19 09:34:54 +08:00
|
|
|
|
return http.post(`/v1/admin/packages/${id}/reject`, data).then((res: any) => {
|
|
|
|
|
|
const body = res;
|
2026-03-23 10:48:09 +08:00
|
|
|
|
if (
|
|
|
|
|
|
body &&
|
|
|
|
|
|
typeof body === "object" &&
|
|
|
|
|
|
"code" in body &&
|
|
|
|
|
|
body.code !== 200 &&
|
|
|
|
|
|
body.code !== 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
throw new Error(body.message || "驳回失败");
|
2026-03-16 11:08:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
return body?.data;
|
|
|
|
|
|
});
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接发布(超级管理员)
|
2026-03-23 10:48:09 +08:00
|
|
|
|
export function directPublishCourse(
|
|
|
|
|
|
id: number,
|
|
|
|
|
|
_skipValidation?: boolean,
|
|
|
|
|
|
): Promise<any> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return api.publishCourse(id) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 13:05:20 +08:00
|
|
|
|
// 发布课程包
|
2026-02-26 15:22:26 +08:00
|
|
|
|
export function publishCourse(id: number): Promise<any> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return api.publishCourse(id) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 下架课程包
|
|
|
|
|
|
export function unpublishCourse(id: number): Promise<any> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return api.archiveCourse(id) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:48:28 +08:00
|
|
|
|
// 重新发布 (使用发布接口)
|
2026-02-26 15:22:26 +08:00
|
|
|
|
export function republishCourse(id: number): Promise<any> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return api.publishCourse(id) as any;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-24 13:57:06 +08:00
|
|
|
|
// 获取课程包统计数据
|
|
|
|
|
|
export function getCourseStats(id: number | string): Promise<any> {
|
|
|
|
|
|
return http.get(`/v1/admin/packages/${id}/stats`) as Promise<any>;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:48:28 +08:00
|
|
|
|
// 获取版本历史 (暂时返回空数组)
|
2026-03-19 09:34:54 +08:00
|
|
|
|
export function getCourseVersions(_id: number): Promise<any[]> {
|
2026-03-13 13:48:28 +08:00
|
|
|
|
return Promise.resolve([]);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 13:05:20 +08:00
|
|
|
|
// ============= 常量 =============
|
|
|
|
|
|
|
2026-02-26 15:22:26 +08:00
|
|
|
|
// 课程状态映射
|
2026-03-23 10:48:09 +08:00
|
|
|
|
export const COURSE_STATUS_MAP: Record<
|
|
|
|
|
|
string,
|
|
|
|
|
|
{ label: string; color: string }
|
|
|
|
|
|
> = {
|
|
|
|
|
|
DRAFT: { label: "草稿", color: "default" },
|
|
|
|
|
|
PENDING: { label: "审核中", color: "processing" },
|
|
|
|
|
|
REJECTED: { label: "已驳回", color: "error" },
|
|
|
|
|
|
PUBLISHED: { label: "已发布", color: "success" },
|
|
|
|
|
|
ARCHIVED: { label: "已下架", color: "warning" },
|
2026-02-26 15:22:26 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取状态显示信息
|
|
|
|
|
|
export function getCourseStatusInfo(status: string) {
|
2026-03-23 10:48:09 +08:00
|
|
|
|
return COURSE_STATUS_MAP[status] || { label: status, color: "default" };
|
2026-02-26 15:22:26 +08:00
|
|
|
|
}
|