196 lines
5.3 KiB
Vue
196 lines
5.3 KiB
Vue
<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>
|