mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-01-24 02:00:25 +08:00
perf: optimize tab switching speed
This commit is contained in:
parent
439291746f
commit
4baf90a5c8
@ -7,6 +7,7 @@
|
||||
### ⚡ Performance Improvements
|
||||
|
||||
- 优化 settingDrawer 代码
|
||||
- 优化多标签页切换速度
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
|
@ -206,7 +206,7 @@
|
||||
// 层级样式
|
||||
&.ant-menu-dark:not(.basic-menu__sidebar-hor) {
|
||||
overflow-x: hidden;
|
||||
background: @first-menu-item-dark-bg-color;
|
||||
background: @menu-item-dark-bg-color;
|
||||
.active-menu-style();
|
||||
|
||||
.ant-menu-item.ant-menu-item-selected.basic-menu-menu-item__level1,
|
||||
@ -215,20 +215,21 @@
|
||||
}
|
||||
|
||||
.basic-menu-item__level1 {
|
||||
background-color: @first-menu-item-dark-bg-color;
|
||||
background-color: @menu-item-dark-bg-color;
|
||||
|
||||
> .ant-menu-sub > li {
|
||||
background-color: @sub-menu-item-dark-bg-color;
|
||||
background-color: lighten(@menu-item-dark-bg-color, 6%);
|
||||
}
|
||||
}
|
||||
|
||||
.basic-menu-item__level2:not(.ant-menu-item-selected),
|
||||
.ant-menu-sub {
|
||||
background-color: @sub-menu-item-dark-bg-color;
|
||||
background-color: lighten(@menu-item-dark-bg-color, 6%);
|
||||
// background-color: @sub-menu-item-dark-bg-color;
|
||||
}
|
||||
|
||||
.basic-menu-item__level3:not(.ant-menu-item-selected) {
|
||||
background-color: @children-menu-item-dark-bg-color;
|
||||
background-color: lighten(@menu-item-dark-bg-color, 10%);
|
||||
}
|
||||
|
||||
.ant-menu-submenu-title {
|
||||
@ -241,7 +242,7 @@
|
||||
&.ant-menu-inline-collapsed {
|
||||
.ant-menu-submenu-selected,
|
||||
.ant-menu-item-selected {
|
||||
background: darken(@first-menu-item-dark-bg-color, 6%) !important;
|
||||
background: darken(@menu-item-dark-bg-color, 6%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,7 +311,7 @@
|
||||
.ant-menu-dark {
|
||||
&.ant-menu-submenu-popup {
|
||||
> ul {
|
||||
background: @first-menu-item-dark-bg-color;
|
||||
background: @menu-item-dark-bg-color;
|
||||
}
|
||||
|
||||
.active-menu-style();
|
||||
|
@ -64,12 +64,7 @@
|
||||
// =================================
|
||||
|
||||
// let -menu
|
||||
@first-menu-item-dark-bg-color: #273352;
|
||||
|
||||
// Level 2 menu dark background color
|
||||
@sub-menu-item-dark-bg-color: #314268;
|
||||
// Level 3 menu dark background color
|
||||
@children-menu-item-dark-bg-color: #4f6088;
|
||||
@menu-item-dark-bg-color: #273352;
|
||||
|
||||
// top-menu
|
||||
@top-menu-active-bg-color: #273352;
|
||||
|
@ -41,7 +41,7 @@
|
||||
background-size: 100% 100%;
|
||||
|
||||
&.ant-layout-sider-dark {
|
||||
background: @first-menu-item-dark-bg-color;
|
||||
background: @menu-item-dark-bg-color;
|
||||
}
|
||||
|
||||
&:not(.ant-layout-sider-dark) {
|
||||
|
@ -2,15 +2,10 @@ import type { TabContentProps } from './tab.data';
|
||||
import type { TabItem } from '/@/store/modules/tab';
|
||||
import type { AppRouteRecordRaw } from '/@/router/types';
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
watch,
|
||||
computed,
|
||||
// ref,
|
||||
unref,
|
||||
// onMounted,
|
||||
toRaw,
|
||||
} from 'vue';
|
||||
import { defineComponent, watch, computed, unref, toRaw } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import router from '/@/router';
|
||||
|
||||
import { Tabs } from 'ant-design-vue';
|
||||
import TabContent from './TabContent';
|
||||
|
||||
@ -18,16 +13,13 @@ import { useGo } from '/@/hooks/web/usePage';
|
||||
|
||||
import { TabContentEnum } from './tab.data';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { tabStore } from '/@/store/modules/tab';
|
||||
import { userStore } from '/@/store/modules/user';
|
||||
|
||||
import { closeTab } from './useTabDropdown';
|
||||
import router from '/@/router';
|
||||
import { useTabs } from '/@/hooks/web/useTabs';
|
||||
// import { PageEnum } from '/@/enums/pageEnum';
|
||||
|
||||
import './index.less';
|
||||
import { userStore } from '/@/store/modules/user';
|
||||
export default defineComponent({
|
||||
name: 'MultiTabs',
|
||||
setup() {
|
||||
@ -41,20 +33,24 @@ export default defineComponent({
|
||||
return tabStore.getTabsState;
|
||||
});
|
||||
|
||||
if (!isAddAffix) {
|
||||
addAffixTabs();
|
||||
isAddAffix = true;
|
||||
}
|
||||
|
||||
// If you monitor routing changes, tab switching will be stuck. So use this method
|
||||
watch(
|
||||
() => unref(currentRoute).path,
|
||||
() => tabStore.getLastChangeRouteState,
|
||||
() => {
|
||||
if (!userStore.getTokenState) return;
|
||||
const { path: rPath, fullPath } = unref(currentRoute);
|
||||
if (activeKeyRef.value !== (fullPath || rPath)) {
|
||||
activeKeyRef.value = fullPath || rPath;
|
||||
if (!isAddAffix) {
|
||||
addAffixTabs();
|
||||
isAddAffix = true;
|
||||
}
|
||||
tabStore.commitAddTab((unref(currentRoute) as unknown) as AppRouteRecordRaw);
|
||||
|
||||
const lastChangeRoute = unref(tabStore.getLastChangeRouteState);
|
||||
|
||||
if (!lastChangeRoute || !userStore.getTokenState) return;
|
||||
|
||||
const { path, fullPath } = lastChangeRoute;
|
||||
if (activeKeyRef.value !== (fullPath || path)) {
|
||||
activeKeyRef.value = fullPath || path;
|
||||
}
|
||||
tabStore.commitAddTab((lastChangeRoute as unknown) as AppRouteRecordRaw);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -12,6 +12,8 @@ import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper';
|
||||
import { setTitle } from '/@/utils/browser';
|
||||
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
|
||||
|
||||
import { tabStore } from '/@/store/modules/tab';
|
||||
|
||||
const { projectSetting, globSetting } = useSetting();
|
||||
export function createGuard(router: Router) {
|
||||
const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = projectSetting;
|
||||
@ -20,8 +22,21 @@ export function createGuard(router: Router) {
|
||||
axiosCanceler = new AxiosCanceler();
|
||||
}
|
||||
router.beforeEach(async (to) => {
|
||||
// Determine whether the tab has been opened
|
||||
const isOpen = getIsOpenTab(to.fullPath);
|
||||
to.meta.inTab = isOpen;
|
||||
|
||||
// Notify routing changes
|
||||
const { fullPath, path, query, params, name, meta } = to;
|
||||
tabStore.commitLastChangeRouteState({
|
||||
fullPath,
|
||||
path,
|
||||
query,
|
||||
params,
|
||||
name,
|
||||
meta,
|
||||
} as any);
|
||||
|
||||
try {
|
||||
if (closeMessageOnSwitch) {
|
||||
Modal.destroyAll();
|
||||
|
@ -8,12 +8,6 @@ const NAME = 'menu';
|
||||
hotModuleUnregisterModule(NAME);
|
||||
@Module({ namespaced: true, name: NAME, dynamic: true, store })
|
||||
class Menu extends VuexModule {
|
||||
// // 默认展开
|
||||
// private collapsedState: boolean = appStore.getProjectConfig.menuSetting.collapsed;
|
||||
|
||||
// // 菜单宽度
|
||||
// private menuWidthState: number = appStore.getProjectConfig.menuSetting.menuWidth;
|
||||
|
||||
// 是否开始拖拽
|
||||
private dragStartState = false;
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
|
||||
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { userStore } from './user';
|
||||
|
||||
import store from '/@/store';
|
||||
import router from '/@/router';
|
||||
@ -43,10 +44,17 @@ class Tab extends VuexModule {
|
||||
|
||||
currentContextMenuState: TabItem | null = null;
|
||||
|
||||
// Last route change
|
||||
lastChangeRouteState: AppRouteRecordRaw | null = null;
|
||||
|
||||
get getTabsState() {
|
||||
return this.tabsState;
|
||||
}
|
||||
|
||||
get getLastChangeRouteState() {
|
||||
return this.lastChangeRouteState;
|
||||
}
|
||||
|
||||
get getCurrentContextMenuIndexState() {
|
||||
return this.currentContextMenuIndexState;
|
||||
}
|
||||
@ -64,6 +72,12 @@ class Tab extends VuexModule {
|
||||
return this.tabsState.find((item) => item.path === route.path)!;
|
||||
}
|
||||
|
||||
@Mutation
|
||||
commitLastChangeRouteState(route: AppRouteRecordRaw): void {
|
||||
if (!userStore.getTokenState) return;
|
||||
this.lastChangeRouteState = route;
|
||||
}
|
||||
|
||||
@Mutation
|
||||
commitClearCache(): void {
|
||||
this.keepAliveTabsState = [];
|
||||
@ -86,7 +100,7 @@ class Tab extends VuexModule {
|
||||
commitAddTab(route: AppRouteRecordRaw | TabItem): void {
|
||||
const { path, name, meta, fullPath, params, query } = route as TabItem;
|
||||
// 404 页面不需要添加tab
|
||||
if (path === PageEnum.ERROR_PAGE) {
|
||||
if (path === PageEnum.ERROR_PAGE || !name) {
|
||||
return;
|
||||
} else if ([REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)) {
|
||||
return;
|
||||
@ -107,7 +121,6 @@ class Tab extends VuexModule {
|
||||
this.tabsState.splice(updateIndex, 1, curTab);
|
||||
return;
|
||||
}
|
||||
|
||||
this.tabsState.push({ path, fullPath, name, meta, params, query });
|
||||
if (unref(getOpenKeepAliveRef) && name) {
|
||||
const noKeepAlive = meta && meta.ignoreKeepAlive;
|
||||
|
130
src/utils/color.ts
Normal file
130
src/utils/color.ts
Normal file
@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 判断是否 十六进制颜色值.
|
||||
* 输入形式可为 #fff000 #f00
|
||||
*
|
||||
* @param String color 十六进制颜色值
|
||||
* @return Boolean
|
||||
*/
|
||||
export const isHexColor = function (color: string) {
|
||||
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
|
||||
return reg.test(color);
|
||||
};
|
||||
|
||||
/**
|
||||
* RGB 颜色值转换为 十六进制颜色值.
|
||||
* r, g, 和 b 需要在 [0, 255] 范围内
|
||||
*
|
||||
* @param Number r 红色色值
|
||||
* @param Number g 绿色色值
|
||||
* @param Number b 蓝色色值
|
||||
* @return String 类似#ff00ff
|
||||
*/
|
||||
export const rgbToHex = function (r: number, g: number, b: number) {
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
const hex = ((r << 16) | (g << 8) | b).toString(16);
|
||||
return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform a HEX color to its RGB representation
|
||||
* @param {string} hex The color to transform
|
||||
* @returns The RGB representation of the passed color
|
||||
*/
|
||||
export const hexToRGB = function (hex: string) {
|
||||
return (
|
||||
parseInt(hex.substring(0, 2), 16) +
|
||||
',' +
|
||||
parseInt(hex.substring(2, 4), 16) +
|
||||
',' +
|
||||
parseInt(hex.substring(4, 6), 16)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Darkens a HEX color given the passed percentage
|
||||
* @param {string} color The color to process
|
||||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The HEX representation of the processed color
|
||||
*/
|
||||
export const darken = (color: string, amount: number) => {
|
||||
color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
|
||||
amount = Math.trunc((255 * amount) / 100);
|
||||
return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(
|
||||
color.substring(2, 4),
|
||||
amount
|
||||
)}${subtractLight(color.substring(4, 6), amount)}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lightens a 6 char HEX color according to the passed percentage
|
||||
* @param {string} color The color to change
|
||||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The processed color represented as HEX
|
||||
*/
|
||||
export const lighten = (color: string, amount: number) => {
|
||||
color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
|
||||
amount = Math.trunc((255 * amount) / 100);
|
||||
return `#${addLight(color.substring(0, 2), amount)}${addLight(
|
||||
color.substring(2, 4),
|
||||
amount
|
||||
)}${addLight(color.substring(4, 6), amount)}`;
|
||||
};
|
||||
|
||||
/* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */
|
||||
/**
|
||||
* Sums the passed percentage to the R, G or B of a HEX color
|
||||
* @param {string} color The color to change
|
||||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The processed part of the color
|
||||
*/
|
||||
const addLight = (color: string, amount: number) => {
|
||||
const cc = parseInt(color, 16) + amount;
|
||||
const c = cc > 255 ? 255 : cc;
|
||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates luminance of an rgb color
|
||||
* @param {number} r red
|
||||
* @param {number} g green
|
||||
* @param {number} b blue
|
||||
*/
|
||||
const luminanace = (r: stri, g: number, b: number) => {
|
||||
const a = [r, g, b].map((v) => {
|
||||
v /= 255;
|
||||
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||
});
|
||||
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates contrast between two rgb colors
|
||||
* @param {string} rgb1 rgb color 1
|
||||
* @param {string} rgb2 rgb color 2
|
||||
*/
|
||||
const contrast = (rgb1: string[], rgb2: number[]) =>
|
||||
(luminanace(rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) /
|
||||
(luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05);
|
||||
|
||||
/**
|
||||
* Determines what the best text color is (black or white) based con the contrast with the background
|
||||
* @param hexColor - Last selected color by the user
|
||||
*/
|
||||
export const calculateBestTextColor = (hexColor: string) => {
|
||||
const rgbColor = hexToRGB(hexColor.substring(1));
|
||||
const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0]);
|
||||
|
||||
return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF';
|
||||
};
|
||||
|
||||
/**
|
||||
* Subtracts the indicated percentage to the R, G or B of a HEX color
|
||||
* @param {string} color The color to change
|
||||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The processed part of the color
|
||||
*/
|
||||
const subtractLight = (color: string, amount: number) => {
|
||||
const cc = parseInt(color, 16) - amount;
|
||||
const c = cc < 0 ? 0 : cc;
|
||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
|
||||
};
|
Loading…
Reference in New Issue
Block a user