Umami是一个高颜值可自部署的统计应用。小道也自豪的采用Umami进行的来访统计,较为真实、连贯,也比较能看得懂。为了更好的让用户访问数据能够较为完善的展现出来,小道也设计了一个“统计”页面,下面我来分享前端调用Umami API数据并渲染展示的全过程。
第一步、获取Token
小道是使用下列工具获取Token的
https://hoppscotch.io/
主要的是选择post,然后点击到body,然后的然后是Content Type选择application/json输入
{
"username": "Umami管理账号",
"password": "Umami管理密码"
}
单后点击Send,就会得到JSON的token。
第二步、获取 websiteId
在Umami的“设置”>“网站”选择需要统计渲染的站点,复制“网站ID”。
第三步、前端调用全站数据
PHP部分,用于存储umami_cache.json,作前端数据动态化调用。
<?php
header('Content-Type: application/json');
header("Access-Control-Allow-Origin: *");
// 配置 Umami API 的凭据
$apiBaseUrl = '已部署Umami统计站点';
$token = '第一步获得的Umamitoken';
$websiteId = '需要获取的网站ID'; // 在这里定义 websiteId
$cacheFile = 'umami_cache.json'; // 保存的json即使更新数据
$cacheTime = 600; // 缓存时间为10分钟(600秒)
// 获取当前时间戳(毫秒级)
$currentTimestamp = time() * 1000;
// Umami API 的起始时间戳(毫秒级)
$startTimestampToday = strtotime("today") * 1000;
$startTimestampYesterday = strtotime("yesterday") * 1000;
// 获取上个月的时间范围
$startTimestampLastMonth = strtotime("first day of last month") * 1000;
$endTimestampLastMonth = strtotime("last day of last month 23:59:59") * 1000;
// 获取去年全年的时间范围
$startTimestampLastYear = strtotime("first day of January last year") * 1000;
$endTimestampLastYear = strtotime("last day of December last year 23:59:59") * 1000;
// 定义 Umami API 请求函数
function fetchUmamiData($apiBaseUrl, $websiteId, $startAt, $endAt, $token) {
$url = "$apiBaseUrl/api/websites/$websiteId/stats?" . http_build_query([
'startAt' => $startAt,
'endAt' => $endAt
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $token",
"Content-Type: application/json"
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Curl error: ' . curl_error($ch);
curl_close($ch);
return null;
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode != 200) {
echo "HTTP error code: $httpCode\n";
curl_close($ch);
return null;
}
curl_close($ch);
return json_decode($response, true);
}
// 检查缓存文件是否存在且未过期
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
// 读取缓存文件
$cachedData = file_get_contents($cacheFile);
echo $cachedData;
} else {
// 获取统计数据
$todayData = fetchUmamiData($apiBaseUrl, $websiteId, $startTimestampToday, $currentTimestamp, $token);
$yesterdayData = fetchUmamiData($apiBaseUrl, $websiteId, $startTimestampYesterday, $startTimestampToday, $token);
$lastMonthData = fetchUmamiData($apiBaseUrl, $websiteId, $startTimestampLastMonth, $endTimestampLastMonth, $token);
$lastYearData = fetchUmamiData($apiBaseUrl, $websiteId, $startTimestampLastYear, $endTimestampLastYear, $token);
// 如果请求失败,返回错误信息
if ($todayData === null || $yesterdayData === null || $lastMonthData === null || $lastYearData === null) {
echo json_encode(["error" => "Failed to fetch data from Umami API"]);
exit;
}
// 组装返回的 JSON 数据
$responseData = [
"today_uv" => $todayData['visitors']['value'] ?? null,
"today_pv" => $todayData['pageviews']['value'] ?? null,
"yesterday_uv" => $yesterdayData['visitors']['value'] ?? null,
"yesterday_pv" => $yesterdayData['pageviews']['value'] ?? null,
"last_month_pv" => $lastMonthData['pageviews']['value'] ?? null,
"last_month_uv" => $lastMonthData['visitors']['value'] ?? null,
"last_year_pv" => $lastYearData['pageviews']['value'] ?? null,
"last_year_uv" => $lastYearData['visitors']['value'] ?? null
];
// 将数据写入缓存文件
file_put_contents($cacheFile, json_encode($responseData));
// 输出 JSON 数据
echo json_encode($responseData);
}
?>
上述几部,大功告成,我会先保存一个json数据组,然后再调用。然后在前端所想要展示的地方加一个<div id="main"></div>,使用Javascript来做数据组化,调用的所保存的json的数据。
Javascript部分,作前端div及数据渲染组合。
async function loadAndDisplayData() {
try {
const response = await fetch('./umami_cache.json');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
// 获取主容器
const mainContainer = document.getElementById('main');
// 清除主容器中的现有内容
mainContainer.innerHTML = '';
// 定义一个函数来创建包含UV和PV的div对
function createStatsPair(uvLabel, uvValue, pvLabel, pvValue) {
const pairContainer = document.createElement('div');
pairContainer.className = 'tongji'; // 使用CSS类来设置样式
const uvDiv = document.createElement('div');
uvDiv.className = 'uvDiv sidecmtcon'; // 设置UV的div类名
uvDiv.textContent = `${uvLabel}: ${uvValue}`;
const pvDiv = document.createElement('div');
pvDiv.className = 'pvDiv sidecmtcon'; // 设置PV的div类名
pvDiv.textContent = `${pvLabel}: ${pvValue}`;
pairContainer.appendChild(uvDiv);
pairContainer.appendChild(pvDiv);
return pairContainer;
}
// 创建UV和PV的div对并添加到主容器中
const statsPairs = [
{ uvLabel: '今日(UV)', uv: data.today_uv, pvLabel: '今日(PV)', pv: data.today_pv },
{ uvLabel: '昨日(UV)', uv: data.yesterday_uv, pvLabel: '昨日(PV)', pv: data.yesterday_pv },
{ uvLabel: '本月(UV)', uv: data.last_month_uv, pvLabel: '本月(PV)', pv: data.last_month_pv }
// { uvLabel: '今年(UV)', uv: data.last_year_uv, pvLabel: '今年(PV)', pv: data.last_year_pv }
];
statsPairs.forEach(pair => {
const pairDiv = createStatsPair(pair.uvLabel, pair.uv, pair.pvLabel, pair.pv);
mainContainer.appendChild(pairDiv);
});
} catch (error) {
console.error('Error loading or parsing data:', error);
// 显示错误消息
document.getElementById('main').textContent = 'Error loading data';
}
}
// 调用函数加载并显示数据
loadAndDisplayData();
好了,小道语言组织能力差,基本上按照上面我写的内容,基本上就基本上的大功告成了,下面请欣赏:
https://www.dao.js.cn/tongji