校验并修改用户手机号

This commit is contained in:
2025-09-11 16:23:07 +08:00
parent 1fb6d6ee65
commit c430ed407b
5 changed files with 308 additions and 100 deletions

View File

@@ -496,4 +496,4 @@
cursor: pointer; cursor: pointer;
} }
} }
} }

View File

@@ -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;

View File

@@ -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;
}
}
}
}

View File

@@ -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;

View File

@@ -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;
}
};