/** * 铃铛通知纯逻辑:未读徽标文案、新通知判定、「已提醒过」去重记录、时间展示。 * 轮询调度与渲染在 NotificationBell.vue;这里只放可单测的纯函数与存储读写。 */ /** 未读轮询间隔(60 秒)。 */ export const NOTIFICATION_POLL_INTERVAL_MS = 60_000 const NOTIFIED_KEY_PREFIX = 'notification:last-notified-id' /** 未读徽标文案:0 或非法值显示空串,>99 显示 99+。 */ export function formatUnreadBadge(count: number | null | undefined): string { const value = Number(count ?? 0) if (!Number.isFinite(value) || value <= 0) { return '' } return value > 99 ? '99+' : String(Math.floor(value)) } /** 是否出现新通知:latestId 大于上次已提醒过的 id。 */ export function hasNewNotification( latestId: number | null | undefined, lastNotifiedId: number | null | undefined, ): boolean { const latest = Number(latestId ?? 0) const notified = Number(lastNotifiedId ?? 0) if (!Number.isFinite(latest) || latest <= 0) { return false } return latest > (Number.isFinite(notified) && notified > 0 ? notified : 0) } function storageKey(uid: string): string { return `${NOTIFIED_KEY_PREFIX}:${uid || '0'}` } /** 读取当前用户「已提醒过的最大通知 id」(多页面/多会话共享)。 */ export function readLastNotifiedId(uid: string): number { try { const raw = window.localStorage.getItem(storageKey(uid)) const value = Number(raw ?? 0) return Number.isFinite(value) && value > 0 ? value : 0 } catch { return 0 } } export function writeLastNotifiedId(uid: string, id: number): void { try { if (Number.isFinite(id) && id > 0) { window.localStorage.setItem(storageKey(uid), String(Math.floor(id))) } } catch { /* 存储不可用时静默忽略(退化为每次都提醒,不影响功能) */ } } /** 当前登录用户 uid(未登录返回 '0')。 */ export function currentNotificationUid(): string { try { return window.localStorage.getItem('uid') || '0' } catch { return '0' } } /** 通知时间展示:今天只显示 HH:mm;昨天显示「昨天 HH:mm」;更早显示 MM-DD HH:mm。 */ export function formatNotificationTime( value: string | null | undefined, now: Date = new Date(), ): string { if (!value) { return '' } const date = new Date(value) if (Number.isNaN(date.getTime())) { return '' } const pad = (input: number) => String(input).padStart(2, '0') const hourMinute = `${pad(date.getHours())}:${pad(date.getMinutes())}` const sameDay = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate() if (sameDay) { return hourMinute } const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1) const isYesterday = date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === yesterday.getDate() if (isYesterday) { return `昨天 ${hourMinute}` } return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${hourMinute}` }