校验并修改用户手机号
This commit is contained in:
@@ -496,4 +496,4 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ const LoginPage: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<View className="login_page">
|
<View className="login_page">
|
||||||
<View className="background_image">
|
<View className="background_image">
|
||||||
<Image
|
<Image
|
||||||
className="bg_img"
|
className="bg_img"
|
||||||
src={require('../../../static/login/login_bg.jpg')}
|
src={require('../../../static/login/login_bg.jpg')}
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
@@ -246,4 +246,4 @@ const LoginPage: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 验证码页面样式
|
// 验证码页面样式
|
||||||
.verification_page {
|
.verification_page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #FAFAFA;
|
background: #fafafa;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
.bg_color {
|
.bg_color {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #FAFAFA;
|
background: #fafafa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
height: 54px;
|
height: 54px;
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
font-family: 'SF Pro';
|
font-family: "SF Pro";
|
||||||
font-weight: 590;
|
font-weight: 590;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
line-height: 1.294;
|
line-height: 1.294;
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -1.33px;
|
right: -1.33px;
|
||||||
top: 4.78px;
|
top: 4.78px;
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: #FFFFFF;
|
background: #ffffff;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: rgba(255, 255, 255, 0.7);
|
||||||
border: 0.35px solid #DEDEDE;
|
border: 0.35px solid #dedede;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
|
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1.5px;
|
top: 1.5px;
|
||||||
left: 1.5px;
|
left: 1.5px;
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
left: 7px;
|
left: 7px;
|
||||||
@@ -213,10 +213,10 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 12px 24px 36px 24px ;
|
padding: 12px 24px 36px 24px;
|
||||||
|
|
||||||
.main_title {
|
.main_title {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
@@ -224,7 +224,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sub_title {
|
.sub_title {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #FFFFFF;
|
background: #ffffff;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
|
|
||||||
.phone_input {
|
.phone_input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
@@ -268,16 +268,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.char_count {
|
.char_count {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.714;
|
line-height: 1.714;
|
||||||
color: rgba(60, 60, 67, 0.3);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
|
|
||||||
.count_number {
|
.count_number {
|
||||||
color: rgba(60, 60, 67, 0.3);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -298,19 +298,19 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #FFFFFF;
|
background: #ffffff;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
width: 210px;
|
width: 210px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
.code_input {
|
.code_input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
@@ -318,7 +318,6 @@
|
|||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgba(60, 60, 67, 0.3);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
@@ -326,16 +325,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.char_count {
|
.char_count {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.714;
|
line-height: 1.714;
|
||||||
color: rgba(60, 60, 67, 0.3);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
|
|
||||||
.count_number {
|
.count_number {
|
||||||
color: rgba(60, 60, 67, 0.3);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -353,11 +352,11 @@
|
|||||||
background: #000000;
|
background: #000000;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: #FFFFFF;
|
color: #ffffff;
|
||||||
box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1);
|
||||||
backdrop-filter: blur(32px);
|
backdrop-filter: blur(32px);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -378,15 +377,15 @@
|
|||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
|
||||||
.countdown_line1 {
|
.countdown_line1 {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
color: #FFFFFF;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown_line2 {
|
.countdown_line2 {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
@@ -396,8 +395,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 登录按钮
|
// 登录按钮
|
||||||
.login_button {
|
.login_button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -405,11 +402,11 @@
|
|||||||
background: #000000;
|
background: #000000;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 6px 2px;
|
padding: 6px 2px;
|
||||||
color: #FFFFFF;
|
color: #ffffff;
|
||||||
box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1);
|
||||||
backdrop-filter: blur(32px);
|
backdrop-filter: blur(32px);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -419,7 +416,6 @@
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 协议区域
|
// 协议区域
|
||||||
@@ -429,14 +425,14 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
.terms_text {
|
.terms_text {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(60, 60, 67, 0.6);
|
color: rgba(60, 60, 67, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.terms_link {
|
.terms_link {
|
||||||
font-family: 'PingFang SC';
|
font-family: "PingFang SC";
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
@@ -463,3 +459,94 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 浮层遮罩
|
||||||
|
.change_mobile_overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
// 底部条款浮层
|
||||||
|
.change_mobile_float_layer {
|
||||||
|
position: relative;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24px 24px 0 0;
|
||||||
|
padding: 24px 24px 34px;
|
||||||
|
z-index: 101;
|
||||||
|
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
|
||||||
|
// 浮层标题
|
||||||
|
.float_title {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
.title_text {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.hint_content {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
.hint_text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bind_mobile_info_box {
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
.bind_item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 44px;
|
||||||
|
color: #3c3c4399;
|
||||||
|
padding: 0 16px;
|
||||||
|
& + .bind_item {
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
.mobile {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.button_group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 0;
|
||||||
|
Button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 16px;
|
||||||
|
flex: 1;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
.cancel_button {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.agree_button {
|
||||||
|
background: #000000;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import { View, Text, Input, Button, Image } from '@tarojs/components';
|
import { View, Text, Input, Button, Image } from "@tarojs/components";
|
||||||
import Taro, { useRouter } from '@tarojs/taro';
|
import Taro, { useRouter } from "@tarojs/taro";
|
||||||
import { phone_auth_login, send_sms_code, save_login_state } from '../../../services/loginService';
|
import {
|
||||||
import './index.scss';
|
phone_auth_login,
|
||||||
|
send_sms_code,
|
||||||
|
save_login_state,
|
||||||
|
updateUserPhone,
|
||||||
|
} from "../../../services/loginService";
|
||||||
|
import "./index.scss";
|
||||||
|
|
||||||
const VerificationPage: React.FC = () => {
|
const VerificationPage: React.FC = () => {
|
||||||
const [phone, setPhone] = useState('');
|
const [phone, setPhone] = useState("");
|
||||||
const [verification_code, setVerificationCode] = useState('');
|
const [verification_code, setVerificationCode] = useState("");
|
||||||
const [countdown, setCountdown] = useState(0);
|
const [countdown, setCountdown] = useState(0);
|
||||||
const [can_send_code, setCanSendCode] = useState(true);
|
const [can_send_code, setCanSendCode] = useState(true);
|
||||||
const [is_loading, setIsLoading] = useState(false);
|
const [is_loading, setIsLoading] = useState(false);
|
||||||
const [code_input_focus, setCodeInputFocus] = useState(false);
|
const [code_input_focus, setCodeInputFocus] = useState(false);
|
||||||
|
const [show_change_mobile_layer, setShowChangeMobileLayer] = useState(false);
|
||||||
|
const [oldPhone, setOldPhone] = useState("");
|
||||||
|
|
||||||
const { params: { redirect } } = useRouter();
|
const {
|
||||||
|
params: { redirect },
|
||||||
|
} = useRouter();
|
||||||
|
|
||||||
// 计算登录按钮是否应该启用
|
// 计算登录按钮是否应该启用
|
||||||
const can_login = phone.length === 11 && verification_code.length === 6 && !is_loading;
|
const can_login =
|
||||||
|
phone.length === 11 && verification_code.length === 6 && !is_loading;
|
||||||
|
|
||||||
// 发送验证码
|
// 发送验证码
|
||||||
const handle_send_code = async () => {
|
const handle_send_code = async () => {
|
||||||
if (!phone || phone.length !== 11) {
|
if (!phone || phone.length !== 11) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请输入正确的手机号',
|
title: "请输入正确的手机号",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -32,66 +41,66 @@ const VerificationPage: React.FC = () => {
|
|||||||
if (!can_send_code) return;
|
if (!can_send_code) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('开始发送验证码,手机号:', phone);
|
console.log("开始发送验证码,手机号:", phone);
|
||||||
|
|
||||||
// 调用发送短信接口
|
// 调用发送短信接口
|
||||||
const result = await send_sms_code(phone);
|
const result = await send_sms_code(phone);
|
||||||
|
|
||||||
console.log('发送验证码结果:', result);
|
console.log("发送验证码结果:", result);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('验证码发送成功,开始倒计时');
|
console.log("验证码发送成功,开始倒计时");
|
||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '验证码已发送',
|
title: "验证码已发送",
|
||||||
icon: 'success',
|
icon: "success",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 开始倒计时
|
// 开始倒计时
|
||||||
setCanSendCode(false);
|
setCanSendCode(false);
|
||||||
setCountdown(60);
|
setCountdown(60);
|
||||||
|
|
||||||
console.log('设置状态: can_send_code = false, countdown = 60');
|
console.log("设置状态: can_send_code = false, countdown = 60");
|
||||||
|
|
||||||
// 发送验证码成功后,让验证码输入框获得焦点并调用系统键盘
|
// 发送验证码成功后,让验证码输入框获得焦点并调用系统键盘
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 设置验证码输入框聚焦状态
|
// 设置验证码输入框聚焦状态
|
||||||
setCodeInputFocus(true);
|
setCodeInputFocus(true);
|
||||||
// 清空验证码,让用户重新输入
|
// 清空验证码,让用户重新输入
|
||||||
setVerificationCode('');
|
setVerificationCode("");
|
||||||
console.log('设置验证码输入框聚焦');
|
console.log("设置验证码输入框聚焦");
|
||||||
}, 500); // 延迟500ms确保Toast显示完成后再聚焦
|
}, 500); // 延迟500ms确保Toast显示完成后再聚焦
|
||||||
} else {
|
} else {
|
||||||
console.log('验证码发送失败:', result.message);
|
console.log("验证码发送失败:", result.message);
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: result.message || '发送失败',
|
title: result.message || "发送失败",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('发送验证码异常:', error);
|
console.error("发送验证码异常:", error);
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '发送失败,请重试',
|
title: "发送失败,请重试",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 倒计时效果
|
// 倒计时效果
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('倒计时 useEffect 触发,countdown:', countdown);
|
console.log("倒计时 useEffect 触发,countdown:", countdown);
|
||||||
|
|
||||||
if (countdown > 0) {
|
if (countdown > 0) {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
console.log('倒计时减少,从', countdown, '到', countdown - 1);
|
console.log("倒计时减少,从", countdown, "到", countdown - 1);
|
||||||
setCountdown(countdown - 1);
|
setCountdown(countdown - 1);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
} else if (countdown === 0 && !can_send_code) {
|
} else if (countdown === 0 && !can_send_code) {
|
||||||
console.log('倒计时结束,重新启用发送按钮');
|
console.log("倒计时结束,重新启用发送按钮");
|
||||||
setCanSendCode(true);
|
setCanSendCode(true);
|
||||||
}
|
}
|
||||||
}, [countdown, can_send_code]);
|
}, [countdown, can_send_code]);
|
||||||
@@ -100,18 +109,18 @@ const VerificationPage: React.FC = () => {
|
|||||||
const handle_phone_login = async () => {
|
const handle_phone_login = async () => {
|
||||||
if (!phone || phone.length !== 11) {
|
if (!phone || phone.length !== 11) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请输入正确的手机号',
|
title: "请输入正确的手机号",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verification_code || verification_code.length !== 6) {
|
if (!verification_code || verification_code.length !== 6) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请输入6位验证码',
|
title: "请输入6位验证码",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -119,45 +128,87 @@ const VerificationPage: React.FC = () => {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// 先进行微信登录获取code
|
// 先进行微信登录获取code
|
||||||
const login_result = await Taro.login();
|
const login_result = await Taro.login();
|
||||||
|
|
||||||
if (!login_result.code) {
|
if (!login_result.code) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: '微信登录失败'
|
message: "微信登录失败",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await phone_auth_login({ phone, verification_code, user_code: login_result.code });
|
const result = await phone_auth_login({
|
||||||
|
phone,
|
||||||
|
verification_code,
|
||||||
|
user_code: login_result.code,
|
||||||
|
});
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
save_login_state(result.token!, result.user_info!)
|
save_login_state(result.token!, result.user_info!);
|
||||||
|
if (result.phone_update_status === "existing") {
|
||||||
|
const { existing_phone = "" } = result;
|
||||||
|
setShowChangeMobileLayer(true);
|
||||||
|
setOldPhone(existing_phone);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Taro.redirectTo({
|
Taro.redirectTo({
|
||||||
url: '/pages/list/index'
|
url: "/pages/list/index",
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
} else {
|
} else {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: result.message || '登录失败',
|
title: result.message || "登录失败",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '登录失败,请重试',
|
title: "登录失败,请重试",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const change_mobile = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await updateUserPhone({ phone });
|
||||||
|
if (res.code === 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: "/pages/list/index",
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
} else {
|
||||||
|
Taro.showToast({
|
||||||
|
title: res.message || "更新手机号失败",
|
||||||
|
icon: "none",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: "更新失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hidePhone = (phone: string): string => {
|
||||||
|
if (!phone) {
|
||||||
|
return "*";
|
||||||
|
}
|
||||||
|
return phone.replace(/(\d{3})(\d*)(\d{4})/,'$1****$3');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="verification_page">
|
<View className="verification_page">
|
||||||
@@ -166,7 +217,6 @@ const VerificationPage: React.FC = () => {
|
|||||||
<View className="bg_color"></View>
|
<View className="bg_color"></View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
||||||
{/* 主要内容 */}
|
{/* 主要内容 */}
|
||||||
<View className="main_content">
|
<View className="main_content">
|
||||||
{/* 标题区域 */}
|
{/* 标题区域 */}
|
||||||
@@ -190,7 +240,11 @@ const VerificationPage: React.FC = () => {
|
|||||||
maxlength={11}
|
maxlength={11}
|
||||||
/>
|
/>
|
||||||
<View className="char_count">
|
<View className="char_count">
|
||||||
<Text className={phone.length > 0 ? 'count_number active' : 'count_number'}>
|
<Text
|
||||||
|
className={
|
||||||
|
phone.length > 0 ? "count_number active" : "count_number"
|
||||||
|
}
|
||||||
|
>
|
||||||
{phone.length}
|
{phone.length}
|
||||||
</Text>
|
</Text>
|
||||||
/11
|
/11
|
||||||
@@ -215,19 +269,25 @@ const VerificationPage: React.FC = () => {
|
|||||||
maxlength={6}
|
maxlength={6}
|
||||||
/>
|
/>
|
||||||
<View className="char_count">
|
<View className="char_count">
|
||||||
<Text className={verification_code.length > 0 ? 'count_number active' : 'count_number'}>
|
<Text
|
||||||
|
className={
|
||||||
|
verification_code.length > 0
|
||||||
|
? "count_number active"
|
||||||
|
: "count_number"
|
||||||
|
}
|
||||||
|
>
|
||||||
{verification_code.length}
|
{verification_code.length}
|
||||||
</Text>
|
</Text>
|
||||||
/6
|
/6
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
className={`send_code_button ${!can_send_code ? 'disabled' : ''}`}
|
className={`send_code_button ${!can_send_code ? "disabled" : ""}`}
|
||||||
onClick={handle_send_code}
|
onClick={handle_send_code}
|
||||||
disabled={!can_send_code}
|
disabled={!can_send_code}
|
||||||
>
|
>
|
||||||
{can_send_code ? (
|
{can_send_code ? (
|
||||||
'获取验证码'
|
"获取验证码"
|
||||||
) : (
|
) : (
|
||||||
<View className="countdown_text">
|
<View className="countdown_text">
|
||||||
<Text className="countdown_line1">验证码已发送</Text>
|
<Text className="countdown_line1">验证码已发送</Text>
|
||||||
@@ -247,11 +307,13 @@ const VerificationPage: React.FC = () => {
|
|||||||
{/* 登录按钮 */}
|
{/* 登录按钮 */}
|
||||||
<View className="button_section">
|
<View className="button_section">
|
||||||
<Button
|
<Button
|
||||||
className={`login_button ${is_loading ? 'loading' : ''} ${!can_login ? 'disabled' : ''}`}
|
className={`login_button ${is_loading ? "loading" : ""} ${
|
||||||
|
!can_login ? "disabled" : ""
|
||||||
|
}`}
|
||||||
onClick={handle_phone_login}
|
onClick={handle_phone_login}
|
||||||
disabled={!can_login}
|
disabled={!can_login}
|
||||||
>
|
>
|
||||||
{'登录'}
|
{"登录"}
|
||||||
</Button>
|
</Button>
|
||||||
{/* 调试信息 */}
|
{/* 调试信息 */}
|
||||||
{/* {process.env.NODE_ENV === 'development' && (
|
{/* {process.env.NODE_ENV === 'development' && (
|
||||||
@@ -260,14 +322,53 @@ const VerificationPage: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
)} */}
|
)} */}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 底部指示器 */}
|
{/* 底部指示器 */}
|
||||||
<View className="home_indicator"></View>
|
<View className="home_indicator"></View>
|
||||||
|
|
||||||
|
{show_change_mobile_layer && (
|
||||||
|
<View className="change_mobile_overlay">
|
||||||
|
{/* 底部修改手机号浮层 */}
|
||||||
|
<View className="change_mobile_float_layer">
|
||||||
|
<View className="float_title">
|
||||||
|
<Text className="title_text">是否更新已绑定的手机号?</Text>
|
||||||
|
</View>
|
||||||
|
<View className="hint_content">
|
||||||
|
<Text className="hint_text">
|
||||||
|
更新后,原手机号将解除绑定,后续登录和通知将使用新的手机号
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View className="bind_mobile_info_box">
|
||||||
|
<View className="bind_item">
|
||||||
|
<Text>当前使用的手机号</Text>
|
||||||
|
<Text className="mobile">{hidePhone(phone)}</Text>
|
||||||
|
</View>
|
||||||
|
<View className="bind_item">
|
||||||
|
<Text>原绑定手机号</Text>
|
||||||
|
<Text className="mobile">{hidePhone(oldPhone)}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="button_group">
|
||||||
|
<Button
|
||||||
|
className="cancel_button"
|
||||||
|
onClick={() => {
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: "/pages/list/index",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{"保持原手机号"}
|
||||||
|
</Button>
|
||||||
|
<Button className="agree_button" onClick={change_mobile}>
|
||||||
|
{"更新为当前手机号"}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default VerificationPage;
|
export default VerificationPage;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export interface LoginResponse {
|
|||||||
message: string;
|
message: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
user_info?: WechatUserInfo;
|
user_info?: WechatUserInfo;
|
||||||
|
phone_update_status?: string;
|
||||||
|
existing_phone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送短信响应接口
|
// 发送短信响应接口
|
||||||
@@ -134,6 +136,11 @@ export interface PhoneLoginParams {
|
|||||||
user_code: string;
|
user_code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新手机号接口参数
|
||||||
|
export interface ChangePhoneParams {
|
||||||
|
phone: string;
|
||||||
|
}
|
||||||
|
|
||||||
// 手机号验证码登录
|
// 手机号验证码登录
|
||||||
export const phone_auth_login = async (
|
export const phone_auth_login = async (
|
||||||
params: PhoneLoginParams,
|
params: PhoneLoginParams,
|
||||||
@@ -159,6 +166,8 @@ export const phone_auth_login = async (
|
|||||||
message: "登录成功",
|
message: "登录成功",
|
||||||
token: verify_response.data?.token,
|
token: verify_response.data?.token,
|
||||||
user_info: verify_response.data?.userInfo,
|
user_info: verify_response.data?.userInfo,
|
||||||
|
phone_update_status: verify_response.data?.phone_update_status,
|
||||||
|
existing_phone: verify_response.data?.existing_phone,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@@ -391,3 +400,14 @@ export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 更新用户手机号
|
||||||
|
export const updateUserPhone = async (payload: ChangePhoneParams) => {
|
||||||
|
try {
|
||||||
|
const response = await httpService.post("/user/update_phone", payload);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新用户手机号失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user