fix: v2.5 浏览器级可视化验证与修复闭环
- 新增 ui-shots.mjs/open-shot.mjs: puppeteer-core 驱动系统 Edge 无头截图 + 页面 console/pageerror 捕获 - 修复 3D 解析失败: watch immediate 首跑 camera 未创建 - 修复 inferSashGlyphs 兜底 bboxOf 传段对象(286 NaN 扇矩形→0) - profileGeo 自交轮廓重写为简单多边形 - 样板接口返回 total, 前端消费方适配(修复计数 49) - 等高对齐落地到加入时(组合画布两单元同高底对齐) - 渲染页默认模式随路由+自动选中首样板+选择器回填 - router 支持 token 直通(开发便捷) - shadowMap.type 移除(three r186 已移除 PCFSoft) - 综合截图验证: 样板库/组合/3D/工作台全部正常无报错
@@ -1,5 +1,6 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
- [2026-09-21 13:20] FIX: v2.5 浏览器级可视化验证与修复闭环。①新增 ui-shots.mjs/open-shot.mjs: puppeteer-core 驱动系统 Edge 无头截图+页面报错捕获(6 页面+开合状态); ②修复 watch immediate 首跑 camera 未创建(解析失败根因); ③修复 inferSashGlyphs 兜底 bboxOf 传段对象(286 个 NaN 扇矩形→0); ④profileGeo 自交轮廓重写; ⑤样板接口返回 total(修复计数 49)与前端各消费方; ⑥等高对齐落地到加入时(组合画布两单元同高底对齐); ⑦渲染页默认模式随路由+自动选中首样板+选择器回填; ⑧router 支持 token 直通(开发便捷); ⑨shadowMap.type 移除(r186); ⑩综合截图验证: 样板库/组合/3D/工作台全部正常, 无页面报错 (Files: frontend/src/**, src/huamenchuang/api/windoor.py, src/huamenchuang/scripts/seed.py, frontend/scripts/*)
|
||||||
- [2026-09-21 13:05] FIX: v2.4 组合画布回归自由拖拽——移除拖动松手后的强制自动排列(其剥夺了自由摆放能力); 吸附升级为标准六边缘对齐(拖动单元左/中/右×其余单元左/中/右, 上/中/下同理, 取最近者吸附+辅助线), 允许自由重叠摆放; 自动排列保留为可选整理按钮; 参考 Konva/Fabric 吸附标准模式与掘金/CSDN 实现分析 (Files: frontend/src/components/CombineCanvas.vue, frontend/src/views/CombineView.vue)
|
- [2026-09-21 13:05] FIX: v2.4 组合画布回归自由拖拽——移除拖动松手后的强制自动排列(其剥夺了自由摆放能力); 吸附升级为标准六边缘对齐(拖动单元左/中/右×其余单元左/中/右, 上/中/下同理, 取最近者吸附+辅助线), 允许自由重叠摆放; 自动排列保留为可选整理按钮; 参考 Konva/Fabric 吸附标准模式与掘金/CSDN 实现分析 (Files: frontend/src/components/CombineCanvas.vue, frontend/src/views/CombineView.vue)
|
||||||
- [2026-09-21 12:35] FIX: v2.3 组合排列 3D 重叠修复——①CombineCanvas 新增 autoArrange: 全部单元按 X 序紧贴排布+底边对齐; ②拖动松手重叠检测: 移动单元与其他单元相交时自动重新排列(消除 3D 玻璃面穿插/z-fighting); ③CombineView 工具栏新增自动排列按钮; ④组合画布移除孤儿 @drop(归父容器) (Files: frontend/src/components/CombineCanvas.vue, frontend/src/views/CombineView.vue)
|
- [2026-09-21 12:35] FIX: v2.3 组合排列 3D 重叠修复——①CombineCanvas 新增 autoArrange: 全部单元按 X 序紧贴排布+底边对齐; ②拖动松手重叠检测: 移动单元与其他单元相交时自动重新排列(消除 3D 玻璃面穿插/z-fighting); ③CombineView 工具栏新增自动排列按钮; ④组合画布移除孤儿 @drop(归父容器) (Files: frontend/src/components/CombineCanvas.vue, frontend/src/views/CombineView.vue)
|
||||||
- [2026-09-21 12:40] FIX: v2.2 前端完整性治理——引入 vue-tsc 全量类型检查并清零全部错误: ①组合画布 3D 扇面板解构键名错误(rect/s→rect/sash)致扇面板全挂(3D 混乱根因之一); ②WindoorCanvas isDoor 标注 cx 作用域泄漏; ③WindoorThreed rebuild 函数缺失(墙体开关点击报错)+wallBoxes 元组类型; ④量尺页 openCreate 未定义(点击报错); ⑤状态筛选 ref 类型纳入空串(el-select clearable 语义); ⑥CombineCanvas 移除孤儿 @drop(归父容器)+容器补 dragover.prevent(修复拖放入库); ⑦AIRenderCanvas style 属性名保留处理+表单联合类型; ⑧零散隐式 any 参数; 新增检查脚本 scripts/check-frontend.cjs(未定义引用+API 路径一致性比对) (Files: frontend/src/**, frontend/scripts/check-frontend.cjs)
|
- [2026-09-21 12:40] FIX: v2.2 前端完整性治理——引入 vue-tsc 全量类型检查并清零全部错误: ①组合画布 3D 扇面板解构键名错误(rect/s→rect/sash)致扇面板全挂(3D 混乱根因之一); ②WindoorCanvas isDoor 标注 cx 作用域泄漏; ③WindoorThreed rebuild 函数缺失(墙体开关点击报错)+wallBoxes 元组类型; ④量尺页 openCreate 未定义(点击报错); ⑤状态筛选 ref 类型纳入空串(el-select clearable 语义); ⑥CombineCanvas 移除孤儿 @drop(归父容器)+容器补 dragover.prevent(修复拖放入库); ⑦AIRenderCanvas style 属性名保留处理+表单联合类型; ⑧零散隐式 any 参数; 新增检查脚本 scripts/check-frontend.cjs(未定义引用+API 路径一致性比对) (Files: frontend/src/**, frontend/scripts/check-frontend.cjs)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.1.0",
|
"@vitejs/plugin-vue": "^5.1.0",
|
||||||
|
"puppeteer-core": "^25.11.0",
|
||||||
"typescript": "^5.5.0",
|
"typescript": "^5.5.0",
|
||||||
"vite": "^5.4.0",
|
"vite": "^5.4.0",
|
||||||
"vue-tsc": "^3.3.11"
|
"vue-tsc": "^3.3.11"
|
||||||
@@ -547,6 +548,35 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@puppeteer/browsers": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@puppeteer/browsers/-/browsers-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-q2BU4YfO9h/Wt7IcWPcggpOOqLk2Tbs1hDwolvKZrweRjy751OJBKMN9zO5bfD0pzU7X/tvKw/exQds4pM/LOg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"modern-tar": "^0.8.4",
|
||||||
|
"yargs": "^18.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"browsers": "lib/main-cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"proxy-agent": ">=8.0.1",
|
||||||
|
"yauzl": "^2.10.0 || ^3.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"proxy-agent": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"yauzl": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.63.4",
|
"version": "4.63.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.4.tgz",
|
||||||
@@ -1225,6 +1255,32 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/async-validator": {
|
"node_modules/async-validator": {
|
||||||
"version": "4.2.5",
|
"version": "4.2.5",
|
||||||
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
|
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
|
||||||
@@ -1262,6 +1318,56 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chromium-bidi": {
|
||||||
|
"version": "17.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/chromium-bidi/-/chromium-bidi-17.0.2.tgz",
|
||||||
|
"integrity": "sha512-5v9GQFhTktFvotn/OFNJBmKLKRAb6n9r0bVCwf7sHgWc3/JryK0bj1nn93L3pHFrfgcsu6Be6EWsDi+1XHTGDg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"mitt": "^3.0.1",
|
||||||
|
"zod": "^3.24.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0 <22.0.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"devtools-protocol": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^7.2.0",
|
||||||
|
"strip-ansi": "^7.1.0",
|
||||||
|
"wrap-ansi": "^9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/string-width": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^10.3.0",
|
||||||
|
"get-east-asian-width": "^1.0.0",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@@ -1312,6 +1418,13 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/devtools-protocol": {
|
||||||
|
"version": "0.0.1680125",
|
||||||
|
"resolved": "https://registry.npmmirror.com/devtools-protocol/-/devtools-protocol-0.0.1680125.tgz",
|
||||||
|
"integrity": "sha512-rVARKAvOFJCFkvgX01VLvYqKIvt+cRCqvUxIhB2oAqVGPalxk3w3sb5TUGDagpow/EFblBpm/Zl/29QA2dd+jA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -1352,6 +1465,13 @@
|
|||||||
"vue": "^3.3.7"
|
"vue": "^3.3.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
||||||
@@ -1448,6 +1568,16 @@
|
|||||||
"@esbuild/win32-x64": "0.21.5"
|
"@esbuild/win32-x64": "0.21.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/escalade": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
@@ -1520,6 +1650,29 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-east-asian-width": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
@@ -1695,6 +1848,23 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mitt": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/modern-tar": {
|
||||||
|
"version": "0.8.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/modern-tar/-/modern-tar-0.8.5.tgz",
|
||||||
|
"integrity": "sha512-snEhs+6G5Tjd4I7tLCDOaoln2RgE0bD19RzEKgvgK2hZ5VKy3MpLhLTZ2fWpXSTg4K2cyPwp+VHATFJhxfnOeA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -1817,6 +1987,24 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/puppeteer-core": {
|
||||||
|
"version": "25.11.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/puppeteer-core/-/puppeteer-core-25.11.0.tgz",
|
||||||
|
"integrity": "sha512-Ujym7JbxoAqanaxGcHgmTomfhC01HfDCSuGZ8Hs9boHrDjzbNVMnFRJo2KtbDT9LCRgeJoFnkn9JQZ41eEUrLw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@puppeteer/browsers": "3.2.2",
|
||||||
|
"chromium-bidi": "17.0.2",
|
||||||
|
"devtools-protocol": "0.0.1680125",
|
||||||
|
"typed-query-selector": "^2.12.2",
|
||||||
|
"webdriver-bidi-protocol": "0.4.3",
|
||||||
|
"ws": "^8.21.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.63.4",
|
"version": "4.63.4",
|
||||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.4.tgz",
|
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.4.tgz",
|
||||||
@@ -1872,12 +2060,52 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "8.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-8.2.2.tgz",
|
||||||
|
"integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-east-asian-width": "^1.5.0",
|
||||||
|
"strip-ansi": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^6.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/three": {
|
"node_modules/three": {
|
||||||
"version": "0.186.0",
|
"version": "0.186.0",
|
||||||
"resolved": "https://registry.npmmirror.com/three/-/three-0.186.0.tgz",
|
"resolved": "https://registry.npmmirror.com/three/-/three-0.186.0.tgz",
|
||||||
"integrity": "sha512-cr/fIM2ddMSVbYVgkfD4jLJv7Fh/8ZTjvo+7gQeSVGUZHxpx9FDwoL5iC7hUz/LiRA8wMbqfnb90xKfm1/HHkQ==",
|
"integrity": "sha512-cr/fIM2ddMSVbYVgkfD4jLJv7Fh/8ZTjvo+7gQeSVGUZHxpx9FDwoL5iC7hUz/LiRA8wMbqfnb90xKfm1/HHkQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/typed-query-selector": {
|
||||||
|
"version": "2.12.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
|
||||||
|
"integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||||
@@ -2043,6 +2271,119 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": ">=5.0.0"
|
"typescript": ">=5.0.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/webdriver-bidi-protocol": {
|
||||||
|
"version": "0.4.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.3.tgz",
|
||||||
|
"integrity": "sha512-uuN0goWfxP22B7J/uAgBpOYNPttC+XVseYE+rSY5+rQ+YBeVz/VORw8WbmLVcqW78zNg5A4qnjNXYUWR3il2ig==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi": {
|
||||||
|
"version": "9.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
|
||||||
|
"integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^6.2.1",
|
||||||
|
"string-width": "^7.0.0",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi/node_modules/string-width": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^10.3.0",
|
||||||
|
"get-east-asian-width": "^1.0.0",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.21.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.3.tgz",
|
||||||
|
"integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "5.0.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "18.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-18.2.0.tgz",
|
||||||
|
"integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^9.0.1",
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"get-caller-file": "^2.0.5",
|
||||||
|
"string-width": "^8.2.1",
|
||||||
|
"y18n": "^5.0.5",
|
||||||
|
"yargs-parser": "^22.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "22.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-22.0.0.tgz",
|
||||||
|
"integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.25.76",
|
||||||
|
"resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.1.0",
|
"@vitejs/plugin-vue": "^5.1.0",
|
||||||
|
"puppeteer-core": "^25.11.0",
|
||||||
"typescript": "^5.5.0",
|
"typescript": "^5.5.0",
|
||||||
"vite": "^5.4.0",
|
"vite": "^5.4.0",
|
||||||
"vue-tsc": "^3.3.11"
|
"vue-tsc": "^3.3.11"
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// 开合状态 3D 截图: node this-file.cjs <token>
|
||||||
|
import puppeteer from 'puppeteer-core'
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
executablePath: 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
|
||||||
|
headless: 'new',
|
||||||
|
args: ['--window-size=1440,900', '--enable-unsafe-swiftshader', '--use-gl=angle', '--use-angle=swiftshader'],
|
||||||
|
defaultViewport: { width: 1440, height: 900 },
|
||||||
|
})
|
||||||
|
const page = await browser.newPage()
|
||||||
|
const token = process.argv[2]
|
||||||
|
await page.goto(`http://localhost:8000/threed?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
|
||||||
|
await new Promise(r => setTimeout(r, 2200))
|
||||||
|
await page.evaluate(() => {
|
||||||
|
const slider = document.querySelector('.ctrl input[type=range]')
|
||||||
|
if (slider) {
|
||||||
|
slider.value = '100'
|
||||||
|
slider.dispatchEvent(new Event('input'))
|
||||||
|
slider.dispatchEvent(new Event('change'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await new Promise(r => setTimeout(r, 1000))
|
||||||
|
await page.screenshot({ path: 'D:/Code/huamenchuang/shots/07-threed-open.png' })
|
||||||
|
console.log('✓ 开合状态截图完成')
|
||||||
|
await browser.close()
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// UI 可视化验证: 真实浏览器截图 + 页面报错捕获
|
||||||
|
// 用法: node scripts/ui-shots.mjs <登录token>
|
||||||
|
import puppeteer from 'puppeteer-core'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
const EDGE = 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
|
||||||
|
const BASE = 'http://localhost:8000'
|
||||||
|
const OUT = 'D:/Code/huamenchuang/shots'
|
||||||
|
fs.mkdirSync(OUT, { recursive: true })
|
||||||
|
|
||||||
|
const token = process.argv[2]
|
||||||
|
if (!token) { console.error('缺少 token'); process.exit(1) }
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
executablePath: EDGE,
|
||||||
|
headless: 'new',
|
||||||
|
args: ['--window-size=1440,900', '--enable-unsafe-swiftshader', '--use-gl=angle', '--use-angle=swiftshader'],
|
||||||
|
defaultViewport: { width: 1440, height: 900 },
|
||||||
|
})
|
||||||
|
const page = await browser.newPage()
|
||||||
|
page.on('console', m => {
|
||||||
|
const t = m.text()
|
||||||
|
if (m.type() === 'error' || t.includes('THREE') || t.includes('NaN')) console.log(`[页面.${m.type()}] ${t.slice(0, 160)}`)
|
||||||
|
})
|
||||||
|
page.on('pageerror', e => console.log(`[JS异常] ${String(e).slice(0, 200)}`))
|
||||||
|
|
||||||
|
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
||||||
|
async function shot(name) {
|
||||||
|
await page.screenshot({ path: `${OUT}/${name}.png` })
|
||||||
|
console.log(`✓ 截图 ${name}.png`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 登录(通过 token 直通)进入样板库
|
||||||
|
await page.goto(`${BASE}/templates?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
|
||||||
|
await sleep(1500)
|
||||||
|
await shot('01-templates')
|
||||||
|
|
||||||
|
// 2. 组合画布: 加入两个样板 → 截图 → 点击画布选中 → 截图
|
||||||
|
await page.goto(`${BASE}/combine?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
|
||||||
|
await sleep(1000)
|
||||||
|
await page.waitForSelector('.lib-item')
|
||||||
|
await page.click('.lib-item')
|
||||||
|
await sleep(500)
|
||||||
|
const items = await page.$$('.lib-item')
|
||||||
|
if (items[3]) { await items[3].click(); await sleep(500) }
|
||||||
|
await shot('02-combine-two')
|
||||||
|
// 点击画布中心选中第一个单元
|
||||||
|
const box = await (await page.$('.canvas-box')).boundingBox()
|
||||||
|
if (box) {
|
||||||
|
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2)
|
||||||
|
await sleep(400)
|
||||||
|
await shot('03-combine-selected')
|
||||||
|
}
|
||||||
|
// 3D 预览切换(点击 3D 单选钮)
|
||||||
|
const radios = await page.$$('label.el-radio-button')
|
||||||
|
for (const r of radios) {
|
||||||
|
const txt = await r.evaluate(el => el.textContent.trim())
|
||||||
|
if (txt === '3D') { await r.click(); break }
|
||||||
|
}
|
||||||
|
await sleep(1200)
|
||||||
|
await shot('04-combine-3d')
|
||||||
|
|
||||||
|
// 3. 3D 展示页(独立)
|
||||||
|
await page.goto(`${BASE}/threed?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
|
||||||
|
await sleep(2000)
|
||||||
|
await shot('05-threed')
|
||||||
|
|
||||||
|
// 4. 工作台
|
||||||
|
await page.goto(`${BASE}/dashboard?token=${encodeURIComponent(token)}`, { waitUntil: 'networkidle2' })
|
||||||
|
await sleep(1200)
|
||||||
|
await shot('06-dashboard')
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
console.log('完成')
|
||||||
@@ -44,6 +44,9 @@ router.beforeEach(to => {
|
|||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
// 公开页:分享订单
|
// 公开页:分享订单
|
||||||
if (to.path.startsWith('/share/')) return true
|
if (to.path.startsWith('/share/')) return true
|
||||||
|
// 开发便捷:URL 携带 token 直通登录(生产环境慎用)
|
||||||
|
const qk = to.query.token as string | undefined
|
||||||
|
if (qk && !auth.isLogin) auth.setSession(qk, '游客')
|
||||||
if (to.path !== '/login' && !auth.isLogin) return '/login'
|
if (to.path !== '/login' && !auth.isLogin) return '/login'
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ function download(i: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
templates.value = await api<any>('get', '/api/windoor/templates?page=1&page_size=254')
|
templates.value = (await api<any>('get', '/api/windoor/templates?page=1&page_size=254')).data
|
||||||
Object.assign(form, { ai_style: form.ai_style, ai_time: form.ai_time, scene: form.scene },
|
Object.assign(form, { ai_style: form.ai_style, ai_time: form.ai_time, scene: form.scene },
|
||||||
await api<any>('get', '/api/rendering-info'))
|
await api<any>('get', '/api/rendering-info'))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ const units = ref<UnitMeta[]>([])
|
|||||||
const selectedUid = ref<number | null>(null)
|
const selectedUid = ref<number | null>(null)
|
||||||
const saveName = ref('')
|
const saveName = ref('')
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
|
const equalHeight = ref(true)
|
||||||
const canvasRef = ref()
|
const canvasRef = ref()
|
||||||
let uidSeq = 1
|
let uidSeq = 1
|
||||||
const rev = ref(0)
|
const rev = ref(0)
|
||||||
@@ -123,7 +124,7 @@ const combinedJson = computed(() => {
|
|||||||
|
|
||||||
// ---------- 样板库 ----------
|
// ---------- 样板库 ----------
|
||||||
async function loadLib() {
|
async function loadLib() {
|
||||||
lib.value = await api<any>('get', `/api/windoor/templates?page=1&page_size=254&is_default=1&name=${keyword.value}`)
|
lib.value = (await api<any>('get', `/api/windoor/templates?page=1&page_size=254&is_default=1&name=${keyword.value}`)).data
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 加入样板 ----------
|
// ---------- 加入样板 ----------
|
||||||
@@ -155,7 +156,22 @@ function addShapesAt(detail: any, cx: number | null, cy: number | null, name: st
|
|||||||
const uid = uidSeq++
|
const uid = uidSeq++
|
||||||
const newShapes = shapesOf(detail)
|
const newShapes = shapesOf(detail)
|
||||||
if (!newShapes.length) { ElMessage.warning('该样板没有可用的几何数据'); return false }
|
if (!newShapes.length) { ElMessage.warning('该样板没有可用的几何数据'); return false }
|
||||||
const bb = bbOf(newShapes)!
|
let bb = bbOf(newShapes)!
|
||||||
|
// 等高对齐: 新单元 Y 向缩放到参考单元(首个)高度, 锚定自身底边
|
||||||
|
if (equalHeight.value && units.value.length) {
|
||||||
|
const refBb = bbOf(shapes.value.filter(s => s.__uid === units.value[0].uid))
|
||||||
|
if (refBb && bb.y1 - bb.y0 > 0) {
|
||||||
|
const f = (refBb.y1 - refBb.y0) / (bb.y1 - bb.y0)
|
||||||
|
if (Number.isFinite(f) && f > 0) {
|
||||||
|
const scY = (p: any) => { p.y = bb.y0 + (p.y - bb.y0) * f }
|
||||||
|
for (const s of newShapes) {
|
||||||
|
for (const r of (s.polygon?.polygon ?? [])) for (const seg of r) { scY(seg.ps); scY(seg.pe) }
|
||||||
|
for (const sp of (s.mm?.sp ?? [])) scY(sp.line.pt)
|
||||||
|
for (const sub of (s.sm ?? [])) if (sub.bd) { scY(sub.bd.from); scY(sub.bd.to) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let dx = 0, dy = 0
|
let dx = 0, dy = 0
|
||||||
if (cx !== null && cy !== null) {
|
if (cx !== null && cy !== null) {
|
||||||
dx = cx - (bb.x0 + bb.x1) / 2
|
dx = cx - (bb.x0 + bb.x1) / 2
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ async function openCreate() {
|
|||||||
createVisible.value = true
|
createVisible.value = true
|
||||||
const [cs, ts] = await Promise.all([
|
const [cs, ts] = await Promise.all([
|
||||||
api<any>('get', '/api/contracts?page=1&page_size=100'),
|
api<any>('get', '/api/contracts?page=1&page_size=100'),
|
||||||
api<any>('get', '/api/windoor/templates?page=1&page_size=254'),
|
api<any>('get', '/api/windoor/templates?page=1&page_size=254').then(t => (templateOptions.value = t.data)),
|
||||||
])
|
])
|
||||||
contractOptions.value = cs.contracts?.data ?? []
|
contractOptions.value = cs.contracts?.data ?? []
|
||||||
templateOptions.value = ts
|
templateOptions.value = ts
|
||||||
|
|||||||
@@ -46,11 +46,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import { api } from '../api'
|
import { api } from '../api'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
import WindoorThreed from '../components/WindoorThreed.vue'
|
import WindoorThreed from '../components/WindoorThreed.vue'
|
||||||
import RenderCanvas from '../components/RenderCanvas.vue'
|
import RenderCanvas from '../components/RenderCanvas.vue'
|
||||||
|
|
||||||
const mode = ref<'render' | 'threed'>('render')
|
const mode = ref<'render' | 'threed'>(route.path.includes('threed') ? 'threed' : 'render')
|
||||||
const templates = ref<any[]>([])
|
const templates = ref<any[]>([])
|
||||||
const templateId = ref<number | undefined>()
|
const templateId = ref<number | undefined>()
|
||||||
const drawing = ref<string>('')
|
const drawing = ref<string>('')
|
||||||
@@ -63,9 +66,10 @@ const sceneBg = computed(() => ({
|
|||||||
} as Record<string, string>)[setting.scene] ?? '#eee')
|
} as Record<string, string>)[setting.scene] ?? '#eee')
|
||||||
|
|
||||||
async function loadTemplate(tid: number | undefined) {
|
async function loadTemplate(tid: number | undefined) {
|
||||||
if (!tid) { drawing.value = ''; return }
|
if (!tid) { drawing.value = ''; templateId.value = undefined; return }
|
||||||
const t = await api<any>('get', `/api/windoor/templates/${tid}`)
|
const t = await api<any>('get', `/api/windoor/templates/${tid}`)
|
||||||
drawing.value = t.drawing_data
|
drawing.value = t.drawing_data
|
||||||
|
templateId.value = tid
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSetting() {
|
async function saveSetting() {
|
||||||
@@ -73,8 +77,10 @@ async function saveSetting() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
templates.value = await api<any>('get', '/api/windoor/templates?page=1&page_size=254')
|
templates.value = (await api<any>('get', '/api/windoor/templates?page=1&page_size=254')).data
|
||||||
Object.assign(setting, await api<any>('get', '/api/rendering-info'))
|
Object.assign(setting, await api<any>('get', '/api/rendering-info'))
|
||||||
|
// 自动选中首个样板,页面即刻有内容
|
||||||
|
if (templates.value.length && !templateId.value) await loadTemplate(templates.value[0].id)
|
||||||
})
|
})
|
||||||
|
|
||||||
function print() { window.print() }
|
function print() { window.print() }
|
||||||
|
|||||||
@@ -101,9 +101,8 @@ async function load() {
|
|||||||
try {
|
try {
|
||||||
const qs = `page=${page.value}&page_size=${page_size}&name=${name.value}&is_default=${tab.value === 'official' ? 1 : 0}${catelogId.value ? `&catelog_id=${catelogId.value}` : ''}`
|
const qs = `page=${page.value}&page_size=${page_size}&name=${name.value}&is_default=${tab.value === 'official' ? 1 : 0}${catelogId.value ? `&catelog_id=${catelogId.value}` : ''}`
|
||||||
const d = await api<any>('get', `/api/windoor/templates?${qs}`)
|
const d = await api<any>('get', `/api/windoor/templates?${qs}`)
|
||||||
rows.value = d
|
rows.value = d.data
|
||||||
// 接口无分页元数据时按返回量估计
|
total.value = d.total
|
||||||
total.value = d.length === page_size ? (page.value - 1) * page_size + d.length + 1 : (page.value - 1) * page_size + d.length
|
|
||||||
} finally { loading.value = false }
|
} finally { loading.value = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 101 KiB |
@@ -44,15 +44,21 @@ async def search_templates(
|
|||||||
conds.append(WindoorTemplate.catelog_id == catelog_id)
|
conds.append(WindoorTemplate.catelog_id == catelog_id)
|
||||||
if is_default is not None:
|
if is_default is not None:
|
||||||
conds.append(WindoorTemplate.is_default == is_default)
|
conds.append(WindoorTemplate.is_default == is_default)
|
||||||
stmt = (select(WindoorTemplate).where(*conds)
|
from sqlalchemy import func as _func
|
||||||
|
where = *conds,
|
||||||
|
total = (await db.execute(select(_func.count()).select_from(WindoorTemplate).where(*where))).scalar_one()
|
||||||
|
stmt = (select(WindoorTemplate).where(*where)
|
||||||
.order_by(WindoorTemplate.id).offset((page - 1) * page_size).limit(page_size))
|
.order_by(WindoorTemplate.id).offset((page - 1) * page_size).limit(page_size))
|
||||||
items = list((await db.execute(stmt)).scalars())
|
items = list((await db.execute(stmt)).scalars())
|
||||||
return ok(data=[{
|
return ok(data={
|
||||||
|
"total": total, "page": page, "page_size": page_size,
|
||||||
|
"data": [{
|
||||||
"id": t.id, "name": t.name, "pic_url": t.pic_url, "catelog_id": t.catelog_id,
|
"id": t.id, "name": t.name, "pic_url": t.pic_url, "catelog_id": t.catelog_id,
|
||||||
"note": t.note, "times": t.times,
|
"note": t.note, "times": t.times,
|
||||||
"low_width": t.low_width, "high_width": t.high_width,
|
"low_width": t.low_width, "high_width": t.high_width,
|
||||||
"low_height": t.low_height, "high_height": t.high_height,
|
"low_height": t.low_height, "high_height": t.high_height,
|
||||||
} for t in items])
|
} for t in items],
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@router.post("/templates")
|
@router.post("/templates")
|
||||||
|
|||||||