侧边栏面板
侧边栏面板
通过 SidebarPanelManager 和 BasePanelComponent 创建自定义侧边栏面板,实现与系统面板的无缝集成。
在线示例
| 示例 | 描述 | 链接 |
|---|---|---|
| 侧边栏面板 | registerSidebarPanel 自定义侧边栏面板 | 在线演示{target="_blank"} |
概述
侧边栏面板是 WebCAD 界面中重要的 UI 组件,用于显示工具、属性、信息等内容。通过 SidebarPanelManager API,开发者可以:
- 注册自定义面板到左侧或右侧侧边栏
- 创建具有丰富交互的面板界面
- 与系统内置面板无缝集成
核心 API
接口定义
/**
* 侧边栏面板配置接口
*/
interface SidebarPanelConfig {
/** 面板唯一标识符 */
name: string;
/** 面板显示标签 */
label: string;
/** 面板图标路径 */
icon: string;
/** 面板位置: "left" | "right"(默认 "left")*/
position?: "left" | "right";
/** 面板组件类(继承自 BasePanelComponent)*/
panelClass: typeof BasePanelComponent;
/** 自定义元素标签名(可选)*/
tagName?: string;
/** 排序顺序(数字越小越靠前,默认 100)*/
order?: number;
}便捷函数
// 注册侧边栏面板
registerSidebarPanel(config: SidebarPanelConfig): boolean
// 注销侧边栏面板
unregisterSidebarPanel(name: string): boolean
// 激活指定面板
activateSidebarPanel(name: string): booleanSidebarPanelManager 类
// 获取管理器实例
const manager = SidebarPanelManager.getInstance();
// 注册面板
manager.registerPanel(config);
// 注销面板
manager.unregisterPanel(name);
// 激活面板
manager.activatePanel(name);
// 获取已注册面板列表
manager.getRegisteredPanels();
// 检查面板是否已注册
manager.isPanelRegistered(name);
// 获取面板实例
manager.getPanelInstance(name);使用步骤
1. 创建面板类
继承 BasePanelComponent 创建自定义面板类:
const { BasePanelComponent, html, css } = vjcad;
class MyToolPanel extends BasePanelComponent {
// 合并基类样式(推荐)
static styles = [
BasePanelComponent.baseStyles,
css`
.my-custom-style {
color: #58a6ff;
}
`
];
// 定义响应式属性
static properties = {
counter: { type: Number }
};
constructor() {
super();
this.counter = 0;
}
// 渲染面板内容
render() {
return html`
<div class="panel-content">
<div class="panel-section">
<div class="panel-section-title">我的工具</div>
<div class="panel-row">
<span class="panel-label">计数器:</span>
<span class="panel-value">${this.counter}</span>
</div>
<button class="panel-btn panel-btn-primary"
@click=${() => this.counter++}>
增加
</button>
</div>
</div>
`;
}
}2. 注册面板
const { registerSidebarPanel } = vjcad;
registerSidebarPanel({
name: "my-tools",
label: "我的工具",
icon: "./images/actbar/tools.svg",
position: "left",
panelClass: MyToolPanel,
order: 10
});3. 激活面板
const { activateSidebarPanel } = vjcad;
// 切换到自定义面板
activateSidebarPanel("my-tools");4. 通过 URL 参数激活面板
可以通过 URL 参数 vcad_panel 指定页面加载后自动激活的面板,无需在代码中手动调用 activateSidebarPanel:
// 页面加载后自动打开自定义面板
https://your-domain.com/?vcad_panel=my-tools
// 结合图纸参数使用
https://your-domain.com/?vcad_mapid=building_plan&vcad_panel=ai-assistant提示
vcad_panel 的值就是注册面板时配置的 name 属性。面板必须在插件初始化阶段已注册,URL 参数激活才能生效。
BasePanelComponent 内置样式
BasePanelComponent.baseStyles 提供了丰富的预定义样式类:
布局类
| 类名 | 说明 |
|---|---|
.panel-content | 面板内容容器,带内边距和滚动条 |
.panel-section | 分组区块容器 |
.panel-section-title | 分组标题,带下划线 |
.panel-row | 行布局,flex 对齐 |
表单类
| 类名 | 说明 |
|---|---|
.panel-label | 标签文字,灰色 |
.panel-value | 值文字,白色 |
.panel-input | 输入框样式,深色背景 |
按钮类
| 类名 | 说明 |
|---|---|
.panel-btn | 基础按钮样式 |
.panel-btn-primary | 主要按钮(蓝色) |
列表类
| 类名 | 说明 |
|---|---|
.panel-list | 列表容器 |
.panel-list-item | 列表项,带 hover 效果 |
.panel-list-item.active | 激活的列表项 |
生命周期方法
BasePanelComponent 提供以下可覆盖的生命周期方法:
class MyPanel extends BasePanelComponent {
// 面板激活时调用
onActivate() {
console.log("面板被激活");
this.loadData();
}
// 面板停用时调用
onDeactivate() {
console.log("面板被停用");
}
// 刷新面板内容
refresh() {
this.requestUpdate();
}
}完整示例
绘图工具面板
const {
BasePanelComponent, html, css,
registerSidebarPanel,
Engine, LineEnt, CircleEnt,
writeMessage
} = vjcad;
class DrawToolsPanel extends BasePanelComponent {
static styles = [
BasePanelComponent.baseStyles,
css`
.tool-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.color-swatch {
width: 24px;
height: 24px;
border-radius: 4px;
cursor: pointer;
border: 2px solid transparent;
}
.color-swatch.active {
border-color: #fff;
}
`
];
static properties = {
currentColor: { type: Number },
entityCount: { type: Number }
};
constructor() {
super();
this.currentColor = 1;
this.entityCount = 0;
}
connectedCallback() {
super.connectedCallback();
this.updateStats();
}
updateStats() {
this.entityCount = (Engine.currentSpace?.items || []).length;
}
drawLine() {
const x1 = Math.random() * 100;
const y1 = Math.random() * 100;
const line = new LineEnt(
[x1, y1],
[x1 + 50, y1 + 30]
);
line.setDefaults();
line.color = this.currentColor;
Engine.addEntities(line);
Engine.zoomExtents();
this.updateStats();
writeMessage("<br/>已创建直线");
}
drawCircle() {
const cx = Math.random() * 100;
const cy = Math.random() * 100;
const circle = new CircleEnt([cx, cy], 20);
circle.setDefaults();
circle.color = this.currentColor;
Engine.addEntities(circle);
Engine.zoomExtents();
this.updateStats();
writeMessage("<br/>已创建圆");
}
setColor(color) {
this.currentColor = color;
}
render() {
const colors = [1, 2, 3, 4, 5, 6, 7];
return html`
<div class="panel-content">
<div class="panel-section">
<div class="panel-section-title">绘图工具</div>
<div class="tool-grid">
<button class="panel-btn" @click=${this.drawLine}>
画直线
</button>
<button class="panel-btn" @click=${this.drawCircle}>
画圆
</button>
</div>
</div>
<div class="panel-section">
<div class="panel-section-title">颜色</div>
<div style="display: flex; gap: 4px;">
${colors.map(c => html`
<div class="color-swatch ${this.currentColor === c ? 'active' : ''}"
style="background: ${this.getColorHex(c)}"
@click=${() => this.setColor(c)}>
</div>
`)}
</div>
</div>
<div class="panel-section">
<div class="panel-section-title">统计</div>
<div class="panel-row">
<span class="panel-label">实体数量:</span>
<span class="panel-value">${this.entityCount}</span>
</div>
</div>
</div>
`;
}
getColorHex(index) {
const map = {
1: '#ff0000', 2: '#ffff00', 3: '#00ff00',
4: '#00ffff', 5: '#0000ff', 6: '#ff00ff', 7: '#ffffff'
};
return map[index] || '#ffffff';
}
}
// 注册面板
registerSidebarPanel({
name: "draw-tools",
label: "绘图工具",
icon: "./images/actbar/actbar-draw-settings.svg",
position: "left",
panelClass: DrawToolsPanel,
order: 5
});实体信息面板
class EntityInfoPanel extends BasePanelComponent {
static styles = [
BasePanelComponent.baseStyles,
css`
.info-card {
background: #1a242e;
border-radius: 6px;
padding: 12px;
margin-bottom: 8px;
}
.entity-type {
color: #58a6ff;
font-weight: 600;
}
`
];
static properties = {
selectedEntities: { type: Array }
};
constructor() {
super();
this.selectedEntities = [];
}
refresh() {
const { ssGetFirst } = vjcad;
const ss = ssGetFirst();
this.selectedEntities = ss || [];
this.requestUpdate();
}
getTypeName(entity) {
const map = {
'LINE': '直线', 'CIRCLE': '圆',
'ARC': '圆弧', 'TEXT': '文字'
};
return map[entity.dxfName] || entity.dxfName;
}
render() {
return html`
<div class="panel-content">
<div class="panel-section">
<div class="panel-section-title">选择信息</div>
<div class="panel-row">
<span class="panel-label">选中数量:</span>
<span class="panel-value">${this.selectedEntities.length}</span>
</div>
<button class="panel-btn panel-btn-primary"
style="width: 100%; margin-top: 8px;"
@click=${this.refresh}>
刷新
</button>
</div>
${this.selectedEntities.length > 0 ? html`
<div class="panel-section">
<div class="panel-section-title">实体详情</div>
${this.selectedEntities.slice(0, 5).map(ent => html`
<div class="info-card">
<div class="entity-type">${this.getTypeName(ent)}</div>
<div class="panel-row">
<span class="panel-label">图层:</span>
<span class="panel-value">${ent.layer || '0'}</span>
</div>
<div class="panel-row">
<span class="panel-label">颜色:</span>
<span class="panel-value">${ent.color ?? 'ByLayer'}</span>
</div>
</div>
`)}
</div>
` : html`
<div style="text-align: center; color: #8b949e; padding: 40px;">
请选择实体后点击刷新
</div>
`}
</div>
`;
}
}
// 注册到右侧
registerSidebarPanel({
name: "entity-info",
label: "实体信息",
icon: "./images/actbar/actbar-property.svg",
position: "right",
panelClass: EntityInfoPanel
});禁止自动切换到特性面板
默认情况下,选择实体时会自动激活特性面板。如果你有自定义面板,不希望选择实体时自动切换离开当前面板,可以在 MainView 配置中禁用此行为:
const cadView = new MainView({
// ... 其他配置
autoActivatePropertyPanel: false // 选择实体时不自动切换到特性面板
});配置内置面板位置
默认情况下,系统有 6 个内置面板:
- 左侧:特性(props)、块(blocks)、图像(images)
- 右侧:捕捉设置(dsettings)、命令列表(commands)、命令别名(alias)
你可以通过 MainView 配置来调整这些面板的左右位置:
const cadView = new MainView({
// ... 其他配置
// 配置左侧面板
leftPanels: ["props", "dsettings"], // 左侧:特性、捕捉设置
// 配置右侧面板
rightPanels: ["blocks", "images", "commands", "alias"] // 右侧:块、图像、命令、别名
});可用面板名称
| 名称 | 描述 |
|---|---|
props | 特性面板 - 显示选中实体的属性 |
blocks | 块面板 - 管理图块 |
images | 图像面板 - 管理图像 |
dsettings | 捕捉设置面板 - 对象捕捉配置 |
commands | 命令列表面板 - 可用命令一览 |
alias | 命令别名面板 - 命令快捷键设置 |
注意
- 每个面板只能出现在一侧,不要在
leftPanels和rightPanels中重复配置同一个面板 - 不在配置中的面板将不会显示
- 配置顺序决定面板在活动栏中的显示顺序
注意事项
自定义元素命名:
tagName必须包含连字符(如my-panel),这是 Web Components 规范要求样式隔离:面板使用 Shadow DOM,样式自动隔离,不会影响其他组件
图标路径:
icon支持相对路径和绝对 URL,推荐使用系统图标路径格式生命周期:利用 LitElement 的生命周期方法(如
connectedCallback)进行初始化响应式属性:使用
static properties定义需要响应式更新的属性面板切换:如果设置了
autoActivatePropertyPanel: false,选择实体后不会自动切换面板,需要用户手动点击活动栏切换