Files
pokopiawiki.tootaio.com/frontend/src/views/LoginView.vue
xiaomai fa656a8d02 refactor(auth): migrate fully to HTTP-only cookie sessions
Remove client-side token storage and Authorization header injection
Backend login now only returns user data, omitting the session token
Remove Authorization from backend CORS allowed headers
Clean up obsolete VITE_* environment variable fallbacks
Update Modal component to use Vue useId() instead of Math.random()
2026-05-06 17:15:46 +08:00

86 lines
2.7 KiB
Vue

<script setup lang="ts">
import { Icon } from '@iconify/vue';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import PageHeader from '../components/PageHeader.vue';
import StatusMessage from '../components/StatusMessage.vue';
import { iconLogin } from '../icons';
import { api, notifyAuthChange } from '../services/api';
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const email = ref('');
const password = ref('');
const rememberMe = ref(false);
const busy = ref(false);
const errorMessage = ref('');
async function submitLogin() {
busy.value = true;
errorMessage.value = '';
try {
await api.login({
email: email.value,
password: password.value,
rememberMe: rememberMe.value
});
notifyAuthChange();
const redirect =
typeof route.query.redirect === 'string' && route.query.redirect.startsWith('/')
? route.query.redirect
: '/pokemon';
await router.push(redirect);
} catch (error) {
errorMessage.value = error instanceof Error && error.message ? error.message : t('auth.loginFailed');
} finally {
busy.value = false;
}
}
</script>
<template>
<section class="auth-page">
<div class="auth-panel">
<PageHeader :title="t('auth.loginTitle')" :subtitle="t('auth.loginSubtitle')">
<template #kicker>{{ t('auth.accountAccess') }}</template>
</PageHeader>
<form class="auth-form" @submit.prevent="submitLogin">
<div class="field">
<label for="login-email">{{ t('auth.email') }}</label>
<input id="login-email" v-model="email" autocomplete="email" required type="email" />
</div>
<div class="field">
<label for="login-password">{{ t('auth.password') }}</label>
<input id="login-password" v-model="password" autocomplete="current-password" required type="password" />
</div>
<div class="auth-options">
<label class="check-row auth-options__remember">
<input v-model="rememberMe" type="checkbox" />
{{ t('auth.rememberMe') }}
</label>
<RouterLink to="/forgot-password">{{ t('auth.forgotPassword') }}</RouterLink>
</div>
<StatusMessage v-if="errorMessage" variant="danger">{{ errorMessage }}</StatusMessage>
<button class="ui-button ui-button--primary" :disabled="busy" type="submit">
<Icon :icon="iconLogin" class="ui-icon" aria-hidden="true" />
{{ busy ? t('auth.loggingIn') : t('nav.login') }}
</button>
</form>
<p class="auth-switch">
{{ t('auth.noAccount') }}
<RouterLink to="/register">{{ t('nav.register') }}</RouterLink>
</p>
</div>
</section>
</template>