Files
gemini-balance/app/templates/keys_status.html
snaily 90161a1f47 feat(ui): 更新密钥状态页面样式和API调用详情
本次提交对密钥状态页面的样式进行了调整,主要变更包括:

- **位置调整**:
  - 将某些元素的位置从右上角移动至右下角,以改善布局。

- **API调用详情表格样式**:
  - 移除API调用详情模态框表格最后一行单元格的边框。
  - 恢复成功/失败状态颜色和图标颜色,确保在API调用详情表格中状态信息的清晰可见。

这些更改旨在提升用户界面的可用性和视觉效果,改善用户体验。
2025-05-10 12:27:35 +08:00

1607 lines
55 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %} {% block title %}API密钥状态 - Gemini Balance{%
endblock %} {% block head_extra_styles %}
<style>
/* keys_status.html specific styles */
.key-content {
transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out,
padding 0.3s ease-in-out; /* Added padding transition */
overflow: hidden; /* Keep hidden initially and during collapse */
}
.key-content.collapsed {
max-height: 0 !important; /* Use important to override inline style during transition */
opacity: 0;
padding-top: 0 !important; /* Collapse padding */
padding-bottom: 0 !important; /* Collapse padding */
/* overflow: hidden; */ /* Already set above */
}
.toggle-icon {
transition: transform 0.3s ease;
}
.toggle-icon.collapsed {
transform: rotate(-90deg);
}
/* Copy status styling is handled by base.html's notification */
/* 现代数据统计样式 */
.stats-dashboard {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
margin-bottom: 2rem;
position: relative;
z-index: 10;
}
@media (min-width: 768px) {
.stats-dashboard {
grid-template-columns: 1fr 1fr;
}
}
/* 统计卡片样式 */
.stats-card {
background-color: rgba(70, 50, 150, 0.7);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-radius: 0.75rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.15),
0 2px 4px -1px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(120, 100, 200, 0.4);
overflow: hidden;
transition: all 0.3s ease-in-out;
}
.stats-card:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2),
0 4px 6px -2px rgba(0, 0, 0, 0.1);
border-color: rgba(150, 130, 230, 0.6);
}
.stats-card-header {
background-color: rgba(80, 60, 160, 0.8);
padding: 0.75rem 1rem;
border-bottom: 1px solid rgba(120, 100, 200, 0.4);
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap; /* Allow wrapping for smaller screens */
gap: 0.5rem; /* Add gap between items */
}
.stats-card-title {
display: flex;
align-items: center;
font-size: 1rem;
font-weight: 600;
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.stats-card-title i {
margin-right: 0.5rem;
color: #c4b5fd;
}
.stats-card-header h2 {
color: #ffffff; /* 设置列表标题为白色 */
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
padding: 0.75rem;
}
/* 统计项样式 */
.stat-item {
padding: 0.75rem;
border-radius: 0.5rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
transition: all 0.3s ease-in-out;
position: relative;
overflow: hidden;
}
.stat-item::before {
content: "";
position: absolute;
inset: 0;
opacity: 0.15;
background-color: currentColor;
z-index: 0;
transition: opacity 0.3s ease-in-out;
}
.stat-item:hover::before {
opacity: 0.25;
}
.stat-item:hover {
transform: scale(1.05);
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
z-index: 10;
position: relative;
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.stat-label {
font-size: 0.75rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 0.25rem;
z-index: 10;
position: relative;
color: #e2e8f0;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.stat-icon {
position: absolute;
right: 0.5rem;
bottom: 0.25rem;
opacity: 0.15;
font-size: 1.875rem;
transform: rotate(12deg);
transition: all 0.3s ease-in-out;
}
.stat-item:hover .stat-icon {
opacity: 0.25;
transform: scale(1.1) rotate(0deg);
}
/* 统计类型样式 */
.stat-primary {
color: #c4b5fd;
background-color: rgba(107, 70, 193, 0.4);
}
.stat-success {
color: #6ee7b7;
background-color: rgba(16, 150, 100, 0.35);
}
.stat-danger {
color: #fca5b3;
background-color: rgba(225, 50, 100, 0.35);
}
.stat-warning {
color: #fde68a;
background-color: rgba(200, 160, 20, 0.35);
}
.stat-info {
color: #bfdbfe;
background-color: rgba(80, 130, 220, 0.35);
}
/* 新增调整API调用统计项的悬停背景色使其更暗更融合主题 */
.stat-item.stat-warning:hover {
background-color: rgba(
200,
160,
20,
0.55
) !important; /* 从 hover:bg-amber-100 调整 */
}
.stat-item.stat-info:hover {
background-color: rgba(
80,
130,
220,
0.55
) !important; /* 从 hover:bg-blue-100 调整 */
}
.stat-item.stat-primary:hover {
background-color: rgba(
107,
70,
193,
0.6
) !important; /* 从 hover:bg-indigo-100 调整 */
}
/* 响应式调整 */
@media (max-width: 640px) {
.stats-dashboard {
gap: 1rem;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
padding: 0.5rem;
}
.stat-item {
padding: 0.5rem;
}
.stat-value {
font-size: 1.25rem;
}
.stat-label {
font-size: 0.625rem;
}
.stats-card-header {
padding: 0.5rem 0.75rem;
} /* Adjust header padding */
.key-content ul {
grid-template-columns: 1fr;
} /* Stack keys vertically on small screens */
}
/* Tailwind Toggle Switch Helper CSS */
.toggle-checkbox:checked {
@apply: right-0 border-primary-600;
right: 0;
border-color: #4f46e5;
}
.toggle-checkbox:checked + .toggle-label {
@apply: bg-primary-600;
background-color: #4f46e5;
}
/* Pagination Controls */
#validPaginationControls,
#invalidPaginationControls {
display: flex;
justify-content: center;
align-items: center;
margin-top: 1rem; /* mt-4 */
gap: 0.5rem; /* space-x-2 */
}
/* Ensure list items are flex for alignment */
#validKeys li,
#invalidKeys li {
display: flex;
align-items: flex-start; /* Align checkbox with top of content */
gap: 0.75rem; /* gap-3 */
}
/* Ensure grid layout for key lists */
#validKeys,
#invalidKeys {
display: grid;
grid-template-columns: 1fr; /* Default single column */
gap: 0.75rem; /* gap-3 */
}
@media (min-width: 768px) {
/* md breakpoint */
#validKeys,
#invalidKeys {
grid-template-columns: repeat(
2,
1fr
); /* Two columns on medium screens and up */
}
}
/* 修改密钥列表背景和卡片样式 */
.key-content {
background-color: rgba(70, 50, 150, 0.6) !important;
}
#validKeys li,
#invalidKeys li {
background-color: rgba(80, 60, 160, 0.85);
border: 1px solid rgba(120, 100, 200, 0.4);
transition: all 0.3s ease;
cursor: pointer;
position: relative;
padding-left: 2.5rem; /* 为自定义复选框留出空间 */
}
#validKeys li:hover,
#invalidKeys li:hover {
border-color: rgba(150, 130, 230, 0.7);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
background-color: rgba(90, 70, 170, 0.9);
}
#validKeys li.selected,
#invalidKeys li.selected {
background-color: rgba(100, 80, 180, 0.95);
border-color: rgba(160, 140, 240, 0.8);
}
/* 隐藏原生复选框 */
.key-checkbox {
display: none;
}
/* 自定义复选框样式 */
#validKeys li::before,
#invalidKeys li::before {
content: "";
position: absolute;
left: 0.75rem;
top: 50%;
transform: translateY(-50%);
width: 1.25rem; /* 20px */
height: 1.25rem; /* 20px */
border: 2px solid rgba(180, 160, 250, 0.7);
border-radius: 0.375rem; /* 6px */
background-color: rgba(255, 255, 255, 0.1);
transition: all 0.2s ease-in-out;
}
#validKeys li.selected::before,
#invalidKeys li.selected::before {
background-color: #a78bfa; /* 紫色背景 */
border-color: #8b5cf6; /* 深紫色边框 */
}
/* 自定义复选框对勾样式 */
#validKeys li.selected::after,
#invalidKeys li.selected::after {
content: "\f00c"; /* Font Awesome check icon */
font-family: "Font Awesome 5 Free";
font-weight: 900;
position: absolute;
left: calc(0.75rem + 0.3rem); /* 调整对勾位置 */
top: 50%;
transform: translateY(-50%) scale(0.9);
color: white;
font-size: 0.8rem;
}
.key-text {
color: #e9d5ff !important; /* 修改API密钥颜色为淡紫色并确保优先 */
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
font-weight: 500;
}
/* 模态框背景色调整 */
#apiCallDetailsModal .bg-white,
#keyUsageDetailsModal .bg-white,
#resultModal .bg-white,
#resetModal .bg-white,
#verifyModal .bg-white {
background-color: rgba(70, 50, 150, 0.95);
color: #ffffff;
border-color: rgba(120, 100, 200, 0.4);
}
/* 模态框标题颜色 */
#apiCallDetailsModalTitle,
#keyUsageDetailsModalTitle,
#resultModalTitle,
#resetModalTitle,
#verifyModalTitle {
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
font-weight: 600;
}
/* 密钥使用详情模态框头部特定样式 */
#keyUsageDetailsModal .bg-white > div.border-b {
/* 针对头部区域的下边框 */
border-bottom-color: rgba(120, 100, 200, 0.6);
}
/* 模态框消息文本颜色 */
#apiCallDetailsContent,
#keyUsageDetailsContent,
#resultModalMessage,
#resetModalMessage,
#verifyModalMessage {
color: #f8fafc;
}
/* 特定调整操作结果模态框的消息区域样式 */
#resultModalMessage {
background-color: rgba(80, 60, 160, 0.7) !important; /* 深色背景 */
border-color: rgba(120, 100, 200, 0.5) !important; /* 协调的边框 */
color: #f8fafc !important; /* 确保浅色文本 */
}
/* 批量验证结果中普通信息列表 (如成功密钥列表) 的暗色主题样式 */
#resultModalMessage ul[class*="bg-gray-50"] {
/* 针对特定灰色背景色的ul */
background-color: rgba(
80,
60,
160,
0.3
) !important; /* 深色、透明紫色背景 */
border-color: rgba(120, 100, 200, 0.4) !important; /* 协调的边框 */
}
#resultModalMessage ul[class*="bg-gray-50"] li {
color: #e2e8f0 !important; /* 浅色文本 */
}
/* 批量验证结果中失败列表的暗色主题样式 */
#resultModalMessage ul[class*="bg-red-50"] {
/* 针对特定背景色的ul */
background-color: rgba(
127,
29,
29,
0.3
) !important; /* 深色、透明红色背景 */
border-color: rgba(153, 27, 27, 0.5) !important; /* 深红色边框 */
}
/* 失败列表中的密钥文本 (如 AIza...lJ6E) */
#resultModalMessage ul[class*="bg-red-50"] li span.font-mono {
color: #fecaca !important; /* 浅红色文本 */
}
/* 失败列表中的 "收起/展开" 按钮 */
#resultModalMessage ul[class*="bg-red-50"] li button[class*="bg-red-200"] {
background-color: rgba(185, 28, 28, 0.4) !important; /* 深红色按钮背景 */
color: #fee2e2 !important; /* 浅红色按钮文本 */
border: 1px solid rgba(220, 38, 38, 0.6) !important; /* 按钮边框 */
box-shadow: none !important;
}
#resultModalMessage
ul[class*="bg-red-50"]
li
button[class*="bg-red-200"]:hover {
background-color: rgba(200, 38, 38, 0.55) !important; /* 悬停时按钮背景 */
color: #fef2f2 !important; /* 悬停时按钮文本 */
}
/* 失败列表中的错误详情框 */
#resultModalMessage ul[class*="bg-red-50"] li div[id^="error-details-"] {
background-color: rgba(153, 27, 27, 0.35) !important; /* 错误详情深色背景 */
border-color: rgba(185, 28, 28, 0.5) !important; /* 错误详情边框 */
color: #fca5a5 !important; /* 错误详情浅红色文本 */
}
/* 密钥使用详情模态框内表格表头样式 */
#keyUsageDetailsModal #keyUsageDetailsContent table th {
background-color: rgba(80, 60, 160, 0.8); /* 与统计卡片头部背景色一致 */
border-bottom: 1px solid rgba(120, 100, 200, 0.4); /* 与统计卡片头部边框颜色一致 */
color: #e2e8f0 !important; /* 确保文字颜色为浅色 */
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); /* 保持文本阴影一致性 */
}
#keyUsageDetailsModal #keyUsageDetailsContent table td {
color: #f8fafc !important; /* 设置浅色文本,!important确保覆盖Tailwind类 */
/* 如果需要,可以添加 text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 来增加细微的深度感 */
}
/* 按钮文本颜色 */
.stats-card button {
color: #ffffff;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
/* 表单控件背景 */
.form-input,
.form-select {
background-color: rgba(80, 60, 160, 0.8);
color: #ffffff;
border-color: rgba(120, 100, 200, 0.5);
}
/* 标签文字颜色 */
.text-gray-500,
.text-gray-600,
.text-gray-700 {
color: #e2e8f0 !important;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
/* 调整全局背景色,使之与紫色背景更加协调 */
.glass-card {
background-color: rgba(80, 60, 160, 0.3) !important;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(150, 130, 230, 0.3);
}
/* 分页控件样式增强 */
.pagination-button {
background-color: rgba(80, 60, 160, 0.8);
color: #ffffff;
border: 1px solid rgba(120, 100, 200, 0.4);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.pagination-button.active {
background-color: rgba(120, 100, 200, 0.9);
border-color: rgba(150, 130, 230, 0.7);
}
/* 状态标签增强 */
.inline-flex.items-center.px-2\.5.py-0\.5.rounded-full {
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
/* 按钮颜色增强 */
button.bg-success-600,
button.bg-blue-600,
button.bg-slate-500,
button.bg-purple-600,
button.bg-primary-700,
button.bg-teal-600 {
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
/* 复选框文本样式 */
input[type="checkbox"] + label {
color: #f1f5f9 !important;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
/* 底部版权栏文本颜色修正 */
.fixed.bottom-0.left-0.w-full.py-3.bg-white .text-gray-200,
.fixed.bottom-0.left-0.w-full.py-3.bg-white .text-gray-300,
.fixed.bottom-0.left-0.w-full.py-3.bg-white .text-gray-400,
.fixed.bottom-0.left-0.w-full.py-3.bg-white .text-gray-500,
.fixed.bottom-0.left-0.w-full.py-3.bg-white .text-gray-600,
.fixed.bottom-0.left-0.w-full.py-3.bg-white .text-gray-700 {
color: #1f2937 !important; /* 使用更深的灰色提高对比度 */
text-shadow: none !important;
}
/* 导航链接悬停样式 (从 config_editor.html 复制) */
.nav-link {
transition: all 0.2s ease-in-out;
}
.nav-link:hover {
background-color: rgba(120, 100, 200, 0.6) !important;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
/* 新增:密钥列表内按钮背景和悬停颜色调整 */
/* 验证按钮 (绿色) */
#validKeys li button.bg-success-600,
#invalidKeys li button.bg-success-600 {
background-color: rgba(22, 163, 74, 0.65) !important;
border: 1px solid rgba(22, 163, 74, 0.85);
}
#validKeys li button.bg-success-600:hover,
#invalidKeys li button.bg-success-600:hover {
background-color: rgba(21, 128, 61, 0.75) !important;
border-color: rgba(21, 128, 61, 0.95);
}
/* 重置按钮 (蓝色) */
#validKeys li button.bg-blue-600,
#invalidKeys li button.bg-blue-600 {
background-color: rgba(37, 99, 235, 0.65) !important;
border: 1px solid rgba(37, 99, 235, 0.85);
}
#validKeys li button.bg-blue-600:hover,
#invalidKeys li button.bg-blue-600:hover {
background-color: rgba(29, 78, 216, 0.75) !important;
border-color: rgba(29, 78, 216, 0.95);
}
/* 复制按钮 (灰色) */
#validKeys li button.bg-slate-500,
#invalidKeys li button.bg-slate-500 {
background-color: rgba(100, 116, 139, 0.65) !important;
border: 1px solid rgba(100, 116, 139, 0.85);
}
#validKeys li button.bg-slate-500:hover,
#invalidKeys li button.bg-slate-500:hover {
background-color: rgba(71, 85, 105, 0.75) !important;
border-color: rgba(71, 85, 105, 0.95);
}
/* 详情按钮 (紫色) */
#validKeys li button.bg-purple-600,
#invalidKeys li button.bg-purple-600 {
background-color: rgba(
147,
51,
234,
0.6
) !important; /* Tailwind purple-600 is 9333ea. Using purple-500 (a855f7) as base for rgba */
border: 1px solid rgba(147, 51, 234, 0.8);
}
#validKeys li button.bg-purple-600:hover,
#invalidKeys li button.bg-purple-600:hover {
background-color: rgba(
126,
34,
206,
0.7
) !important; /* Tailwind purple-600 as base for hover */
border-color: rgba(126, 34, 206, 0.9);
}
/* 删除按钮 (红色) - HTML中使用 bg-red-800 */
#validKeys li button.bg-red-800,
#invalidKeys li button.bg-red-800 {
background-color: rgba(185, 28, 28, 0.65) !important; /* Based on red-700 */
border: 1px solid rgba(185, 28, 28, 0.85);
}
#validKeys li button.bg-red-800:hover,
#invalidKeys li button.bg-red-800:hover {
background-color: rgba(153, 27, 27, 0.75) !important; /* Based on red-800 */
border-color: rgba(153, 27, 27, 0.95);
}
/* 新增:密钥列表内状态标签颜色调整 */
/* 有效标签 (绿色) */
#validKeys li span.bg-success-50.text-success-600,
#invalidKeys li span.bg-success-50.text-success-600 {
background-color: rgba(16, 185, 129, 0.28) !important;
color: #86efac !important; /* Slightly lighter green for text */
border: 1px solid rgba(16, 185, 129, 0.45);
}
/* 失败计数标签 (黄色) */
#validKeys li span.bg-amber-50.text-amber-600,
#invalidKeys li span.bg-amber-50.text-amber-600 {
background-color: rgba(245, 158, 11, 0.28) !important;
color: #fde047 !important; /* Slightly lighter amber for text */
border: 1px solid rgba(245, 158, 11, 0.45);
position: absolute; /* 移动到右下角 */
bottom: 0.75rem; /* 配合li的p-3内边距 */
right: 0.75rem;
z-index: 5; /* 确保在其他元素之上 */
}
/* 无效标签 (红色) - for invalidKeys list */
#invalidKeys li span.bg-danger-50.text-danger-600 {
background-color: rgba(239, 68, 68, 0.28) !important;
color: #fca5a5 !important; /* Standard danger text color often works well */
border: 1px solid rgba(239, 68, 68, 0.45);
}
/* Remove border from the last row's cells in API Call Details Modal table */
#apiCallDetailsContent table tr:last-child td {
border-bottom: none !important;
}
/* Restore success/failure status colors and icon colors within the API call details table */
#apiCallDetailsContent table td span[class*="text-success"],
#apiCallDetailsContent table td span[class*="success"] {
color: #6ee7b7 !important; /* Theme success green */
}
#apiCallDetailsContent table td span[class*="text-success"] i,
#apiCallDetailsContent table td span[class*="success"] i {
color: #6ee7b7 !important;
}
#apiCallDetailsContent table td span[class*="text-danger"],
#apiCallDetailsContent table td span[class*="failure"],
#apiCallDetailsContent table td span[class*="danger"] {
color: #fca5b3 !important; /* Theme failure red */
}
#apiCallDetailsContent table td span[class*="text-danger"] i,
#apiCallDetailsContent table td span[class*="failure"] i,
#apiCallDetailsContent table td span[class*="danger"] i {
color: #fca5b3 !important;
}
/* End of API Call Details Modal Specific Styling Adjustments */
</style>
{% endblock %} {% block head_extra_scripts %}
<!-- keys_status.js needs to be loaded in head because it might be used by inline scripts -->
<script src="/static/js/keys_status.js"></script>
{% endblock %} {% block content %}
<div class="container max-w-6xl mx-auto px-4">
<!-- Increased max-width -->
<div class="glass-card rounded-2xl shadow-xl p-6 md:p-8">
<div class="absolute top-6 right-6 flex items-center gap-3">
<!-- 自动刷新开关 -->
<div class="flex items-center text-sm text-gray-600 select-none">
<span class="mr-2">自动刷新</span>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="autoRefreshToggle"
id="autoRefreshToggle"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="autoRefreshToggle"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<!-- 手动刷新按钮 -->
<button
class="bg-white bg-opacity-20 hover:bg-opacity-30 rounded-full w-8 h-8 flex items-center justify-center text-primary-600 transition-all duration-300"
onclick="refreshPage(this)"
title="手动刷新"
>
<i class="fas fa-sync-alt"></i>
</button>
</div>
<h1
class="text-3xl font-extrabold text-center text-transparent bg-clip-text bg-gradient-to-r from-violet-400 to-pink-400 mb-4"
>
<img
src="/static/icons/logo.png"
alt="Gemini Balance Logo"
class="h-9 inline-block align-middle mr-2"
/>
Gemini Balance - 监控面板
</h1>
<!-- Navigation Tabs -->
<div class="flex justify-center mb-8 overflow-x-auto pb-2 gap-2">
<a
href="/config"
class="nav-link whitespace-nowrap flex items-center justify-center gap-2 px-6 py-3 font-medium rounded-lg text-gray-200 hover:text-white transition-all duration-200"
style="background-color: rgba(107, 70, 193, 0.4)"
>
<i class="fas fa-cog"></i> 配置编辑
</a>
<a
href="/keys"
class="whitespace-nowrap flex items-center justify-center gap-2 px-6 py-3 font-medium rounded-lg bg-violet-600 text-white shadow-md"
>
<i class="fas fa-tachometer-alt"></i> 监控面板
</a>
<a
href="/logs"
class="nav-link whitespace-nowrap flex items-center justify-center gap-2 px-6 py-3 font-medium rounded-lg text-gray-200 hover:text-white transition-all duration-200"
style="background-color: rgba(107, 70, 193, 0.4)"
>
<i class="fas fa-exclamation-triangle"></i> 错误日志
</a>
</div>
<!-- 现代化统计面板 -->
<div class="stats-dashboard animate-fade-in" style="animation-delay: 0.1s">
<!-- 密钥统计卡片 -->
<div class="stats-card">
<div class="stats-card-header">
<h3 class="stats-card-title">
<i class="fas fa-key"></i>
<span>密钥统计</span>
</h3>
<span class="text-xs text-gray-500">总计: {{ total_keys }}</span>
</div>
<div class="stats-grid">
<div class="stat-item stat-primary" title="总密钥数">
<div class="stat-value">{{ total_keys }}</div>
<div class="stat-label">总密钥数</div>
<i class="stat-icon fas fa-key"></i>
</div>
<div class="stat-item stat-success" title="有效密钥">
<div class="stat-value">{{ valid_key_count }}</div>
<div class="stat-label">有效密钥</div>
<i class="stat-icon fas fa-check-circle"></i>
</div>
<div class="stat-item stat-danger" title="无效密钥">
<div class="stat-value">{{ invalid_key_count }}</div>
<div class="stat-label">无效密钥</div>
<i class="stat-icon fas fa-times-circle"></i>
</div>
</div>
</div>
<!-- API调用统计卡片 -->
<div class="stats-card">
<div class="stats-card-header">
<h3 class="stats-card-title">
<i class="fas fa-chart-line"></i>
<span>API调用统计</span>
</h3>
<span class="text-xs text-gray-500"
>本月: {{ api_stats.calls_month.total }}</span
>
</div>
<div class="stats-grid">
<div
class="stat-item stat-warning cursor-pointer hover:bg-amber-100"
title="点击查看1分钟内调用详情"
data-period="1m"
onclick="showApiCallDetails('1m', '{{ api_stats.calls_1m.total }}', '{{ api_stats.calls_1m.success }}', '{{ api_stats.calls_1m.failure }}')"
>
<div class="stat-value">{{ api_stats.calls_1m.total }}</div>
<div class="stat-label">1分钟调用</div>
<i class="stat-icon fas fa-stopwatch"></i>
</div>
<div
class="stat-item stat-info cursor-pointer hover:bg-blue-100"
title="点击查看1小时内调用详情"
data-period="1h"
onclick="showApiCallDetails('1h', '{{ api_stats.calls_1h.total }}', '{{ api_stats.calls_1h.success }}', '{{ api_stats.calls_1h.failure }}')"
>
<div class="stat-value">{{ api_stats.calls_1h.total }}</div>
<div class="stat-label">1小时调用</div>
<i class="stat-icon fas fa-hourglass-half"></i>
</div>
<div
class="stat-item stat-primary cursor-pointer hover:bg-indigo-100"
title="点击查看24小时内调用详情"
data-period="24h"
onclick="showApiCallDetails('24h', '{{ api_stats.calls_24h.total }}', '{{ api_stats.calls_24h.success }}', '{{ api_stats.calls_24h.failure }}')"
>
<div class="stat-value">{{ api_stats.calls_24h.total }}</div>
<div class="stat-label">24小时调用</div>
<i class="stat-icon fas fa-calendar-day"></i>
</div>
</div>
</div>
</div>
<!-- 有效密钥区域 -->
<div class="stats-card mb-6 animate-fade-in" style="animation-delay: 0.2s">
<div
class="stats-card-header cursor-pointer"
onclick="toggleSection(this, 'validKeys')"
>
<!-- Left side: Title and Toggle Icon -->
<div class="flex items-center gap-3 flex-shrink-0">
<!-- Prevent shrinking -->
<i class="fas fa-chevron-down toggle-icon text-primary-600"></i>
<i class="fas fa-check-circle text-success-500"></i>
<h2 class="text-lg font-semibold whitespace-nowrap">
有效密钥列表 ({{ valid_key_count }})
</h2>
</div>
<!-- Middle: Filters and Search (Allow wrapping) -->
<div
class="flex items-center gap-x-4 gap-y-2 flex-grow flex-wrap justify-start md:justify-center"
>
<!-- Allow wrapping, center on medium+ -->
<!-- 失败次数筛选 -->
<div class="flex items-center gap-1">
<label
for="failCountThreshold"
class="text-sm text-gray-600 select-none whitespace-nowrap"
>失败次数≥</label
>
<input
type="number"
id="failCountThreshold"
value="0"
min="0"
class="form-input h-7 w-16 px-2 py-1 text-sm border rounded focus:ring-primary-500 focus:border-primary-500"
onclick="event.stopPropagation();"
/>
</div>
<!-- 密钥搜索 -->
<div class="flex items-center gap-1">
<label
for="keySearchInput"
class="text-sm text-gray-600 select-none whitespace-nowrap"
><i class="fas fa-search mr-1"></i>搜索</label
>
<input
type="search"
id="keySearchInput"
placeholder="输入密钥..."
class="form-input h-7 w-32 px-2 py-1 text-sm border rounded focus:ring-primary-500 focus:border-primary-500"
onclick="event.stopPropagation();"
/>
</div>
<!-- 每页显示数量 -->
<div class="flex items-center gap-1">
<label
for="itemsPerPageSelect"
class="text-sm text-gray-600 select-none whitespace-nowrap"
>每页</label
>
<select
id="itemsPerPageSelect"
class="form-select h-7 px-2 py-1 text-sm border rounded focus:ring-primary-500 focus:border-primary-500"
onclick="event.stopPropagation();"
>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<span class="text-sm text-gray-600 select-none"></span>
</div>
</div>
<!-- Right side: Select All -->
<div
class="flex items-center gap-1 flex-shrink-0"
onclick="event.stopPropagation();"
>
<!-- Prevent shrinking -->
<input
type="checkbox"
id="selectAllValid"
class="form-checkbox h-4 w-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
onchange="toggleSelectAll('valid', this.checked)"
/>
<label
for="selectAllValid"
class="text-sm text-gray-600 select-none whitespace-nowrap"
>全选</label
>
</div>
</div>
<!-- 批量操作按钮组 (仅在选中时显示) -->
<div
id="validBatchActions"
class="p-3 border-t hidden flex items-center flex-wrap gap-3"
style="
background-color: rgba(80, 60, 160, 0.8);
border-color: rgba(120, 100, 200, 0.4);
"
>
<!-- Added flex-wrap -->
<span class="text-sm font-medium text-gray-200 whitespace-nowrap"
>已选择 <span id="validSelectedCount">0</span></span
>
<button
class="flex items-center gap-2 bg-teal-600 hover:bg-teal-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); showVerifyModal('valid', event)"
disabled
>
<i class="fas fa-check-double"></i> 批量验证
</button>
<button
class="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); resetAllKeysFailCount('valid', event)"
data-reset-type="valid"
disabled
>
<i class="fas fa-redo-alt"></i> 批量重置
</button>
<button
class="flex items-center gap-2 bg-primary-700 hover:bg-primary-800 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); copySelectedKeys('valid')"
disabled
>
<i class="fas fa-copy"></i> 批量复制
</button>
<button
class="flex items-center gap-2 bg-red-800 hover:bg-red-900 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); showDeleteConfirmationModal('valid', event)"
disabled
>
<i class="fas fa-trash-alt"></i> 批量删除
</button>
</div>
<div class="key-content p-4 bg-white bg-opacity-40">
<!-- Key list will be populated by JS -->
<ul id="validKeys" class="grid grid-cols-1 md:grid-cols-2 gap-3">
{# Initial keys rendered by server-side for non-JS users or initial
load #} {# JS will replace this content with paginated/filtered
results #} {% if valid_keys %} {% for key, fail_count in
valid_keys.items() %}
<li
class="bg-white rounded-lg p-3 shadow-sm hover:shadow-md transition-all duration-300 border border-gray-100 hover:border-success-300 transform hover:-translate-y-1"
data-fail-count="{{ fail_count }}"
data-key="{{ key }}"
>
<!-- Checkbox -->
<input
type="checkbox"
class="form-checkbox h-5 w-5 text-primary-600 border-gray-300 rounded focus:ring-primary-500 mt-1 key-checkbox"
data-key-type="valid"
value="{{ key }}"
/>
<!-- Key Info -->
<div class="flex-grow">
<div class="flex flex-col justify-between h-full gap-3">
<div class="flex flex-wrap items-center gap-2">
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-success-50 text-success-600"
>
<i class="fas fa-check mr-1"></i> 有效
</span>
<div class="flex items-center gap-1">
<span class="key-text font-mono" data-full-key="{{ key }}"
>{{ key[:4] + '...' + key[-4:] }}</span
>
<button
class="text-gray-500 hover:text-primary-600 transition-colors"
onclick="toggleKeyVisibility(this)"
title="显示/隐藏密钥"
>
<i class="fas fa-eye"></i>
</button>
</div>
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-amber-50 text-amber-600"
>
<i class="fas fa-exclamation-triangle mr-1"></i>
失败: {{ fail_count }}
</span>
</div>
<div class="flex flex-wrap items-center gap-2">
<button
class="flex items-center gap-1 bg-success-600 hover:bg-success-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="verifyKey('{{ key }}', this)"
>
<i class="fas fa-check-circle"></i>
验证
</button>
<button
class="flex items-center gap-1 bg-blue-600 hover:bg-blue-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="resetKeyFailCount('{{ key }}', this)"
>
<i class="fas fa-redo-alt"></i>
重置
</button>
<button
class="flex items-center gap-1 bg-slate-500 hover:bg-slate-600 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="copyKey('{{ key }}')"
>
<i class="fas fa-copy"></i>
复制
</button>
<button
class="flex items-center gap-1 bg-purple-600 hover:bg-purple-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="showKeyUsageDetails('{{ key }}')"
>
<i class="fas fa-chart-pie"></i>
详情
</button>
<button
class="flex items-center gap-1 bg-red-800 hover:bg-red-900 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="showSingleKeyDeleteConfirmModal('{{ key }}', this)"
>
<i class="fas fa-trash-alt"></i>
删除
</button>
</div>
</div>
</div>
</li>
{% endfor %} {% else %}
<li class="text-center text-gray-500 py-4 col-span-full">
暂无有效密钥
</li>
{% endif %}
</ul>
<!-- 有效密钥分页控件容器 -->
<div
id="validPaginationControls"
class="flex justify-center items-center mt-4 space-x-2"
>
<!-- Pagination controls will be generated by JS -->
</div>
</div>
</div>
<!-- 无效密钥区域 -->
<div class="stats-card mb-6 animate-fade-in" style="animation-delay: 0.4s">
<div
class="stats-card-header cursor-pointer"
onclick="toggleSection(this, 'invalidKeys')"
>
<!-- Left side: Title and Toggle Icon -->
<div class="flex items-center gap-3 flex-shrink-0">
<!-- Prevent shrinking -->
<i class="fas fa-chevron-down toggle-icon text-primary-600"></i>
<i class="fas fa-times-circle text-danger-500"></i>
<h2 class="text-lg font-semibold whitespace-nowrap">
无效密钥列表 ({{ invalid_key_count }})
</h2>
</div>
<!-- Right side: Select All -->
<div
class="flex items-center gap-1 ml-auto flex-shrink-0"
onclick="event.stopPropagation();"
>
<!-- Use ml-auto, Prevent shrinking -->
<input
type="checkbox"
id="selectAllInvalid"
class="form-checkbox h-4 w-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
onchange="toggleSelectAll('invalid', this.checked)"
/>
<label
for="selectAllInvalid"
class="text-sm text-gray-600 select-none whitespace-nowrap"
>全选</label
>
</div>
</div>
<!-- 批量操作按钮组 (仅在选中时显示) -->
<div
id="invalidBatchActions"
class="p-3 border-t hidden flex items-center flex-wrap gap-3"
style="
background-color: rgba(80, 60, 160, 0.8);
border-color: rgba(120, 100, 200, 0.4);
"
>
<!-- Added flex-wrap -->
<span class="text-sm font-medium text-gray-200 whitespace-nowrap"
>已选择 <span id="invalidSelectedCount">0</span></span
>
<button
class="flex items-center gap-2 bg-teal-600 hover:bg-teal-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); showVerifyModal('invalid', event)"
disabled
>
<i class="fas fa-check-double"></i> 批量验证
</button>
<button
class="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); resetAllKeysFailCount('invalid', event)"
data-reset-type="invalid"
disabled
>
<i class="fas fa-redo-alt"></i> 批量重置
</button>
<button
class="flex items-center gap-2 bg-primary-700 hover:bg-primary-800 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); copySelectedKeys('invalid')"
disabled
>
<i class="fas fa-copy"></i> 批量复制
</button>
<button
class="flex items-center gap-2 bg-red-800 hover:bg-red-900 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
onclick="event.stopPropagation(); showDeleteConfirmationModal('invalid', event)"
disabled
>
<i class="fas fa-trash-alt"></i> 批量删除
</button>
</div>
<div class="key-content p-4 bg-white bg-opacity-40">
<!-- Key list will be populated by JS -->
<ul id="invalidKeys" class="grid grid-cols-1 md:grid-cols-2 gap-3">
{# Initial keys rendered by server-side #} {# JS will replace this
content with paginated results #} {% if invalid_keys %} {% for key,
fail_count in invalid_keys.items() %}
<li
class="bg-white rounded-lg p-3 shadow-sm hover:shadow-md transition-all duration-300 border border-gray-100 hover:border-danger-300 transform hover:-translate-y-1"
data-key="{{ key }}"
>
<!-- Checkbox -->
<input
type="checkbox"
class="form-checkbox h-5 w-5 text-primary-600 border-gray-300 rounded focus:ring-primary-500 mt-1 key-checkbox"
data-key-type="invalid"
value="{{ key }}"
/>
<!-- Key Info -->
<div class="flex-grow">
<div class="flex flex-col justify-between h-full gap-3">
<div class="flex flex-wrap items-center gap-2">
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-danger-50 text-danger-600"
>
<i class="fas fa-times mr-1"></i> 无效
</span>
<div class="flex items-center gap-1">
<span class="key-text font-mono" data-full-key="{{ key }}"
>{{ key[:4] + '...' + key[-4:] }}</span
>
<button
class="text-gray-500 hover:text-primary-600 transition-colors"
onclick="toggleKeyVisibility(this)"
title="显示/隐藏密钥"
>
<i class="fas fa-eye"></i>
</button>
</div>
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-amber-50 text-amber-600"
>
<i class="fas fa-exclamation-triangle mr-1"></i>
失败: {{ fail_count }}
</span>
</div>
<div class="flex flex-wrap items-center gap-2">
<button
class="flex items-center gap-1 bg-success-600 hover:bg-success-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="verifyKey('{{ key }}', this)"
>
<i class="fas fa-check-circle"></i>
验证
</button>
<button
class="flex items-center gap-1 bg-blue-600 hover:bg-blue-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="resetKeyFailCount('{{ key }}', this)"
>
<i class="fas fa-redo-alt"></i>
重置
</button>
<button
class="flex items-center gap-1 bg-slate-500 hover:bg-slate-600 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="copyKey('{{ key }}')"
>
<i class="fas fa-copy"></i>
复制
</button>
<button
class="flex items-center gap-1 bg-purple-600 hover:bg-purple-700 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="showKeyUsageDetails('{{ key }}')"
>
<i class="fas fa-chart-pie"></i>
详情
</button>
<button
class="flex items-center gap-1 bg-red-800 hover:bg-red-900 text-white px-2.5 py-1 rounded-lg text-xs font-medium transition-all duration-200"
onclick="showSingleKeyDeleteConfirmModal('{{ key }}', this)"
>
<i class="fas fa-trash-alt"></i>
删除
</button>
</div>
</div>
</div>
</li>
{% endfor %} {% else %}
<li class="text-center text-gray-500 py-4 col-span-full">
暂无无效密钥
</li>
{% endif %}
</ul>
<!-- 无效密钥分页控件容器 -->
<div
id="invalidPaginationControls"
class="flex justify-center items-center mt-4 space-x-2"
>
<!-- Pagination controls will be generated by JS -->
</div>
</div>
</div>
<!-- Removed old total keys display -->
</div>
</div>
<!-- Scroll buttons are now in base.html -->
<div class="scroll-buttons">
<button class="scroll-button" onclick="scrollToTop()" title="回到顶部">
<i class="fas fa-chevron-up"></i>
</button>
<button class="scroll-button" onclick="scrollToBottom()" title="滚动到底部">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<!-- Notification component is now in base.html (use id="notification") -->
<div id="notification" class="notification"></div>
<!-- 重置确认模态框 -->
<div
id="resetModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="bg-white rounded-lg p-6 shadow-xl max-w-md w-full animate-fade-in"
>
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-800" id="resetModalTitle">
批量重置失败次数
</h3>
<button
onclick="closeResetModal()"
class="text-gray-500 hover:text-gray-700 focus:outline-none"
>
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-6">
<p class="text-gray-600" id="resetModalMessage"></p>
</div>
<div class="flex justify-end gap-3">
<button
onclick="closeResetModal()"
class="px-3 py-1.5 text-xs font-medium bg-slate-500 hover:bg-slate-600 text-white rounded-lg transition-colors"
>
取消
</button>
<button
id="confirmResetBtn"
class="px-3 py-1.5 text-xs font-medium bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
>
确认
</button>
</div>
</div>
</div>
<!-- 验证确认模态框 -->
<div
id="verifyModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="bg-white rounded-lg p-6 shadow-xl max-w-md w-full animate-fade-in"
style="
background-color: rgba(70, 50, 150, 0.95);
color: #ffffff;
border-color: rgba(120, 100, 200, 0.4);
"
>
<div class="flex items-center justify-between mb-4">
<h3
class="text-lg font-semibold"
id="verifyModalTitle"
style="
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
font-weight: 600;
"
>
批量验证密钥
</h3>
<button
onclick="closeVerifyModal()"
class="text-gray-300 hover:text-white focus:outline-none"
>
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-6">
<p style="color: #f8fafc" id="verifyModalMessage"></p>
</div>
<div class="flex justify-end gap-3">
<button
onclick="closeVerifyModal()"
class="px-3 py-1.5 text-xs font-medium bg-slate-600 hover:bg-slate-700 text-white rounded-lg transition-colors"
>
取消
</button>
<button
id="confirmVerifyBtn"
class="px-3 py-1.5 text-xs font-medium bg-teal-700 hover:bg-teal-800 text-white rounded-lg transition-colors"
>
确认验证
</button>
</div>
</div>
</div>
<!-- 删除确认模态框 -->
<div
id="deleteConfirmModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="bg-white rounded-lg p-6 shadow-xl max-w-md w-full animate-fade-in"
style="
background-color: rgba(70, 50, 150, 0.95);
color: #ffffff;
border-color: rgba(120, 100, 200, 0.4);
"
>
<div class="flex items-center justify-between mb-4">
<h3
class="text-lg font-semibold"
id="deleteConfirmModalTitle"
style="
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
font-weight: 600;
"
>
确认删除
</h3>
<button
onclick="closeDeleteConfirmationModal()"
class="text-gray-300 hover:text-white focus:outline-none"
>
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-6">
<p style="color: #f8fafc" id="deleteConfirmModalMessage"></p>
</div>
<div class="flex justify-end gap-3">
<button
onclick="closeDeleteConfirmationModal()"
class="px-3 py-1.5 text-xs font-medium bg-slate-600 hover:bg-slate-700 text-white rounded-lg transition-colors"
>
取消
</button>
<button
id="confirmDeleteBtn"
class="px-3 py-1.5 text-xs font-medium bg-red-700 hover:bg-red-800 text-white rounded-lg transition-colors"
>
确认删除
</button>
</div>
</div>
</div>
<!-- 新增:单个密钥删除确认模态框 -->
<div
id="singleKeyDeleteConfirmModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="rounded-lg p-6 shadow-xl max-w-md w-full animate-fade-in"
style="
background-color: rgba(70, 50, 150, 0.95);
color: #ffffff;
border-color: rgba(120, 100, 200, 0.4);
"
>
<div class="flex items-center justify-between mb-4">
<h3
class="text-lg font-semibold"
id="singleKeyDeleteConfirmModalTitle"
style="
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
font-weight: 600;
"
>
确认删除密钥
</h3>
<button
onclick="closeSingleKeyDeleteConfirmModal()"
class="text-gray-300 hover:text-white focus:outline-none"
>
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-6">
<p style="color: #f8fafc" id="singleKeyDeleteConfirmModalMessage"></p>
</div>
<div class="flex justify-end gap-3">
<button
onclick="closeSingleKeyDeleteConfirmModal()"
class="px-3 py-1.5 text-xs font-medium bg-slate-600 hover:bg-slate-700 text-white rounded-lg transition-colors"
>
取消
</button>
<button
id="confirmSingleKeyDeleteBtn"
class="px-3 py-1.5 text-xs font-medium bg-red-700 hover:bg-red-800 text-white rounded-lg transition-colors"
>
确认删除
</button>
</div>
</div>
</div>
<!-- 操作结果模态框 -->
<div
id="resultModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="bg-white rounded-2xl p-0 shadow-2xl max-w-lg w-full animate-fade-in border border-gray-200"
>
<div class="flex items-center justify-between px-6 pt-6 pb-2 border-b">
<h3
class="text-xl font-bold text-gray-800 text-center w-full"
id="resultModalTitle"
style="letter-spacing: 0.05em"
>
操作结果
</h3>
<button
onclick="closeResultModal()"
class="absolute right-6 top-6 text-gray-400 hover:text-gray-700 focus:outline-none text-2xl"
>
<i class="fas fa-times"></i>
</button>
</div>
<div class="flex flex-col items-center px-8 pt-6 pb-2">
<div id="resultIcon" class="text-6xl mb-3"></div>
</div>
<div class="px-8 pb-2 w-full">
<div
id="resultModalMessage"
class="text-gray-700 text-base leading-relaxed break-words whitespace-pre-line max-h-80 overflow-y-auto border border-gray-100 rounded-lg bg-gray-50 p-4 shadow-inner"
style="
font-family: 'JetBrains Mono', 'Fira Mono', 'Consolas', 'monospace';
"
>
<!-- Content is dynamically generated by JS -->
</div>
</div>
<div class="flex justify-center px-8 pb-6 pt-2">
<button
id="resultModalConfirmBtn"
onclick="closeResultModal()"
class="px-5 py-1.5 text-sm font-semibold bg-primary-700 hover:bg-primary-800 text-white rounded-lg shadow transition-colors"
>
确定
</button>
</div>
</div>
</div>
<!-- API 调用详情模态框 -->
<div
id="apiCallDetailsModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="bg-white rounded-lg p-6 shadow-xl max-w-3xl w-full animate-fade-in"
>
<!-- Increased max-width -->
<div class="flex items-center justify-between mb-4 border-b pb-3">
<h3
class="text-xl font-semibold text-gray-800"
id="apiCallDetailsModalTitle"
>
API 调用详情
</h3>
<button
onclick="closeApiCallDetailsModal()"
class="text-gray-500 hover:text-gray-700 focus:outline-none text-xl"
>
<i class="fas fa-times"></i>
</button>
</div>
<div
id="apiCallDetailsContent"
class="mb-6 max-h-[60vh] overflow-y-auto pr-2"
>
<!-- Increased max-height and added padding-right -->
<!-- 详细数据将加载到这里 -->
<div class="text-center py-10">
<i class="fas fa-spinner fa-spin text-primary-600 text-3xl"></i>
<p class="text-gray-500 mt-2">加载中...</p>
</div>
</div>
<div class="flex justify-end pt-4 border-t">
<button
onclick="closeApiCallDetailsModal()"
class="px-4 py-1.5 text-xs font-medium bg-slate-500 hover:bg-slate-600 text-white rounded-lg transition-colors"
>
关闭
</button>
</div>
</div>
</div>
<!-- 密钥使用详情模态框 -->
<div
id="keyUsageDetailsModal"
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"
>
<div
class="bg-white rounded-lg p-6 shadow-xl max-w-lg w-full animate-fade-in"
>
<!-- Adjusted max-width -->
<div class="flex items-center justify-between mb-4 border-b pb-3">
<h3
class="text-xl font-semibold text-gray-800"
id="keyUsageDetailsModalTitle"
>
密钥请求详情
</h3>
<button
onclick="closeKeyUsageDetailsModal()"
class="text-gray-500 hover:text-gray-700 focus:outline-none text-xl"
>
<i class="fas fa-times"></i>
</button>
</div>
<div
id="keyUsageDetailsContent"
class="mb-6 max-h-[50vh] overflow-y-auto pr-2"
>
<!-- Adjusted max-height -->
<!-- 详细数据将加载到这里 -->
<div class="text-center py-10">
<i class="fas fa-spinner fa-spin text-primary-600 text-3xl"></i>
<p class="text-gray-500 mt-2">加载中...</p>
</div>
</div>
<div class="flex justify-end pt-4 border-t">
<button
onclick="closeKeyUsageDetailsModal()"
class="px-4 py-1.5 text-xs font-medium bg-slate-500 hover:bg-slate-600 text-white rounded-lg transition-colors"
>
关闭
</button>
</div>
</div>
</div>
<!-- Footer is now in base.html -->
{% endblock %} {% block body_scripts %}
<script>
// keys_status.html specific JavaScript initialization is now handled by keys_status.js
// The DOMContentLoaded listener in keys_status.js will execute after the DOM is ready.
// No inline script needed here anymore.
</script>
{% endblock %}