blob: ad3461d496d3e1ad8302499199fa7f4cd7a12f66 [file] [log] [blame]
2081595154f846f912025-06-04 17:35:21 +08001<template>
2 <div class="register-page">
3 <!-- 背景装饰 -->
4 <div class="bg-decoration">
5 <div class="shape shape-1"></div>
6 <div class="shape shape-2"></div>
7 <div class="shape shape-3"></div>
8 </div>
9
10 <!-- 注册表单容器 -->
11 <div class="register-container">
12 <div class="register-card">
13 <!-- 头部信息 -->
14 <div class="register-header">
15 <div class="logo">
16 <el-icon size="48" color="#a8edea"><UserFilled /></el-icon>
17 </div>
18 <h1 class="title">加入 PT Tracker</h1>
19 <p class="subtitle">开始你的资源分享之旅</p>
20 </div>
21
22 <!-- 注册表单 -->
23 <el-form
24 ref="registerFormRef"
25 :model="registerForm"
26 :rules="registerRules"
27 class="register-form"
28 size="large"
29 >
30 <el-form-item prop="username">
31 <el-input
32 v-model="registerForm.username"
33 placeholder="请输入用户名"
34 clearable
35 :prefix-icon="User"
36 />
37 </el-form-item>
38
39 <el-form-item prop="email">
40 <el-input
41 v-model="registerForm.email"
42 placeholder="请输入邮箱地址"
43 clearable
44 :prefix-icon="Message"
45 />
46 </el-form-item>
47
48 <el-form-item prop="password">
49 <el-input
50 v-model="registerForm.password"
51 type="password"
52 placeholder="请输入密码"
53 show-password
54 :prefix-icon="Lock"
55 />
56 </el-form-item>
57
58 <el-form-item prop="confirmPassword">
59 <el-input
60 v-model="registerForm.confirmPassword"
61 type="password"
62 placeholder="请确认密码"
63 show-password
64 :prefix-icon="Lock"
65 />
66 </el-form-item>
67
68 <el-form-item prop="agreement">
69 <el-checkbox v-model="registerForm.agreement">
70 我已阅读并同意
71 <el-link type="primary" :underline="false">《用户协议》</el-link>
72
73 <el-link type="primary" :underline="false">《隐私政策》</el-link>
74 </el-checkbox>
75 </el-form-item>
76
77 <el-form-item>
78 <el-button
79 type="primary"
80 :loading="loading"
81 style="width: 100%"
82 @click="handleRegister"
83 >
84 <span v-if="!loading">注册</span>
85 <span v-else>注册中...</span>
86 </el-button>
87 </el-form-item>
88 </el-form>
89
90 <!-- 底部链接 -->
91 <div class="register-footer">
92 <span class="footer-text">已有账号?</span>
93 <el-link
94 type="primary"
95 :underline="false"
96 @click="$router.push('/login')"
97 >
98 立即登录
99 </el-link>
100 </div>
101 </div>
102 </div>
103 </div>
104</template>
105
106<script>
107import { ref, reactive } from 'vue'
108import { useRouter } from 'vue-router'
109import { ElMessage } from 'element-plus'
110import { User, Lock, Message, UserFilled } from '@element-plus/icons-vue'
111
112export default {
113 name: 'RegisterView',
114 components: {
115 UserFilled
116 },
117 setup() {
118 const router = useRouter()
119 const registerFormRef = ref()
120 const loading = ref(false)
121
122 // 表单数据
123 const registerForm = reactive({
124 username: '',
125 email: '',
126 password: '',
127 confirmPassword: '',
128 agreement: false
129 })
130
131 // 自定义验证规则
132 const validateConfirmPassword = (rule, value, callback) => {
133 if (value === '') {
134 callback(new Error('请再次输入密码'))
135 } else if (value !== registerForm.password) {
136 callback(new Error('两次输入密码不一致'))
137 } else {
138 callback()
139 }
140 }
141
142 const validateAgreement = (rule, value, callback) => {
143 if (!value) {
144 callback(new Error('请先同意用户协议和隐私政策'))
145 } else {
146 callback()
147 }
148 }
149
150 // 验证规则
151 const registerRules = {
152 username: [
153 { required: true, message: '请输入用户名', trigger: 'blur' },
154 { min: 3, max: 20, message: '用户名长度在3到20个字符', trigger: 'blur' },
155 { pattern: /^[a-zA-Z0-9_]+$/, message: '用户名只能包含字母、数字和下划线', trigger: 'blur' }
156 ],
157 email: [
158 { required: true, message: '请输入邮箱地址', trigger: 'blur' },
159 { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
160 ],
161 password: [
162 { required: true, message: '请输入密码', trigger: 'blur' },
163 { min: 6, max: 20, message: '密码长度在6到20个字符', trigger: 'blur' },
164 { pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]/, message: '密码必须包含大小写字母和数字', trigger: 'blur' }
165 ],
166 confirmPassword: [
167 { required: true, validator: validateConfirmPassword, trigger: 'blur' }
168 ],
169 agreement: [
170 { required: true, validator: validateAgreement, trigger: 'change' }
171 ]
172 }
173
174 // 注册处理
175 const handleRegister = async () => {
176 try {
177 // 表单验证
178 const valid = await registerFormRef.value.validate()
179 if (!valid) return
180
181 loading.value = true
182
183 // 模拟注册API请求
184 await new Promise(resolve => setTimeout(resolve, 1500))
185
186 // 模拟注册成功
187 ElMessage.success('注册成功!请登录您的账号')
188
189 // 跳转到登录页
190 router.push('/login')
191
192 } catch (error) {
193 console.error('注册失败:', error)
194 ElMessage.error('注册失败,请稍后重试')
195 } finally {
196 loading.value = false
197 }
198 }
199
200 return {
201 registerFormRef,
202 registerForm,
203 registerRules,
204 loading,
205 handleRegister,
206 User,
207 Lock,
208 Message
209 }
210 }
211}
212</script>
213
214<style lang="scss" scoped>
215.register-page {
216 min-height: 100vh;
217 background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
218 position: relative;
219 overflow: hidden;
220 display: flex;
221 align-items: center;
222 justify-content: center;
223 padding: 20px;
224}
225
226// 背景装饰(与登录页相同的动画效果)
227.bg-decoration {
228 position: absolute;
229 top: 0;
230 left: 0;
231 width: 100%;
232 height: 100%;
233 overflow: hidden;
234 z-index: 1;
235
236 .shape {
237 position: absolute;
238 border-radius: 50%;
239 background: rgba(255, 255, 255, 0.15);
240 animation: float 8s ease-in-out infinite;
241
242 &.shape-1 {
243 width: 180px;
244 height: 180px;
245 top: 15%;
246 left: 15%;
247 animation-delay: 0s;
248 }
249
250 &.shape-2 {
251 width: 120px;
252 height: 120px;
253 top: 50%;
254 right: 15%;
255 animation-delay: -3s;
256 }
257
258 &.shape-3 {
259 width: 90px;
260 height: 90px;
261 bottom: 25%;
262 left: 25%;
263 animation-delay: -6s;
264 }
265 }
266}
267
268@keyframes float {
269 0%, 100% {
270 transform: translateY(0px) rotate(0deg);
271 }
272 50% {
273 transform: translateY(-15px) rotate(180deg);
274 }
275}
276
277// 注册容器
278.register-container {
279 position: relative;
280 z-index: 10;
281 width: 100%;
282 max-width: 420px;
283}
284
285.register-card {
286 background: rgba(255, 255, 255, 0.95);
287 backdrop-filter: blur(10px);
288 border-radius: 16px;
289 padding: 40px;
290 box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
291 border: 1px solid rgba(255, 255, 255, 0.2);
292
293 .register-header {
294 text-align: center;
295 margin-bottom: 32px;
296
297 .logo {
298 margin-bottom: 16px;
299 }
300
301 .title {
302 font-size: 32px;
303 font-weight: 700;
304 color: #2c3e50;
305 margin-bottom: 8px;
306 letter-spacing: -0.5px;
307 }
308
309 .subtitle {
310 font-size: 15px;
311 color: #7f8c8d;
312 margin: 0;
313 font-weight: 400;
314 }
315 }
316
317 .register-form {
318 .el-form-item {
319 margin-bottom: 20px;
320
321 &:last-child {
322 margin-bottom: 0;
323 }
324 }
325
326 .el-checkbox {
327 :deep(.el-checkbox__label) {
328 font-size: 14px;
329 color: #606266;
330 line-height: 1.5;
331 }
332 }
333
334 .el-button {
335 font-size: 16px;
336 font-weight: 500;
337 height: 48px;
338 border-radius: 8px;
339 }
340 }
341
342 .register-footer {
343 text-align: center;
344 margin-top: 24px;
345 padding-top: 24px;
346 border-top: 1px solid #ebeef5;
347
348 .footer-text {
349 color: #909399;
350 font-size: 14px;
351 margin-right: 8px;
352 }
353
354 .el-link {
355 font-size: 14px;
356 font-weight: 500;
357 }
358 }
359}
360
361// 响应式设计
362@media (max-width: 768px) {
363 .register-page {
364 padding: 16px;
365 }
366
367 .register-card {
368 padding: 32px 24px;
369
370 .register-header .title {
371 font-size: 28px;
372 }
373 }
374}
375</style>