diff --git a/src/components/layout/SideBar.astro b/src/components/layout/SideBar.astro index 2226f636fb..1d5bfffe47 100644 --- a/src/components/layout/SideBar.astro +++ b/src/components/layout/SideBar.astro @@ -5,6 +5,7 @@ import Announcement from "@/components/widget/Announcement.astro"; import Calendar from "@/components/widget/Calendar.astro"; import Categories from "@/components/widget/Categories.astro"; import Dynamic from "@/components/widget/Dynamic.astro"; +import LaStats from "@/components/widget/LaStats.astro"; import Music from "@/components/widget/Music.astro"; import Profile from "@/components/widget/Profile.astro"; import SidebarTOC from "@/components/widget/SidebarTOC.astro"; @@ -49,6 +50,7 @@ const componentMap = { sidebarToc: SidebarTOC, advertisement: Advertisement, stats: SiteStats, + lastats: LaStats, calendar: Calendar, music: Music, siteInfo: SiteInfo, diff --git a/src/components/widget/LaStats.astro b/src/components/widget/LaStats.astro new file mode 100644 index 0000000000..792255e5dc --- /dev/null +++ b/src/components/widget/LaStats.astro @@ -0,0 +1,161 @@ +--- +import { Icon } from "astro-icon/components"; +import WidgetLayout from "@/components/common/WidgetLayout.astro"; +import { analyticsConfig } from "@/config"; +import I18nKey from "@/i18n/i18nKey"; +import { i18n } from "@/i18n/translation"; +import type { WidgetComponentConfig } from "@/types/config"; + +interface Props { + class?: string; + style?: string; + widgetConfig?: WidgetComponentConfig; +} + +const { class: className, style, widgetConfig } = Astro.props; + +const showTitle = widgetConfig?.showTitle !== false; + +const la51Id = analyticsConfig?.la51Analytics?.Id || ""; +const la51StatsDataSource = + analyticsConfig?.la51Analytics?.statsDataSource || "quotejs"; +const la51StatsApiUrl = analyticsConfig?.la51Analytics?.statsApiUrl || ""; + +// 51LA 统计指标定义 +const stats = [ + { + icon: "material-symbols:person-outline", + label: i18n(I18nKey.laStatsOnline), + id: "la-online", + }, + { + icon: "material-symbols:calendar-today-outline", + label: i18n(I18nKey.laStatsTodayUV), + id: "la-today-uv", + }, + { + icon: "material-symbols:visibility-outline", + label: i18n(I18nKey.laStatsTodayPV), + id: "la-today-pv", + }, + { + icon: "material-symbols:calendar-month-outline", + label: i18n(I18nKey.laStatsYesterdayUV), + id: "la-yesterday-uv", + }, + { + icon: "material-symbols:visibility-outline", + label: i18n(I18nKey.laStatsYesterdayPV), + id: "la-yesterday-pv", + }, + { + icon: "material-symbols:calendar-clock-outline", + label: i18n(I18nKey.laStatsMonthPV), + id: "la-month-pv", + }, + { + icon: "mingcute:chart-line-line", + label: i18n(I18nKey.laStatsTotalPV), + id: "la-total-pv", + }, +]; +--- + +{la51Id && ( + +
+ {stats.map((stat) => ( +
+
+
+ +
+ + {stat.label} + +
+ + - + +
+ ))} +
+
+)} + + diff --git a/src/config/analyticsConfig.ts b/src/config/analyticsConfig.ts index 0a449830aa..457e50ed22 100644 --- a/src/config/analyticsConfig.ts +++ b/src/config/analyticsConfig.ts @@ -45,5 +45,14 @@ export const analyticsConfig: AnalyticsConfig = { hashMode: false, // 是否开启网站录屏功能 screenRecord: true, + // 数据挂件数据源:"quotejs" 从官方 quote.js 解析,"api" 从第三方 API 获取 JSON + statsDataSource: "quotejs", + // 第三方 API 地址模板,仅 statsDataSource 为 "api" 时生效 + // 自建API参考: https://jishuzhan.net/article/1997120577757315073 + // 使用 {id} 占位符代表 51la 统计 ID,也可省略则自动追加 /{id} + // 示例:"https://51la.api.trustdev.org/{id}" + // "https://your-worker.workers.dev?id={id}" + // "https://51la.api.trustdev.org" (自动追加 /{id}) + statsApiUrl: "", }, }; diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index 3f406e9303..cb69146eeb 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -129,6 +129,16 @@ export const sidebarLayoutConfig: SidebarLayoutConfig = { // 是否在文章详情页显示 showOnPostPage: false, }, + { + // 组件类型:51LA访问统计组件 + type: "lastats", + // 是否启用该组件 + enable: true, + // 组件位置 + position: "top", + // 是否在文章详情页显示 + showOnPostPage: true, + }, { // 组件类型:站点信息组件 type: "siteInfo", @@ -303,6 +313,14 @@ export const sidebarLayoutConfig: SidebarLayoutConfig = { // 是否在文章详情页显示 showOnPostPage: true, }, + { + // 组件类型:51LA访问统计组件 + type: "lastats", + // 是否启用该组件 + enable: true, + // 是否在文章详情页显示 + showOnPostPage: true, + }, { // 组件类型:站点信息组件 type: "siteInfo", diff --git a/src/i18n/i18nKey.ts b/src/i18n/i18nKey.ts index fd5549c016..019f51587a 100644 --- a/src/i18n/i18nKey.ts +++ b/src/i18n/i18nKey.ts @@ -339,6 +339,16 @@ enum I18nKey { siteStatsDays = "siteStatsDays", today = "today", + // 51LA 访问统计 + laStats = "laStats", + laStatsOnline = "laStatsOnline", + laStatsTodayUV = "laStatsTodayUV", + laStatsTodayPV = "laStatsTodayPV", + laStatsYesterdayUV = "laStatsYesterdayUV", + laStatsYesterdayPV = "laStatsYesterdayPV", + laStatsMonthPV = "laStatsMonthPV", + laStatsTotalPV = "laStatsTotalPV", + // 站点信息 siteInfo = "siteInfo", siteInfoBuildTime = "siteInfoBuildTime", diff --git a/src/i18n/languages/en.ts b/src/i18n/languages/en.ts index d7093476f6..f24602702e 100644 --- a/src/i18n/languages/en.ts +++ b/src/i18n/languages/en.ts @@ -350,6 +350,16 @@ export const en: Translation = { [Key.siteStatsDays]: "{days} days", [Key.today]: "Today", + // 51LA Visitor Statistics + [Key.laStats]: "Visitor Statistics", + [Key.laStatsOnline]: "Online Now", + [Key.laStatsTodayUV]: "Today Visitors", + [Key.laStatsTodayPV]: "Today Views", + [Key.laStatsYesterdayUV]: "Yesterday Visitors", + [Key.laStatsYesterdayPV]: "Yesterday Views", + [Key.laStatsMonthPV]: "This Month Views", + [Key.laStatsTotalPV]: "Total Views", + // Site Info [Key.siteInfo]: "Site Info", [Key.siteInfoBuildTime]: "Build Time", diff --git a/src/i18n/languages/ja.ts b/src/i18n/languages/ja.ts index b40e2166a2..a24b64b94b 100644 --- a/src/i18n/languages/ja.ts +++ b/src/i18n/languages/ja.ts @@ -349,6 +349,16 @@ export const ja: Translation = { [Key.siteStatsDays]: "{days} 日", [Key.today]: "今日", + // 51LA アクセス統計 + [Key.laStats]: "アクセス統計", + [Key.laStatsOnline]: "現在オンライン", + [Key.laStatsTodayUV]: "本日の訪問者", + [Key.laStatsTodayPV]: "本日の閲覧数", + [Key.laStatsYesterdayUV]: "昨日の訪問者", + [Key.laStatsYesterdayPV]: "昨日の閲覧数", + [Key.laStatsMonthPV]: "今月の閲覧数", + [Key.laStatsTotalPV]: "累計閲覧数", + // サイト情報 [Key.siteInfo]: "サイト情報", [Key.siteInfoBuildTime]: "ビルド日時", diff --git a/src/i18n/languages/ko.ts b/src/i18n/languages/ko.ts index 6fb9868095..7447125397 100644 --- a/src/i18n/languages/ko.ts +++ b/src/i18n/languages/ko.ts @@ -348,6 +348,16 @@ export const ko: Translation = { [Key.siteStatsDays]: "{days}일", [Key.today]: "오늘", + // 51LA Visitor Statistics + [Key.laStats]: "방문자 통계", + [Key.laStatsOnline]: "현재 온라인", + [Key.laStatsTodayUV]: "오늘 방문자", + [Key.laStatsTodayPV]: "오늘 조회수", + [Key.laStatsYesterdayUV]: "어제 방문자", + [Key.laStatsYesterdayPV]: "어제 조회수", + [Key.laStatsMonthPV]: "이번 달 조회수", + [Key.laStatsTotalPV]: "총 조회수", + // Site Info [Key.siteInfo]: "사이트 정보", [Key.siteInfoBuildTime]: "빌드 시각", diff --git a/src/i18n/languages/ru.ts b/src/i18n/languages/ru.ts index 9af185c8d9..65751d9c51 100644 --- a/src/i18n/languages/ru.ts +++ b/src/i18n/languages/ru.ts @@ -352,6 +352,16 @@ export const ru: Translation = { [Key.siteStatsDays]: "{days} дней", [Key.today]: "Сегодня", + // 51LA Статистика посещений + [Key.laStats]: "Статистика посещений", + [Key.laStatsOnline]: "Сейчас онлайн", + [Key.laStatsTodayUV]: "Посетители сегодня", + [Key.laStatsTodayPV]: "Просмотры сегодня", + [Key.laStatsYesterdayUV]: "Посетители вчера", + [Key.laStatsYesterdayPV]: "Просмотры вчера", + [Key.laStatsMonthPV]: "Просмотры за месяц", + [Key.laStatsTotalPV]: "Всего просмотров", + // Информация о сайте [Key.siteInfo]: "Информация о сайте", [Key.siteInfoBuildTime]: "Время сборки", diff --git a/src/i18n/languages/zh_CN.ts b/src/i18n/languages/zh_CN.ts index c41175716b..8a27fb82b8 100644 --- a/src/i18n/languages/zh_CN.ts +++ b/src/i18n/languages/zh_CN.ts @@ -341,6 +341,16 @@ export const zh_CN: Translation = { [Key.siteStatsDays]: "{days} 天", [Key.today]: "今天", + // 51LA 访问统计 + [Key.laStats]: "访问统计", + [Key.laStatsOnline]: "当前在线", + [Key.laStatsTodayUV]: "今日访客", + [Key.laStatsTodayPV]: "今日浏览", + [Key.laStatsYesterdayUV]: "昨日访客", + [Key.laStatsYesterdayPV]: "昨日浏览", + [Key.laStatsMonthPV]: "本月浏览", + [Key.laStatsTotalPV]: "累计浏览", + // 站点信息 [Key.siteInfo]: "站点信息", [Key.siteInfoBuildTime]: "构建时间", diff --git a/src/i18n/languages/zh_TW.ts b/src/i18n/languages/zh_TW.ts index 9ebec8ab3e..aa1c5e5d8e 100644 --- a/src/i18n/languages/zh_TW.ts +++ b/src/i18n/languages/zh_TW.ts @@ -343,6 +343,16 @@ export const zh_TW: Translation = { [Key.siteStatsDays]: "{days} 天", [Key.today]: "今天", + // 51LA 訪問統計 + [Key.laStats]: "訪問統計", + [Key.laStatsOnline]: "當前在線", + [Key.laStatsTodayUV]: "今日訪客", + [Key.laStatsTodayPV]: "今日瀏覽", + [Key.laStatsYesterdayUV]: "昨日訪客", + [Key.laStatsYesterdayPV]: "昨日瀏覽", + [Key.laStatsMonthPV]: "本月瀏覽", + [Key.laStatsTotalPV]: "累計瀏覽", + // 站點資訊 [Key.siteInfo]: "站點資訊", [Key.siteInfoBuildTime]: "構建時間", diff --git a/src/types/analyticsConfig.ts b/src/types/analyticsConfig.ts index 9ec276183e..5fe7f39ba0 100644 --- a/src/types/analyticsConfig.ts +++ b/src/types/analyticsConfig.ts @@ -22,5 +22,7 @@ export type AnalyticsConfig = { autoTrack?: boolean; // 开启事件分析功能,默认 true hashMode?: boolean; // 单页面应用统计(Vue/React 等),默认 false screenRecord?: boolean; // 开启网站录屏功能,默认 true + statsDataSource?: "quotejs" | "api"; // 数据挂件数据源:"quotejs" 从官方 quote.js 解析,"api" 从第三方 API 获取 JSON + statsApiUrl?: string; // 第三方 API 地址模板,仅 statsDataSource 为 "api" 时生效,使用 {id} 占位符代表 Id }; }; diff --git a/src/types/sidebarConfig.ts b/src/types/sidebarConfig.ts index 58bbd2a46b..ebc75d57f9 100644 --- a/src/types/sidebarConfig.ts +++ b/src/types/sidebarConfig.ts @@ -7,6 +7,7 @@ export type WidgetComponentType = | "sidebarToc" | "advertisement" | "stats" + | "lastats" | "calendar" | "music" | "siteInfo"