最近新项目,开始独立作一个web B端管理系统,从框架搭建到初版项目线上部署,大概花了1月有余时间。利用这两天时间好好整理下这中间遇到的一些问题。javascript
由于项目周期的缘由,所有本身开发的话花费时间周期将加长,通过多个框架对比,最后选择了Element-admin-ui + vue。前端
以前有用过element UI,但Element-admin-ui仍是一次使用。vue
Element-admin-ui官网地址:https://panjiachen.github.io/vue-element-admin-site/zh/guide/java
点击 基础模板 进入 github,按照步骤克隆下载,这里就不一一介绍了。ios
有5篇 文章,请仔细阅读git
初始版本github
管理员在登陆帐号后接口会返回一个token,并将其存到请求头,以后在每一个接口请求时进行token验证并进行拦截。web
进入登陆页,点登陆时调用接口正常返回可看到axios
我这边是已经修改了登陆接口的地址,而后后端返回的数据。后端
而后将token存入请求头中,名字请自行定义。而后token拦截的状态码需根据接口返回进行更改。
前期不须要拦截的话以下图,将画圈部分注释,再将下面的return注释打开便可
有个需求,管理员经过帐号登陆,接口返回对应的菜单,而后再添加到路由进行渲染。
Element-admin-ui里面是经过登陆帐号的类型去判断,如admin,super_editor等,由前端来控制。如今须要彻底由后端来返回路由进行跳转,因此里边的内容须要改改。
找到\src\store\modules下user.js
这里是调用登陆接口的地方,而后将登陆成功返回的数据增长到缓存中
// 登陆 Login({ commit }, userInfo) { const username = userInfo.username.trim() return new Promise((resolve, reject) => { login(username, userInfo.password).then(response => { const data = response.data setToken(data.token) if (data.userInfoBO) { // 验证返回的userInfoBO是不是一个非空数组 commit('SET_ROLES', data.userInfoBO) } else { reject('getInfo: roles must be a non-null array !') } localStorage.setItem('routerslist', JSON.stringify(data.userMenuBOS)) // 存储缓存 这里拿到返回的菜单 commit('SET_TOKEN', data.token) commit('SET_NAME', data.userInfoBO.nickName) localStorage.setItem('name', data.userInfoBO.nickName || '') commit('SET_AVATAR', data.userInfoBO.headImgUrl) resolve(response) }).catch(error => { reject(error) }) }) },
第2步,在router文件夹下新建两个文件并放入内容
_import_development.js
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
_import_production.js
module.exports = file => () => import('@/views/' + file + '.vue')
第3步,index.js下除了公共路由,其余路由先注释
第4步,打开permission.js,增长路由
import router from './router' const _import = require('./router/_import_' + process.env.NODE_ENV) // 获取组件的方法 import Layout from '@/views/layout/Layout' // Layout 是架构组件,不在后台返回,在文件里单独引入 import NProgress from 'nprogress' // Progress 进度条 import { getToken } from '@/utils/auth' // 验权 var getRouter // 用来获取后台拿到的路由 const whiteList = ['/login'] // 不重定向白名单 router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { if (to.path === '/login') { next({ path: '/' }) NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it } } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { next('/login') NProgress.done() } } if (!getRouter) { // 不加这个判断,路由会陷入死循环 if (!getObjArr('routers')) { // getRouter = rouerConfig // 后台拿到路由 getRouter = JSON.parse(window.localStorage.getItem('routerslist')) // 后台拿到路由 if (!getRouter) { console.log('没拿到返回数据') return false } saveObjArr('routers', getRouter) // 存储路由到localStorage routerGo(to, next) // 执行路由跳转方法 // axios.get('https://www.easy-mock.com/mock/5a5da330d9b48c260cb42ca8/example/antrouter').then(res => { // console.log(res) // getRouter = res.data.data.router//后台拿到路由 // saveObjArr('routers', getRouter) //存储路由到localStorage // routerGo(to, next)//执行路由跳转方法 // }) } else { // 从localStorage拿到了路由 getRouter = getObjArr('routers') // 拿到路由 routerGo(to, next) } } else { next() } }) function routerGo(to, next) { getRouter = filterAsyncRouter(getRouter) // 过滤路由 router.addRoutes(getRouter) // 动态添加路由 global.antRouter = getRouter // 将路由数据传递给全局变量,作侧边栏菜单渲染工做 next({ ...to, replace: true }) } function saveObjArr(name, data) { // localStorage 存储数组对象的方法 localStorage.setItem(name, JSON.stringify(data)) } function getObjArr(name) { // localStorage 获取数组对象的方法 return JSON.parse(window.localStorage.getItem(name)) } // 递归操做 function filterAsyncRouter(asyncRouterMap) { // 遍历后台传来的路由字符串,转换为组件对象 const accessedRouters = asyncRouterMap.filter(route => { if (route.component) { if (route.component === 'Layout') { // Layout组件特殊处理 route.component = Layout } else { route.component = _import(route.component) } } if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children) } return true }) return accessedRouters }
第5步,打开\src\views\layout\components\Sidebar\index.vue,这个文件是控制左菜单展现,而后修改return,从新运行便可。
补充一下:当有一个超级管理的帐户和一个普通帐户,当登陆超级管理后返回菜单,而后非正常状态退出(好比强制清除token,不清除缓存),这样退出再登陆普通帐户后管理页面展现的菜单仍是超级管理的菜单。缘由是由于没有清除缓存。
解决方式:
打开permission.js,在回到登陆页时删除localStorage
由于是管理端,会有各类表格,其中一定须要导出功能,element表格自带选择功能,加入方式请自行查看文档。
那怎么经过点击导出选中下载表格呢?
刚开始我考虑的是用window.open,但这样的话就不会经过token验证和拦截了。因此这种方式不行
点导出后后端返回的是二进制流,后面想着用提交form表单的形式来提交,试了虽然能导出文件,但里边内容没法展现出来,也只能pass。
后面通过同事提醒找到了Blob方法。实现方式以下:
第1步:接口的请求增长responseType: 'arraybuffer'
// 根据ID下载配送表 export function getDeliveryExport(params) { return request({ url: '/manage/health/delivery/export?ids=' + params, responseType: 'arraybuffer', method: 'get' }) }
第2步,导出选中
// 导出选中 addexport() { if (this.multipleSelection.length <= 0) { this.$message({ showClose: true, message: '未选中数据', type: 'error' }) return } for (let i = 0; i < this.multipleSelection.length; i++) { this.ids.push(this.multipleSelection[i].id) } console.log('导出', JSON.stringify(this.ids)) getDeliveryExport(this.ids).then( function(response) { const filename = decodeURI(response.headers['content-disposition'].split(';')[1].split('=')[1]) || '分拣表.xlsx' // 获取后端返回存在请求头里边的表单名字 this.fileDownload(response.data, filename) }.bind(this) ).catch( function(error) { this.$message({ showClose: true, message: error, type: 'error' }) }.bind(this) ) }, fileDownload(data, fileName) { const blob = new Blob([data], { type: 'application/octet-stream' }) const filename = fileName || 'filename.xls' if (typeof window.navigator.msSaveBlob !== 'undefined') { window.navigator.msSaveBlob(blob, filename) } else { var blobURL = window.URL.createObjectURL(blob) var tempLink = document.createElement('a') tempLink.style.display = 'none' tempLink.href = blobURL tempLink.setAttribute('download', filename) if (typeof tempLink.download === 'undefined') { tempLink.setAttribute('target', '_blank') } document.body.appendChild(tempLink) tempLink.click() document.body.removeChild(tempLink) window.URL.revokeObjectURL(blobURL) } },
第3步,选中导出,完成