Files
geMoldInsight/templates/snapshot.html
T
2026-02-16 15:47:28 +08:00

453 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 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">
<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-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f5f5f5;
}
.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>
<div class="data-grid">
${displayKeyInfo(keyInfo)}
</div>
</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-grid">
<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.parting_line_length || 'N/A'}</div>
</div>
<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 class="data-item">
<div class="data-label">型腔材料</div>
<div class="data-value">${manufacturingInfo.mold_material || '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>
`;
}
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>