唯杰地图 logo唯杰地图
VJMAP
EN

文件操作

vjcad/references/quickref/file-operations.md 11.9 KB403 行
原文

打开、保存、导入导出图纸的完整 API 指南。

概览

WebCAD 支持多种文件操作方式:

操作 命令方式 API 方式
从服务端打开 OPENFROMSERVER DrawingManagerService.openDrawing()
保存到服务端 SAVESERVER DrawingManagerService.saveDrawing()
从本地缓存打开 OPENFROMLOCAL localService.loadDrawingById()
保存到本地缓存 SAVELOCAL localService.saveDrawing()
导入 DWG IMPORTDWG service.uploadMap() + openDrawing()
导出 DWG EXPORTDWG drawingManager.exportToDwg()
快速保存 QSAVE 下载 .vcad 文件(WASM 压缩)
另存为 SAVEAS 下载 .vcad 文件(WASM 压缩)

命令方式(简单场景)

const { Engine } = vjcad;

// 从服务端打开(弹出图纸浏览器)
await Engine.editor.executerWithOp('OPENFROMSERVER');

// 保存到服务端
await Engine.editor.executerWithOp('SAVESERVER');

// 从本地缓存打开
await Engine.editor.executerWithOp('OPENFROMLOCAL');

// 保存到本地缓存
await Engine.editor.executerWithOp('SAVELOCAL');

// 导入 DWG(弹出文件选择器)
await Engine.editor.executerWithOp('IMPORTDWG');

// 导出为 DWG
await Engine.editor.executerWithOp('EXPORTDWG');

// 快速保存(下载 .vcad 文件)
await Engine.editor.executerWithOp('QSAVE');

// 另存为(可指定文件名)
await Engine.editor.executerWithOp('SAVEAS');

服务端文件操作 API

DrawingManagerService

const { DrawingManagerService, Engine } = vjcad;

const drawingManager = new DrawingManagerService();

从服务端打开图纸

// 打开指定图纸
const openResult = await drawingManager.openDrawing({
    type: 'imports',           // 类型:'imports' | 'designs'
    mapid: 'drawing-123',      // 图纸ID
    version: 'v1',             // 版本号
    branch: 'main',            // 分支名称
    // patchId 可选:不传 = 该分支最新 patch;传 'base' = 原始导入版本(此时 latestPatchId 也回传 'base',而非分支最新)
    // readOnly: true 只会让返回值 isReadOnly=true,并不会只读加载——只读要给 openDbDoc 传第 3 参
});

if (!openResult.success) {
    console.error(`打开失败: ${openResult.error}`);
    return;
}

// 加载到编辑器
const webcadData = openResult.webcadData;
const jsonString = openResult.webcadJson;
const docName = `drawing-123_v1_main`;
const virtualFile = new File([jsonString], docName, { type: 'application/json' });
await Engine.view.openDbDoc(virtualFile, webcadData);   // 第 3 参 true = 只读加载(模型空间实体只渲染到只读层,不进 items,不可选不可改)

// 保存来源信息(用于后续保存)——openDbDoc 不会设置它,且 setOriginalJson 在没有 serverSource 时直接返回 false
Engine.currentDoc.serverSource = {
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branchName: 'main',
    lastPatchId: openResult.latestPatchId || 'base'
};

// 保存原始数据(用于增量保存时的 diff 计算)
await Engine.currentDoc.setOriginalJson(openResult.webcadJson);

openDrawing 其他可选参数:clipbounds: [x1, y1, x2, y2] + selByCrossing(只取范围内实体,默认完全包含、true 为相交)、editAreas / editLayers(瓦片编辑区域/图层恢复用)、designPath(type:'designs' 时必填);editableFilter 与 readOnly 一样只影响返回的 isReadOnly。

保存到服务端

// 获取当前文档数据
const currentDoc = Engine.currentDoc;
const currentJson = JSON.stringify(currentDoc.toDb());
const originalJson = await currentDoc.getOriginalJson();
const serverSource = currentDoc.serverSource;

