first commit
This commit is contained in:
@@ -0,0 +1,391 @@
|
||||
# 主题五大改进实施计划
|
||||
|
||||
## 任务一:Footer 配置组缺失
|
||||
|
||||
### 问题
|
||||
[Footer.astro](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/src/components/Footer.astro) 引用了 `theme.config?.footer?.footer_copyright`、`footer_icp`、`footer_socials`、`footer_show_powered`、`footer_show_theme`、`footer_custom_html` 等配置项,但 [settings.yaml](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/settings.yaml) 中没有定义 footer 配置组,用户在后台无法设置页脚内容。
|
||||
|
||||
### 修改文件
|
||||
- `settings.yaml` — 新增 footer 配置组
|
||||
|
||||
### 具体改动
|
||||
在 settings.yaml 的 `comment` 配置组之后新增 `footer` 配置组:
|
||||
|
||||
```yaml
|
||||
- group: footer
|
||||
label: 页脚
|
||||
formSchema:
|
||||
- $formkit: text
|
||||
name: footer_copyright
|
||||
label: 版权信息(留空则使用默认格式 © 年份 站点标题)
|
||||
- $formkit: text
|
||||
name: footer_icp
|
||||
label: ICP 备案号
|
||||
- $formkit: repeater
|
||||
name: footer_socials
|
||||
label: 社交链接
|
||||
children:
|
||||
- $formkit: text
|
||||
name: platform
|
||||
label: 平台名称
|
||||
- $formkit: text
|
||||
name: icon
|
||||
label: 图标类名(如 fa-brands fa-github)
|
||||
- $formkit: url
|
||||
name: url
|
||||
label: 链接地址
|
||||
- $formkit: switch
|
||||
name: footer_show_powered
|
||||
label: 显示 "Powered by Halo"
|
||||
value: true
|
||||
- $formkit: switch
|
||||
name: footer_show_theme
|
||||
label: 显示主题版本
|
||||
value: true
|
||||
- $formkit: code
|
||||
name: footer_custom_html
|
||||
label: 自定义 HTML(统计代码等)
|
||||
language: html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务二:SEO Meta 标签严重缺失
|
||||
|
||||
### 问题
|
||||
[Layout.astro](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/src/layouts/Layout.astro) 的 `<head>` 中只有全站 description,缺少 og 标签、canonical URL、RSS 链接等。文章页应使用文章摘要作为 description。
|
||||
|
||||
### 修改文件
|
||||
- `src/layouts/Layout.astro` — 在 `<head>` 中添加 SEO meta 标签
|
||||
- `src/pages/post.astro` — 在 head slot 中添加文章页专属 SEO 标签
|
||||
- `src/pages/page.astro` — 在 head slot 中添加页面专属 SEO 标签
|
||||
|
||||
### 具体改动
|
||||
|
||||
#### Layout.astro — 全局 SEO 标签
|
||||
在现有 `<meta name="description">` 之后添加:
|
||||
|
||||
```html
|
||||
<meta name="keywords" th:content="${site.seo?.keywords}" />
|
||||
<link rel="canonical" th:href="${site.url}" />
|
||||
<link rel="alternate" type="application/rss+xml" th:title="${site.title}" th:href="@{/feed.xml}" />
|
||||
<meta property="og:site_name" th:content="${site.title}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" th:content="${site.url}" />
|
||||
<meta property="og:title" th:content="${site.title}" />
|
||||
<meta property="og:description" th:content="${site.seo?.description}" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
```
|
||||
|
||||
#### post.astro — 文章页专属 SEO 标签
|
||||
在 `<Fragment slot="head">` 中,`<title>` 之后添加:
|
||||
|
||||
```html
|
||||
<meta name="description" th:content="${post.spec.excerpt ?: site.seo?.description}" />
|
||||
<link rel="canonical" th:href="${post.status.permalink}" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:title" th:content="${post.spec.title}" />
|
||||
<meta property="og:description" th:content="${post.spec.excerpt ?: site.seo?.description}" />
|
||||
<meta property="og:url" th:href="@{${post.status.permalink}}" />
|
||||
<meta property="og:image" th:if="${post.spec.cover}" th:content="${post.spec.cover}" />
|
||||
<meta property="article:published_time" th:content="${post.spec.publishTime}" />
|
||||
<meta name="twitter:card" content="summary_large_image" th:if="${post.spec.cover}" />
|
||||
<meta name="twitter:card" content="summary" th:unless="${post.spec.cover}" />
|
||||
<meta name="twitter:title" th:content="${post.spec.title}" />
|
||||
<meta name="twitter:description" th:content="${post.spec.excerpt ?: site.seo?.description}" />
|
||||
<meta name="twitter:image" th:if="${post.spec.cover}" th:content="${post.spec.cover}" />
|
||||
```
|
||||
|
||||
#### page.astro — 自定义页面专属 SEO 标签
|
||||
在 `<Fragment slot="head">` 中,`<title>` 之后添加:
|
||||
|
||||
```html
|
||||
<meta name="description" th:content="${singlePage.spec.excerpt ?: site.seo?.description}" />
|
||||
<link rel="canonical" th:href="${singlePage.status.permalink}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" th:content="${singlePage.spec.title}" />
|
||||
<meta property="og:description" th:content="${singlePage.spec.excerpt ?: site.seo?.description}" />
|
||||
<meta property="og:url" th:href="@{${singlePage.status.permalink}}" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务三:导航当前页面无高亮
|
||||
|
||||
### 问题
|
||||
[Navbar.astro](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/src/components/Navbar.astro) 中 CSS 已定义 `.wi-navbar__link--active` 样式,但模板中没有为当前页面的导航链接添加 active 类名。
|
||||
|
||||
### 修改文件
|
||||
- `src/components/Navbar.astro`
|
||||
|
||||
### 具体改动
|
||||
在导航链接 `<a>` 标签上添加 `th:classappend` 条件判断,通过比较当前请求路径与菜单项链接来判断是否高亮:
|
||||
|
||||
```html
|
||||
<a
|
||||
th:each="menuItem : ${menu.menuItems}"
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:target="${menuItem.spec.target}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
class="wi-navbar__link"
|
||||
th:classappend="${#strings.equals(#request.requestURI, menuItem.status.href)} ? 'wi-navbar__link--active'"
|
||||
>
|
||||
</a>
|
||||
```
|
||||
|
||||
同时需要在 MobileMenu.astro 中也添加同样的高亮逻辑。MobileMenu.astro 第 16-22 行的导航链接结构与 Navbar 相同:
|
||||
|
||||
```html
|
||||
<a
|
||||
th:each="menuItem : ${menu.menuItems}"
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:target="${menuItem.spec.target}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
class="wi-mobile-menu__link"
|
||||
th:classappend="${#strings.equals(#request.requestURI, menuItem.status.href)} ? 'wi-mobile-menu__link--active'"
|
||||
></a>
|
||||
```
|
||||
|
||||
并在 MobileMenu.astro 的 `<style>` 中添加 active 样式:
|
||||
|
||||
```css
|
||||
.wi-mobile-menu__link--active {
|
||||
color: var(--accent);
|
||||
background: var(--bg-raised);
|
||||
font-weight: 600;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务四:移动端文章目录不可用(浮动目录按钮)
|
||||
|
||||
### 问题
|
||||
[post.astro](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/src/pages/post.astro) 中 TOC 仅在 `min-width: 1280px` 时显示,移动端没有任何替代方案。
|
||||
|
||||
### 修改文件
|
||||
- `src/pages/post.astro`
|
||||
|
||||
### 具体改动
|
||||
|
||||
#### 1. 添加移动端浮动 TOC 按钮
|
||||
在文章 `<article>` 内添加一个浮动按钮,仅在 `max-width: 1279px` 时显示:
|
||||
|
||||
```html
|
||||
<button
|
||||
th:if="${theme.config?.article?.article_show_toc ?: true}"
|
||||
class="wi-toc-fab"
|
||||
id="wi-toc-fab"
|
||||
type="button"
|
||||
aria-label="打开目录"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/></svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
#### 2. 添加移动端 TOC 抽屉面板
|
||||
在浮动按钮之后添加一个从底部滑出的抽屉面板:
|
||||
|
||||
```html
|
||||
<div
|
||||
th:if="${theme.config?.article?.article_show_toc ?: true}"
|
||||
class="wi-toc-drawer"
|
||||
id="wi-toc-drawer"
|
||||
>
|
||||
<div class="wi-toc-drawer__overlay"></div>
|
||||
<div class="wi-toc-drawer__panel">
|
||||
<div class="wi-toc-drawer__header">
|
||||
<span class="wi-toc-drawer__title">目录</span>
|
||||
<button class="wi-toc-drawer__close" type="button" aria-label="关闭目录">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<nav class="wi-toc-drawer__nav" id="wi-toc-drawer-nav"></nav>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 3. 添加 CSS 样式
|
||||
```css
|
||||
.wi-toc-fab {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 1.5rem;
|
||||
right: 1.5rem;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--rule);
|
||||
background: var(--bg-raised);
|
||||
color: var(--ink-2);
|
||||
cursor: pointer;
|
||||
z-index: 20;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: var(--shadow-md);
|
||||
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.wi-toc-fab:hover {
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
@media (max-width: 1279px) {
|
||||
.wi-toc-fab {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
.wi-toc-drawer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wi-toc-drawer--open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wi-toc-drawer__overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.wi-toc-drawer__panel {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-height: 60vh;
|
||||
background: var(--bg);
|
||||
border-top: 1px solid var(--rule);
|
||||
border-radius: 16px 16px 0 0;
|
||||
padding: 1.25rem;
|
||||
z-index: 51;
|
||||
overflow-y: auto;
|
||||
transform: translateY(0);
|
||||
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.wi-toc-drawer__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--rule);
|
||||
}
|
||||
|
||||
.wi-toc-drawer__title {
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.wi-toc-drawer__close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--ink-3);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wi-toc-drawer__close:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.wi-toc-drawer__nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.wi-toc-drawer__nav .wi-toc__link {
|
||||
font-size: var(--text-sm);
|
||||
padding: 6px 0;
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 添加 JS 逻辑
|
||||
在现有 `<script is:inline>` 中,TOC 构建逻辑之后,添加移动端抽屉逻辑:
|
||||
|
||||
```javascript
|
||||
var tocFab = document.getElementById("wi-toc-fab");
|
||||
var tocDrawer = document.getElementById("wi-toc-drawer");
|
||||
var tocDrawerNav = document.getElementById("wi-toc-drawer-nav");
|
||||
|
||||
if (tocFab && tocDrawer && tocDrawerNav && tocNav) {
|
||||
tocDrawerNav.innerHTML = tocNav.innerHTML;
|
||||
|
||||
tocFab.addEventListener("click", function () {
|
||||
tocDrawer.classList.add("wi-toc-drawer--open");
|
||||
document.body.style.overflow = "hidden";
|
||||
});
|
||||
|
||||
var drawerClose = tocDrawer.querySelector(".wi-toc-drawer__close");
|
||||
var drawerOverlay = tocDrawer.querySelector(".wi-toc-drawer__overlay");
|
||||
|
||||
function closeDrawer() {
|
||||
tocDrawer.classList.remove("wi-toc-drawer--open");
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
|
||||
if (drawerClose) drawerClose.addEventListener("click", closeDrawer);
|
||||
if (drawerOverlay) drawerOverlay.addEventListener("click", closeDrawer);
|
||||
|
||||
tocDrawerNav.querySelectorAll(".wi-toc__link").forEach(function (link) {
|
||||
link.addEventListener("click", function () {
|
||||
closeDrawer();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务五:404/500 页面深色模式不生效
|
||||
|
||||
### 问题
|
||||
[404.html](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/public/error/404.html) 和 [500.html](file:///c:/Users/Zhang/Documents/Halo/WarmIsland/public/error/500.html) 是纯静态页面,没有读取 localStorage 中的主题偏好,首次直接访问错误页面时深色模式不会生效。
|
||||
|
||||
### 修改文件
|
||||
- `public/error/404.html`
|
||||
- `public/error/500.html`
|
||||
|
||||
### 具体改动
|
||||
在两个文件的 `<head>` 中,`<style>` 标签之前,添加与 Layout.astro 相同的主题检测脚本(简化版,仅检测 localStorage 和系统偏好):
|
||||
|
||||
```html
|
||||
<script>
|
||||
(function () {
|
||||
var stored = localStorage.getItem("wi-theme");
|
||||
var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
var isDark = stored === "dark" || (!stored && prefersDark);
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add("dark");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
```
|
||||
|
||||
这段脚本会在页面渲染前检测用户的主题偏好并添加 `dark` 类名,确保 CSS 变量正确切换。
|
||||
|
||||
---
|
||||
|
||||
## 执行顺序
|
||||
|
||||
1. **settings.yaml** — 新增 footer 配置组
|
||||
2. **Layout.astro** — 添加全局 SEO meta 标签
|
||||
3. **post.astro** — 添加文章页 SEO 标签 + 移动端 TOC 浮动按钮和抽屉
|
||||
4. **page.astro** — 添加页面 SEO 标签
|
||||
5. **Navbar.astro** — 添加导航当前页面高亮
|
||||
6. **MobileMenu.astro** — 添加导航当前页面高亮(需先确认结构)
|
||||
7. **404.html** — 添加深色模式检测脚本
|
||||
8. **500.html** — 添加深色模式检测脚本
|
||||
9. 构建并部署到 Docker 验证
|
||||
Reference in New Issue
Block a user