diff --git a/index.html b/index.html index 9c43e06..3951a5d 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,15 @@ + <%= title %> -
+
+
+
+
+
diff --git a/public/css/loading.css b/public/css/loading.css new file mode 100644 index 0000000..34efd58 --- /dev/null +++ b/public/css/loading.css @@ -0,0 +1,61 @@ +body { + margin: 0; + overflow: hidden; +} +.init-page { + width: 100vw; + height: 100vh; + left: 0; + top: 0; + display: flex; + align-items: center; + justify-content: center; + background: #fff; +} +/* HTML:
*/ +.snow-loader { + width: 45px; + aspect-ratio: 1; + + --c: no-repeat linear-gradient(rgb(22, 93, 255) 0 0); + + background: + var(--c) 0% 50%, + var(--c) 50% 50%, + var(--c) 100% 50%; + background-size: 20% 100%; + animation: l1 1s infinite linear; +} + +@keyframes l1 { + 0% { + background-size: + 20% 100%, + 20% 100%, + 20% 100%; + } + 33% { + background-size: + 20% 10%, + 20% 100%, + 20% 100%; + } + 50% { + background-size: + 20% 100%, + 20% 10%, + 20% 100%; + } + 66% { + background-size: + 20% 100%, + 20% 100%, + 20% 10%; + } + 100% { + background-size: + 20% 100%, + 20% 100%, + 20% 100%; + } +} diff --git a/src/App.vue b/src/App.vue index f4ce8c6..979ccdb 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,7 +5,6 @@ diff --git a/src/layout/components/Header/components/header-right/index.vue b/src/layout/components/Header/components/header-right/index.vue index aca5b52..9bf8027 100644 --- a/src/layout/components/Header/components/header-right/index.vue +++ b/src/layout/components/Header/components/header-right/index.vue @@ -112,7 +112,6 @@ import { useI18n } from "vue-i18n"; import { Modal } from "@arco-design/web-vue"; import { useRouter } from "vue-router"; import { storeToRefs } from "pinia"; -import { useRoutesConfigStore } from "@/store/modules/route-config"; import { useUserInfoStore } from "@/store/modules/user-info"; import { useThemeConfig } from "@/store/modules/theme-config"; import { useThemeMethods } from "@/hooks/useThemeMethods"; @@ -194,9 +193,6 @@ const logOut = () => { // 用户退出 const store = useUserInfoStore(); await store.logOut(); - // 重置路由树 - const route = useRoutesConfigStore(); - await route.resetRoute(); router.replace("/login"); return true; } catch { diff --git a/src/layout/index.vue b/src/layout/index.vue index b37e727..5258a7b 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -9,7 +9,6 @@ diff --git a/src/router/index.ts b/src/router/index.ts index 42ea782..7b11049 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,7 +7,6 @@ import { storeToRefs } from "pinia"; import { useUserInfoStore } from "@/store/modules/user-info"; import { useRoutesConfigStore } from "@/store/modules/route-config"; import { useRoutingMethod } from "@/hooks/useRoutingMethod"; -import { loadingPage } from "@/utils/loading-page"; /** * 创建vue的路由示例 @@ -32,16 +31,16 @@ const router = createRouter({ * 1、去登录页,无token,放行 * 2、没有token,直接重定向到登录页 * 3、去登录页,有token,直接重定向到home页 - * 4、去非登录页,有token,校验是否动态添加过路由,添加过则放行,未添加则执行路由初始化 + * 4、去非登录页,有token,若是从登录页而来则重载路由,若是刷新页面则重载路由,重载后重新跳转 * 注意: * 全局routeTree不能持久化缓存 * 页面刷新会导致addRoute动态添加的路由失效,需要重新初始化路由 */ -router.beforeEach(async (to, _, next) => { +router.beforeEach(async (to, from, next) => { NProgress.start(); // 开启进度条 const store = useUserInfoStore(pinia); const { token } = storeToRefs(store); - // console.log("去", to, "来自", from); + console.log("去", to, "来自", from); // next()内部加了path等于跳转指定路由会再次触发router.beforeEach,内部无参数等于放行,不会触发router.beforeEach if (to.path === "/login" && !token.value) { // 1、去登录页,无token,放行 @@ -59,19 +58,22 @@ router.beforeEach(async (to, _, next) => { const routeStore = useRoutesConfigStore(pinia); const { routeTree } = storeToRefs(routeStore); + // 从登录页跳转过来,需要重置路由(登录后触发) + if (from.path === "/login") { + await routeStore.initSetRouter(); + } + // 获取外链路由的处理函数 // 所有的路由正常放行,只不过额外判断是否是外链,如果是,则打开新窗口跳转外链 // 外链的页面依旧正常打开,只不过不会参与缓存与tabs显示,符合路由跳转的直觉 const { openExternalLinks } = useRoutingMethod(); - // 如果缓存的路由是0,则说明未动态添加路由,先添加再跳转 + // 如果缓存的路由是0,则说明未动态添加路由,先添加再跳转(页面刷新时触发) // 解决刷新页面404的问题 if (routeTree.value.length == 0) { - loadingPage.start(); await routeStore.initSetRouter(); // 处理外链跳转 openExternalLinks(to); - // 处理完重新跳转 next({ path: to.path, query: to.query }); } else { // 处理外链跳转 diff --git a/src/store/modules/route-config.ts b/src/store/modules/route-config.ts index b187f35..cd3d433 100644 --- a/src/store/modules/route-config.ts +++ b/src/store/modules/route-config.ts @@ -1,110 +1,110 @@ -import { defineStore } from "pinia"; -import router from "@/router/index"; -import { RouteRecordRaw } from "vue-router"; -import { getMenuListAPI } from "@/api/modules/system/index"; -import { moduleReplacement, linearArray } from "@/router/route-output"; -/** - * 路由列表 - * @methods setRouteNames 设置路由名称集合 - * @methods setTabs 添加tabs标签页 - * @methods setCurrentRoute 设置系统内的当前路由 - * @methods removeTabsList 删除tabs页的指定路由 - * @methods removeRouteName 删除缓存路由名,用于取消页面缓存,单个删除 - * @methods removeRouteNames 删除缓存路由名,用于取消页面缓存,批量删除 - * @methods initSetRouter 路由初始化 - */ -export const useRoutesConfigStore = defineStore("route-config", { - state: (): any => ({ - routeTree: [], // 有访问权限的路由树 - routeList: [], // 有访问权限的一维路由数组 - cacheRoutes: [], // 所有可缓存路由的路由名 - tabsList: [], // 标签页数据 - currentRoute: {} // 当前路由 - }), - actions: { - /** - * 设置可缓存路由的路由名 - * @param {string} name 路由名 - */ - setRouteNames(name: string) { - let state = this.cacheRoutes.some((item: string) => item === name); - if (state) return; - this.cacheRoutes.push(name); - }, - /** - * 添加tabs标签页 - * @param {object} data 当前tabs路由 - */ - setTabs(data: Menu.MenuOptions) { - // 当前路由在tags中是否存在,不存在则缓存 - let isExist = this.tabsList.some((item: Menu.MenuOptions) => item.name === data.name); - if (isExist) return; - this.tabsList.push(data); - }, - /** - * 设置系统内的当前路由数据 - * @param {object} data 当前路由 - */ - setCurrentRoute(data: Menu.MenuOptions) { - if (this.currentRoute.name && data.name === this.currentRoute.name) return; - this.currentRoute = data; - }, - /** - * 删除tabs页的指定路由 - * @param {string} key 路由name - */ - removeTabsList(key: string) { - const index = this.tabsList.findIndex((item: Menu.MenuOptions) => item.name === key); - if (this.tabsList[index].meta.affix) return; - if (index === -1) return; - this.tabsList.splice(index, 1); - }, - /** - * 删除缓存路由名,用于取消页面缓存,单个删除 - * @param {string} key 路由名 - */ - removeRouteName(key: string) { - const index = this.cacheRoutes.findIndex((item: string) => item === key); - if (index === -1) return; - this.cacheRoutes.splice(index, 1); - }, - /** - * 删除缓存路由名,用于取消页面缓存,批量删除 - * @param {Array} list 路由名 - */ - removeRouteNames(list: Array) { - this.cacheRoutes = this.cacheRoutes.filter((item: string) => !list.includes(item)); - }, - /** - * 重置routeTree路由树 - */ - async resetRoute() { - this.routeTree = []; - }, - /** - * 路由初始化 - * 1、将模块设置为真实模块 - * 2、存储路由树,用于生成菜单 - * 3、根据树生成一维路由数组 - * 4、动态添加路由,设置完整的路由,二维路由:顶层路由 + 二级的一维路由 - * 5、动态添加路由 - * 6、缓存一维路由 - */ - async initSetRouter() { - // 1、获取过滤角色权限后的树,后端做排序处理 - let { data } = await getMenuListAPI(); - // 2、将模块设置为真实模块 - let tree = moduleReplacement(data); - // 3、存储路由树,用于生成菜单 - this.routeTree = tree[0].children; - // 4、根据树生成一维路由数组 - tree[0].children = linearArray(tree[0].children); - // 5、设置完整的路由,二维路由:顶层路由 + 二级的一维路由 - tree[0].redirect = tree[0].children[0].path; - // 6、动态添加路由 - tree.forEach((route: RouteRecordRaw) => router.addRoute(route)); - // 7、缓存一维路由 - this.routeList = tree[0].children; - } - } -}); +import { defineStore } from "pinia"; +import router from "@/router/index"; +import { RouteRecordRaw } from "vue-router"; +import { getMenuListAPI } from "@/api/modules/system/index"; +import { moduleReplacement, linearArray } from "@/router/route-output"; +/** + * 路由列表 + * @methods setRouteNames 设置路由名称集合 + * @methods setTabs 添加tabs标签页 + * @methods setCurrentRoute 设置系统内的当前路由 + * @methods removeTabsList 删除tabs页的指定路由 + * @methods removeRouteName 删除缓存路由名,用于取消页面缓存,单个删除 + * @methods removeRouteNames 删除缓存路由名,用于取消页面缓存,批量删除 + * @methods initSetRouter 路由初始化 + */ +export const useRoutesConfigStore = defineStore("route-config", { + state: (): any => ({ + routeTree: [], // 有访问权限的路由树 + routeList: [], // 有访问权限的一维路由数组 + cacheRoutes: [], // 所有可缓存路由的路由名 + tabsList: [], // 标签页数据 + currentRoute: {} // 当前路由 + }), + actions: { + /** + * 设置可缓存路由的路由名 + * @param {string} name 路由名 + */ + setRouteNames(name: string) { + let state = this.cacheRoutes.some((item: string) => item === name); + if (state) return; + this.cacheRoutes.push(name); + }, + /** + * 添加tabs标签页 + * @param {object} data 当前tabs路由 + */ + setTabs(data: Menu.MenuOptions) { + // 当前路由在tags中是否存在,不存在则缓存 + let isExist = this.tabsList.some((item: Menu.MenuOptions) => item.name === data.name); + if (isExist) return; + this.tabsList.push(data); + }, + /** + * 设置系统内的当前路由数据 + * @param {object} data 当前路由 + */ + setCurrentRoute(data: Menu.MenuOptions) { + if (this.currentRoute.name && data.name === this.currentRoute.name) return; + this.currentRoute = data; + }, + /** + * 删除tabs页的指定路由 + * @param {string} key 路由name + */ + removeTabsList(key: string) { + const index = this.tabsList.findIndex((item: Menu.MenuOptions) => item.name === key); + if (this.tabsList[index].meta.affix) return; + if (index === -1) return; + this.tabsList.splice(index, 1); + }, + /** + * 删除缓存路由名,用于取消页面缓存,单个删除 + * @param {string} key 路由名 + */ + removeRouteName(key: string) { + const index = this.cacheRoutes.findIndex((item: string) => item === key); + if (index === -1) return; + this.cacheRoutes.splice(index, 1); + }, + /** + * 删除缓存路由名,用于取消页面缓存,批量删除 + * @param {Array} list 路由名 + */ + removeRouteNames(list: Array) { + this.cacheRoutes = this.cacheRoutes.filter((item: string) => !list.includes(item)); + }, + /** + * 重置routeTree路由树 + */ + async resetRoute() { + this.routeTree = []; + }, + /** + * 路由初始化 + * 1、将模块设置为真实模块 + * 2、存储路由树,用于生成菜单 + * 3、根据树生成一维路由数组 + * 4、动态添加路由,设置完整的路由,二维路由:顶层路由 + 二级的一维路由 + * 5、动态添加路由 + * 6、缓存一维路由 + */ + async initSetRouter() { + // 1、获取过滤角色权限后的树,后端做排序处理 + let { data } = await getMenuListAPI(); + // 2、将模块设置为真实模块 + let tree = moduleReplacement(data); + // 3、存储路由树,用于生成菜单 + this.routeTree = tree[0].children; + // 4、根据树生成一维路由数组 + tree[0].children = linearArray(tree[0].children); + // 5、设置完整的路由,二维路由:顶层路由 + 二级的一维路由 + tree[0].redirect = tree[0].children[0].path; + // 6、动态添加路由 + tree.forEach((route: RouteRecordRaw) => router.addRoute(route)); + // 7、缓存一维路由 + this.routeList = tree[0].children; + } + } +}); diff --git a/src/store/modules/theme-config.ts b/src/store/modules/theme-config.ts index d2c2d59..7aa205b 100644 --- a/src/store/modules/theme-config.ts +++ b/src/store/modules/theme-config.ts @@ -13,7 +13,7 @@ interface ThemeConfig { watermark: string; watermarkStyle: any; watermarkRotate: number; - watermarkGap: Array; + watermarkGap: [number, number]; layoutType: string; grayMode: Boolean; colorWeakMode: Boolean; diff --git a/src/utils/loading-page.ts b/src/utils/loading-page.ts index 8c20bdc..470a95f 100644 --- a/src/utils/loading-page.ts +++ b/src/utils/loading-page.ts @@ -1,29 +1,29 @@ -/** - * 全局加载 loading-page - * @method start 创建 loading - * @method done 移除 loading - */ -export const loadingPage = { - // 开始渲染loading - start: () => { - // 获取顶层body - // 将新创建的dc-loader元素(div)插入到body元素的子元素列表中的指定位置(在指定元素之前) - // 插入的位置是作为body元素的第一个子元素,即页面的最顶部位置 - const bodyDom: Element = document.body; - const div = document.createElement("div"); - div.className = "loading-page"; - const loader = document.createElement("div"); - loader.className = "dc-loader"; - div.appendChild(loader); - bodyDom.insertBefore(div, bodyDom.firstChild); - }, - // 结束渲染loading - done: (time: number = 0) => { - setTimeout(() => { - // 找到第一个匹配对象 - // 找到loading-page的父节点,移除loading-page - const dom = document.querySelector(".loading-page"); - dom?.parentNode?.removeChild(dom); - }, time); - } -}; +/** + * 全局加载 loading-page + * @method start 创建 loading + * @method done 移除 loading + */ +export const loadingPage = { + // 开始渲染loading + start: () => { + // 获取顶层body + // 将新创建的dc-loader元素(div)插入到body元素的子元素列表中的指定位置(在指定元素之前) + // 插入的位置是作为body元素的第一个子元素,即页面的最顶部位置 + const bodyDom: Element = document.body; + const div = document.createElement("div"); + div.className = "loading-page"; + const loader = document.createElement("div"); + loader.className = "dc-loader"; + div.appendChild(loader); + bodyDom.insertBefore(div, bodyDom.firstChild); + }, + // 结束渲染loading + done: (time: number = 0) => { + setTimeout(() => { + // 找到第一个匹配对象 + // 找到loading-page的父节点,移除loading-page + const dom = document.querySelector(".loading-page"); + dom?.parentNode?.removeChild(dom); + }, time); + } +}; diff --git a/src/views/error/401.vue b/src/views/error/401.vue index 1e241f3..011e1d6 100644 --- a/src/views/error/401.vue +++ b/src/views/error/401.vue @@ -1,50 +1,45 @@ - - - - - + + + + + diff --git a/src/views/error/404.vue b/src/views/error/404.vue index 277db2f..f7cf402 100644 --- a/src/views/error/404.vue +++ b/src/views/error/404.vue @@ -1,49 +1,45 @@ - - - - - + + + + + diff --git a/src/views/error/500.vue b/src/views/error/500.vue index 8ad74c5..c89f051 100644 --- a/src/views/error/500.vue +++ b/src/views/error/500.vue @@ -1,56 +1,51 @@ - - - - - + + + + +