#Login.vue
<template>
<div class="login_container">
<div class="login_box">
<!-- 头像区 -->
<div class="avatar_box">
<img src="../assets/logo.png" alt="" />
</div>
<!-- 登录表单区 ref的引用,实例化表单对象 -->
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginFormRules"
label-width="0"
class="login_form"
>
<!-- 用户名 -->
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
prefix-icon="iconfont icon-user"
></el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
prefix-icon="iconfont icon-3702mima"
type="password"
></el-input>
</el-form-item>
<!-- 按钮区 -->
<el-form-item class="btns">
<el-button type="primary" @click="login">登录</el-button>
<el-button type="info" @click="resetLoginForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//这是登录表单的绑定对象
loginForm: {
username: "admin",
password: "123456",
},
//这是表单的验证规则对象
loginFormRules: {
//验证用户名是否合法
username: [
{ required: true, message: "请输入登录名称", trigger: "blur" },
{
min: 3,
max: 10,
message: "长度在 3 到 10 个字符",
trigger: "blur",
},
],
//验证密码是否合法
password: [
{ required: true, message: "请输入登录密码", trigger: "blur" },
{
min: 6,
max: 15,
message: "长度在 6 到 15 个字符",
trigger: "blur",
},
],
},
};
},
methods: {
// 点击重置按钮,重置登录表单
resetLoginForm() {
// console.log(this);
this.$refs.loginFormRef.resetFields();
},
//预验证
login() {
this.$refs.loginFormRef.validate(async (valid) => {
if (!valid) return;
const { data: res } = await this.$http.post("login", this.loginForm);
// console.log(res);
if (res.meta.status !== 200) return this.$message.error("登录失败");
this.$message.success("登录成功");
//1、将登陆成功后的token保存到客户端的sessionStorage中
// 1.1 项目中除了登录之外的其他API接口,必须在登录之后才能访问
// 1.2 token只应该在当前网站打开期间生效
// console.log(res);
window.sessionStorage.setItem("token", res.data.token);
//2、通过编程式导航跳到后台主页,路由地址是/home
this.$router.push("/home");
});
},
},
};
</script>
<style lang="less" scoped>
.login_container {
background-color: #2b4b6b;
height: 100%;
}
.login_box {
width: 450px;
height: 300px;
background-color: #fff;
border-radius: 3px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.avatar_box {
height: 130px;
width: 130px;
border: 1px solid #eee;
border-radius: 50%;
padding: 10px;
box-shadow: 0 0 10px #ddd;
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
img {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #eee;
}
}
}
.login_form {
position: absolute;
bottom: 0;
width: 100%;
padding: 0 20px;
box-sizing: border-box;
}
.btns {
display: flex;
justify-content: flex-end;
}
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
//导入全局样式表
import './assets/css/global.css'
//导入字体图标
import './assets/fonts/iconfont.css'
//导入NProgress包和css
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
//导入 axios包
import axios from 'axios'
//导入树形表格插件
import TreeTable from 'vue-table-with-tree-grid'
//配置请求的根路径
axios.defaults.baseURL = 'https://lianghj.top:8888/api/private/v1/'
//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息 请求拦截器
//在request拦截器中展示进度条
axios.interceptors.request.use(config => {
//为请求头对象,添加token验证的Authorization字段
NProgress.start()
config.headers.Authorization = window.sessionStorage.getItem("token")
return config
})
//在response拦截器中隐藏进度条
axios.interceptors.response.use(config => {
NProgress.done()
return config
})
//全局注册
Vue.component('tree-table', TreeTable)
//全局挂载
Vue.prototype.$http = axios
//时间转换器
Vue.filter('dateFormat', function (originVal) {
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
home.vue
<template>
<el-container class="home-container">
<!-- 头部区域 -->
<el-header>
<div>
<!-- 黑马logo -->
<img src="../assets/heima.png" alt="">
<!-- 顶部标题 -->
<span>电商后台管理系统</span>
</div>
<el-button type="info" @click="logout"> 退出 </el-button>
</el-header>
<!-- 页面主体区域 -->
<el-container>
<!-- 侧边栏 -->
<el-aside :width=" isCollapse ? '64px': '200px'">
<div class="toggle-button" @click="toggleCollapse">|||</div>
<!-- 侧边栏菜单 unique-opened只能展开一个 collapse-transition折叠动画 router开启路由模式-->
<el-menu
background-color="#333744"
text-color="#fff" :collapse-transition="false"
active-text-color="#409EFF" unique-opened
:collapse="isCollapse" router :default-active="activePath">
<!-- 一级菜单 -->
<el-submenu :index="item.id+''" v-for="item in menuList" :key="item.id">
<!-- 一级菜单模板 -->
<template slot="title">
<!-- 图标 -->
<i :class="iconsObj[item.id]"></i>
<!-- 文本 -->
<span>{{item.authName}}</span>
</template>
<!-- 二级子菜单 -->
<el-menu-item :index="'/'+subItem.path" v-for="subItem in item.children"
:key="subItem.id" @click="saveNavState('/'+subItem.path)">
<!-- 二级菜单模板 -->
<template slot="title">
<!-- 图标 -->
<i class="el-icon-menu"></i>
<!-- 文本 -->
<span>{{subItem.authName}}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 主体结构 -->
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
data() {
return {
// 左侧菜单数据
menuList: null,
isCollapse: false,
iconsObj: {
'125':'iconfont icon-user',
'103':'iconfont icon-tijikongjian',
'101':'iconfont icon-shangpin',
'102':'iconfont icon-danju',
'145':'iconfont icon-baobiao'
},
activePath: ''
}
},
created() {
// 在created阶段请求左侧菜单数据
this.getMenuList();
this.activePath = window.sessionStorage.getItem('activePath');
},
methods: {
logout() {
window.sessionStorage.clear()
this.$router.push('/login')
},
async getMenuList() {
// 发送请求获取左侧菜单数据
const { data: res } = await this.$http.get('menus')
if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
this.menuList = res.data
// console.log(res)
},
//点击按钮切换菜单的折叠与展开
toggleCollapse() {
this.isCollapse = !this.isCollapse;
},
saveNavState( activePath ){
//点击二级菜单的时候保存被点击的二级菜单信息
window.sessionStorage.setItem("activePath",activePath);
this.activePath = activePath;
}
}
}
</script>
<style lang="less" scoped>
.home-container {
height: 100%;
}
.el-header{
background-color:#373D41;
display: flex;
justify-content: space-between;
padding-left: 0;
align-items: center;
color: #fff;
font-size: 20px;
> div {
display: flex;
align-items: center;
span {
margin-left:15px ;
}
}
}
.el-aside{
background-color:#333744;
.el-menu {
border-right: none;
}
}
.el-main{
background-color:#eaedf1;
}
.toggle-button {
background-color: #4A5064;
font-size: 10px;
line-height: 24px;
color: #fff;
text-align: center;
letter-spacing: 0.2em;
cursor: pointer;
}
</style>