實現原理:
/:指向組件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{ width: 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: 320px; }