流程是用户登录后进入模块页面,点击不同的模块,进入菜单页面(模块不同,菜单内容也不同)
遇到的问题
1、菜单数据存储到store中页面刷新后页面空白
解决方法:在全局导航守卫中每次都初始化菜单
2、如何动态生成路由 (动态生成路由会叠加,如果已经存在再生成会警告)
采用方法:router . addRoutes ( data );
3、不同模块切换进入菜单页面,高亮显示有问题
解决方法:
: default-active = " routePath "
created() {
this.routePath = this.$route.path;
},
3、退出登录后如何清空store中数据(vuex中并没有清空的方法)
暂时采用: location . reload ();
下面全部采用json模拟数据
详细代码如下
userPermission.json
{
"data":{"name":"小李"},
"token":"111",
"responseCode":"0000"
}
Login.vue
<template>
<div>
<el-form
:rules="rules"
ref="loginForm"
v-loading="loading"
element-loading-text="正在登录..."
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
:model="loginForm"
class="loginContainer"
>
<h3 class="loginTitle">系统登录</h3>
<el-form-item prop="username">
<el-input
size="normal"
type="text"
v-model="loginForm.username"
auto-complete="off"
placeholder="请输入用户名"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
size="normal"
type="password"
v-model="loginForm.password"
auto-complete="off"
placeholder="请输入密码"
@keydown.enter.native="submitLogin"
></el-input>
</el-form-item>
<el-checkbox size="normal" class="loginRemember" v-model="checked"></el-checkbox>
<el-button size="normal" type="primary" style="width: 100%;" @click="submitLogin">登录</el-button>
</el-form>
<div class="login-bottom">
bottom
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
loading: false,
loginForm: {
username: "admin",
password: "1"
},
checked: true,
rules: {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" }
],
password: [{ required: true, message: "请输入密码", trigger: "blur" }]
}
};
},
methods: {
submitLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
this.$axios
.get("mock/userPermission.json")
.then(res => {
console.log(res.data);
let data = res.data
if(data.responseCode=="0000"){
this.$store.commit("updatePermissionInfo", res.data);
sessionStorage.setItem("user", data.data.name);
sessionStorage.setItem("token", data.token);
this.$router.push("/dashboard");
}
})
.catch(error => {
console.log(error.response)
this.$alert(error, "提示", {
confirmButtonText: "确定",
type: "warning"
});
});
} else {
this.$message.error("请输入所有字段");
return false;
}
});
}
}
};
</script>
<style scoped>
.loginContainer {
width: 450px;
height: 320px;
border-radius: 15px;
background: #fff;
position: absolute;
left: 0;
right: 0;
top: -60px;
bottom: 0;
/* background:linear-gradient(to bottom,#0675bd, #0363a1); */
box-shadow: 0 0 25px #cac6c6;
padding: 15px 35px 15px 35px;
margin: auto;
}
.loginTitle {
margin: 15px auto 20px auto;
text-align: center;
color: #505458;
}
.loginRemember {
text-align: left;
margin: 0px 0px 15px 0px;
}
.loginTip {
color: #d31245;
font-style: italic;
margin-bottom: 25px;
}
.login-bottom {
position: fixed;
bottom: 15px;
width: 100%;
text-align: center;
}
.login-bottom img {
vertical-align: middle;
width: 65px;
margin-right: 10px;
}
</style>
模块页面
dishboardInfo.json
{
"data": [{
"id":1,
"path": "/industry",
"name": "模块1",
"component": "Home"
}, {
"id":2,
"path": "/commercial",
"name": "模块2",
"component": "Home"
}]
}
dashboard.vue
<template>
<div class>
<ul class="dashboard">
<!-- <router-link tag="li" v-for="(item,index) in info" :key="index" :to="item.path">{{item.menuName}}</router-link> -->
<li v-for="(item,index) in info" :key="index" @click="handleClick(index)">{{item.name}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
info: []
};
},
created() {
let userName = sessionStorage.getItem("user");
console.log(userName);
this.$axios.get("mock/dishboardInfo.json").then(res => {
console.log(res.data);
this.info = res.data.data;
});
},
computed: {},
methods: {
handleClick(index) {
this.$router.push({
path: this.info[index].path,
query: { menuId: this.info[index].id }
});
}
}
};
</script>
路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Dashboard from '../views/dashboard.vue'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
// const originalPush = VueRouter.prototype.push
// VueRouter.prototype.push = function push(location) {
// return originalPush.call(this, location).catch(err => err)
// }
const routes = [{
path: '/',
redirect: "/login",
},
{
path: "/login",
name: 'login',
component: Login
},
{
path: "/dashboard",
name: 'dashboard',
component: Dashboard
},
{
path: '/:id',
name: "home",
//component: () => import('../views/Home.vue')
component: Home,
}
]
const router = new VueRouter({
routes
})
export default router
Store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
currentModule: sessionStorage.getItem("currentModule") || "",
currentMenuId: sessionStorage.getItem("currentMenuId") || "",
//routes:[],
routes: {
industry: [],
commercial: [],
person: []
},
permissionInfo: JSON.parse(sessionStorage.getItem("permissionInfo")) || {},
},
mutations: {
updatePermissionInfo(state, permissionInfo) {
state.permissionInfo = permissionInfo
},
initRoutes(state, data) {
console.log(data)
if (data) {
state.routes[data.key] = data.value
} else {
state.routes = {
industry: [],
commercial: []
}
}
// if (JSON.stringify(data) == "{}"){
// console.log("空对象")
// state.routes = {
// industry: [],
// commercial: []
// }
// return
// }
// console.log("有对象")
// state.routes[data.key]= data.value
},
setCurrentModule(state, currentModule) {
state.currentModule = currentModule
sessionStorage.setItem("currentModule", currentModule)
},
setCurrentMenuId(state, currentMenuId) {
state.currentMenuId = currentMenuId
sessionStorage.setItem("currentMenuId", currentMenuId)
}
},
actions: {},
modules: {}
})
menu.js
import axios from 'axios'
export const initMenu = (router, store, to) => {
if (to.path == "/dashboard" || to.path == "/login") {
return
}
let currentMenuId = to.query.menuId || sessionStorage.getItem("currentMenuId")
let currentModule = to.params.id || sessionStorage.getItem("currentModule")
console.log(currentModule)
if (currentModule && store.state.routes[currentModule].length > 0) {
//sessionStorage.setItem("currentModule", currentModule)
store.commit("setCurrentModule", currentModule)
store.commit("setCurrentMenuId", currentMenuId)
console.log("return")
return;
} else {
//console.log("nei-1")
axios.get("mock/menudata-"+currentModule+".json").then(res => {
let data = res.data.data
let fmRoutes = formatRoutes(data);
//router.options.routes = fmRoutes
router.addRoutes(fmRoutes);
let dataObj = {}
dataObj.key = currentModule
dataObj.value = data
console.log(dataObj)
//dataObj.value = fmRoutes
store.commit("initRoutes", dataObj)
store.commit("setCurrentModule", currentModule)
store.commit("setCurrentMenuId", currentMenuId)
});
}
}
export const formatRoutes = (menuData) => {
//console.log(data);
let fmRoutes = [];
menuData.forEach(item => {
let {
path,
menuName,
component,
childMenu
} = item;
//console.log(children)
if (childMenu && childMenu instanceof Array) {
childMenu = formatRoutes(childMenu);
}
let fmRouter = {
path: path,
name: menuName,
children: childMenu,
component(resolve) {
if (component.startsWith("Home")) {
require(["../views/" + component + ".vue"], resolve);
} else if (component.startsWith("Baobiao")) {
require(["../views/baobiao/" + component + ".vue"], resolve);
} else if (component.startsWith("DataAccount")) {
require(["../views/dataAccount/" + component + ".vue"], resolve);
}
}
//component: () => import("../views/" + component + ".vue")
};
fmRoutes.push(fmRouter);
});
//console.log(fmRoutes);
return fmRoutes;
}
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 引入element-ui 组件
import ElementUI from 'element-ui'
// 引入element-ui 样式文件
import 'element-ui/lib/theme-chalk/index.css'
import './assets/js/jQuery-2.1.4.min.js'
import axios from "axios"
import '@/assets/css/global.css'
import 'font-awesome/css/font-awesome.min.css'
import {hasPermission} from "./utils/hasPermission"
import {initMenu} from "./utils/menu"
Vue.use(ElementUI)
//axios.defaults.baseURL = 'http://xxxxx'
// var instance = axios.create({
// baseURL: 'http://localhost:8080/'
// });
// Vue.prototype.$instance = instance
//添加一个请求拦截器
// axios.interceptors.request.use(function (config) {
// config.data=JSON.stringify(config.data);
// return config;
// }, function (error) {
// // Do something with request error
// console.info("error: ");
// console.info(error);
// return Promise.reject(error);
// });
import qs from "qs";
Vue.prototype.$qs = qs;
Vue.prototype.$axios = axios
Vue.prototype.hasPerm = hasPermission
Vue.config.productionTip = false
//正常运行的代码
router.beforeEach((to, from, next) => {
let token = window.sessionStorage.getItem('token');
if (to.path != '/login' && !token) {
next({
path: '/login'
})
} else {
if (to.path == '/login' && token) {
next('/dashboard')
} else {
initMenu(router,store,to)
next()
}
}
})
var VUE = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Home.vue
<template>
<div>
<el-container :style="{height: containerHeight, border: '1px solid #eee'}" id="con">
<el-header style="background:#3c8dbc;">
<div class="left pull-left">
<img src="../assets/imgs/weeglogo.png" alt />
<span>当前模块:</span>
<span>{{currentModuleToChinese}}</span>
</div>
<div class="right pull-right">
<router-link to="/dashboard">
<i class="fa fa-home margin_r20" style="font-size:20px;" aria-hidden="true"></i>
</router-link>
<span class="margin_r20">{{user}}</span>
<span style="color:#f9c05e;" @click="logout">
<i class="fa fa-power-off" aria-hidden="true"></i>
注销
</span>
</div>
</el-header>
<el-container>
<el-aside width="230px">
<el-menu
router
:default-active="routePath"
unique-opened
background-color="#1f3146"
text-color="#32acca"
active-text-color="#ffd04b"
>
<NavMenu :navMenus="menuData"></NavMenu>
</el-menu>
</el-aside>
<el-container>
<el-main>
<el-tabs
:value="activeTabItem"
@tab-remove="closeTab"
class="content-body"
@tab-click="tabClick"
>
<el-tab-pane label="首页" name="adminIndex">
<admin-index></admin-index>
</el-tab-pane>
<el-tab-pane
v-for="item in tabs"
:label="item.label"
:key="item.index"
:name="item.index+''"
:closable="item.closable"
>
</el-tab-pane>
</el-tabs>
<bread-crumb></bread-crumb>
<!-- <div>{{breab}}</div> -->
<router-view></router-view>
</el-main>
<el-footer>
footer
</el-footer>
</el-container>
</el-container>
</el-container>
</div>
</template>
<script>
import NavMenu from "@/components/NavMenu.vue";
import BreadCrumb from "@/components/Breadcrumb.vue";
import AdminIndex from "@/components/AdminIndex.vue";
export default {
data() {
return {
containerHeight: "",
//menuData: [],
routePath: "",
currentModuleChinese: "",
user: window.sessionStorage.getItem("user")
};
},
created() {
this.routePath = this.$route.path;
},
computed: {
menuData() {
let id = this.$store.state.currentModule;
console.log(id);
console.log(this.$store.state.routes[id]);
return this.$store.state.routes[id];
},
currentModuleToChinese() {
let currentModule = this.$store.state.currentModule;
switch (currentModule) {
case "industry":
return "模块1";
break;
case "commercial":
return "模块2";
break;
case "person":
return "模块3";
break;
}
},
tabs(){
return this.$store.state.tabs
},
activeTabItem(){
return this.$store.state.activeTabItem
}
},
components: { NavMenu, BreadCrumb },
methods: {
logout() {
this.$confirm("此操作将注销登录,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
//this.$axios.get("/logout");
window.sessionStorage.removeItem("token");
window.sessionStorage.removeItem("currentMenuId");
//this.$store.commit("initRoutes",{})
//this.$store.commit("initRoutes",null)
console.log(this.$store.state.routes);
location.reload();
//this.$router.replace("/")
})
.catch(() => {
this.$message({
type: "info",
message: "已取消操作"
});
});
}
},
// watch:{
// $route(){
// console.log(this.$route.path)
// this.routePath = this.$route.path
// }
// },
mounted() {
console.log("mounted");
this.containerHeight = window.innerHeight + "px";
console.log($);
$(window).resize(function() {
console.log("hi");
$("#con").height($(window).height() - 2);
});
//this.$router.push("/industrySub2")
}
};
</script>
<style>
.el-header {
background-color: #377fa9;
color: #fff;
height: 50px !important;
line-height: 50px !important;
}
.el-header .left img {
width: 120px;
vertical-align: middle;
}
.el-header .left span {
font-size: 20px;
color: #edf8ff;
margin-left: 15px;
}
.el-header .right {
float: right;
}
.el-header .right a {
color: #fff;
}
.el-aside {
/* color: #32acca !important; */
background: #1f3146 !important;
}
.el-menu {
border-right: none !important;
/* background: #1f3146 !important; */
}
.el-main{padding-top:0 !important;}
.el-footer {
background: gray;
height: 40px !important;
line-height: 40px !important;
}
.el-footer {
border-top: 1px solid #ccc;
background: #f8fafd;
padding: 10px;
margin-left: 0;
}
.el-footer img {
vertical-align: middle;
width: 65px;
margin-right: 10px;
}
</style>
关于无限极菜单,上一篇博客中有详细的介绍