imetting_frontend/src/pages/HomePage.jsx

237 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useState, useEffect } from 'react';
import { Brain, Users, Calendar, TrendingUp, X, User, Lock, Library, Download, LogIn, Eye, EyeOff } from 'lucide-react';
import { Link } from 'react-router-dom';
import apiClient from '../utils/apiClient';
import { buildApiUrl, API_ENDPOINTS } from '../config/api';
import './HomePage.css';
const HomePage = ({ onLogin }) => {
const [showLoginModal, setShowLoginModal] = useState(false);
const [loginForm, setLoginForm] = useState({ username: '', password: '' });
const [loginError, setLoginError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [rememberMe, setRememberMe] = useState(false);
const [showPassword, setShowPassword] = useState(false);
// 组件挂载时从localStorage读取保存的用户名
useEffect(() => {
const savedUsername = localStorage.getItem('rememberedUsername');
const isRemembered = localStorage.getItem('rememberMe') === 'true';
if (savedUsername && isRemembered) {
setLoginForm(prev => ({ ...prev, username: savedUsername }));
setRememberMe(true);
}
}, []);
const handleLogin = async (e) => {
e.preventDefault();
setIsLoading(true);
setLoginError('');
try {
const loginResponse = await apiClient.post(buildApiUrl(API_ENDPOINTS.AUTH.LOGIN), loginForm);
// 处理记住用户名
if (rememberMe) {
localStorage.setItem('rememberedUsername', loginForm.username);
localStorage.setItem('rememberMe', 'true');
} else {
localStorage.removeItem('rememberedUsername');
localStorage.removeItem('rememberMe');
}
onLogin(loginResponse.data);
setShowLoginModal(false);
} catch (error) {
setLoginError(error.response?.data?.message || '登录失败,请重试');
} finally {
setIsLoading(false);
}
};
const handleInputChange = (e) => {
setLoginForm({
...loginForm,
[e.target.name]: e.target.value
});
};
const closeModal = () => {
setShowLoginModal(false);
setLoginForm({ username: '', password: '' });
setLoginError('');
};
return (
<div className="homepage">
{/* Header */}
<header className="homepage-header">
<div className="header-content">
<div className="logo">
<Brain className="logo-icon" />
<span className="logo-text">iMeeting</span>
</div>
<div className="header-actions">
<Link to="/downloads" className="download-link">
<Download size={18} />
<span>下载客户端</span>
</Link>
<button
className="login-btn"
onClick={() => setShowLoginModal(true)}
>
<LogIn size={18} />
<span>登录</span>
</button>
</div>
</div>
</header>
{/* Hero Section */}
<section className="hero">
<div className="hero-content">
<h1 className="hero-title">iMeeting - (Líng Shū)</h1>
<p className="hero-subtitle">
让每一次谈话都产生价值
</p>
<button
className="cta-button"
onClick={() => setShowLoginModal(true)}
>
开始使用
</button>
</div>
</section>
{/* Features Section */}
<section className="features">
<div className="features-container">
<h2 className="features-title">核心功能</h2>
<div className="features-grid">
<div className="feature-card feature-card-1">
<Brain className="feature-icon feature-icon-1" />
<h3>AI智能转录</h3>
<p>自动将会议音频转为文字并识别不同发言人</p>
</div>
<div className="feature-card feature-card-2">
<TrendingUp className="feature-icon feature-icon-2" />
<h3>智能摘要</h3>
<p>AI自动生成会议摘要提取关键信息和决策</p>
</div>
<div className="feature-card feature-card-3">
<Library className="feature-icon feature-icon-3" />
<h3>交叉知识库</h3>
<p>所有会议的摘要和内容汇聚成一个可供检索的知识库</p>
</div>
<div className="feature-card feature-card-4">
<Users className="feature-icon feature-icon-4" />
<h3>参会人管理</h3>
<p>轻松管理会议参与者追踪会议参与情况</p>
</div>
<div className="feature-card feature-card-5">
<Calendar className="feature-icon feature-icon-5" />
<h3>时间轴视图</h3>
<p>按时间顺序展示所有会议快速回顾历史记录</p>
</div>
<div className="feature-card feature-card-6">
<Download className="feature-icon feature-icon-6" />
<h3>数据导出</h3>
<p>导出会议摘要脑图和转录内容方便归档和分享</p>
</div>
</div>
</div>
</section>
{/* Footer */}
<footer className="homepage-footer">
<p>© 2025 紫光汇智信息技术有限公司. All rights reserved.</p>
<p>备案号渝ICP备2023007695号-11</p>
</footer>
{/* Login Modal */}
{showLoginModal && (
<div className="modal-overlay" onClick={closeModal}>
<div className="login-modal" onClick={e => e.stopPropagation()}>
<div className="modal-header">
<h2>登录到iMeeting</h2>
<button className="close-btn" onClick={closeModal}>
<X size={20} />
</button>
</div>
<form className="login-form" onSubmit={handleLogin}>
<div className="form-group">
<label htmlFor="username">
<User size={18} />
用户名
</label>
<input
type="text"
id="username"
name="username"
value={loginForm.username}
onChange={handleInputChange}
placeholder="请输入用户名"
required
/>
</div>
<div className="form-group password-group">
<label htmlFor="password">
<Lock size={18} />
密码
</label>
<div className="password-input-wrapper">
<input
type={showPassword ? "text" : "password"}
id="password"
name="password"
value={loginForm.password}
onChange={handleInputChange}
placeholder="请输入密码"
required
/>
<button
type="button"
className="password-toggle-btn"
onClick={() => setShowPassword(!showPassword)}
aria-label={showPassword ? "隐藏密码" : "显示密码"}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
<label htmlFor="rememberMe" className="remember-me-label">
<input
type="checkbox"
id="rememberMe"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
/>
<span>记住用户名</span>
</label>
</div>
<div className="error-message-container">
{loginError && (
<div className="error-message">{loginError}</div>
)}
</div>
<button
type="submit"
className="submit-btn"
disabled={isLoading}
>
{isLoading ? '登录中...' : '登录'}
</button>
</form>
</div>
</div>
)}
</div>
);
};
export default HomePage;