fix: 调研/产品/UI阶段耗时改为精确时间戳计算
- VersionPlan 新增 actualStartAt 字段 - updatePlan 流转到 in_progress 时自动记录 actualStartAt - updatePlan 流转到 completed 时自动记录 completedAt(如未设) - 概览阶段耗时改用 actualStartAt/completedAt 计算(不再用计划排期) - 个人耗时汇总同步使用实际时间戳 所有阶段(调研/产品/UI/开发/测试)现在统一用精确时间戳算耗时。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -393,15 +393,24 @@ export default function VersionDetailPage() {
|
|||||||
|
|
||||||
// 调研阶段
|
// 调研阶段
|
||||||
const researchPlans = versionPlans.filter((p) => p.type === 'research');
|
const researchPlans = versionPlans.filter((p) => p.type === 'research');
|
||||||
const researchCal = calcCalendar(researchPlans.map((p) => p.startTime), researchPlans.filter((p) => p.status === 'completed').map((p) => p.endTime));
|
const researchCal = calcCalendar(
|
||||||
|
researchPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!),
|
||||||
|
researchPlans.filter((p) => p.completedAt).map((p) => p.completedAt!),
|
||||||
|
);
|
||||||
|
|
||||||
// 产品方案阶段
|
// 产品方案阶段
|
||||||
const productPlans = versionPlans.filter((p) => p.type === 'product');
|
const productPlans = versionPlans.filter((p) => p.type === 'product');
|
||||||
const productCal = calcCalendar(productPlans.map((p) => p.startTime), productPlans.filter((p) => p.status === 'completed').map((p) => p.endTime));
|
const productCal = calcCalendar(
|
||||||
|
productPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!),
|
||||||
|
productPlans.filter((p) => p.completedAt).map((p) => p.completedAt!),
|
||||||
|
);
|
||||||
|
|
||||||
// UI设计阶段
|
// UI设计阶段
|
||||||
const uiPlans = versionPlans.filter((p) => p.type === 'ui');
|
const uiPlans = versionPlans.filter((p) => p.type === 'ui');
|
||||||
const uiCal = calcCalendar(uiPlans.map((p) => p.startTime), uiPlans.filter((p) => p.status === 'completed').map((p) => p.endTime));
|
const uiCal = calcCalendar(
|
||||||
|
uiPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!),
|
||||||
|
uiPlans.filter((p) => p.completedAt).map((p) => p.completedAt!),
|
||||||
|
);
|
||||||
|
|
||||||
// 开发阶段
|
// 开发阶段
|
||||||
const devCal = calcCalendar(
|
const devCal = calcCalendar(
|
||||||
@@ -434,9 +443,8 @@ export default function VersionDetailPage() {
|
|||||||
|
|
||||||
// 调研/产品/UI 用 startTime→endTime 计算
|
// 调研/产品/UI 用 startTime→endTime 计算
|
||||||
versionPlans.forEach((p) => {
|
versionPlans.forEach((p) => {
|
||||||
if (!p.owner || !p.startTime) return;
|
if (!p.owner || !p.actualStartAt) return;
|
||||||
const end = p.status === 'completed' ? p.endTime : undefined;
|
const hours = calcActualHoursByDates(p.actualStartAt, p.completedAt);
|
||||||
const hours = calcActualHoursByDates(p.startTime.slice(0, 10), end?.slice(0, 10));
|
|
||||||
addHours(p.owner, p.type === 'research' ? 'research' : p.type === 'product' ? 'product' : 'ui', hours);
|
addHours(p.owner, p.type === 'research' ? 'research' : p.type === 'product' ? 'product' : 'ui', hours);
|
||||||
});
|
});
|
||||||
versionDevTasks.forEach((t) => {
|
versionDevTasks.forEach((t) => {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface VersionPlan {
|
|||||||
resultFileData?: string;
|
resultFileData?: string;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
overdueReason?: string;
|
overdueReason?: string;
|
||||||
|
actualStartAt?: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
completedAt?: string;
|
completedAt?: string;
|
||||||
addedBy: string;
|
addedBy: string;
|
||||||
|
|||||||
@@ -43,7 +43,18 @@ export const useVersionPlanStore = create<VersionPlanState>((set, get) => ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
updatePlan: (id, data) => {
|
updatePlan: (id, data) => {
|
||||||
const plans = get().plans.map((p) => p.id === id ? { ...p, ...data } : p);
|
const now = new Date().toISOString();
|
||||||
|
const plans = get().plans.map((p) => {
|
||||||
|
if (p.id !== id) return p;
|
||||||
|
const patch = { ...data };
|
||||||
|
if (patch.status === 'in_progress' && !p.actualStartAt) {
|
||||||
|
(patch as any).actualStartAt = now;
|
||||||
|
}
|
||||||
|
if (patch.status === 'completed' && !p.completedAt) {
|
||||||
|
(patch as any).completedAt = now;
|
||||||
|
}
|
||||||
|
return { ...p, ...patch };
|
||||||
|
});
|
||||||
set({ plans });
|
set({ plans });
|
||||||
saveLocal(plans);
|
saveLocal(plans);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user