// 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]) => `
`).join('')}
`;
}
// 构建失败文件列表HTML
let failedFilesHtml = '';
if (results.failed > 0 && results.failed_files && results.failed_files.length > 0) {
failedFilesHtml = `
处理失败的文件:
文件名 |
错误原因 |
${results.failed_files.map(file => `
${file.filename} |
${file.error} |
`).join('')}
`;
}
// 构建完整的结果HTML
elements.batchResult.innerHTML = `
压缩包 ${archive_name} 的处理结果
${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();
}