From 53155db13999ddd90ca0d8dc393f2f5e6bee14d5 Mon Sep 17 00:00:00 2001 From: yvan <8574526@qq.com> Date: Fri, 5 Sep 2025 21:16:21 +0800 Subject: [PATCH] 1 --- App.vue | 44 + http/api/profile.js | 46 +- http/config/config.js | 8 +- pages/profile/profile.vue | 1963 ++++++++++++++++++++++--------------- utils/wechat-auth.js | 271 +++++ 5 files changed, 1555 insertions(+), 777 deletions(-) create mode 100644 utils/wechat-auth.js diff --git a/App.vue b/App.vue index f7422a3..0273d98 100644 --- a/App.vue +++ b/App.vue @@ -1,13 +1,57 @@ diff --git a/http/api/profile.js b/http/api/profile.js index 89b9be5..c1facef 100644 --- a/http/api/profile.js +++ b/http/api/profile.js @@ -71,13 +71,23 @@ export const userLogin = (loginData, config = {}) => { } /** - * 微信登录 + * 微信小程序登录 * @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 自定义配置 - * @returns {Promise} + * @returns {Promise} 返回包含token和用户信息的Promise */ 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: { auth: false, loading: true, @@ -210,12 +220,16 @@ export const updateUserSettings = (settings, config = {}) => { /** * 绑定手机号 * @param {Object} phoneData 手机号数据 + * @param {string} phoneData.encryptedData 加密数据 + * @param {string} phoneData.iv 初始向量 + * @param {string} phoneData.cloudID 云函数ID(可选) * @param {Object} config 自定义配置 * @returns {Promise} */ export const bindPhone = (phoneData, config = {}) => { return uni.$u.http.post('/user/bind-phone', phoneData, { custom: { + auth: true, loading: true, loadingText: '正在绑定手机号...', ...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 自定义配置 diff --git a/http/config/config.js b/http/config/config.js index 513b1f8..7b76ee1 100644 --- a/http/config/config.js +++ b/http/config/config.js @@ -6,7 +6,7 @@ const ENV_CONFIG = { // 开发环境 development: { - baseURL: 'https://dev-api.pet-ai.com', + baseURL: 'http://127.0.0.1:8080', timeout: 30000 }, // 测试环境 @@ -33,8 +33,8 @@ export const HTTP_CONFIG = { // 当前环境配置 ...ENV_CONFIG[CURRENT_ENV], - // 登录页面路径 - loginPage: '/pages/login/login', + // 登录页面路径(个人中心页面,点击头像登录) + loginPage: '/pages/profile/profile', // 存储键名配置 storageKeys: { @@ -59,7 +59,7 @@ export const NO_AUTH_APIS = [ // 用户认证相关 '/auth/login', '/auth/register', - '/auth/wx-login', + '/wechat/user/mini/login', '/auth/refresh', // 公开浏览的接口 diff --git a/pages/profile/profile.vue b/pages/profile/profile.vue index ce129c8..1305581 100644 --- a/pages/profile/profile.vue +++ b/pages/profile/profile.vue @@ -1,922 +1,1345 @@ diff --git a/utils/wechat-auth.js b/utils/wechat-auth.js new file mode 100644 index 0000000..86ffa7a --- /dev/null +++ b/utils/wechat-auth.js @@ -0,0 +1,271 @@ +// 微信小程序认证工具类 +// 封装微信小程序登录的核心逻辑 + +import { HTTP_CONFIG } from '@/http/config/config.js' + +/** + * 微信小程序认证工具类 + */ +export class WechatAuth { + /** + * 微信登录获取code + * @returns {Promise} 微信授权码 + */ + 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} 用户信息 + */ + 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} 用户基本信息 + */ + 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} 登录结果 + */ + 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