plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint) plugins/friendManager:友情链接 CRUD plugins/aboutManager:关于本站 CRUD plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点 applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入 applications/view/public/about.py:关于本站公开页 applications/view/public/friend.py:友情链接公开页 templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字) templates/public/index.html / category.html:卡片右下角访问次数 templates/public/about.html / friend.html:关于 + 友链前台页 docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移) scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
170 lines
6.6 KiB
HTML
170 lines
6.6 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>访问统计</title>
|
|
{% include 'system/common/header.html' %}
|
|
</head>
|
|
<body class="pear-container">
|
|
|
|
<div class="layui-row layui-col-space16" style="margin-top:12px;">
|
|
<div class="layui-col-md3">
|
|
<div class="layui-card">
|
|
<div class="layui-card-body layui-text-center">
|
|
<div style="font-size:32px;color:var(--brand);font-weight:600;" id="stat-total-pv">--</div>
|
|
<div style="color:#888;margin-top:6px;">累计 PV(页面浏览)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="layui-col-md3">
|
|
<div class="layui-card">
|
|
<div class="layui-card-body layui-text-center">
|
|
<div style="font-size:32px;color:#5fb878;font-weight:600;" id="stat-total-uv">--</div>
|
|
<div style="color:#888;margin-top:6px;">累计 UV(独立访客)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="layui-col-md3">
|
|
<div class="layui-card">
|
|
<div class="layui-card-body layui-text-center">
|
|
<div style="font-size:32px;color:#ffb800;font-weight:600;" id="stat-today-pv">--</div>
|
|
<div style="color:#888;margin-top:6px;">今日 PV</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="layui-col-md3">
|
|
<div class="layui-card">
|
|
<div class="layui-card-body layui-text-center">
|
|
<div style="font-size:32px;color:#ff5722;font-weight:600;" id="stat-today-uv">--</div>
|
|
<div style="color:#888;margin-top:6px;">今日 UV</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="layui-card" style="margin-top:16px;">
|
|
<div class="layui-card-header">
|
|
<i class="layui-icon layui-icon-template-1"></i> 最近 7 天趋势
|
|
<select id="days-select" style="float:right;font-size:13px;height:32px;border-radius:4px;padding:0 8px;border:1px solid #ddd;">
|
|
<option value="7">近 7 天</option>
|
|
<option value="14">近 14 天</option>
|
|
<option value="30">近 30 天</option>
|
|
</select>
|
|
</div>
|
|
<div class="layui-card-body">
|
|
<div id="chart-days" style="height:280px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="layui-row layui-col-space16" style="margin-top:16px;">
|
|
<div class="layui-col-md7">
|
|
<div class="layui-card">
|
|
<div class="layui-card-header"><i class="layui-icon layui-icon-link"></i> 路径 TOP10</div>
|
|
<div class="layui-card-body">
|
|
<table class="layui-table" lay-skin="line">
|
|
<thead>
|
|
<tr><th>路径</th><th width="100">PV</th><th width="200">最近访问</th></tr>
|
|
</thead>
|
|
<tbody id="paths-tbody">
|
|
<tr><td colspan="3" class="layui-text-center" style="color:#999;">加载中…</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="layui-col-md5">
|
|
<div class="layui-card">
|
|
<div class="layui-card-header"><i class="layui-icon layui-icon-time"></i> 最近访问</div>
|
|
<div class="layui-card-body">
|
|
<table class="layui-table" lay-skin="line">
|
|
<thead>
|
|
<tr><th>路径</th><th width="120">IP</th><th width="160">时间</th></tr>
|
|
</thead>
|
|
<tbody id="latest-tbody">
|
|
<tr><td colspan="3" class="layui-text-center" style="color:#999;">加载中…</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
{% include 'system/common/footer.html' %}
|
|
<script src="{{ url_for('static', filename='system/component/pear/module/extends/echarts.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='system/component/pear/module/extends/echartsTheme.js') }}"></script>
|
|
<script>
|
|
layui.use(['element', 'jquery'], function(){
|
|
var $ = layui.$;
|
|
var daysChart = echarts.init(document.getElementById('chart-days'));
|
|
|
|
function loadSummary(){
|
|
$.get('/system/stats/summary', function(res){
|
|
if (res.code === 0){
|
|
$('#stat-total-pv').text(res.data.pv_total);
|
|
$('#stat-total-uv').text(res.data.uv_total);
|
|
$('#stat-today-pv').text(res.data.today_pv);
|
|
$('#stat-today-uv').text(res.data.today_uv);
|
|
var rows = res.data.latest || [];
|
|
var html = '';
|
|
if (rows.length === 0){
|
|
html = '<tr><td colspan="3" class="layui-text-center" style="color:#999;">暂无访问</td></tr>';
|
|
} else {
|
|
rows.forEach(function(r){
|
|
html += '<tr><td>' + (r.path || '-') + '</td><td>' + (r.ip || '-') + '</td><td>' + (r.visit_at || '-') + '</td></tr>';
|
|
});
|
|
}
|
|
$('#latest-tbody').html(html);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadDays(n){
|
|
$.get('/system/stats/days?days=' + n, function(res){
|
|
if (res.code === 0){
|
|
var data = res.data || [];
|
|
daysChart.setOption({
|
|
tooltip: {trigger: 'axis'},
|
|
legend: {data: ['PV', 'UV']},
|
|
xAxis: {type: 'category', data: data.map(function(d){return d.day.slice(5);})},
|
|
yAxis: {type: 'value'},
|
|
series: [
|
|
{name: 'PV', type: 'line', smooth: true, data: data.map(function(d){return d.pv;})},
|
|
{name: 'UV', type: 'line', smooth: true, data: data.map(function(d){return d.uv;})}
|
|
]
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadPaths(){
|
|
$.get('/system/stats/paths', function(res){
|
|
if (res.code === 0){
|
|
var rows = res.data || [];
|
|
var html = '';
|
|
if (rows.length === 0){
|
|
html = '<tr><td colspan="3" class="layui-text-center" style="color:#999;">暂无数据</td></tr>';
|
|
} else {
|
|
rows.forEach(function(r){
|
|
html += '<tr><td>' + (r.path || '-') + '</td><td><b>' + (r.pv || 0) + '</b></td><td>' + (r.last_hit || '-') + '</td></tr>';
|
|
});
|
|
}
|
|
$('#paths-tbody').html(html);
|
|
}
|
|
});
|
|
}
|
|
|
|
$('#days-select').on('change', function(){
|
|
loadDays(parseInt($(this).val(), 10));
|
|
});
|
|
|
|
loadSummary();
|
|
loadDays(7);
|
|
loadPaths();
|
|
|
|
window.addEventListener('resize', function(){ daysChart.resize(); });
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|