fix: 计划任务勾选逻辑 + 胶囊进度计算修正

1. 必须点击"开始"后才能勾选任务:
   - 调研/产品方案/UI设计的 checkbox 加 disabled 判断
   - plan.status !== 'in_progress' 时不可交互

2. 调研勾选改为二态(与产品/UI一致):
   - 去掉三态循环(pending→in_progress→completed)
   - 改为 toggle:未完成 ↔ 已完成
   - 显示文字也简化为"已完成"/"未完成"

3. 胶囊进度百分比计算修正:
   - 旧逻辑:所有子任务合并计算(新建空任务导致分母为0返回0%)
   - 新逻辑:以 plan 为单位加权计算
     - completed 的 plan = 权重 1
     - in_progress 的 plan = 子任务完成率
     - pending 的 plan = 权重 0
   - 公式:sum(weights) / plan_count * 100
   - 新建一个 pending 的 plan 不会把已完成的 100% 打回 0%

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-15 15:31:21 +08:00
parent 0a83ab8816
commit 5bbcf02962
2 changed files with 29 additions and 22 deletions

View File

@@ -303,23 +303,27 @@ export default function VersionDetailPage() {
const calcGroupProgress = (group: typeof vPlans, type: 'research' | 'product' | 'ui') => {
if (group.length === 0) return 0;
if (type === 'research') {
const totals = group.reduce((acc, p) => {
const tasks = p.tasks || [];
acc.total += tasks.length;
acc.done += tasks.filter((t) => t.status === 'completed').length;
return acc;
}, { total: 0, done: 0 });
return totals.total > 0 ? Math.round((totals.done / totals.total) * 100) : 0;
// 每个 plan 按权重贡献进度completed=1, in_progress=子任务完成率, pending=0
let totalWeight = 0;
for (const p of group) {
if (p.status === 'completed') {
totalWeight += 1;
} else if (p.status === 'in_progress') {
if (type === 'research') {
const tasks = p.tasks || [];
if (tasks.length > 0) {
totalWeight += tasks.filter((t) => t.status === 'completed').length / tasks.length;
}
} else {
const linked = p.linkedRequirementIds || [];
const completed = p.completedRequirementIds || [];
if (linked.length > 0) {
totalWeight += completed.filter((id) => linked.includes(id)).length / linked.length;
}
}
}
}
const totals = group.reduce((acc, p) => {
const linked = p.linkedRequirementIds || [];
const completed = p.completedRequirementIds || [];
acc.total += linked.length;
acc.done += completed.filter((id) => linked.includes(id)).length;
return acc;
}, { total: 0, done: 0 });
return totals.total > 0 ? Math.round((totals.done / totals.total) * 100) : 0;
return Math.round((totalWeight / group.length) * 100);
};
const getPlanStatus = (group: typeof vPlans): 'idle' | 'active' | 'done' => {