diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f4ed7c1..08a4210 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -11,7 +11,11 @@ "Bash(do echo \"=== $dir ===\")", "Bash(ls:*)", "mcp__context7__resolve-library-id", - "mcp__context7__get-library-docs" + "mcp__context7__get-library-docs", + "WebSearch", + "mcp__chrome-devtools__list_pages", + "mcp__chrome-devtools__navigate_page", + "mcp__chrome-devtools__take_snapshot" ], "deny": [], "ask": [], @@ -19,4 +23,4 @@ "D:\\App\\vue\\Miany" ] } -} \ No newline at end of file +} diff --git a/docs/frontend-technical-overview.md b/docs/frontend-technical-overview.md new file mode 100644 index 0000000..ab95e0d --- /dev/null +++ b/docs/frontend-technical-overview.md @@ -0,0 +1,109 @@ +# 前端技术路线文档 + +## 1. 项目概述 +- **框架基线**:基于 `Vue 3` + Composition API 构建的单页应用,入口由 `src/main.js` 创建并挂载 `App.vue`。 +- **运行模式**:采用 `Vite` 作为开发服务器与构建工具,统一在 `config/vite.config.js` 配置,脚本通过 `npm run dev/build/preview` 调用。 +- **部署目标**:面向工业 CAD 模型统一管理平台的前端界面,所有业务逻辑围绕 CAD 连接、模型查看、分析工具与日志监控展开。 + +## 2. 应用层次结构 +### 2.1 启动流程 +1. `main.js` 创建应用实例并加载全局依赖:`Pinia`、`Vue Router`、`Element Plus`。 +2. 初始化认证状态 (`useAuthStore().initAuth()`),确保刷新后可以恢复登录信息。 +3. 启动 WebSocket 服务 (`websocketService.connect()`),为后续日志与状态同步提供实时通道。 +4. 挂载根组件 `App.vue`,其模板内仅渲染 ``,由路由控制页面结构。 + +### 2.2 路由与页面 +- 使用 `Vue Router 4`(Hash 模式)管理页面,路由定义于 `src/router/index.js`。 +- 主要路由:`/login`(登录页)、`/dashboard`(主控制台),根路径重定向到仪表板。 +- 全局前置守卫根据 `meta.requiresAuth` 校验登录状态,并同步浏览器标题。 + +### 2.3 主场景组织 +- `DashboardView.vue` 作为仪表板容器,组合 `MainLayout` 布局,并通过动态组件与 `KeepAlive` 控制页面切换。 +- 页面组件映射在本地 `pageComponentMap` 中,根据 `PAGE_TYPES`(`src/config/pages.js`)选择展示模块。 +- 关键子组件: + - `AppHeader` 负责页面切换与信息面板控制。 + - `CadSidebar` 管理各 CAD 软件连接入口。 + - `InfoManagementPanel` 展示实时日志、状态信息。 + - `components/pages` 目录承载具体业务页面(模型库、分析、导出、查看器等)。 + +## 3. 状态管理 +- 统一使用 `Pinia`,入口在 `main.js` 注册。 +- Store 设计遵循「只持久化状态,不处理 API 调用」原则。 + +### 3.1 认证状态 (`src/stores/auth.js`) +- 存储当前用户、token、加载状态与错误信息。 +- 提供登录、登出、token 校验、错误清理等方法。 +- 登录逻辑支持模拟异步,后续可平滑接入真实 API。 +- 与本地存储联动,刷新后通过 `initAuth()` 恢复状态。 + +### 3.2 CAD 状态 (`src/stores/cad.js`) +- 根据 `config/cad.js` 中的 CAD 定义初始化连接状态列表。 +- 维持单一连接原则:`setCADConnection` 会在连接新软件前断开其他连接。 +- 暴露当前连接的软件信息、可显示 CAD 列表、项目名称等状态。 +- 提供功能支持检查 (`cadSupportsFeature`) 与连接中状态更新等能力。 + +## 4. 配置体系 +- 所有业务配置集中在 `src/config`: + - `cad.js`:CAD 软件定义、API 端点、通知、WebSocket、导出格式与默认参数;同时提供 URL 构建、功能支持判断等工具函数。 + - `pages.js`:统一的页面类型常量与权限要求,供仪表板和导航模块引用。 + - 其他配置文件(如 `auth`)为 Store 提供默认值、校验规则和超时设置。 + +## 5. 通信与服务层 +### 5.1 HTTP 客户端 (`src/services/apiClient.js`) +- 基于 `fetch` 的统一封装,内置请求/响应拦截、Loading 遮罩、Element Plus 通知、操作日志同步与错误处理。 +- 利用 `PostProcessManager` 组织后处理钩子: + - 成功/失败后自动弹出通知。 + - 将操作日志通过 `websocketService.logOperation` 推送到后台,实现前后端日志统一。 +- 提供 `get/post/put/delete` 便捷方法,默认读取 `config/cad.js` 中的请求头、超时等配置。 + +### 5.2 WebSocket 服务 (`src/services/websocketService.js`) +- 管理实时连接、自动重连、心跳以及后台消息解析。 +- 维护软件列表、操作日志、统计信息等实时状态,并通过事件订阅模式向组件广播。 +- 支持后台命令(启动/停止/重启软件、查询日志等)与操作记录上报。 +- 登录用户信息会动态带入连接参数和日志上报,确保后台可追踪责任主体。 + +## 6. UI 与样式体系 +- UI 组件库:全局注册 `Element Plus`,结合自研组件完成业务场景搭建。 +- 样式策略:在 `src/assets/styles/theme.css` 定义 CSS 变量与通用样式,`App.vue` 导入后覆盖全局;组件内尽量使用作用域样式或复用变量,保持视觉一致性。 +- 主题与布局: + - `layout` 目录包含主框架组件(头部、侧边、布局容器)。 + - `pages` 目录按业务功能拆分子页面,保持单一职责。 + - `model` 目录存放各 CAD 查看器,遵循统一的主题变量与交互规范。 + +## 7. 目录结构概览 +``` +src/ +├── assets/ # 静态资源与全局样式 +├── components/ +│ ├── layout/ # 主布局与导航组件 +│ ├── model/ # 针对 CAD 的模型查看器 +│ ├── pages/ # 业务页面碎片,按 PAGE_TYPES 分类 +│ └── ui/ # 通用 UI 组件(树形节点等) +├── config/ # 统一配置中心(CAD、页面、认证等) +├── router/ # 路由配置与守卫 +├── services/ # HTTP、WebSocket、业务服务封装 +├── stores/ # Pinia Store(认证、CAD 状态等) +├── utils/ # 工具函数与日志封装 +└── views/ # 路由级页面容器(Dashboard、Login 等) +``` + +## 8. 数据与事件流 +1. 用户通过 `CadSidebar` 发起连接请求,组件内调用相应服务(例如 `apiClient` + `config/cad.js` 构建 URL)。 +2. API 返回后,组件根据结果调用 `cadStore.setCADConnection` 更新全局状态,并向父组件触发事件以切换页面或加载数据。 +3. `apiClient` 拦截器自动发送 Element Plus 通知,并通过 WebSocket 记录操作日志;WebSocket 后台返回的最新状态又会驱动 `InfoManagementPanel` 更新展示。 +4. 所有跨组件通信优先使用事件 (`emit`) 与 Pinia 状态,避免组件间直接耦合。 + +## 9. 构建与质量保障 +- `package.json` 中定义脚本: + - `npm run dev`:开发模式。 + - `npm run build`:生产构建。 + - `npm run preview`:本地预览构建结果。 + - `npm run lint`:执行 ESLint(配置位于 `config/eslint.config.js`),自动修复常见问题。 + - `npm run format`:根据 `config/.prettierrc.json` 格式化 `src/` 目录。 +- 推荐 Node 版本 `^20.19.0 || >=22.12.0`,确保与依赖兼容。 + +## 10. 扩展与约束原则 +- 遵循 `CLAUDE.md` 中的严格开发准则:先确认需求,再最小化实现,禁止未授权的额外功能或样式更改。 +- Store 只负责状态管理,API 调用留在组件或服务层,避免职责交叉。 +- 与后台的交互需要通过 `operationContext` 提供统一的通知描述,保持日志与提示文案一致。 +- 新增页面需按“配置 → 组件 → 仪表板页注册”的流程接入,保证页面切换逻辑集中管理。 diff --git a/package-lock.json b/package-lock.json index dca2851..7c3d7e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "element-plus": "^2.11.2", + "online-3d-viewer": "^0.16.0", "pinia": "^3.0.3", "vue": "^3.5.18", "vue-router": "^4.5.1" @@ -59,6 +60,7 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1592,6 +1594,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@simonwep/pickr": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.9.0.tgz", + "integrity": "sha512-oEYvv15PyfZzjoAzvXYt3UyNGwzsrpFxLaZKzkOSd0WYBVwLd19iJerePDONxC1iF6+DpcswPdLIM2KzCJuYFg==", + "license": "MIT", + "dependencies": { + "core-js": "3.32.2", + "nanopop": "2.3.0" + } + }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", @@ -1630,6 +1642,7 @@ "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/lodash": "*" } @@ -2022,6 +2035,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2159,6 +2173,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.2", "caniuse-lite": "^1.0.30001741", @@ -2286,6 +2301,17 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-js": { + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", + "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2526,6 +2552,7 @@ "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2587,6 +2614,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -2829,6 +2857,12 @@ } } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", @@ -3277,13 +3311,15 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-unified": { "version": "1.0.3", @@ -3382,6 +3418,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nanopop": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.3.0.tgz", + "integrity": "sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3452,6 +3494,17 @@ "dev": true, "license": "MIT" }, + "node_modules/online-3d-viewer": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/online-3d-viewer/-/online-3d-viewer-0.16.0.tgz", + "integrity": "sha512-Mcmo41TM3K+svlMDRH8ySKSY2e8s7Sssdb5U9LV3gkFKVWGGuS304Vk5gqxopAJbE72DpsC67Ve3YNtcAuROwQ==", + "license": "MIT", + "dependencies": { + "@simonwep/pickr": "1.9.0", + "fflate": "0.8.2", + "three": "0.176.0" + } + }, "node_modules/open": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", @@ -3678,6 +3731,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -3946,6 +4000,12 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/three": { + "version": "0.176.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.176.0.tgz", + "integrity": "sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -4070,6 +4130,7 @@ "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -4289,6 +4350,7 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.21", "@vue/compiler-sfc": "3.5.21", diff --git a/package.json b/package.json index fbace7d..b99c890 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "element-plus": "^2.11.2", + "online-3d-viewer": "^0.16.0", "pinia": "^3.0.3", "vue": "^3.5.18", "vue-router": "^4.5.1" diff --git a/src/assets/styles/theme.css b/src/assets/styles/theme.css index 3f38688..d785e95 100644 --- a/src/assets/styles/theme.css +++ b/src/assets/styles/theme.css @@ -46,6 +46,7 @@ --color-bg-secondary: rgb(var(--color-bg-secondary-base)); --color-bg-tertiary: rgb(var(--color-bg-tertiary-base)); --color-bg-quaternary: rgb(37, 37, 37); + --color-bg-viewer-light: rgb(220, 222, 225); /* 3D查看器浅色背景 */ --color-bg-card: rgba(var(--color-bg-secondary-base), 0.8); --color-bg-hover: rgba(var(--color-white-base), 0.05); --color-bg-active: rgba(var(--color-primary-base), 0.1); diff --git a/src/components/pages/GeometryOptimizationParams.vue b/src/components/pages/GeometryOptimizationParams.vue index c119e9d..4365370 100644 --- a/src/components/pages/GeometryOptimizationParams.vue +++ b/src/components/pages/GeometryOptimizationParams.vue @@ -214,6 +214,13 @@ import { creoApi } from '@/services/creoApi.js' // Emits const emit = defineEmits(['close', 'start-optimization']) +const props = defineProps({ + subassemblyPath: { + type: String, + default: null + } +}) + // 响应式数据 const sections = ref({ basic: { expanded: true }, @@ -229,7 +236,7 @@ const configDefaults = getGeometryOptimizationDefaults() // 当前参数 const parameters = ref({ software_type: configDefaults.SOFTWARE_TYPE, - project_name: window.appState?.currentProject?.name || 'Unknown Project', + project_name: props.subassemblyPath || window.appState?.currentProject?.name || 'Unknown Project', method: configDefaults.METHOD, quality: configDefaults.QUALITY, chord_height: configDefaults.CHORD_HEIGHT, @@ -273,6 +280,12 @@ const startOptimization = async () => { assign_mass_properties: parameters.value.assign_mass_properties } + // 如果项目名称不是'Unknown Project',则认为是子装配体路径,添加参数 + if (parameters.value.project_name !== 'Unknown Project') { + // 增加一个参数,参数值为项目名称的值 (即传过来的路径) + apiParams.component_path = parameters.value.project_name + } + // 调用API const result = await creoApi.startShrinkwrapShell(apiParams) diff --git a/src/components/pages/HierarchyAnalysisResult.vue b/src/components/pages/HierarchyAnalysisResult.vue index d79565f..9991e8d 100644 --- a/src/components/pages/HierarchyAnalysisResult.vue +++ b/src/components/pages/HierarchyAnalysisResult.vue @@ -175,6 +175,15 @@ 删除选中组件 ({{ selectedComponents.size }}) +