feat: 完善服务监控与下游重投

This commit is contained in:
hectorzhao
2026-08-14 17:21:29 +08:00
parent d30d9ea4d0
commit 1ef4380422
50 changed files with 1279 additions and 82 deletions
+40 -13
View File
@@ -50,6 +50,10 @@ export type AuditNotificationItem = {
to: string;
};
export type AlertNotificationItem = AuditNotificationItem & {
description: string;
};
type AppShellProps = {
title: string;
subtitle: string;
@@ -60,7 +64,7 @@ type AppShellProps = {
userRole: string;
navSections: ShellNavSection[];
auditNotifications?: AuditNotificationItem[];
retirementAlert?: { count: number; to: string };
alertNotifications?: AlertNotificationItem[];
onSessionLockedChange?: (locked: boolean) => void;
};
@@ -73,7 +77,7 @@ export function AppShell({
userRole,
navSections,
auditNotifications = [],
retirementAlert,
alertNotifications = [],
onSessionLockedChange,
}: AppShellProps) {
const [collapsed, setCollapsed] = useState(false);
@@ -81,6 +85,7 @@ export function AppShell({
const [closedSections, setClosedSections] = useState<Record<string, boolean>>({});
const [userMenuOpen, setUserMenuOpen] = useState(false);
const [noticeOpen, setNoticeOpen] = useState(false);
const [alertNoticeOpen, setAlertNoticeOpen] = useState(false);
const [passwordModalOpen, setPasswordModalOpen] = useState(false);
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
@@ -107,6 +112,10 @@ export function AppShell({
() => auditNotifications.reduce((sum, item) => sum + item.count, 0),
[auditNotifications],
);
const alertTotal = useMemo(
() => alertNotifications.reduce((sum, item) => sum + item.count, 0),
[alertNotifications],
);
async function changeOwnPassword() {
if (!currentPassword || newPassword.length < 6) {
@@ -417,23 +426,41 @@ export function AppShell({
<button className="icon-button topbar-help" type="button" aria-label="帮助中心">
<CircleHelp size={18} />
</button>
{retirementAlert ? (
<Link
aria-label={`今日未读且未抑制签名清退预警 ${retirementAlert.count}`}
className={['icon-button', retirementAlert.count > 0 ? 'has-dot' : ''].filter(Boolean).join(' ')}
title="今日未读且未抑制签名清退预警"
to={retirementAlert.to}
>
<Bell size={18} />
{retirementAlert.count > 0 ? <span className="notice-count">{retirementAlert.count}</span> : null}
</Link>
{alertNotifications.length ? (
<div className="notice-menu-wrap">
<button
aria-expanded={alertNoticeOpen}
aria-haspopup="menu"
aria-label="预警通知"
className={['icon-button', alertTotal > 0 ? 'has-dot' : ''].filter(Boolean).join(' ')}
onClick={() => { setAlertNoticeOpen((open) => !open); setNoticeOpen(false); }}
type="button"
>
<Bell size={18} />
{alertTotal > 0 ? <span className="notice-count">{alertTotal}</span> : null}
</button>
{alertNoticeOpen ? (
<div className="notice-popover notice-popover--alerts" role="menu">
<div className="notice-popover__header">
<strong></strong>
<span className={alertTotal === 0 ? 'is-zero' : ''}>{alertTotal} </span>
</div>
{alertNotifications.map((item) => (
<NavLink key={item.to} onClick={() => setAlertNoticeOpen(false)} role="menuitem" to={item.to}>
<span className="notice-popover__copy"><b>{item.label}</b><small>{item.description}</small></span>
<strong className={item.count === 0 ? 'is-zero' : ''}>{item.count}</strong>
</NavLink>
))}
</div>
) : null}
</div>
) : null}
<div className="notice-menu-wrap">
<button
aria-expanded={noticeOpen}
aria-haspopup="menu"
className={['icon-button', auditTotal > 0 ? 'has-dot' : ''].filter(Boolean).join(' ')}
onClick={() => setNoticeOpen((open) => !open)}
onClick={() => { setNoticeOpen((open) => !open); setAlertNoticeOpen(false); }}
type="button"
aria-label="通知"
>