初始化技术博客项目
功能特性: - 前端文章展示 (Markdown渲染、代码高亮) - 后台管理系统 (文章、分类、标签、作者管理) - 图片上传 (点击、拖拽、粘贴) - 网站设置 (Logo、底部宣传图片上传) - 访问统计 (IP记录、趋势图表) - 参考来源、附件支持 - 单端口部署 (16012) 技术栈: - Flask 3.0 + SQLAlchemy - SQLite数据库 - marked.js + highlight.js (Markdown渲染) - Chart.js (统计图表)
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
{% 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 %}
|
||||
Reference in New Issue
Block a user