Files
geMoldInsight/templates/result.html
T
2026-02-16 02:56:11 +08:00

508 lines
19 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/result.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>
.result-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;
}
.task-info-card {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.task-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;
}
.status-badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
}
.status-completed {
background: #e8f5e8;
color: #2e7d32;
}
.status-processing {
background: #fff3e0;
color: #f57c00;
}
.status-failed {
background: #ffebee;
color: #c62828;
}
.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;
}
.error-message {
background: #ffebee;
border: 1px solid #ffcdd2;
border-radius: 6px;
padding: 15px;
color: #c62828;
margin: 20px 0;
}
.no-data {
text-align: center;
color: #999;
padding: 40px;
font-size: 16px;
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="result-container">
<div class="header-nav">
<div>
<h1>📊 历史记录 - 分析结果详情</h1>
<p>查看历史记录中STP文件的详细分析结果</p>
</div>
<div class="nav-buttons">
<button class="upload-btn" onclick="location.href='/history'">
📋 返回历史记录
</button>
<button class="upload-btn" onclick="location.href='/upload'">
📁 上传新文件
</button>
<button class="upload-btn" onclick="location.href='/'">
🏠 返回主页
</button>
</div>
</div>
<div id="taskInfo" class="task-info-card">
<div class="loading">
<div class="spinner"></div>
<p>正在加载任务信息...</p>
</div>
</div>
<div class="results-grid" id="resultsGrid">
<!-- 结果内容将通过JavaScript动态加载 -->
</div>
<!-- HTML可视化展示区域 -->
<div id="htmlVisualization" class="result-section" style="grid-column: 1 / -1; margin-top: 20px; display: none;">
<div class="section-title">🖥️ 3D可视化报告</div>
<div id="htmlViewerContainer">
<!-- HTML内容将在这里加载 -->
</div>
</div>
</div>
<script>
// 从URL获取任务ID
const pathParts = window.location.pathname.split('/');
const taskId = pathParts[pathParts.length - 1];
// 页面加载完成后获取任务数据
document.addEventListener('DOMContentLoaded', function() {
loadTaskData(taskId);
});
async function loadTaskData(taskId) {
try {
const response = await fetch(`/status/${taskId}`);
if (!response.ok) {
throw new Error('获取任务数据失败');
}
const task = await response.json();
displayTaskInfo(task);
displayResults(task);
// 加载HTML可视化内容
await loadHtmlVisualization(taskId);
} catch (error) {
document.getElementById('taskInfo').innerHTML = `
<div class="error-message">
<h3>❌ 加载失败</h3>
<p>${error.message}</p>
<button class="upload-btn" onclick="loadTaskData('${taskId}')">
重试
</button>
</div>
`;
}
}
async function loadHtmlVisualization(taskId) {
try {
// 从后端获取HTML文件信息
const response = await fetch(`/api/html-info/${taskId}`);
if (response.ok) {
const htmlInfo = await response.json();
if (htmlInfo && htmlInfo.file_path) {
displayHtmlVisualization(htmlInfo);
} else {
console.log('该任务没有HTML可视化报告');
}
} else {
console.log('获取HTML信息失败');
}
} catch (error) {
console.error('加载HTML可视化失败:', error);
}
}
function displayHtmlVisualization(htmlInfo) {
const htmlContainer = document.getElementById('htmlVisualization');
const viewerContainer = document.getElementById('htmlViewerContainer');
// 显示HTML可视化区域
htmlContainer.style.display = 'block';
// 创建iframe来显示HTML内容
viewerContainer.innerHTML = `
<div style="margin-bottom: 15px;">
<div class="data-item">
<div class="data-label">报告文件</div>
<div class="data-value">${htmlInfo.filename || 'N/A'}</div>
</div>
<div class="data-item">
<div class="data-label">生成时间</div>
<div class="data-value">${htmlInfo.generated_time ? new Date(htmlInfo.generated_time).toLocaleString() : 'N/A'}</div>
</div>
</div>
<div style="border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; height: 600px;">
<iframe
src="/html-output/${htmlInfo.filename}"
style="width: 100%; height: 100%; border: none;"
title="3D可视化报告"
></iframe>
</div>
<div class="action-buttons">
<button class="upload-btn" onclick="window.open('/html-output/${htmlInfo.filename}', '_blank')">
🔗 在新窗口中打开
</button>
<button class="upload-btn" onclick="downloadHtmlReport('${htmlInfo.filename}')">
📥 下载HTML报告
</button>
</div>
`;
}
function downloadHtmlReport(filename) {
window.open(`/html-output/${filename}?download=true`, '_blank');
}
function displayTaskInfo(task) {
const taskInfo = document.getElementById('taskInfo');
taskInfo.innerHTML = `
<h2>📝 任务信息</h2>
<div class="task-info-grid">
<div class="info-item">
<div class="info-label">任务ID</div>
<div class="info-value">${task.task_id || 'N/A'}</div>
</div>
<div class="info-item">
<div class="info-label">文件名</div>
<div class="info-value">${task.filename || 'N/A'}</div>
</div>
<div class="info-item">
<div class="info-label">文件大小</div>
<div class="info-value">${task.file_size ? formatFileSize(task.file_size) : 'N/A'}</div>
</div>
<div class="info-item">
<div class="info-label">状态</div>
<div class="info-value">
<span class="status-badge status-${task.status}">${getStatusText(task.status)}</span>
</div>
</div>
<div class="info-item">
<div class="info-label">上传时间</div>
<div class="info-value">${task.upload_time ? new Date(task.upload_time).toLocaleString() : 'N/A'}</div>
</div>
<div class="info-item">
<div class="info-label">完成时间</div>
<div class="info-value">${task.completed_at ? new Date(task.completed_at).toLocaleString() : 'N/A'}</div>
</div>
</div>
${task.error ? `
<div class="error-message" style="margin-top: 15px;">
<strong>错误信息:</strong> ${task.error}
</div>
` : ''}
`;
}
function displayResults(task) {
const resultsGrid = document.getElementById('resultsGrid');
if (task.status !== 'completed') {
resultsGrid.innerHTML = `
<div class="no-data">
<div style="font-size: 48px; margin-bottom: 20px;">⏳</div>
<h3>任务尚未完成</h3>
<p>当前状态: ${getStatusText(task.status)}</p>
</div>
`;
return;
}
resultsGrid.innerHTML = `
<div class="result-section">
<div class="section-title">📐 几何属性</div>
<div class="data-grid">
${displayGeometryData(task.geometry_data)}
</div>
</div>
<div class="result-section">
<div class="section-title">🔧 关键工艺参数</div>
<div class="data-grid">
${displayKeyInfo(task.key_info)}
</div>
</div>
<div class="result-section">
<div class="section-title">📦 边界框信息</div>
<div class="data-grid">
${displayBoundingBoxData(task.geometry_data)}
</div>
</div>
<div class="result-section">
<div class="section-title">🔺 拓扑结构</div>
<div class="data-grid">
${displayTopologyData(task.geometry_data)}
</div>
</div>
`;
}
function displayGeometryData(geometryData) {
if (!geometryData) return '<div class="no-data">无几何数据</div>';
return `
<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>
`;
}
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="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>
`;
}
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>