命令架构
大约 5 分钟
命令架构
本章深入介绍 WebCAD 命令系统的架构设计,帮助你理解命令的工作原理。
命令类结构
main() 方法约定
每个命令类必须实现 main() 方法作为命令入口。这是 WebCAD 命令系统的核心约定:
export class MyCommand {
async main(): Promise<void> {
// 命令逻辑
}
}main() 方法的特点:
- 异步方法:返回
Promise<void>,支持await等待用户输入 - 无参数:命令参数通过用户交互获取,而非方法参数
- 无返回值:命令执行结果通过副作用体现(创建实体、修改属性等)
完整命令类示例
import { Point2D } from 'vjcad';
import { Engine } from 'vjcad';
import { LineEnt } from 'vjcad';
import {
getPoint,
PointInputOptions,
InputStatusEnum,
ssSetFirst,
writeMessage
} from 'vjcad';
export class SimpleLineCommand {
// 状态变量
private startPoint: Point2D = new Point2D();
private endPoint: Point2D = new Point2D();
constructor() {
// 初始化状态
this.startPoint = new Point2D();
this.endPoint = new Point2D();
}
async main(): Promise<void> {
// 清空选择集
ssSetFirst([]);
// 开始撤销标记
Engine.undoManager.start_undoMark();
try {
// 获取起点
const p1Options = new PointInputOptions("指定第一点:");
const p1Result = await getPoint(p1Options);
if (p1Result.status !== InputStatusEnum.OK) {
return; // 用户取消
}
this.startPoint = p1Result.value;
// 获取终点
const p2Options = new PointInputOptions("指定第二点:");
p2Options.useBasePoint = true;
p2Options.basePoint = this.startPoint;
const p2Result = await getPoint(p2Options);
if (p2Result.status !== InputStatusEnum.OK) {
return; // 用户取消
}
this.endPoint = p2Result.value;
// 创建直线
const line = new LineEnt(this.startPoint, this.endPoint);
line.setDefaults();
Engine.pcanvas.addEntity(line);
Engine.undoManager.added_undoMark([line]);
writeMessage("<br/>直线已创建。");
} finally {
// 确保结束撤销标记
Engine.undoManager.end_undoMark();
}
}
}命令注册
CommandDefinition 类
CommandDefinition 定义命令的元信息:
import { CommandDefinition, CommandOptions } from 'vjcad';
const definition = new CommandDefinition(
'MYLINE', // 命令名(大写)
'绘制简单直线', // 描述
SimpleLineCommand, // 命令类
new CommandOptions() // 选项
);| 参数 | 类型 | 说明 |
|---|---|---|
name | string | 命令名,必须大写,用户在命令行输入此名称执行命令 |
description | string | 命令描述,显示在命令提示和帮助中 |
commandClass | Class | 命令类,必须有 main() 方法 |
options | CommandOptions | 命令选项 |
CommandOptions 详解
CommandOptions 控制命令的行为特性:
const options = new CommandOptions();
options.useAutoComplete = true; // 启用命令行自动补全
options.overrideSvgFileName = ''; // 自定义图标文件名| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
useAutoComplete | boolean | true | 是否在命令行输入时显示自动补全 |
overrideSvgFileName | string | '' | 自定义命令图标的 SVG 文件名 |
CommandRegistry 注册命令
使用 CommandRegistry 注册命令到系统:
import { CommandRegistry, CommandDefinition, CommandOptions } from 'vjcad';
// 创建命令定义
const options = new CommandOptions();
const cmdDef = new CommandDefinition(
'MYLINE',
'绘制简单直线',
SimpleLineCommand,
options
);
// 注册命令
CommandRegistry.regist(cmdDef);查询已注册命令
// 获取命令定义
const cmd = CommandRegistry.item('MYLINE');
if (cmd) {
console.log('命令名:', cmd.name);
console.log('描述:', cmd.description);
}
// 获取所有命令
const allCommands = CommandRegistry.items;命令生命周期
生命周期阶段
- 命令查找:用户输入命令名,系统从
CommandRegistry查找对应的CommandDefinition - 实例创建:系统创建命令类的新实例
- 执行入口:调用
main()方法 - 用户交互:命令通过输入函数与用户交互
- 状态修改:命令创建/修改实体,记录撤销信息
- 命令结束:
main()方法返回,命令完成
在插件中注册命令
在插件系统中,使用 PluginContext 注册命令更加便捷:
import type { Plugin, PluginContext } from 'vjcad';
// 命令类
class MyPluginCommand {
async main(): Promise<void> {
console.log('插件命令执行');
// 命令逻辑...
}
}
// 插件定义
const plugin: Plugin = {
manifest: {
id: 'my-plugin',
name: '我的插件',
version: '1.0.0',
author: 'Developer',
description: '示例插件'
},
onLoad(context: PluginContext): void {
console.log('插件已加载');
},
onActivate(context: PluginContext): void {
// 注册命令(简化API)
context.registerCommand(
'MYPLUGIN', // 命令名
'我的插件命令', // 描述
MyPluginCommand // 命令类
);
// 可选:添加到 Ribbon 菜单
context.addRibbonGroup('plugins', {
id: 'my-plugin-group',
label: '我的插件',
primaryButtons: [
{
icon: 'myplugin',
cmd: 'MYPLUGIN',
prompt: '执行插件命令'
}
]
});
},
onDeactivate(context: PluginContext): void {
// 注销命令(可选,插件停用时自动清理)
context.unregisterCommand('MYPLUGIN');
},
onUnload(context: PluginContext): void {
console.log('插件已卸载');
}
};
export default plugin;插件命令与直接注册的区别
| 特性 | 直接注册 | 插件注册 |
|---|---|---|
| API | CommandRegistry.regist() | context.registerCommand() |
| 生命周期管理 | 手动 | 自动(随插件激活/停用) |
| UI 集成 | 需额外代码 | 提供便捷 API |
| 适用场景 | 核心命令 | 扩展功能 |
实际插件示例:查找替换
以下是 find-replace-plugin 的简化结构:
import type { Plugin, PluginContext } from 'vjcad';
import { Engine } from 'vjcad';
// 面板实例管理
let panelInstance: FindReplacePanel | null = null;
// 查找替换命令
class FindReplaceCommand {
async main(): Promise<void> {
Engine.writeMessage('<br/>打开查找替换面板');
if (panelInstance) {
panelInstance.show();
return;
}
panelInstance = createFindReplacePanel();
panelInstance.show();
}
}
// 插件定义
const plugin: Plugin = {
manifest: {
id: 'find-replace',
name: '查找替换文字',
version: '1.0.0',
author: 'WebCAD Team',
description: '提供文字查找和替换功能'
},
onActivate(context: PluginContext): void {
// 注册图标
context.registerIcon('FINDREPLACE', ICON_SVG);
// 注册命令
context.registerCommand('FINDREPLACE', '查找替换面板', FindReplaceCommand);
// 添加 Ribbon 按钮
context.addRibbonGroup('plugins', {
id: 'find-replace',
label: '查找替换',
primaryButtons: [
{ icon: 'findreplace', cmd: 'FINDREPLACE', prompt: '文字查找替换', type: 'large' }
]
});
},
onDeactivate(context: PluginContext): void {
// 清理面板
if (panelInstance) {
panelInstance.destroy();
panelInstance = null;
}
}
};
export default plugin;命令执行方式
命令行执行
用户在命令行输入命令名:
LINE
CIRCLE
MOVE脚本执行
通过代码执行命令:
// 方式1:使用 Editor
await Engine.editor.executerWithOp('LINE');
// 方式2:直接实例化
const cmd = new LineCommand();
await cmd.main();带参数执行
某些命令支持脚本参数:
// 执行字符串命令序列
await Engine.editor.executerWithOp('EXECSTR', 'LINE 0,0 100,100 ');