feat(client): 通知中心与未读角标(F16)
- Topbar 增加通知铃铛与未读数 - 新增 /notifications 列表页,支持筛选与标记已读 - 为 workflow/reimbursement/auth 通知提供跳转入口
这个提交包含在:
@@ -0,0 +1,51 @@
|
||||
import { apiRequestData } from "./http";
|
||||
|
||||
export type NotificationItem = {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
body: string | null;
|
||||
targetType: string | null;
|
||||
targetId: string | null;
|
||||
readAt: string | null;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type NotificationListResponse = {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
unreadOnly: boolean;
|
||||
unreadCount: number;
|
||||
items: NotificationItem[];
|
||||
};
|
||||
|
||||
export type NotificationListQuery = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
unreadOnly?: boolean;
|
||||
};
|
||||
|
||||
function toQueryString(query: NotificationListQuery) {
|
||||
const params = new URLSearchParams();
|
||||
if (query.page != null) params.set("page", String(query.page));
|
||||
if (query.pageSize != null) params.set("pageSize", String(query.pageSize));
|
||||
if (query.unreadOnly != null) params.set("unreadOnly", query.unreadOnly ? "1" : "0");
|
||||
const qs = params.toString();
|
||||
return qs ? `?${qs}` : "";
|
||||
}
|
||||
|
||||
export async function fetchNotifications(query: NotificationListQuery = {}) {
|
||||
return apiRequestData<NotificationListResponse>(`/api/notifications${toQueryString(query)}`);
|
||||
}
|
||||
|
||||
export async function markNotificationRead(notificationId: string) {
|
||||
return apiRequestData<{ id: string; readAt: string | null }>(
|
||||
`/api/notifications/${notificationId}/mark-read`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户