From 4f4b2589bbdec7ca5f895714da7d02cbbd080cb8 Mon Sep 17 00:00:00 2001 From: Mikuisnotavailable Date: Tue, 31 Mar 2026 23:08:36 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=BA=86=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E9=AA=8C=E8=AF=81=E7=A0=81=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=E6=96=B0=E7=89=88=E6=9C=AC=E7=9A=84=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=8E=B7=E5=8F=96=E6=89=8B=E6=9C=BA=E5=8F=B7=E5=92=8C?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E8=AE=A4=E8=AF=81=EF=BC=8C=E6=94=B9=E5=8F=98?= =?UTF-8?q?=E4=BA=86=E7=99=BB=E5=BD=95=E7=95=8C=E9=9D=A2=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9=E4=BA=86=E7=9B=B8=E5=85=B3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=92=8Chooks=E7=9A=84=E9=80=BB=E8=BE=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project.config.json | 2 +- project.tt.json | 2 +- src/api/auth.ts | 21 +---- src/hooks/useAuth.ts | 21 ++--- src/pages/login/index.css | 75 ++++++--------- src/pages/login/index.tsx | 192 ++++++++++---------------------------- 6 files changed, 95 insertions(+), 218 deletions(-) diff --git a/project.config.json b/project.config.json index 8efea88..fcdbb4c 100644 --- a/project.config.json +++ b/project.config.json @@ -4,7 +4,7 @@ "description": "luobodayin", "appid": "touristappid", "setting": { - "urlCheck": true, + "urlCheck": false, "es6": false, "enhance": false, "compileHotReLoad": false, diff --git a/project.tt.json b/project.tt.json index 2afd384..a1eee76 100644 --- a/project.tt.json +++ b/project.tt.json @@ -4,7 +4,7 @@ "description": "luobodayin", "appid": "touristappid", "setting": { - "urlCheck": true, + "urlCheck": false, "es6": false, "postcss": false, "minified": false diff --git a/src/api/auth.ts b/src/api/auth.ts index f02e6e6..85a47cd 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,13 +1,11 @@ import { request } from '../utils/request'; import type { UserInfo, LoginResponse, UpdateUserRequest } from '../types'; +// 一键获取手机号登录请求参数 export interface PhoneLoginRequest { - phone: string; - code: string; -} - -export interface SmsCodeRequest { - phone: string; + code: string; // 微信登录凭证 code + encryptedData: string; // 包括敏感数据在内的完整用户信息加密数据 + iv: string; // 加密算法的初始向量 } export const authApi = { @@ -20,7 +18,7 @@ export const authApi = { }); }, - // 手机号登录 + // 一键获取手机号登录 phoneLogin: (data: PhoneLoginRequest) => { return request({ url: '/auth/phone/login', @@ -29,15 +27,6 @@ export const authApi = { }); }, - // 发送短信验证码 - sendSmsCode: (data: SmsCodeRequest) => { - return request({ - url: '/auth/sms/code', - method: 'POST', - data, - }); - }, - refreshToken: (refreshToken: string) => { return request<{ accessToken: string }>({ url: '/auth/refresh', diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 86e8000..d56ba58 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -30,11 +30,15 @@ export const useAuth = () => { } }, [dispatch]); - // 手机号登录 - const phoneLogin = useCallback(async (phone: string, code: string) => { + // 一键获取手机号登录 + const phoneLogin = useCallback(async ( + code: string, + encryptedData: string, + iv: string + ) => { dispatch(setLoading(true)); try { - const res = await authApi.phoneLogin({ phone, code }); + const res = await authApi.phoneLogin({ code, encryptedData, iv }); dispatch(setUser({ user: res.userInfo, token: res.accessToken, @@ -52,16 +56,6 @@ export const useAuth = () => { } }, [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(() => { dispatch(logoutAction()); storage.removeToken(); @@ -99,7 +93,6 @@ export const useAuth = () => { loading, login, phoneLogin, - sendSmsCode, logout, refreshToken: refreshAccessToken, updateInfo, diff --git a/src/pages/login/index.css b/src/pages/login/index.css index 03ec5fa..df975d8 100644 --- a/src/pages/login/index.css +++ b/src/pages/login/index.css @@ -150,49 +150,8 @@ page { 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 { width: 100%; @@ -334,9 +293,10 @@ page { .divider-text { padding: 0 32rpx; - font-size: 26rpx; - color: var(--text-tertiary); + font-size: 28rpx; + color: var(--text-secondary); white-space: nowrap; + font-weight: 500; } /* ========== 微信登录按钮 ========== */ @@ -364,6 +324,31 @@ page { 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 { @@ -377,7 +362,7 @@ page { .footer-text { font-size: 28rpx; - color: var(--text-tertiary); + color: var(--text-secondary); font-weight: 400; } diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 469b9c9..9f00d2b 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,92 +1,16 @@ -import { useState, useCallback, useMemo } from "react"; -import { View, Text, Button, Input } from "@tarojs/components"; +import { useState, useCallback } from "react"; +import { View, Text, Button } from "@tarojs/components"; import Taro from '@tarojs/taro'; import { useAuth } from '../../hooks/useAuth'; import './index.css'; const Login = () => { - const [phone, setPhone] = useState(''); - const [code, setCode] = useState(''); - const [countdown, setCountdown] = useState(0); const [loading, setLoading] = useState(false); // 使用封装好的 useAuth hook - const { login, phoneLogin, sendSmsCode } = 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 { login, phoneLogin } = useAuth(); + // 微信一键登录 const handleWechatLogin = useCallback(async () => { setLoading(true); try { @@ -94,17 +18,7 @@ const Login = () => { const loginResult = await Taro.login(); console.log('微信登录 code:', loginResult.code); - // 2. 尝试获取用户信息(可选,用于展示) - try { - await Taro.getUserProfile({ - desc: '用于完善用户资料', - }); - // 用户信息已通过 login() 从后端获取,这里只是请求授权 - } catch (e) { - console.log('游客模式获取用户信息受限,使用默认信息'); - } - - // 3. 调用后端 API 进行登录 + // 2. 调用后端 API 进行登录 await login(loginResult.code); Taro.showToast({ title: '登录成功', icon: 'success' }); @@ -119,6 +33,37 @@ const 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 ( @@ -127,56 +72,6 @@ const Login = () => { - - 手机号 - - {getPhoneError ? ( - {getPhoneError} - ) : null} - - - - 验证码 - - setCode(e.detail.value.replace(/\D/g, ''))} - /> - - - - - - - - - 其他登录方式 - - - + + + + + + + +