This commit is contained in:
cjw
2026-02-17 00:12:36 +08:00
parent 44d431f4af
commit 64acdb0e8c
5 changed files with 770 additions and 738 deletions
+6 -195
View File
@@ -4,204 +4,15 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>历史记录 - STP文件几何分析工具</title>
<title>历史记录 - STP 模具几何分析中心</title>
<link rel="stylesheet" href="/static/style.css">
<style>
.history-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;
}
.file-list {
display: grid;
gap: 15px;
margin-bottom: 30px;
}
.file-item {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
}
.file-item:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.file-item.expanded {
border-color: #4CAF50;
background: #f9fff9;
}
.file-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.file-info {
flex: 1;
}
.file-name {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.file-meta {
color: #666;
font-size: 14px;
}
.expand-btn {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #666;
}
.record-list {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eee;
display: none;
}
.file-item.expanded .record-list {
display: block;
}
.record-item {
background: white;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 15px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s ease;
}
.record-item:hover {
background: #f5f5f5;
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.record-time {
font-weight: bold;
color: #333;
}
.record-status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.status-completed {
background: #e8f5e8;
color: #2e7d32;
}
.status-processing {
background: #fff3e0;
color: #f57c00;
}
.status-failed {
background: #ffebee;
color: #c62828;
}
.record-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
font-size: 14px;
color: #666;
}
.no-records {
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); }
}
</style>
</head>
<body>
<div class="history-container">
<div class="header-nav">
<div>
<h1>📊 历史记录</h1>
<p>查看所有上传的STP文件分析记录</p>
</div>
<div class="nav-buttons">
<a href="/" class="upload-btn" style="text-decoration: none; display: inline-block; text-align: center;">
📁 上传新文件
</a>
<a href="/" class="upload-btn" style="text-decoration: none; display: inline-block; text-align: center;">
🔄 返回主页
</a>
</div>
</div>
<div id="app"></div>
<div class="file-list" id="fileList">
<div class="loading">
<div class="spinner"></div>
<p>正在加载历史记录...</p>
</div>
</div>
</div>
<script src="/static/history.js"></script>
<!-- Vue3 + Vue Router via CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.prod.js"></script>
<script src="/static/vue-app.js"></script>
</body>
</html>
+6 -118
View File
@@ -4,127 +4,15 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>STP文件几何分析工具</title>
<title>STP 模具几何分析中心</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container">
<div class="header">
<h1>🔧 STP文件几何分析工具</h1>
<p>上传STP/STEP文件,自动分析几何属性和拓扑结构</p>
</div>
<div id="app"></div>
<!-- 上传区域 - 默认显示 -->
<div class="upload-section" id="uploadSection">
<h2>选择STP文件</h2>
<p>支持 .stp 和 .step 格式文件</p>
<div class="upload-area" id="uploadArea">
<div class="upload-icon">📁</div>
<h3>拖放文件到此处或点击选择</h3>
<p>最大文件大小: 100MB</p>
<input type="file" id="fileInput" class="file-input" accept=".stp,.step">
<button class="upload-btn" onclick="document.getElementById('fileInput').click()">
选择文件
</button>
</div>
<button class="upload-btn" id="uploadBtn" onclick="uploadFile()" disabled>
开始分析
</button>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>正在分析文件,请稍候...</p>
</div>
<div class="error-message" id="errorMessage"></div>
</div>
<!-- 结果区域 - 默认隐藏 -->
<div class="results-section" id="resultsSection" style="display: none;">
<h2>📊 分析结果</h2>
<!-- 任务基本信息 -->
<div class="result-card">
<h3>📝 任务信息</h3>
<div class="task-info" id="taskInfo"></div>
</div>
<!-- 几何属性 -->
<div class="result-card">
<h3>📐 几何属性</h3>
<div class="geometry-data" id="geometryData"></div>
</div>
<!-- 边界框信息 -->
<div class="result-card">
<h3>📦 边界框信息</h3>
<div class="bounding-box-data" id="boundingBoxData"></div>
</div>
<!-- 拓扑结构 -->
<div class="result-card">
<h3>🔺 拓扑结构</h3>
<div class="topology-data" id="topologyData"></div>
</div>
<div class="result-card">
<h3>🔧 模具型腔设计</h3>
<div class="cavity-data" id="cavityData"></div>
</div>
<div class="result-card">
<h3>📐 关键工艺参数</h3>
<div class="key-info-data" id="keyInfoData"></div>
</div>
<!-- 检测到的特征 -->
<div class="result-card">
<h3>🎯 检测到的特征</h3>
<div class="features-data" id="featuresData"></div>
</div>
<!-- 设计建议 -->
<div class="result-card">
<h3>💡 设计建议</h3>
<div class="recommendations-data" id="recommendationsData"></div>
</div>
<!-- 质量指标 -->
<div class="result-card">
<h3>📈 质量指标</h3>
<div class="metrics-data" id="metricsData"></div>
</div>
<!-- 分析信息 -->
<div class="result-card">
<h3>🔍 分析信息</h3>
<div class="analysis-info" id="analysisInfo"></div>
</div>
<!-- 返回按钮 -->
<div style="text-align: center; margin-top: 30px;">
<button class="upload-btn" onclick="showUploadSection()">
🔄 分析新文件
</button>
</div>
</div>
<div class="system-info">
<div style="display: flex; justify-content: space-between; align-items: center;">
<p>PythonOCC 可用: {{ pythonocc_available }} | 服务版本: 3.0.0</p>
<div>
<a href="/history" class="upload-btn" style="margin-left: 10px; text-decoration: none; display: inline-block; text-align: center;">
📋 查看历史记录
</a>
</div>
</div>
</div>
</div>
<script src="/static/script.js"></script>
<!-- Vue3 + Vue Router via CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.prod.js"></script>
<script src="/static/vue-app.js"></script>
</body>
</html>
+6 -425
View File
@@ -4,434 +4,15 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分析结果 - STP文件几何分析工具</title>
<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="app"></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>
// 从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);
} 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>
`;
}
}
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>
<!-- Vue3 + Vue Router via CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.prod.js"></script>
<script src="/static/vue-app.js"></script>
</body>
</html>