achievement.ts — 成就 Store
文件: web/src/stores/achievement.ts
职责
- 段位系统计算(5 大级别 × 10 小段 = 50 段)
- 30 枚徽章管理和自动解锁
- 通过
watch监听 user/game Store 数据变化自动检测徽章解锁 - 学习记录触发 EXP/积分奖励
依赖
| Store | 用途 |
|---|---|
useUserStore | 读取 EXP/积分/徽章/学习记录/连续天数/字源查看 |
useCharacterStore | 已导入(当前未直接使用) |
useGameStore | 延迟获取(ensureGameStore),用于游戏徽章检测 |
数据加载
load(): 通过api.get<LevelDef[]>("/achievements/levels")+api.get<BadgeDef[]>("/achievements/badges")异步加载段位定义和徽章定义。- 初始化时自动调用,加载完成后立即检查所有徽章状态。
段位系统
段位定义(LEVELS)通过 GET /achievements/levels 异步加载,不再硬编码。每个 LevelDef 包含:
typescript
interface LevelDef {
tier: string // 级别标识(如 "bronze", "silver")
name: string // 显示名称(如 "青铜")
color: string // 颜色(如 "#cd7f32")
min: number // 该级别最低 EXP
max: number // 该级别最高 EXP
startSegment: number // 起始段位编号
segmentExp: number // 每小段所需 EXP
}每小段 EXP = 100-300(随级别递增),共 50 段。
徽章定义
徽章定义(BADGE_DEFS)通过 GET /achievements/badges 异步加载,不再硬编码。每个 BadgeDef 包含 id、name、desc、category、icon。
Reactive State
| 属性 | 类型 | 说明 |
|---|---|---|
totalEXP | ComputedRef<number> | 用户总经验值(来自 userStore) |
shopPoints | ComputedRef<number> | 用户积分(来自 userStore) |
currentLevel | ComputedRef<CurrentLevel> | 当前段位详情(含进度、下一段 EXP 等) |
allBadges | ComputedRef<BadgeWithStatus[]> | 全部徽章(含 unlocked 状态) |
unlockedBadges | ComputedRef<BadgeWithStatus[]> | 已解锁徽章列表 |
unlockedCount | ComputedRef<number> | 已解锁徽章数量 |
CurrentLevel 结构
typescript
interface CurrentLevel {
tier: string // 当前级别名称
tierColor: string // 级别颜色
tierIcon: string // 级别图标(tier 字段)
tierMin: number // 当前级别最低 EXP
segment: number // 当前段位编号(1-50)
segmentInTier: number // 当前级别内段位(1-10)
currentEXP: number // 当前 EXP
nextSegmentEXP: number // 下一段所需 EXP
progress: number // 当前段进度(0-1)
tierProgress: number // 当前级别进度(0-1)
nextTierEXP: number // 下一级别所需 EXP
nextTierName: string|null // 下一级别名称
}方法
EXP 与积分
| 方法 | 签名 | 说明 |
|---|---|---|
addEXP | (amount: number) => void | 委托给 userStore.addEXP |
addPoints | (amount: number, source: string, sourceId: string, desc: string) => void | 委托给 userStore.addPoints |
徽章
| 方法 | 签名 | 说明 |
|---|---|---|
unlockBadge | (badgeId: string) => void | 解锁徽章(去重),额外奖励 50 EXP + 5 积分 |
事件处理
| 方法 | 签名 | 说明 |
|---|---|---|
handleNewRecord | (record: RecordItem) => void | 处理新学习记录,根据类型/分数自动增加 EXP/积分,检查书写徽章 |
handleDailyReviewComplete | (percent: number) => void | 每日复习完成奖励(≥100%: 50EXP+5分, ≥80%: 30EXP+3分) |
徽章检查(watch 自动触发 + 初始化时主动调用)
| 方法 | 触发条件 | 检查徽章 |
|---|---|---|
checkExplorationBadges | learned.length 变化 | learn_10/50/100/200/300/500 |
checkStreakBadges | streak 变化 | streak_3/7/14/30/60/100 |
checkWriteBadges | 初始化时调用 | first_write, write_10/50/100, perfect_10/30 |
checkEtymologyBadges | etymologyViewed.length 变化 | etymology_10/30/50/100/200/300 |
checkGameBadges | gameData 变化 | game_1/5/10/26, game_perfect_3/10 |
自动监听机制
Store 初始化时注册 5 个 watch:
- watch records →
handleNewRecord(处理新增记录,发放 EXP/积分) - watch learned →
checkExplorationBadges - watch streak →
checkStreakBadges - watch gameData →
checkGameBadges(延迟获取 gameStore 实例) - watch etymologyViewed →
checkEtymologyBadges
load() 完成后立即执行一轮全量徽章检查。