// static/js/dashboard.js // 全局变量和状态管理 const state = { currentPage: 'classify', // 当前显示的页面 documentsData: { // 文档列表数据 items: [], // 当前页的文档条目 pagination: { // 分页信息 total: 0, per_page: 10, current_page: 1, total_pages: 0 }, categories: [], // 所有类别 currentCategory: 'all', // 当前选择的类别 selectedIds: [] // 选中的文档ID } }; // DOM元素缓存 const elements = { // 侧边栏和导航 sidebar: document.querySelector('.sidebar'), sidebarToggle: document.getElementById('sidebar-toggle'), sidebarLinks: document.querySelectorAll('.sidebar-link'), mainContent: document.querySelector('.main-content'), // 页面sections pageSections: document.querySelectorAll('.page-section'), classifySection: document.getElementById('classify-section'), documentsSection: document.getElementById('documents-section'), batchSection: document.getElementById('batch-section'), // 文本分类相关 textInput: document.getElementById('text-input'), classifyTextBtn: document.getElementById('classify-text-btn'), clearTextBtn: document.getElementById('clear-text-btn'), classificationResult: document.getElementById('classification-result'), fileUpload: document.getElementById('file-upload'), uploadFileBtn: document.getElementById('upload-file-btn'), fileResult: document.getElementById('file-result'), // 已处理文本相关 categoryFilter: document.getElementById('category-filter'), perPageSelect: document.getElementById('per-page-select'), downloadSelectedBtn: document.getElementById('download-selected-btn'), documentsTable: document.getElementById('documents-table'), selectAllDocs: document.getElementById('select-all-docs'), paginationContainer: document.getElementById('pagination-container'), // 批量处理相关 batchFileUpload: document.getElementById('batch-file-upload'), uploadBatchBtn: document.getElementById('upload-batch-btn'), batchResult: document.getElementById('batch-result'), // 加载指示器 loadingOverlay: document.getElementById('loading-overlay') }; // 初始化 document.addEventListener('DOMContentLoaded', () => { // 绑定事件 bindEvents(); // 初始化分类模型 initializeModel(); }); // 事件绑定 function bindEvents() { // 侧边栏切换 elements.sidebarToggle?.addEventListener('click', toggleSidebar); // 侧边栏导航链接 elements.sidebarLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const targetPage = link.dataset.page; navigateToPage(targetPage); }); }); // 文本分类相关 elements.classifyTextBtn?.addEventListener('click', classifyTextContent); elements.clearTextBtn?.addEventListener('click', clearTextContent); elements.uploadFileBtn?.addEventListener('click', uploadAndClassifyFile); // 已处理文本相关 elements.categoryFilter?.addEventListener('change', filterDocumentsByCategory); elements.perPageSelect?.addEventListener('change', changePageSize); elements.selectAllDocs?.addEventListener('change', toggleSelectAllDocuments); elements.downloadSelectedBtn?.addEventListener('click', downloadSelectedDocuments); // 批量处理相关 elements.uploadBatchBtn?.addEventListener('click', uploadAndClassifyBatch); } // 侧边栏切换 function toggleSidebar() { elements.sidebar.classList.toggle('active'); elements.mainContent.classList.toggle('sidebar-active'); } // 页面导航 function navigateToPage(targetPage) { // 保存当前页面 state.currentPage = targetPage; // 更新活动链接 elements.sidebarLinks.forEach(link => { if (link.dataset.page === targetPage) { link.classList.add('active'); } else { link.classList.remove('active'); } }); // 显示对应页面 elements.pageSections.forEach(section => { if (section.id === `${targetPage}-section`) { section.classList.remove('d-none'); } else { section.classList.add('d-none'); } }); // 加载页面特定数据 if (targetPage === 'documents') { loadDocuments(); } } // 显示加载指示器 function showLoading() { elements.loadingOverlay.classList.add('show'); } // 隐藏加载指示器 function hideLoading() { elements.loadingOverlay.classList.remove('show'); } // API请求公共方法 async function apiRequest(url, options = {}) { try { const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...options.headers } }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || '请求失败'); } return data; } catch (error) { console.error('API请求失败:', error); throw error; } } // 初始化分类模型 async function initializeModel() { try { await apiRequest('/api/classify/categories'); console.log('模型加载成功'); } catch (error) { console.error('模型初始化失败:', error); alert('模型加载失败,请刷新页面重试'); } } // 文本分类函数 async function classifyTextContent() { const text = elements.textInput.value.trim(); if (!text) { alert('请输入需要分类的文本'); return; } showLoading(); try { const result = await apiRequest('/api/classify/classify-text', { method: 'POST', body: JSON.stringify({ text }) }); // 显示分类结果 displayTextClassificationResult(result); } catch (error) { elements.classificationResult.innerHTML = `
分类失败: ${error.message}
`; elements.classificationResult.classList.remove('result-success'); elements.classificationResult.classList.add('result-error'); } finally { hideLoading(); } } // 清空文本内容 function clearTextContent() { elements.textInput.value = ''; elements.classificationResult.innerHTML = `
分类结果将在这里显示
请在左侧输入文本或上传文件
`; elements.classificationResult.classList.remove('result-success', 'result-error'); } // 显示文本分类结果 function displayTextClassificationResult(result) { if (!result.success) { elements.classificationResult.innerHTML = `
分类失败: ${result.error}
`; elements.classificationResult.classList.remove('result-success'); elements.classificationResult.classList.add('result-error'); return; } // 获取所有类别的置信度,并排序 const confidences = Object.entries(result.all_confidences) .map(([category, value]) => ({ category, value })) .sort((a, b) => b.value - a.value); // 创建置信度条形图HTML const confidenceBarsHtml = confidences.slice(0, 5).map(item => { const percentage = (item.value * 100).toFixed(2); return `
${item.category} ${percentage}%
`; }).join(''); // 更新结果显示 elements.classificationResult.innerHTML = `
${result.category}
置信度: ${(result.confidence * 100).toFixed(2)}%
类别置信度分布:
${confidenceBarsHtml}
该文本被成功分类为 ${result.category} 类别
`; elements.classificationResult.classList.add('result-success'); elements.classificationResult.classList.remove('result-error'); } // 上传并分类文件 async function uploadAndClassifyFile() { const fileInput = elements.fileUpload; if (!fileInput.files || fileInput.files.length === 0) { alert('请选择要上传的文件'); return; } const file = fileInput.files[0]; // 检查文件类型 if (!file.name.toLowerCase().endsWith('.txt')) { alert('只支持上传 .txt 格式的文本文件'); return; } showLoading(); try { const formData = new FormData(); formData.append('file', file); const response = await fetch('/api/classify/single', { method: 'POST', body: formData }); const result = await response.json(); if (!result.success) { throw new Error(result.error || '文件分类失败'); } // 显示文件分类结果 displayFileClassificationResult(result); } catch (error) { elements.fileResult.innerHTML = `
文件分类失败: ${error.message}
`; elements.fileResult.style.display = 'block'; } finally { hideLoading(); } } // static/js/dashboard.js (继续) // 显示文件分类结果 function displayFileClassificationResult(result) { elements.fileResult.innerHTML = `
文件 ${result.filename} 已成功分类为 ${result.category} 类别
${(result.confidence * 100).toFixed(2)}%
文件已保存为: ${result.stored_filename}
`; elements.fileResult.style.display = 'block'; // 添加查看已处理文档按钮事件 document.querySelector('.view-documents-btn')?.addEventListener('click', () => { navigateToPage('documents'); }); // 清空文件上传控件 elements.fileUpload.value = ''; } // 上传并批量分类文件 async function uploadAndClassifyBatch() { const fileInput = elements.batchFileUpload; if (!fileInput.files || fileInput.files.length === 0) { alert('请选择要上传的压缩文件'); return; } const file = fileInput.files[0]; // 检查文件类型 const fileName = file.name.toLowerCase(); if (!fileName.endsWith('.zip') && !fileName.endsWith('.rar')) { alert('只支持上传 .zip 或 .rar 格式的压缩文件'); return; } // 检查文件大小 if (file.size > 10 * 1024 * 1024) { // 10MB alert('文件大小不能超过10MB'); return; } showLoading(); try { const formData = new FormData(); formData.append('file', file); const response = await fetch('/api/classify/batch', { method: 'POST', body: formData }); const result = await response.json(); if (!result.success) { throw new Error(result.error || '批量分类失败'); } // 显示批量分类结果 displayBatchClassificationResult(result); } catch (error) { elements.batchResult.innerHTML = `
批量分类失败: ${error.message}
`; elements.batchResult.style.display = 'block'; } finally { hideLoading(); // 清空文件上传控件 elements.batchFileUpload.value = ''; } } // 显示批量分类结果 function displayBatchClassificationResult(result) { const { archive_name, results } = result; // 计算成功率 const successRate = results.total > 0 ? ((results.success / results.total) * 100).toFixed(1) : 0; // 构建类别分布HTML let categoriesHtml = ''; if (results.categories && Object.keys(results.categories).length > 0) { categoriesHtml = `
分类类别分布:
${Object.entries(results.categories).map(([category, count]) => `
${category}
${count}
文件数
`).join('')}
`; } // 构建失败文件列表HTML let failedFilesHtml = ''; if (results.failed > 0 && results.failed_files && results.failed_files.length > 0) { failedFilesHtml = `
处理失败的文件:
${results.failed_files.map(file => ` `).join('')}
文件名 错误原因
${file.filename} ${file.error}
`; } // 构建完整的结果HTML elements.batchResult.innerHTML = `
压缩包 ${archive_name} 的处理结果
总文件数
${results.total}
成功处理
${results.success}
处理失败
${results.failed}
成功率
${successRate}%
${categoriesHtml} ${failedFilesHtml}
`; elements.batchResult.style.display = 'block'; // 添加查看已处理文档按钮事件 document.querySelector('#batch-result .view-documents-btn')?.addEventListener('click', () => { navigateToPage('documents'); }); } // 加载已分类文档列表 async function loadDocuments() { showLoading(); try { const category = state.documentsData.currentCategory; const page = state.documentsData.pagination.current_page; const perPage = state.documentsData.pagination.per_page; const result = await apiRequest(`/api/classify/documents?category=${category}&page=${page}&per_page=${perPage}`); if (!result.success) { throw new Error(result.error || '加载文档失败'); } // 更新状态 state.documentsData.items = result.documents; state.documentsData.pagination = result.pagination; state.documentsData.categories = result.categories; state.documentsData.currentCategory = result.current_category; // 更新类别筛选下拉菜单 updateCategoryFilter(result.categories, result.current_category); // 更新文档表格 updateDocumentsTable(result.documents); // 更新分页控件 updatePagination(result.pagination); // 重置选中状态 state.documentsData.selectedIds = []; updateDownloadButtonState(); } catch (error) { console.error('加载文档失败:', error); elements.documentsTable.querySelector('tbody').innerHTML = ` 加载文档失败: ${error.message} `; } finally { hideLoading(); } } // 更新类别筛选下拉菜单 function updateCategoryFilter(categories, currentCategory) { const select = elements.categoryFilter; // 保存第一个全部选项 const allOption = select.querySelector('option[value="all"]'); // 清空现有选项 select.innerHTML = ''; // 添加全部选项 select.appendChild(allOption); // 添加类别选项 categories.forEach(category => { const option = document.createElement('option'); option.value = category; option.textContent = category; select.appendChild(option); }); // 设置当前选中值 select.value = currentCategory; } // 更新文档表格 function updateDocumentsTable(documents) { const tbody = elements.documentsTable.querySelector('tbody'); if (!documents || documents.length === 0) { tbody.innerHTML = ` 没有找到文档 `; return; } tbody.innerHTML = documents.map(doc => { // 格式化文件大小 const fileSize = formatFileSize(doc.file_size); // 格式化日期 const classifiedDate = new Date(doc.classified_time).toLocaleString('zh-CN'); return `
${doc.original_filename} ${doc.category} ${fileSize} ${classifiedDate} `; }).join(''); // 添加复选框事件监听 tbody.querySelectorAll('.document-checkbox').forEach(checkbox => { checkbox.addEventListener('change', handleDocumentCheckboxChange); }); } // 格式化文件大小 function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // 更新分页控件 function updatePagination(pagination) { const { total, per_page, current_page, total_pages } = pagination; if (total_pages <= 1) { elements.paginationContainer.innerHTML = ''; return; } // 构建分页按钮 let paginationHtml = `
显示 ${total} 条记录中的 ${Math.min((current_page - 1) * per_page + 1, total)} 至 ${Math.min(current_page * per_page, total)} 条
'; // 更新分页控件 elements.paginationContainer.innerHTML = paginationHtml; // 添加分页点击事件 elements.paginationContainer.querySelectorAll('.page-link').forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); if (this.parentElement.classList.contains('disabled')) { return; } const page = parseInt(this.dataset.page, 10); if (page && page !== current_page) { state.documentsData.pagination.current_page = page; loadDocuments(); } }); }); } // 处理文档复选框变化 function handleDocumentCheckboxChange(e) { const checkbox = e.target; const documentId = parseInt(checkbox.dataset.id, 10); if (checkbox.checked) { // 添加到选中列表 if (!state.documentsData.selectedIds.includes(documentId)) { state.documentsData.selectedIds.push(documentId); } } else { // 从选中列表移除 state.documentsData.selectedIds = state.documentsData.selectedIds.filter(id => id !== documentId); // 取消全选复选框 elements.selectAllDocs.checked = false; } // 更新下载按钮状态 updateDownloadButtonState(); } // 全选/取消全选文档 function toggleSelectAllDocuments(e) { const isChecked = e.target.checked; // 获取当前页面所有文档复选框 const checkboxes = elements.documentsTable.querySelectorAll('.document-checkbox'); checkboxes.forEach(checkbox => { checkbox.checked = isChecked; const documentId = parseInt(checkbox.dataset.id, 10); if (isChecked) { // 添加到选中列表 if (!state.documentsData.selectedIds.includes(documentId)) { state.documentsData.selectedIds.push(documentId); } } else { // 从选中列表移除 state.documentsData.selectedIds = state.documentsData.selectedIds.filter(id => id !== documentId); } }); // 更新下载按钮状态 updateDownloadButtonState(); } // 更新下载按钮状态 function updateDownloadButtonState() { const downloadBtn = elements.downloadSelectedBtn; if (state.documentsData.selectedIds.length > 0) { downloadBtn.classList.remove('disabled'); downloadBtn.textContent = `下载选中文件 (${state.documentsData.selectedIds.length})`; } else { downloadBtn.classList.add('disabled'); downloadBtn.innerHTML = ' 下载选中文件'; } } // 下载选中文档 async function downloadSelectedDocuments() { if (state.documentsData.selectedIds.length === 0) { return; } showLoading(); try { // 单个文件直接下载,多个文件使用API下载压缩包 if (state.documentsData.selectedIds.length === 1) { const documentId = state.documentsData.selectedIds[0]; window.location.href = `/api/classify/download/${documentId}`; hideLoading(); return; } // 多个文件,发送批量下载请求 const response = await fetch('/api/classify/download-multiple', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ document_ids: state.documentsData.selectedIds }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '下载失败'); } // 获取blob数据 const blob = await response.blob(); // 创建下载链接 const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = `documents_${new Date().getTime()}.zip`; document.body.appendChild(a); a.click(); // 清理 window.URL.revokeObjectURL(url); document.body.removeChild(a); } catch (error) { console.error('下载文档失败:', error); alert(`下载文档失败: ${error.message}`); } finally { hideLoading(); } } // 按类别筛选文档 function filterDocumentsByCategory(e) { const category = e.target.value; state.documentsData.currentCategory = category; state.documentsData.pagination.current_page = 1; loadDocuments(); } // 修改每页显示数量 function changePageSize(e) { const perPage = parseInt(e.target.value, 10); state.documentsData.pagination.per_page = perPage; state.documentsData.pagination.current_page = 1; loadDocuments(); }