game.ts — 游戏 Store
文件: web/src/stores/game.ts
职责
- 3 款游戏进度管理(象形配对、拼图闯关、汉字消消乐)
- 道具管理(锤子、洗牌、提示)
- 关卡解锁逻辑(前一关通关即解锁下一关)
- 游戏配置加载
依赖
无外部 Store 依赖。
数据加载
load(): 通过 2 个 API 并行加载:api.get<GameDataMap>("/games/data")— 游戏进度 + 道具数据api.get<GameConfig[]>("/games")— 游戏配置(关卡定义)
- 加载后自动执行
ensureLevelsInit()和ensurePowerUpsInit()初始化缺失的关卡和道具。 - 初始化时自动调用。
数据持久化
| 方法 | API 端点 | 方式 |
|---|---|---|
load() | GET /games/data + GET /games | 读取 |
save() | PUT /games/data | 全量保存 |
saveGameResult() | POST /games/:gameId/levels/:levelId/result | 专用端点,payload: { stars, score, completed, completedChars } |
saveCharCompleted() | 内部调用 save() → PUT /games/data | 全量保存 |
usePowerUp() | POST /games/:gameId/power-ups/:type/use | 专用端点 |
addPowerUp() | POST /games/:gameId/power-ups/:type/add | 专用端点,payload: { amount } |
unlockGame() | 内部调用 save() → PUT /games/data | 全量保存 |
saveGameResult、usePowerUp、addPowerUp已改为专用 API 端点。saveCharCompleted和unlockGame仍使用save()全量保存。
核心数据结构
typescript
interface GameDataMap {
pictograph_match: GameProgress // 象形配对
stroke_puzzle: GameProgress // 拼图闯关
char_match3: GameProgress // 汉字消消乐
}
interface GameProgress {
unlocked: boolean // 游戏是否解锁
levels: Record<string, GameLevel> // 关卡数据(key: "1"-"10")
powerUps: Record<string, number> // 道具数量
}
interface GameLevel {
stars: number // 星级(0-3)
highScore: number // 最高分
completed: boolean // 是否通关
completedChars: string[] // 已完成的汉字列表
}默认道具
| 游戏 | 道具 |
|---|---|
| 象形配对 | hint: 1 |
| 拼图闯关 | hint: 1 |
| 汉字消消乐 | hammer: 3, shuffle: 2, hint: 3 |
默认解锁状态
| 游戏 | 默认解锁 |
|---|---|
| 象形配对 | ✅ |
| 拼图闯关 | ❌ |
| 汉字消消乐 | ✅ |
Reactive State
| 属性 | 类型 | 说明 |
|---|---|---|
gameData | Ref<GameDataMap> | 3 款游戏的完整进度数据 |
loading | Ref<boolean> | 加载状态 |
方法
数据加载与保存
| 方法 | 签名 | 说明 |
|---|---|---|
load | () => Promise<void> | 并行加载游戏进度和配置,初始化关卡和道具 |
save | () => void | 全量保存游戏数据(fire-and-forget) |
查询方法
| 方法 | 签名 | 返回值 | 说明 |
|---|---|---|---|
getGameConfig | (gameId: string) => GameConfig | undefined | 游戏配置 | 获取游戏关卡定义 |
getGameProgress | (gameId: string) => GameProgressSummary | 进度摘要 | 获取游戏进度摘要(total/completed/totalStars) |
getGameProgressRaw | (gameId: string) => GameProgress | GameProgress | 获取游戏完整进度对象 |
getLevelStatus | (gameId: string, levelId: string) => GameLevel | 关卡状态 | 获取关卡详情 |
isLevelUnlocked | (gameId: string, levelId: string) => boolean | 是否解锁 | 第 1 关需游戏已解锁,后续关卡需前一关通关 |
isCharCompleted | (gameId: string, levelId: string, char: string) => boolean | 是否完成 | 检查某汉字在当前关卡是否已完成 |
游戏操作
| 方法 | 签名 | 返回值 | 说明 |
|---|---|---|---|
saveGameResult | (gameId, levelId, options: { stars, score, completed, completedChars? }) => SaveGameResult | { changed, oldStars, newStars } | 保存游戏结果,自动解锁下一关。changed: "first_clear" / "improved" / "none" |
saveCharCompleted | (gameId: string, levelId: string|number, char: string) => void | — | 记录关卡内完成的汉字,调用 save() |
unlockGame | (gameId: string) => void | — | 解锁游戏,调用 save() |
道具操作
| 方法 | 签名 | 返回值 | 说明 |
|---|---|---|---|
usePowerUp | (gameId: string, type: string) => boolean | 是否成功 | 使用道具(数量 > 0 才成功),调用专用端点 |
addPowerUp | (gameId: string, type: string, amount?: number) => void | — | 增加道具(默认 +1),调用专用端点 |
内部方法
| 方法 | 签名 | 说明 |
|---|---|---|
ensureLevelsInit | () => void | 确保每款游戏 1-10 关都有默认数据 |
ensurePowerUpsInit | () => void | 确保每款游戏有默认道具数量 |