Files
education/frontend/src/views/login.vue
T
2026-09-07 10:59:35 +08:00

450 lines
9.7 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">
<div class="login-card">
<aside class="brand-panel">
<div class="brand-main">
<div class="brand-mark" aria-hidden="true"></div>
<h1 class="brand-title">{{ title }}</h1>
<p class="brand-tagline">开课计划 · 课表编排 · 成绩管理 · 教学分析</p>
<div class="brand-illustration" aria-hidden="true">
<svg-icon icon-class="education" />
</div>
</div>
<p class="brand-footnote">仅限授权单位内部使用</p>
</aside>
<section class="form-panel">
<div class="form-heading">
<h2>用户登录</h2>
<p>请使用内网账号进入系统</p>
</div>
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" label-position="top" @submit.native.prevent>
<el-form-item label="用户名" prop="username">
<el-input
v-model="loginForm.username"
type="text"
auto-complete="off"
placeholder="请输入用户名"
/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="请输入密码"
show-password
@keyup.enter.native="handleLogin"
/>
</el-form-item>
<div class="form-extra">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<router-link v-if="register" class="register-link" to="/register">立即注册</router-link>
</div>
<el-form-item class="login-action">
<el-button
:loading="loading"
type="primary"
class="login-btn"
native-type="submit"
@click.native.prevent="handleLogin"
>
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
</el-form>
<p class="access-hint">登录后按账号权限进入对应业务请妥善保管账号与口令</p>
<div class="role-tags" aria-label="系统角色">
<span v-for="role in roles" :key="role.name" class="role-tag" :class="role.tone">{{ role.name }}</span>
</div>
<p class="form-footer">{{ footerContent }}</p>
</section>
</div>
</div>
</template>
<script>
import Cookies from "js-cookie"
import { encrypt, decrypt } from '@/utils/jsencrypt'
export default {
name: "Login",
data() {
return {
title: process.env.VUE_APP_TITLE,
footerContent: "Copyright © 2018-2026 教学管理信息系统 内部使用",
roles: [
{ name: "系统管理员", tone: "admin" },
{ name: "教务人员", tone: "affairs" },
{ name: "教研室", tone: "office" },
{ name: "教员", tone: "teacher" },
{ name: "学员", tone: "student" }
],
loginForm: {
username: "admin",
password: "admin123",
rememberMe: false
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: "请输入您的账号" }
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" }
]
},
loading: false,
register: false,
redirect: undefined
}
},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect
},
immediate: true
}
},
created() {
this.getCookie()
},
methods: {
getCookie() {
const username = Cookies.get("username")
const password = Cookies.get("password")
const rememberMe = Cookies.get('rememberMe')
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
}
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
if (this.loginForm.rememberMe) {
Cookies.set("username", this.loginForm.username, { expires: 30 })
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 })
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 })
} else {
Cookies.remove("username")
Cookies.remove("password")
Cookies.remove('rememberMe')
}
const loginData = {
username: this.loginForm.username,
password: this.loginForm.password
}
this.$store.dispatch("Login", loginData).then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(()=>{})
}).catch(() => {
this.loading = false
})
}
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.login {
display: flex;
justify-content: center;
align-items: center;
min-height: 100%;
padding: 28px 20px;
background:
radial-gradient(circle at 18% 16%, rgba(11, 107, 85, 0.28), transparent 42%),
radial-gradient(circle at 86% 82%, rgba(198, 155, 60, 0.10), transparent 36%),
#10241f;
}
.login-card {
display: flex;
width: min(1080px, 100%);
overflow: hidden;
background: linear-gradient(160deg, #0c7a62 0%, #0b6b55 46%, #084c3f 100%);
border-radius: 18px;
box-shadow: 0 22px 56px rgba(8, 36, 30, 0.32);
}
.brand-panel {
display: flex;
flex-direction: column;
position: relative;
width: 42%;
padding: 48px 40px 24px;
color: #ffffff;
background: transparent;
overflow: hidden;
}
.brand-main {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
min-height: 0;
padding-bottom: 12px;
}
.brand-mark {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 48px;
left: 40px;
width: 48px;
height: 48px;
color: #7a5410;
font-size: 26px;
font-weight: 700;
background: #e4c56a;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(16, 36, 28, 0.18);
}
.brand-title {
margin: 0 0 10px;
font-size: 32px;
font-weight: 700;
line-height: 1.25;
letter-spacing: 1px;
}
.brand-tagline {
margin: 0;
color: rgba(255, 255, 255, 0.78);
font-size: 13px;
letter-spacing: 0.4px;
}
.brand-illustration {
width: 116px;
height: 116px;
margin-top: 44px;
color: rgba(255, 255, 255, 0.12);
font-size: 116px;
line-height: 1;
transform: rotate(-6deg);
}
.brand-illustration .svg-icon {
display: block;
width: 100%;
height: 100%;
}
.brand-footnote {
margin: 16px 0 0;
color: rgba(255, 255, 255, 0.52);
font-size: 12px;
}
.form-panel {
display: flex;
flex-direction: column;
flex: 1;
padding: 56px 56px 28px;
background: #ffffff;
}
.form-heading {
margin-bottom: 32px;
}
.form-heading h2 {
margin: 0 0 8px;
color: #172b25;
font-size: 28px;
font-weight: 700;
letter-spacing: 0.5px;
}
.form-heading p {
margin: 0;
color: #7a8a85;
font-size: 14px;
}
.login-form {
::v-deep .el-form-item {
margin-bottom: 22px;
}
::v-deep .el-form-item__label {
float: none;
display: block;
padding: 0 0 8px;
line-height: 1.2;
color: #172b25;
font-size: 14px;
font-weight: 600;
}
::v-deep .el-form-item__content {
margin-left: 0 !important;
line-height: normal;
}
::v-deep .el-input__inner {
height: 48px;
padding: 0 16px;
color: #172b25;
background: #f3f6f5;
border: 1px solid transparent;
border-radius: 10px;
box-shadow: none;
}
::v-deep .el-input__inner:hover {
border-color: #c8d8d2;
}
::v-deep .el-input__inner:focus {
background: #ffffff;
border-color: #0b6b55;
box-shadow: 0 0 0 3px rgba(11, 107, 85, 0.10);
}
::v-deep .el-input__suffix {
right: 10px;
}
::v-deep .el-form-item__error {
padding-top: 4px;
}
}
.form-extra {
display: flex;
align-items: center;
justify-content: space-between;
margin: -6px 0 18px;
::v-deep .el-checkbox__label {
color: #52645f;
font-size: 13px;
font-weight: 400;
}
}
.register-link {
color: #0b6b55;
font-size: 13px;
}
.login-action {
margin-bottom: 0;
}
.login-btn {
width: 100%;
height: 48px;
font-size: 16px;
font-weight: 600;
letter-spacing: 6px;
border-radius: 10px;
}
.access-hint {
margin: 18px 0 16px;
color: #7a8a85;
font-size: 12px;
line-height: 1.6;
}
.role-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.role-tag {
padding: 4px 10px;
font-size: 12px;
font-weight: 600;
line-height: 1.6;
border-radius: 999px;
}
.role-tag.admin {
color: #3d6ea8;
background: #e8f1fb;
}
.role-tag.affairs {
color: #0b6b55;
background: #e8f3ef;
}
.role-tag.office {
color: #b77a17;
background: #f8f1e4;
}
.role-tag.teacher {
color: #6b5b93;
background: #f0eaf6;
}
.role-tag.student {
color: #9a7424;
background: #f7f0de;
}
.form-footer {
margin-top: auto;
padding-top: 28px;
color: #9aa7a3;
font-size: 12px;
text-align: center;
}
@media (max-width: 900px) {
.login {
align-items: flex-start;
padding: 16px;
}
.login-card {
flex-direction: column;
}
.brand-panel {
width: 100%;
padding: 28px 24px 20px;
}
.brand-mark {
position: static;
margin-bottom: 22px;
}
.brand-title {
font-size: 26px;
}
.brand-illustration {
display: none;
}
.form-panel {
padding: 32px 24px 20px;
}
}
@media (max-width: 640px) {
.form-heading h2 {
font-size: 24px;
}
}
</style>