Files
opencloud/src/features/auth/views/ResetPasswordView.vue
T

154 lines
6.1 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.
<script setup lang="ts">
import { computed, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { NAlert, NButton, NCard, NForm, NFormItem, NInput, NResult } from 'naive-ui'
import { AuthClientError } from '@/features/auth/authClient'
import { useAuthStore } from '@/features/auth/stores/auth'
const authStore = useAuthStore()
const route = useRoute()
const router = useRouter()
const email = typeof route.query.email === 'string' ? route.query.email : ''
const token = typeof route.query.token === 'string' ? route.query.token : ''
const password = ref('')
const confirmPassword = ref('')
const error = ref(!token ? '密码重置链接缺少必要信息,请重新发送邮件。' : '')
const invalid = ref(!token)
const success = ref(false)
const loading = ref(false)
const countdown = ref(5)
let timer: ReturnType<typeof setInterval> | null = null
if (route.query.token !== undefined || route.query.email !== undefined) {
window.history.replaceState(window.history.state, '', route.path)
}
const canSubmit = computed(
() => !invalid.value && password.value.length >= 8 && password.value === confirmPassword.value,
)
function startCountdown() {
timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
if (timer) clearInterval(timer)
void router.push({ path: '/login', query: { email } })
}
}, 1000)
}
async function submit() {
error.value = ''
if (password.value.length < 8) {
error.value = '新密码至少需要 8 位'
return
}
if (password.value !== confirmPassword.value) {
error.value = '两次输入的密码不一致'
return
}
loading.value = true
try {
await authStore.resetPassword(token, password.value)
success.value = true
startCountdown()
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : '密码重置失败,请稍后重试'
if (e instanceof AuthClientError && (e.status === 400 || e.code === 'INVALID_TOKEN'))
invalid.value = true
} finally {
loading.value = false
}
}
onUnmounted(() => {
if (timer) clearInterval(timer)
})
</script>
<template>
<div class="min-h-[calc(100vh-4rem)] px-4 py-10">
<div class="mx-auto max-w-2xl">
<NCard class="oc-panel-card-xl">
<NResult
v-if="success"
status="success"
title="密码已重置"
description="请使用新密码重新登录。"
>
<template #footer>
<div class="space-y-4">
<p class="text-sm text-slate-500">
{{ countdown }} 秒后自动前往登录页面
</p>
<NButton
type="default"
class="oc-panel-button oc-panel-button--teal"
@click="router.push({ path: '/login', query: { email } })"
>立即登录</NButton
>
</div>
</template>
</NResult>
<template v-else>
<div class="mb-8">
<div class="text-sm uppercase tracking-[0.22em] text-slate-500">
Password Recovery
</div>
<h1 class="mt-3 text-3xl font-bold text-slate-900">设置新密码</h1>
<p class="mt-2 text-sm text-slate-500">
提交成功后,此重置链接和现有登录会话都会失效。
</p>
</div>
<NForm @submit.prevent="submit">
<NFormItem label="新密码"
><NInput
v-model:value="password"
type="password"
show-password-on="click"
placeholder="至少 8 位"
:disabled="invalid"
:input-props="{
autocomplete: 'new-password',
name: 'new-password',
required: true,
}"
/></NFormItem>
<NFormItem label="确认新密码"
><NInput
v-model:value="confirmPassword"
type="password"
show-password-on="click"
placeholder="再次输入新密码"
:disabled="invalid"
:input-props="{
autocomplete: 'new-password',
name: 'confirm-new-password',
required: true,
}"
/></NFormItem>
<NAlert v-if="error" type="error" class="mb-4">{{ error }}</NAlert>
<RouterLink
v-if="invalid"
:to="{ path: '/forgot-password', query: { email } }"
class="mb-4 block text-sm font-semibold text-teal-700 hover:text-teal-800"
>重新发送密码重置邮件</RouterLink
>
<NButton
attr-type="submit"
type="primary"
block
size="large"
class="oc-primary-button oc-primary-button--teal"
:disabled="!canSubmit"
:loading="loading"
>保存新密码</NButton
>
</NForm>
</template>
</NCard>
</div>
</div>
</template>