修改用户授权逻辑先静默授权,点击我的时才非静默授权
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 51s

This commit is contained in:
kk
2026-07-10 18:34:07 +08:00
parent 20649c67a9
commit 0fc854cd33
4 changed files with 159 additions and 97 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ const dev = {
user_info: { user_info: {
user_id: 'mock_user_001', user_id: 'mock_user_001',
phone: '13264706088', phone: '13264706088',
open_id: 'test_open_id', open_id: 'mock_openid_002',
union_id: 'test_union_id', union_id: 'test_union_id',
nickname: '测试用户', nickname: '测试用户',
avatar: 'https://img.wewemivending.com/vms/us/img/uploads/2025/12/18/6943d37c7d82b3439.jpeg', avatar: 'https://img.wewemivending.com/vms/us/img/uploads/2025/12/18/6943d37c7d82b3439.jpeg',
+56 -54
View File
@@ -51,7 +51,7 @@ export default {
}, },
methods: { methods: {
/** /**
* 发起 OAuth 授权 * 发起 OAuth 授权(静默授权,只获取 openid
*/ */
async startAuth() { async startAuth() {
const platform = detectPlatform(); const platform = detectPlatform();
@@ -71,15 +71,16 @@ export default {
window.location.origin.replace(/^http:/, 'https:') + window.location.pathname window.location.origin.replace(/^http:/, 'https:') + window.location.pathname
); );
// 使用 snsapi_base 静默授权,不弹窗
const res = await gatewayGet('/api/v1/user/auth/url', { const res = await gatewayGet('/api/v1/user/auth/url', {
app_no: appNo, app_no: appNo,
scopes: 'snsapi_userinfo', scopes: 'snsapi_base',
redirect_uri: redirectUri, redirect_uri: redirectUri,
}); });
const data = res.data || {}; const data = res.data || {};
if (data.code === 0 && data.data && data.data.auth_url) { if (data.code === 0 && data.data && data.data.auth_url) {
// 跳转微信授权页 // 跳转微信授权页(静默,不弹窗)
window.location.href = data.data.auth_url; window.location.href = data.data.auth_url;
} else { } else {
uni.showToast({ title: data.message || '获取授权失败', icon: 'none' }); uni.showToast({ title: data.message || '获取授权失败', icon: 'none' });
@@ -93,7 +94,7 @@ export default {
}, },
/** /**
* 处理授权回调(从 URL 中获取 code 后调用后端 /login * 处理授权回调(从 URL 中获取 code 后换取 openid,再用 openid 登录
*/ */
async handleCallback(code) { async handleCallback(code) {
const appNo = uni.getStorageSync('app_no') || getAppNo(detectPlatform()); const appNo = uni.getStorageSync('app_no') || getAppNo(detectPlatform());
@@ -107,6 +108,7 @@ export default {
try { try {
this.statusText = '正在验证身份...'; this.statusText = '正在验证身份...';
// 用 code 换取 openid(后端处理,前端只传 code)
const res = await gatewayPost('/api/v1/user/login', { const res = await gatewayPost('/api/v1/user/login', {
app_no: appNo, app_no: appNo,
code: code, code: code,
@@ -125,17 +127,17 @@ export default {
url.searchParams.delete('state'); url.searchParams.delete('state');
window.history.replaceState({}, '', url.toString()); window.history.replaceState({}, '', url.toString());
// 存储 openid
uni.setStorageSync('openid', result.openid);
if (result.register) { if (result.register) {
// 已注册 → 缓存 JWT → 跳转首页 // 已注册 → 缓存 JWT → 跳转首页
uni.setStorageSync('token', result.token); uni.setStorageSync('token', result.token);
uni.setStorageSync('user_info', result.user_info); // 不再存储 user_info,点击"我的"时再获取
console.log('[loading] stored user_info:', JSON.stringify(result.user_info, null, 2));
this.goToHome(); this.goToHome();
} else { } else {
// 未注册 → 缓存临时数据 → 跳转注册页 // 未注册 → 缓存临时数据 → 跳转注册页
uni.setStorageSync('temp_token', result.temp_token); uni.setStorageSync('temp_token', result.temp_token);
uni.setStorageSync('auth_info', result.auth_info);
console.log('[loading] stored auth_info:', JSON.stringify(result.auth_info, null, 2));
this.goToRegister(); this.goToRegister();
} }
} else { } else {
@@ -176,60 +178,60 @@ export default {
}, },
/** /**
* Mock 登录(开发环境模拟不同用户状态 * Mock 登录(跳过微信授权,调用真实登录接口
*/ */
async mockLogin() { async mockLogin() {
const mode = config.mockWechatLogin; const mode = config.mockWechatLogin;
const mock = config.mockUser; const mock = config.mockUser;
const mockOpenid = mock.user_info.open_id;
if (mode === 'register' || mode === 'login') { // 'skip' 模式直接登录,不调接口
// 调用真实登录接口,用测试 code if (mode === 'skip') {
this.statusText = '模拟:获取授权信息...';
const appNo = uni.getStorageSync('app_no') || getAppNo(detectPlatform()) || config.mockAppNo;
try {
const res = await gatewayPost('/api/v1/user/login', {
app_no: appNo,
code: 'test_code',
});
const data = res.data || {};
if (data.code === 0 && data.data) {
const result = data.data;
if (result.register) {
// 已注册
uni.setStorageSync('token', result.token);
uni.setStorageSync('user_info', result.user_info);
console.log('[Auth] Mock: existing user → home');
this.goToHome();
} else {
// 未注册 → 存真实的 temp_token,合并 mock 头像昵称
const authInfo = {
...mock.user_info,
...result.auth_info,
};
uni.setStorageSync('temp_token', result.temp_token);
uni.setStorageSync('auth_info', authInfo);
console.log('[Auth] Mock: new user → register, temp_token:', result.temp_token);
this.goToRegister();
}
} else {
uni.showToast({ title: data.message || '登录接口返回异常', icon: 'none' });
console.error('[Auth] Mock login failed:', data);
}
} catch (e) {
console.error('[Auth] Mock login error:', e);
uni.showToast({ title: '网络请求失败', icon: 'none' });
}
} else {
// 'skip' → 直接登录
this.statusText = '模拟登录中...'; this.statusText = '模拟登录中...';
uni.setStorageSync('token', mock.token); uni.setStorageSync('token', mock.token);
uni.setStorageSync('user_info', mock.user_info); uni.setStorageSync('openid', mockOpenid);
console.log('[Auth] Mock: skip → home'); console.log('[Auth] Mock skip → home');
setTimeout(() => this.goToHome(), 500); setTimeout(() => this.goToHome(), 500);
return;
}
// 'register' / 'login' 模式:调用真实登录接口,传 mock openid
this.statusText = '模拟:调用登录接口...';
const appNo = uni.getStorageSync('app_no') || config.mockAppNo;
try {
const res = await gatewayPost('/api/v1/user/login', {
app_no: appNo,
openid: mockOpenid,
});
const data = res.data || {};
console.log('[Auth] Mock login response:', JSON.stringify(data, null, 2));
if (data.code === 0 && data.data) {
const result = data.data;
// 存储 openid
uni.setStorageSync('openid', result.openid || mockOpenid);
if (result.register) {
// 已注册 → 存 token → 跳首页
uni.setStorageSync('token', result.token);
console.log('[Auth] Mock: existing user → home');
this.goToHome();
} else {
// 未注册 → 存 temp_token → 跳注册页
uni.setStorageSync('temp_token', result.temp_token);
console.log('[Auth] Mock: new user → register');
this.goToRegister();
}
} else {
uni.showToast({ title: data.message || '登录失败', icon: 'none' });
console.error('[Auth] Mock login failed:', data);
}
} catch (e) {
console.error('[Auth] Mock login error:', e);
uni.showToast({ title: '网络请求失败', icon: 'none' });
} }
}, },
}, },
+72 -19
View File
@@ -2,11 +2,6 @@
<view class="app-container"> <view class="app-container">
<!-- ========== 首页内容 ========== --> <!-- ========== 首页内容 ========== -->
<view v-if="currentTab === 'home'" class="page-content home-page"> <view v-if="currentTab === 'home'" class="page-content home-page">
<!-- 顶部标题栏 -->
<view class="page-header">
<text class="page-title">智能货柜{{ deviceId ? '-' + deviceId : '' }}</text>
</view>
<scroll-view <scroll-view
class="product-area" class="product-area"
scroll-y scroll-y
@@ -168,7 +163,7 @@
<script> <script>
import PageNavBar from '@/components/page-nav-bar/page-nav-bar.vue'; import PageNavBar from '@/components/page-nav-bar/page-nav-bar.vue';
import config from '@/config/env.js'; import config from '@/config/env.js';
import { get } from '@/utils/request.js'; import { get, gatewayGet } from '@/utils/request.js';
import { checkLoginWithPrompt, getCachedUserInfo } from '@/utils/auth-guard.js'; import { checkLoginWithPrompt, getCachedUserInfo } from '@/utils/auth-guard.js';
import { isVConsoleEnabled, showVConsole, hideVConsole } from '@/utils/vconsole'; import { isVConsoleEnabled, showVConsole, hideVConsole } from '@/utils/vconsole';
@@ -236,10 +231,14 @@ export default {
this.currentTab = 'mine'; this.currentTab = 'mine';
// 记录来源:从其他页面跳转过来的 // 记录来源:从其他页面跳转过来的
this.fromPage = 'navigate'; this.fromPage = 'navigate';
// 获取用户信息
this.fetchUserInfo();
} }
if (options.device_id) { if (options.device_id) {
this.deviceId = options.device_id; this.deviceId = options.device_id;
// 动态设置导航栏标题
uni.setNavigationBarTitle({ title: `智能货柜-${this.deviceId}` });
this.fetchProducts(); this.fetchProducts();
} else if (!options.tab) { } else if (!options.tab) {
// 无设备编号且非 tab 跳转,提示并跳至扫码页 // 无设备编号且非 tab 跳转,提示并跳至扫码页
@@ -272,6 +271,8 @@ export default {
// 记录来源:从 tab 切换的 // 记录来源:从 tab 切换的
if (tab === 'mine') { if (tab === 'mine') {
this.fromPage = 'tab'; this.fromPage = 'tab';
// 获取用户信息
this.fetchUserInfo();
} }
}, },
switchCategory(cat) { this.activeCategory = cat; }, switchCategory(cat) { this.activeCategory = cat; },
@@ -291,6 +292,71 @@ export default {
} }
console.log('[index] this.userInfo:', JSON.stringify(this.userInfo, null, 2)); console.log('[index] this.userInfo:', JSON.stringify(this.userInfo, null, 2));
}, },
// 从接口获取用户信息(点击"我的"时调用)
async fetchUserInfo() {
const appNo = uni.getStorageSync('app_no') || config.mockAppNo;
if (!appNo) {
console.warn('[index] 无法获取应用信息');
return;
}
// Mock 模式下,从缓存读取用户信息
if (config.mockWechatLogin) {
console.log('[index] Mock 模式,从缓存读取用户信息');
const cached = getCachedUserInfo();
if (cached) {
// 有缓存(注册后存储的),直接使用
this.userInfo = { ...this.userInfo, ...cached };
} else {
// 无缓存,使用 mock 数据(已注册用户)
this.userInfo = {
nickname: config.mockUser.user_info.nickname || '',
phone: config.mockUser.user_info.phone || '',
avatar: config.mockUser.user_info.avatar || '',
};
uni.setStorageSync('user_info', this.userInfo);
}
return;
}
try {
// 调用接口获取用户信息
const res = await gatewayGet('/api/v1/user/info', {
app_no: appNo,
});
const data = res.data || {};
console.log('[index] fetchUserInfo response:', JSON.stringify(data, null, 2));
if (data.code === 0 && data.data) {
const userInfo = data.data;
this.userInfo = {
nickname: userInfo.nickname || '',
phone: userInfo.phone || '',
avatar: userInfo.avatar || '',
};
// 存入缓存
uni.setStorageSync('user_info', this.userInfo);
console.log('[index] fetchUserInfo success:', JSON.stringify(this.userInfo, null, 2));
} else if (data.code === 1010) {
// 用户未注册,需要重新走授权流程获取 temp_token
console.warn('[index] 用户未注册,重新授权');
uni.removeStorageSync('token');
uni.removeStorageSync('user_info');
// 跳转到 loading 页重新授权(会获取 temp_token 后跳注册页)
const deviceId = this.deviceId || '';
const loadingUrl = deviceId
? `/pages/auth/loading?device_id=${deviceId}`
: '/pages/auth/loading';
uni.redirectTo({ url: loadingUrl });
} else {
console.warn('[index] fetchUserInfo failed:', data.message);
}
} catch (e) {
console.error('[index] fetchUserInfo error:', e);
}
},
async fetchProducts() { async fetchProducts() {
this.loading = true; this.loading = true;
@@ -405,19 +471,6 @@ page { height: 100%; background-color: #fff; }
/* ====== 首页样式 ====== */ /* ====== 首页样式 ====== */
.home-page { display: flex; flex-direction: column; height: 100%; } .home-page { display: flex; flex-direction: column; height: 100%; }
/* 顶部标题栏 */
.page-header {
padding: 24rpx 32rpx;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.page-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.product-area { flex: 1; overflow-y: auto; height: 0; } .product-area { flex: 1; overflow-y: auto; height: 0; }
/* 热销商品标题 + 装饰 */ /* 热销商品标题 + 装饰 */
+30 -23
View File
@@ -69,6 +69,7 @@
<script> <script>
import { gatewayPost } from '@/utils/request.js'; import { gatewayPost } from '@/utils/request.js';
import config from '@/config/env.js';
export default { export default {
data() { data() {
@@ -153,7 +154,7 @@ export default {
}, 1000); }, 1000);
}, },
// 注册 // 注册Mock 模式也走真实接口)
async handleRegister() { async handleRegister() {
if (!this.canSubmit) return; if (!this.canSubmit) return;
@@ -183,16 +184,21 @@ export default {
const ret = res.data || {}; const ret = res.data || {};
if (ret.code === 0 && ret.data) { if (ret.code === 0 && ret.data) {
// 合并微信授权信息(头像、昵称)到 user_info // 保存 token
const authInfo = uni.getStorageSync('auth_info') || {};
const userInfo = {
...authInfo,
...ret.data.user_info,
};
// 保存 token 和用户信息
uni.setStorageSync('token', ret.data.token); uni.setStorageSync('token', ret.data.token);
uni.setStorageSync('user_info', userInfo);
// Mock 模式下,合并真实注册信息和 mock 配置
if (config.mockWechatLogin) {
const mockInfo = config.mockUser.user_info;
const userInfo = {
user_id: ret.data.user_id || mockInfo.user_id,
phone: this.phone, // 使用真实注册的手机号
nickname: mockInfo.nickname,
avatar: mockInfo.avatar,
};
uni.setStorageSync('user_info', userInfo);
console.log('[register] Mock 模式,合并用户信息:', userInfo);
}
// 清除临时数据 // 清除临时数据
uni.removeStorageSync('temp_token'); uni.removeStorageSync('temp_token');
@@ -200,19 +206,7 @@ export default {
uni.hideLoading(); uni.hideLoading();
uni.showToast({ title: '注册成功', icon: 'success' }); uni.showToast({ title: '注册成功', icon: 'success' });
setTimeout(() => this.navigateToTarget(), 1500);
// 跳转到扫码时的页面(优先使用缓存的扫码URL)
setTimeout(() => {
const scanRedirectUrl = uni.getStorageSync('__scan_redirect_url');
uni.removeStorageSync('__scan_redirect_url'); // 使用后立即清除
const url = scanRedirectUrl
? scanRedirectUrl
: this.deviceId
? `/pages/index/index?device_id=${this.deviceId}`
: '/pages/index/index';
uni.redirectTo({ url });
}, 1500);
} else { } else {
uni.hideLoading(); uni.hideLoading();
uni.showToast({ title: ret.message || '注册失败', icon: 'none' }); uni.showToast({ title: ret.message || '注册失败', icon: 'none' });
@@ -223,6 +217,19 @@ export default {
} }
}, },
// 跳转到目标页面
navigateToTarget() {
const scanRedirectUrl = uni.getStorageSync('__scan_redirect_url');
uni.removeStorageSync('__scan_redirect_url');
const url = scanRedirectUrl
? scanRedirectUrl
: this.deviceId
? `/pages/index/index?device_id=${this.deviceId}`
: '/pages/index/index';
uni.redirectTo({ url });
},
showAgreement(type) { showAgreement(type) {
const title = type === 'user' ? '用户协议' : '隐私政策'; const title = type === 'user' ? '用户协议' : '隐私政策';
uni.showToast({ title: title + '开发中', icon: 'none' }); uni.showToast({ title: title + '开发中', icon: 'none' });