唯杰地图 logo唯杰地图
VJMAP
EN

server

vjcad/references/file/server.md 7.4 KB172 行
原文

本文回答:服务端打开 / 保存的核心是 DrawingManagerService:drawingManager.openDrawing(params) → Engine.view.openDbDoc(file, data) + 设 currentDoc.serverSource + setOriginalJson(json);保存 = 把 currentDoc.toDb() 与 originalJson 一起交给 drawingManager.saveDrawing(...)——diff 在前端用 WASM 计算后只上传 patch,依赖 parentId 检测协作冲突。手写保存要处理 no_change / idMapping / conflict 三种结果,推荐直接用 SAVESERVER。

一、最小代码:从服务端打开

import { DrawingManagerService, Engine } from 'vjcad';

const drawingManager = new DrawingManagerService();

const result = await drawingManager.openDrawing({
  type: 'imports',           // 'imports' | 'designs'
  mapid: 'example_map',
  version: 'v1',
  branch: 'main',
  patchId: 'base',           // 可省略 = 最新
  readOnly: false
});

if (result.success) {
  const file = new File(
    [result.webcadJson],
    'drawing.webcad',
    { type: 'application/json' }
  );
  await Engine.view.openDbDoc(file, result.webcadData);

  // 关键:保存来源信息(后续保存依赖)
  Engine.currentDoc.serverSource = {
    type: 'imports',
    mapid: 'example_map',
    version: 'v1',
    branchName: 'main',
    lastPatchId: result.latestPatchId || 'base'
  };
  await Engine.currentDoc.setOriginalJson(result.webcadJson);
}

MCP 参考:vjcad/doc/file/file-operations.md、vjcad/example/file/01openfromserver.js。

二、IOpenDrawingParams 表

参数 类型 必填 说明
type 'imports'|'designs' 是 图纸类型
mapid string imports必填 图纸 ID
version string imports必填 版本号
designPath string designs必填 设计图路径
branch string 否 默认 main
patchId string 否 默认最新
readOnly boolean 否 是否只读
clipbounds [minX, minY, maxX, maxY] 否 裁剪范围
editAreas Array<{minX, minY, maxX, maxY}> 否 多瓦片编辑区
editLayers string[] 否 编辑图层名

三、IOpenDrawingResult 表

属性 类型 说明
success boolean 是否成功
error string 错误信息
webcadJson string 解压的 webcad JSON
webcadData any 解析后对象
latestPatchId string 最新 Patch ID
isReadOnly boolean 是否只读

MCP 参考:vjcad/doc/file/file-operations.md。

四、保存到服务端(增量)

const currentJson  = JSON.stringify(Engine.currentDoc.toDb());
const originalJson = await Engine.currentDoc.getOriginalJson();
const src          = Engine.currentDoc.serverSource;

const r = await drawingManager.saveDrawing({
  type: src.type,
  mapid: src.mapid,
  version: src.version,
  branchName: src.branchName,
  originalJson,                 // 用于 diff 计算
  currentJson,
  parentId: src.lastPatchId,    // 冲突检测
  drawingName: '我的图纸',
  author: '作者',
  remark: '修改说明'
});

if (r.status && r.patchId === 'no_change') {
  // 无变化:什么都不更新(别把 'no_change' 当成下一次的 parentId)
} else if (r.status) {
  // 内置 SAVESERVER 的做法:先清实体 _modified 标记(GROUP 子实体需递归)再重新序列化作为新基准
  for (const block of Engine.currentDoc.blocks.items) block.items.forEach(e => e.clearModifiedForDiff?.());
  let cleanJson = JSON.stringify(Engine.currentDoc.toDb());
  if (r.idMapping && Object.keys(r.idMapping).length) {
    // 服务端给新增实体重分配了 id({ oldId: newId }):须同步更新本地实体 id 与 cleanJson 后再存基准
  }
  await Engine.currentDoc.setOriginalJson(cleanJson);
  Engine.currentDoc.serverSource.lastPatchId = r.patchId;
} else if (r.conflict?.hasConflict) {
  // 并发冲突:按 r.conflict.conflictingEntities / conflictingLayers 处理后,带 conflictResolution 重新 saveDrawing
} else {
  console.error(r.error, r.errorCode);
}

返回值 ISaveWebcadPatchResponse:{status, patchId?, branch?, designPath?, isNew?, error?, errorCode?, idMapping?, conflict?}。无变化时返回 {status: true, patchId: 'no_change'};idMapping 与冲突处理细节较多,推荐直接用 SAVESERVER 命令(内置处理了上述全部分支并同步本地缓存)。

MCP 参考:vjcad/doc/file/file-operations.md、vjcad/example/file/03saveserver.js。

五、ISaveDrawingParams 表(保存)

参数 类型 必填 说明
type 'imports'|'designs' 是 图纸类型
mapid string imports必填 —
version string imports必填 —
designPath string designs可选 空 = 新建
branchName string 否 默认 main
originalJson string 否 用于 diff
currentJson string 是 当前 webcad JSON
parentId string 否 父 Patch(冲突检测)
drawingName string 否 图纸名
author string 否 作者
remark string 否 备注

六、增量保存特点

  • 只保存修改部分:diff 在浏览器端用 WASM createWebcadDiff(originalJson, currentJson) 计算,只上传 patch(超阈值再压缩),节省带宽 / 存储。
  • 缺少 originalJson 时走整图分支:把 currentJson 作为 vcadData 整图上传,不带 parentId(失去冲突检测),等同新建保存。
  • 支持版本历史:每次保存生成新 patch。
  • 支持冲突检测:通过 parentId 检测并发修改。

MCP 参考:vjcad/doc/file/file-operations.md。

七、命令版

await Engine.editor.executerWithOp('OPENFROMSERVER');   // 弹出图纸浏览器
await Engine.editor.executerWithOp('SAVESERVER');       // 增量保存

八、图纸列表查询

const all = await drawingManager.listAllDrawings();
console.log('后台 DWG:', all.serverMaps);
console.log('已导入:', all.imports);
console.log('设计图:', all.designs);

const drafts = await drawingManager.listWebcadDraws({
  mapid: 'example_map',
  version: 'v1'
});

MCP 参考:vjcad/doc/file/file-operations.md。

⚠️ 分支族方法名的双宿主分工:DrawingManagerService 上是友好名 createBranch/deleteBranch/mergeBranch/listBranches/deleteDrawing;底层 Service 类(new vjcad.Service(url, token),负责原始 HTTP 调用)上才是 createWebcadBranch/deleteWebcadBranch/mergeWebcadBranch/deleteWebcadDraw。listWebcadDraws 两个宿主都有。在 drawingManager 上找 createWebcadBranch 会得到 undefined(反之亦然),跨文档抄代码时注意名字前缀。

反例

  • 打开后没有设 currentDoc.serverSource:SAVESERVER 时找不到归属,保存失败。
  • 没 setOriginalJson:getOriginalJson() 为空,saveDrawing 走整图 vcadData 分支——不做增量 diff、不带 parentId,失去冲突检测。
  • 保存成功后无条件 lastPatchId = r.patchId:无变化时 patchId 是 'no_change',下次会拿它当 parentId。
  • 不传 parentId:多人协作时数据相互覆盖。

参考(官方文档 MCP)

  • 文档:vjcad/doc/file/file-operations.md
  • 示例:vjcad/example/file/01openfromserver.js、03saveserver.js