# 区域编辑 / 图层编辑

> 文档：`vjcad/doc/tile/tile-mode.md`
> 示例：
> - `vjcad/example/tile/02editarea.js` — 区域编辑
> - `vjcad/example/tile/03editlayer.js` — 图层编辑

进入瓦片模式后，**默认只读**。要修改图纸有两种方式：

1. **区域编辑**：拉一个矩形（`editArea`），把范围内实体加载进矢量层
2. **图层编辑**：选定若干图层，意图只加载这些图层的实体——但 vjcad@1.0.16 的 `Service.getWebcadData` **不发送 `editLayers`**（只发 clipbounds/editAreas），服务端收不到图层过滤，实际载入的是 **整图实体**（见第 5 节）

> `isReadOnly` 不拦截命令（库内只用于只读标记和 `QSAVE` 走另存为），所谓"默认只读"只是瓦片里没有矢量可编辑。

底层接口分别是 `TileEditAreaCommand` 与 `TileEditLayerCommand`。

## 1. 区域编辑

### 命令方式

```js
await Engine.editor.executerWithOp('TILEEDITAREA');
// 提示用户拉框；确认后自动调 TileEditAreaCommand.loadEditArea
```

### API 方式

```js
class EditTileAreaCommand {
    async main() {
        // 1. 用户拉框（Engine.getRectangle 不存在；getPoint + getCorner 两点成框）
        const p1 = await Engine.getPoint(new PointInputOptions('指定第一角点:'));
        if (p1.status !== InputStatusEnum.OK) return;
        const p2 = await Engine.getCorner(new CornerInputOptions('指定对角点:', p1.value));
        if (p2.status !== InputStatusEnum.OK) return;

        const editArea = {   // IEditAreaBounds 是 {minX,minY,maxX,maxY} 对象，不是数组
            minX: Math.min(p1.value.x, p2.value.x),
            minY: Math.min(p1.value.y, p2.value.y),
            maxX: Math.max(p1.value.x, p2.value.x),
            maxY: Math.max(p1.value.y, p2.value.y)
        };

        // 2. 调入矢量层
        const drawingManager = new DrawingManagerService();
        const loadResult = await TileEditAreaCommand.loadEditArea(
            editArea,
            Engine.currentDoc.serverSource,   // 来自 open 阶段
            Engine.pcanvas,
            Engine.currentDoc,
            drawingManager
        );

        if (loadResult.success) {
            Engine.currentDoc.isReadOnly = false;
            message.success(`加载 ${loadResult.loadedCount} 个实体到编辑区`);
        } else {
            message.error('加载失败：' + loadResult.error);
        }
    }
}
```

> 完整代码：`vjcad/example/tile/02editarea.js`

`loadEditArea` 之后：

- 矩形内的实体进入 `Engine.currentDoc`，可正常 `MOVE / COPY / ERASE` 等
- 矩形外仍是瓦片图，且 **该矩形对应的瓦片会自动被 mask** 挡住，避免和矢量重合
- 编辑结束用 `SAVESERVER` 把改动作为新 patch 保存到服务端（`QSAVE` 只下载本地 `.vcad`；无 TILESAVE 命令，见第 4 节）

## 2. 图层编辑

### 命令方式

```js
await Engine.editor.executerWithOp('TILEEDITLAYER');
// 弹出图层多选框，选定后自动加载
```

### API 方式

```js
class EditTileLayerCommand {
    async main() {
        const drawingManager = new DrawingManagerService();

        // 1. 拿图层列表（签名是 (mapid, version?)，不是 serverSource 对象）
        const src = Engine.currentDoc.serverSource;
        const layerList = await TileEditLayerCommand.getLayerListFromService(
            src.mapid, src.version
        );
        // layerList: ILayerInfo[] = [{ name, color?, selected? }]  —— 注意是 selected 不是 on

        // 2. 让用户选 / 你自定义筛选
        const selected = layerList.filter(l => l.name.includes('管线'))
                                .map(l => l.name);

        // 3. 加载图层实体
        const result = await TileEditLayerCommand.loadEditLayers(
            selected,
            Engine.currentDoc.serverSource,
            Engine.pcanvas,
            Engine.currentDoc,
            drawingManager
        );

        if (result.success) {
            Engine.currentDoc.isReadOnly = false;
            message.success(`加载图层：${selected.join(', ')}`);
        }
    }
}
```

