From 06225e0d650d4bbec5b9753369a628c5cdbd31e4 Mon Sep 17 00:00:00 2001
From: cjw <792430652@qq.com>
Date: Tue, 17 Feb 2026 00:42:55 +0800
Subject: [PATCH] deving
---
static/history.js | 215 --------------
static/script.js | 657 -----------------------------------------
static/style.css | 291 +++++++++++++-----
static/vue-app.js | 243 +++++++++++----
templates/history.html | 4 +-
templates/index.html | 14 +-
templates/result.html | 4 +-
7 files changed, 423 insertions(+), 1005 deletions(-)
delete mode 100644 static/history.js
delete mode 100644 static/script.js
diff --git a/static/history.js b/static/history.js
deleted file mode 100644
index e2d9c47..0000000
--- a/static/history.js
+++ /dev/null
@@ -1,215 +0,0 @@
-// static/history.js
-
-// 页面加载完成后加载历史记录
-document.addEventListener('DOMContentLoaded', function() {
- loadHistory();
-});
-
-async function loadHistory() {
- const fileList = document.getElementById('fileList');
-
- try {
- const response = await fetch('/api/history', {
- method: 'POST'
- });
- if (!response.ok) {
- throw new Error('获取历史记录失败');
- }
-
- const data = await response.json();
-
- if (data.files && data.files.length > 0) {
- displayFileList(data.files);
- } else {
- fileList.innerHTML = `
-
-
📁
-
暂无历史记录
-
还没有上传过STP文件
-
-
- `;
- }
-
- } catch (error) {
- fileList.innerHTML = `
-
-
❌
-
加载失败
-
${error.message}
-
-
- `;
- }
-}
-
-function displayFileList(files) {
- const fileList = document.getElementById('fileList');
-
- fileList.innerHTML = files.map(file => `
-
- `).join('');
-}
-
-async function toggleFileRecords(filename) {
- const fileItem = document.getElementById(`file-${filename}`);
- const recordList = document.getElementById(`records-${filename}`);
- const expandBtn = fileItem.querySelector('.expand-btn');
-
- // 切换展开状态
- if (fileItem.classList.contains('expanded')) {
- fileItem.classList.remove('expanded');
- expandBtn.textContent = '➕';
- return;
- }
-
- // 先设置展开状态
- fileItem.classList.add('expanded');
- expandBtn.textContent = '➖';
-
- // 如果已经加载过数据,直接显示
- if (recordList.dataset.loaded === 'true') {
- return;
- }
-
- try {
- const response = await fetch(`/api/history/${encodeURIComponent(filename)}`, {
- method: 'POST'
- });
- if (!response.ok) {
- throw new Error('获取记录详情失败');
- }
-
- const records = await response.json();
-
- if (records.length > 0) {
- recordList.innerHTML = records.map(record => `
-
-
-
-
文件大小: ${formatFileSize(record.file_size)}
-
任务ID: ${record.task_id}
-
状态: ${record.status}
- ${record.completed_at ? `
完成时间: ${formatDateTime(record.completed_at)}
` : ''}
-
-
- `).join('');
- } else {
- recordList.innerHTML = `
-
- `;
- }
-
- recordList.dataset.loaded = 'true';
-
- } catch (error) {
- recordList.innerHTML = `
-
-
❌ 加载失败: ${error.message}
-
-
- `;
- }
-}
-
-async function viewRecordDetails(taskId) {
- // 直接跳转到结果页面,由后端从数据库加载完整数据
- window.location.href = `/result/${taskId}`;
-}
-
-async function reanalyzeTask(taskId) {
- try {
- const response = await fetch(`/api/reanalyze/${taskId}`, {
- method: 'POST'
- });
-
- if (response.ok) {
- alert('重新分析任务已启动,请稍后查看结果');
- // 跳转到分析状态页面
- window.location.href = `/status/${taskId}`;
- } else {
- throw new Error('重新分析失败');
- }
- } catch (error) {
- alert(`重新分析失败: ${error.message}`);
- }
-}
-
-function getStatusText(status) {
- const statusMap = {
- 'completed': '已完成',
- 'processing': '处理中',
- '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 formatDateTime(dateString) {
- if (!dateString) return 'N/A';
- // 后端已经返回格式化的时间字符串,直接返回
- return dateString;
-}
-
-// 跳转到上传页面
-async function goToUpload() {
- try {
- // 上传页面需要GET请求,直接跳转
- window.location.href = '/';
- } catch (error) {
- console.error('跳转失败:', error);
- window.location.href = '/';
- }
-}
-
-// 跳转到主页
-async function goToHome() {
- try {
- // 主页需要GET请求,直接跳转
- window.location.href = '/';
- } catch (error) {
- console.error('跳转失败:', error);
- window.location.href = '/';
- }
-}
\ No newline at end of file
diff --git a/static/script.js b/static/script.js
deleted file mode 100644
index 7a41332..0000000
--- a/static/script.js
+++ /dev/null
@@ -1,657 +0,0 @@
-// static/script.js
-let selectedFile = null;
-const uploadSection = document.getElementById('uploadSection');
-const uploadArea = document.getElementById('uploadArea');
-const fileInput = document.getElementById('fileInput');
-const uploadBtn = document.getElementById('uploadBtn');
-const loading = document.getElementById('loading');
-const resultsSection = document.getElementById('resultsSection');
-const errorMessage = document.getElementById('errorMessage');
-const taskInfo = document.getElementById('taskInfo');
-const geometryData = document.getElementById('geometryData');
-const boundingBoxData = document.getElementById('boundingBoxData');
-const topologyData = document.getElementById('topologyData');
-const featuresData = document.getElementById('featuresData');
-const recommendationsData = document.getElementById('recommendationsData');
-const metricsData = document.getElementById('metricsData');
-const analysisInfo = document.getElementById('analysisInfo');
-
-// 页面加载时初始化
-document.addEventListener('DOMContentLoaded', function() {
- console.log('页面加载完成');
- showUploadSection();
-});
-
-// 显示上传区域
-function showUploadSection() {
- uploadSection.style.display = 'block';
- resultsSection.style.display = 'none';
- resetUploadArea();
-}
-
-// 显示结果区域
-function showResultsSection() {
- uploadSection.style.display = 'none';
- resultsSection.style.display = 'block';
-}
-
-// 重置上传区域
-function resetUploadArea() {
- selectedFile = null;
- uploadArea.innerHTML = `
- 📁
- 拖放文件到此处或点击选择
- 最大文件大小: 100MB
-
-
- `;
- uploadBtn.disabled = true;
- hideError();
- loading.style.display = 'none';
-
- // 重新绑定事件
- const newFileInput = document.getElementById('fileInput');
- newFileInput.addEventListener('change', (e) => {
- if (e.target.files.length > 0) {
- handleFileSelect(e.target.files[0]);
- }
- });
-}
-
-// 拖放功能
-uploadArea.addEventListener('dragover', (e) => {
- e.preventDefault();
- uploadArea.classList.add('dragover');
-});
-
-uploadArea.addEventListener('dragleave', () => {
- uploadArea.classList.remove('dragover');
-});
-
-uploadArea.addEventListener('drop', (e) => {
- e.preventDefault();
- uploadArea.classList.remove('dragover');
- const files = e.dataTransfer.files;
- if (files.length > 0) {
- handleFileSelect(files[0]);
- }
-});
-
-// 文件选择
-fileInput.addEventListener('change', (e) => {
- if (e.target.files.length > 0) {
- handleFileSelect(e.target.files[0]);
- }
-});
-
-function handleFileSelect(file) {
- if (!file.name.toLowerCase().endsWith('.stp') && !file.name.toLowerCase().endsWith('.step')) {
- showError('请选择STP或STEP格式的文件');
- return;
- }
-
- if (file.size > 100 * 1024 * 1024) {
- showError('文件大小不能超过100MB');
- return;
- }
-
- selectedFile = file;
- uploadArea.innerHTML = `
- ✅
- 已选择文件
- ${file.name}
- 大小: ${(file.size / 1024 / 1024).toFixed(2)} MB
- `;
- uploadBtn.disabled = false;
- hideError();
-}
-
-async function uploadFile() {
- if (!selectedFile) return;
-
- loading.style.display = 'block';
- uploadBtn.disabled = true;
- hideError();
-
- const formData = new FormData();
- formData.append('file', selectedFile);
-
- try {
- const response = await fetch('/upload', {
- method: 'POST',
- body: formData
- });
-
- if (!response.ok) {
- throw new Error(`上传失败: ${response.status} ${response.statusText}`);
- }
-
- const result = await response.json();
- console.log('上传结果:', result);
-
- // 开始轮询任务状态
- pollTaskStatus(result.task_id);
-
- } catch (error) {
- showError('上传失败: ' + error.message);
- loading.style.display = 'none';
- uploadBtn.disabled = false;
- }
-}
-
-async function pollTaskStatus(taskId) {
- try {
- const response = await fetch(`/status/${taskId}`, {
- method: 'POST'
- });
- const task = await response.json();
-
- console.log('任务状态:', task.status);
- console.log('完整任务数据:', task);
-
- updateTaskInfo(task);
-
- if (task.status === 'completed') {
- loading.style.display = 'none';
- // 跳转到统一的分析结果页面
- window.location.href = `/result/${task.task_id}`;
- } else if (task.status === 'failed') {
- loading.style.display = 'none';
- showError('分析失败: ' + (task.error || '未知错误'));
- uploadBtn.disabled = false;
- } else {
- setTimeout(() => pollTaskStatus(taskId), 1000);
- }
-
- } catch (error) {
- console.error('轮询错误:', error);
- loading.style.display = 'none';
- showError('查询状态失败: ' + error.message);
- uploadBtn.disabled = false;
- }
-}
-
-function updateTaskInfo(task) {
- taskInfo.innerHTML = `
-
-
📋 任务ID
-
${task.task_id || 'N/A'}
-
-
-
🔄 状态
-
- ${getStatusText(task.status)}
-
-
-
-
📁 文件名
-
${task.filename || 'N/A'}
-
-
-
📏 文件大小
-
${task.file_size ? formatFileSize(task.file_size) : 'N/A'}
-
- `;
-}
-
-function displayAllResults(task) {
- console.log('显示所有结果:', task);
-
- displayGeometryData(task);
- displayBoundingBoxData(task);
- displayTopologyData(task);
-
- // 添加模具型腔数据显示
- if (task.cavity_data) {
- displayCavityData(task.cavity_data);
- }
- if (task.key_info) {
- displayKeyInfo(task.key_info);
- }
-
- displayFeaturesData(task);
- displayRecommendationsData(task);
- displayMetricsData(task);
- displayAnalysisInfo(task);
-}
-
-function displayGeometryData(task) {
- if (task.geometry_data) {
- const geo = task.geometry_data;
- geometryData.innerHTML = `
-
-
📦 体积
-
- ${geo.volume ? formatNumber(geo.volume) : 'N/A'}
- mm³
-
-
-
-
📐 表面积
-
- ${geo.surface_area ? formatNumber(geo.surface_area) : 'N/A'}
- mm²
-
-
-
-
📏 体积表面积比
-
- ${geo.volume && geo.surface_area ? (geo.volume / geo.surface_area).toFixed(4) : 'N/A'}
-
-
- `;
- } else {
- geometryData.innerHTML = '';
- }
-}
-
-function displayBoundingBoxData(task) {
- if (task.geometry_data && task.geometry_data.bounding_box) {
- const bbox = task.geometry_data.bounding_box;
- boundingBoxData.innerHTML = `
-
-
📍 最小坐标
-
- X: ${bbox.min[0].toFixed(2)}
- Y: ${bbox.min[1].toFixed(2)}
- Z: ${bbox.min[2].toFixed(2)}
-
-
-
-
📍 最大坐标
-
- X: ${bbox.max[0].toFixed(2)}
- Y: ${bbox.max[1].toFixed(2)}
- Z: ${bbox.max[2].toFixed(2)}
-
-
-
-
📏 尺寸
-
- ${bbox.dimensions[0].toFixed(2)} × ${bbox.dimensions[1].toFixed(2)} × ${bbox.dimensions[2].toFixed(2)}
- mm
-
-
- `;
- } else {
- boundingBoxData.innerHTML = '';
- }
-}
-
-function displayTopologyData(task) {
- if (task.geometry_data && task.geometry_data.topology) {
- const topo = task.geometry_data.topology;
- topologyData.innerHTML = `
-
-
🔺 面数
-
${topo.faces || 0}
-
-
-
📏 边数
-
${topo.edges || 0}
-
-
-
📍 顶点数
-
${topo.vertices || 0}
-
-
-
📊 拓扑复杂度
-
${calculateTopologyComplexity(topo)}
-
- `;
- } else {
- topologyData.innerHTML = '';
- }
-}
-
-function displayFeaturesData(task) {
- if (task.analysis_result && task.analysis_result.detected_features) {
- const features = task.analysis_result.detected_features;
-
- if (features.length > 0) {
- featuresData.innerHTML = features.map(feature => `
-
-
-
-
📍 位置
-
${feature.location.map(v => v.toFixed(2)).join(', ')}
-
-
-
📏 尺寸
-
${feature.dimensions.map(v => v.toFixed(2)).join(' × ')} mm
-
- ${feature.recommendations && feature.recommendations.length > 0 ? `
-
-
💡 建议
-
- ${feature.recommendations.map(rec => `- ${rec}
`).join('')}
-
-
- ` : ''}
-
- `).join('');
- } else {
- featuresData.innerHTML = '';
- }
- } else {
- featuresData.innerHTML = '';
- }
-}
-
-function displayRecommendationsData(task) {
- if (task.analysis_result && task.analysis_result.design_recommendations) {
- const recommendations = task.analysis_result.design_recommendations;
-
- if (recommendations.length > 0) {
- recommendationsData.innerHTML = recommendations.map(rec => `
-
-
-
-
📝 描述
-
${rec.description}
-
-
- ${Object.keys(rec.parameters).length > 0 ? `
-
-
⚙️ 参数
-
${formatParameters(rec.parameters)}
-
- ` : ''}
-
- `).join('');
- } else {
- recommendationsData.innerHTML = '';
- }
- } else {
- recommendationsData.innerHTML = '';
- }
-}
-
-function displayMetricsData(task) {
- if (task.analysis_result && task.analysis_result.quality_metrics) {
- const metrics = task.analysis_result.quality_metrics;
- metricsData.innerHTML = `
-
-
体积利用率
-
- ${(metrics.volume_utilization * 100).toFixed(1)}%
-
-
${getVolumeUtilizationText(metrics.volume_utilization)}
-
-
-
拓扑复杂度
-
- ${metrics.topology_complexity.toFixed(2)}
-
-
${getComplexityText(metrics.topology_complexity)}
-
-
-
壁厚均匀性
-
- ${(metrics.wall_uniformity * 100).toFixed(1)}%
-
-
${getUniformityText(metrics.wall_uniformity)}
-
- `;
- } else {
- metricsData.innerHTML = '';
- }
-}
-
-function displayAnalysisInfo(task) {
- let infoHTML = '';
-
- if (task.geometry_data) {
- const geo = task.geometry_data;
- infoHTML += `
-
-
🔧 分析方法
-
${geo.analysis_method || '未知'}
-
- `;
- }
-
- if (task.analysis_result) {
- const analysis = task.analysis_result;
- infoHTML += `
-
-
✅ 分析状态
-
${task.status === 'completed' ? '分析完成' : '分析中'}
-
-
-
📋 分析摘要
-
${analysis.analysis_summary || '无摘要'}
-
- `;
- }
-
- infoHTML += `
-
-
📅 处理时间
-
${task.completed_at ? new Date(task.completed_at).toLocaleString() : new Date().toLocaleString()}
-
- `;
-
- analysisInfo.innerHTML = infoHTML;
-}
-
-// 工具函数
-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);
- }
-}
-
-function calculateTopologyComplexity(topo) {
- const totalElements = (topo.faces || 0) + (topo.edges || 0) + (topo.vertices || 0);
- if (totalElements < 100) return '简单';
- if (totalElements < 1000) return '中等';
- return '复杂';
-}
-
-function getFeatureTypeText(type) {
- const typeMap = {
- 'thin_wall': '薄壁区域',
- 'thick_wall': '厚壁区域',
- 'rib_structure': '加强筋结构',
- 'boss_feature': 'BOSS柱',
- 'draft_angle': '拔模角度',
- 'cooling_system': '冷却系统'
- };
- return typeMap[type] || type;
-}
-
-function getRecommendationTypeText(type) {
- const typeMap = {
- 'wall_thickness': '壁厚优化',
- 'draft_angle': '拔模角度',
- 'rib_design': '加强筋设计',
- 'boss_design': 'BOSS柱设计',
- 'cooling_system': '冷却系统'
- };
- return typeMap[type] || type;
-}
-
-function getPriorityText(priority) {
- const priorityMap = {
- 'high': '高优先级',
- 'medium': '中优先级',
- 'low': '低优先级'
- };
- return priorityMap[priority] || priority;
-}
-
-function formatParameters(parameters) {
- const parameterMap = {
- 'min_angle': '最小角度',
- 'preferred_angle': '推荐角度'
- };
-
- return Object.entries(parameters).map(([key, value]) => {
- const displayKey = parameterMap[key] || key;
- if (typeof value === 'number') {
- return `${displayKey}: ${value.toFixed(2)}`;
- }
- return `${displayKey}: ${value}`;
- }).join('; ');
-}
-
-function getMetricClass(value, goodThreshold, excellentThreshold, reverse = false) {
- if (reverse) {
- if (value <= goodThreshold) return 'metric-good';
- if (value <= excellentThreshold) return 'metric-warning';
- return 'metric-poor';
- } else {
- if (value >= excellentThreshold) return 'metric-good';
- if (value >= goodThreshold) return 'metric-warning';
- return 'metric-poor';
- }
-}
-
-function getVolumeUtilizationText(value) {
- if (value >= 0.6) return '优秀';
- if (value >= 0.3) return '良好';
- return '待优化';
-}
-
-function getComplexityText(value) {
- if (value <= 0.3) return '简单';
- if (value <= 0.7) return '中等';
- return '复杂';
-}
-
-function getUniformityText(value) {
- if (value >= 0.8) return '均匀';
- if (value >= 0.6) return '一般';
- return '不均匀';
-}
-
-function showError(message) {
- errorMessage.textContent = message;
- errorMessage.style.display = 'block';
-}
-
-function hideError() {
- errorMessage.style.display = 'none';
-}
-
-// 跳转到历史页面
-function goToHistory() {
- // 创建表单进行POST请求跳转,更安全
- const form = document.createElement('form');
- form.method = 'POST';
- form.action = '/history';
- document.body.appendChild(form);
- form.submit();
-}
-
-// 添加显示函数
-function displayCavityData(cavityData) {
- const cavityDiv = document.getElementById('cavityData');
- cavityDiv.innerHTML = `
- ${JSON.stringify(cavityData, null, 2)}
- `;
-}
-
-function displayKeyInfo(keyInfo) {
- const keyInfoDiv = document.getElementById('keyInfoData');
-
- if (!keyInfo) {
- keyInfoDiv.innerHTML = '';
- return;
- }
-
- // 从正确的数据结构中提取数据
- 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 || {};
-
- keyInfoDiv.innerHTML = `
-
-
-
收缩率
-
${metadata.shrinkage_rate !== undefined ? metadata.shrinkage_rate : 'N/A'}
-
-
-
拔模角
-
${metadata.draft_angle !== undefined ? metadata.draft_angle + '°' : 'N/A'}
-
-
-
分型线长度
-
${manufacturingInfo.parting_line_length || 'N/A'}
-
-
-
-
-
产品体积
-
${geoChars.product_volume || 'N/A'}
-
-
-
产品重量
-
${geoChars.product_weight || 'N/A'}
-
-
-
壁厚范围
-
${geoChars.wall_thickness_range || 'N/A'}
-
-
-
-
-
型腔材料
-
${manufacturingInfo.mold_material || 'N/A'}
-
-
-
硬度
-
${manufacturingInfo.mold_hardness || 'N/A'}
-
-
-
表面光洁度
-
${manufacturingInfo.surface_finish || 'N/A'}
-
-
-
预估周期
-
${manufacturingInfo.estimated_cycle_time || 'N/A'}
-
- `;
-}
diff --git a/static/style.css b/static/style.css
index 20ce51c..010855a 100644
--- a/static/style.css
+++ b/static/style.css
@@ -1,102 +1,255 @@
-/* static/style.css */
+/**
+ * STP模具几何分析中心 - 统一设计系统
+ * 优化版本:现代化UI设计,响应式布局
+ */
+
+/* 设计系统变量 */
+:root {
+ /* 颜色系统 */
+ --primary-color: #2563eb;
+ --primary-hover: #1d4ed8;
+ --secondary-color: #64748b;
+ --success-color: #10b981;
+ --warning-color: #f59e0b;
+ --error-color: #ef4444;
+ --info-color: #3b82f6;
+
+ /* 中性色 */
+ --gray-50: #f8fafc;
+ --gray-100: #f1f5f9;
+ --gray-200: #e2e8f0;
+ --gray-300: #cbd5e1;
+ --gray-400: #94a3b8;
+ --gray-500: #64748b;
+ --gray-600: #475569;
+ --gray-700: #334155;
+ --gray-800: #1e293b;
+ --gray-900: #0f172a;
+
+ /* 间距系统 */
+ --space-xs: 0.25rem;
+ --space-sm: 0.5rem;
+ --space-md: 1rem;
+ --space-lg: 1.5rem;
+ --space-xl: 2rem;
+ --space-2xl: 3rem;
+
+ /* 字体系统 */
+ --font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+ --font-size-xs: 0.75rem;
+ --font-size-sm: 0.875rem;
+ --font-size-base: 1rem;
+ --font-size-lg: 1.125rem;
+ --font-size-xl: 1.25rem;
+ --font-size-2xl: 1.5rem;
+ --font-size-3xl: 1.875rem;
+
+ /* 阴影系统 */
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
+
+ /* 圆角系统 */
+ --radius-sm: 0.375rem;
+ --radius-md: 0.5rem;
+ --radius-lg: 0.75rem;
+ --radius-xl: 1rem;
+ --radius-full: 9999px;
+}
+
+/* 基础重置 */
* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ scroll-behavior: smooth;
}
body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background: linear-gradient(135deg, #1f2933 0%, #111827 100%);
- min-height: 100vh;
- padding: 16px;
- color: #111827;
+ font-family: var(--font-family);
+ background: linear-gradient(135deg, var(--gray-900) 0%, var(--gray-800) 100%);
+ min-height: 100vh;
+ padding: var(--space-md);
+ color: var(--gray-800);
+ line-height: 1.6;
}
-/* 旧版容器保留,兼容可能的使用 */
-.container {
- max-width: 1200px;
- margin: 0 auto;
- background: white;
- border-radius: 15px;
- box-shadow: 0 20px 40px rgba(0,0,0,0.1);
- overflow: hidden;
+/* 应用外壳 */
+.app-shell {
+ max-width: 1400px;
+ margin: 0 auto;
+ min-height: calc(100vh - 2 * var(--space-md));
+ display: flex;
+ flex-direction: column;
}
-.header {
- background: linear-gradient(135deg, #2c3e50, #34495e);
- color: white;
- padding: 30px;
- text-align: center;
+/* 通知系统 */
+.notification-container {
+ position: fixed;
+ top: var(--space-md);
+ right: var(--space-md);
+ z-index: 1000;
+ max-width: 400px;
}
-.header h1 {
- font-size: 2.5em;
- margin-bottom: 10px;
+.notification {
+ background: white;
+ border-radius: var(--radius-md);
+ padding: var(--space-md);
+ margin-bottom: var(--space-sm);
+ box-shadow: var(--shadow-lg);
+ border-left: 4px solid var(--info-color);
+ animation: slideIn 0.3s ease-out;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
}
-.header p {
- opacity: 0.9;
- font-size: 1.1em;
+.notification-success {
+ border-left-color: var(--success-color);
}
-.upload-section {
- padding: 40px;
- text-align: center;
+.notification-error {
+ border-left-color: var(--error-color);
}
-.upload-area {
- border: 3px dashed #3498db;
- border-radius: 10px;
- padding: 60px 40px;
- margin: 20px 0;
- background: #f8f9fa;
- transition: all 0.3s ease;
- cursor: pointer;
+.notification-warning {
+ border-left-color: var(--warning-color);
}
-.upload-area:hover {
- border-color: #2980b9;
- background: #e8f4fc;
+.notification-message {
+ flex: 1;
+ color: var(--gray-700);
}
-.upload-area.dragover {
- border-color: #27ae60;
- background: #d5f4e6;
+.notification-close {
+ background: none;
+ border: none;
+ font-size: var(--font-size-lg);
+ color: var(--gray-400);
+ cursor: pointer;
+ padding: var(--space-xs);
+ margin-left: var(--space-sm);
+ border-radius: var(--radius-sm);
+ transition: all 0.2s;
}
-.upload-icon {
- font-size: 4em;
- color: #3498db;
- margin-bottom: 20px;
+.notification-close:hover {
+ background: var(--gray-100);
+ color: var(--gray-600);
}
-.file-input {
- display: none;
+@keyframes slideIn {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
}
-.upload-btn {
- background: linear-gradient(135deg, #3498db, #2980b9);
- color: white;
- border: none;
- padding: 15px 40px;
- font-size: 1.1em;
- border-radius: 50px;
- cursor: pointer;
- transition: all 0.3s ease;
- margin: 10px;
+/* 应用头部 */
+.app-header {
+ background: white;
+ border-radius: var(--radius-xl) var(--radius-xl) 0 0;
+ padding: var(--space-2xl);
+ box-shadow: var(--shadow-md);
+ margin-bottom: var(--space-lg);
}
-.upload-btn:hover {
- transform: translateY(-2px);
- box-shadow: 0 10px 20px rgba(52, 152, 219, 0.3);
+.app-title {
+ display: flex;
+ align-items: center;
+ gap: var(--space-lg);
+ margin-bottom: var(--space-lg);
}
-.upload-btn:disabled {
- background: #bdc3c7;
- cursor: not-allowed;
- transform: none;
- box-shadow: none;
+.logo {
+ font-size: var(--font-size-3xl);
+ background: linear-gradient(135deg, var(--primary-color), var(--info-color));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.app-title h1 {
+ font-size: var(--font-size-3xl);
+ color: var(--gray-900);
+ margin-bottom: var(--space-xs);
+}
+
+.app-subtitle {
+ color: var(--gray-600);
+ font-size: var(--font-size-base);
+}
+
+.app-nav {
+ display: flex;
+ gap: var(--space-md);
+ border-top: 1px solid var(--gray-200);
+ padding-top: var(--space-lg);
+}
+
+.nav-link {
+ padding: var(--space-sm) var(--space-md);
+ text-decoration: none;
+ color: var(--gray-600);
+ border-radius: var(--radius-md);
+ transition: all 0.2s;
+ font-weight: 500;
+}
+
+.nav-link:hover {
+ color: var(--primary-color);
+ background: var(--gray-50);
+}
+
+.nav-link.active {
+ color: var(--primary-color);
+ background: var(--gray-50);
+ box-shadow: var(--shadow-sm);
+}
+
+/* 主要内容区域 */
+.app-main {
+ flex: 1;
+ background: white;
+ border-radius: 0 0 var(--radius-xl) var(--radius-xl);
+ padding: var(--space-2xl);
+ box-shadow: var(--shadow-md);
+}
+
+/* 应用底部 */
+.app-footer {
+ background: var(--gray-800);
+ color: white;
+ padding: var(--space-lg);
+ border-radius: var(--radius-md);
+ margin-top: var(--space-lg);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: var(--font-size-sm);
+}
+
+.footer-left, .footer-right {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+}
+
+.status-success {
+ color: var(--success-color);
+}
+
+.status-error {
+ color: var(--error-color);
}
.results-section {
diff --git a/static/vue-app.js b/static/vue-app.js
index b1eedf1..7dbe93a 100644
--- a/static/vue-app.js
+++ b/static/vue-app.js
@@ -1,9 +1,18 @@
-// static/vue-app.js
-// 使用 CDN 引入的 Vue3 + Vue Router,构建单页应用
+/**
+ * STP模具几何分析中心 - Vue3单页应用
+ * 优化版本:统一前端实现,增强用户体验
+ */
-const { createApp, ref, computed, onMounted } = Vue;
+const { createApp, ref, computed, onMounted, reactive } = Vue;
const { createRouter, createWebHistory, useRoute, useRouter } = VueRouter;
+// 全局状态管理
+const appState = reactive({
+ health: null,
+ loading: false,
+ notifications: []
+});
+
// 通用工具函数
function formatFileSize(bytes) {
if (!bytes || bytes === 0) return "0 Bytes";
@@ -23,7 +32,7 @@ function formatNumber(num) {
function formatDateTime(dateString) {
if (!dateString) return "N/A";
try {
- return new Date(dateString).toLocaleString();
+ return new Date(dateString).toLocaleString('zh-CN');
} catch {
return dateString;
}
@@ -39,12 +48,49 @@ function statusText(status) {
return map[status] || status || "未知";
}
+// 状态样式映射
+function getStatusClass(status) {
+ const classMap = {
+ pending: "status-pending",
+ processing: "status-processing",
+ completed: "status-completed",
+ failed: "status-failed"
+ };
+ return classMap[status] || "status-pending";
+}
+
+// 添加通知
+function addNotification(message, type = 'info') {
+ const notification = {
+ id: Date.now(),
+ message,
+ type,
+ timestamp: new Date()
+ };
+ appState.notifications.push(notification);
+
+ // 自动移除通知
+ setTimeout(() => {
+ const index = appState.notifications.findIndex(n => n.id === notification.id);
+ if (index > -1) {
+ appState.notifications.splice(index, 1);
+ }
+ }, 5000);
+}
+
+// 错误处理
+function handleApiError(error, context = '') {
+ console.error(`API错误 [${context}]:`, error);
+ const message = error.message || '请求失败,请稍后重试';
+ addNotification(message, 'error');
+ return message;
+}
+
// 顶部导航 + 布局
const App = {
setup() {
const route = useRoute();
const router = useRouter();
- const health = ref(null);
const isActive = (pathPrefix) =>
computed(() => route.path === pathPrefix || route.path.startsWith(pathPrefix));
@@ -53,30 +99,66 @@ const App = {
try {
const res = await fetch("/health", { method: "POST" });
if (res.ok) {
- health.value = await res.json();
+ appState.health = await res.json();
}
- } catch {
- health.value = null;
+ } catch (error) {
+ appState.health = null;
+ handleApiError(error, '健康检查');
}
};
- onMounted(loadHealth);
+ const dismissNotification = (id) => {
+ const index = appState.notifications.findIndex(n => n.id === id);
+ if (index > -1) {
+ appState.notifications.splice(index, 1);
+ }
+ };
- return { route, router, health, isActive };
+ onMounted(() => {
+ loadHealth();
+ // 定时检查健康状态(每30秒)
+ setInterval(loadHealth, 30000);
+ });
+
+ return {
+ route,
+ router,
+ appState,
+ isActive,
+ dismissNotification,
+ getStatusClass,
+ statusText
+ };
},
template: `
+
+
+
+ {{ notification.message }}
+
+
+
+
@@ -86,11 +168,15 @@ const App = {
@@ -102,53 +188,77 @@ const DashboardView = {
setup() {
const router = useRouter();
- const selectedFile = ref(null);
- const uploading = ref(false);
- const error = ref("");
- const currentTask = ref(null);
- const polling = ref(false);
- const historySummary = ref(null);
+ const state = reactive({
+ selectedFile: null,
+ uploading: false,
+ error: "",
+ currentTask: null,
+ polling: false,
+ historySummary: null,
+ dragOver: false
+ });
- const totalFiles = computed(() => historySummary.value?.total_files || 0);
+ const totalFiles = computed(() => state.historySummary?.total_files || 0);
const totalRecords = computed(() =>
- (historySummary.value?.files || []).reduce(
+ (state.historySummary?.files || []).reduce(
(sum, f) => sum + (f.record_count || 0),
0
)
);
const latestFile = computed(() =>
- (historySummary.value?.files || [])[0] || null
+ (state.historySummary?.files || [])[0] || null
);
const handleFileChange = (event) => {
const file = event.target.files[0];
if (!file) return;
+ validateAndSelectFile(file);
+ };
- if (
- !file.name.toLowerCase().endsWith(".stp") &&
- !file.name.toLowerCase().endsWith(".step")
- ) {
- error.value = "请选择 STP 或 STEP 格式文件";
- selectedFile.value = null;
+ const validateAndSelectFile = (file) => {
+ if (!file.name.toLowerCase().endsWith(".stp") &&
+ !file.name.toLowerCase().endsWith(".step")) {
+ state.error = "请选择 STP 或 STEP 格式文件";
+ state.selectedFile = null;
return;
}
if (file.size > 100 * 1024 * 1024) {
- error.value = "文件大小不能超过 100MB";
- selectedFile.value = null;
+ state.error = "文件大小不能超过 100MB";
+ state.selectedFile = null;
return;
}
- error.value = "";
- selectedFile.value = file;
+ state.error = "";
+ state.selectedFile = file;
+ addNotification(`已选择文件: ${file.name}`, 'success');
+ };
+
+ const handleDragOver = (event) => {
+ event.preventDefault();
+ state.dragOver = true;
+ };
+
+ const handleDragLeave = (event) => {
+ event.preventDefault();
+ state.dragOver = false;
+ };
+
+ const handleDrop = (event) => {
+ event.preventDefault();
+ state.dragOver = false;
+ const files = event.dataTransfer.files;
+ if (files.length > 0) {
+ validateAndSelectFile(files[0]);
+ }
};
const uploadFile = async () => {
- if (!selectedFile.value) return;
- uploading.value = true;
- error.value = "";
+ if (!state.selectedFile) return;
+ state.uploading = true;
+ state.error = "";
const formData = new FormData();
- formData.append("file", selectedFile.value);
+ formData.append("file", state.selectedFile);
try {
const res = await fetch("/upload", {
@@ -159,22 +269,24 @@ const DashboardView = {
throw new Error(`上传失败: ${res.status} ${res.statusText}`);
}
const data = await res.json();
- currentTask.value = {
+ state.currentTask = {
task_id: data.task_id,
status: "processing",
filename: data.file_info?.filename,
file_size: data.file_info?.size,
};
+ addNotification(`文件上传成功,开始分析...`, 'success');
startPolling(data.task_id);
} catch (e) {
- error.value = e.message || "上传失败";
+ const errorMsg = handleApiError(e, '文件上传');
+ state.error = errorMsg;
} finally {
- uploading.value = false;
+ state.uploading = false;
}
};
const startPolling = async (taskId) => {
- polling.value = true;
+ state.polling = true;
const poll = async () => {
try {
const res = await fetch(`/status/${taskId}`, { method: "POST" });
@@ -182,21 +294,26 @@ const DashboardView = {
throw new Error("查询任务状态失败");
}
const task = await res.json();
- currentTask.value = task;
+ state.currentTask = task;
if (task.status === "completed") {
- polling.value = false;
- // 跳转到结果详情页
- router.push(`/result/${task.task_id}`);
+ state.polling = false;
+ addNotification(`分析完成,正在跳转到结果页面...`, 'success');
+ setTimeout(() => {
+ router.push(`/result/${task.task_id}`);
+ }, 1000);
} else if (task.status === "failed") {
- polling.value = false;
- error.value = `分析失败: ${task.error || "未知错误"}`;
+ state.polling = false;
+ const errorMsg = task.error || "未知错误";
+ state.error = `分析失败: ${errorMsg}`;
+ addNotification(`分析失败: ${errorMsg}`, 'error');
} else {
setTimeout(poll, 1000);
}
} catch (e) {
- polling.value = false;
- error.value = e.message || "轮询失败";
+ state.polling = false;
+ const errorMsg = handleApiError(e, '任务轮询');
+ state.error = errorMsg;
}
};
poll();
@@ -206,32 +323,38 @@ const DashboardView = {
try {
const res = await fetch("/api/history", { method: "POST" });
if (res.ok) {
- historySummary.value = await res.json();
+ state.historySummary = await res.json();
}
- } catch {
- historySummary.value = null;
+ } catch (e) {
+ state.historySummary = null;
+ handleApiError(e, '加载历史记录');
}
};
+ const clearFile = () => {
+ state.selectedFile = null;
+ state.error = "";
+ };
+
onMounted(() => {
loadHistorySummary();
});
return {
- selectedFile,
- uploading,
- error,
- currentTask,
- polling,
- historySummary,
+ state,
totalFiles,
totalRecords,
latestFile,
handleFileChange,
+ handleDragOver,
+ handleDragLeave,
+ handleDrop,
uploadFile,
+ clearFile,
formatFileSize,
statusText,
formatDateTime,
+ getStatusClass
};
},
template: `
diff --git a/templates/history.html b/templates/history.html
index 118e2e6..6e99ec2 100644
--- a/templates/history.html
+++ b/templates/history.html
@@ -1,11 +1,13 @@
-
历史记录 - STP 模具几何分析中心
+
+
+
diff --git a/templates/index.html b/templates/index.html
index 6ce7ce0..51c224a 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,11 +1,13 @@
-
- STP 模具几何分析中心
+ STP 模具几何分析中心 - 仪表盘
+
+
+
@@ -14,5 +16,13 @@
+
+
+
\ No newline at end of file
diff --git a/templates/result.html b/templates/result.html
index 4379e35..4097626 100644
--- a/templates/result.html
+++ b/templates/result.html
@@ -1,11 +1,13 @@
-
分析结果 - STP 模具几何分析中心
+
+
+