feat: improve application access and money precision
This commit is contained in:
@@ -7,10 +7,12 @@ import {
|
||||
CircleHelp,
|
||||
KeyRound,
|
||||
LogOut,
|
||||
Menu,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
|
||||
import { NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { adminApi } from '@/api/adminApi';
|
||||
import {
|
||||
clearSession,
|
||||
@@ -63,6 +65,7 @@ export function AppShell({
|
||||
auditNotifications = [],
|
||||
}: AppShellProps) {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
||||
const [closedSections, setClosedSections] = useState<Record<string, boolean>>({});
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const [noticeOpen, setNoticeOpen] = useState(false);
|
||||
@@ -85,6 +88,7 @@ export function AppShell({
|
||||
const reauthenticationReject = useRef<((error: Error) => void) | null>(null);
|
||||
const lockRequested = useRef(false);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const ToggleIcon = collapsed ? PanelLeftOpen : PanelLeftClose;
|
||||
const auditTotal = useMemo(
|
||||
() => auditNotifications.reduce((sum, item) => sum + item.count, 0),
|
||||
@@ -180,6 +184,19 @@ export function AppShell({
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setMobileNavOpen(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mobileNavOpen) return;
|
||||
const closeOnEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') setMobileNavOpen(false);
|
||||
};
|
||||
window.addEventListener('keydown', closeOnEscape);
|
||||
return () => window.removeEventListener('keydown', closeOnEscape);
|
||||
}, [mobileNavOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const activityEvents = ['pointerdown', 'keydown', 'touchstart', 'scroll'] as const;
|
||||
const onActivity = () => markUserActivity();
|
||||
@@ -263,11 +280,16 @@ export function AppShell({
|
||||
}, [auditTotal, title]);
|
||||
|
||||
return (
|
||||
<div className={['app-shell', collapsed ? 'app-shell--collapsed' : ''].filter(Boolean).join(' ')}>
|
||||
<aside className="sidebar">
|
||||
<div className="brand-block">
|
||||
<img alt={`${title} logo`} className="brand-logo brand-logo--full" src="/logo/logo1.png" />
|
||||
<img alt={`${title} logo`} className="brand-logo brand-logo--compact" src="/logo/logo2.png" />
|
||||
<div className={['app-shell', collapsed ? 'app-shell--collapsed' : '', mobileNavOpen ? 'app-shell--mobile-nav-open' : ''].filter(Boolean).join(' ')}>
|
||||
<aside aria-label="应用导航" className={['sidebar', mobileNavOpen ? 'sidebar--mobile-open' : ''].filter(Boolean).join(' ')}>
|
||||
<div className="sidebar-brand-row">
|
||||
<div className="brand-block">
|
||||
<img alt={`${title} logo`} className="brand-logo brand-logo--full" src="/logo/logo1.png" />
|
||||
<img alt={`${title} logo`} className="brand-logo brand-logo--compact" src="/logo/logo2.png" />
|
||||
</div>
|
||||
<button aria-label="关闭导航" className="icon-button mobile-nav-close" onClick={() => setMobileNavOpen(false)} type="button">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav className="side-nav" aria-label="主导航">
|
||||
@@ -296,7 +318,7 @@ export function AppShell({
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<NavLink key={item.to} to={item.to} end title={item.pending ? `${item.label}(待开发)` : item.label}>
|
||||
<NavLink key={item.to} onClick={() => setMobileNavOpen(false)} to={item.to} end title={item.pending ? `${item.label}(待开发)` : item.label}>
|
||||
<Icon size={17} strokeWidth={2.1} />
|
||||
<span className="side-nav-label">
|
||||
<span className="side-nav-label-text">{item.label}</span>
|
||||
@@ -316,21 +338,35 @@ export function AppShell({
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{mobileNavOpen ? <button aria-label="关闭导航遮罩" className="mobile-nav-backdrop" onClick={() => setMobileNavOpen(false)} type="button" /> : null}
|
||||
|
||||
<main className="main-area">
|
||||
<header className="topbar">
|
||||
<div className="topbar-left">
|
||||
<button
|
||||
className="icon-button"
|
||||
className="icon-button desktop-nav-toggle"
|
||||
onClick={() => setCollapsed((value) => !value)}
|
||||
type="button"
|
||||
aria-label={collapsed ? '展开导航' : '收起导航'}
|
||||
>
|
||||
<ToggleIcon size={18} />
|
||||
</button>
|
||||
<button
|
||||
aria-expanded={mobileNavOpen}
|
||||
aria-label={mobileNavOpen ? '关闭导航' : '打开导航'}
|
||||
className="icon-button mobile-nav-toggle"
|
||||
onClick={() => setMobileNavOpen((open) => !open)}
|
||||
type="button"
|
||||
>
|
||||
<Menu size={20} />
|
||||
</button>
|
||||
<div className="mobile-topbar-brand">
|
||||
<img alt={`${title} logo`} src="/logo/logo1.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="topbar-actions">
|
||||
<button className="icon-button" type="button" aria-label="帮助中心">
|
||||
<button className="icon-button topbar-help" type="button" aria-label="帮助中心">
|
||||
<CircleHelp size={18} />
|
||||
</button>
|
||||
<div className="notice-menu-wrap">
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
ClipboardList,
|
||||
FileText,
|
||||
Home,
|
||||
ImageIcon,
|
||||
MessageSquareText,
|
||||
PenLine,
|
||||
ReceiptText,
|
||||
@@ -52,17 +51,6 @@ export function ClientLayout() {
|
||||
{ label: '接口对接', to: '/client/http-api', icon: Cable },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '彩信服务',
|
||||
items: [
|
||||
{ label: '签名报备', to: '/client/mms-signatures', icon: PenLine, pending: true },
|
||||
{ label: '彩信模板管理', to: '/client/mms-templates', icon: ImageIcon, pending: true },
|
||||
{ label: '发送彩信', to: '/client/mms-send', icon: MessageSquareText, pending: true },
|
||||
{ label: '查看批量任务', to: '/client/mms-batch-tasks', icon: ClipboardList, pending: true },
|
||||
{ label: '彩信发送详情', to: '/client/mms-send-detail', icon: ClipboardList, pending: true },
|
||||
{ label: '查看上行彩信', to: '/client/mms-uplink-messages', icon: ImageIcon, pending: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '账户',
|
||||
items: [
|
||||
|
||||
Reference in New Issue
Block a user