Skip to content

基础设置内容

自动生成侧边栏sidebar.mjs

js
// .vitepress/sidebar.mjs
import { readdirSync, statSync, existsSync, readFileSync } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import matter from 'gray-matter'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

function getFileFrontmatter(filePath) {
  try {
    if (!existsSync(filePath)) {
      return { title: null, order: 999, sidebar: {} }
    }
    const content = readFileSync(filePath, 'utf-8')
    const { data } = matter(content)
    return {
      title: data.title || null,
      order: data.order !== undefined ? data.order : 999,
      sidebar: data.sidebar || {}
    }
  } catch (error) {
    console.warn(`读取文件 ${filePath} frontmatter 失败:`, error.message)
    return { title: null, order: 999, sidebar: {} }
  }
}

function hasValidContent(dirPath) {
  if (!existsSync(dirPath)) {
    return false
  }

  const entries = readdirSync(dirPath).filter(entry =>
    !entry.startsWith('.') &&
    !['.vitepress', 'public', 'node_modules', 'dist'].includes(entry)
  )

  if (entries.includes('index.md')) {
    return true
  }

  const hasMdFiles = entries.some(entry =>
    entry.endsWith('.md') && entry !== 'index.md'
  )
  if (hasMdFiles) {
    return true
  }

  for (const entry of entries) {
    const fullPath = join(dirPath, entry)
    try {
      const stats = statSync(fullPath)
      if (stats.isDirectory() && hasValidContent(fullPath)) {
        return true
      }
    } catch (error) {
      // 忽略权限错误等,继续检查其他条目
    }
  }

  return false
}

/**
 * 递归处理目录结构,生成侧边栏菜单项
 * @param {string} currentPath - 当前目录路径
 * @param {string} baseUrl - 基础 URL 路径
 * @param {number} level - 目录层级(用于控制折叠状态)
 * @returns {Object[]} 生成的菜单项数组
 */
function processDirectory(currentPath, baseUrl, level = 0) {
  const allItems = []

  if (!existsSync(currentPath)) {
    console.warn(`⚠️ 目录不存在: ${currentPath}`)
    return allItems
  }

  let entries = []
  try {
    entries = readdirSync(currentPath).filter(entry =>
      !entry.startsWith('.') &&
      !['.vitepress', 'public', 'node_modules', 'dist'].includes(entry)
    )
  } catch (error) {
    console.error(`❌ 读取目录失败: ${currentPath}`, error.message)
    return allItems
  }

  const dirItems = []
  const fileItems = []

  entries.forEach(entry => {
    const fullPath = join(currentPath, entry)

    try {
      const stats = statSync(fullPath)

      if (stats.isDirectory()) {
        // 检查目录是否有有效内容
        if (hasValidContent(fullPath)) {
          // 递归处理子目录
          const subItems = processDirectory(fullPath, `${baseUrl}${entry}/`, level + 1)

          // 读取目录下的 index.md 获取目录的排序信息
          const indexMdPath = join(fullPath, 'index.md')
          const dirFrontmatter = getFileFrontmatter(indexMdPath)

          dirItems.push({
            type: 'directory',
            text: dirFrontmatter.title || entry,
            collapsed: level >= 1,
            items: subItems,
            order: dirFrontmatter.order
          })
        }
      } else if (stats.isFile() && entry.endsWith('.md') && entry !== 'index.md') {
        // 处理 Markdown 文件(排除 index.md)
        const fileName = entry.replace('.md', '')
        const fileFrontmatter = getFileFrontmatter(fullPath)

        fileItems.push({
          type: 'file',
          text: fileFrontmatter.title || fileName,
          link: `${baseUrl}${fileName}`,
          order: fileFrontmatter.order
        })
      }
    } catch (error) {
      console.warn(`处理路径 ${fullPath} 时出错:`, error.message)
    }
  })

  // 分别对目录和文件按 order 排序
  dirItems.sort((a, b) => (a.order || 999) - (b.order || 999))
  fileItems.sort((a, b) => (a.order || 999) - (b.order || 999))

  return [...dirItems, ...fileItems]
}

/**
 * 生成侧边栏配置(支持 frontmatter 排序)
 * @returns {Object} 侧边栏配置对象
 */
