diff --git a/web/src/App.vue b/web/src/App.vue index 7d46744..4e580c1 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -1,11 +1,9 @@ @@ -385,4 +231,4 @@ watch([showMenu, isManualPaused], ([menuVisible, manualPaused]) => { flex-direction: column; z-index: 20; } - \ No newline at end of file + diff --git a/web/src/api/game.ts b/web/src/api/game.ts deleted file mode 100644 index 0588074..0000000 --- a/web/src/api/game.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { httpClient } from './http'; -import type { - InitialStateDTO, - MapResponseDTO, - DetailResponseDTO, - SaveFileDTO -} from '../types/api'; - -export interface HoverParams { - type: string; - id: string; -} - -// --- New Types --- - -export interface GameDataDTO { - sects: Array<{ id: number; name: string; alignment: string }>; - personas: Array<{ id: number; name: string; desc: string; rarity: string }>; - realms: string[]; - techniques: Array<{ id: number; name: string; grade: string; attribute: string; sect: string | null }>; - weapons: Array<{ id: number; name: string; grade: string; type: string }>; - auxiliaries: Array<{ id: number; name: string; grade: string }>; - alignments: Array<{ value: string; label: string }>; -} - -export interface SimpleAvatarDTO { - id: string; - name: string; - sect_name: string; - realm: string; - gender: string; - age: number; -} - -export interface CreateAvatarParams { - surname?: string; - given_name?: string; - gender?: string; - age?: number; - level?: number; - sect_id?: number; - persona_ids?: number[]; - pic_id?: number; - technique_id?: number; - weapon_id?: number; - auxiliary_id?: number; - alignment?: string; - appearance?: number; - relations?: Array<{ target_id: string; relation: string }>; -} - -export interface PhenomenonDTO { - id: number; - name: string; - desc: string; - rarity: string; - duration_years: number; - effect_desc: string; -} - -export interface LLMConfigDTO { - base_url: string; - api_key: string; - model_name: string; - fast_model_name: string; - mode: string; -} - -export interface GameStartConfigDTO { - init_npc_num: number; - sect_num: number; - protagonist: string; - npc_awakening_rate_per_month: number; -} - -export interface CurrentConfigDTO { - game: { - init_npc_num: number; - sect_num: number; - npc_awakening_rate_per_month: number; - }; - avatar: { - protagonist: string; - }; -} - -// --- Events Pagination --- - -export interface EventDTO { - id: string; - text: string; - content: string; - year: number; - month: number; - month_stamp: number; - related_avatar_ids: string[]; - is_major: boolean; - is_story: boolean; - created_at: number; -} - -export interface EventsResponseDTO { - events: EventDTO[]; - next_cursor: string | null; - has_more: boolean; -} - -export interface FetchEventsParams { - avatar_id?: string; - avatar_id_1?: string; - avatar_id_2?: string; - cursor?: string; - limit?: number; -} - -export interface InitStatusDTO { - status: 'idle' | 'pending' | 'in_progress' | 'ready' | 'error'; - phase: number; - phase_name: string; - progress: number; - elapsed_seconds: number; - error: string | null; - llm_check_failed: boolean; - llm_error_message: string; -} - -export const gameApi = { - // --- World State --- - - fetchInitialState() { - return httpClient.get('/api/state'); - }, - - fetchMap() { - return httpClient.get('/api/map'); - }, - - fetchAvatarMeta() { - // Add timestamp to prevent caching - return httpClient.get<{ males: number[]; females: number[] }>(`/api/meta/avatars?t=${Date.now()}`); - }, - - fetchPhenomenaList() { - return httpClient.get<{ phenomena: PhenomenonDTO[] }>('/api/meta/phenomena'); - }, - - setPhenomenon(id: number) { - return httpClient.post('/api/control/set_phenomenon', { id }); - }, - - // --- Information --- - - fetchDetailInfo(params: HoverParams) { - const query = new URLSearchParams(Object.entries(params)); - return httpClient.get(`/api/detail?${query}`); - }, - - // --- Actions --- - - setLongTermObjective(avatarId: string, content: string) { - return httpClient.post('/api/action/set_long_term_objective', { - avatar_id: avatarId, - content - }); - }, - - clearLongTermObjective(avatarId: string) { - return httpClient.post('/api/action/clear_long_term_objective', { - avatar_id: avatarId - }); - }, - - // --- Controls --- - - pauseGame() { - return httpClient.post('/api/control/pause', {}); - }, - - resumeGame() { - return httpClient.post('/api/control/resume', {}); - }, - - // --- Saves --- - - fetchSaves() { - return httpClient.get<{ saves: SaveFileDTO[] }>('/api/saves'); - }, - - saveGame(filename?: string) { - return httpClient.post<{ status: string; filename: string }>('/api/game/save', { filename }); - }, - - loadGame(filename: string) { - return httpClient.post<{ status: string; message: string }>('/api/game/load', { filename }); - }, - - // --- Avatar Management --- - - fetchGameData() { - return httpClient.get('/api/meta/game_data'); - }, - - fetchAvatarList() { - return httpClient.get<{ avatars: SimpleAvatarDTO[] }>('/api/meta/avatar_list'); - }, - - createAvatar(params: CreateAvatarParams) { - return httpClient.post<{ status: string; message: string; avatar_id: string }>('/api/action/create_avatar', params); - }, - - deleteAvatar(avatarId: string) { - return httpClient.post<{ status: string; message: string }>('/api/action/delete_avatar', { avatar_id: avatarId }); - }, - - // --- LLM Config --- - - fetchLLMConfig() { - return httpClient.get('/api/config/llm'); - }, - - testLLMConnection(config: LLMConfigDTO) { - return httpClient.post<{ status: string; message: string }>('/api/config/llm/test', config); - }, - - saveLLMConfig(config: LLMConfigDTO) { - return httpClient.post<{ status: string; message: string }>('/api/config/llm/save', config); - }, - - fetchLLMStatus() { - return httpClient.get<{ configured: boolean }>('/api/config/llm/status'); - }, - - // --- Events Pagination --- - - fetchEvents(params: FetchEventsParams = {}) { - const query = new URLSearchParams(); - if (params.avatar_id) query.set('avatar_id', params.avatar_id); - if (params.avatar_id_1) query.set('avatar_id_1', params.avatar_id_1); - if (params.avatar_id_2) query.set('avatar_id_2', params.avatar_id_2); - if (params.cursor) query.set('cursor', params.cursor); - if (params.limit) query.set('limit', String(params.limit)); - const qs = query.toString(); - return httpClient.get(`/api/events${qs ? '?' + qs : ''}`); - }, - - cleanupEvents(keepMajor = true, beforeMonthStamp?: number) { - const query = new URLSearchParams(); - query.set('keep_major', String(keepMajor)); - if (beforeMonthStamp !== undefined) query.set('before_month_stamp', String(beforeMonthStamp)); - return httpClient.delete<{ deleted: number }>(`/api/events/cleanup?${query}`); - }, - - // --- Init Status --- - - fetchInitStatus() { - return httpClient.get('/api/init-status'); - }, - - startNewGame() { - // Legacy: replaced by startGame logic usually, but kept for compatibility if needed - return httpClient.post<{ status: string; message: string }>('/api/game/new', {}); - }, - - reinitGame() { - return httpClient.post<{ status: string; message: string }>('/api/control/reinit', {}); - }, - - // --- Game Start Config --- - - fetchCurrentConfig() { - return httpClient.get('/api/config/current'); - }, - - startGame(config: GameStartConfigDTO) { - return httpClient.post<{ status: string; message: string }>('/api/game/start', config); - } -}; diff --git a/web/src/api/index.ts b/web/src/api/index.ts new file mode 100644 index 0000000..a03f31c --- /dev/null +++ b/web/src/api/index.ts @@ -0,0 +1,9 @@ +// 导出子模块 +export { worldApi } from './modules/world'; +export { avatarApi, type HoverParams } from './modules/avatar'; +export { systemApi } from './modules/system'; +export { llmApi } from './modules/llm'; +export { eventApi } from './modules/event'; + +// 保持向后兼容的聚合对象 (Optional, for transition) +// 但这次我们直接重构,不再保留大对象,鼓励按需引用 diff --git a/web/src/api/modules/avatar.ts b/web/src/api/modules/avatar.ts new file mode 100644 index 0000000..9152ceb --- /dev/null +++ b/web/src/api/modules/avatar.ts @@ -0,0 +1,53 @@ +import { httpClient } from '../http'; +import type { + DetailResponseDTO, + SimpleAvatarDTO, + CreateAvatarParams, + GameDataDTO +} from '../../types/api'; + +export interface HoverParams { + type: string; + id: string; +} + +export const avatarApi = { + fetchAvatarMeta() { + // Add timestamp to prevent caching + return httpClient.get<{ males: number[]; females: number[] }>(`/api/meta/avatars?t=${Date.now()}`); + }, + + fetchDetailInfo(params: HoverParams) { + const query = new URLSearchParams(Object.entries(params)); + return httpClient.get(`/api/detail?${query}`); + }, + + setLongTermObjective(avatarId: string, content: string) { + return httpClient.post('/api/action/set_long_term_objective', { + avatar_id: avatarId, + content + }); + }, + + clearLongTermObjective(avatarId: string) { + return httpClient.post('/api/action/clear_long_term_objective', { + avatar_id: avatarId + }); + }, + + fetchGameData() { + return httpClient.get('/api/meta/game_data'); + }, + + fetchAvatarList() { + return httpClient.get<{ avatars: SimpleAvatarDTO[] }>('/api/meta/avatar_list'); + }, + + createAvatar(params: CreateAvatarParams) { + return httpClient.post<{ status: string; message: string; avatar_id: string }>('/api/action/create_avatar', params); + }, + + deleteAvatar(avatarId: string) { + return httpClient.post<{ status: string; message: string }>('/api/action/delete_avatar', { avatar_id: avatarId }); + } +}; diff --git a/web/src/api/modules/event.ts b/web/src/api/modules/event.ts new file mode 100644 index 0000000..fcc4afe --- /dev/null +++ b/web/src/api/modules/event.ts @@ -0,0 +1,25 @@ +import { httpClient } from '../http'; +import type { + EventsResponseDTO, + FetchEventsParams +} from '../../types/api'; + +export const eventApi = { + fetchEvents(params: FetchEventsParams = {}) { + const query = new URLSearchParams(); + if (params.avatar_id) query.set('avatar_id', params.avatar_id); + if (params.avatar_id_1) query.set('avatar_id_1', params.avatar_id_1); + if (params.avatar_id_2) query.set('avatar_id_2', params.avatar_id_2); + if (params.cursor) query.set('cursor', params.cursor); + if (params.limit) query.set('limit', String(params.limit)); + const qs = query.toString(); + return httpClient.get(`/api/events${qs ? '?' + qs : ''}`); + }, + + cleanupEvents(keepMajor = true, beforeMonthStamp?: number) { + const query = new URLSearchParams(); + query.set('keep_major', String(keepMajor)); + if (beforeMonthStamp !== undefined) query.set('before_month_stamp', String(beforeMonthStamp)); + return httpClient.delete<{ deleted: number }>(`/api/events/cleanup?${query}`); + } +}; diff --git a/web/src/api/modules/llm.ts b/web/src/api/modules/llm.ts new file mode 100644 index 0000000..b2997fe --- /dev/null +++ b/web/src/api/modules/llm.ts @@ -0,0 +1,20 @@ +import { httpClient } from '../http'; +import type { LLMConfigDTO } from '../../types/api'; + +export const llmApi = { + fetchConfig() { + return httpClient.get('/api/config/llm'); + }, + + testConnection(config: LLMConfigDTO) { + return httpClient.post<{ status: string; message: string }>('/api/config/llm/test', config); + }, + + saveConfig(config: LLMConfigDTO) { + return httpClient.post<{ status: string; message: string }>('/api/config/llm/save', config); + }, + + fetchStatus() { + return httpClient.get<{ configured: boolean }>('/api/config/llm/status'); + } +}; diff --git a/web/src/api/modules/system.ts b/web/src/api/modules/system.ts new file mode 100644 index 0000000..636bbff --- /dev/null +++ b/web/src/api/modules/system.ts @@ -0,0 +1,49 @@ +import { httpClient } from '../http'; +import type { + SaveFileDTO, + InitStatusDTO, + GameStartConfigDTO, + CurrentConfigDTO +} from '../../types/api'; + +export const systemApi = { + pauseGame() { + return httpClient.post('/api/control/pause', {}); + }, + + resumeGame() { + return httpClient.post('/api/control/resume', {}); + }, + + fetchSaves() { + return httpClient.get<{ saves: SaveFileDTO[] }>('/api/saves'); + }, + + saveGame(filename?: string) { + return httpClient.post<{ status: string; filename: string }>('/api/game/save', { filename }); + }, + + loadGame(filename: string) { + return httpClient.post<{ status: string; message: string }>('/api/game/load', { filename }); + }, + + fetchInitStatus() { + return httpClient.get('/api/init-status'); + }, + + startNewGame() { + return httpClient.post<{ status: string; message: string }>('/api/game/new', {}); + }, + + reinitGame() { + return httpClient.post<{ status: string; message: string }>('/api/control/reinit', {}); + }, + + fetchCurrentConfig() { + return httpClient.get('/api/config/current'); + }, + + startGame(config: GameStartConfigDTO) { + return httpClient.post<{ status: string; message: string }>('/api/game/start', config); + } +}; diff --git a/web/src/api/modules/world.ts b/web/src/api/modules/world.ts new file mode 100644 index 0000000..a136d00 --- /dev/null +++ b/web/src/api/modules/world.ts @@ -0,0 +1,24 @@ +import { httpClient } from '../http'; +import type { + InitialStateDTO, + MapResponseDTO, + PhenomenonDTO +} from '../../types/api'; + +export const worldApi = { + fetchInitialState() { + return httpClient.get('/api/state'); + }, + + fetchMap() { + return httpClient.get('/api/map'); + }, + + fetchPhenomenaList() { + return httpClient.get<{ phenomena: PhenomenonDTO[] }>('/api/meta/phenomena'); + }, + + setPhenomenon(id: number) { + return httpClient.post('/api/control/set_phenomenon', { id }); + } +}; diff --git a/web/src/components/LoadingOverlay.vue b/web/src/components/LoadingOverlay.vue index 3fb0406..d88ed60 100644 --- a/web/src/components/LoadingOverlay.vue +++ b/web/src/components/LoadingOverlay.vue @@ -1,6 +1,6 @@