> **本文回答**：`vjmap.Draw.actionDraw*` 一次性绘制 API —— Promise 风格，无工具栏。

## 一、API 清单

| 函数 | 返回 |
|---|---|
| `actionDraw(map, mode, options)` | 通用 |
| `actionDrawPoint(map, options)` | 点 |
| `actionDrawLineSting(map, options)` | 折线 |
| `actionDrawPolygon(map, options)` | 多边形 |
| `actionDrawRectangle(map, options)` | 矩形 |
| `actionDrawSlantRectangle(map, options)` | 斜矩形 |
| `actionDrawCircle(map, options)` | 圆 |
| `actionSelect(map, draw, options)` | 选择实体 |

`actionDraw*` 的 Promise **从不 reject**：画成时 resolve 为 `draw.create` 事件对象 `{ features: [Feature], styleId, state, ... }`（不是带 `type` 的 FeatureCollection，但 `res.features[0]` 可用）；未画成就退出（ESC、点数不足按 Enter、切换模式等）resolve 为 `{ cancel: true }`（按 Enter 结束时另带 `isEnterKey: true`）。`actionSelect` resolve 为 `draw.getSelected()`（FeatureCollection），只在**右键**（`draw.simpleselect.contextmenu`）或开了 `selectSingle` 且选中后才结束。

## 二、通用 options

```js
{
  api: { getSnapFeatures: snapObj },   // 捕捉
  updatecoordinate: (e) => { /* 实时更新 */ },
  contextMenu: (e) => { /* 右键菜单 */ },
  statusChange: (e) => {
    if (e.isStart)   { /* 开始绘制 */ }
    if (e.isEnd)     { /* 结束绘制 */ }
  },
  pointCount: N,       // 画到 N 个点自动结束（线 / 多边形）
  isClosed: true,      // 自动闭合（线）
  selectSingle: true,  // actionSelect：选一个即返回
  styles: [...]        // 自定义样式
}
```

## 三、典型流程

```js
// 画一条线
const res = await vjmap.Draw.actionDrawLineSting(map, {
  api: { getSnapFeatures: snapObj },
  updatecoordinate: (e) => {
    if (!e.lnglat) return;
    // 实时回显
  }
});
if (res.cancel) return;   // 用户 ESC

// 拿到点
const coords = res.features[0].geometry.coordinates;   // [lng, lat]
const cadPts = map.fromLngLat(coords);                  // CAD 坐标
```

## 四、绘直线（两点）

```js
const res = await vjmap.Draw.actionDrawLineSting(map, { pointCount: 2 });
```

## 五、绘闭合折线

```js
const res = await vjmap.Draw.actionDrawLineSting(map, { isClosed: true });
```

## 六、自定义绘制时样式

```js
const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
const styles = opts.styles;
styles[7].paint["line-width"] = ["case", ["has", "line_width"], ["get", "line_width"], 8];
styles[7].paint["line-color"] = "#0ff";

const res = await vjmap.Draw.actionDrawLineSting(map, { styles });
```

## 七、选择实体（不是 Draw 的实体，而是 CAD 实体）

两种不同的"选择"：

- **`actionSelect(map, draw, options)`** —— 选 Draw 里的要素。
- **自定义 selectFeatures(map, ...)** —— 选 CAD 图上的实体，需要 `MapOpenWay.GeomRender`。

### CAD 实体框选 / 点选

