修改前端项目
先去官网下载前端项目,
官网地址: Ant Design Pro of Vue
介绍
Ant Design Pro 一个企业级中后台前端/设计解决方案,提供了很多的典型模板,并据此构建了一套基于 Ant Design Pro的中后台管理控制台的脚手架,,它可以帮助你快速搭建企业级中后台产品原型。
这个项目需要你对前端知识有一些基础,比如 vue,vuex,ant design vue等,如果对这些都没有了解,建议你先去查看相关文档。
开始使用
下载项目
git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git my-project
导入自己的开发工具中,我用的是idea,
导入之后,右下角会有提示,运行 install命令,点击运行,或者在terminal中运行 npm install命令
npm install
安装完成之后
运行 npm run serve 启动项目
npm run serve
启动成功
如果启动报错的 checkexp.... ,注销 NumberInfo.vue 中 style 的 @import "index"
点击链接或浏览器输入地址 访问
用户名 密码都是admin
到现在,前端项目启动成功
删除广告
官网中有提示,
- 删除basicLayout.vue中的 ads 代码段
2.删除在控制台打印的screelog
删除 src/bootstrap.js中 printansi 相关代码
注意爆红,是因为ESlint代码检查,可以自行关闭
(但是关闭ellint在提交代码到gitee中时会报错,只要在提交时,取消run git hooks 就可以)
修改路由信息
- 在前端的router.config.js中定义好路由与权限信息,用户登录后,根据后台返回的权限信息,过滤出用户可操作的路由,然后加入到路由表中
- 在用户登录后,根据角色或权限请求后台,后台返回用户可操作的路由信息,然后加入到路由表中
其实两种写法都可以,看在项目中需要哪一种,
官方文档对这两种都有说明,
我使用的是第一种,官方提供了一张流程图,理解后修改就简单了
流程图特别清楚, 用户在登录后会调用src/permission.js=>GetInfo方法,GetInfo方法的返回值就只是右侧的用户信息对象结构体,包含用户信息,角色和权限信息,获取到角色信息后再调用store(就是vuex,集中式存储管理应用的所有组件的状态,不懂得可以去搜一下)/module/permission.js=>GenerateRoutes过滤出可操作的路由表,最后会存储在store中,然后在src/permission.js=>GetInfo().then()中从store中获取存储的路由表,调用router.addRouters()加载到路由中。
当然,用户实体可以自己定义,比如,我在前端项目中只需要权限信息,不需要角色信息,我的用户实体可以定义成这样
我的后台可以直接返回一个权限数组,同时在调用/module/permission.js=>GenerateRoutes过滤权限的时候传递的也是权限信息,GenerateRoutes中根据权限信息去过滤出可操作的权限,
Ant Design Pro 提供的是前端代码和 mock
模拟数据的开发模式,
mock可以模拟 Ajax 请求,生成并返回模拟数据,有需要的可以看下官方文档
目前我们的数据都是来自上边图中的文件里
首先修改 user.js,修改成只有用户数据和权限信息的格式
mock/service/user.js,只需要用户信息userInfo和权限信息permissions
import Mock from 'mockjs2'
import { builder } from '../util'
const info = options => {
console.log('options', options)
const userInfo = {
id: '4291d7da9005377ec9aec4a71ea837f',
name: '天野远子',
username: 'admin',
password: '',
avatar: '/avatar2.jpg',
status: 1,
telephone: '',
lastLoginIp: '27.154.74.117',
lastLoginTime: 1534837621348,
creatorId: 'admin',
createTime: 1497160610259,
merchantCode: 'TLif2btpzg079h15bk',
deleted: 0,
roleId: 'admin',
permissions: [
'dashboard', 'table'
]
}
return builder(userInfo)
}
Mock.mock(/\/api\/user\/info/, 'get', info)
修改src/permission.js,注意红线圈中的地方,是需要修改的地方
注:如果在是idea中,点击方法是可以跳转到对应文件的,因为后边好多是重名的文件或方法名,所以一定要确定自己改对了文件
store.geeters.roles.length 修改为 store.getters.permission
generatorDynamicRouter(token)方法修改为 store.dispatch('GenerateRoutes').
import router, { resetRouter } from './router'
import store from './store'
import storage from 'store'
import NProgress from 'nprogress' // progress bar
import '@/components/NProgress/nprogress.less' // progress bar custom style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { i18nRender } from '@/locales'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const allowList = ['login', 'register', 'registerResult'] // no redirect allowList
const loginRoutePath = '/user/login'
const defaultRoutePath = '/dashboard/workplace'
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
to.meta && typeof to.meta.title !== 'undefined' && setDocumentTitle(`${i18nRender(to.meta.title)} - ${domTitle}`)
/* has token */
const token = storage.get(ACCESS_TOKEN)
if (token) {
if (to.path === loginRoutePath) {
next({ path: defaultRoutePath })
NProgress.done()
} else {
// check login user.permissions is null
if (store.getters.permissions.length === 0) {
// request login userInfo
store
.dispatch('GetInfo')
.then(res => {
// generate dynamic router
store.dispatch('GenerateRoutes').then(() => {
// 根据roles权限生成可访问的路由表
// 动态添加可访问路由表
// VueRouter@3.5.0+ New API
resetRouter() // 重置路由 防止退出重新登录或者token过期后页面未刷新,导致的路由重复添加
store.getters.addRouters.forEach(r => {
router.addRoute(r)
})
// 请求带有 redirect 重定向时,登录自动重定向到该地址
const redirect = decodeURIComponent(from.query.redirect || to.path)
if (to.path === redirect) {
// set the replace: true so the navigation will not leave a history record
next({ ...to, replace: true })
} else {
// 跳转到目的路由
next({ path: redirect })
}
})
})
.catch(() => {
notification.error({
message: '错误',
description: '请求用户信息失败,请重试'
})
// 失败时,获取用户信息失败时,调用登出,来清空历史保留信息
store.dispatch('Logout').then(() => {
next({ path: loginRoutePath, query: { redirect: to.fullPath } })
})
})
} else {
next()
}
}
} else {
if (allowList.includes(to.name)) {
// 在免登录名单,直接进入
next()
} else {
next({ path: loginRoutePath, query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
user.js中添加变量 permissions和SET_PERMISSIONS方法
并且在actions中登录成功后获取用户信息方法里调用 SET_PERMISSON方法把权限信息存储到store中,当然退出时要也要删除权限信息
import storage from 'store'
import expirePlugin from 'store/plugins/expire'
import { login, getInfo, logout } from '@/api/login'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { welcome } from '@/utils/util'
storage.addPlugin(expirePlugin)
const user = {
state: {
token: '',
name: '',
welcome: '',
avatar: '',
roles: [],
info: {},
permissions: []
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, { name, welcome }) => {
state.name = name
state.welcome = welcome
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
},
SET_INFO: (state, info) => {
state.info = info
}
},
actions: {
// 登录
Login ({ commit }, userInfo) {
return new Promise((resolve, reject) => {
login(userInfo).then(response => {
const result = response.result
storage.set(ACCESS_TOKEN, result.token, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
commit('SET_TOKEN', result.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo ({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(response => {
const result = response.result
if (result.permissions && result.permissions.length > 0) {
const permissions = result.permissions
commit('SET_PERMISSIONS', permissions)
commit('SET_INFO', result)
} else {
reject(new Error('getInfo: roles must be a non-null array !'))
}
commit('SET_NAME', { name: result.name, welcome: welcome() })
commit('SET_AVATAR', result.avatar)
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 登出
Logout ({ commit, state }) {
return new Promise((resolve) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
storage.remove(ACCESS_TOKEN)
resolve()
}).catch((err) => {
console.log('logout fail:', err)
// resolve()
}).finally(() => {
})
})
}
}
}
export default user
接着看 src/permission.js,获取权限信息之后,就可以根据权限生成用户可操作的路由
modules/permissions.js,GenerateRoutes 最终会过滤出可操作的路由并且存储到store中
import { asyncRouterMap, constantRouterMap } from '@/config/router.config'
import cloneDeep from 'lodash.clonedeep'
import store from '../index'
/**
* 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
*
* @param permission
* @param route
* @returns {boolean}
*/
function hasPermission (permission, route) {
if (route.meta && route.meta.permission) {
let flag = false
for (let i = 0, len = permission.length; i < len; i++) {
flag = route.meta.permission.includes(permission[i])
if (flag) {
return true
}
}
return false
}
return true
}
/**
* 单账户多角色时,使用该方法可过滤角色不存在的菜单
*
* @param roles
* @param route
* @returns {*}
*/
// eslint-disable-next-line
function hasRole(roles, route) {
if (route.meta && route.meta.roles) {
return route.meta.roles.includes(roles.id)
} else {
return true
}
}
// 过滤权限
function filterAsyncRouter (routerMap, permissions) {
const accessedRouters = routerMap.filter(route => {
if (hasPermission(permissions, route)) {
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, permissions)
}
return true
}
return false
})
return accessedRouters
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes ({ commit }) {
return new Promise(resolve => {
console.log(store.getters.permissions)
const routerMap = cloneDeep(asyncRouterMap)
const accessedRouters = filterAsyncRouter(routerMap, store.getters.permissions)
commit('SET_ROUTERS', accessedRouters)
resolve()
})
}
}
}
export default permission
然后就是在 src/permission.js 中 挂载到基础路由中了
到这里,重启项目 看下是不是只有 'dashboard', 'table' 这两个权限对用的模块了。
到这里,前端就算是告一段落了,接下来就是后端项目了
后端项目springboot+mybatis+shiro+redis+mysql