Files
tech-blog/app/templates/admin/stats_logs.html
hz4th_coder e689fed71e 初始化技术博客项目
功能特性:
- 前端文章展示 (Markdown渲染、代码高亮)
- 后台管理系统 (文章、分类、标签、作者管理)
- 图片上传 (点击、拖拽、粘贴)
- 网站设置 (Logo、底部宣传图片上传)
- 访问统计 (IP记录、趋势图表)
- 参考来源、附件支持
- 单端口部署 (16012)

技术栈:
- Flask 3.0 + SQLAlchemy
- SQLite数据库
- marked.js + highlight.js (Markdown渲染)
- Chart.js (统计图表)
2026-06-04 23:12:34 +08:00

172 lines
4.6 KiB
HTML

{% extends 'admin/base.html' %}
{% block title %}访问日志{% endblock %}
{% block page_title %}访问日志{% endblock %}
{% block content %}
<div class="stats-tabs">
<a href="{{ url_for('admin.stats') }}" class="tab">概览</a>
<a href="{{ url_for('admin.stats_charts') }}" class="tab">趋势图表</a>
<a href="{{ url_for('admin.stats_logs') }}" class="tab active">访问日志</a>
</div>
<div class="toolbar">
<form method="GET" class="search-form">
<input type="text" name="path" value="{{ path_filter }}"
placeholder="筛选路径...">
<button type="submit" class="btn btn-secondary">筛选</button>
{% if path_filter %}
<a href="{{ url_for('admin.stats_logs') }}" class="btn btn-secondary">清除</a>
{% endif %}
</form>
<form action="{{ url_for('admin.stats_clear') }}" method="POST" class="clear-form">
<input type="number" name="days" value="90" min="30" max="365" style="width: 80px;">
<button type="submit" class="btn btn-danger"
onclick="return confirm('确定要清理旧日志吗?')">清理旧日志</button>
</form>
</div>
{% if logs.items %}
<table class="data-table">
<thead>
<tr>
<th width="15%">IP地址</th>
<th width="25%">访问路径</th>
<th width="15%">User Agent</th>
<th width="15%">来源页面</th>
<th width="20%">访问时间</th>
<th width="10%">文章</th>
</tr>
</thead>
<tbody>
{% for log in logs.items %}
<tr>
<td>{{ log.ip_address }}</td>
<td>{{ log.path }}</td>
<td>
<span class="user-agent" title="{{ log.user_agent }}">
{{ (log.user_agent or '-')[:50] }}{% if log.user_agent and len(log.user_agent) > 50 %}...{% endif %}
</span>
</td>
<td>
{% if log.referer %}
<a href="{{ log.referer }}" target="_blank" title="{{ log.referer }}">
{{ log.referer[:30] }}...
</a>
{% else %}
直接访问
{% endif %}
</td>
<td>{{ log.visited_at|datetime_format('%Y-%m-%d %H:%M:%S') }}</td>
<td>
{% if log.article %}
<a href="{{ url_for('admin.article_edit', article_id=log.article.id) }}">
{{ log.article.title[:20] }}...
</a>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 分页 -->
{% if logs.pages > 1 %}
<div class="pagination">
{% if logs.has_prev %}
<a href="{{ url_for('admin.stats_logs', page=logs.prev_num, path=path_filter) }}"
class="page-link">上一页</a>
{% endif %}
{% for page in logs.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
{% if page %}
{% if page == logs.page %}
<span class="page-link active">{{ page }}</span>
{% else %}
<a href="{{ url_for('admin.stats_logs', page=page, path=path_filter) }}"
class="page-link">{{ page }}</a>
{% endif %}
{% else %}
<span class="page-link ellipsis">...</span>
{% endif %}
{% endfor %}
{% if logs.has_next %}
<a href="{{ url_for('admin.stats_logs', page=logs.next_num, path=path_filter) }}"
class="page-link">下一页</a>
{% endif %}
</div>
{% endif %}
{% else %}
<div class="empty-state">
<p>暂无访问日志</p>
</div>
{% endif %}
<style>
.stats-tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
background: var(--admin-card-bg);
border-radius: 5px;
color: var(--admin-text);
border: 1px solid var(--admin-border);
}
.tab:hover {
text-decoration: none;
background: var(--admin-primary);
color: #fff;
}
.tab.active {
background: var(--admin-primary);
color: #fff;
border-color: var(--admin-primary);
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.search-form {
display: flex;
gap: 10px;
}
.search-form input {
padding: 8px 12px;
border: 1px solid var(--admin-border);
border-radius: 4px;
}
.clear-form {
display: flex;
gap: 10px;
align-items: center;
}
.clear-form input {
padding: 8px 12px;
border: 1px solid var(--admin-border);
border-radius: 4px;
}
.user-agent {
color: var(--admin-text-light);
font-size: 0.85rem;
}
</style>
{% endblock %}