```js
async function selectFeatures(map, isPointSel) {
  if (map.getService().currentMapParam()?.mapopenway == vjmap.MapOpenWay.Memory) {
    console.error("内存打开模式不支持选择实体");
    return;
  }

  const snapObj = {};
  getMapSnapPoints(map, snapObj);

  const selected = [];
  while (true) {
    let co;
    if (isPointSel) {
      const r = await vjmap.Draw.actionDrawPoint(map, {
        api: { getSnapFeatures: snapObj },
        contextMenu: () => map.fire("keyup", { keyCode: 13 })   // 右键 = 结束
      });
      if (r.cancel) break;
      const pt = r.features[0].geometry.coordinates;
      co = [pt, pt, pt, pt];
    } else {
      const r = await vjmap.Draw.actionDrawRectangle(map, {
        api: { getSnapFeatures: snapObj },
        contextMenu: () => map.fire("keyup", { keyCode: 13 })
      });
      if (r.cancel) break;
      co = r.features[0].geometry.coordinates[0];
    }
    co = map.fromLngLat(co);
    const bounds = [co[0].x, co[0].y, co[2].x, co[1].y];
    const geom = await getHighlightEntities(map, bounds, true);
    selected.push(...geom.features);
  }
  return selected;
}
```

`getHighlightEntities` 内部会调用 `svc.conditionQueryFeature`（条件查询 geom 字段=true）。

## 八、在 Action 模式里手动输入坐标

```js
await vjmap.Draw.actionDrawPoint(map, {
  statusChange: (e) => {
    if (e.isStart) window.currentStyleId = e.styleId;
  }
});

// 在其他地方（如键盘输入 x,y 回车）
map.fire("draw.input.click", {
  lngLat: map.toLngLat([x, y]),
  styleId: window.currentStyleId
});
```

## 九、旋转所选实体（复合动作）

```js
async function selectRotate(map, draw) {
  const sel = await vjmap.Draw.actionSelect(map, draw);
  if (sel.features.length === 0) return;

  const base = await vjmap.Draw.actionDrawPoint(map, {});
  if (base.cancel) return;

  const oldSel = vjmap.cloneDeep(sel.features);
  const pt0 = map.fromLngLat(base.features[0].geometry.coordinates);

  const endRes = await vjmap.Draw.actionDrawPoint(map, {
    updatecoordinate: (e) => {
      if (!e.lnglat) return;
      const pt = map.fromLngLat(e.lnglat);
      const angle = pt.angleTo(pt0);
      // 把 oldSel 每个几何围绕 pt0 旋转 angle
      const updated = vjmap.cloneDeep(oldSel);
      for (const f of updated) {
        const ng = vjmap.transform.convert(f, g => {
          const p = map.fromLngLat(g);
          return map.toLngLat(p.roateAround(angle, pt0));
        });
        draw.setFeatureProperty(f.id, "coordinates", ng);
      }
      draw.forceRefresh();
    }
  });

  if (endRes.cancel) {
    for (const f of oldSel) draw.setFeatureProperty(f.id, "coordinates", f);
    draw.forceRefresh();
  }
}
```

## 十、常见坑

1. **`updatecoordinate` 里修改业务状态** → 每帧都会触发，注意节流。
2. **连续绘制** → `while (true) { const r = await action...; if (r.cancel) break; }`。
3. **"右键确认" 需要在 contextMenu 里 fire Enter**：`map.fire("keyup", { keyCode: 13 })`。
4. **Promise 无法 await 但 ctx 仍在绘制**：可以保存状态并用 setTimeout 清理。
5. **控件事件监听是延迟挂载的**：`actionDraw*` 返回 Promise 时控件已创建，但其 mousemove/mouseup/keyup 监听要等 `map.loaded()` 才注册（内部 `connect()` 门控，瓦片未就绪时会拖很久）。**程序化/合成事件驱动绘制前必须等连接完成**——轮询 `map.loaded()` 或等本控件 draw 源出现（源名 `vjmap-map-draw-(hot|cold)_<styleId>`，styleId 在 `opts.drawContext.options.styleId`）。否则早期点击/按键静默丢失。
6. **点吸附到上一个已提交顶点 = 结束绘制**：`tr.onClick` 里 `snapped==lastVertex` 时直接 `changeMode(simple_select)`（等同双击结束的语义）。同样，顶点不足（折线 <2 个提交点）按 Enter 会 resolve `{cancel:true}` 而非 features。
