printbench 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -3
- package/dist/printbench.css +1 -0
- package/dist/printbench.d.ts +29 -0
- package/dist/printbench.js +5990 -0
- package/package.json +51 -4
package/README.md
CHANGED
|
@@ -1,3 +1,145 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# PrintBench
|
|
2
|
+
|
|
3
|
+
纯浏览器 3D 打印模型工作台(Vue 3 + three.js + Manifold)。在浏览器内解析、体检、修复网格,再导出给切片软件。几何处理在本地完成,审核报告默认不上传。
|
|
4
|
+
|
|
5
|
+
可作为 **Vue 3 组件** 嵌入其它项目。需现代浏览器(WebGL、Web Worker、WASM)。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install printbench
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peer 依赖(宿主需自行安装,大版本对齐):
|
|
14
|
+
|
|
15
|
+
- `vue` ^3.5
|
|
16
|
+
- `pinia` ^3
|
|
17
|
+
|
|
18
|
+
`three`、`manifold-3d` 等运行时依赖会随本包安装。请在宿主中注册 Pinia(`app.use(createPinia())`)。
|
|
19
|
+
|
|
20
|
+
### Vite 宿主
|
|
21
|
+
|
|
22
|
+
开发时建议排除预打包:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// vite.config.ts
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
optimizeDeps: {
|
|
28
|
+
exclude: ['printbench', 'manifold-3d'],
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
改配置后删除 `node_modules/.vite` 再重启 `dev`。
|
|
34
|
+
|
|
35
|
+
## 使用
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { createApp } from 'vue'
|
|
39
|
+
import { createPinia } from 'pinia'
|
|
40
|
+
import { PrintBench } from 'printbench'
|
|
41
|
+
import 'printbench/style.css'
|
|
42
|
+
import App from './App.vue'
|
|
43
|
+
|
|
44
|
+
createApp(App).use(createPinia()).mount('#app')
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```vue
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import { PrintBench } from 'printbench'
|
|
50
|
+
import type { PrintBenchExportedPayload } from 'printbench'
|
|
51
|
+
import 'printbench/style.css'
|
|
52
|
+
|
|
53
|
+
function onExported(payload: PrintBenchExportedPayload) {
|
|
54
|
+
// payload.blob / payload.fileName / payload.format / payload.unit
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<PrintBench
|
|
60
|
+
height="100vh"
|
|
61
|
+
upload-url="https://api.example.com/api/printbench/exports"
|
|
62
|
+
:model-url="modelUrl"
|
|
63
|
+
:custom-export="false"
|
|
64
|
+
@ready="onReady"
|
|
65
|
+
@error="onError"
|
|
66
|
+
@exported="onExported"
|
|
67
|
+
@report="onReport"
|
|
68
|
+
/>
|
|
69
|
+
</template>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Props
|
|
73
|
+
|
|
74
|
+
| 属性 | 类型 | 默认 | 说明 |
|
|
75
|
+
|------|------|------|------|
|
|
76
|
+
| `height` | `string` | `'100%'` | 根容器高度 |
|
|
77
|
+
| `modelUrl` | `string` | — | 挂载后按 URL 自动导入模型(需 CORS) |
|
|
78
|
+
| `uploadUrl` | `string` | — | 「一键上传」的 POST 地址;未设则提示接口未配置 |
|
|
79
|
+
| `customExport` | `boolean` | `false` | 为 `true` 时,下载 / 一键上传只生成文件并触发 `exported`,不本地下载、不 POST |
|
|
80
|
+
|
|
81
|
+
### Events
|
|
82
|
+
|
|
83
|
+
| 事件 | 负载 | 说明 |
|
|
84
|
+
|------|------|------|
|
|
85
|
+
| `ready` | — | 几何内核就绪 |
|
|
86
|
+
| `error` | `string` | 可展示错误 |
|
|
87
|
+
| `exported` | `{ format, fileName, blob, unit, axis? }` | 导出完成(下载、上传或 `customExport` 均会触发) |
|
|
88
|
+
| `report` | 审核报告对象 | 下载 JSON / HTML 审核报告时触发 |
|
|
89
|
+
|
|
90
|
+
`exported` 的 `format` 为 `'stl' | '3mf'`;多零件 STL 时 `fileName` 为 zip,`blob` 为打包后的文件。
|
|
91
|
+
|
|
92
|
+
「一键上传」以 `multipart/form-data` POST,字段为 `file` 与 `meta`(JSON:fileName、format、unit 等)。
|
|
93
|
+
|
|
94
|
+
## 工作台能力
|
|
95
|
+
|
|
96
|
+
推荐流程:导入模型并确认单位 → 看右侧诊断,必要时一键修复 → 导出 STL / 3MF 给切片软件(坐标按毫米或导出时所选单位解释)。
|
|
97
|
+
|
|
98
|
+
有红色阻断项时无法导出。黄色警告可继续,但可能影响打印质量。
|
|
99
|
+
|
|
100
|
+
### 导入
|
|
101
|
+
|
|
102
|
+
支持 **GLB / GLTF / STL / OBJ / 3MF**,以及含上述模型的 **ZIP**,单文件约 **500MB**。可从本地选择、拖入画布、粘贴文件,或填写 http(s) 直链(需 CORS;GitHub `blob` 会改成 raw)。
|
|
103
|
+
|
|
104
|
+
导入后按毫米预览包围盒,再选真实单位(mm / cm / m)。3MF 会读文件内 model unit。动画、材质不进入打印网格。
|
|
105
|
+
|
|
106
|
+
### 界面
|
|
107
|
+
|
|
108
|
+
| 区域 | 作用 |
|
|
109
|
+
|------|------|
|
|
110
|
+
| 顶栏 | 导入、撤销/重做、修复、导出、显示模式、测量、草稿、设置、帮助 |
|
|
111
|
+
| 左栏 | 零件树与编辑(布尔、拆件、切底座等),可收起 |
|
|
112
|
+
| 画布 | 轨道旋转缩放;双击或 **F** 自适应;**复位视角** 回默认相机 |
|
|
113
|
+
| 右栏 | 诊断与测量,可收起 |
|
|
114
|
+
| 底栏 | 包围盒、内部单位 mm、体积、文件名 |
|
|
115
|
+
|
|
116
|
+
### 导出
|
|
117
|
+
|
|
118
|
+
- **STL**:二进制;无单位字段,切片软件需与导出单位一致(推荐毫米)。单零件 `.stl`,多零件 `.zip`(每件一个 STL)
|
|
119
|
+
- **3MF**:自带毫米单位;每零件单独 object
|
|
120
|
+
|
|
121
|
+
### 快捷键
|
|
122
|
+
|
|
123
|
+
| 按键 | 作用 |
|
|
124
|
+
|------|------|
|
|
125
|
+
| Ctrl+Z / Ctrl+Y | 撤销 / 重做 |
|
|
126
|
+
| Ctrl+S | 保存自动草稿(IndexedDB) |
|
|
127
|
+
| 1 / 2 / 3 | 移动 / 旋转 / 缩放 |
|
|
128
|
+
| Esc | 取消手柄、预览、测量 |
|
|
129
|
+
| F | 按模型自适应视角 |
|
|
130
|
+
| Shift | 变换吸附 |
|
|
131
|
+
| Ctrl | 多选、框选 |
|
|
132
|
+
|
|
133
|
+
草稿保存在浏览器本地。设置含默认导入单位、默认导出格式、网格/灯光、最小壁厚。
|
|
134
|
+
|
|
135
|
+
## 开发本仓库
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm install
|
|
139
|
+
npm run dev # 完整站,路径 /print-bench/
|
|
140
|
+
npm run build # SPA 静态资源(写入 dist/)
|
|
141
|
+
npm run build:lib # npm 库产物(写入 dist/,与 SPA 互斥覆盖)
|
|
142
|
+
npm test
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
发布前请使用 `npm run build:lib`,不要用 SPA 的 `build` 覆盖 `dist/`。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg: #0e1116;--panel: #171b22;--panel-2: #1e242e;--border: #2c3440;--text: #e8edf4;--muted: #8b95a5;--accent: #5b8def;--danger: #ff5c5c;--warn: #f0b429;--ok: #3dcc7a;--shadow: 0 8px 32px rgba(0, 0, 0, .35);font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif}.printbench-root,.printbench-root *{box-sizing:border-box}.printbench-root{height:100%;min-height:0;background:var(--bg);color:var(--text);color-scheme:dark;font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif}.printbench-root button,.printbench-root input,.printbench-root select,.printbench-root textarea{font:inherit;color:inherit;color-scheme:dark}.printbench-root select option{background:var(--panel-2);color:var(--text)}.workbench{display:grid;grid-template-rows:48px 1fr 28px;grid-template-columns:292px 1fr 300px;height:100%}.workbench.no-left{grid-template-columns:36px 1fr 300px}.workbench.no-right{grid-template-columns:292px 1fr 36px}.workbench.no-left.no-right{grid-template-columns:36px 1fr 36px}.topbar,.statusbar{grid-column:1 / -1}.panel{background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.panel.right{border-right:none;border-left:1px solid var(--border)}.panel-hd{display:flex;align-items:center;gap:6px;padding:10px 12px;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);border-bottom:1px solid var(--border)}.panel-hd .ttl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.panel-toggle{flex:none;width:24px;height:24px;padding:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;color:var(--text);cursor:pointer;line-height:1;font-size:16px}.panel-toggle:hover{border-color:var(--accent);color:var(--accent)}.panel.collapsed .panel-hd{flex-direction:column;height:100%;padding:8px 4px;border-bottom:none;justify-content:flex-start}.panel.collapsed .panel-toggle{order:-1}.panel.collapsed .ttl{writing-mode:vertical-rl;flex:none;letter-spacing:.2em}.panel-bd{padding:10px;overflow:auto;flex:1;display:flex;flex-direction:column;gap:8px}.topbar{display:flex;align-items:center;gap:8px;padding:0 10px;background:var(--panel);border-bottom:1px solid var(--border)}.brand{font-weight:700;margin-right:12px}.brand span{color:var(--accent)}.btn{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 10px;cursor:pointer}.btn:hover{border-color:var(--accent)}.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn.danger{border-color:var(--danger);color:var(--danger)}.btn:disabled{opacity:.45;cursor:not-allowed}.canvas-wrap{position:relative;min-width:0;min-height:0;background:#12151c}.canvas-wrap canvas{width:100%;height:100%;display:block}.empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;color:var(--muted);text-align:center}.empty h2{color:var(--text);margin:0 0 8px;font-weight:600}.steps{display:flex;gap:18px;margin-top:16px;font-size:13px}.steps b{color:var(--accent)}.tree{display:flex;flex-direction:column;gap:2px;background:var(--panel-2);border:1px solid var(--border);border-radius:8px;padding:4px;min-height:120px;overflow-y:auto}.tree-empty{margin:0;padding:14px 8px;text-align:center;font-size:12px;color:var(--muted)}.item{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer}.item:hover{background:#5b8def14}.item.active{background:#263044}.item .idx{flex:none;width:18px;font-size:11px;color:var(--muted);text-align:right}.item .grow{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.item input[type=checkbox]{accent-color:var(--accent);margin:0}.hint{margin:0;font-size:11px;line-height:1.45;color:var(--muted)}.sec{margin:4px 0 0;padding:10px 0 2px;border-top:1px solid var(--border);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}.btn-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.btn-grid .btn,.btn-row .btn{width:100%;font-size:12px;padding:6px 8px}.btn-grid select{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:6px 8px;font-size:12px}.btn-row{display:flex;gap:6px}.rename{display:grid;grid-template-columns:1fr auto;gap:6px}.rename input{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px}.xyz{display:grid;grid-template-columns:48px 1fr 1fr 1fr;gap:4px;align-items:center;font-size:12px}.xyz>span{color:var(--muted)}.xyz input,.xyz select{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 5px;color:var(--text);color-scheme:dark}.xyz input{text-align:center}.xyz select{grid-column:2 / -1}.xyz-hd{display:grid;grid-template-columns:48px 1fr 1fr 1fr;gap:4px;font-size:10px;color:var(--muted);text-align:center;margin-bottom:-2px}.stack{display:flex;flex-direction:column;gap:6px}.stack select,.stack input[type=number]{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px}.check{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}.diag{border-left:3px solid var(--border);padding:8px 10px;margin-bottom:8px;background:var(--panel-2);border-radius:0 6px 6px 0;cursor:pointer}.diag.red{border-color:var(--danger)}.diag.yellow{border-color:var(--warn)}.diag.green{border-color:var(--ok)}.diag h4{margin:0 0 4px;font-size:13px}.diag p{margin:0;font-size:12px;color:var(--muted)}.statusbar{display:flex;align-items:center;gap:16px;padding:0 12px;font-size:12px;color:var(--muted);background:var(--panel);border-top:1px solid var(--border)}.overlay{position:fixed;inset:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:40}.dialog{width:min(480px,92vw);background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:18px}.dialog h3{margin:0 0 10px}.dialog .row{margin:10px 0;display:flex;gap:8px;align-items:center}.dialog select{background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:5px 8px;color-scheme:dark}.dialog select option{background:var(--panel-2);color:var(--text)}.dialog .actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.dialog.help{width:min(640px,94vw);max-height:min(82vh,720px);overflow:auto}.dialog.help p{font-size:13px;line-height:1.55}.dialog .url-input{width:100%;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:8px 10px}.toasts{position:fixed;right:16px;bottom:40px;display:flex;flex-direction:column;gap:8px;z-index:50}.toast{padding:10px 14px;border-radius:8px;background:var(--panel-2);border:1px solid var(--border)}.toast.ok{border-color:var(--ok)}.toast.fail{border-color:var(--danger)}.busy{position:absolute;inset:0;background:#0e11168c;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:5}.bar{width:180px;height:4px;background:#2a303c;border-radius:4px;overflow:hidden}.bar i{display:block;height:100%;width:40%;background:var(--accent);animation:slide 1s infinite}@keyframes slide{0%{transform:translate(-100%)}to{transform:translate(280%)}}.field{display:flex;align-items:center;gap:8px;margin:6px 0;font-size:13px}.field input,.field select{background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 6px;width:90px}.tour{position:absolute;left:50%;top:72px;transform:translate(-50%);background:var(--panel);border:1px solid var(--accent);border-radius:10px;padding:14px 16px;z-index:8;width:360px}.preview-tag{position:absolute;top:10px;left:10px;background:#1d3a24;color:#9dffb0;padding:4px 8px;border-radius:4px;font-size:12px}.marquee{position:absolute;border:1px dashed var(--accent);background:#5b8def1f;pointer-events:none}.ctx{position:fixed;z-index:60;background:var(--panel);border:1px solid var(--border);border-radius:6px;min-width:140px;padding:4px}.ctx button{display:block;width:100%;text-align:left;background:none;border:none;padding:6px 8px;cursor:pointer}.ctx button:hover{background:var(--panel-2)}.clip-slider{position:absolute;top:12px;left:50%;transform:translate(-50%);background:#141820e0;padding:8px 12px;border-radius:8px;font-size:12px;display:flex;align-items:center;gap:8px;z-index:4}.clip-slider input{width:180px}.diag.active{outline:1px solid var(--accent)}.live-read{position:absolute;left:12px;bottom:12px;background:#141820d9;padding:6px 10px;border-radius:6px;font-size:13px;color:#ffe08a}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DefineComponent } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface PrintBenchExportedPayload {
|
|
4
|
+
format: 'stl' | '3mf'
|
|
5
|
+
fileName: string
|
|
6
|
+
blob: Blob
|
|
7
|
+
unit: 'mm' | 'cm' | 'm'
|
|
8
|
+
axis?: 'y-up' | 'z-up'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ReviewReport {
|
|
12
|
+
generatedAt: string
|
|
13
|
+
fileName: string
|
|
14
|
+
units: string
|
|
15
|
+
printable?: boolean
|
|
16
|
+
[key: string]: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PrintBenchProps {
|
|
20
|
+
uploadUrl?: string
|
|
21
|
+
modelUrl?: string
|
|
22
|
+
height?: string
|
|
23
|
+
customExport?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare const PrintBench: DefineComponent<PrintBenchProps>
|
|
27
|
+
export { PrintBench }
|
|
28
|
+
export default PrintBench
|
|
29
|
+
export type { ReviewReport }
|