This commit is contained in:
2026-06-26 16:27:49 +08:00
parent 02864677ec
commit a78c93df13
6 changed files with 64 additions and 139 deletions
-1
View File
@@ -7,6 +7,5 @@ from app.enums.user import *
from app.enums.credit_record import * from app.enums.credit_record import *
from app.enums.token_usage import * from app.enums.token_usage import *
from app.enums.generation_task import * from app.enums.generation_task import *
from app.enums.recent_generation import *
from app.enums.generation_status import * from app.enums.generation_status import *
from app.enums.sms import * from app.enums.sms import *
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
} }
})(); })();
</script> </script>
<script type="module" crossorigin src="/assets/index-BNvl4Dex.js"></script> <script type="module" crossorigin src="/assets/index-CyfX-OBY.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css"> <link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
</head> </head>
<body> <body>
-55
View File
@@ -183,58 +183,3 @@
border-right-color: var(--border); border-right-color: var(--border);
} }
} }
/* Login Page Responsive Styles */
.login-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
@media (min-width: 900px) {
.login-container {
flex-direction: row;
}
}
.login-left-section {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding: 40px 24px;
z-index: 1;
}
@media (min-width: 900px) {
.login-left-section {
padding: 0 80px;
}
}
.login-features {
display: none;
}
@media (min-width: 900px) {
.login-features {
display: flex;
flex-direction: column;
gap: 20px;
}
}
.login-right-section {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
padding: 20px 24px 40px;
}
@media (min-width: 900px) {
.login-right-section {
padding: 40px 24px;
}
}
@@ -460,7 +460,13 @@ const AppLayout: React.FC = () => {
{/* Menu */} {/* Menu */}
<div style={{ flex: 1, padding: '8px 8px', overflow: 'auto' }}> <div style={{ flex: 1, padding: '8px 8px', overflow: 'auto' }}>
{/* {!collapsed && (
<div style={{ color: 'rgba(0,0,0,0.3)', fontSize: 11, fontWeight: 600, padding: '8px 12px 6px', letterSpacing: 1 }}>
导航
</div>
)} */}
{(() => { {(() => {
// Build child map for all menu items
const childMap: Record<string, MenuConfig[]> = {}; const childMap: Record<string, MenuConfig[]> = {};
menuItems.forEach(m => { menuItems.forEach(m => {
const pid = m.parent_id ?? m.parentId; const pid = m.parent_id ?? m.parentId;
@@ -470,8 +476,10 @@ const AppLayout: React.FC = () => {
} }
}); });
// Get top-level items (parent_id is null/undefined)
const topLevelItems = menuItems.filter(m => !(m.parent_id ?? m.parentId)); const topLevelItems = menuItems.filter(m => !(m.parent_id ?? m.parentId));
// Sort top-level items by sort_order
topLevelItems.sort((a, b) => { topLevelItems.sort((a, b) => {
const orderA = typeof a.sortOrder === 'number' ? a.sortOrder : Infinity; const orderA = typeof a.sortOrder === 'number' ? a.sortOrder : Infinity;
const orderB = typeof b.sortOrder === 'number' ? b.sortOrder : Infinity; const orderB = typeof b.sortOrder === 'number' ? b.sortOrder : Infinity;
@@ -483,87 +491,52 @@ const AppLayout: React.FC = () => {
const renderMenuItem = (item: MenuConfig, depth: number = 0) => { const renderMenuItem = (item: MenuConfig, depth: number = 0) => {
const isActive = item.path === selectedKey; const isActive = item.path === selectedKey;
const menuIcon = iconMap[item.icon] || <HomeOutlined />; const menuIcon = iconMap[item.icon] || <HomeOutlined />;
const hasChildren = childMap[item.id] && childMap[item.id].length > 0;
const isExpanded = collapsedGroups[item.id] !== true;
const handleClick = () => {
if (hasChildren) {
setCollapsedGroups(prev => ({
...prev,
[item.id]: !prev[item.id]
}));
} else if (item.path) {
navigate(item.path);
}
};
return ( return (
<div key={item.id}> <div key={item.id} onClick={() => item.path && navigate(item.path)} style={{
<div display: 'flex', alignItems: 'center',
onClick={handleClick} justifyContent: 'flex-start',
style={{ gap: 10,
display: 'flex', alignItems: 'center', padding: depth > 0 ? '6px 12px 6px 32px' : '6px 14px',
justifyContent: 'flex-start', borderRadius: 12, margin: '1px 4px', cursor: 'pointer',
gap: 10, fontSize: depth > 0 ? 13 : 14, fontWeight: isActive ? 600 : 400,
padding: depth > 0 ? '6px 12px 6px 32px' : '6px 14px', color: isActive ? '#4f46e5' : '#475569',
borderRadius: 12, margin: '1px 4px', cursor: 'pointer', background: isActive ? 'linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.08) 100%)' : 'transparent',
fontSize: depth > 0 ? 13 : 14, fontWeight: isActive ? 600 : 400, transition: 'all 0.2s ease',
color: isActive ? '#4f46e5' : '#475569', }}>
background: isActive ? 'linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.08) 100%)' : 'transparent', <span style={{
transition: 'all 0.2s ease', fontSize: depth > 0 ? 14 : 16,
}} flexShrink: 0,
> color: isActive ? '#6366f1' : '#64748b',
<span style={{ }}>{menuIcon}</span>
fontSize: depth > 0 ? 14 : 16, <span style={{ whiteSpace: 'nowrap' }}>{item.label}</span>
flexShrink: 0,
color: isActive ? '#6366f1' : '#64748b',
}}>{menuIcon}</span>
<span style={{ whiteSpace: 'nowrap', flex: 1, textAlign: 'left' }}>{item.label}</span>
{hasChildren && (
<span style={{
fontSize: 14,
color: '#94a3b8',
transition: 'transform 0.2s ease',
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
}}>
<RightOutlined />
</span>
)}
</div>
{hasChildren && isExpanded && (
<div style={{ overflow: 'hidden' }}>
{(childMap[item.id] || []).sort((a, b) => {
const orderA = typeof a.sortOrder === 'number' ? a.sortOrder : Infinity;
const orderB = typeof b.sortOrder === 'number' ? b.sortOrder : Infinity;
return orderA - orderB;
}).map(c => renderMenuItem(c, depth + 1))}
</div>
)}
</div> </div>
); );
}; };
// Render top-level items and their children in order
topLevelItems.forEach(item => { topLevelItems.forEach(item => {
const menuType = item.menu_type ?? item.menuType; const menuType = item.menu_type ?? item.menuType;
if (menuType === 'group') { if (menuType === 'group') {
// Render group with its children
const children = (childMap[item.id] || []).sort((a, b) => {
const orderA = typeof a.sortOrder === 'number' ? a.sortOrder : Infinity;
const orderB = typeof b.sortOrder === 'number' ? b.sortOrder : Infinity;
return orderA - orderB;
});
items.push( items.push(
<div key={item.id}> <div key={item.id}>
<div style={{ <div style={{
color: '#94a3b8', fontSize: 12, fontWeight: 600, color: '#94a3b8', fontSize: 12, fontWeight: 600,
padding: '10px 14px 4px', letterSpacing: 0.5, textTransform: 'uppercase', padding: '10px 14px 4px', letterSpacing: 0.5, textTransform: 'uppercase',
}}> }}>
{item.label} {item.label}
</div> </div>
{(childMap[item.id] || []).sort((a, b) => { {children.map(c => renderMenuItem(c, 1))}
const orderA = typeof a.sortOrder === 'number' ? a.sortOrder : Infinity;
const orderB = typeof b.sortOrder === 'number' ? b.sortOrder : Infinity;
return orderA - orderB;
}).map(c => renderMenuItem(c, 1))}
</div> </div>
); );
} else { } else {
// Render standalone page
items.push(renderMenuItem(item)); items.push(renderMenuItem(item));
} }
}); });
+23 -15
View File
@@ -280,7 +280,9 @@ const LoginPage: React.FC = () => {
}; };
return ( return (
<div className="login-container" style={{ <div style={{
minHeight: '100vh',
display: 'flex',
backgroundImage: `url(/backimage.png)`, backgroundImage: `url(/backimage.png)`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
@@ -308,34 +310,37 @@ const LoginPage: React.FC = () => {
}} /> }} />
{/* Left side - features */} {/* Left side - features */}
<div className="login-left-section"> <div style={{
<Space direction="vertical" size={24}> flex: 1, display: 'flex', flexDirection: 'column',
justifyContent: 'center', padding: '0 80px', zIndex: 1,
}}>
<Space direction="vertical" size={36}>
<div> <div>
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}>
{siteLogo ? ( {siteLogo ? (
<img src={siteLogo} alt="logo" style={{ width: 48, height: 48, borderRadius: 12, objectFit: 'contain' }} /> <img src={siteLogo} alt="logo" style={{ width: 52, height: 52, borderRadius: 14, objectFit: 'contain' }} />
) : ( ) : (
<div style={{ <div style={{
width: 48, height: 48, borderRadius: 12, width: 52, height: 52, borderRadius: 14,
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 8px 24px rgba(99,102,241,0.25)', boxShadow: '0 8px 24px rgba(99,102,241,0.25)',
}}> }}>
<ThunderboltOutlined style={{ fontSize: 24, color: '#fff' }} /> <ThunderboltOutlined style={{ fontSize: 26, color: '#fff' }} />
</div> </div>
)} )}
<span style={{ color: '#1e293b', fontSize: 24, fontWeight: 800, letterSpacing: -0.5 }}> <span style={{ color: '#1e293b', fontSize: 28, fontWeight: 800, letterSpacing: -0.5 }}>
{siteName} {siteName}
</span> </span>
</div> </div>
<div className="shiny-text-container" style={{ fontSize: 20 }}> <div className="shiny-text-container">
<span className="shiny-text">AI赋能创意</span> <span className="shiny-text">AI赋能创意</span>
</div> </div>
<Typography.Paragraph style={{ color: '#64748b', fontSize: 15, maxWidth: 480, lineHeight: 1.8 }}> <Typography.Paragraph style={{ color: '#64748b', fontSize: 17, maxWidth: 480, lineHeight: 1.8 }}>
AI AI <br />
</Typography.Paragraph> </Typography.Paragraph>
</div> </div>
<div className="login-features"> <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
{features.map((f, i) => ( {features.map((f, i) => (
<div <div
key={i} key={i}
@@ -370,12 +375,15 @@ const LoginPage: React.FC = () => {
</div> </div>
{/* Right side - login/register form */} {/* Right side - login/register form */}
<div className="login-right-section"> <div style={{
flex: 1, display: 'flex', alignItems: 'center',
justifyContent: 'center', zIndex: 1, padding: '40px 24px',
}}>
<Card style={{ <Card style={{
width: '100%', maxWidth: 440, borderRadius: 20, width: 440, maxWidth: '100%', borderRadius: 20,
boxShadow: '0 20px 60px rgba(0,0,0,0.08)', boxShadow: '0 20px 60px rgba(0,0,0,0.08)',
border: '1px solid #e2e8f0', background: '#fff', border: '1px solid #e2e8f0', background: '#fff',
}} styles={{ body: { padding: '28px 24px' } }}> }} styles={{ body: { padding: '36px 28px' } }}>
<Typography.Title level={3} style={{ textAlign: 'center', marginBottom: 6, color: '#1e293b', fontWeight: 700 }}> <Typography.Title level={3} style={{ textAlign: 'center', marginBottom: 6, color: '#1e293b', fontWeight: 700 }}>
{mode === 'register' ? '创建账号' : '欢迎回来'} {mode === 'register' ? '创建账号' : '欢迎回来'}
</Typography.Title> </Typography.Title>