feat: add camera switch, fix click event/dialog resize/map state sync

- fix(engine): adapt click handler to base engine array format data[0].url/ids
- feat(toolbar): add perspective/orthographic camera switch button with dynamic icon
- fix(dialog): clamp resize to container bounds to prevent overflow
- feat(demo): add auto-combine feature with robust URL parsing and validation
- fix(walk): close minimap on walk exit and sync map state between toolbar and walk panel
- fix(engine): correct MiniMap getstate() casing to match base engine API
- build: rebuild demo libs
This commit is contained in:
yuding
2026-03-05 17:43:50 +08:00
parent b96e5f3262
commit 507112fcf9
18 changed files with 6890 additions and 6501 deletions

View File

@@ -739,6 +739,25 @@ export class BimButtonGroup implements IBimComponent {
private getIcon(icon?: string): string { return icon || this.DEFAULT_ICON; }
/**
* 更新指定按钮的图标
* @param id 按钮 ID
* @param icon 新的 SVG 图标字符串
*/
public updateButtonIcon(id: string, icon: string): void {
const btnEl = this.btnRefs.get(id);
if (!btnEl) return;
const iconEl = btnEl.querySelector('.opt-btn-icon');
if (iconEl) {
iconEl.innerHTML = this.getIcon(icon);
}
// 同步更新 button 对象的 icon 属性
const button = this.findButtonById(id);
if (button) {
button.icon = icon;
}
}
public updateButtonVisibility(id: string, visible: boolean): void {
if (!this.options.visibility) this.options.visibility = {};
this.options.visibility[id] = visible;

View File

@@ -0,0 +1,25 @@
import type { ButtonConfig } from '../../../index.type';
import { getIcon } from '../../../../../utils/icon-manager';
import type { ManagerRegistry } from '../../../../../core/manager-registry';
export const createCameraSwitchButton = (registry: ManagerRegistry): ButtonConfig => {
return {
id: 'camera-switch',
groupId: 'group-1',
type: 'button',
label: 'toolbar.cameraSwitch',
icon: getIcon('透视相机'),
keepActive: false,
onClick: () => {
const engineComponent = registry.engine3d?.getEngineComponent();
if (!engineComponent) return;
engineComponent.switchCamera();
// 切换后更新图标
const newType = engineComponent.getCameraType();
const newIcon = getIcon(newType === 'orthographic' ? '正交相机' : '透视相机');
registry.toolbar?.updateButtonIcon('camera-switch', newIcon);
}
};
};

View File

@@ -15,6 +15,9 @@ export const createMapButton = (registry: ManagerRegistry): ButtonConfig => {
icon: getIcon('地图'),
onClick: () => {
registry.engine3d?.getEngineComponent()?.toggleMiniMap();
// 同步漫游面板的小地图按钮状态
const mapState = registry.engine3d?.getEngineComponent()?.getMiniMapState() ?? false;
registry.walkControl?.panel?.setPlanViewActive(mapState);
}
};
};

View File

@@ -21,10 +21,12 @@ export class Toolbar extends BimButtonGroup {
const { createSectionAxisButton } = await import('./buttons/section/section-axis');
const { createSectionBoxButton } = await import('./buttons/section/section-box');
const { createAiChatButton } = await import('./buttons/ai-chat');
const { createCameraSwitchButton } = await import('./buttons/camera-switch');
this.addGroup('group-1');
this.addButton(createHomeButton(registry));
this.addButton(createCameraSwitchButton(registry));
this.addButton(createZoomBoxButton(registry));
this.addButton(createMeasureButton(registry));
this.addButton(createSectionMenuButton(registry));

View File

@@ -425,6 +425,10 @@ export class BimDialog implements IBimComponent {
let startY = 0;
let startW = 0;
let startH = 0;
let containerW = 0;
let containerH = 0;
let elLeft = 0;
let elTop = 0;
const onMouseDown = (e: MouseEvent) => {
e.preventDefault();
@@ -434,6 +438,12 @@ export class BimDialog implements IBimComponent {
startW = this.element.offsetWidth;
startH = this.element.offsetHeight;
// 缓存容器尺寸和弹窗位置,用于计算最大可缩放范围
containerW = this.container.clientWidth;
containerH = this.container.clientHeight;
elLeft = this.element.offsetLeft;
elTop = this.element.offsetTop;
// 关键:使用 capture: true
document.addEventListener('mousemove', onMouseMove, { capture: true });
document.addEventListener('mouseup', onMouseUp, { capture: true });
@@ -449,8 +459,12 @@ export class BimDialog implements IBimComponent {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
const newW = Math.max(this.options.minWidth || 100, startW + dx);
const newH = Math.max(this.options.minHeight || 50, startH + dy);
// 最大宽高:不超出容器右边界/下边界
const maxW = containerW - elLeft;
const maxH = containerH - elTop;
const newW = Math.min(maxW, Math.max(this.options.minWidth || 100, startW + dx));
const newH = Math.min(maxH, Math.max(this.options.minHeight || 50, startH + dy));
this.element.style.width = `${newW}px`;
this.element.style.height = `${newH}px`;

View File

@@ -7,8 +7,8 @@ import type { MeasureUnit, MeasurePrecision } from '../measure-panel/types';
import type { SectionBoxRange } from '../section-box-panel/types';
import type { ManagerRegistry } from '../../core/manager-registry';
// 导入第三方 SDK 的 createEngine 函数(从 npm 包引入)
//import { createEngine as createEngineSDK } from 'iflow-engine-base';
import { createEngine as createEngineSDK } from '../../../../bim_engine_base/dist/bim-engine-sdk.es';
import { createEngine as createEngineSDK } from 'iflow-engine-base';
//import { createEngine as createEngineSDK } from '../../../../bim_engine_base/dist/bim-engine-sdk.es';
import "../../../../bim_engine_base/dist/iflow-engine-base.css"
export type { EngineOptions, ModelLoadOptions, EngineInfo };
@@ -131,12 +131,13 @@ export class Engine implements IBimComponent {
this.setTheme(themeManager.getTheme());
// 监听构件点击事件
this.engine.events.on('click', (hit: any) => {
// 底层 interactionModule trigger 格式: [{url: string, ids: string[]}]
this.engine.events.on('click', (data: any) => {
const registry = this.registry;
if (hit && hit.object) {
if (data && Array.isArray(data) && data.length > 0 && data[0].url) {
this.selectedComponent = {
url: hit.object.url,
id: hit.object.name
url: data[0].url,
id: data[0].ids?.[0]
};
console.log('[Engine] 构件选中:', this.selectedComponent);
registry.emit('component:selected', this.selectedComponent);
@@ -593,6 +594,34 @@ export class Engine implements IBimComponent {
// ==================== 结束:剖切功能 ====================
// ==================== 相机切换 ====================
/**
* 切换相机类型(透视/正交)
* @remarks 底层调用 cameraModule.switchCurrentCamera(),保留当前相机位置
*/
public switchCamera(): void {
if (!this._isInitialized || !this.engine?.cameraModule) {
console.warn('[Engine] Cannot switch camera: engine not initialized.');
return;
}
this.engine.cameraModule.switchCurrentCamera();
}
/**
* 获取当前相机类型
* @returns 'perspective' | 'orthographic'
*/
public getCameraType(): 'perspective' | 'orthographic' {
if (!this._isInitialized || !this.engine?.cameraModule) {
return 'perspective';
}
// 底层 CameraType enum: PERSPECTIVE = 0, ORTHOGRAPHIC = 1
const type = this.engine.cameraModule.getCameraType();
return type === 1 ? 'orthographic' : 'perspective';
}
// ==================== 结束:相机切换 ====================
// ==================== 渲染模式 ====================
/**
@@ -943,7 +972,7 @@ export class Engine implements IBimComponent {
if (!this._isInitialized || !this.engine) {
return false;
}
return this.engine.minMap?.getState() ?? false;
return this.engine.minMap?.getstate() ?? false;
}
/**