Commit 7692ffb95b94672b6fbc8c25fd43d9dd1a1da81e

Authored by vben
1 parent bda3e5da

feat: new menu and top bar color selection color matching

CHANGELOG.zh_CN.md
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 4
5 - 表单项的`componentsProps`支持函数类型 5 - 表单项的`componentsProps`支持函数类型
6 - 菜单新增 tag 显示 6 - 菜单新增 tag 显示
  7 +- 新增菜单及顶栏颜色选择配色
7 8
8 ### ⚡ Performance Improvements 9 ### ⚡ Performance Improvements
9 10
index.html
@@ -43,7 +43,7 @@ @@ -43,7 +43,7 @@
43 43
44 .app-loading .g-loading { 44 .app-loading .g-loading {
45 display: block; 45 display: block;
46 - width: 64px; 46 + width: 48px;
47 margin: 30px auto; 47 margin: 30px auto;
48 -webkit-animation: load 1.2s linear infinite; 48 -webkit-animation: load 1.2s linear infinite;
49 animation: load 1.2s linear infinite; 49 animation: load 1.2s linear infinite;
src/components/Menu/src/BasicMenu.tsx
1 import type { MenuState } from './types'; 1 import type { MenuState } from './types';
2 import type { Menu as MenuType } from '/@/router/types'; 2 import type { Menu as MenuType } from '/@/router/types';
3 3
4 -import { computed, defineComponent, unref, reactive, toRef, watch, onMounted, ref } from 'vue'; 4 +import { computed, defineComponent, unref, reactive, watch, onMounted, ref, toRefs } from 'vue';
5 import { Menu } from 'ant-design-vue'; 5 import { Menu } from 'ant-design-vue';
6 import SearchInput from './SearchInput.vue'; 6 import SearchInput from './SearchInput.vue';
7 import MenuContent from './MenuContent'; 7 import MenuContent from './MenuContent';
@@ -40,8 +40,10 @@ export default defineComponent({ @@ -40,8 +40,10 @@ export default defineComponent({
40 }); 40 });
41 const { currentRoute } = useRouter(); 41 const { currentRoute } = useRouter();
42 42
  43 + const { items, flatItems, isAppMenu, mode, accordion } = toRefs(props);
  44 +
43 const { handleInputChange, handleInputClick } = useSearchInput({ 45 const { handleInputChange, handleInputClick } = useSearchInput({
44 - flatMenusRef: toRef(props, 'flatItems'), 46 + flatMenusRef: flatItems,
45 emit: emit, 47 emit: emit,
46 menuState, 48 menuState,
47 handleMenuChange, 49 handleMenuChange,
@@ -49,11 +51,11 @@ export default defineComponent({ @@ -49,11 +51,11 @@ export default defineComponent({
49 51
50 const { handleOpenChange, resetKeys, setOpenKeys } = useOpenKeys( 52 const { handleOpenChange, resetKeys, setOpenKeys } = useOpenKeys(
51 menuState, 53 menuState,
52 - toRef(props, 'items'),  
53 - toRef(props, 'flatItems'),  
54 - toRef(props, 'isAppMenu'),  
55 - toRef(props, 'mode'),  
56 - toRef(props, 'accordion') 54 + items,
  55 + flatItems,
  56 + isAppMenu,
  57 + mode,
  58 + accordion
57 ); 59 );
58 60
59 const getOpenKeys = computed(() => { 61 const getOpenKeys = computed(() => {
@@ -98,6 +100,8 @@ export default defineComponent({ @@ -98,6 +100,8 @@ export default defineComponent({
98 return cls; 100 return cls;
99 }); 101 });
100 102
  103 + const showTitle = computed(() => props.collapsedShowTitle && menuStore.getCollapsedState);
  104 +
101 watch( 105 watch(
102 () => currentRoute.value.name, 106 () => currentRoute.value.name,
103 (name: string) => { 107 (name: string) => {
@@ -130,9 +134,7 @@ export default defineComponent({ @@ -130,9 +134,7 @@ export default defineComponent({
130 const { beforeClickFn } = props; 134 const { beforeClickFn } = props;
131 if (beforeClickFn && isFunction(beforeClickFn)) { 135 if (beforeClickFn && isFunction(beforeClickFn)) {
132 const flag = await beforeClickFn(menu); 136 const flag = await beforeClickFn(menu);
133 - if (!flag) {  
134 - return;  
135 - } 137 + if (!flag) return;
136 } 138 }
137 const { path } = menu; 139 const { path } = menu;
138 menuState.selectedKeys = [path]; 140 menuState.selectedKeys = [path];
@@ -141,9 +143,7 @@ export default defineComponent({ @@ -141,9 +143,7 @@ export default defineComponent({
141 143
142 function handleMenuChange() { 144 function handleMenuChange() {
143 const { flatItems } = props; 145 const { flatItems } = props;
144 - if (!unref(flatItems) || flatItems.length === 0) {  
145 - return;  
146 - } 146 + if (!unref(flatItems) || flatItems.length === 0) return;
147 const findMenu = flatItems.find((menu) => menu.path === unref(currentRoute).path); 147 const findMenu = flatItems.find((menu) => menu.path === unref(currentRoute).path);
148 if (findMenu) { 148 if (findMenu) {
149 if (menuState.mode !== MenuModeEnum.HORIZONTAL) { 149 if (menuState.mode !== MenuModeEnum.HORIZONTAL) {
@@ -155,10 +155,6 @@ export default defineComponent({ @@ -155,10 +155,6 @@ export default defineComponent({
155 } 155 }
156 } 156 }
157 157
158 - const showTitle = computed(() => {  
159 - return props.collapsedShowTitle && menuStore.getCollapsedState;  
160 - });  
161 -  
162 // render menu item 158 // render menu item
163 function renderMenuItem(menuList?: MenuType[], index = 1) { 159 function renderMenuItem(menuList?: MenuType[], index = 1) {
164 if (!menuList) return; 160 if (!menuList) return;
@@ -183,6 +179,7 @@ export default defineComponent({ @@ -183,6 +179,7 @@ export default defineComponent({
183 <MenuContent 179 <MenuContent
184 item={menu} 180 item={menu}
185 level={index} 181 level={index}
  182 + isTop={props.isTop}
186 showTitle={unref(showTitle)} 183 showTitle={unref(showTitle)}
187 searchValue={menuState.searchValue} 184 searchValue={menuState.searchValue}
188 />, 185 />,
@@ -198,6 +195,7 @@ export default defineComponent({ @@ -198,6 +195,7 @@ export default defineComponent({
198 showTitle={unref(showTitle)} 195 showTitle={unref(showTitle)}
199 item={menu} 196 item={menu}
200 level={index} 197 level={index}
  198 + isTop={props.isTop}
201 searchValue={menuState.searchValue} 199 searchValue={menuState.searchValue}
202 />, 200 />,
203 ], 201 ],
src/components/Menu/src/MenuContent.tsx
@@ -26,6 +26,10 @@ export default defineComponent({ @@ -26,6 +26,10 @@ export default defineComponent({
26 type: Number as PropType<number>, 26 type: Number as PropType<number>,
27 default: 0, 27 default: 0,
28 }, 28 },
  29 + isTop: {
  30 + type: Boolean as PropType<boolean>,
  31 + default: true,
  32 + },
29 }, 33 },
30 setup(props) { 34 setup(props) {
31 /** 35 /**
@@ -56,14 +60,16 @@ export default defineComponent({ @@ -56,14 +60,16 @@ export default defineComponent({
56 if (!props.item) { 60 if (!props.item) {
57 return null; 61 return null;
58 } 62 }
59 - const { showTitle } = props; 63 + const { showTitle, isTop } = props;
60 const { name, icon } = props.item; 64 const { name, icon } = props.item;
61 const searchValue = props.searchValue || ''; 65 const searchValue = props.searchValue || '';
62 const index = name.indexOf(searchValue); 66 const index = name.indexOf(searchValue);
63 67
64 const beforeStr = name.substr(0, index); 68 const beforeStr = name.substr(0, index);
65 const afterStr = name.substr(index + searchValue.length); 69 const afterStr = name.substr(index + searchValue.length);
66 - const cls = showTitle ? 'show-title' : 'basic-menu__name'; 70 + let cls = showTitle ? ['show-title'] : ['basic-menu__name'];
  71 +
  72 + isTop && !showTitle && (cls = []);
67 return ( 73 return (
68 <> 74 <>
69 {renderIcon(icon!)} 75 {renderIcon(icon!)}
src/components/Menu/src/SearchInput.vue
@@ -102,7 +102,7 @@ @@ -102,7 +102,7 @@
102 102
103 .set-bg() { 103 .set-bg() {
104 color: #fff; 104 color: #fff;
105 - background: @input-dark-bg-color; 105 + background: @sider-dark-lighten-1-bg-color;
106 border: 0; 106 border: 0;
107 outline: none; 107 outline: none;
108 } 108 }
src/components/Menu/src/index.less
@@ -52,10 +52,11 @@ @@ -52,10 +52,11 @@
52 // collapsed show title end 52 // collapsed show title end
53 .ant-menu-submenu-title { 53 .ant-menu-submenu-title {
54 > .basic-menu__name { 54 > .basic-menu__name {
55 - display: flex;  
56 - width: 100%;  
57 - justify-content: space-between;  
58 - align-items: center; 55 + .basic-menu__tag {
  56 + float: right;
  57 + margin-top: @app-menu-item-height / 2;
  58 + transform: translate(0%, -50%);
  59 + }
59 } 60 }
60 } 61 }
61 62
@@ -254,7 +255,7 @@ @@ -254,7 +255,7 @@
254 // 层级样式 255 // 层级样式
255 &.ant-menu-dark:not(.basic-menu__sidebar-hor) { 256 &.ant-menu-dark:not(.basic-menu__sidebar-hor) {
256 overflow-x: hidden; 257 overflow-x: hidden;
257 - background: @menu-item-dark-bg-color; 258 + background: @sider-dark-bg-color;
258 .active-menu-style(); 259 .active-menu-style();
259 260
260 .ant-menu-item.ant-menu-item-selected.basic-menu-menu-item__level1, 261 .ant-menu-item.ant-menu-item-selected.basic-menu-menu-item__level1,
@@ -263,21 +264,20 @@ @@ -263,21 +264,20 @@
263 } 264 }
264 265
265 .basic-menu-item__level1 { 266 .basic-menu-item__level1 {
266 - background-color: @menu-item-dark-bg-color; 267 + background-color: @sider-dark-bg-color;
267 268
268 > .ant-menu-sub > li { 269 > .ant-menu-sub > li {
269 - background-color: lighten(@menu-item-dark-bg-color, 6%); 270 + background-color: @sider-dark-lighten-1-bg-color;
270 } 271 }
271 } 272 }
272 273
273 .basic-menu-item__level2:not(.ant-menu-item-selected), 274 .basic-menu-item__level2:not(.ant-menu-item-selected),
274 .ant-menu-sub { 275 .ant-menu-sub {
275 - background-color: lighten(@menu-item-dark-bg-color, 6%);  
276 - // background-color: @sub-menu-item-dark-bg-color; 276 + background-color: @sider-dark-lighten-1-bg-color;
277 } 277 }
278 278
279 .basic-menu-item__level3:not(.ant-menu-item-selected) { 279 .basic-menu-item__level3:not(.ant-menu-item-selected) {
280 - background-color: lighten(@menu-item-dark-bg-color, 10%); 280 + background-color: @sider-dark-lighten-2-bg-color;
281 } 281 }
282 282
283 .ant-menu-submenu-title { 283 .ant-menu-submenu-title {
@@ -290,7 +290,7 @@ @@ -290,7 +290,7 @@
290 &.ant-menu-inline-collapsed { 290 &.ant-menu-inline-collapsed {
291 .ant-menu-submenu-selected, 291 .ant-menu-submenu-selected,
292 .ant-menu-item-selected { 292 .ant-menu-item-selected {
293 - background: darken(@menu-item-dark-bg-color, 6%) !important; 293 + background: @sider-dark-darken-bg-color !important;
294 } 294 }
295 } 295 }
296 } 296 }
@@ -359,7 +359,7 @@ @@ -359,7 +359,7 @@
359 .ant-menu-dark { 359 .ant-menu-dark {
360 &.ant-menu-submenu-popup { 360 &.ant-menu-submenu-popup {
361 > ul { 361 > ul {
362 - background: @menu-item-dark-bg-color; 362 + background: @sider-dark-bg-color;
363 } 363 }
364 364
365 .active-menu-style(); 365 .active-menu-style();
src/design/color.less
  1 +:root {
  2 + // header
  3 + --header-bg-color: #394664;
  4 + --header-bg-hover-color: #273352;
  5 + --header-active-menu-bg-color: #273352;
  6 +
  7 + // sider
  8 + --sider-dark-bg-color: #273352;
  9 + --sider-dark-darken-bg-color: #273352;
  10 + --sider-dark-lighten-1-bg-color: #273352;
  11 + --sider-dark-lighten-2-bg-color: #273352;
  12 + --sider-dark-lighten-3-bg-color: #273352;
  13 +}
  14 +
1 @white: #fff; 15 @white: #fff;
2 @info-color: @primary-color; 16 @info-color: @primary-color;
3 17
@@ -53,21 +67,24 @@ @@ -53,21 +67,24 @@
53 // ==============Header============= 67 // ==============Header=============
54 // ================================= 68 // =================================
55 69
56 -@header-dark-bg-color: #394664;  
57 -@header-dark-bg-hover-color: #273352; 70 +@header-dark-bg-color: var(--header-bg-color);
  71 +@header-dark-bg-hover-color: var(--header-bg-hover-color);
58 @header-light-bg-hover-color: #f6f6f6; 72 @header-light-bg-hover-color: #f6f6f6;
59 @header-light-desc-color: #7c8087; 73 @header-light-desc-color: #7c8087;
60 @header-light-bottom-border-color: #eee; 74 @header-light-bottom-border-color: #eee;
  75 +// top-menu
  76 +@top-menu-active-bg-color: var(--header-active-menu-bg-color);
61 77
62 // ================================= 78 // =================================
63 // ==============Menu============ 79 // ==============Menu============
64 // ================================= 80 // =================================
65 81
66 // let -menu 82 // let -menu
67 -@menu-item-dark-bg-color: #273352;  
68 -  
69 -// top-menu  
70 -@top-menu-active-bg-color: #273352; 83 +@sider-dark-bg-color: var(--sider-dark-bg-color);
  84 +@sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color);
  85 +@sider-dark-lighten-1-bg-color: var(--sider-dark-lighten-1-bg-color);
  86 +@sider-dark-lighten-2-bg-color: var(--sider-dark-lighten-2-bg-color);
  87 +@sider-dark-lighten-3-bg-color: var(--sider-dark-lighten-3-bg-color);
71 88
72 // trigger 89 // trigger
73 @trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2); 90 @trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2);
src/layouts/default/header/LayoutHeader.tsx
@@ -79,9 +79,8 @@ export default defineComponent({ @@ -79,9 +79,8 @@ export default defineComponent({
79 }); 79 });
80 80
81 const showHeaderTrigger = computed(() => { 81 const showHeaderTrigger = computed(() => {
82 - const { show, trigger, hidden } = unref(getProjectConfigRef).menuSetting;  
83 -  
84 - if (!show || !hidden) return false; 82 + const { show, trigger, hidden, type } = unref(getProjectConfigRef).menuSetting;
  83 + if (type === MenuTypeEnum.TOP_MENU || !show || !hidden) return false;
85 return trigger === TriggerEnum.HEADER; 84 return trigger === TriggerEnum.HEADER;
86 }); 85 });
87 86
src/layouts/default/index.less
@@ -41,7 +41,7 @@ @@ -41,7 +41,7 @@
41 background-size: 100% 100%; 41 background-size: 100% 100%;
42 42
43 &.ant-layout-sider-dark { 43 &.ant-layout-sider-dark {
44 - background: @menu-item-dark-bg-color; 44 + background: @sider-dark-bg-color;
45 } 45 }
46 46
47 &:not(.ant-layout-sider-dark) { 47 &:not(.ant-layout-sider-dark) {
src/layouts/default/index.tsx
@@ -25,13 +25,9 @@ export default defineComponent({ @@ -25,13 +25,9 @@ export default defineComponent({
25 25
26 const { getFullContent } = useFullContent(); 26 const { getFullContent } = useFullContent();
27 27
28 - const getProjectConfigRef = computed(() => {  
29 - return appStore.getProjectConfig;  
30 - }); 28 + const getProjectConfigRef = computed(() => appStore.getProjectConfig);
31 29
32 - const getLockMainScrollStateRef = computed(() => {  
33 - return appStore.getLockMainScrollState;  
34 - }); 30 + const getLockMainScrollStateRef = computed(() => appStore.getLockMainScrollState);
35 31
36 const showHeaderRef = computed(() => { 32 const showHeaderRef = computed(() => {
37 const { 33 const {
@@ -47,6 +43,12 @@ export default defineComponent({ @@ -47,6 +43,12 @@ export default defineComponent({
47 return type !== MenuTypeEnum.SIDEBAR && unref(showHeaderRef); 43 return type !== MenuTypeEnum.SIDEBAR && unref(showHeaderRef);
48 }); 44 });
49 45
  46 + const getIsLockRef = computed(() => {
  47 + const { getLockInfo } = appStore;
  48 + const { isLock } = getLockInfo;
  49 + return isLock;
  50 + });
  51 +
50 const showSideBarRef = computed(() => { 52 const showSideBarRef = computed(() => {
51 const { 53 const {
52 menuSetting: { show, mode, split }, 54 menuSetting: { show, mode, split },
@@ -54,59 +56,74 @@ export default defineComponent({ @@ -54,59 +56,74 @@ export default defineComponent({
54 return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent)); 56 return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent));
55 }); 57 });
56 58
57 - function getTarget(): any {  
58 - const {  
59 - headerSetting: { fixed },  
60 - } = unref(getProjectConfigRef);  
61 - return document.querySelector(`.default-layout__${fixed ? 'main' : 'content'}`);  
62 - } 59 + const showFullHeaderRef = computed(() => {
  60 + return !unref(getFullContent) && unref(isShowMixHeaderRef) && unref(showHeaderRef);
  61 + });
63 62
64 - return () => {  
65 - const { getLockInfo } = appStore; 63 + const showInsetHeaderRef = computed(() => {
  64 + return !unref(getFullContent) && !unref(isShowMixHeaderRef) && unref(showHeaderRef);
  65 + });
  66 +
  67 + const fixedHeaderClsRef = computed(() => {
66 const { 68 const {
67 - useOpenBackTop,  
68 - showSettingButton,  
69 - multiTabsSetting: { show: showTabs },  
70 headerSetting: { fixed }, 69 headerSetting: { fixed },
71 - menuSetting: { split, hidden },  
72 } = unref(getProjectConfigRef); 70 } = unref(getProjectConfigRef);
73 -  
74 const fixedHeaderCls = fixed 71 const fixedHeaderCls = fixed
75 ? 'fixed' + (unref(getLockMainScrollStateRef) ? ' lock' : '') 72 ? 'fixed' + (unref(getLockMainScrollStateRef) ? ' lock' : '')
76 : ''; 73 : '';
  74 + return fixedHeaderCls;
  75 + });
77 76
78 - const { isLock } = getLockInfo; 77 + const showTabsRef = computed(() => {
  78 + const {
  79 + multiTabsSetting: { show },
  80 + } = unref(getProjectConfigRef);
  81 + return show && !unref(getFullContent);
  82 + });
  83 +
  84 + const showClassSideBarRef = computed(() => {
  85 + const {
  86 + menuSetting: { split, hidden },
  87 + } = unref(getProjectConfigRef);
  88 + return split ? hidden : true;
  89 + });
79 90
80 - const showSideBar = split ? hidden : true; 91 + function getTarget(): any {
  92 + const {
  93 + headerSetting: { fixed },
  94 + } = unref(getProjectConfigRef);
  95 + return document.querySelector(`.default-layout__${fixed ? 'main' : 'content'}`);
  96 + }
  97 +
  98 + return () => {
  99 + const { useOpenBackTop, showSettingButton } = unref(getProjectConfigRef);
81 return ( 100 return (
82 <Layout class="default-layout relative"> 101 <Layout class="default-layout relative">
83 {() => ( 102 {() => (
84 <> 103 <>
85 {/* lock page */} 104 {/* lock page */}
86 - {isLock && <LockPage />} 105 + {unref(getIsLockRef) && <LockPage />}
87 {/* back top */} 106 {/* back top */}
88 {useOpenBackTop && <BackTop target={getTarget} />} 107 {useOpenBackTop && <BackTop target={getTarget} />}
89 {/* open setting drawer */} 108 {/* open setting drawer */}
90 {showSettingButton && <SettingBtn />} 109 {showSettingButton && <SettingBtn />}
91 110
92 - {!unref(getFullContent) && unref(isShowMixHeaderRef) && unref(showHeaderRef) && (  
93 - <LayoutHeader />  
94 - )} 111 + {unref(showFullHeaderRef) && <LayoutHeader />}
95 112
96 <Layout> 113 <Layout>
97 {() => ( 114 {() => (
98 <> 115 <>
99 - {unref(showSideBarRef) && <LayoutSideBar class={showSideBar ? '' : 'hidden'} />}  
100 - <Layout class={[`default-layout__content`, fixedHeaderCls]}> 116 + {unref(showSideBarRef) && (
  117 + <LayoutSideBar class={unref(showClassSideBarRef) ? '' : 'hidden'} />
  118 + )}
  119 + <Layout class={[`default-layout__content`, unref(fixedHeaderClsRef)]}>
101 {() => ( 120 {() => (
102 <> 121 <>
103 - {!unref(getFullContent) &&  
104 - !unref(isShowMixHeaderRef) &&  
105 - unref(showHeaderRef) && <LayoutHeader />} 122 + {unref(showInsetHeaderRef) && <LayoutHeader />}
106 123
107 - {showTabs && !unref(getFullContent) && <MultipleTabs />} 124 + {unref(showTabsRef) && <MultipleTabs />}
108 125
109 - <LayoutContent class={fixedHeaderCls} /> 126 + <LayoutContent class={unref(fixedHeaderClsRef)} />
110 </> 127 </>
111 )} 128 )}
112 </Layout> 129 </Layout>
src/layouts/default/setting/SettingDrawer.tsx
@@ -20,12 +20,12 @@ import { updateColorWeak, updateGrayMode } from &#39;/@/setup/theme&#39;; @@ -20,12 +20,12 @@ import { updateColorWeak, updateGrayMode } from &#39;/@/setup/theme&#39;;
20 import { baseHandler } from './handler'; 20 import { baseHandler } from './handler';
21 import { 21 import {
22 HandlerEnum, 22 HandlerEnum,
23 - themeOptions,  
24 contentModeOptions, 23 contentModeOptions,
25 topMenuAlignOptions, 24 topMenuAlignOptions,
26 menuTriggerOptions, 25 menuTriggerOptions,
27 routerTransitionOptions, 26 routerTransitionOptions,
28 } from './const'; 27 } from './const';
  28 +import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/colorSetting';
29 29
30 interface SwitchOptions { 30 interface SwitchOptions {
31 config?: DeepPartial<ProjectConfig>; 31 config?: DeepPartial<ProjectConfig>;
@@ -41,6 +41,11 @@ interface SelectConfig { @@ -41,6 +41,11 @@ interface SelectConfig {
41 handler?: Fn; 41 handler?: Fn;
42 } 42 }
43 43
  44 +interface ThemeOptions {
  45 + def?: string;
  46 + handler?: Fn;
  47 +}
  48 +
44 export default defineComponent({ 49 export default defineComponent({
45 name: 'SettingDrawer', 50 name: 'SettingDrawer',
46 setup(_, { attrs }) { 51 setup(_, { attrs }) {
@@ -98,8 +103,7 @@ export default defineComponent({ @@ -98,8 +103,7 @@ export default defineComponent({
98 103
99 function renderSidebar() { 104 function renderSidebar() {
100 const { 105 const {
101 - headerSetting: { theme: headerTheme },  
102 - menuSetting: { type, theme: menuTheme, split }, 106 + menuSetting: { type, split },
103 } = unref(getProjectConfigRef); 107 } = unref(getProjectConfigRef);
104 108
105 const typeList = ref([ 109 const typeList = ref([
@@ -154,22 +158,22 @@ export default defineComponent({ @@ -154,22 +158,22 @@ export default defineComponent({
154 def: split, 158 def: split,
155 disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX, 159 disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
156 }), 160 }),
157 - renderSelectItem('顶栏主题', {  
158 - handler: (e) => {  
159 - baseHandler(HandlerEnum.HEADER_THEME, e);  
160 - },  
161 - def: headerTheme,  
162 - options: themeOptions,  
163 - disabled: !unref(getShowHeaderRef),  
164 - }),  
165 - renderSelectItem('菜单主题', {  
166 - handler: (e) => {  
167 - baseHandler(HandlerEnum.MENU_THEME, e);  
168 - },  
169 - def: menuTheme,  
170 - options: themeOptions,  
171 - disabled: !unref(getShowMenuRef),  
172 - }), 161 + // renderSelectItem('顶栏主题', {
  162 + // handler: (e) => {
  163 + // baseHandler(HandlerEnum.HEADER_THEME, e);
  164 + // },
  165 + // def: headerTheme,
  166 + // options: themeOptions,
  167 + // disabled: !unref(getShowHeaderRef),
  168 + // }),
  169 + // renderSelectItem('菜单主题', {
  170 + // handler: (e) => {
  171 + // baseHandler(HandlerEnum.MENU_THEME, e);
  172 + // },
  173 + // def: menuTheme,
  174 + // options: themeOptions,
  175 + // disabled: !unref(getShowMenuRef),
  176 + // }),
173 ]; 177 ];
174 } 178 }
175 /** 179 /**
@@ -413,7 +417,6 @@ export default defineComponent({ @@ -413,7 +417,6 @@ export default defineComponent({
413 return ( 417 return (
414 <div class={`setting-drawer__cell-item`}> 418 <div class={`setting-drawer__cell-item`}>
415 <span>{text}</span> 419 <span>{text}</span>
416 - {/* @ts-ignore */}  
417 <Select 420 <Select
418 {...opt} 421 {...opt}
419 disabled={disabled} 422 disabled={disabled}
@@ -447,6 +450,50 @@ export default defineComponent({ @@ -447,6 +450,50 @@ export default defineComponent({
447 ); 450 );
448 } 451 }
449 452
  453 + function renderTheme() {
  454 + const { headerBgColor, menuBgColor } = unref(getProjectConfigRef);
  455 + return (
  456 + <>
  457 + <Divider>{() => '顶栏主题'}</Divider>
  458 + {renderThemeItem(HEADER_PRESET_BG_COLOR_LIST, {
  459 + def: headerBgColor,
  460 + handler: (e) => {
  461 + baseHandler(HandlerEnum.HEADER_THEME, e);
  462 + },
  463 + })}
  464 + <Divider>{() => '菜单主题'}</Divider>
  465 + {renderThemeItem(SIDE_BAR_BG_COLOR_LIST, {
  466 + def: menuBgColor,
  467 + handler: (e) => {
  468 + baseHandler(HandlerEnum.MENU_THEME, e);
  469 + },
  470 + })}
  471 + </>
  472 + );
  473 + }
  474 +
  475 + function renderThemeItem(colorList: string[], opt: ThemeOptions) {
  476 + const { def, handler } = opt;
  477 + return (
  478 + <div class={`setting-drawer__theme-item`}>
  479 + {colorList.map((item) => {
  480 + return (
  481 + <span
  482 + onClick={() => handler && handler(item)}
  483 + key={item}
  484 + class={[def === item ? 'active' : '']}
  485 + style={{
  486 + background: item,
  487 + }}
  488 + >
  489 + <CheckOutlined class="icon" />
  490 + </span>
  491 + );
  492 + })}
  493 + </div>
  494 + );
  495 + }
  496 +
450 return () => ( 497 return () => (
451 <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer"> 498 <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
452 {{ 499 {{
@@ -454,6 +501,9 @@ export default defineComponent({ @@ -454,6 +501,9 @@ export default defineComponent({
454 <> 501 <>
455 <Divider>{() => '导航栏模式'}</Divider> 502 <Divider>{() => '导航栏模式'}</Divider>
456 {renderSidebar()} 503 {renderSidebar()}
  504 +
  505 + {renderTheme()}
  506 +
457 <Divider>{() => '界面功能'}</Divider> 507 <Divider>{() => '界面功能'}</Divider>
458 {renderFeatures()} 508 {renderFeatures()}
459 <Divider>{() => '界面显示'}</Divider> 509 <Divider>{() => '界面显示'}</Divider>
src/layouts/default/setting/handler.ts
1 import { HandlerEnum } from './const'; 1 import { HandlerEnum } from './const';
2 -import { MenuThemeEnum, MenuTypeEnum } from '/@/enums/menuEnum';  
3 -import { updateColorWeak, updateGrayMode } from '/@/setup/theme'; 2 +// import { MenuThemeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  3 +import {
  4 + updateColorWeak,
  5 + updateGrayMode,
  6 + updateHeaderBgColor,
  7 + updateSidebarBgColor,
  8 +} from '/@/setup/theme';
4 import { appStore } from '/@/store/modules/app'; 9 import { appStore } from '/@/store/modules/app';
5 import { ProjectConfig } from '/@/types/config'; 10 import { ProjectConfig } from '/@/types/config';
6 11
@@ -14,12 +19,12 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -14,12 +19,12 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
14 case HandlerEnum.CHANGE_LAYOUT: 19 case HandlerEnum.CHANGE_LAYOUT:
15 const { mode, type, split } = value; 20 const { mode, type, split } = value;
16 const splitOpt = split === undefined ? { split } : {}; 21 const splitOpt = split === undefined ? { split } : {};
17 - let headerSetting = {};  
18 - if (type === MenuTypeEnum.TOP_MENU) {  
19 - headerSetting = {  
20 - theme: MenuThemeEnum.DARK,  
21 - };  
22 - } 22 + // let headerSetting = {};
  23 + // if (type === MenuTypeEnum.TOP_MENU) {
  24 + // headerSetting = {
  25 + // theme: MenuThemeEnum.DARK,
  26 + // };
  27 + // }
23 return { 28 return {
24 menuSetting: { 29 menuSetting: {
25 mode, 30 mode,
@@ -28,7 +33,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -28,7 +33,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
28 show: true, 33 show: true,
29 ...splitOpt, 34 ...splitOpt,
30 }, 35 },
31 - headerSetting, 36 + // headerSetting,
32 }; 37 };
33 38
34 case HandlerEnum.MENU_HAS_DRAG: 39 case HandlerEnum.MENU_HAS_DRAG:
@@ -81,10 +86,12 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -81,10 +86,12 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
81 }, 86 },
82 }; 87 };
83 case HandlerEnum.MENU_THEME: 88 case HandlerEnum.MENU_THEME:
  89 + updateSidebarBgColor(value);
84 return { 90 return {
85 - menuSetting: {  
86 - theme: value,  
87 - }, 91 + menuBgColor: value,
  92 + // menuSetting: {
  93 + // theme: value,
  94 + // },
88 }; 95 };
89 case HandlerEnum.MENU_SPLIT: 96 case HandlerEnum.MENU_SPLIT:
90 return { 97 return {
@@ -150,7 +157,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -150,7 +157,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
150 showQuick: value, 157 showQuick: value,
151 }, 158 },
152 }; 159 };
153 - case HandlerEnum.TABS_SHOW_QUICK: 160 + case HandlerEnum.TABS_SHOW_ICON:
154 return { 161 return {
155 multiTabsSetting: { 162 multiTabsSetting: {
156 showIcon: value, 163 showIcon: value,
@@ -163,10 +170,9 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -163,10 +170,9 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
163 }, 170 },
164 }; 171 };
165 case HandlerEnum.HEADER_THEME: 172 case HandlerEnum.HEADER_THEME:
  173 + updateHeaderBgColor(value);
166 return { 174 return {
167 - headerSetting: {  
168 - theme: value,  
169 - }, 175 + headerBgColor: value,
170 }; 176 };
171 case HandlerEnum.HEADER_FIXED: 177 case HandlerEnum.HEADER_FIXED:
172 return { 178 return {
src/layouts/default/setting/index.less
@@ -20,25 +20,26 @@ @@ -20,25 +20,26 @@
20 display: flex; 20 display: flex;
21 flex-wrap: wrap; 21 flex-wrap: wrap;
22 margin: 16px 0; 22 margin: 16px 0;
  23 + justify-content: space-around;
23 24
24 - span {  
25 - display: inline-block; 25 + > span {
26 width: 20px; 26 width: 20px;
27 height: 20px; 27 height: 20px;
28 - margin-top: 10px;  
29 - margin-right: 10px;  
30 cursor: pointer; 28 cursor: pointer;
31 - border-radius: 4px; 29 + border: 1px solid #ddd;
  30 + border-radius: 2px;
32 31
33 svg { 32 svg {
34 display: none; 33 display: none;
35 } 34 }
36 35
37 &.active { 36 &.active {
  37 + border: 1px solid lighten(@primary-color, 10%);
  38 +
38 svg { 39 svg {
39 display: inline-block; 40 display: inline-block;
40 - margin-left: 4px;  
41 - font-size: 0.8em; 41 + margin: 0 0 3px 3px;
  42 + font-size: 12px;
42 fill: @white; 43 fill: @white;
43 } 44 }
44 } 45 }
src/layouts/page/index.tsx
@@ -41,13 +41,12 @@ export default defineComponent({ @@ -41,13 +41,12 @@ export default defineComponent({
41 // No longer show animations that are already in the tab 41 // No longer show animations that are already in the tab
42 const name = route.meta.inTab ? 'fade' : null; 42 const name = route.meta.inTab ? 'fade' : null;
43 43
44 - // TODO add key?  
45 const Content = openCache ? ( 44 const Content = openCache ? (
46 <KeepAlive max={max} include={cacheTabs}> 45 <KeepAlive max={max} include={cacheTabs}>
47 - <Component key={route.path} /> 46 + <Component key={route.fullPath} />
48 </KeepAlive> 47 </KeepAlive>
49 ) : ( 48 ) : (
50 - <Component key={route.path} /> 49 + <Component key={route.fullPath} />
51 ); 50 );
52 return openRouterTransition ? ( 51 return openRouterTransition ? (
53 <Transition 52 <Transition
src/router/index.ts
@@ -3,7 +3,7 @@ import type { App } from &#39;vue&#39;; @@ -3,7 +3,7 @@ import type { App } from &#39;vue&#39;;
3 3
4 import { createRouter, createWebHashHistory } from 'vue-router'; 4 import { createRouter, createWebHashHistory } from 'vue-router';
5 5
6 -import { scrollWaiter } from '../utils/scrollWaiter'; 6 +import { scrollWaiter } from './scrollWaiter';
7 7
8 import { createGuard } from './guard/'; 8 import { createGuard } from './guard/';
9 9
@@ -13,6 +13,7 @@ import { basicRoutes } from &#39;./routes/&#39;; @@ -13,6 +13,7 @@ import { basicRoutes } from &#39;./routes/&#39;;
13 const router = createRouter({ 13 const router = createRouter({
14 history: createWebHashHistory(), 14 history: createWebHashHistory(),
15 routes: basicRoutes as RouteRecordRaw[], 15 routes: basicRoutes as RouteRecordRaw[],
  16 + strict: true,
16 scrollBehavior: async (to, from, savedPosition) => { 17 scrollBehavior: async (to, from, savedPosition) => {
17 await scrollWaiter.wait(); 18 await scrollWaiter.wait();
18 if (savedPosition) { 19 if (savedPosition) {
src/utils/scrollWaiter.ts renamed to src/router/scrollWaiter.ts
  1 +// see https://github.com/vuejs/vue-router-next/blob/master/playground/scrollWaiter.ts
1 class ScrollQueue { 2 class ScrollQueue {
2 private resolve: (() => void) | null = null; 3 private resolve: (() => void) | null = null;
3 private promise: Promise<any> | null = null; 4 private promise: Promise<any> | null = null;
src/settings/colorSetting.ts 0 → 100644
  1 +// header preset color
  2 +export const HEADER_PRESET_BG_COLOR_LIST: string[] = [
  3 + '#ffffff',
  4 + '#009688',
  5 + '#18bc9c',
  6 + '#1E9FFF',
  7 + '#018ffb',
  8 + '#409eff',
  9 + '#4e73df',
  10 + '#e74c3c',
  11 + '#f39c12',
  12 + '#394664',
  13 + '#001529',
  14 +];
  15 +
  16 +// sider preset color
  17 +export const SIDE_BAR_BG_COLOR_LIST: string[] = [
  18 + '#273352',
  19 + '#ffffff',
  20 + '#001529',
  21 + '#304156',
  22 + '#28333E',
  23 + '#344058',
  24 +];
src/settings/projectSetting.ts
@@ -7,6 +7,16 @@ import { isProdMode } from &#39;/@/utils/env&#39;; @@ -7,6 +7,16 @@ import { isProdMode } from &#39;/@/utils/env&#39;;
7 7
8 // ! You need to clear the browser cache after the change 8 // ! You need to clear the browser cache after the change
9 const setting: ProjectConfig = { 9 const setting: ProjectConfig = {
  10 + // color
  11 + // TODO 主题色
  12 + themeColor: primaryColor,
  13 +
  14 + // header bg color
  15 + headerBgColor: '#ffffff',
  16 +
  17 + // sidebar menu bg color
  18 + menuBgColor: '#273352',
  19 +
10 // Whether to show the configuration button 20 // Whether to show the configuration button
11 showSettingButton: true, 21 showSettingButton: true,
12 // 权限模式 22 // 权限模式
@@ -15,8 +25,7 @@ const setting: ProjectConfig = { @@ -15,8 +25,7 @@ const setting: ProjectConfig = {
15 grayMode: false, 25 grayMode: false,
16 // 色弱模式 26 // 色弱模式
17 colorWeak: false, 27 colorWeak: false,
18 - // 主题色  
19 - themeColor: primaryColor, 28 +
20 // 是否取消菜单,顶部,多标签页显示, 用于可能内嵌在别的系统内 29 // 是否取消菜单,顶部,多标签页显示, 用于可能内嵌在别的系统内
21 fullContent: false, 30 fullContent: false,
22 // content mode 31 // content mode
src/setup/theme/index.ts
  1 +import useCssVar from '/@/hooks/web/useCssVar';
  2 +import { isHexColor, colorIsDark, lighten, darken } from '/@/utils/color';
  3 +import { appStore } from '/@/store/modules/app';
  4 +import { MenuThemeEnum } from '/@/enums/menuEnum';
  5 +
  6 +const HEADER_BG_COLOR_VAR = '--header-bg-color';
  7 +const HEADER_BG_HOVER_COLOR_VAR = '--header-bg-hover-color';
  8 +const HEADER_MENU_ACTIVE_BG_COLOR_VAR = '--header-active-menu-bg-color';
  9 +
  10 +const SIDER_DARK_BG_COLOR = '--sider-dark-bg-color';
  11 +const SIDER_DARK_DARKEN_BG_COLOR = '--sider-dark-darken-bg-color';
  12 +const SIDER_LIGHTEN_1_BG_COLOR = '--sider-dark-lighten-1-bg-color';
  13 +const SIDER_LIGHTEN_2_BG_COLOR = '--sider-dark-lighten-2-bg-color';
  14 +
1 function toggleClass(flag: boolean, clsName: string) { 15 function toggleClass(flag: boolean, clsName: string) {
2 const body = document.body; 16 const body = document.body;
3 let { className } = body; 17 let { className } = body;
4 className = className.replace(clsName, ''); 18 className = className.replace(clsName, '');
5 document.body.className = flag ? `${className} ${clsName} ` : className; 19 document.body.className = flag ? `${className} ${clsName} ` : className;
6 } 20 }
  21 +
7 export const updateColorWeak = (colorWeak: boolean) => { 22 export const updateColorWeak = (colorWeak: boolean) => {
8 toggleClass(colorWeak, 'color-weak'); 23 toggleClass(colorWeak, 'color-weak');
9 }; 24 };
@@ -11,3 +26,46 @@ export const updateColorWeak = (colorWeak: boolean) =&gt; { @@ -11,3 +26,46 @@ export const updateColorWeak = (colorWeak: boolean) =&gt; {
11 export const updateGrayMode = (gray: boolean) => { 26 export const updateGrayMode = (gray: boolean) => {
12 toggleClass(gray, 'gray-mode'); 27 toggleClass(gray, 'gray-mode');
13 }; 28 };
  29 +
  30 +export function updateHeaderBgColor(color: string) {
  31 + if (!isHexColor(color)) return;
  32 + const bgColorRef = useCssVar(HEADER_BG_COLOR_VAR);
  33 + const bgHoverColorRef = useCssVar(HEADER_BG_HOVER_COLOR_VAR);
  34 + const topMenuActiveBgColorRef = useCssVar(HEADER_MENU_ACTIVE_BG_COLOR_VAR);
  35 + // bg color
  36 + bgColorRef.value = color;
  37 + // hover color
  38 + const hoverColor = lighten(color, 6);
  39 + bgHoverColorRef.value = hoverColor;
  40 + topMenuActiveBgColorRef.value = hoverColor;
  41 +
  42 + const isDark = colorIsDark(color);
  43 +
  44 + appStore.commitProjectConfigState({
  45 + headerSetting: {
  46 + theme: isDark ? MenuThemeEnum.DARK : MenuThemeEnum.LIGHT,
  47 + },
  48 + });
  49 +}
  50 +
  51 +export function updateSidebarBgColor(color: string) {
  52 + if (!isHexColor(color)) return;
  53 +
  54 + const siderBgColor = useCssVar(SIDER_DARK_BG_COLOR);
  55 + const darkenBgColor = useCssVar(SIDER_DARK_DARKEN_BG_COLOR);
  56 + const lighten1Color = useCssVar(SIDER_LIGHTEN_1_BG_COLOR);
  57 + const lighten2Color = useCssVar(SIDER_LIGHTEN_2_BG_COLOR);
  58 +
  59 + siderBgColor.value = color;
  60 + darkenBgColor.value = darken(color, 6);
  61 + lighten1Color.value = lighten(color, 4);
  62 + lighten2Color.value = lighten(color, 8);
  63 + // only #ffffff is light
  64 + const isLight = ['#fff', '#ffffff'].includes(color.toLowerCase());
  65 +
  66 + appStore.commitProjectConfigState({
  67 + menuSetting: {
  68 + theme: isLight ? MenuThemeEnum.LIGHT : MenuThemeEnum.DARK,
  69 + },
  70 + });
  71 +}
src/types/config.d.ts
@@ -55,6 +55,10 @@ export interface HeaderSetting { @@ -55,6 +55,10 @@ export interface HeaderSetting {
55 showNotice: boolean; 55 showNotice: boolean;
56 } 56 }
57 export interface ProjectConfig { 57 export interface ProjectConfig {
  58 + // header背景色
  59 + headerBgColor: string;
  60 + // 左侧菜单背景色
  61 + menuBgColor: string;
58 // 是否显示配置按钮 62 // 是否显示配置按钮
59 showSettingButton: boolean; 63 showSettingButton: boolean;
60 // 权限模式 64 // 权限模式
src/useApp.ts
@@ -9,7 +9,12 @@ import { PROJ_CFG_KEY } from &#39;/@/enums/cacheEnum&#39;; @@ -9,7 +9,12 @@ import { PROJ_CFG_KEY } from &#39;/@/enums/cacheEnum&#39;;
9 import projectSetting from '/@/settings/projectSetting'; 9 import projectSetting from '/@/settings/projectSetting';
10 import { getLocal } from '/@/utils/helper/persistent'; 10 import { getLocal } from '/@/utils/helper/persistent';
11 import { isUnDef, isNull } from '/@/utils/is'; 11 import { isUnDef, isNull } from '/@/utils/is';
12 -import { updateGrayMode, updateColorWeak } from '/@/setup/theme'; 12 +import {
  13 + updateGrayMode,
  14 + updateColorWeak,
  15 + updateHeaderBgColor,
  16 + updateSidebarBgColor,
  17 +} from '/@/setup/theme';
13 18
14 import { appStore } from '/@/store/modules/app'; 19 import { appStore } from '/@/store/modules/app';
15 import { useNetWork } from '/@/hooks/web/useNetWork'; 20 import { useNetWork } from '/@/hooks/web/useNetWork';
@@ -48,7 +53,7 @@ export function useInitAppConfigStore() { @@ -48,7 +53,7 @@ export function useInitAppConfigStore() {
48 if (!projCfg) { 53 if (!projCfg) {
49 projCfg = projectSetting; 54 projCfg = projectSetting;
50 } 55 }
51 - const { colorWeak, grayMode } = projCfg; 56 + const { colorWeak, grayMode, headerBgColor, menuBgColor } = projCfg;
52 try { 57 try {
53 // if ( 58 // if (
54 // themeColor !== primaryColor && 59 // themeColor !== primaryColor &&
@@ -57,6 +62,8 @@ export function useInitAppConfigStore() { @@ -57,6 +62,8 @@ export function useInitAppConfigStore() {
57 // ) { 62 // ) {
58 // updateTheme(themeColor); 63 // updateTheme(themeColor);
59 // } 64 // }
  65 + headerBgColor && updateHeaderBgColor(headerBgColor);
  66 + menuBgColor && updateSidebarBgColor(menuBgColor);
60 grayMode && updateGrayMode(grayMode); 67 grayMode && updateGrayMode(grayMode);
61 colorWeak && updateColorWeak(colorWeak); 68 colorWeak && updateColorWeak(colorWeak);
62 } catch (error) { 69 } catch (error) {
src/utils/color.ts
@@ -31,13 +31,31 @@ export const rgbToHex = function (r: number, g: number, b: number) { @@ -31,13 +31,31 @@ export const rgbToHex = function (r: number, g: number, b: number) {
31 * @returns The RGB representation of the passed color 31 * @returns The RGB representation of the passed color
32 */ 32 */
33 export const hexToRGB = function (hex: string) { 33 export const hexToRGB = function (hex: string) {
34 - return (  
35 - parseInt(hex.substring(0, 2), 16) +  
36 - ',' +  
37 - parseInt(hex.substring(2, 4), 16) +  
38 - ',' +  
39 - parseInt(hex.substring(4, 6), 16)  
40 - ); 34 + let sHex = hex.toLowerCase();
  35 + if (isHexColor(hex)) {
  36 + if (sHex.length === 4) {
  37 + let sColorNew = '#';
  38 + for (let i = 1; i < 4; i += 1) {
  39 + sColorNew += sHex.slice(i, i + 1).concat(sHex.slice(i, i + 1));
  40 + }
  41 + sHex = sColorNew;
  42 + }
  43 + const sColorChange = [];
  44 + for (let i = 1; i < 7; i += 2) {
  45 + sColorChange.push(parseInt('0x' + sHex.slice(i, i + 2)));
  46 + }
  47 + return 'RGB(' + sColorChange.join(',') + ')';
  48 + }
  49 + return sHex;
  50 +};
  51 +
  52 +export const colorIsDark = (color: string) => {
  53 + if (!isHexColor(color)) return;
  54 + const [r, g, b] = hexToRGB(color)
  55 + .replace(/(?:\(|\)|rgb|RGB)*/g, '')
  56 + .split(',')
  57 + .map((item) => Number(item));
  58 + return r * 0.299 + g * 0.578 + b * 0.114 < 192;
41 }; 59 };
42 60
43 /** 61 /**
@@ -89,7 +107,7 @@ const addLight = (color: string, amount: number) =&gt; { @@ -89,7 +107,7 @@ const addLight = (color: string, amount: number) =&gt; {
89 * @param {number} g green 107 * @param {number} g green
90 * @param {number} b blue 108 * @param {number} b blue
91 */ 109 */
92 -const luminanace = (r: stri, g: number, b: number) => { 110 +const luminanace = (r: number, g: number, b: number) => {
93 const a = [r, g, b].map((v) => { 111 const a = [r, g, b].map((v) => {
94 v /= 255; 112 v /= 255;
95 return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); 113 return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
@@ -103,7 +121,7 @@ const luminanace = (r: stri, g: number, b: number) =&gt; { @@ -103,7 +121,7 @@ const luminanace = (r: stri, g: number, b: number) =&gt; {
103 * @param {string} rgb2 rgb color 2 121 * @param {string} rgb2 rgb color 2
104 */ 122 */
105 const contrast = (rgb1: string[], rgb2: number[]) => 123 const contrast = (rgb1: string[], rgb2: number[]) =>
106 - (luminanace(rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) / 124 + (luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) /
107 (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); 125 (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05);
108 126
109 /** 127 /**