From 84e7ae4ef17866e9f27f8d01d9d67baed8d71e8e Mon Sep 17 00:00:00 2001 From: SZCJW <792430652@qq.com> Date: Sun, 8 Mar 2026 00:51:39 +0800 Subject: [PATCH] x --- src/models/database.py | 4 + src/services/storage_integration_rustfs.py | 20 ++- static/style.css | 112 ++++++++++++++++ static/vue-app.js | 147 ++++++++++----------- 4 files changed, 204 insertions(+), 79 deletions(-) diff --git a/src/models/database.py b/src/models/database.py index 000a065..7b5cf95 100644 --- a/src/models/database.py +++ b/src/models/database.py @@ -162,6 +162,7 @@ class STPFile(Base): analysis_metrics = relationship("AnalysisMetrics", back_populates="stp_file", uselist=False) feature_detections = relationship("FeatureDetection", back_populates="stp_file") design_recommendations = relationship("DesignRecommendation", back_populates="stp_file") + processing_tasks = relationship("ProcessingTask", back_populates="stp_file") def __repr__(self): return f"" @@ -294,6 +295,9 @@ class ProcessingTask(Base): # 处理参数 parameters = Column(JSON, nullable=True) # 任务参数 + # 关联关系 + stp_file = relationship("STPFile", back_populates="processing_tasks") + def __repr__(self): return f"" diff --git a/src/services/storage_integration_rustfs.py b/src/services/storage_integration_rustfs.py index 024a191..c5b6eb7 100644 --- a/src/services/storage_integration_rustfs.py +++ b/src/services/storage_integration_rustfs.py @@ -535,8 +535,12 @@ class StorageIntegrationService: limit: int = 50 ) -> list: """获取同一文件名的所有上传历史记录""" + from models.database import ProcessingTask + from sqlalchemy.orm import joinedload - query = select(STPFile).where( + query = select(STPFile).options( + joinedload(STPFile.processing_tasks) + ).where( STPFile.original_filename == filename ).order_by(STPFile.upload_time.desc()) @@ -546,11 +550,12 @@ class StorageIntegrationService: query = query.limit(limit) result = await session.execute(query) - files = result.scalars().all() + files = result.unique().scalars().all() return [ { 'id': f.id, + 'task_id': f.processing_tasks[0].task_id if f.processing_tasks else None, 'upload_batch': f.upload_batch, 'upload_time': f.upload_time.isoformat() if f.upload_time else None, 'file_size': f.file_size, @@ -572,6 +577,8 @@ class StorageIntegrationService: """获取所有文件分组(按文件名分组),包含每个文件的最新分析结果""" from sqlalchemy import func, desc + from sqlalchemy.orm import joinedload + from models.database import ProcessingTask # 子查询:获取每个文件名的最新上传 subquery = ( @@ -591,7 +598,9 @@ class StorageIntegrationService: # 主查询:获取最新记录和统计信息 query = ( - select(STPFile) + select(STPFile).options( + joinedload(STPFile.processing_tasks) + ) .join( subquery, (STPFile.original_filename == subquery.c.original_filename) & @@ -601,7 +610,7 @@ class StorageIntegrationService: ) result = await session.execute(query) - latest_files = result.scalars().all() + latest_files = result.unique().scalars().all() # 获取每个文件名的上传次数 file_groups = [] @@ -615,9 +624,12 @@ class StorageIntegrationService: count_result = await session.execute(count_query) upload_count = count_result.scalar() + task_id = f.processing_tasks[0].task_id if f.processing_tasks else None + file_groups.append({ 'filename': f.original_filename, 'latest_id': f.id, + 'latest_task_id': task_id, 'latest_upload_time': f.upload_time.isoformat() if f.upload_time else None, 'latest_status': f.status, 'upload_count': upload_count, diff --git a/static/style.css b/static/style.css index 6a388ac..3783d25 100644 --- a/static/style.css +++ b/static/style.css @@ -525,6 +525,118 @@ body { border: 1px solid rgba(239, 68, 68, 0.2); } +/* ================================ + 历史记录下拉展开 + ================================ */ +.file-row { + cursor: pointer; + transition: background-color var(--duration-fast) var(--ease-default); +} + +.file-row:hover { + background-color: var(--bg-secondary); +} + +.expand-icon { + display: inline-block; + width: 20px; + color: var(--text-tertiary); + font-size: var(--text-xs); + transition: transform var(--duration-fast) var(--ease-default); +} + +.history-detail-row { + background-color: var(--bg-secondary); +} + +.history-detail-row td { + padding: 0; +} + +.history-detail { + padding: var(--space-4) var(--space-6); +} + +.inner-table { + background: var(--bg-primary); + border-radius: var(--radius-md); + overflow: hidden; + border: 1px solid var(--border-light); +} + +.inner-table th { + background: var(--bg-tertiary); + font-size: var(--text-xs); +} + +.inner-table td { + font-size: var(--text-xs); +} + +.action-buttons { + display: flex; + gap: var(--space-2); +} + +.btn-sm { + padding: var(--space-1) var(--space-3); + font-size: var(--text-xs); + border-radius: var(--radius-sm); +} + +/* ================================ + 进度条 + ================================ */ +.file-row { + cursor: pointer; + transition: background-color var(--duration-fast) var(--ease-default); +} + +.file-row:hover { + background-color: var(--bg-secondary); +} + +.expand-icon { + display: inline-block; + width: 20px; + color: var(--text-tertiary); + font-size: var(--text-xs); + transition: transform var(--duration-fast) var(--ease-default); +} + +.history-detail-row { + background-color: var(--bg-secondary); +} + +.history-detail-row td { + padding: 0; +} + +.history-detail { + padding: var(--space-4) var(--space-6); +} + +.inner-table { + background: var(--bg-primary); + border-radius: var(--radius-md); + overflow: hidden; + border: 1px solid var(--border-light); +} + +.inner-table th { + background: var(--bg-tertiary); + font-size: var(--text-xs); +} + +.inner-table td { + font-size: var(--text-xs); +} + +.action-buttons { + display: flex; + gap: var(--space-2); +} + /* ================================ 进度条 ================================ */ diff --git a/static/vue-app.js b/static/vue-app.js index 93d64d6..7756645 100644 --- a/static/vue-app.js +++ b/static/vue-app.js @@ -761,9 +761,7 @@ const MoldInsightView = { dragOver: false, progress: 0, history: null, - showFileHistory: false, - selectedFileHistory: null, - fileHistoryRecords: [] + expandedFiles: {} }); const loadHistory = async () => { @@ -774,19 +772,21 @@ const MoldInsightView = { } }; - const viewFileHistory = async (filename) => { - try { - state.selectedFileHistory = filename; - state.fileHistoryRecords = await apiRequest(`/api/history/${encodeURIComponent(filename)}`); - state.showFileHistory = true; - } catch (e) { - handleApiError(e, '加载文件历史'); + const toggleFileHistory = async (filename) => { + if (state.expandedFiles[filename]) { + state.expandedFiles[filename] = null; + } else { + try { + const records = await apiRequest(`/api/history/${encodeURIComponent(filename)}`); + state.expandedFiles[filename] = records; + } catch (e) { + handleApiError(e, '加载文件历史'); + } } }; const viewResult = (record) => { - state.showFileHistory = false; - router.push(`/moldinsight/result/${record.id}`); + router.push(`/moldinsight/result/${record.task_id}`); }; const handleFileChange = (event) => { @@ -899,7 +899,7 @@ const MoldInsightView = { uploadFile, formatFileSize, formatDateTime, - viewFileHistory, + toggleFileHistory, viewResult }; }, @@ -968,74 +968,71 @@ const MoldInsightView = { - - {{ file.filename }} - - {{ file.upload_count }} 次 - - {{ formatFileSize(file.file_size) }} - - - {{ file.latest_status }} - - - {{ formatDateTime(file.latest_upload_time) }} - -
- - -
- - - - - - - -