feat(test-case+bug): 测试用例模块 + Bug 模块完整实现

需求验收体系:
- 测试用例:五态状态机(待执行/执行中/通过/失败/阻塞)
- Bug:六态状态机(待修复/修复中/已修复/验证中/已关闭/已拒绝)
- Bug 通过测试用例间接关联需求(不冗余存版本)
- Bug 默认修复人 = 关联需求的开发任务负责人
- 测试进度 = 已执行用例/总用例, 通过率 = 通过/已执行

UI:
- 版本详情页新增"测试用例" Tab + "BUG" Tab
- 测试用例:统计栏+筛选+按需求分组列表+详情抽屉+提BUG入口
- Bug:统计栏+筛选+列表+详情抽屉(链式跳转用例→需求)
- 概览胶囊"测试"阶段进度联动
- "与我相关"新增测试用例/Bug分组

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-12 13:11:08 +08:00
parent 75b0d12fa0
commit 7fe3a3854a
16 changed files with 1296 additions and 5 deletions

View File

@@ -18,8 +18,12 @@ import { OVERTIME_REASON_LABEL } from '@/lib/overtime';
import { VersionRequirementsTab } from '@/components/version/VersionRequirementsTab';
import { PlanTab } from '@/components/version/PlanTab';
import { DevTaskTab } from '@/components/dev-task/DevTaskTab';
import { TestCaseTab } from '@/components/test-case/TestCaseTab';
import { BugTab } from '@/components/bug/BugTab';
import { useVersionPlanStore } from '@/stores/useVersionPlanStore';
import { useDevTaskStore } from '@/stores/useDevTaskStore';
import { useTestCaseStore } from '@/stores/useTestCaseStore';
import { useBugStore } from '@/stores/useBugStore';
import { useAuthStore } from '@/stores/useAuthStore';
import { calcGroupProgress as calcDevTaskProgress } from '@/lib/dev-task';
@@ -51,6 +55,8 @@ export default function VersionDetailPage() {
const { records, fetchRecords } = useOvertimeStore();
const { plans, fetchPlans, createPlan, updatePlan, completePlan, deletePlan } = useVersionPlanStore();
const { tasks: devTasks, fetchTasks: fetchDevTasks } = useDevTaskStore();
const { testCases, fetchTestCases } = useTestCaseStore();
const { bugs, fetchBugs } = useBugStore();
const user = useAuthStore((s) => s.user);
const [activeTab, setActiveTab] = useState('overview');
@@ -59,6 +65,8 @@ export default function VersionDetailPage() {
useEffect(() => { fetchRecords(); }, [fetchRecords]);
useEffect(() => { fetchPlans(); }, [fetchPlans]);
useEffect(() => { fetchDevTasks(); }, [fetchDevTasks]);
useEffect(() => { fetchTestCases(); }, [fetchTestCases]);
useEffect(() => { fetchBugs(); }, [fetchBugs]);
const version = useMemo(() => getVersionDetail(overview, versionId), [overview, versionId]);
@@ -299,6 +307,14 @@ export default function VersionDetailPage() {
stageProgress['dev'] = { percent: devProgress, daysSpent: devDays };
}
// 测试阶段进度由 TestCase 完成率驱动
const versionTestCases = testCases.filter((c) => versionReqs.some((r) => r.id === c.requirementId));
if (versionTestCases.length > 0) {
const executed = versionTestCases.filter((c) => c.status === 'passed' || c.status === 'failed' || c.status === 'blocked').length;
const testPercent = Math.round((executed / versionTestCases.length) * 100);
stageProgress['testing'] = { percent: testPercent, daysSpent: 0 };
}
return <CapsuleStages currentStage={version.currentStage} progress={version.progress} stageProgress={stageProgress} />;
})()}
@@ -476,6 +492,16 @@ export default function VersionDetailPage() {
/>
);
})()
) : activeTab === 'testcases' ? (
(() => {
const versionReqs = requirements.filter((r) => r.versionId === version.id);
return <TestCaseTab versionId={version.id} requirementIds={versionReqs.map((r) => r.id)} />;
})()
) : activeTab === 'bugs' ? (
(() => {
const versionReqs = requirements.filter((r) => r.versionId === version.id);
return <BugTab versionId={version.id} requirementIds={versionReqs.map((r) => r.id)} />;
})()
) : (
<div className="rounded-xl border border-[var(--line)] bg-[var(--bg-card)] p-12 flex items-center justify-center">
<span className="text-[13px] text-[var(--ink-muted)]"></span>