- Home Page
+
+ User
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 1b7e30c..6f414c0 100755
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -1,24 +1,29 @@
@@ -35,7 +40,15 @@ export default {
height: 100%;
transition: width 0.28s;
}
- #main {
+ #app-header {
+ position: relative;
+ width: 100%;
+ padding: 0;
+ background-color: #fff;
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
+ z-index: 1000;
+ }
+ #app-main {
position: relative;
width: 100%;
background: #fff;
diff --git a/src/pages/login/login.vue b/src/pages/login/login.vue
index bbd90e6..e50f23b 100755
--- a/src/pages/login/login.vue
+++ b/src/pages/login/login.vue
@@ -49,9 +49,9 @@ export default {
login() {
let successMsg = '登录成功'
let errorMsg = '账号或密码错误'
- this.$store.dispatch('GET_LOGIN_DATA', this.form).then((res) => {
+ this.$store.dispatch('login/getLoginToken', this.form).then((res) => {
this.$message.success(successMsg)
- this.$route.query.redirect ? this.$router.push(this.$route.query.redirect) : this.$router.push('/')
+ this.$route.query.redirect ? this.$router.push(this.$route.query.redirect) : this.$router.push('/index')
}).catch((err) => {
console.log(err)
this.$message.error(errorMsg)
diff --git a/src/pages/other/redirect.vue b/src/pages/other/redirect.vue
new file mode 100644
index 0000000..a5998da
--- /dev/null
+++ b/src/pages/other/redirect.vue
@@ -0,0 +1,12 @@
+
diff --git a/src/router/index.js b/src/router/index.js
index 35b41bd..2af2d62 100755
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -2,100 +2,78 @@ import Vue from 'vue'
import Router from 'vue-router'
// 视图组件
-// const view = () => import('@/layout/view')
+// const PageView = () => import('@/layout/PageView')
Vue.use(Router)
-/* sideRoutes config
+/* Routes Config
* @meta
* icon: '' 菜单图标(支持svg-icon、el-icon)
* title: '' 菜单标题
* login: false 是否需要登录
* role: 'admin' || ['admin'] 是否需要权限
-* keep: false 是否需要缓存
+* keep: false 是否需要缓存(需要name才能生效)
* hidden: false 是否显示在菜单
* open: false 是否展开菜单(有子菜单前提下)
* redirectIndex: 0 重定向到第index位子菜单(有子菜单前提下)
+* affix: false 是否常驻在tagView组件上(外链无效)
*/
-// 要在侧边栏渲染的路由
-export const sideRoutes = [
+// 异步路由
+export const asyncRoutes = [
{
- name: 'home',
- path: 'home',
- component: () => import('@/pages/index/children/home/home'),
+ name: 'dashboard',
+ path: 'dashboard',
+ component: () => import('@/pages/index/children/dashboard'),
meta: {
icon: 'dashboard',
- title: '主页'
+ title: '主页',
+ affix: true
+ }
+ },
+ {
+ name: 'user',
+ path: 'user',
+ component: () => import('@/pages/index/children/user'),
+ meta: {
+ icon: 'user',
+ title: '用户管理'
}
}
]
-const routes = setRedirect([
+// 本地路由
+export const localRoutes = [
{
path: '',
- redirect: '/index'
+ redirect: '/login'
},
{
- name: 'index',
- path: '/index',
- component: () => import('@/pages/index/index'),
- meta: {
- title: '首页',
- login: true
- },
- children: sideRoutes
- },
- {
- name: 'login',
path: '/login',
component: () => import('@/pages/login/login')
},
{
- name: 'page401',
path: '/page401',
component: () => import('@/pages/other/page401')
},
{
- name: 'page404',
path: '/page404',
component: () => import('@/pages/other/page404')
- },
- {
- path: '*',
- redirect: '/page404'
}
-])
+]
-export default new Router({
+const createRouter = () => new Router({
// mode: 'history',
- routes,
- scrollBehavior(to, from, savedPosition) {
- if (savedPosition) {
- return savedPosition
- } else {
- return { x: 0, y: 0 }
- }
- }
+ routes: localRoutes,
+ scrollBehavior: () => ({ y: 0 })
})
-// 自动设置路由的重定向(有子路由前提下)
-function setRedirect(routes, redirect = '') {
- routes.forEach(route => {
- if (route.children && route.children.length > 0) {
- if (!route.redirect) {
- let defaultRedirectRoute = route.children.filter(item => !item.meta || !item.meta.hidden)[0]
- let redirectIndex = route.meta && route.meta.redirectIndex
- if (redirectIndex) {
- defaultRedirectRoute = route.children[redirectIndex]
- }
- let redirectName = defaultRedirectRoute.name
- route.redirect = `${redirect}/${route.name}/${redirectName}`
- }
- let index = route.redirect && route.redirect.lastIndexOf('/')
- let fatherDir = route.redirect && route.redirect.substring(0, index)
- route.children = setRedirect(route.children, fatherDir)
- }
- })
- return routes
+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
}
+
+export default router
diff --git a/src/store/index.js b/src/store/index.js
index 6b0462d..98d4f73 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,13 +1,11 @@
import Vue from 'vue'
import Vuex from 'vuex'
import cache from '@/utils/cache'
-import { getLogin, getUser } from '@/api/login'
Vue.use(Vuex)
const state = {
logs: [],
- user: '',
sidebarStatus: cache.getCookie('sidebarStatus') !== 'false'
}
const getters = {
@@ -16,13 +14,6 @@ const mutations = {
SET_LOGS(state, error) {
state.logs.unshift(error)
},
- SET_USER(state, val) {
- state.user = val
- },
- SET_LOGOUT(state) {
- state.user = ''
- cache.removeToken()
- },
SET_SIDEBAR_STATUS(state) {
let status = !state.sidebarStatus
state.sidebarStatus = status
@@ -30,42 +21,21 @@ const mutations = {
}
}
const actions = {
- // 获取登录数据
- async GET_LOGIN_DATA({ commit }, params) {
- return new Promise((resolve, reject) => {
- getLogin(params).then(res => {
- // console.log('login', res)
- if (res && res.token) {
- cache.setToken(res.token)
- resolve(res)
- } else {
- reject(new Error('nothing login data'))
- }
- }).catch(err => {
- reject(err)
- })
- })
- },
- // 获取用户数据
- async GET_USER_DATA({ commit }, token) {
- return new Promise((resolve, reject) => {
- getUser(token).then(res => {
- // console.log('user', res)
- if (res && res.code === 200 && res.data) {
- commit('SET_USER', res.data)
- resolve(res.data)
- } else {
- reject(new Error('nothing user data'))
- }
- }).catch(err => {
- reject(err)
- })
- })
- }
}
+
+// 自动引入和注册modules下的文件
+const modulesFiles = require.context('./modules', false, /\.js$/)
+const modules = modulesFiles.keys().reduce((modules, modulePath) => {
+ const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
+ const value = modulesFiles(modulePath)
+ modules[moduleName] = value.default
+ return modules
+}, {})
+
export default new Vuex.Store({
state,
getters,
mutations,
- actions
+ actions,
+ modules
})
diff --git a/src/store/modules/login.js b/src/store/modules/login.js
new file mode 100644
index 0000000..b4688a2
--- /dev/null
+++ b/src/store/modules/login.js
@@ -0,0 +1,58 @@
+import { getLogin, getUser } from '@/api/login'
+import { resetRouter } from '@/router'
+import cache from '@/utils/cache'
+
+export default {
+ namespaced: true,
+ state: {
+ user: ''
+ },
+ mutations: {
+ SET_USER(state, val) {
+ state.user = val
+ }
+ },
+ actions: {
+ // 获取登录数据
+ async getLoginToken({ commit }, params) {
+ return new Promise((resolve, reject) => {
+ getLogin(params).then(res => {
+ // console.log('login', res)
+ if (res && res.token) {
+ cache.setToken(res.token)
+ resolve(res)
+ } else {
+ reject(new Error('nothing login data'))
+ }
+ }).catch(err => {
+ reject(err)
+ })
+ })
+ },
+ // 获取用户数据
+ async getUserData({ commit }) {
+ return new Promise((resolve, reject) => {
+ let token = cache.getToken()
+ getUser(token).then(res => {
+ // console.log('user', res)
+ if (res.data) {
+ commit('SET_USER', res.data)
+ resolve(res.data)
+ } else {
+ reject(new Error('nothing user data'))
+ }
+ }).catch(err => {
+ reject(err)
+ })
+ })
+ },
+ logout({ commit }) {
+ return new Promise((resolve, reject) => {
+ cache.removeToken()
+ resetRouter()
+ commit('SET_USER', '')
+ resolve()
+ })
+ }
+ }
+}
diff --git a/src/store/modules/routes.js b/src/store/modules/routes.js
new file mode 100644
index 0000000..cc5830f
--- /dev/null
+++ b/src/store/modules/routes.js
@@ -0,0 +1,73 @@
+import { localRoutes, asyncRoutes } from '@/router'
+
+export default {
+ namespaced: true,
+ state: {
+ allRoutes: [], // 全部路由
+ sideRoutes: [] // 侧边栏路由
+ },
+ mutations: {
+ SET_ALL_ROUTES: (state, routes) => {
+ state.allRoutes = [...routes]
+ },
+ SET_SIDE_ROUTES: (state, routes) => {
+ state.sideRoutes = [...routes]
+ }
+ },
+ actions: {
+ generateRoutes({ commit }) {
+ return new Promise(resolve => {
+ asyncParentRoutes.children = [...asyncRoutes, extraPanentRoutes]
+ const sideRoutes = setRedirect([asyncParentRoutes])
+ const addRoutes = [...sideRoutes, extraGlobalRoutes] // 实际动态添加的路由
+ const allRoutes = [...localRoutes, ...addRoutes] // 所有路由
+ commit('SET_SIDE_ROUTES', sideRoutes)
+ commit('SET_ALL_ROUTES', allRoutes)
+ resolve(addRoutes)
+ })
+ }
+ }
+}
+
+const asyncParentRoutes = {
+ name: 'index',
+ path: '/index',
+ component: () => import('@/pages/index/index'),
+ meta: {
+ login: true,
+ title: '首页'
+ },
+ children: []
+}
+const extraPanentRoutes = {
+ path: 'redirect/:path*',
+ component: () => import('@/pages/other/redirect'),
+ meta: {
+ hidden: true
+ }
+}
+const extraGlobalRoutes = {
+ path: '*',
+ redirect: '/page404'
+}
+
+// 自动设置路由的重定向(有子路由前提下)
+function setRedirect(routes, redirect = '') {
+ routes.forEach(route => {
+ if (route.children && route.children.length > 0) {
+ if (!route.redirect) {
+ let defaultRedirectRoute = route.children.filter(item => !item.meta || !item.meta.hidden)[0]
+ let redirectIndex = route.meta && route.meta.redirectIndex
+ if (redirectIndex) {
+ defaultRedirectRoute = route.children[redirectIndex]
+ }
+ let redirectName = defaultRedirectRoute.name
+ route.redirect = `${redirect}/${route.name}/${redirectName}`
+ }
+ let index = route.redirect && route.redirect.lastIndexOf('/')
+ let fatherDir = route.redirect && route.redirect.substring(0, index)
+ route.children = setRedirect(route.children, fatherDir)
+ }
+ })
+ return routes
+}
diff --git a/src/store/modules/tagsView.js b/src/store/modules/tagsView.js
new file mode 100644
index 0000000..af926ea
--- /dev/null
+++ b/src/store/modules/tagsView.js
@@ -0,0 +1,72 @@
+import path from 'path'
+import { isExternal } from '@/utils/validate'
+
+export default {
+ namespaced: true,
+ state: {
+ tagsView: []
+ },
+ mutations: {
+ SET_TAGS_VIEW(state, views) {
+ state.tagsView = [...views]
+ },
+ ADD_TAGS_VIEW(state, view) {
+ if (view.name && view.meta && !view.meta.hidden) {
+ if (state.tagsView.some(v => v.path === view.path)) return
+ state.tagsView.push({ ...view })
+ }
+ },
+ CLOSE_TAGS_VIEW(state, view) {
+ let index = state.tagsView.findIndex(v => v.path === view.path)
+ state.tagsView.splice(index, 1)
+ },
+ CLOSE_OTHER_TAGS_VIEW(state, view) {
+ state.tagsView = state.tagsView.filter(v => v.meta.affix || v.path === view.path)
+ },
+ CLEAR_TAGS_VIEW(state) {
+ state.tagsView = state.tagsView.filter(v => v.meta.affix)
+ }
+ },
+ actions: {
+ initTagsView({ commit, rootState }) {
+ let affixTags = filterAffixTags(rootState.routes.sideRoutes[0].children)
+ commit('SET_TAGS_VIEW', affixTags)
+ function filterAffixTags(routes, basePath = '/index') {
+ let tags = []
+ routes.forEach(route => {
+ // 过滤外链
+ if (!isExternal(route.path)) {
+ let tagPath = path.resolve(basePath, route.path)
+ if (route.meta && route.meta.affix) {
+ tags.push({
+ fullPath: tagPath,
+ path: tagPath,
+ name: route.name,
+ meta: { ...route.meta }
+ })
+ }
+ if (route.children) {
+ const tempTags = filterAffixTags(route.children, tagPath)
+ if (tempTags.length >= 1) {
+ tags = [...tags, ...tempTags]
+ }
+ }
+ }
+ })
+ return tags
+ }
+ },
+ closeTagsView({ state, commit }, view) {
+ return new Promise(resolve => {
+ commit('CLOSE_TAGS_VIEW', view)
+ resolve([...state.tagsView])
+ })
+ },
+ clearTagsView({ state, commit }, view) {
+ return new Promise(resolve => {
+ commit('CLEAR_TAGS_VIEW', view)
+ resolve([...state.tagsView])
+ })
+ }
+ }
+}
diff --git a/src/utils/permission.js b/src/utils/permission.js
index 4273d9a..bec204d 100755
--- a/src/utils/permission.js
+++ b/src/utils/permission.js
@@ -5,39 +5,39 @@ import cache from '@/utils/cache'
// 登录验证,权限验证
router.beforeEach((to, from, next) => {
// 是否需要登录
- if (to.matched.some(record => record.meta.login)) {
- if (cache.getToken()) {
- if (to.path === '/login') {
- next('/')
+ if (cache.getToken()) {
+ if (to.path === '/login') {
+ next('/index')
+ } else {
+ // 是否已有用户信息
+ let userInfo = store.state.login.user
+ if (userInfo) {
+ assessPermission(userInfo.roles, to.meta.roles, next)
} else {
- // 是否已有用户信息
- if (store.state.user) {
- assessPermission(store.state.user.role, to.meta.role, next)
- } else {
- store.dispatch('GET_USER_DATA').then(res => {
- assessPermission(res.role, to.meta.role, next)
- }).catch(err => {
- console.log(err)
- // 可根据错误信息,做相应需求,这里默认token值失效
- window.alert('登录已失效,请重新登录')
- store.commit('SET_LOGOUT')
- next({ path: '/login', query: { redirect: to.fullPath } })
+ store.dispatch('login/getUserData').then(res => {
+ store.dispatch('routes/generateRoutes').then(addRoutes => {
+ router.addRoutes(addRoutes)
+ next({ ...to, replace: true })
})
- }
+ }).catch(err => {
+ console.log(err)
+ // 可根据错误信息,做相应需求,这里默认token值失效
+ window.alert('登录已失效,请重新登录')
+ store.commit('login/SET_LOGOUT')
+ next({ path: '/login', query: { redirect: to.fullPath } })
+ })
}
- } else {
- next({ path: '/login', query: { redirect: to.fullPath } })
}
} else {
- if (to.path === '/login' && cache.getToken()) {
- next('/')
- } else {
+ if (to.path === '/login') {
next()
+ } else {
+ next({ path: '/login', query: { redirect: to.fullPath } })
}
}
})
-// 验证权限
+// 验证权限(页面级)
function assessPermission(userRole, pageRole, next) {
let pass = false
// 页面无需权限 || 用户是管理员
diff --git a/src/utils/request.js b/src/utils/request.js
index 77c46fd..f552ccf 100755
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -52,8 +52,14 @@ export const request = async (url = '', type = 'GET', data = {}, isForm = false)
if (isForm) {
let form = new FormData()
Object.keys(data).forEach(key => {
- console.log('key', key)
- form.append(key, data[key])
+ let value = data[key]
+ if (Array.isArray(value)) {
+ value.forEach(item => {
+ form.append(key, item)
+ })
+ } else {
+ form.append(key, data[key])
+ }
})
data = form
}
diff --git a/src/utils/validate.js b/src/utils/validate.js
new file mode 100644
index 0000000..4393187
--- /dev/null
+++ b/src/utils/validate.js
@@ -0,0 +1,7 @@
+/**
+ * @param {String} path
+ * @returns {Boolean}
+ */
+export function isExternal(path) {
+ return /^(https?:|mailto:|tel:)/.test(path)
+}