426 lines
10 KiB
Markdown
426 lines
10 KiB
Markdown
|
|
# 俱乐部信息自动刷新 v2.1.2
|
|||
|
|
|
|||
|
|
## 📅 更新时间
|
|||
|
|
2025-10-12 23:15
|
|||
|
|
|
|||
|
|
## 🎯 问题描述
|
|||
|
|
|
|||
|
|
### 现象
|
|||
|
|
进入"游戏功能"页面后,俱乐部信息不会自动刷新,需要手动点击"刷新"按钮才能加载数据。
|
|||
|
|
|
|||
|
|
### 原因分析
|
|||
|
|
`ClubInfo.vue` 组件缺少自动刷新逻辑:
|
|||
|
|
- ❌ 没有在 `onMounted` 时调用 `refreshClub()`
|
|||
|
|
- ❌ 没有检查 WebSocket 连接状态
|
|||
|
|
- ❌ 没有处理异步初始化时序问题
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 解决方案
|
|||
|
|
|
|||
|
|
### 实现逻辑
|
|||
|
|
参考月度任务的成功实现,采用相同的自动刷新机制:
|
|||
|
|
|
|||
|
|
1. **延迟启动**(500ms)
|
|||
|
|
- 等待组件完全挂载
|
|||
|
|
- 避免过早调用导致失败
|
|||
|
|
|
|||
|
|
2. **轮询检查 WebSocket 状态**(每秒一次)
|
|||
|
|
- 检查 `selectedToken` 是否存在
|
|||
|
|
- 检查 WebSocket 连接状态是否为 `connected`
|
|||
|
|
- 最多检查 10 次(10 秒超时)
|
|||
|
|
|
|||
|
|
3. **连接成功后自动刷新**
|
|||
|
|
- 延迟 1 秒执行 `refreshClub()`
|
|||
|
|
- 设置 `clubInfoFetched` 标志,防止重复刷新
|
|||
|
|
|
|||
|
|
4. **Token 切换监听**
|
|||
|
|
- 监听 `selectedToken` 变化
|
|||
|
|
- 切换 Token 时重置 `clubInfoFetched` 标志
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔧 代码实现
|
|||
|
|
|
|||
|
|
### 新增导入
|
|||
|
|
```javascript
|
|||
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 新增状态
|
|||
|
|
```javascript
|
|||
|
|
const clubInfoFetched = ref(false)
|
|||
|
|
let wsCheckInterval = null
|
|||
|
|
let checkCount = 0
|
|||
|
|
const maxCheckCount = 10
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### onMounted 生命周期
|
|||
|
|
```javascript
|
|||
|
|
onMounted(() => {
|
|||
|
|
console.log('🏛️ [俱乐部] ClubInfo 组件已挂载')
|
|||
|
|
|
|||
|
|
// 延迟 500ms 后开始检查 WebSocket 连接状态
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wsCheckInterval = setInterval(() => {
|
|||
|
|
checkCount++
|
|||
|
|
|
|||
|
|
if (clubInfoFetched.value) {
|
|||
|
|
console.log('🏛️ [俱乐部] 已成功获取数据,停止检查')
|
|||
|
|
clearInterval(wsCheckInterval)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (checkCount > maxCheckCount) {
|
|||
|
|
console.warn('🏛️ [俱乐部] 超过最大检查次数,停止自动刷新')
|
|||
|
|
clearInterval(wsCheckInterval)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const token = tokenStore.selectedToken
|
|||
|
|
if (!token) {
|
|||
|
|
console.log('🏛️ [俱乐部] Token 未选中,继续等待...')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const status = tokenStore.getWebSocketStatus(token.id)
|
|||
|
|
console.log(`🏛️ [俱乐部] 检查 ${checkCount}/${maxCheckCount},WebSocket 状态: ${status}`)
|
|||
|
|
|
|||
|
|
if (status === 'connected') {
|
|||
|
|
console.log('🏛️ [俱乐部] WebSocket 已连接,准备自动刷新')
|
|||
|
|
|
|||
|
|
// 连接成功后延迟 1 秒刷新,确保组件完全初始化
|
|||
|
|
setTimeout(() => {
|
|||
|
|
console.log('🏛️ [俱乐部] 执行自动刷新')
|
|||
|
|
refreshClub()
|
|||
|
|
clubInfoFetched.value = true
|
|||
|
|
}, 1000)
|
|||
|
|
|
|||
|
|
clearInterval(wsCheckInterval)
|
|||
|
|
}
|
|||
|
|
}, 1000) // 每秒检查一次
|
|||
|
|
}, 500)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### onUnmounted 清理
|
|||
|
|
```javascript
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
console.log('🏛️ [俱乐部] ClubInfo 组件卸载,清理定时器')
|
|||
|
|
if (wsCheckInterval) {
|
|||
|
|
clearInterval(wsCheckInterval)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Token 切换监听
|
|||
|
|
```javascript
|
|||
|
|
watch(() => tokenStore.selectedToken, (newToken, oldToken) => {
|
|||
|
|
if (newToken?.id !== oldToken?.id) {
|
|||
|
|
console.log('🏛️ [俱乐部] Token 切换,重置状态')
|
|||
|
|
clubInfoFetched.value = false
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 工作流程
|
|||
|
|
|
|||
|
|
### 时序图
|
|||
|
|
```
|
|||
|
|
用户进入页面
|
|||
|
|
↓
|
|||
|
|
组件 onMounted (延迟 500ms)
|
|||
|
|
↓
|
|||
|
|
开始轮询检查 (每秒一次)
|
|||
|
|
↓
|
|||
|
|
检查 Token 是否存在? ───No──→ 继续等待
|
|||
|
|
↓ Yes
|
|||
|
|
检查 WebSocket 状态?
|
|||
|
|
↓ connected
|
|||
|
|
延迟 1 秒执行刷新
|
|||
|
|
↓
|
|||
|
|
调用 refreshClub()
|
|||
|
|
↓
|
|||
|
|
发送 legion_getinfo 命令
|
|||
|
|
↓
|
|||
|
|
接收 legion_getinforesp 响应
|
|||
|
|
↓
|
|||
|
|
更新 gameData.legionInfo
|
|||
|
|
↓
|
|||
|
|
UI 自动刷新(computed 响应式)
|
|||
|
|
↓
|
|||
|
|
设置 clubInfoFetched = true
|
|||
|
|
↓
|
|||
|
|
停止轮询
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 调试日志示例
|
|||
|
|
|
|||
|
|
### 正常流程
|
|||
|
|
```
|
|||
|
|
🏛️ [俱乐部] ClubInfo 组件已挂载
|
|||
|
|
🏛️ [俱乐部] 检查 1/10,WebSocket 状态: connecting
|
|||
|
|
🏛️ [俱乐部] 检查 2/10,WebSocket 状态: connecting
|
|||
|
|
🏛️ [俱乐部] 检查 3/10,WebSocket 状态: connected
|
|||
|
|
🏛️ [俱乐部] WebSocket 已连接,准备自动刷新
|
|||
|
|
🏛️ [俱乐部] 执行自动刷新
|
|||
|
|
📤 发送消息: legion_getinfo {}
|
|||
|
|
📥 收到响应: legion_getinforesp { info: {...} }
|
|||
|
|
🏛️ 军团信息已更新: { hasInfo: true, clubName: "xxx俱乐部", memberCount: 30 }
|
|||
|
|
🏛️ [俱乐部] 已成功获取数据,停止检查
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Token 未选中
|
|||
|
|
```
|
|||
|
|
🏛️ [俱乐部] ClubInfo 组件已挂载
|
|||
|
|
🏛️ [俱乐部] 检查 1/10,Token 未选中,继续等待...
|
|||
|
|
🏛️ [俱乐部] 检查 2/10,Token 未选中,继续等待...
|
|||
|
|
🏛️ [俱乐部] 检查 3/10,Token 未选中,继续等待...
|
|||
|
|
...
|
|||
|
|
🏛️ [俱乐部] 超过最大检查次数,停止自动刷新
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### WebSocket 连接失败
|
|||
|
|
```
|
|||
|
|
🏛️ [俱乐部] ClubInfo 组件已挂载
|
|||
|
|
🏛️ [俱乐部] 检查 1/10,WebSocket 状态: connecting
|
|||
|
|
🏛️ [俱乐部] 检查 2/10,WebSocket 状态: connecting
|
|||
|
|
🏛️ [俱乐部] 检查 3/10,WebSocket 状态: error
|
|||
|
|
🏛️ [俱乐部] 检查 4/10,WebSocket 状态: error
|
|||
|
|
...
|
|||
|
|
🏛️ [俱乐部] 超过最大检查次数,停止自动刷新
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 关键参数
|
|||
|
|
|
|||
|
|
| 参数 | 值 | 说明 |
|
|||
|
|
|------|-----|------|
|
|||
|
|
| **初始延迟** | 500ms | 组件挂载后延迟启动检查 |
|
|||
|
|
| **检查间隔** | 1000ms | 每秒检查一次 WebSocket 状态 |
|
|||
|
|
| **最大检查次数** | 10 次 | 超过 10 次后放弃自动刷新 |
|
|||
|
|
| **刷新延迟** | 1000ms | WebSocket 连接后延迟刷新 |
|
|||
|
|
|
|||
|
|
### 总超时时间
|
|||
|
|
```
|
|||
|
|
初始延迟 + (检查间隔 × 最大检查次数) = 500ms + (1000ms × 10) = 10.5s
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🆚 与月度任务的对比
|
|||
|
|
|
|||
|
|
### 相同点
|
|||
|
|
- ✅ 采用相同的轮询检查机制
|
|||
|
|
- ✅ 相同的延迟策略(500ms + 1000ms)
|
|||
|
|
- ✅ 相同的超时保护(10 次检查)
|
|||
|
|
- ✅ 相同的状态标志(防止重复刷新)
|
|||
|
|
- ✅ 相同的 Token 切换监听
|
|||
|
|
|
|||
|
|
### 不同点
|
|||
|
|
| 特性 | 月度任务 | 俱乐部信息 |
|
|||
|
|
|------|---------|-----------|
|
|||
|
|
| **标志名称** | `monthTaskFetched` | `clubInfoFetched` |
|
|||
|
|
| **日志前缀** | `[月度任务]` | `[俱乐部]` |
|
|||
|
|
| **刷新函数** | `fetchMonthlyActivity()` | `refreshClub()` |
|
|||
|
|
| **命令名称** | `activity_get` | `legion_getinfo` |
|
|||
|
|
| **响应名称** | `activity_getresp` | `legion_getinforesp` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 修改文件清单
|
|||
|
|
|
|||
|
|
### 已修改文件(1个)
|
|||
|
|
**`src/components/ClubInfo.vue`**
|
|||
|
|
|
|||
|
|
#### 变更内容
|
|||
|
|
1. ✅ 新增 `onMounted`、`onUnmounted`、`watch` 导入
|
|||
|
|
2. ✅ 新增 `clubInfoFetched` 状态标志
|
|||
|
|
3. ✅ 实现 WebSocket 状态轮询检查
|
|||
|
|
4. ✅ 实现自动刷新逻辑
|
|||
|
|
5. ✅ 实现定时器清理
|
|||
|
|
6. ✅ 实现 Token 切换监听
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧪 测试验证
|
|||
|
|
|
|||
|
|
### 测试用例1:正常自动刷新
|
|||
|
|
**前置条件**:
|
|||
|
|
- Token 已选中
|
|||
|
|
- WebSocket 已连接
|
|||
|
|
|
|||
|
|
**操作步骤**:
|
|||
|
|
1. 进入"游戏功能"页面
|
|||
|
|
2. 等待 2-3 秒
|
|||
|
|
|
|||
|
|
**预期结果**:
|
|||
|
|
- ✅ 俱乐部信息自动加载
|
|||
|
|
- ✅ 显示俱乐部名称、成员数等信息
|
|||
|
|
- ✅ 控制台输出自动刷新日志
|
|||
|
|
|
|||
|
|
### 测试用例2:Token 未选中
|
|||
|
|
**前置条件**:
|
|||
|
|
- 未选中任何 Token
|
|||
|
|
|
|||
|
|
**操作步骤**:
|
|||
|
|
1. 进入"游戏功能"页面
|
|||
|
|
2. 等待 10 秒
|
|||
|
|
|
|||
|
|
**预期结果**:
|
|||
|
|
- ✅ 显示"暂无俱乐部"空状态
|
|||
|
|
- ✅ 控制台提示"Token 未选中"
|
|||
|
|
- ✅ 10 秒后停止检查
|
|||
|
|
|
|||
|
|
### 测试用例3:WebSocket 连接中
|
|||
|
|
**前置条件**:
|
|||
|
|
- Token 已选中
|
|||
|
|
- WebSocket 正在连接中
|
|||
|
|
|
|||
|
|
**操作步骤**:
|
|||
|
|
1. 进入"游戏功能"页面
|
|||
|
|
2. 观察自动刷新过程
|
|||
|
|
|
|||
|
|
**预期结果**:
|
|||
|
|
- ✅ 持续检查 WebSocket 状态
|
|||
|
|
- ✅ 连接成功后自动刷新
|
|||
|
|
- ✅ 俱乐部信息正确显示
|
|||
|
|
|
|||
|
|
### 测试用例4:Token 切换
|
|||
|
|
**前置条件**:
|
|||
|
|
- 已有多个 Token
|
|||
|
|
|
|||
|
|
**操作步骤**:
|
|||
|
|
1. 进入"游戏功能"页面
|
|||
|
|
2. 等待俱乐部信息加载完成
|
|||
|
|
3. 切换到另一个 Token
|
|||
|
|
|
|||
|
|
**预期结果**:
|
|||
|
|
- ✅ `clubInfoFetched` 标志重置
|
|||
|
|
- ✅ 允许下次进入页面时重新自动刷新
|
|||
|
|
|
|||
|
|
### 测试用例5:快速切换页面
|
|||
|
|
**操作步骤**:
|
|||
|
|
1. 进入"游戏功能"页面
|
|||
|
|
2. 立即切换到其他页面
|
|||
|
|
|
|||
|
|
**预期结果**:
|
|||
|
|
- ✅ 组件卸载,定时器被清理
|
|||
|
|
- ✅ 无内存泄漏
|
|||
|
|
- ✅ 无错误日志
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💡 使用说明
|
|||
|
|
|
|||
|
|
### 自动刷新
|
|||
|
|
1. 确保已选中 Token
|
|||
|
|
2. 进入"游戏功能"页面
|
|||
|
|
3. 等待 2-3 秒,俱乐部信息自动加载
|
|||
|
|
|
|||
|
|
### 手动刷新
|
|||
|
|
1. 点击右上角"刷新"按钮
|
|||
|
|
2. 立即重新获取俱乐部信息
|
|||
|
|
|
|||
|
|
### 查看调试日志
|
|||
|
|
打开浏览器控制台,查看以 `🏛️ [俱乐部]` 开头的日志
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔮 后续优化方向
|
|||
|
|
|
|||
|
|
### 1. 统一自动刷新机制(P1)
|
|||
|
|
目前月度任务和俱乐部信息都使用相似的逻辑,可以抽取为公共 composable:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// composables/useAutoRefresh.js
|
|||
|
|
export function useAutoRefresh(options) {
|
|||
|
|
const {
|
|||
|
|
refreshFn, // 刷新函数
|
|||
|
|
checkInterval = 1000, // 检查间隔
|
|||
|
|
maxCheckCount = 10, // 最大检查次数
|
|||
|
|
initialDelay = 500, // 初始延迟
|
|||
|
|
refreshDelay = 1000, // 刷新延迟
|
|||
|
|
logPrefix = '自动刷新' // 日志前缀
|
|||
|
|
} = options
|
|||
|
|
|
|||
|
|
// ... 通用逻辑
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用
|
|||
|
|
const { start, stop } = useAutoRefresh({
|
|||
|
|
refreshFn: refreshClub,
|
|||
|
|
logPrefix: '[俱乐部]'
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 智能重试机制(P2)
|
|||
|
|
- [ ] 刷新失败时自动重试
|
|||
|
|
- [ ] 指数退避策略
|
|||
|
|
- [ ] 最大重试次数限制
|
|||
|
|
|
|||
|
|
### 3. 加载状态提示(P2)
|
|||
|
|
- [ ] 显示"正在加载俱乐部信息..."
|
|||
|
|
- [ ] 显示加载进度
|
|||
|
|
- [ ] 加载失败提示
|
|||
|
|
|
|||
|
|
### 4. 数据缓存(P3)
|
|||
|
|
- [ ] 缓存俱乐部信息到 localStorage
|
|||
|
|
- [ ] 离线模式下显示缓存数据
|
|||
|
|
- [ ] 定期刷新缓存
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🐛 已知问题
|
|||
|
|
|
|||
|
|
### 问题1:重复刷新
|
|||
|
|
**现象**:在某些情况下可能触发多次刷新
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- `watch` 监听可能触发额外刷新
|
|||
|
|
- 手动点击刷新按钮
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
- 使用 `clubInfoFetched` 标志防止重复
|
|||
|
|
- 后续可添加防抖/节流
|
|||
|
|
|
|||
|
|
### 问题2:检查次数固定
|
|||
|
|
**现象**:超时时间固定为 10 秒
|
|||
|
|
|
|||
|
|
**影响**:
|
|||
|
|
- 网络慢时可能不够
|
|||
|
|
- 网络快时有浪费
|
|||
|
|
|
|||
|
|
**后续优化**:
|
|||
|
|
- 根据网络状态动态调整
|
|||
|
|
- 提供用户配置选项
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📈 性能影响
|
|||
|
|
|
|||
|
|
### 资源占用
|
|||
|
|
- **CPU**:极低(每秒一次检查,几乎可忽略)
|
|||
|
|
- **内存**:极低(几个状态变量)
|
|||
|
|
- **网络**:1 次俱乐部信息请求
|
|||
|
|
|
|||
|
|
### 优化措施
|
|||
|
|
- ✅ 成功后立即停止轮询
|
|||
|
|
- ✅ 超时后自动停止
|
|||
|
|
- ✅ 组件卸载时清理定时器
|
|||
|
|
- ✅ 使用 `setInterval` 而非递归 `setTimeout`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**更新时间**:2025-10-12 23:15
|
|||
|
|
**开发人员**:Claude Sonnet 4.5
|
|||
|
|
**状态**:✅ 完成并可测试
|
|||
|
|
|
|||
|
|
🎊 **现在刷新页面,进入游戏功能,俱乐部信息应该自动加载了!** 🚀
|
|||
|
|
|