246 lines
11 KiB
HTML
246 lines
11 KiB
HTML
{% extends "admin/base.html" %}
|
|
|
|
{% block title %}操作日志 - 太白购物商城管理后台{% endblock %}
|
|
|
|
{% block page_title %}操作日志{% endblock %}
|
|
{% block page_description %}查看系统操作日志,监控用户和管理员行为{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<link href="{{ url_for('static', filename='css/admin_logs.css') }}" rel="stylesheet">
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="admin-logs">
|
|
<!-- 筛选条件 -->
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<form method="GET" class="row g-3">
|
|
<div class="col-md-3">
|
|
<label for="user_type" class="form-label">用户类型</label>
|
|
<select class="form-select" id="user_type" name="user_type">
|
|
<option value="">全部类型</option>
|
|
<option value="1" {% if user_type == '1' %}selected{% endif %}>普通用户</option>
|
|
<option value="2" {% if user_type == '2' %}selected{% endif %}>管理员</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="action" class="form-label">操作类型</label>
|
|
<input type="text" class="form-control" id="action" name="action"
|
|
value="{{ action }}" placeholder="搜索操作类型">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label"> </label>
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-search"></i> 搜索
|
|
</button>
|
|
<a href="{{ url_for('admin.logs') }}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-clockwise"></i> 重置
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 日志统计 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card stats-card">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h5 class="card-title">{{ logs.total }}</h5>
|
|
<p class="card-text">总日志数</p>
|
|
</div>
|
|
<div class="icon-wrapper primary">
|
|
<i class="bi bi-journal-text"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card stats-card">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h5 class="card-title">{{ logs.items | selectattr('user_type', 'equalto', 1) | list | length }}</h5>
|
|
<p class="card-text">用户操作</p>
|
|
</div>
|
|
<div class="icon-wrapper info">
|
|
<i class="bi bi-person"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card stats-card">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h5 class="card-title">{{ logs.items | selectattr('user_type', 'equalto', 2) | list | length }}</h5>
|
|
<p class="card-text">管理员操作</p>
|
|
</div>
|
|
<div class="icon-wrapper warning">
|
|
<i class="bi bi-shield-check"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card stats-card">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h5 class="card-title">{{ today_logs_count }}</h5>
|
|
<p class="card-text">今日操作</p>
|
|
</div>
|
|
<div class="icon-wrapper success">
|
|
<i class="bi bi-clock"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 日志列表 -->
|
|
<div class="card">
|
|
<div class="card-header bg-white">
|
|
<h5 class="mb-0">
|
|
<i class="bi bi-journal-text"></i>
|
|
操作日志
|
|
<small class="text-muted ms-2">共 {{ logs.total }} 条记录</small>
|
|
</h5>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th width="15%">时间</th>
|
|
<th width="10%">操作者</th>
|
|
<th width="15%">操作类型</th>
|
|
<th width="20%">操作内容</th>
|
|
<th width="15%">IP地址</th>
|
|
<th width="25%">用户代理</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% if logs.items %}
|
|
{% for log in logs.items %}
|
|
<tr>
|
|
<td>
|
|
<div>{{ log.created_at.strftime('%Y-%m-%d') if log.created_at else '-' }}</div>
|
|
<small class="text-muted">{{ log.created_at.strftime('%H:%M:%S') if log.created_at else '' }}</small>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<span class="badge bg-{{ 'warning' if log.user_type == 2 else 'info' }} me-2">
|
|
{{ '管理员' if log.user_type == 2 else '用户' }}
|
|
</span>
|
|
<span class="fw-bold">#{{ log.user_id or '-' }}</span>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="operation-action">{{ log.action }}</span>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
{% if log.resource_type %}
|
|
<span class="resource-type">{{ log.resource_type }}</span>
|
|
{% if log.resource_id %}
|
|
<span class="resource-id">#{{ log.resource_id }}</span>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="text-muted">-</span>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<code>{{ log.ip_address or '-' }}</code>
|
|
</td>
|
|
<td>
|
|
<div class="user-agent-wrapper">
|
|
{% if log.user_agent %}
|
|
<span class="user-agent" title="{{ log.user_agent }}">
|
|
{{ log.user_agent[:50] }}{% if log.user_agent|length > 50 %}...{% endif %}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted">-</span>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="6" class="text-center py-4">
|
|
<div class="empty-state">
|
|
<i class="bi bi-journal-x"></i>
|
|
<div>暂无操作日志</div>
|
|
{% if user_type or action %}
|
|
<small class="text-muted">尝试调整筛选条件</small>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
{% if logs.pages > 1 %}
|
|
<div class="card-footer bg-white">
|
|
<nav aria-label="操作日志分页">
|
|
<ul class="pagination justify-content-center mb-0">
|
|
{% if logs.has_prev %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('admin.logs', page=logs.prev_num, user_type=user_type, action=action) }}">
|
|
<i class="bi bi-chevron-left"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for page_num in logs.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
|
|
{% if page_num %}
|
|
{% if page_num != logs.page %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('admin.logs', page=page_num, user_type=user_type, action=action) }}">
|
|
{{ page_num }}
|
|
</a>
|
|
</li>
|
|
{% else %}
|
|
<li class="page-item active">
|
|
<span class="page-link">{{ page_num }}</span>
|
|
</li>
|
|
{% endif %}
|
|
{% else %}
|
|
<li class="page-item disabled">
|
|
<span class="page-link">…</span>
|
|
</li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if logs.has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="{{ url_for('admin.logs', page=logs.next_num, user_type=user_type, action=action) }}">
|
|
<i class="bi bi-chevron-right"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script src="{{ url_for('static', filename='js/admin_logs.js') }}"></script>
|
|
{% endblock %}
|