feat: light theme, frontend link, nav fix, rename to 七虎AI

This commit is contained in:
qyyfw committed 2026-07-07 22:33:08 +08:00
1 parent effef176e2
commit e38a32e04e
12 files changed
+429 -345

No files matched your search

+66 -38
View File
@@ -11,8 +11,8 @@
display: flex;
height: 100vh;
overflow: hidden;
background: #0A0E1A;
color: #E8ECF4;
background: #F5F7FC;
color: #1A1F36;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;
}
@@ -20,8 +20,8 @@
.admin-sidebar {
width: var(--admin-sidebar-width);
min-width: var(--admin-sidebar-width);
background: linear-gradient(180deg, #0E1225 0%, #111627 100%);
border-right: 1px solid rgba(255, 255, 255, 0.06);
background: linear-gradient(180deg, #FFFFFF 0%, #FAFBFE 100%);
border-right: 1px solid rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
transition: width 0.25s ease, min-width 0.25s ease;
@@ -41,7 +41,7 @@
justify-content: space-between;
padding: 16px 16px 16px 20px;
height: 60px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
flex-shrink: 0;
}
@@ -54,10 +54,10 @@
.admin-brand {
font-size: 15px;
font-weight: 700;
color: #E8ECF4;
color: #1A1F36;
letter-spacing: -0.02em;
white-space: nowrap;
background: linear-gradient(135deg, #C4B5FD, #A78BFA);
background: linear-gradient(135deg, #6C63FF, #A78BFA);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -72,7 +72,7 @@
border-radius: 6px;
border: none;
background: transparent;
color: #5A6480;
color: #8892B0;
transition: all 0.15s ease;
cursor: pointer;
flex-shrink: 0;
@@ -109,7 +109,7 @@
}
.admin-nav::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.08);
background: rgba(108, 99, 255, 0.10);
border-radius: 2px;
}
@@ -192,7 +192,7 @@
}
.admin-nav-item:hover {
background: rgba(255, 255, 255, 0.05);
background: rgba(108, 99, 255, 0.06);
color: #CCD6F6;
}
@@ -257,7 +257,7 @@
/* Sidebar Footer */
.admin-sidebar-footer {
padding: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
border-top: 1px solid rgba(0, 0, 0, 0.08);
flex-shrink: 0;
}
@@ -277,7 +277,7 @@
justify-content: space-between;
padding: 0 32px;
height: 60px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
background: rgba(10, 14, 26, 0.85);
backdrop-filter: blur(12px);
flex-shrink: 0;
@@ -292,7 +292,7 @@
.admin-page-title {
font-size: 17px;
font-weight: 600;
color: #E8ECF4;
color: #1A1F36;
margin: 0;
white-space: nowrap;
}
@@ -306,7 +306,7 @@
}
.breadcrumb-item {
color: #5A6480;
color: #8892B0;
}
.breadcrumb-item.current {
@@ -343,7 +343,7 @@
}
.admin-topbar-action:hover {
background: rgba(255, 255, 255, 0.05);
background: rgba(108, 99, 255, 0.06);
color: #A78BFA;
}
@@ -355,7 +355,7 @@
padding: 2px 12px 2px 4px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border: 1px solid rgba(0, 0, 0, 0.10);
font-size: 13px;
color: #CCD6F6;
}
@@ -379,7 +379,7 @@
display: flex;
flex-direction: column;
overflow: hidden;
background: #0A0E1A;
background: #F5F7FC;
}
.admin-content {
@@ -398,7 +398,7 @@
}
.admin-content::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.08);
background: rgba(108, 99, 255, 0.10);
border-radius: 3px;
}
@@ -414,7 +414,7 @@
.admin-stat-card {
background: linear-gradient(135deg, rgba(17, 22, 39, 0.8), rgba(17, 22, 39, 0.6));
border: 1px solid rgba(255, 255, 255, 0.06);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 12px;
padding: 20px;
transition: all 0.2s ease;
@@ -435,7 +435,7 @@
.admin-stat-card .stat-value {
font-size: 28px;
font-weight: 700;
color: #E8ECF4;
color: #1A1F36;
line-height: 1.2;
}
@@ -455,7 +455,7 @@
/* Page section card */
.admin-section {
background: rgba(17, 22, 39, 0.5);
border: 1px solid rgba(255, 255, 255, 0.06);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
@@ -467,13 +467,13 @@
justify-content: space-between;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.admin-section-header h3 {
font-size: 16px;
font-weight: 600;
color: #E8ECF4;
color: #1A1F36;
margin: 0;
}
@@ -522,7 +522,7 @@
text-transform: uppercase;
letter-spacing: 0.05em;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
background: rgba(255, 255, 255, 0.02);
}
@@ -565,7 +565,7 @@
.admin-btn-secondary {
background: rgba(255, 255, 255, 0.06);
color: #8892B0;
border: 1px solid rgba(255, 255, 255, 0.08);
border: 1px solid rgba(0, 0, 0, 0.10);
}
.admin-btn-secondary:hover {
@@ -614,7 +614,7 @@
gap: 8px;
padding: 8px 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border: 1px solid rgba(0, 0, 0, 0.10);
border-radius: 8px;
color: #8892B0;
flex: 1;
@@ -631,7 +631,7 @@
flex: 1;
border: none;
background: none;
color: #E8ECF4;
color: #1A1F36;
font-size: 13px;
outline: none;
}
@@ -708,8 +708,8 @@
}
.admin-modal {
background: #111627;
border: 1px solid rgba(255, 255, 255, 0.08);
background: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.10);
border-radius: 16px;
padding: 24px;
min-width: 420px;
@@ -722,7 +722,7 @@
.admin-modal h3 {
font-size: 18px;
font-weight: 600;
color: #E8ECF4;
color: #1A1F36;
margin: 0 0 20px 0;
}
@@ -745,9 +745,9 @@
width: 100%;
padding: 10px 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border: 1px solid rgba(0, 0, 0, 0.10);
border-radius: 8px;
color: #E8ECF4;
color: #1A1F36;
font-size: 13px;
transition: border-color 0.15s ease;
outline: none;
@@ -766,8 +766,8 @@
}
.admin-form-group select option {
background: #111627;
color: #E8ECF4;
background: #FFFFFF;
color: #1A1F36;
}
/* Toggle switch */
@@ -840,7 +840,7 @@
width: 32px;
height: 32px;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.08);
border: 1px solid rgba(0, 0, 0, 0.10);
background: transparent;
color: #8892B0;
font-size: 13px;
@@ -905,7 +905,7 @@
justify-content: space-between;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.admin-modal-header h3 {
@@ -926,7 +926,7 @@
}
.admin-modal-close:hover {
color: #E8ECF4;
color: #1A1F36;
background: rgba(255, 255, 255, 0.06);
}
@@ -939,7 +939,7 @@
gap: 8px;
justify-content: flex-end;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
/* Spinner animation for logo upload */
@@ -987,3 +987,31 @@
max-width: 90vw;
}
}
/* Frontend access link */
.admin-frontend-link {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
margin: 8px 12px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
color: #6C63FF;
background: rgba(108, 99, 255, 0.08);
text-decoration: none;
transition: all 0.2s;
cursor: pointer;
}
.admin-frontend-link:hover {
background: rgba(108, 99, 255, 0.15);
color: #5B5FED;
}
/* Nav link cursor pointer */
.landing-nav-v2 a {
cursor: pointer;
}
@@ -139,9 +139,13 @@ const AdminLayout: React.FC = () => {
<div className="admin-layout">
<aside className="admin-sidebar">
<div className="admin-sidebar-header">
<h1 className="admin-logo">AIFlow</h1>
<h1 className="admin-logo">七虎AI</h1>
<span className="admin-subtitle">管理控制台</span>
</div>
<a href="/#/portal" target="_blank" rel="noopener noreferrer" className="admin-frontend-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
<span>访问前台</span>
</a>
<nav className="sidebar-nav">
{adminNavGroups.map((group) => {
const isCollapsed = collapsedGroups.has(group.title);
+63 -19
View File
@@ -1,16 +1,18 @@
/* ===== Front Layout ===== */
/* ===== Front Layout V3 - Light Theme ===== */
.front-layout {
display: flex;
height: 100vh;
overflow: hidden;
background: #F5F7FC;
color: #1A1F36;
}
/* Sidebar */
.front-sidebar {
width: var(--sidebar-width);
min-width: var(--sidebar-width);
background: var(--color-sidebar-bg);
border-right: 1px solid var(--color-border-light);
background: linear-gradient(180deg, #FFFFFF 0%, #FAFBFE 100%);
border-right: 1px solid rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
transition: width var(--transition-slow), min-width var(--transition-slow);
@@ -29,7 +31,7 @@
justify-content: space-between;
padding: var(--space-lg) var(--space-lg);
height: var(--header-height);
border-bottom: 1px solid var(--color-border-light);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.sidebar-logo {
@@ -41,7 +43,7 @@
.sidebar-brand {
font-size: var(--text-lg);
font-weight: 700;
color: var(--color-text-primary);
color: #1A1F36;
letter-spacing: -0.02em;
white-space: nowrap;
}
@@ -53,13 +55,14 @@
width: 32px;
height: 32px;
border-radius: var(--radius-md);
color: var(--color-text-tertiary);
color: #8892B0;
transition: all var(--transition-fast);
cursor: pointer;
}
.collapse-btn:hover {
background: var(--color-bg-hover);
color: var(--color-text-primary);
background: rgba(0, 0, 0, 0.08);
color: #8892B0;
}
/* Navigation */
@@ -68,7 +71,7 @@
display: flex;
flex-direction: column;
justify-content: space-between;
padding: var(--space-sm) var(--space-sm);
padding: var(--space-sm);
overflow-y: auto;
}
@@ -79,12 +82,13 @@
}
.nav-item {
position: relative;
display: flex;
align-items: center;
gap: var(--space-md);
padding: var(--space-md) var(--space-md);
padding: 10px var(--space-md);
border-radius: var(--radius-md);
color: var(--color-text-secondary);
color: #8892B0;
text-decoration: none;
font-size: var(--text-base);
font-weight: 500;
@@ -93,13 +97,13 @@
}
.nav-item:hover {
background: var(--color-sidebar-hover);
color: var(--color-text-primary);
background: rgba(108, 99, 255, 0.06);
color: #1A1F36;
}
.nav-item.active {
background: var(--color-sidebar-active);
color: var(--color-accent);
background: rgba(108, 99, 255, 0.12);
color: #8B85FF;
}
.nav-icon {
@@ -115,10 +119,30 @@
flex: 1;
}
/* Active dot indicator */
.nav-active-dot {
display: none;
}
.nav-item.active .nav-active-dot {
display: block;
position: absolute;
right: 8px;
width: 6px;
height: 6px;
border-radius: 50%;
background: #6C63FF;
box-shadow: 0 0 8px rgba(108, 99, 255, 0.5);
}
/* Sidebar Footer */
.sidebar-footer {
padding: var(--space-md);
border-top: 1px solid var(--color-border-light);
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.sidebar-footer .nav-item {
margin-top: 2px;
}
.user-info-mini {
@@ -126,13 +150,14 @@
align-items: center;
gap: var(--space-md);
padding: var(--space-sm);
margin-bottom: 4px;
}
.user-avatar-sm {
width: 32px;
height: 32px;
border-radius: var(--radius-full);
background: linear-gradient(135deg, var(--color-accent), #8B8DFF);
background: linear-gradient(135deg, #6C63FF, #A78BFA);
color: white;
display: flex;
align-items: center;
@@ -151,16 +176,21 @@
.user-name {
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text-primary);
color: #1A1F36;
line-height: 1.3;
}
.user-plan {
font-size: var(--text-xs);
color: var(--color-accent);
color: #8B85FF;
line-height: 1.3;
}
.logout-btn:hover {
color: #EF4444 !important;
background: rgba(239, 68, 68, 0.1) !important;
}
/* Main Content */
.front-main {
flex: 1;
@@ -168,6 +198,12 @@
display: flex;
flex-direction: column;
min-width: 0;
background: #F5F7FC;
}
/* Forward-slash route for apps - hidden default route */
.front-main > * {
height: 100%;
}
/* Responsive */
@@ -187,3 +223,11 @@
transform: translateX(0);
}
}
/* Home & Admin links */
.nav-home-link {
margin-bottom: 4px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
padding-bottom: 8px;
}
+31 -14
View File
@@ -5,13 +5,13 @@ import './FrontLayout.css';
const navItems = [
{ path: '/chat', icon: 'chat', label: 'AI聊天' },
{ path: '/channels', icon: 'channel', label: '频道' },
{ path: '/channels', icon: 'channel', label: 'IM Bot' },
{ path: '/knowledge', icon: 'knowledge', label: '知识库' },
{ path: '/market', icon: 'market', label: '应用市场' },
{ path: '/market', icon: 'market', label: '技能市场' },
{ path: '/token', icon: 'token', label: 'Token驾驶舱' },
{ path: '/pricing', icon: 'pricing', label: '付费中心' },
{ path: '/assets', icon: 'assets', label: '资产' },
{ path: '/evolution', icon: 'evolution', label: 'AI进化' },
{ path: '/assets', icon: 'assets', label: '资产文件' },
{ path: '/evolution', icon: 'evolution', label: 'AI自进化' },
];
const bottomItems = [
@@ -24,6 +24,7 @@ const iconPaths: Record<string, JSX.Element> = {
channel: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
knowledge: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>,
market: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.2"/><rect x="14" y="3" width="7" height="7" rx="1.2"/><rect x="3" y="14" width="7" height="7" rx="1.2"/><rect x="14" y="14" width="7" height="7" rx="1.2"/></svg>,
apps: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="2" width="9" height="9" rx="2"/><rect x="13" y="2" width="9" height="9" rx="2"/><rect x="2" y="13" width="9" height="9" rx="2"/><rect x="13" y="13" width="5" height="5" rx="1.5"/><path d="M18 18v3"/><path d="M19.5 16.5v6"/></svg>,
token: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>,
pricing: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"/><path d="M12 18V6"/></svg>,
profile: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>,
@@ -46,32 +47,48 @@ const FrontLayout: React.FC = () => {
<div className="front-layout">
{/* Sidebar */}
<aside className={`front-sidebar ${collapsed ? 'collapsed' : ''}`}>
{/* Header */}
<div className="sidebar-header">
<div className="sidebar-logo">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="8" fill="#5B5FED"/>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
<defs>
<linearGradient id="flGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#6C63FF"/>
<stop offset="100%" stopColor="#A78BFA"/>
</linearGradient>
</defs>
<rect width="32" height="32" rx="8" fill="url(#flGrad)"/>
<path d="M8 20V12l4 4 4-4v8" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
<path d="M6 22h20" stroke="white" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/>
</svg>
{!collapsed && <span className="sidebar-brand">Qihu-AIFlow</span>}
{!collapsed && <span className="sidebar-brand">七虎AI</span>}
</div>
<button className="collapse-btn" onClick={() => setCollapsed(!collapsed)}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/>
{collapsed ? <><polyline points="13 17 18 12 13 7"/><polyline points="6 17 11 12 6 7"/></> : <><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></>}
</svg>
</button>
</div>
{/* Navigation */}
<nav className="sidebar-nav">
<NavLink to="/" className={({ isActive }) => `nav-item nav-home-link`} end>
<span className="nav-item-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
</span>
{!collapsed && <span className="nav-item-label">返回首页</span>}
</NavLink>
<div className="nav-section">
{navItems.map(item => (
<NavLink
key={item.path}
to={item.path}
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
title={collapsed ? item.label : undefined}
>
<span className="nav-icon">{iconPaths[item.icon]}</span>
{!collapsed && <span className="nav-label">{item.label}</span>}
{!collapsed && <span className="nav-active-dot" />}
</NavLink>
))}
</div>
@@ -81,14 +98,17 @@ const FrontLayout: React.FC = () => {
key={item.path}
to={item.path}
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
title={collapsed ? item.label : undefined}
>
<span className="nav-icon">{iconPaths[item.icon]}</span>
{!collapsed && <span className="nav-label">{item.label}</span>}
{!collapsed && <span className="nav-active-dot" />}
</NavLink>
))}
</div>
</nav>
{/* Footer */}
<div className="sidebar-footer">
<div className="user-info-mini">
<div className="user-avatar-sm">
@@ -105,17 +125,14 @@ const FrontLayout: React.FC = () => {
<NavLink
to="/admin"
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
title="返回后台"
title="管理后台"
>
<span className="nav-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1.2"/>
<rect x="14" y="3" width="7" height="7" rx="1.2"/>
<rect x="3" y="14" width="7" height="7" rx="1.2"/>
<rect x="14" y="14" width="7" height="7" rx="1.2"/>
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
</span>
{!collapsed && <span className="nav-label">返回后台</span>}
{!collapsed && <span className="nav-label">管理后台</span>}
</NavLink>
)}
<button
+2 -2
View File
@@ -312,7 +312,7 @@ const SiteSettings: React.FC = () => {
<FormGroup label="网站名称" required>
<input className="admin-form-input" value={brand.siteName}
onChange={e => setBrand({ ...brand, siteName: e.target.value })}
placeholder="例如: Qihu-AIFlow" />
placeholder="例如: 七虎AI" />
</FormGroup>
<FormGroup label="LOGO URL">
<input className="admin-form-input" value={brand.logo}
@@ -638,7 +638,7 @@ const SiteSettings: React.FC = () => {
value={registration.welcome_message}
onChange={e => setRegistration({ ...registration, welcome_message: e.target.value })}
style={{ minHeight: 80 }}
placeholder="欢迎加入 Qihu-AIFlow!从这里开始你的 AI 创造之旅..." />
placeholder="欢迎加入七虎AI!从这里开始你的 AI 创造之旅..." />
</FormGroup>
<FormActions>
@@ -407,7 +407,7 @@ const TimelinePanel: React.FC<{
pdf.text('我的AI进化时间线', 10, 15);
pdf.setFontSize(10);
pdf.text(`生成日期: ${dateStr}`, 10, 22);
pdf.text('Qihu-AIFlow', pageWidth - 10, 22, { align: 'right' });
pdf.text('七虎AI', pageWidth - 10, 22, { align: 'right' });
// 单页添加图片,自动缩放适应
const maxHeight = pageHeight - 35;
@@ -417,7 +417,7 @@ const TimelinePanel: React.FC<{
// 底部水印
pdf.setFontSize(8);
pdf.setTextColor(200, 200, 200);
pdf.text('Qihu-AIFlow', pageWidth / 2, pageHeight - 5, { align: 'center' });
pdf.text('七虎AI', pageWidth / 2, pageHeight - 5, { align: 'center' });
pdf.save(`${filename}.pdf`);
setExportMsg(`时间线已导出为 PDF: ${filename}.pdf`);
@@ -746,7 +746,7 @@ const ProfilePanel: React.FC<{ profile: EvolutionProfile; skills: EvolutionSkill
{/* Footer */}
<div className="evo-share-card-footer">
<span style={{ opacity: 0.5, fontSize: '11px' }}>{new Date().toLocaleDateString()}</span>
<span style={{ opacity: 0.5, fontSize: '11px' }}>Qihu-AIFlow</span>
<span style={{ opacity: 0.5, fontSize: '11px' }}>七虎AI</span>
</div>
</div>
</div>
+23 -23
View File
@@ -10,27 +10,27 @@
overflow-x: hidden;
}
/* === Dark Theme Variables (scoped to landing page) === */
/* === Light Theme Variables (scoped to landing page) === */
.landing-page-v2 {
--lp-bg: #0A0E1A;
--lp-bg-secondary: #111627;
--lp-bg-card: #161B2E;
--lp-bg-card-hover: #1C2240;
--lp-bg-glass: rgba(22, 27, 46, 0.7);
--lp-border: rgba(255, 255, 255, 0.06);
--lp-border-light: rgba(255, 255, 255, 0.04);
--lp-text: #E8ECF4;
--lp-text-secondary: #8892B0;
--lp-text-tertiary: #5A6480;
--lp-bg: #FFFFFF;
--lp-bg-secondary: #F5F7FC;
--lp-bg-card: #FFFFFF;
--lp-bg-card-hover: #F5F7FC;
--lp-bg-glass: rgba(255, 255, 255, 0.8);
--lp-border: rgba(0, 0, 0, 0.08);
--lp-border-light: rgba(0, 0, 0, 0.05);
--lp-text: #1A1F36;
--lp-text-secondary: #5A6480;
--lp-text-tertiary: #8892B0;
--lp-accent: #6C63FF;
--lp-accent-light: #8B85FF;
--lp-accent-glow: rgba(108, 99, 255, 0.3);
--lp-gradient-1: linear-gradient(135deg, #6C63FF 0%, #A78BFA 50%, #C084FC 100%);
--lp-gradient-2: linear-gradient(135deg, #06B6D4 0%, #3B82F6 100%);
--lp-gradient-3: linear-gradient(135deg, #10B981 0%, #34D399 100%);
--lp-gradient-hero: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(108, 99, 255, 0.15), transparent 70%);
--lp-shadow-card: 0 1px 3px rgba(0,0,0,0.3), 0 4px 12px rgba(0,0,0,0.2);
--lp-shadow-lg: 0 8px 32px rgba(0,0,0,0.4);
--lp-gradient-hero: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(108, 99, 255, 0.08), transparent 70%);
--lp-shadow-card: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
--lp-shadow-lg: 0 8px 32px rgba(0,0,0,0.10);
--lp-radius: 12px;
--lp-radius-sm: 8px;
--lp-radius-lg: 16px;
@@ -42,7 +42,7 @@
top: 24px;
left: 50%;
transform: translateX(-50%);
background: rgba(22, 27, 46, 0.95);
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border: 1px solid rgba(108, 99, 255, 0.3);
color: var(--lp-text);
@@ -71,7 +71,7 @@
}
.landing-header-v2.scrolled {
background: rgba(10, 14, 26, 0.85);
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--lp-border);
padding: 10px 0;
@@ -311,7 +311,7 @@
font-size: 16px;
font-weight: 500;
color: var(--lp-text-secondary);
background: rgba(255, 255, 255, 0.04);
background: rgba(108, 99, 255, 0.04);
border: 1px solid var(--lp-border);
cursor: pointer;
transition: all 0.3s ease;
@@ -319,8 +319,8 @@
.btn-hero-secondary:hover {
color: var(--lp-text);
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.12);
background: rgba(108, 99, 255, 0.10);
border-color: rgba(0, 0, 0, 0.10);
}
.hero-stats-v2 {
@@ -441,7 +441,7 @@
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
background: rgba(108, 99, 255, 0.20);
border: none;
cursor: pointer;
transition: all 0.3s;
@@ -622,8 +622,8 @@
font-size: 13px;
font-weight: 500;
color: var(--lp-text-secondary);
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.06);
background: rgba(108, 99, 255, 0.04);
border: 1px solid rgba(0, 0, 0, 0.08);
transition: all 0.2s;
white-space: nowrap;
}
@@ -763,7 +763,7 @@
.filter-chip:hover {
color: var(--lp-text);
border-color: rgba(255, 255, 255, 0.12);
border-color: rgba(0, 0, 0, 0.10);
}
.filter-chip.active {
+17 -17
View File
@@ -157,14 +157,14 @@ const LandingPage: React.FC = () => {
<path d="M8 20V12l4 4 4-4v8" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
<path d="M6 22h20" stroke="white" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/>
</svg>
<span className="landing-brand-text">Qihu-AIFlow</span>
<span className="landing-brand-text">七虎AI</span>
</div>
<nav className="landing-nav-v2">
<a href="#what-is">平台介绍</a>
<a href="#architecture">平台架构</a>
<a href="#capabilities">能力体系</a>
<a href="#scenarios">应用场景</a>
<a href="#market">技能生态</a>
<nav className="landing-nav-v2" style={{ cursor: 'pointer' }}>
<a onClick={() => document.getElementById('what-is')?.scrollIntoView({ behavior: 'smooth' })}>平台介绍</a>
<a onClick={() => document.getElementById('architecture')?.scrollIntoView({ behavior: 'smooth' })}>平台架构</a>
<a onClick={() => document.getElementById('capabilities')?.scrollIntoView({ behavior: 'smooth' })}>能力体系</a>
<a onClick={() => document.getElementById('scenarios')?.scrollIntoView({ behavior: 'smooth' })}>应用场景</a>
<a onClick={() => document.getElementById('market')?.scrollIntoView({ behavior: 'smooth' })}>技能生态</a>
</nav>
<div className="landing-actions-v2">
<button className="btn-ghost-v2" onClick={() => navigate('/login')}>登录</button>
@@ -173,7 +173,7 @@ const LandingPage: React.FC = () => {
</div>
</header>
{/* Hero Section — What is Qihu-AIFlow */}
{/* Hero Section — What is 七虎AI */}
<section className="hero-section-v2">
<div className="hero-bg-orb orb-1" />
<div className="hero-bg-orb orb-2" />
@@ -188,7 +188,7 @@ const LandingPage: React.FC = () => {
<span className="hero-gradient-text"> 的超级工作台</span>
</h1>
<p className="hero-subtitle-v2">
Qihu-AIFlow 是一站式 AI 能力集成平台,让你在一个地方完成对话、知识库、技能编排、模型切换、自动化搭建<br/>
七虎AI 是一站式 AI 能力集成平台,让你在一个地方完成对话、知识库、技能编排、模型切换、自动化搭建<br/>
无需拼接多个工具,从想法到落地,一个平台就够了
</p>
<div className="hero-cta-v2">
@@ -226,11 +226,11 @@ const LandingPage: React.FC = () => {
</div>
</section>
{/* What is Qihu-AIFlow — Platform Introduction */}
{/* What is 七虎AI — Platform Introduction */}
<section id="what-is" className="whatis-section-v2">
<div className="section-header-v2">
<span className="section-badge-v2">平台介绍</span>
<h2>Qihu-AIFlow 是什么?</h2>
<h2>七虎AI 是什么?</h2>
<p>一个面向 AI 爱好者和从业者的个人 AI 能力集成平台</p>
</div>
<div className="whatis-cards-v2">
@@ -262,7 +262,7 @@ const LandingPage: React.FC = () => {
<div className="section-header-v2">
<span className="section-badge-v2">平台架构</span>
<h2>四层架构 · 层层赋能</h2>
<p>从基础设施到上层应用,理解 Qihu-AIFlow 的技术全貌</p>
<p>从基础设施到上层应用,理解 七虎AI 的技术全貌</p>
</div>
<div className="arch-layers-v2">
{archLayers.map((layer, idx) => (
@@ -417,14 +417,14 @@ const LandingPage: React.FC = () => {
<path d="M8 20V12l4 4 4-4v8" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
<path d="M6 22h20" stroke="white" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/>
</svg>
<span>Qihu-AIFlow</span>
<span>七虎AI</span>
</div>
<div className="footer-links-v2">
<div className="footer-col-v2">
<h4>平台</h4>
<a href="#what-is">平台介绍</a>
<a href="#architecture">平台架构</a>
<a href="#capabilities">能力体系</a>
<a onClick={() => document.getElementById('what-is')?.scrollIntoView({ behavior: 'smooth' })}>平台介绍</a>
<a onClick={() => document.getElementById('architecture')?.scrollIntoView({ behavior: 'smooth' })}>平台架构</a>
<a onClick={() => document.getElementById('capabilities')?.scrollIntoView({ behavior: 'smooth' })}>能力体系</a>
</div>
<div className="footer-col-v2">
<h4>资源</h4>
@@ -441,7 +441,7 @@ const LandingPage: React.FC = () => {
</div>
</div>
<div className="footer-bottom-v2">
<p>&copy; 2026 Qihu-AIFlow. All rights reserved.</p>
<p>&copy; 2026 七虎AI. All rights reserved.</p>
</div>
</footer>
</div>
+159 -154
View File
@@ -1,14 +1,15 @@
/* ===== Login Page ===== */
/* ===== Login Page V3 - Dark Theme ===== */
.login-page {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: var(--color-bg-primary);
background: #F5F7FC;
position: relative;
overflow: hidden;
}
/* Background decorations */
.login-bg-decor {
position: absolute;
inset: 0;
@@ -19,35 +20,37 @@
.bg-circle {
position: absolute;
border-radius: 50%;
opacity: 0.08;
filter: blur(80px);
}
.bg-circle.c1 {
width: 600px; height: 600px;
background: var(--color-accent);
background: #6C63FF;
top: -200px; right: -100px;
opacity: 0.12;
}
.bg-circle.c2 {
width: 400px; height: 400px;
background: #8B8DFF;
background: #06B6D4;
bottom: -100px; left: -50px;
opacity: 0.1;
}
.bg-circle.c3 {
width: 300px; height: 300px;
background: var(--color-accent);
background: #A78BFA;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
opacity: 0.04;
opacity: 0.06;
}
.bg-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(0,0,0,0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,0,0,0.02) 1px, transparent 1px);
linear-gradient(rgba(108, 99, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(108, 99, 255, 0.03) 1px, transparent 1px);
background-size: 60px 60px;
}
@@ -56,203 +59,212 @@
display: flex;
width: 900px;
min-height: 560px;
background: #FFFFFF;
border-radius: var(--radius-2xl);
box-shadow: var(--shadow-xl);
background: rgba(17, 22, 39, 0.95);
backdrop-filter: blur(24px);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
overflow: hidden;
z-index: var(--z-base);
position: relative;
z-index: 1;
animation: loginSlideUp 0.5s ease;
}
/* Brand Panel */
@keyframes loginSlideUp {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
/* Left brand panel */
.login-brand-panel {
width: 400px;
background: linear-gradient(135deg, #2D2B55 0%, #1A1A2E 50%, #2D2B55 100%);
background: linear-gradient(135deg, rgba(108, 99, 255, 0.08) 0%, rgba(167, 139, 250, 0.05) 100%);
padding: 48px 40px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.login-brand-panel::before {
content: '';
position: absolute;
width: 300px; height: 300px;
border-radius: 50%;
background: radial-gradient(circle, rgba(91,95,237,0.3) 0%, transparent 70%);
top: -50px; right: -50px;
}
.login-brand-panel::after {
content: '';
position: absolute;
width: 200px; height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(139,141,255,0.2) 0%, transparent 70%);
bottom: -30px; left: -30px;
border-right: 1px solid rgba(255, 255, 255, 0.04);
}
.brand-content {
text-align: center;
color: white;
z-index: 1;
width: 100%;
}
.brand-icon {
margin-bottom: var(--space-xl);
margin-bottom: 20px;
}
.brand-title {
font-size: 36px;
font-size: 32px;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: var(--space-sm);
color: #1A1F36;
margin-bottom: 8px;
letter-spacing: -0.02em;
}
.brand-desc {
font-size: var(--text-base);
color: rgba(255,255,255,0.6);
margin-bottom: var(--space-3xl);
font-size: 15px;
color: #5A6480;
margin-bottom: 32px;
}
.brand-features {
display: flex;
flex-direction: column;
gap: var(--space-md);
text-align: left;
gap: 14px;
}
.brand-feature {
display: flex;
align-items: center;
gap: var(--space-md);
font-size: var(--text-sm);
color: rgba(255,255,255,0.75);
gap: 10px;
font-size: 14px;
color: #5A6480;
}
.bf-icon {
color: #6C63FF;
font-weight: 700;
font-size: var(--text-base);
}
/* Form Panel */
.login-form-panel {
flex: 1;
width: 20px;
height: 20px;
border-radius: 50%;
background: rgba(16, 185, 129, 0.15);
color: #10B981;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-4xl);
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
}
/* Right form panel */
.login-form-panel {
flex: 1;
padding: 48px 40px;
display: flex;
align-items: center;
}
.login-form-inner {
width: 100%;
max-width: 340px;
}
/* Tabs */
.login-tabs {
display: flex;
gap: var(--space-2xl);
margin-bottom: var(--space-3xl);
background: rgba(255, 255, 255, 0.04);
border-radius: 10px;
padding: 4px;
margin-bottom: 24px;
}
.login-tab {
font-size: var(--text-lg);
font-weight: 700;
color: var(--color-text-tertiary);
padding-bottom: var(--space-sm);
border-bottom: 2px solid transparent;
transition: all var(--transition-fast);
flex: 1;
padding: 10px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
color: #5A6480;
background: transparent;
border: none;
cursor: pointer;
transition: all 0.2s;
}
.login-tab.active {
color: var(--color-text-primary);
border-bottom-color: var(--color-accent);
background: rgba(108, 99, 255, 0.15);
color: #8B85FF;
}
.login-tab:hover {
color: var(--color-text-primary);
.login-tab:hover:not(.active) {
color: #5A6480;
}
/* Error message */
.form-error {
padding: 10px 14px;
border-radius: 8px;
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.2);
color: #F87171;
font-size: 13px;
text-align: center;
margin-bottom: 16px;
}
/* Form */
.login-form {
display: flex;
flex-direction: column;
gap: var(--space-xl);
gap: 16px;
}
.form-group {
display: flex;
flex-direction: column;
gap: var(--space-sm);
margin-bottom: 16px;
gap: 6px;
}
.form-label {
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text-primary);
margin-bottom: 6px;
font-size: 13px;
font-weight: 500;
color: #5A6480;
}
.required {
color: #EF4444;
}
.form-input {
width: 100%;
padding: 12px 16px;
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
font-size: var(--text-base);
color: var(--color-text-primary);
background: var(--color-bg-primary);
transition: all var(--transition-fast);
padding: 12px 14px;
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.10);
background: rgba(255, 255, 255, 0.03);
color: #1A1F36;
font-size: 14px;
outline: none;
transition: all 0.2s;
width: 100%;
}
.form-input:focus {
border-color: var(--color-accent);
box-shadow: 0 0 0 3px var(--color-accent-light);
background: #FFFFFF;
border-color: rgba(108, 99, 255, 0.4);
background: rgba(108, 99, 255, 0.05);
box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.1);
}
.form-input::placeholder {
color: var(--color-text-disabled);
color: #3A4460;
}
/* Submit button */
.login-submit-btn {
height: 46px;
background: linear-gradient(135deg, #5B5FED, #6C63FF);
width: 100%;
padding: 13px;
border-radius: 10px;
border: none;
background: linear-gradient(135deg, #6C63FF, #A78BFA);
color: white;
border-radius: var(--radius-lg);
font-size: var(--text-base);
font-weight: 700;
letter-spacing: 0.15em;
transition: all var(--transition-fast);
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
margin-top: 8px;
}
.login-submit-btn:hover {
background: linear-gradient(135deg, #4A4ECC, #5B5FED);
.login-submit-btn:hover:not(:disabled) {
box-shadow: 0 4px 20px rgba(108, 99, 255, 0.4);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.login-submit-btn:active {
transform: translateY(0);
}
.login-submit-btn:disabled {
opacity: 0.5;
opacity: 0.6;
cursor: not-allowed;
}
.login-submit-btn:disabled:hover {
transform: none;
box-shadow: none;
}
/* Register Method Tabs */
/* Register method tabs */
.register-method-tabs {
display: flex;
gap: var(--space-sm);
margin-bottom: var(--space-lg);
gap: 12px;
margin-bottom: 4px;
}
.register-method-tab {
@@ -261,32 +273,32 @@
align-items: center;
justify-content: center;
gap: 6px;
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
background: var(--color-bg-primary);
font-size: var(--text-sm);
padding: 8px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.10);
background: transparent;
color: #5A6480;
font-size: 13px;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: all var(--transition-fast);
}
.register-method-tab:hover {
border-color: var(--color-accent-light);
transition: all 0.2s;
}
.register-method-tab.active {
border-color: var(--color-accent);
background: var(--color-accent-subtle);
color: var(--color-accent);
font-weight: 600;
border-color: rgba(108, 99, 255, 0.3);
background: rgba(108, 99, 255, 0.1);
color: #8B85FF;
}
/* SMS Row */
.register-method-tab:hover:not(.active) {
border-color: rgba(255, 255, 255, 0.12);
color: #5A6480;
}
/* SMS row */
.sms-row {
display: flex;
gap: var(--space-sm);
gap: 10px;
}
.sms-input {
@@ -294,49 +306,42 @@
}
.sms-btn {
padding: var(--space-sm) var(--space-lg);
border-radius: var(--radius-md);
border: 1px solid var(--color-accent);
background: transparent;
color: var(--color-accent);
font-size: var(--text-sm);
padding: 10px 16px;
border-radius: 10px;
border: 1px solid rgba(108, 99, 255, 0.2);
background: rgba(108, 99, 255, 0.08);
color: #8B85FF;
font-size: 13px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
transition: all var(--transition-fast);
transition: all 0.2s;
}
.sms-btn:hover:not(:disabled) {
background: var(--color-accent);
color: white;
background: rgba(108, 99, 255, 0.15);
}
.sms-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
border-color: var(--color-border);
color: var(--color-text-tertiary);
}
/* Required Marker */
.required {
color: var(--color-error);
}
/* Responsive */
@media (max-width: 768px) {
.login-container {
flex-direction: column;
width: 90vw;
width: calc(100vw - 32px);
min-height: auto;
margin: 16px;
}
.login-brand-panel {
width: 100%;
padding: var(--space-3xl) var(--space-2xl);
padding: 32px 24px;
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.login-form-panel {
padding: var(--space-2xl);
}
.brand-features {
display: none;
padding: 32px 24px;
}
}
+26 -40
View File
@@ -65,38 +65,22 @@ const LoginPage: React.FC = () => {
setLoading(true);
try {
const res = await authAPI.login(loginAccount, loginPassword);
const { access_token, user: userData } = res.data;
localStorage.setItem('aiflow_token', access_token);
const data: any = (res as any).data || res;
const token = data.access_token || data.token;
const userData = data.user || data;
localStorage.setItem('aiflow_token', token);
const userObj: User = {
id: userData.id,
username: userData.username,
email: userData.email || '',
phone: userData.phone || '',
avatar: userData.avatar || '',
role: userData.role,
status: userData.status,
realNameStatus: userData.real_name_status || 'unverified',
createdAt: userData.created_at || new Date().toISOString(),
membership: userData.membership_level > 0 ? {
planId: userData.membership_level >= 3 ? 'mp3' : (userData.membership_level >= 2 ? 'mp2' : 'mp1'),
planName: userData.role === 'admin' ? '管理员' : (userData.membership_level >= 3 ? '企业版' : (userData.membership_level >= 2 ? '专业版' : '免费版')),
level: userData.membership_level,
expireAt: userData.membership_expire_at || '',
autoRenew: userData.auto_renew || false,
} : undefined,
id: userData.id, username: userData.username,
email: userData.email || '', phone: userData.phone || '',
avatar: userData.avatar || '', role: userData.role || 'user',
status: 'active', realNameStatus: 'unverified',
createdAt: new Date().toISOString(),
};
login(userObj);
navigate(userData.role === 'admin' ? '/admin' : '/chat');
navigate(userObj.role === 'admin' ? '/admin' : '/chat');
} catch (e: any) {
// 处理后端返回的精确错误信息,区分用户不存在和密码错误
const status = e.status || e.response?.status || 0;
const msg = e.detail || e.message || '';
if (status === 403) {
setErrorMsg('账号已被禁用,请联系管理员');
} else if (status === 401) {
// 后端的detail精确指明了原因:"用户名不存在" 或 "密码错误"
setErrorMsg(msg || '用户名或密码错误');
} else if (status === 0 || msg.includes('NetworkError') || msg.includes('Failed to fetch')) {
const msg = (e as any).detail || (e as any).message || '';
if (msg.includes('Network') || msg.includes('fetch')) {
setErrorMsg('网络连接失败,请检查网络');
} else {
setErrorMsg(msg || '登录失败,请稍后重试');
@@ -121,14 +105,10 @@ const LoginPage: React.FC = () => {
const { access_token, user: userData } = res.data;
localStorage.setItem('aiflow_token', access_token);
const userObj: User = {
id: userData.id,
username: userData.username,
email: userData.email || '',
phone: userData.phone || '',
avatar: userData.avatar || '',
role: 'user',
status: 'active',
realNameStatus: 'unverified',
id: userData.id, username: userData.username,
email: userData.email || '', phone: userData.phone || '',
avatar: userData.avatar || '', role: 'user',
status: 'active', realNameStatus: 'unverified',
createdAt: new Date().toISOString(),
};
login(userObj);
@@ -158,12 +138,18 @@ const LoginPage: React.FC = () => {
<div className="brand-content">
<div className="brand-icon">
<svg width="48" height="48" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="8" fill="white"/>
<path d="M8 20V12l4 4 4-4v8" stroke="#5B5FED" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
<path d="M6 22h20" stroke="#5B5FED" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/>
<defs>
<linearGradient id="lpBrandGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#6C63FF"/>
<stop offset="100%" stopColor="#A78BFA"/>
</linearGradient>
</defs>
<rect width="32" height="32" rx="8" fill="url(#lpBrandGrad)"/>
<path d="M8 20V12l4 4 4-4v8" stroke="white" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
<path d="M6 22h20" stroke="white" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/>
</svg>
</div>
<h1 className="brand-title">AIFlow</h1>
<h1 className="brand-title">七虎AI</h1>
<p className="brand-desc">智能自动化PaaS平台</p>
<div className="brand-features">
<div className="brand-feature"><span className="bf-icon">&#x2713;</span><span>一站式AI自动化SOP</span></div>
+33 -33
View File
@@ -1,22 +1,22 @@
/* ===== AIFlow Design System v2 - Dark Theme Default ===== */
/* 设计方向:融合暗黑科技感 + 极简主义 */
/* ===== 七虎AI Design System v2 - Light Theme ===== */
/* 设计方向:明亮简洁 + 透明玻璃态 */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap');
/* CSS Custom Properties - Dark Theme */
/* CSS Custom Properties - Light Theme */
:root {
/* Colors - Dark Theme */
--color-bg-primary: #0A0E1A;
--color-bg-secondary: #111627;
--color-bg-tertiary: #161B2E;
--color-bg-hover: #1C2240;
--color-bg-active: #22284A;
--color-bg-primary: #FFFFFF;
--color-bg-secondary: #F5F7FC;
--color-bg-tertiary: #FFFFFF;
--color-bg-hover: #F0F2F8;
--color-bg-active: #E8EBF5;
--color-text-primary: #E8ECF4;
--color-text-secondary: #8892B0;
--color-text-tertiary: #5A6480;
--color-text-disabled: #3A4460;
--color-text-inverse: #0A0E1A;
--color-text-primary: #1A1F36;
--color-text-secondary: #5A6480;
--color-text-tertiary: #8892B0;
--color-text-disabled: #C4C9D9;
--color-text-inverse: #FFFFFF;
--color-accent: #6C63FF;
--color-accent-hover: #8B85FF;
@@ -32,25 +32,25 @@
--color-info: #3B82F6;
--color-info-light: rgba(59, 130, 246, 0.12);
--color-border: rgba(255, 255, 255, 0.08);
--color-border-light: rgba(255, 255, 255, 0.05);
--color-border-hover: rgba(255, 255, 255, 0.14);
--color-border: rgba(0, 0, 0, 0.10);
--color-border-light: rgba(0, 0, 0, 0.06);
--color-border-hover: rgba(0, 0, 0, 0.16);
--color-sidebar-bg: #0E1225;
--color-sidebar-hover: rgba(255, 255, 255, 0.05);
--color-sidebar-bg: #FFFFFF;
--color-sidebar-hover: rgba(108, 99, 255, 0.06);
--color-sidebar-active: rgba(108, 99, 255, 0.12);
/* Surface/card colors */
--color-surface: #111627;
--color-surface-hover: #161B2E;
--color-surface-elevated: #1C2240;
--color-surface: #FFFFFF;
--color-surface-hover: #F5F7FC;
--color-surface-elevated: #FFFFFF;
/* Shadows - Dark theme */
--shadow-xs: 0 1px 2px rgba(0,0,0,0.2);
--shadow-sm: 0 1px 3px rgba(0,0,0,0.25), 0 1px 2px rgba(0,0,0,0.15);
--shadow-md: 0 4px 12px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.2);
--shadow-lg: 0 12px 32px rgba(0,0,0,0.35), 0 2px 6px rgba(0,0,0,0.2);
--shadow-xl: 0 20px 48px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.25);
--shadow-xs: 0 1px 2px rgba(0,0,0,0.06);
--shadow-sm: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.10), 0 1px 3px rgba(0,0,0,0.06);
--shadow-lg: 0 12px 32px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.06);
--shadow-xl: 0 20px 48px rgba(0,0,0,0.15), 0 4px 12px rgba(0,0,0,0.08);
/* Spacing */
--space-xs: 4px;
@@ -553,7 +553,7 @@ input, textarea, select {
.admin-card,
.stat-card,
.dashboard-card {
background: rgba(22, 27, 46, 0.6) !important;
background: rgba(255, 255, 255, 0.7) !important;
backdrop-filter: blur(12px) !important;
-webkit-backdrop-filter: blur(12px) !important;
border: 1px solid rgba(255, 255, 255, 0.06) !important;
@@ -605,7 +605,7 @@ select,
.form-input,
.form-control {
background: rgba(10, 14, 26, 0.6) !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
border: 1px solid rgba(0, 0, 0, 0.08) !important;
color: var(--color-text-primary) !important;
border-radius: var(--radius-md) !important;
transition: border-color 0.2s, box-shadow 0.2s !important;
@@ -647,14 +647,14 @@ button[type="submit"] {
/* Secondary/ghost button */
.btn-secondary,
.admin-btn-secondary {
background: rgba(255, 255, 255, 0.05) !important;
background: rgba(108, 99, 255, 0.06) !important;
color: var(--color-text-secondary) !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
border: 1px solid rgba(0, 0, 0, 0.08) !important;
}
.btn-secondary:hover,
.admin-btn-secondary:hover {
background: rgba(255, 255, 255, 0.08) !important;
background: rgba(108, 99, 255, 0.10) !important;
color: var(--color-text-primary) !important;
}
@@ -735,7 +735,7 @@ a:hover {
/* Modal/dialog overlay */
.modal-overlay,
.dialog-overlay {
background: rgba(10, 14, 26, 0.7) !important;
background: rgba(255, 255, 255, 0.7) !important;
backdrop-filter: blur(4px) !important;
}
@@ -743,5 +743,5 @@ a:hover {
.admin-card:hover,
.stat-card:hover {
border-color: rgba(108, 99, 255, 0.2) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08) !important;
}