2024-09-13 16:34:15 +08:00
|
|
|
<template>
|
2024-10-31 08:57:31 +08:00
|
|
|
<div class="login-container">
|
|
|
|
|
<div class="login-card">
|
|
|
|
|
<img src="@/assets/logo.png" alt="logo" class="login-logo" />
|
|
|
|
|
|
|
|
|
|
<el-form label-position="top" class="login-form">
|
|
|
|
|
<el-form-item :label="T('Username')">
|
|
|
|
|
<el-input v-model="form.username" class="login-input"></el-input>
|
2024-09-13 16:34:15 +08:00
|
|
|
</el-form-item>
|
2024-10-31 08:57:31 +08:00
|
|
|
|
|
|
|
|
<el-form-item :label="T('Password')">
|
|
|
|
|
<el-input v-model="form.password" type="password" @keyup.enter.native="login" show-password
|
|
|
|
|
class="login-input"></el-input>
|
2024-09-13 16:34:15 +08:00
|
|
|
</el-form-item>
|
2024-10-31 08:57:31 +08:00
|
|
|
|
2024-09-13 16:34:15 +08:00
|
|
|
<el-form-item>
|
2024-10-31 08:57:31 +08:00
|
|
|
<el-button @click="login" type="primary" class="login-button">{{ T('Login') }}</el-button>
|
2024-09-13 16:34:15 +08:00
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
2024-10-31 08:57:31 +08:00
|
|
|
|
|
|
|
|
<div class="divider" v-if="options.length > 0">
|
|
|
|
|
<span>{{ T('or login in with') }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="oidc-options">
|
|
|
|
|
<div v-for="(option, index) in options" :key="index" class="oidc-option">
|
|
|
|
|
<el-button @click="handleOIDCLogin(option.name)" class="oidc-btn">
|
|
|
|
|
<img :src="getProviderImage(option.name)" alt="provider" class="oidc-icon" />
|
|
|
|
|
{{ T(option.name) }}
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-09-13 16:34:15 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
2024-09-25 22:24:16 +08:00
|
|
|
<script setup>
|
2024-10-31 08:57:31 +08:00
|
|
|
import { reactive, onMounted, ref } from 'vue';
|
|
|
|
|
import { useUserStore } from '@/store/user'
|
|
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
|
|
import { T } from '@/utils/i18n';
|
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
import { loginOptions, oidcAuth, oidcQuery } from '@/api/login';
|
|
|
|
|
import { getCode, removeCode } from '@/utils/auth'
|
|
|
|
|
|
|
|
|
|
const oauthInfo = ref({})
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
const options = reactive([]); // 存储 OIDC 登录选项
|
|
|
|
|
|
|
|
|
|
let platform = window.navigator.platform
|
|
|
|
|
if (navigator.platform.indexOf('Mac') === 0) {
|
|
|
|
|
platform = 'mac'
|
|
|
|
|
} else if (navigator.platform.indexOf('Win') === 0) {
|
|
|
|
|
platform = 'windows'
|
|
|
|
|
} else if (navigator.platform.indexOf('Linux armv') === 0) {
|
|
|
|
|
platform = 'android'
|
|
|
|
|
} else if (navigator.platform.indexOf('Linux') === 0) {
|
|
|
|
|
platform = 'linux'
|
|
|
|
|
}
|
|
|
|
|
const userAgent = navigator.userAgent;
|
|
|
|
|
let browser = 'Unknown Browser';
|
|
|
|
|
if (/chrome|crios/i.test(userAgent)) browser = 'Chrome';
|
|
|
|
|
else if (/firefox|fxios/i.test(userAgent)) browser = 'Firefox';
|
|
|
|
|
else if (/safari/i.test(userAgent) && !/chrome/i.test(userAgent)) browser = 'Safari';
|
|
|
|
|
else if (/edg/i.test(userAgent)) browser = 'Edge';
|
|
|
|
|
|
|
|
|
|
const form = reactive({
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
platform: platform,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const redirect = route.query?.redirect
|
|
|
|
|
const login = async () => {
|
|
|
|
|
const res = await userStore.login(form)
|
|
|
|
|
if (res) {
|
|
|
|
|
ElMessage.success(T('LoginSuccess'))
|
|
|
|
|
router.push({ path: redirect || '/', replace: true })
|
2024-09-25 22:24:16 +08:00
|
|
|
}
|
2024-10-31 08:57:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleOIDCLogin = (provider) => {
|
|
|
|
|
userStore.oidc(provider, platform, browser)
|
|
|
|
|
};
|
2024-09-19 10:46:07 +08:00
|
|
|
|
2024-10-31 09:14:32 +08:00
|
|
|
import googleImage from '@/assets/google.png';
|
|
|
|
|
import githubImage from '@/assets/github.png';
|
|
|
|
|
import oidcImage from '@/assets/oidc.png';
|
|
|
|
|
import webauthImage from '@/assets/webauth.png';
|
|
|
|
|
import defaultImage from '@/assets/oidc.png';
|
|
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
const providerImageMap = {
|
2024-10-31 09:14:32 +08:00
|
|
|
google: googleImage,
|
|
|
|
|
github: githubImage,
|
|
|
|
|
oidc: oidcImage,
|
|
|
|
|
webauth: webauthImage,
|
|
|
|
|
default: defaultImage,
|
2024-10-31 08:57:31 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getProviderImage = (provider) => {
|
|
|
|
|
return providerImageMap[provider] || providerImageMap.default;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const loadLoginOptions = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const res = await loginOptions().catch(() => []);
|
|
|
|
|
if (!Array.isArray(res) || !res.length) return console.warn('No valid response received');
|
|
|
|
|
|
|
|
|
|
const jsonPart = res[0].split('/')[1];
|
|
|
|
|
if (!jsonPart) return console.error('Invalid input string:', res[0]);
|
|
|
|
|
|
|
|
|
|
// const ops = JSON.parse(jsonPart).map(option => ({ name: option.name }));
|
|
|
|
|
// 不确定怎么处理webauth,不显示
|
|
|
|
|
// 解析 JSON,并过滤掉 "webauth" 类型的选项
|
|
|
|
|
const ops = JSON.parse(jsonPart)
|
|
|
|
|
.filter(option => option.name !== "webauth") // 排除 "webauth" 类型的选项
|
|
|
|
|
.map(option => ({ name: option.name })); // 创建新的对象数组
|
|
|
|
|
if (!ops.length) return;
|
|
|
|
|
|
|
|
|
|
options.push(...ops);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading login options:', error.message);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
const code = getCode();
|
|
|
|
|
if (code) {
|
|
|
|
|
// 如果code存在,进行query获取user info
|
|
|
|
|
const res = await userStore.query(code)
|
2024-09-25 22:24:16 +08:00
|
|
|
if (res) {
|
2024-10-31 08:57:31 +08:00
|
|
|
// 删除code,确保跳转之前对code进行清楚
|
|
|
|
|
removeCode()
|
2024-09-25 22:24:16 +08:00
|
|
|
ElMessage.success(T('LoginSuccess'))
|
|
|
|
|
router.push({ path: redirect || '/', replace: true })
|
|
|
|
|
}
|
2024-10-31 08:57:31 +08:00
|
|
|
} else {
|
|
|
|
|
// 如果code不存在, 现实登陆页面
|
|
|
|
|
loadLoginOptions(); // 组件挂载后调用登录选项加载函数
|
2024-09-25 22:24:16 +08:00
|
|
|
}
|
2024-10-31 08:57:31 +08:00
|
|
|
});
|
2024-09-13 16:34:15 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
2024-10-31 08:57:31 +08:00
|
|
|
.login-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
2024-09-19 10:46:07 +08:00
|
|
|
height: 100vh;
|
|
|
|
|
background-color: #2d3a4b;
|
2024-10-31 08:57:31 +08:00
|
|
|
padding: 20px;
|
|
|
|
|
}
|
2024-09-13 16:34:15 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
.login-card {
|
|
|
|
|
width: 360px;
|
|
|
|
|
background-color: #283342;
|
|
|
|
|
padding: 40px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h1 {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-form {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-button {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 40px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.divider {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin: 20px 0;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: #888;
|
|
|
|
|
|
|
|
|
|
&::before,
|
|
|
|
|
&::after {
|
|
|
|
|
content: '';
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 1px;
|
|
|
|
|
background-color: #ddd;
|
2024-09-19 10:46:07 +08:00
|
|
|
}
|
2024-09-13 16:34:15 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
&::before {
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
}
|
2024-09-19 10:46:07 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
&::after {
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oidc-options {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
2024-09-19 10:46:07 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
.oidc-btn {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 50px;
|
|
|
|
|
background-color: white;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: black;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
2024-09-13 16:34:15 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
.oidc-icon {
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 24px;
|
|
|
|
|
}
|
2024-09-13 16:34:15 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
.login-logo {
|
|
|
|
|
width: 80px;
|
|
|
|
|
height: 80px;
|
|
|
|
|
margin: 0 auto 20px;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-form-item {
|
|
|
|
|
::v-deep(.el-form-item__label) {
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-input {
|
|
|
|
|
::v-deep(.el-input__wrapper) {
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
2024-09-13 16:34:15 +08:00
|
|
|
|
2024-10-31 08:57:31 +08:00
|
|
|
::v-deep(input) {
|
|
|
|
|
color: #fff;
|
2024-09-13 16:34:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
2024-09-19 10:46:07 +08:00
|
|
|
}
|
2024-10-31 08:57:31 +08:00
|
|
|
</style>
|