用户授权/验证文件上传
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 45s

This commit is contained in:
kk
2026-07-02 18:25:23 +08:00
parent 127834e8dc
commit 525f94cb80
9 changed files with 356 additions and 264 deletions
+17 -184
View File
@@ -1,20 +1,12 @@
/**
* 用户授权工具
* 登录检查、授权跳转、回调处理、绑定手机号
* 登录检查、授权跳转、退出登录
*
* 授权相关接口(auth/url、auth/callback、sms/send、bindPhone)走网关 apiGatewayUrl
* 业务接口(商品列表等)走 apiBaseUrl
* 新流程:
* - App.vue 检查 JWT,无则跳转 /pages/auth/loading
* - loading 页发起 OAuth → 获取 code → 调用 /login
* - 未注册用户跳转 /pages/register/register
*/
import config from '@/config/env.js';
import { gatewayGet, gatewayPost } from './request.js';
// 场景提示文案
const SCENE_TIPS = {
order: '登录后即可查看订单',
mine: '登录后即可查看个人信息',
pay: '登录后即可下单购买',
bindPhone: '需要绑定手机号完成登录',
};
/**
* 检查是否已登录
@@ -25,13 +17,19 @@ export function isLoggedIn() {
}
/**
* 检查登录状态,未登录时弹窗引导授权
* @param {string} scene - 触发场景:'order' | 'mine' | 'pay' | 'bindPhone'
* 检查登录状态,未登录时弹窗引导
* @param {string} scene - 触发场景:'order' | 'mine' | 'pay'
* @returns {boolean} 是否已登录
*/
export function checkLoginWithPrompt(scene = '') {
if (isLoggedIn()) return true;
const SCENE_TIPS = {
order: '登录后即可查看订单',
mine: '登录后即可查看个人信息',
pay: '登录后即可下单购买',
};
uni.showModal({
title: '提示',
content: SCENE_TIPS[scene] || '需要登录后操作',
@@ -39,7 +37,8 @@ export function checkLoginWithPrompt(scene = '') {
cancelText: '暂不',
success: (res) => {
if (res.confirm) {
startAuth();
// 跳转到授权 Loading 页
uni.redirectTo({ url: '/pages/auth/loading' });
}
},
});
@@ -48,153 +47,15 @@ export function checkLoginWithPrompt(scene = '') {
}
/**
* 检查登录状态,未登录时静默跳转授权(不弹窗)
* 检查登录状态,未登录时静默跳转
* @returns {boolean} 是否已登录
*/
export function requireLogin() {
if (isLoggedIn()) return true;
startAuth();
uni.redirectTo({ url: '/pages/auth/loading' });
return false;
}
/**
* 发起授权跳转
* 获取 auth_url 后跳转到微信/支付宝授权页
*/
export async function startAuth() {
const appNo = uni.getStorageSync('app_no') || '';
if (!appNo) {
uni.showToast({ title: '当前环境不支持授权', icon: 'none' });
return;
}
try {
const res = await gatewayGet('/api/v1/user/auth/url', {
app_no: appNo,
scopes: '',
redirect_uri: config.authRedirectUri || '',
});
const data = res.data || {};
if (data.code === 0 && data.data && data.data.auth_url) {
// #ifdef H5
window.location.href = data.data.auth_url;
// #endif
// #ifndef H5
// 小程序环境走其他授权方式
uni.setStorageSync('pending_auth', true);
// #endif
} else {
uni.showToast({ title: data.message || '获取授权地址失败', icon: 'none' });
}
} catch (e) {
console.error('[Auth] startAuth error:', e);
uni.showToast({ title: '网络请求失败', icon: 'none' });
}
}
/**
* 处理授权回调
* 解析 URL 中的 code/auth_code 参数,调用后端回调接口
* @returns {Promise<Object>} 回调结果
*/
export async function handleAuthCallback() {
// #ifdef H5
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code') || urlParams.get('auth_code') || '';
const appNo = urlParams.get('state') || uni.getStorageSync('app_no') || '';
if (!code) return null;
try {
const res = await gatewayGet('/api/v1/user/auth/callback', {
app_no: appNo,
code,
state: appNo,
});
const data = res.data || {};
if (data.code === 0 && data.data) {
return data.data;
}
return null;
} catch (e) {
return null;
}
// #endif
// #ifndef H5
return null;
// #endif
}
/**
* 授权成功后处理
* 缴存 token 和用户信息
* @param {Object} authResult - 后端返回的授权结果
*/
export function onAuthSuccess(authResult) {
if (!authResult) return;
const { token, user_info, auth_info } = authResult;
if (token) {
uni.setStorageSync('token', token);
}
if (user_info) {
uni.setStorageSync('user_info', user_info);
}
if (auth_info) {
uni.setStorageSync('auth_info', auth_info);
}
}
/**
* 发送短信验证码
* @param {string} phone - 手机号
* @param {string} tempToken - 临时 token
* @returns {Promise<Object>}
*/
export async function sendSmsCode(phone, tempToken) {
const res = await gatewayPost('/api/v1/user/sms/send', {
phone,
temp_token: tempToken,
purpose: 'bindPhone',
});
const data = res.data || {};
return data.code === 0;
}
/**
* 绑定手机号
* @param {string} tempToken - 临时 token
* @param {string} phone - 手机号
* @param {string} code - 验证码
* @returns {Promise<Object>} 绑定结果(含 token 和 user_info
*/
export async function bindPhone(tempToken, phone, code) {
const res = await gatewayPost('/api/v1/user/bindPhone', {
temp_token: tempToken,
phone,
code,
});
const data = res.data || {};
if (data.code === 0 && data.data) {
// 绑定成功,缓存 token 和用户信息
onAuthSuccess(data.data);
uni.removeStorageSync('temp_token');
return data.data;
}
uni.showToast({ title: data.message || '绑定失败', icon: 'none' });
return null;
}
/**
* 退出登录
*/
@@ -216,31 +77,3 @@ export function getCachedUserInfo() {
return null;
}
}
/**
* 显示绑定手机号弹窗
* @param {string} tempToken - 临时 token
* @param {Function} onSuccess - 绑定成功回调
*/
export function showBindPhoneDialog(tempToken, onSuccess) {
let phone = '';
let code = '';
let countdown = 0;
let timer = null;
// 使用 uni.showModal 不够灵活,这里用自定义弹窗
// 简化实现:先弹出手机号输入
uni.showModal({
title: '绑定手机号',
content: '请先绑定手机号完成注册',
confirmText: '去绑定',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
// 跳转到绑定手机号页面(预留)
// 后续可实现专门的绑定手机号页面
uni.showToast({ title: '绑定手机号功能开发中', icon: 'none' });
}
},
});
}