Merge pull request #183 from LostElkByte/master

refactor(Menu): ♻️ 浅色主题Menu色系搭配改版
This commit is contained in:
Ray Hao
2025-02-19 13:43:57 +08:00
committed by GitHub
14 changed files with 185 additions and 21 deletions

View File

@@ -16,3 +16,17 @@ export const enum ThemeEnum {
*/ */
AUTO = "auto", AUTO = "auto",
} }
/**
* 浅色主题下的侧边栏配色方案枚举
*/
export const enum SidebarLightThemeEnum {
/**
* 深蓝色
*/
DARKBLUE = "light-darkBlue",
/**
* 白色
*/
WHITE = "light-white",
}

View File

@@ -57,6 +57,9 @@ export default {
themeColor: "Theme Color", themeColor: "Theme Color",
tagsView: "Tags View", tagsView: "Tags View",
sidebarLogo: "Sidebar Logo", sidebarLogo: "Sidebar Logo",
sidebarColorScheme: "Sidebar Color Scheme",
watermark: "Watermark", watermark: "Watermark",
white: "white",
darkBlue: "darkBlue",
}, },
}; };

View File

@@ -57,6 +57,9 @@ export default {
themeColor: "主题颜色", themeColor: "主题颜色",
tagsView: "开启 Tags-View", tagsView: "开启 Tags-View",
sidebarLogo: "侧边栏 Logo", sidebarLogo: "侧边栏 Logo",
sidebarColorScheme: "侧边栏配色方案",
watermark: "开启水印", watermark: "开启水印",
white: "白色",
darkBlue: "深蓝色",
}, },
}; };

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="navbar__right"> <div class="navbar__right" :class="navbarRightClass">
<!-- 桌面端显示 --> <!-- 桌面端显示 -->
<template v-if="isDesktop"> <template v-if="isDesktop">
<!-- 搜索 --> <!-- 搜索 -->
@@ -48,6 +48,7 @@ import { DeviceEnum } from "@/enums/DeviceEnum";
import { useAppStore, useSettingsStore, useUserStore, useTagsViewStore } from "@/store"; import { useAppStore, useSettingsStore, useUserStore, useTagsViewStore } from "@/store";
import Notification from "./Notification.vue"; import Notification from "./Notification.vue";
import { SidebarLightThemeEnum } from "@/enums/ThemeEnum";
const appStore = useAppStore(); const appStore = useAppStore();
const settingStore = useSettingsStore(); const settingStore = useSettingsStore();
@@ -65,6 +66,13 @@ function handleProfileClick() {
router.push({ name: "Profile" }); router.push({ name: "Profile" });
} }
// 根据浅色模式-侧边栏颜色配置选择navbar右侧的样式类
const navbarRightClass = computed(() => {
return settingStore.sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
? "navbar__right--darkBlue"
: "navbar__right--white";
});
/** /**
* 注销登出 * 注销登出
*/ */
@@ -125,11 +133,17 @@ function logout() {
} }
} }
.layout-top .navbar__right > *, .layout-top .navbar__right--darkBlue > *,
.layout-mix .navbar__right > * { .layout-mix .navbar__right--darkBlue > * {
color: #fff; color: #fff;
} }
.layout-top .navbar__right--white > *,
.layout-mix .navbar__right--white > * {
color: #000;
}
.dark .navbar__right > *:hover { .dark .navbar__right > *:hover {
color: #ccc; color: #ccc;
} }

View File

