pet/pages/auth/phone-auth.vue

412 lines
9.2 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="phone-auth-container page-container-with-bg">
<!-- 顶部说明卡片 -->
<view class="auth-card">
<view class="auth-header">
<view class="auth-icon">
<text class="icon-text">📱</text>
</view>
<text class="auth-title">手机号授权</text>
<text class="auth-subtitle">为了更好地为您提供服务需要获取您的手机号</text>
</view>
<view class="auth-benefits">
<view class="benefit-item">
<text class="benefit-icon">🔒</text>
<text class="benefit-text">保护账号安全</text>
</view>
<view class="benefit-item">
<text class="benefit-icon">📞</text>
<text class="benefit-text">重要消息通知</text>
</view>
<view class="benefit-item">
<text class="benefit-icon">🎯</text>
<text class="benefit-text">个性化服务</text>
</view>
</view>
</view>
<!-- 授权按钮卡片 -->
<view class="button-card">
<button
class="auth-button"
:class="{ 'loading': authorizing }"
open-type="getPhoneNumber"
@getphonenumber="onGetPhoneNumber"
:disabled="authorizing"
>
<view class="button-content">
<view v-if="authorizing" class="loading-spinner"></view>
<text v-if="!authorizing">授权手机号</text>
<text v-else>授权中...</text>
</view>
</button>
<view class="auth-tips">
<text class="tips-text">点击授权后,我们将获取您的手机号用于账号验证</text>
</view>
</view>
<!-- 隐私说明 -->
<view class="privacy-card">
<text class="privacy-title">隐私保护</text>
<text class="privacy-text">我们承诺严格保护您的个人信息,仅用于账号安全验证和必要的服务通知,不会用于其他商业用途。</text>
</view>
<!-- 跳过按钮 -->
<view class="skip-section">
<text class="skip-button" @click="skipAuth">暂时跳过</text>
</view>
</view>
</template>
<script>
import { ref } from 'vue'
import { wxPhoneLogin } from '@/http/api/auth.js'
import {
savePhoneAuthData,
markPhoneSkipped,
STORAGE_KEYS
} from '@/utils/loginState.js'
export default {
name: 'PhoneAuthPage',
setup() {
// 响应式数据
const authorizing = ref(false)
// 防止重复点击
let isProcessing = false
// 错误处理函数
const handleError = (error) => {
let errorMessage = '授权失败,请重试'
let shouldReturnToLogin = false
console.error('手机号授权错误详情:', error)
if (error.message) {
if (error.message.includes('网络')) {
errorMessage = '网络连接异常,请检查网络后重试'
} else if (error.message.includes('登录状态已过期') || error.message.includes('重新获取微信登录code失败')) {
errorMessage = '登录状态已过期,请重新登录'
shouldReturnToLogin = true
} else if (error.message.includes('API返回数据格式错误')) {
errorMessage = '服务器响应异常,请稍后重试'
} else if (error.message.includes('用户拒绝')) {
errorMessage = '需要授权手机号才能继续使用'
} else if (error.code === 401) {
errorMessage = '授权已过期,请重新登录'
shouldReturnToLogin = true
} else if (error.code === 500) {
errorMessage = '服务器错误,请稍后重试'
} else {
errorMessage = error.message || '授权失败,请重试'
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
})
// 如果需要返回登录页面
if (shouldReturnToLogin) {
setTimeout(() => {
uni.reLaunch({
url: '/pages/profile/profile'
})
}, 3000)
}
}
// 方法定义
const onGetPhoneNumber = async (e) => {
console.log('获取手机号回调:', e)
// 防止重复点击
if (isProcessing) {
console.log('正在处理中,忽略重复点击')
return
}
if (e.detail.errMsg === 'getPhoneNumber:ok') {
isProcessing = true
authorizing.value = true
try {
// 获取微信登录code
let loginCode = uni.getStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
// 检查登录code是否存在和有效
if (!loginCode) {
console.warn('微信登录code不存在尝试重新获取')
// 尝试重新获取微信登录code
try {
const loginRes = await new Promise((resolve, reject) => {
uni.login({
success: resolve,
fail: reject
})
})
if (loginRes.code) {
loginCode = loginRes.code
uni.setStorageSync(STORAGE_KEYS.WX_LOGIN_CODE, loginCode)
console.log('重新获取微信登录code成功')
} else {
throw new Error('重新获取微信登录code失败')
}
} catch (loginError) {
console.error('重新获取微信登录code失败:', loginError)
throw new Error('登录状态已过期,请重新登录')
}
}
// 调用后端API进行手机号登录
const phoneData = {
code: loginCode,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv
}
// 调用手机号登录API
const result = await wxPhoneLogin(phoneData, {
custom: {
loading: false, // 使用页面自己的loading状态
catch: true // 确保错误能被catch到
}
})
// 处理登录结果
if (result && result.token) {
// 保存手机号登录数据
savePhoneAuthData({
token: result.token,
refreshToken: result.refreshToken,
userInfo: result.userInfo || result
})
uni.showToast({
title: '手机号授权成功',
icon: 'success',
duration: 1500
})
// 跳转到用户信息设置页面
setTimeout(() => {
uni.navigateTo({
url: '/pages/profile/user-info?mode=setup'
})
}, 1500)
} else {
throw new Error('手机号登录失败')
}
} catch (error) {
console.error('手机号授权失败:', error)
handleError(error)
} finally {
authorizing.value = false
isProcessing = false
}
} else {
// 用户拒绝授权
uni.showToast({
title: '需要授权手机号才能继续',
icon: 'none'
})
}
}
const skipAuth = () => {
uni.showModal({
title: '确认跳过',
content: '跳过手机号授权可能会影响部分功能的使用,确定要跳过吗?',
success: (res) => {
if (res.confirm) {
// 使用状态管理工具标记跳过状态
markPhoneSkipped()
// 跳转到个人信息设置页面
uni.navigateTo({
url: '/pages/profile/user-info?mode=setup&phoneSkipped=true'
})
}
}
})
}
return {
authorizing,
onGetPhoneNumber,
skipAuth
}
}
}
</script>
<style lang="scss" scoped>
.phone-auth-container {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
min-height: 100vh;
padding: 40rpx 30rpx;
display: flex;
flex-direction: column;
}
/* 通用卡片样式 */
.auth-card,
.button-card,
.privacy-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 24rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
}
/* 授权说明卡片 */
.auth-header {
text-align: center;
margin-bottom: 32rpx;
.auth-icon {
margin-bottom: 16rpx;
.icon-text {
font-size: 80rpx;
}
}
.auth-title {
display: block;
font-size: 36rpx;
font-weight: 600;
color: #333333;
margin-bottom: 12rpx;
}
.auth-subtitle {
display: block;
font-size: 28rpx;
color: #666666;
line-height: 1.5;
}
}
.auth-benefits {
.benefit-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.benefit-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.benefit-text {
font-size: 28rpx;
color: #333333;
}
}
}
/* 按钮卡片 */
.button-card {
text-align: center;
.auth-button {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
border-radius: 24rpx;
border: none;
color: white;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
transition: all 0.3s ease;
&.loading {
opacity: 0.8;
transform: scale(0.98);
}
&:disabled {
opacity: 0.7;
}
.button-content {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
}
.loading-spinner {
width: 32rpx;
height: 32rpx;
border: 4rpx solid rgba(255, 255, 255, 0.3);
border-top: 4rpx solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.auth-tips {
.tips-text {
font-size: 24rpx;
color: #999999;
line-height: 1.4;
}
}
}
/* 隐私说明卡片 */
.privacy-card {
.privacy-title {
display: block;
font-size: 28rpx;
font-weight: 600;
color: #333333;
margin-bottom: 12rpx;
}
.privacy-text {
font-size: 24rpx;
color: #666666;
line-height: 1.6;
}
}
/* 跳过按钮 */
.skip-section {
margin-top: auto;
text-align: center;
padding: 20rpx 0;
.skip-button {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
text-decoration: underline;
}
}
</style>