DG7/MRF7 移动信息架构与展示去重治理
这个提交包含在:
@@ -1,7 +1,7 @@
|
||||
import { Link, useSearchParams } from "react-router-dom";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import { fetchNotifications, markAllNotificationsRead, markNotificationRead } from "../api/notifications";
|
||||
import { fetchNotifications, markAllNotificationsRead, markNotificationRead, type NotificationItem } from "../api/notifications";
|
||||
import { isApiError } from "../api/types";
|
||||
import { mapApiErrorMessage } from "../api/errorMessages";
|
||||
import { PageHeader } from "../components/common/PageHeader";
|
||||
@@ -9,8 +9,7 @@ import { Pagination } from "../components/common/Pagination";
|
||||
import { PageScaffold } from "../components/patterns/PageScaffold";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { Tabs, TabsList, TabsTrigger } from "../components/ui/tabs";
|
||||
import { resolveNotificationTargetLink } from "../lib/notificationLinks";
|
||||
import { resolveMobileEquivalentPath } from "../lib/runtimeContext";
|
||||
import { resolveMobileNotificationTargetLink } from "../lib/notificationLinks";
|
||||
import { resolveErrorMessage } from "../lib/resolveErrorMessage";
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
@@ -33,19 +32,13 @@ function resolveNotificationError(error: unknown) {
|
||||
return "加载通知失败";
|
||||
}
|
||||
|
||||
function resolveMobileNotificationTarget(item: Parameters<typeof resolveNotificationTargetLink>[0]) {
|
||||
const targetLink = resolveNotificationTargetLink(item);
|
||||
if (!targetLink) return null;
|
||||
const [pathname, search = ""] = targetLink.split("?");
|
||||
return resolveMobileEquivalentPath(pathname, search ? `?${search}` : "");
|
||||
}
|
||||
|
||||
function formatTime(value: string) {
|
||||
return value.slice(0, 19).replace("T", " ");
|
||||
}
|
||||
|
||||
export function MobileNotificationsPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const unreadOnlyRaw = searchParams.get("unreadOnly") ?? "0";
|
||||
@@ -82,6 +75,21 @@ export function MobileNotificationsPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const openNotificationTarget = async (item: NotificationItem, targetLink: string | null) => {
|
||||
if (!targetLink) {
|
||||
toast.info("这条通知没有可直达的业务详情");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!item.readAt) {
|
||||
await markReadMutation.mutateAsync(item.id);
|
||||
}
|
||||
navigate(targetLink);
|
||||
} catch (error) {
|
||||
toast.error(resolveErrorMessage(error));
|
||||
}
|
||||
};
|
||||
|
||||
const markAllReadMutation = useMutation({
|
||||
mutationFn: () => markAllNotificationsRead(),
|
||||
onSuccess: async () => {
|
||||
@@ -174,9 +182,15 @@ export function MobileNotificationsPage() {
|
||||
) : null}
|
||||
|
||||
{notificationItems.map((item) => {
|
||||
const targetLink = resolveMobileNotificationTarget(item);
|
||||
const targetLink = resolveMobileNotificationTargetLink(item);
|
||||
return (
|
||||
<article data-testid="mobile-notification-card" key={item.id} className="mobile-list-card">
|
||||
<button
|
||||
data-testid="mobile-notification-card"
|
||||
key={item.id}
|
||||
className="mobile-list-card w-full text-left"
|
||||
type="button"
|
||||
onClick={() => void openNotificationTarget(item, targetLink)}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-semibold text-zinc-900">{item.title}</div>
|
||||
@@ -188,28 +202,8 @@ export function MobileNotificationsPage() {
|
||||
</div>
|
||||
|
||||
<div className="mt-3 text-xs text-zinc-500">{formatTime(item.createdAt)}</div>
|
||||
<div className="mobile-two-action-grid mt-3">
|
||||
{targetLink ? (
|
||||
<Button asChild size="sm" variant="secondary" data-testid="mobile-notification-target-link">
|
||||
<Link to={targetLink}>查看</Link>
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{!item.readAt ? (
|
||||
<Button
|
||||
className={targetLink ? "" : "col-span-2"}
|
||||
data-testid="mobile-notification-mark-read"
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
disabled={markReadMutation.isPending}
|
||||
onClick={() => markReadMutation.mutate(item.id)}
|
||||
>
|
||||
标为已读
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
<div className="mt-3 text-xs text-primary">{targetLink ? "查看详情" : "暂无直达详情"}</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户