实现原理:
/:指向组件App.vue,它是最外层组件,下面的/login和/main对应的组件都会包含在其中;
/login:指向登录组件Login.vue;
/main:指向登录后组件Main.vue,其下会包括很多子组件来展示不同菜单项。
用户有没有登录需要给其指定状态(用islogin表示),当用户登录了,我们用localStorage在Login.vue文件中为其状态设定为1:
methods: {
submitForm(formName) {
let _this = this;
let param = new URLSearchParams();
param.append('username', _this.ruleForm.username);
param.append('password', _this.ruleForm.password);
this.$refs[formName].validate((valid) => {
if (valid) {
_this.$axios({
method: 'POST',
url: '/api/blog/check_login_status/',
data: param
})
.then(res => {
if(res.data.ret){
localStorage.setItem("islogin", 1);
console.log(localStorage.getItem("islogin"));
_this.$router.push({path: "/main/form/radio"});
}else{
_this.$message('用户名或密码错误!');
return false;
}
})
.catch(err => {
console.log(err);
});
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
这里可能会有Django后台获取不到前端axios-post请求提交的参数的问题,方法请参考:
https://blog.csdn.net/duansamve/article/details/101942106
退出时需要为其指定状态为0:
localStorage.setItem("islogin", 0);
路由拦截需要用到导航守卫,关键代码如下:
router.beforeEach((to, from, next) => {
let islogin = localStorage.getItem("islogin");
islogin = Boolean(Number(islogin));
if(to.path == "/login"){
if(islogin){
next("/table");
}else{
next();
}
}else{
// requireAuth:可以在路由元信息指定哪些页面需要登录权限
if(to.meta.requireAuth && islogin) {
next();
}else{
next("/login");
}
}
})
src/router/index.js中,可以看到所有路由如下:
let router = new Router({
mode: 'hash',
routes: [
{
path: '/',
name: 'index',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: Login,
meta: {
title: 'Login',
icon: 'el-icon-eleme',
requireAuth: true
}
},
{
path: '/main',
name: 'main',
component: Main,
show: false,
meta: {
title: 'Main',
icon: 'el-icon-eleme',
requireAuth: true
},
children: [
{
path: '/main/form/radio',
name: 'radio',
component: Radio,
show: true,
meta: {
title: 'Radio',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/form/checkbox',
name: 'checkbox',
component: Checkbox,
show: true,
meta: {
title: 'Checkbox',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/data/table',
name: 'table',
component: Table,
show: true,
meta: {
title: 'Table',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/data/tag',
name: 'tag',
component: Tag,
show: true,
meta: {
title: 'Tag',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/button',
name: 'button',
component: Button,
show: true,
meta: {
title: 'Button',
icon: 'el-icon-s-order',
requireAuth: true
}
},
{
path: '/main/tabs',
name: 'tabs',
component: Tabs,
show: true,
meta: {
title: 'Tabs',
icon: 'el-icon-s-ticket',
requireAuth: true
}
},
{
path: '/main/echarts',
name: 'echarts',
component: Echarts,
show: true,
meta: {
title: 'Echarts',
icon: 'el-icon-s-marketing',
requireAuth: true
}
}
]
}
]
});
我们只需要把/main的子路由在菜单中循环出来,其它不需要显示,所以在Main.vue中增加如下方法提取/main的所有子路由:
methods: {
getRoutes() {
for (let i = 0; i < this.$router.options.routes.length; i++) {
if (this.$router.options.routes[i].name === "main") {
return this.$router.options.routes[i].children;
}
}
}
}
关键文件代码:
router/index.js:
import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login'
import Main from '@/components/Main'
import Radio from '@/components/Radio'
import Checkbox from '@/components/Checkbox'
import Table from '@/components/Table'
import Tag from '@/components/Tag'
import Button from '@/components/Button'
import Tabs from '@/components/Tabs'
import Echarts from '@/components/Echarts'
Vue.use(Router);
let router = new Router({
mode: 'hash',
routes: [
{
path: '/',
name: 'index',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: Login,
meta: {
title: 'Login',
icon: 'el-icon-eleme',
requireAuth: true
}
},
{
path: '/main',
name: 'main',
component: Main,
show: false,
meta: {
title: 'Main',
icon: 'el-icon-eleme',
requireAuth: true
},
children: [
{
path: '/main/form/radio',
name: 'radio',
component: Radio,
show: true,
meta: {
title: 'Radio',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/form/checkbox',
name: 'checkbox',
component: Checkbox,
show: true,
meta: {
title: 'Checkbox',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/data/table',
name: 'table',
component: Table,
show: true,
meta: {
title: 'Table',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/data/tag',
name: 'tag',
component: Tag,
show: true,
meta: {
title: 'Tag',
icon: 'el-icon-s-marketing',
requireAuth: true
}
},
{
path: '/main/button',
name: 'button',
component: Button,
show: true,
meta: {
title: 'Button',
icon: 'el-icon-s-order',
requireAuth: true
}
},
{
path: '/main/tabs',
name: 'tabs',
component: Tabs,
show: true,
meta: {
title: 'Tabs',
icon: 'el-icon-s-ticket',
requireAuth: true
}
},
{
path: '/main/echarts',
name: 'echarts',
component: Echarts,
show: true,
meta: {
title: 'Echarts',
icon: 'el-icon-s-marketing',
requireAuth: true
}
}
]
}
]
});
export default router
router.beforeEach((to, from, next) => {
let islogin = localStorage.getItem("islogin");
console.log(islogin);
console.log(to.path);
islogin = Boolean(Number(islogin));
if(to.path == "/login"){
if(islogin){
next("/main/form/radio");
}else{
next();
}
}else{
// requireAuth:可以在路由元信息指定哪些页面需要登录权限
if(to.meta.requireAuth && islogin) {
next();
}else{
next("/login");
}
}
})
Login.vue:
<template>
<div id="login-container">
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="checkUser">
<el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="checkPass">
<el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'Login',
components: {
},
data() {
var validateUser = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入用户名'));
} else {
callback();
}
};
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
callback();
}
};
return {
ruleForm: {
username: '',
password: ''
},
rules: {
checkUser: [
{ validator: validateUser, trigger: 'blur' }
],
checkPass: [
{ validator: validatePass, trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
let _this = this;
let param = new URLSearchParams();
param.append('username', _this.ruleForm.username);
param.append('password', _this.ruleForm.password);
this.$refs[formName].validate((valid) => {
if (valid) {
_this.$axios({
method: 'POST',
url: '/api/blog/check_login_status/',
data: param
})
.then(res => {
if(res.data.ret){
localStorage.setItem("islogin", 1);
console.log(localStorage.getItem("islogin"));
_this.$router.push({path: "/main/form/radio"});
}else{
_this.$message('用户名或密码错误!');
return false;
}
})
.catch(err => {
console.log(err);
});
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style scoped>
body{
margin: 0;
}
#login-container{
400px;
height: 300px;
background: #e5e9f2;
position: absolute;
left: 50%;
top: 50%;
margin-left: -220px;
margin-top: -170px;
border-radius: 5px;
padding-top: 40px;
padding-right: 40px;
}
</style>
Main.vue:
<template>
<div id="app">
<el-container style="height: 100%;">
<el-header style="height: 80px;" :style="topBg">
<Header/>
</el-header>
<el-container>
<el-aside width="210px" :style="leftBg">
<el-row class="tac">
<el-col :span="24">
<el-menu
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
>
<template v-for="route in getRoutes()">
<el-submenu
:key="route.path"
:index="route.path"
v-if="route.children && route.children.length"
>
<!-- 循环有子目录的菜单 -->
<template slot="title">
<i :class="route.meta.icon"></i>
<span>{{route.meta.title}}</span>
</template>
<el-menu-item-group>
<router-link :to="todo.path" :key="todo.path" v-for="todo in route.children">
<el-menu-item
:index="todo.path"
>{{todo.meta.title}}</el-menu-item>
</router-link>
</el-menu-item-group>
</el-submenu>
<!-- 循环没有子目录的菜单 -->
<router-link
:to="route.path"
:key="route.path"
v-else-if="!route.children && route.path != '/' && route.path != '/login'"
>
<el-menu-item :index="route.path">
<i :class="route.meta.icon"></i>
<span>{{route.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-menu>
</el-col>
</el-row>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import Header from '@/components/Header'
export default {
name: "App",
data() {
return {
leftBg: {
background: "#235d8b url(" + require("../assets/left-bg.png") + ") no-repeat scroll 0 bottom"
},
topBg: {
background: "#235d8b url(" + require("../assets/top-bg.png") + ") no-repeat scroll right 0",
height: '80px',
fontSize: '32px',
color: '#ffffff'
}
}
},
components: {
Header
},
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log(key, keyPath);
},
getRoutes() {
for (let i = 0; i < this.$router.options.routes.length; i++) {
if (this.$router.options.routes[i].name === "main") {
return this.$router.options.routes[i].children;
}
}
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
height: 100%;
}
.el-header,
.el-footer {
background-color: #b3c0d1;
color: #333;
line-height: 80px;
}
.el-aside {
background-color: #d3dce6;
color: #333;
}
.el-aside a{
text-decoration: none;
}
.el-menu {
background: none;
}
.el-main {
background-color: #ffffff;
color: #333;
}
body > .el-container {
margin-bottom: 40px;
}
.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: