这里是用到的一些插件
npm install axios --save
npm install element-ui --save
npm install stylus --save
npm install stylus-loader --save
在 mian.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'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
// 权限控制(这里没有做登陆拦截)
router.beforeEach((to, from, next) => {
// 根据路由元属性requiresAuth判断改路由是否需要控权限
if (to.matched.some(item => item.meta.requiresAuth)) {
// 1、(首次访问页面)登录页面跳转过来、直接访问地址、刷新页面时,from.path为 "/",
// 先请求权限接口,判断权限后再判断是否拦截
// 2、已访问本系统但权限为空
if (from.path === '/' || store.state.power_list.length === 0) {
store.dispatch('POWER_LIST').then(res => {
if (res.length === 0) {
next('/error')
} else {
// 验证是否有访问该页面的权限
store.dispatch('HAS_POWER', to).then(res => {
res === true ? next() : next('/error')
})
// 设置导航菜单选中状态
store.commit('SELECTITEM', to)
}
})
} else {
// 验证是否有访问该页面的权限
store.dispatch('HAS_POWER', to).then(res => {
res === true ? next() : next('/error')
})
// 设置导航菜单选中状态
store.commit('SELECTITEM', to)
}
} else {
next()
}
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
获取的用户权限列表存入这里
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 用户可以访问的url,用于判断访问权限
power_list: [],
// 导航列表选中项
selectItem: ''
},
mutations: {
// 设置权限列表
SETPOWERLIST (state, list) {
state.power_list = list
},
// 设置导航菜单选中状态
SELECTITEM (state, to) {
state.selectItem = to.path || window.location.pathname
}
},
actions: {
// 获取权限列表
POWER_LIST ({ state, commit }, to) {
return new Promise((resolve, reject) => {
axios.get('/api/power.json').then(function (res) {
let result = res.data.data
let newArr = result.find((item) => {
return item.userId === Number(sessionStorage.userId)
})
if (newArr && newArr.powerlist.length !== 0) {
// 设置权限列表
commit('SETPOWERLIST', newArr.powerlist)
resolve(newArr.powerlist)
} else {
resolve([])
}
})
})
},
// 判断是否有权限访问当前页面
HAS_POWER (context, to) {
return new Promise((resolve, reject) => {
let flag = context.state.power_list.some(item => {
return item.path.includes(to.path) // 判断是否包含指定的路径
})
resolve(flag)
})
}
}
})
需要验证权限的路由都添加了 meta 属性
import Vue from 'vue'
import Router from 'vue-router'
import Login from './components/login.vue'
import Home from './components/Home.vue'
import Transfer from './components/transfer.vue'
import Error from './components/error.vue'
// 异步加载组件(按需加载)
const Album = () => import('./components/album.vue')
const BarChart = () => import('./components/barChart.vue')
const LineChar = () => import('./components/lineChart.vue')
const PieChart = () => import('./components/pieChart.vue')
const LiquidfillChart = () => import('./components/liquidfillChart.vue')
const WorldChart = () => import('./components/worldChart.vue')
const ChinaChart = () => import('./components/chinaChart.vue')
const ProvinceChart = () => import('./components/provinceChart.vue')
const ScatterChart = () => import('./components/scatterChart.vue')
const BDMap = () => import('./components/BDmap.vue')
const ChooseCity = () => import('./components/chooseCity.vue')
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'login',
component: Login
},
{
path: '/home',
name: 'home',
component: Home,
children: [
{
path: '/home/',
name: 'transfer',
component: Transfer,
meta: {
requiresAuth: true
}
},
{
path: 'album',
name: 'album',
component: Album,
meta: {
requiresAuth: true
}
},
{
path: 'barchart',
name: 'barchart',
component: BarChart,
meta: {
requiresAuth: true
}
},
{
path: 'linechart',
name: 'linechar',
component: LineChar,
meta: {
requiresAuth: true
}
},
{
path: 'piechart',
name: 'piechart',
component: PieChart,
meta: {
requiresAuth: true
}
},
{
path: 'liquidfillchart',
name: 'liquidfillchart',
component: LiquidfillChart,
meta: {
requiresAuth: true
}
},
{
path: 'worldchart',
name: 'worldchart',
component: WorldChart,
meta: {
requiresAuth: true
}
},
{
path: 'chinachart',
name: 'chinachart',
component: ChinaChart,
meta: {
requiresAuth: true
}
},
{
path: 'provincechart',
name: 'provincechart',
component: ProvinceChart,
meta: {
requiresAuth: true
}
},
{
path: 'scatterchart',
name: 'scatterchart',
component: ScatterChart,
meta: {
requiresAuth: true
}
},
{
path: 'BDmap',
name: 'BDmap',
component: BDMap,
meta: {
requiresAuth: true
}
},
{
path: 'choosecity',
name: 'choosecity',
component: ChooseCity,
meta: {
requiresAuth: true
}
}
]
},
{
path: '/error',
name: 'error',
component: Error
}
]
})
<template>
<div class="wrapper">
<el-form :model="ruleForm" ref="ruleForm" :rules="rules" label-width="80px">
<el-form-item prop="username" label="用户名">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input v-model="ruleForm.password"></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>
import axios from 'axios'
export default {
data () {
return {
ruleForm: {
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] }
]
}
}
},
methods: {
submitForm (formName) {
let that = this
this.$refs[formName].validate((valid) => {
if (valid) {
axios.get('/api/users.json').then(function (res) {
let result = res.data.data
let flag = false
for (var i = 0; i < result.length; i++) {
if (result[i].username === that.ruleForm.username && result[i].password
=== that.ruleForm.password) {
flag = true
// 将符合条件的用户ID和昵称存储到本地
sessionStorage.userId = result[i].id
sessionStorage.nickname = result[i].nickname
}
}
if (flag) {
that.$router.push('/home/')
} else {
that.$message.error('用户名或密码错误')
}
})
}
})
},
resetForm (formName) {
this.$refs[formName].resetFields() // 重置用户名和密码
}
}
}
</script>
<style scoped>
.wrapper {
width: 340px;
border: 1px solid #cccccc;
border-radius: 5px;
padding: 50px;
position: absolute;
top: 200px;
left: 50%;
transform: translateX(-50%);
}
</style>
登录成功后会进入这个组件,它主要用来跳转到权限列表的第一个路由地址
<template>
<div class="wrapper"></div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'transfer',
data () {
return {}
},
computed: {
// 使用 mapState 辅助函数会将store中的state映射到局部计算属性中
...mapState(['power_list'])
},
mounted () {
// 该页面只做一个重定向的操作
let redirectUrl = this.power_list[0].path
this.$router.push({ path: redirectUrl })
}
}
</script>
<style scoped>
</style>
<template>
<el-container>
<el-aside>
<div class="title"><i class="el-icon-apple"></i>后台管理系统</div>
<el-menu :default-active="selectItem" class="el-menu-vertical-demo" :router="true">
<el-menu-item :index="item.path" v-for="item in power_list" :key="item.name">
<i class="el-icon-menu"></i>
<span slot="title">{{item.name}}</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px;">
<div class="fold"><i class="el-icon-s-fold"></i></div>
<el-dropdown @command="handleCommand">
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="personal"><i class="el-icon-user"></i>个人中心</el-dropdown-item>
<el-dropdown-item command="account"><i class="el-icon-setting"></i>账号设置</el-dropdown-item>
<el-dropdown-item command="logout"><i class="el-icon-switch-button"></i>退出登录</el-dropdown-item>
</el-dropdown-menu>
<el-avatar src="../static/avatar.jpg" shape="square" size="large"></el-avatar>
</el-dropdown>
<div class="welcome">欢迎您,{{nickname}}</div>
</el-header>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
data () {
return {
nickname: sessionStorage.nickname || '未知用户'
}
},
computed: {
// 使用 mapState 辅助函数会将store中的state映射到局部计算属性中
// ...mapState({
// power_list: state => state.power_list
// selectItem: state => state.selectItem
// })
...mapState(['power_list', 'selectItem'])
},
methods: {
// 使用 mapMutations 辅助函数会将store中的mutations的方法映射到methods中
// ...mapMutations({
// SETPOWERLIST: 'SETPOWERLIST'
// })
...mapMutations(['SETPOWERLIST']),
handleCommand (command) {
if (command === 'logout') {
this.$confirm('此操作将退出系统, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
sessionStorage.clear() // 清空本地存储
this.SETPOWERLIST([]) // 将权限列表清空
this.$router.replace({ path: '/' }) // 返回登录页
}).catch(() => {
console.log('取消')
})
} else {
this.$notify.info({
title: '提示',
message: '暂时没有该功能哦',
duration: 3000
})
}
}
}
}
</script>
<style lang="stylus" scoped>
.el-container
height 100%
position relative
.el-header
height 100%
font-size 18px !important
line-height 60px
background #fff
border-bottom 1px solid #e6e6e6
.fold
float left
width 40px
font-size 28px
text-align left
cursor pointer
.welcome
float right
margin-right 20px
.el-dropdown
float right
height 100%
display flex
align-items center
font-size 20px !important
cursor pointer
.el-main
padding 0
position relative
.el-aside
width 210px !important
border-right solid 1px #e6e6e6 !important
background #fff
.title
height 60px
line-height 60px
font-size 22px
font-weight bold
padding-left 20px
box-sizing border-box
border-bottom 1px solid #e6e6e6
i
font-size 25px
.el-menu
border-right none
</style>
<template>
<div class="wrapper">
<h2>您暂无权限,请联系管理员</h2>
<input type="button" value="返回" class="back" @click="back">
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
}
},
computed: {
// 使用 mapState 辅助函数会将store中的state映射到局部计算属性中
// ...mapState({
// power_list: state => state.power_list
// })
...mapState(['power_list'])
},
methods: {
back () {
// 如果权限列表不为空,说明是登录后进入的错误页面
if (this.power_list.length !== 0) {
this.$router.go(-2) // 返回上一页
} else {
// 如果权限列表为空,说明未登录,从地址栏强行进入跳转到的错误页面
// 或者登陆了却没有权限,跳转到的错误页面
sessionStorage.clear()
this.$router.replace({ path: '/' }) // 返回登录页
}
}
}
}
</script>
<style scoped>
h2 {
margin: 50px 0 0 50px;
font-size: 40px;
}
.back {
width: 80px;
height: 30px;
line-height: 30px;
text-align: center;
background: DarkOrange;
border-radius: 5px;
color: #fff;
margin: 100px;
cursor: pointer;
}
.back:hover {
background: orange;
}
</style>
在 vue.config.js 文件里配置(这里是代理到本地)
module.exports = {
baseUrl: process.env.NODE_ENV === 'production' ? '/' : '/',
// 输出文件目录
outputDir: 'dist',
// 静态资源目录 (js, css, img, fonts)
assetsDir: 'assets',
// lintOnSave:{ type:Boolean default:true } 是否使用eslint
lintOnSave: true,
// productionSourceMap:{ type:Bollean,default:true } 生产源映射
// 如果不需要生产时的源映射,那么将此设置为 false 可以加速生产构建
productionSourceMap: true,
// devServer: { type: Object } 3个属性 host, port, https, 它支持 webpack-dev-server 的所有选项
devServer: {
port: 8080,
host: 'localhost',
https: false,
open: false, // 配置自动启动浏览器
// 配置跨域处理
proxy: {
'/api': {
target: 'http://localhost:8080',
pathRewrite: {
'^/api': '/mock'
}
}
}
}
}
用户列表数据 users.json
{
"status": true,
"data": [
{
"id": 1,
"username": "admin",
"password": "123456",
"nickname": "凡夫"
},
{
"id": 2,
"username": "tom",
"password": "123456",
"nickname": "Tom"
},
{
"id": 3,
"username": "jack",
"password": "123456",
"nickname": "Jack"
},
{
"id": 4,
"username": "rose",
"password": "123456",
"nickname": "Rose"
}
]
}
每个用户对应的权限列表数据 power.json
{
"status": true,
"data": [
{
"userId": 1,
"powerlist": [
{ "name": "仿去哪相册", "path": "/home/album" },
{ "name": "柱状图", "path": "/home/barchart" },
{ "name": "折线图", "path": "/home/linechart" },
{ "name": "饼状图", "path": "/home/piechart" },
{ "name": "水球图", "path": "/home/liquidfillchart" },
{ "name": "世界地图", "path": "/home/worldchart" },
{ "name": "中国地图", "path": "/home/chinachart" },
{ "name": "省份地图", "path": "/home/provincechart" },
{ "name": "地图散点图", "path": "/home/scatterchart" },
{ "name": "百度地图", "path": "/home/BDmap" },
{ "name": "选择城市", "path": "/home/choosecity" }
]
},
{
"userId": 2,
"powerlist": [
{ "name": "柱状图", "path": "/home/barchart" },
{ "name": "折线图", "path": "/home/linechart" },
{ "name": "饼状图", "path": "/home/piechart" },
{ "name": "水球图", "path": "/home/liquidfillchart" },
{ "name": "百度地图", "path": "/home/BDmap" },
{ "name": "选择城市", "path": "/home/choosecity" }
]
},
{
"userId": 3,
"powerlist": [
{ "name": "折线图", "path": "/home/linechart" },
{ "name": "饼状图", "path": "/home/piechart" },
{ "name": "水球图", "path": "/home/liquidfillchart" },
{ "name": "省份地图", "path": "/home/provincechart" },
{ "name": "地图散点图", "path": "/home/scatterchart" }
]
},
{
"userId": 4,
"powerlist": []
}
]
}