🌈个人主页:前端青山
🔥系列专栏:vue篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来vue篇专栏内容:Vue2后台管理:项目开发全流程(一)
目录
Vue项目开发
项目架构搭建
1、创建项目
2、技术栈说明
初始化项目包
3、UI组件库
4、路由规划和配置
二、功能实现
1、注册功能
2、登录功能
3、后台首页布局
4、实现菜单跳转路由
5、注销登录
6、防止翻墙越权
7、回车确认提交
Vue项目开发
Vue开发项目核心点使用JS开发项目,前端页面。
Vue和原生JS及其jQuery的区别:换一种操作语法,尽量少的操作DOM
拥有了组件化,工程化的概念。可以进打包,压缩,混淆代码。。。。。 单管理功能......针对不同的业务模块设置和开发的各种功能
项目架构搭建
前后端分离项目
1、创建项目
# 安装脚手架
npm i -g @vue/cli
# 生成项目包 在哪儿生成项目包 在哪儿执行
vue create 项目名称
选择创建项目包的参数选项
2、技术栈说明
“客户端:
Vuejs 渲染库
Vue-Router SPA 单页面应用路由管理
Vuex 组件状态共享和管理
axios ajax请求的发送和封装
momentjs 处理时间格式化
scss css预处理器
服务端:
1、nodejs结合mongodb实现API接口
2、json-server 模拟API数据结构
3、使用服务端人员开发的接口
初始化项目包
根据需求删除项目包中无用的文件和内容
3、UI组件库
UI组件库 能够帮助开发者快速实现页面效果的一种方式。封装好的一些组件开源出来,供大家使用。
自己调查一下,上网查,问问你的朋友同学他们在公司用什么?
组件库名称 | 使用端 | 框架 |
---|---|---|
vantUI 有赞 | 移动端 | Vue,React,微信小程序,支付宝小程序 |
mintUI 饿了么 | 移动端 | Vue |
elementUI 饿了么 | PC端 | Vue,React,Angular |
antdesign 蚂蚁金服 | PC端 | Vue,React,Angular |
antmobile 蚂蚁金服 | 移动端 | React |
nuxtui 京东 | 移动端 | Vue,React |
本次使用elementUI:https://element.eleme.io/#/zh-CN
①安装组件库
npm i element-ui -S
②完整引入elementUI
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 1、添加引入以下两行
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
// 2、使用组件库
Vue.use(ElementUI)
new Vue({
router,
store,
render: (h) => h(App)
}).$mount('#app')
③测试使用
App.vue 测试按钮组件引入使用情况
<template>
<div>
<el-row>
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
</el-row>
<el-row>
<el-button plain>朴素按钮</el-button>
<el-button
type="primary"
plain
>主要按钮</el-button>
<el-button
type="success"
plain
>成功按钮</el-button>
<el-button
type="info"
plain
>信息按钮</el-button>
<el-button
type="warning"
plain
>警告按钮</el-button>
<el-button
type="danger"
plain
>危险按钮</el-button>
</el-row>
<el-row>
<el-button round>圆角按钮</el-button>
<el-button :round="true">圆角按钮</el-button>
<el-button
type="primary"
round
>主要按钮</el-button>
<el-button
type="success"
round
>成功按钮</el-button>
<el-button
type="info"
round
>信息按钮</el-button>
<el-button
type="warning"
round
>警告按钮</el-button>
<el-button
type="danger"
round
>危险按钮</el-button>
</el-row>
<el-row>
<el-button
icon="el-icon-search"
circle
></el-button>
<el-button
type="primary"
icon="el-icon-edit"
circle
></el-button>
<el-button
type="success"
icon="el-icon-check"
circle
></el-button>
<el-button
type="info"
icon="el-icon-message"
circle
></el-button>
<el-button
type="warning"
icon="el-icon-star-off"
circle
></el-button>
<el-button
type="danger"
icon="el-icon-delete"
circle
></el-button>
</el-row>
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
</style>
4、路由规划和配置
功能 | URL | 备注 |
---|---|---|
注册 | /register | |
登录 | /login | |
管理功能 | /admin | 父级路由 |
用户管理功能 | /admin/user | 嵌套子路由 |
“注意:在配置好路由文件后,一定要记得在App.vue添加渲染标签 否则无法切换显示
<router-view></router-view>
二、功能实现
1、注册功能
①表单选项布局
根据注册功能需要的表单选项进行表单的设置及其校验工作。收集数据并通过ajax提交
src\views\Register.vue
<template>
<div class="container">
<div class="title">综合数据运营管理平台</div>
<!-- 注册表单 -->
<!-- card组件 -->
<el-card style="margin-top: 20px;padding: 10px 30px">
<!-- 表单组件 -->
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px">
<!-- 表单每个元素 -->
<el-form-item prop="username">
<!-- 输入框 -->
<el-input v-model="ruleForm.username" placeholder="用户名"></el-input>
</el-form-item>
<el-form-item prop="pass">
<el-input type="password" v-model="ruleForm.pass" autocomplete="off" placeholder="密码"></el-input>
</el-form-item>
<el-form-item prop="checkPass">
<el-input type="password" v-model="ruleForm.checkPass" autocomplete="off" placeholder="确认密码"></el-input>
</el-form-item>
<el-form-item style="display: flex;justify-content:space-around;">
<el-button type="primary" style="width: 220px;" @click="submitForm('ruleForm')">注册</el-button>
<!-- <el-button @click="resetForm('ruleForm')">重置</el-button> -->
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
export default {
data() {
// 校验用户名
var checkUsername = (rule, value, callback) => {
if (!value) {
return callback(new Error('用户名不能为空'));
}
return callback()
};
// 校验密码
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else if (value.length < 6) {
callback(new Error('密码长度最少6位'))
}
else {
if (this.ruleForm.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
// 校验确认密码
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ruleForm.pass) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
// 表单双向绑定数据
ruleForm: {
username: '',
pass: '',
checkPass: '',
},
// 校验规则及其校验时机
rules: {
pass: [
{ validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ validator: validatePass2, trigger: 'blur' }
],
username: [
{ validator: checkUsername, trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style lang="scss" scoped>
.container {
height: 100vh;
background: url('../assets/images/bg.jpg');
background-size: cover;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.title {
font-size: 28px;
padding: 15px;
border-radius: 10px;
font-weight: bold;
color: white;
background-color: rgba($color: #000000, $alpha: 0.4);
}
}
/* 使用::v-deep(选择器) 实现样式向下级组件穿透 */
::v-deep(.el-form-item__content) {
margin-left: 0px !important;
}
</style>
②安装axios
npm i axios
③发送根据请求进行判断业务逻辑
methods: {
// 提交表单
submitForm(formName) {
// ref获取到子组件的实例 validate el-from表单组件里的校验方法
this.$refs[formName].validate((valid) => {
if (valid) {
// 通过就提交表单
// alert('submit!');
// 收集数据
console.log(this.ruleForm);
const { username, pass } = this.ruleForm
// 组合接口需要的数据结构
const data = { username, password: pass }
axios.post('http://localhost:5000/api/v1/register', data).then(res => {
console.log(res);
// message提示框组件
this.$message({
type: res.data.code === 0 ? 'success' : 'error',
duration:1000,
message: res.data.msg,
onClose: () => {
// 跳转到登录页面
this.$router.push('/login')
}
});
})
} else {
// 校验不通过
console.log('error submit!!');
return false;
}
});
},
}
请求方法的拦截器封装
src\utils\request.js
/**
* axios拦截器
* 请求拦截器 统一配置请求参数信息
* 响应拦截器 统一处理响应返回数据
*
*/
import axios from 'axios'
import { Loading } from 'element-ui'
var loading
const instance = axios.create({
// 接口基础地址 接口的公共地址 一般域名
// baseURL:'',
// 超时时间
// timeout:5000
})
// 请求拦截器
instance.interceptors.request.use(cfg => {
// 封装请求加载状态
loading = Loading.service({
text: "loading...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.8)"
});
return cfg
})
// 响应拦截器
instance.interceptors.response.use(res => {
setTimeout(() => {
// 请求加载状态停止
loading.close()
}, 700)
return res
})
export default instance
接口地址封装
src\config\url.js
/**
* 封装接口url地址
* 方便统一管理
*
*/
const prefix = 'http://localhost:5000/api/v1'
const url = {
// 注册
Register: prefix + '/register'
}
export default url
2、登录功能
①表单收集用户登录信息
②发送请求给服务端接口
③根据返回结果处理业务逻辑 服务端
1> 登录成功返回token
2> 登录失败提示错误
④存储token和用户信息方便后续使用
响应拦截器 \backend\src\http\req.js
axios.interceptors.response.use((res) => {
// 统一存储token
if (res.data.code === 0) {
localStorage.setItem('token', res.data.token)
}
return res.data
})
登录成功后设置 \backend\src\views\Login.vue
if (res.code === 0) {
localStorage.setItem('username',res.data.username)
this.$message({
message: '登录成功',
type: 'success',
duration:1000,
onClose: () => {
this.$router.push('/admin')
}
})
}
3、后台首页布局
布局实现
src\views\Admin\Admin.vue
<template>
<div>
<!-- 整个容器布局 -->
<el-container>
<!-- 左侧菜单 -->
<el-aside width="200px">
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose"
background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
</el-menu>
</el-aside>
<!-- 右侧容器 -->
<el-container>
<!-- 头部 -->
<el-header>Header</el-header>
<!-- 主体 -->
<el-main>Main</el-main>
<!-- 底部 -->
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
.el-container {
/* 设置容器的高度 */
height: 100vh;
}
.el-menu {
/* 默认菜单右侧1px边框 去掉 */
border-right: 0;
}
.el-header,
.el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
左侧列表菜单
使用组件:https://element.eleme.io/#/zh-CN/component/menu
NavMenu 导航菜单
el-menu 整个菜单组件
el-menu-item 每个菜单组件
src\views\Admin\Admin.vue
<template>
<div>
<!-- 整个容器布局 -->
<el-container>
<!-- 左侧菜单 -->
<el-aside width="200px">
<div class="logo">
<div>
<img src="@/assets/logo.png" alt="" srcset="">
</div>
<div>综合数据管理平台</div>
</div>
<!-- 菜单 -->
<el-menu router default-active="2" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
<!-- index 开启router路由模式 会作为路由跳转的路径 -->
<el-menu-item index="/admin/dashboard">
<!-- icon图标 菜单左侧 -->
<i class="el-icon-menu"></i>
<span slot="title">控制台</span>
</el-menu-item>
<!-- <el-submenu index="/admin/user">
<template slot="title">
<i class="el-icon-location"></i>
<span slot="title">用户管理</span>
</template> -->
<!-- <el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">管理员管理</span>
</el-menu-item> -->
<el-menu-item index="/admin/user">
<i class="el-icon-setting"></i>
<span slot="title">会员管理</span>
</el-menu-item>
<!-- </el-submenu> -->
</el-menu>
</el-aside>
<!-- 右侧容器 -->
<el-container>
<!-- 头部 -->
<el-header>Header</el-header>
<!-- 主体 -->
<el-main>
<!-- 嵌套路由渲染容器 -->
<router-view></router-view>
</el-main>
<!-- 底部 -->
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
.el-container {
/* 设置容器的高度 */
height: 100vh;
}
.el-menu {
/* 默认菜单右侧1px边框 去掉 */
border-right: 0;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
}
.logo {
height: 60px;
display: flex;
justify-content: space-around;
align-items: center;
color: white;
font-weight: bold;
background-color: rgb(84, 92, 100);
>div:first-child {
width: 40px;
height: 40px;
>img {
width: 100%;
}
}
}
.el-main {
background-color: #E9EEF3;
color: #333;
}
.el-header,
.el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
4、实现菜单跳转路由
①在后台首页面中添加渲染容器
\src\views\Admin\Admin.vue
<el-main>
<!-- 子路由渲染容器 -->
<router-view></router-view>
</el-main>
②根据嵌套路由的配置规则创建对应路由和页面组件
src\router\index.js
{
path:'/admin',
component:()=>import('@/views/Admin/Admin.vue'),
children:[
{
path:'dashboard',
component:()=>import('@/views/admin/Dashboard.vue'),
},
{
path:'user',
component:()=>import('@/views/admin/User.vue'),
},
{
path:'goods',
component:()=>import('@/views/admin/Goods.vue'),
},
]
}
③开启菜单路由功能,并且配置对应的index参数
\backend\src\views\admin\Index.vue
<el-menu
default-active="/admin/dashboard"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="#001529"
text-color="#fff"
active-text-color="#409eff"
router
>
<el-menu-item index="/admin/dashboard">
<!-- <i class="el-icon-platform-eleme" style="color:#409eff"></i> -->
<i class="el-icon-platform-eleme"></i>
<span slot="title">控制台</span>
</el-menu-item>
<el-menu-item index="/admin/user">
<i class="el-icon-user-solid"></i>
<span slot="title">用户管理</span>
</el-menu-item>
<el-menu-item index="/admin/goods">
<i class="el-icon-s-goods"></i>
<span slot="title">商品管理</span>
</el-menu-item>
</el-menu>
5、注销登录
思路:
1、按钮点击触发事件 处理注销
2、登录依据是判断是否有token,及其是否过期,退出登录的核心就是把token删除
退出按钮
<!-- 显示管理登录信息及其下拉菜单 -->
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<el-avatar size="medium"
src="https://img1.baidu.com/it/u=3096599450,2589974591&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"></el-avatar>
<span style="font-weight: bold;">{{ username }}</span><i
class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="ucenter">个人中心</el-dropdown-item>
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
js代码部分删除token
handleCommand(value) {
console.log(value);
if (value === 'logout') {
this.$confirm('确认退出吗?')
// 确定
.then(_ => {
// 将用户的token和记录信息全部清空掉
localStorage.removeItem('token')
localStorage.removeItem('username')
this.$message({
message: '退出成功',
type: 'success',
duration: 700,
onClose: () => {
this.$router.replace('/login')
}
})
})
// 取消
.catch(_ => {
// console.log(_);
// console.log('2222');
});
}
}
6、防止翻墙越权
注意校验是否登录权限,需要携带token信息在请求头
// 请求拦截器
instance.interceptors.request.use(cfg => {
// 如果token存在,则统一添加token到请求头信息
if (localStorage.getItem('token')) {
cfg.headers.Authorization = localStorage.getItem('token')
}
// 封装请求加载状态
loading = Loading.service({
text: "loading...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.8)"
});
return cfg
})
实现方法:
方法一:父级路由组件的beforeCreate生命周期拦截
beforeCreate() {
if (!localStorage.getItem('token')) {
this.$tip('请先登录', 'error')
this.$router.replace('/login')
} else {
this.$http.get('http://127.0.0.1:5000/api/v1/profile').then((res) => {
if (res.data.code === 401) {
this.$tip('请先登录', 'error')
this.$router.replace('/login')
} else {
localStorage.setItem('userInfo', JSON.stringify(res.data.data))
}
})
}
},
方法二:使用路由守卫进行拦截
全局路由前置守卫
src\router\index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import { Message } from 'element-ui';
import url from '@/config/url';
import req from '@/utils/request'
import Register from '../views/Register.vue'
import Login from '../views/Login.vue'
import Admin from '../views/Admin/Admin.vue'
import Dashboard from '../views/Admin/Dashboard.vue'
import User from '../views/Admin/User.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/register'
},
{
path: '/register',
name: 'register',
component: Register,
meta: {
isAuth: false
}
},
{
path: '/login',
name: 'login',
component: Login,
// 路由元信息 路由传参
meta: {
isAuth: false
}
},
{
path: '/admin',
name: 'admin',
component: Admin,
children: [
{
// 嵌套路由中 path 不需要写/
path: 'dashboard',
name: 'dashboard',
component: Dashboard
},
{
path: 'user',
name: 'user',
component: User
}
],
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
console.log(to, from);
// 根据路由元信息 判断哪些路由是需要校验登录
if (to.meta.isAuth === false) {
next()
} else {
// 如果本地存储未报错token 肯定没有登录
if (!localStorage.getItem('token')) {
Message({
message: '未登录,请先登录',
type: 'error',
duration: 1000,
onClose: () => {
// 跳转到登录界面
next('/login')
}
})
} else {
// 校验token的有效性
req.get(url.Profile).then(res => {
console.log(res);
if (res.data.code === 0) {
// 存储管理员登录信息
localStorage.setItem('username', res.data.data.username)
} else {
Message({
message: '登录失效,重新登录',
type: 'error',
duration: 1000,
onClose: () => {
// 跳转到登录界面
next('/login')
}
})
}
})
next()
}
}
})
export default router
路由独享守卫
src\router\index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import { Message } from 'element-ui';
import url from '@/config/url';
import req from '@/utils/request'
import Register from '../views/Register.vue'
import Login from '../views/Login.vue'
import Admin from '../views/Admin/Admin.vue'
import Dashboard from '../views/Admin/Dashboard.vue'
import User from '../views/Admin/User.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/register'
},
{
path: '/register',
name: 'register',
component: Register,
meta: {
isAuth: false
}
},
{
path: '/login',
name: 'login',
component: Login,
// 路由元信息 路由传参
meta: {
isAuth: false
}
},
{
path: '/admin',
name: 'admin',
component: Admin,
children: [
{
// 嵌套路由中 path 不需要写/
path: 'dashboard',
name: 'dashboard',
component: Dashboard
},
{
path: 'user',
name: 'user',
component: User
}
],
// 路由 独享守卫
beforeEnter: (to, from, next) => {
// console.log(to);
// // 如果本地存储未报错token 肯定没有登录
if (!localStorage.getItem('token')) {
Message({
message: '未登录,请先登录',
type: 'error',
duration: 1000,
onClose: () => {
// 跳转到登录界面
next('/login')
}
})
} else {
// 校验token的有效性
req.get(url.Profile).then(res => {
console.log(res);
if (res.data.code === 0) {
// 存储管理员登录信息
localStorage.setItem('username', res.data.data.username)
next()
} else {
Message({
message: '登录失效,重新登录',
type: 'error',
duration: 1000,
onClose: () => {
// 跳转到登录界面
next('/login')
}
})
}
})
}
},
}
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
// }
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
组件内部的前置守卫
src\views\Admin\Admin.vue
<template>
<div>
<!-- 整个容器布局 -->
<el-container>
<!-- 左侧菜单 -->
<el-aside width="200px">
<div class="logo">
<div>
<img src="@/assets/logo.png" alt="" srcset="">
</div>
<div>综合数据管理平台</div>
</div>
<!-- 菜单 -->
<!-- default-active 根据路由路径匹配 选中的对应的菜单高亮 -->
<el-menu router :default-active="$route.path" background-color="#001529" text-color="#ccc">
<!-- index 开启router路由模式 会作为路由跳转的路径 -->
<el-menu-item index="/admin/dashboard">
<!-- icon图标 菜单左侧 -->
<i class="el-icon-data-line"></i>
<span slot="title">控制台</span>
</el-menu-item>
<!-- <el-submenu index="/admin/user">
<template slot="title">
<i class="el-icon-location"></i>
<span slot="title">用户管理</span>
</template> -->
<!-- <el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">管理员管理</span>
</el-menu-item> -->
<el-menu-item index="/admin/user">
<i class="el-icon-user"></i>
<span slot="title">会员管理</span>
</el-menu-item>
<!-- </el-submenu> -->
</el-menu>
</el-aside>
<!-- 右侧容器 -->
<el-container>
<!-- 头部 -->
<el-header>Header</el-header>
<!-- 主体 -->
<el-main>
<!-- 嵌套路由渲染容器 -->
<div>
<router-view></router-view>
</div>
</el-main>
<!-- 底部 -->
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
import { Message } from 'element-ui';
import url from '@/config/url';
import req from '@/utils/request'
export default {
// 拦截操作 判断用户是否登录
// 路由 组件内置守卫 内部获取不到组件的this 此时组件还未被创建
beforeRouteEnter(to, form, next) {
// 如果本地存储未报错token 肯定没有登录
if (!localStorage.getItem('token')) {
Message({
message: '未登录,请先登录',
type: 'error',
duration: 1000,
onClose: () => {
// 跳转到登录界面
next('/login')
}
})
} else {
// 校验token的有效性
req.get(url.Profile).then(res => {
console.log(res);
if (res.data.code === 0) {
// 存储管理员登录信息
localStorage.setItem('username',res.data.data.username)
} else {
Message({
message: '登录失效,重新登录',
type: 'error',
duration: 1000,
onClose: () => {
// 跳转到登录界面
next('/login')
}
})
}
})
next()
}
}
}
</script>
<style lang="scss" scoped>
.el-container {
/* 设置容器的高度 */
height: 100vh;
}
.el-menu {
/* 默认菜单右侧1px边框 去掉 */
border-right: 0;
}
.el-aside {
background-color: #001529;
color: #333;
/* text-align: center; */
}
.logo {
height: 60px;
display: flex;
/* justify-content: space-around; */
align-items: center;
color: white;
font-weight: bold;
background-color: #001529;
padding: 5px;
>div:first-child {
width: 40px;
height: 40px;
>img {
width: 100%;
}
}
>div:nth-child(2) {
margin-left: 10px;
}
}
.el-main {
background-color: #f5f5f5;
>div {
background-color: #fff;
min-height: calc(100vh - 60px - 60px - 20px - 20px - 20px);
margin-top: 20px;
}
}
.el-menu-item {
height: 40px;
line-height: 40px;
margin: 5px 10px;
border-radius: 10px;
}
.el-menu-item.is-active {
color: white !important;
background-color: #1677ff !important;
}
.el-header {
background-color: #ffffff;
text-align: center;
line-height: 60px;
}
.el-footer {
background-color: #f5f5f5;
text-align: center;
line-height: 60px;
}
</style>
7、回车确认提交
mounted() {
// 文档对象监听键盘事件 回车键
document.onkeyup = (event) => {
var e = event || window.event
if (e && e.keyCode == 13) {
// console.log('回车')
this.submitForm('ruleForm')
}
}
},