# 问题修复 - 发车显示不及时 (v3.10.2) ## 📋 问题描述 用户反馈:"执行进度里,token的发车显示不及时" ### 具体表现 在批量任务执行过程中,`TaskProgressCard.vue` 组件显示的发车状态和发车上限信息不会实时更新: ``` 批量任务开始时: ┌─────────────────────┐ │ 809服-xxx │ │ 发车: 0/4 今日未发车│ ← 初始显示 └─────────────────────┘ 发车任务执行中... └─ 服务器返回 12000050 └─ localStorage 更新为 4/4 执行完成后: ┌─────────────────────┐ │ 809服-xxx │ │ 发车: 0/4 今日未发车│ ← 仍然显示旧数据! └─────────────────────┘ ``` ## 🔍 问题根源 ### 技术原因 `TaskProgressCard.vue` 中的 `dailyCarSendCount` 是一个 `computed` 属性,直接从 `localStorage` 读取数据: ```javascript // 原代码(有问题) const dailyCarSendCount = computed(() => { const today = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }) const key = `car_daily_send_count_${today}_${props.tokenId}` return parseInt(localStorage.getItem(key) || '0') }) ``` ### 为什么不更新? **Vue 的响应式系统无法追踪 `localStorage` 的变化!** 1. **`computed` 的响应式机制**: - `computed` 只会在其依赖的 **响应式数据** 发生变化时重新计算 - `localStorage.getItem()` 不是响应式的 2. **数据流向**: ``` batchTaskStore.js └─ 更新 localStorage.setItem('car_daily_send_count_...', '4') ↓ localStorage 已更新 ↓ TaskProgressCard.vue └─ computed 不知道 localStorage 变了 └─ 继续显示旧值 0 ``` 3. **无法自动检测**: - Vue 无法监听到 `localStorage` 的 `setItem` 操作 - 即使数据已经更新,组件也不会重新渲染 ## 💡 v3.10.2 修复方案 ### 核心思路 **将 `computed` 改为 `ref`,并手动触发刷新** 1. 使用 `ref` 代替 `computed`,以便手动更新 2. 监听任务结果的变化,自动刷新发车次数 3. 在组件挂载时刷新一次,确保初始显示正确 ### 修复内容 #### 1. 改用 `ref` + 手动刷新函数 **修改前**: ```javascript const dailyCarSendCount = computed(() => { const today = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }) const key = `car_daily_send_count_${today}_${props.tokenId}` return parseInt(localStorage.getItem(key) || '0') }) ``` **修改后**: ```javascript // 获取今日发车次数的 key const getCarSendCountKey = () => { const today = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }) return `car_daily_send_count_${today}_${props.tokenId}` } // 使用 ref 而不是 computed,以便手动刷新 const dailyCarSendCount = ref(parseInt(localStorage.getItem(getCarSendCountKey()) || '0')) // 刷新发车次数的函数 const refreshCarSendCount = () => { const key = getCarSendCountKey() const newCount = parseInt(localStorage.getItem(key) || '0') dailyCarSendCount.value = newCount console.log(`🔄 [${props.tokenId}] 刷新发车次数显示: ${newCount}/4`) } ``` #### 2. 监听任务结果变化 ```javascript // 监听任务结果变化,自动刷新发车次数 watch(() => props.progress?.result?.sendCar, (newResult, oldResult) => { if (newResult && newResult !== oldResult) { // 发车任务有新结果,等待一小段时间确保 localStorage 已更新 setTimeout(() => { refreshCarSendCount() }, 100) } }, { deep: true }) ``` **工作原理**: - 监听 `props.progress.result.sendCar` 的变化 - 当 `sendCar` 任务有新结果时(成功或失败) - 等待 100ms 确保 `localStorage` 已更新 - 调用 `refreshCarSendCount()` 从 `localStorage` 读取最新值 #### 3. 监听任务状态变化 ```javascript // 监听任务状态变化,当任务完成或失败时刷新 watch(() => props.progress?.status, (newStatus, oldStatus) => { if (newStatus !== oldStatus && (newStatus === 'completed' || newStatus === 'failed')) { // 任务完成或失败时,刷新发车次数 setTimeout(() => { refreshCarSendCount() }, 100) } }) ``` **工作原理**: - 监听整个任务的状态变化 - 当任务完成或失败时,刷新发车次数 - 作为兜底机制,确保任务结束后一定会刷新 #### 4. 组件挂载时刷新 ```javascript // 组件挂载时刷新发车次数 onMounted(() => { refreshCarSendCount() }) ``` **工作原理**: - 组件首次渲染时从 `localStorage` 读取最新值 - 确保初始显示是准确的 #### 5. 添加必要的导入 ```javascript import { ref, computed, watch, onMounted } from 'vue' ``` ## 📊 修复效果对比 ### 修改前(v3.10.1) ``` 时间线: 00:00 - 批量任务开始 TaskProgressCard 显示: 发车: 0/4 ✓ 00:05 - sendCar 任务执行 batchTaskStore 更新 localStorage: 0 → 4 00:06 - sendCar 任务完成 TaskProgressCard 显示: 发车: 0/4 ✗ (未更新!) 00:10 - 批量任务完成 TaskProgressCard 显示: 发车: 0/4 ✗ (仍未更新!) 用户需要刷新页面才能看到正确的 4/4 ``` ### 修改后(v3.10.2) ``` 时间线: 00:00 - 批量任务开始 TaskProgressCard 显示: 发车: 0/4 ✓ 00:05 - sendCar 任务执行 batchTaskStore 更新 localStorage: 0 → 4 00:06 - sendCar 任务完成 props.progress.result.sendCar 更新 触发 watch 回调 100ms 后调用 refreshCarSendCount() TaskProgressCard 显示: 发车: 4/4 ✓ (自动更新!) 00:10 - 批量任务完成 再次触发刷新(兜底) TaskProgressCard 显示: 发车: 4/4 ✓ 用户无需刷新页面,自动显示正确的 4/4 ``` ## 🎯 关键优势 | 特性 | 修改前 | 修改后 | |------|--------|--------| | **数据类型** | `computed` | `ref` + 手动刷新 | | **更新机制** | 依赖响应式系统 | 主动监听和刷新 | | **初始显示** | 可能不准确 | `onMounted` 确保准确 | | **任务执行中** | 不更新 | **实时更新** | | **任务完成后** | 不更新 | **自动更新** | | **用户体验** | ❌ 需要手动刷新页面 | ✅ 自动显示最新数据 | ## 📝 技术细节 ### 为什么使用 `setTimeout(100)`? ```javascript setTimeout(() => { refreshCarSendCount() }, 100) ``` **原因**: 1. **确保 localStorage 已更新**: - `batchTaskStore` 更新 `localStorage` - Vue 更新 `props.progress.result.sendCar` - `watch` 回调触发 - 这些操作可能不是完全同步的 2. **异步写入的保险**: - 虽然 `localStorage.setItem()` 通常是同步的 - 但在某些浏览器或情况下可能有延迟 - 100ms 的延迟可以确保数据已经写入 3. **避免竞态条件**: - 如果立即读取,可能还是旧值 - 延迟后读取,确保是最新值 ### 为什么使用两个 `watch`? 1. **第一个 watch**:监听 `sendCar` 任务结果 - 更精确:只在发车任务有结果时触发 - 更及时:任务一完成就立即刷新 2. **第二个 watch**:监听整体任务状态 - 兜底机制:确保任务结束时一定会刷新 - 更全面:即使第一个 watch 没触发,这个也会 ### 为什么使用 `{ deep: true }`? ```javascript watch(() => props.progress?.result?.sendCar, ..., { deep: true }) ``` **原因**: - `result.sendCar` 是一个对象,包含 `success`、`data`、`message` 等字段 - `deep: true` 确保对象内部任何属性变化都会触发 watch - 这样无论任务成功还是失败,都能正确触发刷新 ## 🧪 测试建议 ### 测试场景1:首次打开批量任务面板 1. 打开批量任务面板 2. 观察 Token 卡片的发车显示 3. **预期结果**:显示正确的发车次数(从 localStorage 读取) ### 测试场景2:执行发车任务 1. 执行包含发车任务的批量自动化 2. 观察 Token 卡片的发车显示 3. **预期结果**: - 发车任务执行过程中,显示实时更新 - 任务完成后,立即显示最新的发车次数 - 控制台输出:`🔄 [token_xxx] 刷新发车次数显示: X/4` ### 测试场景3:服务器端已达上限 1. 使用已达上限的账号(服务器端4次) 2. 客户端 localStorage 为 0/4 3. 执行批量任务 4. **预期结果**: - 初始显示:0/4 - 任务执行后立即更新为:4/4(根据错误码 12000050 更新) - Token 卡片显示:"今日已达上限" ### 测试场景4:多 Token 并发 1. 选择多个 Token 执行批量任务 2. 观察每个 Token 卡片的发车显示 3. **预期结果**: - 每个 Token 的发车次数独立更新 - 不会互相干扰 - 所有 Token 都显示正确的发车次数 ## 🔗 相关修复 本次修复建立在以下修复的基础上: 1. **v3.10.0** - 同步服务器发车次数(失败) 2. **v3.10.1** - 修复服务器 `sendCount` 不可靠 3. **v3.10.2** - 修复发车显示不及时(本次) ## 📋 更新日志 **版本**: v3.10.2 **日期**: 2025-10-08 **类型**: 问题修复 **修改内容**: 1. ✅ 将 `dailyCarSendCount` 从 `computed` 改为 `ref` 2. ✅ 添加 `refreshCarSendCount()` 手动刷新函数 3. ✅ 监听 `props.progress.result.sendCar` 变化,自动刷新 4. ✅ 监听 `props.progress.status` 变化,兜底刷新 5. ✅ 在 `onMounted` 时刷新,确保初始显示准确 6. ✅ 添加必要的导入:`watch`、`onMounted` **影响范围**: - `src/components/TaskProgressCard.vue` **解决问题**: - 发车次数显示不及时 - 任务执行后显示不更新 - 需要刷新页面才能看到最新数据 **用户体验提升**: - ✅ 发车次数实时更新 - ✅ 任务完成后自动刷新 - ✅ 无需手动刷新页面 - ✅ 初始显示准确