415 lines
9.2 KiB
Vue
415 lines
9.2 KiB
Vue
<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>
|