refactor: 重构oauth相关代码 添加新的默认oauth登录方式
This commit is contained in:
@@ -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: [''],
|
||||
|
@@ -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'],
|
||||
},
|
||||
|
@@ -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>
|
||||
|
@@ -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',
|
||||
},
|
||||
];
|
||||
|
@@ -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>
|
||||
|
@@ -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}平台` });
|
||||
|
Reference in New Issue
Block a user