> 完整代码：`vjcad/example/tile/03editlayer.js`

## 3. 加载结果（loadResult）

两个 API 的返回类型**不同**，不要混用字段：

```ts
// TileEditAreaCommand.loadEditArea → ILoadEditAreaResult
{ success: boolean; error?: string; loadedCount?: number; }

// TileEditAreaCommand.loadEditAreas（批量）→ ILoadEditAreasResult
{ success: boolean; error?: string; totalLoadedCount: number; areasCount: number;
  viewInfo?: { lookPt?: [number, number]; zoom?: number }; }

// TileEditLayerCommand.loadEditLayers → ILoadEditLayersResult
{ success: boolean; error?: string; totalLoadedCount: number; layersCount: number;
  viewInfo?: { lookPt?: [number, number]; zoom?: number }; }
```

没有 `entityCount` / `layerNames` 字段——取实体数用 `loadedCount` / `totalLoadedCount`，图层数用 `layersCount`。

## 4. 保存与退出

⚠️ 没有 `TILESAVE`/`TILEEXIT` 命令（`CommandRegistry` 无注册）。瓦片模式下的编辑改动走普通保存/关闭路径：

```js
await Engine.editor.executerWithOp('SAVESERVER');   // 存服务端：生成 patch，serverSource.lastPatchId 更新
// QSAVE 只把当前文档下载为本地 .vcad（只读时转另存为），不会写服务端补丁
// 退出即关闭图纸（如 OPENFROMSERVER 打开的图）
```

`SAVESERVER` 会把 `editAreas` 与瓦片透明度 `tileAlpha` 随补丁一起记录；之后用 `OPENFROMSERVER` 的"瓦片打开"重开该补丁时，会自动恢复瓦片透明度，并按记录的编辑区重新载入矢量、加回遮罩（`OpenFromServerCommand`）。`SAVELOCAL` / `OPENFROMLOCAL` 在本地存储里做同样的记录与恢复。`OPENFROMSERVER` 的图纸浏览器本身还带分支创建/删除、合并（含冲突对话框）、删补丁、删图纸等版本管理入口。

## 5. 区域 vs 图层 对比

| 维度 | 区域编辑 | 图层编辑 |
|---|---|---|
| 范围 | 用户拉框 | 设计上限于选中图层（1.0.16 实际整图，见下行） |
| 适合场景 | 局部改造（车位、施工细节） | 跨范围属性变更（颜色、可见性） |
| 加载量 | 小 | 大：1.0.16 不发送 `editLayers`，实际载入整图实体 |
| 是否加遮罩 | 是（背景色矩形挡住该区瓦片） | 否：不遮罩也不替换瓦片，瓦片与矢量会重复显示（可调 `setWmsTileAlpha` 淡化瓦片） |

## 红旗

- API 方式加载完毕后建议把 `isReadOnly` 设回 `false`（与命令行为一致）——它不拦截编辑命令，但为 `true` 时文档标签显示只读、`QSAVE` 走另存为
- `editArea` 是 `IEditAreaBounds` **对象** `{minX, minY, maxX, maxY}`，不是 `[x1,y1,x2,y2]` 数组也不是 `Point2D`
- `loadEditLayers` 在 1.0.16 不管选了哪些图层都会载入整图实体（`editLayers` 未发往服务端），大图 UI 要加 loading；只想动局部时优先用区域编辑
- 多次区域 / 图层编辑是 **累积加载**：`fromDbPartial` 追加模式按实体 id 去重，已加载的实体（含未保存的修改）保留不被覆盖，切换区域不会丢改动
- `TileEditLayerCommand.getLayerListFromService` 是网络请求，不要在循环里调用

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/tile/tile-mode.md`
- 示例：
- `vjcad/example/tile/02editarea.js` — 区域编辑
- `vjcad/example/tile/03editlayer.js` — 图层编辑
