From b5baf54431678830ced01654679b93c9f4f4567a Mon Sep 17 00:00:00 2001
From: yli
Date: Sun, 22 Feb 2026 01:56:23 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=9B=AE=E5=BD=95?=
=?UTF-8?q?=E6=A0=8F=E6=94=B6=E8=B5=B7/=E5=B1=95=E5=BC=80=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/.vitepress/theme/Layout.vue | 165 ++++++++++++++++++++++++++++++-
1 file changed, 164 insertions(+), 1 deletion(-)
diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue
index 0fa5be8..45b7ed0 100644
--- a/docs/.vitepress/theme/Layout.vue
+++ b/docs/.vitepress/theme/Layout.vue
@@ -3,7 +3,7 @@ import DefaultTheme from 'vitepress/theme'
import { useData } from 'vitepress'
import TextType from './components/TextType.vue'
import GitHubStars from './components/GitHubStars.vue'
-import { onMounted, ref, watch } from 'vue'
+import { onMounted, ref, watch, computed } from 'vue'
import ReadingProgress from './components/ReadingProgress.vue'
import { Setting } from '@element-plus/icons-vue'
@@ -75,6 +75,18 @@ const resetLineHeight = () => {
lineHeight.value = DEFAULT_LINE_HEIGHT
}
+// ============================================
+// 目录栏(左侧 VPSidebar)收起/展开功能
+// ============================================
+const SIDEBAR_COLLAPSED_KEY = 'ev-sidebar-collapsed'
+const sidebarCollapsed = ref(false)
+
+const toggleSidebar = () => {
+ sidebarCollapsed.value = !sidebarCollapsed.value
+}
+
+const isHomePage = computed(() => frontmatter.value.layout === 'home')
+
onMounted(() => {
const saved = clampFontSize(localStorage.getItem(FONT_SIZE_STORAGE_KEY))
const savedLineHeight = clampLineHeight(
@@ -86,6 +98,13 @@ onMounted(() => {
applyLineHeight(savedLineHeight)
isHydrated.value = true
+ // 恢复目录栏收起状态
+ const savedCollapsed = localStorage.getItem(SIDEBAR_COLLAPSED_KEY)
+ if (savedCollapsed === 'true') {
+ sidebarCollapsed.value = true
+ document.body.classList.add('ev-sidebar-collapsed')
+ }
+
initOutlineAutoScroll()
})
@@ -263,10 +282,30 @@ watch(lineHeight, (next) => {
applyLineHeight(normalized)
localStorage.setItem(LINE_HEIGHT_STORAGE_KEY, String(normalized))
})
+
+watch(sidebarCollapsed, (collapsed) => {
+ if (typeof document === 'undefined') return
+ document.body.classList.toggle('ev-sidebar-collapsed', collapsed)
+ localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(collapsed))
+})
+
+
+
@@ -389,6 +428,21 @@ watch(lineHeight, (next) => {
+
+
+
@@ -519,4 +573,113 @@ watch(lineHeight, (next) => {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
+
+/* ============================================
+ 目录栏收起/展开
+ ============================================ */
+
+/* 导航栏左侧的收起按钮 */
+.ev-sidebar-nav-btn {
+ display: none;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border: none;
+ border-radius: 6px;
+ background: transparent;
+ color: var(--vp-c-text-2);
+ cursor: pointer;
+ margin-right: 4px;
+ flex-shrink: 0;
+}
+.ev-sidebar-nav-btn:hover {
+ color: var(--vp-c-text-1);
+ background: var(--vp-c-bg-soft);
+}
+
+/* 分界线上的收起按钮 */
+.ev-sidebar-toggle-btn {
+ display: none;
+ position: fixed;
+ top: 50%;
+ transform: translateY(-50%);
+ left: calc(var(--vp-sidebar-width, 272px) - 12px);
+ z-index: 30;
+ width: 24px;
+ height: 48px;
+ border: 1px solid var(--vp-c-divider);
+ border-radius: 0 6px 6px 0;
+ background: var(--vp-c-bg);
+ color: var(--vp-c-text-3);
+ cursor: pointer;
+ align-items: center;
+ justify-content: center;
+ transition: left 0.3s ease, opacity 0.2s;
+}
+.ev-sidebar-toggle-btn:hover {
+ color: var(--vp-c-text-1);
+ border-color: var(--vp-c-brand);
+}
+.ev-sidebar-toggle-btn.collapsed {
+ left: 0;
+ border-radius: 0 6px 6px 0;
+}
+
+/* 桌面端才显示按钮 */
+@media (min-width: 960px) {
+ .ev-sidebar-nav-btn {
+ display: inline-flex;
+ }
+ .ev-sidebar-toggle-btn {
+ display: inline-flex;
+ }
+}
+
+/* @1440px 时分界线按钮跟随侧边栏实际宽度 */
+@media (min-width: 1440px) {
+ .ev-sidebar-toggle-btn:not(.collapsed) {
+ left: calc((100% - (var(--vp-layout-max-width, 1440px) - 64px)) / 2 + var(--vp-sidebar-width, 272px) - 32px - 12px);
+ }
+}
+
+/* ---- 收起状态下的 CSS 覆盖 ---- */
+
+/* 隐藏侧边栏 */
+.ev-sidebar-collapsed .VPSidebar {
+ display: none !important;
+}
+
+/* 内容区域填满页面 */
+@media (min-width: 960px) {
+ .ev-sidebar-collapsed .VPContent.has-sidebar {
+ padding-left: 0 !important;
+ }
+ .ev-sidebar-collapsed .VPNavBar.has-sidebar .content {
+ padding-left: 0 !important;
+ }
+ .ev-sidebar-collapsed .VPNavBar.has-sidebar .divider {
+ padding-left: 0 !important;
+ }
+}
+
+@media (min-width: 1440px) {
+ .ev-sidebar-collapsed .VPContent.has-sidebar {
+ padding-left: calc((100% - var(--vp-layout-max-width, 1440px)) / 2) !important;
+ }
+ .ev-sidebar-collapsed .VPNavBar.has-sidebar .content {
+ padding-left: calc((100% - var(--vp-layout-max-width, 1440px)) / 2) !important;
+ }
+ .ev-sidebar-collapsed .VPNavBar.has-sidebar .divider {
+ padding-left: calc((100% - var(--vp-layout-max-width, 1440px)) / 2) !important;
+ }
+}
+
+/* 收起/展开过渡动画 */
+.VPSidebar,
+.VPContent.has-sidebar,
+.VPNavBar.has-sidebar .content,
+.VPNavBar.has-sidebar .divider {
+ transition: padding-left 0.3s ease, transform 0.3s ease;
+}