You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
6.3 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { Iphone, Message, Key } from '@element-plus/icons-vue'
import { ResetService } from '@/api/user'
const router = useRouter()
const captchaCanvas = ref(null)
const forgetForm = ref(null)
const submitting = ref(false)
// 表单数据
const form = ref({
phone: '',
email: '',
captcha: ''
})
// 验证码答案
const captchaAnswer = ref('')
// 表单验证规则
const rules = {
phone: [{ required: true, message: '请输入手机号码', trigger: 'blur' }],
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
captcha: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ validator: validateCaptcha, trigger: 'blur' }
]
}
// 验证码校验
function validateCaptcha(rule, value, callback) {
if (value.toLowerCase() !== captchaAnswer.value.toLowerCase()) {
callback(new Error('验证码不正确'))
} else {
callback()
}
}
// 生成验证码
const generateCaptcha = () => {
const canvas = captchaCanvas.value
if (!canvas) return
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
// 随机字符
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'
let captcha = ''
// 绘制背景
ctx.fillStyle = '#f5f7fa'
ctx.fillRect(0, 0, canvas.width, canvas.height)
// 绘制干扰线
for (let i = 0; i < 5; i++) {
ctx.strokeStyle = getRandomColor(100, 200)
ctx.beginPath()
ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height)
ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height)
ctx.stroke()
}
// 绘制验证码字符
for (let i = 0; i < 4; i++) {
const char = chars[Math.floor(Math.random() * chars.length)]
captcha += char
ctx.fillStyle = getRandomColor(50, 150)
ctx.font = `${Math.floor(20 + Math.random() * 10)}px Arial`
ctx.fillText(char, 10 + i * 30, 30 + (Math.random() * 10 - 5))
}
captchaAnswer.value = captcha
}
// 获取随机颜色
const getRandomColor = (min, max) => {
const r = min + Math.floor(Math.random() * (max - min))
const g = min + Math.floor(Math.random() * (max - min))
const b = min + Math.floor(Math.random() * (max - min))
return `rgb(${r}, ${g}, ${b})`
}
// 刷新验证码
const refreshCaptcha = () => {
generateCaptcha()
}
// 提交表单
const submitForm = () => {
forgetForm.value.validate(async (valid) => {
if (valid) {
submitting.value = true
const {
data: { message }
} = await ResetService(form.value)
submitting.value = false
resetForm()
ElMessage.success(message)
}
})
}
// 重置表单
function resetForm() {
forgetForm.value.resetFields()
refreshCaptcha()
}
// 返回登录页
function goToLogin() {
router.push('/login')
}
onMounted(() => {
generateCaptcha()
})
</script>
<template>
<div class="forget-password-page">
<div class="brand-header">
<h1>校园 e 站通</h1>
<h2>华交场馆管控一体化平台</h2>
</div>
<div class="forget-password-container">
<div class="form-panel">
<h3 class="form-title">找回密码</h3>
<el-form
ref="forgetForm"
:model="form"
:rules="rules"
label-width="100px"
class="forget-form"
>
<el-form-item label="手机号码" prop="phone">
<el-input
v-model="form.phone"
placeholder="请输入注册时绑定的手机号"
clearable
>
<template #prefix>
<el-icon><Iphone /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="QQ邮箱" prop="email">
<el-input
v-model="form.email"
placeholder="请输入注册时绑定的QQ邮箱"
clearable
>
<template #prefix>
<el-icon><Message /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="验证码" prop="captcha">
<div class="captcha-input">
<el-input
v-model="form.captcha"
placeholder="请输入右侧验证码"
clearable
>
<template #prefix>
<el-icon><Key /></el-icon>
</template>
</el-input>
<div class="captcha-image" @click="refreshCaptcha">
<canvas ref="captchaCanvas" width="250" height="40"></canvas>
</div>
</div>
</el-form-item>
<div>
<el-button
type="primary"
@click="submitForm"
:loading="submitting"
class="submit-btn"
>
提交验证
</el-button>
<el-button @click="resetForm" class="reset-btn">重置</el-button>
</div>
</el-form>
<div class="back-login">
<el-link type="primary" @click="goToLogin"></el-link>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.forget-password-page {
min-height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
display: flex;
flex-direction: column;
align-items: center;
padding-top: 50px;
}
.brand-header {
text-align: center;
margin-bottom: 40px;
color: #2c3e50;
}
.brand-header h1 {
font-size: 32px;
font-weight: bold;
margin-bottom: 10px;
}
.brand-header h2 {
font-size: 18px;
font-weight: normal;
color: #5a6a85;
}
.forget-password-container {
width: 100%;
max-width: 500px;
}
.form-panel {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 30px;
}
.form-title {
text-align: center;
margin-bottom: 30px;
color: #2c3e50;
font-size: 20px;
}
.forget-form {
margin-top: 20px;
}
.captcha-input {
display: flex;
align-items: center;
gap: 10px;
}
.captcha-image {
cursor: pointer;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
}
.captcha-image:hover {
border-color: #c0c4cc;
}
.submit-btn {
width: 60%;
margin-left: 1.9vw;
}
.reset-btn {
width: 30%;
}
.back-login {
text-align: center;
margin-top: 20px;
}
.el-form-item {
margin-bottom: 22px;
}
</style>