deving
This commit is contained in:
+2
-2
@@ -155,8 +155,8 @@ async function pollTaskStatus(taskId) {
|
||||
|
||||
if (task.status === 'completed') {
|
||||
loading.style.display = 'none';
|
||||
showResultsSection();
|
||||
displayAllResults(task);
|
||||
// 跳转到统一的分析结果页面
|
||||
window.location.href = `/result/${task.task_id}`;
|
||||
} else if (task.status === 'failed') {
|
||||
loading.style.display = 'none';
|
||||
showError('分析失败: ' + (task.error || '未知错误'));
|
||||
|
||||
@@ -1,467 +0,0 @@
|
||||
<!-- templates/snapshot.html -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>分析结果快照 - STP文件几何分析工具</title>
|
||||
<link rel="stylesheet" href="/static/style.css?v=12">
|
||||
<style>
|
||||
.snapshot-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header-nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.nav-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.snapshot-info-card {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.snapshot-info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.results-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
color: #333;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #4CAF50;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #ffebee;
|
||||
border: 1px solid #ffcdd2;
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
color: #c62828;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="snapshot-container">
|
||||
<div class="header-nav">
|
||||
<div>
|
||||
<h1>📊 分析结果快照</h1>
|
||||
<p>查看历史分析结果的完整副本</p>
|
||||
</div>
|
||||
<div class="nav-buttons">
|
||||
<button class="upload-btn" onclick="location.href='/history'">
|
||||
📋 返回历史记录
|
||||
</button>
|
||||
<button class="upload-btn" onclick="location.href='/'">
|
||||
📁 上传新文件
|
||||
</button>
|
||||
<button class="upload-btn" onclick="location.href='/')">
|
||||
🏠 返回主页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="snapshotInfo" class="snapshot-info-card">
|
||||
<div class="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>正在加载快照信息...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="results-grid" id="resultsGrid">
|
||||
<!-- 结果内容将通过JavaScript动态加载 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 从URL获取快照ID
|
||||
const pathParts = window.location.pathname.split('/');
|
||||
const snapshotId = pathParts[pathParts.length - 1];
|
||||
|
||||
// 页面加载完成后获取快照数据
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadSnapshotData(snapshotId);
|
||||
});
|
||||
|
||||
async function loadSnapshotData(snapshotId) {
|
||||
try {
|
||||
const response = await fetch(`/api/snapshot/${snapshotId}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('获取快照数据失败');
|
||||
}
|
||||
|
||||
const snapshot = await response.json();
|
||||
displaySnapshotInfo(snapshot);
|
||||
displayResults(snapshot);
|
||||
|
||||
} catch (error) {
|
||||
document.getElementById('snapshotInfo').innerHTML = `
|
||||
<div class="error-message">
|
||||
<h3>❌ 加载失败</h3>
|
||||
<p>${error.message}</p>
|
||||
<button class="upload-btn" onclick="loadSnapshotData('${snapshotId}')">
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function displaySnapshotInfo(snapshot) {
|
||||
const snapshotInfo = document.getElementById('snapshotInfo');
|
||||
|
||||
snapshotInfo.innerHTML = `
|
||||
<h2>📝 快照信息</h2>
|
||||
<div class="snapshot-info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">快照名称</div>
|
||||
<div class="info-value">${snapshot.snapshot_name || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">快照ID</div>
|
||||
<div class="info-value">${snapshot.id || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">文件ID</div>
|
||||
<div class="info-value">${snapshot.stp_file_id || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">分析时间</div>
|
||||
<div class="info-value">${snapshot.analysis_time ? new Date(snapshot.analysis_time).toLocaleString() : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">保存时间</div>
|
||||
<div class="info-value">${snapshot.created_time ? new Date(snapshot.created_time).toLocaleString() : 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function displayResults(snapshot) {
|
||||
const resultsGrid = document.getElementById('resultsGrid');
|
||||
|
||||
if (!snapshot.task_data) {
|
||||
resultsGrid.innerHTML = `
|
||||
<div class="no-data">
|
||||
<div style="font-size: 48px; margin-bottom: 20px;">📁</div>
|
||||
<h3>无分析结果数据</h3>
|
||||
<p>该快照没有包含分析结果数据</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
const task = snapshot.task_data;
|
||||
const geometryData = snapshot.geometry_data;
|
||||
const keyInfo = snapshot.key_info;
|
||||
|
||||
resultsGrid.innerHTML = `
|
||||
<div class="result-section">
|
||||
<div class="section-title">📝 任务信息</div>
|
||||
<div class="data-grid">
|
||||
<div class="data-item">
|
||||
<div class="data-label">文件名</div>
|
||||
<div class="data-value">${task.filename || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">文件大小</div>
|
||||
<div class="data-value">${task.file_size ? formatFileSize(task.file_size) : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">状态</div>
|
||||
<div class="data-value">${getStatusText(task.status)}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">上传时间</div>
|
||||
<div class="data-value">${task.upload_time ? new Date(task.upload_time).toLocaleString() : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">完成时间</div>
|
||||
<div class="data-value">${task.completed_at ? new Date(task.completed_at).toLocaleString() : 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${geometryData ? `
|
||||
<div class="result-section">
|
||||
<div class="section-title">📐 几何属性</div>
|
||||
<div class="data-grid">
|
||||
<div class="data-item">
|
||||
<div class="data-label">体积</div>
|
||||
<div class="data-value">${geometryData.volume ? formatNumber(geometryData.volume) + ' mm³' : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">表面积</div>
|
||||
<div class="data-value">${geometryData.surface_area ? formatNumber(geometryData.surface_area) + ' mm²' : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">体积表面积比</div>
|
||||
<div class="data-value">${geometryData.volume && geometryData.surface_area ? (geometryData.volume / geometryData.surface_area).toFixed(4) : 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
` : '<div class="no-data">无几何数据</div>'}
|
||||
|
||||
${keyInfo ? `
|
||||
<div class="result-section">
|
||||
<div class="section-title">🔧 关键工艺参数</div>
|
||||
${displayKeyInfo(keyInfo)}
|
||||
</div>
|
||||
` : '<div class="no-data">无关键信息数据</div>'}
|
||||
|
||||
${geometryData && geometryData.bounding_box ? `
|
||||
<div class="result-section">
|
||||
<div class="section-title">📦 边界框信息</div>
|
||||
<div class="data-grid">
|
||||
${displayBoundingBoxData(geometryData)}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
${geometryData && geometryData.topology ? `
|
||||
<div class="result-section">
|
||||
<div class="section-title">🔺 拓扑结构</div>
|
||||
<div class="data-grid">
|
||||
${displayTopologyData(geometryData)}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
function displayKeyInfo(keyInfo) {
|
||||
if (!keyInfo) return '<div class="no-data">无关键信息数据</div>';
|
||||
|
||||
const metadata = keyInfo.metadata || {};
|
||||
const manufacturingInfo = keyInfo.manufacturing_info || {};
|
||||
const moldCavities = keyInfo.mold_cavities || {};
|
||||
const cavityKeyInfo = moldCavities.cavity_key_info || {};
|
||||
const geoChars = cavityKeyInfo.geometric_characteristics || {};
|
||||
|
||||
return `
|
||||
<div class="key-info-categories">
|
||||
<!-- 模具参数 -->
|
||||
<div class="category-section">
|
||||
<div class="category-title">🔧 模具参数</div>
|
||||
<div class="category-content">
|
||||
<div class="data-item">
|
||||
<div class="data-label">收缩率</div>
|
||||
<div class="data-value">${metadata.shrinkage_rate !== undefined ? metadata.shrinkage_rate : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">拔模角</div>
|
||||
<div class="data-value">${metadata.draft_angle !== undefined ? metadata.draft_angle + '°' : 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">型腔材料</div>
|
||||
<div class="data-value">${manufacturingInfo.mold_material || 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 几何特性 -->
|
||||
<div class="category-section">
|
||||
<div class="category-title">📐 几何特性</div>
|
||||
<div class="category-content">
|
||||
<div class="data-item">
|
||||
<div class="data-label">产品体积</div>
|
||||
<div class="data-value">${geoChars.product_volume || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">产品重量</div>
|
||||
<div class="data-value">${geoChars.product_weight || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">壁厚范围</div>
|
||||
<div class="data-value">${geoChars.wall_thickness_range || 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 制造要求 -->
|
||||
<div class="category-section">
|
||||
<div class="category-title">🏭 制造要求</div>
|
||||
<div class="category-content">
|
||||
<div class="data-item">
|
||||
<div class="data-label">分型线长度</div>
|
||||
<div class="data-value">${manufacturingInfo.parting_line_length || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">预估周期</div>
|
||||
<div class="data-value">${manufacturingInfo.estimated_cycle_time || 'N/A'}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">制造精度</div>
|
||||
<div class="data-value">${manufacturingInfo.manufacturing_tolerance || 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function displayBoundingBoxData(geometryData) {
|
||||
if (!geometryData || !geometryData.bounding_box) return '<div class="no-data">无边界框数据</div>';
|
||||
|
||||
const bbox = geometryData.bounding_box;
|
||||
return `
|
||||
<div class="data-item">
|
||||
<div class="data-label">最小坐标</div>
|
||||
<div class="data-value">X: ${bbox.min[0].toFixed(2)}<br>Y: ${bbox.min[1].toFixed(2)}<br>Z: ${bbox.min[2].toFixed(2)}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">最大坐标</div>
|
||||
<div class="data-value">X: ${bbox.max[0].toFixed(2)}<br>Y: ${bbox.max[1].toFixed(2)}<br>Z: ${bbox.max[2].toFixed(2)}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">尺寸</div>
|
||||
<div class="data-value">${bbox.dimensions[0].toFixed(2)} × ${bbox.dimensions[1].toFixed(2)} × ${bbox.dimensions[2].toFixed(2)} mm</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function displayTopologyData(geometryData) {
|
||||
if (!geometryData || !geometryData.topology) return '<div class="no-data">无拓扑数据</div>';
|
||||
|
||||
const topo = geometryData.topology;
|
||||
return `
|
||||
<div class="data-item">
|
||||
<div class="data-label">面数</div>
|
||||
<div class="data-value">${topo.faces || 0}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">边数</div>
|
||||
<div class="data-value">${topo.edges || 0}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">顶点数</div>
|
||||
<div class="data-value">${topo.vertices || 0}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
function getStatusText(status) {
|
||||
const statusMap = {
|
||||
'processing': '处理中',
|
||||
'completed': '已完成',
|
||||
'failed': '失败'
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
}
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (!bytes || 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 formatNumber(num) {
|
||||
if (!num) return 'N/A';
|
||||
if (num >= 1000000) {
|
||||
return (num / 1000000).toFixed(2) + 'M';
|
||||
} else if (num >= 1000) {
|
||||
return (num / 1000).toFixed(2) + 'K';
|
||||
} else {
|
||||
return num.toFixed(2);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user