Files
geMoldInsight/templates/result.html
T
2026-02-16 18:03:17 +08:00

744 lines
31 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?v=14">
<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;
}
/* 绝对强制关键工艺参数一行显示 - 三列并排 */
.key-info-categories {
display: flex !important;
flex-direction: row !important;
justify-content: space-between !important;
align-items: stretch !important;
gap: 15px !important;
margin-bottom: 20px !important;
width: 100% !important;
min-height: 220px !important;
flex-wrap: nowrap !important;
overflow: visible !important;
white-space: nowrap !important;
box-sizing: border-box !important;
}
.key-info-categories .category-section {
display: flex !important;
flex-direction: column !important;
width: 32% !important;
min-width: 280px !important;
max-width: 380px !important;
min-height: 220px !important;
background: #f8f9fa !important;
border: 1px solid #e0e0e0 !important;
border-radius: 8px !important;
padding: 15px !important;
flex-shrink: 0 !important;
flex-grow: 0 !important;
box-sizing: border-box !important;
}
.key-info-categories .category-title {
font-weight: bold !important;
font-size: 16px !important;
color: #333 !important;
margin-bottom: 15px !important;
padding-bottom: 8px !important;
border-bottom: 1px solid #ddd !important;
}
.key-info-categories .category-content {
display: flex !important;
flex-direction: column !important;
gap: 12px !important;
}
.key-info-categories .data-item {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 8px 0 !important;
border-bottom: 1px solid #f0f0f0 !important;
}
.key-info-categories .data-item:last-child {
border-bottom: none !important;
}
.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='/'">
📁 上传新文件
</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>
</div>
<script>
// 从模板获取任务数据
const task = JSON.parse('{{ task | tojson | safe }}');
// 页面加载完成后显示任务数据
document.addEventListener('DOMContentLoaded', function() {
if (task && task.task_id) {
displayTaskInfo(task);
displayResults(task);
} else {
document.getElementById('taskInfo').innerHTML = `
<div class="error-message">
<h3>❌ 加载失败</h3>
<p>无法获取任务数据</p>
</div>
`;
}
});
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" style="grid-column: 1 / -1;">
<div class="section-title">🔧 关键工艺参数</div>
${displayKeyInfo(task.key_info)}
</div>
<div class="result-section">
<div class="section-title">🏭 模具型腔数据</div>
<div class="data-grid">
${displayCavityData(task)}
</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>
<div class="result-section">
<div class="section-title">🎯 检测到的特征</div>
<div class="data-grid">
${displayFeaturesData(task)}
</div>
</div>
<div class="result-section">
<div class="section-title">💡 设计建议</div>
<div class="data-grid">
${displayRecommendationsData(task)}
</div>
</div>
<div class="result-section">
<div class="section-title">📈 质量指标</div>
<div class="data-grid">
${displayMetricsData(task)}
</div>
</div>
<div class="result-section">
<div class="section-title">🔍 分析信息</div>
<div class="data-grid">
${displayAnalysisInfo(task)}
</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 style="display: flex; flex-direction: row; justify-content: space-between; gap: 15px; width: 100%; flex-wrap: nowrap; box-sizing: border-box;">
<!-- 模具参数 -->
<div style="display: flex; flex-direction: column; width: 32%; min-width: 280px; background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; flex-shrink: 0; box-sizing: border-box;">
<div style="font-weight: bold; font-size: 16px; color: #333; margin-bottom: 15px; padding-bottom: 8px; border-bottom: 1px solid #ddd;">🔧 模具参数</div>
<div style="display: flex; flex-direction: column; gap: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="font-weight: bold; color: #666;">收缩率</div>
<div style="color: #333; text-align: right;">${metadata.shrinkage_rate !== undefined ? metadata.shrinkage_rate : 'N/A'}</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="font-weight: bold; color: #666;">拔模角</div>
<div style="color: #333; text-align: right;">${metadata.draft_angle !== undefined ? metadata.draft_angle + '°' : 'N/A'}</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0;">
<div style="font-weight: bold; color: #666;">型腔材料</div>
<div style="color: #333; text-align: right;">${manufacturingInfo.mold_material || 'N/A'}</div>
</div>
</div>
</div>
<!-- 几何特性 -->
<div style="display: flex; flex-direction: column; width: 32%; min-width: 280px; background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; flex-shrink: 0; box-sizing: border-box;">
<div style="font-weight: bold; font-size: 16px; color: #333; margin-bottom: 15px; padding-bottom: 8px; border-bottom: 1px solid #ddd;">📐 几何特性</div>
<div style="display: flex; flex-direction: column; gap: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="font-weight: bold; color: #666;">产品体积</div>
<div style="color: #333; text-align: right;">${geoChars.product_volume || 'N/A'}</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="font-weight: bold; color: #666;">产品重量</div>
<div style="color: #333; text-align: right;">${geoChars.product_weight || 'N/A'}</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0;">
<div style="font-weight: bold; color: #666;">壁厚范围</div>
<div style="color: #333; text-align: right;">${geoChars.wall_thickness_range || 'N/A'}</div>
</div>
</div>
</div>
<!-- 制造要求 -->
<div style="display: flex; flex-direction: column; width: 32%; min-width: 280px; background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; flex-shrink: 0; box-sizing: border-box;">
<div style="font-weight: bold; font-size: 16px; color: #333; margin-bottom: 15px; padding-bottom: 8px; border-bottom: 1px solid #ddd;">🏭 制造要求</div>
<div style="display: flex; flex-direction: column; gap: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="font-weight: bold; color: #666;">分型线长度</div>
<div style="color: #333; text-align: right;">${manufacturingInfo.parting_line_length || 'N/A'}</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="font-weight: bold; color: #666;">预估周期</div>
<div style="color: #333; text-align: right;">${manufacturingInfo.estimated_cycle_time || 'N/A'}</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0;">
<div style="font-weight: bold; color: #666;">制造精度</div>
<div style="color: #333; text-align: right;">${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 displayCavityData(task) {
// 检查是否有型腔数据
let cavityData = {};
// 检查多个可能的数据位置
if (task.mold_cavity_data) {
cavityData = task.mold_cavity_data;
} else if (task.cavity_data) {
cavityData = task.cavity_data;
} else if (task.key_info && task.key_info.mold_cavities) {
cavityData = task.key_info;
}
if (!cavityData || Object.keys(cavityData).length === 0) {
return '<div class="no-data">无型腔数据</div>';
}
// 显示完整的JSON格式数据
return `
<div style="background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 15px; margin-top: 10px;">
<div style="font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.4; white-space: pre-wrap; overflow-x: auto;">
${JSON.stringify(cavityData, null, 2)}
</div>
</div>
`;
}
function displayFeaturesData(task) {
// 检查是否有特征数据
let features = [];
// 检查多个可能的数据位置
if (task.features && task.features.length > 0) {
features = task.features;
} else if (task.mold_cavity_data && task.mold_cavity_data.detected_features) {
features = task.mold_cavity_data.detected_features;
} else if (task.detected_features) {
features = task.detected_features;
}
if (features.length > 0) {
return features.map(feature => `
<div class="data-item">
<div class="data-label">${feature.feature_type || '未知特征'}</div>
<div class="data-value">
${feature.description ? feature.description + '<br>' : ''}
置信度: ${(feature.confidence || 0).toFixed(2)}
${feature.location ? '<br>位置: ' + feature.location : ''}
</div>
</div>
`).join('');
}
return '<div class="no-data">无特征数据</div>';
}
function displayRecommendationsData(task) {
// 检查是否有建议数据
let recommendations = [];
// 检查多个可能的数据位置
if (task.recommendations && task.recommendations.length > 0) {
recommendations = task.recommendations;
} else if (task.mold_cavity_data && task.mold_cavity_data.design_recommendations) {
recommendations = task.mold_cavity_data.design_recommendations;
} else if (task.design_recommendations) {
recommendations = task.design_recommendations;
}
if (recommendations.length > 0) {
return recommendations.map(rec => `
<div class="data-item">
<div class="data-label">${rec.rec_type || '建议'}</div>
<div class="data-value">
优先级: ${rec.priority || '中'}
${rec.description ? '<br>' + rec.description : ''}
${rec.reason ? '<br>原因: ' + rec.reason : ''}
</div>
</div>
`).join('');
}
return '<div class="no-data">无建议数据</div>';
}
function displayMetricsData(task) {
// 检查是否有质量指标数据
let metrics = {};
// 检查多个可能的数据位置
if (task.quality_metrics) {
metrics = task.quality_metrics;
} else if (task.mold_cavity_data && task.mold_cavity_data.quality_metrics) {
metrics = task.mold_cavity_data.quality_metrics;
}
let metricsHTML = '';
// 如果有质量指标数据
if (Object.keys(metrics).length > 0) {
// 体积利用率
if (metrics.volume_utilization) {
metricsHTML += `
<div class="data-item">
<div class="data-label">体积利用率</div>
<div class="data-value">${metrics.volume_utilization}%</div>
</div>
`;
}
// 壁厚均匀性
if (metrics.wall_thickness_uniformity) {
metricsHTML += `
<div class="data-item">
<div class="data-label">壁厚均匀性</div>
<div class="data-value">${metrics.wall_thickness_uniformity}%</div>
</div>
`;
}
// 几何复杂度
if (metrics.geometric_complexity) {
metricsHTML += `
<div class="data-item">
<div class="data-label">几何复杂度</div>
<div class="data-value">${metrics.geometric_complexity}</div>
</div>
`;
}
// 可成型性评分
if (metrics.moldability_score) {
metricsHTML += `
<div class="data-item">
<div class="data-label">可成型性评分</div>
<div class="data-value">${metrics.moldability_score}</div>
</div>
`;
}
}
// 如果没有质量指标数据,从几何数据中计算一些基础指标
if (task.geometry_data && metricsHTML === '') {
const geo = task.geometry_data;
// 体积表面积比作为质量指标
if (geo.volume && geo.surface_area) {
const volumeSurfaceRatio = geo.volume / geo.surface_area;
metricsHTML += `
<div class="data-item">
<div class="data-label">体积表面积比</div>
<div class="data-value">${volumeSurfaceRatio.toFixed(4)}</div>
</div>
`;
}
// 如果有边界框数据,计算紧凑度
if (geo.bounding_box && geo.bounding_box.dimensions) {
const dims = geo.bounding_box.dimensions;
const compactness = geo.volume ? geo.volume / (dims[0] * dims[1] * dims[2]) : 0;
if (compactness > 0) {
metricsHTML += `
<div class="data-item">
<div class="data-label">几何紧凑度</div>
<div class="data-value">${(compactness * 100).toFixed(2)}%</div>
</div>
`;
}
}
// 拓扑复杂度
if (geo.topology) {
const faces = geo.topology.faces || 0;
const edges = geo.topology.edges || 0;
const vertices = geo.topology.vertices || 0;
metricsHTML += `
<div class="data-item">
<div class="data-label">拓扑复杂度</div>
<div class="data-value">面:${faces} 边:${edges} 顶点:${vertices}</div>
</div>
`;
}
}
return metricsHTML || '<div class="no-data">无质量指标数据</div>';
}
function displayAnalysisInfo(task) {
let infoHTML = '';
if (task.geometry_data) {
const geo = task.geometry_data;
infoHTML += `
<div class="data-item">
<div class="data-label">分析时间</div>
<div class="data-value">${task.upload_time || 'N/A'}</div>
</div>
<div class="data-item">
<div class="data-label">分析方法</div>
<div class="data-value">${geo.analysis_method || 'N/A'}</div>
</div>
`;
}
if (task.analysis_result) {
infoHTML += `
<div class="data-item">
<div class="data-label">分析状态</div>
<div class="data-value">${task.status || 'N/A'}</div>
</div>
`;
}
return infoHTML || '<div class="no-data">无分析信息</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>