mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-10-26 20:57:11 +08:00
feat: 支持第三方账号登录
Just Auth(开箱即用的整合第三方登录的开源组件,脱离繁琐的第三方登录 SDK,让登录变得 So easy!)
This commit is contained in:
@@ -32,3 +32,11 @@ export function getUserInfo() {
|
||||
export function listRoute() {
|
||||
return axios.get<RouteRecordNormalized[]>(`${BASE_URL}/route`);
|
||||
}
|
||||
|
||||
export function socialAuth(source: string) {
|
||||
return axios.get<string>(`${BASE_URL}/${source}`);
|
||||
}
|
||||
|
||||
export function socialLogin(source: string, req: any) {
|
||||
return axios.post<LoginRes>(`${BASE_URL}/${source}`, req);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export const WHITE_LIST = [
|
||||
{ name: 'notFound', children: [] },
|
||||
{ name: 'login', children: [] },
|
||||
{ name: 'SocialCallback', children: [] },
|
||||
];
|
||||
|
||||
export const NOT_FOUND = {
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (to.name === 'login') {
|
||||
if (to.name === 'login' || to.name === 'SocialCallback') {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,14 @@ const router = createRouter({
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/social/callback',
|
||||
name: 'SocialCallback',
|
||||
component: () => import('@/views/login/social/index.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
...appRoutes,
|
||||
...fixedRoutes,
|
||||
...demoRoutes,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import {
|
||||
login as userLogin,
|
||||
socialLogin as userSocialLogin,
|
||||
logout as userLogout,
|
||||
getUserInfo,
|
||||
LoginReq,
|
||||
@@ -52,6 +53,17 @@ const useLoginStore = defineStore('user', {
|
||||
}
|
||||
},
|
||||
|
||||
// 社交身份登录
|
||||
async socialLogin(source: string, req: any) {
|
||||
try {
|
||||
const res = await userSocialLogin(source, req);
|
||||
setToken(res.data.token);
|
||||
} catch (err) {
|
||||
clearToken();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
// 用户退出
|
||||
async logout() {
|
||||
try {
|
||||
|
||||
@@ -8,7 +8,14 @@ export default function getAvatar(
|
||||
) {
|
||||
if (avatar) {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL;
|
||||
return `${baseUrl}/avatar/${avatar}`;
|
||||
if (
|
||||
!avatar.startsWith('http://') &&
|
||||
!avatar.startsWith('https://') &&
|
||||
!avatar.startsWith('blob:')
|
||||
) {
|
||||
return `${baseUrl}/avatar/${avatar}`;
|
||||
}
|
||||
return avatar;
|
||||
}
|
||||
|
||||
if (gender === 1) {
|
||||
|
||||
@@ -11,21 +11,15 @@
|
||||
<div class="container">
|
||||
<div class="left-banner"></div>
|
||||
<div class="login-card">
|
||||
<div class="title"
|
||||
>{{ $t('login.welcome') }} {{ appStore.getTitle }}</div
|
||||
>
|
||||
<div class="title">
|
||||
{{ $t('login.welcome') }} {{ appStore.getTitle }}
|
||||
</div>
|
||||
<EmailLogin v-if="isEmailLogin" />
|
||||
<a-tabs v-else class="account-tab" default-active-key="1">
|
||||
<a-tab-pane
|
||||
key="1"
|
||||
:title="$t('login.account')"
|
||||
>
|
||||
<a-tab-pane key="1" :title="$t('login.account')">
|
||||
<AccountLogin />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane
|
||||
key="2"
|
||||
:title="$t('login.phone')"
|
||||
>
|
||||
<a-tab-pane key="2" :title="$t('login.phone')">
|
||||
<PhoneLogin />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
@@ -40,8 +34,8 @@
|
||||
<div v-else class="account app" @click="toggleLoginMode">
|
||||
<icon-user /> {{ $t('login.account.txt') }}
|
||||
</div>
|
||||
<a-tooltip content="Gitee(即将开放)" mini>
|
||||
<a href="javascript: void(0);" class="app">
|
||||
<a-tooltip content="Gitee" mini>
|
||||
<a-link class="app" @click="handleSocialAuth('gitee')">
|
||||
<svg
|
||||
class="icon"
|
||||
fill="#C71D23"
|
||||
@@ -53,10 +47,10 @@
|
||||
d="M11.984 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.016 0zm6.09 5.333c.328 0 .593.266.592.593v1.482a.594.594 0 0 1-.593.592H9.777c-.982 0-1.778.796-1.778 1.778v5.63c0 .327.266.592.593.592h5.63c.982 0 1.778-.796 1.778-1.778v-.296a.593.593 0 0 0-.592-.593h-4.15a.592.592 0 0 1-.592-.592v-1.482a.593.593 0 0 1 .593-.592h6.815c.327 0 .593.265.593.592v3.408a4 4 0 0 1-4 4H5.926a.593.593 0 0 1-.593-.593V9.778a4.444 4.444 0 0 1 4.445-4.444h8.296Z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</a-link>
|
||||
</a-tooltip>
|
||||
<a-tooltip content="GitHub(即将开放)" mini>
|
||||
<a href="javascript: void(0);" class="app">
|
||||
<a-tooltip content="GitHub" mini>
|
||||
<a-link class="app" @click="handleSocialAuth('github')">
|
||||
<svg
|
||||
class="icon"
|
||||
role="img"
|
||||
@@ -67,7 +61,7 @@
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</a-link>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,6 +81,7 @@
|
||||
import { useAppStore } from '@/store';
|
||||
import getFile from '@/utils/file';
|
||||
import useResponsive from '@/hooks/responsive';
|
||||
import { socialAuth } from '@/api/auth/login';
|
||||
import AccountLogin from './components/account-login.vue';
|
||||
import PhoneLogin from './components/phone-login.vue';
|
||||
import EmailLogin from './components/email-login.vue';
|
||||
@@ -96,6 +91,16 @@
|
||||
useResponsive(true);
|
||||
const isEmailLogin = ref(false);
|
||||
|
||||
/**
|
||||
* 第三方登录授权
|
||||
*
|
||||
* @param source 来源
|
||||
*/
|
||||
const handleSocialAuth = async (source: string) => {
|
||||
const { data } = await socialAuth(source);
|
||||
window.location.href = data;
|
||||
};
|
||||
|
||||
const toggleLoginMode = () => {
|
||||
isEmailLogin.value = !isEmailLogin.value;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ export default {
|
||||
'login.phone': 'Phone Login',
|
||||
'login.email': 'Email Login',
|
||||
'login.other': 'Other Login',
|
||||
'login.ing': 'Login...',
|
||||
|
||||
'login.account.placeholder.username': 'Please enter username',
|
||||
'login.account.placeholder.password': 'Please enter password',
|
||||
|
||||
@@ -4,6 +4,7 @@ export default {
|
||||
'login.phone': '手机号登录',
|
||||
'login.email': '邮箱登录',
|
||||
'login.other': '其他登录方式',
|
||||
'login.ing': '登录中...',
|
||||
|
||||
'login.account.placeholder.username': '请输入用户名',
|
||||
'login.account.placeholder.password': '请输入密码',
|
||||
|
||||
67
continew-admin-ui/src/views/login/social/index.vue
Normal file
67
continew-admin-ui/src/views/login/social/index.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<a-spin :loading="loading" :tip="$t('login.ing')">
|
||||
<div></div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useLoginStore } from '@/store';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loginStore = useLoginStore();
|
||||
const loading = ref(false);
|
||||
const source = route.query.source as string;
|
||||
|
||||
/**
|
||||
* 社会化身份登录
|
||||
*/
|
||||
const handleSocialLogin = () => {
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
||||
loginStore
|
||||
.socialLogin(source, othersQuery)
|
||||
.then(() => {
|
||||
router.push({
|
||||
name: (redirect as string) || 'Workplace',
|
||||
});
|
||||
proxy.$notification.success(t('login.success'));
|
||||
})
|
||||
.catch(() => {
|
||||
router.push({
|
||||
name: 'login',
|
||||
query: {
|
||||
...othersQuery,
|
||||
},
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
handleSocialLogin();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'SocialCallback',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
div {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 45%;
|
||||
margin-left: -50px;
|
||||
margin-top: -50px;
|
||||
}
|
||||
</style>
|
||||
@@ -147,7 +147,7 @@
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="ID" data-index="id" />
|
||||
<a-table-column title="用户名" :width="115">
|
||||
<a-table-column title="用户名" :width="120" ellipsis tooltip>
|
||||
<template #cell="{ record }">
|
||||
<a-link @click="toDetail(record.id)">{{
|
||||
record.username
|
||||
|
||||
Reference in New Issue
Block a user