移除了手动获取验证码,添加了新版本的自动获取手机号和微信认证,改变了登录界面样式,修改了相关接口和hooks的逻辑。
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
"description": "luobodayin",
|
"description": "luobodayin",
|
||||||
"appid": "touristappid",
|
"appid": "touristappid",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": true,
|
"urlCheck": false,
|
||||||
"es6": false,
|
"es6": false,
|
||||||
"enhance": false,
|
"enhance": false,
|
||||||
"compileHotReLoad": false,
|
"compileHotReLoad": false,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"description": "luobodayin",
|
"description": "luobodayin",
|
||||||
"appid": "touristappid",
|
"appid": "touristappid",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": true,
|
"urlCheck": false,
|
||||||
"es6": false,
|
"es6": false,
|
||||||
"postcss": false,
|
"postcss": false,
|
||||||
"minified": false
|
"minified": false
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { request } from '../utils/request';
|
import { request } from '../utils/request';
|
||||||
import type { UserInfo, LoginResponse, UpdateUserRequest } from '../types';
|
import type { UserInfo, LoginResponse, UpdateUserRequest } from '../types';
|
||||||
|
|
||||||
|
// 一键获取手机号登录请求参数
|
||||||
export interface PhoneLoginRequest {
|
export interface PhoneLoginRequest {
|
||||||
phone: string;
|
code: string; // 微信登录凭证 code
|
||||||
code: string;
|
encryptedData: string; // 包括敏感数据在内的完整用户信息加密数据
|
||||||
}
|
iv: string; // 加密算法的初始向量
|
||||||
|
|
||||||
export interface SmsCodeRequest {
|
|
||||||
phone: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const authApi = {
|
export const authApi = {
|
||||||
@@ -20,7 +18,7 @@ export const authApi = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 手机号登录
|
// 一键获取手机号登录
|
||||||
phoneLogin: (data: PhoneLoginRequest) => {
|
phoneLogin: (data: PhoneLoginRequest) => {
|
||||||
return request<LoginResponse>({
|
return request<LoginResponse>({
|
||||||
url: '/auth/phone/login',
|
url: '/auth/phone/login',
|
||||||
@@ -29,15 +27,6 @@ export const authApi = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 发送短信验证码
|
|
||||||
sendSmsCode: (data: SmsCodeRequest) => {
|
|
||||||
return request<void>({
|
|
||||||
url: '/auth/sms/code',
|
|
||||||
method: 'POST',
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshToken: (refreshToken: string) => {
|
refreshToken: (refreshToken: string) => {
|
||||||
return request<{ accessToken: string }>({
|
return request<{ accessToken: string }>({
|
||||||
url: '/auth/refresh',
|
url: '/auth/refresh',
|
||||||
|
|||||||
@@ -30,11 +30,15 @@ export const useAuth = () => {
|
|||||||
}
|
}
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
// 手机号登录
|
// 一键获取手机号登录
|
||||||
const phoneLogin = useCallback(async (phone: string, code: string) => {
|
const phoneLogin = useCallback(async (
|
||||||
|
code: string,
|
||||||
|
encryptedData: string,
|
||||||
|
iv: string
|
||||||
|
) => {
|
||||||
dispatch(setLoading(true));
|
dispatch(setLoading(true));
|
||||||
try {
|
try {
|
||||||
const res = await authApi.phoneLogin({ phone, code });
|
const res = await authApi.phoneLogin({ code, encryptedData, iv });
|
||||||
dispatch(setUser({
|
dispatch(setUser({
|
||||||
user: res.userInfo,
|
user: res.userInfo,
|
||||||
token: res.accessToken,
|
token: res.accessToken,
|
||||||
@@ -52,16 +56,6 @@ export const useAuth = () => {
|
|||||||
}
|
}
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
// 发送短信验证码
|
|
||||||
const sendSmsCode = useCallback(async (phone: string) => {
|
|
||||||
try {
|
|
||||||
await authApi.sendSmsCode({ phone });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Send SMS code failed:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(() => {
|
||||||
dispatch(logoutAction());
|
dispatch(logoutAction());
|
||||||
storage.removeToken();
|
storage.removeToken();
|
||||||
@@ -99,7 +93,6 @@ export const useAuth = () => {
|
|||||||
loading,
|
loading,
|
||||||
login,
|
login,
|
||||||
phoneLogin,
|
phoneLogin,
|
||||||
sendSmsCode,
|
|
||||||
logout,
|
logout,
|
||||||
refreshToken: refreshAccessToken,
|
refreshToken: refreshAccessToken,
|
||||||
updateInfo,
|
updateInfo,
|
||||||
|
|||||||
@@ -150,49 +150,8 @@ page {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== 验证码输入 ========== */
|
|
||||||
|
|
||||||
.code-input-wrapper {
|
/* ========== 登录相关样式 ========== */
|
||||||
display: flex;
|
|
||||||
gap: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-input {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-btn {
|
|
||||||
width: 220rpx;
|
|
||||||
height: 96rpx;
|
|
||||||
background: var(--primary);
|
|
||||||
color: var(--text-inverse);
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
transition: all var(--transition-scale);
|
|
||||||
white-space: nowrap;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-btn:active {
|
|
||||||
transform: scale(0.98);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-btn[disabled],
|
|
||||||
.code-btn.disabled {
|
|
||||||
background: var(--border-primary);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========== 登录按钮 ========== */
|
|
||||||
|
|
||||||
.login-btn {
|
.login-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -334,9 +293,10 @@ page {
|
|||||||
|
|
||||||
.divider-text {
|
.divider-text {
|
||||||
padding: 0 32rpx;
|
padding: 0 32rpx;
|
||||||
font-size: 26rpx;
|
font-size: 28rpx;
|
||||||
color: var(--text-tertiary);
|
color: var(--text-secondary);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== 微信登录按钮 ========== */
|
/* ========== 微信登录按钮 ========== */
|
||||||
@@ -364,6 +324,31 @@ page {
|
|||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========== 手机号一键登录按钮 ========== */
|
||||||
|
|
||||||
|
.phone-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: var(--primary);
|
||||||
|
color: #07C160;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8rpx 32rpx rgba(0, 122, 255, 0.25);
|
||||||
|
transition: all var(--transition-scale);
|
||||||
|
letter-spacing: 1rpx;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-btn:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========== 底部链接 ========== */
|
/* ========== 底部链接 ========== */
|
||||||
|
|
||||||
.login-footer {
|
.login-footer {
|
||||||
@@ -377,7 +362,7 @@ page {
|
|||||||
|
|
||||||
.footer-text {
|
.footer-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: var(--text-tertiary);
|
color: var(--text-secondary);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,92 +1,16 @@
|
|||||||
import { useState, useCallback, useMemo } from "react";
|
import { useState, useCallback } from "react";
|
||||||
import { View, Text, Button, Input } from "@tarojs/components";
|
import { View, Text, Button } from "@tarojs/components";
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from '@tarojs/taro';
|
||||||
import { useAuth } from '../../hooks/useAuth';
|
import { useAuth } from '../../hooks/useAuth';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const [phone, setPhone] = useState('');
|
|
||||||
const [code, setCode] = useState('');
|
|
||||||
const [countdown, setCountdown] = useState(0);
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
// 使用封装好的 useAuth hook
|
// 使用封装好的 useAuth hook
|
||||||
const { login, phoneLogin, sendSmsCode } = useAuth();
|
const { login, phoneLogin } = useAuth();
|
||||||
|
|
||||||
const handlePhoneInput = useCallback((e: any) => {
|
|
||||||
const value = e.detail.value;
|
|
||||||
const numericValue = value.replace(/\D/g, '');
|
|
||||||
const truncatedValue = numericValue.slice(0, 11);
|
|
||||||
setPhone(truncatedValue);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const isPhoneValid = useMemo(() => {
|
|
||||||
return /^1[3-9]\d{9}$/.test(phone);
|
|
||||||
}, [phone]);
|
|
||||||
|
|
||||||
const getPhoneError = useMemo(() => {
|
|
||||||
if (phone.length === 0) return '';
|
|
||||||
if (phone.length < 11) return '请输入完整的11位手机号';
|
|
||||||
if (!/^1[3-9]/.test(phone)) return '手机号格式不正确';
|
|
||||||
return '';
|
|
||||||
}, [phone]);
|
|
||||||
|
|
||||||
const handleGetCode = useCallback(async () => {
|
|
||||||
if (!phone) {
|
|
||||||
Taro.showToast({ title: '请输入手机号', icon: 'none' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isPhoneValid) {
|
|
||||||
Taro.showToast({ title: '请输入正确的手机号', icon: 'none' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await sendSmsCode(phone);
|
|
||||||
setCountdown(60);
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
setCountdown(prev => {
|
|
||||||
if (prev <= 1) {
|
|
||||||
clearInterval(timer);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return prev - 1;
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
Taro.showToast({ title: '验证码已发送', icon: 'success' });
|
|
||||||
} catch (error) {
|
|
||||||
Taro.showToast({ title: '发送失败,请重试', icon: 'none' });
|
|
||||||
}
|
|
||||||
}, [phone, isPhoneValid, sendSmsCode]);
|
|
||||||
|
|
||||||
const handlePhoneLogin = useCallback(async () => {
|
|
||||||
if (!phone) {
|
|
||||||
Taro.showToast({ title: '请输入手机号', icon: 'none' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isPhoneValid) {
|
|
||||||
Taro.showToast({ title: '请输入正确的手机号', icon: 'none' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!code) {
|
|
||||||
Taro.showToast({ title: '请输入验证码', icon: 'none' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
await phoneLogin(phone, code);
|
|
||||||
Taro.showToast({ title: '登录成功', icon: 'success' });
|
|
||||||
setTimeout(() => {
|
|
||||||
Taro.navigateBack();
|
|
||||||
}, 1500);
|
|
||||||
} catch (error) {
|
|
||||||
Taro.showToast({ title: '登录失败,请重试', icon: 'none' });
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}, [phone, isPhoneValid, code, phoneLogin]);
|
|
||||||
|
|
||||||
// 移除 handleCreateNickname 函数,因为现在使用后端登录
|
|
||||||
|
|
||||||
|
// 微信一键登录
|
||||||
const handleWechatLogin = useCallback(async () => {
|
const handleWechatLogin = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -94,17 +18,7 @@ const Login = () => {
|
|||||||
const loginResult = await Taro.login();
|
const loginResult = await Taro.login();
|
||||||
console.log('微信登录 code:', loginResult.code);
|
console.log('微信登录 code:', loginResult.code);
|
||||||
|
|
||||||
// 2. 尝试获取用户信息(可选,用于展示)
|
// 2. 调用后端 API 进行登录
|
||||||
try {
|
|
||||||
await Taro.getUserProfile({
|
|
||||||
desc: '用于完善用户资料',
|
|
||||||
});
|
|
||||||
// 用户信息已通过 login() 从后端获取,这里只是请求授权
|
|
||||||
} catch (e) {
|
|
||||||
console.log('游客模式获取用户信息受限,使用默认信息');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 调用后端 API 进行登录
|
|
||||||
await login(loginResult.code);
|
await login(loginResult.code);
|
||||||
|
|
||||||
Taro.showToast({ title: '登录成功', icon: 'success' });
|
Taro.showToast({ title: '登录成功', icon: 'success' });
|
||||||
@@ -119,6 +33,37 @@ const Login = () => {
|
|||||||
}
|
}
|
||||||
}, [login]);
|
}, [login]);
|
||||||
|
|
||||||
|
// 一键获取手机号并登录
|
||||||
|
const handleGetPhoneNumber = useCallback(async (event: any) => {
|
||||||
|
const { detail } = event;
|
||||||
|
|
||||||
|
// 用户拒绝授权或数据不完整
|
||||||
|
if (!detail || !detail.encryptedData || !detail.iv) {
|
||||||
|
Taro.showToast({ title: '您已拒绝授权获取手机号', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 1. 获取微信登录 code
|
||||||
|
const loginResult = await Taro.login();
|
||||||
|
console.log('微信登录 code:', loginResult.code);
|
||||||
|
|
||||||
|
// 2. 调用后端一键登录接口,传入 code 和手机号信息
|
||||||
|
await phoneLogin(loginResult.code, detail.encryptedData, detail.iv);
|
||||||
|
|
||||||
|
Taro.showToast({ title: '登录成功', icon: 'success' });
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.navigateBack();
|
||||||
|
}, 1500);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('一键登录失败:', error);
|
||||||
|
Taro.showToast({ title: '登录失败,请重试', icon: 'none' });
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [phoneLogin]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='login-page'>
|
<View className='login-page'>
|
||||||
<View className='login-header'>
|
<View className='login-header'>
|
||||||
@@ -127,56 +72,6 @@ const Login = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className='login-form'>
|
<View className='login-form'>
|
||||||
<View className='form-item'>
|
|
||||||
<Text className='form-label'>手机号</Text>
|
|
||||||
<Input
|
|
||||||
className='form-input'
|
|
||||||
type='number'
|
|
||||||
placeholder='请输入手机号'
|
|
||||||
maxlength={11}
|
|
||||||
value={phone}
|
|
||||||
onInput={handlePhoneInput}
|
|
||||||
/>
|
|
||||||
{getPhoneError ? (
|
|
||||||
<Text className='form-error'>{getPhoneError}</Text>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className='form-item'>
|
|
||||||
<Text className='form-label'>验证码</Text>
|
|
||||||
<View className='code-input-wrapper'>
|
|
||||||
<Input
|
|
||||||
className='form-input code-input'
|
|
||||||
type='number'
|
|
||||||
placeholder='请输入验证码'
|
|
||||||
maxlength={6}
|
|
||||||
value={code}
|
|
||||||
onInput={(e) => setCode(e.detail.value.replace(/\D/g, ''))}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className={`code-btn ${!isPhoneValid ? 'disabled' : ''}`}
|
|
||||||
onClick={handleGetCode}
|
|
||||||
disabled={countdown > 0 || !isPhoneValid}
|
|
||||||
>
|
|
||||||
{countdown > 0 ? `${countdown}s` : '获取验证码'}
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className='login-btn'
|
|
||||||
onClick={handlePhoneLogin}
|
|
||||||
loading={loading}
|
|
||||||
>
|
|
||||||
登录
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<View className='divider'>
|
|
||||||
<View className='divider-line' />
|
|
||||||
<Text className='divider-text'>其他登录方式</Text>
|
|
||||||
<View className='divider-line' />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className='wechat-btn'
|
className='wechat-btn'
|
||||||
onClick={handleWechatLogin}
|
onClick={handleWechatLogin}
|
||||||
@@ -184,6 +79,21 @@ const Login = () => {
|
|||||||
>
|
>
|
||||||
微信一键登录
|
微信一键登录
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<View className='divider'>
|
||||||
|
<View className='divider-line' />
|
||||||
|
<Text className='divider-text'>或</Text>
|
||||||
|
<View className='divider-line' />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className='phone-btn'
|
||||||
|
openType="getPhoneNumber"
|
||||||
|
onGetPhoneNumber={handleGetPhoneNumber}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
自动获取手机号登录
|
||||||
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className='login-footer'>
|
<View className='login-footer'>
|
||||||
|
|||||||
Reference in New Issue
Block a user