// frontend/js/pages/keys/addApiModal.js // [REFACTORED] 引入全局的 taskCenterManager 和 modalManager 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 AddApiModal { constructor({ onImportSuccess }) { this.modalId = 'add-api-modal'; this.onImportSuccess = onImportSuccess; this.activeGroupId = null; this.elements = { modal: document.getElementById(this.modalId), title: document.getElementById('add-api-modal-title'), inputView: document.getElementById('add-api-input-view'), textarea: document.getElementById('api-add-textarea'), importBtn: document.getElementById('add-api-import-btn'), validateCheckbox: document.getElementById('validate-on-import-checkbox'), }; if (!this.elements.modal) { throw new Error(`Modal with id "${this.modalId}" not found.`); } this._initEventListeners(); } open(activeGroupId) { if (!activeGroupId) { console.error("Cannot open AddApiModal: activeGroupId is required."); return; } this.activeGroupId = activeGroupId; this._reset(); modalManager.show(this.modalId); } _initEventListeners() { this.elements.importBtn?.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.importBtn.disabled = true; this.elements.importBtn.innerHTML = `正在启动...`; const addKeysTask = { start: async () => { const shouldValidate = this.elements.validateCheckbox.checked; const response = await apiKeyManager.addKeysToGroup(this.activeGroupId, cleanedKeys.join('\n'), shouldValidate); 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 newlyLinked = result.newly_linked_count || 0; const alreadyLinked = result.already_linked_count || 0; const summaryTitle = `批量链接 ${newlyLinked} Key,已跳过 ${alreadyLinked}`; contentHtml = `

${summaryTitle}

`; } else if (!data.is_running && data.error) { // --- ERROR state --- contentHtml = `

批量添加失败

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

`; } else { // --- RUNNING state --- contentHtml = `

批量添加 ${data.total} 个API Key

运行中...

`; } return `${contentHtml}
${timeAgo}
`; }, 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.onImportSuccess) this.onImportSuccess(); // (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'); } }; // Pass the entire definition to the dispatcher taskCenterManager.startTask(addKeysTask); modalManager.hide(this.modalId); this._reset(); } _reset() { // [REMOVED] 不再需要管理 resultView this.elements.title.textContent = '批量添加 API Keys'; this.elements.inputView.classList.remove('hidden'); this.elements.textarea.value = ''; this.elements.textarea.disabled = false; this.elements.importBtn.disabled = false; this.elements.importBtn.innerHTML = '导入'; // 使用 innerHTML 避免潜在的 XSS } _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)]; } }