mirror of
https://github.com/Ad-closeNN/blog-fuwari.git
synced 2026-05-31 01:20:06 -04:00
feat(component): 加入总字数统计模块
This commit is contained in:
@@ -3,6 +3,7 @@ import { Icon } from "astro-icon/components";
|
|||||||
import { profileConfig } from "../../config";
|
import { profileConfig } from "../../config";
|
||||||
import { url } from "../../utils/url-utils";
|
import { url } from "../../utils/url-utils";
|
||||||
import ImageWrapper from "../misc/ImageWrapper.astro";
|
import ImageWrapper from "../misc/ImageWrapper.astro";
|
||||||
|
import TotalWords from "./TotalWords.astro";
|
||||||
|
|
||||||
const config = profileConfig;
|
const config = profileConfig;
|
||||||
---
|
---
|
||||||
@@ -45,6 +46,8 @@ const config = profileConfig;
|
|||||||
</a>}
|
</a>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<TotalWords />
|
||||||
|
|
||||||
<!-- 全站访问量统计 https://github.com/afoim/fuwari/blob/81f22decb17ff7ee1dd480c10773f7ba8f4df296/src/components/widget/Profile.astro -->
|
<!-- 全站访问量统计 https://github.com/afoim/fuwari/blob/81f22decb17ff7ee1dd480c10773f7ba8f4df296/src/components/widget/Profile.astro -->
|
||||||
<div class="text-center text-sm text-neutral-500 dark:text-neutral-400 mt-3 pt-3 border-t border-neutral-200 dark:border-neutral-700">
|
<div class="text-center text-sm text-neutral-500 dark:text-neutral-400 mt-3 pt-3 border-t border-neutral-200 dark:border-neutral-700">
|
||||||
<div class="flex items-center justify-center gap-1">
|
<div class="flex items-center justify-center gap-1">
|
||||||
@@ -74,18 +77,37 @@ const config = profileConfig;
|
|||||||
fetch("https://v1.hitokoto.cn")
|
fetch("https://v1.hitokoto.cn")
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
// API 返回的字段是 data.text
|
const hitokotoElement = document.getElementById("hitokoto");
|
||||||
document.getElementById("hitokoto").innerText = data.hitokoto;
|
if (hitokotoElement) {
|
||||||
|
hitokotoElement.innerText = data.hitokoto;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error("获取一言失败:", error);
|
console.error("获取一言失败:", error);
|
||||||
//document.getElementById("hitokoto").innerText = "获取失败,请稍后再试。";
|
const hitokotoElement = document.getElementById("hitokoto");
|
||||||
//失败后用内置的,成功了用别人的。
|
if (hitokotoElement) {
|
||||||
document.getElementById("hitokoto").innerText = "再热情的心也经不起冷漠,再爱你的人也经不起冷落。";
|
hitokotoElement.innerText = "再热情的心也经不起冷漠,再爱你的人也经不起冷落。";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
type SiteStatsData = {
|
||||||
|
pageviews?: {
|
||||||
|
value?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type BlogUmamiStore = {
|
||||||
|
getStats: (key: string, createUrl: (websiteId: string) => string) => Promise<SiteStatsData | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__blogUmami?: BlogUmamiStore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取全站访问量统计
|
// 获取全站访问量统计
|
||||||
async function loadSiteStats() {
|
async function loadSiteStats() {
|
||||||
const statsElement = document.getElementById('site-stats');
|
const statsElement = document.getElementById('site-stats');
|
||||||
@@ -96,8 +118,8 @@ fetch("https://v1.hitokoto.cn")
|
|||||||
statsElement.dataset.umamiState = 'loading';
|
statsElement.dataset.umamiState = 'loading';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const umamiStore = window['__blogUmami'];
|
const umamiStore = window.__blogUmami;
|
||||||
const statsData = await umamiStore?.getStats('site:all', (websiteId) => {
|
const statsData = await umamiStore?.getStats('site:all', (websiteId: string) => {
|
||||||
const currentTimestamp = Date.now();
|
const currentTimestamp = Date.now();
|
||||||
return `https://umami.adclosenn.top/api/websites/${websiteId}/stats?startAt=0&endAt=${currentTimestamp}&unit=hour&timezone=${encodeURIComponent('Asia/Shanghai')}&compare=false`;
|
return `https://umami.adclosenn.top/api/websites/${websiteId}/stats?startAt=0&endAt=${currentTimestamp}&unit=hour&timezone=${encodeURIComponent('Asia/Shanghai')}&compare=false`;
|
||||||
});
|
});
|
||||||
@@ -125,43 +147,43 @@ fetch("https://v1.hitokoto.cn")
|
|||||||
</script>
|
</script>
|
||||||
<!-- 获取 Commit 信息 via API -->
|
<!-- 获取 Commit 信息 via API -->
|
||||||
<script>
|
<script>
|
||||||
|
type GithubCommit = {
|
||||||
|
sha: string;
|
||||||
|
html_url: string;
|
||||||
|
commit: {
|
||||||
|
message: string;
|
||||||
|
committer: {
|
||||||
|
date: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
async function loadCommitStats() {
|
async function loadCommitStats() {
|
||||||
try {
|
try {
|
||||||
const statsElement = document.getElementById('github-commit'); // 查找 id
|
const statsElement = document.getElementById('github-commit');
|
||||||
const link = document.getElementById('github-commit-link'); // 查找 id
|
const link = document.getElementById('github-commit-link');
|
||||||
|
|
||||||
// 第一步:调用 API
|
|
||||||
const githubResponse = await fetch(`https://api.github.com/repos/Ad-closeNN/blog-fuwari/commits?per_page=1`);
|
const githubResponse = await fetch(`https://api.github.com/repos/Ad-closeNN/blog-fuwari/commits?per_page=1`);
|
||||||
|
|
||||||
if (!githubResponse.ok) {
|
if (!githubResponse.ok) {
|
||||||
throw new Error('获取信息失败');
|
throw new Error('获取信息失败');
|
||||||
}
|
}
|
||||||
|
|
||||||
let Data = await githubResponse.json();
|
const data = (await githubResponse.json()) as GithubCommit[];
|
||||||
Data = Data[0];
|
const latestCommit = data[0];
|
||||||
// 第二步:获取 Commit 数据
|
|
||||||
const latestCommit = Data;
|
|
||||||
|
|
||||||
const commitData = {
|
if (!latestCommit) {
|
||||||
hash: latestCommit.sha.slice(0, 7),
|
throw new Error('未获取到提交信息');
|
||||||
fullHash: latestCommit.sha,
|
|
||||||
message: latestCommit.commit.message.split('\n')[0],
|
|
||||||
author: latestCommit.commit.author.name,
|
|
||||||
date: latestCommit.commit.author.date,
|
|
||||||
url: latestCommit.html_url
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (statsElement) {
|
|
||||||
statsElement.textContent = `当前提交:${Data.sha.slice(0,7)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (statsElement) {
|
||||||
|
statsElement.textContent = `当前提交:${latestCommit.sha.slice(0, 7)}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (link){
|
if (link instanceof HTMLAnchorElement) {
|
||||||
// const gurl = "https://github.com/Ad-closeNN/blog-fuwari/commit/"+Data.sha;
|
const infoUrl = "/info/";
|
||||||
const gurl = "/info/";
|
link.href = infoUrl;
|
||||||
link.href = gurl;
|
link.title = `(${latestCommit.commit.committer.date}) ${latestCommit.commit.message}`;
|
||||||
link.title = "("+Data.commit.committer.date + ")" + " " + Data.commit.message;
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取 Commit 信息失败:', error);
|
console.error('获取 Commit 信息失败:', error);
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
import { Icon } from "astro-icon/components";
|
||||||
|
import I18nKey from "@i18n/i18nKey";
|
||||||
|
import { i18n } from "@i18n/translation";
|
||||||
|
import { getTotalWords } from "@utils/content-utils";
|
||||||
|
|
||||||
|
const totalWords = await getTotalWords();
|
||||||
|
const formattedTotalWords = totalWords.toLocaleString("zh-CN");
|
||||||
|
---
|
||||||
|
<div class="text-center text-sm text-neutral-500 dark:text-neutral-400 mt-3 pt-3 border-t border-neutral-200 dark:border-neutral-700">
|
||||||
|
<div class="flex items-center justify-center gap-1">
|
||||||
|
<Icon name="material-symbols:article-outline" class="text-base"></Icon>
|
||||||
|
<span>总计 {formattedTotalWords} {i18n(I18nKey.wordsCount)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
::github{repo="afoim/fuwari"}
|
::github{repo="afoim/fuwari"}
|
||||||
|
|
||||||
# 站点分流
|
# 站点分流
|
||||||
经过几个月的测试,**本站**之后将弃用 Netlify 托管,转而使用免费且更强大的 Cloudflare。之前使用过的分流测试版站点已移除。谢谢 Netlify,你好 Cloudflare!
|
经过几个月的测试,**本站**之后将弃用 Netlify 托管,转而使用免费且更强大的 Cloudflare。之前使用过的分流测试版站点已移除,且博客域名已改为 `blog.adclosenn.top`。谢谢你 Netlify,你好 Cloudflare!
|
||||||
|
|
||||||
# 关于我
|
# 关于我
|
||||||
一位住在 [中华人民共和国广西壮族自治区](https://baike.baidu.com/item/%E5%B9%BF%E8%A5%BF%E5%A3%AE%E6%97%8F%E8%87%AA%E6%B2%BB%E5%8C%BA/163178) 的学生。 [me.adclosenn.top](https://me.adclosenn.top)
|
一位住在 [中华人民共和国广西壮族自治区](https://baike.baidu.com/item/%E5%B9%BF%E8%A5%BF%E5%A3%AE%E6%97%8F%E8%87%AA%E6%B2%BB%E5%8C%BA/163178) 的学生。 [me.adclosenn.top](https://me.adclosenn.top)
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
## 联系方式
|
## 联系方式
|
||||||
电子邮箱:[admin@adclosenn.top](mailto:admin@adclosenn.top)
|
电子邮箱:[admin@adclosenn.top](mailto:admin@adclosenn.top)
|
||||||
Discord:https://discord.com/users/1068060784300658688
|
Discord:https://discord.com/users/1068060784300658688
|
||||||
BlueSky:https://bsky.app/profile/adclosenn.top
|
X:https://x.com/Ad_closeNN
|
||||||
|
|
||||||
# 关于本站
|
# 关于本站
|
||||||
## 字体
|
## 字体
|
||||||
|
|||||||
@@ -92,7 +92,8 @@ const customcover = entry.data.customcover;
|
|||||||
data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title"
|
data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title"
|
||||||
class="transition w-full block font-bold mb-3
|
class="transition w-full block font-bold mb-3
|
||||||
text-3xl md:text-[2.25rem]/[2.75rem]
|
text-3xl md:text-[2.25rem]/[2.75rem]
|
||||||
text-black/90 dark:text-white/90
|
text-[var(--primary)]
|
||||||
|
underline decoration-[3px] decoration-[var(--primary)] underline-offset-[0.45rem]
|
||||||
md:before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
|
md:before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
|
||||||
before:absolute before:top-[0.75rem] before:left-[-1.125rem]
|
before:absolute before:top-[0.75rem] before:left-[-1.125rem]
|
||||||
">
|
">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type CollectionEntry, getCollection } from "astro:content";
|
import { render, type CollectionEntry, getCollection } from "astro:content";
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from "@i18n/i18nKey";
|
||||||
import { i18n } from "@i18n/translation";
|
import { i18n } from "@i18n/translation";
|
||||||
import { getCategoryUrl } from "@utils/url-utils.ts";
|
import { getCategoryUrl } from "@utils/url-utils.ts";
|
||||||
@@ -46,6 +46,24 @@ export async function getSortedPostsList(): Promise<PostForList[]> {
|
|||||||
|
|
||||||
return sortedPostsList;
|
return sortedPostsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let totalWordsCache: number | undefined;
|
||||||
|
|
||||||
|
export async function getTotalWords(): Promise<number> {
|
||||||
|
if (totalWordsCache !== undefined) {
|
||||||
|
return totalWordsCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const posts = await getRawSortedPosts();
|
||||||
|
const renderedPosts = await Promise.all(posts.map((post) => render(post)));
|
||||||
|
const totalWords = renderedPosts.reduce((sum, { remarkPluginFrontmatter }) => {
|
||||||
|
const words = Number(remarkPluginFrontmatter?.words ?? 0);
|
||||||
|
return sum + (Number.isFinite(words) ? words : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
totalWordsCache = totalWords;
|
||||||
|
return totalWords;
|
||||||
|
}
|
||||||
export type Tag = {
|
export type Tag = {
|
||||||
name: string;
|
name: string;
|
||||||
count: number;
|
count: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user