diff --git a/snowy-admin-web/index.html b/snowy-admin-web/index.html index 9b3a7ffb..55d2e348 100644 --- a/snowy-admin-web/index.html +++ b/snowy-admin-web/index.html @@ -12,7 +12,7 @@ .app-loading__logo {margin-bottom: 30px;} .app-loading__logo img {width: 90px;vertical-align: bottom;} .app-loading__title {font-size: 24px;color: #333;margin-top: 30px;} - .h-full { height: 100% } + .app-main { height: 100% } @keyframes loader { 0% { transform: rotate(0deg); @@ -29,7 +29,7 @@ We're sorry but Snowy2.0 doesn't work properly without JavaScript enabled. Please enable it to continue. -
+
diff --git a/snowy-admin-web/public/img/login_background.png b/snowy-admin-web/public/img/login_background.png index 8e03f8ef..9a7424a1 100644 Binary files a/snowy-admin-web/public/img/login_background.png and b/snowy-admin-web/public/img/login_background.png differ diff --git a/snowy-admin-web/public/img/login_background_front.png b/snowy-admin-web/public/img/login_background_front.png deleted file mode 100644 index 877ace17..00000000 Binary files a/snowy-admin-web/public/img/login_background_front.png and /dev/null differ diff --git a/snowy-admin-web/src/assets/images/index_001.png b/snowy-admin-web/src/assets/images/index_001.png deleted file mode 100644 index 9e6af8d1..00000000 Binary files a/snowy-admin-web/src/assets/images/index_001.png and /dev/null differ diff --git a/snowy-admin-web/src/assets/images/index_002.png b/snowy-admin-web/src/assets/images/index_002.png deleted file mode 100644 index d67b31ef..00000000 Binary files a/snowy-admin-web/src/assets/images/index_002.png and /dev/null differ diff --git a/snowy-admin-web/src/components/Table/index.vue b/snowy-admin-web/src/components/Table/index.vue index 86bc18d0..aa481fb5 100644 --- a/snowy-admin-web/src/components/Table/index.vue +++ b/snowy-admin-web/src/components/Table/index.vue @@ -415,9 +415,12 @@ } else { data.localDataSource = r.records } - data.localLoading = false getTableProps() }) + .catch(() => {}) + .finally(() => { + data.localLoading = false + }) } } diff --git a/snowy-admin-web/src/layout/components/tags.vue b/snowy-admin-web/src/layout/components/tags.vue index 8b376efb..a196c5dd 100644 --- a/snowy-admin-web/src/layout/components/tags.vue +++ b/snowy-admin-web/src/layout/components/tags.vue @@ -19,7 +19,7 @@
-
关闭其他标签
+
关闭其他
@@ -28,7 +28,7 @@
-
新窗口打开
+
新窗口
diff --git a/snowy-admin-web/src/router/index.js b/snowy-admin-web/src/router/index.js index 1823808e..24596f6e 100644 --- a/snowy-admin-web/src/router/index.js +++ b/snowy-admin-web/src/router/index.js @@ -18,9 +18,9 @@ import whiteListRouters from './whiteList' import userRoutes from '@/config/route' import tool from '@/utils/tool' import { cloneDeep } from 'lodash-es' -const modules = import.meta.glob('/src/views/**/**.vue') -import { globalStore, searchStore } from '@/store' +import { globalStore } from '@/store' import { NextLoading } from '@/utils/loading' +import { useMenuStore } from '@/store/menu' // 进度条配置 NProgress.configure({ showSpinner: false, speed: 500 }) @@ -72,13 +72,21 @@ router.beforeEach(async (to, from, next) => { return false } + if (!isGetRouter.value) { + // 初始化菜单加载,代码位置不能变动 + const menuStore = useMenuStore() + menuStore.refreshMenu() + isGetRouter.value = true + next({ ...to, replace: true }) + return false + } + const token = tool.data.get('TOKEN') // 页面刷新,加载loading if (from.path === '/' && to.path !== '/login' && !window.nextLoading && token) { NextLoading.start() } - if (to.path === '/login') { // 当用户输入了login路由,将其跳转首页即可 if (token) { @@ -89,7 +97,6 @@ router.beforeEach(async (to, from, next) => { } // 删除路由(替换当前layout路由) router.addRoute(routes[0]) - isGetRouter.value = false next() return false } else { @@ -108,27 +115,6 @@ router.beforeEach(async (to, from, next) => { if (to.meta.fullpage) { to.matched = [to.matched[to.matched.length - 1]] } - // 加载动态/静态路由 - if (!isGetRouter.value) { - const apiMenu = tool.data.get('MENU') || [] - if (apiMenu.length === 0) { - // 创建默认模块,显示默认菜单 - apiMenu[0] = cloneDeep(userRoutes.module[0]) - } - const childrenApiMenu = apiMenu[0].children - apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu] - let menuRouter = filterAsyncRouter(apiMenu) - menuRouter = flatAsyncRoutes(menuRouter) - menuRouter.forEach((item) => { - router.addRoute('layout', item) - }) - - const search_store = searchStore() - search_store.init(menuRouter) - isGetRouter.value = true - next({ ...to, replace: true }) - return false - } beforeEach(to, from) next() }) @@ -150,7 +136,8 @@ router.onError((error) => { // 入侵追加自定义方法、对象 router.getMenu = () => { - let apiMenu = tool.data.get('MENU') || [] + const menuStore = useMenuStore() + let apiMenu = menuStore.menuData.value || tool.data.get('MENU') || [] // 增加固定路由 if (apiMenu.length === 0) { // 创建默认模块,显示默认菜单 @@ -182,64 +169,4 @@ const filterUrl = (map) => { return newMap } -// 转换 -const filterAsyncRouter = (routerMap) => { - const accessedRouters = [] - routerMap.forEach((item) => { - item.meta = item.meta ? item.meta : {} - // 处理外部链接特殊路由 - if (item.meta.type === 'iframe') { - item.meta.url = item.path - item.path = `/${item.name}` - } - // MAP转路由对象 - const route = { - path: item.path, - name: item.name, - meta: item.meta, - redirect: item.redirect, - children: item.children ? filterAsyncRouter(item.children) : null, - component: loadComponent(item.component) - } - accessedRouters.push(route) - }) - return accessedRouters -} -const loadComponent = (component) => { - if (component) { - if (component.includes('/')) { - return modules[`/src/views/${component}.vue`] - } - return modules[`/src/views/${component}/index.vue`] - } else { - return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`) - } -} - -// 路由扁平化 -const flatAsyncRoutes = (routes, breadcrumb = []) => { - const res = [] - routes.forEach((route) => { - const tmp = { ...route } - if (tmp.children) { - const childrenBreadcrumb = [...breadcrumb] - childrenBreadcrumb.push(route) - const tmpRoute = { ...route } - tmpRoute.meta.breadcrumb = childrenBreadcrumb - delete tmpRoute.children - res.push(tmpRoute) - const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb) - childrenRoutes.map((item) => { - res.push(item) - }) - } else { - const tmpBreadcrumb = [...breadcrumb] - tmpBreadcrumb.push(tmp) - tmp.meta.breadcrumb = tmpBreadcrumb - res.push(tmp) - } - }) - return res -} - export default router diff --git a/snowy-admin-web/src/router/systemRouter.js b/snowy-admin-web/src/router/systemRouter.js index 15caf94f..e3d19eaf 100644 --- a/snowy-admin-web/src/router/systemRouter.js +++ b/snowy-admin-web/src/router/systemRouter.js @@ -15,6 +15,7 @@ import routerUtil from '@/utils/routerUtil' import Layout from '@/layout/index.vue' import Login from '@/views/auth/login/login.vue' import Findpwd from '@/views/auth/findPwd/index.vue' +import Callback from '@/views/auth/login/callback.vue' // 系统路由 const routes = [ @@ -41,7 +42,7 @@ const routes = [ }, { path: '/callback', - component: () => import('@/views/auth/login/callback.vue'), + component: Callback, meta: { title: '三方登录' } diff --git a/snowy-admin-web/src/store/menu.js b/snowy-admin-web/src/store/menu.js new file mode 100644 index 00000000..62aca7b7 --- /dev/null +++ b/snowy-admin-web/src/store/menu.js @@ -0,0 +1,142 @@ +/** + * Copyright [2022] [https://www.xiaonuo.vip] + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +import { defineStore } from 'pinia' +import tool from '@/utils/tool' +import { cloneDeep } from 'lodash-es' +import userRoutes from '@/config/route' +import { searchStore } from '@/store/search' +import router from '@/router' +import userCenterApi from '@/api/sys/userCenterApi' +import whiteList from '@/router/whiteList' +import routesData from '@/router/systemRouter' + +const modules = import.meta.glob('/src/views/**/**.vue') +export const useMenuStore = defineStore('menuStore', () => { + const menuData = ref([]) + const refreshFlag = ref(false) + // 改变刷新标志 + const changeRefreshFlag = (flag) => { + refreshFlag.value = flag + } + // 加载菜单 + const loadMenu = () => { + // 获取用户菜单 + const apiMenu = tool.data.get('MENU') || [] + if (apiMenu.length === 0) { + // 创建默认模块,显示默认菜单 + apiMenu[0] = cloneDeep(userRoutes.module[0]) + } + const childrenApiMenu = apiMenu[0].children + apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu] + let menuRouter = filterAsyncRouter(apiMenu) + menuRouter = flatAsyncRoutes(menuRouter) + menuData.value = menuRouter + // 初始化搜索 + const search_store = searchStore() + search_store.init(menuRouter) + } + // 过滤异步路由 + const filterAsyncRouter = (routerMap) => { + const accessedRouters = [] + routerMap.forEach((item) => { + item.meta = item.meta ? item.meta : {} + // 处理外部链接特殊路由 + if (item.meta.type === 'iframe') { + item.meta.url = item.path + item.path = `/${item.name}` + } + // MAP转路由对象 + const route = { + path: item.path, + name: item.name, + meta: item.meta, + redirect: item.redirect, + children: item.children ? filterAsyncRouter(item.children) : null, + component: loadComponent(item.component) + } + accessedRouters.push(route) + }) + return accessedRouters + } + // 将异步路由扁平化 + const flatAsyncRoutes = (routes, breadcrumb = []) => { + const res = [] + routes.forEach((route) => { + const tmp = { ...route } + if (tmp.children) { + const childrenBreadcrumb = [...breadcrumb] + childrenBreadcrumb.push(route) + const tmpRoute = { ...route } + tmpRoute.meta.breadcrumb = childrenBreadcrumb + delete tmpRoute.children + res.push(tmpRoute) + const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb) + childrenRoutes.map((item) => { + res.push(item) + }) + } else { + const tmpBreadcrumb = [...breadcrumb] + tmpBreadcrumb.push(tmp) + tmp.meta.breadcrumb = tmpBreadcrumb + res.push(tmp) + } + }) + return res + } + // 动态加载组件 + const loadComponent = (component) => { + if (component) { + if (component.includes('/')) { + return modules[`/src/views/${component}.vue`] + } + return modules[`/src/views/${component}/index.vue`] + } else { + return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`) + } + } + // 从路由中移除菜单 + const removeFromRouter = () => { + const routes = router.getRoutes() + // 遍历所有路由 + routes.forEach((route) => { + // 过滤白名单 + if ( + whiteList.filter((e) => e.path === route.path).length > 0 || + routesData.filter((e) => e.path === route.path).length > 0 + ) { + return + } + if (route.name && route.name !== 'layout') { + router.removeRoute(route.name) + } + }) + } + // 获取用户菜单 + const fetchMenu = async () => { + const menu = await userCenterApi.userLoginMenu() + tool.data.set('MENU', menu) + refreshMenu() + } + // 刷新菜单 + const refreshMenu = () => { + loadMenu() + removeFromRouter() + addToRouter() + changeRefreshFlag(true) + } + // 将菜单添加到路由 + const addToRouter = () => { + menuData.value.forEach((item) => { + router.addRoute('layout', item) + }) + } + return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu } +}) diff --git a/snowy-admin-web/src/store/user.js b/snowy-admin-web/src/store/user.js new file mode 100644 index 00000000..6fb74018 --- /dev/null +++ b/snowy-admin-web/src/store/user.js @@ -0,0 +1,32 @@ +/** + * Copyright [2022] [https://www.xiaonuo.vip] + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +import { defineStore } from 'pinia' +import loginApi from '@/api/auth/loginApi' +import { useGlobalStore } from '@/store' +import tool from '@/utils/tool' +export const userStore = defineStore('userStore', () => { + // 初始化用户信息 + const initUserInfo = async () => { + const data = await loginApi.getLoginUser() + const globalStore = useGlobalStore() + globalStore.setUserInfo(data) + tool.data.set('USER_INFO', data) + } + // 刷新登录用户信息 + const refreshUserLoginUserInfo = () => { + loginApi.getLoginUser().then((data) => { + const globalStore = useGlobalStore() + globalStore.setUserInfo(data) + tool.data.set('USER_INFO', data) + }) + } + return { initUserInfo, refreshUserLoginUserInfo } +}) diff --git a/snowy-admin-web/src/store/viewTags.js b/snowy-admin-web/src/store/viewTags.js index f4257d70..b0862ccc 100644 --- a/snowy-admin-web/src/store/viewTags.js +++ b/snowy-admin-web/src/store/viewTags.js @@ -45,6 +45,26 @@ export const viewTagsStore = defineStore('viewTags', () => { } }) } + // 更新或删除视图标签 + const updateOrRemoveViewTags = (routes) => { + if (routes && routes.length > 0) { + viewTags.value.forEach((item, index) => { + const target = routes.find((route) => route.path === item.fullPath) + if (!target) { + // 路由不存在,删除 + viewTags.value.splice(index, 1) + } else { + // 路由存在,更新 + viewTags.value = viewTags.value.map((item) => { + if (item.fullPath === target.path) { + return { ...item, meta: target.meta } + } + return item + }) + } + }) + } + } const updateViewTagsTitle = (title = '') => { const nowFullPath = location.hash.substring(1) viewTags.value.forEach((item) => { @@ -63,6 +83,7 @@ export const viewTagsStore = defineStore('viewTags', () => { removeViewTags, updateViewTags, updateViewTagsTitle, - clearViewTags + clearViewTags, + updateOrRemoveViewTags } }) diff --git a/snowy-admin-web/src/style/index.less b/snowy-admin-web/src/style/index.less index 67901ca7..056a13f5 100644 --- a/snowy-admin-web/src/style/index.less +++ b/snowy-admin-web/src/style/index.less @@ -258,7 +258,7 @@ a, button, input, textarea { } /*页面最大化*/ -.admin-ui.main-maximize { +.app-main.main-maximize { .main-maximize-exit { display: block; } diff --git a/snowy-admin-web/src/utils/errorHandler.js b/snowy-admin-web/src/utils/errorHandler.js index 29d0ee56..1671cb53 100644 --- a/snowy-admin-web/src/utils/errorHandler.js +++ b/snowy-admin-web/src/utils/errorHandler.js @@ -8,12 +8,6 @@ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip */ -/** - * 全局代码错误捕捉 - * 比如 null.length 就会被捕捉到 - */ -import { notification } from 'ant-design-vue' - export default (error) => { // 过滤HTTP请求错误 if (error.code) { @@ -30,10 +24,6 @@ export default (error) => { } const errorName = errorMap[error.name] || '未知错误' nextTick(() => { - notification.error({ - message: '错误', - description: errorName - }) - console.error(error) + console.error(errorName) }) } diff --git a/snowy-admin-web/src/views/auth/findPwd/index.vue b/snowy-admin-web/src/views/auth/findPwd/index.vue index 0c83bbb4..9c63da2b 100644 --- a/snowy-admin-web/src/views/auth/findPwd/index.vue +++ b/snowy-admin-web/src/views/auth/findPwd/index.vue @@ -1,6 +1,22 @@ diff --git a/snowy-admin-web/src/views/auth/login/callback.vue b/snowy-admin-web/src/views/auth/login/callback.vue index a76726fb..fe274855 100644 --- a/snowy-admin-web/src/views/auth/login/callback.vue +++ b/snowy-admin-web/src/views/auth/login/callback.vue @@ -1,6 +1,22 @@