Files
pengcheng-exam-teacher/TASKID_FIX_SUMMARY.md
2025-10-20 15:37:28 +08:00

5.0 KiB
Raw Blame History

TaskId 丢失问题修复说明

问题描述

在 Tauri 两个独立窗口之间通信时,容易出现 taskId 丢失的情况,导致编辑窗口中的 taskId 为空,影响后续操作。

根本原因

  1. 缺少数据持久化窗口之间传递数据时如果事件丢失或延迟taskId 会丢失
  2. 缺少数据准备状态控制:组件在数据未到达时就开始渲染,导致 taskId 为空
  3. 缺少兜底机制没有处理不同字段名taskId/taskid/id的情况
  4. 缺少超时机制:如果事件一直没收到,窗口会一直处于等待状态

修复方案

1. 引入 taskStorage 模块

使用 localStorage 持久化存储 taskId确保数据不会因窗口通信失败而丢失

import {
  setTaskId,
  bindBeforeUnloadClear,
  clearTaskId,
  unbindBeforeUnloadClear,
  getTaskId
} from '../../../taskStorage'

2. 添加数据准备状态控制

添加 isDataReady 状态,在数据未准备好之前显示加载界面:

const isDataReady = ref(false)
<div v-if="!isDataReady" class="loading-overlay">
  <el-empty description="正在加载数据..." />
</div>

3. 添加超时机制

如果 5 秒内没有收到数据,也显示界面(避免永久等待):

const timeout = setTimeout(() => {
  console.warn(`5秒内未收到数据显示空界面`)
  isDataReady.value = true
}, 5000)

4. 规范化和兜底 taskId

支持多种字段名,并使用 localStorage 作为兜底:

// 规范化并兜底 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

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 持久化机制

关键改进点

前置

// ❌ 旧代码
let unlistenFunc: (() => void) | null = null

// ✅ 新代码
const isDataReady = ref(false)
let unlistenFunc: any = null

事件监听

// ❌ 旧代码
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
  }
})

清理

// ❌ 旧代码
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 兜底
  • 数据加载时显示友好的加载界面
  • 支持多种字段名格式
  • 超时后不会永久卡住
  • 窗口关闭后正确清理数据