This commit is contained in:
parent
4ae72d107c
commit
53155db139
44
App.vue
44
App.vue
|
|
@ -1,13 +1,57 @@
|
||||||
<script>
|
<script>
|
||||||
|
import WechatAuth from '@/utils/wechat-auth.js'
|
||||||
|
import { getUserInfo } from '@/http/api/profile.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onLaunch: function() {
|
onLaunch: function() {
|
||||||
console.log('App Launch')
|
console.log('App Launch')
|
||||||
|
this.initApp()
|
||||||
},
|
},
|
||||||
onShow: function() {
|
onShow: function() {
|
||||||
console.log('App Show')
|
console.log('App Show')
|
||||||
},
|
},
|
||||||
onHide: function() {
|
onHide: function() {
|
||||||
console.log('App Hide')
|
console.log('App Hide')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 应用初始化
|
||||||
|
async initApp() {
|
||||||
|
try {
|
||||||
|
await this.checkLoginStatus()
|
||||||
|
} catch (error) {
|
||||||
|
// 静默处理初始化错误
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查登录状态
|
||||||
|
async checkLoginStatus() {
|
||||||
|
const token = WechatAuth.getToken()
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证token有效性
|
||||||
|
const userInfo = await getUserInfo({
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新本地用户信息
|
||||||
|
if (userInfo) {
|
||||||
|
WechatAuth.saveLoginInfo(token, WechatAuth.getRefreshToken(), userInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// token无效,清除登录信息
|
||||||
|
if (error.code === 401 || error.statusCode === 401) {
|
||||||
|
WechatAuth.clearLoginInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,23 @@ export const userLogin = (loginData, config = {}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信登录
|
* 微信小程序登录
|
||||||
* @param {Object} wxData 微信登录数据
|
* @param {Object} wxData 微信登录数据
|
||||||
|
* @param {string} wxData.code 微信授权码(必需)
|
||||||
|
* @param {string} wxData.encryptedData 加密数据(可选,用户授权后获取)
|
||||||
|
* @param {string} wxData.iv 初始向量(可选,用户授权后获取)
|
||||||
|
* @param {string} wxData.signature 签名(可选,用户授权后获取)
|
||||||
|
* @param {Object} wxData.userInfo 用户信息(可选,用户授权后获取)
|
||||||
* @param {Object} config 自定义配置
|
* @param {Object} config 自定义配置
|
||||||
* @returns {Promise}
|
* @returns {Promise} 返回包含token和用户信息的Promise
|
||||||
*/
|
*/
|
||||||
export const wxLogin = (wxData, config = {}) => {
|
export const wxLogin = (wxData, config = {}) => {
|
||||||
return uni.$u.http.post('/auth/wx-login', wxData, {
|
// 验证必需参数
|
||||||
|
if (!wxData || !wxData.code) {
|
||||||
|
return Promise.reject(new Error('微信授权码(code)是必需的'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return uni.$u.http.post('/wechat/user/mini/login', wxData, {
|
||||||
custom: {
|
custom: {
|
||||||
auth: false,
|
auth: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
|
@ -210,12 +220,16 @@ export const updateUserSettings = (settings, config = {}) => {
|
||||||
/**
|
/**
|
||||||
* 绑定手机号
|
* 绑定手机号
|
||||||
* @param {Object} phoneData 手机号数据
|
* @param {Object} phoneData 手机号数据
|
||||||
|
* @param {string} phoneData.encryptedData 加密数据
|
||||||
|
* @param {string} phoneData.iv 初始向量
|
||||||
|
* @param {string} phoneData.cloudID 云函数ID(可选)
|
||||||
* @param {Object} config 自定义配置
|
* @param {Object} config 自定义配置
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
export const bindPhone = (phoneData, config = {}) => {
|
export const bindPhone = (phoneData, config = {}) => {
|
||||||
return uni.$u.http.post('/user/bind-phone', phoneData, {
|
return uni.$u.http.post('/user/bind-phone', phoneData, {
|
||||||
custom: {
|
custom: {
|
||||||
|
auth: true,
|
||||||
loading: true,
|
loading: true,
|
||||||
loadingText: '正在绑定手机号...',
|
loadingText: '正在绑定手机号...',
|
||||||
...config.custom
|
...config.custom
|
||||||
|
|
@ -224,6 +238,32 @@ export const bindPhone = (phoneData, config = {}) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小程序登录并绑定手机号
|
||||||
|
* @param {Object} loginData 登录和手机号数据
|
||||||
|
* @param {string} loginData.code 微信授权码
|
||||||
|
* @param {string} loginData.phoneEncryptedData 手机号加密数据(可选)
|
||||||
|
* @param {string} loginData.phoneIv 手机号初始向量(可选)
|
||||||
|
* @param {string} loginData.phoneCloudID 手机号云函数ID(可选)
|
||||||
|
* @param {Object} config 自定义配置
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export const wxLoginWithPhone = (loginData, config = {}) => {
|
||||||
|
if (!loginData || !loginData.code) {
|
||||||
|
return Promise.reject(new Error('微信授权码(code)是必需的'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return uni.$u.http.post('/auth/wx-login-phone', loginData, {
|
||||||
|
custom: {
|
||||||
|
auth: false,
|
||||||
|
loading: true,
|
||||||
|
loadingText: '正在登录...',
|
||||||
|
...config.custom
|
||||||
|
},
|
||||||
|
...config
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户权限
|
* 获取用户权限
|
||||||
* @param {Object} config 自定义配置
|
* @param {Object} config 自定义配置
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
const ENV_CONFIG = {
|
const ENV_CONFIG = {
|
||||||
// 开发环境
|
// 开发环境
|
||||||
development: {
|
development: {
|
||||||
baseURL: 'https://dev-api.pet-ai.com',
|
baseURL: 'http://127.0.0.1:8080',
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
},
|
},
|
||||||
// 测试环境
|
// 测试环境
|
||||||
|
|
@ -33,8 +33,8 @@ export const HTTP_CONFIG = {
|
||||||
// 当前环境配置
|
// 当前环境配置
|
||||||
...ENV_CONFIG[CURRENT_ENV],
|
...ENV_CONFIG[CURRENT_ENV],
|
||||||
|
|
||||||
// 登录页面路径
|
// 登录页面路径(个人中心页面,点击头像登录)
|
||||||
loginPage: '/pages/login/login',
|
loginPage: '/pages/profile/profile',
|
||||||
|
|
||||||
// 存储键名配置
|
// 存储键名配置
|
||||||
storageKeys: {
|
storageKeys: {
|
||||||
|
|
@ -59,7 +59,7 @@ export const NO_AUTH_APIS = [
|
||||||
// 用户认证相关
|
// 用户认证相关
|
||||||
'/auth/login',
|
'/auth/login',
|
||||||
'/auth/register',
|
'/auth/register',
|
||||||
'/auth/wx-login',
|
'/wechat/user/mini/login',
|
||||||
'/auth/refresh',
|
'/auth/refresh',
|
||||||
|
|
||||||
// 公开浏览的接口
|
// 公开浏览的接口
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="profile-container page-container-with-bg">
|
<view class="profile-container page-container-with-bg">
|
||||||
<!-- 用户信息卡片 -->
|
<!-- 用户信息卡片 -->
|
||||||
<view class="user-info-card">
|
<view class="user-info-card" @click="handleWxLogin">
|
||||||
<view class="user-avatar-section">
|
<view class="user-avatar-section">
|
||||||
<view class="avatar-wrapper">
|
<view class="avatar-wrapper">
|
||||||
<u-avatar
|
<u-avatar
|
||||||
|
|
@ -23,8 +23,19 @@
|
||||||
<view class="user-status">{{ userInfo.nickName ? '已登录' : '未登录' }}</view>
|
<view class="user-status">{{ userInfo.nickName ? '已登录' : '未登录' }}</view>
|
||||||
<view class="login-days" v-if="userInfo.nickName">已使用 {{ loginDays }} 天</view>
|
<view class="login-days" v-if="userInfo.nickName">已使用 {{ loginDays }} 天</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="user-action" @click="handleUserAction">
|
<view class="user-action">
|
||||||
<text class="action-icon">{{ userInfo.nickName ? '⚙️' : '👋' }}</text>
|
<!-- 登录按钮 -->
|
||||||
|
<button
|
||||||
|
v-if="!userInfo.nickName"
|
||||||
|
class="login-btn"
|
||||||
|
|
||||||
|
>
|
||||||
|
<text class="action-icon">👋</text>
|
||||||
|
</button>
|
||||||
|
<!-- 已登录状态 -->
|
||||||
|
<view v-else @click="handleUserAction" class="logged-in-action">
|
||||||
|
<text class="action-icon">⚙️</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
@ -149,11 +160,37 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 手机号授权按钮 -->
|
||||||
|
<view v-if="showPhoneAuthBtn" class="phone-auth-modal">
|
||||||
|
<view class="auth-overlay" @click="cancelPhoneAuth"></view>
|
||||||
|
<view class="auth-content">
|
||||||
|
<view class="auth-title">手机号授权</view>
|
||||||
|
<view class="auth-desc">为了更好的服务体验,需要获取您的手机号</view>
|
||||||
|
<view class="auth-buttons">
|
||||||
|
<button
|
||||||
|
class="auth-btn primary"
|
||||||
|
open-type="getPhoneNumber"
|
||||||
|
@getphonenumber="handlePhoneAuth"
|
||||||
|
>
|
||||||
|
授权手机号
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="auth-btn secondary"
|
||||||
|
@click="cancelPhoneAuth"
|
||||||
|
>
|
||||||
|
跳过
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {reactive, ref, onMounted, computed} from 'vue'
|
import {reactive, ref, onMounted, computed} from 'vue'
|
||||||
|
import WechatAuth from '@/utils/wechat-auth.js'
|
||||||
|
import {wxLogin, wxLoginWithPhone, bindPhone} from '@/http/api/profile.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProfilePage',
|
name: 'ProfilePage',
|
||||||
|
|
@ -166,6 +203,8 @@ export default {
|
||||||
|
|
||||||
const loginDays = ref(0)
|
const loginDays = ref(0)
|
||||||
const notificationCount = ref(3)
|
const notificationCount = ref(3)
|
||||||
|
const showPhoneAuthBtn = ref(false)
|
||||||
|
const currentLoginCode = ref('')
|
||||||
|
|
||||||
// 宠物统计数据
|
// 宠物统计数据
|
||||||
const petStats = reactive({
|
const petStats = reactive({
|
||||||
|
|
@ -193,12 +232,14 @@ export default {
|
||||||
|
|
||||||
const checkLogin = () => {
|
const checkLogin = () => {
|
||||||
// 检查是否已登录
|
// 检查是否已登录
|
||||||
const savedUserInfo = uni.getStorageSync('userInfo')
|
if (WechatAuth.isLoggedIn()) {
|
||||||
|
const savedUserInfo = WechatAuth.getUserInfoFromStorage()
|
||||||
if (savedUserInfo) {
|
if (savedUserInfo) {
|
||||||
userInfo.value = savedUserInfo
|
userInfo.value = savedUserInfo
|
||||||
calculateLoginDays()
|
calculateLoginDays()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const calculateLoginDays = () => {
|
const calculateLoginDays = () => {
|
||||||
const loginDate = uni.getStorageSync('loginDate')
|
const loginDate = uni.getStorageSync('loginDate')
|
||||||
|
|
@ -266,38 +307,306 @@ export default {
|
||||||
if (userInfo.value.nickName) {
|
if (userInfo.value.nickName) {
|
||||||
// 已登录,跳转到个人信息设置
|
// 已登录,跳转到个人信息设置
|
||||||
navigateTo('/pages/profile/user-info')
|
navigateTo('/pages/profile/user-info')
|
||||||
} else {
|
|
||||||
// 未登录,执行登录
|
|
||||||
wxLogin()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxLogin = () => {
|
const handleWxLogin = () => {
|
||||||
uni.login({
|
|
||||||
provider: 'weixin',
|
wx.login({
|
||||||
success: (res) => {
|
success: (loginRes) => {
|
||||||
console.log('登录成功', res)
|
if (loginRes.code) {
|
||||||
// 模拟登录成功
|
console.log(loginRes)
|
||||||
userInfo.value = {
|
// 3. 调用后端登录接口
|
||||||
nickName: '宠物主人',
|
handleWxLoginSuccess(loginRes.code, userInfo.value)
|
||||||
avatarUrl: ''
|
} else {
|
||||||
|
handleLoginError(new Error('获取微信授权码失败'))
|
||||||
}
|
}
|
||||||
uni.setStorageSync('userInfo', userInfo.value)
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
handleLoginError(new Error('微信登录失败: ' + (error.errMsg || '未知错误')))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// 微信登录按钮点击事件
|
||||||
|
// performWxLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUserInfo = (e) => {
|
||||||
|
// 处理用户信息授权回调
|
||||||
|
console.log('用户信息授权结果:', e.detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
const performWxLogin = async () => {
|
||||||
|
try {
|
||||||
|
console.log(999)
|
||||||
|
// 1. 先获取用户信息
|
||||||
|
wx.getUserProfile({
|
||||||
|
desc: "获取用户头像昵称",
|
||||||
|
success: (res) => {
|
||||||
|
// 更新用户信息
|
||||||
|
userInfo.value = {
|
||||||
|
...res.userInfo,
|
||||||
|
avatarUrl: res.userInfo.avatarUrl || '',
|
||||||
|
nickName: res.userInfo.nickName || "未命名用户",
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("111")
|
||||||
|
|
||||||
|
// 2. 获取微信登录码
|
||||||
|
wx.login({
|
||||||
|
success: (loginRes) => {
|
||||||
|
if (loginRes.code) {
|
||||||
|
// 3. 调用后端登录接口
|
||||||
|
handleWxLoginSuccess(loginRes.code, userInfo.value)
|
||||||
|
} else {
|
||||||
|
handleLoginError(new Error('获取微信授权码失败'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
handleLoginError(new Error('微信登录失败: ' + (error.errMsg || '未知错误')))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.log(error)
|
||||||
|
handleLoginError(new Error('获取用户信息失败: ' + (error.errMsg || '未知错误')))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
handleLoginError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWxLoginSuccess = async (code, wxUserInfo) => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在登录...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用后端登录接口
|
||||||
|
const response = await wxLogin({
|
||||||
|
code: code,
|
||||||
|
userInfo: wxUserInfo
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理登录成功
|
||||||
|
handleLoginSuccess(response)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
handleLoginError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showPhoneAuthModal = (code) => {
|
||||||
|
// 保存当前登录码
|
||||||
|
currentLoginCode.value = code
|
||||||
|
|
||||||
|
// 显示手机号授权按钮
|
||||||
|
showPhoneAuthBtn.value = true
|
||||||
|
|
||||||
|
// 提示用户点击授权按钮
|
||||||
|
uni.showToast({
|
||||||
|
title: '请点击授权按钮',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const doBasicLogin = async (code) => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在登录...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用后端登录接口
|
||||||
|
const response = await wxLogin({code})
|
||||||
|
|
||||||
|
// 处理登录成功
|
||||||
|
handleLoginSuccess(response)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
handleLoginError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const doWxLoginWithPhone = async () => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在登录...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 1. 获取微信授权码
|
||||||
|
const code = await WechatAuth.getWxCode()
|
||||||
|
|
||||||
|
// 2. 显示手机号授权提示
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showModal({
|
||||||
|
title: '手机号授权',
|
||||||
|
content: '为了更好的服务体验,需要获取您的手机号',
|
||||||
|
confirmText: '授权',
|
||||||
|
cancelText: '跳过',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 用户同意授权,显示手机号授权页面
|
||||||
|
showPhoneAuthPage(code)
|
||||||
|
} else {
|
||||||
|
// 用户跳过,使用基础登录
|
||||||
|
doWxLoginOnly(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
handleLoginError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showPhoneAuthPage = (code) => {
|
||||||
|
// 跳转到手机号授权页面
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/profile/phone-auth?code=${code}`,
|
||||||
|
fail: () => {
|
||||||
|
// 如果页面不存在,使用基础登录
|
||||||
|
doWxLoginOnly(code)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const doWxLoginOnly = async (code) => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在登录...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await wxLogin({code})
|
||||||
|
handleLoginSuccess(response)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
handleLoginError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLoginSuccess = (response) => {
|
||||||
|
if (response && response.token) {
|
||||||
|
WechatAuth.saveLoginInfo(
|
||||||
|
response.token,
|
||||||
|
response.refreshToken,
|
||||||
|
response.userInfo || userInfo.value
|
||||||
|
)
|
||||||
|
|
||||||
|
// 如果后端返回了用户信息,使用后端的,否则使用本地的
|
||||||
|
if (response.userInfo) {
|
||||||
|
userInfo.value = response.userInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存登录日期
|
||||||
uni.setStorageSync('loginDate', new Date().toISOString())
|
uni.setStorageSync('loginDate', new Date().toISOString())
|
||||||
calculateLoginDays()
|
calculateLoginDays()
|
||||||
|
|
||||||
|
// 刷新统计数据
|
||||||
|
loadUserStats()
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
// 询问是否获取手机号
|
||||||
|
uni.showModal({
|
||||||
|
title: '完善信息',
|
||||||
|
content: '是否授权获取手机号,以便为您提供更好的服务?',
|
||||||
|
confirmText: '授权',
|
||||||
|
cancelText: '跳过',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 显示手机号授权
|
||||||
|
showPhoneAuthBtn.value = true
|
||||||
|
} else {
|
||||||
|
// 跳过手机号授权
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '登录成功',
|
title: '登录成功',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
fail: (err) => {
|
}
|
||||||
console.error('登录失败', err)
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error('登录响应数据异常')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePhoneAuth = async (e) => {
|
||||||
|
// 隐藏授权按钮
|
||||||
|
showPhoneAuthBtn.value = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在绑定手机号...',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取手机号授权结果
|
||||||
|
const phoneResult = WechatAuth.getPhoneNumber(e)
|
||||||
|
|
||||||
|
if (phoneResult.success) {
|
||||||
|
// 手机号授权成功,调用绑定手机号接口
|
||||||
|
const response = await bindPhone({
|
||||||
|
encryptedData: phoneResult.encryptedData,
|
||||||
|
iv: phoneResult.iv,
|
||||||
|
cloudID: phoneResult.cloudID
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '登录失败',
|
title: '手机号绑定成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 手机号授权失败
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '手机号授权失败',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '绑定失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelPhoneAuth = () => {
|
||||||
|
// 隐藏授权按钮
|
||||||
|
showPhoneAuthBtn.value = false
|
||||||
|
// 使用基础登录
|
||||||
|
doBasicLogin(currentLoginCode.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLoginError = (error) => {
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
let errorMessage = '登录失败'
|
||||||
|
if (error.message.includes('用户拒绝授权')) {
|
||||||
|
errorMessage = '需要授权才能使用完整功能'
|
||||||
|
} else if (error.message.includes('网络')) {
|
||||||
|
errorMessage = '网络连接失败,请检查网络'
|
||||||
|
} else if (error.message.includes('code')) {
|
||||||
|
errorMessage = '微信授权失败,请重试'
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '登录提示',
|
||||||
|
content: errorMessage,
|
||||||
|
showCancel: false,
|
||||||
|
confirmText: '确定'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,7 +704,7 @@ export default {
|
||||||
// 初始化时检查新功能
|
// 初始化时检查新功能
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initPage()
|
initPage()
|
||||||
checkNewFeatures()
|
// checkNewFeatures()
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -405,7 +714,12 @@ export default {
|
||||||
petStats,
|
petStats,
|
||||||
familyStats,
|
familyStats,
|
||||||
adoptionStats,
|
adoptionStats,
|
||||||
|
showPhoneAuthBtn,
|
||||||
handleUserAction,
|
handleUserAction,
|
||||||
|
handleWxLogin,
|
||||||
|
handleUserInfo,
|
||||||
|
handlePhoneAuth,
|
||||||
|
cancelPhoneAuth,
|
||||||
navigateTo,
|
navigateTo,
|
||||||
navigateToFamily,
|
navigateToFamily,
|
||||||
inviteFamily,
|
inviteFamily,
|
||||||
|
|
@ -914,9 +1228,118 @@ export default {
|
||||||
animation: fadeIn 0.5s ease-out;
|
animation: fadeIn 0.5s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-container > view:nth-child(1) { animation-delay: 0.1s; }
|
.profile-container > view:nth-child(1) {
|
||||||
.profile-container > view:nth-child(2) { animation-delay: 0.2s; }
|
animation-delay: 0.1s;
|
||||||
.profile-container > view:nth-child(3) { animation-delay: 0.3s; }
|
}
|
||||||
.profile-container > view:nth-child(4) { animation-delay: 0.4s; }
|
|
||||||
.profile-container > view:nth-child(5) { animation-delay: 0.5s; }
|
.profile-container > view:nth-child(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-container > view:nth-child(3) {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-container > view:nth-child(4) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-container > view:nth-child(5) {
|
||||||
|
animation-delay: 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 手机号授权模态框 */
|
||||||
|
.phone-auth-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.auth-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-content {
|
||||||
|
position: relative;
|
||||||
|
background: white;
|
||||||
|
margin: 0 60rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 60rpx 40rpx;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
.auth-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-desc {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
.auth-btn {
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 登录按钮样式 */
|
||||||
|
.login-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.action-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logged-in-action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.action-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
// 微信小程序认证工具类
|
||||||
|
// 封装微信小程序登录的核心逻辑
|
||||||
|
|
||||||
|
import { HTTP_CONFIG } from '@/http/config/config.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小程序认证工具类
|
||||||
|
*/
|
||||||
|
export class WechatAuth {
|
||||||
|
/**
|
||||||
|
* 微信登录获取code
|
||||||
|
* @returns {Promise<string>} 微信授权码
|
||||||
|
*/
|
||||||
|
static async getWxCode() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
wx.login({
|
||||||
|
success: (res) => {
|
||||||
|
console.log(res)
|
||||||
|
if (res.code) {
|
||||||
|
resolve(res.code)
|
||||||
|
} else {
|
||||||
|
reject(new Error('获取微信授权码失败'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
reject(new Error('微信登录失败: ' + (error.errMsg || '未知错误')))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息(需要用户授权)
|
||||||
|
* @param {string} desc 申请理由
|
||||||
|
* @returns {Promise<Object>} 用户信息
|
||||||
|
*/
|
||||||
|
static async getUserProfile(desc = '获取用户头像昵称') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
wx.getUserProfile({
|
||||||
|
desc: desc,
|
||||||
|
success: (res) => {
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
if (error.errMsg && error.errMsg.includes('auth deny')) {
|
||||||
|
reject(new Error('用户拒绝授权'))
|
||||||
|
} else {
|
||||||
|
reject(new Error('获取用户信息失败: ' + (error.errMsg || '未知错误')))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
reject(new Error('当前环境不支持微信授权'))
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户基本信息(无需授权,但信息有限)
|
||||||
|
* @returns {Promise<Object>} 用户基本信息
|
||||||
|
*/
|
||||||
|
static async getUserInfo() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.getUserInfo({
|
||||||
|
success: (res) => {
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
reject(new Error('获取用户基本信息失败: ' + (error.errMsg || '未知错误')))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户手机号(需要用户授权)
|
||||||
|
* 注意:这个方法需要在button组件的open-type="getPhoneNumber"的回调中使用
|
||||||
|
* @param {Object} e 手机号授权回调事件对象
|
||||||
|
* @returns {Object} 手机号相关数据
|
||||||
|
*/
|
||||||
|
static getPhoneNumber(e) {
|
||||||
|
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
encryptedData: e.detail.encryptedData,
|
||||||
|
iv: e.detail.iv,
|
||||||
|
cloudID: e.detail.cloudID
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: e.detail.errMsg || '获取手机号失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机头像
|
||||||
|
* @returns {string} 随机头像URL
|
||||||
|
*/
|
||||||
|
static generateRandomAvatar() {
|
||||||
|
const avatars = [
|
||||||
|
'https://img.yzcdn.cn/vant/cat.jpeg',
|
||||||
|
'https://img.yzcdn.cn/vant/dog.jpeg',
|
||||||
|
'https://img.yzcdn.cn/vant/rabbit.jpeg',
|
||||||
|
'https://img.yzcdn.cn/vant/bird.jpeg'
|
||||||
|
]
|
||||||
|
return avatars[Math.floor(Math.random() * avatars.length)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存登录信息到本地存储
|
||||||
|
* @param {string} token 访问令牌
|
||||||
|
* @param {string} refreshToken 刷新令牌(可选)
|
||||||
|
* @param {Object} userInfo 用户信息
|
||||||
|
*/
|
||||||
|
static saveLoginInfo(token, refreshToken = null, userInfo = null) {
|
||||||
|
try {
|
||||||
|
// 使用配置中定义的存储键名
|
||||||
|
uni.setStorageSync(HTTP_CONFIG.storageKeys.token, token)
|
||||||
|
|
||||||
|
if (refreshToken) {
|
||||||
|
uni.setStorageSync(HTTP_CONFIG.storageKeys.refreshToken, refreshToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo) {
|
||||||
|
uni.setStorageSync(HTTP_CONFIG.storageKeys.userInfo, userInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('登录信息保存成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存登录信息失败:', error)
|
||||||
|
throw new Error('保存登录信息失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地存储的token
|
||||||
|
* @returns {string|null} 访问令牌
|
||||||
|
*/
|
||||||
|
static getToken() {
|
||||||
|
try {
|
||||||
|
return uni.getStorageSync(HTTP_CONFIG.storageKeys.token) || null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取token失败:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地存储的刷新令牌
|
||||||
|
* @returns {string|null} 刷新令牌
|
||||||
|
*/
|
||||||
|
static getRefreshToken() {
|
||||||
|
try {
|
||||||
|
return uni.getStorageSync(HTTP_CONFIG.storageKeys.refreshToken) || null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取refreshToken失败:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地存储的用户信息
|
||||||
|
* @returns {Object|null} 用户信息
|
||||||
|
*/
|
||||||
|
static getUserInfoFromStorage() {
|
||||||
|
try {
|
||||||
|
const userInfo = uni.getStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
||||||
|
return userInfo ? (typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo) : null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否已登录
|
||||||
|
* @returns {boolean} 是否已登录
|
||||||
|
*/
|
||||||
|
static isLoggedIn() {
|
||||||
|
const token = this.getToken()
|
||||||
|
return !!token
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有登录信息
|
||||||
|
*/
|
||||||
|
static clearLoginInfo() {
|
||||||
|
try {
|
||||||
|
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
|
||||||
|
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
|
||||||
|
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
||||||
|
console.log('登录信息清除成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('清除登录信息失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转到登录页面
|
||||||
|
*/
|
||||||
|
static navigateToLogin() {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: HTTP_CONFIG.loginPage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查登录状态并处理
|
||||||
|
* @returns {boolean} 是否已登录
|
||||||
|
*/
|
||||||
|
static checkLoginStatus() {
|
||||||
|
if (!this.isLoggedIn()) {
|
||||||
|
console.log('用户未登录,跳转到登录页')
|
||||||
|
this.navigateToLogin()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完整的微信小程序登录流程
|
||||||
|
* @param {boolean} needUserInfo 是否需要获取用户详细信息
|
||||||
|
* @returns {Promise<Object>} 登录结果
|
||||||
|
*/
|
||||||
|
static async performWxLogin(needUserInfo = true) {
|
||||||
|
try {
|
||||||
|
// 1. 获取微信授权码
|
||||||
|
const code = await this.getWxCode()
|
||||||
|
|
||||||
|
// 2. 准备登录数据
|
||||||
|
const loginData = { code }
|
||||||
|
|
||||||
|
// 3. 如果需要用户信息,尝试获取
|
||||||
|
if (needUserInfo) {
|
||||||
|
try {
|
||||||
|
const userProfile = await this.getUserProfile()
|
||||||
|
loginData.encryptedData = userProfile.encryptedData
|
||||||
|
loginData.iv = userProfile.iv
|
||||||
|
loginData.signature = userProfile.signature
|
||||||
|
loginData.userInfo = userProfile.userInfo
|
||||||
|
} catch (userInfoError) {
|
||||||
|
console.warn('获取用户详细信息失败,使用基本登录:', userInfoError.message)
|
||||||
|
// 继续使用基本登录,不中断流程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: loginData,
|
||||||
|
message: '微信授权成功'
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('微信登录流程失败:', error)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
message: '微信登录失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认导出,便于直接导入使用
|
||||||
|
*/
|
||||||
|
export default WechatAuth
|
||||||
Loading…
Reference in New Issue