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

@@ -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>