feat: 注册、重置密码、重发验证邮件添加验证码功能
后端修改: - 添加通用验证码验证函数 verifyCaptcha() - /api/register 接口添加验证码验证 - /api/password/forgot 接口添加验证码验证 - /api/resend-verification 接口添加验证码验证 前端修改: - 注册表单添加验证码输入框和图片 - 忘记密码模态框添加验证码 - 重发验证邮件区域添加验证码输入 - 添加各表单的验证码刷新方法 - 提交失败后自动刷新验证码 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1038,7 +1038,14 @@
|
||||
<small style="color: var(--text-muted); font-size: 12px;">点击图片刷新验证码</small>
|
||||
</div>
|
||||
<div v-if="showResendVerify" class="alert alert-info" style="margin-bottom: 10px;">
|
||||
邮箱未验证?<a style="color:#667eea; cursor: pointer;" @click="resendVerification">点击重发激活邮件</a>
|
||||
<div>邮箱未验证?请输入验证码后重发激活邮件</div>
|
||||
<div style="display: flex; gap: 10px; align-items: center; margin-top: 8px;">
|
||||
<input type="text" class="form-input" v-model="resendVerifyCaptcha" placeholder="验证码" style="flex: 1; height: 40px;" @focus="!resendVerifyCaptchaUrl && refreshResendVerifyCaptcha()">
|
||||
<img v-if="resendVerifyCaptchaUrl" :src="resendVerifyCaptchaUrl" @click="refreshResendVerifyCaptcha" style="height: 40px; cursor: pointer; border-radius: 4px;" title="点击刷新验证码">
|
||||
<button type="button" class="btn btn-primary" @click="resendVerification" style="height: 40px; white-space: nowrap;">
|
||||
重发邮件
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; margin-bottom: 15px;">
|
||||
<a @click="showForgotPasswordModal = true" style="color: #667eea; cursor: pointer; font-size: 14px; text-decoration: none;">
|
||||
@@ -1049,7 +1056,7 @@
|
||||
<i class="fas fa-right-to-bracket"></i> 登录
|
||||
</button>
|
||||
</form>
|
||||
<form v-else @submit.prevent="handleRegister">
|
||||
<form v-else @submit.prevent="handleRegister" @focusin="!registerCaptchaUrl && refreshRegisterCaptcha()">
|
||||
<div class="form-group">
|
||||
<label class="form-label">用户名(3-20字符)</label>
|
||||
<input type="text" class="form-input" v-model="registerForm.username" required minlength="3" maxlength="20">
|
||||
@@ -1062,6 +1069,13 @@
|
||||
<label class="form-label">密码 (至少6字符)</label>
|
||||
<input type="password" class="form-input" v-model="registerForm.password" required minlength="6">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">验证码</label>
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<input type="text" class="form-input" v-model="registerForm.captcha" placeholder="请输入验证码" required style="flex: 1;">
|
||||
<img v-if="registerCaptchaUrl" :src="registerCaptchaUrl" @click="refreshRegisterCaptcha" style="height: 44px; cursor: pointer; border-radius: 4px;" title="点击刷新验证码">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-user-plus"></i> 注册
|
||||
</button>
|
||||
@@ -2590,7 +2604,7 @@
|
||||
|
||||
<!-- 忘记密码模态框 -->
|
||||
<div v-if="showForgotPasswordModal" class="modal-overlay" @mousedown.self="handleModalMouseDown" @mouseup.self="handleModalMouseUp('showForgotPasswordModal')">
|
||||
<div class="modal-content" @click.stop>
|
||||
<div class="modal-content" @click.stop @focusin="!forgotPasswordCaptchaUrl && refreshForgotPasswordCaptcha()">
|
||||
<h3 style="margin-bottom: 20px;">忘记密码 - 邮箱重置</h3>
|
||||
<p style="color: var(--text-secondary); margin-bottom: 15px; font-size: 14px;">
|
||||
请输入注册邮箱,我们会发送重置链接到您的邮箱
|
||||
@@ -2599,11 +2613,18 @@
|
||||
<label class="form-label">邮箱</label>
|
||||
<input type="email" class="form-input" v-model="forgotPasswordForm.email" placeholder="请输入注册邮箱" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">验证码</label>
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<input type="text" class="form-input" v-model="forgotPasswordForm.captcha" placeholder="请输入验证码" required style="flex: 1;">
|
||||
<img v-if="forgotPasswordCaptchaUrl" :src="forgotPasswordCaptchaUrl" @click="refreshForgotPasswordCaptcha" style="height: 44px; cursor: pointer; border-radius: 4px;" title="点击刷新验证码">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; margin-top: 20px;">
|
||||
<button class="btn btn-primary" @click="requestPasswordReset" style="flex: 1;">
|
||||
<i class="fas fa-paper-plane"></i> 发送重置邮件
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="showForgotPasswordModal = false; forgotPasswordForm = {email: ''}" style="flex: 1;">
|
||||
<button class="btn btn-secondary" @click="showForgotPasswordModal = false; forgotPasswordForm = {email: '', captcha: ''}; forgotPasswordCaptchaUrl = ''" style="flex: 1;">
|
||||
<i class="fas fa-times"></i> 取消
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -29,8 +29,10 @@ createApp({
|
||||
registerForm: {
|
||||
username: '',
|
||||
email: '',
|
||||
password: ''
|
||||
password: '',
|
||||
captcha: ''
|
||||
},
|
||||
registerCaptchaUrl: '',
|
||||
|
||||
// 验证码相关
|
||||
showCaptcha: false,
|
||||
@@ -125,8 +127,10 @@ createApp({
|
||||
// 忘记密码
|
||||
showForgotPasswordModal: false,
|
||||
forgotPasswordForm: {
|
||||
email: ''
|
||||
email: '',
|
||||
captcha: ''
|
||||
},
|
||||
forgotPasswordCaptchaUrl: '',
|
||||
showResetPasswordModal: false,
|
||||
resetPasswordForm: {
|
||||
token: '',
|
||||
@@ -134,6 +138,8 @@ createApp({
|
||||
},
|
||||
showResendVerify: false,
|
||||
resendVerifyEmail: '',
|
||||
resendVerifyCaptcha: '',
|
||||
resendVerifyCaptchaUrl: '',
|
||||
|
||||
// 系统设置
|
||||
systemSettings: {
|
||||
@@ -604,18 +610,37 @@ handleDragLeave(e) {
|
||||
}
|
||||
},
|
||||
|
||||
// 刷新验证码
|
||||
// 刷新验证码(登录)
|
||||
refreshCaptcha() {
|
||||
this.captchaUrl = `${this.apiBase}/api/captcha?t=${Date.now()}`;
|
||||
},
|
||||
|
||||
// 刷新注册验证码
|
||||
refreshRegisterCaptcha() {
|
||||
this.registerCaptchaUrl = `${this.apiBase}/api/captcha?t=${Date.now()}`;
|
||||
},
|
||||
|
||||
// 刷新忘记密码验证码
|
||||
refreshForgotPasswordCaptcha() {
|
||||
this.forgotPasswordCaptchaUrl = `${this.apiBase}/api/captcha?t=${Date.now()}`;
|
||||
},
|
||||
|
||||
// 刷新重发验证邮件验证码
|
||||
refreshResendVerifyCaptcha() {
|
||||
this.resendVerifyCaptchaUrl = `${this.apiBase}/api/captcha?t=${Date.now()}`;
|
||||
},
|
||||
|
||||
async resendVerification() {
|
||||
if (!this.resendVerifyEmail) {
|
||||
this.showToast('error', '错误', '请输入邮箱或用户名后再重试');
|
||||
return;
|
||||
}
|
||||
if (!this.resendVerifyCaptcha) {
|
||||
this.showToast('error', '错误', '请输入验证码');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = {};
|
||||
const payload = { captcha: this.resendVerifyCaptcha };
|
||||
if (this.resendVerifyEmail.includes('@')) {
|
||||
payload.email = this.resendVerifyEmail;
|
||||
} else {
|
||||
@@ -624,10 +649,17 @@ handleDragLeave(e) {
|
||||
const response = await axios.post(`${this.apiBase}/api/resend-verification`, payload);
|
||||
if (response.data.success) {
|
||||
this.showToast('success', '成功', '验证邮件已发送,请查收');
|
||||
this.showResendVerify = false;
|
||||
this.resendVerifyEmail = '';
|
||||
this.resendVerifyCaptcha = '';
|
||||
this.resendVerifyCaptchaUrl = '';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重发验证邮件失败:', error);
|
||||
this.showToast('error', '错误', error.response?.data?.message || '发送失败');
|
||||
// 刷新验证码
|
||||
this.resendVerifyCaptcha = '';
|
||||
this.refreshResendVerifyCaptcha();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -661,8 +693,10 @@ handleDragLeave(e) {
|
||||
this.registerForm = {
|
||||
username: '',
|
||||
email: '',
|
||||
password: ''
|
||||
password: '',
|
||||
captcha: ''
|
||||
};
|
||||
this.registerCaptchaUrl = '';
|
||||
}
|
||||
} catch (error) {
|
||||
const errorData = error.response?.data;
|
||||
@@ -671,6 +705,9 @@ handleDragLeave(e) {
|
||||
} else {
|
||||
this.errorMessage = errorData?.message || '注册失败';
|
||||
}
|
||||
// 刷新验证码
|
||||
this.registerForm.captcha = '';
|
||||
this.refreshRegisterCaptcha();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1881,6 +1918,10 @@ handleDragLeave(e) {
|
||||
this.showToast('error', '错误', '请输入注册邮箱');
|
||||
return;
|
||||
}
|
||||
if (!this.forgotPasswordForm.captcha) {
|
||||
this.showToast('error', '错误', '请输入验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
@@ -1891,11 +1932,15 @@ handleDragLeave(e) {
|
||||
if (response.data.success) {
|
||||
this.showToast('success', '成功', '如果邮箱存在,将收到重置邮件');
|
||||
this.showForgotPasswordModal = false;
|
||||
this.forgotPasswordForm = { email: '' };
|
||||
this.forgotPasswordForm = { email: '', captcha: '' };
|
||||
this.forgotPasswordCaptchaUrl = '';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交密码重置请求失败:', error);
|
||||
this.showToast('error', '错误', error.response?.data?.message || '提交失败');
|
||||
// 刷新验证码
|
||||
this.forgotPasswordForm.captcha = '';
|
||||
this.refreshForgotPasswordCaptcha();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user