| src/assets/styles/sidebar.scss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/assets/styles/variables.module.scss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/layout/components/Sidebar/Logo.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/layout/components/Sidebar/SidebarItem.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/layout/components/Sidebar/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/assets/styles/sidebar.scss
@@ -1,133 +1,152 @@ #app { .main-container { min-height: 100%; transition: margin-left 0.28s; margin-left: $base-sidebar-width; position: relative; background: transparent; } .sidebarHide { margin-left: 0 !important; } .sidebar-container { transition: width 0.28s; width: $base-sidebar-width !important; height: 100%; position: fixed; font-size: 0px; top: 0; bottom: 0; left: 0; z-index: 1001; overflow: hidden; padding: 12px 0 16px 16px; background: transparent; box-shadow: none; // reset element-ui css .horizontal-collapse-transition { transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; } .scrollbar-wrapper { overflow-x: hidden !important; } .el-scrollbar__bar.is-vertical { right: 0px; } .el-scrollbar { height: 100%; } &.has-logo { .el-scrollbar { height: calc(100% - 72px); margin-top: 10px; } } .is-horizontal { display: none; } a { display: inline-block; width: 100%; overflow: hidden; } .svg-icon { margin-right: 16px; } .el-menu { border: none; height: 100%; width: 100% !important; padding: 10px 8px 18px; border-radius: 22px; background: var(--menu-surface); backdrop-filter: blur(18px); box-shadow: var(--shadow-sm); #app { .main-container { min-height: 100%; transition: margin-left 0.28s; margin-left: $base-sidebar-width; position: relative; background: transparent; } .sidebarHide { margin-left: 0 !important; } .sidebar-container { transition: width 0.28s; width: $base-sidebar-width !important; height: 100%; position: fixed; font-size: 0px; top: 0; bottom: 0; left: 0; z-index: 1001; overflow: hidden; padding: 12px 0 16px 16px; background: transparent; box-shadow: none; // reset element-ui css .horizontal-collapse-transition { transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; } .el-menu-item, .menu-title { overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; } .el-menu-item .el-menu-tooltip__trigger { display: inline-block !important; } // menu hover .submenu-title-noDropdown, .el-sub-menu__title { &:hover { background-color: var(--menu-hover) !important; border-radius: 14px; .scrollbar-wrapper { overflow-x: hidden !important; } .el-scrollbar__bar.is-vertical { right: 0px; } .el-scrollbar { height: 100%; } &.has-logo { .el-scrollbar { height: calc(100% - 72px); margin-top: 10px; } } & .theme-light .is-active > .el-sub-menu__title { color: var(--current-color) !important; } .is-horizontal { display: none; } a { display: inline-block; width: 100%; overflow: hidden; } .svg-icon { margin-right: 16px; } .el-menu { border: 1px solid var(--surface-border) !important; height: 100%; width: 100% !important; padding: 12px 10px 20px; border-radius: var(--radius-lg); background: var(--menu-surface); backdrop-filter: blur(20px); box-shadow: var(--shadow-sm); transition: all 0.3s ease; } .el-menu-item, .menu-title { overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; } .el-menu-item .el-menu-tooltip__trigger { display: inline-block !important; } // menu hover - 优化后的悬停效果 .submenu-title-noDropdown, .el-sub-menu__title { transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); border: none !important; &:hover { background-color: var(--menu-hover) !important; border-radius: var(--radius-sm); transform: translateX(2px); } } & .theme-light .is-active > .el-sub-menu__title, & .theme-dark .is-active > .el-sub-menu__title { color: var(--menu-active-text) !important; background: var(--menu-active-bg) !important; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); border: none !important; } & .nest-menu .el-sub-menu > .el-sub-menu__title, & .el-sub-menu .el-menu-item { min-width: 0 !important; margin: 0 12px 6px; width: calc(100% - 24px); padding-left: 8px !important; padding-right: 8px !important; margin: 0 10px 5px; width: calc(100% - 20px); padding-left: 10px !important; padding-right: 10px !important; box-sizing: border-box; border-radius: var(--radius-xs); transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); color: var(--sidebar-text); border: none !important; &:hover { background-color: var(--menu-hover) !important; transform: translateX(2px); } &.is-active { background-color: var(--menu-active-bg) !important; border-radius: 14px; background: var(--menu-active-bg) !important; border-radius: var(--radius-sm); color: var(--menu-active-text) !important; font-weight: 500; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); } } & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title, & .theme-light .el-sub-menu .el-menu-item { //background-color: transparent; & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title, & .theme-light .el-sub-menu .el-menu-item, & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, & .theme-dark .el-sub-menu .el-menu-item { &:hover { background-color: var(--menu-hover) !important; border-radius: 14px; border-radius: var(--radius-xs); } } } .hideSidebar { .sidebar-container { width: 68px !important; @@ -138,7 +157,7 @@ .main-container { margin-left: 84px; } .submenu-title-noDropdown { padding: 0 !important; position: relative; @@ -231,86 +250,96 @@ } } } .el-menu--collapse .el-menu .el-sub-menu { min-width: $base-sidebar-width !important; } // mobile responsive .mobile { .main-container { margin-left: 0px; } .sidebar-container { transition: transform 0.28s; width: $base-sidebar-width !important; } &.hideSidebar { .sidebar-container { pointer-events: none; transition-duration: 0.3s; transform: translate3d(-$base-sidebar-width, 0, 0); } } } .withoutAnimation { .main-container, .sidebar-container { transition: none; } } } // when menu collapsed .el-menu--vertical { & > .el-menu { .svg-icon { margin-right: 16px; } } .el-menu--collapse .el-menu .el-sub-menu { min-width: $base-sidebar-width !important; } // mobile responsive .mobile { .main-container { margin-left: 0px; } .sidebar-container { transition: transform 0.28s; width: $base-sidebar-width !important; } &.hideSidebar { .sidebar-container { pointer-events: none; transition-duration: 0.3s; transform: translate3d(-$base-sidebar-width, 0, 0); } } } .withoutAnimation { .main-container, .sidebar-container { transition: none; } } } // when menu collapsed .el-menu--vertical { & > .el-menu { .svg-icon { margin-right: 14px; } } .nest-menu .el-sub-menu > .el-sub-menu__title, .el-menu-item { min-width: 0 !important; margin: 0 12px 6px; width: calc(100% - 24px); padding-left: 8px !important; padding-right: 8px !important; margin: 0 10px 5px; width: calc(100% - 20px); padding-left: 10px !important; padding-right: 10px !important; box-sizing: border-box; border-radius: var(--radius-xs); transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); color: var(--sidebar-text); border: none !important; &:hover { // you can use $sub-menuHover background-color: var(--menu-hover) !important; transform: translateX(2px); } &.is-active { background-color: var(--menu-active-bg) !important; border-radius: 14px; background: var(--menu-active-bg) !important; color: var(--menu-active-text) !important; border-radius: var(--radius-sm); font-weight: 500; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); } } // the scroll bar appears when the sub-menu is too long > .el-menu--popup { max-height: 100vh; overflow-y: auto; padding: 8px; border-radius: 18px; border: 1px solid var(--surface-border); box-shadow: var(--shadow-md); &::-webkit-scrollbar-track-piece { background: #dfe7e1; } &::-webkit-scrollbar { width: 6px; } &::-webkit-scrollbar-thumb { background: #9aa79e; border-radius: 20px; } } } // the scroll bar appears when the sub-menu is too long > .el-menu--popup { max-height: 100vh; overflow-y: auto; padding: 10px; border-radius: var(--radius-md); border: 1px solid var(--surface-border); box-shadow: var(--shadow-md); background: var(--menu-surface); backdrop-filter: blur(20px); &::-webkit-scrollbar-track-piece { background: var(--surface-muted); } &::-webkit-scrollbar { width: 5px; } &::-webkit-scrollbar-thumb { background: var(--accent-light); border-radius: 10px; } } } src/assets/styles/variables.module.scss
@@ -8,31 +8,31 @@ $yellow: #fec171; $panGreen: #30b08f; // menu palette $menuText: #677287; $menuActiveText: #1f7a72; $menuBg: #f4f7f4; $menuHover: #e7eeea; // menu palette - 使用主题色 $menuText: #5a6478; $menuActiveText: #ffffff; $menuBg: #f8fafb; $menuHover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.08); // light theme $menuLightBg: #f4f7f4; $menuLightHover: #e7eeea; $menuLightText: #3b4658; $menuLightActiveText: #1f7a72; // light theme - 使用主题色 $menuLightBg: #f8fafb; $menuLightHover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.08); $menuLightText: #3d4858; $menuLightActiveText: #ffffff; // layout $base-sidebar-width: 216px; $sideBarWidth: 216px; // sidebar $base-menu-color: #677287; $base-menu-color-active: #1f7a72; $base-menu-background: #f4f7f4; $base-sub-menu-background: #eef3ef; // sidebar - 优化后的侧边栏配色 $base-menu-color: #5a6478; $base-menu-color-active: #0d9488; $base-menu-background: #f8fafb; $base-sub-menu-background: #f0f5f4; $base-sub-menu-hover: #ffffff; // component $--color-primary: #1f7a72; // component - 优化后的主题色 $--color-primary: #0d9488; $--color-success: #67c23a; $--color-warning: #d89b41; $--color-danger: #d25b52; @@ -66,37 +66,44 @@ :root { --sidebar-bg: #{$menuBg}; --sidebar-text: #{$menuText}; --sidebar-muted: #93a0b1; --menu-hover: #{$menuHover}; --menu-active-bg: #dfe9e4; --menu-surface: rgba(255, 255, 255, 0.72); --sidebar-muted: #5a6478; --menu-hover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.08); --menu-active-bg: var(--el-color-primary, #0d9488); --menu-active-text: #ffffff; --menu-surface: #f8fafb; --app-bg: #eef2ee; --app-bg-accent: #dfe8e2; --app-bg: #f0f4f3; --app-bg-accent: #e0ebe9; --surface-base: #ffffff; --surface-soft: #f7faf8; --surface-muted: #eff4f1; --surface-border: #d8e1db; --surface-border-strong: #c9d5ce; --text-primary: #21313f; --text-secondary: #5f6d7e; --text-tertiary: #8a98a8; --shadow-sm: 0 10px 30px rgba(31, 49, 38, 0.06); --shadow-md: 0 18px 50px rgba(31, 49, 38, 0.1); --radius-lg: 24px; --radius-md: 18px; --radius-sm: 12px; --surface-soft: #f7faf9; --surface-muted: #eef3f2; --surface-border: #d5e0de; --surface-border-strong: #c5d5d2; --text-primary: #1e293b; --text-secondary: #4a5568; --text-tertiary: #718096; --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.06); --shadow-md: 0 8px 28px rgba(0, 0, 0, 0.1); --shadow-menu: 0 2px 8px rgba(0, 0, 0, 0.05); --radius-lg: 20px; --radius-md: 14px; --radius-sm: 10px; --radius-xs: 6px; --navbar-bg: rgba(255, 255, 255, 0.78); --navbar-text: #21313f; --navbar-hover: rgba(31, 122, 114, 0.08); --navbar-bg: rgba(255, 255, 255, 0.85); --navbar-text: #1e293b; --navbar-hover: rgba(13, 148, 136, 0.08); --tags-bg: transparent; --tags-item-bg: rgba(255, 255, 255, 0.74); --tags-item-border: rgba(201, 213, 206, 0.88); --tags-item-text: #5f6d7e; --tags-item-hover: rgba(31, 122, 114, 0.08); --tags-close-hover: rgba(31, 122, 114, 0.18); --tags-item-bg: rgba(255, 255, 255, 0.8); --tags-item-border: rgba(197, 213, 210, 0.9); --tags-item-text: #4a5568; --tags-item-hover: rgba(13, 148, 136, 0.1); --tags-close-hover: rgba(13, 148, 136, 0.2); --accent-primary: #0d9488; --accent-light: #14b8a6; --accent-lighter: #5eead4; --splitpanes-default-bg: #ffffff; } @@ -109,10 +116,19 @@ --el-border-color: #434343; --el-border-color-light: #434343; --sidebar-bg: #141414; --sidebar-text: #ffffff; --menu-hover: #2d2d2d; --menu-active-text: #{$menuActiveText}; --sidebar-bg: #1a1a1a; --sidebar-text: #d0d0d0; --sidebar-muted: #888888; --menu-hover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.12); --menu-active-bg: var(--el-color-primary, #0d9488); --menu-active-text: #ffffff; --menu-surface: #1a1a1a; --text-primary: #ffffff; --text-secondary: #d0d0d0; --text-tertiary: #888888; --accent-primary: var(--el-color-primary, #0d9488); --accent-light: var(--el-color-primary-light-3, #14b8a6); --navbar-bg: #141414; --navbar-text: #ffffff; @@ -139,13 +155,22 @@ .sidebar-container { .el-menu-item, .menu-title { color: var(--el-text-color-regular); color: var(--sidebar-text); } .el-menu-item.is-active, .el-menu-item.is-active .menu-title { color: var(--menu-active-text) !important; } & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, & .theme-dark .el-sub-menu .el-menu-item { background-color: var(--el-bg-color) !important; } & .theme-dark .el-sub-menu .el-menu-item.is-active { background-color: var(--menu-active-bg) !important; } } .el-menu--horizontal { src/layout/components/Sidebar/Logo.vue
@@ -84,12 +84,14 @@ width: 100% !important; height: 56px !important; line-height: 56px; background: rgba(255, 255, 255, 0.78); background: var(--menu-surface); border: 1px solid var(--surface-border); border-radius: 22px; border-radius: var(--radius-lg); text-align: center; overflow: hidden; box-shadow: var(--shadow-sm); backdrop-filter: blur(20px); transition: all 0.3s ease; .sidebar-logo-link { height: 100%; src/layout/components/Sidebar/SidebarItem.vue
@@ -1,17 +1,19 @@ <template> <div v-if="!item.hidden"> <div v-if="!item.hidden" class="sidebar-item-wrapper"> <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)"> <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"> <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/> <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template> <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" class="menu-icon"/> <template #title> <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span> </template> </el-menu-item> </app-link> </template> <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported> <template v-if="item.meta" #title> <svg-icon :icon-class="item.meta && item.meta.icon" /> <svg-icon :icon-class="item.meta && item.meta.icon" class="menu-icon" /> <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span> </template> @@ -98,3 +100,56 @@ } } </script> <style lang="scss" scoped> .sidebar-item-wrapper { :deep(.menu-icon) { width: 18px; height: 18px; margin-right: 12px; flex-shrink: 0; transition: all 0.25s ease; color: var(--sidebar-text); opacity: 0.8; } :deep(.el-menu-item:hover .menu-icon), :deep(.el-sub-menu__title:hover .menu-icon) { color: var(--el-color-primary, var(--accent-primary)); opacity: 1; transform: scale(1.1); } :deep(.el-menu-item.is-active .menu-icon) { color: var(--menu-active-text) !important; opacity: 1; } :deep(.menu-title) { font-weight: 450; transition: all 0.25s ease; color: var(--sidebar-text); } :deep(.el-menu-item:hover .menu-title), :deep(.el-sub-menu__title:hover .menu-title) { color: var(--el-color-primary, var(--accent-primary)); } :deep(.el-menu-item.is-active .menu-title) { color: var(--menu-active-text) !important; } :deep(.nest-menu) { .menu-icon { width: 16px; height: 16px; margin-right: 10px; } .menu-title { font-size: 13px; } } } </style> src/layout/components/Sidebar/index.vue
@@ -64,7 +64,7 @@ <style lang="scss" scoped> .sidebar-container { background-color: v-bind(getMenuBackground); border-radius: 22px; border-radius: var(--radius-lg); overflow: hidden; .scrollbar-wrapper { @@ -72,29 +72,50 @@ } .el-menu { border: none; border: 1px solid var(--surface-border) !important; height: 100%; width: 100% !important; border-radius: 22px; border-radius: var(--radius-lg); .el-menu-item, .el-sub-menu__title { margin-bottom: 6px; border-radius: 14px; margin-bottom: 5px; border-radius: var(--radius-xs); color: v-bind(getMenuTextColor); font-size: 13px; font-size: 13.5px; letter-spacing: 0.2px; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); border: none !important; &:hover { background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important; border-radius: 14px; background-color: var(--menu-hover) !important; border-radius: var(--radius-sm); transform: translateX(2px); } .svg-icon { transition: all 0.25s ease; } &:hover .svg-icon { transform: scale(1.1); color: var(--el-color-primary, var(--accent-primary)); } } .el-menu-item { color: var(--sidebar-text); &.is-active { color: v-bind(theme); background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; font-weight: 600; background: var(--menu-active-bg) !important; color: var(--menu-active-text) !important; font-weight: 500; border-radius: var(--radius-sm); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); .svg-icon { color: var(--menu-active-text) !important; } } } @@ -112,45 +133,74 @@ right: 12px; font-size: 14px !important; color: currentColor !important; opacity: 0.72; opacity: 0.7; transition: all 0.25s ease; } :deep(.el-sub-menu.is-opened .el-sub-menu__icon-arrow) { transform: rotate(180deg); } :deep(.el-sub-menu.is-active > .el-sub-menu__title) { color: v-bind(theme) !important; color: var(--menu-active-text) !important; font-weight: 600; background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; border-radius: 14px; margin: 0 10px 6px !important; // width: calc(100% - 20px) !important; border-radius: var(--radius-sm); margin: 0 10px 5px !important; padding-left: 10px !important; padding-right: 10px !important; box-sizing: border-box; overflow: hidden; background-clip: padding-box; background: var(--menu-active-bg) !important; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); border: none !important; } :deep(.el-menu-item.is-active) { margin: 0 10px 6px !important; margin: 0 10px 5px !important; width: calc(100% - 20px) !important; padding-left: 10px !important; padding-right: 10px !important; box-sizing: border-box; overflow: hidden; background-clip: padding-box; border-radius: 14px; border-radius: var(--radius-sm); } :deep(.el-sub-menu.is-active > .el-sub-menu__title .menu-title), :deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon), :deep(.el-sub-menu.is-active > .el-sub-menu__title .el-sub-menu__icon-arrow), :deep(.el-menu-item.is-active .menu-title), :deep(.el-menu-item.is-active .svg-icon) { color: v-bind(theme) !important; :deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon) { color: var(--menu-active-text) !important; } :deep(.el-menu-item.is-active .menu-title) { color: var(--menu-active-text) !important; } :deep(.el-sub-menu__title:hover), :deep(.el-menu-item:hover) { border-radius: 14px; border-radius: var(--radius-sm); } // 子菜单展开动画优化 :deep(.el-sub-menu .el-menu) { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } // 菜单项进入动画 :deep(.el-menu-item), :deep(.el-sub-menu__title) { animation: menuItemFadeIn 0.3s ease forwards; } @keyframes menuItemFadeIn { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: translateX(0); } } } }