# TaskId 丢失问题修复说明 ## 问题描述 在 Tauri 两个独立窗口之间通信时,容易出现 taskId 丢失的情况,导致编辑窗口中的 taskId 为空,影响后续操作。 ## 根本原因 1. **缺少数据持久化**:窗口之间传递数据时,如果事件丢失或延迟,taskId 会丢失 2. **缺少数据准备状态控制**:组件在数据未到达时就开始渲染,导致 taskId 为空 3. **缺少兜底机制**:没有处理不同字段名(taskId/taskid/id)的情况 4. **缺少超时机制**:如果事件一直没收到,窗口会一直处于等待状态 ## 修复方案 ### 1. 引入 taskStorage 模块 使用 localStorage 持久化存储 taskId,确保数据不会因窗口通信失败而丢失: ```typescript import { setTaskId, bindBeforeUnloadClear, clearTaskId, unbindBeforeUnloadClear, getTaskId } from '../../../taskStorage' ``` ### 2. 添加数据准备状态控制 添加 `isDataReady` 状态,在数据未准备好之前显示加载界面: ```typescript const isDataReady = ref(false) ``` ```vue
``` ### 3. 添加超时机制 如果 5 秒内没有收到数据,也显示界面(避免永久等待): ```typescript const timeout = setTimeout(() => { console.warn(`5秒内未收到数据,显示空界面`) isDataReady.value = true }, 5000) ``` ### 4. 规范化和兜底 taskId 支持多种字段名,并使用 localStorage 作为兜底: ```typescript // 规范化并兜底 taskId(支持 taskid/id),最后再持久化 const incomingId = data?.taskId ?? data?.taskid ?? data?.id const resolvedId = (incomingId ?? getTaskId() ?? '').toString() if (!form.taskId && resolvedId) { form.taskId = resolvedId } if (form.taskId) setTaskId(form.taskId) ``` ### 5. 清理机制 在窗口关闭或组件卸载时清理 taskId: ```typescript const handleCancel = async () => { clearTaskId() // 主动关闭时清空 taskId await emit('task-edit-closed') await getCurrentWindow().close() } onUnmounted(() => { clearTaskId() unbindBeforeUnloadClear() }) ``` ## 修复文件列表 1. ✅ `src/views/task/selftrans/ai/components/task-edit-window.vue` 2. ✅ `src/views/task/selftrans/random/components/task-edit-window.vue` 3. ✅ `src/views/task/selftrans/module/components/task-edit-window.vue` 4. ✅ `src/views/task/selftrans/collegeexam/components/task-edit-window.vue` ## 已存在的正确实现参考 - `src/views/task/exam/components/task-edit-window.vue` - 已经包含完整的 taskId 持久化机制 ## 关键改进点 ### 前置 ```typescript // ❌ 旧代码 let unlistenFunc: (() => void) | null = null // ✅ 新代码 const isDataReady = ref(false) let unlistenFunc: any = null ``` ### 事件监听 ```typescript // ❌ 旧代码 unlistenFunc = await listen(eventName, (event: any) => { const { data } = event.payload || {} if (data) { assignFields(data) activeStep.value = 0 } }) // ✅ 新代码 const timeout = setTimeout(() => { isDataReady.value = true }, 5000) unlistenFunc = await listen(eventName, (event: any) => { clearTimeout(timeout) const { data } = event.payload || {} if (data) { isDataReady.value = false assignFields(data) // 规范化和兜底 const incomingId = data?.taskId ?? data?.taskid ?? data?.id const resolvedId = (incomingId ?? getTaskId() ?? '').toString() if (!form.taskId && resolvedId) { form.taskId = resolvedId } if (form.taskId) setTaskId(form.taskId) isDataReady.value = true activeStep.value = 0 } }) ``` ### 清理 ```typescript // ❌ 旧代码 onUnmounted(() => { if (unlistenFunc) { unlistenFunc() } }) // ✅ 新代码 onUnmounted(() => { if (unlistenFunc) { unlistenFunc() unlistenFunc = null } clearTaskId() unbindBeforeUnloadClear() }) ``` ## 测试建议 1. **正常流程测试**:从列表页打开编辑窗口,检查 taskId 是否正确传递 2. **延迟测试**:模拟网络延迟,检查超时机制是否正常工作 3. **重复打开测试**:多次打开和关闭编辑窗口,检查是否有 taskId 残留 4. **跨步骤测试**:在编辑窗口的不同步骤间切换,检查 taskId 是否始终存在 5. **刷新测试**:在编辑窗口中刷新页面,检查 taskId 是否能从 localStorage 恢复 ## 注意事项 1. **TypeScript 编译错误是正常的**:在文件保存后,TypeScript 会重新编译,导入的函数会被识别为已使用 2. **bindBeforeUnloadClear 暂未使用**:这个函数在某些场景下可能需要,暂时保留导入 3. **不同模块的路径**:注意 `taskStorage` 的导入路径根据文件位置可能不同(`../../../taskStorage`) ## 预期效果 修复后,taskId 将更加稳定: - ✅ 窗口通信失败时有 localStorage 兜底 - ✅ 数据加载时显示友好的加载界面 - ✅ 支持多种字段名格式 - ✅ 超时后不会永久卡住 - ✅ 窗口关闭后正确清理数据