This commit is contained in:
2026-06-16 11:25:05 +08:00
parent 0ade463175
commit e78a1cd65f
8 changed files with 229 additions and 133 deletions
+1
View File
@@ -0,0 +1 @@
@import "https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap";:root{--font-sans:"Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--nav-bg:#08080c;--nav-surface:#ffffff0a;--nav-border:#ffffff0f;--nav-text:#e8e8ec;--nav-text-muted:#8b8fa3;--nav-hover:#ffffff14;--nav-active:#ffffff1f}*,:before,:after{box-sizing:border-box}html,body{font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#1a1a2e;letter-spacing:-.01em;background:#f8f9fc;margin:0;padding:0}#root{min-height:100vh}
File diff suppressed because one or more lines are too long
+6 -2
View File
@@ -4,8 +4,12 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VideoGen.AI 管理后台</title> <link rel="preconnect" href="https://fonts.googleapis.com" />
<script type="module" crossorigin src="/assets/index-C93H_elA.js"></script> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>后台管理</title>
<script type="module" crossorigin src="/assets/index-ECGzn5Wr.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+20
View File
@@ -8,6 +8,26 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>后台管理</title> <title>后台管理</title>
<script>
(function() {
var cached = localStorage.getItem('siteInfo');
if (cached) {
try {
var info = JSON.parse(cached);
if (info.siteName) {
document.title = info.siteName + ' - 管理后台';
}
if (info.siteLogo) {
var link = document.querySelector('link[rel="icon"]');
if (link) {
link.href = info.siteLogo;
link.type = 'image/png';
}
}
} catch (e) {}
}
})();
</script>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+4
View File
@@ -315,6 +315,10 @@ export async function adminChangePassword(oldPwd: string, newPwd: string): Promi
await api.post('/admin/change-password', { old_password: oldPwd, new_password: newPwd }); await api.post('/admin/change-password', { old_password: oldPwd, new_password: newPwd });
} }
export async function getSiteInfo(): Promise<{ siteName: string; siteLogo: string; userAgreementUrl: string; privacyPolicyUrl: string }> {
return api.get('/auth/site-info', false);
}
// ── Recharge Packages ─────────────────────────────────── // ── Recharge Packages ───────────────────────────────────
export async function getRechargePackages(): Promise<any[]> { export async function getRechargePackages(): Promise<any[]> {
+38 -4
View File
@@ -16,7 +16,7 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Outlet, useNavigate, useLocation, Navigate } from 'react-router-dom'; import { Outlet, useNavigate, useLocation, Navigate } from 'react-router-dom';
import { useAdminStore } from '../store'; import { useAdminStore } from '../store';
import { getMenuConfigs, adminChangePassword } from '../api'; import { getMenuConfigs, adminChangePassword, getSiteInfo } from '../api';
const { Sider, Content } = Layout; const { Sider, Content } = Layout;
@@ -53,6 +53,35 @@ const AdminLayout: React.FC = () => {
const [menuItems, setMenuItems] = useState<any[]>([]); const [menuItems, setMenuItems] = useState<any[]>([]);
const [pwdModal, setPwdModal] = useState(false); const [pwdModal, setPwdModal] = useState(false);
const [pwdForm] = Form.useForm(); const [pwdForm] = Form.useForm();
const [siteName, setSiteName] = useState(() => {
const cached = localStorage.getItem('siteInfo');
return cached ? JSON.parse(cached).siteName || '管理后台' : '管理后台';
});
const [siteLogo, setSiteLogo] = useState(() => {
const cached = localStorage.getItem('siteInfo');
return cached ? JSON.parse(cached).siteLogo || '' : '';
});
useEffect(() => {
getSiteInfo().then(info => {
const name = info.siteName || '管理后台';
const logo = info.siteLogo || '';
setSiteName(name);
setSiteLogo(logo);
document.title = name;
localStorage.setItem('siteInfo', JSON.stringify({ siteName: name, siteLogo: logo }));
if (logo) {
let faviconLink = document.querySelector('link[rel="icon"]') as HTMLLinkElement;
if (!faviconLink) {
faviconLink = document.createElement('link');
faviconLink.rel = 'icon';
document.head.appendChild(faviconLink);
}
faviconLink.href = logo;
faviconLink.type = 'image/png';
}
}).catch(() => {});
}, []);
useEffect(() => { useEffect(() => {
getMenuConfigs().then(data => { getMenuConfigs().then(data => {
@@ -198,12 +227,17 @@ const AdminLayout: React.FC = () => {
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
display: 'flex', alignItems: 'center', justifyContent: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center',
boxShadow: '0 2px 8px rgba(99,102,241,0.3)', boxShadow: '0 2px 8px rgba(99,102,241,0.3)',
overflow: 'hidden',
}}> }}>
<ThunderboltOutlined style={{ fontSize: 16, color: '#fff' }} /> {siteLogo ? (
<img src={siteLogo} alt="logo" style={{ width: 24, height: 24, objectFit: 'contain' }} />
) : (
<ThunderboltOutlined style={{ fontSize: 16, color: '#fff' }} />
)}
</div> </div>
{!collapsed && ( {!collapsed && (
<span style={{ color: '#1f2937', fontSize: 15, fontWeight: 600, letterSpacing: -0.02 }}> <span style={{ color: '#1f2937', fontSize: 15, fontWeight: 600, letterSpacing: -0.02 }}>
{siteName}
</span> </span>
)} )}
</div> </div>
@@ -218,12 +252,12 @@ const AdminLayout: React.FC = () => {
background: 'transparent', background: 'transparent',
border: 'none', border: 'none',
paddingTop: 8, paddingTop: 8,
marginTop: 8,
}} }}
onClick={({ key }) => { onClick={({ key }) => {
if (key.startsWith('group-')) return; if (key.startsWith('group-')) return;
navigate(key); navigate(key);
}} }}
style={{ background: 'transparent', borderRight: 0, marginTop: 8 }}
theme="light" theme="light"
/> />
+21 -1
View File
@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
@@ -8,6 +8,26 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>民众智创</title> <title>民众智创</title>
<script>
(function() {
var cached = localStorage.getItem('siteInfo');
if (cached) {
try {
var info = JSON.parse(cached);
if (info.siteName) {
document.title = info.siteName;
}
if (info.siteLogo) {
var link = document.querySelector('link[rel="icon"]');
if (link) {
link.href = info.siteLogo;
link.type = 'image/png';
}
}
} catch (e) {}
}
})();
</script>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
@@ -141,11 +141,24 @@ const AppLayout: React.FC = () => {
const [unreadCount, setUnreadCount] = useState(0); const [unreadCount, setUnreadCount] = useState(0);
const [siteName, setSiteName] = useState(() => { const [siteName, setSiteName] = useState(() => {
const cached = localStorage.getItem('siteInfo'); const cached = localStorage.getItem('siteInfo');
return cached ? JSON.parse(cached).siteName || '' : ''; const name = cached ? JSON.parse(cached).siteName || '' : '';
if (name) document.title = name;
return name;
}); });
const [siteLogo, setSiteLogo] = useState(() => { const [siteLogo, setSiteLogo] = useState(() => {
const cached = localStorage.getItem('siteInfo'); const cached = localStorage.getItem('siteInfo');
return cached ? JSON.parse(cached).siteLogo || '' : ''; const logo = cached ? JSON.parse(cached).siteLogo || '' : '';
if (logo) {
let faviconLink = document.querySelector('link[rel="icon"]') as HTMLLinkElement;
if (!faviconLink) {
faviconLink = document.createElement('link');
faviconLink.rel = 'icon';
document.head.appendChild(faviconLink);
}
faviconLink.href = logo;
faviconLink.type = 'image/png';
}
return logo;
}); });
const [siteInfoLoading, setSiteInfoLoading] = useState(!localStorage.getItem('siteInfo')); const [siteInfoLoading, setSiteInfoLoading] = useState(!localStorage.getItem('siteInfo'));
const [qrCodeModalOpen, setQrCodeModalOpen] = useState(false); const [qrCodeModalOpen, setQrCodeModalOpen] = useState(false);
@@ -171,29 +184,29 @@ const AppLayout: React.FC = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const cached = localStorage.getItem('siteInfo');
if (!cached) {
setSiteName('民众智创');
setSiteLogo('');
document.title = '民众智创';
}
getSiteInfo().then(info => { getSiteInfo().then(info => {
const siteName = info.siteName || '民众智创'; const name = info.siteName || '民众智创';
const siteLogo = info.siteLogo || ''; const logo = info.siteLogo || '';
setSiteName(siteName);
setSiteLogo(siteLogo); if (name !== siteName) {
document.title = siteName; setSiteName(name);
localStorage.setItem('siteInfo', JSON.stringify({ siteName, siteLogo })); document.title = name;
}).catch(() => {
// 如果没有缓存且请求失败,使用默认值
if (!cached) {
setSiteName('民众智创');
document.title = '民众智创';
} }
}).finally(() => {
setSiteInfoLoading(false); if (logo && logo !== siteLogo) {
}); setSiteLogo(logo);
let faviconLink = document.querySelector('link[rel="icon"]') as HTMLLinkElement;
if (!faviconLink) {
faviconLink = document.createElement('link');
faviconLink.rel = 'icon';
document.head.appendChild(faviconLink);
}
faviconLink.href = logo;
faviconLink.type = 'image/png';
}
localStorage.setItem('siteInfo', JSON.stringify({ siteName: name, siteLogo: logo }));
}).catch(() => {});
}, []); }, []);
const loadUnreadCount = () => { const loadUnreadCount = () => {