From 4278e4d5658d47f2b58135e4ceab343512f58707 Mon Sep 17 00:00:00 2001 From: Script Generator Date: Fri, 26 Jun 2026 17:17:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(workspace):=20=E5=A2=9E=E5=8A=A0=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B4=BB=E5=8A=A8=E6=97=A5=E6=8A=A5=E5=BC=95=E6=93=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/server/src/modules/data/data-keys.ts | 1 + .../src/modules/data/data.service.spec.ts | 4 + apps/web/app/workspace/page.tsx | 6 +- .../dev-task/DevTaskDetailDrawer.tsx | 72 +++++ .../components/workspace/DailyReportPanel.tsx | 102 +++++-- apps/web/lib/server-data.ts | 1 + apps/web/lib/work-activity-factory.test.ts | 130 +++++++++ apps/web/lib/work-activity-factory.ts | 270 ++++++++++++++++++ apps/web/lib/work-activity.test.ts | 33 +++ apps/web/lib/work-activity.ts | 69 +++++ apps/web/lib/workspace-daily-report.test.ts | 109 +++++++ apps/web/lib/workspace-daily-report.ts | 105 ++++++- apps/web/stores/useBugStore.ts | 10 + apps/web/stores/useDevTaskStore.ts | 16 ++ apps/web/stores/useTestCaseStore.ts | 12 + apps/web/stores/useVersionPlanStore.ts | 19 +- apps/web/stores/useWorkActivityStore.ts | 108 +++++++ docs/architecture.md | 12 + docs/decisions.md | 13 + docs/roadmap.md | 4 + docs/workflow.md | 18 ++ 21 files changed, 1090 insertions(+), 24 deletions(-) create mode 100644 apps/web/lib/work-activity-factory.test.ts create mode 100644 apps/web/lib/work-activity-factory.ts create mode 100644 apps/web/lib/work-activity.test.ts create mode 100644 apps/web/lib/work-activity.ts create mode 100644 apps/web/stores/useWorkActivityStore.ts diff --git a/apps/server/src/modules/data/data-keys.ts b/apps/server/src/modules/data/data-keys.ts index b9d8f8a..afdd4e4 100644 --- a/apps/server/src/modules/data/data-keys.ts +++ b/apps/server/src/modules/data/data-keys.ts @@ -8,6 +8,7 @@ export const APP_DATA_KEYS = [ 'members', 'task-categories', 'task-worklogs', + 'work-activities', 'overtime', ] as const; diff --git a/apps/server/src/modules/data/data.service.spec.ts b/apps/server/src/modules/data/data.service.spec.ts index ca1d56a..ec7eb1f 100644 --- a/apps/server/src/modules/data/data.service.spec.ts +++ b/apps/server/src/modules/data/data.service.spec.ts @@ -58,6 +58,10 @@ describe('DataService', () => { key: 'task-worklogs', value, }); + await expect(service.put('work-activities', value)).resolves.toEqual({ + key: 'work-activities', + value, + }); await expect(service.put('overtime', { records: [], reasons: [] })).resolves.toEqual({ key: 'overtime', value, diff --git a/apps/web/app/workspace/page.tsx b/apps/web/app/workspace/page.tsx index d1c36b8..e900573 100644 --- a/apps/web/app/workspace/page.tsx +++ b/apps/web/app/workspace/page.tsx @@ -11,6 +11,7 @@ import { useTestCaseStore } from '@/stores/useTestCaseStore'; import { useBugStore } from '@/stores/useBugStore'; import { useAuthStore } from '@/stores/useAuthStore'; import { useTaskWorklogStore } from '@/stores/useTaskWorklogStore'; +import { useWorkActivityStore } from '@/stores/useWorkActivityStore'; import { flattenVersions } from '@/lib/derive'; import { aggregateWorkItems, WORK_ITEM_TYPE_LABEL } from '@/lib/workspace-engine'; import type { WorkItem, WorkItemType } from '@/lib/workspace-engine'; @@ -55,6 +56,7 @@ export default function WorkspacePage() { const { testCases, fetchTestCases } = useTestCaseStore(); const { bugs, fetchBugs } = useBugStore(); const { worklogs, fetchWorklogs } = useTaskWorklogStore(); + const { activities, fetchActivities } = useWorkActivityStore(); const user = useAuthStore((s) => s.user); const [activeTab, setActiveTab] = useState('all'); const [showCompleted, setShowCompleted] = useState(true); @@ -69,6 +71,7 @@ export default function WorkspacePage() { useEffect(() => { fetchTestCases(); }, [fetchTestCases]); useEffect(() => { fetchBugs(); }, [fetchBugs]); useEffect(() => { fetchWorklogs(); }, [fetchWorklogs]); + useEffect(() => { fetchActivities(); }, [fetchActivities]); const userName = user?.name ?? ''; const allVersions = useMemo(() => flattenVersions(overview), [overview]); @@ -122,12 +125,13 @@ export default function WorkspacePage() { const dailyReport = useMemo(() => getWorkspaceDailyReport({ + activities, worklogs, workItems, userId: userName, date: today, }), - [worklogs, workItems, userName, today] + [activities, worklogs, workItems, userName, today] ); // 待办数量按 tab 分(受 version filter 影响) diff --git a/apps/web/components/dev-task/DevTaskDetailDrawer.tsx b/apps/web/components/dev-task/DevTaskDetailDrawer.tsx index 90e4f5e..ab1eb1c 100644 --- a/apps/web/components/dev-task/DevTaskDetailDrawer.tsx +++ b/apps/web/components/dev-task/DevTaskDetailDrawer.tsx @@ -5,6 +5,7 @@ import { X, AlertTriangle, Link2, ChevronRight, Clock, User, Tag, Play, Trash2, import { StatusBadge } from './StatusBadge'; import { CategoryChip } from './CategoryChip'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; +import { useWorkActivityStore } from '@/stores/useWorkActivityStore'; import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useMemberStore } from '@/stores/useMemberStore'; @@ -29,6 +30,7 @@ interface Props { export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose, contextLabel }: Props) { const { tasks, changeStatus, setBlocked, deleteTask, updateTask } = useDevTaskStore(); + const addProgressNote = useWorkActivityStore((s) => s.addProgressNote); const { categories } = useTaskCategoryStore(); const { requirements } = useRequirementStore(); const { members } = useMemberStore(); @@ -36,6 +38,10 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose, contextLabel const [transferTo, setTransferTo] = useState(''); const [showDelayInput, setShowDelayInput] = useState(false); const [delayReason, setDelayReason] = useState(''); + const [progressNote, setProgressNote] = useState(''); + const [progressBlocker, setProgressBlocker] = useState(''); + const [progressHelperId, setProgressHelperId] = useState(''); + const [progressDelayRisk, setProgressDelayRisk] = useState(''); const task = tasks.find((t) => t.id === taskId); if (!task) return null; @@ -86,6 +92,28 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose, contextLabel setBlockReason(''); }; + const handleProgressNote = () => { + const note = progressNote.trim(); + const blocker = progressBlocker.trim(); + const delayRisk = progressDelayRisk.trim(); + if (!note && !blocker && !delayRisk) return; + + addProgressNote({ + actorId: task.assigneeId, + sourceType: 'dev_task', + sourceId: task.id, + title: task.title, + note: note || '今日进展已更新', + blocker: blocker || undefined, + helperId: progressHelperId || undefined, + delayRisk: delayRisk || undefined, + }); + setProgressNote(''); + setProgressBlocker(''); + setProgressHelperId(''); + setProgressDelayRisk(''); + }; + return (
e.stopPropagation()}> @@ -199,6 +227,50 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose, contextLabel
+ {task.status !== 'todo' && task.status !== 'submitted' && ( +
+
今日进展
+