kindergarten_java/reading-platform-frontend/src/api/collections.ts
zhonghua 8f5a0fcdda fix: 套餐管理优化与 gradeLevels/重复包修复
- gradeLevels: 后端 JSON 解析修复,前端兼容错误格式
- 筛选下拉: 状态选项按业务流程排序
- 已下架: 列表与详情页增加编辑、删除操作
- 课程包关联: 前后端去重,修复 uk_collection_package 唯一约束冲突

Made-with: Cursor
2026-03-19 11:37:04 +08:00

237 lines
6.4 KiB
TypeScript

import { getReadingPlatformAPI } from './generated';
const api = getReadingPlatformAPI();
// ============= 类型定义 =============
export interface CollectionQueryParams {
pageNum?: number;
pageSize?: number;
status?: string;
}
export interface Collection {
id: number;
name: string;
description?: string;
price: number;
discountPrice?: number;
discountType?: string;
gradeLevels: string[];
packageCount?: number;
status: string;
createdAt: string;
updatedAt: string;
packages?: CollectionPackage[];
reviewComment?: string;
}
export interface CollectionPackage {
id: number;
name: string;
description?: string;
}
export interface CreateCollectionDto {
name: string;
description?: string;
price: number;
discountPrice?: number;
discountType?: string;
gradeLevels: string[];
}
export interface UpdateCollectionDto {
name?: string;
description?: string;
price?: number;
discountPrice?: number;
discountType?: string;
gradeLevels?: string[];
}
// ============= 辅助函数 =============
// 修复 gradeLevels 格式:后端返回的格式是 ["[\"小班\"", " \"中班\""]
// 需要转换为 ["小班", "中班"]
function fixGradeLevels(gradeLevels: string | string[] | undefined): string[] {
if (!gradeLevels) return [];
// 如果是字符串数组,尝试拼接并解析
if (Array.isArray(gradeLevels)) {
if (gradeLevels.length === 0) return [];
// 检查是否是被分割的 JSON 字符串
if (gradeLevels[0]?.startsWith('[')) {
try {
const joined = gradeLevels.join('');
return JSON.parse(joined);
} catch {
return [];
}
}
return gradeLevels;
}
// 如果是字符串,尝试解析 JSON
if (typeof gradeLevels === 'string') {
try {
return JSON.parse(gradeLevels);
} catch {
return [];
}
}
return [];
}
// 转换 API 响应数据为前端格式
function normalizeCollection(data: any): Collection {
// 如果数据是包装的响应格式 { code, message, data },提取 data 字段
const collectionData = data?.data ?? data;
if (!collectionData) {
throw new Error('套餐数据为空');
}
return {
...collectionData,
id: Number(collectionData.id),
price: Number(collectionData.price),
discountPrice: collectionData.discountPrice ? Number(collectionData.discountPrice) : undefined,
gradeLevels: fixGradeLevels(collectionData.gradeLevels),
};
}
function normalizePageResult(raw: any): {
items: Collection[];
total: number;
page: number;
pageSize: number;
} {
const list = raw?.list ?? raw?.items ?? [];
return {
items: Array.isArray(list) ? list.map(normalizeCollection) : [],
total: Number(raw?.total ?? 0),
page: Number(raw?.pageNum ?? raw?.page ?? 1),
pageSize: Number(raw?.pageSize ?? 10),
};
}
// ============= API 函数 =============
// 获取课程套餐列表
export function getCollections(params: CollectionQueryParams = {}): Promise<{
items: Collection[];
total: number;
page: number;
pageSize: number;
}> {
return (api.page as any)({
pageNum: params.pageNum ?? 1,
pageSize: params.pageSize ?? 10,
...(params.status ? { status: params.status } : {}),
}).then(normalizePageResult) as any;
}
// 获取课程套餐详情
export async function getCollection(id: number | string): Promise<Collection> {
const data = await (api.findOne2 as any)(Number(id)) as any;
if (!data) {
throw new Error('获取套餐详情失败:返回数据为空');
}
return normalizeCollection(data);
}
// 创建课程套餐
export function createCollection(data: CreateCollectionDto): Promise<Collection> {
return (api.create as any)(data) as any;
}
// 更新课程套餐
export function updateCollection(id: number | string, data: UpdateCollectionDto): Promise<Collection> {
return (api.update2 as any)(id as number, data) as any;
}
// 删除课程套餐
export function deleteCollection(id: number | string): Promise<void> {
return (api.delete2 as any)(id as number) as any;
}
// 设置套餐课程包
export function setCollectionPackages(id: number | string, packageIds: number[]): Promise<void> {
return (api.setPackages as any)(id as number, packageIds) as any;
}
// 发布课程套餐
export function publishCollection(id: number | string): Promise<void> {
return (api.publish as any)(id as number) as any;
}
// 下架课程套餐
export function archiveCollection(id: number | string): Promise<void> {
return (api.archive as any)(id as number) as any;
}
// 重新发布课程套餐
export function republishCollection(id: number | string): Promise<void> {
return (api.republish as any)(id as number) as any;
}
// 撤销审核
export function withdrawCollection(id: number | string): Promise<void> {
return (api.withdraw as any)(id as number) as any;
}
// 授予租户
export function grantToTenant(collectionId: number | string, tenantIds: number[]): Promise<void> {
return (api.grantToTenant as any)(collectionId as number, tenantIds) as any;
}
// ============= 常量 =============
export const COLLECTION_STATUS_MAP: Record<string, { label: string; color: string }> = {
DRAFT: { label: '草稿', color: 'default' },
PENDING: { label: '待审核', color: 'processing' },
APPROVED: { label: '已通过', color: 'success' },
PUBLISHED: { label: '已发布', color: 'blue' },
ARCHIVED: { label: '已下架', color: 'warning' },
REJECTED: { label: '已驳回', color: 'error' },
};
export function getCollectionStatusInfo(status: string) {
return COLLECTION_STATUS_MAP[status] || { label: status, color: 'default' };
}
// 解析适用年级(统一处理列表展示与创建时的格式)
export function parseGradeLevels(gradeLevels: string | string[] | undefined): string[] {
if (!gradeLevels) return [];
if (Array.isArray(gradeLevels)) {
if (gradeLevels.length === 0) return [];
// 兼容后端错误格式:["[\"小班\"", " \"中班\""] -> 拼接后解析
if (gradeLevels[0]?.toString().startsWith('[')) {
try {
return JSON.parse(gradeLevels.join(''));
} catch {
return [];
}
}
return gradeLevels;
}
try {
return JSON.parse(gradeLevels || '[]');
} catch {
return [];
}
}
// 格式化价格
export function formatPrice(price: number | null | undefined): string {
if (price === null || price === undefined) return '-';
return `¥${(price / 100).toFixed(2)}`;
}
// 格式化日期
export function formatDate(date: string): string {
return new Date(date).toLocaleString('zh-CN');
}