Files
ApiServer-Web-admin_dashboa…/src/views/Login.vue
T
2026-01-20 17:54:45 +08:00

418 lines
8.1 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.
<template>
<div class="login-container">
<!-- 登录表单区域 -->
<div class="login-form-container">
<div class="login-content">
<div class="login-header">
<h1 class="system-name">零零七云计算后台控制面板</h1>
</div>
<h2 class="welcome-text">欢迎回来</h2>
<p class="sub-title">登录您的账户以继续访问</p>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="请输入用户名"
:prefix-icon="User"
clearable
size="large"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
:prefix-icon="Lock"
show-password
clearable
size="large"
@keyup.enter="handleLogin"
/>
</el-form-item>
<div class="login-options">
<el-checkbox v-model="rememberMe">
<span class="remember-text">记住我</span>
</el-checkbox>
<el-button link type="primary" class="forget-btn" @click="forgetPassword">忘记密码</el-button>
</div>
<el-button
type="primary"
class="login-button"
:loading="loading"
@click="handleLogin"
>
{{ loading ? '登录中...' : '登录' }}
</el-button>
</el-form>
</div>
<div class="login-footer">
<p class="copyright">Copyright © 2025 零零七云计算 All Rights Reserved.</p>
</div>
</div>
<!-- 装饰区域 -->
<div class="login-decoration">
<div class="decoration-content">
<h2 class="slogan">新一代企业级管理系统</h2>
<p class="description">基于 Vue3 + Element Plus 的现代化管理系统</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { User, Lock, Monitor, Connection } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import {getUserInfo, userLogin} from "@/api/login.js";
const router = useRouter()
const loginFormRef = ref(null)
const loading = ref(false)
const rememberMe = ref(false)
const loginForm = reactive({
username: '',
password: ''
})
const loginRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
// password: [
// { required: true, message: '请输入密码', trigger: 'blur' },
// { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
// ]
}
const forgetPassword = () => {
ElMessage.warning('请前往官网重置密码!')
}
const handleLogin = () => {
loginFormRef.value?.validate(async valid =>{
window.localStorage.removeItem('token')
window.localStorage.removeItem('tokenExpire')
window.localStorage.removeItem('userInfo')
if (valid) {
loading.value = true
let resp = await userLogin(loginForm.username, loginForm.password)
console.log("login:",resp)
loading.value = false
if(resp.code === 200){
// 保存token和过期时间
window.localStorage.setItem('token', resp.data.token)
if (resp.data.expire) {
window.localStorage.setItem('tokenExpire', resp.data.expire.toString())
}
let userInfo = await getUserInfo()
if(userInfo.data.is_admin){
// 保存用户信息到localStorage
window.localStorage.setItem('userInfo', JSON.stringify(userInfo.data))
await router.push('/dashboard')
} else {
ElMessage.warning('你不是管理员,不能登陆到后台控制面板')
}
}else{
ElMessage.error(resp.message)
}
}
})
}
</script>
<style scoped>
.login-container {
display: flex;
min-height: 100vh;
background-color: #f5f7fa;
}
.login-form-container {
flex: 1;
display: flex;
flex-direction: column;
padding: 40px;
background: #fff;
position: relative;
max-width: 520px;
}
.login-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
max-width: 360px;
margin: 0 auto;
width: 100%;
}
.login-header {
display: flex;
align-items: center;
margin-bottom: 48px;
}
.logo {
width: 48px;
height: 48px;
margin-right: 16px;
}
.system-name {
font-size: 28px;
font-weight: 600;
background: linear-gradient(45deg, #1890ff, #36cfc9);
-webkit-background-clip: text;
color: transparent;
margin: 0;
}
.welcome-text {
font-size: 28px;
font-weight: 600;
color: #1f2937;
margin: 0 0 8px 0;
}
.sub-title {
font-size: 16px;
color: #6b7280;
margin: 0 0 40px 0;
}
.login-form :deep(.el-input) {
--el-input-height: 44px;
margin-bottom: 4px;
}
.login-form :deep(.el-input__wrapper) {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border-radius: 8px;
}
.login-form :deep(.el-input__inner) {
font-size: 15px;
}
.login-options {
display: flex;
justify-content: space-between;
align-items: center;
margin: 16px 0 24px;
}
.remember-text {
color: #4b5563;
font-size: 14px;
}
.forget-btn {
font-size: 14px;
padding: 0;
}
.login-button {
width: 100%;
height: 44px;
font-size: 16px;
border-radius: 8px;
margin-bottom: 32px;
}
.divider {
display: flex;
align-items: center;
margin: 0 0 24px;
color: #9ca3af;
font-size: 14px;
}
.divider::before,
.divider::after {
content: '';
flex: 1;
height: 1px;
background: #e5e7eb;
}
.divider-text {
padding: 0 16px;
}
.social-login {
display: flex;
justify-content: center;
gap: 16px;
}
.social-btn {
width: 44px;
height: 44px;
border-radius: 8px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.social-btn:hover {
transform: translateY(-2px);
}
.wechat {
background: #07c160;
border-color: #07c160;
color: white;
}
.dingtalk {
background: #1890ff;
border-color: #1890ff;
color: white;
}
.github {
background: #24292e;
border-color: #24292e;
color: white;
}
.login-footer {
text-align: center;
color: #9ca3af;
font-size: 14px;
margin-top: 40px;
}
/* 装饰区域样式 */
.login-decoration {
flex: 1.5;
background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%);
padding: 60px;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
}
.login-decoration::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('../assets/grid.svg') repeat;
opacity: 0.1;
}
.decoration-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
color: white;
position: relative;
z-index: 1;
}
.slogan {
font-size: 42px;
font-weight: 600;
margin: 0 0 16px;
line-height: 1.2;
}
.description {
font-size: 18px;
opacity: 0.9;
margin: 0 0 60px;
}
.feature-list {
display: grid;
gap: 32px;
max-width: 600px;
}
.feature-item {
display: flex;
align-items: flex-start;
gap: 16px;
}
.feature-icon {
font-size: 24px;
padding: 12px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
}
.feature-info h3 {
font-size: 18px;
margin: 0 0 8px;
}
.feature-info p {
font-size: 14px;
opacity: 0.8;
margin: 0;
}
.decoration-footer {
text-align: center;
}
.tech-stack {
display: flex;
justify-content: center;
gap: 24px;
}
.tech-icon {
height: 32px;
opacity: 0.9;
}
@media (max-width: 1200px) {
.login-decoration {
display: none;
}
.login-form-container {
max-width: none;
}
}
@media (max-width: 640px) {
.login-form-container {
padding: 20px;
}
.welcome-text {
font-size: 24px;
}
.sub-title {
font-size: 14px;
}
}
</style>