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