feat: 版本详情完善关联关系处理
1. 删除版本时清理所有孤儿数据: - 释放关联需求回需求池 - 删除计划任务(调研/产品/UI) - 删除开发任务 - 删除测试用例 - 删除 Bug 2. Header 面包屑链路化: 版本 / 产品名(可点击) / 项目名 / 版本名 点击产品名跳转产品页 3. 概览统计卡片增加完整链路数据: 关联需求 | 计划任务 | 开发任务 | 测试用例 | 待开发 | 开发中 | 未关闭Bug | 加班时长 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,9 +54,9 @@ export default function VersionDetailPage() {
|
|||||||
const { requirements, fetchRequirements, updateRequirement } = useRequirementStore();
|
const { requirements, fetchRequirements, updateRequirement } = useRequirementStore();
|
||||||
const { records, fetchRecords } = useOvertimeStore();
|
const { records, fetchRecords } = useOvertimeStore();
|
||||||
const { plans, fetchPlans, createPlan, updatePlan, completePlan, deletePlan } = useVersionPlanStore();
|
const { plans, fetchPlans, createPlan, updatePlan, completePlan, deletePlan } = useVersionPlanStore();
|
||||||
const { tasks: devTasks, fetchTasks: fetchDevTasks } = useDevTaskStore();
|
const { tasks: devTasks, fetchTasks: fetchDevTasks, deleteTask: deleteDevTask } = useDevTaskStore();
|
||||||
const { testCases, fetchTestCases } = useTestCaseStore();
|
const { testCases, fetchTestCases, deleteTestCase } = useTestCaseStore();
|
||||||
const { bugs, fetchBugs } = useBugStore();
|
const { bugs, fetchBugs, deleteBug } = useBugStore();
|
||||||
const user = useAuthStore((s) => s.user);
|
const user = useAuthStore((s) => s.user);
|
||||||
const [activeTab, setActiveTab] = useState('overview');
|
const [activeTab, setActiveTab] = useState('overview');
|
||||||
|
|
||||||
@@ -117,9 +117,17 @@ export default function VersionDetailPage() {
|
|||||||
const buttons: { label: string; action: () => void; danger?: boolean }[] = [];
|
const buttons: { label: string; action: () => void; danger?: boolean }[] = [];
|
||||||
if (version.status === 'planned') {
|
if (version.status === 'planned') {
|
||||||
buttons.push({ label: '删除', action: () => {
|
buttons.push({ label: '删除', action: () => {
|
||||||
if (confirm('确认删除该版本?关联的需求会回到需求池。')) {
|
if (confirm('确认删除该版本?关联的需求会回到需求池,版本下的计划、开发任务、测试用例、Bug 将被清除。')) {
|
||||||
// 释放关联需求
|
// 释放关联需求
|
||||||
requirements.filter((r) => r.versionId === version.id).forEach((r) => updateRequirement(r.id, { versionId: undefined, addedToVersionBy: undefined }));
|
requirements.filter((r) => r.versionId === version.id).forEach((r) => updateRequirement(r.id, { versionId: undefined, addedToVersionBy: undefined }));
|
||||||
|
// 清理计划任务
|
||||||
|
plans.filter((p) => p.versionId === version.id).forEach((p) => deletePlan(p.id));
|
||||||
|
// 清理开发任务
|
||||||
|
const versionReqIds = new Set(requirements.filter((r) => r.versionId === version.id).map((r) => r.id));
|
||||||
|
devTasks.filter((t) => versionReqIds.has(t.requirementId)).forEach((t) => deleteDevTask(t.id));
|
||||||
|
// 清理测试用例和Bug
|
||||||
|
testCases.filter((c) => c.versionId === version.id).forEach((c) => deleteTestCase(c.id));
|
||||||
|
bugs.filter((b) => b.versionId === version.id).forEach((b) => deleteBug(b.id));
|
||||||
deleteVersion(version.productId, version.id);
|
deleteVersion(version.productId, version.id);
|
||||||
router.push('/versions');
|
router.push('/versions');
|
||||||
}
|
}
|
||||||
@@ -152,7 +160,15 @@ export default function VersionDetailPage() {
|
|||||||
<ChevronLeft className="h-3.5 w-3.5" strokeWidth={2} />版本
|
<ChevronLeft className="h-3.5 w-3.5" strokeWidth={2} />版本
|
||||||
</button>
|
</button>
|
||||||
<span className="ml-2 text-[var(--ink-muted)]">/</span>
|
<span className="ml-2 text-[var(--ink-muted)]">/</span>
|
||||||
<span className="ml-2 text-[15px] font-semibold text-[var(--ink)]">{version.name}</span>
|
<button onClick={() => router.push('/products')} className="ml-2 text-[12px] text-[var(--ink-muted)] hover:text-[var(--accent)] hover:underline">{version.productName}</button>
|
||||||
|
{version.projectName && version.projectName !== '未关联' && (
|
||||||
|
<>
|
||||||
|
<span className="ml-1.5 text-[var(--ink-muted)]">/</span>
|
||||||
|
<span className="ml-1.5 text-[12px] text-[var(--ink-muted)]">{version.projectName}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<span className="ml-1.5 text-[var(--ink-muted)]">/</span>
|
||||||
|
<span className="ml-1.5 text-[15px] font-semibold text-[var(--ink)]">{version.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">{renderActions()}</div>
|
<div className="flex items-center gap-2">{renderActions()}</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -236,13 +252,15 @@ export default function VersionDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 统计卡片 */}
|
{/* 统计卡片 */}
|
||||||
<div className="grid grid-cols-6 gap-3">
|
<div className="grid grid-cols-4 gap-3 sm:grid-cols-8">
|
||||||
<StatCard label="加班时长" value={`${totalOTHours}h`} accent />
|
|
||||||
<StatCard label="关联需求" value={versionReqs.length} />
|
<StatCard label="关联需求" value={versionReqs.length} />
|
||||||
|
<StatCard label="计划任务" value={plans.filter((p) => p.versionId === version.id).length} />
|
||||||
|
<StatCard label="开发任务" value={versionDevTasks.length} />
|
||||||
|
<StatCard label="测试用例" value={versionTCs.length} />
|
||||||
<StatCard label="待开发" value={devTaskTodo} />
|
<StatCard label="待开发" value={devTaskTodo} />
|
||||||
<StatCard label="开发中" value={devTaskInProgress} accent />
|
<StatCard label="开发中" value={devTaskInProgress} accent />
|
||||||
<StatCard label="阻塞中" value={devTaskBlocked} warn={devTaskBlocked > 0} />
|
|
||||||
<StatCard label="未关闭Bug" value={bugOpenCount} warn={bugOpenCount > 0} />
|
<StatCard label="未关闭Bug" value={bugOpenCount} warn={bugOpenCount > 0} />
|
||||||
|
<StatCard label="加班时长" value={`${totalOTHours}h`} accent />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 风险详情(胶囊条上方) */}
|
{/* 风险详情(胶囊条上方) */}
|
||||||
|
|||||||
Reference in New Issue
Block a user