feat(registry): 注册 ComponentDetailManager 到全局 Registry 和 BimEngine
This commit is contained in:
12237
src/bim-engine-sdk.es.js
12237
src/bim-engine-sdk.es.js
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ import { SectionAxisDialogManager } from './managers/section-axis-dialog-manager
|
||||
import { SectionBoxDialogManager } from './managers/section-box-dialog-manager';
|
||||
import { WalkControlManager } from './managers/walk-control-manager';
|
||||
import { MapDialogManager } from './managers/map-dialog-manager';
|
||||
import { ComponentDetailManager } from './managers/component-detail-manager';
|
||||
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
||||
import { localeManager } from './services/locale';
|
||||
import { themeManager } from './services/theme';
|
||||
@@ -40,6 +41,7 @@ export class BimEngine {
|
||||
public sectionBox: SectionBoxDialogManager | null = null;
|
||||
public walkControl: WalkControlManager | null = null;
|
||||
public map: MapDialogManager | null = null;
|
||||
public componentDetail: ComponentDetailManager | null = null;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement | string,
|
||||
@@ -129,6 +131,9 @@ export class BimEngine {
|
||||
this.registry.walkControl = this.walkControl;
|
||||
this.registry.map = this.map;
|
||||
|
||||
this.componentDetail = new ComponentDetailManager();
|
||||
this.registry.componentDetail = this.componentDetail;
|
||||
|
||||
this.updateTheme(themeManager.getTheme());
|
||||
themeManager.subscribe((theme) => {
|
||||
this.updateTheme(theme);
|
||||
|
||||
@@ -786,6 +786,21 @@ export class BimButtonGroup implements IBimComponent {
|
||||
public setBackgroundColor(color: string): void { this.setColors({ backgroundColor: color }); }
|
||||
private isVisible(id: string): boolean { return this.options.visibility?.[id] !== false; }
|
||||
|
||||
public setType(type: 'default' | 'glass-pill'): void {
|
||||
this.container.classList.remove('type-default', 'type-glass-pill');
|
||||
this.options.type = type;
|
||||
|
||||
if (type && type !== 'default') {
|
||||
this.container.classList.add(`type-${type}`);
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
public getType(): 'default' | 'glass-pill' {
|
||||
return this.options.type || 'default';
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
if (this.unsubscribeLocale) {
|
||||
this.unsubscribeLocale();
|
||||
|
||||
@@ -12,7 +12,7 @@ export const createZoomBoxButton = (): ButtonConfig => {
|
||||
icon: getIcon('框选放大'),
|
||||
onClick: () => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.engine3d?.getEngine().rangeScale.active();
|
||||
registry.engine3d?.activateZoomBox();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -274,20 +274,20 @@ export class BimDialog implements IBimComponent {
|
||||
|
||||
/**
|
||||
* 边界夹紧:保持当前 left/top 不变的前提下,确保弹窗不超出容器
|
||||
* 说明:用于 fitHeight / fitWidth 后的“尺寸变化”场景,避免弹窗被裁切。
|
||||
* 说明:用于 fitHeight / fitWidth 后的"尺寸变化"场景,避免弹窗被裁切。
|
||||
*/
|
||||
private clampToContainer(): void {
|
||||
const containerW = this.container.clientWidth;
|
||||
const containerH = this.container.clientHeight;
|
||||
const elW = this.element.offsetWidth;
|
||||
const elH = this.element.offsetHeight;
|
||||
const bottomOffset = this.options.bottomOffset ?? 70;
|
||||
|
||||
// 当前 left/top(优先从 style 读取,避免 NaN)
|
||||
const currentLeft = this.element.offsetLeft;
|
||||
const currentTop = this.element.offsetTop;
|
||||
|
||||
const maxLeft = Math.max(0, containerW - elW);
|
||||
const maxTop = Math.max(0, containerH - elH);
|
||||
const maxTop = Math.max(0, containerH - elH - bottomOffset);
|
||||
|
||||
const nextLeft = Math.max(0, Math.min(currentLeft, maxLeft));
|
||||
const nextTop = Math.max(0, Math.min(currentTop, maxTop));
|
||||
@@ -302,8 +302,8 @@ export class BimDialog implements IBimComponent {
|
||||
private initPosition() {
|
||||
const pos = this.options.position;
|
||||
const elRect = this.element.getBoundingClientRect();
|
||||
const bottomOffset = this.options.bottomOffset ?? 70;
|
||||
|
||||
// 计算相对父容器的定位
|
||||
let left = 0;
|
||||
let top = 0;
|
||||
|
||||
@@ -326,9 +326,9 @@ export class BimDialog implements IBimComponent {
|
||||
case 'top-right': left = pW - elW; top = 0; break;
|
||||
case 'left-center': left = 0; top = (pH - elH) / 2; break;
|
||||
case 'right-center': left = pW - elW; top = (pH - elH) / 2; break;
|
||||
case 'bottom-left': left = 0; top = pH - elH; break;
|
||||
case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break;
|
||||
case 'bottom-right': left = pW - elW; top = pH - elH; break;
|
||||
case 'bottom-left': left = 0; top = pH - elH - bottomOffset; break;
|
||||
case 'bottom-center': left = (pW - elW) / 2; top = pH - elH - bottomOffset; break;
|
||||
case 'bottom-right': left = pW - elW; top = pH - elH - bottomOffset; break;
|
||||
default:
|
||||
left = (pW - elW) / 2;
|
||||
top = (pH - elH) / 2;
|
||||
@@ -336,7 +336,7 @@ export class BimDialog implements IBimComponent {
|
||||
}
|
||||
|
||||
left = Math.max(0, Math.min(left, pW - elW));
|
||||
top = Math.max(0, Math.min(top, pH - elH));
|
||||
top = Math.max(0, Math.min(top, pH - elH - bottomOffset));
|
||||
|
||||
this.element.style.left = `${left}px`;
|
||||
this.element.style.top = `${top}px`;
|
||||
@@ -354,24 +354,23 @@ export class BimDialog implements IBimComponent {
|
||||
let containerH = 0;
|
||||
let elW = 0;
|
||||
let elH = 0;
|
||||
let bottomOffset = 0;
|
||||
|
||||
const onMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault(); // 阻止默认行为(如选中文本),非常重要,防止卡顿
|
||||
e.stopPropagation(); // 阻止传递给 Three.js
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
startLeft = this.element.offsetLeft;
|
||||
startTop = this.element.offsetTop;
|
||||
|
||||
// 缓存尺寸,减少 reflow
|
||||
containerW = this.container.clientWidth;
|
||||
containerH = this.container.clientHeight;
|
||||
elW = this.element.offsetWidth;
|
||||
elH = this.element.offsetHeight;
|
||||
bottomOffset = this.options.bottomOffset ?? 70;
|
||||
|
||||
// 关键:使用 capture: true
|
||||
// 确保即使 createDom 阻止了冒泡,document 也能在捕获阶段收到事件
|
||||
document.addEventListener('mousemove', onMouseMove, { capture: true });
|
||||
document.addEventListener('mouseup', onMouseUp, { capture: true });
|
||||
};
|
||||
@@ -380,7 +379,6 @@ export class BimDialog implements IBimComponent {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// 节流优化:使用 requestAnimationFrame
|
||||
if (this.rafId) return;
|
||||
|
||||
this.rafId = requestAnimationFrame(() => {
|
||||
@@ -391,7 +389,7 @@ export class BimDialog implements IBimComponent {
|
||||
let newTop = startTop + dy;
|
||||
|
||||
const maxLeft = containerW - elW;
|
||||
const maxTop = containerH - elH;
|
||||
const maxTop = containerH - elH - bottomOffset;
|
||||
|
||||
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
|
||||
newTop = Math.max(0, Math.min(newTop, maxTop));
|
||||
|
||||
@@ -50,6 +50,8 @@ export interface DialogOptions extends DialogColors {
|
||||
minWidth?: number;
|
||||
/** 最小高度限制 */
|
||||
minHeight?: number;
|
||||
/** 底部偏移量(用于避开底部工具栏),默认 70px */
|
||||
bottomOffset?: number;
|
||||
/** 关闭时的回调函数 */
|
||||
onClose?: () => void;
|
||||
/** 打开时的回调函数 */
|
||||
|
||||
@@ -294,30 +294,7 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.bim-measure-clear-btn:hover,
|
||||
.bim-measure-clear-btn:active,
|
||||
.bim-measure-clear-btn:focus {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.bim-measure-clear-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--bim-measure-danger, white); /* 先用偏绿(接近截图),可由主题覆盖 */
|
||||
cursor: pointer;
|
||||
/* 缩小可点击区域:仅文字本身附近 */
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
/* 防止外部环境(如 demo)给 button 设置 flex: 1 导致“各占一半” */
|
||||
flex: 0 0 auto !important;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 你要求:删除按钮不需要 hover 效果 */
|
||||
/* 删除按钮不需要 hover 效果 */
|
||||
.bim-measure-clear-btn:hover,
|
||||
.bim-measure-clear-btn:active,
|
||||
.bim-measure-clear-btn:focus {
|
||||
|
||||
@@ -20,10 +20,11 @@ import type { SectionAxisDialogManager } from '../managers/section-axis-dialog-m
|
||||
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';
|
||||
import type { ComponentDetailManager } from '../managers/component-detail-manager';
|
||||
|
||||
/**
|
||||
* Manager 注册表 - 单例模式
|
||||
* 提供所有 Manager 的全局访问点,替代 engine 层层传递
|
||||
* 提供所有 Manager 的全局访问点,替代 engine 层层传递
|
||||
*/
|
||||
export class ManagerRegistry {
|
||||
/** 单例实例 */
|
||||
@@ -66,6 +67,8 @@ export class ManagerRegistry {
|
||||
public walkPath: WalkPathDialogManager | null = null;
|
||||
/** 漫游平面图对话框管理器 */
|
||||
public walkPlanView: WalkPlanViewDialogManager | null = null;
|
||||
/** 构件详情管理器 */
|
||||
public componentDetail: ComponentDetailManager | null = null;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ export class EngineManager extends BaseManager {
|
||||
group: 'component',
|
||||
onClick: () => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
// @ts-expect-error - componentDetail will be added in Task 4
|
||||
registry.componentDetail?.show(selected.url, selected.id);
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export class MeasureDialogManager extends BaseDialogManager {
|
||||
},
|
||||
onClearAll: () => {
|
||||
console.log('[MeasureDialogManager] 删除全部');
|
||||
this.registry.engine3d?.clearAllMeasures();
|
||||
},
|
||||
onSettings: () => {
|
||||
console.log('[MeasureDialogManager] 打开设置');
|
||||
|
||||
@@ -57,27 +57,30 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
defaultHidden: false,
|
||||
defaultReversed: false,
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionBoxDialogManager] 隐藏切换:', isHidden);
|
||||
// 底层暂不支持隐藏功能
|
||||
console.log('[SectionBoxDialogManager] 隐藏切换(底层暂不支持):', isHidden);
|
||||
},
|
||||
onReverseToggle: (isReversed) => {
|
||||
console.log('[SectionBoxDialogManager] 反向切换:', isReversed);
|
||||
// 底层暂不支持反向功能
|
||||
console.log('[SectionBoxDialogManager] 反向切换(底层暂不支持):', isReversed);
|
||||
},
|
||||
onFitToModel: () => {
|
||||
console.log('[SectionBoxDialogManager] 适应到模型');
|
||||
this.registry.engine3d?.fitSectionBoxToModel();
|
||||
},
|
||||
onReset: () => {
|
||||
console.log('[SectionBoxDialogManager] 重置');
|
||||
this.registry.engine3d?.resetSectionBox();
|
||||
},
|
||||
onRangeChange: (range) => {
|
||||
console.log('[SectionBoxDialogManager] 范围变化:', range);
|
||||
this.registry.engine3d?.setSectionBoxRange(range);
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
/** 对话框创建后的回调,自适应高度 */
|
||||
/** 对话框创建后的回调,激活剖切盒并自适应高度 */
|
||||
protected onDialogCreated(): void {
|
||||
this.registry.engine3d?.activateSectionBox();
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
|
||||
@@ -86,8 +89,9 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
this.registry.toolbar?.setBtnActive('section-box', false);
|
||||
}
|
||||
|
||||
/** 销毁前的清理,销毁面板实例 */
|
||||
/** 销毁前的清理,停用剖切盒并销毁面板实例 */
|
||||
protected onBeforeDestroy(): void {
|
||||
this.registry.engine3d?.deactivateSectionBox();
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -157,4 +157,12 @@ export class ToolbarManager extends BaseManager {
|
||||
public getContainer(): HTMLElement | null {
|
||||
return this.toolbarContainer;
|
||||
}
|
||||
|
||||
public setType(type: 'default' | 'glass-pill'): void {
|
||||
this.toolbar?.setType(type);
|
||||
}
|
||||
|
||||
public getType(): 'default' | 'glass-pill' {
|
||||
return this.toolbar?.getType() || 'default';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ export class WalkControlManager extends BaseManager {
|
||||
|
||||
this.registry.toolbar.hide();
|
||||
|
||||
// 打开漫游面板时,默认激活第一人称模式
|
||||
console.log('[WalkControl] 打开漫游面板,激活第一人称模式');
|
||||
this.registry.engine3d?.activateFirstPersonMode();
|
||||
|
||||
this.panel = new WalkControlPanel({
|
||||
onPlanViewToggle: (isActive) => {
|
||||
console.log('[WalkControl] 地图:', isActive);
|
||||
@@ -55,19 +59,16 @@ export class WalkControlManager extends BaseManager {
|
||||
this.emit('walk:path-mode-toggle', { isActive });
|
||||
},
|
||||
onWalkModeToggle: (isActive) => {
|
||||
console.log('[WalkControl] 第一人称漫游:', isActive);
|
||||
console.log('[WalkControl] 第三人称漫游按钮点击:', isActive);
|
||||
if (isActive) {
|
||||
this.pathManager?.hide();
|
||||
this.registry.engine3d?.activateFirstPersonMode();
|
||||
} else {
|
||||
this.registry.engine3d?.deactivateFirstPersonMode();
|
||||
alert('第三人称功能开发中');
|
||||
}
|
||||
this.emit('walk:walk-mode-toggle', { isActive });
|
||||
},
|
||||
onSpeedChange: (speed) => {
|
||||
console.log('[WalkControl] 速度变化:', speed);
|
||||
// 将 UI 速度值转换为引擎速度值(UI: 1-10, 引擎: 0.01-0.1)
|
||||
const engineSpeed = speed * 0.01;
|
||||
const engineSpeed = speed * 0.1;
|
||||
this.registry.engine3d?.setWalkSpeed(engineSpeed);
|
||||
this.emit('walk:speed-change', { speed });
|
||||
},
|
||||
@@ -122,6 +123,9 @@ export class WalkControlManager extends BaseManager {
|
||||
public hide(): void {
|
||||
this.pathManager?.hide();
|
||||
|
||||
console.log('[WalkControl] 关闭漫游面板,退出第一人称模式');
|
||||
this.registry.engine3d?.deactivateFirstPersonMode();
|
||||
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
Reference in New Issue
Block a user