// 增量保存
const saveResult = await drawingManager.saveDrawing({
    type: serverSource.type,
    mapid: serverSource.mapid,
    version: serverSource.version,
    branchName: serverSource.branchName,
    originalJson: originalJson,        // 原始数据
    currentJson: currentJson,          // 当前数据
    parentId: serverSource.lastPatchId,
    drawingName: '我的图纸',
    author: '作者名',
    remark: '修改说明'
});

if (saveResult.status) {
    if (saveResult.patchId === 'no_change') {
        console.log('没有变化,未生成 patch');   // 不要把 'no_change' 写进 lastPatchId
    } else {
        console.log(`保存成功! Patch ID: ${saveResult.patchId}`);
        // saveResult.idMapping({旧id: 新id})非空 = 服务器给新增实体重分配了 ID,须先按映射改本地实体 id
        // 再更新原始数据(SAVESERVER 命令内部会做;手写 API 时可改用该命令,或保存后重新 openDrawing)
        await currentDoc.setOriginalJson(currentJson);
        currentDoc.serverSource.lastPatchId = saveResult.patchId;
        Engine.undoManager.saveModified();      // 清除文档"已修改"标记(SAVESERVER 也会调用)
    }
}

图纸类型说明

类型 说明
imports 已导入的 DWG 图纸,支持增量 patch
designs 设计图,从零创建的图纸

本地缓存操作 API

本地缓存使用 IndexedDB 存储,支持离线编辑。

获取本地存储服务

const { getLocalStorageService, Engine } = vjcad;

const localService = getLocalStorageService();

保存到本地缓存

const currentDoc = Engine.currentDoc;
const currentJson = JSON.stringify(currentDoc.toDb());

// serverSource 必填(内部读 serverSource.type 生成 key;undefined 会被捕获后返回 success:false)
// 新建图纸没有 serverSource 时仿照 SAVELOCAL 命令补一个临时来源
const serverSource = currentDoc.serverSource
    ?? (currentDoc.serverSource = { type: 'designs', designPath: `local/${currentDoc.name}` });

const saveResult = await localService.saveDrawing({
    serverSource,
    webcadJson: currentJson,
    serviceUrl: 'https://api.example.com',
    drawingName: '我的图纸'
});

if (saveResult.success) {
    console.log(`保存成功! ID: ${saveResult.id}`);
}

获取本地图纸列表

const drawings = await localService.listDrawings();

drawings.forEach(drawing => {
    console.log(`${drawing.id}: ${drawing.drawingName}`);
});

从本地缓存加载

const loadResult = await localService.loadDrawingById(drawingId);

if (loadResult.success) {
    // 返回 { success, record, webcadJson }——没有 serverSource 字段,
    // 来源信息在 record 上(type/mapid/version/designPath/branchName/lastPatchId…)
    const { record, webcadJson } = loadResult;
}

获取缓存统计

const stats = await localService.getStats();
console.log(`共 ${stats.totalCount} 个图纸, ${stats.totalSize} 字节`);

删除本地图纸

await localService.deleteDrawingById(drawingId);   // 按数字 id(listDrawings 返回的 id)删除
await localService.deleteDrawing(drawingKey);      // 按字符串 key(listDrawings 返回的 key)删除
// 注意:deleteDrawing 传数字 id 什么也不会删,却照样返回 success: true

导入 DWG/DXF

完整流程

const { DrawingManagerService, Service, MapOpenWay, openMapDarkStyle, Engine } = vjcad;

// 1. 创建文件输入
const input = document.createElement('input');
input.type = 'file';
input.accept = '.dwg,.dxf';

