Files
qitongxue-admin/src/views/Login.vue
2026-09-29 10:58:25 +08:00

196 lines
5.3 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>
import { reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import { useUserStore } from '@/stores/user'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const loading = ref(false)
const form = reactive({ username: '', password: '', remember: true })
async function onSubmit() {
if (!form.username || !form.password) {
message.warning('请输入账号和密码')
return
}
loading.value = true
try {
await userStore.login(
{ username: form.username, password: form.password, remember: form.remember },
() => router.replace(route.query.redirect || '/'),
)
} catch {
/* 业务错误已由 request 拦截器提示 */
} finally {
loading.value = false
}
}
</script>
<template>
<div class="login">
<div class="app-bg-blobs"><span class="app-bg-blobs__mid"></span></div>
<!-- 背景贴纸装饰(手绘描边风,纯装饰) -->
<svg viewBox="0 0 64 64" class="login__doodle login__doodle--star">
<path d="M32 6l7.6 15.9 17.4 2.3-12.8 12.1 3.3 17.2L32 45l-15.5 7.5 3.3-17.2L7 24.2l17.4-2.3z"
fill="#FFC53D" stroke="#3D2A1E" stroke-width="3" stroke-linejoin="round" />
</svg>
<svg viewBox="0 0 64 64" class="login__doodle login__doodle--star2">
<path d="M32 6l7.6 15.9 17.4 2.3-12.8 12.1 3.3 17.2L32 45l-15.5 7.5 3.3-17.2L7 24.2l17.4-2.3z"
fill="#3ECFB2" stroke="#3D2A1E" stroke-width="3" stroke-linejoin="round" />
</svg>
<svg viewBox="0 0 64 64" class="login__doodle login__doodle--rainbow">
<path d="M9 45a23 23 0 0 1 46 0" fill="none" stroke="#FF6B35" stroke-width="7" stroke-linecap="round" />
<path d="M17 45a15 15 0 0 1 30 0" fill="none" stroke="#FFC53D" stroke-width="7" stroke-linecap="round" />
<path d="M25 45a7 7 0 0 1 14 0" fill="none" stroke="#3ECFB2" stroke-width="7" stroke-linecap="round" />
</svg>
<svg viewBox="0 0 64 64" class="login__doodle login__doodle--bolt">
<path d="M36 4L14 36h12l-4 24 24-34H32l4-22z" fill="#FF7BA9" stroke="#3D2A1E" stroke-width="3" stroke-linejoin="round" />
</svg>
<div class="login__card glass glass-strong">
<div class="login__brand">
<div class="login__logo">
<svg viewBox="0 0 64 64" class="w-8 h-8">
<path d="M32 4c9 7 13 17 13 27l-5 9H24l-5-9C19 21 23 11 32 4z" fill="#fff" />
<circle cx="32" cy="24" r="6" fill="#FFE8DC" />
</svg>
</div>
<div class="login__title">工具百宝箱</div>
<div class="login__subtitle">管理后台</div>
</div>
<a-form layout="vertical" @submit.prevent="onSubmit">
<a-form-item label="账号">
<a-input v-model:value="form.username" placeholder="请输入账号" size="large" />
</a-form-item>
<a-form-item label="密码">
<a-input-password v-model:value="form.password" placeholder="请输入密码" size="large" />
</a-form-item>
<a-checkbox v-model:checked="form.remember" class="mb-4">记住我</a-checkbox>
<a-button type="primary" html-type="submit" block size="large" :loading="loading">登 录</a-button>
</a-form>
<div class="login__tip">开发环境种子账号:admin / admin123</div>
</div>
</div>
</template>
<style scoped>
.login {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 0 var(--space-4);
overflow: hidden;
background: linear-gradient(
135deg,
var(--brand-50) 0%,
var(--bg-page) 45%,
var(--brand-100) 100%
);
}
.login__doodle {
position: absolute;
opacity: 0.8;
pointer-events: none;
}
.login__doodle--star {
width: 96px;
height: 96px;
left: 12%;
top: 16%;
animation: login-pulse 3.2s ease-in-out infinite;
}
.login__doodle--star2 {
width: 64px;
height: 64px;
right: 14%;
top: 24%;
}
.login__doodle--rainbow {
width: 80px;
height: 80px;
left: 18%;
bottom: 14%;
}
.login__doodle--bolt {
width: 56px;
height: 56px;
right: 18%;
bottom: 18%;
}
@keyframes login-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.06); }
}
.login__card {
position: relative;
z-index: 1;
width: 100%;
max-width: 380px;
border-radius: var(--radius-lg);
padding: var(--space-8);
}
.login__brand {
margin-bottom: var(--space-8);
text-align: center;
}
.login__logo {
width: 56px;
height: 56px;
margin: 0 auto var(--space-4);
border-radius: var(--radius-lg);
background: linear-gradient(135deg, var(--brand-400), var(--brand-600));
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-3);
}
.login__title {
font-size: var(--font-2xl);
font-weight: 800;
letter-spacing: -0.01em;
color: var(--text-1);
}
.login__subtitle {
margin-top: var(--space-1);
font-size: var(--font-sm);
color: var(--text-3);
}
.login__tip {
margin-top: var(--space-6);
text-align: center;
font-size: var(--font-xs);
color: var(--text-3);
}
.login :deep(.ant-btn-primary) {
background: var(--color-primary);
border-color: var(--color-primary);
box-shadow: none;
}
.login :deep(.ant-btn-primary:not(:disabled):hover) {
background: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
</style>