@@ -27,6 +27,13 @@
<span class="text-xs">{{ $t("settings.watermark") }}</span> <span class="text-xs">{{ $t("settings.watermark") }}</span>
<el-switch v-model="settingsStore.watermarkEnabled" /> <el-switch v-model="settingsStore.watermarkEnabled" />
</div> </div>
<div v-if="!isDark" class="py-1 flex-x-between">
<span class="text-xs">{{ $t("settings.sidebarColorScheme") }}</span>
<el-radio-group v-model="settingsStore.sidebarColorScheme" @change="changeSidebarColorScheme">
<el-radio :value="SidebarLightThemeEnum.WHITE">{{ $t("settings.white") }}</el-radio>
<el-radio :value="SidebarLightThemeEnum.DARKBLUE">{{ $t("settings.darkBlue") }}</el-radio>
</el-radio-group>
</div>
<el-divider>{{ $t("settings.navigation") }}</el-divider> <el-divider>{{ $t("settings.navigation") }}</el-divider>
@@ -37,7 +44,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { LayoutEnum } from "@/enums/LayoutEnum"; import { LayoutEnum } from "@/enums/LayoutEnum";
import { ThemeEnum } from "@/enums/ThemeEnum"; import { ThemeEnum } from "@/enums/ThemeEnum";
import { SidebarLightThemeEnum } from "@/enums/ThemeEnum";
import { useSettingsStore, usePermissionStore, useAppStore } from "@/store"; import { useSettingsStore, usePermissionStore, useAppStore } from "@/store";
const route = useRoute(); const route = useRoute();
@@ -74,6 +81,16 @@ const changeTheme = (val: any) => {
settingsStore.changeTheme(isDark.value ? ThemeEnum.DARK : ThemeEnum.LIGHT); settingsStore.changeTheme(isDark.value ? ThemeEnum.DARK : ThemeEnum.LIGHT);
}; };
/**
* 更改侧边栏颜色方案
*
* @param val 颜色方案名称
*/
const changeSidebarColorScheme = (val: any) => {
console.log(val);
settingsStore.changeSidebarColorScheme(val);
};
/** /**
* 切换布局 * 切换布局
* *

View File

@@ -34,7 +34,7 @@ defineProps({
margin-left: 10px; margin-left: 10px;
font-size: 14px; font-size: 14px;
font-weight: bold; font-weight: bold;
color: #ffffff; color: $sidebar-logo-text-color;
} }
} }

View File

@@ -4,9 +4,22 @@
ref="menuRef" ref="menuRef"
:default-active="currentRoute.path" :default-active="currentRoute.path"
:collapse="!appStore.sidebar.opened" :collapse="!appStore.sidebar.opened"
:background-color="variables['menu-background']" :background-color="
:text-color="variables['menu-text']" theme === 'dark' || sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
:active-text-color="variables['menu-active-text']" ? variables['menu-background']
: undefined
"
:text-color="
theme === 'dark' || sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
? variables['menu-text']
: undefined
"
:active-text-color="
theme === 'dark' || sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
? variables['menu-active-text']
: undefined
"
:popper-effect="theme"
:unique-opened="false" :unique-opened="false"
:collapse-transition="false" :collapse-transition="false"
:mode="menuMode" :mode="menuMode"
@@ -29,6 +42,7 @@ import type { MenuInstance } from "element-plus";
import type { RouteRecordRaw } from "vue-router"; import type { RouteRecordRaw } from "vue-router";
import { LayoutEnum } from "@/enums/LayoutEnum"; import { LayoutEnum } from "@/enums/LayoutEnum";
import { SidebarLightThemeEnum } from "@/enums/ThemeEnum";
import { useSettingsStore, useAppStore } from "@/store"; import { useSettingsStore, useAppStore } from "@/store";
import { isExternal } from "@/utils/index"; import { isExternal } from "@/utils/index";
@@ -60,6 +74,12 @@ const menuMode = computed(() => {
return settingsStore.layout === LayoutEnum.TOP ? "horizontal" : "vertical"; return settingsStore.layout === LayoutEnum.TOP ? "horizontal" : "vertical";
}); });
// 获取主题
const theme = computed(() => settingsStore.theme);
// 获取浅色主题下的侧边栏配色方案
const sidebarColorScheme = computed(() => settingsStore.sidebarColorScheme);
/** /**
* 获取完整路径 * 获取完整路径
* *

View File

@@ -187,7 +187,15 @@ function resolvePath(routePath: string) {
} }
} }
.el-menu-item:hover { html.dark {
background-color: $menu-hover; .el-menu-item:hover {
background-color: $menu-hover;
}
}
html.light-darkBlue {
.el-menu-item:hover {
background-color: $menu-hover;
}
} }
</style> </style>

View File

@@ -4,9 +4,21 @@
<el-menu <el-menu
mode="horizontal" mode="horizontal"
:default-active="activePath" :default-active="activePath"
:background-color="variables['menu-background']" :background-color="
:text-color="variables['menu-text']" theme === 'dark' || sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
:active-text-color="variables['menu-active-text']" ? variables['menu-background']
: undefined
"
:text-color="
theme === 'dark' || sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
? variables['menu-text']
: undefined
"
:active-text-color="
theme === 'dark' || sidebarColorScheme === SidebarLightThemeEnum.DARKBLUE
? variables['menu-active-text']
: undefined
"
@select="handleMenuSelect" @select="handleMenuSelect"
> >
<el-menu-item v-for="route in topMenus" :key="route.path" :index="route.path"> <el-menu-item v-for="route in topMenus" :key="route.path" :index="route.path">
@@ -29,17 +41,25 @@
<script lang="ts" setup> <script lang="ts" setup>
import { LocationQueryRaw, RouteRecordRaw } from "vue-router"; import { LocationQueryRaw, RouteRecordRaw } from "vue-router";
import { usePermissionStore, useAppStore } from "@/store"; import { usePermissionStore, useAppStore, useSettingsStore } from "@/store";
import { translateRouteTitle } from "@/utils/i18n"; import { translateRouteTitle } from "@/utils/i18n";
import variables from "@/styles/variables.module.scss"; import variables from "@/styles/variables.module.scss";
import { SidebarLightThemeEnum } from "@/enums/ThemeEnum";
const router = useRouter(); const router = useRouter();
const appStore = useAppStore(); const appStore = useAppStore();
const permissionStore = usePermissionStore(); const permissionStore = usePermissionStore();
const settingsStore = useSettingsStore();
// 当前激活的顶部菜单路径 // 当前激活的顶部菜单路径
const activePath = computed(() => appStore.activeTopMenuPath); const activePath = computed(() => appStore.activeTopMenuPath);
// 获取主题
const theme = computed(() => settingsStore.theme);
// 获取浅色主题下的侧边栏配色方案
const sidebarColorScheme = computed(() => settingsStore.sidebarColorScheme);
// 顶部菜单列表 // 顶部菜单列表
const topMenus = ref<RouteRecordRaw[]>([]); const topMenus = ref<RouteRecordRaw[]>([]);

View File

@@ -33,6 +33,8 @@ const defaultSettings: AppSettings = {
watermarkEnabled: false, watermarkEnabled: false,
// 水印内容 // 水印内容
watermarkContent: pkg.name, watermarkContent: pkg.name,
// 侧边栏配色方案
sidebarColorScheme: "light-white",
}; };
export default defaultSettings; export default defaultSettings;

View File

@@ -1,6 +1,11 @@
import defaultSettings from "@/settings"; import defaultSettings from "@/settings";
import { ThemeEnum } from "@/enums/ThemeEnum"; import { SidebarLightThemeEnum, ThemeEnum } from "@/enums/ThemeEnum";
import { generateThemeColors, applyTheme, toggleDarkMode } from "@/utils/theme"; import {
generateThemeColors,
applyTheme,
toggleDarkMode,
toggleLightModeSidebarColorScheme,
} from "@/utils/theme";
type SettingsValue = boolean | string; type SettingsValue = boolean | string;
@@ -11,6 +16,11 @@ export const useSettingsStore = defineStore("setting", () => {
const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView); const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView);
// 侧边栏 Logo // 侧边栏 Logo
const sidebarLogo = useStorage<boolean>("sidebarLogo", defaultSettings.sidebarLogo); const sidebarLogo = useStorage<boolean>("sidebarLogo", defaultSettings.sidebarLogo);
// 浅色主题下的侧边栏配色方案 (白色/深蓝色)
const sidebarColorScheme = useStorage<string>(
"sidebarColorScheme",
defaultSettings.sidebarColorScheme
);
// 布局 // 布局
const layout = useStorage<string>("layout", defaultSettings.layout); const layout = useStorage<string>("layout", defaultSettings.layout);
// 水印 // 水印
@@ -34,10 +44,20 @@ export const useSettingsStore = defineStore("setting", () => {
{ immediate: true } { immediate: true }
); );
// 监听浅色侧边栏配色方案变化
watch(
[sidebarColorScheme],
([newSidebarColorScheme]) => {
toggleLightModeSidebarColorScheme(newSidebarColorScheme === SidebarLightThemeEnum.DARKBLUE);
},
{ immediate: true }
);
// 设置映射 // 设置映射
const settingsMap: Record<string, Ref<SettingsValue>> = { const settingsMap: Record<string, Ref<SettingsValue>> = {
tagsView, tagsView,
sidebarLogo, sidebarLogo,
sidebarColorScheme,
layout, layout,
watermarkEnabled, watermarkEnabled,
}; };
@@ -51,6 +71,10 @@ export const useSettingsStore = defineStore("setting", () => {
theme.value = val; theme.value = val;
} }
function changeSidebarColorScheme(val: string) {
sidebarColorScheme.value = val;
}
function changeThemeColor(color: string) { function changeThemeColor(color: string) {
themeColor.value = color; themeColor.value = color;
} }
@@ -63,6 +87,7 @@ export const useSettingsStore = defineStore("setting", () => {
settingsVisible, settingsVisible,
tagsView, tagsView,
sidebarLogo, sidebarLogo,
sidebarColorScheme,
layout, layout,
themeColor, themeColor,
theme, theme,
@@ -71,5 +96,6 @@ export const useSettingsStore = defineStore("setting", () => {
changeTheme, changeTheme,
changeThemeColor, changeThemeColor,
changeLayout, changeLayout,
changeSidebarColorScheme,
}; };
}); });

View File

@@ -25,18 +25,31 @@
/** 全局SCSS变量 */ /** 全局SCSS变量 */
:root { :root {
--menu-background: #304156; --menu-background: #fff; // 菜单背景色
--menu-text: #bfcbd9; --menu-text: #212121; // 菜单文字颜色 浅色主题-白色侧边栏配色下仅占位,实际颜色由 el-menu-item 组件决定
--menu-active-text: var(--el-menu-active-color); --menu-active-text: var(
--menu-hover: #263445; --el-menu-active-color
--sidebar-logo-background: #2d3748; ); // 菜单激活文字颜色 浅色主题-白色侧边栏配色下仅占位,实际颜色由 el-menu-item 组件决定
--menu-hover: #e6f4ff; // 菜单悬停背景色 浅色主题-白色侧边栏配色下仅占位,实际颜色由 el-menu-item 组件决定
--sidebar-logo-background: #f5f5f5; // 侧边栏 Logo 背景色
--sidebar-logo-text-color: #333; // 侧边栏 Logo 文字颜色
// 修复表格 fixed 列被选中后由于透明色导致叠字的 bug // 修复表格 fixed 列被选中后由于透明色导致叠字的 bug
.el-table { .el-table {
--el-table-current-row-bg-color: rgb(235 243 250); --el-table-current-row-bg-color: rgb(235 243 250);
} }
} }
/** 浅色主题-深蓝色侧边栏配色 */
html.light-darkBlue {
--menu-background: #304156; // 菜单背景色
--menu-text: #bfcbd9; // 菜单文字颜色
--menu-active-text: var(--el-menu-active-color); // 菜单激活文字颜色
--menu-hover: #263445; // 菜单悬停背景色
--sidebar-logo-background: #2d3748; // 侧边栏 Logo 背景色
--sidebar-logo-text-color: #fff; // 侧边栏 Logo 文字颜色
}
/** 暗黑主题 */ /** 暗黑主题 */
html.dark { html.dark {
--menu-background: var(--el-bg-color-overlay); --menu-background: var(--el-bg-color-overlay);
@@ -44,6 +57,7 @@ html.dark {
--menu-active-text: var(--el-menu-active-color); --menu-active-text: var(--el-menu-active-color);
--menu-hover: rgb(0 0 0 / 20%); --menu-hover: rgb(0 0 0 / 20%);
--sidebar-logo-background: rgb(0 0 0 / 20%); --sidebar-logo-background: rgb(0 0 0 / 20%);
--sidebar-logo-text-color: #fff;
} }
$menu-background: var(--menu-background); // 菜单背景色 $menu-background: var(--menu-background); // 菜单背景色
@@ -51,6 +65,7 @@ $menu-text: var(--menu-text); // 菜单文字颜色
$menu-active-text: var(--menu-active-text); // 菜单激活文字颜色 $menu-active-text: var(--menu-active-text); // 菜单激活文字颜色
$menu-hover: var(--menu-hover); // 菜单悬停背景色 $menu-hover: var(--menu-hover); // 菜单悬停背景色
$sidebar-logo-background: var(--sidebar-logo-background); // 侧边栏 Logo 背景色 $sidebar-logo-background: var(--sidebar-logo-background); // 侧边栏 Logo 背景色
$sidebar-logo-text-color: var(--sidebar-logo-text-color); // 侧边栏 Logo 文字颜色
$sidebar-width: 210px; // 侧边栏宽度 $sidebar-width: 210px; // 侧边栏宽度
$sidebar-width-collapsed: 54px; // 侧边栏收缩宽度 $sidebar-width-collapsed: 54px; // 侧边栏收缩宽度

View File

@@ -76,6 +76,8 @@ declare global {
watermarkEnabled: boolean; watermarkEnabled: boolean;
/** 水印内容 */ /** 水印内容 */
watermarkContent: string; watermarkContent: string;
/** 侧边栏配色方案(light-white | light-darkBlue) */
sidebarColorScheme: "light-white" | "light-darkBlue";
} }
/** /**

View File

@@ -1,3 +1,5 @@
import { SidebarLightThemeEnum } from "@/enums/ThemeEnum";
// 辅助函数:将十六进制颜色转换为 RGB // 辅助函数:将十六进制颜色转换为 RGB
function hexToRgb(hex: string): [number, number, number] { function hexToRgb(hex: string): [number, number, number] {
const bigint = parseInt(hex.slice(1), 16); const bigint = parseInt(hex.slice(1), 16);
@@ -43,6 +45,11 @@ export function applyTheme(colors: Record<string, string>) {
}); });
} }
/**
* 切换暗黑模式
*
* @param isDark 是否启用暗黑模式
*/
export function toggleDarkMode(isDark: boolean) { export function toggleDarkMode(isDark: boolean) {
if (isDark) { if (isDark) {
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
@@ -50,3 +57,16 @@ export function toggleDarkMode(isDark: boolean) {
document.documentElement.classList.remove("dark"); document.documentElement.classList.remove("dark");
} }
} }
/**
* 切换浅色主题下的侧边栏颜色方案
*
* @param isBlue 布尔值,表示是否开启深蓝色侧边栏颜色方案
*/
export function toggleLightModeSidebarColorScheme(isDarkBlueDark: boolean) {
if (isDarkBlueDark) {
document.documentElement.classList.add(SidebarLightThemeEnum.DARKBLUE);
} else {
document.documentElement.classList.remove(SidebarLightThemeEnum.DARKBLUE);
}
}