merge main

This commit is contained in:
2026-08-11 10:16:38 +08:00
156 changed files with 22362 additions and 1211 deletions
@@ -62,12 +62,14 @@ import {
MenuOutlined,
ArrowLeftOutlined,
RobotOutlined,
ProfileOutlined,
} from '@ant-design/icons';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '../../store/useAuthStore';
import { getMenuConfigs, getCreditProductCatalog, getPaymentMethods, createRechargeOrder, getPaymentOrder, cancelPaymentOrder, getSiteInfo, getUnreadCount, createContactRequest, getUser, changePassword, changeUsername } from '../../api';
import type { CreditProduct, CreditProductCatalog } from '../../types';
import NotificationPopup from '../NotificationPopup';
import ActivityBanner from './ActivityBanner';
import './AppLayout.css';
import bg1 from '../../assets/bg1.png';
@@ -392,6 +394,7 @@ const AppLayout: React.FC = () => {
const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
const currentOrderNoRef = useRef<string | null>(null);
const [enabledMethods, setEnabledMethods] = useState<{ alipay: boolean; wechat: boolean }>({ alipay: false, wechat: false });
const [bannerVisible, setBannerVisible] = useState(false);
// 资源存储容量(从 getUser().resource_capacity 获取)
const [resourceCapacity, setResourceCapacity] = useState<{
@@ -639,6 +642,7 @@ const AppLayout: React.FC = () => {
...(user?.isTeamManager ? [{ key: 'teamManagement' as const, icon: <TeamOutlined style={{ color: '#6366f1' }} />, label: '团队管理' }] : []),
{ key: 'myCredits', icon: <WalletOutlined />, label: '积分明细' },
{ key: 'orderRecords', icon: <FileTextOutlined />, label: '订单记录' },
{ key: 'invoice', icon: <ProfileOutlined />, label: '申请开票' },
{ key: 'messages', icon: <BellOutlined />, label: `消息中心${unreadCount > 0 ? `(${unreadCount})` : ''}` },
...(operationManualUrl ? [{ key: 'manual' as const, icon: <FileTextOutlined />, label: '操作手册' }] : []),
{ type: 'divider' as const },
@@ -655,6 +659,7 @@ const AppLayout: React.FC = () => {
else if (key === 'teamManagement') { navigate('/team-management'); }
else if (key === 'myCredits') { navigate('/user-center?tab=credits'); }
else if (key === 'orderRecords') { navigate('/user-center?tab=orders'); }
else if (key === 'invoice') { navigate('/invoice'); }
else if (key === 'manual') { window.open(operationManualUrl, '_blank'); }
};
@@ -862,16 +867,21 @@ const AppLayout: React.FC = () => {
return (
<Layout style={{
minHeight: '100vh',
height: '100vh',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
}}>
<ActivityBanner onVisibilityChange={(v) => setBannerVisible(v)} />
<div style={{ display: 'flex', position: 'relative', padding: 4, gap: 12, flex: 1, minHeight: 0, overflow: 'hidden' }}>
<div className="desktop-sidebar" style={{
width: sidebarW, position: 'fixed', left: 16, top: 16, bottom: 16, zIndex: 100,
width: sidebarW, position: 'sticky', top: 4, alignSelf: 'flex-start', zIndex: 100,
height: '100%',
background: 'linear-gradient(180deg, #ffffff 0%, #f8fafc 100%)',
borderRadius: '20px',
boxShadow: '0 4px 32px rgba(0, 0, 0, 0.06), 0 1px 8px rgba(0, 0, 0, 0.04)',
display: 'flex', flexDirection: 'column',
transition: 'width 0.25s ease, left 0.25s ease',
transition: 'width 0.25s ease',
overflow: 'hidden',
border: '1px solid rgba(0, 0, 0, 0.06)',
}}>
@@ -1085,28 +1095,17 @@ const AppLayout: React.FC = () => {
</div>
<div className="desktop-content" style={{
marginLeft: sidebarW + 28,
marginRight: 12,
marginTop: 16,
marginBottom: 16,
flex: 1,
minHeight: 'calc(100vh - 32px)',
background: 'transparent',
padding: 0,
transition: 'margin-left 0.25s ease',
background: '#f1f2f3',
borderRadius: '20px',
boxShadow: '0 4px 32px rgba(0, 0, 0, 0.06), 0 1px 8px rgba(0, 0, 0, 0.04)',
padding: '24px 32px 32px',
border: '1px solid rgba(0, 0, 0, 0.06)',
overflow: 'auto',
minHeight: 0,
}}>
<div style={{
boxSizing: 'border-box',
height: '100%',
background: '#f1f2f3',
borderRadius: '20px',
boxShadow: '0 4px 32px rgba(0, 0, 0, 0.06), 0 1px 8px rgba(0, 0, 0, 0.04)',
minHeight: '100%',
padding: '24px 32px 32px',
border: '1px solid rgba(0, 0, 0, 0.06)',
}}>
{!isMobile && <Outlet />}
</div>
{!isMobile && <Outlet />}
</div>
</div>
<div className="mobile-header">