teek配置rewrite模式
Teek配置rewrite模式
目录
[toc]
前言
那么,为什么要切换到rewrite呢?
- 左下角会有中文路径提示
- url打开会有中文提示
注意
使用原则
- 必须保证 一级目录下的 所有md都得有同一个一级前缀,例如 /一级前缀/随机uid
- 一级前缀决定了是否都在同一个侧边栏显示
- 你一级目录下,都是其子目录,默认都弄成同一个一级前缀就好了呗。如果不想让他出现在侧边栏,那就单独指定一个一级目录。
- 所以,一级前缀 就统一整成 /技术/xxx /专题/xxx /生活/xxx /兴趣/xxx /关于/xxx (这里的中文可以替换为对应的英文)
1、从proxy模式切换到rewrite模式
默认安装的Teek是proxy模式,这里需要手动切换到rewrite模式。
1、导入
编辑docs\.vitepress\config.ts
文件
import { createRewrites } from "vitepress-theme-teek/config";
2、配置
编辑docs\.vitepress\config.ts
文件
rewrites: createRewrites({
srcDir: 'docs',
}),
permalink: true,
sidebar: true,
sidebarOption: {
// initItems: false, //这条命令注释后,才会让文档和目录的样式保持一致
collapsed: true, //打开侧边栏自动收缩功能
ignoreList: [/^_.*$/],
resolveRule: "rewrites",
checkRewritesPrefix: true,
},
NOTE
以上配置就将我们的Teek从proxy模式切换到了rewrite模式。
但是,如何把自己之前的老库全部迁移过来呢?(老库有成千上万的md文档时,我们的头都要炸了……此时,就可以用到如下autoformatter插件了,让脚本替我们完成。😜)
2、配置autoformatter插件
这里利用autoformatter插件来自动注入 带对应一级前缀的permalink。
📌1.安装插件
pnpm add vitepress-plugin-auto-frontmatter
📌2.配置代码
01.导入
编辑docs\.vitepress\config.ts
文件
import AutoFrontmatter, {FileInfo} from "vitepress-plugin-auto-frontmatter";
import { useTransformByRules, type TransformRule } from "./theme/composables/useTransform";
02.配置
编辑docs\.vitepress\config.ts
文件
plugins: [
// 自动注入一级前缀(rewrite模式)
AutoFrontmatter({
pattern: "**/*.md",
// exclude 指定的对象如果在 markdown frontmatter 存在,则忽略该文件。当 include 和 exclude 存在相同文件时,exclude 优先级高
//exclude: { coverImg: true},
recoverTransform: true, // false 只添加不存在的字段
// 返回一个新的 frontmatter 或只返回 undefined,如果返回 {},则清空 MD 文件本身存在的 frontmatter
transform: (frontMatter: Record<string, any>, fileInfo: FileInfo) => {
// 定义需要处理的所有规则(可扩展多个)
const rules: TransformRule[] = [
// { folderName: "95.Teek", prefix: "/teek" }, // 添加前缀
// { folderName: "10.Teek", prefix: "/teek" }, // 添加前缀
// { folderName: "20.工具资源/01.SSL证书", prefix: "/tool", removeLevel: 1 }, // 移除一层前缀后再添加前缀
// { folderName: "10.笔记专栏/99.博客搭建", prefix: "/note", clear: true }, // 清空 permalink,优先级最高
// { folderName: "20.文档", prefix: "/note", clear: true }, // 清空 permalink,优先级最高
// { folderName: "01.前端/01.vite/", prefix: "/testa/$uuid5/$uuid1/$uuid10/$uuid99", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "10.运维", prefix: "/linux/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "20.前端", prefix: "/qianduan/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "30.编程", prefix: "/code/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "35.黑客", prefix: "/hacker/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "40.专题", prefix: "/zhuanti/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "50.工具", prefix: "/tools/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "60.生活", prefix: "/life/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "70.精神小屋", prefix: "/love/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "80.娱乐", prefix: "/yule/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "85.兴趣", prefix: "/xingqu/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "90.关于", prefix: "/about/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
{ folderName: "100.Teek", prefix: "/teek/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
];
// 应用规则转换
return useTransformByRules(frontMatter, fileInfo, rules);
/* // 如果文件本身存在了 coverImg,则不生成
if (frontMatter.coverImg) return; // 随机获取 coverImg
const list = [...Wallpaper, ...BlogCover];
const coverImg = list[Math.floor(Math.random() * list.length)];
const transformResult = { ...frontMatter, coverImg };
console.log("transformResult", transformResult)
return Object.keys(transformResult).length
? transformResult
: undefined;*/
},
}),
],
03.创建docs\.vitepress\theme\composables\useTransform.ts
文件
import { FileInfo } from "vitepress-plugin-auto-frontmatter";
// 定义规则类型
export interface TransformRule {
folderName: string; // 匹配文件或文件夹名称
prefix: string; // 要添加的前缀
removeLevel?: number; // 可选:要移除的前缀层级(以 / 分割),可以填写一个很大的数,那么就会全部清空然后添加前缀,适合移除前缀并添加的场景
clear?: boolean; // 可选,是否清空 permalink,适合只想要清空使用。true=清空(即使填了prefix也会清空,clear优先级高),默认false
}
/**
* 获取路径按 / 分割后的第一个有效分组(忽略空字符串)
* @param path
*/
const getFirstPathSegment = (path: string): string => {
// 按 / 分割并过滤空字符串(处理开头/结尾的斜杠或连续斜杠)
const segments = path.split('/').filter(segment => segment.trim() !== '');
return segments.length > 0 ? segments[0] : '';
};
/**
* 生成指定长度的随机字符串(数字 + 小写字母)
* @param length 字符串长度(最大为10)
* @returns 指定长度的随机字符串
*/
const generateRandomString = (length: number): string => {
if (length <= 0) return '';
const maxLen = 10;
const actualLength = Math.min(length, maxLen); // 最大10位
const chars = '0123456789abcdefghijklmnopqrstuvwxyz';
const charsLength = chars.length;
let result = '';
for (let i = 0; i < actualLength; i++) {
result += chars[Math.floor(Math.random() * charsLength)];
}
return result;
};
/**
* 替换字符串中的 $UUID{n} 占位符
* 支持 $UUID2, $UUID5, $UUID10 等格式
* - n 超过 10 按 10 处理
* - 不区分大小写(可选)
* @param str 原始字符串
* @returns 替换后的字符串
* @example
* replaceUuidPlaceholder('/test/$UUID10') → '/test/a3k9m2x8p1'
* replaceUuidPlaceholder('/user/$UUID5/$UUID2') → '/user/abc12/de'
*/
const replaceUuidPlaceholder = (str: string): string => {
return str.replace(/\$UUID(\d+)/gi, (match, numStr) => {
const length = parseInt(numStr, 10);
return generateRandomString(length);
});
};
export const useTransformByRules = (frontMatter: Record<string, any>, fileInfo: FileInfo, rules: TransformRule[]) => {
// 转换函数:支持移除指定层级前缀后再添加新前缀,新增clear清空逻辑 + UUID占位符替换
for (const rule of rules) {
const { folderName, prefix, removeLevel, clear = false } = rule; // 解构时给clear默认值false
// 1. 检查文件路径是否匹配文件夹规则(精确匹配单个文件时,需完全一致)
if (!fileInfo.relativePath.startsWith(folderName)) {
continue;
}
// 处理日期:减去8小时抵消时区转换(原有逻辑不变)
if (frontMatter.date) {
const originalDate = new Date(frontMatter.date);
originalDate.setHours(originalDate.getHours() - 8);
frontMatter.date = originalDate;
}
// 2. 如果clear为true,直接清空permalink并返回(优先级最高)
if (clear) {
const newFrontMatter = { ...frontMatter, permalink: '' };
console.log(`匹配规则:${folderName}(clear=true)→ 清空permalink`);
return newFrontMatter;
}
/* // 3. 非clear模式:检查permalink是否存在(原有逻辑不变)
if (!frontMatter.permalink) {
continue;
}*/
// 4. 新增!替换prefix中的$UUID/$UUID10占位符
let normalizedPrefix = replaceUuidPlaceholder(prefix);
// 原有:标准化前缀(确保以 / 开头)
normalizedPrefix = normalizedPrefix.startsWith('/') ? normalizedPrefix : `/${normalizedPrefix}`;
if (prefix === ""){
normalizedPrefix = ""
}
let originalPermalink = frontMatter.permalink;
if (originalPermalink === null || originalPermalink === undefined) {
originalPermalink = "";
}
// 5. 核心调整:按 / 分组,比较第一个前缀是否一致(原有逻辑不变)
// 获取目标前缀的第一个分组(如 "/test/12345" → "test")
const targetFirstSegment = getFirstPathSegment(normalizedPrefix);
// 获取当前 permalink 的第一个分组(如 "/old/path" → "old")
const currentFirstSegment = getFirstPathSegment(originalPermalink);
// 若第一个分组相同,说明已包含目标前缀,无需处理(原有逻辑不变)
if (currentFirstSegment === targetFirstSegment) {
continue;
}
// 7. 处理 permalink:先移除指定层级,再添加新前缀(原有逻辑不变)
if (removeLevel !== undefined && removeLevel > 0) {
// 分割permalink(处理空字符串和开头的 /)
const parts = originalPermalink.split('/').filter(part => part);
// 确保移除的层级不超过实际存在的层级(removeLevel=99时,会移除所有层级,只剩根路径)
const actualRemoveLevel = Math.min(removeLevel, parts.length);
// 移除前N个层级,再重新拼接
const remainingParts = parts.slice(actualRemoveLevel);
originalPermalink = remainingParts.length > 0
? `/${remainingParts.join('/')}`
: ''; // 移除所有层级后,originalPermalink为 ""
}
// 8. 拼接新 permalink 并返回结果(原有逻辑不变,此时prefix已替换占位符)
const newPermalink = `${normalizedPrefix}${originalPermalink}`;
const newFrontMatter = { ...frontMatter, permalink: newPermalink };
console.log(`原permalink:${frontMatter.permalink} → 新permalink:${newPermalink}`);
return newFrontMatter;
}
// 没有匹配的规则,返回undefined(不修改数据)
return undefined;
}
📌3.配置一级前缀
警告
根据自己docs下的目录,填好上述代码,然后运行测试:
📌4.修改nav
编辑D:\vitepress-theme-teek-one-private\docs\.vitepress\ConfigHyde\Nav.ts
文件:
// nav导航栏配置
import { TeekIcon, VdoingIcon, SSLIcon, BlogIcon } from "./icon/NavIcon";
export const Nav = [
{ text: "🏡首页", link: "/" },
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/teek.svg" alt="" style="width: 16px; height: 16px;">
<span>Teek</span>
</div>
`,
link: '/teek/teek',
},
// 笔记
{
text: '📚知识库',
items: [
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/linux.svg" alt="" style="width: 16px; height: 16px;">
<span>运维</span>
</div>
`,
link: '/linux/linux',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/前端.svg" alt="" style="width: 16px; height: 16px;">
<span>前端</span>
</div>
`,
link: '/qianduan/qianduan',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/编程.svg" alt="" style="width: 16px; height: 16px;">
<span>编程</span>
</div>
`,
link: '/code/code',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/黑客.svg" alt="" style="width: 16px; height: 16px;">
<span>黑客</span>
</div>
`,
link: '/hacker/hacker',
},
],
},
// 专题
{
text: '🛠️专题',
items: [
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/博客.svg" alt="" style="width: 16px; height: 16px;">
<span>博客搭建</span>
</div>
`,
link: '/zhuanti/blog',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/前端demo.svg" alt="" style="width: 16px; height: 16px;">
<span>前端demo</span>
</div>
`,
link: '/zhuanti/qianduan-demo',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/Git.svg" alt="" style="width: 16px; height: 16px;">
<span>Git</span>
</div>
`,
link: '/zhuanti/git',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/面试.svg" alt="" style="width: 16px; height: 16px;">
<span>面试</span>
</div>
`,
link: '/zhuanti/mianshi',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/NAS.svg" alt="" style="width: 16px; height: 16px;">
<span>NAS</span>
</div>
`,
link: '/zhuanti/NAS',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/脚本.svg" alt="" style="width: 16px; height: 16px;">
<span>脚本</span>
</div>
`,
link: '/zhuanti/jiaoben',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/工具.svg" alt="" style="width: 16px; height: 16px;">
<span>工具</span>
</div>
`,
link: '/tools/tools',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/开源项目.svg" alt="" style="width: 16px; height: 16px;">
<span>开源项目</span>
</div>
`,
link: '/zhuanti/opensource',
},
],
},
// 生活
{
text: '🏓生活',
items: [
{
// 分组标题1
text: '娱乐',
items: [
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/相册.svg" alt="" style="width: 16px; height: 16px;">
<span>相册</span>
</div>
`,
link: '/yule/photo',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/电影.svg" alt="" style="width: 16px; height: 16px;">
<span>电影</span>
</div>
`,
link: '/yule/movie',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/音乐.svg" alt="" style="width: 16px; height: 16px;">
<span>音乐</span>
</div>
`,
link: '/yule/music',
},
],
},
{
// 分组标题2
text: '小屋',
items: [
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/精神小屋.svg" alt="" style="width: 16px; height: 16px;">
<span>精神小屋</span>
</div>
`,
link: '/love/love',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/时间管理.svg" alt="" style="width: 16px; height: 16px;">
<span>时间管理</span>
</div>
`,
link: '/love/time-plan',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/文案.svg" alt="" style="width: 16px; height: 16px;">
<span>情感文案</span>
</div>
`,
link: '/love/wenan',
},
// { text: "💖情侣空间", link: "https://fxj.onedayxyy.cn/" },
],
},
],
},
// 兴趣
{
text: '🎨兴趣',
items: [
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/旅行.svg" alt="" style="width: 16px; height: 16px;">
<span>旅行</span>
</div>
`,
link: '/xingqu/travel',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/读书.svg" alt="" style="width: 16px; height: 16px;">
<span>读书</span>
</div>
`,
link: '/xingqu/reading',
},
],
},
// 索引
{
text: '👏索引',
items: [
{ text: '📃分类页', link: '/categories' },
{ text: '🔖标签页', link: '/tags' },
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/归档.svg" alt="" style="width: 16px; height: 16px;">
<span>归档页</span>
</div>
`,
link: '/archives',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/清单.svg" alt="" style="width: 16px; height: 16px;">
<span>清单页</span>
</div>
`,
link: '/articleOverview',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/登录.svg" alt="" style="width: 16px; height: 16px;">
<span>登录页</span>
</div>
`,
link: '/login',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/风险提示.svg" alt="" style="width: 16px; height: 16px;">
<span>风险链接提示页</span>
</div>
`,
link: '/risk-link?target=https://onedayxyy.cn/',
},
],
},
// 关于
{
text: '🍷关于',
items: [
{ text: '👋关于我', link: '/about/me' },
{ text: '🎉关于本站', link: '/about/website' },
{ text: '🌐网站导航', link: '/about/websites' },
{ text: "👂留言区", link: "/about/liuyanqu" },
{ text: "💡思考", link: "/about/thinking" },
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/时间轴.svg" alt="" style="width: 16px; height: 16px;">
<span>时间轴</span>
</div>
`,
link: 'https://one.onedayxyy.cn/',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/网站统计.svg" alt="" style="width: 16px; height: 16px;">
<span>网站统计</span>
</div>
`,
link: 'https://umami.onedayxyy.cn/share/DzS4g85V8JkxsNRk/onedayxyy.cn',
},
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="/img/nav/站点监控.svg" alt="" style="width: 16px; height: 16px;">
<span>站点监控</span>
</div>
`,
link: 'https://status.onedayxyy.cn/status/monitor',
},
],
},
]
以上配置完成后,验证。
3、验证
2025年8月27日测试。
FAQ
✅5级目录时permalink失效问题
📌背景
📌结论
当前临时解决方法:把这个五级目录改为4级。
解决了:
威威提的:
✅initItems配置决定vp 2种不同风格侧边栏
📌背景
不如之前那个风格:。。。
更新后风格:
之前风格:
📌回答:群主
📌回答:群主
📌回答:另个大佬
你把这个内置进去吧 比你那个好多了 插件加入到pack "vitepress-sidebar": "^1.31.1", 规则 这个 import { generateSidebar } from 'vitepress-sidebar';
下面具体的 只要你文件下的 自动引入 sidebar: generateSidebar([
{
documentRootPath: '/posts',
scanStartPath: 'blog',
resolvePath: '/blog/',
collapseDepth: 2, // 折叠组 3 级菜单
sortMenusByName: true, // 按名称对菜单项中的项目进行排序
useTitleFromFileHeading: true, // 从 h1 标签中获取菜单标题
useFolderTitleFromIndexFile: true, // 使用当前文件夹的 index.md 文件中的信息来获取菜单名称
sortMenusOrderNumericallyFromLink: true, // 如果菜单名称以数字开头,则按数字而不是名称排序
},
📌曲线救国
📌结论
配置方法:
(1)注释下面这个命令就好:
编辑docs\.vitepress\config.ts
文件:
// initItems: false, //这条命令注释后,才会让文档和目录的样式保持一致
(2)再次运行观察效果
✅其它格式化文档permainlk的方法
📌01.用vscode的正则
^coverImg:.*$\n
📌02.用python脚本处理docs下各一级目录
python md_permalink_processor.py
具体代码:(次脚本测试成功)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Markdown文件permalink字段批量处理脚本
递归处理指定目录下的所有.md文件,修改其中的permalink字段
"""
import os
import re
import uuid
import argparse
from pathlib import Path
from typing import Tuple, Dict, Any
import yaml
class PermalinkProcessor:
def __init__(self, prefix_char: str = 'p'):
"""
初始化处理器
Args:
prefix_char (str): 一级前缀字符,默认为'p'
"""
self.prefix_char = prefix_char.strip('/') # 移除可能的斜杠
self.success_count = 0
self.error_count = 0
self.processed_files = []
self.error_files = []
def extract_yaml_frontmatter(self, content: str) -> Tuple[Dict[str, Any], str]:
"""
从markdown内容中提取YAML front matter
Args:
content (str): markdown文件内容
Returns:
Tuple[Dict[str, Any], str]: (front matter字典, 剩余内容)
"""
# 匹配YAML front matter的正则表达式
yaml_pattern = r'^---\s*\n(.*?)\n---\s*\n(.*)'
match = re.match(yaml_pattern, content, re.DOTALL)
if match:
yaml_content = match.group(1)
remaining_content = match.group(2)
try:
# 解析YAML内容
front_matter = yaml.safe_load(yaml_content) or {}
return front_matter, remaining_content
except yaml.YAMLError as e:
print(f"YAML解析错误: {e}")
return {}, content
else:
# 没有找到YAML front matter
return {}, content
def generate_new_permalink(self, old_permalink: str = None) -> str:
"""
生成新的permalink
Args:
old_permalink (str): 原有的permalink
Returns:
str: 新的permalink
"""
if old_permalink and old_permalink.strip():
# 如果已存在permalink,提取最后一个斜杠后的内容
old_suffix = old_permalink.split('/')[-1] if '/' in old_permalink else old_permalink
new_permalink = f"/{self.prefix_char}/{old_suffix}"
else:
# 如果permalink为空,生成新的UUID
random_uid = str(uuid.uuid4()).replace('-', '')[:8] # 使用8位UUID
new_permalink = f"/{self.prefix_char}/{random_uid}"
return new_permalink
def update_permalink_in_content(self, content: str) -> str:
"""
更新内容中的permalink字段
Args:
content (str): 原始文件内容
Returns:
str: 更新后的文件内容
"""
front_matter, remaining_content = self.extract_yaml_frontmatter(content)
if not front_matter and not content.startswith('---'):
# 文件没有YAML front matter,需要创建
new_permalink = self.generate_new_permalink()
new_front_matter = {'permalink': new_permalink}
# 创建新的YAML front matter
yaml_str = yaml.dump(new_front_matter, default_flow_style=False, allow_unicode=True)
new_content = f"---\n{yaml_str}---\n{content}"
return new_content
# 处理现有的front matter
old_permalink = front_matter.get('permalink', '')
new_permalink = self.generate_new_permalink(old_permalink)
front_matter['permalink'] = new_permalink
# 重新构建文件内容
yaml_str = yaml.dump(front_matter, default_flow_style=False, allow_unicode=True)
new_content = f"---\n{yaml_str}---\n{remaining_content}"
return new_content
def process_md_file(self, file_path: Path) -> bool:
"""
处理单个markdown文件
Args:
file_path (Path): 文件路径
Returns:
bool: 处理是否成功
"""
try:
# 读取文件内容
with open(file_path, 'r', encoding='utf-8') as f:
original_content = f.read()
# 更新permalink
updated_content = self.update_permalink_in_content(original_content)
# 写回文件
with open(file_path, 'w', encoding='utf-8') as f:
f.write(updated_content)
self.success_count += 1
self.processed_files.append(str(file_path))
print(f"✓ 成功处理: {file_path}")
return True
except Exception as e:
self.error_count += 1
self.error_files.append((str(file_path), str(e)))
print(f"✗ 处理失败: {file_path} - 错误: {e}")
return False
def process_directory(self, directory_path: str) -> None:
"""
递归处理目录中的所有.md文件
Args:
directory_path (str): 目录路径
"""
directory = Path(directory_path)
if not directory.exists():
print(f"错误: 目录不存在 - {directory_path}")
return
if not directory.is_dir():
print(f"错误: 路径不是目录 - {directory_path}")
return
print(f"开始处理目录: {directory_path}")
print(f"前缀字符: {self.prefix_char}")
print("-" * 50)
# 递归查找所有.md文件
md_files = list(directory.rglob("*.md"))
if not md_files:
print("未找到任何.md文件")
return
print(f"找到 {len(md_files)} 个.md文件")
print("-" * 50)
# 处理每个文件
for md_file in md_files:
self.process_md_file(md_file)
# 输出统计结果
self.print_summary()
def print_summary(self) -> None:
"""打印处理结果统计"""
print("\n" + "=" * 50)
print("处理结果统计")
print("=" * 50)
print(f"成功处理文件数: {self.success_count}")
print(f"失败处理文件数: {self.error_count}")
print(f"总文件数: {self.success_count + self.error_count}")
if self.error_files:
print("\n失败文件详情:")
for file_path, error in self.error_files:
print(f" - {file_path}: {error}")
def main():
"""主函数"""
parser = argparse.ArgumentParser(
description="递归处理目录中所有.md文件的permalink字段",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
python script.py -d "D:\\我的开源项目\\vitepress-theme-teek-one-public\\docs\\20.文档" -p "doc"
python script.py --directory "/path/to/docs" --prefix "blog"
python script.py -d "./docs" (使用默认前缀 'p')
"""
)
parser.add_argument(
'-d', '--directory',
type=str,
default=r"D:\我的开源项目\vitepress-theme-teek-one-public\docs\10.Teek",
# help='要处理的目录路径 (默认: D:\\我的开源项目\\vitepress-theme-teek-one-public\\docs\\10.Teek)'
help='要处理的目录路径 (默认: D:\\我的开源项目\\vitepress-theme-teek-one-public\\docs\\20.文档)'
)
parser.add_argument(
'-p', '--prefix',
type=str,
default='pages',
help='一级前缀字符 (默认: p)'
)
parser.add_argument(
'--dry-run',
action='store_true',
help='仅预览要处理的文件,不实际修改'
)
args = parser.parse_args()
# 验证参数
if not args.prefix.strip():
print("错误: 前缀字符不能为空")
return
# 如果是预览模式
if args.dry_run:
directory = Path(args.directory)
if directory.exists():
md_files = list(directory.rglob("*.md"))
print(f"预览模式 - 找到 {len(md_files)} 个.md文件:")
for md_file in md_files:
print(f" - {md_file}")
else:
print(f"错误: 目录不存在 - {args.directory}")
return
# 创建处理器并开始处理
processor = PermalinkProcessor(prefix_char=args.prefix)
processor.process_directory(args.directory)
if __name__ == "__main__":
main()
结束。
(这个teek早修复了)文件名称或路径改了后,不算子监控数据也会清0
作者回复:(2025年8月28日)