/** * 数据库管理模块 JavaScript * 提供数据集的查看、创建、编辑、删除、导入功能,以及QA对管理功能 */ (function() { 'use strict'; // 引用全局的翻译函数 // 翻译函数由 i18n-helper.js 提供 // 模块私有状态 let datasetsPaginator = null; let qaPaginator = null; let editingId = null; let currentDatasetId = null; let container = null; let usersCache = null; // 数据库管理模块 const DatasetManagement = { init: function(containerElement) { container = containerElement; this.initPaginators(); this.initEventListeners(); // 等待 i18next 初始化完成后再加载数据 const loadData = () => { console.log('Loading datasets with i18next language:', window.i18next?.language); this.loadDatasets(); this.loadDatasetsForSelect(); }; // 检查 i18next 是否已可用(通过测试实际翻译) const checkI18nReady = () => { if (window.i18next && typeof window.i18next.t === 'function') { try { // 测试翻译一个已知存在的键 const testResult1 = window.i18next.t('common.loading'); const testResult2 = window.i18next.t('dataset.manageConfigs'); // 如果返回的值不是键名本身,说明翻译已正常工作 const isWorking = testResult1 && testResult1 !== 'common.loading'; console.log('Checking i18next ready:', isWorking, 'language:', window.i18next.language); console.log(' Test common.loading:', testResult1); console.log(' Test dataset.manageConfigs:', testResult2); console.log(' Test dataset.manageConfigs === key?:', testResult2 === 'dataset.manageConfigs'); return isWorking; } catch (e) { console.log('i18next test failed:', e); return false; } } console.log('i18next not available'); return false; }; // 立即检查一次 if (checkI18nReady()) { loadData(); return; } console.log('i18next not ready yet, waiting...'); // 等待 i18next 初始化 const checkI18n = setInterval(() => { if (checkI18nReady()) { clearInterval(checkI18n); loadData(); } }, 100); // 超时保护(10秒后强制加载) let loadDataTimeout = setTimeout(() => { clearInterval(checkI18n); console.warn('i18next initialization timeout, loading data anyway. Language:', window.i18next?.language); loadData(); }, 10000); // 同时监听 i18next-ready 事件(更可靠的方式) window.addEventListener('i18next-ready', function onI18nReady() { console.log('Received i18next-ready event, language:', window.i18next?.language); clearInterval(checkI18n); clearTimeout(loadDataTimeout); window.removeEventListener('i18next-ready', onI18nReady); loadData(); }); }, initPaginators: function() { // 初始化数据集管理分页器 datasetsPaginator = createPaginator('datasets', { initialPage: 1, initialPageSize: 20, onPageChange: (page, pageSize) => { this.loadDatasets(); }, onPageSizeChange: (pageSize) => { this.loadDatasets(); } }); // 初始化QA对分页器 qaPaginator = createPaginator('qa', { initialPage: 1, initialPageSize: 20, onPageChange: (page, pageSize) => { if (currentDatasetId) { this.loadQAPairs(currentDatasetId); } }, onPageSizeChange: (pageSize) => { if (currentDatasetId) { this.loadQAPairs(currentDatasetId); } } }); }, initEventListeners: function() { // 添加数据集按钮 const addDatasetBtn = container.querySelector('#addDatasetBtn'); if (addDatasetBtn) { addDatasetBtn.addEventListener('click', () => { this.showDatasetForm(); }); } // 导入数据集按钮 const importDatasetBtn = container.querySelector('#importDatasetBtn'); if (importDatasetBtn) { importDatasetBtn.addEventListener('click', () => { this.showImportDatasetForm(); }); } // 刷新数据集按钮 const refreshDatasetsBtn = container.querySelector('#refreshDatasetsBtn'); if (refreshDatasetsBtn) { refreshDatasetsBtn.addEventListener('click', () => { this.loadDatasets(); }); } // 标签页切换 container.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { const tab = btn.dataset.tab; this.switchTab(tab); }); }); // 数据集选择 const datasetSelect = container.querySelector('#datasetSelect'); if (datasetSelect) { datasetSelect.addEventListener('change', (e) => { currentDatasetId = e.target.value ? parseInt(e.target.value) : null; if (currentDatasetId) { if (qaPaginator) qaPaginator.reset(); this.loadQAPairs(currentDatasetId); } else { const tbody = container.querySelector('#qaPairsTableBody'); if (tbody) { tbody.innerHTML = '' + t('system.selectDatasetFirst') + ''; } const qaPaginationContainer = container.querySelector('#qaPaginationContainer'); if (qaPaginationContainer) qaPaginationContainer.style.display = 'none'; } }); } // 导入数据按钮 const importDataBtn = container.querySelector('#importDataBtn'); if (importDataBtn) { importDataBtn.addEventListener('click', () => { this.showImportDataForm(); }); } }, switchTab: function(tab) { // 更新tab按钮状态 container.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.tab === tab); }); // 更新tab内容状态 container.querySelectorAll('.tab-content').forEach(content => { content.classList.toggle('active', content.id === tab); }); if (tab === 'dataset-items' && !currentDatasetId) { this.loadDatasetsForSelect(); } }, loadDatasets: async function() { if (!datasetsPaginator) return; try { const skip = datasetsPaginator.getSkip(); const limit = datasetsPaginator.getLimit(); const datasets = await apiGet(`/datasets/?skip=${skip}&limit=${limit}`); // 获取总数:如果返回的数据量小于limit,说明这是最后一页 // 如果等于limit,可能还有更多数据,尝试获取下一页来判断 let totalCount; if (datasets.length < limit) { totalCount = skip + datasets.length; } else { // 尝试获取下一页来判断是否还有更多数据 try { const nextPage = await apiGet(`/datasets/?skip=${skip + limit}&limit=1`); if (nextPage.length > 0) { // 还有更多数据,但不知道总数,使用估算值(当前页数 * 每页数量 + 1) totalCount = (skip + limit) + 1; } else { // 没有更多数据了 totalCount = skip + limit; } } catch (error) { // 如果获取失败,假设还有更多数据 totalCount = (skip + limit) + 1; } } datasetsPaginator.setTotalCount(totalCount); this.renderDatasetsTable(datasets); this.loadDatasetsForSelect(); } catch (error) { console.error('Failed to load datasets:', error); showError(t('messages.loadFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, renderDatasetsTable: async function(datasets) { const tbody = container.querySelector('#datasetsTableBody'); const paginationContainer = container.querySelector('#datasetsPaginationContainer'); if (datasets.length === 0) { tbody.innerHTML = '' + t('dataset.noDatasets') + ''; if (paginationContainer) paginationContainer.style.display = 'none'; return; } // 先渲染基础信息 tbody.innerHTML = datasets.map(dataset => ` ${dataset.id} ${escapeHtml(dataset.name)} ${escapeHtml(dataset.description || '-')} ${escapeHtml(dataset.version || '-')} ${t(`status.${dataset.status || 'active'}`)} ${t('common.loading')} ${t('common.loading')} ${escapeHtml(dataset.annotator_name || '-')} ${formatDateTime(dataset.created_at)} `).join(''); // 异步加载统计数据和标注进展 datasets.forEach(async dataset => { try { const [stats, progress] = await Promise.all([ this.getDatasetStats(dataset.id), this.getDatasetAnnotationProgress(dataset.id).catch(() => null) ]); const row = tbody.querySelector(`tr[data-dataset-id="${dataset.id}"]`); if (row) { const countCell = row.querySelector('.items-count'); if (countCell) { countCell.textContent = stats.items_count || 0; } const progressCell = row.querySelector('.annotation-progress'); if (progressCell && progress) { progressCell.innerHTML = this.renderAnnotationProgress(progress); } else if (progressCell) { progressCell.textContent = '-'; } } } catch (error) { console.error('Failed to load statistics:', error); const row = tbody.querySelector(`tr[data-dataset-id="${dataset.id}"]`); if (row) { const countCell = row.querySelector('.items-count'); if (countCell) { countCell.textContent = '0'; } const progressCell = row.querySelector('.annotation-progress'); if (progressCell) { progressCell.textContent = '-'; } } } }); // 显示分页器 if (paginationContainer && datasetsPaginator) { paginationContainer.style.display = 'block'; datasetsPaginator.render(); } }, getDatasetStats: async function(datasetId) { try { return await apiGet(`/datasets/${datasetId}/stats`); } catch (error) { return { items_count: 0, configs_count: 0 }; } }, getDatasetAnnotationProgress: async function(datasetId) { try { return await apiGet(`/datasets/${datasetId}/annotation-progress`); } catch (error) { console.error('Failed to get annotation progress:', error); return null; } }, renderAnnotationProgress: function(progress) { if (!progress || progress.total_items === 0) { return '' + t('common.noData') + ''; } const overallRate = progress.overall_progress_rate || 0; const progressColor = overallRate >= 80 ? '#2e7d32' : overallRate >= 50 ? '#f57c00' : '#d32f2f'; let html = `
${overallRate.toFixed(1)}%
${progress.annotated_items || 0} / ${progress.total_items}
`; // 如果有配置进展,显示详细信息(可展开) if (progress.config_progress && progress.config_progress.length > 0) { const configDetails = progress.config_progress.map(cp => { const cpColor = cp.progress_rate >= 80 ? '#2e7d32' : cp.progress_rate >= 50 ? '#f57c00' : '#d32f2f'; return `
${escapeHtml(cp.config_name)}:
${cp.progress_rate.toFixed(1)}%
`; }).join(''); html += `
${t('system.viewDetails')}
${configDetails}
`; } html += '
'; return html; }, loadDatasetsForSelect: function() { apiGet('/datasets/').then(datasets => { const select = container.querySelector('#datasetSelect'); if (select) { select.innerHTML = '' + datasets.map(ds => ``).join(''); if (currentDatasetId) { select.value = currentDatasetId; } } }).catch(error => { console.error('Failed to load dataset list:', error); }); }, loadUsersForSelect: async function() { if (usersCache) { return usersCache; } try { const users = await apiGet('/users/?skip=0&limit=1000'); usersCache = users; return users; } catch (error) { console.error('Failed to load user list:', error); return []; } }, showDatasetForm: async function(dataset = null) { editingId = dataset ? dataset.id : null; const title = dataset ? t('dataset.editDataset') : t('dataset.addDataset'); // 加载用户列表 const users = await this.loadUsersForSelect(); const userOptions = '' + users.map(user => `` ).join(''); const content = `
${t('dataset.annotatorHelp')}
${t('dataset.extraFieldsHelp')}
`; openModal(title, content, async () => { await this.saveDataset(); }); }, saveDataset: async function() { const displayExtraFieldsInput = document.getElementById('display_extra_fields').value.trim(); const displayExtraFields = displayExtraFieldsInput ? displayExtraFieldsInput.split(',').map(f => f.trim()).filter(f => f) : null; const annotatorIdInput = document.getElementById('annotator_id').value; const annotatorId = annotatorIdInput ? parseInt(annotatorIdInput) : null; // 获取标注者名称 let annotatorName = null; if (annotatorId) { const users = await this.loadUsersForSelect(); const annotator = users.find(u => u.id === annotatorId); if (annotator) { annotatorName = annotator.username; } } const data = { name: document.getElementById('name').value, description: document.getElementById('description').value || null, version: document.getElementById('version').value || null, status: document.getElementById('status').value || 'active', category: document.getElementById('category').value || null, annotator_id: annotatorId, annotator_name: annotatorName, display_extra_fields: displayExtraFields }; try { if (editingId) { data.id = editingId; await apiPut(`/datasets/${editingId}`, data); showSuccess(t('messages.updateSuccess')); } else { await apiPost('/datasets/', data); showSuccess(t('messages.createSuccess')); } // 保存 editingId,因为 closeModal 可能会重置它 const savedEditingId = editingId; closeModal(); // 如果是新建数据集,重置到第一页 if (!savedEditingId && datasetsPaginator) { datasetsPaginator.reset(); } this.loadDatasets(); // 如果是从项目管理页面调用的,刷新项目管理页面的数据 if (window._editingDatasetFromProject && window._projectIdToRefresh) { const projectId = window._projectIdToRefresh; // 清除标志 window._editingDatasetFromProject = false; window._projectIdToRefresh = null; // 触发自定义事件,通知 manager.js 刷新数据 window.dispatchEvent(new CustomEvent('datasetSavedFromProject', { detail: { projectId: projectId, datasetId: savedEditingId } })); } } catch (error) { showError(t('messages.saveFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, editDataset: async function(datasetId) { try { const dataset = await apiGet(`/datasets/${datasetId}`); this.showDatasetForm(dataset); } catch (error) { showError(t('messages.loadFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, deleteDataset: async function(datasetId) { if (!confirm(t('dataset.deleteConfirm'))) { return; } try { await apiDelete(`/datasets/${datasetId}`); showSuccess(t('messages.deleteSuccess')); // 如果当前页没有数据了,回到上一页 if (datasetsPaginator) { datasetsPaginator.adjustPageAfterDelete(); } this.loadDatasets(); if (currentDatasetId === datasetId) { currentDatasetId = null; const datasetSelect = container.querySelector('#datasetSelect'); if (datasetSelect) datasetSelect.value = ''; const tbody = container.querySelector('#qaPairsTableBody'); if (tbody) { tbody.innerHTML = '' + t('system.selectDatasetFirst') + ''; } } } catch (error) { showError(t('messages.deleteFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, manageDatasetConfigs: async function(datasetId) { try { // 获取数据集信息 const dataset = await apiGet(`/datasets/${datasetId}`); // 获取已关联的标注配置 const associatedConfigs = await apiGet(`/datasets/${datasetId}/configs`); // 获取所有可用的标注配置 const allConfigs = await apiGet('/annotation-configs/?skip=0&limit=1000'); // 找出未关联的配置 const associatedIds = new Set(associatedConfigs.map(c => c.id)); const unassociatedConfigs = allConfigs.filter(c => !associatedIds.has(c.id)); // 显示管理模态框 this.showDatasetConfigsModal(dataset, associatedConfigs, unassociatedConfigs); } catch (error) { showError(t('dataset.loadConfigsFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, showDatasetConfigsModal: function(dataset, associatedConfigs, unassociatedConfigs) { const title = `${t('dataset.manageConfigs')} - ${escapeHtml(dataset.name)}`; // 构建已关联配置列表 const associatedList = associatedConfigs.length > 0 ? associatedConfigs.map(config => `
${escapeHtml(config.name)} ${escapeHtml(config.annotation_type)} ${config.description ? `
${escapeHtml(config.description)}
` : ''}
`).join('') : '
' + t('dataset.noAssociatedConfigs') + '
'; // 构建未关联配置选择器 const unassociatedOptions = unassociatedConfigs.length > 0 ? unassociatedConfigs.map(config => `` ).join('') : ''; const content = `

${t('dataset.associatedConfigs')}

${associatedList}

${t('dataset.addConfig')}

`; openModal(title, content, null, true); // 第四个参数表示不需要提交按钮 }, addDatasetConfig: async function(datasetId) { const configSelect = document.getElementById('configSelect'); const configId = configSelect.value; if (!configId) { showError(t('dataset.selectConfigToAdd')); return; } try { await apiPost(`/annotation-configs/${configId}/associate/${datasetId}`); showSuccess(t('dataset.configAddSuccess')); // 重新加载管理界面 this.manageDatasetConfigs(datasetId); } catch (error) { showError(t('dataset.configAddFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, removeDatasetConfig: async function(datasetId, configId) { if (!confirm(t('dataset.configRemoveConfirm'))) { return; } try { await apiDelete(`/annotation-configs/${configId}/associate/${datasetId}`); showSuccess(t('dataset.configRemoveSuccess')); // 重新加载管理界面 this.manageDatasetConfigs(datasetId); } catch (error) { showError(t('dataset.configRemoveFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, showImportDatasetForm: async function() { const title = t('dataset.importDataset'); // 加载用户列表 const users = await this.loadUsersForSelect(); const userOptions = '' + users.map(user => `` ).join(''); const content = `
${t('dataset.jsonlFileHelp')}

${t('dataset.metadataOptional')}

${t('dataset.metadataHelp')}
${t('dataset.annotatorHelp')}
`; openModal(title, content, async () => { await this.handleImportDataset(); }); }, handleImportDataset: async function() { const fileInput = document.getElementById('importDatasetFile'); const file = fileInput.files[0]; if (!file) { showError(t('dataset.selectFileError')); return; } if (!file.name.endsWith('.jsonl')) { showError(t('dataset.fileFormatError')); return; } // 收集元数据 const name = document.getElementById('importDatasetName').value.trim(); const description = document.getElementById('importDatasetDescription').value.trim(); const version = document.getElementById('importDatasetVersion').value.trim(); const category = document.getElementById('importDatasetCategory').value.trim(); const status = document.getElementById('importDatasetStatus').value; const tags = document.getElementById('importDatasetTags').value.trim(); const source = document.getElementById('importDatasetSource').value.trim(); const sourceUrl = document.getElementById('importDatasetSourceUrl').value.trim(); const annotatorIdInput = document.getElementById('importDatasetAnnotatorId').value; const annotatorId = annotatorIdInput ? parseInt(annotatorIdInput) : null; // 显示进度 const progressDiv = document.getElementById('importDatasetProgress'); const resultDiv = document.getElementById('importDatasetResult'); progressDiv.style.display = 'block'; resultDiv.style.display = 'none'; try { // 构建FormData,包含文件和元数据 const formData = new FormData(); formData.append('file', file); if (name) formData.append('name', name); if (description) formData.append('description', description); if (version) formData.append('version', version); if (category) formData.append('category', category); if (status) formData.append('status', status); if (tags) formData.append('tags', tags); if (source) formData.append('source', source); if (sourceUrl) formData.append('source_url', sourceUrl); if (annotatorId) formData.append('annotator_id', annotatorId); // 获取token const token = getToken(); const headers = {}; if (token) { headers['Authorization'] = `Bearer ${token}`; } // 发送请求 const response = await fetch(`${API_BASE_URL}/datasets/import`, { method: 'POST', headers: headers, body: formData }); // 处理响应 let result; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { try { result = await response.json(); } catch (error) { result = {}; } } else { result = await response.text(); } // 如果响应不成功,抛出错误 if (!response.ok) { const error = new Error(result.detail || result.message || `${t('messages.httpError')}: ${response.status}`); error.status = response.status; error.data = result; throw error; } // 隐藏进度,显示结果 progressDiv.style.display = 'none'; resultDiv.style.display = 'block'; const datasetAction = result.created ? t('dataset.created') : t('dataset.updated'); const successMsg = `${t('dataset.importComplete')} ${datasetAction} ${t('dataset.dataset')} "${result.dataset_name}", ${t('dataset.importedCount', { imported: result.imported_count, failed: result.failed_count, total: result.total_lines })}`; if (result.failed_count > 0 && result.errors && result.errors.length > 0) { const errorsHtml = result.errors.map(err => `
  • ${escapeHtml(err)}
  • `).join(''); resultDiv.innerHTML = `

    ${successMsg}

    ${result.errors.length > 0 ? `
    ${t('dataset.errorDetails', { count: result.errors.length })}
    ` : ''}
    `; } else { resultDiv.innerHTML = `

    ${successMsg}

    `; } // 如果导入成功,刷新数据集列表 if (result.imported_count > 0 || result.created) { setTimeout(() => { this.loadDatasets(); }, 1000); } showSuccess(successMsg); // 3秒后自动关闭模态框 setTimeout(() => { closeModal(); }, 3000); } catch (error) { progressDiv.style.display = 'none'; // 如果是401未授权错误,清除token并跳转到登录页 if (error.status === 401) { clearToken(); const currentPath = window.location.pathname; if (currentPath !== '/auth') { const redirectUrl = encodeURIComponent(window.location.href); window.location.href = `/auth?redirect=${redirectUrl}`; } } showError(t('dataset.importFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, loadQAPairs: async function(datasetId) { if (!datasetId || !qaPaginator) { return; } try { const skip = qaPaginator.getSkip(); const limit = qaPaginator.getLimit(); // 同时获取数据集信息和QA对 const [dataset, qaPairs] = await Promise.all([ apiGet(`/datasets/${datasetId}`), apiGet(`/datasets/${datasetId}/items/?skip=${skip}&limit=${limit}`) ]); // 获取总数(通过获取数据集统计信息) let totalCount; try { const stats = await apiGet(`/datasets/${datasetId}/stats`); totalCount = stats.items_count || 0; } catch (error) { // 如果获取统计失败,根据返回的数据量估算 const currentPage = qaPaginator.currentPage; totalCount = qaPairs.length === limit ? (currentPage * limit + 1) : ((currentPage - 1) * limit + qaPairs.length); } qaPaginator.setTotalCount(totalCount); this.renderQAPairsTable(qaPairs, dataset); } catch (error) { console.error('Failed to load QA pairs:', error); showError(t('dataset.loadQAPairsFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, renderQAPairsTable: function(qaPairs, dataset = null) { const tbody = container.querySelector('#qaPairsTableBody'); const thead = tbody ? tbody.closest('table').querySelector('thead tr') : null; const paginationContainer = container.querySelector('#qaPaginationContainer'); if (!tbody || !thead) return; // 获取要显示的extra字段列表 const displayExtraFields = dataset && dataset.display_extra_fields ? dataset.display_extra_fields : []; // 动态更新表头 const baseHeaders = [t('dataset.serialNumber'), t('dataset.question'), t('dataset.answer')]; const extraHeaders = displayExtraFields.map(field => escapeHtml(field)); const allHeaders = [...baseHeaders, ...extraHeaders, t('common.actions')]; thead.innerHTML = allHeaders.map(header => `${header}`).join(''); // 计算总列数(用于colspan) const totalCols = allHeaders.length; if (qaPairs.length === 0) { tbody.innerHTML = `${t('dataset.noQAPairs')}`; if (paginationContainer) paginationContainer.style.display = 'none'; return; } // 计算当前页的起始序号 const startIndex = qaPaginator ? qaPaginator.getSkip() : 0; // 渲染表格内容 tbody.innerHTML = qaPairs.map((qa, index) => { // 基础列 let row = ` ${startIndex + index + 1} ${escapeHtml(qa.question).substring(0, 100)}${qa.question.length > 100 ? '...' : ''} ${escapeHtml(qa.answer).substring(0, 100)}${qa.answer.length > 100 ? '...' : ''} `; // 添加extra字段列 displayExtraFields.forEach(field => { const value = qa[field]; if (value !== undefined && value !== null) { const displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value); row += `${escapeHtml(displayValue).substring(0, 50)}${displayValue.length > 50 ? '...' : ''}`; } else { row += '-'; } }); // 操作列 row += '-'; return `${row}`; }).join(''); // 显示分页器 if (paginationContainer && qaPaginator) { paginationContainer.style.display = 'block'; qaPaginator.render(); } }, showImportDataForm: function() { if (!currentDatasetId) { showError(t('system.selectDatasetFirst')); return; } const title = t('dataset.importData'); const content = `
    ${t('dataset.jsonlFileHelp')}
    `; openModal(title, content, async () => { await this.handleImportData(); }); }, handleImportData: async function() { const fileInput = document.getElementById('importFile'); const file = fileInput.files[0]; if (!file) { showError(t('dataset.selectFileError')); return; } if (!file.name.endsWith('.jsonl')) { showError(t('dataset.fileFormatError')); return; } // 显示进度 const progressDiv = document.getElementById('importProgress'); const resultDiv = document.getElementById('importResult'); progressDiv.style.display = 'block'; resultDiv.style.display = 'none'; try { const result = await apiUploadFile(`/datasets/${currentDatasetId}/import`, file); // 隐藏进度,显示结果 progressDiv.style.display = 'none'; resultDiv.style.display = 'block'; const successMsg = `${t('dataset.importComplete')} ${t('dataset.importedCount', { imported: result.imported_count, failed: result.failed_count, total: result.total_lines })}`; if (result.failed_count > 0 && result.errors && result.errors.length > 0) { const errorsHtml = result.errors.map(err => `
  • ${escapeHtml(err)}
  • `).join(''); resultDiv.innerHTML = `

    ${successMsg}

    ${result.errors.length > 0 ? `
    ${t('dataset.errorDetails', { count: result.errors.length })}
    ` : ''}
    `; } else { resultDiv.innerHTML = `

    ${successMsg}

    `; } // 如果导入成功,刷新QA对列表 if (result.imported_count > 0) { setTimeout(() => { if (qaPaginator) qaPaginator.reset(); this.loadQAPairs(currentDatasetId); this.loadDatasets(); // 刷新数据集列表以更新统计 }, 1000); } showSuccess(successMsg); // 3秒后自动关闭模态框 setTimeout(() => { closeModal(); }, 3000); } catch (error) { progressDiv.style.display = 'none'; showError(t('dataset.importFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, exportAnnotations: function(datasetId) { // 显示格式选择对话框 const title = t('dataset.exportAnnotations'); const content = `
    ${t('dataset.exportFormatHelp')}
    `; openModal(title, content, async () => { await this.handleExportAnnotations(datasetId); }); }, handleExportAnnotations: async function(datasetId) { const format = document.getElementById('exportFormat').value; if (!format) { showError(t('dataset.selectExportFormat')); return; } try { // 获取token const token = getToken(); const headers = {}; if (token) { headers['Authorization'] = `Bearer ${token}`; } // 构建导出URL const exportUrl = `${API_BASE_URL}/annotation-results/datasets/${datasetId}/export?format=${format}`; // 使用fetch下载文件 const response = await fetch(exportUrl, { method: 'GET', headers: headers }); if (!response.ok) { const errorData = await response.json().catch(() => ({ detail: t('dataset.exportFailed') })); throw new Error(errorData.detail || errorData.message || `${t('messages.httpError')}: ${response.status}`); } // 获取文件名(从Content-Disposition头中提取,或使用默认名称) const contentDisposition = response.headers.get('Content-Disposition'); let filename = `dataset_${datasetId}_annotations.${format}`; if (contentDisposition) { const filenameMatch = contentDisposition.match(/filename="?([^"]+)"?/); if (filenameMatch) { filename = filenameMatch[1]; } } // 获取文件内容 const blob = await response.blob(); // 创建下载链接 const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); // 清理 window.URL.revokeObjectURL(url); document.body.removeChild(a); showSuccess(`${t('dataset.exportSuccess')} ${format.toUpperCase()}`); closeModal(); } catch (error) { console.error('Export failed:', error); // 如果是401未授权错误,清除token并跳转到登录页 if (error.status === 401 || (error.message && error.message.includes('401'))) { clearToken(); const currentPath = window.location.pathname; if (currentPath !== '/auth') { const redirectUrl = encodeURIComponent(window.location.href); window.location.href = `/auth?redirect=${redirectUrl}`; } } showError(t('dataset.exportFailed') + ': ' + (error.message || t('messages.unknownError'))); } }, destroy: function() { // 清理资源 datasetsPaginator = null; qaPaginator = null; editingId = null; currentDatasetId = null; container = null; usersCache = null; } }; // 暴露到全局 window.DatasetManagement = DatasetManagement; })();