# 员工导入结果跨页面持久化实施计划 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **目标:** 实现员工导入结果的跨页面持久化,使用户在切换菜单后仍能查看上一次的导入失败记录 **架构:** 使用浏览器localStorage存储最近一次导入的任务信息,在页面加载时恢复状态,实现导入状态的持久化保存 **技术栈:** - Vue 2.6.12 - localStorage API - Element UI 2.15.14 --- ## 前置准备 ### Task 0: 验证环境 **Files:** - 检查: `ruoyi-ui/src/views/ccdiEmployee/index.vue` **Step 1: 阅读现有代码** 读取 `ruoyi-ui/src/views/ccdiEmployee/index.vue` 文件,特别关注: - `data()` 中的 `showFailureButton`、`currentTaskId`、`pollingTimer` 等状态变量 - `handleFileSuccess()` 方法 - 导入上传成功处理 - `handleImportComplete()` 方法 - 导入完成处理 - `getFailureList()` 方法 - 查询失败记录 - `created()` 和 `beforeDestroy()` 生命周期钩子 确认当前实现确实存在状态丢失问题。 **Step 2: 理解localStorage使用场景** 理解需要持久化的数据: ```javascript { taskId: 'uuid', status: 'SUCCESS' | 'PARTIAL_SUCCESS' | 'FAILED', timestamp: 1707225900000, saveTime: 1707225900000, hasFailures: true, totalCount: 100, successCount: 95, failureCount: 5 } ``` **Step 3: 无需提交** 这只是验证步骤,无需提交代码。 --- ## 核心功能实现 ### Task 1: 新增localStorage工具方法 **Files:** - Modify: `ruoyi-ui/src/views/ccdiEmployee/index.vue` (在 methods 对象中添加) **Step 1: 添加 saveImportTaskToStorage 方法** 在 `methods` 对象中添加以下方法(放在 `methods` 的开头部分): ```javascript /** * 保存导入任务到localStorage * @param {Object} taskData - 任务数据 */ saveImportTaskToStorage(taskData) { try { const data = { ...taskData, saveTime: Date.now() }; localStorage.setItem('employee_import_last_task', JSON.stringify(data)); } catch (error) { console.error('保存导入任务状态失败:', error); } }, ``` **Step 2: 添加 getImportTaskFromStorage 方法** 在 `saveImportTaskToStorage` 方法后添加: ```javascript /** * 从localStorage读取导入任务 * @returns {Object|null} 任务数据或null */ getImportTaskFromStorage() { try { const data = localStorage.getItem('employee_import_last_task'); if (!data) return null; const task = JSON.parse(data); // 数据格式校验 if (!task || !task.taskId) { this.clearImportTaskFromStorage(); return null; } // 时间戳校验 if (task.saveTime && typeof task.saveTime !== 'number') { this.clearImportTaskFromStorage(); return null; } // 过期检查(7天) const sevenDays = 7 * 24 * 60 * 60 * 1000; if (Date.now() - task.saveTime > sevenDays) { this.clearImportTaskFromStorage(); return null; } return task; } catch (error) { console.error('读取导入任务状态失败:', error); this.clearImportTaskFromStorage(); return null; } }, ``` **Step 3: 添加 clearImportTaskFromStorage 方法** 在 `getImportTaskFromStorage` 方法后添加: ```javascript /** * 清除localStorage中的导入任务 */ clearImportTaskFromStorage() { try { localStorage.removeItem('employee_import_last_task'); } catch (error) { console.error('清除导入任务状态失败:', error); } }, ``` **Step 4: 手动测试 - 打开浏览器控制台验证** 1. 启动前端开发服务器: `npm run dev` (在 ruoyi-ui 目录) 2. 打开浏览器,访问员工管理页面 3. 打开浏览器开发者工具(F12),切换到 Console 标签 4. 在控制台输入: ```javascript // 测试保存 localStorage.setItem('employee_import_last_task', JSON.stringify({ taskId: 'test-123', status: 'SUCCESS', timestamp: Date.now(), saveTime: Date.now(), hasFailures: true, totalCount: 100, successCount: 95, failureCount: 5 })) // 测试读取 JSON.parse(localStorage.getItem('employee_import_last_task')) // 测试清除 localStorage.removeItem('employee_import_last_task') ``` 5. 确认每个操作都正常工作 **Step 5: 提交** ```bash git add ruoyi-ui/src/views/ccdiEmployee/index.vue git commit -m "feat: 添加localStorage工具方法用于导入状态持久化 - saveImportTaskToStorage: 保存导入任务到localStorage - getImportTaskFromStorage: 读取并校验导入任务数据 - clearImportTaskFromStorage: 清除localStorage数据 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ### Task 2: 添加状态恢复和用户交互方法 **Files:** - Modify: `ruoyi-ui/src/views/ccdiEmployee/index.vue` **Step 1: 添加 restoreImportState 方法** 在 `clearImportTaskFromStorage` 方法后添加: ```javascript /** * 恢复导入状态 * 在created()钩子中调用 */ async restoreImportState() { const savedTask = this.getImportTaskFromStorage(); if (!savedTask) { this.showFailureButton = false; this.currentTaskId = null; return; } // 如果有失败记录,恢复按钮显示 if (savedTask.hasFailures && savedTask.taskId) { this.currentTaskId = savedTask.taskId; this.showFailureButton = true; } }, ``` **Step 2: 添加 getLastImportTooltip 方法** 在 `restoreImportState` 方法后添加: ```javascript /** * 获取上次导入的提示信息 * @returns {String} 提示文本 */ getLastImportTooltip() { const savedTask = this.getImportTaskFromStorage(); if (savedTask && savedTask.timestamp) { const date = new Date(savedTask.timestamp); const timeStr = this.parseTime(date, '{y}-{m}-{d} {h}:{i}'); return `上次导入: ${timeStr}`; } return ''; }, ``` **Step 3: 添加 clearImportHistory 方法** 在 `getLastImportTooltip` 方法后添加: ```javascript /** * 清除导入历史记录 * 用户手动触发 */ clearImportHistory() { this.$confirm('确认清除上次导入记录?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.clearImportTaskFromStorage(); this.showFailureButton = false; this.currentTaskId = null; this.failureDialogVisible = false; this.$message.success('已清除'); }).catch(() => {}); }, ``` **Step 4: 修改 created() 生命周期钩子** 找到 `created()` 方法,在 `this.getList();` 后添加: ```javascript created() { this.getList(); this.getDeptTree(); this.restoreImportState(); // 新增:恢复导入状态 }, ``` **Step 5: 手动测试 - 状态恢复功能** 1. 在浏览器控制台手动设置测试数据: ```javascript localStorage.setItem('employee_import_last_task', JSON.stringify({ taskId: 'test-restore-123', status: 'PARTIAL_SUCCESS', timestamp: Date.now(), saveTime: Date.now(), hasFailures: true, totalCount: 100, successCount: 95, failureCount: 5 })) ``` 2. 刷新员工管理页面 3. 确认"查看上次导入失败记录"按钮显示出来 4. 打开Vue DevTools(如果有的话),检查 `showFailureButton` 为 `true`, `currentTaskId` 为 `'test-restore-123'` **Step 6: 提交** ```bash git add ruoyi-ui/src/views/ccdiEmployee/index.vue git commit -m "feat: 添加导入状态恢复和用户交互方法 - restoreImportState: 从localStorage恢复导入状态 - getLastImportTooltip: 获取导入时间提示信息 - clearImportHistory: 用户手动清除历史记录 - created(): 添加状态恢复调用 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ### Task 3: 修改导入成功处理逻辑 **Files:** - Modify: `ruoyi-ui/src/views/ccdiEmployee/index.vue` **Step 1: 修改 handleFileSuccess 方法** 找到 `handleFileSuccess` 方法,替换为: ```javascript // 文件上传成功处理 handleFileSuccess(response, file, fileList) { this.upload.isUploading = false; this.upload.open = false; if (response.code === 200) { const taskId = response.data.taskId; // 清除旧的导入记录(防止并发) if (this.pollingTimer) { clearInterval(this.pollingTimer); this.pollingTimer = null; } this.clearImportTaskFromStorage(); // 保存新任务的初始状态 this.saveImportTaskToStorage({ taskId: taskId, status: 'PROCESSING', timestamp: Date.now(), hasFailures: false }); // 重置状态 this.showFailureButton = false; this.currentTaskId = taskId; // 显示后台处理提示 this.$notify({ title: '导入任务已提交', message: '正在后台处理中,处理完成后将通知您', type: 'info', duration: 3000 }); // 开始轮询检查状态 this.startImportStatusPolling(taskId); } else { this.$modal.msgError(response.msg); } }, ``` 关键改动: - 添加清除旧轮询定时器的逻辑 - 调用 `clearImportTaskFromStorage()` 清除旧数据 - 调用 `saveImportTaskToStorage()` 保存新任务初始状态 - 重置 `showFailureButton` 和 `currentTaskId` **Step 2: 修改 handleImportComplete 方法** 找到 `handleImportComplete` 方法,替换为: ```javascript /** 处理导入完成 */ handleImportComplete(statusResult) { const hasFailures = statusResult.failureCount > 0; // 更新localStorage中的任务状态 this.saveImportTaskToStorage({ taskId: statusResult.taskId, status: statusResult.status, timestamp: Date.now(), hasFailures: hasFailures, totalCount: statusResult.totalCount, successCount: statusResult.successCount, failureCount: statusResult.failureCount }); if (statusResult.status === 'SUCCESS') { this.$notify({ title: '导入完成', message: `全部成功!共导入${statusResult.totalCount}条数据`, type: 'success', duration: 5000 }); this.getList(); } else if (hasFailures) { this.$notify({ title: '导入完成', message: `成功${statusResult.successCount}条,失败${statusResult.failureCount}条`, type: 'warning', duration: 5000 }); // 显示查看失败记录按钮 this.showFailureButton = true; this.currentTaskId = statusResult.taskId; // 刷新列表 this.getList(); } }, ``` 关键改动: - 在方法开头调用 `saveImportTaskToStorage()` 更新完整状态 **Step 3: 手动测试 - 导入流程** 1. 准备一个包含错误数据的Excel文件 2. 打开浏览器开发者工具 > Application > Local Storage 3. 上传Excel文件,开始导入 4. 观察 Local Storage 中是否有 `employee_import_last_task` 键 5. 等待导入完成 6. 检查 localStorage 中的数据是否包含完整的统计信息(totalCount, successCount, failureCount) 7. 刷新页面,确认按钮仍然显示 **Step 4: 提交** ```bash git add ruoyi-ui/src/views/ccdiEmployee/index.vue git commit -m "feat: 修改导入处理逻辑以支持状态持久化 - handleFileSuccess: 清除旧数据,保存新任务初始状态 - handleImportComplete: 更新localStorage中的完整任务状态 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ### Task 4: 增强失败记录查询的错误处理 **Files:** - Modify: `ruoyi-ui/src/views/ccdiEmployee/index.vue` **Step 1: 修改 getFailureList 方法** 找到 `getFailureList` 方法,替换为: ```javascript /** 查询失败记录列表 */ getFailureList() { this.failureLoading = true; getImportFailures( this.currentTaskId, this.failureQueryParams.pageNum, this.failureQueryParams.pageSize ).then(response => { this.failureList = response.rows; this.failureTotal = response.total; this.failureLoading = false; }).catch(error => { this.failureLoading = false; // 处理不同类型的错误 if (error.response) { const status = error.response.status; if (status === 404) { // 记录不存在或已过期 this.$modal.msgWarning('导入记录已过期,无法查看失败记录'); this.clearImportTaskFromStorage(); this.showFailureButton = false; this.currentTaskId = null; this.failureDialogVisible = false; } else if (status === 500) { this.$modal.msgError('服务器错误,请稍后重试'); } else { this.$modal.msgError(`查询失败: ${error.response.data.msg || '未知错误'}`); } } else if (error.request) { // 请求发送了但没有收到响应 this.$modal.msgError('网络连接失败,请检查网络'); } else { this.$modal.msgError('查询失败记录失败: ' + error.message); } }); }, ``` 关键改动: - 添加详细的错误分类处理 - 404错误时清除localStorage并隐藏按钮 - 添加网络错误和服务器错误的友好提示 **Step 2: 手动测试 - 错误处理** 由于需要模拟后端404错误,这里提供两种测试方式: **方式1: 修改localStorage时间戳模拟过期** ```javascript // 在控制台执行 const data = JSON.parse(localStorage.getItem('employee_import_last_task')); data.saveTime = Date.now() - (8 * 24 * 60 * 60 * 1000); // 8天前 localStorage.setItem('employee_import_last_task', JSON.stringify(data)); ``` 然后刷新页面,虽然不会触发API 404,但可以验证localStorage的过期清除逻辑。 **方式2: 使用无效的taskId测试** ```javascript // 在控制台执行 localStorage.setItem('employee_import_last_task', JSON.stringify({ taskId: 'invalid-task-id-12345', status: 'PARTIAL_SUCCESS', timestamp: Date.now(), saveTime: Date.now(), hasFailures: true, totalCount: 100, successCount: 95, failureCount: 5 })); ``` 刷新页面,点击"查看上次导入失败记录"按钮,应该会显示错误提示。 **Step 3: 提交** ```bash git add ruoyi-ui/src/views/ccdiEmployee/index.vue git commit -m "feat: 增强失败记录查询的错误处理 - 添加404错误处理(记录过期) - 添加500错误和500错误的友好提示 - 错误时自动清除localStorage并隐藏按钮 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ### Task 5: 添加计算属性和模板优化 **Files:** - Modify: `ruoyi-ui/src/views/ccdiEmployee/index.vue` **Step 1: 添加 computed 计算属性** 找到 `export default {` 中的 `data()` 方法,在 `data()` 后添加 `computed`: ```javascript computed: { /** * 上次导入信息摘要 */ lastImportInfo() { const savedTask = this.getImportTaskFromStorage(); if (savedTask && savedTask.totalCount) { return `导入时间: ${this.parseTime(savedTask.timestamp)} | 总数: ${savedTask.totalCount}条 | 成功: ${savedTask.successCount}条 | 失败: ${savedTask.failureCount}条`; } return ''; } }, ``` **Step 2: 修改失败记录按钮 - 添加tooltip** 找到"查看导入失败记录"按钮的代码(大约在第70-78行),替换为: ```vue 查看上次导入失败记录 ``` **Step 3: 修改失败记录对话框 - 添加信息提示和清除按钮** 找到导入失败记录对话框(大约在第269-294行),在 `` 上方添加信息提示,在footer添加清除按钮: ```vue ``` **Step 4: 手动测试 - UI优化验证** 1. 完成一次有失败记录的导入 2. 鼠标悬停在"查看上次导入失败记录"按钮上 3. 确认显示tooltip提示上次导入时间 4. 点击按钮打开对话框 5. 确认对话框顶部显示导入统计信息 6. 点击"清除历史记录"按钮 7. 确认弹出确认对话框 8. 确认后对话框关闭,按钮消失 **Step 5: 提交** ```bash git add ruoyi-ui/src/views/ccdiEmployee/index.vue git commit -m "feat: 添加UI优化和用户体验增强 - 新增lastImportInfo计算属性显示导入统计 - 失败记录按钮添加tooltip显示导入时间 - 失败记录对话框添加统计信息展示 - 对话框添加清除历史记录按钮 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ## 完整功能测试 ### Task 6: 端到端功能测试 **Files:** - 无修改,仅测试 **Step 1: 测试场景1 - 导入成功无失败后刷新** 1. 准备一个正确的Excel文件(所有数据都有效) 2. 上传文件并等待导入完成 3. 确认不显示"查看上次导入失败记录"按钮 4. 刷新页面(F5) 5. **预期**: 仍然不显示失败记录按钮 6. **实际**: 验证符合预期 **Step 2: 测试场景2 - 导入有失败后刷新** 1. 准备一个包含错误数据的Excel文件 2. 上传文件并等待导入完成 3. 确认显示"查看上次导入失败记录"按钮 4. 刷新页面(F5) 5. **预期**: 按钮仍然显示 6. **实际**: 验证符合预期 7. 点击按钮,确认能正常查看失败记录 **Step 3: 测试场景3 - 导入有失败后切换菜单** 1. 准备一个包含错误数据的Excel文件 2. 上传文件并等待导入完成 3. 确认显示"查看上次导入失败记录"按钮 4. 点击左侧菜单,切换到其他页面(如"部门管理") 5. 再点击菜单返回"员工管理" 6. **预期**: 按钮仍然显示 7. **实际**: 验证符合预期 **Step 4: 测试场景4 - 新导入覆盖旧记录** 1. 完成一次有失败记录的导入 2. 确认显示按钮 3. 上传新的Excel文件(正确或错误都可以) 4. **预期**: 新导入开始时,旧记录被清除 5. **实际**: 验证localStorage中的数据被新的taskId覆盖 **Step 5: 测试场景5 - 手动清除历史记录** 1. 完成一次有失败记录的导入 2. 点击"查看上次导入失败记录"按钮 3. 在对话框中点击"清除历史记录"按钮 4. **预期**: 弹出确认对话框,确认后对话框关闭,按钮消失 5. **实际**: 验证符合预期 6. 刷新页面 7. **预期**: 按钮仍然不显示 8. **实际**: 验证符合预期 **Step 6: 测试场景6 - localStorage过期处理** 这个场景由于Redis TTL是7天,手动测试比较困难,可以通过修改localStorage数据模拟: ```javascript // 在浏览器控制台执行 const data = JSON.parse(localStorage.getItem('employee_import_last_task')); if (data) { // 将saveTime改为8天前 data.saveTime = Date.now() - (8 * 24 * 60 * 60 * 1000); localStorage.setItem('employee_import_last_task', JSON.stringify(data)); } ``` 然后刷新页面,确认数据被自动清除,按钮不显示。 **Step 7: 浏览器兼容性快速测试** 在不同浏览器中重复上述测试场景: - Chrome (主要浏览器) - Edge (如果可用) - Firefox (如果可用) 确认功能在各个浏览器中正常工作。 **Step 8: 无需提交** 这是纯测试步骤,无需提交代码。 --- ## 文档更新 ### Task 7: 更新API文档(可选) **Files:** - Check: `doc/api/ccdi-employee-import-api.md` **Step 1: 检查API文档是否需要更新** 由于这个改动是纯前端实现,不涉及后端API的变化,因此API文档理论上不需要更新。 检查 `doc/api/ccdi-employee-import-api.md` 文档中是否有关于前端行为或状态的说明,如果有的话,补充说明现在支持跨页面状态持久化。 **Step 2: 如需要,在文档末尾添加说明** ```markdown ### 前端行为说明 #### 导入结果持久化 - 前端使用localStorage存储最近一次导入的任务信息 - 支持在切换菜单或刷新页面后继续查看上一次的导入失败记录 - 存储期限: 7天(与后端Redis TTL一致) - 下次导入开始时,自动清除上一次的导入记录 - 用户可以手动清除导入历史记录 ``` **Step 3: 提交(如果进行了修改)** ```bash git add doc/api/ccdi-employee-import-api.md git commit -m "docs: 补充导入结果持久化说明 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ## 最终验证 ### Task 8: 代码审查和最终验证 **Files:** - Review: `ruoyi-ui/src/views/ccdiEmployee/index.vue` **Step 1: 代码审查清单** - [ ] 所有新增方法都有适当的注释 - [ ] localStorage操作都有try-catch保护 - [ ] 错误处理覆盖了主要场景(404, 500, 网络错误) - [ ] 代码格式符合项目规范 - [ ] 没有console.log等调试代码残留 - [ ] 没有硬编码的测试数据 **Step 2: 最终功能回归测试** 按照 Task 6 的所有测试场景再执行一遍,确保所有功能正常。 **Step 3: 浏览器控制台检查** 打开浏览器控制台,执行以下操作,确认没有错误或警告: 1. 刷新页面 2. 完成一次导入 3. 切换菜单 4. 查看失败记录 **Step 4: 性能检查** 打开浏览器开发者工具 > Performance 或 Lighthouse(如果可用): 1. 录制页面加载过程 2. 确认localStorage读写操作不会明显影响页面加载性能 3. 预期: 增加的开销 < 10ms **Step 5: 最终提交** 所有代码已经在前面的任务中提交,这里只需确认所有提交都已完成: ```bash # 查看最近的提交历史 git log --oneline -10 ``` 应该看到以下提交: 1. `feat: 添加localStorage工具方法用于导入状态持久化` 2. `feat: 添加导入状态恢复和用户交互方法` 3. `feat: 修改导入处理逻辑以支持状态持久化` 4. `feat: 增强失败记录查询的错误处理` 5. `feat: 添加UI优化和用户体验增强` 6. (可选) `docs: 补充导入结果持久化说明` **Step 6: 创建功能总结提交** ```bash git commit --allow-empty -m "feat: 完成员工导入结果跨页面持久化功能 功能概述: - 使用localStorage存储最近一次导入任务信息 - 支持切换菜单后查看上一次的导入失败记录 - 自动过期处理(7天) - 完整的错误处理和用户友好的提示信息 - 新增清除历史记录功能 测试场景: - 导入成功无失败后刷新页面 - 导入有失败后刷新页面 - 导入有失败后切换菜单 - 新导入覆盖旧记录 - 手动清除历史记录 - localStorage过期处理 相关提交: - b932a7d docs: 添加员工导入结果跨页面持久化设计文档 Co-Authored-By: Claude Sonnet 4.5 " ``` --- ## 附录 ### A. 相关设计文档 - `doc/plans/2026-02-06-employee-import-result-persistence-design.md` - 详细设计文档 - `doc/plans/2026-02-06-employee-async-import-design.md` - 异步导入功能设计文档 ### B. 测试数据准备 **正确的Excel文件**: - 柜员号: 7位数字,唯一 - 姓名: 非空 - 身份证号: 18位有效身份证号 - 部门: 系统中存在的部门ID - 电话: 11位手机号 - 状态: 0(在职)或1(离职) **包含错误数据的Excel文件**: - 至少包含以下几种错误: - 重复的柜员号 - 无效的身份证号(位数不对或校验位错误) - 不存在的部门ID - 无效的手机号格式 ### C. 常见问题排查 **问题1: 按钮不显示** - 检查localStorage是否有数据 - 检查hasFailures是否为true - 检查taskId是否存在 **问题2: 点击查询报错** - 检查后端API是否正常 - 检查taskId是否有效 - 查看浏览器控制台的错误信息 **问题3: 数据没有持久化** - 检查浏览器是否支持localStorage - 检查是否在隐私模式/无痕模式 - 查看控制台是否有异常 ### D. 回滚方案 如果需要回滚此功能: ```bash # 查看提交历史 git log --oneline # 回滚到功能之前的提交(假设功能前的提交是 abc1234) git revert abc1234..HEAD # 或者硬重置(慎用) git reset --hard abc1234 ``` --- **计划版本**: 1.0 **创建日期**: 2026-02-06 **预计工时**: 2-3小时