diff --git a/src/hooks/common/index.ts b/src/hooks/common/index.ts index 22e574b5..c997d079 100644 --- a/src/hooks/common/index.ts +++ b/src/hooks/common/index.ts @@ -3,7 +3,19 @@ import useContext from './useContext'; import useRouterChange from './useRouterChange'; import useRouteParam from './useRouteParam'; import useRouteQuery from './useRouteQuery'; +import useRouteProps from './useRouteProps'; import useBoolean from './useBoolean'; import useLoading from './useLoading'; +import useScrollBehavior from './useScrollBehavior'; -export { useAppTitle, useContext, useRouterChange, useRouteParam, useRouteQuery, useBoolean, useLoading }; +export { + useAppTitle, + useContext, + useRouterChange, + useRouteParam, + useRouteQuery, + useRouteProps, + useBoolean, + useLoading, + useScrollBehavior +}; diff --git a/src/hooks/common/useRouteProps.ts b/src/hooks/common/useRouteProps.ts new file mode 100644 index 00000000..117d2e51 --- /dev/null +++ b/src/hooks/common/useRouteProps.ts @@ -0,0 +1,22 @@ +import { computed } from 'vue'; +import { useRoute } from 'vue-router'; + +export default function useRouteProps() { + const route = useRoute(); + const props = computed(() => { + /** 路由名称 */ + const name = route.name as string; + /** 混存页面 */ + const keepAlive = Boolean(route.meta?.keepAlive); + /** 视高100% */ + const fullPage = Boolean(route.meta?.fullPage); + + return { + name, + keepAlive, + fullPage + }; + }); + + return props; +} diff --git a/src/hooks/common/useScrollBehavior.ts b/src/hooks/common/useScrollBehavior.ts new file mode 100644 index 00000000..9518f469 --- /dev/null +++ b/src/hooks/common/useScrollBehavior.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue'; + +/** 滚动行为 */ +export default function useScrollBehavior() { + const scrollbar = ref(null); + + /** 重置滚动条行为 */ + function resetScrollBehavior() { + scrollbar.value?.scrollTo({ left: 0, top: 0 }); + } + + return { + scrollbar, + resetScrollBehavior + }; +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index d169464f..4d2d8658 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -4,7 +4,9 @@ export { useRouterChange, useRouteParam, useRouteQuery, + useRouteProps, useBoolean, - useLoading + useLoading, + useScrollBehavior } from './common'; export { useCountDown, useSmsCode } from './business'; diff --git a/src/layouts/BasicLayout/components/GlobalContent/index.vue b/src/layouts/BasicLayout/components/GlobalContent/index.vue new file mode 100644 index 00000000..45a4d4fe --- /dev/null +++ b/src/layouts/BasicLayout/components/GlobalContent/index.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/layouts/BasicLayout/components/index.ts b/src/layouts/BasicLayout/components/index.ts index 2d174daa..cbbf81e6 100644 --- a/src/layouts/BasicLayout/components/index.ts +++ b/src/layouts/BasicLayout/components/index.ts @@ -1,7 +1,8 @@ import GlobalSider from './GlobalSider/index.vue'; import GlobalHeader from './GlobalHeader/index.vue'; import GlobalTab from './GlobalTab/index.vue'; +import GlobalContent from './GlobalContent/index.vue'; import GlobalFooter from './GlobalFooter/index.vue'; import SettingDrawer from './SettingDrawer/index.vue'; -export { GlobalSider, GlobalHeader, GlobalTab, GlobalFooter, SettingDrawer }; +export { GlobalSider, GlobalHeader, GlobalTab, GlobalContent, GlobalFooter, SettingDrawer }; diff --git a/src/layouts/BasicLayout/index.vue b/src/layouts/BasicLayout/index.vue index 8e9db3e1..48727696 100644 --- a/src/layouts/BasicLayout/index.vue +++ b/src/layouts/BasicLayout/index.vue @@ -16,14 +16,7 @@ > - - - - - - - - + @@ -33,17 +26,17 @@ diff --git a/src/layouts/composables/index.ts b/src/layouts/composables/index.ts deleted file mode 100644 index 759ebb42..00000000 --- a/src/layouts/composables/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { computed, ref, watch } from 'vue'; -import { useRoute } from 'vue-router'; - -export function useRouteProps() { - const route = useRoute(); - const props = computed(() => { - /** 路由名称 */ - const name = route.name as string; - /** 混存页面 */ - const keepAlive = Boolean(route.meta?.keepAlive); - /** 视高100% */ - const fullPage = Boolean(route.meta?.fullPage); - - return { - name, - keepAlive, - fullPage - }; - }); - - return props; -} - -/** 路由切换,重置滚动行为 */ -export function useScrollBehavior() { - const scrollbar = ref(null); - const route = useRoute(); - - function resetScrollBehavior() { - scrollbar.value?.scrollTo({ left: 0, top: 0 }); - } - - watch( - () => route.name, - () => { - resetScrollBehavior(); - } - ); - - return { - scrollbar - }; -} diff --git a/src/router/cache.ts b/src/router/cache.ts new file mode 100644 index 00000000..4ec17973 --- /dev/null +++ b/src/router/cache.ts @@ -0,0 +1,38 @@ +import type { RouteRecordRaw } from 'vue-router'; +import { routes } from './routes'; + +function getCacheRoutes() { + const cacheNames: string[] = []; + routes.forEach(route => { + const isCache = isKeepAlive(route); + cacheNames.push(...getCacheName(route, isCache)); + }); + return cacheNames; +} + +function getCacheName(route: RouteRecordRaw, isCache: boolean) { + const cacheNames: string[] = []; + const hasChild = hasChildren(route); + if (isCache && !hasChild) { + const name = route.name as string; + cacheNames.push(name); + } + if (hasChild) { + const children = route.children as RouteRecordRaw[]; + children.forEach(item => { + const isChildCache = isCache || isKeepAlive(item); + cacheNames.push(...getCacheName(item, isChildCache)); + }); + } + return cacheNames; +} + +function isKeepAlive(route: RouteRecordRaw) { + return Boolean(route?.meta?.keepAlive); +} +function hasChildren(route: RouteRecordRaw) { + return Boolean(route.children && route.children.length); +} + +/** 被缓存的路由 */ +export const cacheRoutes = getCacheRoutes(); diff --git a/src/router/components.ts b/src/router/components.ts new file mode 100644 index 00000000..4ffbde96 --- /dev/null +++ b/src/router/components.ts @@ -0,0 +1,35 @@ +import { getRouteNameMap, setCacheName } from './helpers'; +import Login from '@/views/system/login/index.vue'; +import NoPermission from '@/views/system/exception/403.vue'; +import NotFound from '@/views/system/exception/404.vue'; +import ServiceError from '@/views/system/exception/500.vue'; +import DashboardAnalysis from '@/views/dashboard/analysis/index.vue'; +import DashboardWorkbench from '@/views/dashboard/workbench/index.vue'; + +const Exception403 = { ...NoPermission }; +const Exception404 = { ...NotFound }; +const Exception500 = { ...ServiceError }; + +const RouteNameMap = getRouteNameMap(); + +setCacheName(Login, RouteNameMap.get('login')); +setCacheName(NoPermission, RouteNameMap.get('no-permission')); +setCacheName(NotFound, RouteNameMap.get('not-found')); +setCacheName(ServiceError, RouteNameMap.get('service-error')); +setCacheName(DashboardAnalysis, RouteNameMap.get('dashboard-analysis')); +setCacheName(DashboardWorkbench, RouteNameMap.get('dashboard-workbench')); +setCacheName(Exception404, RouteNameMap.get('exception-404')); +setCacheName(Exception403, RouteNameMap.get('exception-403')); +setCacheName(Exception500, RouteNameMap.get('exception-500')); + +export { + Login, + NoPermission, + NotFound, + ServiceError, + DashboardAnalysis, + DashboardWorkbench, + Exception403, + Exception404, + Exception500 +}; diff --git a/src/router/helpers.ts b/src/router/helpers.ts new file mode 100644 index 00000000..286cba13 --- /dev/null +++ b/src/router/helpers.ts @@ -0,0 +1,15 @@ +import type { Component } from 'vue'; +import { EnumRoutePath } from '@/enum'; +import type { RoutePathKey } from '@/interface'; + +/** 获取路由name map */ +export function getRouteNameMap() { + return new Map((Object.keys(EnumRoutePath) as RoutePathKey[]).map(v => [v, v])); +} + +/** 给需要缓存的页面组件设置名称 */ +export function setCacheName(component: Component, name?: string) { + if (name) { + Object.assign(component, { name }); + } +} diff --git a/src/router/index.ts b/src/router/index.ts index db564f36..831aa7da 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,3 +1,4 @@ export { router, setupRouter } from './setup'; export { RouteNameMap, ROUTE_HOME, customRoutes } from './routes'; export { menus } from './menus'; +export { cacheRoutes } from './cache'; diff --git a/src/router/menus.ts b/src/router/menus.ts index 5057093f..b8eafcee 100644 --- a/src/router/menus.ts +++ b/src/router/menus.ts @@ -1,9 +1,9 @@ import type { Component } from 'vue'; import { customRoutes } from './routes'; -import { CustomRoute, GlobalMenuOption } from '@/interface'; +import type { CustomRoute, GlobalMenuOption } from '@/interface'; import { dynamicIconRender } from '@/utils'; -export function transformRouteToMenu(routes: CustomRoute[]) { +function transformRouteToMenu(routes: CustomRoute[]) { const globalMenu: GlobalMenuOption[] = []; routes.forEach(route => { if (asMenu(route)) { diff --git a/src/router/routes.ts b/src/router/routes.ts index 52791d18..06885a89 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -3,14 +3,25 @@ import { Dashboard } from '@vicons/carbon'; import { ExceptionOutlined } from '@vicons/antd'; import { BasicLayout, BlankLayout } from '@/layouts'; import { EnumRoutePath, EnumRouteTitle } from '@/enum'; -import type { CustomRoute, RoutePathKey, LoginModuleType } from '@/interface'; +import type { CustomRoute, LoginModuleType } from '@/interface'; import { getLoginModuleRegExp } from '@/utils'; +import { + Login, + NoPermission, + NotFound, + ServiceError, + DashboardAnalysis, + DashboardWorkbench, + Exception403, + Exception404, + Exception500 +} from './components'; +import { getRouteNameMap } from './helpers'; /** 路由名称 */ -export const RouteNameMap = new Map( - (Object.keys(EnumRoutePath) as RoutePathKey[]).map(v => [v, v]) -); +export const RouteNameMap = getRouteNameMap(); +/** 登录模块的正则字符串 */ const loginModuleRegExp = getLoginModuleRegExp(); /** @@ -24,6 +35,7 @@ const constantRoutes: RouteRecordRaw[] = [ component: BlankLayout, redirect: { name: RouteNameMap.get('not-found') }, meta: { + keepAlive: true, title: EnumRouteTitle.system }, children: [ @@ -31,7 +43,7 @@ const constantRoutes: RouteRecordRaw[] = [ { name: RouteNameMap.get('login'), path: `${EnumRoutePath.login}/:module(/${loginModuleRegExp}/)?`, - component: () => import('@/views/system/login/index.vue'), + component: Login, props: route => { const moduleType: LoginModuleType = (route.params.module as LoginModuleType) || 'pwd-login'; return { @@ -43,31 +55,31 @@ const constantRoutes: RouteRecordRaw[] = [ fullPage: true } }, - // 404 - { - name: RouteNameMap.get('not-found'), - path: EnumRoutePath['not-found'], - component: () => import('@/views/system/exception/404.vue'), - meta: { - title: EnumRouteTitle['not-found'], - fullPage: true - } - }, // 403 { name: RouteNameMap.get('no-permission'), path: EnumRoutePath['no-permission'], - component: () => import('@/views/system/exception/403.vue'), + component: NoPermission, meta: { title: EnumRouteTitle['no-permission'], fullPage: true } }, + // 404 + { + name: RouteNameMap.get('not-found'), + path: EnumRoutePath['not-found'], + component: NotFound, + meta: { + title: EnumRouteTitle['not-found'], + fullPage: true + } + }, // 500 { name: RouteNameMap.get('service-error'), path: EnumRoutePath['service-error'], - component: () => import('@/views/system/exception/500.vue'), + component: ServiceError, meta: { title: EnumRouteTitle['service-error'], fullPage: true @@ -86,7 +98,7 @@ const constantRoutes: RouteRecordRaw[] = [ export const ROUTE_HOME: CustomRoute = { name: RouteNameMap.get('dashboard-analysis'), path: EnumRoutePath['dashboard-analysis'], - component: () => import('@/views/dashboard/analysis/index.vue'), + component: DashboardAnalysis, meta: { keepAlive: true, requiresAuth: true, @@ -105,20 +117,7 @@ export const customRoutes: CustomRoute[] = [ redirect: { name: ROUTE_HOME.name }, meta: { isNotMenu: true - }, - children: [ - // 重载 - { - name: RouteNameMap.get('reload'), - path: EnumRoutePath.reload, - component: () => import('@/views/system/reload/index.vue'), - meta: { - title: EnumRouteTitle.reload, - isNotMenu: true, - fullPage: true - } - } - ] + } }, { name: RouteNameMap.get('dashboard'), @@ -134,8 +133,9 @@ export const customRoutes: CustomRoute[] = [ { name: RouteNameMap.get('dashboard-workbench'), path: EnumRoutePath['dashboard-workbench'], - component: () => import('@/views/dashboard/workbench/index.vue'), + component: DashboardWorkbench, meta: { + keepAlive: true, requiresAuth: true, title: EnumRouteTitle['dashboard-workbench'] } @@ -155,7 +155,7 @@ export const customRoutes: CustomRoute[] = [ { name: RouteNameMap.get('exception-403'), path: EnumRoutePath['exception-403'], - component: () => import('@/views/system/exception/403.vue'), + component: Exception403, meta: { requiresAuth: true, title: EnumRouteTitle['exception-403'], @@ -165,7 +165,7 @@ export const customRoutes: CustomRoute[] = [ { name: RouteNameMap.get('exception-404'), path: EnumRoutePath['exception-404'], - component: () => import('@/views/system/exception/404.vue'), + component: Exception404, meta: { requiresAuth: true, title: EnumRouteTitle['exception-404'], @@ -175,7 +175,7 @@ export const customRoutes: CustomRoute[] = [ { name: RouteNameMap.get('exception-500'), path: EnumRoutePath['exception-500'], - component: () => import('@/views/system/exception/500.vue'), + component: Exception500, meta: { requiresAuth: true, title: EnumRouteTitle['exception-500'], diff --git a/src/settings/theme.ts b/src/settings/theme.ts index 7adc8df1..35c3c201 100644 --- a/src/settings/theme.ts +++ b/src/settings/theme.ts @@ -63,7 +63,7 @@ const themeSettings: ThemeSettings = { }, pageStyle: { animate: true, - animateType: 'zoom-fade', + animateType: 'fade-slide', animateTypeList: [ { value: 'zoom-fade', label: EnumAnimate['zoom-fade'] }, { value: 'zoom-out', label: EnumAnimate['zoom-out'] }, diff --git a/src/styles/css/animation.css b/src/styles/css/animation.css index d1036fb4..e69de29b 100644 --- a/src/styles/css/animation.css +++ b/src/styles/css/animation.css @@ -1,9 +0,0 @@ -/* opacity透明过度 */ -.transition-opacity-enter-active, -.transition-opacity-enter-active { - transition: opacity 0.4s ease-out; -} -.transition-opacity-enter-from, -.transition-opacity-leave-to { - opacity: 0; -} diff --git a/src/styles/css/global.css b/src/styles/css/global.css index 7f930797..fdae5040 100644 --- a/src/styles/css/global.css +++ b/src/styles/css/global.css @@ -1,5 +1,5 @@ @import './scrollbar.css'; -@import './animation.css'; +@import './transition.css'; html, body, diff --git a/src/styles/css/transition.css b/src/styles/css/transition.css new file mode 100644 index 00000000..81b4b6fe --- /dev/null +++ b/src/styles/css/transition.css @@ -0,0 +1,86 @@ +/* opacity透明过度 */ +.transition-opacity-enter-active, +.transition-opacity-enter-active { + transition: opacity 0.4s ease-out; +} +.transition-opacity-enter-from, +.transition-opacity-leave-to { + opacity: 0; +} + +/* zoom-fade */ +.zoom-fade-enter-active, +.zoom-fade-leave-active { + transition: transform 0.2s, opacity 0.3s ease-out; +} +.zoom-fade-enter-from { + opacity: 0; + transform: scale(0.92); +} +.zoom-fade-leave-to { + opacity: 0; + transform: scale(1.06); +} + +/* zoom-out */ +.zoom-out-enter-active, +.zoom-out-leave-active { + transition: opacity 0.1s ease-in-out, transform 0.15s ease-out; +} +.zoom-out-enter-from, +.zoom-out-leave-to { + opacity: 0; + transform: scale(0); +} + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.2s ease-in-out; +} +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +/* fade-slide */ +.fade-slide-leave-active, +.fade-slide-enter-active { + transition: all 0.3s; +} +.fade-slide-enter-from { + opacity: 0; + transform: translateX(-30px); +} +.fade-slide-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* fade-bottom */ +.fade-bottom-enter-active, +.fade-bottom-leave-active { + transition: opacity 0.25s, transform 0.3s; +} +.fade-bottom-enter-from { + opacity: 0; + transform: translateY(-10%); +} +.fade-bottom-leave-to { + opacity: 0; + transform: translateY(10%); +} + +/* fade-scale */ +.fade-scale-leave-active, +.fade-scale-enter-active { + transition: all 0.28s; +} +.fade-scale-enter-from { + opacity: 0; + transform: scale(1.2); +} +.fade-scale-leave-to { + opacity: 0; + transform: scale(0.8); +} diff --git a/src/views/dashboard/analysis/index.vue b/src/views/dashboard/analysis/index.vue index c403da3a..22d54241 100644 --- a/src/views/dashboard/analysis/index.vue +++ b/src/views/dashboard/analysis/index.vue @@ -4,7 +4,6 @@ - -