Files
chat/frontend/src/views/auth/ForgotPasswordView.vue
T
2026-06-14 11:16:42 +08:00

111 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="forgot-page">
<div class="forgot-card">
<div class="logo">🌿</div>
<h2>找回密码</h2>
<p class="subtitle">输入注册邮箱验证码将发送开发期打印在服务器日志</p>
<n-form>
<n-form-item label="注册邮箱">
<n-input v-model:value="email" placeholder="your@email.com" size="large" />
</n-form-item>
<template v-if="step === 2">
<n-form-item label="验证码(见后端日志)">
<n-input v-model:value="code" placeholder="6 位验证码" size="large" maxlength="6" />
</n-form-item>
<n-form-item label="新密码">
<n-input v-model:value="newPassword" type="password" show-password-on="click" placeholder="新密码" size="large" />
</n-form-item>
</template>
<n-button v-if="step === 1" type="primary" block size="large" :loading="sending" @click="sendCode">
发送验证码
</n-button>
<n-button v-else type="primary" block size="large" :loading="resetting" @click="reset">
重置密码
</n-button>
</n-form>
<div class="footer">
<router-link to="/login"> 返回登录</router-link>
</div>
<div v-if="devHint" class="dev-hint">
💡 开发模式验证码已打印到后端控制台docker compose logs backend
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useMessage } from 'naive-ui'
import api from '@/api/client'
const router = useRouter()
const message = useMessage()
const email = ref('')
const code = ref('')
const newPassword = ref('')
const step = ref(1)
const sending = ref(false)
const resetting = ref(false)
const devHint = ref(false)
async function sendCode() {
if (!email.value.trim()) { message.error('请输入邮箱'); return }
sending.value = true
try {
await api.post('/auth/forgot', { email: email.value.trim() })
step.value = 2
devHint.value = true
message.success('验证码已发送(开发期见后端日志)')
} catch (e: any) {
message.error(e.response?.data?.detail || '发送失败')
} finally {
sending.value = false
}
}
async function reset() {
if (!code.value || !newPassword.value) { message.error('请填写完整'); return }
resetting.value = true
try {
await api.post('/auth/reset', {
email: email.value.trim(),
code: code.value.trim(),
new_password: newPassword.value,
})
message.success('密码已重置,请重新登录')
router.push('/login')
} catch (e: any) {
message.error(e.response?.data?.detail || '重置失败')
} finally {
resetting.value = false
}
}
</script>
<style scoped>
.forgot-page {
min-height: 100vh; display: flex; align-items: center; justify-content: center;
background: linear-gradient(135deg, #009688 0%, #26A69A 100%);
padding: 20px;
}
.forgot-card {
width: 400px; max-width: 100%; background: var(--color-surface, #fff);
border-radius: 20px; padding: 36px 32px; box-shadow: 0 12px 40px rgba(0,0,0,0.15);
}
.logo { font-size: 48px; text-align: center; }
.forgot-card h2 { text-align: center; margin: 8px 0 4px; color: var(--color-primary-dark, #00796B); }
.subtitle { text-align: center; font-size: 13px; color: var(--color-text-hint, #999); margin-bottom: 24px; }
.footer { text-align: center; margin-top: 20px; }
.footer a { font-size: 13px; color: var(--color-primary, #009688); text-decoration: none; }
.dev-hint {
margin-top: 16px; padding: 10px; background: #FFF3E0; border-radius: 8px;
font-size: 12px; color: #E65100; text-align: center;
}
</style>