111
This commit is contained in:
171
src/components/PostMeta.astro
Normal file
171
src/components/PostMeta.astro
Normal file
@@ -0,0 +1,171 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { umamiConfig } from "../config";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import { i18n } from "../i18n/translation";
|
||||
import { formatDateToYYYYMMDD } from "../utils/date-utils";
|
||||
import { getCategoryUrl, getTagUrl } from "../utils/url-utils";
|
||||
|
||||
// 解析 umami
|
||||
const umamiEnabled = umamiConfig.enabled || false;
|
||||
const umamiWebsiteId =
|
||||
umamiConfig.scripts.match(/data-website-id="([^"]+)"/)?.[1] || "";
|
||||
const umamiApiKey = umamiConfig.apiKey || "";
|
||||
const umamiBaseUrl = umamiConfig.baseUrl || "";
|
||||
|
||||
export interface Props {
|
||||
published: Date;
|
||||
updated?: Date;
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
hideUpdateDate?: boolean;
|
||||
hideTagsForMobile?: boolean;
|
||||
isHome?: boolean;
|
||||
className?: string;
|
||||
id?: string;
|
||||
showOnlyBasicMeta?: boolean; // 新增属性,控制是否只显示基本元数据
|
||||
words?: number; // 字数统计
|
||||
minutes?: number; // 阅读时间(分钟)
|
||||
showWordCount?: boolean; // 是否显示字数统计
|
||||
}
|
||||
|
||||
const {
|
||||
published,
|
||||
updated,
|
||||
category,
|
||||
tags,
|
||||
hideUpdateDate,
|
||||
hideTagsForMobile,
|
||||
isHome,
|
||||
className = "",
|
||||
id,
|
||||
showOnlyBasicMeta = false, // 默认为false,保持原有行为
|
||||
words,
|
||||
// minutes,
|
||||
showWordCount = false, // 默认不显示字数统计
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<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">
|
||||
<Icon name="material-symbols:calendar-today-outline-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<span class="text-50 text-sm font-medium">{formatDateToYYYYMMDD(published)}</span>
|
||||
</div>
|
||||
|
||||
<!-- update date -->
|
||||
{!hideUpdateDate && updated && updated.getTime() !== published.getTime() && (
|
||||
<div class="flex items-center">
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- categories -->
|
||||
<div class="flex items-center">
|
||||
<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">
|
||||
<a href={getCategoryUrl(category || '')} aria-label={`View all posts in the ${category} category`}
|
||||
class="link-lg transition text-50 text-sm font-medium
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)] whitespace-nowrap">
|
||||
{category || i18n(I18nKey.uncategorized)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- word count -->
|
||||
{showWordCount && words && (
|
||||
<div class="flex items-center">
|
||||
<div class="meta-icon">
|
||||
<Icon name="material-symbols:article-outline-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<span class="text-50 text-sm font-medium">
|
||||
{words} {words > 1 ? i18n(I18nKey.wordsCount) : i18n(I18nKey.wordCount)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- tags (只有在不显示基本元数据时才显示) -->
|
||||
{!showOnlyBasicMeta && (
|
||||
<div class:list={["items-center", {"flex": !hideTagsForMobile, "hidden md:flex": hideTagsForMobile}]}>
|
||||
<div class="meta-icon">
|
||||
<Icon name="material-symbols:tag-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap items-center">
|
||||
{(tags && tags.length > 0) && tags.map((tag, i) => (
|
||||
<>
|
||||
<div class:list={[{"hidden": i == 0}, "mx-1.5 text-[var(--meta-divider)] text-sm"]}>/</div>
|
||||
<a href={getTagUrl(tag)} aria-label={`View all posts with the ${tag.trim()} tag`}
|
||||
class="link-lg transition text-50 text-sm font-medium
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)] whitespace-nowrap">
|
||||
{tag.trim()}
|
||||
</a>
|
||||
</>
|
||||
))}
|
||||
{!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- 访问量(首页不显示,且umami.enabled为true时显示) -->
|
||||
{!isHome && umamiEnabled && id && (
|
||||
<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>
|
||||
|
||||
<!-- 只有在非首页且启用umami且有slug时才加载脚本 -->
|
||||
{!isHome && umamiEnabled && id && (
|
||||
<script is:inline define:vars={{ id, umamiBaseUrl, umamiApiKey, umamiWebsiteId, umamiConfig }}>
|
||||
// 客户端统计文案生成函数
|
||||
function generateStatsText(pageViews, visitors) {
|
||||
return `浏览量 ${pageViews} · 访客 ${visitors}`;
|
||||
}
|
||||
|
||||
// 获取访问量统计
|
||||
async function fetchPageViews(isRetry = false) {
|
||||
// @ts-ignore
|
||||
if (!umamiConfig.enabled || !umamiWebsiteId || !id || !isRetry) return;
|
||||
|
||||
try {
|
||||
// 构造文章页面的URL路径
|
||||
const pageUrl = `/posts/${id}/`;
|
||||
|
||||
// 调用全局工具获取特定页面的 Umami 统计数据
|
||||
const stats = await getUmamiPageStats(umamiBaseUrl, umamiApiKey, umamiWebsiteId, pageUrl);
|
||||
|
||||
// 从返回的数据中提取页面浏览量和访客数
|
||||
const pageViews = stats.pageviews || 0;
|
||||
const visitors = stats.visitors || 0;
|
||||
|
||||
const displayElement = document.getElementById('page-views-display');
|
||||
if (displayElement) {
|
||||
displayElement.textContent = generateStatsText(pageViews, visitors);
|
||||
}
|
||||
} 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>
|
||||
)}
|
||||
Reference in New Issue
Block a user