第七章:fuwari - Mizuki 主题魔改
本章记录对 Mizuki 主题的定制化修改,包括功能增强和问题修复。
7.1 修复 Umami 自搭建实例支持
7.1.1 问题说明
Mizuki 主题自带的 umami-share.js 文件存在以下问题:
- 仅支持 Umami Cloud:原代码硬编码了
/v1/websites/ API 路径和 x-umami-api-key 认证方式,只适用于 Umami Cloud 官方服务 - 不支持自搭建实例:自搭建的 Umami 实例使用不同的 API 路径(
/api/websites/)和认证方式(Authorization: Bearer),导致配置后出现 404 或 401 错误 - 无法自动识别实例类型:代码无法自动判断是使用 Cloud 还是自搭建实例,需要手动修改代码
7.1.2 解决方案
第一步:获取 Bearer Token
- 登录你的 Umami 管理后台
- 打开浏览器开发者工具(F12)→ Network 标签页
- 刷新页面,找到对
/api/auth/verify 的请求 - 在请求头中找到
Authorization: Bearer <token> - 复制
<token> 部分(不含 "Bearer " 前缀)
第二步:配置 API Key
在 src/config.ts 中更新 umamiConfig:
1 2 3 4 5 6 7 8
| export const umamiConfig = { enabled: true, apiKey: import.meta.env.UMAMI_API_KEY || "你的Bearer Token", baseUrl: "https://umami.prorise666.site/api", scripts: ` <script defer src="https://umami.prorise666.site/script.js" data-website-id="你的网站ID"></script> `.trim(), } as const;
|
第三步:替换 umami-share.js 文件
将以下代码完整复制,替换 public/js/umami-share.js 文件的所有内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
| ((global) => { const cacheKey = "umami-share-cache"; const cacheTTL = 3600_000;
function isSelfHostedInstance(baseUrl) { const isCloud = baseUrl.includes('api.umami.is'); return !isCloud && (baseUrl.endsWith('/api') || (baseUrl.includes('/api') && !baseUrl.includes('/v1'))); }
async function fetchWebsiteStats(baseUrl, apiKey, websiteId) { const cached = localStorage.getItem(cacheKey); if (cached) { try { const parsed = JSON.parse(cached); if (Date.now() - parsed.timestamp < cacheTTL) { return parsed.value; } } catch { localStorage.removeItem(cacheKey); } }
const currentTimestamp = Date.now(); const isSelfHosted = isSelfHostedInstance(baseUrl); const apiPath = isSelfHosted ? `/websites/${websiteId}/stats` : `/v1/websites/${websiteId}/stats`; const statsUrl = `${baseUrl}${apiPath}?startAt=0&endAt=${currentTimestamp}`;
const authValue = isSelfHosted ? (apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`) : apiKey; const headers = isSelfHosted ? { "Authorization": authValue } : { "x-umami-api-key": apiKey };
const res = await fetch(statsUrl, { headers });
if (!res.ok) { const errorText = await res.text().catch(() => ''); throw new Error(`获取网站统计数据失败: HTTP ${res.status} ${res.statusText}${errorText ? ' - ' + errorText : ''}`); }
const stats = await res.json();
localStorage.setItem( cacheKey, JSON.stringify({ timestamp: Date.now(), value: stats }), );
return stats; }
async function fetchPageStats( baseUrl, apiKey, websiteId, urlPath, startAt = 0, endAt = Date.now(), ) { const isSelfHosted = isSelfHostedInstance(baseUrl); const apiPath = isSelfHosted ? `/websites/${websiteId}/stats` : `/v1/websites/${websiteId}/stats`; const statsUrl = `${baseUrl}${apiPath}?startAt=${startAt}&endAt=${endAt}&path=${encodeURIComponent(urlPath)}`;
const authValue = isSelfHosted ? (apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`) : apiKey; const headers = isSelfHosted ? { "Authorization": authValue } : { "x-umami-api-key": apiKey };
const res = await fetch(statsUrl, { headers });
if (!res.ok) { const errorText = await res.text().catch(() => ''); throw new Error(`获取页面统计数据失败: HTTP ${res.status} ${res.statusText}${errorText ? ' - ' + errorText : ''}`); }
return await res.json(); }
global.getUmamiWebsiteStats = async (baseUrl, apiKey, websiteId) => { try { return await fetchWebsiteStats(baseUrl, apiKey, websiteId); } catch (err) { throw new Error(`获取Umami统计数据失败: ${err.message}`); } };
global.getUmamiPageStats = async ( baseUrl, apiKey, websiteId, urlPath, startAt, endAt, ) => { try { return await fetchPageStats( baseUrl, apiKey, websiteId, urlPath, startAt, endAt, ); } catch (err) { throw new Error(`获取Umami页面统计数据失败: ${err.message}`); } };
global.clearUmamiShareCache = () => { localStorage.removeItem(cacheKey); }; })(window);
|
修复后的功能:
- ✅ 自动识别 Umami Cloud 和自搭建实例
- ✅ 自动使用正确的 API 路径(
/api/websites/ 或 /api/v1/websites/) - ✅ 自动使用正确的认证方式(
Authorization: Bearer 或 x-umami-api-key) - ✅ 支持 API Key 已包含 "Bearer " 前缀的情况
- ✅ 改进的错误提示,便于调试
7.2 配置默认主题(亮色/暗色)
7.2.1 功能说明
Mizuki 主题默认使用亮色主题,但可以通过配置项设置默认主题为暗色或亮色。当用户首次访问时,会使用配置的默认主题;如果用户之前手动切换过主题,则优先使用用户的选择。
7.2.2 修改步骤
第一步:修改类型定义文件
文件路径:src/types/config.ts
定位到 themeColor 配置项(约第 55-58 行),在其后添加:
1 2 3 4 5 6 7
| themeColor: { hue: number; fixed: boolean; };
defaultTheme?: "light" | "dark";
|
第二步:修改配置文件
文件路径:src/config.ts
定位到 themeColor 配置项(约第 32-35 行),在其后添加:
1 2 3 4 5 6
| themeColor: { hue: 360, fixed: false, },
defaultTheme: "dark",
|
第三步:修改布局文件
文件路径:src/layouts/Layout.astro
- 定位到约第 240 行,找到:
1
| <script is:inline define:vars={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, configHue}}>
|
修改为:
1
| <script is:inline define:vars={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, configHue, defaultTheme: siteConfig.defaultTheme || DEFAULT_THEME}}>
|
- 定位到约第 242 行,找到:
1
| const theme = localStorage.getItem('theme') || DEFAULT_THEME;
|
修改为:
1
| const theme = localStorage.getItem('theme') || defaultTheme || DEFAULT_THEME;
|
- 定位到约第 881 行,找到:
1
| const storedTheme = localStorage.getItem('theme') || DEFAULT_THEME;
|
修改为:
1
| const storedTheme = localStorage.getItem('theme') || (siteConfig.defaultTheme || DEFAULT_THEME);
|
第四步:修改工具函数
文件路径:src/utils/setting-utils.ts
定位到约第 143-145 行,找到:
1 2 3
| export function getStoredTheme(): LIGHT_DARK_MODE { return (localStorage.getItem("theme") as LIGHT_DARK_MODE) || DEFAULT_THEME; }
|
修改为:
1 2 3 4 5 6
| export function getStoredTheme(): LIGHT_DARK_MODE { const stored = localStorage.getItem("theme") as LIGHT_DARK_MODE; if (stored) return stored; return (siteConfig.defaultTheme as LIGHT_DARK_MODE) || DEFAULT_THEME; }
|
7.2.3 配置说明
在 src/config.ts 中,你可以这样配置:
1 2 3 4 5 6 7
| export const siteConfig: SiteConfig = { defaultTheme: "dark", defaultTheme: "light", }
|
优先级说明:
- 用户手动切换的主题(存储在 localStorage)
- 配置中的
defaultTheme - 系统默认值(亮色主题)
7.3 将 About 页面改造为 GitHub README 风格
7.3.1 功能说明
Mizuki 主题的 About 页面默认是简单的 Markdown 内容展示。本魔改将 About 页面改造为类似 GitHub README 的风格,支持:
- 动态打字效果
- 社交平台徽章展示
- 技能图标展示
- GitHub 活动统计图表
- 全宽图片和居中对齐
- 响应式布局
7.3.2 修改步骤
第一步:替换 about.md 内容
文件路径:src/content/spec/about.md
将以下模板复制到 about.md,并根据提示填写你的个人信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| <!-- 网站介绍(可选) --> This website is built with the **Astro** framework using the [Mizuki](https://github.com/matsuzaka-yuki/mizuki) theme.
::github{repo="matsuzaka-yuki/Mizuki"}
<!-- 顶部装饰线 --> <div align="center"> <img src="https://capsule-render.vercel.app/api?type=soft&color=gradient&customColorList=12&height=3" width="100%" /> </div>
<!-- 个人介绍横幅 --> <div align="center"> <!-- 修改:将 YOUR_USERNAME 替换为你的 GitHub 用户名 --> <img src="https://capsule-render.vercel.app/api?type=waving&color=0:000000,25:282424,50:504949,75:786d6d,100:9e9494&height=250§ion=header&text=YOUR_USERNAME&fontSize=90&fontAlignY=35&animation=fadeIn&fontColor=white&desc=你的个性签名&descAlignY=55" />
<!-- 修改:将 YOUR_NAME 替换为你的名字 --> # 👋 Hello,here is YOUR_NAME
<!-- 修改:自定义打字效果内容,lines= 后面用分号分隔多行 --> [](https://git.io/typing-svg)
<!-- 社交平台徽章 --> <p align="center"> <!-- 修改:添加或删除你需要的社交平台链接 --> <a href="https://github.com/YOUR_USERNAME" style="text-decoration: none;"> <img src="https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white" alt="GitHub" style="margin: 2px;"/> </a> <a href="你的链接" style="text-decoration: none;"> <img src="https://img.shields.io/badge/平台名称-颜色代码?style=for-the-badge&logo=logo名称&logoColor=white" alt="平台名称" style="margin: 2px;"/> </a> <!-- 更多徽章... --> </p>
<!-- 统计信息 --> <p> <!-- 修改:将 YOUR_USERNAME 替换为你的 GitHub 用户名 --> <img src="https://komarev.com/ghpvc/?username=YOUR_USERNAME&style=for-the-badge&color=blueviolet" alt="访问计数器" /> <img src="https://img.shields.io/github/followers/YOUR_USERNAME?style=for-the-badge&color=FF5F6D&labelColor=141321" alt="关注者" /> <img src="https://img.shields.io/github/stars/YOUR_USERNAME?style=for-the-badge&color=FFC371&labelColor=141321" alt="星标" /> </p> <p> <img src="https://count.getloli.com/get/@YOUR_USERNAME?theme=rule34" alt="访问计数器" /> </p> </div>
<!-- 分隔线 --> <img width="100%" src="https://capsule-render.vercel.app/api?type=rect&color=gradient&height=2" />
<!-- 技能与工具 --> <h2 align="center"> <img src="https://media2.giphy.com/media/QssGEmpkyEOhBCb7e1/giphy.gif?cid=ecf05e47a0n3gi1bfqntqmob8g9aid1oyj2wr3ds3mg700bl&rid=giphy.gif" width="30px" height="30px" style="vertical-align: middle; position: relative; top: -2px;"/> 技能与工具 </h2> <div align="center"> <!-- 修改:skillicons.dev 支持的技术栈图标,i= 后面用逗号分隔技术名称 --> <!-- 访问 https://skillicons.dev/ 查看所有支持的图标 --> <img src="https://skillicons.dev/icons?i=html,css,javascript,typescript,vue,react,nodejs&perline=15" /> <img src="https://skillicons.dev/icons?i=python,java,go,rust&perline=15" /> <img src="https://skillicons.dev/icons?i=mysql,mongodb,redis,postgres&perline=15" /> <img src="https://skillicons.dev/icons?i=linux,docker,nginx,git,github&perline=15" /> <br/> <!-- 修改:自定义你的工具描述 --> <p>🚀 <b>你的工具描述:</b> 工具1 | 工具2 | 工具3</p> </div>
<!-- 分隔线 --> <div align="center"> <img src="https://capsule-render.vercel.app/api?type=soft&color=gradient&customColorList=12&height=3" width="100%" /> </div>
<!-- GitHub 活动 --> <h2 align="center"> <img src="https://i.imgur.com/dBaSKWF.gif" height="25px" width="25px" style="vertical-align: middle; position: relative; top: -2px;"/> GitHub 活动 <img src="https://i.imgur.com/dBaSKWF.gif" height="25px" width="25px" style="vertical-align: middle; position: relative; top: -2px;"/> </h2>
<div align="center"> <!-- 修改:将 YOUR_USERNAME 替换为你的 GitHub 用户名 --> <!-- GitHub 贡献图(支持暗色/亮色主题自动切换) --> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/YOUR_USERNAME/YOUR_USERNAME/output/github-contribution-grid-snake-dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/YOUR_USERNAME/YOUR_USERNAME/output/github-contribution-grid-snake.svg"> <img alt="github contribution grid snake animation" src="https://raw.githubusercontent.com/YOUR_USERNAME/YOUR_USERNAME/output/github-contribution-grid-snake.svg" width="100%"> </picture> <!-- GitHub 统计卡片 --> <img src="https://github-profile-summary-cards.vercel.app/api/cards/profile-details?username=YOUR_USERNAME&theme=radical" width="100%" /> </div>
<!-- 自定义内容区域 --> <div align="center"> <!-- 修改:可以添加自定义的打字效果、名言等 --> <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=600&size=22&pause=1000&color=FF9190¢er=true&vCenter=true&random=false&width=600&height=80&lines=第一行;第二行;第三行" alt="自定义内容" /> </div>
<!-- 结尾 --> <h3 align="center"> <img src="https://emojis.slackmojis.com/emojis/images/1531849430/4246/blob-sunglasses.gif?1531849430" width="30px" height="30px" style="vertical-align: middle; position: relative; top: -2px;"/> 感谢访问我的个人主页! <img src="https://emojis.slackmojis.com/emojis/images/1531849430/4246/blob-sunglasses.gif?1531849430" width="30px" height="30px" style="vertical-align: middle; position: relative; top: -2px;"/> </h3>
<p align="center"> <i>你的个性签名✨</i> </p>
|
模板使用说明:
- YOUR_USERNAME:替换为你的 GitHub 用户名
- YOUR_NAME:替换为你的名字或昵称
- 社交平台徽章:访问 shields.io 生成自定义徽章
- 技能图标:访问 skillicons.dev 查看所有支持的图标
- GitHub 活动图:需要配置 GitHub Actions 自动生成,参考 github-readme-streak-stats
第二步:修改 about.astro 文件
文件路径:src/pages/about.astro
修改原因: 需要为 Markdown 组件添加 about-page 类名,以便应用专门的样式。
定位到约第 36 行,找到:
1 2 3
| <Markdown class="mt-2"> <Content /> </Markdown>
|
修改为:
1 2 3
| <Markdown class="mt-2 about-page"> <Content /> </Markdown>
|
修改说明:
- 添加
about-page 类名,用于在 CSS 中针对 About 页面应用特殊样式 - 这样可以确保样式只影响 About 页面,不会影响其他页面的 Markdown 内容
第三步:添加 CSS 样式
文件路径:src/styles/markdown.css
修改原因: GitHub README 风格的页面需要特殊的样式支持,包括:
- 居中对齐的元素(div, p, h2, h3)
- 全宽图片的处理
- 链接内图片的悬停效果
- 特殊图片的间距控制
在文件末尾(约第 150 行后)添加以下样式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| .custom-md.about-page { div[align="center"], p[align="center"], h2[align="center"], h3[align="center"] { @apply text-center; } div[align="center"] { @apply flex flex-col items-center justify-center; p { @apply text-center; } a { @apply inline-block; } } img { @apply max-w-full h-auto; display: inline-block; &[width="100%"] { width: 100% !important; max-width: 100% !important; } } a img { @apply transition-transform hover:scale-105; margin: 2px; } img[src*="capsule-render"] { @apply my-4; } img[src*="skillicons"] { @apply my-2; } }
|
样式说明:
- 居中对齐样式:支持
align="center" 属性的 div、p、h2、h3 元素自动居中对齐 - 图片样式:
- 所有图片默认响应式(
max-w-full h-auto) width="100%" 的图片会强制全宽显示
- 徽章悬停效果:链接内的图片(徽章)在悬停时会轻微放大
- 特殊图片间距:
capsule-render 图片上下间距为 my-4skillicons 图片上下间距为 my-2
7.3.3 效果预览
完成以上修改后,你的 About 页面将具备:
- ✅ GitHub README 风格的布局
- ✅ 动态打字效果
- ✅ 社交平台徽章展示
- ✅ 技能图标展示
- ✅ GitHub 活动统计图表
- ✅ 响应式设计,适配各种设备
7.3.4 常见问题
Q: 为什么要在 Markdown 组件上添加 about-page 类?
A: 这样可以确保样式只影响 About 页面,不会影响其他页面的 Markdown 内容。使用 .custom-md.about-page 选择器可以精确控制样式作用范围。
Q: 如何自定义徽章样式?
A: 访问 shields.io 可以生成自定义徽章,支持自定义颜色、图标、文字等。
Q: GitHub 活动图不显示怎么办?
A: 需要配置 GitHub Actions 自动生成活动图。可以参考 github-readme-streak-stats 的文档进行配置。
Q: 如何添加更多技能图标?
A: 访问 skillicons.dev 查看所有支持的图标,然后在 i= 参数中添加技术名称,用逗号分隔。