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
286 lines
6.3 KiB
Vue
2 weeks ago
|
<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>
|