feat: 登录界面优化与API代理配置

- 移除登录页面的登录/注册切换,只保留登录功能
- 添加API代理配置支持远程访问
- 添加favicon.ico
- 后端CORS配置支持前端访问

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Developer
2026-03-18 10:54:18 +08:00
co-authored by Claude Opus 4.6
parent a89d4f2402
commit 02f48927a3
5 changed files with 34 additions and 37 deletions
+7 -1
View File
@@ -1,4 +1,4 @@
const API_BASE = ''; // 使用相对路径,同源代理 const API_BASE = ''; // 使用相对路径
function getAuthHeaders() { function getAuthHeaders() {
const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null; const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null;
@@ -15,6 +15,12 @@ async function apiCall(url, options = {}) {
}, },
}); });
const contentType = res.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
const text = await res.text();
throw new Error(`服务器错误: ${res.status} - ${text.substring(0, 100)}`);
}
const data = await res.json(); const data = await res.json();
if (!res.ok) { if (!res.ok) {
+17
View File
@@ -0,0 +1,17 @@
export default async function handler(req, res) {
const { 0: path } = req.query;
const backendUrl = `http://localhost:3501/${path}`;
const response = await fetch(backendUrl, {
method: req.method,
headers: {
'Content-Type': 'application/json',
...(req.headers.authorization ? { Authorization: req.headers.authorization } : {}),
},
body: req.method !== 'GET' && req.method !== 'HEAD' ? JSON.stringify(req.body) : undefined,
});
const data = await response.json();
res.status(response.status).json(data);
}
+5 -34
View File
@@ -8,7 +8,6 @@ import { useToast } from '../components/ToastProvider';
export default function Login() { export default function Login() {
const router = useRouter(); const router = useRouter();
const { showToast } = useToast(); const { showToast } = useToast();
const [isLoginMode, setIsLoginMode] = useState(true);
const [username, setUsername] = useState(''); const [username, setUsername] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [error, setError] = useState(''); const [error, setError] = useState('');
@@ -26,13 +25,8 @@ export default function Login() {
setLoading(true); setLoading(true);
try { try {
if (isLoginMode) { await login(username, password);
await login(username, password); showToast('登录成功', { tone: 'success' });
showToast('登录成功', { tone: 'success' });
} else {
await register(username, password);
showToast('注册成功', { tone: 'success' });
}
router.push('/'); router.push('/');
} catch (err) { } catch (err) {
setError(err.message); setError(err.message);
@@ -50,31 +44,8 @@ export default function Login() {
<div className="auth-logo__mark" aria-hidden="true"> <div className="auth-logo__mark" aria-hidden="true">
<Icon name="chart" size={24} /> <Icon name="chart" size={24} />
</div> </div>
<h1>{isLoginMode ? '欢迎回来' : '创建账户'}</h1> <h1>欢迎回来</h1>
<p>{isLoginMode ? '登录后继续记录每一笔收支' : '注册后即可开始管理你的账本'}</p> <p>登录后继续记录每一笔收支</p>
</div>
<div className="auth-switch" role="tablist" aria-label="登录或注册">
<button
type="button"
className={isLoginMode ? 'is-active' : ''}
onClick={() => {
setIsLoginMode(true);
setError('');
}}
>
登录
</button>
<button
type="button"
className={!isLoginMode ? 'is-active' : ''}
onClick={() => {
setIsLoginMode(false);
setError('');
}}
>
注册
</button>
</div> </div>
{error && <div className="error-msg">{error}</div>} {error && <div className="error-msg">{error}</div>}
@@ -110,7 +81,7 @@ export default function Login() {
<Icon name="spinner" size={16} className="is-spinning" /> <Icon name="spinner" size={16} className="is-spinning" />
处理中... 处理中...
</span> </span>
) : isLoginMode ? '登录' : '注册'} ) : '登录'}
</button> </button>
</form> </form>
</div> </div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+5 -2
View File
@@ -6,11 +6,14 @@ const cors = require('cors');
const path = require('path'); const path = require('path');
const app = express(); const app = express();
const PORT = 3500; const PORT = 3501;
const JWT_SECRET = process.env.JWT_SECRET || 'accountbook_secret_key_2024'; const JWT_SECRET = process.env.JWT_SECRET || 'accountbook_secret_key_2024';
// 中间件 // 中间件
app.use(cors()); app.use(cors({
origin: ['http://localhost:3500', 'http://127.0.0.1:3500'],
credentials: true
}));
app.use(express.json()); app.use(express.json());
app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'frontend/.next'))); app.use(express.static(path.join(__dirname, 'frontend/.next')));