尝试给网站添加umami浏览量显示

This commit is contained in:
mckero committed 2025-08-18 14:09:08 +08:00
1 parent 987f74ec2e
commit 3dc146c04b
3 files changed
+201 -12

No files matched your search

+68 -2
View File
@@ -7,6 +7,7 @@ import { i18n } from "../i18n/translation";
import { getDir } from "../utils/url-utils";
import ImageWrapper from "./misc/ImageWrapper.astro";
import PostMetadata from "./PostMeta.astro";
import { umamiConfig } from "../config";
interface Props {
class?: string;
@@ -64,7 +65,7 @@ const { remarkPluginFrontmatter } = await entry.render();
{ description || remarkPluginFrontmatter.excerpt }
</div>
<!-- word count and read time -->
<!-- word count, read time and page views -->
<div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition">
<div>
{remarkPluginFrontmatter.words} {" " + i18n(remarkPluginFrontmatter.words === 1 ? I18nKey.wordCount : I18nKey.wordsCount)}
@@ -73,6 +74,10 @@ const { remarkPluginFrontmatter } = await entry.render();
<div>
{remarkPluginFrontmatter.minutes} {" " + i18n(remarkPluginFrontmatter.minutes === 1 ? I18nKey.minuteCount : I18nKey.minutesCount)}
</div>
<div>|</div>
<div>
<span class="text-50 text-sm font-medium" id={`page-views-${entry.slug}`}>加载中...</span>
</div>
</div>
</div>
@@ -106,5 +111,66 @@ const { remarkPluginFrontmatter } = await entry.render();
</div>
<div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 md:hidden"></div>
<script define:vars={{ entry, umamiConfig }}>
// 获取文章浏览量统计
async function fetchPostCardViews(slug) {
if (!umamiConfig.enable) {
return;
}
try {
// 第一步:获取网站ID和token
const shareResponse = await fetch(`${umamiConfig.baseUrl}/api/share/${umamiConfig.shareId}`);
if (!shareResponse.ok) {
throw new Error('获取分享信息失败');
}
const shareData = await shareResponse.json();
const { websiteId, token } = shareData;
// 第二步:获取统计数据
const currentTimestamp = Date.now();
const statsUrl = `${umamiConfig.baseUrl}/api/websites/${websiteId}/stats?startAt=0&endAt=${currentTimestamp}&unit=hour&timezone=${encodeURIComponent(umamiConfig.timezone)}&url=%2Fposts%2F${slug}%2F&compare=false`;
const statsResponse = await fetch(statsUrl, {
headers: {
'x-umami-share-token': token
}
});
if (!statsResponse.ok) {
throw new Error('获取统计数据失败');
}
const statsData = await statsResponse.json();
const pageViews = statsData.pageviews?.value || 0;
const displayElement = document.getElementById(`page-views-${slug}`);
if (displayElement) {
displayElement.textContent = `浏览量 ${pageViews}`;
}
} catch (error) {
console.error('Error fetching page views for', slug, ':', error);
const displayElement = document.getElementById(`page-views-${slug}`);
if (displayElement) {
displayElement.textContent = '统计不可用';
}
}
}
// 页面加载完成后获取统计数据
function initPostCardStats() {
const slug = entry.slug;
if (slug) {
fetchPostCardViews(slug);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPostCardStats);
} else {
initPostCardStats();
}
</script>
<style define:vars={{coverWidth}}>
</style>
</style>
+75 -9
View File
@@ -4,6 +4,7 @@ import I18nKey from "../i18n/i18nKey";
import { i18n } from "../i18n/translation";
import { formatDateToYYYYMMDD } from "../utils/date-utils";
import { getCategoryUrl, getTagUrl } from "../utils/url-utils";
import { umamiConfig } from "../config";
interface Props {
class: string;
@@ -13,6 +14,7 @@ interface Props {
category: string | null;
hideTagsForMobile?: boolean;
hideUpdateDate?: boolean;
slug?: string;
}
const {
published,
@@ -21,6 +23,7 @@ const {
category,
hideTagsForMobile = false,
hideUpdateDate = false,
slug,
} = Astro.props;
const className = Astro.props.class;
---
@@ -28,8 +31,7 @@ const className = Astro.props.class;
<div class:list={["flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2", className]}>
<!-- publish date -->
<div class="flex items-center">
<div class="meta-icon"
>
<div class="meta-icon">
<Icon name="material-symbols:calendar-today-outline-rounded" class="text-xl"></Icon>
</div>
<span class="text-50 text-sm font-medium">{formatDateToYYYYMMDD(published)}</span>
@@ -38,8 +40,7 @@ const className = Astro.props.class;
<!-- update date -->
{!hideUpdateDate && updated && updated.getTime() !== published.getTime() && (
<div class="flex items-center">
<div class="meta-icon"
>
<div class="meta-icon">
<Icon name="material-symbols:edit-calendar-outline-rounded" class="text-xl"></Icon>
</div>
<span class="text-50 text-sm font-medium">{formatDateToYYYYMMDD(updated)}</span>
@@ -48,8 +49,7 @@ const className = Astro.props.class;
<!-- categories -->
<div class="flex items-center">
<div class="meta-icon"
>
<div class="meta-icon">
<Icon name="material-symbols:book-2-outline-rounded" class="text-xl"></Icon>
</div>
<div class="flex flex-row flex-nowrap items-center">
@@ -63,8 +63,7 @@ const className = Astro.props.class;
<!-- tags -->
<div class:list={["items-center", {"flex": !hideTagsForMobile, "hidden md:flex": hideTagsForMobile}]}>
<div class="meta-icon"
>
<div class="meta-icon">
<Icon name="material-symbols:tag-rounded" class="text-xl"></Icon>
</div>
<div class="flex flex-row flex-nowrap items-center">
@@ -79,4 +78,71 @@ const className = Astro.props.class;
{!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
</div>
</div>
</div>
<!-- page views (only show if slug is provided, typically for post detail pages) -->
{slug && (
<div class="flex items-center">
<div class="meta-icon">
<Icon name="material-symbols:visibility-outline-rounded" class="text-xl"></Icon>
</div>
<span class="text-50 text-sm font-medium" id="page-views-display">加载中...</span>
</div>
)}
</div>
{slug && (
<script define:vars={{ slug, umamiConfig }}>
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) {
return;
}
try {
// 第一步:获取网站ID和token
const shareResponse = await fetch(`${umamiConfig.baseUrl}/api/share/${umamiConfig.shareId}`);
if (!shareResponse.ok) {
throw new Error('获取分享信息失败');
}
const shareData = await shareResponse.json();
const { websiteId, token } = shareData;
// 第二步:获取统计数据
const currentTimestamp = Date.now();
const statsUrl = `${umamiConfig.baseUrl}/api/websites/${websiteId}/stats?startAt=0&endAt=${currentTimestamp}&unit=hour&timezone=${encodeURIComponent(umamiConfig.timezone)}&url=%2Fposts%2F${slug}%2F&compare=false`;
const statsResponse = await fetch(statsUrl, {
headers: {
'x-umami-share-token': token
}
});
if (!statsResponse.ok) {
throw new Error('获取统计数据失败');
}
const statsData = await statsResponse.json();
const pageViews = statsData.pageviews?.value || 0;
const visits = statsData.visits?.value || 0;
const displayElement = document.getElementById('page-views-display');
if (displayElement) {
displayElement.textContent = `浏览量 ${pageViews} · 访问数 ${visits}`;
}
} catch (error) {
console.error('Error fetching page views:', error);
const displayElement = document.getElementById('page-views-display');
if (displayElement) {
displayElement.textContent = '统计不可用';
}
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
</script>
)}
+58 -1
View File
@@ -1,6 +1,6 @@
---
import { Icon } from "astro-icon/components";
import { profileConfig } from "../../config";
import { profileConfig, umamiConfig } from "../../config";
import { url } from "../../utils/url-utils";
import ImageWrapper from "../misc/ImageWrapper.astro";
@@ -34,6 +34,63 @@ const config = profileConfig;
{config.links[0].name}
</a>}
</div>
<!-- 全站访问量统计 -->
<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:visibility-outline" class="text-base"></Icon>
<span id="site-stats">加载中...</span>
</div>
</div>
</div>
</div>
<script define:vars={{ umamiConfig }}>
// 获取全站访问量统计
async function loadSiteStats() {
if (!umamiConfig.enable) {
return;
}
try {
// 第一步:获取网站ID和token
const shareResponse = await fetch(`${umamiConfig.baseUrl}/api/share/${umamiConfig.shareId}`);
if (!shareResponse.ok) {
throw new Error('获取分享信息失败');
}
const shareData = await shareResponse.json();
const { websiteId, token } = shareData;
// 第二步:获取全站统计数据(不指定url参数获取全站数据)
const currentTimestamp = Date.now();
const statsUrl = `${umamiConfig.baseUrl}/api/websites/${websiteId}/stats?startAt=0&endAt=${currentTimestamp}&unit=hour&timezone=${encodeURIComponent(umamiConfig.timezone)}&compare=false`;
const statsResponse = await fetch(statsUrl, {
headers: {
'x-umami-share-token': token
}
});
if (!statsResponse.ok) {
throw new Error('获取统计数据失败');
}
const statsData = await statsResponse.json();
const pageviews = statsData.pageviews?.value || 0;
const statsElement = document.getElementById('site-stats');
if (statsElement) {
statsElement.textContent = `总浏览量 ${pageviews}`;
}
} catch (error) {
console.error('获取全站统计失败:', error);
const statsElement = document.getElementById('site-stats');
if (statsElement) {
statsElement.textContent = '统计不可用';
}
}
}
// 页面加载完成后获取统计数据
document.addEventListener('DOMContentLoaded', loadSiteStats);
</script>