mirror of
https://github.com/stackblitz/bolt.new
synced 2025-06-26 18:17:50 +00:00
feat: 修改登录功能以支持手机号登录
This commit is contained in:
parent
3955a13050
commit
2cd653bbdc
@ -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"
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 };
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user