refactor: 重构oauth相关代码 添加新的默认oauth登录方式

This commit is contained in:
dap
2025-03-23 01:23:30 +08:00
parent db955071d7
commit 6c942418b4
10 changed files with 130 additions and 102 deletions

View File

@@ -6,6 +6,7 @@ import type { TenantResp } from '#/api';
import { computed, onMounted, ref, useTemplateRef } from 'vue';
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
import { DEFAULT_TENANT_ID } from '@vben/constants';
import { $t } from '@vben/locales';
import { Alert, message } from 'ant-design-vue';
@@ -50,7 +51,7 @@ const formSchema = computed((): VbenFormSchema[] => {
})),
placeholder: $t('authentication.selectAccount'),
},
defaultValue: '000000',
defaultValue: DEFAULT_TENANT_ID,
dependencies: {
if: () => tenantInfo.value.tenantEnabled,
triggerFields: [''],

View File

@@ -7,6 +7,7 @@ import type { CaptchaResponse } from '#/api/core/captcha';
import { computed, onMounted, ref, useTemplateRef } from 'vue';
import { AuthenticationLogin, z } from '@vben/common-ui';
import { DEFAULT_TENANT_ID } from '@vben/constants';
import { $t } from '@vben/locales';
import { omit } from 'lodash-es';
@@ -15,6 +16,7 @@ import { tenantList } from '#/api';
import { captchaImage } from '#/api/core/captcha';
import { useAuthStore } from '#/store';
import { useLoginTenantId } from '../oauth-common';
import OAuthLogin from './oauth-login.vue';
defineOptions({ name: 'Login' });
@@ -56,6 +58,8 @@ onMounted(async () => {
await Promise.all([loadCaptcha(), loadTenant()]);
});
const { loginTenantId } = useLoginTenantId();
const formSchema = computed((): VbenFormSchema[] => {
return [
{
@@ -69,16 +73,13 @@ const formSchema = computed((): VbenFormSchema[] => {
})),
placeholder: $t('authentication.selectAccount'),
},
defaultValue: '000000',
defaultValue: DEFAULT_TENANT_ID,
dependencies: {
if: () => tenantInfo.value.tenantEnabled,
// 这里大致上是watch的一个效果
componentProps: (model) => {
localStorage.setItem(
'__oauth_tenant_id',
model?.tenantId ?? '000000',
);
return {};
// 可以把这里当做watch
trigger: (model) => {
// 给oauth登录使用
loginTenantId.value = model?.tenantId ?? DEFAULT_TENANT_ID;
},
triggerFields: ['', 'tenantId'],
},

View File

@@ -3,21 +3,16 @@ import { $t } from '@vben/locales';
import { Col, Row, Tooltip } from 'ant-design-vue';
import { accountBindList } from '../oauth-common';
import { accountBindList, handleAuthBinding } from '../oauth-common';
defineOptions({
name: 'OAuthLogin',
});
/**
* 有action方法才会显示
*/
const clientList = accountBindList.filter((item) => item.action);
</script>
<template>
<div class="w-full sm:mx-auto md:max-w-md">
<div class="mt-4 flex items-center justify-between">
<div class="my-4 flex items-center justify-between">
<span class="border-input w-[35%] border-b dark:border-gray-600"></span>
<span class="text-muted-foreground text-center text-xs uppercase">
{{ $t('authentication.thirdPartyLogin') }}
@@ -26,15 +21,20 @@ const clientList = accountBindList.filter((item) => item.action);
</div>
<Row class="enter-x flex items-center justify-evenly">
<!-- todo 这里在点击登录时要disabled -->
<Col v-for="item in clientList" :key="item.key" :span="4" class="my-2">
<Col
v-for="item in accountBindList"
:key="item.source"
:span="4"
class="my-2"
>
<Tooltip :title="`${item.title}登录`">
<span class="flex cursor-pointer items-center justify-center">
<component
:is="item.avatar"
v-if="item.avatar"
:style="{ color: item.color }"
:is="item.avatar"
:style="item?.style ?? {}"
class="size-[24px]"
@click="item.action"
@click="handleAuthBinding(item.source)"
/>
</span>
</Tooltip>

View File

@@ -1,104 +1,103 @@
import type { Component } from 'vue';
import type { Component, CSSProperties } from 'vue';
import { ref } from 'vue';
import { DEFAULT_TENANT_ID } from '@vben/constants';
import {
AlipayIcon,
DingdingIcon,
GiteeIcon,
GithubOAuthIcon,
TaobaoIcon,
SvgMaxKeyIcon,
SvgTopiamIcon,
SvgWechatIcon,
} from '@vben/icons';
import { createGlobalState } from '@vueuse/core';
import { authBinding } from '#/api/core/auth';
/**
* @description: 菜单
* @param key key
* @description: oauth登录
* @param title 标题
* @param description 描述
* @param extra 按钮文字
* @param avatar 图标
* @param color 图标颜色可直接写英文颜色/hex
*/
export interface ListItem {
key: string;
title: string;
description: string;
extra?: string;
avatar?: Component;
color?: string;
style?: CSSProperties;
}
/**
* @description: 绑定账号
* @param source 来源 如gitee github 与后端的social-callback?source=xxx对应
* @param bound 是否已经绑定
* @param action 账号绑定回调
*/
export interface BindItem extends ListItem {
source: string;
bound?: boolean;
action?: (source: string) => Promise<any>;
}
/**
* todo tenantId
* 绑定授权从userStore.userInfo获取
* 登录从localStorage获取
* 这里存储登录页的tenantId 由于个人中心也会用到 需要共享
* 所以使用`createGlobalState`
* @see https://vueuse.org/shared/createGlobalState/
*/
export const useLoginTenantId = createGlobalState(() => {
const loginTenantId = ref(DEFAULT_TENANT_ID);
return {
loginTenantId,
};
});
/**
* 绑定授权
* @param source
*/
async function handleAuthBinding(source: string) {
const tenantId = localStorage.getItem('__oauth_tenant_id') ?? '000000';
export async function handleAuthBinding(source: string) {
const { loginTenantId } = useLoginTenantId();
// 这里返回打开授权页面的链接
const href = await authBinding(source, tenantId);
const href = await authBinding(source, loginTenantId.value);
window.location.href = href;
}
/**
* 账号绑定 list
* 添加账号绑定只需要在这里增加即可
* 添加过的项目会在个人主页-绑定账号中显示
* action不为空的会在登录页显示
*/
export const accountBindList: BindItem[] = [
{
avatar: TaobaoIcon,
color: '#ff4000',
description: '绑定淘宝账号',
key: '1',
source: 'taobao',
title: '淘宝',
},
{
avatar: AlipayIcon,
color: '#2eabff',
description: '绑定支付宝账号',
key: '2',
source: 'alipay',
title: '支付宝',
},
{
avatar: DingdingIcon,
color: '#2eabff',
description: '绑定钉钉账号',
key: '3',
source: 'ding',
title: '钉钉',
},
{
action: () => handleAuthBinding('gitee'),
avatar: GiteeIcon,
color: '#c71d23',
description: '绑定GITEE账号',
key: '4',
description: '绑定Gitee账号',
source: 'gitee',
title: 'GITEE',
title: 'Gitee',
style: { color: '#c71d23' },
},
{
action: () => handleAuthBinding('github'),
avatar: GithubOAuthIcon,
color: '',
description: '绑定GITHUB账号',
key: '5',
description: '绑定Github账号',
source: 'github',
title: 'GITHUB',
title: 'Github',
},
{
avatar: SvgMaxKeyIcon,
description: '绑定MaxKey账号',
source: 'maxkey',
title: 'MaxKey',
style: { verticalAlign: 'middle' },
},
{
avatar: SvgTopiamIcon,
description: '绑定topiam账号',
source: 'topiam',
title: 'Topiam',
},
{
avatar: SvgWechatIcon,
description: '绑定wechat账号',
source: 'wechat',
title: 'Wechat',
},
];

View File

@@ -7,27 +7,12 @@ import { computed, ref, unref } from 'vue';
import { useVbenVxeGrid } from '@vben/plugins/vxe-table';
import {
Alert,
Avatar,
Card,
List,
ListItem,
message,
Modal,
} from 'ant-design-vue';
import { Alert, Avatar, Card, List, ListItem, Modal } from 'ant-design-vue';
import { authUnbinding } from '#/api';
import { socialList } from '#/api/system/social';
import { accountBindList } from '../../oauth-common';
/**
* 没有传递action事件则不支持绑定 弹出默认提示
*/
function defaultTip(title: string) {
message.info({ content: `暂不支持绑定${title}` });
}
import { accountBindList, handleAuthBinding } from '../../oauth-common';
function buttonText(item: BindItem) {
return item.bound ? '已绑定' : '绑定';
@@ -151,7 +136,7 @@ function handleUnbind(record: Record<string, any>) {
<component
:is="item.avatar"
v-if="item.avatar"
:style="{ color: item.color }"
:style="item?.style ?? {}"
class="size-[40px]"
/>
</div>
@@ -170,9 +155,7 @@ function handleUnbind(record: Record<string, any>) {
:disabled="item.bound"
size="small"
type="link"
@click="
item.action ? item.action() : defaultTip(item.title)
"
@click="handleAuthBinding(item.source)"
>
{{ buttonText(item) }}
</a-button>
@@ -191,10 +174,6 @@ function handleUnbind(record: Record<string, any>) {
</span>
中accountBindList按模板添加
</p>
<p>
添加对应模板后会在此处显示绑定, 但只有
<span class="font-bold">实现了action才能在登录页显示</span>
</p>
</template>
</Alert>
</div>

View File

@@ -4,7 +4,7 @@ import type { AuthApi } from '#/api';
import { onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { DEFAULT_HOME_PATH } from '@vben/constants';
import { DEFAULT_HOME_PATH, DEFAULT_TENANT_ID } from '@vben/constants';
import { useAccessStore } from '@vben/stores';
import { message } from 'ant-design-vue';
@@ -22,7 +22,7 @@ const stateJson = JSON.parse(atob(state));
// 来源
const source = route.query.source as string;
// 租户ID
const defaultTenantId = '000000';
const defaultTenantId = DEFAULT_TENANT_ID;
const tenantId = (stateJson.tenantId as string) ?? defaultTenantId;
const domain = stateJson.domain as string;
@@ -44,7 +44,7 @@ onMounted(async () => {
try {
// 已经实现的平台
const currentClient = accountBindList.find(
(item) => item.source === source && item.action,
(item) => item.source === source,
);
if (!currentClient) {
message.error({ content: `未找到${source}平台` });