Files
Leantime/dev/开发文档.md

170 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 2D / 3D 在线查看器 · 开发文档
> 更新2026-08-21。本文覆盖 `dev/` 目录下的自研查看器(不含 2D/、3D/ 里的新迪官方案例)。
## 一、项目结构
```
dev/
├── index.html ← 统一入口:落地页 + 2D/3D 数模库 + 嵌入预览弹窗
├── library.php ← 数模库元数据接口GET 读 / POST 保存)
├── library.json ← 数模库记录(上传自动收录 + 手动增删改)
├── DWGViewer/ ← 2D 图纸查看器(.dwg / .dxf
│ ├── index.html / app.js / style.css
│ ├── js/ ← source.js(解析) → flatten.js(展平) → render.js(Canvas2D) → tools.js
│ ├── libs/dxf-parser(MIT) libs/libredwg-web(GPL-3.0, 仅打开 .dwg 时动态 import)
│ ├── samples/ ← XQD601.dwg、patent.dwg、pulley.dxf、cushion.dxf
│ └── api/ ← token.php / model.php / upload.php防下载三件套
└── STEPViewer/ ← 3D 模型查看器(.step / .stp浏览器内 WASM 直读,无云服务)
├── index.html / app.js / style.css
├── libs/three.js + occt-import-js0.0.23
├── samples/ ← as1-oc-214.stp 等
└── api/ ← 同款三件套SECRET 与 DWGViewer 不同)
```
## 二、本地开发
- 服务器python http.server 脚本(`%TEMP%\nds_server_step.py` 等),**root 指向 dev/**
端口 **8920**;日志 `%TEMP%\req_log_*.txt`
- 打开 `http://127.0.0.1:8920/`(入口)或直接开 `DWGViewer/index.html?file=samples/XQD601.dwg`
- **本地没有 PHP**:查看器会自动退回直连(判断依据是接口响应的 content-type 是否为
`application/json`——python 会把 token.php 源码当静态文件 200 发回来,只看状态码会误判);
数模库只读(增删改走 library.php 会 501 并给出明确提示)
- 改完 JS 用 `node --check app.js` 做语法检查
## 三、架构要点
**2D 数据流**`source.js`(解析+归一化libredwg 全给弧度dxf-parser 混用——统一成弧度)
`flatten.js`展平成世界坐标显示列表INSERT 递归烘焙变换,**只做一次**;缩放平移只重算
屏幕坐标 → 2.9 万实体 6ms/帧)→ `render.js`Canvas2D支持旋转/三种背景/线宽)。
**3D 数据流**:拖入/`?file=` 取到 STEP 字节 → `occt-import-js` WASM 转换 → three.js 场景
MeshPhongMaterial + Ambient/2×Directional30° 法线平滑)。零件树按 mesh 名分组,
`S.meshEntryMap` 做 mesh→树行精确映射视口点选用它不要按树顺序 first-match
**入口分发**dev/index.html
- 按扩展名路由dwg/dxf → DWGViewerstep/stp → STEPVieweriframe 加载
- 本地文件postMessageviewer-ready 握手后发 viewer-file只传 ArrayBuffer 不落地)
- 路径换算优先「查看器目录内」HEAD 探测),入口目录兜底;**选中候选后要换算成
相对查看器的路径**,否则双目录 404
- 参数透传:`embed=1``bg=` 必须显式转发给查看器(入口 URL 不会自动带过去)
**嵌入模式**`?file=路径&embed=1` 隐藏顶栏3D 另强制高精度);`bg=grey|beige|black` 定背景。
嵌入地址规则:私有目录文件用**裸文件名**`?file=xxx.enc`web 目录内文件写相对路径
`samples/xxx.stp`**`../` 逃出查看器目录的路径会被令牌通道拒绝**(防目录穿越)。
## 四、防下载令牌三件套PHP 部署后自动生效)
```
查看器 fetch → api/token.php同源校验 + HMAC 5 分钟令牌)
→ api/model.php验令牌 → .enc XOR 解密 / 原文件原样 → gzip 传输)
上传POST api/upload.phpX-Filename 头;客户端 gzip → 服务端再 gzip → XOR 加密落盘)
```
- `SECRET`:同一查看器内 token/model/upload 三处一致DWG 与 STEP 的 SECRET 不同
- **私有目录**`$PRIVATE_DIR = '/volume1/stepviewer_private'`DWG 为 dwgviewer_private——
模型存 web 目录外web 里根本没有文件可下载model.php 按 `basename($f)` 在私有目录找,
**找不到时退回 web 目录**(留在 web 目录里的样例仍可按原路径用,但可直链下载)
- 落盘名STEP = 原名+8 位随机 hex+.encDWG = 纯随机 hex.enc原文件名藏加密体内
model.php 用 `X-Filename` 头带回)
- 客户端 `apiRequest`STEP`.php` 必须拼在 `?` 之前(曾经拼成
`api/token?f=xxx.stp.php` 导致 PHP 部署下令牌流程整体失效——血泪教训)
- 接口未部署自动退回直连;接口在跑但拒绝时明确报错,不悄悄回退
- 网页端无法绝对防抓包,此方案挡的是直接下载、批量爬取、链接外传猜名
## 五、数模库(首页库表)
- 记录结构:`{path, name, type(2d|3d), note, time}``library.json`(入口目录)
- **path=链接核心)只在新增时可填、之后锁定 🔒**name/type/note 可编辑;删除只删记录
- `library.php`GET 返回全量(静态 JSON 也能读,无 PHP 只读可用POST 整体保存
清洗path 拒绝 `..`、限制字符长度LOCK_EX 写文件Sec-Fetch-Site 弱校验
- **自动收录**:两个查看器 upload.php 成功后调 `add_to_library()``../../library.json`
同 path 更新、否则插最前),并 postMessage `library-updated` 让入口刷新
- 库表功能:打开(按 type 进对应查看器、复制链接embed=1 完整地址)、预览
(弹窗内 iframe 以 embed=1 直载查看器 + 复制 iframe 代码)、编辑、删除、新增、
搜索(名称/路径/备注)、类型筛选
## 六、手机适配≤768px
- 2D 图层 / 3D 模型树:桌面**默认显示**(按钮 toggle 隐藏);手机变**抽屉**
(默认隐藏,点「图层」/「结构树」弹出,抽屉内 ✕ 或点空白处收起)
- 顶栏收起 logo/文件名;底部工具栏横向滚动、图标缩小;浮动面板限宽可滚动
- 首页:库表外层 `overflow-x:auto` 横向滚动、预览弹窗全屏
- 实现要点:
- 抽屉 = `body.tree-open` 类 + `transform: translateX(-100%→0)` + transition
- **「点外部收起」的 document click 监听会把打开按钮的那次点击也当外部点击**——
按钮 handler 必须 `stopPropagation()`
- **媒体查询必须写在被覆盖规则之后**(同特异性后写生效;放前面会被基础规则吃掉)
- STEP 的 `#viewport` left 是内联样式(会压过 CSS手机态要 `style.left=''`
## 七、测试
playwright-core + 系统 Edgeheadless脚本在 `%TEMP%\nds3dtest\`
```js
const { chromium } = require('playwright-core')
const browser = await chromium.launch({
executablePath: 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
headless: true, args: ['--enable-unsafe-swiftshader', '--disable-gpu', '--ignore-certificate-errors'],
})
```
关键脚本:`embed_matrix3.js`(嵌入矩阵)、`dwg_php_sim.js` / route 拦截模拟 PHP 令牌流程、
`prod_final_all.js`(生产回归)、`mobile_test2.js`(手机行为)、`diag_*.js`(诊断)。
测试坑:
- 加载断点别用 `#fileName` 默认文本(非空,会提前 break 假失败)——等含「实体」/「加载完成」
或 errBox 出现
- 嵌入模式下顶栏隐藏,`#btnEmbed` 点不到——测嵌入面板用非嵌入模式
- playwright 点击偶报「tree 拦截指针事件」而 `elementFromPoint` 实际命中按钮——加载完
等 1.5s 再点 + fallback 坐标点击
- headless 全新加载测不出缓存问题——**线上用户复现的白屏往往是缓存混版**(见下)
## 八、⚠️ 缓存版本号(每次改文件必做)
两个查看器的 index.html 用固定 `?v=` 引用 app.js/style.css。**任何一轮修改都必须升级
版本号**当前DWG `app.js?v=2`/`style.css?v=2`STEP `app.js?v=5`/`style.css?v=5`
否则用户浏览器缓存新旧混合(旧 index.html + 新 app.js 引用新 DOM 元素 → 模块崩溃白屏)。
跨文件 DOM 引用(如 `#btnTreeClose`)一律加 null 容错。
## 九、已踩过的坑(速查)
**2D**
- 大半径圆弧包围盒按**实际弧段**算不能按整圆R=49000 的过渡弧会把图纸撑大 20 倍)
- HATCH 的 offset(DXF 45/46) 是已旋转到世界系的位移向量ANSI31 等预定义图案要内置图案表现场展开
- 文档内单位一律弧度;`$INSUNITS` 国内图纸常年默认值,只认显式公制,默认 mm
- dxf-parser 的 dist 是无扩展名相对 import + 依赖 loglevelimportmap 指到本地 shim
- libredwg 的块表在 `db.tables.BLOCK_RECORD.entries[]`,模型空间取 `*Model_Space`
- 该 wasm 构建禁用了 DXF 读写(错误码 2048alpha:false canvas 要先画一次白底
**3D**
- occt 0.0.23 的 `attributes.position/index``{array}` 包装(转 TypedArray树节点字段是
`meshes``brep_faces``{first,last,color}[]`
- 顶点颜色必须 `Color.setRGB(..., THREE.SRGBColorSpace)`,否则模型洗白
- ⚠️ 曾试 PBR(MeshStandard+RoomEnvironment+ACES) 被用户否掉(上色被洗淡),**不要再加
色调映射/环境反射**;过暗 CAD 面色(<0.3自动提亮到 0.35 保色相
- 剖切平面必须按模型包围盒中心重算固定原点切不到
**PHP/接口**
- PHP 没执行时 token.php 返回源码 200——判部署看 content-type 是否 JSON
- 群晖 Web Station PHP **按「网站门户」逐个启用**新域名不会自动继承
- 同源校验Sec-Fetch-Site 优先不受父页面 Referrer-Policy 影响跨站 iframe 嵌入可用
Referer 兜底直链/curl 两者都过不了
**许可**DWGViewer libredwg-web GPL-3.0前端分发有传染性商用需评估换服务端
dwg2dxf / ODA SDK 只需替换 `source.js` `loadDwg()`
## 十、部署(群晖 NAS Web Station生产 3d.ruiguo.wang
1. dev 内容放站点根目录3d.ruiguo.wang 根即入口本地 FTP 目录与生产双向同步
2. Web Station 网站门户 该站点 编辑 **PHP 选 PHP 8.x**不选=纯静态返回源码)
3. File Station web 目录外建 `/volume1/stepviewer_private``/volume1/dwgviewer_private`
`http` 用户组读写权限
4. 两个查看器 api/ `$SECRET` 已设为随机值同查看器三处一致
5. nginx `.htaccess` 不生效——防下载靠私有目录方案不需要 nginx 规则
6. 验证清单token.php 返回 JSON上传返回裸文件名直开 `.stp/.dwg` 已不在 web 目录
嵌入链接能打开库表增删改持久化
> 另外 `ftp.ruiguo.wang/上海新迪3D/dev/` 也是同一套文件PHP 未启用),
> 上传的加密模型在那里打不开属预期(只读回退),正式使用请走 3d.ruiguo.wang。