// frontend/js/pages/keys/deleteApiModal.js import { modalManager } from '../../components/ui.js'; import { taskCenterManager, toastManager } from '../../components/taskCenter.js'; import { apiKeyManager } from '../../components/apiKeyManager.js'; import { isValidApiKeyFormat } from '../../utils/utils.js'; export default class DeleteApiModal { constructor({ onDeleteSuccess }) { this.modalId = 'delete-api-modal'; this.onDeleteSuccess = onDeleteSuccess; this.activeGroupId = null; this.elements = { modal: document.getElementById(this.modalId), textarea: document.getElementById('api-delete-textarea'), deleteBtn: document.getElementById(this.modalId).querySelector('.modal-btn-danger'), }; if (!this.elements.modal) { throw new Error(`Modal with id "${this.modalId}" not found.`); } this._initEventListeners(); } open(activeGroupId) { if (!activeGroupId) { console.error("Cannot open DeleteApiModal: activeGroupId is required."); return; } this.activeGroupId = activeGroupId; this._reset(); modalManager.show(this.modalId); } _initEventListeners() { this.elements.deleteBtn?.addEventListener('click', this._handleSubmit.bind(this)); const closeAction = () => { this._reset(); modalManager.hide(this.modalId); }; const closeTriggers = this.elements.modal.querySelectorAll(`[data-modal-close="${this.modalId}"]`); closeTriggers.forEach(trigger => trigger.addEventListener('click', closeAction)); this.elements.modal.addEventListener('click', (event) => { if (event.target === this.elements.modal) closeAction(); }); } async _handleSubmit(event) { event.preventDefault(); const cleanedKeys = this._parseAndCleanKeys(this.elements.textarea.value); if (cleanedKeys.length === 0) { alert('没有检测到有效的API Keys。'); return; } this.elements.deleteBtn.disabled = true; this.elements.deleteBtn.innerHTML = `正在启动...`; const deleteKeysTask = { start: async () => { const response = await apiKeyManager.unlinkKeysFromGroup(this.activeGroupId, cleanedKeys.join('\n')); if (!response.success || !response.data) throw new Error(response.message || '启动解绑任务失败。'); return response.data; }, poll: async (taskId) => { return await apiKeyManager.getTaskStatus(taskId, { noCache: true }); }, renderTaskCenterItem: (data, timestamp, formatTimeAgo) => { const timeAgo = formatTimeAgo(timestamp); let contentHtml = ''; if (!data.is_running && !data.error) { // --- SUCCESS state --- const result = data.result || {}; const unlinked = result.unlinked_count || 0; const deleted = result.hard_deleted_count || 0; const notFound = result.not_found_count || 0; const totalInput = data.total; const summaryTitle = `解绑 ${unlinked} Key,清理 ${deleted}`; // [MODIFIED] Applied Flexbox layout for proper spacing. contentHtml = `

${summaryTitle}

`; } else if (!data.is_running && data.error) { // --- ERROR state --- // [MODIFIED] Applied Flexbox layout for proper spacing. contentHtml = `

批量删除失败

${data.error || '未知错误'}

`; } else { // --- RUNNING state --- // [MODIFIED] Applied Flexbox layout with gap for spacing. // [FIX] Replaced 'fa-spin' with Tailwind's 'animate-spin' for reliable animation. contentHtml = `

批量删除 ${data.total} 个API Key

运行中...

`; } return `${contentHtml}
${timeAgo}
`; }, // he Toast is now solely responsible for showing real-time progress. renderToastNarrative: (data, oldData, toastManager) => { const toastId = `task-${data.id}`; const progress = data.total > 0 ? (data.processed / data.total) * 100 : 0; // It just reports the current progress, that's its only job. toastManager.showProgressToast(toastId, `批量删除Key`, '处理中', progress); // (Change title for delete modal) }, // This now ONLY shows the FINAL summary toast, after everything else is done. onSuccess: (data) => { if (this.onDeleteSuccess) this.onDeleteSuccess(); // (Or onDeleteSuccess) const newlyLinked = data.result?.newly_linked_count || 0; // (Or unlinked_count) toastManager.show(`任务完成!成功删除 ${newlyLinked} 个Key。`, 'success'); // (Adjust text for delete) }, // This is the final error handler. onError: (data) => { toastManager.show(`任务失败: ${data.error || '未知错误'}`, 'error'); } }; taskCenterManager.startTask(deleteKeysTask); modalManager.hide(this.modalId); this._reset(); } _reset() { this.elements.textarea.value = ''; this.elements.deleteBtn.disabled = false; this.elements.deleteBtn.innerHTML = '删除'; } _parseAndCleanKeys(text) { const keys = text.replace(/[,;]/g, ' ').split(/[\s\n]+/); const cleanedKeys = keys.map(key => key.trim()).filter(key => isValidApiKeyFormat(key)); return [...new Set(cleanedKeys)]; } }