From cbc96dd1669f92da00ca7b3585e4ee29c9ea1ea7 Mon Sep 17 00:00:00 2001 From: HHY Date: Mon, 5 Oct 2026 06:23:18 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=95=88=E6=9E=9C=E5=9B=BE=E9=A1=B5?= =?UTF-8?q?=E5=88=87Tab=E5=90=8E3D=E7=A9=BA=E7=99=BD(v-show=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E4=B8=8B0=E5=B0=BA=E5=AF=B8=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96+NaN=E6=8A=95=E5=BD=B1=E7=9F=A9=E9=98=B5),=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0ResizeObserver=E5=AE=B9=E5=99=A8=E5=B0=BA=E5=AF=B8?= =?UTF-8?q?=E8=BF=BD=E8=B8=AA=E4=B8=8E=E9=9B=B6=E5=B0=BA=E5=AF=B8=E4=BF=9D?= =?UTF-8?q?=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG | 1 + frontend/src/components/WindoorThreed.vue | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4a1dfb0..e0abca2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # CHANGELOG +- [2026-10-05 06:23] FIX: v2.38 效果图页切 Tab 后 3D 展示空白修复。根因: WindoorThreed 在 /rendering 页以 v-show=false(display:none) 挂载, 容器 clientWidth=0 时 Three.js 以 0 尺寸初始化(camera.aspect=0/0=NaN), 页内切 Tab 显示后仅 window resize 事件无法触发重设尺寸→画布永远空白(直接访问 /threed 路由正常, 故 24 页巡检未覆盖); 修复: onResize 加零尺寸保护(避免 NaN 投影矩阵)+ResizeObserver 监听容器(显示瞬间尺寸 0→正常自动恢复)+初始 setSize 兜底 ||1; 无头复验 /rendering 切 Tab 场景渲染正常; 巡检 24 页 0 异常 (Files: frontend/src/components/WindoorThreed.vue) - [2026-10-05 06:11] FIX: v2.37 效果图渲染歪斜线修复——Canvas stroke 落错路径。根因: 玻璃多边形 fill 后叠加高光四边形(beginPath 新路径), 其后 ctx.stroke() 描的是最后构建的高光斜四边形而非玻璃多边形, 型材线全部变成斜线; 同型缺陷 3 处一并修复: RenderCanvas.vue(效果图渲染, stroke 移至高光前)+variantRenderer.ts renderAI(AI 炫图本地渲染, restore 后重建玻璃多边形路径再 stroke)+photoCompose.ts(照片式 AI 合成, 同 RenderCanvas 改法); 排查方法: 无头浏览器 hook moveTo/lineTo/beginPath 还原绘制序列, 证实矩形区域路径全部轴对齐、歪线恰为 3 个高光平行四边形; 高光斜带本为设计效果(clip 玻璃内)保留不变; 巡检 24 页 0 异常 (Files: frontend/src/components/RenderCanvas.vue, frontend/src/components/variantRenderer.ts, frontend/src/components/photoCompose.ts) - [2026-10-05 05:58] FEAT: v2.36 成本核算模块——鲁铭成本核算 Excel 全量适配。解析 12 表工作簿(铝成本/铝计算/木成本+价格库 8 表): 价格库 580 条(华健型材模具 397/丝吉利娅 207/索宾柯 52/隔热条 42/胶条 56/赛遨 54/螺丝 39/其他 23)导入产品价格本(同名更新/异名新建, material 按 sheet 语义映射 profile/hardware/addon/glass); 「铝成本」页→CostSeriesTemplate 系列成本模板(75框扇齐平1.4/85框扇齐平1.8/95窗纱一体1.8, data JSON 八分区: window/profile/addon/strip/seal/glass/extra/hardware/labor); 估算器口径对齐核算表(固定类料随固定面积/开启类随开启面积/once 按樘数缩放, 人工+其他成本优先用核算表每平实际口径, 毛利阶梯 10%~70%); 75 系列基准窗估算 total=1118.20 与核算表成本合计完全一致(85/95 同样精确还原); API: /api/costbook import-excel/series/estimate CRUD; 前端 /cost_estimate 页(系列卡片+参数估算+八分区明细+毛利阶梯+价格库 Tab+Excel 上传导入); 种子脚本 seed_costbook.py; 修复 6 处解析缺陷(95系列号格式/隔热条total字段/胶条辅材辅件编号列空漏采/玻璃面积重复乘块数/once分支漏加total/基准窗面积等效); 回归: 冒烟 60/60(新增 COST-1~9)+24 页巡检 0 异常+深度审计 16/16 (Files: src/huamenchuang/services/costbook_service.py, src/huamenchuang/api/costbook.py, src/huamenchuang/api/router.py, src/huamenchuang/models/business.py, src/huamenchuang/models/__init__.py, src/huamenchuang/scripts/seed_costbook.py, src/huamenchuang/scripts/smoke_all.py, frontend/src/views/CostEstimateView.vue, frontend/src/views/LayoutView.vue, frontend/src/router/index.ts, frontend/scripts/verify-pages.mjs, doc/API.md) - [2026-09-26 08:30] CHORE: v2.35 系统更名"华门窗"→"定兴门窗"。替换全部用户可见名称: 侧边栏 logo(折叠态 定)/面包屑/登录页标题/分享页 logo 与页脚/页面 title(index.html)/FastAPI title(/docs Swagger)/Dockerfile 注释/各包 docstring; 代码标识符(包名 huamenchuang/数据表/JWT iss)不变; 全仓库 0 处残留, vue-tsc 0 错+构建通过, 无头浏览器验证 logo/面包屑/页面标题生效 (Files: frontend/src/views/LayoutView.vue, frontend/src/views/LoginView.vue, frontend/src/views/ShareView.vue, frontend/index.html, src/huamenchuang/main.py, src/huamenchuang/*/…)__ diff --git a/frontend/src/components/WindoorThreed.vue b/frontend/src/components/WindoorThreed.vue index 5aa21d1..99dffad 100644 --- a/frontend/src/components/WindoorThreed.vue +++ b/frontend/src/components/WindoorThreed.vue @@ -54,6 +54,8 @@ * - 视角预设 / 自动旋转 / 截图导出 */ import { onMounted, onBeforeUnmount, ref, watch } from 'vue' + +let ro: ResizeObserver | null = null import { ElMessage } from 'element-plus' import * as THREE from 'three' import { @@ -475,6 +477,8 @@ function loop() { function onResize() { const wrap = wrapRef.value, cv = cvRef.value if (!wrap || !cv || !renderer || !camera) return + // 容器不可见(display:none)时尺寸为 0,跳过避免 NaN 投影矩阵 + if (!wrap.clientWidth || !wrap.clientHeight) return renderer.setSize(wrap.clientWidth, wrap.clientHeight) camera.aspect = wrap.clientWidth / wrap.clientHeight camera.updateProjectionMatrix() @@ -512,15 +516,18 @@ function screenshot() { onMounted(() => { const wrap = wrapRef.value!, cv = cvRef.value! renderer = new THREE.WebGLRenderer({ canvas: cv, antialias: true, preserveDrawingBuffer: true }) - renderer.setSize(wrap.clientWidth, wrap.clientHeight) + renderer.setSize(wrap.clientWidth || 1, wrap.clientHeight || 1) renderer.shadowMap.enabled = true - camera = new THREE.PerspectiveCamera(50, wrap.clientWidth / wrap.clientHeight, 0.01, 100) + camera = new THREE.PerspectiveCamera(50, (wrap.clientWidth || 1) / (wrap.clientHeight || 1), 0.01, 100) cv.addEventListener('mousedown', onDown) window.addEventListener('mousemove', onMove) window.addEventListener('mouseup', onUp) cv.addEventListener('wheel', e => { e.preventDefault(); onWheel(e) }, { passive: false }) cv.addEventListener('contextmenu', e => e.preventDefault()) window.addEventListener('resize', onResize) + // 容器尺寸追踪:v-show 隐藏下挂载(如效果图页切 Tab)时初始尺寸为 0,显示瞬间自动恢复 + ro = new ResizeObserver(onResize) + ro.observe(wrap) watch(() => props.drawingData, () => { try { if (props.drawingData) build(parseDrawing(props.drawingData)) @@ -535,6 +542,7 @@ onMounted(() => { onBeforeUnmount(() => { cancelAnimationFrame(raf) dispose() + ro?.disconnect() window.removeEventListener('resize', onResize) renderer?.dispose() })