Files
crawler-plugin/frontend-vue/src/pages/login/index.vue
2026-03-19 13:15:47 +08:00

149 lines
3.4 KiB
Vue

<template>
<PageShell theme="light">
<div class="login-page">
<div class="login-header">南日AI</div>
<el-card class="login-card" shadow="never">
<template #header>
<div class="login-title">登录</div>
</template>
<el-alert
v-if="errorMessage"
:title="errorMessage"
type="error"
:closable="false"
class="login-alert"
/>
<el-form label-width="72px" @submit.prevent="handleSubmit">
<el-form-item label="用户名">
<el-input v-model="form.username" placeholder="请输入用户名" autofocus />
</el-form-item>
<el-form-item label="密码">
<el-input
v-model="form.password"
type="password"
show-password
placeholder="请输入密码"
@keydown.enter="handleSubmit"
/>
</el-form-item>
<el-button type="primary" :loading="submitting" class="login-button" @click="handleSubmit">
登录
</el-button>
</el-form>
</el-card>
</div>
</PageShell>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { checkAuth, login } from '@/shared/api/auth'
import { getBootstrapState } from '@/shared/utils/bootstrap'
const router = useRouter()
const bootstrap = getBootstrapState()
const submitting = ref(false)
const errorMessage = ref(bootstrap.error || '')
const form = reactive({
username: '',
password: '',
})
onMounted(async () => {
try {
const result = await checkAuth()
if (!result.logged_in) return
const target = result.redirect || '/home'
if (target.startsWith('/')) {
await router.push(target)
} else {
window.location.href = target
}
} catch {
// ignore auth check failures during bootstrap
}
})
const handleSubmit = async () => {
if (!form.username || !form.password) {
errorMessage.value = '请输入用户名和密码'
return
}
submitting.value = true
errorMessage.value = ''
try {
const result = await login(form.username, form.password)
if (result.success) {
const target = result.redirect || '/home'
if (target.startsWith('/')) {
await router.push(target)
} else {
window.location.href = target
}
return
}
errorMessage.value = result.error || '登录失败'
} catch (error) {
errorMessage.value = error instanceof Error ? error.message : '登录失败'
} finally {
submitting.value = false
}
}
</script>
<style scoped>
.login-page {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 16px;
background: linear-gradient(180deg, #dce8f1 0%, #eef4f8 100%);
}
.login-header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 56px;
display: flex;
align-items: center;
padding: 0 24px;
background: rgba(255, 255, 255, 0.6);
color: #303133;
font-size: 18px;
font-weight: 600;
}
.login-card {
width: 100%;
max-width: 420px;
border-radius: var(--radius-lg);
border: 1px solid var(--color-border-light);
box-shadow: var(--shadow-card);
}
.login-title {
text-align: center;
font-size: 24px;
font-weight: 600;
}
.login-alert {
margin-bottom: 16px;
}
.login-button {
width: 100%;
}
</style>