input.onchange = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    
    const drawingManager = new DrawingManagerService();
    const service = drawingManager.getService();
    
    // 2. 上传文件到服务器
    const uploadResult = await service.uploadMap(file);
    if (uploadResult.error) {
        console.error('上传失败:', uploadResult.error);
        return;
    }
    
    // 3. 生成图纸 ID
    const mapid = file.name.replace(/\.(dwg|dxf)$/i, '') + '_' + Date.now();
    
    // 4. 解析 DWG 文件
    const openResult = await service.openMap({
        mapid: mapid,
        fileid: uploadResult.fileid,
        uploadname: file.name,
        mapopenway: MapOpenWay.Memory,
        style: openMapDarkStyle()
    }, true);
    
    if (openResult.error) {
        console.error('解析失败:', openResult.error);
        return;
    }
    
    // 5. 获取 webcad 数据并加载
    const drawingResult = await drawingManager.openDrawing({
        type: 'imports',
        mapid: openResult.mapid,
        version: openResult.version,
        branch: 'main'
    });
    
    if (drawingResult.success) {
        const virtualFile = new File(
            [drawingResult.webcadJson],
            'drawing.webcad',
            { type: 'application/json' }
        );
        await Engine.view.openDbDoc(virtualFile, drawingResult.webcadData);
        
        // 6. 保存来源信息
        Engine.currentDoc.serverSource = {
            type: 'imports',
            mapid: openResult.mapid,
            version: openResult.version,
            branchName: 'main',
            lastPatchId: drawingResult.latestPatchId || 'base'
        };
        await Engine.currentDoc.setOriginalJson(drawingResult.webcadJson);
    }
};

// 触发文件选择
input.click();

支持的格式

格式 说明
.dwg AutoCAD 原生格式
.dxf 图形交换格式

导出 DWG

命令方式

await Engine.editor.executerWithOp('EXPORTDWG');

API 方式

const { DrawingManagerService, Engine } = vjcad;

const drawingManager = new DrawingManagerService();
const service = drawingManager.getService();

// 获取当前文档数据
const currentDoc = Engine.currentDoc;
const webcadJson = JSON.stringify(currentDoc.toDb());

// 调用导出接口
const exportResult = await drawingManager.exportToDwg({
    type: 'imports',
    webcadJson: webcadJson,
    mapid: '你的图ID',
    version: '图纸版本'
});

// 返回 { status, error?, path, filename, entityCount }——DWG 生成在服务器端,
// 没有 success / data 字段,也没有文件二进制(类型里的 downloadUrl 实际不填充)
if (exportResult.status) {
    console.log(`已导出 ${exportResult.filename},实体数 ${exportResult.entityCount}`);
    // EXPORTDWG 命令随后打开云端管理页让用户下载(公有云地址;私有化把服务地址的 /api/v1 换成 /_cloud)
    const fileid = exportResult.filename.replace('.dwg', '');
    window.open(`https://vjmap.com/app/cloud/#/upload?fileid=${fileid}&mapid=${fileid}`);
} else {
    console.error(exportResult.error);
}

新建文档

// 新建空白文档
Engine.view.newDocument();

// 设置文档名称
Engine.currentDoc.name = '新图纸';

关键属性

Engine.currentDoc.serverSource

保存图纸的服务器来源信息,用于后续保存和同步。

Engine.currentDoc.serverSource = {
    type: 'imports',          // 图纸类型
    mapid: 'drawing-123',     // 图纸 ID
    version: 'v1',            // 版本号
    branchName: 'main',       // 分支名称
    lastPatchId: 'patch-456'  // 最后的 Patch ID
};

Engine.currentDoc.isReadOnly

只是一个状态标记:只被 QSAVE/SAVEAS(只读时改走另存为)和文档状态指示读取,直接赋值不会阻止编辑。真正的只读加载是 Engine.view.openDbDoc(file, webcadData, true)(第 3 参,同时会把此标记置 true)。

Engine.currentDoc.isReadOnly = true;   // 仅标记,实体仍可选可改

增量保存说明

WebCAD 使用增量保存机制:

  1. 原始数据:通过 setOriginalJson() 保存的基准数据
  2. 当前数据:通过 toDb() 获取的当前文档数据
  3. Diff 计算:服务端比较两者差异,只保存变更部分
  4. Patch 链:每次保存生成一个 Patch,形成版本历史

好处:

  • 节省存储空间
  • 支持版本回溯
  • 支持多人协作冲突检测