export function generateSidebar() {
  const sidebar = {}
  const docsRootPath = join(__dirname, '..')

  // 定义顶级目录
  const rootDirs = ['知识积累', '考试', '工作', '兴趣', '软件学习', '其他']

  rootDirs.forEach(dir => {
    const dirPath = join(docsRootPath, dir)

    if (!existsSync(dirPath)) {
      console.warn(`⚠️ 目录 "${dir}" 不存在,已跳过`)
      return
    }

    try {
      let sidebarItems = []

      // 添加目录首页(如果存在 index.md)
      const indexMdPath = join(dirPath, 'index.md')

      if (existsSync(indexMdPath)) {
        const indexFrontmatter = getFileFrontmatter(indexMdPath)
        sidebarItems.push({
          type: 'index',
          text: indexFrontmatter.title || `${dir}首页`,
          link: `/${dir}/`,
          order: indexFrontmatter.order || 0
        })
      }

      // 递归处理目录结构
      const directoryItems = processDirectory(dirPath, `/${dir}/`)
      sidebarItems = sidebarItems.concat(directoryItems)

      // 按 order 字段排序整个侧边栏
      sidebarItems.sort((a, b) => (a.order || 999) - (b.order || 999))

      // 设置侧边栏配置
      if (sidebarItems.length > 0) {
        // 为整个顶级目录创建一个侧边栏分组
        sidebar[`/${dir}/`] = [
          {
            text: dir,
            collapsed: false, // 顶级目录默认展开
            items: sidebarItems.map(item => {
              const { type, order, ...rest } = item
              return rest
            })
          }
        ]

        // 关键:为每个子目录创建独立的侧边栏配置
        // 遍历子目录,为它们创建独立的侧边栏
        const subDirs = readdirSync(dirPath).filter(entry => {
          const fullPath = join(dirPath, entry)
          try {
            return statSync(fullPath).isDirectory() && hasValidContent(fullPath)
          } catch {
            return false
          }
        })

        subDirs.forEach(subDir => {
          const subDirPath = join(dirPath, subDir)
          const subIndexPath = join(subDirPath, 'index.md')
          const subDirFrontmatter = getFileFrontmatter(subIndexPath)

          // 创建子目录的侧边栏
          const subSidebarItems = []

          // 添加子目录首页
          if (existsSync(subIndexPath)) {
            subSidebarItems.push({
              text: subDirFrontmatter.title || `${subDir}首页`,
              link: `/${dir}/${subDir}/`,
              order: subDirFrontmatter.order || 0
            })
          }

          // 处理子目录的内容
          const subDirItems = processDirectory(subDirPath, `/${dir}/${subDir}/`, 1)
          subSidebarItems.push(...subDirItems)

          // 排序
          subSidebarItems.sort((a, b) => (a.order || 999) - (b.order || 999))

          // 为子目录创建独立的侧边栏配置
          sidebar[`/${dir}/${subDir}/`] = [
            {
              text: subDirFrontmatter.title || subDir,
              collapsed: false,
              items: subSidebarItems.map(item => {
                const { type, order, ...rest } = item
                return rest
              })
            }
          ]
        })
      } else {
        console.warn(`⚠️ ${dir} 目录下没有找到有效内容`)
      }

    } catch (error) {
      console.error(`❌ 处理目录 "${dir}" 时发生错误:`, error.message)
    }
  })

  return sidebar
}

// 导出默认生成的侧边栏
export default generateSidebar()

配置信息 config.mjs

json
import { defineConfig } from 'vitepress'
import nav from './nav.mjs'
import head from './head.mjs'
import { generateSidebar } from './sidebar.mjs'

// https://vitepress.dev/reference/site-config
export default defineConfig({
  base: '/notes/',
  title: "笔记站",
  description: "我的笔记站",
  themeConfig: {
    // https://vitepress.dev/reference/default-theme-config
    nav,
    head: head,
    sidebar: generateSidebar(),
    // 分页导航
        docFooter: {
          prev: '上一页',
          next: '下一页'
        },

        // 编辑此页(如需开启请替换仓库地址)
        editLink: {
          text: '编辑此页面',
          // 示例:替换为你的实际仓库路径
          pattern: 'https://github.com/wuhaotdcq-s/notes/tree/main/docs/:path'
        },
    footer: {
      message: '笔记站',
      copyright: `版权所有 © 2025-${new Date().getFullYear()} 笔记站`
    },
// 侧边栏相关
        selectText: '选择笔记',
        sidebarMenuLabel: '侧边栏',
        toggleSidebar: '切换侧边栏',
    outline: {
      level: [1, 3],
      label: "目录",
    },
    lastUpdated: {
      text: '最近更新时间',
      formatOptions: {
        dateStyle: 'long',
        timeStyle: 'medium'
      }
    },
    darkModeSwitchLabel: "主题",
    lightModeSwitchTitle: "切换到浅色模式",
    darkModeSwitchTitle: "切换到深色模式",
    search: {
      provider: 'local',
      options: {
        translations: {
          button: {
            buttonText: '搜索文档',
            buttonAriaLabel: '搜索文档'
          },
          modal: {
            noResultsText: '无法找到相关结果',
            resetButtonTitle: '清除查询条件',
            footer: {
              selectText: '选择',
              navigateText: '切换',
              closeText: '关闭'
            },
          },
        },
      },
    },
   markdown: {
    lineNumbers: true,
    container: {
      tipLabel: '提示',
      warningLabel: '注意',
      dangerLabel: '警告',
      infoLabel: '信息',
      detailsLabel: '详情'
    },

    math: true,
    codeTransformers: [],

    config: (md) => {
      // 可在此处添加 Markdown 插件
    }
  },
    socialLinks: [
      { icon: 'github', link: 'https://github.com/vuejs/vitepress' },
      { icon: 'bilibili', link: 'https://space.bilibili.com/66129848' },
      { icon: 'wechat', link: 'https://weixin.su/' }
    ]
  }

})

笔记站