refactor: 重构 Manager 架构,引入 ManagerRegistry 和 BaseManager 基类
- 新增 ManagerRegistry 单例注册表,统一管理所有 Manager 实例 - 新增 BaseManager 基类,自动管理事件订阅清理 - 新增 BaseDialogManager 基类,统一对话框生命周期管理 - 重构 15 个 Manager 使用新基类 - 重构 Toolbar 按钮和 Menu 按钮移除 engine 参数依赖 - 删除 BimComponent 基类(已不再使用) - 为所有 Manager 和核心模块添加中文 JSDoc 注释
This commit is contained in:
@@ -1,45 +1,45 @@
|
||||
import './bim-engine.css';
|
||||
import {ToolbarManager} from './managers/toolbar-manager';
|
||||
import {ButtonGroupManager} from './managers/button-group-manager';
|
||||
import {DialogManager} from './managers/dialog-manager';
|
||||
import {EngineManager} from './managers/engine-manager';
|
||||
import {RightKeyManager} from './managers/right-key-manager';
|
||||
import {ConstructTreeManagerBtn} from './managers/construct-tree-manager-btn';
|
||||
import {PropertyPanelManager} from './managers/property-panel-manager';
|
||||
import {MeasureDialogManager} from './managers/measure-dialog-manager';
|
||||
import {SectionPlaneDialogManager} from './managers/section-plane-dialog-manager';
|
||||
import {SectionAxisDialogManager} from './managers/section-axis-dialog-manager';
|
||||
import {SectionBoxDialogManager} from './managers/section-box-dialog-manager';
|
||||
import {WalkControlManager} from './managers/walk-control-manager';
|
||||
import {MapDialogManager} from './managers/map-dialog-manager';
|
||||
import type {EngineOptions, ModelLoadOptions} from './components/engine';
|
||||
import {localeManager} from './services/locale';
|
||||
import {themeManager} from './services/theme';
|
||||
import type {LocaleType} from './locales/types';
|
||||
import type {ThemeType, ThemeConfig} from './themes/types';
|
||||
import {EventEmitter} from './core/event-emitter';
|
||||
import {EngineEvents} from './types/events';
|
||||
import { ToolbarManager } from './managers/toolbar-manager';
|
||||
import { ButtonGroupManager } from './managers/button-group-manager';
|
||||
import { DialogManager } from './managers/dialog-manager';
|
||||
import { EngineManager } from './managers/engine-manager';
|
||||
import { RightKeyManager } from './managers/right-key-manager';
|
||||
import { ConstructTreeManagerBtn } from './managers/construct-tree-manager-btn';
|
||||
import { PropertyPanelManager } from './managers/property-panel-manager';
|
||||
import { MeasureDialogManager } from './managers/measure-dialog-manager';
|
||||
import { SectionPlaneDialogManager } from './managers/section-plane-dialog-manager';
|
||||
import { SectionAxisDialogManager } from './managers/section-axis-dialog-manager';
|
||||
import { SectionBoxDialogManager } from './managers/section-box-dialog-manager';
|
||||
import { WalkControlManager } from './managers/walk-control-manager';
|
||||
import { MapDialogManager } from './managers/map-dialog-manager';
|
||||
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
||||
import { localeManager } from './services/locale';
|
||||
import { themeManager } from './services/theme';
|
||||
import type { LocaleType } from './locales/types';
|
||||
import type { ThemeType, ThemeConfig } from './themes/types';
|
||||
import { ManagerRegistry } from './core/manager-registry';
|
||||
import { EngineEvents } from './types/events';
|
||||
|
||||
export type {EngineOptions, ModelLoadOptions};
|
||||
export type { EngineOptions, ModelLoadOptions };
|
||||
|
||||
export class BimEngine extends EventEmitter {
|
||||
export class BimEngine {
|
||||
public container: HTMLElement;
|
||||
private wrapper: HTMLElement | null = null;
|
||||
private registry: ManagerRegistry;
|
||||
|
||||
public toolbar: ToolbarManager | null = null; // 底部专用
|
||||
public constructTreeBtn: ConstructTreeManagerBtn | null = null; // 底部专用
|
||||
public buttonGroup: ButtonGroupManager | null = null; // 通用
|
||||
public toolbar: ToolbarManager | null = null;
|
||||
public constructTreeBtn: ConstructTreeManagerBtn | null = null;
|
||||
public buttonGroup: ButtonGroupManager | null = null;
|
||||
public dialog: DialogManager | null = null;
|
||||
public engine: EngineManager | null = null; // 3D 引擎管理器
|
||||
public rightKey: RightKeyManager | null = null; // 右键菜单管理器
|
||||
public propertyPanel: PropertyPanelManager | null = null; // 属性面板 (演示 Collapse)
|
||||
public measure: MeasureDialogManager | null = null; // 测量面板
|
||||
public sectionPlane: SectionPlaneDialogManager | null = null; // 拾取面剖切面板
|
||||
public sectionAxis: SectionAxisDialogManager | null = null; // 轴向剖切面板
|
||||
public sectionBox: SectionBoxDialogManager | null = null; // 剖切盒面板
|
||||
public walkControl: WalkControlManager | null = null; // 漫游控制面板
|
||||
public map: MapDialogManager | null = null; // 地图面板
|
||||
|
||||
public engine: EngineManager | null = null;
|
||||
public rightKey: RightKeyManager | null = null;
|
||||
public propertyPanel: PropertyPanelManager | null = null;
|
||||
public measure: MeasureDialogManager | null = null;
|
||||
public sectionPlane: SectionPlaneDialogManager | null = null;
|
||||
public sectionAxis: SectionAxisDialogManager | null = null;
|
||||
public sectionBox: SectionBoxDialogManager | null = null;
|
||||
public walkControl: WalkControlManager | null = null;
|
||||
public map: MapDialogManager | null = null;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement | string,
|
||||
@@ -48,11 +48,12 @@ export class BimEngine extends EventEmitter {
|
||||
theme?: ThemeType;
|
||||
}
|
||||
) {
|
||||
super();
|
||||
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
||||
if (!el) throw new Error('Container not found');
|
||||
this.container = el;
|
||||
|
||||
this.registry = ManagerRegistry.getInstance();
|
||||
|
||||
if (options?.locale) localeManager.setLocale(options.locale);
|
||||
if (options?.theme) {
|
||||
if (options.theme === 'custom') {
|
||||
@@ -65,13 +66,12 @@ export class BimEngine extends EventEmitter {
|
||||
this.init();
|
||||
}
|
||||
|
||||
// Typed wrappers for events
|
||||
public emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]) {
|
||||
super.emit(event, payload);
|
||||
this.registry.emit(event, payload);
|
||||
}
|
||||
|
||||
public on<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): () => void {
|
||||
return super.on(event, listener);
|
||||
return this.registry.on(event, listener);
|
||||
}
|
||||
|
||||
public setLocale(locale: LocaleType) {
|
||||
@@ -96,32 +96,45 @@ export class BimEngine extends EventEmitter {
|
||||
this.wrapper.className = 'bim-engine-wrapper';
|
||||
this.container.appendChild(this.wrapper);
|
||||
|
||||
// 创建 3D 引擎管理器
|
||||
this.engine = new EngineManager(this, this.wrapper);
|
||||
this.dialog = new DialogManager(this, this.wrapper);
|
||||
this.toolbar = new ToolbarManager(this, this.wrapper);
|
||||
this.buttonGroup = new ButtonGroupManager(this, this.wrapper);
|
||||
this.rightKey = new RightKeyManager(this, this.wrapper);
|
||||
this.constructTreeBtn = new ConstructTreeManagerBtn(this, this.wrapper);
|
||||
this.propertyPanel = new PropertyPanelManager(this);
|
||||
this.measure = new MeasureDialogManager(this);
|
||||
this.sectionPlane = new SectionPlaneDialogManager(this);
|
||||
this.sectionAxis = new SectionAxisDialogManager(this);
|
||||
this.sectionBox = new SectionBoxDialogManager(this);
|
||||
this.walkControl = new WalkControlManager(this);
|
||||
this.registry.container = this.container;
|
||||
this.registry.wrapper = this.wrapper;
|
||||
|
||||
this.engine = new EngineManager(this.wrapper);
|
||||
this.dialog = new DialogManager(this.wrapper);
|
||||
this.toolbar = new ToolbarManager(this.wrapper);
|
||||
this.buttonGroup = new ButtonGroupManager(this.wrapper);
|
||||
this.rightKey = new RightKeyManager(this.wrapper);
|
||||
this.constructTreeBtn = new ConstructTreeManagerBtn(this.wrapper);
|
||||
this.propertyPanel = new PropertyPanelManager();
|
||||
this.measure = new MeasureDialogManager();
|
||||
this.sectionPlane = new SectionPlaneDialogManager();
|
||||
this.sectionAxis = new SectionAxisDialogManager();
|
||||
this.sectionBox = new SectionBoxDialogManager();
|
||||
this.walkControl = new WalkControlManager();
|
||||
this.walkControl.init();
|
||||
this.map = new MapDialogManager(this);
|
||||
this.map = new MapDialogManager();
|
||||
this.map.init();
|
||||
|
||||
// 初始主题
|
||||
this.registry.engine3d = this.engine;
|
||||
this.registry.dialog = this.dialog;
|
||||
this.registry.toolbar = this.toolbar;
|
||||
this.registry.buttonGroup = this.buttonGroup;
|
||||
this.registry.rightKey = this.rightKey;
|
||||
this.registry.constructTree = this.constructTreeBtn;
|
||||
this.registry.propertyPanel = this.propertyPanel;
|
||||
this.registry.measure = this.measure;
|
||||
this.registry.sectionPlane = this.sectionPlane;
|
||||
this.registry.sectionAxis = this.sectionAxis;
|
||||
this.registry.sectionBox = this.sectionBox;
|
||||
this.registry.walkControl = this.walkControl;
|
||||
this.registry.map = this.map;
|
||||
|
||||
this.updateTheme(themeManager.getTheme());
|
||||
// 订阅主题变化
|
||||
themeManager.subscribe((theme) => {
|
||||
this.updateTheme(theme);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private updateTheme(theme: ThemeConfig) {
|
||||
if (this.wrapper) {
|
||||
this.wrapper.style.color = theme.textPrimary;
|
||||
@@ -141,6 +154,6 @@ export class BimEngine extends EventEmitter {
|
||||
this.sectionBox?.destroy();
|
||||
this.walkControl?.destroy();
|
||||
this.container.innerHTML = '';
|
||||
this.clear();
|
||||
ManagerRegistry.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,9 @@ import { t, localeManager } from '../../services/locale';
|
||||
import { themeManager } from '../../services/theme';
|
||||
import type { ThemeConfig } from '../../themes/types';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import type { BimEngine } from '../../bim-engine';
|
||||
import { ManagerRegistry } from '../../core/manager-registry';
|
||||
import { EngineEvents } from '../../types/events';
|
||||
|
||||
/**
|
||||
* 通用按钮组组件 (BimButtonGroup)
|
||||
*/
|
||||
export class BimButtonGroup implements IBimComponent {
|
||||
private container: HTMLElement;
|
||||
private options: ButtonGroupOptions;
|
||||
@@ -24,12 +21,10 @@ export class BimButtonGroup implements IBimComponent {
|
||||
private btnRefs: Map<string, HTMLElement> = new Map();
|
||||
private dropdownElement: HTMLElement | null = null;
|
||||
private hoverTimeout: number | null = null;
|
||||
private customColors: Set<keyof ButtonGroupColors> = new Set(); // 记录用户自定义的颜色属性
|
||||
private customColors: Set<keyof ButtonGroupColors> = new Set();
|
||||
private unsubscribeLocale: (() => void) | null = null;
|
||||
private unsubscribeTheme: (() => void) | null = null;
|
||||
|
||||
protected engine: BimEngine | null = null;
|
||||
|
||||
private readonly DEFAULT_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect></svg>';
|
||||
|
||||
constructor(options: ButtonGroupOptions) {
|
||||
@@ -67,16 +62,9 @@ export class BimButtonGroup implements IBimComponent {
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
public setEngine(engine: BimEngine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
protected emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]) {
|
||||
if (this.engine) {
|
||||
this.engine.emit(event, payload);
|
||||
} else {
|
||||
console.warn('[BimButtonGroup] Engine not set, cannot emit event:', event);
|
||||
}
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.emit(event, payload);
|
||||
}
|
||||
|
||||
private initContainer(): void {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
|
||||
/**
|
||||
* 全屏按钮配置
|
||||
*/
|
||||
export const createFullscreenButton = (_engine: BimEngine): ButtonConfig => {
|
||||
export const createFullscreenButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'fullscreen',
|
||||
groupId: 'group-2',
|
||||
@@ -16,14 +12,12 @@ export const createFullscreenButton = (_engine: BimEngine): ButtonConfig => {
|
||||
onClick: async () => {
|
||||
console.log('全屏按钮被点击');
|
||||
|
||||
// 0. 环境检查 (帮助调试 Iframe 问题)
|
||||
const isIframe = window.self !== window.top;
|
||||
if (isIframe) {
|
||||
console.warn('检测到在 Iframe 中运行,请确保父级 iframe 标签拥有 allow="fullscreen" 属性');
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 获取当前全屏状态 (使用 any 绕过 TS 检查)
|
||||
const doc = document as any;
|
||||
const fullscreenElement = doc.fullscreenElement ||
|
||||
doc.webkitFullscreenElement ||
|
||||
@@ -33,43 +27,32 @@ export const createFullscreenButton = (_engine: BimEngine): ButtonConfig => {
|
||||
const isFullscreen = !!fullscreenElement;
|
||||
console.log('当前是否全屏:', isFullscreen);
|
||||
|
||||
// 2. 确定要全屏的目标元素
|
||||
// 优先查找 BIM 容器,如果找不到则使用 document.body
|
||||
const bimContainer = document.querySelector('.bim-engine-container') as HTMLElement;
|
||||
const targetElem = bimContainer || document.body;
|
||||
|
||||
// 将 targetElem 断言为 any,解决 "Property 'webkitRequestFullscreen' does not exist" 报错
|
||||
const el = targetElem as any;
|
||||
|
||||
if (!isFullscreen) {
|
||||
// === 进入全屏 ===
|
||||
console.log('准备进入全屏...');
|
||||
|
||||
// 关键:防止全屏后背景变黑
|
||||
if (targetElem.style.backgroundColor === '' || targetElem.style.backgroundColor === 'transparent') {
|
||||
targetElem.style.backgroundColor = '#ffffff'; // 根据你的主题颜色调整
|
||||
targetElem.style.backgroundColor = '#ffffff';
|
||||
}
|
||||
|
||||
// 兼容不同浏览器的 API
|
||||
const requestMethod = el.requestFullscreen ||
|
||||
el.webkitRequestFullscreen ||
|
||||
el.mozRequestFullScreen ||
|
||||
el.msRequestFullscreen;
|
||||
|
||||
if (requestMethod) {
|
||||
// 使用 call 绑定正确的上下文
|
||||
await requestMethod.call(el, { navigationUI: 'hide' });
|
||||
console.log('全屏请求已发送');
|
||||
} else {
|
||||
console.warn('当前浏览器不支持全屏 API');
|
||||
alert('当前浏览器不支持全屏功能');
|
||||
}
|
||||
|
||||
} else {
|
||||
// === 退出全屏 ===
|
||||
console.log('准备退出全屏...');
|
||||
|
||||
// 兼容不同浏览器的退出 API
|
||||
const exitMethod = doc.exitFullscreen ||
|
||||
doc.webkitExitFullscreen ||
|
||||
doc.mozCancelFullScreen ||
|
||||
@@ -82,11 +65,10 @@ export const createFullscreenButton = (_engine: BimEngine): ButtonConfig => {
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('全屏操作失败:', error);
|
||||
// 专门提示权限问题
|
||||
if (error && error.message && error.message.includes('denied')) {
|
||||
console.error('全屏请求被拒绝。如果是 Iframe,请检查 allow="fullscreen"。如果是自动触发,请确保由用户点击触发。');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 首页按钮配置
|
||||
* 使用工厂函数模式,注入 engine 实例
|
||||
*/
|
||||
export const createHomeButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createHomeButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'home',
|
||||
groupId: 'group-1',
|
||||
@@ -16,7 +12,8 @@ export const createHomeButton = (engine: BimEngine): ButtonConfig => {
|
||||
keepActive: false,
|
||||
onClick: (button) => {
|
||||
console.log('首页按钮被点击:', button.id);
|
||||
engine.engine?.CameraGoHome()
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.engine3d?.CameraGoHome();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { infoIcon } from './icon';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 信息按钮配置
|
||||
* 说明:当前仍保留 demo 的事件触发方式;engine 已注入,便于未来替换为 SDK 内部逻辑。
|
||||
*/
|
||||
export const createInfoButton = (_engine: BimEngine): ButtonConfig => {
|
||||
export const createInfoButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'toolbar-info',
|
||||
id: 'info',
|
||||
groupId: 'group-2',
|
||||
type: 'button',
|
||||
label: 'toolbar.info',
|
||||
icon: infoIcon,
|
||||
icon: getIcon('信息'),
|
||||
keepActive: false,
|
||||
onClick: () => {
|
||||
// WORKAROUND: Dispatch a standard custom event on document
|
||||
document.dispatchEvent(new CustomEvent('bim-demo:open-property-panel'));
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.emit('ui:open-dialog', { id: 'info' });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 地图按钮配置(开关按钮)
|
||||
*/
|
||||
export const createMapButton = (engine: BimEngine): ButtonConfig => {
|
||||
// 监听地图打开/关闭事件,同步按钮状态
|
||||
engine.on('map:opened', () => {
|
||||
engine.toolbar?.setBtnActive('map', true);
|
||||
export const createMapButton = (): ButtonConfig => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
|
||||
registry.on('map:opened', () => {
|
||||
registry.toolbar?.setBtnActive('map', true);
|
||||
});
|
||||
|
||||
engine.on('map:closed', () => {
|
||||
engine.toolbar?.setBtnActive('map', false);
|
||||
registry.on('map:closed', () => {
|
||||
registry.toolbar?.setBtnActive('map', false);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -24,11 +22,10 @@ export const createMapButton = (engine: BimEngine): ButtonConfig => {
|
||||
keepActive: true,
|
||||
icon: getIcon('地图'),
|
||||
onClick: () => {
|
||||
// 切换地图显示状态
|
||||
if (engine.map?.isOpen()) {
|
||||
engine.map?.hide();
|
||||
if (registry.map?.isOpen()) {
|
||||
registry.map?.hide();
|
||||
} else {
|
||||
engine.map?.show();
|
||||
registry.map?.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type {ButtonConfig} from '../../../index.type';
|
||||
import type {BimEngine} from '../../../../../bim-engine';
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 测量按钮配置
|
||||
* 使用工厂函数模式,注入 engine 实例
|
||||
*/
|
||||
export const createMeasureButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createMeasureButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'measure',
|
||||
groupId: 'group-1',
|
||||
@@ -15,10 +11,11 @@ export const createMeasureButton = (engine: BimEngine): ButtonConfig => {
|
||||
icon: getIcon('测量'),
|
||||
keepActive: true,
|
||||
onClick: (button) => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
if (button.isActive) {
|
||||
engine.measure?.show()
|
||||
registry.measure?.show();
|
||||
} else {
|
||||
engine.measure?.destroy()
|
||||
registry.measure?.destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 构件详情按钮配置
|
||||
*/
|
||||
export const createPropertyButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createPropertyButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'property',
|
||||
groupId: 'group-1',
|
||||
@@ -15,7 +12,8 @@ export const createPropertyButton = (engine: BimEngine): ButtonConfig => {
|
||||
icon: getIcon('文档'),
|
||||
onClick: () => {
|
||||
console.log('构件详情按钮被点击');
|
||||
engine.propertyPanel?.show();
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.propertyPanel?.show();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 轴向剖切按钮配置
|
||||
*/
|
||||
export const createSectionAxisButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createSectionAxisButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'section-axis',
|
||||
groupId: 'group-1',
|
||||
@@ -17,10 +14,11 @@ export const createSectionAxisButton = (engine: BimEngine): ButtonConfig => {
|
||||
label: 'toolbar.sectionAxis',
|
||||
icon: getIcon('轴向剖切'),
|
||||
onClick: (button) => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
if (button.isActive) {
|
||||
engine.sectionAxis?.show();
|
||||
registry.sectionAxis?.show();
|
||||
} else {
|
||||
engine.sectionAxis?.destroy();
|
||||
registry.sectionAxis?.destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 剖切盒按钮配置
|
||||
*/
|
||||
export const createSectionBoxButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createSectionBoxButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'section-box',
|
||||
groupId: 'group-1',
|
||||
@@ -18,12 +15,11 @@ export const createSectionBoxButton = (engine: BimEngine): ButtonConfig => {
|
||||
icon: getIcon('剖切盒'),
|
||||
onClick: (button) => {
|
||||
console.log('剖切盒被点击:', button.id, '激活状态:', button.isActive);
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
if (button.isActive) {
|
||||
// 激活时显示弹窗
|
||||
engine.sectionBox?.show();
|
||||
registry.sectionBox?.show();
|
||||
} else {
|
||||
// 关闭时隐藏弹窗
|
||||
engine.sectionBox?.hide();
|
||||
registry.sectionBox?.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
|
||||
/**
|
||||
* 剖切菜单按钮配置
|
||||
*/
|
||||
export const createSectionMenuButton = (_engine: BimEngine): ButtonConfig => {
|
||||
export const createSectionMenuButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'section',
|
||||
groupId: 'group-1',
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 拾取面剖切按钮配置
|
||||
*/
|
||||
export const createSectionPlaneButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createSectionPlaneButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'section-plane',
|
||||
groupId: 'group-1',
|
||||
@@ -18,12 +15,11 @@ export const createSectionPlaneButton = (engine: BimEngine): ButtonConfig => {
|
||||
icon: getIcon('拾曲面剖切'),
|
||||
onClick: (button) => {
|
||||
console.log('拾取面剖切被点击:', button.id, '激活状态:', button.isActive);
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
if (button.isActive) {
|
||||
// 激活时显示弹窗
|
||||
engine.sectionPlane?.show();
|
||||
registry.sectionPlane?.show();
|
||||
} else {
|
||||
// 关闭时隐藏弹窗
|
||||
engine.sectionPlane?.hide();
|
||||
registry.sectionPlane?.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
|
||||
/**
|
||||
* 设置按钮配置
|
||||
*/
|
||||
export const createSettingButton = (_engine: BimEngine): ButtonConfig => {
|
||||
export const createSettingButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'setting',
|
||||
groupId: 'group-2',
|
||||
@@ -14,7 +10,6 @@ export const createSettingButton = (_engine: BimEngine): ButtonConfig => {
|
||||
icon: getIcon('设置'),
|
||||
keepActive: false,
|
||||
onClick: (button) => {
|
||||
// 预留:未来接入设置逻辑(此处已注入 engine)
|
||||
console.log('设置按钮被点击:', button.id);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
|
||||
/**
|
||||
* 第三人称(鸟瞰)漫游按钮配置
|
||||
*/
|
||||
export const createWalkBirdButton = (_engine: BimEngine): ButtonConfig => {
|
||||
export const createWalkBirdButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'walk-bird',
|
||||
groupId: 'group-1',
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 漫游按钮配置(普通按钮,不带子菜单)
|
||||
*/
|
||||
export const createWalkMenuButton = (engine: BimEngine): ButtonConfig => {
|
||||
export const createWalkMenuButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'walk',
|
||||
groupId: 'group-1',
|
||||
@@ -15,7 +12,8 @@ export const createWalkMenuButton = (engine: BimEngine): ButtonConfig => {
|
||||
icon: getIcon('漫游'),
|
||||
onClick: () => {
|
||||
console.log('漫游按钮被点击');
|
||||
engine.walkControl?.show();
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.walkControl?.show();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import type { ButtonConfig } from '../../../../index.type';
|
||||
import type { BimEngine } from '../../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../../utils/icon-manager';
|
||||
|
||||
/**
|
||||
* 第一人称漫游按钮配置
|
||||
*/
|
||||
export const createWalkPersonButton = (_engine: BimEngine): ButtonConfig => {
|
||||
export const createWalkPersonButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'walk-person',
|
||||
groupId: 'group-1',
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import type { BimEngine } from '../../../../../bim-engine';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 选框放大按钮配置
|
||||
*
|
||||
* 说明:
|
||||
* - 当前仅添加 UI 按钮,点击事件先留空(后续接入引擎能力再实现)
|
||||
* - 使用工厂函数模式注入 engine,便于未来调用 engine API
|
||||
*/
|
||||
export const createZoomBoxButton = (engine: BimEngine): ButtonConfig => {
|
||||
return {
|
||||
id: 'zoom-box',
|
||||
groupId: 'group-1',
|
||||
keepActive: false,
|
||||
type: 'button',
|
||||
label: 'toolbar.zoomBox',
|
||||
icon: getIcon('框选放大'),
|
||||
onClick: () => {
|
||||
engine.engine?.getEngine().rangeScale.active();
|
||||
// 事件先留空:后续实现“框选放大/框选缩放”能力时再接入
|
||||
// 这里不做任何动作,避免误触影响用户操作
|
||||
}
|
||||
};
|
||||
export const createZoomBoxButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'zoom-box',
|
||||
groupId: 'group-1',
|
||||
keepActive: false,
|
||||
type: 'button',
|
||||
label: 'toolbar.zoomBox',
|
||||
icon: getIcon('框选放大'),
|
||||
onClick: () => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.engine3d?.getEngine().rangeScale.active();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import { BimButtonGroup } from '../index';
|
||||
|
||||
/**
|
||||
* 底部工具栏 (Toolbar)
|
||||
* BimButtonGroup 的子类,专门用于加载工具栏默认按钮。
|
||||
*/
|
||||
export class Toolbar extends BimButtonGroup {
|
||||
/**
|
||||
* 重写初始化,加载默认按钮
|
||||
*/
|
||||
public async init(): Promise<void> {
|
||||
await super.init();
|
||||
|
||||
// 动态加载默认按钮配置
|
||||
const { createHomeButton } = await import('./buttons/home');
|
||||
const { createZoomBoxButton } = await import('./buttons/zoom-box');
|
||||
const { createWalkMenuButton } = await import('./buttons/walk/walk-menu');
|
||||
@@ -28,26 +20,20 @@ export class Toolbar extends BimButtonGroup {
|
||||
|
||||
this.addGroup('group-1');
|
||||
|
||||
// 使用工厂函数创建按钮,并注入 engine
|
||||
if (this.engine) {
|
||||
this.addButton(createHomeButton(this.engine));
|
||||
// 你要求:在"首页"后面添加"选框放大"
|
||||
this.addButton(createZoomBoxButton(this.engine));
|
||||
this.addButton(createMeasureButton(this.engine));
|
||||
this.addButton(createSectionMenuButton(this.engine));
|
||||
this.addButton(createSectionPlaneButton(this.engine));
|
||||
this.addButton(createSectionAxisButton(this.engine));
|
||||
this.addButton(createSectionBoxButton(this.engine));
|
||||
this.addButton(createWalkMenuButton(this.engine));
|
||||
this.addButton(createMapButton(this.engine));
|
||||
this.addButton(createPropertyButton(this.engine));
|
||||
this.addGroup('group-2');
|
||||
this.addButton(createSettingButton(this.engine));
|
||||
this.addButton(createInfoButton(this.engine));
|
||||
this.addButton(createFullscreenButton(this.engine));
|
||||
} else {
|
||||
console.warn('[Toolbar] Engine not available when creating buttons.');
|
||||
}
|
||||
this.addButton(createHomeButton());
|
||||
this.addButton(createZoomBoxButton());
|
||||
this.addButton(createMeasureButton());
|
||||
this.addButton(createSectionMenuButton());
|
||||
this.addButton(createSectionPlaneButton());
|
||||
this.addButton(createSectionAxisButton());
|
||||
this.addButton(createSectionBoxButton());
|
||||
this.addButton(createWalkMenuButton());
|
||||
this.addButton(createMapButton());
|
||||
this.addButton(createPropertyButton());
|
||||
this.addGroup('group-2');
|
||||
this.addButton(createSettingButton());
|
||||
this.addButton(createInfoButton());
|
||||
this.addButton(createFullscreenButton());
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { BimEngine } from "../../../bim-engine";
|
||||
import { MenuItemConfig } from "../item";
|
||||
import { MenuItemConfig } from '../item';
|
||||
import { ManagerRegistry } from '../../../core/manager-registry';
|
||||
|
||||
export const fourMenuButton = (engine: BimEngine): MenuItemConfig => {
|
||||
export const fourMenuButton = (): MenuItemConfig => {
|
||||
return {
|
||||
id: "fourMenu",
|
||||
label: "menu.info",
|
||||
id: 'fourMenu',
|
||||
label: 'menu.info',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 7q.425 0 .713-.288T13 6t-.288-.712T12 5t-.712.288T11 6t.288.713T12 7m0 8q.425 0 .713-.288T13 14v-4q0-.425-.288-.712T12 9t-.712.288T11 10v4q0 .425.288.713T12 15m-6 3l-2.3 2.3q-.475.475-1.088.213T2 19.575V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v12q0 .825-.587 1.413T20 18z"/></svg>',
|
||||
onClick: () => {
|
||||
console.log('dianjile')
|
||||
engine.dialog?.showInfoDialog()
|
||||
engine.engine?.rightKey?.hide()
|
||||
console.log('dianjile');
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.dialog?.showInfoDialog();
|
||||
registry.engine3d?.rightKey?.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { BimEngine } from "../../../bim-engine";
|
||||
import { MenuItemConfig } from "../item";
|
||||
import { fourMenuButton } from "./four";
|
||||
import { secondMenuButton } from "./second";
|
||||
import { MenuItemConfig } from '../item';
|
||||
import { ManagerRegistry } from '../../../core/manager-registry';
|
||||
import { secondMenuButton } from './second';
|
||||
import { fourMenuButton } from './four';
|
||||
|
||||
export const homeMenuButton = (engine: BimEngine): MenuItemConfig => {
|
||||
export const homeMenuButton = (): MenuItemConfig => {
|
||||
return {
|
||||
id: "homeMenu",
|
||||
label: "menu.home",
|
||||
id: 'homeMenu',
|
||||
label: 'menu.home',
|
||||
group: 'home',
|
||||
children: [secondMenuButton(engine), fourMenuButton(engine)],
|
||||
children: [secondMenuButton(), fourMenuButton()],
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 7q.425 0 .713-.288T13 6t-.288-.712T12 5t-.712.288T11 6t.288.713T12 7m0 8q.425 0 .713-.288T13 14v-4q0-.425-.288-.712T12 9t-.712.288T11 10v4q0 .425.288.713T12 15m-6 3l-2.3 2.3q-.475.475-1.088.213T2 19.575V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v12q0 .825-.587 1.413T20 18z"/></svg>',
|
||||
onClick: () => {
|
||||
engine.dialog?.showInfoDialog()
|
||||
engine.engine?.rightKey?.hide()
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.dialog?.showInfoDialog();
|
||||
registry.engine3d?.rightKey?.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { BimEngine } from "../../../bim-engine";
|
||||
import { MenuItemConfig } from "../item";
|
||||
import { MenuItemConfig } from '../item';
|
||||
import { ManagerRegistry } from '../../../core/manager-registry';
|
||||
|
||||
export const infoMenuButton = (engine: BimEngine): MenuItemConfig => {
|
||||
export const infoMenuButton = (): MenuItemConfig => {
|
||||
return {
|
||||
id: "infoMenu",
|
||||
label: "menu.info",
|
||||
id: 'infoMenu',
|
||||
label: 'menu.info',
|
||||
group: 'info',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 7q.425 0 .713-.288T13 6t-.288-.712T12 5t-.712.288T11 6t.288.713T12 7m0 8q.425 0 .713-.288T13 14v-4q0-.425-.288-.712T12 9t-.712.288T11 10v4q0 .425.288.713T12 15m-6 3l-2.3 2.3q-.475.475-1.088.213T2 19.575V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v12q0 .825-.587 1.413T20 18z"/></svg>',
|
||||
onClick: () => {
|
||||
console.log('dianjile')
|
||||
engine.dialog?.showInfoDialog()
|
||||
engine.engine?.rightKey?.hide()
|
||||
console.log('dianjile');
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.dialog?.showInfoDialog();
|
||||
registry.engine3d?.rightKey?.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { BimEngine } from "../../../bim-engine";
|
||||
import { MenuItemConfig } from "../item";
|
||||
import { MenuItemConfig } from '../item';
|
||||
import { ManagerRegistry } from '../../../core/manager-registry';
|
||||
|
||||
export const secondMenuButton = (engine: BimEngine): MenuItemConfig => {
|
||||
export const secondMenuButton = (): MenuItemConfig => {
|
||||
return {
|
||||
id: "infoMenu",
|
||||
label: "menu.info",
|
||||
id: 'infoMenu',
|
||||
label: 'menu.info',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 7q.425 0 .713-.288T13 6t-.288-.712T12 5t-.712.288T11 6t.288.713T12 7m0 8q.425 0 .713-.288T13 14v-4q0-.425-.288-.712T12 9t-.712.288T11 10v4q0 .425.288.713T12 15m-6 3l-2.3 2.3q-.475.475-1.088.213T2 19.575V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v12q0 .825-.587 1.413T20 18z"/></svg>',
|
||||
onClick: () => {
|
||||
console.log('dianjile')
|
||||
engine.dialog?.showInfoDialog()
|
||||
engine.engine?.rightKey?.hide()
|
||||
console.log('dianjile');
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.dialog?.showInfoDialog();
|
||||
registry.engine3d?.rightKey?.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
144
src/core/base-dialog-manager.ts
Normal file
144
src/core/base-dialog-manager.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* 对话框管理器基类
|
||||
* 提供对话框的通用生命周期管理
|
||||
*/
|
||||
import { BaseManager } from './base-manager';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
|
||||
/** 对话框配置选项 */
|
||||
export interface DialogManagerOptions {
|
||||
/** 是否可拖拽 */
|
||||
draggable?: boolean;
|
||||
/** 是否可调整大小 */
|
||||
resizable?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框管理器抽象基类
|
||||
* 子类需要实现 dialogId、dialogTitle 和 createContent 方法
|
||||
*/
|
||||
export abstract class BaseDialogManager extends BaseManager {
|
||||
/** 对话框实例 */
|
||||
protected dialog: BimDialog | null = null;
|
||||
/** 对话框是否可见 */
|
||||
protected isVisible: boolean = false;
|
||||
|
||||
/** 对话框唯一标识(子类必须实现) */
|
||||
protected abstract get dialogId(): string;
|
||||
/** 对话框标题,支持国际化 key(子类必须实现) */
|
||||
protected abstract get dialogTitle(): string;
|
||||
|
||||
/** 对话框宽度,默认 300 */
|
||||
protected get dialogWidth(): number {
|
||||
return 300;
|
||||
}
|
||||
|
||||
/** 对话框高度,默认 'auto' 自适应 */
|
||||
protected get dialogHeight(): number | 'auto' {
|
||||
return 'auto';
|
||||
}
|
||||
|
||||
/** 对话框选项,默认可拖拽不可缩放 */
|
||||
protected get dialogOptions(): DialogManagerOptions {
|
||||
return { draggable: true, resizable: false };
|
||||
}
|
||||
|
||||
/** 创建对话框内容(子类必须实现) */
|
||||
protected abstract createContent(): HTMLElement;
|
||||
|
||||
/** 对话框关闭时的回调,子类可重写 */
|
||||
protected onDialogClose(): void {}
|
||||
/** 对话框创建后的回调,子类可重写 */
|
||||
protected onDialogCreated(): void {}
|
||||
/** 销毁前的回调,子类可重写 */
|
||||
protected onBeforeDestroy(): void {}
|
||||
|
||||
/**
|
||||
* 获取对话框位置
|
||||
* 默认定位在容器右侧居中,子类可重写
|
||||
*/
|
||||
protected getDialogPosition(): { x: number; y: number } {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const width = this.dialogWidth;
|
||||
const height = typeof this.dialogHeight === 'number' ? this.dialogHeight : 300;
|
||||
const paddingRight = 20;
|
||||
|
||||
return {
|
||||
x: containerWidth - width - paddingRight,
|
||||
y: (containerHeight - height) / 2
|
||||
};
|
||||
}
|
||||
|
||||
/** 显示对话框 */
|
||||
public show(): void {
|
||||
if (!this.registry.dialog || !this.registry.container) {
|
||||
console.warn(`[${this.dialogId}] Dialog manager or container is not initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dialog) {
|
||||
this.destroyDialog();
|
||||
}
|
||||
|
||||
const position = this.getDialogPosition();
|
||||
const content = this.createContent();
|
||||
const options = this.dialogOptions;
|
||||
|
||||
this.dialog = this.registry.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: this.dialogTitle,
|
||||
content,
|
||||
width: this.dialogWidth,
|
||||
height: this.dialogHeight,
|
||||
position,
|
||||
draggable: options.draggable,
|
||||
resizable: options.resizable,
|
||||
onClose: () => {
|
||||
this.onDialogClose();
|
||||
this.destroyDialog();
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
this.isVisible = true;
|
||||
this.onDialogCreated();
|
||||
}
|
||||
|
||||
/** 隐藏对话框 */
|
||||
public hide(): void {
|
||||
this.destroyDialog();
|
||||
}
|
||||
|
||||
/** 切换对话框显示状态 */
|
||||
public toggle(): void {
|
||||
if (this.isVisible) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取对话框是否打开 */
|
||||
public isOpen(): boolean {
|
||||
return this.isVisible;
|
||||
}
|
||||
|
||||
/** 销毁对话框 */
|
||||
protected destroyDialog(): void {
|
||||
this.onBeforeDestroy();
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
}
|
||||
this.isVisible = false;
|
||||
}
|
||||
|
||||
/** 销毁管理器 */
|
||||
public destroy(): void {
|
||||
this.destroyDialog();
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
56
src/core/base-manager.ts
Normal file
56
src/core/base-manager.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Manager 基类
|
||||
* 提供所有 Manager 的通用功能
|
||||
*/
|
||||
import { ManagerRegistry } from './manager-registry';
|
||||
import type { EngineEvents } from '../types/events';
|
||||
|
||||
/**
|
||||
* Manager 抽象基类
|
||||
* - 自动获取 Registry 实例
|
||||
* - 自动管理事件订阅的清理
|
||||
*/
|
||||
export abstract class BaseManager {
|
||||
/** 注册表实例 */
|
||||
protected registry: ManagerRegistry;
|
||||
/** 事件订阅列表,用于自动清理 */
|
||||
private subscriptions: Array<() => void> = [];
|
||||
|
||||
constructor() {
|
||||
this.registry = ManagerRegistry.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅事件(自动追踪,destroy 时自动取消)
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
protected subscribe<K extends keyof EngineEvents>(
|
||||
event: K,
|
||||
handler: (payload: EngineEvents[K]) => void
|
||||
): void {
|
||||
const unsubscribe = this.registry.on(event, handler);
|
||||
this.subscriptions.push(unsubscribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送事件
|
||||
* @param event 事件名称
|
||||
* @param payload 事件数据
|
||||
*/
|
||||
protected emit<K extends keyof EngineEvents>(
|
||||
event: K,
|
||||
payload: EngineEvents[K]
|
||||
): void {
|
||||
this.registry.emit(event, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁 Manager,清理所有订阅
|
||||
* 子类应该调用 super.destroy()
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.subscriptions.forEach(unsub => unsub());
|
||||
this.subscriptions = [];
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { EngineEvents } from '../types/events';
|
||||
|
||||
export abstract class BimComponent {
|
||||
protected engine: BimEngine;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to send events easily
|
||||
*/
|
||||
protected emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]): void {
|
||||
this.engine.emit(event, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to listen to events easily
|
||||
* Returns an unsubscribe function
|
||||
*/
|
||||
protected on<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): () => void {
|
||||
return this.engine.on(event, listener);
|
||||
}
|
||||
|
||||
abstract destroy(): void;
|
||||
}
|
||||
@@ -1,18 +1,39 @@
|
||||
/**
|
||||
* 事件发射器
|
||||
* 提供简单的发布/订阅模式实现
|
||||
*/
|
||||
|
||||
/** 事件监听器类型 */
|
||||
type Listener<T = any> = (payload: T) => void;
|
||||
|
||||
/**
|
||||
* 事件发射器类
|
||||
* 用于组件间的事件通信
|
||||
*/
|
||||
export class EventEmitter {
|
||||
/** 事件监听器映射表 */
|
||||
private events: Map<string, Listener[]> = new Map();
|
||||
|
||||
/**
|
||||
* 订阅事件
|
||||
* @param event 事件名称
|
||||
* @param listener 事件处理函数
|
||||
* @returns 取消订阅的函数
|
||||
*/
|
||||
public on(event: string, listener: Listener): () => void {
|
||||
if (!this.events.has(event)) {
|
||||
this.events.set(event, []);
|
||||
}
|
||||
this.events.get(event)!.push(listener);
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => this.off(event, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅事件
|
||||
* @param event 事件名称
|
||||
* @param listener 要移除的事件处理函数
|
||||
*/
|
||||
public off(event: string, listener: Listener): void {
|
||||
const listeners = this.events.get(event);
|
||||
if (!listeners) return;
|
||||
@@ -23,6 +44,11 @@ export class EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送事件
|
||||
* @param event 事件名称
|
||||
* @param payload 事件数据
|
||||
*/
|
||||
public emit(event: string, payload?: any): void {
|
||||
const listeners = this.events.get(event);
|
||||
if (listeners) {
|
||||
@@ -36,6 +62,7 @@ export class EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/** 清除所有事件监听器 */
|
||||
public clear(): void {
|
||||
this.events.clear();
|
||||
}
|
||||
|
||||
126
src/core/manager-registry.ts
Normal file
126
src/core/manager-registry.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Manager 注册表
|
||||
* 全局单例,提供所有 Manager 的集中访问点
|
||||
*/
|
||||
import { EventEmitter } from './event-emitter';
|
||||
import type { EngineEvents } from '../types/events';
|
||||
|
||||
import type { ToolbarManager } from '../managers/toolbar-manager';
|
||||
import type { DialogManager } from '../managers/dialog-manager';
|
||||
import type { EngineManager } from '../managers/engine-manager';
|
||||
import type { ButtonGroupManager } from '../managers/button-group-manager';
|
||||
import type { RightKeyManager } from '../managers/right-key-manager';
|
||||
import type { ConstructTreeManagerBtn } from '../managers/construct-tree-manager-btn';
|
||||
import type { PropertyPanelManager } from '../managers/property-panel-manager';
|
||||
import type { MeasureDialogManager } from '../managers/measure-dialog-manager';
|
||||
import type { WalkControlManager } from '../managers/walk-control-manager';
|
||||
import type { MapDialogManager } from '../managers/map-dialog-manager';
|
||||
import type { SectionPlaneDialogManager } from '../managers/section-plane-dialog-manager';
|
||||
import type { SectionAxisDialogManager } from '../managers/section-axis-dialog-manager';
|
||||
import type { SectionBoxDialogManager } from '../managers/section-box-dialog-manager';
|
||||
import type { WalkPathDialogManager } from '../managers/walk-path-dialog-manager';
|
||||
import type { WalkPlanViewDialogManager } from '../managers/walk-plan-view-dialog-manager';
|
||||
|
||||
/**
|
||||
* Manager 注册表 - 单例模式
|
||||
* 提供所有 Manager 的全局访问点,替代 engine 层层传递
|
||||
*/
|
||||
export class ManagerRegistry {
|
||||
/** 单例实例 */
|
||||
private static instance: ManagerRegistry | null = null;
|
||||
/** 事件发射器 */
|
||||
private eventEmitter: EventEmitter = new EventEmitter();
|
||||
|
||||
/** 主容器元素 */
|
||||
public container: HTMLElement | null = null;
|
||||
/** 包装容器元素 */
|
||||
public wrapper: HTMLElement | null = null;
|
||||
|
||||
/** 工具栏管理器 */
|
||||
public toolbar: ToolbarManager | null = null;
|
||||
/** 对话框管理器 */
|
||||
public dialog: DialogManager | null = null;
|
||||
/** 3D 引擎管理器 */
|
||||
public engine3d: EngineManager | null = null;
|
||||
/** 按钮组管理器 */
|
||||
public buttonGroup: ButtonGroupManager | null = null;
|
||||
/** 右键菜单管理器 */
|
||||
public rightKey: RightKeyManager | null = null;
|
||||
/** 构件树管理器 */
|
||||
public constructTree: ConstructTreeManagerBtn | null = null;
|
||||
/** 属性面板管理器 */
|
||||
public propertyPanel: PropertyPanelManager | null = null;
|
||||
/** 测量对话框管理器 */
|
||||
public measure: MeasureDialogManager | null = null;
|
||||
/** 漫游控制管理器 */
|
||||
public walkControl: WalkControlManager | null = null;
|
||||
/** 地图对话框管理器 */
|
||||
public map: MapDialogManager | null = null;
|
||||
/** 拾取面剖切对话框管理器 */
|
||||
public sectionPlane: SectionPlaneDialogManager | null = null;
|
||||
/** 轴向剖切对话框管理器 */
|
||||
public sectionAxis: SectionAxisDialogManager | null = null;
|
||||
/** 剖切盒对话框管理器 */
|
||||
public sectionBox: SectionBoxDialogManager | null = null;
|
||||
/** 漫游路径对话框管理器 */
|
||||
public walkPath: WalkPathDialogManager | null = null;
|
||||
/** 漫游平面图对话框管理器 */
|
||||
public walkPlanView: WalkPlanViewDialogManager | null = null;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/** 获取单例实例 */
|
||||
public static getInstance(): ManagerRegistry {
|
||||
if (!ManagerRegistry.instance) {
|
||||
ManagerRegistry.instance = new ManagerRegistry();
|
||||
}
|
||||
return ManagerRegistry.instance;
|
||||
}
|
||||
|
||||
/** 重置单例(用于测试或重新初始化) */
|
||||
public static reset(): void {
|
||||
if (ManagerRegistry.instance) {
|
||||
ManagerRegistry.instance.eventEmitter.clear();
|
||||
ManagerRegistry.instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送事件
|
||||
* @param event 事件名称
|
||||
* @param payload 事件数据
|
||||
*/
|
||||
public emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]): void {
|
||||
this.eventEmitter.emit(event, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅事件
|
||||
* @param event 事件名称
|
||||
* @param listener 事件处理函数
|
||||
* @returns 取消订阅的函数
|
||||
*/
|
||||
public on<K extends keyof EngineEvents>(
|
||||
event: K,
|
||||
listener: (payload: EngineEvents[K]) => void
|
||||
): () => void {
|
||||
return this.eventEmitter.on(event, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅事件
|
||||
* @param event 事件名称
|
||||
* @param listener 事件处理函数
|
||||
*/
|
||||
public off<K extends keyof EngineEvents>(
|
||||
event: K,
|
||||
listener: (payload: EngineEvents[K]) => void
|
||||
): void {
|
||||
this.eventEmitter.off(event, listener);
|
||||
}
|
||||
|
||||
/** 清除所有事件监听器 */
|
||||
public clearEvents(): void {
|
||||
this.eventEmitter.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,65 @@
|
||||
/**
|
||||
* 按钮组管理器
|
||||
* 负责创建和管理按钮组实例
|
||||
*/
|
||||
import { BimButtonGroup } from '../components/button-group';
|
||||
import type { ButtonGroupOptions } from '../components/button-group/index.type';
|
||||
import type { ThemeConfig } from '../themes/types';
|
||||
import { BimComponent } from '../core/component';
|
||||
import type { BimEngine } from '../bim-engine';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
|
||||
/**
|
||||
* 通用按钮组管理器 (ButtonGroupManager)
|
||||
* 负责创建和管理通用的按钮组实例。
|
||||
* 按钮组管理器
|
||||
* 统一管理多个按钮组的创建、主题更新和销毁
|
||||
*/
|
||||
export class ButtonGroupManager extends BimComponent {
|
||||
export class ButtonGroupManager extends BaseManager {
|
||||
/** 按钮组映射表 */
|
||||
private groups: Map<string, BimButtonGroup> = new Map();
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建按钮组
|
||||
* @param id 按钮组 ID
|
||||
* @param options 按钮组配置
|
||||
* @returns 按钮组实例
|
||||
*/
|
||||
public create(id: string, options: Omit<ButtonGroupOptions, 'container'>): BimButtonGroup {
|
||||
const group = new BimButtonGroup({
|
||||
container: this.container,
|
||||
...options
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
group.setEngine(this.engine);
|
||||
|
||||
group.init();
|
||||
this.groups.set(id, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取按钮组
|
||||
* @param id 按钮组 ID
|
||||
* @returns 按钮组实例
|
||||
*/
|
||||
public get(id: string): BimButtonGroup | undefined {
|
||||
return this.groups.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有按钮组的主题
|
||||
* @param theme 主题配置
|
||||
*/
|
||||
public updateTheme(theme: ThemeConfig) {
|
||||
this.groups.forEach(group => group.setTheme(theme));
|
||||
}
|
||||
|
||||
/** 销毁管理器和所有按钮组 */
|
||||
public destroy() {
|
||||
this.groups.forEach(group => group.destroy());
|
||||
this.groups.clear();
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import type {ButtonGroupColors, ButtonConfig} from '../components/button-group/index.type';
|
||||
import {Toolbar} from '../components/button-group/toolbar';
|
||||
import {BimComponent} from '../core/component';
|
||||
import type {BimEngine} from '../bim-engine';
|
||||
import {BimButtonGroup} from "../components/button-group";
|
||||
import {BimTree} from "../components/tree";
|
||||
import {TreeNodeConfig} from "../components/tree/types.ts";
|
||||
import {BimDialog} from "../components/dialog";
|
||||
import {BimTab} from "../components/tab";
|
||||
import {getIcon} from "../utils/icon-manager";
|
||||
/**
|
||||
* 构件树管理器
|
||||
* 负责管理构件树按钮和构件树对话框
|
||||
*/
|
||||
import type { ButtonGroupColors, ButtonConfig } from '../components/button-group/index.type';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { BimButtonGroup } from '../components/button-group';
|
||||
import { BimTree } from '../components/tree';
|
||||
import { TreeNodeConfig } from '../components/tree/types';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
import { BimTab } from '../components/tab';
|
||||
import { getIcon } from '../utils/icon-manager';
|
||||
|
||||
const MOCK_STRUCT_DATA: TreeNodeConfig[] =[
|
||||
/** 模拟的构件树数据 */
|
||||
const MOCK_STRUCT_DATA: TreeNodeConfig[] = [
|
||||
{
|
||||
id: 'root',
|
||||
label: '全部构件',
|
||||
@@ -20,10 +23,10 @@ const MOCK_STRUCT_DATA: TreeNodeConfig[] =[
|
||||
id: 'level-1',
|
||||
label: '一层',
|
||||
expanded: false,
|
||||
icon:'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M20.73 16.52V7.59a.7.7 0 0 0-.08-.33a.74.74 0 0 0-.36-.36l-8-3.58a.75.75 0 0 0-.62 0l-8 3.58a.8.8 0 0 0-.44.69v8.82a.83.83 0 0 0 .44.69l8 3.58a.72.72 0 0 0 .62 0l8-3.58a.77.77 0 0 0 .44-.58m-16-7.78l6.5 2.92v7.18l-6.5-2.91Zm8 2.92l6.5-2.92v7.19l-6.5 2.91ZM12 4.82l6.17 2.77L12 10.35L5.83 7.59Z"/></svg>',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M20.73 16.52V7.59a.7.7 0 0 0-.08-.33a.74.74 0 0 0-.36-.36l-8-3.58a.75.75 0 0 0-.62 0l-8 3.58a.8.8 0 0 0-.44.69v8.82a.83.83 0 0 0 .44.69l8 3.58a.72.72 0 0 0 .62 0l8-3.58a.77.77 0 0 0 .44-.58m-16-7.78l6.5 2.92v7.18l-6.5-2.91Zm8 2.92l6.5-2.92v7.19l-6.5 2.91ZM12 4.82l6.17 2.77L12 10.35L5.83 7.59Z"/></svg>',
|
||||
clickAction: 'expand',
|
||||
children: [
|
||||
{ id: 'l1-wall', label: '墙体(128)'},
|
||||
{ id: 'l1-wall', label: '墙体(128)' },
|
||||
{ id: 'l1-column', label: '柱(46)' },
|
||||
{ id: 'l1-beam', label: '梁(82)' },
|
||||
{ id: 'l1-slab', label: '楼板(12)' },
|
||||
@@ -75,23 +78,27 @@ const MOCK_STRUCT_DATA: TreeNodeConfig[] =[
|
||||
];
|
||||
|
||||
/**
|
||||
* 底部工具栏管理器 (ToolbarManager)
|
||||
* 仅负责管理底部工具栏实例。
|
||||
* 构件树管理器
|
||||
* 管理左上角的构件树按钮和对话框
|
||||
*/
|
||||
export class ConstructTreeManagerBtn extends BimComponent {
|
||||
private toolbar: Toolbar | null = null;
|
||||
export class ConstructTreeManagerBtn extends BaseManager {
|
||||
/** 按钮组实例 */
|
||||
private toolbar: BimButtonGroup | null = null;
|
||||
/** 按钮容器元素 */
|
||||
private toolbarContainer: HTMLElement | null = null;
|
||||
/** 主容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 构件树对话框实例 */
|
||||
private dialog: BimDialog | null = null;
|
||||
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.container = container;
|
||||
this.init();
|
||||
}
|
||||
|
||||
/** 初始化按钮 */
|
||||
private init() {
|
||||
// 创建底部工具栏专用容器
|
||||
this.toolbarContainer = document.createElement('div');
|
||||
this.toolbarContainer.id = 'bim-construct-tree';
|
||||
this.container.appendChild(this.toolbarContainer);
|
||||
@@ -99,12 +106,11 @@ export class ConstructTreeManagerBtn extends BimComponent {
|
||||
container: this.toolbarContainer,
|
||||
showLabel: false,
|
||||
direction: 'column',
|
||||
position: 'top-left', // 底部居中
|
||||
align: 'vertical', // 图标在上
|
||||
expand: 'up' // 向上展开
|
||||
position: 'top-left',
|
||||
align: 'vertical',
|
||||
expand: 'up'
|
||||
});
|
||||
this.toolbar.init();
|
||||
this.toolbar.setEngine(this.engine);
|
||||
this.toolbar.addGroup('construct-tree');
|
||||
this.toolbar.addButton({
|
||||
id: 'construct-tree-btn',
|
||||
@@ -113,16 +119,16 @@ export class ConstructTreeManagerBtn extends BimComponent {
|
||||
label: 'construct-tree',
|
||||
icon: getIcon('目录树'),
|
||||
onClick: () => {
|
||||
this.openConstructTreeDialog()
|
||||
this.openConstructTreeDialog();
|
||||
}
|
||||
});
|
||||
this.toolbar.render();
|
||||
}
|
||||
|
||||
/** 打开构件树对话框 */
|
||||
public openConstructTreeDialog() {
|
||||
this.setVisible(false);
|
||||
|
||||
// 构件树实例(放在“构件”标签内)
|
||||
const tree = new BimTree({
|
||||
data: MOCK_STRUCT_DATA,
|
||||
checkable: true,
|
||||
@@ -146,18 +152,15 @@ export class ConstructTreeManagerBtn extends BimComponent {
|
||||
});
|
||||
tree.init();
|
||||
|
||||
// 系统/空间暂留空占位,可后续填充业务内容
|
||||
const systemPlaceholder = document.createElement('div');
|
||||
systemPlaceholder.className = 'construct-tab__panel-content';
|
||||
const spacePlaceholder = document.createElement('div');
|
||||
spacePlaceholder.className = 'construct-tab__panel-content';
|
||||
|
||||
// 构件面板容器,确保内部树区域可滚动
|
||||
const componentPanel = document.createElement('div');
|
||||
componentPanel.className = 'construct-tab__panel-content';
|
||||
componentPanel.appendChild(tree.element);
|
||||
|
||||
// 创建 Tab 容器(仅在本弹窗内使用,不额外挂 Manager)
|
||||
const tabMount = document.createElement('div');
|
||||
tabMount.className = 'construct-tab__container';
|
||||
tabMount.style.height = '100%';
|
||||
@@ -165,24 +168,23 @@ export class ConstructTreeManagerBtn extends BimComponent {
|
||||
const tab = new BimTab({
|
||||
container: tabMount,
|
||||
tabs: [
|
||||
{id: 'component', title: 'tab.component', content: componentPanel},
|
||||
{id: 'system', title: 'tab.system', content: systemPlaceholder},
|
||||
{id: 'space', title: 'tab.space', content: spacePlaceholder},
|
||||
{ id: 'component', title: 'tab.component', content: componentPanel },
|
||||
{ id: 'system', title: 'tab.system', content: systemPlaceholder },
|
||||
{ id: 'space', title: 'tab.space', content: spacePlaceholder },
|
||||
],
|
||||
activeId: 'component',
|
||||
onChange: () => {
|
||||
// 切换后根据内容宽度刷新弹窗
|
||||
this.dialog?.fitWidth();
|
||||
}
|
||||
});
|
||||
tab.init();
|
||||
|
||||
this.dialog = this.engine.dialog!.create({
|
||||
this.dialog = this.registry.dialog!.create({
|
||||
title: 'constructTree.title',
|
||||
minWidth: 320,
|
||||
height: 420,
|
||||
content: tabMount,
|
||||
position: {x: 20, y: 20},
|
||||
position: { x: 20, y: 20 },
|
||||
resizable: false,
|
||||
onClose: () => {
|
||||
tab.destroy();
|
||||
@@ -193,44 +195,76 @@ export class ConstructTreeManagerBtn extends BimComponent {
|
||||
this.dialog?.fitWidth();
|
||||
}
|
||||
|
||||
/** 刷新渲染 */
|
||||
public refresh() {
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
/** 销毁管理器 */
|
||||
public destroy() {
|
||||
this.toolbar?.destroy();
|
||||
this.toolbar = null;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
// --- 转发 API ---
|
||||
/**
|
||||
* 添加按钮组
|
||||
* @param groupId 组 ID
|
||||
* @param beforeGroupId 插入位置
|
||||
*/
|
||||
public addGroup(groupId: string, beforeGroupId?: string) {
|
||||
this.toolbar?.addGroup(groupId, beforeGroupId);
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加按钮
|
||||
* @param config 按钮配置
|
||||
*/
|
||||
public addButton(config: ButtonConfig) {
|
||||
this.toolbar?.addButton(config);
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮可见性
|
||||
* @param id 按钮 ID
|
||||
* @param v 是否可见
|
||||
*/
|
||||
public setButtonVisibility(id: string, v: boolean) {
|
||||
this.toolbar?.updateButtonVisibility(id, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否显示标签
|
||||
* @param show 是否显示
|
||||
*/
|
||||
public setShowLabel(show: boolean) {
|
||||
this.toolbar?.setShowLabel(show);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮组可见性
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
public setVisible(visible: boolean) {
|
||||
if (this.toolbarContainer) {
|
||||
this.toolbarContainer.style.visibility = visible ? 'visible' : 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置背景颜色
|
||||
* @param color 颜色值
|
||||
*/
|
||||
public setBackgroundColor(color: string) {
|
||||
this.toolbar?.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮组颜色
|
||||
* @param colors 颜色配置
|
||||
*/
|
||||
public setColors(colors: ButtonGroupColors) {
|
||||
this.toolbar?.setColors(colors);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
/**
|
||||
* 对话框管理器
|
||||
* 负责创建和管理所有对话框实例
|
||||
*/
|
||||
import { BimDialog } from '../components/dialog';
|
||||
import { BimInfoDialog } from '../components/dialog/bimInfoDialog';
|
||||
import type { DialogOptions } from '../components/dialog/index.type';
|
||||
import type { ThemeConfig } from '../themes/types';
|
||||
import { themeManager } from '../services/theme';
|
||||
import { BimComponent } from '../core/component';
|
||||
import type { BimEngine } from '../bim-engine';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
|
||||
/**
|
||||
* 弹窗管理器
|
||||
* 负责创建和管理应用中的各类弹窗。
|
||||
* 对话框管理器
|
||||
* 统一管理对话框的创建、主题更新和销毁
|
||||
*/
|
||||
export class DialogManager extends BimComponent {
|
||||
/** 弹窗挂载的父容器 */
|
||||
export class DialogManager extends BaseManager {
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 活跃的弹窗实例列表 */
|
||||
/** 活跃的对话框列表 */
|
||||
private activeDialogs: BimDialog[] = [];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param engine 引擎实例
|
||||
* @param container 弹窗挂载的目标容器
|
||||
*/
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.container = container;
|
||||
|
||||
// 监听打开弹窗事件
|
||||
this.on('ui:open-dialog', (payload) => {
|
||||
// 这里可以根据 payload.id 做更复杂的逻辑,目前简单演示
|
||||
this.subscribe('ui:open-dialog', (payload) => {
|
||||
console.log('[DialogManager] Received open-dialog event:', payload);
|
||||
// 示例:如果 payload.id 是 'info',则打开 info dialog
|
||||
if (payload.id === 'info') {
|
||||
this.showInfoDialog();
|
||||
}
|
||||
@@ -37,41 +32,33 @@ export class DialogManager extends BimComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个通用弹窗
|
||||
* @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)
|
||||
* @returns BimDialog 实例
|
||||
* 创建对话框
|
||||
* @param options 对话框配置选项
|
||||
* @returns 对话框实例
|
||||
*/
|
||||
public create(options: Omit<DialogOptions, 'container'>): BimDialog {
|
||||
const dialog = new BimDialog({
|
||||
container: this.container,
|
||||
...options,
|
||||
onClose: () => {
|
||||
// 从活跃列表中移除
|
||||
this.activeDialogs = this.activeDialogs.filter(d => d !== dialog);
|
||||
if (options.onClose) options.onClose();
|
||||
}
|
||||
});
|
||||
|
||||
// 应用当前主题
|
||||
dialog.setTheme(themeManager.getTheme());
|
||||
|
||||
this.activeDialogs.push(dialog);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示二次封装的模型信息弹窗
|
||||
* 演示如何调用特定的业务弹窗组件
|
||||
*/
|
||||
/** 显示信息对话框 */
|
||||
public showInfoDialog() {
|
||||
// 最佳实践:所有弹窗应通过 create 统一管理,或者手动加入管理。
|
||||
new BimInfoDialog(this.container);
|
||||
// 暂时不做主题追踪,作为遗留逻辑保留
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应全局主题变更
|
||||
* @param theme 全局主题配置
|
||||
* 更新所有对话框的主题
|
||||
* @param theme 主题配置
|
||||
*/
|
||||
public updateTheme(theme: ThemeConfig) {
|
||||
this.activeDialogs.forEach(dialog => {
|
||||
@@ -81,8 +68,10 @@ export class DialogManager extends BimComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/** 销毁管理器和所有对话框 */
|
||||
public destroy() {
|
||||
this.activeDialogs.forEach(d => d.destroy());
|
||||
this.activeDialogs = [];
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/**
|
||||
* 3D 引擎管理器
|
||||
* 负责管理 3D 渲染引擎的初始化、模型加载和测量功能
|
||||
*/
|
||||
import { Engine, type EngineOptions, type ModelLoadOptions } from '../components/engine';
|
||||
import { BimComponent } from '../core/component';
|
||||
import type { BimEngine } from '../bim-engine';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { RightKeyManager } from './right-key-manager';
|
||||
import { infoMenuButton } from '../components/menu/buttons/info';
|
||||
import { homeMenuButton } from '../components/menu/buttons/home';
|
||||
@@ -8,34 +11,27 @@ import type { MeasureMode } from '../types/measure';
|
||||
|
||||
/**
|
||||
* 3D 引擎管理器
|
||||
* 负责连接 Engine 组件和 BimEngine,向外部暴露简化的 API
|
||||
* 采用延迟初始化模式,用户需主动调用 initialize() 方法
|
||||
* 封装底层 3D 引擎,提供模型加载、相机控制、测量等功能
|
||||
*/
|
||||
export class EngineManager extends BimComponent {
|
||||
/** 3D 引擎挂载的父容器 */
|
||||
export class EngineManager extends BaseManager {
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 3D 引擎组件实例 */
|
||||
/** 引擎实例 */
|
||||
private engineInstance: Engine | null = null;
|
||||
/** 右键菜单管理器 */
|
||||
public rightKey: RightKeyManager | null = null;
|
||||
|
||||
public rightKey: RightKeyManager | null = null; // 右键菜单管理器
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param engine 引擎实例
|
||||
* @param container 3D 引擎挂载的目标容器
|
||||
*/
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 3D 引擎
|
||||
* @param options 引擎配置选项(可选,如果不提供则使用默认配置)
|
||||
* @param options 引擎配置选项
|
||||
* @returns 是否初始化成功
|
||||
*/
|
||||
public initialize(options?: Omit<EngineOptions, 'container'>): boolean {
|
||||
// 如果已经初始化,先销毁旧的实例
|
||||
if (this.engineInstance && this.engineInstance.isInitialized()) {
|
||||
console.warn('[EngineManager] 3D Engine already initialized. Destroying old instance...');
|
||||
this.engineInstance.destroy();
|
||||
@@ -43,24 +39,19 @@ export class EngineManager extends BimComponent {
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建 Engine 组件实例
|
||||
// options 中的配置会自动复制给 createEngine 使用
|
||||
this.engineInstance = new Engine({
|
||||
container: this.container,
|
||||
...options, // 合并配置选项
|
||||
...options,
|
||||
});
|
||||
|
||||
// 调用组件的 init 方法初始化引擎
|
||||
this.engineInstance.init();
|
||||
|
||||
// 初始化右键 (移到 return 之前)
|
||||
this.rightKey = new RightKeyManager(this.engine, this.container);
|
||||
this.rightKey = new RightKeyManager(this.container);
|
||||
|
||||
// 注册默认右键菜单
|
||||
this.rightKey.registerHandler((_e) => {
|
||||
return [
|
||||
infoMenuButton(this.engine),
|
||||
homeMenuButton(this.engine)
|
||||
infoMenuButton(),
|
||||
homeMenuButton()
|
||||
];
|
||||
});
|
||||
|
||||
@@ -71,16 +62,19 @@ export class EngineManager extends BimComponent {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检<EFBFBD><EFBFBD><EFBFBD> 3D 引擎是否已初始化
|
||||
* 检查引擎是否已初始化
|
||||
* @returns 是否已初始化
|
||||
*/
|
||||
public isInitialized(): boolean {
|
||||
return this.engineInstance !== null && this.engineInstance.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 3D 模型
|
||||
* @param url 模型文件 URL
|
||||
* @param options 加载选项(位置、旋转、缩放)
|
||||
* 加载模型
|
||||
* @param url 模型 URL
|
||||
* @param options 加载选项
|
||||
*/
|
||||
public loadModel(url: string, options?: ModelLoadOptions): void {
|
||||
if (!this.engineInstance) {
|
||||
@@ -90,10 +84,9 @@ export class EngineManager extends BimComponent {
|
||||
this.engineInstance.loadModel(url, options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取原始 3D 引擎实例
|
||||
* 用于直接调用第三方引擎的其他 API
|
||||
* 获取底层引擎实例
|
||||
* @returns 引擎实例
|
||||
*/
|
||||
public getEngine(): any {
|
||||
if (!this.engineInstance) {
|
||||
@@ -103,9 +96,7 @@ export class EngineManager extends BimComponent {
|
||||
return this.engineInstance.getEngine();
|
||||
}
|
||||
|
||||
/**
|
||||
* 回到主视角
|
||||
*/
|
||||
/** 相机回到初始位置 */
|
||||
public CameraGoHome(): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
@@ -113,9 +104,10 @@ export class EngineManager extends BimComponent {
|
||||
}
|
||||
this.engineInstance.CameraGoHome();
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活测量功能
|
||||
* @param mode 测量类型
|
||||
* 激活测量模式
|
||||
* @param mode 测量模式
|
||||
*/
|
||||
public activateMeasure(mode: MeasureMode): void {
|
||||
if (!this.engineInstance) {
|
||||
@@ -125,9 +117,7 @@ export class EngineManager extends BimComponent {
|
||||
this.engineInstance.activateMeasure(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用测量功能
|
||||
*/
|
||||
/** 停用测量模式 */
|
||||
public deactivateMeasure(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
@@ -136,7 +126,8 @@ export class EngineManager extends BimComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前激活的测量类型
|
||||
* 获取当前测量类型
|
||||
* @returns 当前测量模式
|
||||
*/
|
||||
public getCurrentMeasureType(): MeasureMode | null {
|
||||
if (!this.engineInstance) {
|
||||
@@ -145,11 +136,7 @@ export class EngineManager extends BimComponent {
|
||||
return this.engineInstance.getCurrentMeasureType();
|
||||
}
|
||||
|
||||
// ==================== 结束:测量功能方法 ====================
|
||||
|
||||
/**
|
||||
* 销毁 3D 引擎实例
|
||||
*/
|
||||
/** 销毁引擎管理器 */
|
||||
public destroy(): void {
|
||||
if (this.engineInstance) {
|
||||
this.engineInstance.destroy();
|
||||
@@ -159,8 +146,6 @@ export class EngineManager extends BimComponent {
|
||||
this.rightKey.destroy();
|
||||
this.rightKey = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,107 +1,76 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
/**
|
||||
* 地图对话框管理器
|
||||
* 负责管理地图/平面图对话框的显示和交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { MapPanel } from '../components/map-panel';
|
||||
|
||||
/**
|
||||
* 地图弹窗管理器(独立通用组件)
|
||||
* 地图对话框管理器
|
||||
* 继承自 BaseDialogManager,提供地图面板的对话框管理功能
|
||||
*/
|
||||
export class MapDialogManager extends BimComponent {
|
||||
private dialogId = 'map-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class MapDialogManager extends BaseDialogManager {
|
||||
/** 地图面板实例 */
|
||||
private panel: MapPanel | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
}
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId() { return 'map-dialog'; }
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle() { return 'map.dialogTitle'; }
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth() { return 300; }
|
||||
/** 对话框高度 */
|
||||
protected get dialogHeight(): number { return 400; }
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
}
|
||||
/** 初始化 */
|
||||
public init(): void {}
|
||||
|
||||
/**
|
||||
* 显示弹窗
|
||||
* 获取对话框位置
|
||||
* 定位在容器左下角
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
protected getDialogPosition() {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 20, y: 100 };
|
||||
|
||||
// 如果已打开,不重复打开
|
||||
if (this.isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建面板
|
||||
this.panel = new MapPanel();
|
||||
this.panel.init();
|
||||
|
||||
const dialogWidth = 300;
|
||||
const dialogHeight = 400;
|
||||
const paddingLeft = 20;
|
||||
const paddingBottom = 20;
|
||||
const container = this.engine.container;
|
||||
const containerHeight = container.clientHeight;
|
||||
|
||||
// 左下角:left: 20px, bottom: 20px
|
||||
const x = paddingLeft;
|
||||
const y = containerHeight - dialogHeight - paddingBottom;
|
||||
return {
|
||||
x: paddingLeft,
|
||||
y: containerHeight - this.dialogHeight - paddingBottom
|
||||
};
|
||||
}
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'map.dialogTitle',
|
||||
width: dialogWidth,
|
||||
height: dialogHeight,
|
||||
position: { x, y },
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
content: this.panel.element,
|
||||
onClose: () => {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
/** 创建对话框内容 */
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new MapPanel();
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
// 触发地图打开事件
|
||||
/** 对话框创建后的回调 */
|
||||
protected onDialogCreated(): void {
|
||||
this.emit('map:opened', {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏弹窗
|
||||
*/
|
||||
public hide(): void {
|
||||
this.destroy();
|
||||
// 触发地图关闭事件
|
||||
/** 对话框关闭时的回调 */
|
||||
protected onDialogClose(): void {
|
||||
this.emit('map:closed', {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查地图是否打开
|
||||
*/
|
||||
public isOpen(): boolean {
|
||||
return this.dialog !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁弹窗和面板
|
||||
*/
|
||||
public destroy(): void {
|
||||
// 先保存 dialog 引用,避免在回调中重复调用
|
||||
const dialog = this.dialog;
|
||||
|
||||
// 立即清空引用,防止递归
|
||||
this.dialog = null;
|
||||
|
||||
// 关闭弹窗
|
||||
if (dialog) {
|
||||
dialog.destroy();
|
||||
}
|
||||
|
||||
// 销毁面板
|
||||
/** 销毁前的清理 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 隐藏对话框 */
|
||||
public hide(): void {
|
||||
super.hide();
|
||||
this.emit('map:closed', {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,100 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from "../components/dialog";
|
||||
/**
|
||||
* 测量对话框管理器
|
||||
* 负责管理测量工具对话框的显示、隐藏和测量面板的交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { MeasurePanel } from '../components/measure-panel';
|
||||
import type { MeasureConfig, MeasureMode, MeasureResult } from '../components/measure-panel/types';
|
||||
|
||||
/**
|
||||
* 测量弹窗管理器
|
||||
* 测量对话框管理器
|
||||
* 继承自 BaseDialogManager,提供测量工具的对话框管理功能
|
||||
*/
|
||||
export class MeasureDialogManager extends BimComponent {
|
||||
private dialogId = 'measure-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class MeasureDialogManager extends BaseDialogManager {
|
||||
/** 测量面板实例 */
|
||||
private panel: MeasurePanel | null = null;
|
||||
/**
|
||||
* 测量配置项(单位/精度)
|
||||
* 说明:MeasurePanel 会自行从缓存加载默认配置,Manager 这里只做“对外读取/设置”的镜像。
|
||||
*/
|
||||
/** 测量配置(单位、精度等) */
|
||||
private config: MeasureConfig | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId(): string {
|
||||
return 'measure-dialog';
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle(): string {
|
||||
return 'measure.dialogTitle';
|
||||
}
|
||||
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth(): number {
|
||||
return 250;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示测量弹窗
|
||||
* 创建对话框内容
|
||||
* 初始化测量面板并设置回调
|
||||
*/
|
||||
public show() {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
const dialogWidth = 250;
|
||||
const dialogHeight = 300;
|
||||
const paddingRight = 20; // 你想要的右边距
|
||||
const container = this.engine.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const x = containerWidth - dialogWidth - paddingRight;
|
||||
const y = (containerHeight - dialogHeight) / 2;
|
||||
|
||||
// 如果已打开过,先销毁旧实例,避免重复创建/重复订阅
|
||||
this.destroy();
|
||||
|
||||
// 创建测量面板(只做 UI,不实现真实测量)
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new MeasurePanel({
|
||||
defaultMode: 'distance', // 默认展示前四个,且默认选中"距离"
|
||||
defaultMode: 'distance',
|
||||
defaultExpanded: false,
|
||||
onModeChange: (mode) => {
|
||||
console.log('[MeasureDialogManager] 当前测量方式已切换:', mode);
|
||||
this.engine.engine?.activateMeasure(mode);
|
||||
this.registry.engine3d?.activateMeasure(mode);
|
||||
},
|
||||
onClearAll: () => {
|
||||
// 预留:未来可清理引擎测量绘制/标注
|
||||
console.log('[MeasureDialogManager] 删除全部(仅 UI 清空,本次不清理引擎侧内容)');
|
||||
console.log('[MeasureDialogManager] 删除全部');
|
||||
},
|
||||
onSettings: () => {
|
||||
// 预留:未来可打开设置弹窗/面板
|
||||
console.log('[MeasureDialogManager] 打开设置(仅预留接口)');
|
||||
console.log('[MeasureDialogManager] 打开设置');
|
||||
},
|
||||
onExpandedChange: () => {
|
||||
// 展开/收起时,动态适配 Dialog 高度,避免遮挡底部操作按钮
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
// 同步一次当前配置(由组件从缓存/默认加载)
|
||||
this.config = this.panel.getConfig();
|
||||
|
||||
// 注意:你要求“组件本身不加边距”,因此在 Manager 这里用 wrapper 增加左右内边距
|
||||
// 这样 MeasurePanel 可以保持通用性,避免在不同场景复用时产生多余 padding。
|
||||
const panelWrapper = document.createElement('div');
|
||||
panelWrapper.style.padding = '12px';
|
||||
panelWrapper.appendChild(this.panel.element);
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'measure.dialogTitle',
|
||||
content: panelWrapper,
|
||||
width: dialogWidth,
|
||||
// 高度交给 fitHeight 动态计算(避免内容展开后遮挡底部操作区)
|
||||
height: 'auto',
|
||||
position: {
|
||||
x: x,
|
||||
y: y
|
||||
},
|
||||
onClose: () => {
|
||||
this.engine.toolbar?.setBtnActive('measure', false)
|
||||
this.destroy()
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
return panelWrapper;
|
||||
}
|
||||
|
||||
// 初次打开时也执行一次自适应高度(收起态)
|
||||
this.dialog.fitHeight(false);
|
||||
/** 对话框创建后的回调,自适应高度 */
|
||||
protected onDialogCreated(): void {
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
|
||||
/** 对话框关闭时的回调,取消工具栏按钮激活状态 */
|
||||
protected onDialogClose(): void {
|
||||
this.registry.toolbar?.setBtnActive('measure', false);
|
||||
}
|
||||
|
||||
/** 销毁前的清理,停用测量功能并销毁面板 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.registry.engine3d) {
|
||||
this.registry.engine3d.deactivateMeasure();
|
||||
}
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前测量方式
|
||||
* 说明:如果面板未创建,则返回 null
|
||||
* 获取当前激活的测量模式
|
||||
* @returns 当前测量模式,如 'distance'、'angle' 等
|
||||
*/
|
||||
public getActiveMode(): MeasureMode | null {
|
||||
return this.panel ? this.panel.getActiveMode() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换测量方式(你要求的“切换类型的方法”)
|
||||
* @param mode 测量方式
|
||||
* 切换测量模式
|
||||
* @param mode 目标测量模式
|
||||
*/
|
||||
public switchMode(mode: MeasureMode): void {
|
||||
if (!this.panel) return;
|
||||
@@ -116,22 +102,17 @@ export class MeasureDialogManager extends BimComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置测量结果(推荐使用的新方法名)
|
||||
* 说明:内部直接调用 MeasurePanel.setResult()
|
||||
* @param result 测量结果;传 null 表示清空
|
||||
* 设置测量结果
|
||||
* @param result 测量结果对象
|
||||
*/
|
||||
public setMeasureResult(result: MeasureResult | null): void {
|
||||
// 按你的要求:仅当 panel 存在时才调用,不做缓存
|
||||
if (!this.panel) {
|
||||
return;
|
||||
}
|
||||
if (!this.panel) return;
|
||||
this.panel.setResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取测量配置(单位/精度)
|
||||
* - 如果面板存在:返回面板当前配置
|
||||
* - 否则:返回 Manager 缓存的最后一次配置(可能为 null)
|
||||
* 获取测量配置
|
||||
* @returns 测量配置副本
|
||||
*/
|
||||
public getConfig(): MeasureConfig | null {
|
||||
if (this.panel) {
|
||||
@@ -141,63 +122,35 @@ export class MeasureDialogManager extends BimComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置测量配置(单位/精度)
|
||||
* @param partial 部分更新
|
||||
* @param persist 是否写入缓存(默认 true)
|
||||
* 设置测量配置
|
||||
* @param partial 部分配置
|
||||
* @param persist 是否持久化
|
||||
*/
|
||||
public setConfig(partial: Partial<MeasureConfig>, persist: boolean = true): void {
|
||||
// 面板存在则直接设置面板;否则仅更新 Manager 缓存
|
||||
if (this.panel) {
|
||||
this.panel.setConfig(partial, persist);
|
||||
this.config = this.panel.getConfig();
|
||||
// 配置变化可能影响高度(比如设置面板显示/隐藏),安全起见做一次 fit
|
||||
this.dialog?.fitHeight(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 面板未创建:只更新本地缓存
|
||||
const prev = this.config;
|
||||
const next: MeasureConfig = {
|
||||
unit: partial.unit ?? prev?.unit ?? 'mm',
|
||||
precision: partial.precision ?? prev?.precision ?? 2
|
||||
};
|
||||
this.config = next;
|
||||
// 注意:缓存写入由 MeasurePanel 负责(你要求默认维护在组件里)
|
||||
// 这里不写 localStorage,避免重复逻辑。
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除全部(仅清空 UI;真实测量清理逻辑后续再接)
|
||||
*/
|
||||
/** 清除所有测量结果 */
|
||||
public clearAll(): void {
|
||||
if (!this.panel) return;
|
||||
this.panel.clearAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开设置(仅预留方法/回调)
|
||||
*/
|
||||
/** 打开测量设置面板 */
|
||||
public openSettings(): void {
|
||||
if (!this.panel) return;
|
||||
this.panel.openSettings();
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
// 停用测量功能
|
||||
if (this.engine.engine) {
|
||||
this.engine.engine.deactivateMeasure();
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
}
|
||||
|
||||
// 销毁测量面板(清理订阅与 DOM)
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
/**
|
||||
* 属性面板管理器
|
||||
* 负责管理构件属性面板的显示和内容
|
||||
*/
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { BimCollapse } from '../components/collapse/index';
|
||||
import { BimDescription } from '../components/description/index';
|
||||
import { BimTab } from '../components/tab/index';
|
||||
|
||||
/**
|
||||
* 属性面板管理器
|
||||
* 负责展示和管理属性面板弹窗 (演示 Tab + Collapse + Description 组件)
|
||||
* 显示选中构件的属性信息和材质信息
|
||||
*/
|
||||
export class PropertyPanelManager extends BimComponent {
|
||||
export class PropertyPanelManager extends BaseManager {
|
||||
/** 对话框 ID */
|
||||
private dialogId = 'property-panel-dialog';
|
||||
private dialog: any = null; // 保存 dialog 引用
|
||||
/** 对话框实例 */
|
||||
private dialog: any = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/** 初始化,监听打开事件 */
|
||||
public init(): void {
|
||||
// 监听来自 Demo 的打开属性面板事件
|
||||
document.addEventListener('bim-demo:open-property-panel', () => {
|
||||
this.show();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示属性面板
|
||||
*/
|
||||
/** 显示属性面板 */
|
||||
public show() {
|
||||
if (!this.engine.dialog) {
|
||||
if (!this.registry.dialog) {
|
||||
console.warn('Dialog manager is not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已打开,不重复打开
|
||||
if (this.isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 创建弹窗
|
||||
const width = 360; // 稍微加宽一点以容纳 Tab
|
||||
const width = 360;
|
||||
const x = document.body.clientWidth - width - 40;
|
||||
console.log('x', x)
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
this.dialog = this.registry.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'panel.property.title', // '构件详情'
|
||||
title: 'panel.property.title',
|
||||
content: '',
|
||||
width: `${width}px`,
|
||||
height: '500px',
|
||||
@@ -56,7 +56,6 @@ export class PropertyPanelManager extends BimComponent {
|
||||
}
|
||||
} as any);
|
||||
|
||||
// 2. 创建内容容器
|
||||
const contentContainer = document.createElement('div');
|
||||
contentContainer.style.height = '100%';
|
||||
contentContainer.style.display = 'flex';
|
||||
@@ -64,33 +63,29 @@ export class PropertyPanelManager extends BimComponent {
|
||||
|
||||
this.dialog.setContent(contentContainer);
|
||||
|
||||
// 3. 创建标签页组件
|
||||
const tab = new BimTab({
|
||||
container: contentContainer,
|
||||
tabs: [
|
||||
{
|
||||
id: 'props',
|
||||
title: 'panel.property.tab.props', // '属性'
|
||||
title: 'panel.property.tab.props',
|
||||
content: this.createPropsTabContent()
|
||||
},
|
||||
{
|
||||
id: 'material',
|
||||
title: 'panel.property.tab.material', // '材质'
|
||||
title: 'panel.property.tab.material',
|
||||
content: this.createMaterialTabContent()
|
||||
}
|
||||
]
|
||||
});
|
||||
tab.init();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建"属性"标签页的内容 (包含 Collapse)
|
||||
*/
|
||||
/** 创建属性标签页内容 */
|
||||
private createPropsTabContent(): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
container.style.height = '100%';
|
||||
container.style.overflowY = 'auto'; // 内容区域滚动
|
||||
container.style.overflowY = 'auto';
|
||||
|
||||
new BimCollapse({
|
||||
container: container,
|
||||
@@ -99,13 +94,13 @@ export class PropertyPanelManager extends BimComponent {
|
||||
items: [
|
||||
{
|
||||
id: 'base',
|
||||
title: 'panel.property.base', // '基本属性'
|
||||
title: 'panel.property.base',
|
||||
content: this.createBaseInfoContent(),
|
||||
},
|
||||
{
|
||||
id: 'advanced',
|
||||
title: 'panel.property.advanced', // '高级设置'
|
||||
content: this.createAdvancedInfoContent(), // 新增一个内容
|
||||
title: 'panel.property.advanced',
|
||||
content: this.createAdvancedInfoContent(),
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
@@ -114,9 +109,7 @@ export class PropertyPanelManager extends BimComponent {
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建"材质"标签页的内容 (包含 Collapse)
|
||||
*/
|
||||
/** 创建材质标签页内容 */
|
||||
private createMaterialTabContent(): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
container.style.height = '100%';
|
||||
@@ -129,7 +122,7 @@ export class PropertyPanelManager extends BimComponent {
|
||||
items: [
|
||||
{
|
||||
id: 'material',
|
||||
title: 'panel.property.material', // '材质信息'
|
||||
title: 'panel.property.material',
|
||||
content: this.createMaterialContent(),
|
||||
}
|
||||
]
|
||||
@@ -138,6 +131,7 @@ export class PropertyPanelManager extends BimComponent {
|
||||
return container;
|
||||
}
|
||||
|
||||
/** 创建基本信息内容 */
|
||||
private createBaseInfoContent(): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
|
||||
@@ -156,6 +150,7 @@ export class PropertyPanelManager extends BimComponent {
|
||||
return container;
|
||||
}
|
||||
|
||||
/** 创建高级信息内容 */
|
||||
private createAdvancedInfoContent(): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
|
||||
@@ -174,10 +169,10 @@ export class PropertyPanelManager extends BimComponent {
|
||||
return container;
|
||||
}
|
||||
|
||||
/** 创建材质内容 */
|
||||
private createMaterialContent(): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
|
||||
// 材质预览块
|
||||
const preview = document.createElement('div');
|
||||
preview.style.display = 'flex';
|
||||
preview.style.alignItems = 'center';
|
||||
@@ -204,15 +199,14 @@ export class PropertyPanelManager extends BimComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查属性面板是否打开
|
||||
* 检查面板是否打开
|
||||
* @returns 是否打开
|
||||
*/
|
||||
public isOpen(): boolean {
|
||||
return this.dialog !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏属性面板
|
||||
*/
|
||||
/** 隐藏面板 */
|
||||
public hide(): void {
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
@@ -220,7 +214,9 @@ export class PropertyPanelManager extends BimComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** 销毁管理器 */
|
||||
public destroy(): void {
|
||||
this.hide();
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
/**
|
||||
* 右键菜单管理器
|
||||
* 负责管理右键上下文菜单的显示和交互
|
||||
*/
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { BimRightKey } from '../components/right-key';
|
||||
import { BimMenu } from '../components/menu';
|
||||
import { MenuItemConfig } from '../components/menu/item';
|
||||
|
||||
/**
|
||||
* 右键菜单管理器 (RightKeyManager)
|
||||
* 负责协调右键交互流程:
|
||||
* 1. 监听 Canvas/容器的 contextmenu 事件
|
||||
* 2. 通过注册的处理器 (Handler) 获取需要显示的菜单项
|
||||
* 3. 实例化 Menu 组件并装载到 RightKey 容器中显示
|
||||
* 右键菜单管理器
|
||||
* 支持注册多个上下文处理器,动态生成右键菜单
|
||||
*/
|
||||
export class RightKeyManager extends BimComponent {
|
||||
export class RightKeyManager extends BaseManager {
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 右键面板实例 */
|
||||
private rightKeyPanel: BimRightKey;
|
||||
|
||||
// 存储注册的上下文处理器
|
||||
// 每个处理器接收鼠标事件,返回一组菜单项(如果没有对应菜单则返回 null)
|
||||
/** 上下文处理器列表 */
|
||||
private contextHandlers: Array<(e: MouseEvent) => MenuItemConfig[] | null> = [];
|
||||
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.container = container;
|
||||
|
||||
// 初始化右键容器,设置极高的层级以覆盖所有 UI
|
||||
// 将事件监听和触发逻辑下放给 BimRightKey 组件
|
||||
this.rightKeyPanel = new BimRightKey({
|
||||
zIndex: 9000,
|
||||
container: this.container,
|
||||
@@ -33,55 +31,44 @@ export class RightKeyManager extends BimComponent {
|
||||
this.rightKeyPanel.init();
|
||||
}
|
||||
|
||||
/** 销毁管理器 */
|
||||
public destroy(): void {
|
||||
this.rightKeyPanel.destroy();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册上下文菜单处理器
|
||||
* @param handler 处理函数,接收鼠标事件,返回菜单项数组
|
||||
* 注册上下文处理器
|
||||
* @param handler 处理器函数,返回菜单项配置
|
||||
*/
|
||||
public registerHandler(handler: (e: MouseEvent) => MenuItemConfig[] | null): void {
|
||||
this.contextHandlers.push(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动显示菜单
|
||||
* 允许外部直接调用以显示特定的菜单,不一定依赖右键事件
|
||||
* @param x 屏幕 X 坐标
|
||||
* @param y 屏幕 Y 坐标
|
||||
* @param items 菜单项列表
|
||||
* @param groupOrder 可选的分组顺序
|
||||
* 显示菜单
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
* @param items 菜单项配置
|
||||
* @param groupOrder 分组顺序
|
||||
*/
|
||||
public showMenu(x: number, y: number, items: MenuItemConfig[], groupOrder?: string[]): void {
|
||||
if (!items || items.length === 0) return;
|
||||
|
||||
// 1. 创建菜单内容组件
|
||||
const menu = new BimMenu({ items, groupOrder });
|
||||
menu.init(); // 必须初始化以生成 DOM
|
||||
menu.init();
|
||||
|
||||
// 2. 将菜单挂载到右键容器
|
||||
this.rightKeyPanel.mount(menu);
|
||||
|
||||
// 3. 显示容器
|
||||
this.rightKeyPanel.show(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏右键菜单
|
||||
*/
|
||||
/** 隐藏菜单 */
|
||||
public hide(): void {
|
||||
this.rightKeyPanel.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理右键点击事件
|
||||
* 由 BimRightKey 组件在检测到有效右键点击时调用
|
||||
*/
|
||||
/** 处理右键点击事件 */
|
||||
private handleContextMenu = (e: MouseEvent): void => {
|
||||
// 1. 确定上下文项
|
||||
// 遍历所有注册的处理器,找到第一个返回非空结果的处理器
|
||||
// 这种责任链模式允许插件优先处理特定对象的右键
|
||||
let items: MenuItemConfig[] | null = null;
|
||||
for (const handler of this.contextHandlers) {
|
||||
const result = handler(e);
|
||||
@@ -91,11 +78,9 @@ export class RightKeyManager extends BimComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 如果有菜单项,则显示
|
||||
if (items && items.length > 0) {
|
||||
this.showMenu(e.clientX, e.clientY, items);
|
||||
} else {
|
||||
// 如果没有任何内容,则关闭可能存在的菜单
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,135 +1,122 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
/**
|
||||
* 轴向剖切对话框管理器
|
||||
* 负责管理轴向剖切工具对话框的显示、隐藏和剖切面板的交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { SectionAxisPanel } from '../components/section-axis-panel';
|
||||
import type { SectionAxis } from '../components/section-axis-panel/types';
|
||||
|
||||
/**
|
||||
* 轴向剖切弹窗管理器
|
||||
* 轴向剖切对话框管理器
|
||||
* 继承自 BaseDialogManager,提供 X/Y/Z 轴向剖切的对话框管理功能
|
||||
*/
|
||||
export class SectionAxisDialogManager extends BimComponent {
|
||||
private dialogId = 'section-axis-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
/** 轴向剖切面板实例 */
|
||||
private panel: SectionAxisPanel | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId(): string {
|
||||
return 'section-axis-dialog';
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle(): string {
|
||||
return 'sectionAxis.dialogTitle';
|
||||
}
|
||||
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth(): number {
|
||||
return 240;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示弹窗
|
||||
* 获取对话框位置
|
||||
* 定位在容器右下角
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
protected getDialogPosition(): { x: number; y: number } {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
// 如果已打开,先销毁
|
||||
this.destroy();
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const paddingRight = 20;
|
||||
const paddingBottom = 50;
|
||||
|
||||
// 创建面板
|
||||
return {
|
||||
x: containerWidth - this.dialogWidth - paddingRight,
|
||||
y: containerHeight - paddingBottom - 200
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话框内容
|
||||
* 初始化轴向剖切面板并设置回调
|
||||
*/
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new SectionAxisPanel({
|
||||
defaultAxis: 'x',
|
||||
defaultHidden: false,
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionAxisDialogManager] 隐藏切换:', isHidden);
|
||||
// TODO: 实现隐藏/显示剖切面的逻辑
|
||||
},
|
||||
onReverse: () => {
|
||||
console.log('[SectionAxisDialogManager] 反向剖切');
|
||||
// TODO: 实现反向剖切的逻辑
|
||||
},
|
||||
onAxisChange: (axis) => {
|
||||
console.log('[SectionAxisDialogManager] 切换轴向:', axis);
|
||||
// TODO: 实现轴向切换的逻辑
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
// 创建弹窗
|
||||
const dialogWidth = 240;
|
||||
const paddingRight = 20;
|
||||
const paddingBottom = 50;
|
||||
const container = this.engine.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const x = containerWidth - dialogWidth - paddingRight;
|
||||
const y = containerHeight - paddingBottom - 200; // 临时y值,会被fitHeight调整
|
||||
/** 对话框创建后的回调,自适应高度 */
|
||||
protected onDialogCreated(): void {
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'sectionAxis.dialogTitle',
|
||||
width: dialogWidth,
|
||||
height: 'auto', // 自动高度
|
||||
position: { x, y },
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
content: this.panel.element,
|
||||
onClose: () => {
|
||||
this.engine.toolbar?.setBtnActive('section-axis', false);
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
/** 对话框关闭时的回调,取消工具栏按钮激活状态 */
|
||||
protected onDialogClose(): void {
|
||||
this.registry.toolbar?.setBtnActive('section-axis', false);
|
||||
}
|
||||
|
||||
// 自适应高度
|
||||
this.dialog.fitHeight(false);
|
||||
/** 销毁前的清理,销毁面板实例 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏弹窗
|
||||
*/
|
||||
public hide(): void {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隐藏状态
|
||||
* 获取剖切面隐藏状态
|
||||
* @returns 是否隐藏
|
||||
*/
|
||||
public getHiddenState(): boolean {
|
||||
return this.panel?.getHiddenState() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置隐藏状态
|
||||
* 设置剖切面隐藏状态
|
||||
* @param isHidden 是否隐藏
|
||||
*/
|
||||
public setHiddenState(isHidden: boolean): void {
|
||||
this.panel?.setHiddenState(isHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前激活的轴向
|
||||
* 获取当前激活的剖切轴向
|
||||
* @returns 当前轴向 'x' | 'y' | 'z'
|
||||
*/
|
||||
public getActiveAxis(): SectionAxis {
|
||||
return this.panel?.getActiveAxis() ?? 'x';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置激活的轴向
|
||||
* 设置剖切轴向
|
||||
* @param axis 目标轴向
|
||||
*/
|
||||
public setActiveAxis(axis: SectionAxis): void {
|
||||
this.panel?.setActiveAxis(axis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁弹窗和面板
|
||||
*/
|
||||
public destroy(): void {
|
||||
// 关闭弹窗
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
}
|
||||
|
||||
// 销毁面板
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,159 +1,144 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
/**
|
||||
* 剖切盒对话框管理器
|
||||
* 负责管理剖切盒工具对话框的显示、隐藏和剖切盒面板的交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { SectionBoxPanel } from '../components/section-box-panel';
|
||||
import type { SectionBoxRange } from '../components/section-box-panel/types';
|
||||
|
||||
/**
|
||||
* 剖切盒弹窗管理器
|
||||
* 剖切盒对话框管理器
|
||||
* 继承自 BaseDialogManager,提供六面体剖切盒的对话框管理功能
|
||||
*/
|
||||
export class SectionBoxDialogManager extends BimComponent {
|
||||
private dialogId = 'section-box-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
/** 剖切盒面板实例 */
|
||||
private panel: SectionBoxPanel | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId(): string {
|
||||
return 'section-box-dialog';
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle(): string {
|
||||
return 'sectionBox.dialogTitle';
|
||||
}
|
||||
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth(): number {
|
||||
return 280;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示弹窗
|
||||
* 获取对话框位置
|
||||
* 定位在容器右下角
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
protected getDialogPosition(): { x: number; y: number } {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
// 如果已打开,先销毁
|
||||
this.destroy();
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const paddingRight = 20;
|
||||
const paddingBottom = 50;
|
||||
|
||||
// 创建面板
|
||||
return {
|
||||
x: containerWidth - this.dialogWidth - paddingRight,
|
||||
y: containerHeight - paddingBottom - 300
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建对话框内容
|
||||
* 初始化剖切盒面板并设置回调
|
||||
*/
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new SectionBoxPanel({
|
||||
defaultHidden: false,
|
||||
defaultReversed: false,
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionBoxDialogManager] 隐藏切换:', isHidden);
|
||||
// TODO: 实现隐藏/显示剖切盒的逻辑
|
||||
},
|
||||
onReverseToggle: (isReversed) => {
|
||||
console.log('[SectionBoxDialogManager] 反向切换:', isReversed);
|
||||
// TODO: 实现反向剖切的逻辑
|
||||
},
|
||||
onFitToModel: () => {
|
||||
console.log('[SectionBoxDialogManager] 适应到模型');
|
||||
// TODO: 实现自动适应模型的逻辑
|
||||
},
|
||||
onReset: () => {
|
||||
console.log('[SectionBoxDialogManager] 重置');
|
||||
// 注意:不要在这里调用 panel.reset(),会造成无限递归
|
||||
// panel 的 reset 按钮已经在内部处理了状态重置
|
||||
// TODO: 这里只需要通知 3D 引擎重置剖切盒即可
|
||||
},
|
||||
onRangeChange: (range) => {
|
||||
console.log('[SectionBoxDialogManager] 范围变化:', range);
|
||||
// TODO: 实现范围变化的逻辑
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
// 创建弹窗
|
||||
const dialogWidth = 280;
|
||||
const paddingRight = 20;
|
||||
const paddingBottom = 50;
|
||||
const container = this.engine.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const x = containerWidth - dialogWidth - paddingRight;
|
||||
const y = containerHeight - paddingBottom - 300; // 临时y值,会被fitHeight调整
|
||||
/** 对话框创建后的回调,自适应高度 */
|
||||
protected onDialogCreated(): void {
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'sectionBox.dialogTitle',
|
||||
width: dialogWidth,
|
||||
height: 'auto',
|
||||
position: { x, y },
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
content: this.panel.element,
|
||||
onClose: () => {
|
||||
this.engine.toolbar?.setBtnActive('section-box', false);
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
/** 对话框关闭时的回调,取消工具栏按钮激活状态 */
|
||||
protected onDialogClose(): void {
|
||||
this.registry.toolbar?.setBtnActive('section-box', false);
|
||||
}
|
||||
|
||||
// 自适应高度
|
||||
this.dialog.fitHeight(false);
|
||||
/** 销毁前的清理,销毁面板实例 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏弹窗
|
||||
*/
|
||||
public hide(): void {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隐藏状态
|
||||
* 获取剖切盒隐藏状态
|
||||
* @returns 是否隐藏
|
||||
*/
|
||||
public getHiddenState(): boolean {
|
||||
return this.panel?.getHiddenState() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置隐藏状态
|
||||
* 设置剖切盒隐藏状态
|
||||
* @param isHidden 是否隐藏
|
||||
*/
|
||||
public setHiddenState(isHidden: boolean): void {
|
||||
this.panel?.setHiddenState(isHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反向状态
|
||||
* 获取剖切盒反向状态
|
||||
* @returns 是否反向(显示盒内/盒外)
|
||||
*/
|
||||
public getReversedState(): boolean {
|
||||
return this.panel?.getReversedState() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置反向状态
|
||||
* 设置剖切盒反向状态
|
||||
* @param isReversed 是否反向
|
||||
*/
|
||||
public setReversedState(isReversed: boolean): void {
|
||||
this.panel?.setReversedState(isReversed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取范围值
|
||||
* 获取剖切盒范围
|
||||
* @returns 六面体范围 { minX, maxX, minY, maxY, minZ, maxZ }
|
||||
*/
|
||||
public getRange(): SectionBoxRange | null {
|
||||
return this.panel?.getRange() ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置范围值
|
||||
* 设置剖切盒范围
|
||||
* @param range 部分或全部范围值
|
||||
*/
|
||||
public setRange(range: Partial<SectionBoxRange>): void {
|
||||
this.panel?.setRange(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁弹窗和面板
|
||||
*/
|
||||
public destroy(): void {
|
||||
// 关闭弹窗
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
}
|
||||
|
||||
// 销毁面板
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +1,73 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
/**
|
||||
* 拾取面剖切对话框管理器
|
||||
* 负责管理拾取面剖切工具对话框的显示和交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { SectionPlanePanel } from '../components/section-plane-panel';
|
||||
|
||||
/**
|
||||
* 拾取面剖切弹窗管理器
|
||||
* 拾取面剖切对话框管理器
|
||||
* 继承自 BaseDialogManager,提供拾取面剖切功能的对话框管理
|
||||
*/
|
||||
export class SectionPlaneDialogManager extends BimComponent {
|
||||
private dialogId = 'section-plane-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
/** 剖切面板实例 */
|
||||
private panel: SectionPlanePanel | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
}
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId() { return 'section-plane-dialog'; }
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle() { return 'sectionPlane.dialogTitle'; }
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth() { return 240; }
|
||||
/** 对话框高度 */
|
||||
protected get dialogHeight(): number { return 120; }
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
}
|
||||
/** 初始化 */
|
||||
public init(): void {}
|
||||
|
||||
/**
|
||||
* 显示拾取面剖切弹窗
|
||||
* 获取对话框位置
|
||||
* 定位在容器右下角
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
protected getDialogPosition() {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
// 如果已打开过,先销毁旧实例
|
||||
this.destroy();
|
||||
const paddingRight = 20;
|
||||
const paddingBottom = 50;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
|
||||
// 创建面板
|
||||
return {
|
||||
x: containerWidth - this.dialogWidth - paddingRight,
|
||||
y: containerHeight - this.dialogHeight - paddingBottom
|
||||
};
|
||||
}
|
||||
|
||||
/** 创建对话框内容 */
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new SectionPlanePanel({
|
||||
onHide: () => {
|
||||
console.log('[SectionPlaneDialogManager] 隐藏');
|
||||
// TODO: 调用引擎的隐藏功能
|
||||
},
|
||||
onReverse: () => {
|
||||
console.log('[SectionPlaneDialogManager] 反向');
|
||||
// TODO: 调用引擎的反向功能
|
||||
},
|
||||
onReset: () => {
|
||||
console.log('[SectionPlaneDialogManager] 重置');
|
||||
// TODO: 调用引擎的重置功能
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
|
||||
// 创建弹窗
|
||||
const dialogWidth = 240;
|
||||
const dialogHeight = 120;
|
||||
const paddingRight = 20;
|
||||
const paddingBottom = 50;
|
||||
const container = this.engine.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const x = containerWidth - dialogWidth - paddingRight;
|
||||
const y = containerHeight - dialogHeight - paddingBottom;
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'sectionPlane.dialogTitle',
|
||||
width: dialogWidth,
|
||||
height: dialogHeight,
|
||||
position: { x, y },
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
content: this.panel.element,
|
||||
onClose: () => {
|
||||
this.engine.toolbar?.setBtnActive('section-plane', false);
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏弹窗
|
||||
*/
|
||||
public hide(): void {
|
||||
this.destroy();
|
||||
/** 对话框关闭时的回调 */
|
||||
protected onDialogClose(): void {
|
||||
this.registry.toolbar?.setBtnActive('section-plane', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁弹窗
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
}
|
||||
/** 销毁前的清理 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
/**
|
||||
* 工具栏管理器
|
||||
* 负责管理底部工具栏的创建、按钮配置和显示控制
|
||||
*/
|
||||
import type { ButtonGroupColors, ButtonConfig } from '../components/button-group/index.type';
|
||||
import { Toolbar } from '../components/button-group/toolbar';
|
||||
import type { ThemeConfig } from '../themes/types';
|
||||
import { BimComponent } from '../core/component';
|
||||
import type { BimEngine } from '../bim-engine';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
|
||||
/**
|
||||
* 底部工具栏管理器 (ToolbarManager)
|
||||
* 仅负责管理底部工具栏实例。
|
||||
* 工具栏管理器
|
||||
* 提供工具栏按钮的添加、显示/隐藏、主题更新等功能
|
||||
*/
|
||||
export class ToolbarManager extends BimComponent {
|
||||
export class ToolbarManager extends BaseManager {
|
||||
/** 工具栏实例 */
|
||||
private toolbar: Toolbar | null = null;
|
||||
/** 工具栏容器元素 */
|
||||
private toolbarContainer: HTMLElement | null = null;
|
||||
/** 主容器元素 */
|
||||
private container: HTMLElement;
|
||||
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.container = container;
|
||||
this.init();
|
||||
}
|
||||
|
||||
/** 初始化工具栏 */
|
||||
private init() {
|
||||
// 创建底部工具栏专用容器
|
||||
this.toolbarContainer = document.createElement('div');
|
||||
this.toolbarContainer.id = 'opt-btn-groups';
|
||||
this.toolbarContainer.className = 'bim-engine-opt-btn-container is-bottom-toolbar';
|
||||
@@ -36,52 +42,108 @@ export class ToolbarManager extends BimComponent {
|
||||
expand: 'up'
|
||||
});
|
||||
|
||||
// 注入 engine 到 Toolbar
|
||||
// @ts-ignore - Toolbar 还没更新类型,暂时忽略
|
||||
this.toolbar.setEngine(this.engine);
|
||||
|
||||
this.toolbar.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新工具栏主题
|
||||
* @param theme 主题配置
|
||||
*/
|
||||
public updateTheme(theme: ThemeConfig) {
|
||||
this.toolbar?.setTheme(theme);
|
||||
}
|
||||
|
||||
/** 刷新工具栏渲染 */
|
||||
public refresh() {
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
/** 销毁工具栏 */
|
||||
public destroy() {
|
||||
this.toolbar?.destroy();
|
||||
this.toolbar = null;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
// --- 转发 API ---
|
||||
public addGroup(groupId: string, beforeGroupId?: string) { this.toolbar?.addGroup(groupId, beforeGroupId); this.toolbar?.render(); }
|
||||
public addButton(config: ButtonConfig) { this.toolbar?.addButton(config); this.toolbar?.render(); }
|
||||
public setButtonVisibility(id: string, v: boolean) { this.toolbar?.updateButtonVisibility(id, v); }
|
||||
public setShowLabel(show: boolean) { this.toolbar?.setShowLabel(show); }
|
||||
public setBtnActive(id: string, active?: boolean) { this.toolbar?.setBtnActive(id, active); }
|
||||
/**
|
||||
* 添加按钮组
|
||||
* @param groupId 组 ID
|
||||
* @param beforeGroupId 插入到指定组之前
|
||||
*/
|
||||
public addGroup(groupId: string, beforeGroupId?: string) {
|
||||
this.toolbar?.addGroup(groupId, beforeGroupId);
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加按钮
|
||||
* @param config 按钮配置
|
||||
*/
|
||||
public addButton(config: ButtonConfig) {
|
||||
this.toolbar?.addButton(config);
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮可见性
|
||||
* @param id 按钮 ID
|
||||
* @param v 是否可见
|
||||
*/
|
||||
public setButtonVisibility(id: string, v: boolean) {
|
||||
this.toolbar?.updateButtonVisibility(id, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否显示标签
|
||||
* @param show 是否显示
|
||||
*/
|
||||
public setShowLabel(show: boolean) {
|
||||
this.toolbar?.setShowLabel(show);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮激活状态
|
||||
* @param id 按钮 ID
|
||||
* @param active 是否激活
|
||||
*/
|
||||
public setBtnActive(id: string, active?: boolean) {
|
||||
this.toolbar?.setBtnActive(id, active);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置工具栏可见性
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
public setVisible(visible: boolean) {
|
||||
if (this.toolbarContainer) {
|
||||
this.toolbarContainer.style.visibility = visible ? 'visible' : 'hidden';
|
||||
}
|
||||
}
|
||||
public setBackgroundColor(color: string) { this.toolbar?.setBackgroundColor(color); }
|
||||
public setColors(colors: ButtonGroupColors) { this.toolbar?.setColors(colors); }
|
||||
|
||||
/**
|
||||
* 隐藏工具栏
|
||||
* 设置背景颜色
|
||||
* @param color 颜色值
|
||||
*/
|
||||
public setBackgroundColor(color: string) {
|
||||
this.toolbar?.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮组颜色
|
||||
* @param colors 颜色配置
|
||||
*/
|
||||
public setColors(colors: ButtonGroupColors) {
|
||||
this.toolbar?.setColors(colors);
|
||||
}
|
||||
|
||||
/** 隐藏工具栏 */
|
||||
public hide(): void {
|
||||
if (this.toolbarContainer) {
|
||||
this.toolbarContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示工具栏
|
||||
*/
|
||||
/** 显示工具栏 */
|
||||
public show(): void {
|
||||
if (this.toolbarContainer) {
|
||||
this.toolbarContainer.style.display = '';
|
||||
@@ -90,6 +152,7 @@ export class ToolbarManager extends BimComponent {
|
||||
|
||||
/**
|
||||
* 获取工具栏容器
|
||||
* @returns 容器元素
|
||||
*/
|
||||
public getContainer(): HTMLElement | null {
|
||||
return this.toolbarContainer;
|
||||
|
||||
@@ -1,47 +1,48 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
/**
|
||||
* 漫游控制管理器
|
||||
* 负责管理漫游模式的控制面板和相关交互
|
||||
*/
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { WalkControlPanel } from '../components/walk-control-panel';
|
||||
import { WalkPathDialogManager } from './walk-path-dialog-manager';
|
||||
|
||||
/**
|
||||
* 漫游控制管理器
|
||||
* 提供第一人称漫游、路径漫游等功能的控制界面
|
||||
*/
|
||||
export class WalkControlManager extends BimComponent {
|
||||
export class WalkControlManager extends BaseManager {
|
||||
/** 漫游控制面板实例 */
|
||||
public panel: WalkControlPanel | null = null;
|
||||
/** 路径漫游对话框管理器 */
|
||||
private pathManager: WalkPathDialogManager | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/** 初始化管理器 */
|
||||
public init(): void {
|
||||
// 初始化子 manager
|
||||
this.pathManager = new WalkPathDialogManager(this.engine);
|
||||
this.pathManager = new WalkPathDialogManager();
|
||||
this.pathManager.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示漫游控制面板
|
||||
*/
|
||||
/** 显示漫游控制面板 */
|
||||
public show(): void {
|
||||
if (!this.engine.toolbar) {
|
||||
if (!this.registry.toolbar) {
|
||||
console.warn('Toolbar not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
// 隐藏 toolbar
|
||||
this.engine.toolbar.hide();
|
||||
this.registry.toolbar.hide();
|
||||
|
||||
// 创建漫游控制面板
|
||||
this.panel = new WalkControlPanel({
|
||||
onPlanViewToggle: (isActive) => {
|
||||
console.log('[WalkControl] 地图:', isActive);
|
||||
if (isActive) {
|
||||
this.engine.map?.show();
|
||||
this.registry.map?.show();
|
||||
} else {
|
||||
this.engine.map?.hide();
|
||||
this.registry.map?.hide();
|
||||
}
|
||||
// 触发事件
|
||||
this.emit('walk:plan-view-toggle', { isActive });
|
||||
},
|
||||
onPathModeToggle: (isActive) => {
|
||||
@@ -51,40 +52,32 @@ export class WalkControlManager extends BimComponent {
|
||||
} else {
|
||||
this.pathManager?.hide();
|
||||
}
|
||||
// 触发事件
|
||||
this.emit('walk:path-mode-toggle', { isActive });
|
||||
},
|
||||
onWalkModeToggle: (isActive) => {
|
||||
console.log('[WalkControl] 漫游模式:', isActive);
|
||||
// 切换到漫游模式时,关闭路径漫游弹窗
|
||||
if (isActive) {
|
||||
this.pathManager?.hide();
|
||||
}
|
||||
// 触发事件
|
||||
this.emit('walk:walk-mode-toggle', { isActive });
|
||||
},
|
||||
onSpeedChange: (speed) => {
|
||||
console.log('[WalkControl] 速度变化:', speed);
|
||||
// 触发事件
|
||||
this.emit('walk:speed-change', { speed });
|
||||
},
|
||||
onGravityToggle: (enabled) => {
|
||||
console.log('[WalkControl] 重力:', enabled);
|
||||
// 触发事件
|
||||
this.emit('walk:gravity-toggle', { enabled });
|
||||
},
|
||||
onCollisionToggle: (enabled) => {
|
||||
console.log('[WalkControl] 碰撞:', enabled);
|
||||
// 触发事件
|
||||
this.emit('walk:collision-toggle', { enabled });
|
||||
},
|
||||
onCharacterModelChange: (model) => {
|
||||
console.log('[WalkControl] 角色模型:', model);
|
||||
// TODO: 实现角色模型变化逻辑
|
||||
},
|
||||
onWalkModeChange: (mode) => {
|
||||
console.log('[WalkControl] 行走模式:', mode);
|
||||
// TODO: 实现行走模式变化逻辑
|
||||
},
|
||||
onExit: () => {
|
||||
this.hide();
|
||||
@@ -92,60 +85,50 @@ export class WalkControlManager extends BimComponent {
|
||||
});
|
||||
this.panel.init();
|
||||
|
||||
// 如果地图已经打开,同步按钮状态
|
||||
if (this.engine.map?.isOpen()) {
|
||||
if (this.registry.map?.isOpen()) {
|
||||
this.panel.setPlanViewActive(true);
|
||||
}
|
||||
|
||||
// 监听地图事件,同步漫游面板中的地图按钮状态
|
||||
this.engine.on('map:opened', () => {
|
||||
this.subscribe('map:opened', () => {
|
||||
this.panel?.setPlanViewActive(true);
|
||||
});
|
||||
|
||||
this.engine.on('map:closed', () => {
|
||||
this.subscribe('map:closed', () => {
|
||||
this.panel?.setPlanViewActive(false);
|
||||
});
|
||||
|
||||
// 将面板添加到主容器中,定位在底部中间(类似toolbar的位置)
|
||||
if (this.engine.container) {
|
||||
// 添加定位样式
|
||||
if (this.registry.container) {
|
||||
this.panel.element.style.position = 'absolute';
|
||||
this.panel.element.style.bottom = '20px';
|
||||
this.panel.element.style.left = '50%';
|
||||
this.panel.element.style.transform = 'translateX(-50%)';
|
||||
this.panel.element.style.zIndex = '1000';
|
||||
|
||||
this.engine.container.appendChild(this.panel.element);
|
||||
this.registry.container.appendChild(this.panel.element);
|
||||
} else {
|
||||
console.warn('[WalkControlManager] Container not found');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏漫游控制面板
|
||||
*/
|
||||
/** 隐藏漫游控制面板 */
|
||||
public hide(): void {
|
||||
// 关闭路径漫游弹窗(但不关闭地图,因为地图可能是用户单独打开的)
|
||||
this.pathManager?.hide();
|
||||
|
||||
// 销毁面板
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
|
||||
// 显示 toolbar
|
||||
if (this.engine.toolbar) {
|
||||
this.engine.toolbar.show();
|
||||
if (this.registry.toolbar) {
|
||||
this.registry.toolbar.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁管理器
|
||||
*/
|
||||
/** 销毁管理器 */
|
||||
public destroy(): void {
|
||||
this.hide();
|
||||
this.pathManager?.destroy();
|
||||
this.pathManager = null;
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +1,64 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
/**
|
||||
* 漫游路径对话框管理器
|
||||
* 负责管理漫游路径设置对话框的显示和交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { WalkPathPanel } from '../components/walk-path-panel';
|
||||
|
||||
/**
|
||||
* 路径漫游弹窗管理器
|
||||
* 漫游路径对话框管理器
|
||||
* 继承自 BaseDialogManager,提供漫游路径配置的对话框管理功能
|
||||
*/
|
||||
export class WalkPathDialogManager extends BimComponent {
|
||||
private dialogId = 'walk-path-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class WalkPathDialogManager extends BaseDialogManager {
|
||||
/** 漫游路径面板实例 */
|
||||
private panel: WalkPathPanel | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
}
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId() { return 'walk-path-dialog'; }
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle() { return 'walkControl.path.dialogTitle'; }
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth() { return 300; }
|
||||
/** 对话框高度 */
|
||||
protected get dialogHeight(): number { return 400; }
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
}
|
||||
/** 初始化 */
|
||||
public init(): void {}
|
||||
|
||||
/**
|
||||
* 显示弹窗
|
||||
* 获取对话框位置
|
||||
* 定位在容器右侧居中
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
protected getDialogPosition() {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
// 如果已打开,先销毁
|
||||
this.destroy();
|
||||
const paddingRight = 20;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
|
||||
// 创建面板(暂时空内容)
|
||||
return {
|
||||
x: containerWidth - this.dialogWidth - paddingRight,
|
||||
y: (containerHeight - this.dialogHeight) / 2
|
||||
};
|
||||
}
|
||||
|
||||
/** 创建对话框内容 */
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new WalkPathPanel();
|
||||
this.panel.init();
|
||||
|
||||
const dialogWidth = 300;
|
||||
const dialogHeight = 400;
|
||||
const paddingRight = 20;
|
||||
const container = this.engine.container;
|
||||
const containerHeight = container.clientHeight;
|
||||
const containerWidth = container.clientWidth;
|
||||
|
||||
// 右边中间:right: 20px, 垂直居中
|
||||
const x = containerWidth - dialogWidth - paddingRight;
|
||||
const y = (containerHeight - dialogHeight) / 2;
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'walkControl.path.dialogTitle',
|
||||
width: dialogWidth,
|
||||
height: dialogHeight,
|
||||
position: { x, y },
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
content: this.panel.element,
|
||||
onClose: () => {
|
||||
// 通知主控制面板更新状态
|
||||
if (this.engine.walkControl && this.engine.walkControl.panel) {
|
||||
this.engine.walkControl.panel.setPathModeActive(false);
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏弹窗
|
||||
*/
|
||||
public hide(): void {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁弹窗和面板
|
||||
*/
|
||||
public destroy(): void {
|
||||
// 先保存 dialog 引用,避免在回调中重复调用
|
||||
const dialog = this.dialog;
|
||||
|
||||
// 立即清空引用,防止递归
|
||||
this.dialog = null;
|
||||
|
||||
// 关闭弹窗
|
||||
if (dialog) {
|
||||
dialog.destroy();
|
||||
/** 对话框关闭时的回调 */
|
||||
protected onDialogClose(): void {
|
||||
if (this.registry.walkControl && this.registry.walkControl.panel) {
|
||||
this.registry.walkControl.panel.setPathModeActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁面板
|
||||
/** 销毁前的清理 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -1,94 +1,64 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimEngine } from '../bim-engine';
|
||||
import { BimDialog } from '../components/dialog';
|
||||
/**
|
||||
* 漫游平面图对话框管理器
|
||||
* 负责管理漫游平面图对话框的显示和交互
|
||||
*/
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { WalkPlanViewPanel } from '../components/walk-plan-view-panel';
|
||||
|
||||
/**
|
||||
* 平面图弹窗管理器
|
||||
* 漫游平面图对话框管理器
|
||||
* 继承自 BaseDialogManager,提供漫游平面图的对话框管理功能
|
||||
*/
|
||||
export class WalkPlanViewDialogManager extends BimComponent {
|
||||
private dialogId = 'walk-plan-view-dialog';
|
||||
private dialog: BimDialog | null = null;
|
||||
export class WalkPlanViewDialogManager extends BaseDialogManager {
|
||||
/** 漫游平面图面板实例 */
|
||||
private panel: WalkPlanViewPanel | null = null;
|
||||
|
||||
constructor(engine: BimEngine) {
|
||||
super(engine);
|
||||
}
|
||||
/** 对话框唯一标识 */
|
||||
protected get dialogId() { return 'walk-plan-view-dialog'; }
|
||||
/** 对话框标题(国际化 key) */
|
||||
protected get dialogTitle() { return 'walkControl.planView.dialogTitle'; }
|
||||
/** 对话框宽度 */
|
||||
protected get dialogWidth() { return 300; }
|
||||
/** 对话框高度 */
|
||||
protected get dialogHeight(): number { return 400; }
|
||||
|
||||
public init(): void {
|
||||
// 可以在这里监听事件
|
||||
}
|
||||
/** 初始化 */
|
||||
public init(): void {}
|
||||
|
||||
/**
|
||||
* 显示弹窗
|
||||
* 获取对话框位置
|
||||
* 定位在容器左下角
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.engine.dialog || !this.engine.container) {
|
||||
console.warn('Dialog manager or container is not initialized');
|
||||
return;
|
||||
}
|
||||
protected getDialogPosition() {
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 20, y: 100 };
|
||||
|
||||
// 如果已打开,先销毁
|
||||
this.destroy();
|
||||
|
||||
// 创建面板(暂时空内容)
|
||||
this.panel = new WalkPlanViewPanel();
|
||||
this.panel.init();
|
||||
|
||||
const dialogWidth = 300;
|
||||
const dialogHeight = 400;
|
||||
const paddingLeft = 20;
|
||||
const paddingBottom = 20;
|
||||
const container = this.engine.container;
|
||||
const containerHeight = container.clientHeight;
|
||||
|
||||
// 左下角:left: 20px, bottom: 20px
|
||||
const x = paddingLeft;
|
||||
const y = containerHeight - dialogHeight - paddingBottom;
|
||||
|
||||
this.dialog = this.engine.dialog.create({
|
||||
id: this.dialogId,
|
||||
title: 'walkControl.planView.dialogTitle',
|
||||
width: dialogWidth,
|
||||
height: dialogHeight,
|
||||
position: { x, y },
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
content: this.panel.element,
|
||||
onClose: () => {
|
||||
// 通知主控制面板更新状态
|
||||
if (this.engine.walkControl && this.engine.walkControl.panel) {
|
||||
this.engine.walkControl.panel.setPlanViewActive(false);
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
this.dialog.init();
|
||||
return {
|
||||
x: paddingLeft,
|
||||
y: containerHeight - this.dialogHeight - paddingBottom
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏弹窗
|
||||
*/
|
||||
public hide(): void {
|
||||
this.destroy();
|
||||
/** 创建对话框内容 */
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new WalkPlanViewPanel();
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁弹窗和面板
|
||||
*/
|
||||
public destroy(): void {
|
||||
// 先保存 dialog 引用,避免在回调中重复调用
|
||||
const dialog = this.dialog;
|
||||
|
||||
// 立即清空引用,防止递归
|
||||
this.dialog = null;
|
||||
|
||||
// 关闭弹窗
|
||||
if (dialog) {
|
||||
dialog.destroy();
|
||||
/** 对话框关闭时的回调 */
|
||||
protected onDialogClose(): void {
|
||||
if (this.registry.walkControl && this.registry.walkControl.panel) {
|
||||
this.registry.walkControl.panel.setPlanViewActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁面板
|
||||
/** 销毁前的清理 */
|
||||
protected onBeforeDestroy(): void {
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -5,41 +5,29 @@ export interface EngineEvents {
|
||||
|
||||
// Engine Events
|
||||
'engine:model-loaded': { url: string };
|
||||
'engine:object-clicked': { objectId: string; position: { x: number, y: number, z: number } };
|
||||
|
||||
// 树组件事件
|
||||
|
||||
'ui:tree-node-check': { id: string; checked: boolean; node: any };
|
||||
|
||||
'ui:tree-node-select': { id: string; selected: boolean; node: any };
|
||||
|
||||
'ui:tree-node-expand': { id: string; expanded: boolean };
|
||||
|
||||
|
||||
|
||||
// 折叠面板事件
|
||||
|
||||
'ui:collapse-change': { activeIds: string[] };
|
||||
|
||||
|
||||
|
||||
// 系统事件
|
||||
'engine:object-clicked': { objectId: string; position: { x: number; y: number; z: number } };
|
||||
|
||||
'sys:theme-changed': { theme: string };
|
||||
// 树组件事件
|
||||
'ui:tree-node-check': { id: string; checked: boolean; node: any };
|
||||
'ui:tree-node-select': { id: string; selected: boolean; node: any };
|
||||
'ui:tree-node-expand': { id: string; expanded: boolean };
|
||||
|
||||
'sys:locale-changed': { locale: string };
|
||||
// 折叠面板事件
|
||||
'ui:collapse-change': { activeIds: string[] };
|
||||
|
||||
// 漫游控制事件
|
||||
'walk:path-mode-toggle': { isActive: boolean };
|
||||
'walk:walk-mode-toggle': { isActive: boolean };
|
||||
'walk:plan-view-toggle': { isActive: boolean };
|
||||
'walk:speed-change': { speed: number };
|
||||
'walk:gravity-toggle': { enabled: boolean };
|
||||
'walk:collision-toggle': { enabled: boolean };
|
||||
// 系统事件
|
||||
'sys:theme-changed': { theme: string };
|
||||
'sys:locale-changed': { locale: string };
|
||||
|
||||
// 地图事件
|
||||
'map:opened': {};
|
||||
'map:closed': {};
|
||||
}
|
||||
|
||||
|
||||
// 漫游控制事件
|
||||
'walk:path-mode-toggle': { isActive: boolean };
|
||||
'walk:walk-mode-toggle': { isActive: boolean };
|
||||
'walk:plan-view-toggle': { isActive: boolean };
|
||||
'walk:speed-change': { speed: number };
|
||||
'walk:gravity-toggle': { enabled: boolean };
|
||||
'walk:collision-toggle': { enabled: boolean };
|
||||
|
||||
// 地图事件
|
||||
'map:opened': {};
|
||||
'map:closed': {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user