Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -85,8 +85,9 @@
|
||||
.contact-button-wrapper {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
bottom: 24px;
|
||||
bottom: 25%;
|
||||
z-index: 1000;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.contact-tooltip {
|
||||
@@ -94,7 +95,7 @@
|
||||
right: 64px;
|
||||
bottom: 8px;
|
||||
padding: 8px 16px;
|
||||
background: #1e293b;
|
||||
background: rgba(30, 41, 59, 0.9);
|
||||
color: #ffffff;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
@@ -108,19 +109,26 @@
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.8) 0%, rgba(139, 92, 246, 0.8) 100%);
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.4);
|
||||
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-button:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 24px rgba(99, 102, 241, 0.5);
|
||||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.95) 0%, rgba(139, 92, 246, 0.95) 100%);
|
||||
box-shadow: 0 6px 24px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
|
||||
.contact-button.dragging {
|
||||
cursor: grabbing;
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 8px 30px rgba(99, 102, 241, 0.5);
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
@@ -272,12 +272,23 @@ const AppLayout: React.FC = () => {
|
||||
const { user, logout } = useAuthStore();
|
||||
const [pwdModalOpen, setPwdModalOpen] = useState(false);
|
||||
const [rechargeModalOpen, setRechargeModalOpen] = useState(false);
|
||||
const [contactModalOpen, setContactModalOpen] = useState(false);
|
||||
const [pwdForm] = Form.useForm();
|
||||
const [selectedPlan, setSelectedPlan] = useState<number | null>(null);
|
||||
const [menuItems, setMenuItems] = useState<MenuConfig[]>([]);
|
||||
const [rechargeOptions, setRechargeOptions] = useState<any[]>([]);
|
||||
const [unreadCount, setUnreadCount] = useState(0);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [contactHovered, setContactHovered] = useState(false);
|
||||
const [contactForm] = Form.useForm();
|
||||
const [submittingContact, setSubmittingContact] = useState(false);
|
||||
const [contactPosition, setContactPosition] = useState<{ x: number; y: number }>(() => ({
|
||||
x: 24,
|
||||
y: window.innerHeight * 0.75
|
||||
}));
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const hasMovedRef = useRef(false);
|
||||
const dragStartRef = useRef({ x: 0, y: 0 });
|
||||
const [mobileExpandedMenus, setMobileExpandedMenus] = useState<Record<string, boolean>>({});
|
||||
const [siteName, setSiteName] = useState(() => {
|
||||
const cached = localStorage.getItem('siteInfo');
|
||||
@@ -310,10 +321,6 @@ 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 [contactModalOpen, setContactModalOpen] = useState(false);
|
||||
const [contactForm] = Form.useForm();
|
||||
const [contactHovered, setContactHovered] = useState(false);
|
||||
const [submittingContact, setSubmittingContact] = useState(false);
|
||||
|
||||
// 资源存储容量(从 getUser().resource_capacity 获取)
|
||||
const [resourceCapacity, setResourceCapacity] = useState<{
|
||||
@@ -355,30 +362,52 @@ const AppLayout: React.FC = () => {
|
||||
}).catch(() => { });
|
||||
}, []);
|
||||
|
||||
// 拉取用户资源容量信息
|
||||
useEffect(() => {
|
||||
getUser().then((res: any) => {
|
||||
console.log('[Storage] getUser 返回:', res);
|
||||
const handleContactMouseDown = (e: React.MouseEvent) => {
|
||||
if (e.button === 0) {
|
||||
setIsDragging(true);
|
||||
hasMovedRef.current = false;
|
||||
dragStartRef.current = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const rc = res?.resourceCapacity;
|
||||
if (rc) {
|
||||
setResourceCapacity({
|
||||
enabled: !!rc.enabled,
|
||||
usedBytes: Number(rc.usedBytes) || 0,
|
||||
totalBytes: Number(rc.totalBytes) || 0,
|
||||
availableBytes: Number(rc.availableBytes) || 0,
|
||||
usagePercent: Number(rc.usagePercent) || 0,
|
||||
exceeded: !!rc.exceeded,
|
||||
limitValue: rc.limitValue ?? '',
|
||||
limitUnit: rc.limitUnit || 'GB',
|
||||
});
|
||||
}
|
||||
// 没数据时不显示
|
||||
}).catch((err: any) => {
|
||||
console.error('[Storage] getUser 失败:', err);
|
||||
// 接口失败也不显示
|
||||
});
|
||||
}, []);
|
||||
const handleContactMouseMove = (e: MouseEvent) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const deltaX = Math.abs(e.clientX - dragStartRef.current.x);
|
||||
const deltaY = Math.abs(e.clientY - dragStartRef.current.y);
|
||||
|
||||
if (deltaX > 5 || deltaY > 5) {
|
||||
hasMovedRef.current = true;
|
||||
}
|
||||
|
||||
const newY = Math.max(60, Math.min(window.innerHeight - 60, e.clientY - (dragStartRef.current.y - contactPosition.y)));
|
||||
|
||||
setContactPosition(prev => ({ x: prev.x, y: newY }));
|
||||
};
|
||||
|
||||
const handleContactMouseUp = () => {
|
||||
const moved = hasMovedRef.current;
|
||||
setIsDragging(false);
|
||||
hasMovedRef.current = false;
|
||||
|
||||
if (!moved) {
|
||||
setContactModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isDragging) {
|
||||
document.addEventListener('mousemove', handleContactMouseMove);
|
||||
document.addEventListener('mouseup', handleContactMouseUp);
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleContactMouseMove);
|
||||
document.removeEventListener('mouseup', handleContactMouseUp);
|
||||
};
|
||||
}
|
||||
}, [isDragging]);
|
||||
|
||||
const loadUnreadCount = () => {
|
||||
getUnreadCount().then(count => {
|
||||
@@ -693,7 +722,7 @@ const AppLayout: React.FC = () => {
|
||||
}}>
|
||||
<div style={{
|
||||
width: 42, height: 42, borderRadius: 14, flexShrink: 0,
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a78bfa 100%)',
|
||||
//background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a78bfa 100%)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: '0 4px 16px rgba(99, 102, 241, 0.35)',
|
||||
overflow: 'hidden',
|
||||
@@ -777,12 +806,12 @@ const AppLayout: React.FC = () => {
|
||||
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)',
|
||||
}} />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ color: '#1e293b', fontSize: 14, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: -0.01 }}>
|
||||
<div style={{ color: '#1e293b', fontSize: 16, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: -0.01 }}>
|
||||
{user?.username}
|
||||
</div>
|
||||
<div style={{
|
||||
color: '#6366f1',
|
||||
fontSize: 12,
|
||||
fontSize: 16,
|
||||
fontWeight: 500,
|
||||
letterSpacing: 0,
|
||||
background: 'rgba(99, 102, 241, 0.08)',
|
||||
@@ -1096,8 +1125,16 @@ const AppLayout: React.FC = () => {
|
||||
gap: 8,
|
||||
}}>
|
||||
<InfoOutlined style={{ color: '#6366f1', fontSize: 14 }} />
|
||||
<Typography.Text style={{ color: '#ff0000ff', fontSize: 13 }}>
|
||||
当前平台仅支持支付宝/微信扫码充值,如需转账支付请联系我们
|
||||
<Typography.Text style={{ color: '#64748b', fontSize: 13 }}>
|
||||
当前平台仅支持支付宝/微信扫码充值,如需转账支付请
|
||||
<Typography.Text
|
||||
style={{
|
||||
color: '#ff0000ff',
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
onClick={() => { setRechargeModalOpen(false); setContactModalOpen(true); }}
|
||||
>联系我们</Typography.Text>
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
|
||||
@@ -1372,7 +1409,13 @@ const AppLayout: React.FC = () => {
|
||||
|
||||
<NotificationPopup />
|
||||
|
||||
<div className="contact-button-wrapper">
|
||||
<div
|
||||
className="contact-button-wrapper"
|
||||
style={{
|
||||
right: `${contactPosition.x}px`,
|
||||
bottom: `${window.innerHeight - contactPosition.y}px`,
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
position: 'relative',
|
||||
}}>
|
||||
@@ -1385,10 +1428,11 @@ const AppLayout: React.FC = () => {
|
||||
联系我们
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setContactModalOpen(true)}
|
||||
className="contact-button"
|
||||
className={`contact-button ${isDragging ? 'dragging' : ''}`}
|
||||
onMouseEnter={() => setContactHovered(true)}
|
||||
onMouseLeave={() => setContactHovered(false)}
|
||||
onMouseDown={handleContactMouseDown}
|
||||
onMouseUp={handleContactMouseUp}
|
||||
>
|
||||
<MessageOutlined style={{ fontSize: 20 }} />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user