fix: 胶囊进度按子任务加权 + 调研任务清单必填+预设选项
1. 胶囊进度计算改为子任务加权:
- 分子 = 所有plan中已完成的子任务数
- 分母 = 所有plan的子任务总数
- completed的plan视为其子任务全部完成
- 没有子任务的plan按1项计算(不会导致分母为0)
- 例:4个completed(共8子任务) + 1个pending(2子任务)
= 8/(8+2) = 80%
2. 调研任务清单必须至少1项:
- handleSubmit 加校验:tasks.length === 0 时 return
- 红色提示:"请至少添加一项任务"
3. 预设可选任务选项(空列表时显示):
- 竞品分析、用户访谈、数据调研
- 技术可行性分析、市场调研、需求分析
- 点击即添加,也可自定义输入
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -303,27 +303,33 @@ export default function VersionDetailPage() {
|
||||
|
||||
const calcGroupProgress = (group: typeof vPlans, type: 'research' | 'product' | 'ui') => {
|
||||
if (group.length === 0) return 0;
|
||||
// 每个 plan 按权重贡献进度:completed=1, in_progress=子任务完成率, pending=0
|
||||
let totalWeight = 0;
|
||||
// 分子=已完成子任务数,分母=所有plan的子任务总数
|
||||
// completed 的 plan:其子任务全算已完成
|
||||
let totalItems = 0;
|
||||
let doneItems = 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;
|
||||
}
|
||||
if (type === 'research') {
|
||||
const tasks = p.tasks || [];
|
||||
const count = Math.max(tasks.length, 1);
|
||||
totalItems += count;
|
||||
if (p.status === 'completed') {
|
||||
doneItems += count;
|
||||
} else {
|
||||
doneItems += tasks.filter((t) => t.status === 'completed').length;
|
||||
}
|
||||
} else {
|
||||
const linked = p.linkedRequirementIds || [];
|
||||
const count = Math.max(linked.length, 1);
|
||||
totalItems += count;
|
||||
if (p.status === 'completed') {
|
||||
doneItems += count;
|
||||
} else {
|
||||
const linked = p.linkedRequirementIds || [];
|
||||
const completed = p.completedRequirementIds || [];
|
||||
if (linked.length > 0) {
|
||||
totalWeight += completed.filter((id) => linked.includes(id)).length / linked.length;
|
||||
}
|
||||
doneItems += completed.filter((id) => linked.includes(id)).length;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.round((totalWeight / group.length) * 100);
|
||||
return totalItems > 0 ? Math.round((doneItems / totalItems) * 100) : 0;
|
||||
};
|
||||
|
||||
const getPlanStatus = (group: typeof vPlans): 'idle' | 'active' | 'done' => {
|
||||
|
||||
Reference in New Issue
Block a user