You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/ui/page/login.html

437 lines
11 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>登录与注册</title>
<meta name="description" content="用户登录与注册页面" />
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Montserrat', sans-serif;
height: 100vh;
overflow: hidden;
background: #ffebee;
/* 红色主题背景 */
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.background {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
overflow: hidden;
background: linear-gradient(to right, #ef5350, #e53935);
/* 红色渐变 */
}
.bubble {
position: absolute;
bottom: -150px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
animation: rise linear infinite;
}
@keyframes rise {
0% {
transform: translateY(0) rotate(0);
opacity: 0.5;
}
100% {
transform: translateY(-120vh) rotate(360deg);
opacity: 0;
}
}
h1 {
font-weight: bold;
margin: 0;
margin-bottom: 15px;
}
p {
font-size: 14px;
font-weight: 100;
line-height: 20px;
letter-spacing: 0.5px;
margin: 20px 0 30px;
}
span {
font-size: 12px;
margin: 15px 0;
}
a {
color: #c62828;
/* 红色链接 */
font-size: 14px;
text-decoration: none;
margin: 15px 0;
}
button {
border-radius: 20px;
border: 1px solid #e53935;
/* 红色边框 */
background-color: #e53935;
/* 红色背景 */
color: #ffffff;
font-size: 12px;
font-weight: bold;
padding: 12px 45px;
letter-spacing: 1px;
text-transform: uppercase;
transition: transform 80ms ease-in;
cursor: pointer;
}
button:active {
transform: scale(0.95);
}
button:focus {
outline: none;
}
button.ghost {
background-color: transparent;
border-color: #ffffff;
}
form {
background-color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 50px;
height: 100%;
text-align: center;
}
input {
background-color: #eee;
border: none;
padding: 12px 15px;
margin: 8px 0;
width: 100%;
border-radius: 5px;
}
.container {
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
position: relative;
overflow: hidden;
width: 768px;
max-width: 100%;
min-height: 600px;
}
.form-container {
position: absolute;
top: 0;
height: 100%;
transition: all 0.6s ease-in-out;
}
.sign-in-container {
left: 0;
width: 50%;
z-index: 2;
}
.sign-up-container {
left: 0;
width: 50%;
opacity: 0;
z-index: 1;
}
.container.right-panel-active .sign-in-container {
transform: translateX(100%);
}
.container.right-panel-active .sign-up-container {
transform: translateX(100%);
opacity: 1;
z-index: 5;
}
.overlay-container {
position: absolute;
top: 0;
left: 50%;
width: 50%;
height: 100%;
overflow: hidden;
transition: transform 0.6s ease-in-out;
z-index: 100;
}
.container.right-panel-active .overlay-container {
transform: translateX(-100%);
}
.overlay {
background: #e53935;
/* 红色背景 */
background: linear-gradient(to right, #ef5350, #e53935);
/* 红色渐变 */
background-repeat: no-repeat;
background-size: cover;
background-position: 0 0;
color: #ffffff;
position: relative;
left: -100%;
height: 100%;
width: 200%;
transform: translateX(0);
transition: transform 0.6s ease-in-out;
}
.container.right-panel-active .overlay {
transform: translateX(50%);
}
.overlay-panel {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 40px;
text-align: center;
top: 0;
height: 100%;
width: 50%;
transform: translateX(0);
transition: transform 0.6s ease-in-out;
}
.overlay-left {
transform: translateX(-20%);
}
.container.right-panel-active .overlay-left {
transform: translateX(0);
}
.overlay-right {
right: 0;
transform: translateX(0);
}
.container.right-panel-active .overlay-right {
transform: translateX(20%);
}
.social-container {
margin: 20px 0;
}
.social-container a {
border: 1px solid #dddddd;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
margin: 0 5px;
height: 40px;
width: 40px;
transition: all 0.3s;
}
.social-container a:hover {
background-color: #f2f2f2;
}
.error-message {
color: red;
font-size: 12px;
margin-top: 8px;
}
</style>
</head>
<body layout='public'>
<div class="background" id="background">
<div v-for="(bubble, index) in bubbles" :key="index" class="bubble" :style="bubble.style"></div>
</div>
<div class="container" :class="{ 'right-panel-active': isSignUp }" id="container">
<div class="form-container sign-up-container">
<form @submit="handleSignUp">
<h1>创建账户</h1>
<div class="social-container">
<a href="#" @click="handleSocialLogin('github')"><i class="fa-brands fa-github"></i></a>
<a href="#" @click="handleSocialLogin('weixin')"><i class="fa-brands fa-weixin"></i></a>
<a href="#" @click="handleSocialLogin('qq')"><i class="fa-brands fa-qq"></i></a>
</div>
<span>或使用您的用户名进行注册</span>
<input type="text" placeholder="用户名" v:value="signUpForm.username" />
<input type="password" placeholder="密码" v:value="signUpForm.password" />
<div class="error-message">{{ signUpError }}</div>
<button>注册</button>
</form>
</div>
<div class="form-container sign-in-container">
<form @submit="handleSignIn">
<h1>登录</h1>
<div class="social-container">
<a href="#" @click="handleSocialLogin('github')"><i class="fa-brands fa-github"></i></a>
<a href="#" @click="handleSocialLogin('weixin')"><i class="fa-brands fa-weixin"></i></a>
<a href="#" @click="handleSocialLogin('qq')"><i class="fa-brands fa-qq"></i></a>
</div>
<span>或使用您的账户</span>
<input type="text" placeholder="用户名" v:value="signInForm.username" />
<input type="password" placeholder="密码" v:value="signInForm.password" />
<a href="#">忘记密码?</a>
<button>登录</button>
</form>
</div>
<div class="overlay-container">
<div class="overlay">
<div class="overlay-panel overlay-left">
<h1>欢迎回来!</h1>
<p>请使用您的个人信息登录,保持连接。</p>
<button class="ghost" @click="switchToSignIn">登录</button>
</div>
<div class="overlay-panel overlay-right">
<h1>你好,朋友!</h1>
<p>输入您的个人信息,开始您的旅程。</p>
<button class="ghost" @click="switchToSignUp">注册</button>
</div>
</div>
</div>
</div>
<script setup>
// 响应式数据
logout = $router.query.logout
redirect = $router.query.redirect || '/'
isSignUp = false;
signUpForm = {username: '', password: ''};
signInForm = {username: '', password: ''};
bubbles = [];
signUpError = '';
// 验证密码是否符合要求
const validatePassword = (password) => {
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[_]).{9,}$/;
return regex.test(password);
};
// 切换到注册页面
switchToSignUp = () => {
isSignUp = true;
};
// 切换到登录页面
switchToSignIn = () => {
isSignUp = false;
};
// 处理第三方登录
handleSocialLogin = (provider) => {
console.log(`尝试使用 ${provider} 登录`);
alert(`已启动 ${provider} 登录!`);
};
function deriveKey(password, salt) {
return CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32, iterations:
100, hasher: CryptoJS.algo.SHA256
})
}
// 处理注册表单提交
handleSignUp = async (e) => {
e.preventDefault();
signUpError = '';
if (!validatePassword(signUpForm.password) && false) {
signUpError = '密码必须大于8位且包含大小写字母、下划线和数字。';
return;
}
if (signUpForm.username.length < 2) {
signUpError = '用户名必须大于2位。';
return;
}
signUpError = '';
try {
const response = await $axios.post('/api/user', {
username: signUpForm.username,
code: btoa(signUpForm.password),
});
if (response) {
alert('注册成功!');
switchToSignIn();
}
} catch (error) {
signUpError = error.message || '注册失败,请重试。';
console.error(signUpError);
}
};
// 处理登录表单提交
handleSignIn = async (e) => {
e.preventDefault();
try {
const loginResponse = await $axios.post('/api/user/login', {
username: signInForm.username,
code: btoa(signInForm.password),
});
if (loginResponse) {
localStorage.setItem('refresh', loginResponse)
window.location.href = redirect
}
} catch (error) {
alert(error.message || '登录失败,请检查您的凭据。');
}
};
// 创建动态背景气泡
createBubbles = () => {
const numberOfBubbles = 20;
for (let i = 0; i < numberOfBubbles; i++) {
const size = Math.random() * 100 + 50;
const left = Math.random() * 100;
const delay = Math.random() * 15;
const duration = Math.random() * 15 + 10;
bubbles.push({
style: {
width: `${size}px`,
height: `${size}px`,
left: `${left}%`,
animationDelay: `${delay}s`,
animationDuration: `${duration}s`,
},
});
}
};
</script>
<script>
// 页面加载时创建气泡
createBubbles();
</script>
</body>
</html>