pet/pages/auth/phone-auth.vue

369 lines
7.8 KiB
Vue
Raw 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 = '授权失败,请重试'
if (error.message) {
if (error.message.includes('网络')) {
errorMessage = '网络连接异常,请检查网络后重试'
} else if (error.message.includes('登录状态已过期')) {
errorMessage = '登录状态已过期,请重新登录'
// 3秒后自动返回profile页面
setTimeout(() => {
uni.reLaunch({
url: '/pages/profile/profile'
})
}, 3000)
} else if (error.message.includes('API返回数据格式错误')) {
errorMessage = '服务器响应异常,请稍后重试'
} else {
errorMessage = error.message
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 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
const loginCode = uni.getStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
if (!loginCode) {
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状态
}
})
// 使用状态管理工具保存手机号授权数据
if (result && result.token) {
savePhoneAuthData(result)
} else {
throw new Error('API返回数据格式错误')
}
uni.showToast({
title: '授权成功',
icon: 'success'
})
// 跳转到个人信息设置页面
setTimeout(() => {
uni.navigateTo({
url: '/pages/profile/user-info?mode=setup'
})
}, 1500)
} 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>