@ 3dn- 建设者/ 建设者
将3DN产品制造器嵌入任何商店前端. SDK加载了构建者,运行了登录回程,刷新了许可证,保存并报告每个保存. 您的站点提供了会话结束点、 登录到的结束点和它自己的登录 UI .
安装
npm install @3dn-builder/builder
无害环境管理,零运行时间依赖。 入口点 : ., (中文(简体) ), ./events (只有类型和常数), ./testing (一个假的) <threed-builder>) (中文(简体) ).
创建、挂载、销毁
import { createBuilder } from '@3dn-builder/builder';
const builder = createBuilder({
session: {
get: ({ hostProductId }) => api.getBuilderSession(hostProductId),
refreshLicense: () => api.refreshBuilderLicense(),
},
login: {
getTokens: () => api.mintBuilderTokens(), // null on 401
onRequired: (action) => showLogin(action), // 'logged-in' | 'redirected' | 'cancelled'
},
onSaved: (saved) => showSavedDesign(saved.productId),
onImagesSaved: (saved) => refreshGallery(saved.productId),
onLicenseError: (error) => reportSetupError(error.code),
onCloseRequested: () => closeModal(), // the builder already asked about unsaved changes
});
const instance = await builder.mount({ target: container, hostProductId: 'alpine-mug' });
const design = await instance.save(); // design.productId goes on the cart line
instance.destroy();
createBuilder() 触摸页面上的任何内容, 因此在服务器侧渲染时安全; 构建者加载 load() 或者说 mount()目标需要一个真正的高度.
建造者最迟会加载,除非您通过 version: '1.2.3'。每个发布的版本都保持在线状态,所以一个被绑定的版本会不断加载,但是它只在3DN API仍然接受它发送的东西的同时继续工作. 坚持住 'latest' 除非你需要等待释放.
| 方法 | 行为 |
|---|---|
load() | 可选预装,每页一次. |
mount(options) | { target, hostProductId, mode?: 'designer' | 'viewer', designId?, loader?: 'container' | 'page', externalExport?, observabilityContext? } - 比如说. |
open() | 显示隐藏挂载后的构建器 UI . |
save() | 决定: { productId, version } 保存之后,任何克隆记录, onSaved 和图像刷新。 45分超时. |
refreshSession() | 电话 session.get() 并应用结果. |
refreshLicense() | 每个挂载的建筑商共享许可证刷新一次 . |
logout() | 移除shopper 令牌, 保留许可证令牌 。 延迟登录结果被删除 . |
destroy() | 删除建构器及其收听器; 待调用 DESTROYED。 。 。 . |
loader 设定加载和许可屏幕覆盖的内容。 默认, 'container',仅覆盖 target,所以在建设者加载时,你的其余页面仍然可以使用. 过 'page' 以屏蔽整个浏览器窗口,例如当构建器作为全屏模式打开时。 对话框和图像编辑器总是覆盖整个窗口.
observabilityContext 是建造者的 observability-context:一个 JSON 对象字符串,命名您的应用程序,租户,网站,假名用户和请求,加上您的页面 sentry-trace 财务报告和审定财务报表 baggage,所以建筑商自己的错误报告 说他们在哪里发生。 在元素连接之前设定; instance.setObservabilityContext(json) 稍后替换它,例如一旦你的页面的哨兵有踪迹。 关键及其规则载于综合指南第6.3节.
控制器的方法在最近安装的构建器上起作用. 页面上有多个构建者,请联系实例 mount() 已经返回.
会议
session: {
get(request: { hostProductId: string }): Promise<BuilderSession>;
refreshLicense(): Promise<{ licenseToken: string }>;
}
interface BuilderSession {
licenseToken: string;
productId: string; // the shopper's saved copy, else the global product
viewToken?: string; // required for a private product
token?: string; // optional shopper tokens, always together
refreshToken?: string;
}
hostProductId 您的后端决定它打开的 3DN 产品 。 在服务器上保留安装密钥 : createBuilder() 拒绝任何看起来像一个, 和任何选项 命名建设者加载从.
登录
login: {
getTokens(): Promise<{ token: string; refreshToken: string } | null>; // null on 401
onRequired(action: LoginRequiredAction): Promise<'logged-in' | 'redirected' | 'cancelled'>;
onMintFailed?(): Promise<'retry' | 'cancel'>; // getTokens() threw; default 'cancel'
}
通过重定向登录: 发送 sphop 到 loginReturnPath() 登录后( 用 isSafeReturnPath()页:1 'redirected',然后在 consumeResumeMarker() 是真的; 构建者完成待定保存. hasDraft(productId) 说明是否在这个设备上保留了草稿 .
保存
onSaved?(saved: { hostProductId: string; productId: string; version: number }): void;
onImagesSaved?(saved: { hostProductId: string; productId: string; version: number }): void;
customization?: {
record(link: {
hostProductId: string;
sourceProductId: string;
savedProductId: string;
asCopy: boolean;
}): Promise<void>;
};
onSaved 每一次成功救出一次,从 save() 或者从建设者自己的保存按钮。 第一次保存 使shopper的复制: productId 然后是副本,和 onSaved 仅在副本存在后开火 customization.record() 解决。 它从不为失败的保存或失败的记录发火;下一次保存的记录再次发火.
onSaved 在产品图像(它的缩略图和相机角截图)被刷新之前。 读取图像于 onImagesSaved:一旦他们显示设计 version,在那之后的储蓄 onSaved。当图像无法更新时,它不会起火;产品保留了以前的图像. save() 仅在图像被刷新或失败后才解决,所以一个主机会在之后摧毁构建者 await save() 从不缩短上传时间。 这需要一个建筑师 发送 tdn-images-saved,用这个SDK或稍后释放的; 有更古老的钉子 version, (中文(简体) ), save() 时间流出.
一个shoper先将复制品在来源后和被保存后的名字保存下来,例如"Mug Grey – 19 October, 14:32",所以可以从一个产品中分解出几个设计.
asCopy 说抄本的意思. true:shopper要求一个("保存为副本",在保存相冲突后也提供),所以把它作为新的设计来保存原件. false:保存必须叉出一个shopper不拥有的产品,因此复制件取代了开放设计.
命令
当发布命令时,将命令的设计从后端锁定在安装密钥上:
POST {API_BASE}/plugin/products/{productId}/lock
x-installation-key: {INSTALLATION_KEY}
{ "orderRef": "1042-A", "version": 7 }
发送 productId 财务报告和审定财务报表 version 从 onSaved 你储存在车线上。 A 类 409 指该版本后再次被shoper所保存,或者已经锁定的另一订单. 锁定的设计无法再更改( 没有解锁): 它只打开可读, 下载并保存为副本, 保存失败 reason: 'locked'。要更改命令的设计,请取消命令,让shoper订购副本.
POST {API_BASE}/plugin/products/{productId}/view-only-link 返回时 { url }:在裸取取景器中打开设计的一个链接(3D中的模型,有2D / Split / 3D开关,动画滑行器,相机角度和下载打印文件,没有编辑工具),在没有登录的情况下下载其打印文件,例如用于打印行. 没有任何东西可以通过它保存或复制.
特征
features?: {
download?: boolean; // default true: Download print files (and a design area's SVG)
share?: boolean; // default true: Share a link to the design
save?: boolean; // default true: Save, Save as copy, version history and the save state
feedback?: boolean; // default true: Report a problem
};
设定 download: false 或者说 share: false 将动作隐藏在桌面和电话布局中,例如当购买者不应拿走艺术品时。 设定 save: false 对于一个不保存任何内容的试运行编辑器:shopper可以设计,在2D和3D之间切换并使用每个工具,但是没有保存,没有保存为副本,没有版本历史和保存状态; save() 仍然为主机工作。 设定 feedback: false 来隐藏“ 报告一个问题”, shopper 描述出出错之处, 3DN 接收时会话记录 。 全部显示,除非你关掉。 任何其他密钥,或者一个不是布尔的值,是 CONFIG_INVALID 错误.
错误
每一次失败都是 BuilderError数字 : code页:1 message 安全显示, retryable, (中文(简体) ), fatal和建筑师的 reason。 。 。 .
| 代码 | 何时 |
|---|---|
CONFIG_INVALID | 错误的选项, 或者一个秘密或脚本 URL 传入 . |
NOT_IN_BROWSER | load() 或者说 mount() 在安全部门改革期间. |
LOAD_FAILED | 建造者没有加载; 下一次调用重试 . |
VERSION_CONFLICT | 另一个构建器版本已经在页面上运行. |
SESSION_FAILED | session.get() 失败或返回无效会话. |
LICENSE_ERROR | 该许可证被拒绝。 使登山者死亡; onLicenseError 名为. |
LOGIN_CANCELLED | 直升机没有登录. |
SAVE_FAILED | 保存失败, 或 customization.record() 失败( e)reason: 'customization') (中文(简体) ). |
SAVE_TIMEOUT | 内部无结果 saveTimeoutMs。 。 。 . |
EXPORT_FAILED, (中文(简体) ), EXPORT_TIMEOUT | externalExport.run() 失败或超时 . |
DESTROYED | destroy() 在电话待发时跑掉了. |
A 类 SAVE_FAILED 与 reason: 'locked' 指将设计锁定的命令;不能重试,由构建者提供保存为副本.
A 类 SAVE_FAILED 与 reason: 'outdated' 指页面上的构建器比服务器更老,服务器不再使用其保存. 它不能重试;构建者向shoper提供一页重载,它加载了最新的构建者,并携带了未保存的设计可以穿过的地方.
构建器仅在被问到时保存:它的保存按钮 Ctrl/Cmd+S,它的近快提示,或者您的 save(). 在此之前,未保存的工作作为草稿保存在设备上(每个用户,从不发送到服务器).
onCloseRequested({ productId?, hasUnsavedChanges, discarded }) 直升机关了建筑工就开火 在未保存的更改中, 构建者首先询问保存 / 不保存 / 取消 : 一旦保存和图像完成, 请保存关闭 discarded: true (不要再问了),取消保留打开。 您的页面不需要自己的未保存的更改提示 .
其他回调 : onStatusChange({ status, step?, message?, error? }) (装入进度,从不保存). status: 'error' 当建设者自己的部分代码无法下载时, 也会开火; 然后, 建设者会显示一个 重新装入页面 按钮.
角质
@Component({ selector: 'app-designer', template: '<div #target style="height: 100%"></div>' })
export class DesignerComponent implements OnDestroy {
@Input({ required: true }) productRef!: string;
@ViewChild('target', { static: true }) target!: ElementRef<HTMLElement>;
private instance?: BuilderInstance;
constructor() {
afterNextRender(async () => {
const { createBuilder } = await import('@3dn-builder/builder'); // browser only
const builder = createBuilder({ session, login, onSaved: (s) => this.saved(s) });
this.instance = await builder.mount({ target: this.target.nativeElement, hostProductId: this.productRef });
});
}
ngOnDestroy() {
this.instance?.destroy();
}
}
测试您的整合
import { installFakeBuilder, findFakeBuilders } from '@3dn-builder/builder/testing';
installFakeBuilder(); // before mount(): no real builder is loaded
const instance = await builder.mount({ target, hostProductId: 'mug' });
const saving = instance.save();
await findFakeBuilders()[0].emitSavedAsClone('clone-1');
await saving; // customization.record(), onSaved and onImagesSaved have run
emitSaved() 财务报告和审定财务报表 emitSavedAsClone() 发送图像结果与构建者一样; emitImagesSaved() 财务报告和审定财务报表 emitImagesSaveFailed() 自己派一个.
开发此软件包
npx nx test builder-host
npx nx lint builder-host
npx nx build builder-host # dist/libs/builder-host
事件名称和有效载荷类型来自 @three-dn/constants 并捆绑在已公布的文件中。 从根部印出版本 package.json; 数据 publish-sdk 任务在 .github/workflows/deploy.yml 发布每期作品.