- [HIGH] 前端 SSE store 补充 alert/vital_update 事件监听 - [LOW] seed.rs 软删除列表补充 device_readings - [LOW] 小程序 device-sync 补充 index.config.ts 页面配置
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import { create } from 'zustand';
|
|
import { getUnreadCount, listMessages, markRead, type MessageInfo } from '../api/messages';
|
|
|
|
interface MessageState {
|
|
unreadCount: number;
|
|
recentMessages: MessageInfo[];
|
|
fetchUnreadCount: () => Promise<void>;
|
|
fetchRecentMessages: () => Promise<void>;
|
|
markAsRead: (id: string) => Promise<void>;
|
|
connectSSE: () => () => void;
|
|
}
|
|
|
|
// 请求去重:记录正在进行的请求,防止并发重复调用
|
|
let unreadCountPromise: Promise<void> | null = null;
|
|
let recentMessagesPromise: Promise<void> | null = null;
|
|
|
|
export const useMessageStore = create<MessageState>((set, get) => ({
|
|
unreadCount: 0,
|
|
recentMessages: [],
|
|
|
|
fetchUnreadCount: async () => {
|
|
// 如果已有进行中的请求,复用该 Promise
|
|
if (unreadCountPromise) {
|
|
await unreadCountPromise;
|
|
return;
|
|
}
|
|
unreadCountPromise = (async () => {
|
|
try {
|
|
const result = await getUnreadCount();
|
|
set({ unreadCount: result.count });
|
|
} catch {
|
|
// 静默失败,不影响用户体验
|
|
} finally {
|
|
unreadCountPromise = null;
|
|
}
|
|
})();
|
|
await unreadCountPromise;
|
|
},
|
|
|
|
fetchRecentMessages: async () => {
|
|
if (recentMessagesPromise) {
|
|
await recentMessagesPromise;
|
|
return;
|
|
}
|
|
recentMessagesPromise = (async () => {
|
|
try {
|
|
const result = await listMessages({ page: 1, page_size: 5 });
|
|
set({ recentMessages: result.data });
|
|
} catch {
|
|
// 静默失败
|
|
} finally {
|
|
recentMessagesPromise = null;
|
|
}
|
|
})();
|
|
await recentMessagesPromise;
|
|
},
|
|
|
|
markAsRead: async (id: string) => {
|
|
const prev = { unreadCount: get().unreadCount, recentMessages: get().recentMessages };
|
|
set((state) => ({
|
|
unreadCount: Math.max(0, state.unreadCount - 1),
|
|
recentMessages: state.recentMessages.map((m) =>
|
|
m.id === id ? { ...m, is_read: true } : m,
|
|
),
|
|
}));
|
|
try {
|
|
await markRead(id);
|
|
} catch {
|
|
set({ unreadCount: prev.unreadCount, recentMessages: prev.recentMessages });
|
|
}
|
|
},
|
|
|
|
connectSSE: () => {
|
|
const baseUrl = import.meta.env.VITE_API_BASE_URL || '/api/v1';
|
|
const token = localStorage.getItem('token');
|
|
if (!token) return () => {};
|
|
|
|
const url = `${baseUrl}/messages/stream?token=${encodeURIComponent(token)}`;
|
|
const es = new EventSource(url);
|
|
|
|
es.addEventListener('message', () => {
|
|
get().fetchUnreadCount();
|
|
get().fetchRecentMessages();
|
|
});
|
|
|
|
es.addEventListener('alert', () => {
|
|
get().fetchUnreadCount();
|
|
});
|
|
|
|
es.addEventListener('vital_update', () => {
|
|
// 体征数据更新事件 — 预留:未来可触发趋势图刷新
|
|
});
|
|
|
|
es.onerror = () => {
|
|
// SSE 连接断开时 EventSource 会自动重连
|
|
};
|
|
|
|
return () => {
|
|
es.close();
|
|
};
|
|
},
|
|
}));
|