feat: 修改登录功能以支持手机号登录

This commit is contained in:
zyh 2024-10-22 06:34:56 +00:00
parent 3955a13050
commit 2cd653bbdc
4 changed files with 32 additions and 12 deletions

View File

@ -1,33 +1,40 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useNavigate } from '@remix-run/react'; import { useAuth } from '~/hooks/useAuth';
import type { LoginResponse } from '~/routes/api.auth.login';
export function Login() { export function Login() {
const [phone, setPhone] = useState(''); const [phone, setPhone] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const navigate = useNavigate(); const [error, setError] = useState<string | null>(null);
const { login } = useAuth();
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
setError(null);
try { try {
const response = await fetch('/api/auth/login', { const response = await fetch('/api/auth/login', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ phone, password }), body: JSON.stringify({ phone, password }),
}); });
if (response.ok) { const data = (await response.json()) as LoginResponse;
const data = (await response.json()) as { token: string }; if (response.ok && data.token && data.user) {
localStorage.setItem('token', data.token); login(data.token, data.user);
navigate('/dashboard'); // 登录成功后的处理,例如重定向或显示成功消息
} else { } else {
// 处理错误 setError(data.error || '登录失败,请检查您的手机号和密码');
} }
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error('Login failed:', error);
setError('登录失败,请稍后再试');
} }
}; };
return ( return (
<form onSubmit={handleSubmit} className="space-y-6"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="phone" className="block text-sm font-medium text-bolt-elements-textPrimary"> <label htmlFor="phone" className="block text-sm font-medium text-bolt-elements-textPrimary">
@ -54,6 +61,7 @@ export function Login() {
className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background" className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background"
/> />
</div> </div>
{error && <div className="text-red-500 text-sm mt-2">{error}</div>}
<div> <div>
<button <button
type="submit" type="submit"

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useNavigate } from '@remix-run/react'; import { useNavigate } from '@remix-run/react';
interface User { export interface User {
id: number; id: number;
phone: string; phone: string;
nickname: string; nickname: string;

View File

@ -3,6 +3,18 @@ import type { ActionFunction } from '@remix-run/node';
import { validatePhoneNumber } from '~/utils/validation'; import { validatePhoneNumber } from '~/utils/validation';
import { verifyLogin, createToken } from '~/utils/auth.server'; import { verifyLogin, createToken } from '~/utils/auth.server';
export interface LoginResponse {
success: boolean;
token?: string;
user?: {
id: number;
phone: string;
nickname: string;
avatarUrl: string;
};
error?: string;
}
export const action: ActionFunction = async ({ request }) => { export const action: ActionFunction = async ({ request }) => {
const { phone, password } = await request.json() as { phone: string, password: string }; const { phone, password } = await request.json() as { phone: string, password: string };

View File

@ -8,8 +8,8 @@ export async function hashPassword(password: string) {
return bcrypt.hash(password, 10); return bcrypt.hash(password, 10);
} }
export async function verifyLogin(email: string, password: string) { export async function verifyLogin(phone: string, password: string) {
const user = await db('users').where({ email }).first(); const user = await db('users').where({ phone }).first();
if (!user) return null; if (!user) return null;
const isValid = await bcrypt.compare(password, user.password); const isValid = await bcrypt.compare(password, user.password);