import { computed, onMounted, ref, watch } from 'vue' import { CornerDownLeft, Database, Sparkles, Sun } from 'lucide-vue-next' import { scheduleCenterApi, type ScheduleCenterDateResponse, type ScheduleCenterDaySummary } from '@/api/scheduleCenter' export interface SidebarFocusItem { id: string; label: string; title: string; meta: string; tone: 'done' | 'doing' | 'pending' } export interface SidebarNewsItem { id: string; title: string; meta: string } export const sidebarCollapsedModules = [ { id: 'calendar', label: '日历', icon: Sun }, { id: 'status', label: '计划', icon: Database }, { id: 'focus', label: '重点', icon: Sparkles }, { id: 'review', label: '复盘', icon: CornerDownLeft }, ] function formatDateKey(date: Date) { const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${year}-${month}-${day}` } function formatMonthKey(date: Date) { const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') return `${year}-${month}` } export function useSidebarPlan(clientTimeRef: { value: Date }, loadDailyDigestFn: () => void) { const todayPlanDetail = ref(null) const monthPlanDays = ref([]) const todayDateKey = computed(() => formatDateKey(clientTimeRef.value)) const monthPlanSummaryMap = computed(() => new Map(monthPlanDays.value.map((item) => [item.date, item]))) const calendarCells = computed(() => { const year = clientTimeRef.value.getFullYear() const month = clientTimeRef.value.getMonth() const daysInMonth = new Date(year, month + 1, 0).getDate() const firstDayOffset = (new Date(year, month, 1).getDay() + 6) % 7 const cells: Array<{ key: string; value: number | null; active: boolean; busy: boolean }> = [] for (let index = 0; index < firstDayOffset; index += 1) { cells.push({ key: `blank-start-${index}`, value: null, active: false, busy: false }) } for (let day = 1; day <= daysInMonth; day += 1) { const monthDate = new Date(year, month, day) const dateKey = formatDateKey(monthDate) const summary = monthPlanSummaryMap.value.get(dateKey) const busy = Boolean(summary && (summary.todo_total + summary.task_due_total + summary.goal_total + summary.reminder_total) > 0) cells.push({ key: dateKey, value: day, active: day === clientTimeRef.value.getDate(), busy }) } while (cells.length % 7 !== 0) { cells.push({ key: `blank-end-${cells.length}`, value: null, active: false, busy: false }) } return cells }) const todayPlanCounters = computed(() => { const detail = todayPlanDetail.value if (!detail) return { done: 0, doing: 0, pending: 0, total: 0, completion: 0 } const todoDone = detail.todos.filter((item) => item.is_completed).length const todoPending = detail.todos.filter((item) => !item.is_completed).length const taskDone = detail.tasks.filter((item) => item.status === 'done').length const taskDoing = detail.tasks.filter((item) => item.status === 'in_progress').length const taskPending = detail.tasks.filter((item) => item.status === 'todo').length const goalDone = detail.goals.filter((item) => item.status === 'done').length const goalPending = detail.goals.filter((item) => item.status !== 'done').length const reminderDone = detail.reminders.filter((item) => item.status === 'done' || item.is_dismissed).length const reminderPending = detail.reminders.filter((item) => item.status !== 'done' && !item.is_dismissed).length const done = todoDone + taskDone + goalDone + reminderDone const doing = taskDoing const pending = todoPending + taskPending + goalPending + reminderPending const total = done + doing + pending return { done, doing, pending, total, completion: total > 0 ? Math.round((done / total) * 100) : 0 } }) const monthReviewStats = computed(() => monthPlanDays.value.reduce( (acc, item) => { acc.todoTotal += item.todo_total acc.todoCompleted += item.todo_completed acc.taskTotal += item.task_due_total acc.reminderTotal += item.reminder_total acc.goalTotal += item.goal_total acc.highPriorityTotal += item.high_priority_total if (item.todo_total + item.task_due_total + item.reminder_total + item.goal_total > 0) acc.activeDays += 1 return acc }, { todoTotal: 0, todoCompleted: 0, taskTotal: 0, reminderTotal: 0, goalTotal: 0, highPriorityTotal: 0, activeDays: 0 }, )) const sidebarWeekLabels = ['一', '二', '三', '四', '五', '六', '日'] const sidebarStatusHeadline = computed(() => ( todayPlanCounters.value.total ? `今日共 ${todayPlanCounters.value.total} 项计划,已完成 ${todayPlanCounters.value.done} 项` : '今日计划正在同步,稍后会显示最新状态' )) const sidebarStatusBreakdown = computed(() => [ { key: 'done', label: '已完成', value: todayPlanCounters.value.done, tone: 'done' }, { key: 'doing', label: '进行中', value: todayPlanCounters.value.doing, tone: 'doing' }, { key: 'pending', label: '未开始', value: todayPlanCounters.value.pending, tone: 'pending' }, ]) const sidebarFocusItems = computed(() => { const detail = todayPlanDetail.value if (!detail) return [] const goalItems = detail.goals.filter((goal) => goal.status !== 'done').map((goal) => ({ id: `goal-${goal.id}`, label: '目标', title: goal.title, meta: goal.note || '今日目标推进', tone: 'doing' as const, })) const taskItems = detail.tasks.filter((task) => task.status !== 'done' && task.status !== 'cancelled') .sort((a, b) => { const r = { urgent: 0, high: 1, medium: 2, low: 3 }; return r[a.priority] - r[b.priority] }) .map((task) => ({ id: `task-${task.id}`, label: task.priority === 'urgent' || task.priority === 'high' ? '高优任务' : '任务', title: task.title, meta: task.status === 'in_progress' ? '处理中' : '待启动', tone: task.status === 'in_progress' ? 'doing' as const : 'pending' as const, })) const reminderItems = detail.reminders.filter((r) => r.status !== 'done' && !r.is_dismissed) .map((r) => ({ id: `reminder-${r.id}`, label: '提醒', title: r.title, meta: r.reminder_at.slice(11, 16), tone: 'pending' as const })) const todoItems = detail.todos.filter((t) => !t.is_completed) .map((t) => ({ id: `todo-${t.id}`, label: '待办', title: t.title, meta: t.source === 'manual' ? '手动记录' : '系统同步', tone: 'pending' as const })) return [...goalItems, ...taskItems, ...reminderItems, ...todoItems].slice(0, 5) }) const sidebarReviewAchievements = computed(() => { const stats = monthReviewStats.value const items = [ stats.todoCompleted > 0 ? `累计完成 ${stats.todoCompleted} 项待办,执行节啬已形成闭环。` : '', stats.activeDays > 0 ? `本月已有 ${stats.activeDays} 天产生有效计划记录,日程连贯性稳定。` : '', stats.highPriorityTotal > 0 ? `高优事项共 ${stats.highPriorityTotal} 项进行中,重点任务没有脱离视野。` : '', ].filter(Boolean) if (items.length > 0) return items.slice(0, 3) return ['本月计划数据还在积累中,可以从今日重点开始逐步建立复盘样本。'] }) const sidebarReviewReflections = computed(() => { const stats = monthReviewStats.value const pendingTodoCount = Math.max(stats.todoTotal - stats.todoCompleted, 0) const items = [ pendingTodoCount > 0 ? `仍有 ${pendingTodoCount} 项待办未完成,建议拆成更短的收尾窗口。` : '', stats.highPriorityTotal >= 8 ? '高优事项密度偏高,最好提前锁定 1 到 2 个绝对优先级别。' : '', stats.reminderTotal >= Math.max(6, stats.activeDays) ? '提醒数量较多,说明执行中断点偏多,适合增加固定回固时段。' : '', ].filter(Boolean) if (items.length > 0) return items.slice(0, 3) return ['本月节啬相对稳定,下一步可以把重点事项再收到更清晰的主线。'] }) const sidebarFeedItems = computed(() => [ { id: 'fallback-1', title: 'AI 研发节啬继续升温,模型与工作流一体化成为主溜话题。', meta: 'Industry' }, { id: 'fallback-2', title: '本地知识库与计划系统的联动体验,正在成为效率工具的新竞争点。', meta: 'Product' }, { id: 'fallback-3', title: '建议接入真实 RSS 源后替换当前占位卡片,以获得即时资讯流。', meta: 'System' }, ]) const topbarFeedItems = computed(() => sidebarFeedItems.value.length > 0 ? [...sidebarFeedItems.value, ...sidebarFeedItems.value] : []) async function loadSidebarPlanSnapshot(date = new Date()) { const dateKey = formatDateKey(date) const monthKey = formatMonthKey(date) try { const [todayResponse, monthResponse] = await Promise.all([ scheduleCenterApi.date(dateKey), scheduleCenterApi.month(monthKey), ]) todayPlanDetail.value = todayResponse.data monthPlanDays.value = monthResponse.data.days } catch (err) { console.warn('Failed to load sidebar plan snapshot:', err) todayPlanDetail.value = null monthPlanDays.value = [] } } watch(todayDateKey, (next, previous) => { if (next === previous) return void loadDailyDigestFn() void loadSidebarPlanSnapshot(clientTimeRef.value) }) onMounted(() => { void loadDailyDigestFn() void loadSidebarPlanSnapshot(new Date()) }) return { todayPlanDetail, monthPlanDays, todayDateKey, monthPlanSummaryMap, calendarCells, todayPlanCounters, monthReviewStats, sidebarWeekLabels, sidebarStatusHeadline, sidebarStatusBreakdown, sidebarFocusItems, sidebarReviewAchievements, sidebarReviewReflections, sidebarFeedItems, topbarFeedItems, loadSidebarPlanSnapshot, sidebarCollapsedModules } }