2026-02-16 01:18:51 +08:00
|
|
|
|
<!-- 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>
|
2026-02-16 18:03:17 +08:00
|
|
|
|
<link rel="stylesheet" href="/static/style.css?v=14">
|
2026-02-16 01:18:51 +08:00
|
|
|
|
<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;
|
|
|
|
|
|
}
|
2026-02-16 16:36:44 +08:00
|
|
|
|
|
2026-02-16 17:17:21 +08:00
|
|
|
|
/* 绝对强制关键工艺参数一行显示 - 三列并排 */
|
2026-02-16 16:36:44 +08:00
|
|
|
|
.key-info-categories {
|
2026-02-16 17:10:28 +08:00
|
|
|
|
display: flex !important;
|
|
|
|
|
|
flex-direction: row !important;
|
|
|
|
|
|
justify-content: space-between !important;
|
2026-02-16 17:17:21 +08:00
|
|
|
|
align-items: stretch !important;
|
|
|
|
|
|
gap: 15px !important;
|
2026-02-16 16:36:44 +08:00
|
|
|
|
margin-bottom: 20px !important;
|
|
|
|
|
|
width: 100% !important;
|
2026-02-16 17:17:21 +08:00
|
|
|
|
min-height: 220px !important;
|
2026-02-16 17:10:28 +08:00
|
|
|
|
flex-wrap: nowrap !important;
|
2026-02-16 17:17:21 +08:00
|
|
|
|
overflow: visible !important;
|
2026-02-16 17:10:28 +08:00
|
|
|
|
white-space: nowrap !important;
|
2026-02-16 17:17:21 +08:00
|
|
|
|
box-sizing: border-box !important;
|
2026-02-16 16:36:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.key-info-categories .category-section {
|
2026-02-16 17:17:21 +08:00
|
|
|
|
display: flex !important;
|
|
|
|
|
|
flex-direction: column !important;
|
|
|
|
|
|
width: 32% !important;
|
|
|
|
|
|
min-width: 280px !important;
|
|
|
|
|
|
max-width: 380px !important;
|
|
|
|
|
|
min-height: 220px !important;
|
2026-02-16 16:36:44 +08:00
|
|
|
|
background: #f8f9fa !important;
|
|
|
|
|
|
border: 1px solid #e0e0e0 !important;
|
|
|
|
|
|
border-radius: 8px !important;
|
|
|
|
|
|
padding: 15px !important;
|
2026-02-16 17:10:28 +08:00
|
|
|
|
flex-shrink: 0 !important;
|
|
|
|
|
|
flex-grow: 0 !important;
|
2026-02-16 17:17:21 +08:00
|
|
|
|
box-sizing: border-box !important;
|
2026-02-16 16:36:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
2026-02-16 16:25:32 +08:00
|
|
|
|
|
2026-02-16 01:18:51 +08:00
|
|
|
|
.data-label {
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
2026-02-16 16:25:32 +08:00
|
|
|
|
|
2026-02-16 01:18:51 +08:00
|
|
|
|
.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>
|
2026-02-16 03:01:22 +08:00
|
|
|
|
<h1>📊 分析结果详情</h1>
|
|
|
|
|
|
<p>查看STP文件的详细分析结果</p>
|
2026-02-16 01:18:51 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="nav-buttons">
|
|
|
|
|
|
<button class="upload-btn" onclick="location.href='/history'">
|
|
|
|
|
|
📋 返回历史记录
|
|
|
|
|
|
</button>
|
2026-02-16 15:33:52 +08:00
|
|
|
|
<button class="upload-btn" onclick="location.href='/'">
|
2026-02-16 01:18:51 +08:00
|
|
|
|
📁 上传新文件
|
|
|
|
|
|
</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>
|
2026-02-16 16:25:32 +08:00
|
|
|
|
// 从模板获取任务数据
|
2026-02-16 17:10:28 +08:00
|
|
|
|
const task = JSON.parse('{{ task | tojson | safe }}');
|
2026-02-16 16:25:32 +08:00
|
|
|
|
|
|
|
|
|
|
// 页面加载完成后显示任务数据
|
2026-02-16 01:18:51 +08:00
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2026-02-16 16:25:32 +08:00
|
|
|
|
if (task && task.task_id) {
|
2026-02-16 01:18:51 +08:00
|
|
|
|
displayTaskInfo(task);
|
|
|
|
|
|
displayResults(task);
|
2026-02-16 16:25:32 +08:00
|
|
|
|
} else {
|
2026-02-16 01:18:51 +08:00
|
|
|
|
document.getElementById('taskInfo').innerHTML = `
|
|
|
|
|
|
<div class="error-message">
|
|
|
|
|
|
<h3>❌ 加载失败</h3>
|
2026-02-16 16:25:32 +08:00
|
|
|
|
<p>无法获取任务数据</p>
|
2026-02-16 01:18:51 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
}
|
2026-02-16 16:25:32 +08:00
|
|
|
|
});
|
2026-02-16 01:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
|
2026-02-16 17:38:08 +08:00
|
|
|
|
<div class="result-section" style="grid-column: 1 / -1;">
|
2026-02-16 01:18:51 +08:00
|
|
|
|
<div class="section-title">🔧 关键工艺参数</div>
|
2026-02-16 16:12:17 +08:00
|
|
|
|
${displayKeyInfo(task.key_info)}
|
2026-02-16 01:18:51 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-16 18:03:17 +08:00
|
|
|
|
<div class="result-section">
|
|
|
|
|
|
<div class="section-title">🏭 模具型腔数据</div>
|
|
|
|
|
|
<div class="data-grid">
|
|
|
|
|
|
${displayCavityData(task)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-16 01:18:51 +08:00
|
|
|
|
<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>
|
2026-02-16 17:48:46 +08:00
|
|
|
|
|
|
|
|
|
|
<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>
|
2026-02-16 01:18:51 +08:00
|
|
|
|
`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 `
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<div style="display: flex; flex-direction: row; justify-content: space-between; gap: 15px; width: 100%; flex-wrap: nowrap; box-sizing: border-box;">
|
2026-02-16 15:59:25 +08:00
|
|
|
|
<!-- 模具参数 -->
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-16 15:47:28 +08:00
|
|
|
|
</div>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 几何特性 -->
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-16 15:47:28 +08:00
|
|
|
|
</div>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 制造要求 -->
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
2026-02-16 17:23:22 +08:00
|
|
|
|
<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>
|
2026-02-16 15:59:25 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-16 15:47:28 +08:00
|
|
|
|
</div>
|
2026-02-16 01:18:51 +08:00
|
|
|
|
</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>
|
|
|
|
|
|
`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-16 18:03:17 +08:00
|
|
|
|
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>
|
|
|
|
|
|
`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-16 17:48:46 +08:00
|
|
|
|
function displayFeaturesData(task) {
|
2026-02-16 18:03:17 +08:00
|
|
|
|
// 检查是否有特征数据
|
|
|
|
|
|
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 : ''}
|
2026-02-16 17:48:46 +08:00
|
|
|
|
</div>
|
2026-02-16 18:03:17 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
`).join('');
|
2026-02-16 17:48:46 +08:00
|
|
|
|
}
|
2026-02-16 18:03:17 +08:00
|
|
|
|
return '<div class="no-data">无特征数据</div>';
|
2026-02-16 17:48:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function displayRecommendationsData(task) {
|
2026-02-16 18:03:17 +08:00
|
|
|
|
// 检查是否有建议数据
|
|
|
|
|
|
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 : ''}
|
2026-02-16 17:48:46 +08:00
|
|
|
|
</div>
|
2026-02-16 18:03:17 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
`).join('');
|
2026-02-16 17:48:46 +08:00
|
|
|
|
}
|
2026-02-16 18:03:17 +08:00
|
|
|
|
return '<div class="no-data">无建议数据</div>';
|
2026-02-16 17:48:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function displayMetricsData(task) {
|
2026-02-16 18:03:17 +08:00
|
|
|
|
// 检查是否有质量指标数据
|
|
|
|
|
|
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;
|
2026-02-16 17:48:46 +08:00
|
|
|
|
}
|
2026-02-16 18:03:17 +08:00
|
|
|
|
|
|
|
|
|
|
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>';
|
2026-02-16 17:48:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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>';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-16 01:18:51 +08:00
|
|
|
|
// 工具函数
|
|
|
|
|
|
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>
|