diff --git a/apps/web/components/test-case/TestCaseTab.tsx b/apps/web/components/test-case/TestCaseTab.tsx index 8d1dc44..a11bd44 100644 --- a/apps/web/components/test-case/TestCaseTab.tsx +++ b/apps/web/components/test-case/TestCaseTab.tsx @@ -1,9 +1,10 @@ 'use client'; import { useState, useMemo, useEffect } from 'react'; -import { Plus, ClipboardCheck, Filter } from 'lucide-react'; +import { Plus, ClipboardCheck, Filter, CheckCircle2 } from 'lucide-react'; import { useTestCaseStore } from '@/stores/useTestCaseStore'; import { useBugStore } from '@/stores/useBugStore'; +import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useAuthStore } from '@/stores/useAuthStore'; import { TestCaseRow } from './TestCaseRow'; @@ -22,6 +23,7 @@ interface Props { export function TestCaseTab({ versionId, requirementIds }: Props) { const { testCases, fetchTestCases } = useTestCaseStore(); const { bugs, fetchBugs } = useBugStore(); + const { tasks: devTasks } = useDevTaskStore(); const { requirements } = useRequirementStore(); const user = useAuthStore((s) => s.user); @@ -30,9 +32,13 @@ export function TestCaseTab({ versionId, requirementIds }: Props) { const versionCases = useMemo( () => testCases.filter((c) => c.versionId === versionId), - [testCases, requirementIds], + [testCases, versionId], ); + // 提测状态判断 + const versionDevTasks = useMemo(() => devTasks.filter((t) => requirementIds.includes(t.requirementId)), [devTasks, requirementIds]); + const allSubmitted = versionDevTasks.length > 0 && versionDevTasks.every((t) => t.status === 'submitted' || t.status === 'done'); + const stats = calcTestProgress(versionCases); // 筛选 @@ -73,6 +79,19 @@ export function TestCaseTab({ versionId, requirementIds }: Props) { return (
+ {/* 提测通知 */} + {allSubmitted && ( +
+ + 开发已全部提测,可以开始测试 + {versionDevTasks.length > 0 && {versionDevTasks.length} 个开发任务已交付} +
+ )} + {versionDevTasks.length > 0 && !allSubmitted && ( +
+ 开发进行中,尚未全部提测({versionDevTasks.filter((t) => t.status === 'submitted' || t.status === 'done').length}/{versionDevTasks.length}) +
+ )} {/* 统计栏 */}
diff --git a/apps/web/stores/useTestCaseStore.ts b/apps/web/stores/useTestCaseStore.ts index 35fbf7c..266c05e 100644 --- a/apps/web/stores/useTestCaseStore.ts +++ b/apps/web/stores/useTestCaseStore.ts @@ -83,6 +83,26 @@ export const useTestCaseStore = create((set, get) => ({ if (to === 'blocked' && extra?.blockReason) patch.blockReason = extra.blockReason; if (to === 'running') { patch.failReason = undefined; patch.blockReason = undefined; } get().updateTestCase(id, patch); + + // 副作用:当版本所有用例通过 + Bug全关闭 → DevTask 自动完成 + if (to === 'passed' && tc.versionId) { + setTimeout(() => { + try { + const { useDevTaskStore } = require('@/stores/useDevTaskStore'); + const { useBugStore } = require('@/stores/useBugStore'); + const allCases = get().testCases.filter((c) => c.versionId === tc.versionId); + const allPassed = allCases.every((c) => c.id === id ? true : c.status === 'passed'); + if (!allPassed) return; + const allBugs = useBugStore.getState().bugs.filter((b: any) => b.versionId === tc.versionId); + const allBugsClosed = allBugs.every((b: any) => b.status === 'closed' || b.status === 'rejected'); + if (!allBugsClosed) return; + const devStore = useDevTaskStore.getState(); + const versionTasks = devStore.tasks.filter((t: any) => t.status === 'submitted'); + versionTasks.forEach((t: any) => devStore.changeStatus(t.id, 'done')); + } catch {} + }, 0); + } + return { ok: true }; },