243 lines
5.7 KiB
Vue
243 lines
5.7 KiB
Vue
<template>
|
||
<div class="register-container">
|
||
<!-- 深空背景 -->
|
||
<div class="space-background">
|
||
<div class="stars" ref="stars"></div>
|
||
</div>
|
||
|
||
<div class="register-content">
|
||
<!-- Logo和标题 -->
|
||
<div class="logo-section">
|
||
<div class="logo">
|
||
<div class="time-capsule"></div>
|
||
</div>
|
||
<h1 class="app-title">加入 ChronoMail</h1>
|
||
<p class="app-slogan">开始你的时间之旅</p>
|
||
</div>
|
||
|
||
<!-- 注册表单 -->
|
||
<div class="register-form glass-card p-30">
|
||
<van-form @submit="onSubmit">
|
||
<van-field
|
||
v-model="username"
|
||
name="username"
|
||
placeholder="用户名"
|
||
:rules="[{ required: true, message: '请填写用户名' }]"
|
||
class="custom-field"
|
||
/>
|
||
<van-field
|
||
v-model="email"
|
||
name="email"
|
||
placeholder="邮箱"
|
||
:rules="[
|
||
{ required: true, message: '请填写邮箱' },
|
||
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: '请输入正确的邮箱格式' }
|
||
]"
|
||
class="custom-field"
|
||
/>
|
||
<van-field
|
||
v-model="password"
|
||
type="password"
|
||
name="password"
|
||
placeholder="密码"
|
||
:rules="[
|
||
{ required: true, message: '请填写密码' },
|
||
{ pattern: /^(?=.*[a-zA-Z])(?=.*\d).{6,}$/, message: '密码至少6位,包含字母和数字' }
|
||
]"
|
||
class="custom-field"
|
||
/>
|
||
<van-field
|
||
v-model="confirmPassword"
|
||
type="password"
|
||
name="confirmPassword"
|
||
placeholder="确认密码"
|
||
:rules="[
|
||
{ required: true, message: '请确认密码' },
|
||
{ validator: validatePassword }
|
||
]"
|
||
class="custom-field"
|
||
/>
|
||
<div class="form-actions">
|
||
<van-button round block type="primary" native-type="submit" class="register-button">
|
||
注册
|
||
</van-button>
|
||
<div class="login-link mt-20">
|
||
已有账号?<span @click="goToLogin">立即登录</span>
|
||
</div>
|
||
</div>
|
||
</van-form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { ref, onMounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { showLoadingToast, showSuccessToast, showFailToast, closeToast } from 'vant'
|
||
import { userActions } from '../store'
|
||
|
||
export default {
|
||
name: 'Register',
|
||
setup() {
|
||
const router = useRouter()
|
||
const username = ref('')
|
||
const email = ref('')
|
||
const password = ref('')
|
||
const confirmPassword = ref('')
|
||
const stars = ref(null)
|
||
|
||
// 生成星空背景
|
||
const generateStars = () => {
|
||
if (!stars.value) return
|
||
|
||
const starsContainer = stars.value
|
||
const starCount = 100
|
||
|
||
for (let i = 0; i < starCount; i++) {
|
||
const star = document.createElement('div')
|
||
star.className = 'star'
|
||
|
||
// 随机位置
|
||
const left = Math.random() * 100
|
||
const top = Math.random() * 100
|
||
|
||
// 随机大小
|
||
const size = Math.random() * 3 + 1
|
||
|
||
// 随机动画延迟
|
||
const delay = Math.random() * 4
|
||
|
||
star.style.left = `${left}%`
|
||
star.style.top = `${top}%`
|
||
star.style.width = `${size}px`
|
||
star.style.height = `${size}px`
|
||
star.style.animationDelay = `${delay}s`
|
||
|
||
starsContainer.appendChild(star)
|
||
}
|
||
}
|
||
|
||
// 验证密码
|
||
const validatePassword = () => {
|
||
if (password.value !== confirmPassword.value) {
|
||
return '两次输入的密码不一致'
|
||
}
|
||
return true
|
||
}
|
||
|
||
// 注册处理
|
||
const onSubmit = async () => {
|
||
try {
|
||
showLoadingToast({
|
||
message: '注册中...',
|
||
forbidClick: true,
|
||
})
|
||
|
||
// 调用API进行注册
|
||
await userActions.register({
|
||
username: username.value,
|
||
email: email.value,
|
||
password: password.value
|
||
})
|
||
|
||
closeToast()
|
||
showSuccessToast('注册成功')
|
||
|
||
// 跳转到登录页
|
||
router.push('/login')
|
||
} catch (error) {
|
||
closeToast()
|
||
const errorMessage = error.response?.data?.message || '注册失败,请稍后再试'
|
||
showFailToast(errorMessage)
|
||
}
|
||
}
|
||
|
||
// 跳转到登录页
|
||
const goToLogin = () => {
|
||
router.push('/login')
|
||
}
|
||
|
||
onMounted(() => {
|
||
generateStars()
|
||
})
|
||
|
||
return {
|
||
username,
|
||
email,
|
||
password,
|
||
confirmPassword,
|
||
stars,
|
||
validatePassword,
|
||
onSubmit,
|
||
goToLogin
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.register-container {
|
||
height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.register-content {
|
||
width: 90%;
|
||
max-width: 400px;
|
||
z-index: 1;
|
||
}
|
||
|
||
.logo-section {
|
||
text-align: center;
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.logo {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.app-title {
|
||
font-size: 32px;
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
background: linear-gradient(135deg, #00D4FF, #ffffff);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
|
||
.app-slogan {
|
||
font-size: 16px;
|
||
color: var(--text-secondary);
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.register-form {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.register-button {
|
||
height: 50px;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.login-link {
|
||
text-align: center;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.login-link span {
|
||
color: var(--accent-color);
|
||
cursor: pointer;
|
||
font-weight: bold;
|
||
}
|
||
</style> |