2025-08-19 19:04:11 +08:00

415 lines
9.2 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>
<n-modal v-model:show="showModal" :mask-closable="false" :close-on-esc="false" class="register-modal">
<div class="register-modal-container">
<!-- 关闭按钮 -->
<button class="close-btn" @click="closeModal">
<img src="/images/icon/shut-down.png" alt="关闭" class="close-icon" />
</button>
<div class="register-content">
<h2 class="form-title">账号注册</h2>
<form @submit.prevent="handleRegister">
<div class="form-group">
<div class="input-wrapper">
<img src="/images/auth/phone.png" alt="手机号/邮箱" class="input-icon" />
<input v-model="registerForm.email" type="email" placeholder="请输入手机号" class="form-input" required />
</div>
</div>
<div class="form-group">
<div class="input-wrapper verification-wrapper">
<img src="/images/auth/captcha.png" alt="验证码" class="input-icon" />
<input v-model="registerForm.verificationCode" type="text" placeholder="请输入验证码"
class="form-input verification-input" required />
<button type="button" class="verification-btn" :disabled="verificationCountdown > 0"
@click="sendVerificationCode">
{{ verificationCountdown > 0 ? `${verificationCountdown}s` : '获取验证码' }}
</button>
</div>
</div>
<div class="form-group last-form-group">
<div class="input-wrapper">
<img src="/images/auth/registered-password.png" alt="密码" class="input-icon" />
<input v-model="registerForm.password" type="password" placeholder="请输入密码" class="form-input" required />
</div>
</div>
<div class="password-hint">
8-12位字符需同时包含数字/字母/符号
</div>
<button type="submit" class="submit-btn" :disabled="isLoading">
{{ isLoading ? '注册中...' : '注册' }}
</button>
</form>
<div class="form-footer">
<p>注册即代表同意我们的 <a href="#" class="link">服务协议隐私政策</a></p>
</div>
</div>
</div>
</n-modal>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { useMessage } from 'naive-ui'
import { AuthApi } from '@/api'
interface Props {
show: boolean
}
interface Emits {
(e: 'update:show', value: boolean): void
(e: 'success'): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const message = useMessage()
const showModal = computed({
get: () => props.show,
set: (value) => emit('update:show', value)
})
const isLoading = ref(false)
const verificationCountdown = ref(0)
// 注册表单数据
const registerForm = reactive({
email: '',
verificationCode: '',
password: ''
})
// 关闭模态框
const closeModal = () => {
showModal.value = false
}
// 处理注册
const handleRegister = async () => {
if (!registerForm.email || !registerForm.verificationCode || !registerForm.password) {
message.warning('请填写完整的注册信息')
return
}
// 验证邮箱或手机号格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const phoneRegex = /^1[3-9]\d{9}$/
if (!emailRegex.test(registerForm.email) && !phoneRegex.test(registerForm.email)) {
message.warning('请输入正确的邮箱或手机号格式')
return
}
if (registerForm.password.length < 3) {
message.warning('密码长度不能少于3位')
return
}
if (registerForm.verificationCode.length !== 6) {
message.warning('请输入6位验证码')
return
}
isLoading.value = true
try {
// 调用注册API
const response = await AuthApi.register({
username: registerForm.email.split('@')[0] || registerForm.email.substring(0, 8),
email: registerForm.email,
password: registerForm.password,
confirmPassword: registerForm.password,
captcha: registerForm.verificationCode
})
if (response.code === 200) {
message.success('注册成功!请使用您的账号登录')
emit('success')
closeModal()
// 清空表单
registerForm.email = ''
registerForm.verificationCode = ''
registerForm.password = ''
} else {
message.error(response.message || '注册失败')
}
} catch (error: any) {
console.error('注册失败:', error)
// 处理不同类型的错误
if (error.response?.status === 400) {
message.error('请求参数错误,请检查输入信息')
} else if (error.response?.status === 409) {
message.error('邮箱已被注册,请使用其他邮箱')
} else if (error.response?.status === 422) {
message.error('验证码错误或已过期')
} else if (error.response?.data?.message) {
message.error(error.response.data.message)
} else {
message.error('网络错误,请检查网络连接')
}
} finally {
isLoading.value = false
}
}
// 发送验证码
const sendVerificationCode = async () => {
if (!registerForm.email) {
message.warning('请先输入邮箱地址')
return
}
// 邮箱格式验证
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(registerForm.email)) {
message.warning('请输入正确的邮箱格式')
return
}
if (verificationCountdown.value > 0) {
return
}
try {
// 调用发送验证码API
const response = await AuthApi.sendEmailVerification(registerForm.email)
if (response.code === 200) {
message.success('验证码已发送到您的邮箱')
// 开始倒计时
verificationCountdown.value = 60
const timer = setInterval(() => {
verificationCountdown.value--
if (verificationCountdown.value <= 0) {
clearInterval(timer)
}
}, 1000)
} else {
message.error(response.message || '发送验证码失败')
}
} catch (error: any) {
console.error('发送验证码失败:', error)
if (error.response?.status === 429) {
message.error('发送过于频繁,请稍后再试')
} else if (error.response?.data?.message) {
message.error(error.response.data.message)
} else {
message.error('发送验证码失败,请稍后重试')
}
}
}
</script>
export default {
name: 'RegisterModal'
}
<style scoped>
.register-modal-container {
position: relative;
background: white;
border-radius: 5px;
width: 346px;
height: 374px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.close-btn {
position: absolute;
top: 15px;
right: 15px;
background: none;
border: none;
color: #999;
cursor: pointer;
padding: 4px;
border-radius: 4px;
transition: all 0.2s;
z-index: 10;
}
.close-btn:hover {
color: #666;
background: #f5f5f5;
}
.close-icon {
width: 14px;
height: 14px;
object-fit: contain;
}
.register-content {
padding: 33px 40px 40px 40px;
}
.form-title {
font-size: 15px;
color: #333;
margin: 0 0 18px 0;
text-align: center;
}
.form-group {
margin-bottom: 13px;
}
.last-form-group {
margin-bottom: 8px;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.input-icon {
position: absolute;
left: 12px;
z-index: 2;
width: 11px;
height: 11px;
object-fit: contain;
}
.form-input {
min-width: 278px;
width: 278px;
height: 41px;
padding: 0 16px 0 30px;
border: 1px solid #D8D8D8;
border-radius: 6px;
font-size: 12px;
color: #000;
background: #fff;
transition: all 0.2s;
}
.form-input:focus {
outline: none;
border-color: #1890ff;
background: white;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
}
.form-input::placeholder {
color: #999;
}
.verification-wrapper {
position: relative;
}
.verification-input {
padding-right: 120px;
}
.verification-btn {
position: absolute;
right: -2px;
top: 50%;
transform: translateY(-50%);
background: #fff;
color: #0288D1;
border: none;
padding: 6px 6px;
border-radius: 4px;
font-size: 10px;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
border: 1px solid #0288D1;
}
.verification-btn:hover:not(:disabled) {
background: #0288D1;
color: #fff;
}
.verification-btn:disabled {
background: #d9d9d9;
cursor: not-allowed;
}
.password-hint {
font-size: 10px;
color: #999;
line-height: 1;
}
.submit-btn {
margin-top: 35px;
width: 278px;
height: 39.5px;
background: #0288D1;
color: white;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
margin-bottom: 5px;
}
.submit-btn:hover:not(:disabled) {
background: #40a9ff;
}
.submit-btn:disabled {
background: #d9d9d9;
cursor: not-allowed;
}
.form-footer {
text-align: center;
}
.form-footer p {
text-align: left;
font-size: 10px;
color: #999;
line-height: 1.4;
margin: 0;
}
.link {
color: #1890ff;
text-decoration: none;
}
.link:hover {
text-decoration: underline;
}
/* 响应式设计 */
@media (max-width: 768px) {
.register-modal-container {
width: 95vw;
margin: 20px;
}
.register-content {
padding: 30px 20px;
}
.form-title {
font-size: 20px;
margin-bottom: 20px;
}
}
</style>