工程mountain-element-ui是基於 vue-admin-template擴展的, 主要實現權限管理系統,包括用戶管理、
角色管理、部門管理、菜單管理等。實現動態路由加載,樹形結構展現、表格數據展現等。複製代碼
前端工程地址javascript
後端工程地址前端
上圖列出來了主要須要注意的文件目錄,學習這個工程前須要要有vuejs基礎,瞭解webpack框架,用過vue-cli客戶端搭建過工程。vue
登陸頁面java
主頁面webpack
登陸頁面vue代碼在src/views/login
路徑下git
主頁面header(頭文件)和側邊欄頁面代碼在src/layout/components
這個路徑中,左上角圖標是在Sidebar
側邊欄中使用Logo.vue
組件github
頁面佈局組件:請看src/layout/index.vue
組件web
用戶管理vue-router
角色管理vue-cli
部門管理
菜單管理
頁面代碼在src/views/sys/
路徑下,user:用戶管理、dept:部門管理、role:角色管理、menu:菜單管理
頁面中的組件都是element ui中的,請查看Element UI官網
在src/router/index.js
中配置,建立路由路由數組
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
}
]
},
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首頁', icon: 'dashboard' }
}]
}
]複製代碼
根據上面的數組建立路由對象
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}複製代碼
從後臺接口獲取菜單配置json數據
把菜單數據轉化成前端js的路由可用數據
動態的把轉換的數據添加到路由中
主要代碼以下:
export function remoteRouter(menuList) {
const initRouter = getLocalStorage('initRouter')
router.options.routes = initRouter
router.addRoutes(router.options.routes)
const list = getMenuTree(menuList.children)
// 動態添加路由
for (let i = 0; i < list.length; i++) {
var isFlag = router.options.routes.some(function(obj) {
if (obj.path === list[i].path) {
return true
}
})
if (!isFlag) {
router.options.routes.push(list[i])
}
}
router.addRoutes(router.options.routes)
}複製代碼
循環路由以前處理事件:
獲取緩存的靜態路由和動態路由數據
動態路由添加路由
獲取緩存token,判斷token是否存在,不存在跳轉到登陸頁面
router.beforeEach(async(to, from, next) => {
const initRouterList = getLocalStorage('initRouter')
const list = getLocalStorage('router')
if (router.options.routes.length <= initRouterList.length && list != null) {
const remoteRouter = menuTreeToPageMenu(list)
// 動態添加路由
if (remoteRouter !== null && remoteRouter !== undefined) {
for (let i = 0; i < remoteRouter.length; i++) {
var isFlag = router.options.routes.some(function(obj) {
if (obj.path === remoteRouter[i].path) {
return true
}
})
if (!isFlag) {
router.options.routes.push(remoteRouter[i])
}
}
router.addRoutes(router.options.routes)
next({ ...to, replace: true })
}
}
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// get user info
await store.dispatch('user/getInfo')
next()
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})複製代碼
因爲這個工程師先後分離,先後端工程同時開發,我把後端接口服務容許跨域訪問,前端工程沒有作跨域配置;同時把原來工程中mock接口屏蔽掉。在main.js
中註釋掉mock引用,以下:
// import { mockXHR } from '../mock'
// if (process.env.NODE_ENV === 'production') {
// mockXHR()
// }複製代碼
在用戶登陸時,登陸成功後,服務端返回數據中有token,把token存儲在全局裏,這樣其餘接口訪問時就能夠帶上這個token參數,後端驗證這個token,token值合法在繼續查詢接口;若是不合法返回前端提示。代碼在src/store/modules/user.js
文件中,主要代碼以下:
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { data } = response
commit('SET_TOKEN', data.token)
setToken(data.token)
if (response.data.sessionId !== undefined) {
setCookies('sessionId', response.data.sessionId)
}
setLocalStorage('router', response.sysMenus.children)
remoteRouter(response.sysMenus)
resolve(response)
}).catch(error => {
reject(error)
})
})
}複製代碼
退出接口:在用戶退出系統時,清除token、重置router等,主要代碼以下:
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
removeToken()
resetRouter()
removeLocalStorage('router')
resolve()
}).catch(error => {
reject(error)
})
})
}複製代碼
請求封裝:在請求後端接口時,包裝一個全局的訪問request.js
文件,在請求接口時,請求頭中添加X-Token
參數,主要代碼以下:
service.interceptors.request.use(
config => {
// do something before request is sent
const sessionId = getCookies('sessionId')
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = getToken()
}
if (sessionId != null && sessionId !== undefined) {
config.headers['token'] = sessionId
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
複製代碼
請求返回封裝:請求返回數據後,全局判斷返回碼code
,若是須要作全局判斷在這裏判斷,主要代碼以下:
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('您已經註銷,您能夠取消以停留在此頁面,或再次登陸', '確認註銷', {
confirmButtonText: '從新登陸',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
if (res.code === 600) {
Message({
message: '您沒有操做權限',
type: 'warning',
duration: 5 * 1000
})
return
}
const promise = new Promise(function(resolve, reject) {
reject(new Error(res.message || 'Error'))
})
promise.catch(function(error) {
console.log(error)
})
return promise
// return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
}
複製代碼
配置文件路徑/vue.config.js
publicPath: '/mt',
outputDir: 'dist',
assetsDir: 'static',
lintOnSave: process.env.NODE_ENV === 'development'
複製代碼
開發環境配置:配置文件.env.development
VUE_APP_BASE_API = 'http://127.0.0.1:8080/mt'
複製代碼
生產環境配置:配置文件.env.production
VUE_APP_BASE_API = 'http://172.22.112.130:8080/mt'
複製代碼
開發啓動:npm run dev
構建命令:npm run build:prod
集成Swagger2
集成quartz框架
集成docker
權限管理系統實現:用戶管理、角色管理、部門管理、菜單管理等模塊