state-machine
本文回答:多步骤交互命令(如 PLINE)建议把
step / state当字段,async main()写一个while (this.step > 0) { switch(this.step) {...} }循环,每步是一个async stepN_xxx()方法;用keywords接收C(close) / U(undo)这类关键字;getPoint空输入回车/空格返回InputStatusEnum.None(5000),据此判"完成"(EnterOrSpace只由getEntity返回)。
一、PLINE 风格的状态机模板
class MyPlineCommand {
constructor() {
this.step = 1;
this.points = [];
this.isClosed = false;
}
async main() {
Engine.undoManager.start_undoMark('绘制多段线');
try {
while (this.step > 0) {
switch (this.step) {
case 1: await this.step1_getFirstPoint(); break;
case 2: await this.step2_getNextPoints(); break;
case 3: this.step3_finish(); break;
}
}
} finally {
Engine.undoManager.end_undoMark();
Engine.clearPreview();
}
}
async step1_getFirstPoint() {
const opt = new PointInputOptions('指定起点:');
const r = await Engine.getPoint(opt);
if (r.status === InputStatusEnum.OK) {
this.points.push(r.value);
this.step = 2;
} else {
this.step = 0;
}
}
async step2_getNextPoints() {
const lastPt = this.points[this.points.length - 1];
const opt = new PointInputOptions('指定下一点 [闭合(C)/撤销(U)] <完成>:');
opt.keywords = ['C', 'U'];
opt.useBasePoint = true;
opt.basePoint = lastPt;
opt.callback = (canvasPt) => {
const wp = Engine.canvasToWcs(canvasPt);
this.drawPreview(wp);
};
const r = await Engine.getPoint(opt);
if (r.status === InputStatusEnum.OK) {
this.points.push(r.value);
} else if (r.stringResult === 'C') {
if (this.points.length >= 3) { this.isClosed = true; this.step = 3; }
else writeMessage('<br/>至少需要3个点才能闭合');
} else if (r.stringResult === 'U') {
if (this.points.length > 1) { this.points.pop(); }
else this.step = 1;
} else if (r.status === InputStatusEnum.None) { // 空输入回车/空格 = 完成(getPoint 不返回 EnterOrSpace)
this.step = 3;
} else {
this.step = 0;
}
}
step3_finish() {
if (this.points.length >= 2) {
const pl = new PolylineEnt();
for (const pt of this.points) pl.addVertex([pt.x, pt.y]);
pl.isClosed = this.isClosed;
pl.setDefaults();
Engine.addEntities(pl);
}
this.step = 0;
}
drawPreview(currentPt) {
if (this.points.length === 0) return;
const pl = new PolylineEnt();
for (const pt of this.points) pl.addVertex([pt.x, pt.y]);
pl.addVertex([currentPt.x, currentPt.y]);
pl.setDefaults();
Engine.clearPreview();
Engine.drawPreviewEntity(pl);
}
}
MCP 参考:vjcad/example/command/04statemachine.js。
二、状态机关键点
- 退出靠
step = 0:把 0 作为 sentinel,while (this.step > 0)自然退出。 - 关键字判定靠
result.stringResult:PointInputOptions.keywords = ['C', 'U']配合result.status === InputStatusEnum.Keyword(或部分实现里直接用result.stringResult)。 - 回车 / 空格判定:
getPoint空输入回车/空格返回InputStatusEnum.None(5000),判"完成"写r.status === InputStatusEnum.None(内置 LINE 命令同此写法);EnterOrSpace(3)只由getEntity返回,拿它判 getPoint 的回车永远不成立(值见 input/get-string-number 的InputStatusEnum表)。Esc 返回InputStatusEnum.Cancel,不抛异常。 - 预览必须每次清:
Engine.clearPreview()+Engine.drawPreviewEntity(...),命令结束finally再 clear 一次。
三、文档里的另一种范式(按状态字段)
class MultiStepCommand {
state = 'START';
async main() {
while (this.state !== 'END') {
switch (this.state) {
case 'START': await this.handleStart(); break;
case 'SELECT': await this.handleSelect(); break;
case 'CONFIRM': await this.handleConfirm(); break;
}
}
}
}
MCP 参考:vjcad/doc/command/index.md。
反例
- 在
step2里循环取点但忘记points.length === 0判断,导致lastPt = undefined。 - 关键字大小写没统一:
opt.keywords = ['C']但比对时写r.stringResult === 'c',会一直走不到。 - 用
while(true)死循环但没在step = 0时break/return。
参考(官方文档 MCP)
- 文档:
vjcad/doc/command/index.md - 示例:
vjcad/example/command/04statemachine.js、vjcad/example/input/06getkeyword.js