Files
HAOVP/frontend/video-frontend-web/src/views/login/register.vue
T

446 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="auth-page">
<div class="auth-shell">
<section class="auth-showcase">
<div class="showcase-copy">
<p class="eyebrow">
New Account
</p>
<h1>创建账号,进入你的创作与互动空间</h1>
<p class="lead">
注册成功后可以保存观看记录、创建收藏夹、进入消息中心,并继续使用上传、审核与播放联调后的完整链路。
</p>
</div>
<div class="showcase-board">
<div class="board-card">
<strong>保存记录</strong>
<span>同步个人资料、播放历史、收藏夹和消息通知。</span>
</div>
<div class="board-card">
<strong>参与互动</strong>
<span>评论、回复、关注、私信和通知都依赖注册后的个人身份。</span>
</div>
<div class="board-card">
<strong>开始创作</strong>
<span>上传视频并进入个人主页,逐步搭建自己的创作者空间。</span>
</div>
</div>
</section>
<section class="auth-panel">
<div class="panel-top">
<button
class="back-home"
@click="router.push('/')"
>
返回首页
</button>
<div class="brand-lockup">
<h1 class="logo">
HAOVP
</h1>
<p class="slogan">
创建账号,保存你的观看与创作记录
</p>
</div>
</div>
<van-form @submit="handleRegister">
<div class="captcha-banner">
<div>
<strong>注册验证</strong>
<span>当前注册同样要求验证码,点击图片可以刷新。</span>
</div>
<van-image
:src="captchaImage"
fit="contain"
class="captcha-image"
@click="refreshCaptcha"
/>
</div>
<van-cell-group inset>
<van-field
v-model="form.username"
name="username"
label="用户名"
placeholder="请输入用户名"
:rules="[{ required: true, message: '请输入用户名' }]"
/>
<van-field
v-model="form.email"
name="email"
label="邮箱"
placeholder="请输入邮箱"
:rules="[{ required: true, message: '请输入邮箱' }]"
/>
<van-field
v-model="form.phone"
name="phone"
label="手机号"
placeholder="请输入手机号"
/>
<van-field
v-model="form.password"
type="password"
name="password"
label="密码"
placeholder="请输入密码"
:rules="[{ required: true, message: '请输入密码' }, { pattern: /^.{6,}$/, message: '密码至少6位' }]"
/>
<van-field
v-model="form.confirmPassword"
type="password"
name="confirmPassword"
label="确认密码"
placeholder="请再次输入密码"
:rules="[{ required: true, message: '请确认密码' }, { validator: validatePassword, message: '两次密码不一致' }]"
/>
<van-field
v-model="form.captcha"
name="captcha"
label="验证码"
placeholder="请输入验证码"
:rules="[{ required: true, message: '请输入验证码' }]"
>
<template #button>
<van-image
:src="captchaImage"
fit="contain"
class="captcha-btn"
@click="refreshCaptcha"
/>
</template>
</van-field>
</van-cell-group>
<div class="tips-row">
<span>注册成功后将跳转到登录页,请使用刚创建的账号继续登录。</span>
</div>
<div class="register-actions">
<van-button
round
block
type="primary"
native-type="submit"
:loading="loading"
class="submit-btn"
>
注册
</van-button>
</div>
</van-form>
<div class="register-footer">
<router-link
to="/login"
class="link"
>
已有账号?去登录
</router-link>
</div>
</section>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { showSuccessToast, showToast } from 'vant'
import { authApi, type CaptchaResult } from '@/api/auth.ts'
const router = useRouter()
const loading = ref(false)
const captchaKey = ref('')
const captchaImage = ref('')
const form = reactive({
username: '',
email: '',
phone: '',
password: '',
confirmPassword: '',
captcha: ''
})
const validatePassword = () => form.password === form.confirmPassword
const refreshCaptcha = async () => {
try {
const res: CaptchaResult = await authApi.getCaptcha()
captchaKey.value = res.captchaKey
captchaImage.value = res.captchaImage
} catch (error) {
showToast('获取验证码失败')
}
}
const handleRegister = async () => {
if (!validatePassword()) {
showToast('两次密码不一致')
return
}
loading.value = true
try {
await authApi.register({
username: form.username,
email: form.email,
phone: form.phone,
password: form.password,
confirmPassword: form.confirmPassword,
captcha: form.captcha,
captchaKey: captchaKey.value
})
showSuccessToast('注册成功')
router.push('/login')
} catch (error: any) {
showToast(error.message || '注册失败')
refreshCaptcha()
} finally {
loading.value = false
}
}
onMounted(() => {
refreshCaptcha()
})
</script>
<style scoped lang="scss">
.auth-page {
min-height: 100vh;
background:
radial-gradient(circle at 18% 18%, rgba(34, 197, 94, 0.16), transparent 24%),
radial-gradient(circle at 82% 82%, rgba(59, 130, 246, 0.2), transparent 22%),
linear-gradient(135deg, #08111f 0%, #12345b 52%, #0f766e 100%);
display: flex;
align-items: center;
justify-content: center;
padding: 28px 20px;
}
.auth-shell {
width: min(1180px, 100%);
display: grid;
grid-template-columns: minmax(0, 1.15fr) 460px;
gap: 20px;
}
.auth-showcase,
.auth-panel {
border-radius: 30px;
overflow: hidden;
}
.auth-showcase {
padding: 34px 32px;
color: #fff;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.2), transparent 28%),
linear-gradient(135deg, rgba(15, 23, 42, 0.92), rgba(15, 118, 110, 0.92));
box-shadow: 0 24px 80px rgba(4, 16, 33, 0.34);
display: grid;
align-content: space-between;
}
.eyebrow {
margin: 0 0 12px;
font-size: 12px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.68);
}
.showcase-copy {
h1 {
margin: 0 0 12px;
font-size: 40px;
line-height: 1.18;
}
.lead {
margin: 0;
max-width: 620px;
color: rgba(255, 255, 255, 0.82);
line-height: 1.8;
}
}
.showcase-board {
display: grid;
gap: 12px;
margin-top: 28px;
}
.board-card {
padding: 18px 20px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(12px);
strong {
display: block;
margin-bottom: 6px;
font-size: 16px;
}
span {
color: rgba(255, 255, 255, 0.76);
font-size: 13px;
line-height: 1.7;
}
}
.auth-panel {
background: rgba(255, 255, 255, 0.96);
padding: 28px 26px 26px;
box-shadow: 0 24px 80px rgba(4, 16, 33, 0.34);
}
.panel-top {
margin-bottom: 22px;
}
.back-home {
border: none;
background: #f8fafc;
color: #475569;
padding: 8px 12px;
border-radius: 999px;
font-size: 12px;
cursor: pointer;
}
.brand-lockup {
margin-top: 16px;
}
.logo {
margin: 0;
font-size: 34px;
font-weight: 800;
color: #00a1d6;
}
.slogan {
margin: 10px 0 0;
color: #64748b;
font-size: 13px;
line-height: 1.7;
}
.captcha-banner {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: center;
margin-bottom: 16px;
padding: 14px 16px;
border-radius: 18px;
background: #f8fafc;
strong {
display: block;
margin-bottom: 4px;
color: #18191c;
font-size: 14px;
}
span {
color: #64748b;
font-size: 12px;
line-height: 1.6;
}
}
.captcha-image,
.captcha-btn {
flex-shrink: 0;
cursor: pointer;
}
.captcha-image {
width: 120px;
height: 40px;
}
.captcha-btn {
width: 100px;
height: 32px;
}
:deep(.van-cell-group--inset) {
margin: 0;
border-radius: 20px;
overflow: hidden;
box-shadow: inset 0 0 0 1px #eef2f7;
}
:deep(.van-field) {
padding: 14px 16px;
}
.tips-row {
margin-top: 12px;
color: #64748b;
font-size: 12px;
}
.register-actions {
margin-top: 22px;
}
.submit-btn {
:deep(.van-button) {
background: linear-gradient(135deg, #fb7299, #ff8fab);
border: none;
box-shadow: 0 14px 28px rgba(251, 114, 153, 0.24);
}
}
.register-footer {
text-align: center;
margin-top: 22px;
}
.link {
color: #00a1d6;
font-size: 14px;
text-decoration: none;
}
@media (max-width: 960px) {
.auth-shell {
grid-template-columns: 1fr;
}
.auth-showcase {
display: none;
}
}
@media (max-width: 768px) {
.auth-page {
padding: 16px 12px;
}
.auth-panel {
padding: 22px 18px 20px;
border-radius: 24px;
}
.captcha-banner {
flex-direction: column;
align-items: flex-start;
}
.logo {
font-size: 30px;
}
}
</style>