diff --git a/apps/web-antd/src/router/guard.ts b/apps/web-antd/src/router/guard.ts
index 8b88c834e..588545278 100644
--- a/apps/web-antd/src/router/guard.ts
+++ b/apps/web-antd/src/router/guard.ts
@@ -49,12 +49,40 @@ function setupCommonGuard(router: Router) {
* @param router
*/
function setupAccessGuard(router: Router) {
+ // 一次性检查:hash 路由模式下,OAuth 回调的 code/state 在 URL query(?code=xxx)中,
+ // Vue Router 读不到,需要转存到 sessionStorage 供个人中心绑定页面使用
+ const pendingBind = sessionStorage.getItem('socialBindAction');
+ if (pendingBind === 'bind') {
+ const url = new URL(window.location.href);
+ const code = url.searchParams.get('code');
+ const state = url.searchParams.get('state');
+ if (code) {
+ sessionStorage.setItem('socialBindCode', code);
+ sessionStorage.setItem('socialBindState', state || '');
+ sessionStorage.removeItem('socialBindAction');
+ // 清理 URL 中的 OAuth 参数
+ url.searchParams.delete('code');
+ url.searchParams.delete('state');
+ url.searchParams.delete('appid');
+ window.history.replaceState({}, '', url.toString());
+ }
+ }
+
router.beforeEach(async (to, from) => {
const accessStore = useAccessStore();
const userStore = useUserStore();
const authStore = useAuthStore();
const dictStore = useDictStore();
+ // 社交绑定回调:检测到待处理的绑定参数,重定向到个人中心处理
+ if (
+ sessionStorage.getItem('socialBindCode') &&
+ accessStore.accessToken &&
+ to.path !== '/profile'
+ ) {
+ return '/profile';
+ }
+
// 基本路由,这些路由不需要进入权限拦截
if (coreRouteNames.includes(to.name as string)) {
if (to.path === LOGIN_PATH && accessStore.accessToken) {
diff --git a/apps/web-antd/src/views/_core/authentication/login.vue b/apps/web-antd/src/views/_core/authentication/login.vue
index 6cf0be002..e23292e61 100644
--- a/apps/web-antd/src/views/_core/authentication/login.vue
+++ b/apps/web-antd/src/views/_core/authentication/login.vue
@@ -241,22 +241,22 @@ const formSchema = computed((): VbenFormSchema[] => {
-
-
+ -->
+
diff --git a/apps/web-antd/src/views/_core/profile/modules/user-social.vue b/apps/web-antd/src/views/_core/profile/modules/user-social.vue
index e4927bf99..9f51a7c5f 100644
--- a/apps/web-antd/src/views/_core/profile/modules/user-social.vue
+++ b/apps/web-antd/src/views/_core/profile/modules/user-social.vue
@@ -2,13 +2,11 @@
import type { SystemSocialUserApi } from '#/api/system/social/user';
import { computed, onMounted, ref } from 'vue';
-import { useRoute } from 'vue-router';
-
import { confirm } from '@vben/common-ui';
import { DICT_TYPE, SystemUserSocialTypeEnum } from '@vben/constants';
import { getDictLabel } from '@vben/hooks';
import { $t } from '@vben/locales';
-import { formatDateTime, getUrlValue } from '@vben/utils';
+import { formatDateTime } from '@vben/utils';
import { Avatar, Button, Image, message, Tag, Tooltip } from 'ant-design-vue';
@@ -23,16 +21,11 @@ const emit = defineEmits<{
(e: 'update:activeName', v: string): void;
}>();
-const route = useRoute();
-
/** 已经绑定的平台 */
const bindList = ref([]);
-/** 暂不支持钉钉和企业微信,后续开放时移除此过滤 */
-const HIDDEN_SOCIAL_TYPES = new Set([
- SystemUserSocialTypeEnum.DINGTALK.type,
- SystemUserSocialTypeEnum.WECHAT_ENTERPRISE.type,
-]);
+/** 暂不支持钉钉,后续开放时移除此过滤 */
+const HIDDEN_SOCIAL_TYPES = new Set([SystemUserSocialTypeEnum.DINGTALK.type]);
interface SocialBindItem {
title: string;
@@ -47,7 +40,7 @@ const allBindList = computed(() => {
.filter((social) => !HIDDEN_SOCIAL_TYPES.has(social.type))
.map((social) => {
const socialUser = bindList.value.find(
- (item) => item.type === social.type,
+ (item) => Number(item.type) === social.type,
);
return {
...social,
@@ -80,32 +73,43 @@ async function onBind(bind: SocialBindItem) {
return;
}
try {
- const redirectUri = `${location.origin}/profile?${encodeURIComponent(`type=${type}`)}`;
+ // 标记绑定操作,redirect_uri 只用 origin(hash 路由模式下不能带 # 路径)
+ sessionStorage.setItem('socialBindType', String(type));
+ sessionStorage.setItem('socialBindAction', 'bind');
+ const redirectUri = location.origin;
window.location.href = await socialAuthRedirect(type, redirectUri);
} catch (error) {
console.error('社交绑定处理失败:', error);
}
}
-/** 监听路由变化,处理社交绑定回调 */
-async function bindSocial() {
- const type = Number(getUrlValue('type'));
- const code = route.query.code as string;
- const state = route.query.state as string;
- if (!code) {
+/** 处理社交绑定回调(参数由路由守卫从 URL query 转存到 sessionStorage) */
+async function processPendingBind() {
+ const code = sessionStorage.getItem('socialBindCode');
+ const state = sessionStorage.getItem('socialBindState');
+ const type = Number(sessionStorage.getItem('socialBindType'));
+ if (!code || !type) {
return;
}
- await socialBind({ type, code, state });
- message.success('绑定成功');
- emit('update:activeName', 'userSocial');
- await loadBindList();
- window.history.replaceState({}, '', location.pathname);
+ // 立即清理,防止重复处理
+ sessionStorage.removeItem('socialBindCode');
+ sessionStorage.removeItem('socialBindState');
+ sessionStorage.removeItem('socialBindType');
+ try {
+ await socialBind({ type, code, state: state || '' });
+ message.success('绑定成功');
+ emit('update:activeName', 'userSocial');
+ await loadBindList();
+ } catch (error) {
+ console.error('社交绑定失败:', error);
+ message.error('绑定失败,请重试');
+ }
}
/** 初始化 */
onMounted(async () => {
await loadBindList();
- await bindSocial();
+ await processPendingBind();
});