From 64acdb0e8c2b9386303a9be640bf359bd2b0cef4 Mon Sep 17 00:00:00 2001
From: cjw <792430652@qq.com>
Date: Tue, 17 Feb 2026 00:12:36 +0800
Subject: [PATCH] deving
---
src/api/routes.py | 7 +
static/vue-app.js | 745 +++++++++++++++++++++++++++++++++++++++++
templates/history.html | 201 +----------
templates/index.html | 124 +------
templates/result.html | 431 +-----------------------
5 files changed, 770 insertions(+), 738 deletions(-)
create mode 100644 static/vue-app.js
diff --git a/src/api/routes.py b/src/api/routes.py
index 625cd50..98a0d68 100644
--- a/src/api/routes.py
+++ b/src/api/routes.py
@@ -405,6 +405,13 @@ async def process_file_core(
mesh_json=mesh_json,
quality="medium",
)
+ # 将简要网格摘要写入内存任务,便于前端展示汇总信息
+ tasks[task_id]["mesh_summary"] = {
+ "vertex_count": len(vertices),
+ "face_count": len(faces),
+ "point_count": pointcloud.get("count"),
+ "quality": "medium",
+ }
except Exception as mesh_err:
# 网格失败不影响整体流程,只记录日志
logger.warning(f"网格生成或保存失败,不影响主流程: {mesh_err}")
diff --git a/static/vue-app.js b/static/vue-app.js
new file mode 100644
index 0000000..b1eedf1
--- /dev/null
+++ b/static/vue-app.js
@@ -0,0 +1,745 @@
+// static/vue-app.js
+// 使用 CDN 引入的 Vue3 + Vue Router,构建单页应用
+
+const { createApp, ref, computed, onMounted } = Vue;
+const { createRouter, createWebHistory, useRoute, useRouter } = VueRouter;
+
+// 通用工具函数
+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 === null || num === undefined) return "N/A";
+ if (num >= 1_000_000) return (num / 1_000_000).toFixed(2) + "M";
+ if (num >= 1_000) return (num / 1_000).toFixed(2) + "K";
+ return num.toFixed ? num.toFixed(2) : String(num);
+}
+
+function formatDateTime(dateString) {
+ if (!dateString) return "N/A";
+ try {
+ return new Date(dateString).toLocaleString();
+ } catch {
+ return dateString;
+ }
+}
+
+function statusText(status) {
+ const map = {
+ pending: "排队中",
+ processing: "处理中",
+ completed: "已完成",
+ failed: "失败",
+ };
+ return map[status] || status || "未知";
+}
+
+// 顶部导航 + 布局
+const App = {
+ setup() {
+ const route = useRoute();
+ const router = useRouter();
+ const health = ref(null);
+
+ const isActive = (pathPrefix) =>
+ computed(() => route.path === pathPrefix || route.path.startsWith(pathPrefix));
+
+ const loadHealth = async () => {
+ try {
+ const res = await fetch("/health", { method: "POST" });
+ if (res.ok) {
+ health.value = await res.json();
+ }
+ } catch {
+ health.value = null;
+ }
+ };
+
+ onMounted(loadHealth);
+
+ return { route, router, health, isActive };
+ },
+ template: `
+
+
+
+
+
+
+
+
+
+ `,
+};
+
+// 仪表盘视图:上传 + 总览
+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 totalFiles = computed(() => historySummary.value?.total_files || 0);
+ const totalRecords = computed(() =>
+ (historySummary.value?.files || []).reduce(
+ (sum, f) => sum + (f.record_count || 0),
+ 0
+ )
+ );
+ const latestFile = computed(() =>
+ (historySummary.value?.files || [])[0] || null
+ );
+
+ const handleFileChange = (event) => {
+ const file = event.target.files[0];
+ if (!file) return;
+
+ if (
+ !file.name.toLowerCase().endsWith(".stp") &&
+ !file.name.toLowerCase().endsWith(".step")
+ ) {
+ error.value = "请选择 STP 或 STEP 格式文件";
+ selectedFile.value = null;
+ return;
+ }
+ if (file.size > 100 * 1024 * 1024) {
+ error.value = "文件大小不能超过 100MB";
+ selectedFile.value = null;
+ return;
+ }
+
+ error.value = "";
+ selectedFile.value = file;
+ };
+
+ const uploadFile = async () => {
+ if (!selectedFile.value) return;
+ uploading.value = true;
+ error.value = "";
+
+ const formData = new FormData();
+ formData.append("file", selectedFile.value);
+
+ try {
+ const res = await fetch("/upload", {
+ method: "POST",
+ body: formData,
+ });
+ if (!res.ok) {
+ throw new Error(`上传失败: ${res.status} ${res.statusText}`);
+ }
+ const data = await res.json();
+ currentTask.value = {
+ task_id: data.task_id,
+ status: "processing",
+ filename: data.file_info?.filename,
+ file_size: data.file_info?.size,
+ };
+ startPolling(data.task_id);
+ } catch (e) {
+ error.value = e.message || "上传失败";
+ } finally {
+ uploading.value = false;
+ }
+ };
+
+ const startPolling = async (taskId) => {
+ polling.value = true;
+ const poll = async () => {
+ try {
+ const res = await fetch(`/status/${taskId}`, { method: "POST" });
+ if (!res.ok) {
+ throw new Error("查询任务状态失败");
+ }
+ const task = await res.json();
+ currentTask.value = task;
+
+ if (task.status === "completed") {
+ polling.value = false;
+ // 跳转到结果详情页
+ router.push(`/result/${task.task_id}`);
+ } else if (task.status === "failed") {
+ polling.value = false;
+ error.value = `分析失败: ${task.error || "未知错误"}`;
+ } else {
+ setTimeout(poll, 1000);
+ }
+ } catch (e) {
+ polling.value = false;
+ error.value = e.message || "轮询失败";
+ }
+ };
+ poll();
+ };
+
+ const loadHistorySummary = async () => {
+ try {
+ const res = await fetch("/api/history", { method: "POST" });
+ if (res.ok) {
+ historySummary.value = await res.json();
+ }
+ } catch {
+ historySummary.value = null;
+ }
+ };
+
+ onMounted(() => {
+ loadHistorySummary();
+ });
+
+ return {
+ selectedFile,
+ uploading,
+ error,
+ currentTask,
+ polling,
+ historySummary,
+ totalFiles,
+ totalRecords,
+ latestFile,
+ handleFileChange,
+ uploadFile,
+ formatFileSize,
+ statusText,
+ formatDateTime,
+ };
+ },
+ template: `
+
+
+
+
📁 上传 STP / STEP 文件
+
上传后系统会自动解析几何、生成模具型腔并计算关键工艺参数。
+
+
+
+
+
+
+
{{ error }}
+
+
+
+
当前任务
+
+ 任务 ID:{{ currentTask.task_id }}
+
+
+ 文件名:{{ currentTask.filename || 'N/A' }}
+
+
+ 文件大小:{{ currentTask.file_size ? formatFileSize(currentTask.file_size) : 'N/A' }}
+
+
+ 状态:
+
+ {{ statusText(currentTask.status) }}
+
+
+
正在后台分析,请稍候,完成后会跳转到结果页。
+
+
+
+
+
📊 项目总览
+
+
+
已分析文件数
+
{{ totalFiles }}
+
+
+
总处理记录数
+
{{ totalRecords }}
+
+
+
最近上传文件
+
+
{{ latestFile.filename }}
+
+ 最近上传:{{ formatDateTime(latestFile.last_upload) }}
+ 历史记录:{{ latestFile.record_count }} 条
+
+
+
+
+ 还没有历史记录,先上传一个 STP 文件试试吧。
+
+
+
+
查看详细历史记录 →
+
+
+
+ `,
+};
+
+// 历史记录视图
+const HistoryView = {
+ setup() {
+ const router = useRouter();
+ const files = ref([]);
+ const loading = ref(true);
+ const error = ref("");
+ const expanded = ref({});
+
+ const loadHistory = async () => {
+ loading.value = true;
+ error.value = "";
+ try {
+ const res = await fetch("/api/history", { method: "POST" });
+ if (!res.ok) throw new Error("获取历史记录失败");
+ const data = await res.json();
+ files.value = data.files || [];
+ } catch (e) {
+ error.value = e.message || "加载失败";
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const toggleExpand = async (filename) => {
+ expanded.value[filename] = !expanded.value[filename];
+ // 首次展开时加载该文件的所有记录
+ if (expanded.value[filename]) {
+ const file = files.value.find((f) => f.filename === filename);
+ if (!file.records) {
+ try {
+ const res = await fetch(
+ `/api/history/${encodeURIComponent(filename)}`,
+ { method: "POST" }
+ );
+ if (!res.ok) throw new Error("加载记录失败");
+ file.records = await res.json();
+ } catch (e) {
+ error.value = e.message || "加载记录失败";
+ }
+ }
+ }
+ };
+
+ const openResult = (taskId) => {
+ router.push(`/result/${taskId}`);
+ };
+
+ onMounted(loadHistory);
+
+ return {
+ files,
+ loading,
+ error,
+ expanded,
+ toggleExpand,
+ openResult,
+ formatFileSize,
+ formatDateTime,
+ statusText,
+ };
+ },
+ template: `
+
+
📋 历史记录
+
按文件名聚合展示所有上传与分析任务。
+
+
+
+
{{ error }}
+
+
+
📁
+
暂无历史记录,先在仪表盘上传一个文件吧。
+
+
+
+
+
+
+
+
+
+ 暂无详细记录
+
+
+
+
+ 任务 ID:{{ record.task_id }}
+ 文件大小:{{ formatFileSize(record.file_size) }}
+ 完成时间:{{ formatDateTime(record.completed_at) }}
+
+
+
+
+
+
+ `,
+};
+
+// 结果详情视图
+const ResultView = {
+ setup() {
+ const route = useRoute();
+ const task = ref(null);
+ const loading = ref(true);
+ const error = ref("");
+
+ const taskId = computed(() => route.params.taskId);
+
+ const loadTask = async () => {
+ loading.value = true;
+ error.value = "";
+ try {
+ const res = await fetch(`/status/${taskId.value}`, { method: "POST" });
+ if (!res.ok) throw new Error("获取任务数据失败");
+ const data = await res.json();
+ task.value = data;
+ } catch (e) {
+ error.value = e.message || "加载失败";
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const geometry = computed(() => task.value?.geometry_data || null);
+ const keyInfo = computed(() => task.value?.key_info || null);
+ const meshSummary = computed(() => task.value?.mesh_summary || null);
+
+ onMounted(loadTask);
+
+ return {
+ task,
+ loading,
+ error,
+ taskId,
+ geometry,
+ keyInfo,
+ meshSummary,
+ formatNumber,
+ formatFileSize,
+ formatDateTime,
+ statusText,
+ };
+ },
+ template: `
+
+
📊 分析结果详情
+
+
+
+
正在加载任务 {{ taskId }} 的分析结果...
+
+
+
{{ error }}
+
+
+
+ 📝 任务信息
+
+
+ 任务 ID
+ {{ task.task_id || 'N/A' }}
+
+
+ 文件名
+ {{ task.filename || 'N/A' }}
+
+
+ 文件大小
+ {{ task.file_size ? formatFileSize(task.file_size) : 'N/A' }}
+
+
+ 状态
+
+
+ {{ statusText(task.status) }}
+
+
+
+
+ 上传时间
+ {{ task.upload_time ? formatDateTime(task.upload_time) : 'N/A' }}
+
+
+ 完成时间
+ {{ task.completed_at ? formatDateTime(task.completed_at) : 'N/A' }}
+
+
+
+ 错误信息:{{ task.error }}
+
+
+
+
+
+
📐 几何属性
+
+
+ 体积
+
+ {{ geometry.volume ? formatNumber(geometry.volume) + ' mm³' : 'N/A' }}
+
+
+
+ 表面积
+
+ {{ geometry.surface_area ? formatNumber(geometry.surface_area) + ' mm²' : 'N/A' }}
+
+
+
+ 体积/面积比
+
+ {{
+ geometry.volume && geometry.surface_area
+ ? (geometry.volume / geometry.surface_area).toFixed(4)
+ : 'N/A'
+ }}
+
+
+
+
无几何数据
+
+
+
+
📦 边界框
+
+
+ 尺寸 (mm)
+
+ {{ geometry.bounding_box.dimensions[0].toFixed(2) }} ×
+ {{ geometry.bounding_box.dimensions[1].toFixed(2) }} ×
+ {{ geometry.bounding_box.dimensions[2].toFixed(2) }}
+
+
+
+ 最小坐标
+
+ X: {{ geometry.bounding_box.min[0].toFixed(2) }},
+ Y: {{ geometry.bounding_box.min[1].toFixed(2) }},
+ Z: {{ geometry.bounding_box.min[2].toFixed(2) }}
+
+
+
+ 最大坐标
+
+ X: {{ geometry.bounding_box.max[0].toFixed(2) }},
+ Y: {{ geometry.bounding_box.max[1].toFixed(2) }},
+ Z: {{ geometry.bounding_box.max[2].toFixed(2) }}
+
+
+
+
无边界框数据
+
+
+
+
🔺 拓扑结构
+
+
+ 面数
+ {{ geometry.topology.faces || 0 }}
+
+
+ 边数
+ {{ geometry.topology.edges || 0 }}
+
+
+ 顶点数
+ {{ geometry.topology.vertices || 0 }}
+
+
+
无拓扑数据
+
+
+
+
+
+
🔧 模具 / 工艺关键信息
+
+
+ 收缩率
+
+ {{ keyInfo.metadata?.shrinkage_rate ?? 'N/A' }}
+
+
+
+ 拔模角
+
+ {{
+ keyInfo.metadata?.draft_angle !== undefined
+ ? keyInfo.metadata.draft_angle + '°'
+ : 'N/A'
+ }}
+
+
+
+ 预估夹紧力
+
+ {{ keyInfo.manufacturing_info?.estimated_clamping_force || 'N/A' }}
+
+
+
+ 模具尺寸 (mm)
+
+ {{
+ keyInfo.manufacturing_info?.estimated_mold_size
+ ? keyInfo.manufacturing_info.estimated_mold_size.length +
+ ' × ' +
+ keyInfo.manufacturing_info.estimated_mold_size.width +
+ ' × ' +
+ keyInfo.manufacturing_info.estimated_mold_size.height
+ : 'N/A'
+ }}
+
+
+
+ 产品重量
+
+ {{
+ keyInfo.mold_cavities?.cavity_key_info?.geometric_characteristics
+ ?.product_weight || 'N/A'
+ }}
+
+
+
+ 壁厚范围
+
+ {{
+ keyInfo.mold_cavities?.cavity_key_info?.geometric_characteristics
+ ?.wall_thickness_range || 'N/A'
+ }}
+
+
+
+ 预估成型周期
+
+ {{ keyInfo.manufacturing_info?.estimated_cycle_time || 'N/A' }}
+
+
+
+
无型腔 / 工艺数据
+
+
+
+
🧱 网格摘要
+
+
+ 网格质量等级
+ {{ meshSummary.quality }}
+
+
+ 顶点数
+ {{ meshSummary.vertex_count }}
+
+
+ 面数
+ {{ meshSummary.face_count }}
+
+
+ 点云采样点数
+ {{ meshSummary.point_count ?? 'N/A' }}
+
+
+
+ 当前任务未生成网格摘要,或网格生成过程中出现问题。
+
+
+
+
+
+ `,
+};
+
+// 路由配置
+const routes = [
+ { path: "/", component: DashboardView },
+ { path: "/history", component: HistoryView },
+ { path: "/result/:taskId", component: ResultView },
+];
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes,
+});
+
+// 挂载应用
+createApp(App).use(router).mount("#app");
+
diff --git a/templates/history.html b/templates/history.html
index 9422bf1..118e2e6 100644
--- a/templates/history.html
+++ b/templates/history.html
@@ -4,204 +4,15 @@
- 历史记录 - STP文件几何分析工具
+ 历史记录 - STP 模具几何分析中心
-
-
-
-
+
+
+
+