diff --git a/app/static/js/keys_status.js b/app/static/js/keys_status.js index 209bcc2..d46ef86 100644 --- a/app/static/js/keys_status.js +++ b/app/static/js/keys_status.js @@ -108,8 +108,14 @@ function initStatItemAnimations() { // 获取指定类型区域内选中的密钥 function getSelectedKeys(type) { + let selectorRoot; + if (type === 'attention') { + selectorRoot = '#attentionKeysList'; + } else { + selectorRoot = `#${type}Keys`; + } const checkboxes = document.querySelectorAll( - `#${type}Keys .key-checkbox:checked` + `${selectorRoot} .key-checkbox:checked` ); return Array.from(checkboxes).map((cb) => cb.value); } @@ -119,27 +125,27 @@ function updateBatchActions(type) { const selectedKeys = getSelectedKeys(type); const count = selectedKeys.length; const batchActionsDiv = document.getElementById(`${type}BatchActions`); + if (!batchActionsDiv) return; const selectedCountSpan = document.getElementById(`${type}SelectedCount`); const buttons = batchActionsDiv.querySelectorAll("button"); if (count > 0) { batchActionsDiv.classList.remove("hidden"); - selectedCountSpan.textContent = count; + if (selectedCountSpan) selectedCountSpan.textContent = count; buttons.forEach((button) => (button.disabled = false)); } else { batchActionsDiv.classList.add("hidden"); - selectedCountSpan.textContent = "0"; + if (selectedCountSpan) selectedCountSpan.textContent = "0"; buttons.forEach((button) => (button.disabled = true)); } // 更新全选复选框状态 - const selectAllCheckbox = document.getElementById( - `selectAll${type.charAt(0).toUpperCase() + type.slice(1)}` - ); - const allCheckboxes = document.querySelectorAll(`#${type}Keys .key-checkbox`); + const selectAllId = `selectAll${type.charAt(0).toUpperCase() + type.slice(1)}`; + const selectAllCheckbox = document.getElementById(selectAllId); + const rootId = type === 'attention' ? 'attentionKeysList' : `${type}Keys`; // 只有在有可见的 key 时才考虑全选状态 const visibleCheckboxes = document.querySelectorAll( - `#${type}Keys li:not([style*="display: none"]) .key-checkbox` + `#${rootId} li:not([style*="display: none"]) .key-checkbox` ); if (selectAllCheckbox && visibleCheckboxes.length > 0) { selectAllCheckbox.checked = count === visibleCheckboxes.length; @@ -153,29 +159,28 @@ function updateBatchActions(type) { // 全选/取消全选指定类型的密钥 function toggleSelectAll(type, isChecked) { - const listElement = document.getElementById(`${type}Keys`); - // Select checkboxes within LI elements that are NOT styled with display:none - // This targets currently visible items based on filtering. + const rootId = type === 'attention' ? 'attentionKeysList' : `${type}Keys`; + const listElement = document.getElementById(rootId); + if (!listElement) return; const visibleCheckboxes = listElement.querySelectorAll( `li:not([style*="display: none"]) .key-checkbox` ); visibleCheckboxes.forEach((checkbox) => { checkbox.checked = isChecked; - const listItem = checkbox.closest("li[data-key]"); // Get the LI from the current DOM + const listItem = checkbox.closest("li[data-key]"); if (listItem) { listItem.classList.toggle("selected", isChecked); - - // Sync with master array - const key = listItem.dataset.key; - const masterList = type === "valid" ? allValidKeys : allInvalidKeys; - if (masterList) { - // Ensure masterList is defined - const masterListItem = masterList.find((li) => li.dataset.key === key); - if (masterListItem) { - const masterCheckbox = masterListItem.querySelector(".key-checkbox"); - if (masterCheckbox) { - masterCheckbox.checked = isChecked; + if (type !== 'attention') { + const key = listItem.dataset.key; + const masterList = type === "valid" ? allValidKeys : allInvalidKeys; + if (masterList) { + const masterListItem = masterList.find((li) => li.dataset.key === key); + if (masterListItem) { + const masterCheckbox = masterListItem.querySelector(".key-checkbox"); + if (masterCheckbox) { + masterCheckbox.checked = isChecked; + } } } } @@ -1162,20 +1167,21 @@ function initializeKeySelectionListeners() { if (listItem) { listItem.classList.toggle("selected", checkbox.checked); - // Sync with master array - const key = listItem.dataset.key; - const masterList = - keyType === "valid" ? allValidKeys : allInvalidKeys; - if (masterList) { - // Ensure masterList is defined - const masterListItem = masterList.find( - (li) => li.dataset.key === key - ); - if (masterListItem) { - const masterCheckbox = - masterListItem.querySelector(".key-checkbox"); - if (masterCheckbox) { - masterCheckbox.checked = checkbox.checked; + // Sync with master array (only for valid/invalid lists) + if (keyType !== 'attention') { + const key = listItem.dataset.key; + const masterList = + keyType === "valid" ? allValidKeys : allInvalidKeys; + if (masterList) { + const masterListItem = masterList.find( + (li) => li.dataset.key === key + ); + if (masterListItem) { + const masterCheckbox = + masterListItem.querySelector(".key-checkbox"); + if (masterCheckbox) { + masterCheckbox.checked = checkbox.checked; + } } } } @@ -1187,6 +1193,7 @@ function initializeKeySelectionListeners() { setupEventListenersForList("validKeys", "valid"); setupEventListenersForList("invalidKeys", "invalid"); + setupEventListenersForList("attentionKeysList", "attention"); } @@ -1579,33 +1586,43 @@ async function fetchAndRenderAttentionKeys(statusCode = 429, limit = 10) { listEl.innerHTML = ''; if (!data || (Array.isArray(data) && data.length === 0) || data.error) { listEl.innerHTML = '
  • 暂无需要注意的Key
  • '; + updateBatchActions('attention'); return; } data.forEach(item => { const li = document.createElement('li'); li.className = 'flex items-center justify-between bg-white rounded border px-3 py-2'; + li.dataset.key = item.key || ''; const masked = item.key ? `${item.key.substring(0,4)}...${item.key.substring(item.key.length-4)}` : 'N/A'; const code = item.status_code ?? statusCode; li.innerHTML = ` - \u003cdiv class=\"flex items-center gap-3\"\u003e - \u003cspan class=\"font-mono text-sm\"\u003e${masked}\u003c/span\u003e - \u003cspan class=\"text-xs bg-red-100 text-red-700 px-2 py-0.5 rounded\"\u003e${code}: ${item.count}\u003c/span\u003e - \u003c/div\u003e - \u003cdiv class=\"flex items-center gap-2\"\u003e - \u003cbutton class=\"px-2 py-1 text-xs rounded bg-success-600 hover:bg-success-700 text-white\" title=\"验证此Key\"\u003e验证\u003c/button\u003e - \u003cbutton class=\"px-2 py-1 text-xs rounded bg-blue-600 hover:bg-blue-700 text-white\" title=\"查看24小时详情\"\u003e详情\u003c/button\u003e - \u003cbutton class=\"px-2 py-1 text-xs rounded bg-blue-500 hover:bg-blue-600 text-white\" title=\"复制Key\"\u003e复制\u003c/button\u003e - \u003cbutton class=\"px-2 py-1 text-xs rounded bg-red-800 hover:bg-red-900 text-white\" title=\"删除此Key\"\u003e删除\u003c/button\u003e - \u003c/div\u003e`; +
    + + ${masked} + ${code}: ${item.count} +
    +
    + + + + +
    `; const [verifyBtn, detailBtn, copyBtn, deleteBtn] = li.querySelectorAll('button'); - verifyBtn.addEventListener('click', (e) => verifyKey(item.key, e.currentTarget)); - detailBtn.addEventListener('click', () => window.showKeyUsageDetails(item.key)); - copyBtn.addEventListener('click', () => copyKey(item.key)); - deleteBtn.addEventListener('click', (e) => showSingleKeyDeleteConfirmModal(item.key, e.currentTarget)); + verifyBtn.addEventListener('click', (e) => { e.stopPropagation(); verifyKey(item.key, e.currentTarget); }); + detailBtn.addEventListener('click', (e) => { e.stopPropagation(); window.showKeyUsageDetails(item.key); }); + copyBtn.addEventListener('click', (e) => { e.stopPropagation(); copyKey(item.key); }); + deleteBtn.addEventListener('click', (e) => { e.stopPropagation(); showSingleKeyDeleteConfirmModal(item.key, e.currentTarget); }); + // Checkbox change updates batch actions + const checkbox = li.querySelector('.key-checkbox'); + if (checkbox) { + checkbox.addEventListener('change', () => updateBatchActions('attention')); + } listEl.appendChild(li); }); + updateBatchActions('attention'); } catch (e) { listEl.innerHTML = `
  • 加载失败: ${e.message}
  • `; + updateBatchActions('attention'); } } diff --git a/app/templates/keys_status.html b/app/templates/keys_status.html index 343f62c..0a5de82 100644 --- a/app/templates/keys_status.html +++ b/app/templates/keys_status.html @@ -322,12 +322,13 @@ endblock %} {% block head_extra_styles %} border-color: rgba(59, 130, 246, 0.3); } - /* 隐藏原生复选框 */ - .key-checkbox { + /* 隐藏原生复选框(仅隐藏有效/无效列表中的复选框,保留值得注意的Key列表中的复选框可见) */ + #validKeys .key-checkbox, + #invalidKeys .key-checkbox { display: none; } - /* 自定义复选框样式 */ + /* 自定义复选框样式(仅针对有效/无效列表) */ #validKeys li::before, #invalidKeys li::before { content: ""; @@ -363,6 +364,31 @@ endblock %} {% block head_extra_styles %} font-size: 0.8rem; } + /* 值得注意的Key列表样式与选中态(保留原生复选框可见) */ + #attentionKeysList li { + display: flex; + align-items: center; + justify-content: space-between; + background-color: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 0.5rem; + padding: 0.5rem 0.75rem; + transition: all 0.2s ease; + cursor: pointer; + } + #attentionKeysList li:hover { + border-color: rgba(0, 0, 0, 0.12); + box-shadow: 0 4px 12px rgba(0,0,0,0.08); + background-color: rgba(249, 250, 251, 0.95); + } + #attentionKeysList li.selected { + background-color: rgba(239, 246, 255, 0.95); /* light blue */ + border-color: rgba(59, 130, 246, 0.35); + } + #attentionKeysList .key-checkbox { + margin-right: 0.25rem; + } + .key-text { color: #374151 !important; /* gray-700 for light theme */ text-shadow: none; @@ -1293,18 +1319,59 @@ endblock %} {% block head_extra_styles %} -
    +
    + +
    + + +
    -
    - +
    + + +
    +