diff --git a/admin/package.json b/admin/package.json index cf878abe..80f76fc7 100644 --- a/admin/package.json +++ b/admin/package.json @@ -17,7 +17,7 @@ "nprogress": "^0.2.0", "vue": "^3.2.25", "vue-echarts": "^6.0.0", - "vue-router": "^4.0.0-0", + "vue-router": "^4.0.14", "vuedraggable": "^4.1.0", "vuex": "^4.0.0-0" }, diff --git a/admin/src/App.vue b/admin/src/App.vue index 4b364594..2d983563 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -1,18 +1,14 @@ diff --git a/admin/src/permission.ts b/admin/src/permission.ts index 8d775886..dc4a138e 100644 --- a/admin/src/permission.ts +++ b/admin/src/permission.ts @@ -4,7 +4,7 @@ import NProgress from 'nprogress' import store from './store' -import router, { asyncRoutes } from './router' +import router from './router' import 'nprogress/nprogress.css' // NProgress配置 @@ -22,16 +22,28 @@ router.beforeEach(async (to, from, next) => { const token = store.getters.token if (token) { // 获取用户信息 - if (store.getters.permission == null) { - store.commit('permission/setSidebar', asyncRoutes[0].children) - await store.dispatch('user/getUser') - await store.dispatch('permission/getPermission') - } - if (to.path === loginPath) { - next({ path: defaultPath }) + if (store.getters.permission.length === 0) { + try { + await store.dispatch('user/getUser') + const routes = await store.dispatch('permission/generateRoutes') + routes.forEach((route: any) => { + router.addRoute('index', route) // 动态添加可访问路由表 + }) + console.log(router.getRoutes()) + if (to.path === '/login') { + next({ path: '/' }) + } else { + next({ ...to, replace: true }) //确保addRoutes已完成 + } + } catch { + await store.dispatch('user/logout') + next({ path: loginPath, query: { redirect: to.fullPath } }) + NProgress.done() + } } else { next() } + } else if (whiteList.includes(to.path as string)) { // 在免登录白名单,直接进入 next() diff --git a/admin/src/router/index.ts b/admin/src/router/index.ts index 483db509..509b2fda 100644 --- a/admin/src/router/index.ts +++ b/admin/src/router/index.ts @@ -1,55 +1,76 @@ + import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' -import workbench from '@/views/workbench/index.vue' import Layout from '@/layout/index.vue' -import Error404 from '@/views/error/404.vue' -import Error500 from '@/views/error/500.vue' -// Router modules -import setting from './modules/setting' -import permission from './modules/permission' -import decoration from './modules/decoration' -import content from './modules/content' -import channel from './modules/channel' -import application from './modules/application' -export const asyncRoutes: Array = [ +/** + * Note: 路由配置项 + * + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * + * meta : { + keepAlive: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏的名字 + icon: 'svg-name' // 设置该路由的图标 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + query: '{"id": 1}' // 访问路由的默认传递参数 + hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 + } + */ + +// 公共路由 +export const constantRoutes: Array = [ { path: '/', redirect: 'workbench', + name: 'index', + component: Layout + }, + + { + path: '/permission', component: Layout, children: [ { - path: '/workbench', - component: workbench, - meta: { title: '工作台', icon: 'icon-home', permission: ['view'] } + path: 'admin/edit', + component: () => import('@/views/permission/admin/edit.vue'), + meta: { title: '编辑管理员', activeMenu: '/permission/admin' } }, - // decoration, // 装修管理 - // application,// 应用管理 - // content, // 内容管理 - // channel, // 渠道管理 - permission, - setting + { + path: 'menu/edit', + component: () => import('@/views/permission/menu/edit.vue'), + meta: { title: '编辑菜单', activeMenu: '/permission/menu' } + }, + { + path: 'role/edit', + component: () => import('@/views/permission/role/edit.vue'), + meta: { title: '编辑角色', activeMenu: '/permission/role' } + } ] - } -] - -export const constRoutes: Array = [ + }, { path: '/login', component: () => import('@/views/account/login.vue') }, { - path: '/error/500', - component: Error500 + path: '/500', + component: () => import('@/views/error/500.vue') }, - { path: '/:pathMatch(.*)*', name: '404', component: Error404 } + { + path: '/:pathMatch(.*)*', + component: () => import('@/views/error/404.vue') + } ] -export const getAsyncRoutes = () => { - return asyncRoutes -} const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes: [...asyncRoutes, ...constRoutes] + routes: constantRoutes, + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition + } else { + return { top: 0 } + } + }, }) export default router diff --git a/admin/src/router/modules/application.ts b/admin/src/router/modules/application.ts deleted file mode 100644 index 6cd782fd..00000000 --- a/admin/src/router/modules/application.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { RouteRecordRaw, RouterView } from 'vue-router' - -const routes: RouteRecordRaw = { - path: '/application', - redirect: '/application/notification', - component: RouterView, - meta: { title: '应用管理', icon: 'icon-setting' }, - children: [ - { - path: '/application/notification', - redirect: '/application/notification/index', - component: RouterView, - meta: { title: '消息通知' }, - children: [ - { - path: '/application/notification/index', - component: () => import('@/views/application/notification/index.vue'), - meta: { title: '通知设置', permission: ['view'] }, - }, - { - path: '/application/notification/detail', - component: () => import('@/views/application/notification/detail.vue'), - meta: { hidden: true, title: '通知设置', permission: ['view'] }, - }, - { - path: '/application/sms/index', - component: () => import('@/views/application/sms/index.vue'), - meta: { title: '短信设置', permission: ['view'] }, - }, - { - path: '/application/sms/detail', - component: () => import('@/views/application/sms/detail.vue'), - meta: { hidden: true, title: '短信设置', permission: ['view'] }, - }, - ], - }, - ], -} - -export default routes diff --git a/admin/src/router/modules/channel.ts b/admin/src/router/modules/channel.ts deleted file mode 100644 index 94241de8..00000000 --- a/admin/src/router/modules/channel.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { RouteRecordRaw, RouterView } from "vue-router" -const routes: RouteRecordRaw = { - path: '/channel', - name: 'channel', - meta: { title: '渠道管理', icon: 'icon-setting' }, - component: RouterView, - children: [{ - path: '/channel/mp_wechat', - name: 'mp_wechat', - meta: { - title: '微信公众号', - parentPath: '/channel', - }, - component: RouterView, - children: [{ - path: '/channel/mp_wechat/index', - name: 'mp_wechat_index', - meta: { - title: '渠道设置', - parentPath: '/channel', - permission: ['view'] - }, - component: () => import('@/views/channel/mp_wechat/index.vue'), - }, { - path: '/channel/mp_wechat/menu', - name: 'mp_wechat_menu', - meta: { - title: '菜单管理', - parentPath: '/channel', - permission: ['view'] - }, - component: () => import('@/views/channel/mp_wechat/menu.vue'), - }, { - path: '/channel/mp_wechat/reply/follow_reply', - name: 'follow_reply', - meta: { - title: '关注回复', - parentPath: '/channel', - permission: ['view'] - }, - component: () => import('@/views/channel/mp_wechat/reply/follow_reply.vue'), - }, { - path: '/channel/mp_wechat/reply/keyword_reply', - name: 'keyword_reply', - meta: { - title: '关键字回复', - parentPath: '/channel', - permission: ['view'] - }, - component: () => import('@/views/channel/mp_wechat/reply/keyword_reply.vue'), - }, { - path: '/channel/mp_wechat/reply/default_reply', - name: 'default_reply', - meta: { - title: '默认回复', - parentPath: '/channel', - permission: ['view'] - }, - component: () => import('@/views/channel/mp_wechat/reply/default_reply.vue'), - }, { - path: '/channel/mp_wechat/reply/reply_edit', - name: 'reply_edit', - meta: { - title: '默认编辑', - parentPath: '/channel', - hidden: true, - permission: ['view'] - }, - component: () => import('@/views/channel/mp_wechat/reply/reply_edit.vue'), - }] - }, { - path: '/channel/wechat_app', - name: 'wechat_app', - meta: { - title: '微信小程序', - parentPath: '/channel' - }, - component: () => import('@/views/channel/wechat_app/index.vue') - }, { - path: '/channel/app_store', - name: 'app_store', - meta: { - title: 'APP', - parentPath: '/channel', - }, - component: () => import('@/views/channel/app_store/index.vue'), - }, { - path: '/channel/h5_store', - name: 'h5_store', - meta: { - title: 'H5', - parentPath: '/channel', - }, - component: () => import('@/views/channel/h5_store/index.vue') - }, { - path: '/wechat/wechat_platform', - name: 'wechat_platform', - meta: { - title: '微信开放平台', - parentPath: '/channel', - }, - component: () => import('@/views/channel/wechat_platform/index.vue') - }] -} - -export default routes \ No newline at end of file diff --git a/admin/src/router/modules/content.ts b/admin/src/router/modules/content.ts deleted file mode 100644 index 0f1ed60b..00000000 --- a/admin/src/router/modules/content.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { RouteRecordRaw, RouterView } from 'vue-router' - -const routes: RouteRecordRaw = { - path: '/content', - redirect: '/content/advertising', - component: RouterView, - meta: { title: '内容管理', icon: 'icon-setting' }, - children: [ - // { - // path: '/content/advertising', - // redirect: '/content/advertising/lists', - // component: RouterView, - // meta: { title: '广告管理' }, - // children: [ - // { - // path: '/content/advertising/lists', - // component: () => import('@/views/content/advertising/advertising.vue'), - // meta: { title: '广告列表' }, - // }, - // { - // path: '/content/advertising/advertising_edit', - // component: () => import('@/views/decoration/advertising_edit.vue'), - // meta: { - // title: '广告列表', - // parent: '/content/advertising/lists', - // hidden: true, - // }, - // }, - // { - // path: '/content/advertising/position', - // component: () => import('@/views/content/advertising/position.vue'), - // meta: { title: '广告位' }, - // }, - // ], - // }, - { - path: '/content/information', - redirect: '/content/information/lists', - component: RouterView, - meta: { title: '资讯管理' }, - children: [ - { - path: '/content/information/lists', - component: () => import('@/views/content/information/lists.vue'), - meta: { title: '资讯列表' }, - }, - { - path: '/content/information/information_edit', - component: () => import('@/views/content/information/information_edit.vue'), - meta: { - title: '资讯列表', - parent: '/content/information/lists', - hidden: true, - }, - }, - { - path: '/content/information/position', - component: () => import('@/views/content/information/category.vue'), - meta: { title: '资讯分类' }, - }, - ], - }, - ], -} - -export default routes diff --git a/admin/src/router/modules/decoration.ts b/admin/src/router/modules/decoration.ts deleted file mode 100644 index 692355da..00000000 --- a/admin/src/router/modules/decoration.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { RouteRecordRaw, RouterView } from 'vue-router' - -const routes: RouteRecordRaw = { - path: '/decoration', - redirect: '/decoration/home', - component: RouterView, - meta: { title: '装修管理', icon: 'icon-setting' }, - children: [ - { - path: '/decoration/home', - component: () => import('@/views/decoration/home.vue'), - meta: { title: '首页装修' }, - }, - { - path: '/decoration/home_edit', - component: () => import('@/views/decoration/home_edit.vue'), - meta: { title: '首页装修', parent: '/decoration/home', hidden: true }, - }, - { - path: '/decoration/tabbar', - component: () => import('@/views/decoration/tabbar.vue'), - meta: { title: '底部标签栏' }, - }, - - { - path: '/decoration/advertising', - component: () => import('@/views/decoration/advertising.vue'), - meta: { title: '广告管理' }, - }, - { - path: '/decoration/advertising_edit', - component: () => import('@/views/decoration/advertising_edit.vue'), - meta: { title: '广告管理', parent: '/decoration/advertising', hidden: true }, - }, - ], -} - -export default routes diff --git a/admin/src/router/modules/permission.ts b/admin/src/router/modules/permission.ts deleted file mode 100644 index af102811..00000000 --- a/admin/src/router/modules/permission.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { RouteRecordRaw, RouterView } from 'vue-router' - -const routes: RouteRecordRaw = { - path: '/permission', - redirect: '/permission/admin', - component: RouterView, - meta: { title: '权限管理', icon: 'icon-quanxian' }, - children: [ - { - path: '/permission/admin', - component: () => import('@/views/permission/admin/index.vue'), - meta: { title: '管理员', permission: ['view'] } - }, - { - path: '/permission/admin/edit', - component: () => import('@/views/permission/admin/edit.vue'), - meta: { - title: '管理员', - parent: '/permission/admin', - hidden: true - } - }, - { - path: '/permission/role', - component: () => import('@/views/permission/role/index.vue'), - meta: { title: '角色', permission: ['view'] } - }, - { - path: '/permission/role/edit', - component: () => import('@/views/permission/role/edit.vue'), - meta: { - title: '角色', - parent: '/permission/role', - hidden: true - } - }, - { - path: '/permission/menu', - component: () => import('@/views/permission/menu/index.vue'), - meta: { title: '菜单', permission: ['view'] } - }, - { - path: '/permission/menu/edit', - component: () => import('@/views/permission/menu/edit.vue'), - meta: { - title: '菜单', - parent: '/permission/menu', - hidden: true - } - } - ] -} - -export default routes diff --git a/admin/src/router/modules/setting.ts b/admin/src/router/modules/setting.ts deleted file mode 100644 index b7b78432..00000000 --- a/admin/src/router/modules/setting.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { RouteRecordRaw, RouterView } from 'vue-router' - -const routes: RouteRecordRaw = { - path: '/setting', - redirect: '/setting/service', - component: RouterView, - meta: { title: '系统设置', icon: 'icon-setting' }, - children: [ - { - path: '/setting/service', - redirect: '/setting/service/online_service', - component: RouterView, - meta: { - title: '客服设置', - hidden: true - }, - children: [ - { - path: '/setting/service/online_service', - component: () => import('@/views/setting/service/online_service.vue'), - meta: { - title: '在线客服' - } - } - ] - }, - { - path: '/setting/website', - redirect: '/setting/website/information', - component: RouterView, - meta: { title: '网站设置' }, - children: [ - { - path: '/setting/website/information', - component: () => import('@/views/setting/website/information.vue'), - meta: { - title: '网站信息', - permission: ['view'] - } - }, - { - path: '/setting/website/filing', - component: () => import('@/views/setting/website/filing.vue'), - meta: { - title: '备案信息', - permission: ['view'] - } - }, - { - path: '/setting/website/protocol', - component: () => import('@/views/setting/website/protocol.vue'), - meta: { - title: '政策/协议', - permission: ['view'], - hidden: true - } - } - ] - }, - { - path: '/setting/user', - redirect: '/setting/user', - component: RouterView, - meta: { - title: '用户设置', - hidden: true - }, - children: [ - { - path: '/setting/user', - component: () => import('@/views/setting/user/index.vue'), - meta: { - title: '用户设置', - permission: ['view'] - } - }, - { - path: '/setting/user/login', - component: () => import('@/views/setting/user/login.vue'), - meta: { - title: '登录注册', - permission: ['view'] - } - } - ] - }, - { - path: '/setting/system', - redirect: '/setting/system/environment', - component: RouterView, - meta: { title: '系统维护' }, - children: [ - { - path: '/setting/website/environment', - component: () => import('@/views/setting/system/environment.vue'), - meta: { - title: '系统环境', - permission: ['view'] - } - }, - { - path: '/setting/website/journal', - component: () => import('@/views/setting/system/journal.vue'), - meta: { - title: '系统日志', - permission: ['view'] - } - }, - { - path: '/setting/website/cache', - component: () => import('@/views/setting/system/cache.vue'), - meta: { - title: '系统缓存', - permission: ['view'] - } - } - ] - }, - { - // component: RouterView, - path: '/setting/personal/personal_data', - component: () => import('@/views/setting/personal/personal_data.vue'), - meta: { - title: '个人设置', - permission: ['view'], - hidden: true - } - } - ] -} - -export default routes diff --git a/admin/src/store/getters.ts b/admin/src/store/getters.ts index 6768e95e..0ec73e2b 100644 --- a/admin/src/store/getters.ts +++ b/admin/src/store/getters.ts @@ -9,8 +9,7 @@ const getters: GetterTree = { // 通用配置 config: state => state.app.config, // 权限列表 - permission: state => state.permission.permission, - isAdmin: state => state.permission.isAdmin + permission: state => state.user.permissions } export default getters diff --git a/admin/src/store/modules/app.ts b/admin/src/store/modules/app.ts index e8b27d2e..9c625c86 100644 --- a/admin/src/store/modules/app.ts +++ b/admin/src/store/modules/app.ts @@ -7,7 +7,8 @@ export interface AppModule { const app: Module = { namespaced: true, state: { - config: {} + // 公共配置 + config: {}, }, mutations: { setConfig(state, data) { diff --git a/admin/src/store/modules/permission.ts b/admin/src/store/modules/permission.ts index 0f678005..0861132a 100644 --- a/admin/src/store/modules/permission.ts +++ b/admin/src/store/modules/permission.ts @@ -1,45 +1,40 @@ import { Module } from 'vuex' import { RouteRecordRaw } from 'vue-router' -import { apiConfigGetAuth } from '@/api/auth' +import { apiConfigGetRoutes } from '@/api/auth' +import { filterAsyncRoutes } from '@/core/lib/router' + export interface PermissionModule { + routes: Array sidebar: Array - permission: any[] | null - isAdmin: number } const permission: Module = { namespaced: true, state: { + // 路由 + routes: [], // 左侧菜单 - sidebar: [], - // 权限列表 - permission: null, - // 是否是管理员 - isAdmin: 0 + sidebar: [] }, getters: {}, mutations: { setSidebar(state, data) { state.sidebar = data }, - setPermission(state, data) { - state.permission = data.auth - state.isAdmin = data.root - } }, actions: { - getPermission({ commit }) { + generateRoutes({ commit }) { return new Promise((resolve, reject) => { - // apiConfigGetAuth() - // .then(data => { - // commit('setPermission', data) - // resolve(data) - // }) - // .catch(err => { - // reject(err) - // }) - - resolve({}) + apiConfigGetRoutes() + .then((data: any) => { + const rdata = JSON.parse(JSON.stringify(data)) + const routes = filterAsyncRoutes(rdata) + commit('setSidebar', routes) + resolve(routes) + }) + .catch(err => { + reject(err) + }) }) } } diff --git a/admin/src/store/modules/user.ts b/admin/src/store/modules/user.ts index fadd1884..e90c3370 100644 --- a/admin/src/store/modules/user.ts +++ b/admin/src/store/modules/user.ts @@ -5,12 +5,14 @@ import { apiLogin, apiLogout, apiUserInfo } from '@/api/user' export interface UserModule { token: string user: object + permissions: any[] } const user: Module = { namespaced: true, state: { token: cache.get(TOKEN) || '', - user: {} + user: {}, + permissions: [] }, mutations: { setToken(state, data) { @@ -19,13 +21,15 @@ const user: Module = { }, setUser(state, data) { state.user = data + }, + setPermission(state, permissions) { + state.permissions = permissions } }, actions: { // 登录 login({ commit }, data) { const { account, password } = data - console.log(data, '----------------------') return new Promise((resolve, reject) => { apiLogin({ @@ -48,6 +52,7 @@ const user: Module = { .then(data => { commit('setToken', '') commit('setUser', {}) + commit('setPermission', []) cache.remove(TOKEN) resolve(data) }) @@ -60,8 +65,9 @@ const user: Module = { getUser({ commit }) { return new Promise((resolve, reject) => { apiUserInfo() - .then(data => { + .then((data: any) => { commit('setUser', data) + commit('setPermission', data.permissions) resolve(data) }) .catch(error => { diff --git a/admin/src/styles/index.scss b/admin/src/styles/index.scss index beb722b7..2209dc40 100644 --- a/admin/src/styles/index.scss +++ b/admin/src/styles/index.scss @@ -8,3 +8,5 @@ @import 'common'; // element样式 @import 'element'; +// 动画 +@import 'transition'; diff --git a/admin/src/styles/transition.scss b/admin/src/styles/transition.scss new file mode 100644 index 00000000..15d4fbb3 --- /dev/null +++ b/admin/src/styles/transition.scss @@ -0,0 +1,27 @@ + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-25px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(25px); +} diff --git a/admin/src/utils/enum.ts b/admin/src/utils/enum.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/admin/src/utils/validate.ts b/admin/src/utils/validate.ts new file mode 100644 index 00000000..b12d6fe3 --- /dev/null +++ b/admin/src/utils/validate.ts @@ -0,0 +1,8 @@ +/** + * @description 判断字符串是否是id=1&name=2这种类型 + * @param { String } str + * @returns { Boolean } + */ +export function isQuery(str: string) { + return /([^?&=]+)=([^?&=]*)/g.test(str) +} \ No newline at end of file diff --git a/admin/src/views/account/login.vue b/admin/src/views/account/login.vue index 5aee5813..55b04c2e 100644 --- a/admin/src/views/account/login.vue +++ b/admin/src/views/account/login.vue @@ -117,6 +117,7 @@ export default defineComponent({ query: { redirect } } = route const path = typeof redirect === 'string' ? redirect : '/' + router.replace(path) }) .catch(err => { diff --git a/admin/src/views/permission/admin/index.vue b/admin/src/views/permission/admin/index.vue index e31fc2de..5c0b33ff 100644 --- a/admin/src/views/permission/admin/index.vue +++ b/admin/src/views/permission/admin/index.vue @@ -28,7 +28,7 @@ - 新增管理员 + 新增管理员
@@ -69,6 +69,7 @@