chore: 集成 extra-api 统计、页脚备案配置、友链申请内嵌
- extra-api: 文章卡片/详情页展示字数+阅读时长、归档页展示全站总字数、瞬间页展示单条字数(pluginFinder.available 守卫,未安装自动隐藏) - 页脚新增 ICP 备案号与公网安备号配置(settings.yaml footer 分组,partials/layout.html 渲染并加安全导航守卫) - 友链申请表单由独立单页改为内嵌 /links 页底部(点击「申请友链」展开),移除独立 links-apply.html 及其自定义模板注册 - 重新打包 dist/whitespace-0.1.0.zip(移除陈旧 1.0.0)
This commit is contained in:
@@ -23,14 +23,13 @@
|
||||
|
||||
- **plugin-moments**:`/moments` 瞬间页
|
||||
- **plugin-photos**:`/photos` 图库页
|
||||
- **plugin-links**:`/links` 友链页;并额外提供
|
||||
- `links-apply.html`:访客友链申请表单(调用插件公共 REST API,含图形验证码)
|
||||
- **plugin-links**:`/links` 友链页;友链申请表单已**直接内嵌**在友链页底部(点击「申请友链」展开,调用插件公共 REST API,含图形验证码);并额外提供
|
||||
- `links-subscribe.html`:RSS 订阅动态聚合页(使用 `linkFeedFinder`)
|
||||
- **halo-weread-plugin**:`weread.html` 微信读书书架页(前端拉取插件 `/shelf-html` 并注入)
|
||||
- **留言板**:`page_guestbook.html` 以评论区作为留言板的单页模板
|
||||
- **plugin-extra-api**:API 扩展包(无独立页面),主题已适配其 shiki 代码高亮与暗色样式
|
||||
- **plugin-extra-api**:API 扩展包(无独立页面),主题已适配其 shiki 代码高亮与暗色样式;并在文章卡片 / 详情页展示**字数 + 阅读时长**、归档页展示**全站总字数**、瞬间页展示**单条字数**(通过 `extraApiStatsFinder`,插件未安装时自动隐藏)
|
||||
|
||||
> `weread` / `留言板` / `友链申请` / `链接订阅` 均为「自定义页面模板」,在控制台新建单页并选择对应模板即可启用。
|
||||
> `weread` / `留言板` / `链接订阅` 均为「自定义页面模板」,在控制台新建单页并选择对应模板即可启用。`/links` 友链页与内嵌的友链申请无需单独建单页。
|
||||
|
||||
## 目录结构
|
||||
|
||||
@@ -86,6 +85,8 @@ pnpm check # 格式化 + Lint 修复
|
||||
| about | avatar / bio / github_url / twitter_url / contact_email | 关于页(自定义模板 `page_about.html`)内容 |
|
||||
| seo | enable_metadata | 输出 canonical / Open Graph / Twitter Card |
|
||||
| seo | enable_structured_data | 输出文章页 BlogPosting JSON-LD |
|
||||
| footer | icp_number / icp_url | ICP 备案号与查询链接(默认工信部备案管理系统) |
|
||||
| footer | police_number / police_url | 公网安备号与查询链接(默认全国互联网安全管理服务平台) |
|
||||
|
||||
## 约定与说明
|
||||
|
||||
@@ -93,6 +94,7 @@ pnpm check # 格式化 + Lint 修复
|
||||
- 社交分享 / 结构化数据中用 `status.permalink` 与 `site.routes.*` 拼出地址,不写死可配置路由。
|
||||
- 搜索由官方 `PluginSearchWidget` 插件提供,按钮仅在插件可用时出现。
|
||||
- 浏览量来自 Halo 原生统计(`post.stats.visit`),无需主题自行计数。
|
||||
- 字数 / 阅读时长来自 `halo-plugin-extra-api` 的 `extraApiStatsFinder`(插件未安装时相关展示自动隐藏),全站字数为缓存计算结果。
|
||||
|
||||
## 致谢
|
||||
|
||||
|
||||
@@ -33,3 +33,10 @@ footer.theme=Theme by {0}
|
||||
about.social.github=GitHub
|
||||
about.social.twitter=Twitter
|
||||
about.social.email=Email
|
||||
post.words={0} words
|
||||
post.reading_time=About {0} min
|
||||
archive.stats.words=Total words
|
||||
links.apply=Apply for link
|
||||
links.apply_sent=Application submitted, awaiting review.
|
||||
links.apply_failed=Submission failed, please check and retry.
|
||||
links.apply_captcha_failed=Captcha failed to load, please refresh.
|
||||
|
||||
@@ -33,3 +33,10 @@ footer.theme=Theme by {0}
|
||||
about.social.github=GitHub
|
||||
about.social.twitter=Twitter
|
||||
about.social.email=邮箱
|
||||
post.words={0} 字
|
||||
post.reading_time=约 {0} 分钟
|
||||
archive.stats.words=总字数
|
||||
links.apply=申请友链
|
||||
links.apply_sent=申请已提交,等待管理员审核。
|
||||
links.apply_failed=提交失败,请检查信息后重试。
|
||||
links.apply_captcha_failed=验证码加载失败,请刷新页面重试。
|
||||
|
||||
@@ -74,3 +74,28 @@ spec:
|
||||
label: 输出文章结构化数据
|
||||
value: true
|
||||
help: 在文章页输出 Schema.org BlogPosting JSON-LD;若已由插件提供,请关闭
|
||||
- group: footer
|
||||
label: 页脚备案
|
||||
formSchema:
|
||||
- $formkit: text
|
||||
name: icp_number
|
||||
label: ICP 备案号
|
||||
value: ""
|
||||
placeholder: 京ICP备12345678号
|
||||
help: 工信部备案号,留空则不显示
|
||||
- $formkit: text
|
||||
name: icp_url
|
||||
label: ICP 备案查询链接
|
||||
value: "https://beian.miit.gov.cn"
|
||||
help: 点击备案号跳转的查询地址,默认工信部备案管理系统
|
||||
- $formkit: text
|
||||
name: police_number
|
||||
label: 公网安备号
|
||||
value: ""
|
||||
placeholder: 京公网安备11000002000001号
|
||||
help: 公安机关备案号,留空则不显示
|
||||
- $formkit: text
|
||||
name: police_url
|
||||
label: 公网安备查询链接
|
||||
value: "https://beian.mps.gov.cn"
|
||||
help: 点击公网安备号跳转的地址,默认全国互联网安全管理服务平台
|
||||
|
||||
@@ -38,6 +38,17 @@
|
||||
Tags
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:if="${pluginFinder.available('extra-api')}">
|
||||
<div class="archive-stat">
|
||||
<div
|
||||
class="archive-stat-number"
|
||||
th:text="${extraApiStatsFinder.getPostWordCount()}"
|
||||
>0</div>
|
||||
<div class="archive-stat-label" th:text="#{archive.stats.words}">
|
||||
Words
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<!-- 按年归档 -->
|
||||
|
||||
@@ -1113,6 +1113,38 @@ a.pagination-item:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.footer-beian {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.5rem 1rem;
|
||||
margin-top: var(--spacing-sm);
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.footer-beian-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.footer-beian-item:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.footer-police-icon {
|
||||
width: 0.95rem;
|
||||
height: 0.95rem;
|
||||
}
|
||||
|
||||
.post-words,
|
||||
.post-reading {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.moment-words {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
回到顶部按钮
|
||||
======================================== */
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
<include src="partials/layout.html">
|
||||
<template name="head">
|
||||
<title
|
||||
th:text="|${singlePage.spec.title} - ${site.title}|"
|
||||
th:if="${singlePage != null}"
|
||||
>
|
||||
申请友链
|
||||
</title>
|
||||
<style>
|
||||
.apply-form {
|
||||
max-width: 32rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.apply-form .form-row {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.apply-form label {
|
||||
display: block;
|
||||
margin-bottom: 0.35rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
.apply-form input,
|
||||
.apply-form textarea {
|
||||
width: 100%;
|
||||
padding: 0.6rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
}
|
||||
.apply-form textarea {
|
||||
min-height: 5rem;
|
||||
resize: vertical;
|
||||
}
|
||||
.apply-captcha {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
}
|
||||
.apply-captcha img {
|
||||
height: 2.75rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.apply-status {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: var(--radius-sm);
|
||||
display: none;
|
||||
}
|
||||
.apply-status.ok {
|
||||
display: block;
|
||||
background: rgba(46, 160, 67, 0.12);
|
||||
color: #2ea043;
|
||||
border: 1px solid rgba(46, 160, 67, 0.4);
|
||||
}
|
||||
.apply-status.err {
|
||||
display: block;
|
||||
background: rgba(229, 57, 53, 0.12);
|
||||
color: #e53935;
|
||||
border: 1px solid rgba(229, 57, 53, 0.4);
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
|
||||
<div class="container">
|
||||
<section class="apply-form">
|
||||
<h1 th:text="${singlePage.spec.title}" th:if="${singlePage != null}">申请友链</h1>
|
||||
<p
|
||||
class="apply-intro"
|
||||
th:if="${singlePage.spec.description != null}"
|
||||
th:utext="${singlePage.spec.description}"
|
||||
></p>
|
||||
|
||||
<form id="link-apply-form" novalidate>
|
||||
<div class="form-row">
|
||||
<label for="f-url">网站地址 *</label>
|
||||
<input id="f-url" name="url" type="url" required placeholder="https://example.com" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-name">网站名称 *</label>
|
||||
<input id="f-name" name="displayName" type="text" required placeholder="我的站点" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-logo">Logo 地址</label>
|
||||
<input id="f-logo" name="logo" type="url" placeholder="https://example.com/logo.png" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-desc">网站描述</label>
|
||||
<input id="f-desc" name="description" type="text" placeholder="一句话介绍你的站点" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-email">联系邮箱</label>
|
||||
<input id="f-email" name="email" type="email" placeholder="you@example.com" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-backlink">反链页面</label>
|
||||
<input id="f-backlink" name="backlink" type="url" placeholder="含本站链接的页面地址" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-feeds">RSS / Atom 地址(每行一个)</label>
|
||||
<textarea id="f-feeds" name="feedUrls" placeholder="https://example.com/feed.xml"></textarea>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-captcha">验证码 *</label>
|
||||
<div class="apply-captcha">
|
||||
<img id="captcha-img" alt="验证码" title="点击刷新" />
|
||||
<input id="f-captcha" name="captchaCode" type="text" maxlength="5" required autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">提交申请</button>
|
||||
</form>
|
||||
|
||||
<div class="apply-status" id="apply-status" role="alert"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script th:inline="none">
|
||||
(function () {
|
||||
var API = "/apis/api.link.halo.run/v1alpha1/link-applications";
|
||||
var CAPTCHA = "/apis/api.link.halo.run/v1alpha1/link-applications/captcha";
|
||||
var form = document.getElementById("link-apply-form");
|
||||
var img = document.getElementById("captcha-img");
|
||||
var status = document.getElementById("apply-status");
|
||||
var challengeId = null;
|
||||
|
||||
function loadCaptcha() {
|
||||
fetch(CAPTCHA, { method: "POST" })
|
||||
.then(function (r) {
|
||||
if (!r.ok) throw new Error("captcha");
|
||||
return r.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
challengeId = data.challengeId;
|
||||
img.src = data.image;
|
||||
})
|
||||
.catch(function () {
|
||||
status.className = "apply-status err";
|
||||
status.textContent = "验证码加载失败,请刷新页面重试。";
|
||||
});
|
||||
}
|
||||
|
||||
img.addEventListener("click", loadCaptcha);
|
||||
|
||||
form.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
status.className = "apply-status";
|
||||
status.textContent = "";
|
||||
if (!challengeId) {
|
||||
status.className = "apply-status err";
|
||||
status.textContent = "请等待验证码加载完成。";
|
||||
return;
|
||||
}
|
||||
var fd = new FormData(form);
|
||||
var feeds = (fd.get("feedUrls") || "")
|
||||
.split(/\r?\n/)
|
||||
.map(function (s) { return s.trim(); })
|
||||
.filter(Boolean);
|
||||
var payload = {
|
||||
url: fd.get("url"),
|
||||
displayName: fd.get("displayName"),
|
||||
logo: fd.get("logo") || undefined,
|
||||
description: fd.get("description") || undefined,
|
||||
email: fd.get("email") || undefined,
|
||||
backlink: fd.get("backlink") || undefined,
|
||||
feedUrls: feeds,
|
||||
challengeId: challengeId,
|
||||
captchaCode: fd.get("captchaCode")
|
||||
};
|
||||
fetch(API, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(function (r) {
|
||||
if (r.status === 201) {
|
||||
form.reset();
|
||||
challengeId = null;
|
||||
loadCaptcha();
|
||||
status.className = "apply-status ok";
|
||||
status.textContent = "申请已提交,等待管理员审核。";
|
||||
} else {
|
||||
return r.json().then(function (err) {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
loadCaptcha();
|
||||
status.className = "apply-status err";
|
||||
status.textContent = "提交失败,请检查信息后重试(验证码已刷新)。";
|
||||
});
|
||||
});
|
||||
|
||||
loadCaptcha();
|
||||
})();
|
||||
</script>
|
||||
</include>
|
||||
+204
@@ -56,6 +56,71 @@
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.links-apply {
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
.link-apply-panel {
|
||||
max-width: 32rem;
|
||||
margin: 1.25rem auto 0;
|
||||
text-align: left;
|
||||
}
|
||||
.link-apply-title {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
.apply-form .form-row {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.apply-form label {
|
||||
display: block;
|
||||
margin-bottom: 0.35rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
.apply-form input,
|
||||
.apply-form textarea {
|
||||
width: 100%;
|
||||
padding: 0.6rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
}
|
||||
.apply-form textarea {
|
||||
min-height: 5rem;
|
||||
resize: vertical;
|
||||
}
|
||||
.apply-captcha {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
}
|
||||
.apply-captcha img {
|
||||
height: 2.75rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.apply-status {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: var(--radius-sm);
|
||||
display: none;
|
||||
}
|
||||
.apply-status.ok {
|
||||
display: block;
|
||||
background: rgba(46, 160, 67, 0.12);
|
||||
color: #2ea043;
|
||||
border: 1px solid rgba(46, 160, 67, 0.4);
|
||||
}
|
||||
.apply-status.err {
|
||||
display: block;
|
||||
background: rgba(229, 57, 53, 0.12);
|
||||
color: #e53935;
|
||||
border: 1px solid rgba(229, 57, 53, 0.4);
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
|
||||
@@ -96,10 +161,149 @@
|
||||
</li>
|
||||
</ul>
|
||||
<p th:unless="${not #lists.isEmpty(links)}">暂无链接</p>
|
||||
|
||||
<div class="links-apply">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
id="link-apply-toggle"
|
||||
type="button"
|
||||
th:text="#{links.apply}"
|
||||
>申请友链</button>
|
||||
<div class="link-apply-panel apply-form" id="link-apply-panel" hidden>
|
||||
<h2 class="link-apply-title" th:text="#{links.apply}">申请友链</h2>
|
||||
<form id="link-apply-form" novalidate>
|
||||
<div class="form-row">
|
||||
<label for="f-url">网站地址 *</label>
|
||||
<input id="f-url" name="url" type="url" required placeholder="https://example.com" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-name">网站名称 *</label>
|
||||
<input id="f-name" name="displayName" type="text" required placeholder="我的站点" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-logo">Logo 地址</label>
|
||||
<input id="f-logo" name="logo" type="url" placeholder="https://example.com/logo.png" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-desc">网站描述</label>
|
||||
<input id="f-desc" name="description" type="text" placeholder="一句话介绍你的站点" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-email">联系邮箱</label>
|
||||
<input id="f-email" name="email" type="email" placeholder="you@example.com" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-backlink">反链页面</label>
|
||||
<input id="f-backlink" name="backlink" type="url" placeholder="含本站链接的页面地址" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-feeds">RSS / Atom 地址(每行一个)</label>
|
||||
<textarea id="f-feeds" name="feedUrls" placeholder="https://example.com/feed.xml"></textarea>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="f-captcha">验证码 *</label>
|
||||
<div class="apply-captcha">
|
||||
<img id="captcha-img" alt="验证码" title="点击刷新" />
|
||||
<input id="f-captcha" name="captchaCode" type="text" maxlength="5" required autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">提交申请</button>
|
||||
</form>
|
||||
<div class="apply-status" id="apply-status" role="alert"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="comments-section" th:if="${haloCommentEnabled}">
|
||||
<halo:comment group="plugin.halo.run" kind="Plugin" th:attr="name=${pluginName}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script th:inline="none">
|
||||
(function () {
|
||||
var API = "/apis/api.link.halo.run/v1alpha1/link-applications";
|
||||
var CAPTCHA = "/apis/api.link.halo.run/v1alpha1/link-applications/captcha";
|
||||
var toggle = document.getElementById("link-apply-toggle");
|
||||
var panel = document.getElementById("link-apply-panel");
|
||||
var form = document.getElementById("link-apply-form");
|
||||
var img = document.getElementById("captcha-img");
|
||||
var status = document.getElementById("apply-status");
|
||||
var challengeId = null;
|
||||
|
||||
function loadCaptcha() {
|
||||
fetch(CAPTCHA, { method: "POST" })
|
||||
.then(function (r) {
|
||||
if (!r.ok) throw new Error("captcha");
|
||||
return r.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
challengeId = data.challengeId;
|
||||
img.src = data.image;
|
||||
})
|
||||
.catch(function () {
|
||||
status.className = "apply-status err";
|
||||
status.textContent = "验证码加载失败,请刷新页面重试。";
|
||||
});
|
||||
}
|
||||
|
||||
toggle.addEventListener("click", function () {
|
||||
panel.hidden = !panel.hidden;
|
||||
if (!panel.hidden && !challengeId) {
|
||||
loadCaptcha();
|
||||
}
|
||||
});
|
||||
|
||||
img.addEventListener("click", loadCaptcha);
|
||||
|
||||
form.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
status.className = "apply-status";
|
||||
status.textContent = "";
|
||||
if (!challengeId) {
|
||||
status.className = "apply-status err";
|
||||
status.textContent = "请先点击「申请友链」并等待验证码加载。";
|
||||
return;
|
||||
}
|
||||
var fd = new FormData(form);
|
||||
var feeds = (fd.get("feedUrls") || "")
|
||||
.split(/\r?\n/)
|
||||
.map(function (s) { return s.trim(); })
|
||||
.filter(Boolean);
|
||||
var payload = {
|
||||
url: fd.get("url"),
|
||||
displayName: fd.get("displayName"),
|
||||
logo: fd.get("logo") || undefined,
|
||||
description: fd.get("description") || undefined,
|
||||
email: fd.get("email") || undefined,
|
||||
backlink: fd.get("backlink") || undefined,
|
||||
feedUrls: feeds,
|
||||
challengeId: challengeId,
|
||||
captchaCode: fd.get("captchaCode")
|
||||
};
|
||||
fetch(API, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(function (r) {
|
||||
if (r.status === 201) {
|
||||
form.reset();
|
||||
challengeId = null;
|
||||
loadCaptcha();
|
||||
status.className = "apply-status ok";
|
||||
status.textContent = "申请已提交,等待管理员审核。";
|
||||
} else {
|
||||
return r.json().then(function (err) {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
loadCaptcha();
|
||||
status.className = "apply-status err";
|
||||
status.textContent = "提交失败,请检查信息后重试(验证码已刷新)。";
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</include>
|
||||
|
||||
@@ -23,4 +23,18 @@
|
||||
>1 views</span
|
||||
>
|
||||
</th:block>
|
||||
<th:block th:if="${pluginFinder.available('extra-api')}">
|
||||
<th:block th:with="wc=${extraApiStatsFinder.getPostWordCount({name: post.metadata.name})}">
|
||||
<th:block th:if="${wc != null and wc > 0}">
|
||||
<span class="post-meta-sep">·</span>
|
||||
<span class="post-words" th:text="#{post.words(${wc})}">字数</span>
|
||||
<span class="post-meta-sep">·</span>
|
||||
<span
|
||||
class="post-reading"
|
||||
th:text="#{post.reading_time(${#numbers.formatInteger((wc + 399).divide(java.math.BigInteger.valueOf(400)), 0)})}"
|
||||
>阅读时长</span
|
||||
>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
@@ -91,6 +91,15 @@
|
||||
>发布时间</time
|
||||
>
|
||||
</a>
|
||||
<th:block th:if="${pluginFinder.available('extra-api') and content != null}">
|
||||
<span
|
||||
class="moment-words"
|
||||
th:with="wc=${extraApiStatsFinder.getContentWordCount(content.html)}"
|
||||
th:if="${wc != null and wc > 0}"
|
||||
th:text="#{post.words(${wc})}"
|
||||
>字数</span
|
||||
>
|
||||
</th:block>
|
||||
</header>
|
||||
|
||||
<div
|
||||
|
||||
@@ -180,6 +180,35 @@
|
||||
Theme by
|
||||
<a href="https://kehua.me" target="_blank" rel="noopener">kehua</a>
|
||||
</p>
|
||||
<div
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config?.footer?.icp_number) or not #strings.isEmpty(theme.config?.footer?.police_number)}"
|
||||
>
|
||||
<a
|
||||
class="footer-beian-item"
|
||||
th:if="${not #strings.isEmpty(theme.config?.footer?.icp_number)}"
|
||||
th:href="${theme.config?.footer?.icp_url}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
th:text="${theme.config?.footer?.icp_number}"
|
||||
>ICP</a
|
||||
>
|
||||
<a
|
||||
class="footer-beian-item footer-police"
|
||||
th:if="${not #strings.isEmpty(theme.config?.footer?.police_number)}"
|
||||
th:href="${theme.config?.footer?.police_url}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
class="footer-police-icon"
|
||||
src="https://beian.mps.gov.cn/img/footerPoliceIcon.png"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
/>
|
||||
<span th:text="${theme.config?.footer?.police_number}">公网安备</span>
|
||||
</a>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -33,10 +33,6 @@ spec:
|
||||
description: 以评论区作为留言板的单页模板
|
||||
screenshot:
|
||||
file: page_guestbook.html
|
||||
- name: 友链申请页面
|
||||
description: 适配 plugin-links 的访客友链申请表单
|
||||
screenshot:
|
||||
file: links-apply.html
|
||||
- name: 链接订阅页面
|
||||
description: 适配 plugin-links 的 RSS 订阅动态聚合页
|
||||
screenshot:
|
||||
|
||||
Reference in New Issue
Block a user