feat(workspace): 增加工作活动日报引擎

This commit is contained in:
Script Generator
2026-06-26 17:17:35 +08:00
parent 64491789ae
commit 4278e4d565
21 changed files with 1090 additions and 24 deletions

View File

@@ -0,0 +1,108 @@
'use client';
import { create } from 'zustand';
import { formatLocalDate } from '@/lib/format';
import { mergeWorkActivities, type WorkActivity, type WorkActivityDraft, type WorkActivitySourceType } from '@/lib/work-activity';
import { loadServerData, saveServerData } from '@/lib/server-data';
interface ProgressNoteInput {
actorId: string;
sourceType: WorkActivitySourceType;
sourceId: string;
title: string;
note: string;
blocker?: string;
helperId?: string;
delayRisk?: string;
}
interface WorkActivityState {
activities: WorkActivity[];
fetchActivities: () => Promise<void>;
addActivity: (data: WorkActivityDraft) => WorkActivity;
addProgressNote: (data: ProgressNoteInput) => WorkActivity;
deleteActivity: (id: string) => void;
}
async function saveStored(
items: WorkActivity[],
setActivities?: (items: WorkActivity[]) => void,
options: { mergeRemote?: boolean } = {},
) {
try {
if (options.mergeRemote === false) {
await saveServerData('work-activities', items);
return;
}
const remote = await loadServerData<WorkActivity[]>('work-activities');
const merged = mergeWorkActivities(Array.isArray(remote) ? remote : [], items);
setActivities?.(merged);
await saveServerData('work-activities', merged);
} catch {
saveServerData('work-activities', items).catch(() => {});
}
}
async function loadStored(): Promise<WorkActivity[] | null> {
try {
return await loadServerData<WorkActivity[]>('work-activities');
} catch {}
return null;
}
function createActivityId(): string {
return `act-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
}
export const useWorkActivityStore = create<WorkActivityState>((set, get) => ({
activities: [],
fetchActivities: async () => {
const cached = await loadStored();
if (cached) set({ activities: cached });
},
addActivity: (data) => {
const now = new Date();
const item: WorkActivity = {
...data,
id: createActivityId(),
date: data.date ?? formatLocalDate(now),
occurredAt: data.occurredAt ?? now.toISOString(),
};
const updated = [...get().activities, item];
set({ activities: updated });
saveStored(updated, (activities) => set({ activities }));
return item;
},
addProgressNote: (data) => {
const details = [
data.note.trim(),
data.blocker?.trim() ? `阻塞:${data.blocker.trim()}` : '',
data.helperId?.trim() ? `需协助:${data.helperId.trim()}` : '',
data.delayRisk?.trim() ? `延期风险:${data.delayRisk.trim()}` : '',
].filter(Boolean);
return get().addActivity({
actorId: data.actorId,
sourceType: data.sourceType,
sourceId: data.sourceId,
action: 'progress_note_added',
category: data.blocker?.trim() || data.delayRisk?.trim() ? 'risk' : 'note',
title: data.title,
summary: `补充进展:${details.join('')}`,
metadata: {
note: data.note.trim(),
blocker: data.blocker?.trim() || undefined,
helperId: data.helperId?.trim() || undefined,
delayRisk: data.delayRisk?.trim() || undefined,
},
});
},
deleteActivity: (id) => {
const updated = get().activities.filter((activity) => activity.id !== id);
set({ activities: updated });
saveStored(updated, (activities) => set({ activities }), { mergeRemote: false });
},
}));