hookehuyr

feat(登录): 添加忘记密码功能

在登录页面中,将忘记密码的链接替换为`<router-link>`,并添加忘记密码页面及其路由配置。用户现在可以通过该页面重置密码。
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-20 22:46:37
* @LastEditTime: 2025-03-21 10:27:02
* @FilePath: /mlaj/src/router/index.js
* @Description: 文件描述
*/
......@@ -45,6 +45,12 @@ const routes = [
meta: { title: '注册' }
},
{
path: '/forgotPwd',
name: 'ForgotPassword',
component: () => import('../views/auth/ForgotPasswordPage.vue'),
meta: { title: '忘记密码' }
},
{
path: '/activities',
name: 'Activities',
component: () => import('../views/activities/ActivitiesPage.vue'),
......
<template>
<div class="min-h-screen flex flex-col bg-gradient-to-br from-green-50 via-teal-50 to-blue-50 py-12 px-4 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<h1 class="text-center text-3xl font-bold text-gray-800 mb-2">亲子学院</h1>
<h2 class="text-center text-xl font-medium text-gray-600">重置密码</h2>
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<FrostedGlass class="py-8 px-6 rounded-lg">
<div v-if="error" class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-md">
{{ error }}
</div>
<form class="space-y-6" @submit.prevent="handleSubmit">
<div>
<label for="phone" class="block text-sm font-medium text-gray-700">
手机号 <span class="text-red-500">*</span>
</label>
<input
id="phone"
v-model="formData.phone"
type="tel"
required
pattern="^1[3-9]\d{9}$"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
/>
</div>
<div>
<label for="verificationCode" class="block text-sm font-medium text-gray-700">
验证码 <span class="text-red-500">*</span>
</label>
<div class="flex space-x-2">
<input
id="verificationCode"
v-model="formData.verificationCode"
type="text"
required
maxlength="6"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
/>
<button
type="button"
:disabled="countdown > 0"
@click="sendVerificationCode"
class="mt-1 px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</button>
</div>
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700">
新密码 <span class="text-red-500">*</span>
</label>
<input
id="password"
v-model="formData.password"
type="password"
required
minlength="6"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
/>
</div>
<div>
<label for="confirmPassword" class="block text-sm font-medium text-gray-700">
确认密码 <span class="text-red-500">*</span>
</label>
<input
id="confirmPassword"
v-model="formData.confirmPassword"
type="password"
required
minlength="6"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
/>
</div>
<div>
<button
type="submit"
:disabled="loading"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
:class="{ 'opacity-70 cursor-not-allowed': loading }"
>
{{ loading ? '提交中...' : '重置密码' }}
</button>
</div>
</form>
<div class="text-center mt-6">
<p class="text-sm text-gray-600">
记起密码了?
<router-link to="/login" class="font-medium text-green-600 hover:text-green-500">
返回登录
</router-link>
</p>
</div>
</FrostedGlass>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import FrostedGlass from '@/components/ui/FrostedGlass.vue'
const router = useRouter()
const error = ref('')
const loading = ref(false)
const countdown = ref(0)
const formData = reactive({
phone: '',
verificationCode: '',
password: '',
confirmPassword: ''
})
const startCountdown = () => {
countdown.value = 60
const timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
clearInterval(timer)
}
}, 1000)
}
const sendVerificationCode = async () => {
if (!/^1[3-9]\d{9}$/.test(formData.phone)) {
error.value = '请输入正确的手机号'
return
}
try {
// TODO: 调用发送验证码API
startCountdown()
} catch (err) {
console.error('Send verification code error:', err)
error.value = '发送验证码失败,请稍后重试'
}
}
const handleSubmit = async () => {
if (formData.password !== formData.confirmPassword) {
error.value = '两次输入的密码不一致'
return
}
try {
error.value = ''
loading.value = true
// TODO: 调用重置密码API
// 重置成功后跳转到登录页
router.push('/login')
} catch (err) {
console.error('Reset password error:', err)
error.value = '重置密码失败,请稍后重试'
} finally {
loading.value = false
}
}
</script>
......@@ -56,9 +56,9 @@
</div>
<div class="text-sm">
<a href="#" class="font-medium text-green-600 hover:text-green-500">
<router-link to="/forgotPwd" class="font-medium text-green-600 hover:text-green-500">
忘记密码?
</a>
</router-link>
</div>
</div>
......