hookehuyr

feat(wx): 启用微信分享API并优化OAuth流程

修复微信JSSDK签名URL包含hash导致的重复刷新问题
在OAuth流程中保留原始hash路由位置
重构路由守卫代码格式
1 <!-- 1 <!--
2 * @Date: 2025-03-20 19:53:12 2 * @Date: 2025-03-20 19:53:12
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-10-15 10:11:57 4 + * @LastEditTime: 2025-12-09 16:43:21
5 * @FilePath: /mlaj/src/App.vue 5 * @FilePath: /mlaj/src/App.vue
6 * @Description: 入口文件 6 * @Description: 入口文件
7 --> 7 -->
...@@ -27,22 +27,29 @@ import 'vant/es/toast/style' ...@@ -27,22 +27,29 @@ import 'vant/es/toast/style'
27 provideAuth(); // 提供全局认证状态 27 provideAuth(); // 提供全局认证状态
28 provideCart('single'); // 提供全局购物车状态,单一商品模式 28 provideCart('single'); // 提供全局购物车状态,单一商品模式
29 29
30 -// 初始化微信配置 30 +/**
31 + * @function initWxConfig
32 + * @description 初始化微信 JSSDK 配置;签名 URL 必须使用不包含 hash 的完整地址,避免进入详情页在微信环境出现重复刷新。
33 + * @returns {void}
34 + */
31 const initWxConfig = async () => { 35 const initWxConfig = async () => {
32 - const raw_url = encodeURIComponent(location.pathname + location.hash); 36 + // 使用不带 hash 的完整 URL 参与签名,规避重复刷新问题
33 - try { 37 + const sign_url = encodeURIComponent(window.location.href.split('#')[0]);
34 - const wxJs = await wxJsAPI({ url: raw_url }) 38 + try {
35 - wxJs.data.jsApiList = apiList 39 + const wxJs = await wxJsAPI({ url: sign_url });
36 - wx.config(wxJs.data) 40 + wxJs.data.jsApiList = apiList;
37 - wx.ready(() => { 41 + wx.config(wxJs.data);
38 - wx.showAllNonBaseMenuItem() 42 + wx.ready(() => {
39 - }) 43 + // 微信 JSSDK 初始化完成,展示所有非基础菜单项
40 - wx.error((err) => { 44 + wx.showAllNonBaseMenuItem();
41 - console.warn('微信配置初始化失败:', err) 45 + });
42 - }) 46 + wx.error((err) => {
43 - } catch (error) { 47 + // 微信 JSSDK 初始化失败日志
44 - console.error('初始化微信配置失败:', error) 48 + console.warn('微信配置初始化失败:', err);
45 - } 49 + });
50 + } catch (error) {
51 + console.error('初始化微信配置失败:', error);
52 + }
46 } 53 }
47 54
48 // 在非开发环境下初始化微信配置 55 // 在非开发环境下初始化微信配置
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
8 export const apiList = [ 8 export const apiList = [
9 - // "updateAppMessageShareData", 9 + "updateAppMessageShareData",
10 - // "updateTimelineShareData", 10 + "updateTimelineShareData",
11 "onMenuShareTimeline", 11 "onMenuShareTimeline",
12 "onMenuShareAppMessage", 12 "onMenuShareAppMessage",
13 "onMenuShareQQ", 13 "onMenuShareQQ",
......
...@@ -38,5 +38,25 @@ app.config.warnHandler = () => null; ...@@ -38,5 +38,25 @@ app.config.warnHandler = () => null;
38 38
39 app.config.globalProperties.$http = axios; // 关键语句 39 app.config.globalProperties.$http = axios; // 关键语句
40 app.component('font-awesome-icon', FontAwesomeIcon) 40 app.component('font-awesome-icon', FontAwesomeIcon)
41 +/**
42 + * @function restoreHashAfterOAuth
43 + * @description 前端复原 OAuth 回跳的 hash 路由位置:当 URL 中存在 ret_hash 参数且当前无 hash 时,将其拼回地址栏。
44 + * @returns {void}
45 + */
46 +function restoreHashAfterOAuth() {
47 + const url = new URL(window.location.href);
48 + const ret_hash = url.searchParams.get('ret_hash');
49 + if (ret_hash && !window.location.hash) {
50 + // 删除 ret_hash,保留其他查询参数
51 + url.searchParams.delete('ret_hash');
52 + const base = url.toString().split('#')[0];
53 + const new_url = base + ret_hash;
54 + // 使用 replaceState 避免再次刷新与历史记录污染
55 + window.history.replaceState(null, '', new_url);
56 + }
57 +}
58 +
59 +// 在安装路由前进行一次 hash 复原,确保初始路由正确
60 +restoreHashAfterOAuth()
41 app.use(router) 61 app.use(router)
42 app.mount('#app') 62 app.mount('#app')
......
1 /* 1 /*
2 * @Date: 2025-03-20 20:36:36 2 * @Date: 2025-03-20 20:36:36
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-04 11:08:58 4 + * @LastEditTime: 2025-12-09 16:47:55
5 * @FilePath: /mlaj/src/router/guards.js 5 * @FilePath: /mlaj/src/router/guards.js
6 * @Description: 路由守卫逻辑 6 * @Description: 路由守卫逻辑
7 */ 7 */
...@@ -10,26 +10,26 @@ import { wxInfo } from "@/utils/tools" ...@@ -10,26 +10,26 @@ import { wxInfo } from "@/utils/tools"
10 10
11 // 需要登录才能访问的路由 11 // 需要登录才能访问的路由
12 export const authRequiredRoutes = [ 12 export const authRequiredRoutes = [
13 - { 13 + {
14 - path: '/profile', 14 + path: '/profile',
15 - exact: false, 15 + exact: false,
16 - }, 16 + },
17 - { 17 + {
18 - path: '/checkout', 18 + path: '/checkout',
19 - exact: true, 19 + exact: true,
20 - }, 20 + },
21 - { 21 + {
22 - path: '/activities/[^/]+/signup', 22 + path: '/activities/[^/]+/signup',
23 - regex: true, 23 + regex: true,
24 - }, 24 + },
25 - { 25 + {
26 - path: '/checkin', 26 + path: '/checkin',
27 - exact: false, 27 + exact: false,
28 - }, 28 + },
29 - { 29 + {
30 - path: '/teacher', 30 + path: '/teacher',
31 - exact: false, 31 + exact: false,
32 - }, 32 + },
33 ] 33 ]
34 34
35 /** 35 /**
...@@ -54,6 +54,11 @@ export const checkWxAuth = async () => { ...@@ -54,6 +54,11 @@ export const checkWxAuth = async () => {
54 * @description 手动发起微信授权登录(仅在用户点击微信图标时触发) 54 * @description 手动发起微信授权登录(仅在用户点击微信图标时触发)
55 * @returns {void} 55 * @returns {void}
56 */ 56 */
57 +/**
58 + * @function startWxAuth
59 + * @description 手动发起微信授权登录;使用不含 hash 的完整 URL 作为回跳参数,避免微信浏览器对 hash 的处理差异导致回跳异常。
60 + * @returns {void}
61 + */
57 export const startWxAuth = async () => { 62 export const startWxAuth = async () => {
58 // 开发环境不触发微信授权 63 // 开发环境不触发微信授权
59 if (import.meta.env.DEV) { 64 if (import.meta.env.DEV) {
...@@ -77,9 +82,10 @@ export const startWxAuth = async () => { ...@@ -77,9 +82,10 @@ export const startWxAuth = async () => {
77 // 探测失败不影响授权流程,继续跳转 82 // 探测失败不影响授权流程,继续跳转
78 } 83 }
79 84
80 - // 跳转到微信授权地址 85 + // 跳转到微信授权地址(签名与回跳使用不含 hash 的完整 URL),并通过 ret_hash 参数保留授权前页面位置
81 - const raw_url = encodeURIComponent(location.href); 86 + const base_url = encodeURIComponent(window.location.href.split('#')[0]);
82 - const short_url = `/srv/?f=behalo&a=openid&res=${raw_url}`; 87 + const ret_hash = encodeURIComponent(window.location.hash || '');
88 + const short_url = `/srv/?f=behalo&a=openid&res=${base_url}&ret_hash=${ret_hash}`;
83 location.href = short_url; 89 location.href = short_url;
84 } 90 }
85 91
......