kindergarten_java/reading-platform-frontend/src/api/course.ts
zhonghua 40782a8905 feat(theme): 主题字典颜色、课程主题 Tag 展示与列表数据规范化
- 后端:theme 表增加 color 字段;主题创建/更新/课程响应返回 themeColor
- 前端:主题管理页颜色选择器与列表;管理端课程列表与详情主题 Tag
- 课程中心/课程包卡片展示主题 Tag,course-center 规范化接口字段
- 隐藏管理端课程配置列与筛选;课程详情关联主题使用 themeName/color

Made-with: Cursor
2026-03-24 15:11:40 +08:00

317 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { getReadingPlatformAPI } from "./generated";
import { axios, customMutator } from "./generated/mutator";
// 创建 API 实例
const api = getReadingPlatformAPI();
// 封装 http 方法(兼容原有代码)
export const http = {
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 }),
};
// ============= 类型定义(保持向后兼容) =============
export interface CourseQueryParams {
pageNum?: number;
pageSize?: number;
grade?: string;
gradeTags?: string; // 年级筛选(逗号分隔,如 "小班,中班,大班"
status?: string;
keyword?: string;
/** 审核管理页专用:仅返回待审核和已驳回,排除已通过 */
reviewOnly?: boolean;
}
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;
updatedAt: Date;
submittedAt?: Date;
reviewedAt?: Date;
reviewComment?: string;
// 新增字段
themeId?: number;
theme?: { id: number; name: string };
themeName?: string;
/** 主题颜色(hex),来自主题字典 */
themeColor?: string;
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;
}
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;
}
// ============= API 函数(使用生成的客户端) =============
// 转换查询参数类型
const toFindAllParams = (params: CourseQueryParams): any => ({
pageNum: params.pageNum ?? 1,
pageSize: params.pageSize ?? 10,
keyword: params.keyword,
category: params.grade,
gradeTags: params.gradeTags, // 年级筛选(逗号分隔)
status: params.status || undefined,
reviewOnly: params.reviewOnly,
});
// 后端 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,
};
}
// 获取课程包列表7 步流程创建的教学资源)
// 注意:这里的 Course 实际对应后端的 CoursePackage课程包
// 使用扁平 query 参数,确保 gradeTags适用年级正确传给后端
export function getCourses(params: CourseQueryParams): Promise<{
items: Course[];
total: number;
page: number;
pageSize: number;
}> {
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;
}
// 获取审核列表(仅返回待审核和已驳回,不含已通过)
export function getReviewList(params: CourseQueryParams): Promise<{
items: Course[];
total: number;
page: number;
pageSize: number;
}> {
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;
}
// 获取课程包详情id 支持 number | string避免大整数精度丢失
export function getCourse(id: number | string): Promise<any> {
return api.getCourse1(id as number) as any;
}
// 创建课程包
export function createCourse(data: any): Promise<any> {
return api.createCourse(data) as any;
}
// 更新课程包
export function updateCourse(id: number | string, data: any): Promise<any> {
return api.updateCourse(id as number, data) as any;
}
// 删除课程包
export function deleteCourse(id: number | string): Promise<any> {
return api.deleteCourse(id as number) as any;
}
// 验证课程完整性 (暂时返回 true后端可能没有此接口)
export function validateCourse(_id: number): Promise<ValidationResult> {
return Promise.resolve({ valid: true, errors: [], warnings: [] });
}
// 提交审核
export function submitCourse(id: number | string): Promise<any> {
return http.post(`/v1/admin/packages/${id}/submit`).then((res: any) => {
const body = res;
if (
body &&
typeof body === "object" &&
"code" in body &&
body.code !== 200 &&
body.code !== 0
) {
throw new Error(body.message || "提交失败");
}
return body?.data;
});
}
// 撤销审核 (暂时使用更新接口,需要确认后端是否有此功能)
export function withdrawCourse(id: number): Promise<any> {
return api.updateCourse(id, { status: "DRAFT" }) as any;
}
// 审核通过
export function approveCourse(
id: number,
_data: { checklist?: any; comment?: string },
): Promise<any> {
return api.publishCourse(id) as any;
}
// 审核驳回(课程专用,调用 POST /v1/admin/packages/{id}/reject
export function rejectCourse(
id: number | string,
data: { comment: string },
): Promise<any> {
return http.post(`/v1/admin/packages/${id}/reject`, data).then((res: any) => {
const body = res;
if (
body &&
typeof body === "object" &&
"code" in body &&
body.code !== 200 &&
body.code !== 0
) {
throw new Error(body.message || "驳回失败");
}
return body?.data;
});
}
// 直接发布(超级管理员)
export function directPublishCourse(
id: number,
_skipValidation?: boolean,
): Promise<any> {
return api.publishCourse(id) as any;
}
// 发布课程包
export function publishCourse(id: number): Promise<any> {
return api.publishCourse(id) as any;
}
// 下架课程包
export function unpublishCourse(id: number): Promise<any> {
return api.archiveCourse(id) as any;
}
// 重新发布 (使用发布接口)
export function republishCourse(id: number): Promise<any> {
return api.publishCourse(id) as any;
}
// 获取课程包统计数据
export function getCourseStats(id: number | string): Promise<any> {
return http.get(`/v1/admin/packages/${id}/stats`) as Promise<any>;
}
// 获取版本历史 (暂时返回空数组)
export function getCourseVersions(_id: number): Promise<any[]> {
return Promise.resolve([]);
}
// ============= 常量 =============
// 课程状态映射
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" },
};
// 获取状态显示信息
export function getCourseStatusInfo(status: string) {
return COURSE_STATUS_MAP[status] || { label: status, color: "default" };
}