网站的后台管理系统,使用的是vue框架,不过当时使用的还是vue2.0,并未使用3.0。服务端的API上一节已经讲过,使用node开发的。
UI使用的element框架,也是非常简单友好的一套vue的UI框架,基本上也不用再自己设置什么样式了。
一,封装axios
数据通讯必须使用ajax实现,自然要使用到axios框架,对于常用的第三方框架,通常都要自己再封装一层
1,万一以后项目更改或者框架不稳定了,可以直接修改自己的封装层即可
2,自己封装的代码里,可以增加拦截器、验证等功能。
我这里主要是每次发出请求时,都需要从本地调用token,然后放入header发送给服务端,如果token错误,服务端则会给出相应的错误代码,如果没有错误,则继续执行ajax请求,代码如下:
import axios from 'axios'
import qs from 'qs'
import Vue from 'vue'
import Router from '../router'
import clear from 'utils/clear'
const ajax = axios.create({
baseURL: 'https://api.shuanghei.com',
timeout: 5000,
withCredentials: false,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
ajax.defaults.transformRequest = (data) =>
qs.stringify(data)
// , { arrayFormat: 'brackets' }, { indices: false } 暂时不需要
ajax.interceptors.request.use(
(config) => {
const token = window.localStorage.getItem('access_token')
config.headers.authorization = token // 请求时添加coken
return config
},
(err) => {
return Promise.reject(err)
}
)
ajax.interceptors.response.use(
(res) => {
return res.data
},
(err) => {
// 这里可以预处理错误
if (err.response.status === 401) {
const { code } = err.response.data
if (code === 10007) {
// 此code是服务端 返回得错误编号,固定死
clear()
Router.push('/login')
Vue.prototype.$message.error('帐号已在别处登录')
return
}
if (code === 10006) {
clear()
Router.push('/login')
Vue.prototype.$message.error('帐号已被管理员强制下线')
return
}
if (code === 10003) {
clear()
Router.push('/login')
Vue.prototype.$message.error('已超时,请重新登录')
return
}
}
return Promise.reject(err.response.data)
}
)
export default ajax
二,自定义组件
项目中很多组件都需要重用,比如左边的树形菜单、面包屑导航、table的某些列等,这里就说说列吧。
绝大部分页面,都会有以上这些列,我本来是想把这几个列合成一个完成的自定义组件的,后来发现element2.0下,会出现排列错乱的问题,不知道现在3.0解决了没,那么我就把每一个列创建一个自定义组件,如下图:
我们就以createuser列为例吧,CreateUser.vue代码如下:
<template>
<el-table-column prop="createUser"
label="创建人"
:width="width">
</el-table-column>
</template>
<script>
export default {
props: {
width: {
type: String,
default: '100'
}
}
}
</script>
index.js代码如下,进行组件的安装:
import Component from './CreateUser'
export default {
install: function (Vue) {
Vue.component('col-createuser', Component)
}
}
三,权限控制
这一块还是比较麻烦的,分三块
1,左侧菜单栏,是否显示相对应的链接
2,用户直接输入url,要在页面当前判断是否有权限进入
3,页面中的增删改button的判断,是否显示
用户在登陆的时候,如果登陆成功,则会从服务端返回相对应的权限列表,加密后保存到本地储存,代码如下:
.then((res) => {
window.sessionStorage.setItem('apiid', res.data._id) // 只保留session
window.sessionStorage.setItem('apikey', encrypt(res.data.apikey))
window.sessionStorage.setItem('username', this.loginForm.name)
window.sessionStorage.setItem('menuList', encrypt(res.data.menuList)) // 菜单
window.sessionStorage.setItem('routerList', encrypt(res.data.routerList))// 每个路由的增删改
window.sessionStorage.setItem('UIrouter', encrypt(res.data.UIrouter))// 是否有路由的入口
window.sessionStorage.setItem('RoleList', encrypt(res.data.rolenameList))// 用户角色
this.$router.push('/welcome')
})
左侧菜单栏,则根据res.data.menuList来加载。
每个页面的路由控制,我会在router.beforeEach里判断,核心代码如下:
router.beforeEach((to, from, next) => {
document.title = to.meta.title
if (to.path === '/login') {
return next()
} else {
if (
!window.sessionStorage.getItem('apiid') ||
!window.sessionStorage.getItem('apikey')
) {
clear()
return next('/login')
// 如果本地没有存储apikey,那就要用户强制登录,页面只要关闭就要重新登录
}
}
if (!window.localStorage.getItem('access_token')) {
refreshToken(next)
} else {
const createtime = Number.parseInt(
window.localStorage.getItem('createtime')
)
const expiresIn = Number.parseInt(window.localStorage.getItem('expiresIn'))
const currtime = Math.floor(Date.now() / 1000)
if (expiresIn + createtime < currtime + expiresIn / 2) {
// 如果超过一半时间
refreshToken(next)
} else {
const UIrouter = JSON.parse(
decrypt(window.sessionStorage.getItem('UIrouter'))
) // 判断用户是有此路由的权限,避免用户直接输入浏览器访问
if (to.path.toLowerCase() === '/welcome' || UIrouter.includes(to.path)) {
next()
} else {
Vue.prototype.$message.warning('非法进入')
clear()
next('/login')
}
}
}
})
最后,页面中,button是否显示,我使用了一个mixins,代码如下:
import { decrypt } from 'utils/crypto'
export default {
computed: {
isDisable () {
return function (model, method) {
const item = JSON.parse(
decrypt(window.sessionStorage.getItem('routerList'))
).find((n) => {
return n.model === model
})
if (item) {
return !item.methods.includes(method)
}
return true
}
}
}
}