certmanager/templates/certificate_list.html
2025-06-15 09:21:44 +08:00

215 lines
8.8 KiB
HTML

{% extends "base.html" %}
{% block title %}证书列表{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h2 class="mb-1">证书列表</h2>
<div class="text-muted fs-6">
<i class="fas fa-certificate me-1"></i>共 {{ total }} 个证书
</div>
</div>
<div>
<button id="batchDeleteBtn" class="btn btn-danger btn-sm me-2" disabled>
<i class="fas fa-trash-alt me-1"></i> 批量删除
</button>
<a href="{{ url_for('create_certificate_view') }}" class="btn btn-primary btn-sm text-nowrap">
<i class="fas fa-plus me-1"></i> 创建证书
</a>
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th width="40" class="ps-4">
<input type="checkbox" id="selectAll" class="form-check-input">
</th>
<th>ID</th>
<th>通用名</th>
<th>CA机构</th>
<th>状态</th>
<th>有效期至</th>
<th>创建者</th>
<th>创建时间</th>
<th class="pe-4">操作</th>
</tr>
</thead>
<tbody>
{% for cert in certificates %}
<tr>
<td class="ps-4">
<input type="checkbox" class="form-check-input cert-checkbox" value="{{ cert.id }}">
</td>
<td>{{ cert.id }}</td>
<td>
<a href="{{ url_for('certificate_detail', cert_id=cert.id) }}" class="text-decoration-none">
{{ cert.common_name }}
</a>
</td>
<td>{{ cert.ca_name }}</td>
<td>
{% if cert.status == 'active' %}
<span class="badge bg-success rounded-pill">
<i class="fas fa-check-circle me-1"></i> 有效
</span>
{% elif cert.status == 'revoked' %}
<span class="badge bg-danger rounded-pill">
<i class="fas fa-ban me-1"></i> 已吊销
</span>
{% else %}
<span class="badge bg-secondary rounded-pill">
<i class="fas fa-clock me-1"></i> 已过期
</span>
{% endif %}
</td>
<td>{{ cert.expires_at.strftime('%Y-%m-%d') }}</td>
<td>{{ get_username(cert.created_by) }}</td>
<td>{{ cert.created_at.strftime('%Y-%m-%d') }}</td>
<td class="pe-4">
<div class="btn-group btn-group-sm">
<a href="{{ url_for('certificate_detail', cert_id=cert.id) }}"
class="btn btn-outline-primary"
data-bs-toggle="tooltip"
title="查看详情">
<i class="fas fa-eye"></i>
</a>
<a href="{{ url_for('export_certificate_view', cert_id=cert.id) }}"
class="btn btn-outline-success"
data-bs-toggle="tooltip"
title="导出证书">
<i class="fas fa-download"></i>
</a>
{% if cert.status == 'active' %}
<a href="{{ url_for('revoke_certificate_view', cert_id=cert.id) }}"
class="btn btn-outline-warning"
data-bs-toggle="tooltip"
title="吊销证书">
<i class="fas fa-ban"></i>
</a>
{% endif %}
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="8" class="text-center py-4">
<i class="fas fa-certificate fa-3x text-muted mb-3"></i>
<p class="text-muted">暂无证书记录</p>
<a href="{{ url_for('create_certificate_view') }}" class="btn btn-primary btn-sm">
<i class="fas fa-plus me-2"></i> 创建证书
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if total_pages > 1 %}
<div class="card-footer bg-white">
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center mb-0">
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('certificate_list', page=page-1) }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% for p in range(1, total_pages + 1) %}
<li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" href="{{ url_for('certificate_list', page=p) }}">{{ p }}</a>
</li>
{% endfor %}
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('certificate_list', page=page+1) }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script>
document.addEventListener('DOMContentLoaded', function() {
// 启用工具提示
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
// 全选/取消全选
const selectAll = document.getElementById('selectAll')
const checkboxes = document.querySelectorAll('.cert-checkbox')
const batchDeleteBtn = document.getElementById('batchDeleteBtn')
selectAll.addEventListener('change', function() {
checkboxes.forEach(checkbox => {
checkbox.checked = selectAll.checked
})
updateBatchDeleteBtn()
})
// 单个复选框变化时更新全选状态
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
selectAll.checked = [...checkboxes].every(cb => cb.checked)
updateBatchDeleteBtn()
})
})
// 更新批量删除按钮状态
function updateBatchDeleteBtn() {
const checkedCount = document.querySelectorAll('.cert-checkbox:checked').length
batchDeleteBtn.disabled = checkedCount === 0
}
// 批量删除
batchDeleteBtn.addEventListener('click', function() {
const checkedBoxes = document.querySelectorAll('.cert-checkbox:checked')
const ids = Array.from(checkedBoxes).map(checkbox => checkbox.value)
if (ids.length === 0) {
return
}
if (!confirm(`确定要删除选中的 ${ids.length} 个证书吗?`)) {
return
}
fetch("{{ url_for('batch_delete_certificates') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ ids: ids })
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(data.message)
window.location.reload()
} else {
alert(data.message)
}
})
.catch(error => {
console.error('Error:', error)
alert('操作失败,请稍后再试')
})
})
})
</script>
{% endblock %}