Files
pengcheng-exam-teacher/TASKID_FIX_SUMMARY.md

193 lines
5.0 KiB
Markdown
Raw Permalink Normal View History

2025-10-20 15:37:28 +08:00
# 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
<div v-if="!isDataReady" class="loading-overlay">
<el-empty description="正在加载数据..." />
</div>
```
### 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 兜底
- ✅ 数据加载时显示友好的加载界面
- ✅ 支持多种字段名格式
- ✅ 超时后不会永久卡住
- ✅ 窗口关闭后正确清理数据