import { ref } from 'vue' import { ApiError, adminApi, mockMode, mockReason } from '../../api/client' import type { Athlete, AthleteInput, EntryImportResult, Event, EventCategory, EventCategoryInput, EventInput, ScoringProfile, ScoringProfileInput, ScoreChannel, ScoreChannelInput, Sport, SportInput, Team, TeamInput, } from '@/shared' import { mockBaseData } from './mockBaseData' /** * 基础数据接口入口。 * * 复用 adminApi.client 的 REST 方法,但补上与管理端一致的降级策略: * 只有「服务端未就绪」(网络/超时/解析失败/404)才落回示例数据, * 业务错误(鉴权、校验、冲突)原样抛出,避免把真实报错伪装成示例数据。 * * 之所以不直接用 adminApi 的封装:api/client.ts 不在本页允许修改的范围内, * 且这里需要区分「基础数据接口没实现」与「整个后端没起来」两种降级。 */ const FALLBACK_CODES: readonly string[] = ['NETWORK', 'TIMEOUT', 'PARSE_FAILED'] /** * 本页接口的降级标记。 * * 不能只看全局 mockMode:服务端可能整体正常、只有基础数据接口尚未实现, * 此时 adminApi.bootstrap 会把 mockMode 重置为 false,页面却仍在用示例数据。 */ export const baseDataFallback = ref(false) function isFallback(error: unknown): boolean { // 404 表示接口在当前服务端尚未实现,与整个后端没起来一样按「未就绪」处理。 return error instanceof ApiError && (FALLBACK_CODES.includes(error.code) || error.status === 404) } async function call(real: () => Promise, mock: () => T | Promise): Promise { try { const value = await real() baseDataFallback.value = false if (mockMode.value) { mockMode.value = false mockReason.value = null } return value } catch (error) { if (!isFallback(error)) throw error baseDataFallback.value = true mockMode.value = true mockReason.value = error instanceof Error ? error.message : '无法连接服务端' return await mock() } } export const baseDataApi = { teams: { list: (): Promise => call(() => adminApi.client.teams.list(), () => mockBaseData.listTeams()), create: (input: TeamInput): Promise => call(() => adminApi.client.teams.create(input), () => mockBaseData.createTeam(input)), update: (id: number, input: TeamInput): Promise => call(() => adminApi.client.teams.update(id, input), () => mockBaseData.updateTeam(id, input)), }, sports: { list: (): Promise => call(() => adminApi.client.sports.list(), () => mockBaseData.listSports()), create: (input: SportInput): Promise => call(() => adminApi.client.sports.create(input), () => mockBaseData.createSport(input)), update: (id: number, input: SportInput): Promise => call(() => adminApi.client.sports.update(id, input), () => mockBaseData.updateSport(id, input)), remove: (id: number): Promise => call(() => adminApi.client.sports.remove(id), () => mockBaseData.removeSport(id)), }, events: { list: (): Promise => call(() => adminApi.client.events.list(), () => mockBaseData.listEvents()), get: (id: number): Promise => call(() => adminApi.client.events.get(id), () => mockBaseData.getEvent(id)), create: (input: EventInput): Promise => call(() => adminApi.client.events.create(input), () => mockBaseData.createEvent(input)), update: (id: number, input: EventInput): Promise => call(() => adminApi.client.events.update(id, input), () => mockBaseData.updateEvent(id, input)), remove: (id: number): Promise => call(() => adminApi.client.events.remove(id), () => mockBaseData.removeEvent(id)), saveCategories: (eventId: number, inputs: EventCategoryInput[]): Promise => call( () => adminApi.client.events.saveCategories(eventId, inputs), () => mockBaseData.saveCategories(eventId, inputs), ), saveChannels: (eventId: number, inputs: ScoreChannelInput[]): Promise => call( () => adminApi.client.events.saveChannels(eventId, inputs), () => mockBaseData.saveChannels(eventId, inputs), ), }, profiles: { list: (): Promise => call(() => adminApi.client.scoringProfiles.list(), () => mockBaseData.listProfiles()), create: (input: ScoringProfileInput): Promise => call(() => adminApi.client.scoringProfiles.create(input), () => mockBaseData.createProfile(input)), update: (id: number, input: ScoringProfileInput): Promise => call(() => adminApi.client.scoringProfiles.update(id, input), () => mockBaseData.updateProfile(id, input)), }, athletes: { list: (): Promise => call(() => adminApi.client.athletes.list(), () => mockBaseData.listAthletes()), create: (input: AthleteInput): Promise => call(() => adminApi.client.athletes.create(input), () => mockBaseData.createAthlete(input)), update: (id: number, input: AthleteInput): Promise => call(() => adminApi.client.athletes.update(id, input), () => mockBaseData.updateAthlete(id, input)), remove: (id: number): Promise => call(() => adminApi.client.athletes.remove(id), () => mockBaseData.removeAthlete(id)), }, entries: { importFile: (file: File): Promise => call( () => adminApi.client.entries.importFile(file), () => mockBaseData.importEntries(file), ), }, }