Files
opencloud/src/views/auth/LoginView.vue
T

151 lines
5.4 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 { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { NAlert, NButton, NCard, NForm, NFormItem, NInput } from 'naive-ui'
import { AuthClientError } from '@/lib/authClient'
import { useAuthStore } from '@/stores/auth'
const authStore = useAuthStore()
const router = useRouter()
const route = useRoute()
const email = ref(typeof route.query.email === 'string' ? route.query.email : '')
const password = ref('')
const error = ref('')
const loading = ref(false)
const needsEmailConfirmation = ref(false)
function getSafeRedirect(value: unknown) {
if (typeof value !== 'string' || !value.startsWith('/')) return '/'
try {
const target = new URL(value, window.location.origin)
return target.origin === window.location.origin
? `${target.pathname}${target.search}${target.hash}`
: '/'
} catch {
return '/'
}
}
async function handleLogin() {
error.value = ''
needsEmailConfirmation.value = false
loading.value = true
try {
await authStore.login(email.value, password.value)
void router.push(getSafeRedirect(route.query.redirect))
} catch (e: unknown) {
needsEmailConfirmation.value = e instanceof AuthClientError && e.code === 'EMAIL_NOT_VERIFIED'
error.value = e instanceof Error ? e.message : '登录失败,请稍后重试'
} finally {
loading.value = false
}
}
</script>
<template>
<div class="min-h-[calc(100vh-4rem)] px-4 py-10">
<div class="mx-auto grid min-w-0 max-w-6xl gap-8 lg:grid-cols-[1.1fr_0.9fr] lg:items-stretch">
<section class="oc-panel-card-xl flex min-w-0 h-full flex-col justify-between overflow-hidden border border-slate-200 bg-[linear-gradient(135deg,#f2faf6_0%,#ffffff_48%,#eff6ff_100%)] p-6 sm:p-8">
<div>
<p class="text-sm uppercase tracking-[0.26em] text-teal-700">Sky Log In</p>
<h1 class="mt-4 max-w-xl break-words text-4xl font-black leading-[1.05] text-slate-900 sm:text-5xl">
登录后继续记录
<span class="block text-teal-700">你眼中的每一朵云</span>
</h1>
<!-- <p class="mt-6 max-w-lg text-base leading-8 text-slate-600">
在地图、百科和画廊之间浏览观测记录。上传新的云图后,审核通过的作品会进入公开画廊与地图。
</p> -->
</div>
<div class="mt-8 grid min-w-0 grid-cols-2 gap-4">
<div class="min-w-0 border border-slate-200 bg-white px-4 py-3">
<div class="oc-stat-label">Collection</div>
<div class="oc-stat-value">10</div>
<div class="oc-field-help">基础云属资料</div>
</div>
<div class="min-w-0 border border-slate-200 bg-white px-4 py-3">
<div class="oc-stat-label">Mode</div>
<div class="oc-stat-value">Atlas</div>
<div class="oc-field-help">地图、百科、画廊一体</div>
</div>
</div>
</section>
<NCard class="oc-panel-card-xl min-w-0 h-full">
<div class="mb-8">
<div class="text-sm uppercase tracking-[0.22em] text-slate-500">Member Access</div>
<h2 class="mt-3 text-3xl font-bold text-slate-900">登录 OpenCloud</h2>
<p class="mt-2 text-sm text-slate-500">输入邮箱和密码,继续你的天空档案。</p>
</div>
<NForm @submit.prevent="handleLogin">
<NFormItem label="邮箱">
<NInput
v-model:value="email"
type="text"
:input-props="{
autocomplete: 'username',
inputmode: 'email',
name: 'username',
required: true,
}"
placeholder="your@email.com"
/>
</NFormItem>
<NFormItem label="密码">
<NInput
v-model:value="password"
type="password"
show-password-on="click"
:input-props="{
autocomplete: 'current-password',
name: 'current-password',
required: true,
}"
placeholder="输入密码"
/>
</NFormItem>
<NAlert v-if="error" type="error" class="mb-4">
{{ error }}
<div v-if="needsEmailConfirmation" class="mt-2">
<RouterLink
:to="{ path: '/resend-confirmation', query: { email } }"
class="font-semibold text-rose-700 underline underline-offset-2"
>
重新发送验证邮件
</RouterLink>
</div>
</NAlert>
<NButton
attr-type="submit"
type="primary"
block
size="large"
class="oc-primary-button oc-primary-button--teal"
:loading="loading"
>
{{ loading ? '登录中...' : '登录' }}
</NButton>
</NForm>
<div class="mt-6 flex flex-wrap items-center justify-between gap-3 text-sm text-slate-500">
<span>
还没有账号?
<RouterLink to="/register" class="font-semibold text-sky-700 hover:text-sky-800">立即注册</RouterLink>
</span>
<RouterLink
:to="{ path: '/forgot-password', query: { email } }"
class="font-semibold text-teal-700 hover:text-teal-800"
>
忘记密码
</RouterLink>
</div>
</NCard>
</div>
</div>
</template>