Files
YG_FT/frontend/src/views/login/LoginView.vue
2026-07-27 09:12:47 +08:00

576 lines
11 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, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
const router = useRouter()
const auth = useAuthStore()
const loginFormRef = ref<FormInstance>()
const loading = ref(false)
const loginForm = reactive({
username: '',
password: '',
})
const rules: FormRules = {
username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
}
async function handleLogin() {
if (!loginFormRef.value || loading.value) return
try {
const valid = await loginFormRef.value.validate()
if (!valid) return
loading.value = true
await auth.login(loginForm.username, loginForm.password)
ElMessage.success('登录成功')
await router.push('/dashboard')
} catch {
// 拦截器已提示错误
} finally {
loading.value = false
}
}
</script>
<template>
<div class="login-page">
<section class="login-visual" aria-labelledby="platform-title">
<div class="login-visual-content">
<div class="login-visual-copy">
<h1 id="platform-title">远光软件微调平台</h1>
<p>大模型微调评测与推理的一体化工作台</p>
</div>
<div class="login-visual-footer" aria-label="平台核心能力">
<span>数据准备</span>
<span aria-hidden="true">·</span>
<span>模型训练</span>
<span aria-hidden="true">·</span>
<span>效果评测</span>
</div>
</div>
</section>
<main class="login-panel">
<div class="brand-lockup" role="img" aria-label="远光软件">
<span class="brand-logo-crop brand-logo-crop-mark" aria-hidden="true">
<img src="/logo.png" alt="" />
</span>
<span class="brand-logo-crop brand-logo-crop-wordmark" aria-hidden="true">
<img src="/logo.png" alt="" />
</span>
</div>
<div class="login-form-wrap">
<div class="login-heading">
<span>账号登录</span>
<h2>欢迎回来</h2>
<p>登录后继续使用微调平台</p>
</div>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="rules"
label-position="top"
size="large"
@keyup.enter="handleLogin"
>
<el-form-item label="账号" prop="username">
<el-input
v-model="loginForm.username"
placeholder="请输入账号"
clearable
/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
show-password
/>
</el-form-item>
<div class="login-options">
<el-checkbox>记住密码</el-checkbox>
<el-link type="primary" :underline="false">忘记密码</el-link>
</div>
<el-button
type="primary"
class="login-btn"
:loading="loading"
@click="handleLogin"
>
登录
</el-button>
</el-form>
</div>
<footer>© 2026 远光软件</footer>
</main>
</div>
</template>
<style scoped lang="scss">
.login-page {
height: 100vh;
height: 100dvh;
display: grid;
grid-template-columns: minmax(0, 58fr) minmax(500px, 42fr);
overflow: hidden;
background: #fcfcfe;
}
.login-visual {
min-height: 0;
height: 100%;
position: relative;
color: #fff;
background-color: #121127;
background-image: url('@/assets/login-hero-flow.jpg');
background-size: cover;
background-position: center;
overflow: hidden;
}
.login-visual::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(12, 12, 35, 0.2), transparent 36%, rgba(12, 12, 35, 0.2));
pointer-events: none;
}
.login-visual-content {
min-height: 0;
height: 100%;
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
padding: clamp(80px, 10vh, 132px) clamp(56px, 6vw, 112px) clamp(56px, 7vh, 84px);
}
.login-visual-copy {
max-width: 680px;
margin-top: clamp(80px, 11vh, 136px);
h1 {
margin: 0;
font-size: clamp(38px, 3.8vw, 62px);
line-height: 1.2;
letter-spacing: -0.035em;
font-weight: 700;
text-wrap: balance;
}
p {
margin: 22px 0 0;
color: rgba(226, 232, 240, 0.76);
font-size: clamp(17px, 1.5vw, 24px);
line-height: 1.6;
letter-spacing: 0.01em;
}
}
.login-visual-footer {
width: min(100%, 640px);
margin-top: auto;
display: grid;
grid-template-columns: 1fr auto 1fr auto 1fr;
align-items: center;
gap: 24px;
color: rgba(226, 232, 240, 0.72);
font-size: 16px;
letter-spacing: 0.04em;
span:nth-child(3),
span:nth-child(5) {
text-align: center;
}
span:nth-child(even) {
color: rgba(199, 210, 254, 0.8);
}
}
.login-panel {
min-height: 0;
height: 100%;
position: relative;
display: grid;
place-items: center;
padding: 120px clamp(48px, 5vw, 88px) 96px;
border-left: 1px solid #e8eaf1;
background: #fcfcfe;
}
.brand-lockup {
position: absolute;
top: 42px;
right: clamp(36px, 4vw, 64px);
display: flex;
align-items: center;
gap: 9px;
}
.brand-logo-crop {
position: relative;
display: block;
overflow: hidden;
img {
position: absolute;
left: 0;
width: 100%;
max-width: none;
}
}
.brand-logo-crop-mark {
width: 40px;
height: 38px;
img {
top: 0;
}
}
.brand-logo-crop-wordmark {
width: 96px;
height: 25px;
img {
bottom: 0;
}
}
.login-form-wrap {
width: min(100%, 480px);
}
.login-heading {
margin-bottom: 42px;
> span {
display: block;
margin-bottom: 24px;
color: #4f46e5;
font-size: 16px;
line-height: 1;
font-weight: 600;
}
h2 {
margin: 0;
color: #17182c;
font-size: clamp(38px, 3vw, 48px);
line-height: 1.15;
letter-spacing: -0.035em;
font-weight: 700;
}
p {
margin: 14px 0 0;
color: #8a93a6;
font-size: 16px;
line-height: 1.6;
}
}
:deep(.el-form-item) {
margin-bottom: 26px;
}
:deep(.el-form-item__label) {
height: auto;
margin-bottom: 10px;
padding: 0;
color: #272a3a;
font-size: 15px;
line-height: 1.4;
font-weight: 600;
}
:deep(.el-input__wrapper) {
min-height: 54px;
padding: 0 18px;
border-radius: 8px;
background: #fff;
box-shadow: 0 0 0 1px #cfd4df inset;
transition: box-shadow 160ms ease, background-color 160ms ease;
&:hover {
box-shadow: 0 0 0 1px #aeb6c6 inset;
}
&.is-focus {
box-shadow: 0 0 0 1px #4f46e5 inset, 0 0 0 3px rgba(79, 70, 229, 0.12);
}
}
:deep(.el-input__inner) {
font-size: 15px;
color: #1e293b;
&::placeholder {
color: #a4acba;
}
}
.login-options {
display: flex;
align-items: center;
justify-content: space-between;
margin: -2px 0 30px;
:deep(.el-checkbox__label) {
color: #556070;
font-size: 14px;
}
:deep(.el-link) {
font-weight: 500;
color: #4f46e5;
&:hover {
color: #4338ca;
}
}
}
.login-btn {
width: 100%;
height: 54px;
font-size: 16px;
font-weight: 600;
border-radius: 8px;
border: none;
background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%);
color: #fff;
box-shadow: 0 10px 22px rgba(79, 70, 229, 0.18);
transition: transform 160ms ease, box-shadow 160ms ease, background 160ms ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 14px 26px rgba(79, 70, 229, 0.24);
background: linear-gradient(135deg, #4338ca 0%, #4f46e5 100%);
}
&:active {
transform: translateY(0);
box-shadow: 0 6px 14px rgba(79, 70, 229, 0.2);
}
}
.login-panel footer {
position: absolute;
right: clamp(36px, 4vw, 64px);
bottom: 34px;
color: #9aa2b2;
font-size: 13px;
}
@media (max-width: 1440px) {
.login-page {
grid-template-columns: minmax(0, 55fr) minmax(500px, 45fr);
}
.login-visual-content {
padding-inline: clamp(52px, 5vw, 72px);
}
.login-visual-copy {
max-width: 580px;
h1 {
font-size: clamp(42px, 3.6vw, 52px);
}
p {
font-size: clamp(16px, 1.45vw, 20px);
}
}
.login-panel {
padding-inline: clamp(44px, 4.2vw, 64px);
}
.login-form-wrap {
max-width: 440px;
}
}
@media (max-width: 1180px) {
.login-page {
grid-template-columns: minmax(0, 52fr) minmax(470px, 48fr);
}
.login-visual-content {
padding-inline: 48px;
}
.login-visual-copy h1 {
font-size: 42px;
}
.login-visual-footer {
gap: 14px;
font-size: 14px;
}
}
@media (max-height: 820px) and (min-width: 901px) {
.login-visual {
background-position: center 54%;
}
.login-visual-content {
padding-top: 48px;
padding-bottom: 38px;
}
.login-visual-copy {
max-width: 560px;
margin-top: clamp(52px, 9vh, 72px);
h1 {
font-size: clamp(38px, 3.4vw, 46px);
}
p {
margin-top: 14px;
font-size: clamp(15px, 1.35vw, 18px);
}
}
.login-visual-footer {
gap: 14px;
font-size: 14px;
}
.login-panel {
padding-top: 78px;
padding-bottom: 56px;
}
.brand-lockup {
top: 24px;
}
.login-heading {
margin-bottom: 26px;
> span {
margin-bottom: 16px;
font-size: 15px;
}
h2 {
font-size: 38px;
}
p {
margin-top: 8px;
font-size: 15px;
}
}
:deep(.el-form-item) {
margin-bottom: 18px;
}
:deep(.el-form-item__label) {
margin-bottom: 7px;
font-size: 14px;
}
:deep(.el-input__wrapper) {
min-height: 48px;
}
.login-options {
margin-bottom: 20px;
}
.login-btn {
height: 50px;
}
.login-panel footer {
bottom: 18px;
}
}
@media (max-width: 900px) {
.login-page {
display: block;
height: auto;
min-height: 100vh;
min-height: 100dvh;
overflow: auto;
}
.login-visual {
display: none;
}
.login-panel {
height: auto;
min-height: 100vh;
min-height: 100dvh;
padding: 116px 32px 84px;
border-left: 0;
}
.login-panel footer {
right: 50%;
transform: translateX(50%);
white-space: nowrap;
}
}
@media (max-width: 560px) {
.login-panel {
display: flex;
align-items: center;
padding: 100px 24px 72px;
}
.brand-lockup {
top: 28px;
right: 24px;
transform: scale(0.88);
transform-origin: top right;
}
.login-heading {
margin-bottom: 32px;
> span {
margin-bottom: 18px;
}
h2 {
font-size: 36px;
}
}
:deep(.el-form-item) {
margin-bottom: 22px;
}
.login-options {
margin-bottom: 26px;
}
}
</style>