前端调用Umami API数据并渲染展示
Umami是一个高颜值可自部署的统计应用。小道也自豪的采用Umami进行的来访统计,较为真实、连贯,也比较能看得懂。为了更好的让用户访问数据能够较为完善的展现出来,小道也设计了一个“统计”页面,下面我来分享前端调用Umami API数据并渲染展示的全过程。
第一步、获取Token
小道是使用下列工具获取Token的
主要的是选择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; // 获取今天、昨天、本月和本年的起始时间戳(毫秒级) $startTimestampToday = strtotime("today 00:00:00") * 1000; $startTimestampYesterday = strtotime("yesterday 00:00:00") * 1000; // 获取本月起始时间戳 $startTimestampThisMonth = strtotime("first day of this month 00:00:00") * 1000; // 获取本年起始时间戳 $startTimestampThisYear = strtotime("first day of January this year 00:00:00") * 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); $thisMonthData = fetchUmamiData($apiBaseUrl, $websiteId, $startTimestampThisMonth, $currentTimestamp, $token); $thisYearData = fetchUmamiData($apiBaseUrl, $websiteId, $startTimestampThisYear, $currentTimestamp, $token); // 如果请求失败,返回错误信息 if ($todayData === null || $yesterdayData === null || $thisMonthData === null || $thisYearData === 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, "this_month_uv" => $thisMonthData['visitors']['value'] ?? null, "this_month_pv" => $thisMonthData['pageviews']['value'] ?? null, "this_year_uv" => $thisYearData['visitors']['value'] ?? null, "this_year_pv" => $thisYearData['pageviews']['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.this_month_uv, pvLabel: '本月(PV)', pv: data.this_month_pv }, { uvLabel: '今年(UV)', uv: data.this_year_uv, pvLabel: '今年(PV)', pv: data.this_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();
好了,小道语言组织能力差,基本上按照上面我写的内容,基本上就基本上的大功告成了,下面请欣赏: