refactor: slim down EngineManager from 861 to 290 lines by removing passthrough proxy pattern
- EngineManager now only exposes public SDK API (initialize, loadModel, pause/resumeRendering, getEngineComponent, destroy) - Internal managers access Engine component directly via this.engineComponent getter on BaseManager - Non-manager components use registry.engine3d.getEngineComponent() for direct Engine access - Replaced getEngine() with onRawEvent()/offRawEvent() for raw engine event access - Migrated 62 call sites across 13 files (9 managers, 1 panel, 3 toolbar buttons) - Updated all architecture docs, API docs, and README to reflect new patterns
This commit is contained in:
@@ -12,7 +12,7 @@ export const createHomeButton = (registry: ManagerRegistry): ButtonConfig => {
|
||||
keepActive: false,
|
||||
onClick: (button) => {
|
||||
console.log('首页按钮被点击:', button.id);
|
||||
registry.engine3d?.CameraGoHome();
|
||||
registry.engine3d?.getEngineComponent()?.CameraGoHome();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ export const createMapButton = (registry: ManagerRegistry): ButtonConfig => {
|
||||
keepActive: true,
|
||||
icon: getIcon('地图'),
|
||||
onClick: () => {
|
||||
registry.engine3d?.toggleMiniMap();
|
||||
registry.engine3d?.getEngineComponent()?.toggleMiniMap();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ export const createZoomBoxButton = (registry: ManagerRegistry): ButtonConfig =>
|
||||
label: 'toolbar.zoomBox',
|
||||
icon: getIcon('框选放大'),
|
||||
onClick: () => {
|
||||
registry.engine3d?.activateZoomBox();
|
||||
registry.engine3d?.getEngineComponent()?.activateZoomBox();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -204,10 +204,22 @@ export class Engine implements IBimComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始 3D 引擎实例
|
||||
* 订阅原始引擎事件
|
||||
* 用于需要访问底层引擎事件的场景(如测量回调、剖切移动等)
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public getEngine(): any {
|
||||
return this.engine;
|
||||
public onRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.events?.on(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅原始引擎事件
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public offRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.events?.off(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -612,6 +624,202 @@ export class Engine implements IBimComponent {
|
||||
|
||||
// ==================== 结束:渲染模式 ====================
|
||||
|
||||
// ==================== 设置功能 ====================
|
||||
|
||||
// ---- 边线 ----
|
||||
|
||||
/** 启用模型边线显示 */
|
||||
public activeModelEdge(): void {
|
||||
if (!this._isInitialized || !this.engine?.modelEdge) {
|
||||
console.warn('[Engine] Cannot active model edge: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.modelEdge.active();
|
||||
}
|
||||
|
||||
/** 关闭模型边线显示 */
|
||||
public disActiveModelEdge(): void {
|
||||
if (!this._isInitialized || !this.engine?.modelEdge) {
|
||||
return;
|
||||
}
|
||||
this.engine.modelEdge.disActive();
|
||||
}
|
||||
|
||||
/** 获取边线显示状态 */
|
||||
public getModelEdgeActive(): boolean {
|
||||
if (!this._isInitialized || !this.engine?.modelEdge) {
|
||||
return false;
|
||||
}
|
||||
return this.engine.modelEdge.getActive?.() ?? false;
|
||||
}
|
||||
|
||||
// ---- 光照强度 ----
|
||||
|
||||
/**
|
||||
* 设置环境光照强度
|
||||
* @param intensity 0-100
|
||||
*/
|
||||
public setAmbientLightIntensity(intensity: number): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set ambient light intensity: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setAmbientLightIntensity(intensity);
|
||||
}
|
||||
|
||||
/** 获取当前环境光照强度 */
|
||||
public getAmbientLightIntensity(): number {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return 50;
|
||||
}
|
||||
return this.engine.setting.getAmbientLightIntensity?.() ?? 50;
|
||||
}
|
||||
|
||||
// ---- 对比度 ----
|
||||
|
||||
/**
|
||||
* 设置场景对比度
|
||||
* @param contrast 0-100
|
||||
*/
|
||||
public setSceneContrast(contrast: number): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set scene contrast: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setSceneContrast(contrast);
|
||||
}
|
||||
|
||||
/** 获取当前场景对比度 */
|
||||
public getSceneContrast(): number {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return 50;
|
||||
}
|
||||
return this.engine.setting.getSceneContrast?.() ?? 50;
|
||||
}
|
||||
|
||||
// ---- 饾和度 ----
|
||||
|
||||
/**
|
||||
* 设置场景饾和度
|
||||
* @param saturation 0-100
|
||||
*/
|
||||
public setSceneSaturation(saturation: number): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set scene saturation: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setSceneSaturation(saturation);
|
||||
}
|
||||
|
||||
/** 获取当前场景饾和度 */
|
||||
public getSceneSaturation(): number {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return 50;
|
||||
}
|
||||
return this.engine.setting.getSceneSaturation?.() ?? 50;
|
||||
}
|
||||
|
||||
// ---- 环境背景 ----
|
||||
|
||||
/** 获取 HDR 背景列表 */
|
||||
public getHDRBackgroundList(): { name: string; id: string }[] {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return [];
|
||||
}
|
||||
return this.engine.setting.getHDRBackground?.() ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前 HDR 背景
|
||||
* @param id 背景 ID
|
||||
*/
|
||||
public setHDRBackgroundId(id: string): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set HDR background: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setHDRBackgroundId(id);
|
||||
}
|
||||
|
||||
/** 获取当前 HDR 背景 ID */
|
||||
public getHDRBackgroundId(): string {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return '';
|
||||
}
|
||||
return this.engine.setting.getHDRBackgroundId?.() ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 HDR 背景可见性
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
public setHDRBackgroundVisibility(visible: boolean): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set HDR background visibility: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setHDRBackgroundVisibility(visible);
|
||||
}
|
||||
|
||||
/** 获取 HDR 背景可见性 */
|
||||
public getHDRBackgroundVisibility(): boolean {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return true;
|
||||
}
|
||||
return this.engine.setting.getHDRBackgroundVisibility?.() ?? true;
|
||||
}
|
||||
|
||||
// ---- 地面 ----
|
||||
|
||||
/** 获取地面列表 */
|
||||
public getGroundList(): { name: string; id: string }[] {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return [];
|
||||
}
|
||||
return this.engine.setting.getGroundList?.() ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前地面类型
|
||||
* @param id 地面 ID
|
||||
*/
|
||||
public setGroundId(id: string): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set ground: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setGroundId(id);
|
||||
}
|
||||
|
||||
/** 获取当前地面 ID */
|
||||
public getGroundId(): string {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return '';
|
||||
}
|
||||
return this.engine.setting.getGroundId?.() ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置地面高度(单位:m)
|
||||
* @param elevation 地面高度值
|
||||
*/
|
||||
public setGroundElevation(elevation: number): void {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
console.warn('[Engine] Cannot set ground elevation: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engine.setting.setGroundElevation(elevation);
|
||||
}
|
||||
|
||||
/** 获取当前地面高度(单位:m) */
|
||||
public getGroundElevation(): number {
|
||||
if (!this._isInitialized || !this.engine?.setting) {
|
||||
return 0;
|
||||
}
|
||||
return this.engine.setting.getGroundElevation?.() ?? 0;
|
||||
}
|
||||
|
||||
// ==================== 结束:设置功能 ====================
|
||||
// ==================== 漫游功能 ====================
|
||||
|
||||
/** 漫游模式是否激活 */
|
||||
|
||||
@@ -66,7 +66,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
* 在面板打开时调用,获取底层引擎中已存在的漫游点
|
||||
*/
|
||||
private loadPointsFromEngine(): void {
|
||||
const enginePoints = this.registry.engine3d?.pathRoamingGetPoints() ?? [];
|
||||
const enginePoints = this.registry.engine3d?.getEngineComponent()?.pathRoamingGetPoints() ?? [];
|
||||
// 将引擎返回的点转换为本地格式
|
||||
this.points = enginePoints.map((_: any, index: number) => ({
|
||||
index: index
|
||||
@@ -288,7 +288,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
*/
|
||||
private addPoint(): void {
|
||||
// 调用引擎添加漫游点
|
||||
this.registry.engine3d?.pathRoamingAddPoint();
|
||||
this.registry.engine3d?.getEngineComponent()?.pathRoamingAddPoint();
|
||||
// 更新本地状态
|
||||
const newIndex = this.points.length;
|
||||
this.points.push({ index: newIndex });
|
||||
@@ -302,7 +302,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
*/
|
||||
private deletePoint(index: number): void {
|
||||
// 调用引擎删除漫游点
|
||||
this.registry.engine3d?.pathRoamingRemovePoint(index);
|
||||
this.registry.engine3d?.getEngineComponent()?.pathRoamingRemovePoint(index);
|
||||
// 从本地列表中移除
|
||||
this.points.splice(index, 1);
|
||||
// 重新索引
|
||||
@@ -316,7 +316,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
*/
|
||||
private deleteAllPoints(): void {
|
||||
// 调用引擎清除所有漫游点
|
||||
this.registry.engine3d?.pathRoamingClearPoints();
|
||||
this.registry.engine3d?.getEngineComponent()?.pathRoamingClearPoints();
|
||||
// 清空本地列表
|
||||
this.points = [];
|
||||
// 重新渲染
|
||||
@@ -328,7 +328,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
* @param index 目标漫游点索引
|
||||
*/
|
||||
private jumpToPoint(index: number): void {
|
||||
this.registry.engine3d?.pathRoamingJumpToPoint(index);
|
||||
this.registry.engine3d?.getEngineComponent()?.pathRoamingJumpToPoint(index);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +343,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
|
||||
console.log('[WalkPathPanel] 开始播放漫游', { duration: this.duration, loop: this.loop, pointsCount: this.points.length });
|
||||
|
||||
this.registry.engine3d?.pathRoamingPlay({
|
||||
this.registry.engine3d?.getEngineComponent()?.pathRoamingPlay({
|
||||
duration: this.duration,
|
||||
loop: this.loop,
|
||||
onPointComplete: (pointIndex: number) => {
|
||||
@@ -365,7 +365,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
*/
|
||||
private stopPath(): void {
|
||||
console.log('[WalkPathPanel] 停止漫游');
|
||||
this.registry.engine3d?.pathRoamingStop();
|
||||
this.registry.engine3d?.getEngineComponent()?.pathRoamingStop();
|
||||
this.isPlaying = false;
|
||||
this.playingPointIndex = -1;
|
||||
this.render();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
import { ManagerRegistry } from './manager-registry';
|
||||
import type { EngineEvents } from '../types/events';
|
||||
import type { Engine } from '../components/engine';
|
||||
|
||||
/**
|
||||
* Manager 抽象基类
|
||||
@@ -20,6 +21,15 @@ export abstract class BaseManager {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Engine 组件实例的便捷访问器
|
||||
* 内部管理器通过此 getter 直接访问 Engine 组件的全部能力
|
||||
* 替代原来的 this.registry.engine3d?.xxx() 透传链
|
||||
*/
|
||||
protected get engineComponent(): Engine | null {
|
||||
return this.registry.engine3d?.getEngineComponent?.() ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅事件(自动追踪,destroy 时自动取消)
|
||||
* @param event 事件名称
|
||||
|
||||
@@ -227,27 +227,13 @@ export const enUS: TranslationDictionary = {
|
||||
advanced: 'Quality',
|
||||
},
|
||||
edgeLine: 'Edge Lines',
|
||||
ground: 'Ground',
|
||||
groundSize: 'Size',
|
||||
groundTypes: {
|
||||
none: 'None',
|
||||
concrete: 'Concrete',
|
||||
grass: 'Grass',
|
||||
tile: 'Tile',
|
||||
water: 'Water',
|
||||
wood: 'Wood',
|
||||
},
|
||||
contrast: 'Contrast',
|
||||
saturation: 'Saturation',
|
||||
lightIntensity: 'Light Intensity',
|
||||
environment: 'Environment',
|
||||
environments: {
|
||||
default: 'Default',
|
||||
outdoor: 'Outdoor',
|
||||
indoor: 'Indoor',
|
||||
night: 'Night',
|
||||
overcast: 'Overcast',
|
||||
studio: 'Studio',
|
||||
},
|
||||
backgroundVisible: 'Show Background',
|
||||
ground: 'Ground',
|
||||
groundElevation: 'Ground Elevation',
|
||||
groundElevationUnit: 'm',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -250,17 +250,6 @@ export interface TranslationDictionary {
|
||||
};
|
||||
/** 边线 */
|
||||
edgeLine: string;
|
||||
/** 显示地面 */
|
||||
ground: string;
|
||||
groundSize: string;
|
||||
groundTypes: {
|
||||
none: string;
|
||||
concrete: string;
|
||||
grass: string;
|
||||
tile: string;
|
||||
water: string;
|
||||
wood: string;
|
||||
};
|
||||
/** 对比度 */
|
||||
contrast: string;
|
||||
/** 饱和度 */
|
||||
@@ -269,14 +258,14 @@ export interface TranslationDictionary {
|
||||
lightIntensity: string;
|
||||
/** 环境背景 */
|
||||
environment: string;
|
||||
environments: {
|
||||
default: string;
|
||||
outdoor: string;
|
||||
indoor: string;
|
||||
night: string;
|
||||
overcast: string;
|
||||
studio: string;
|
||||
};
|
||||
/** 显示背景 */
|
||||
backgroundVisible: string;
|
||||
/** 显示地面 */
|
||||
ground: string;
|
||||
/** 地面高度 */
|
||||
groundElevation: string;
|
||||
/** 地面高度单位 */
|
||||
groundElevationUnit: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -227,27 +227,13 @@ export const zhCN: TranslationDictionary = {
|
||||
advanced: '效果模式',
|
||||
},
|
||||
edgeLine: '边线',
|
||||
ground: '显示地面',
|
||||
groundSize: '大小',
|
||||
groundTypes: {
|
||||
none: '无',
|
||||
concrete: '混凝土',
|
||||
grass: '草地',
|
||||
tile: '瓷砖',
|
||||
water: '水面',
|
||||
wood: '木地板',
|
||||
},
|
||||
contrast: '对比度',
|
||||
saturation: '饱和度',
|
||||
lightIntensity: '光照强度',
|
||||
environment: '环境背景',
|
||||
environments: {
|
||||
default: '默认',
|
||||
outdoor: '室外',
|
||||
indoor: '室内',
|
||||
night: '夜晚',
|
||||
overcast: '阴天',
|
||||
studio: '工作室',
|
||||
},
|
||||
backgroundVisible: '显示背景',
|
||||
ground: '显示地面',
|
||||
groundElevation: '地面高度',
|
||||
groundElevationUnit: 'm',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,14 +71,12 @@ export class ComponentDetailManager extends BaseManager {
|
||||
|
||||
this.showLoading();
|
||||
|
||||
this.registry.engine3d?.getComponentProperties(
|
||||
this.currentSelection.url,
|
||||
this.currentSelection.id,
|
||||
(data) => {
|
||||
this.propertiesData = data;
|
||||
this.renderTabbedContent();
|
||||
}
|
||||
);
|
||||
this.engineComponent?.getComponentProperties(this.currentSelection.url,
|
||||
this.currentSelection.id,
|
||||
(data) => {
|
||||
this.propertiesData = data;
|
||||
this.renderTabbedContent();
|
||||
});
|
||||
}
|
||||
|
||||
private showLoading(): void {
|
||||
|
||||
@@ -334,9 +334,9 @@ export class ConstructTreeManagerBtn extends BaseManager {
|
||||
this.setVisible(false);
|
||||
|
||||
// 从 3D 引擎获取原始树数据
|
||||
const levelTreeData = this.registry.engine3d?.getLevelTreeData() ?? [];
|
||||
const typeTreeData = this.registry.engine3d?.getTypeTreeData() ?? [];
|
||||
const majorTreeData = this.registry.engine3d?.getMajorTreeData() ?? [];
|
||||
const levelTreeData = this.engineComponent?.getLevelTreeData() ?? [];
|
||||
const typeTreeData = this.engineComponent?.getTypeTreeData() ?? [];
|
||||
const majorTreeData = this.engineComponent?.getMajorTreeData() ?? [];
|
||||
|
||||
// 调试日志:输出原始数据
|
||||
console.log('[ConstructTree] 原始数据 (Level):', levelTreeData);
|
||||
@@ -370,9 +370,9 @@ export class ConstructTreeManagerBtn extends BaseManager {
|
||||
if (!modelParam.length) return;
|
||||
|
||||
if (node.checkState === TreeNodeCheckState.Checked) {
|
||||
this.registry.engine3d?.showModel(modelParam);
|
||||
this.engineComponent?.showModel(modelParam);
|
||||
} else {
|
||||
this.registry.engine3d?.hideModels(modelParam);
|
||||
this.engineComponent?.hideModels(modelParam);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -383,14 +383,14 @@ export class ConstructTreeManagerBtn extends BaseManager {
|
||||
const modelParam = collectModelParams(node);
|
||||
if (!modelParam.length) return;
|
||||
|
||||
this.registry.engine3d?.unhighlightAllModels();
|
||||
this.registry.engine3d?.highlightModel(modelParam);
|
||||
this.registry.engine3d?.viewScaleToModel(modelParam);
|
||||
this.engineComponent?.unhighlightAllModels();
|
||||
this.engineComponent?.highlightModel(modelParam);
|
||||
this.engineComponent?.viewScaleToModel(modelParam);
|
||||
},
|
||||
|
||||
// 再次点击已选中节点时取消高亮
|
||||
onNodeDeselect: () => {
|
||||
this.registry.engine3d?.unhighlightAllModels();
|
||||
this.engineComponent?.unhighlightAllModels();
|
||||
},
|
||||
|
||||
onNodeExpand: () => {
|
||||
@@ -430,7 +430,7 @@ export class ConstructTreeManagerBtn extends BaseManager {
|
||||
* 在 Tab 切换和对话框初始化时调用
|
||||
*/
|
||||
const resetAllTrees = () => {
|
||||
this.registry.engine3d?.showAllModels();
|
||||
this.engineComponent?.showAllModels();
|
||||
componentTree.checkAllNodes(true);
|
||||
typeTree.checkAllNodes(true);
|
||||
majorTree.checkAllNodes(true);
|
||||
|
||||
@@ -29,7 +29,7 @@ export class EngineInfoDialogManager extends BaseDialogManager {
|
||||
}
|
||||
|
||||
protected createContent(): HTMLElement {
|
||||
const info: EngineInfo | null = this.registry.engine3d?.getEngineInfo() ?? null;
|
||||
const info: EngineInfo | null = this.engineComponent?.getEngineInfo() ?? null;
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.className = 'engine-info-content';
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
/**
|
||||
* 3D 引擎管理器
|
||||
* 负责管理 3D 渲染引擎的初始化、模型加载和测量功能
|
||||
* 负责管理 3D 渲染引擎的初始化、模型加载和生命周期
|
||||
*
|
||||
* 设计原则:
|
||||
* - EngineManager 只暴露面向外部用户的公共 API
|
||||
* - 内部管理器通过 getEngineComponent() 直接访问 Engine 组件
|
||||
* - 不再透传 Engine 组件的方法
|
||||
*/
|
||||
import { Engine, type EngineOptions, type ModelLoadOptions, type EngineInfo } from '../components/engine';
|
||||
import { Engine, type EngineOptions, type ModelLoadOptions } from '../components/engine';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { RightKeyManager } from './right-key-manager';
|
||||
import type { MeasureMode, ClearHeightDirection, ClearHeightSelectType } from '../types/measure';
|
||||
import type { MeasureUnit, MeasurePrecision } from '../components/measure-panel/types';
|
||||
import type { SectionBoxRange } from '../components/section-box-panel/types';
|
||||
import type { MenuItemConfig } from '../components/menu/item';
|
||||
import { ManagerRegistry } from '../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 3D 引擎管理器
|
||||
* 封装底层 3D 引擎,提供模型加载、相机控制、测量等功能
|
||||
* 封装底层 3D 引擎,提供模型加载、渲染控制等公共 API
|
||||
*/
|
||||
export class EngineManager extends BaseManager {
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 引擎实例 */
|
||||
/** 引擎组件实例 */
|
||||
private engineInstance: Engine | null = null;
|
||||
/** 右键菜单管理器 */
|
||||
public rightKey: RightKeyManager | null = null;
|
||||
@@ -28,6 +30,15 @@ export class EngineManager extends BaseManager {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Engine 组件实例
|
||||
* 内部管理器通过此方法直接访问 Engine 组件的全部能力
|
||||
* @returns Engine 组件实例,未初始化时返回 null
|
||||
*/
|
||||
public getEngineComponent(): Engine | null {
|
||||
return this.engineInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 3D 引擎
|
||||
* @param options 引擎配置选项
|
||||
@@ -51,7 +62,7 @@ export class EngineManager extends BaseManager {
|
||||
this.rightKey = new RightKeyManager(this.container, this.registry);
|
||||
|
||||
this.rightKey.registerHandler((_e) => {
|
||||
const selected = this.getSelectedComponent();
|
||||
const selected = this.engineInstance?.getSelectedComponent();
|
||||
const items: MenuItemConfig[] = [];
|
||||
|
||||
if (selected) {
|
||||
@@ -76,9 +87,9 @@ export class EngineManager extends BaseManager {
|
||||
group: 'component',
|
||||
order: 2,
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.hideModels(models);
|
||||
this.engineInstance?.hideModels(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -91,9 +102,9 @@ export class EngineManager extends BaseManager {
|
||||
group: 'component',
|
||||
order: 3,
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.translucentModels(models);
|
||||
this.engineInstance?.translucentModels(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -106,7 +117,7 @@ export class EngineManager extends BaseManager {
|
||||
group: 'component',
|
||||
order: 4,
|
||||
onClick: () => {
|
||||
this.unTranslucentModel();
|
||||
this.engineInstance?.unTranslucentModel();
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
});
|
||||
@@ -123,9 +134,9 @@ export class EngineManager extends BaseManager {
|
||||
id: 'hideOthers',
|
||||
label: 'menu.hideOthers',
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.isolateModels(models);
|
||||
this.engineInstance?.isolateModels(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -134,9 +145,9 @@ export class EngineManager extends BaseManager {
|
||||
id: 'transparentOthers',
|
||||
label: 'menu.transparentOthers',
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.translucentOtherModels(models);
|
||||
this.engineInstance?.translucentOtherModels(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -155,9 +166,9 @@ export class EngineManager extends BaseManager {
|
||||
id: 'selectSameType',
|
||||
label: 'menu.selectSameType',
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.batchSelectSameTypeModel(models);
|
||||
this.engineInstance?.batchSelectSameTypeModel(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -166,9 +177,9 @@ export class EngineManager extends BaseManager {
|
||||
id: 'selectSameLevel',
|
||||
label: 'menu.selectSameLevel',
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.batchSelectSameLevelModel(models);
|
||||
this.engineInstance?.batchSelectSameLevelModel(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -177,9 +188,9 @@ export class EngineManager extends BaseManager {
|
||||
id: 'selectSameLevelType',
|
||||
label: 'menu.selectSameLevelType',
|
||||
onClick: () => {
|
||||
const models = this.getHighlightModels();
|
||||
const models = this.engineInstance?.getHighlightModels();
|
||||
if (models) {
|
||||
this.batchSelectSameLevelTypeModel(models);
|
||||
this.engineInstance?.batchSelectSameLevelTypeModel(models);
|
||||
}
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -194,7 +205,7 @@ export class EngineManager extends BaseManager {
|
||||
group: 'component',
|
||||
order: 7,
|
||||
onClick: () => {
|
||||
this.fitSectionBoxToModel();
|
||||
this.engineInstance?.fitSectionBoxToModel();
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
});
|
||||
@@ -207,7 +218,7 @@ export class EngineManager extends BaseManager {
|
||||
group: 'component',
|
||||
order: 8,
|
||||
onClick: () => {
|
||||
this.showAllModels();
|
||||
this.engineInstance?.showAllModels();
|
||||
this.emit('menu:show-all', {});
|
||||
this.rightKey?.hide();
|
||||
}
|
||||
@@ -245,27 +256,6 @@ export class EngineManager extends BaseManager {
|
||||
this.engineInstance.loadModel(urls, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层引擎实例
|
||||
* @returns 引擎实例
|
||||
*/
|
||||
public getEngine(): any {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return null;
|
||||
}
|
||||
return this.engineInstance.getEngine();
|
||||
}
|
||||
|
||||
/** 相机回到初始位置 */
|
||||
public CameraGoHome(): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.CameraGoHome();
|
||||
}
|
||||
|
||||
/** 暂停渲染 */
|
||||
public pauseRendering(): void {
|
||||
if (!this.engineInstance) {
|
||||
@@ -284,457 +274,6 @@ export class EngineManager extends BaseManager {
|
||||
this.engineInstance.resumeRendering();
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活测量模式
|
||||
* @param mode 测量模式
|
||||
*/
|
||||
public activateMeasure(mode: MeasureMode): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.activateMeasure(mode);
|
||||
}
|
||||
|
||||
/** 停用测量模式 */
|
||||
public deactivateMeasure(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.deactivateMeasure();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前测量类型
|
||||
* @returns 当前测量模式
|
||||
*/
|
||||
public getCurrentMeasureType(): MeasureMode | null {
|
||||
if (!this.engineInstance) {
|
||||
return null;
|
||||
}
|
||||
return this.engineInstance.getCurrentMeasureType();
|
||||
}
|
||||
|
||||
public clearAllMeasures(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.clearAllMeasures();
|
||||
}
|
||||
|
||||
public saveMeasureSetting(setting: { unit: MeasureUnit; precision: MeasurePrecision }): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.saveMeasureSetting(setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置净高测量朝向
|
||||
* @param direction 0=朝下,1=朝上
|
||||
*/
|
||||
public setClearHeightDirection(direction: ClearHeightDirection): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.setClearHeightDirection(direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置净高测量选择对象类型
|
||||
* @param selectType 'point'=选择点,'element'=选择构件
|
||||
*/
|
||||
public setClearHeightSelectType(selectType: ClearHeightSelectType): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.setClearHeightSelectType(selectType);
|
||||
}
|
||||
|
||||
public activeSection(mode: 'x' | 'y' | 'z' | 'box' | 'face'): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.activeSection(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前激活的剖切模式
|
||||
* @returns 当前剖切模式,未激活时返回 null
|
||||
*/
|
||||
public getCurrentSectionMode(): 'x' | 'y' | 'z' | 'box' | 'face' | null {
|
||||
if (!this.engineInstance) {
|
||||
return null;
|
||||
}
|
||||
return this.engineInstance.getCurrentSectionMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用所有剖切功能
|
||||
* @remarks 关闭剖切对话框时调用
|
||||
*/
|
||||
public deactivateSection(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.deactivateSection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置剖切盒范围
|
||||
* @param range 各轴的剖切范围 (百分比 0-100)
|
||||
* @remarks 仅在 'box' 模式下有效
|
||||
*/
|
||||
public setSectionBoxRange(range: SectionBoxRange): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.setSectionBoxRange(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏剖切面(临时隐藏,可恢复)
|
||||
* @remarks 配合 recoverSection() 使用
|
||||
*/
|
||||
public hideSection(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.hideSection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复剖切面(从隐藏状态恢复)
|
||||
* @remarks 恢复被 hideSection() 隐藏的剖切面
|
||||
*/
|
||||
public recoverSection(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.recoverSection();
|
||||
}
|
||||
|
||||
public fitSectionBoxToModel(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.fitSectionBoxToModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 剖切盒适应(缩放到场景整体包围盒)
|
||||
* @remarks 对接底层 clipping.scaleBox()
|
||||
*/
|
||||
public scaleSectionBox(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.scaleSectionBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* 反向剖切
|
||||
* @remarks 对接底层 clipping.reverse()
|
||||
*/
|
||||
public reverseSection(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.reverseSection();
|
||||
}
|
||||
|
||||
/** 激活框选放大功能 */
|
||||
public activateZoomBox(): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.activateZoomBox();
|
||||
}
|
||||
|
||||
// ==================== 漫游功能 ====================
|
||||
|
||||
/** 激活第一人称漫游模式 */
|
||||
public activateFirstPersonMode(): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.activateFirstPersonMode();
|
||||
}
|
||||
|
||||
/** 停用第一人称漫游模式 */
|
||||
public deactivateFirstPersonMode(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.deactivateFirstPersonMode();
|
||||
}
|
||||
|
||||
public setWalkSpeed(speed: number): void {
|
||||
this.engineInstance?.setWalkSpeed(speed);
|
||||
}
|
||||
|
||||
public setWalkGravity(enabled: boolean): void {
|
||||
this.engineInstance?.setWalkGravity(enabled);
|
||||
}
|
||||
|
||||
public setWalkCollision(enabled: boolean): void {
|
||||
this.engineInstance?.setWalkCollision(enabled);
|
||||
}
|
||||
|
||||
public toggleMiniMap(): void {
|
||||
this.engineInstance?.toggleMiniMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小地图显示状态
|
||||
* @returns true=显示,false=隐藏
|
||||
*/
|
||||
public getMiniMapState(): boolean {
|
||||
return this.engineInstance?.getMiniMapState() ?? false;
|
||||
}
|
||||
|
||||
public isFirstPersonModeActive(): boolean {
|
||||
return this.engineInstance?.isFirstPersonModeActive() ?? false;
|
||||
}
|
||||
|
||||
// ==================== 结束:漫游功能 ====================
|
||||
|
||||
// ==================== 构件选中 ====================
|
||||
|
||||
/**
|
||||
* 获取当前选中的构件
|
||||
* @returns 选中构件的 URL 和 ID,未选中时返回 null
|
||||
*/
|
||||
public getSelectedComponent(): { url: string; id: string } | null {
|
||||
return this.engineInstance?.getSelectedComponent() ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取构件属性
|
||||
* @param url 模型 URL
|
||||
* @param id 构件 ID
|
||||
* @param callback 回调函数,接收属性数据
|
||||
*/
|
||||
public getComponentProperties(
|
||||
url: string,
|
||||
id: string,
|
||||
callback: (data: any) => void
|
||||
): void {
|
||||
this.engineInstance?.getComponentProperties(url, id, callback);
|
||||
}
|
||||
|
||||
// ==================== 结束:构件选中 ====================
|
||||
|
||||
// ==================== 模型树 ====================
|
||||
|
||||
public getTypeTreeData(): any[] {
|
||||
return this.engineInstance?.getTypeTreeData() ?? [];
|
||||
}
|
||||
|
||||
public getLevelTreeData(): any[] {
|
||||
return this.engineInstance?.getLevelTreeData() ?? [];
|
||||
}
|
||||
|
||||
public getMajorTreeData(): any[] {
|
||||
return this.engineInstance?.getMajorTreeData() ?? [];
|
||||
}
|
||||
|
||||
public getEngineInfo(): EngineInfo | null {
|
||||
return this.engineInstance?.getEngineInfo() ?? null;
|
||||
}
|
||||
// ==================== 结束:模型树 ====================
|
||||
|
||||
// ==================== 路径漫游 ====================
|
||||
|
||||
/**
|
||||
* 添加漫游点
|
||||
* 将当前相机位置添加为一个漫游点
|
||||
*/
|
||||
public pathRoamingAddPoint(): void {
|
||||
this.engineInstance?.pathRoamingAddPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定索引的漫游点
|
||||
* @param index 要删除的漫游点索引
|
||||
*/
|
||||
public pathRoamingRemovePoint(index: number): void {
|
||||
this.engineInstance?.pathRoamingRemovePoint(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有漫游点
|
||||
*/
|
||||
public pathRoamingClearPoints(): void {
|
||||
this.engineInstance?.pathRoamingClearPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有漫游点
|
||||
* @returns 漫游点数组
|
||||
*/
|
||||
public pathRoamingGetPoints(): any[] {
|
||||
return this.engineInstance?.pathRoamingGetPoints() ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到指定漫游点
|
||||
* @param index 目标漫游点索引
|
||||
*/
|
||||
public pathRoamingJumpToPoint(index: number): void {
|
||||
this.engineInstance?.pathRoamingJumpToPoint(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放漫游
|
||||
* @param options 播放选项,包含时长、循环、回调等配置
|
||||
*/
|
||||
public pathRoamingPlay(options?: {
|
||||
duration?: number;
|
||||
loop?: boolean;
|
||||
onComplete?: () => void;
|
||||
onPointComplete?: (pointIndex: number) => void;
|
||||
}): void {
|
||||
this.engineInstance?.pathRoamingPlay(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止漫游
|
||||
*/
|
||||
public pathRoamingStop(): void {
|
||||
this.engineInstance?.pathRoamingStop();
|
||||
}
|
||||
|
||||
// ==================== 结束:路径漫游 ====================
|
||||
|
||||
// ==================== 构件操作 ====================
|
||||
|
||||
/**
|
||||
* 获取当前高亮(选中)的模型
|
||||
* @returns 高亮模型对象,未选中时返回 null
|
||||
*/
|
||||
public getHighlightModels(): any {
|
||||
return this.engineInstance?.getHighlightModels() ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 高亮指定模型构件
|
||||
*
|
||||
* @param models - 要高亮的模型数组,格式: [{ url: string, ids: string[] }]
|
||||
*
|
||||
* @example
|
||||
* manager.highlightModel([
|
||||
* { url: 'https://xxx/models/xxx/', ids: [350518, 350520] }
|
||||
* ]);
|
||||
*/
|
||||
public highlightModel(models: { url: string; ids: number[] }[]): void {
|
||||
this.engineInstance?.highlightModel(models);
|
||||
}
|
||||
|
||||
public unhighlightAllModels(): void {
|
||||
this.engineInstance?.unhighlightAllModels();
|
||||
}
|
||||
|
||||
public viewScaleToModel(models: { url: string; ids: number[] }[]): void {
|
||||
this.engineInstance?.viewScaleToModel(models);
|
||||
}
|
||||
|
||||
public hideModels(models: { url: string; ids: number[] }[]): void {
|
||||
this.engineInstance?.hideModels(models);
|
||||
}
|
||||
|
||||
public showModel(models: { url: string; ids: number[] }[]): void {
|
||||
this.engineInstance?.showModel(models);
|
||||
}
|
||||
|
||||
/**
|
||||
* 半透明指定模型
|
||||
* @param models 要半透明的模型对象
|
||||
*/
|
||||
public translucentModels(models: any): void {
|
||||
this.engineInstance?.translucentModels(models);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消半透明
|
||||
*/
|
||||
public unTranslucentModel(): void {
|
||||
this.engineInstance?.unTranslucentModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 隔离指定模型(隐藏其他)
|
||||
* @param models 要隔离的模型对象
|
||||
*/
|
||||
public isolateModels(models: any): void {
|
||||
this.engineInstance?.isolateModels(models);
|
||||
}
|
||||
|
||||
/**
|
||||
* 半透明其他构件
|
||||
* @param models 基准模型对象
|
||||
*/
|
||||
public translucentOtherModels(models: any): void {
|
||||
this.engineInstance?.translucentOtherModels(models);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示所有模型
|
||||
*/
|
||||
public showAllModels(): void {
|
||||
this.engineInstance?.showAllModels();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量选择同类模型
|
||||
* @param models 基准模型对象
|
||||
*/
|
||||
public batchSelectSameTypeModel(models: any): void {
|
||||
this.engineInstance?.batchSelectSameTypeModel(models);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量选择同层模型
|
||||
* @param models 基准模型对象
|
||||
*/
|
||||
public batchSelectSameLevelModel(models: any): void {
|
||||
this.engineInstance?.batchSelectSameLevelModel(models);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量选择同层同类模型
|
||||
* @param models 基准模型对象
|
||||
*/
|
||||
public batchSelectSameLevelTypeModel(models: any): void {
|
||||
this.engineInstance?.batchSelectSameLevelTypeModel(models);
|
||||
}
|
||||
|
||||
// ==================== 结束:构件操作 ====================
|
||||
|
||||
// ==================== 渲染模式 ====================
|
||||
|
||||
/**
|
||||
* 获取当前渲染模式
|
||||
* @returns 'simple' | 'balance' | 'advanced'
|
||||
*/
|
||||
public getRenderMode(): string {
|
||||
return this.engineInstance?.getRenderMode() ?? 'balance';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置渲染模式
|
||||
* @param mode 'simple' | 'balance' | 'advanced'
|
||||
*/
|
||||
public setRenderMode(mode: 'simple' | 'balance' | 'advanced'): void {
|
||||
this.engineInstance?.setRenderMode(mode);
|
||||
}
|
||||
|
||||
// ==================== 结束:渲染模式 ====================
|
||||
|
||||
/** 销毁引擎管理器 */
|
||||
public destroy(): void {
|
||||
if (this.engineInstance) {
|
||||
|
||||
@@ -42,11 +42,11 @@ export class MeasureDialogManager extends BaseDialogManager {
|
||||
defaultExpanded: false,
|
||||
onModeChange: (mode) => {
|
||||
console.log('[MeasureDialogManager] 当前测量方式已切换:', mode);
|
||||
this.registry.engine3d?.activateMeasure(mode);
|
||||
this.engineComponent?.activateMeasure(mode);
|
||||
},
|
||||
onClearAll: () => {
|
||||
console.log('[MeasureDialogManager] 删除全部');
|
||||
this.registry.engine3d?.clearAllMeasures();
|
||||
this.engineComponent?.clearAllMeasures();
|
||||
},
|
||||
onSettings: () => {
|
||||
console.log('[MeasureDialogManager] 打开设置');
|
||||
@@ -56,18 +56,18 @@ export class MeasureDialogManager extends BaseDialogManager {
|
||||
},
|
||||
onConfigSave: (config) => {
|
||||
console.log('[MeasureDialogManager] 保存设置:', config);
|
||||
this.registry.engine3d?.saveMeasureSetting({
|
||||
this.engineComponent?.saveMeasureSetting({
|
||||
unit: config.unit,
|
||||
precision: config.precision
|
||||
});
|
||||
},
|
||||
onClearHeightDirectionChange: (direction) => {
|
||||
console.log('[MeasureDialogManager] 净高朝向切换:', direction);
|
||||
this.registry.engine3d?.setClearHeightDirection(direction);
|
||||
this.engineComponent?.setClearHeightDirection(direction);
|
||||
},
|
||||
onClearHeightSelectTypeChange: (selectType) => {
|
||||
console.log('[MeasureDialogManager] 净高选择对象切换:', selectType);
|
||||
this.registry.engine3d?.setClearHeightSelectType(selectType);
|
||||
this.engineComponent?.setClearHeightSelectType(selectType);
|
||||
},
|
||||
});
|
||||
this.panel.init();
|
||||
@@ -75,7 +75,7 @@ export class MeasureDialogManager extends BaseDialogManager {
|
||||
this.config = this.panel.getConfig();
|
||||
if (this.config) {
|
||||
console.log('[MeasureDialogManager] 同步缓存设置到引擎:', this.config);
|
||||
this.registry.engine3d?.saveMeasureSetting({
|
||||
this.engineComponent?.saveMeasureSetting({
|
||||
unit: this.config.unit,
|
||||
precision: this.config.precision
|
||||
});
|
||||
@@ -92,22 +92,22 @@ export class MeasureDialogManager extends BaseDialogManager {
|
||||
this.dialog?.fitHeight(false);
|
||||
|
||||
// 同步净高默认值到引擎(朝下=0,选择点='point')
|
||||
this.registry.engine3d?.setClearHeightDirection(0);
|
||||
this.registry.engine3d?.setClearHeightSelectType('point');
|
||||
this.engineComponent?.setClearHeightDirection(0);
|
||||
this.engineComponent?.setClearHeightSelectType('point');
|
||||
|
||||
const engine = this.registry.engine3d?.getEngine();
|
||||
if (engine?.events) {
|
||||
const ec = this.engineComponent;
|
||||
if (ec) {
|
||||
const handler = (data: EngineMeasureData) => {
|
||||
console.log('[MeasureDialogManager] 测量值回调:', data);
|
||||
if (data && this.panel) {
|
||||
this.handleMeasureChanged(data);
|
||||
}
|
||||
};
|
||||
engine.events.on('measure-changed', handler);
|
||||
engine.events.on('measure-click', handler);
|
||||
ec.onRawEvent('measure-changed', handler);
|
||||
ec.onRawEvent('measure-click', handler);
|
||||
this.unsubscribeMeasureChanged = () => {
|
||||
engine.events.off('measure-changed', handler);
|
||||
engine.events.off('measure-click', handler);
|
||||
ec.offRawEvent('measure-changed', handler);
|
||||
ec.offRawEvent('measure-click', handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,9 +158,7 @@ export class MeasureDialogManager extends BaseDialogManager {
|
||||
this.unsubscribeMeasureChanged();
|
||||
this.unsubscribeMeasureChanged = null;
|
||||
}
|
||||
if (this.registry.engine3d) {
|
||||
this.registry.engine3d.deactivateMeasure();
|
||||
}
|
||||
this.engineComponent?.deactivateMeasure();
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -64,9 +64,9 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionAxisDialogManager] 隐藏切换:', isHidden);
|
||||
if (isHidden) {
|
||||
this.registry.engine3d?.hideSection();
|
||||
this.engineComponent?.hideSection();
|
||||
} else {
|
||||
this.registry.engine3d?.recoverSection();
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onReverse: () => {
|
||||
@@ -75,7 +75,7 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
},
|
||||
onAxisChange: (axis) => {
|
||||
console.log('[SectionAxisDialogManager] 切换轴向:', axis);
|
||||
this.registry.engine3d?.activeSection(axis);
|
||||
this.engineComponent?.activeSection(axis);
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
@@ -87,13 +87,13 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
this.dialog?.fitHeight(false);
|
||||
|
||||
// 检查 Engine 是否已初始化
|
||||
if (!this.registry.engine3d) {
|
||||
if (!this.engineComponent) {
|
||||
console.error('[SectionAxisDialogManager] Engine not initialized. Call initEngine() first.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 自动激活默认轴向剖切(X轴)
|
||||
this.registry.engine3d.activeSection('x');
|
||||
this.engineComponent.activeSection('x');
|
||||
}
|
||||
|
||||
/** 对话框关闭时的回调,取消工具栏按钮激活状态 */
|
||||
@@ -103,7 +103,7 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 销毁前的清理,销毁面板实例 */
|
||||
protected onBeforeDestroy(): void {
|
||||
this.registry.engine3d?.deactivateSection();
|
||||
this.engineComponent?.deactivateSection();
|
||||
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
|
||||
@@ -56,30 +56,30 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionBoxDialogManager] 隐藏切换:', isHidden);
|
||||
if (isHidden) {
|
||||
this.registry.engine3d?.hideSection();
|
||||
this.engineComponent?.hideSection();
|
||||
} else {
|
||||
this.registry.engine3d?.recoverSection();
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onReverseToggle: (isReversed) => {
|
||||
console.log('[SectionBoxDialogManager] 反向切换:', isReversed);
|
||||
// 底层 reverse() 为“切换一次”,这里不使用 isReversed 作为入参,只要用户点击就触发。
|
||||
this.registry.engine3d?.reverseSection();
|
||||
this.engineComponent?.reverseSection();
|
||||
},
|
||||
onFitToModel: () => {
|
||||
// 对接底层 scaleBox():缩放剖切盒到场景整体包围盒
|
||||
this.registry.engine3d?.scaleSectionBox();
|
||||
this.engineComponent?.scaleSectionBox();
|
||||
},
|
||||
onReset: () => {
|
||||
// 重置定义:关闭剖切再打开剖切盒。
|
||||
// UI 侧会自行将滑块强制恢复到 0-100,并将隐藏/反向按钮恢复为关闭状态。
|
||||
this.registry.engine3d?.deactivateSection();
|
||||
this.registry.engine3d?.activeSection('box');
|
||||
this.engineComponent?.deactivateSection();
|
||||
this.engineComponent?.activeSection('box');
|
||||
// 确保剖切可见(避免上一次处于隐藏状态导致“看起来没重置”)
|
||||
this.registry.engine3d?.recoverSection();
|
||||
this.engineComponent?.recoverSection();
|
||||
},
|
||||
onRangeChange: (range) => {
|
||||
this.registry.engine3d?.setSectionBoxRange(range);
|
||||
this.engineComponent?.setSectionBoxRange(range);
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
@@ -87,18 +87,18 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
}
|
||||
|
||||
protected onDialogCreated(): void {
|
||||
this.registry.engine3d?.activeSection('box');
|
||||
this.engineComponent?.activeSection('box');
|
||||
this.dialog?.fitHeight(false);
|
||||
|
||||
const engine = this.registry.engine3d?.getEngine();
|
||||
if (engine?.events) {
|
||||
const ec = this.engineComponent;
|
||||
if (ec) {
|
||||
const handler = (data: any) => {
|
||||
if (data && this.panel) {
|
||||
this.panel.setRange(data as Partial<SectionBoxRange>);
|
||||
}
|
||||
};
|
||||
engine.events.on('section-move', handler);
|
||||
this.unsubscribeSectionMove = () => engine.events.off('section-move', handler);
|
||||
ec.onRawEvent('section-move', handler);
|
||||
this.unsubscribeSectionMove = () => ec.offRawEvent('section-move', handler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
this.unsubscribeSectionMove();
|
||||
this.unsubscribeSectionMove = null;
|
||||
}
|
||||
this.registry.engine3d?.deactivateSection();
|
||||
this.engineComponent?.deactivateSection();
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -56,9 +56,9 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionPlaneDialogManager] 隐藏切换:', isHidden);
|
||||
if (isHidden) {
|
||||
this.registry.engine3d?.hideSection();
|
||||
this.engineComponent?.hideSection();
|
||||
} else {
|
||||
this.registry.engine3d?.recoverSection();
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onReverse: () => {
|
||||
@@ -74,7 +74,7 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 对话框创建后的回调 */
|
||||
protected onDialogCreated(): void {
|
||||
this.registry.engine3d?.activeSection('face');
|
||||
this.engineComponent?.activeSection('face');
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 销毁前的清理 */
|
||||
protected onBeforeDestroy(): void {
|
||||
this.registry.engine3d?.deactivateSection();
|
||||
this.engineComponent?.deactivateSection();
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -5,11 +5,8 @@ import { t } from '../services/locale';
|
||||
/** 渲染模式类型 */
|
||||
type RenderMode = 'simple' | 'balance' | 'advanced';
|
||||
|
||||
/** 地面类型 */
|
||||
type GroundType = 'none' | 'concrete' | 'grass' | 'tile' | 'water' | 'wood';
|
||||
|
||||
/** 环境背景类型 */
|
||||
type EnvironmentType = 'default' | 'outdoor' | 'indoor' | 'night' | 'overcast' | 'studio';
|
||||
/** 设置项列表项(环境背景/地面类型由底层引擎动态返回) */
|
||||
type SettingListItem = { name: string; id: string };
|
||||
|
||||
// ======================== 通用样式常量 ========================
|
||||
// 全部使用项目主题 CSS 变量(--bim-*),同时提供浅色/深色皆可见的 fallback
|
||||
@@ -46,7 +43,7 @@ const HOVER_BG = 'var(--bim-component-bg-hover, rgba(0,0,0,0.04))';
|
||||
|
||||
/**
|
||||
* 设置弹窗管理器
|
||||
* 管理全局设置面板,包含渲染模式、边线、地面、对比度/饱和度、光照强度、环境背景等设置项
|
||||
* 管理全局设置面板,包含渲染模式、边线、光照/对比度/饱和度、环境背景、地面等设置项
|
||||
*/
|
||||
export class SettingDialogManager extends BaseDialogManager {
|
||||
protected get dialogId() { return 'setting-dialog'; }
|
||||
@@ -57,18 +54,26 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 边线开关状态 */
|
||||
private edgeLineEnabled: boolean = false;
|
||||
/** 当前选中的地面类型 */
|
||||
private groundType: GroundType = 'none';
|
||||
/** 地面大小 */
|
||||
private groundSize: number = 100;
|
||||
/** 对比度(0-200,100为默认值) */
|
||||
private contrastValue: number = 100;
|
||||
/** 饱和度(0-200,100为默认值) */
|
||||
private saturationValue: number = 100;
|
||||
/** 光照强度(0-200,100为默认值) */
|
||||
private lightIntensityValue: number = 100;
|
||||
/** 当前选中的环境背景 */
|
||||
private environmentType: EnvironmentType = 'default';
|
||||
/** 对比度(0-100,50为默认值) */
|
||||
private contrastValue: number = 50;
|
||||
/** 饱和度(0-100,50为默认值) */
|
||||
private saturationValue: number = 50;
|
||||
/** 光照强度(0-100,50为默认值) */
|
||||
private lightIntensityValue: number = 50;
|
||||
/** 环境背景列表(从底层引擎动态获取) */
|
||||
private environmentList: SettingListItem[] = [];
|
||||
/** 当前选中的环境背景 ID */
|
||||
private environmentId: string = '';
|
||||
/** 是否显示背景 */
|
||||
private backgroundVisible: boolean = true;
|
||||
/** 地面列表(从底层引擎动态获取) */
|
||||
private groundList: SettingListItem[] = [];
|
||||
/** 当前选中的地面 ID */
|
||||
private groundId: string = '';
|
||||
/** 地面高度(单位:m) */
|
||||
private groundElevation: number = 0;
|
||||
/** 保存的对话框位置(用于 hide/show 时保持位置不变) */
|
||||
private savedPosition: { x: number; y: number } | null = null;
|
||||
|
||||
constructor(registry: ManagerRegistry) {
|
||||
super(registry);
|
||||
@@ -76,8 +81,15 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
public init(): void { }
|
||||
|
||||
/** 对话框居中显示 */
|
||||
/** 对话框位置:若有保存的位置则恢复,否则居中 */
|
||||
protected getDialogPosition() {
|
||||
// 若上次 hide/show 保存了位置,则恢复到原位
|
||||
if (this.savedPosition) {
|
||||
const pos = this.savedPosition;
|
||||
this.savedPosition = null;
|
||||
return pos;
|
||||
}
|
||||
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
@@ -90,9 +102,39 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从底层引擎加载当前状态(回显)
|
||||
* 在 createContent() 前调用,保证 UI 和引擎状态同步
|
||||
*/
|
||||
private loadEngineState(): void {
|
||||
const engine = this.engineComponent;
|
||||
if (!engine) return;
|
||||
|
||||
// 边线
|
||||
this.edgeLineEnabled = engine.getModelEdgeActive();
|
||||
|
||||
// 光照 / 对比度 / 饱和度
|
||||
this.lightIntensityValue = engine.getAmbientLightIntensity();
|
||||
this.contrastValue = engine.getSceneContrast();
|
||||
this.saturationValue = engine.getSceneSaturation();
|
||||
|
||||
// 环境背景
|
||||
this.environmentList = engine.getHDRBackgroundList();
|
||||
this.environmentId = engine.getHDRBackgroundId();
|
||||
this.backgroundVisible = engine.getHDRBackgroundVisibility();
|
||||
|
||||
// 地面
|
||||
this.groundList = engine.getGroundList();
|
||||
this.groundId = engine.getGroundId();
|
||||
this.groundElevation = engine.getGroundElevation();
|
||||
}
|
||||
|
||||
// ======================== 主内容构建 ========================
|
||||
|
||||
protected createContent(): HTMLElement {
|
||||
// 从引擎加载当前状态(回显)
|
||||
this.loadEngineState();
|
||||
|
||||
// 注入一次全局滑块样式
|
||||
this.ensureSliderStyle();
|
||||
|
||||
@@ -111,32 +153,41 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
content.appendChild(this.createSliderSection(
|
||||
t('setting.lightIntensity'),
|
||||
this.lightIntensityValue,
|
||||
0, 200,
|
||||
(v) => { this.lightIntensityValue = v; }
|
||||
0, 100,
|
||||
(v) => {
|
||||
this.lightIntensityValue = v;
|
||||
this.engineComponent?.setAmbientLightIntensity(v);
|
||||
}
|
||||
));
|
||||
|
||||
// 4. 对比度
|
||||
content.appendChild(this.createSliderSection(
|
||||
t('setting.contrast'),
|
||||
this.contrastValue,
|
||||
0, 200,
|
||||
(v) => { this.contrastValue = v; }
|
||||
0, 100,
|
||||
(v) => {
|
||||
this.contrastValue = v;
|
||||
this.engineComponent?.setSceneContrast(v);
|
||||
}
|
||||
));
|
||||
|
||||
// 5. 饱和度
|
||||
content.appendChild(this.createSliderSection(
|
||||
t('setting.saturation'),
|
||||
this.saturationValue,
|
||||
0, 200,
|
||||
(v) => { this.saturationValue = v; }
|
||||
0, 100,
|
||||
(v) => {
|
||||
this.saturationValue = v;
|
||||
this.engineComponent?.setSceneSaturation(v);
|
||||
}
|
||||
));
|
||||
content.appendChild(this.createDivider());
|
||||
|
||||
// 6. 环境背景(下拉)
|
||||
// 6. 环境背景(动态列表 + 显示开关)
|
||||
content.appendChild(this.createEnvironmentSection());
|
||||
content.appendChild(this.createDivider());
|
||||
|
||||
// 7. 显示地面(下拉 + 大小输入)
|
||||
// 7. 地面(动态列表 + 地面高度)
|
||||
content.appendChild(this.createGroundSection());
|
||||
|
||||
return content;
|
||||
@@ -155,7 +206,7 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 创建渲染模式按钮组 */
|
||||
private createRenderModeSection(): HTMLElement {
|
||||
const currentMode = (this.registry.engine3d?.getRenderMode() ?? 'balance') as RenderMode;
|
||||
const currentMode = (this.engineComponent?.getRenderMode() ?? 'balance') as RenderMode;
|
||||
|
||||
const section = document.createElement('div');
|
||||
|
||||
@@ -201,8 +252,12 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
});
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
this.registry.engine3d?.setRenderMode(mode.key);
|
||||
// 重建弹窗以刷新状态
|
||||
this.engineComponent?.setRenderMode(mode.key);
|
||||
// 保存当前对话框位置,重建后恢复
|
||||
const el = document.getElementById(this.dialogId);
|
||||
if (el) {
|
||||
this.savedPosition = { x: el.offsetLeft, y: el.offsetTop };
|
||||
}
|
||||
this.hide();
|
||||
this.show();
|
||||
});
|
||||
@@ -218,45 +273,18 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 创建边线开关行 */
|
||||
private createEdgeLineSection(): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.style.cssText = ROW_STYLE;
|
||||
|
||||
// 标签
|
||||
const label = document.createElement('span');
|
||||
label.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
label.textContent = t('setting.edgeLine');
|
||||
row.appendChild(label);
|
||||
|
||||
// 开关容器 —— 关闭态使用 --bim-border-strong 保证浅色主题可见
|
||||
const toggleOffBg = 'var(--bim-border-strong, #cbd5e1)';
|
||||
const toggleOnBg = PRIMARY;
|
||||
|
||||
const toggle = document.createElement('div');
|
||||
toggle.style.cssText = `
|
||||
width: 40px; height: 22px; border-radius: 11px; cursor: pointer;
|
||||
position: relative; transition: background 0.2s;
|
||||
background: ${this.edgeLineEnabled ? toggleOnBg : toggleOffBg};
|
||||
`;
|
||||
|
||||
// 滑块圆点
|
||||
const knob = document.createElement('div');
|
||||
knob.style.cssText = `
|
||||
width: 18px; height: 18px; border-radius: 50%;
|
||||
background: #fff; position: absolute; top: 2px;
|
||||
transition: left 0.2s;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||||
left: ${this.edgeLineEnabled ? '20px' : '2px'};
|
||||
`;
|
||||
toggle.appendChild(knob);
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
this.edgeLineEnabled = !this.edgeLineEnabled;
|
||||
toggle.style.background = this.edgeLineEnabled ? toggleOnBg : toggleOffBg;
|
||||
knob.style.left = this.edgeLineEnabled ? '20px' : '2px';
|
||||
});
|
||||
|
||||
row.appendChild(toggle);
|
||||
return row;
|
||||
return this.createToggleRow(
|
||||
t('setting.edgeLine'),
|
||||
this.edgeLineEnabled,
|
||||
(enabled) => {
|
||||
this.edgeLineEnabled = enabled;
|
||||
if (enabled) {
|
||||
this.engineComponent?.activeModelEdge();
|
||||
} else {
|
||||
this.engineComponent?.disActiveModelEdge();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// ======================== 3/4/5. 通用滑块 ========================
|
||||
@@ -351,98 +379,112 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// ======================== 6. 环境背景(下拉) ========================
|
||||
// ======================== 6. 环境背景(动态列表 + 显示开关) ========================
|
||||
|
||||
/** 创建环境背景下拉选择区 */
|
||||
/** 创建环境背景区 */
|
||||
private createEnvironmentSection(): HTMLElement {
|
||||
const section = document.createElement('div');
|
||||
|
||||
// 标签
|
||||
const label = document.createElement('div');
|
||||
label.style.cssText = SECTION_LABEL_STYLE;
|
||||
label.textContent = t('setting.environment');
|
||||
section.appendChild(label);
|
||||
|
||||
const envKeys: EnvironmentType[] = ['default', 'outdoor', 'indoor', 'night', 'overcast', 'studio'];
|
||||
|
||||
// 下拉列表(从底层引擎动态获取)
|
||||
const select = this.createSelect(
|
||||
envKeys.map(key => ({
|
||||
value: key,
|
||||
label: t(`setting.environments.${key}` as any)
|
||||
})),
|
||||
this.environmentType,
|
||||
(val) => { this.environmentType = val as EnvironmentType; }
|
||||
this.environmentList.map(item => ({ value: item.id, label: item.name })),
|
||||
this.environmentId,
|
||||
(val) => {
|
||||
this.environmentId = val;
|
||||
this.engineComponent?.setHDRBackgroundId(val);
|
||||
}
|
||||
);
|
||||
|
||||
section.appendChild(select);
|
||||
|
||||
// 显示背景开关
|
||||
const toggleRow = this.createToggleRow(
|
||||
t('setting.backgroundVisible'),
|
||||
this.backgroundVisible,
|
||||
(enabled) => {
|
||||
this.backgroundVisible = enabled;
|
||||
this.engineComponent?.setHDRBackgroundVisibility(enabled);
|
||||
}
|
||||
);
|
||||
toggleRow.style.marginTop = '10px';
|
||||
section.appendChild(toggleRow);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
// ======================== 7. 显示地面(下拉 + 大小) ========================
|
||||
// ======================== 7. 地面(动态列表 + 地面高度) ========================
|
||||
|
||||
/** 创建地面设置区 */
|
||||
private createGroundSection(): HTMLElement {
|
||||
const section = document.createElement('div');
|
||||
|
||||
// 地面类型标签 + 下拉
|
||||
// 标签
|
||||
const label = document.createElement('div');
|
||||
label.style.cssText = SECTION_LABEL_STYLE;
|
||||
label.textContent = t('setting.ground');
|
||||
section.appendChild(label);
|
||||
|
||||
const groundKeys: GroundType[] = ['none', 'concrete', 'grass', 'tile', 'water', 'wood'];
|
||||
|
||||
// 下拉列表(从底层引擎动态获取)
|
||||
const select = this.createSelect(
|
||||
groundKeys.map(key => ({
|
||||
value: key,
|
||||
label: t(`setting.groundTypes.${key}` as any)
|
||||
})),
|
||||
this.groundType,
|
||||
this.groundList.map(item => ({ value: item.id, label: item.name })),
|
||||
this.groundId,
|
||||
(val) => {
|
||||
this.groundType = val as GroundType;
|
||||
// 显示/隐藏大小输入
|
||||
sizeRow.style.display = val === 'none' ? 'none' : 'flex';
|
||||
this.groundId = val;
|
||||
this.engineComponent?.setGroundId(val);
|
||||
}
|
||||
);
|
||||
section.appendChild(select);
|
||||
|
||||
// 大小输入行
|
||||
const sizeRow = document.createElement('div');
|
||||
sizeRow.style.cssText = ROW_STYLE + ' margin-top: 10px;';
|
||||
sizeRow.style.display = this.groundType === 'none' ? 'none' : 'flex';
|
||||
// 地面高度输入行
|
||||
const elevationRow = document.createElement('div');
|
||||
elevationRow.style.cssText = ROW_STYLE + ' margin-top: 10px;';
|
||||
|
||||
const sizeLabel = document.createElement('span');
|
||||
sizeLabel.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
sizeLabel.textContent = t('setting.groundSize');
|
||||
sizeRow.appendChild(sizeLabel);
|
||||
const elevationLabel = document.createElement('span');
|
||||
elevationLabel.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
elevationLabel.textContent = t('setting.groundElevation');
|
||||
elevationRow.appendChild(elevationLabel);
|
||||
|
||||
const sizeInput = document.createElement('input');
|
||||
sizeInput.type = 'number';
|
||||
sizeInput.value = String(this.groundSize);
|
||||
sizeInput.min = '1';
|
||||
sizeInput.max = '10000';
|
||||
sizeInput.style.cssText = `
|
||||
width: 80px; padding: 4px 8px; border-radius: 4px;
|
||||
// 输入框 + 单位容器
|
||||
const inputWrapper = document.createElement('div');
|
||||
inputWrapper.style.cssText = 'display: flex; align-items: center; gap: 4px;';
|
||||
|
||||
const elevationInput = document.createElement('input');
|
||||
elevationInput.type = 'number';
|
||||
elevationInput.value = String(this.groundElevation);
|
||||
elevationInput.style.cssText = `
|
||||
width: 72px; padding: 4px 8px; border-radius: 4px;
|
||||
border: 1px solid ${BORDER_COLOR};
|
||||
background: ${INPUT_BG};
|
||||
color: ${TEXT_PRIMARY};
|
||||
font-size: 13px; outline: none; text-align: right;
|
||||
`;
|
||||
sizeInput.addEventListener('input', () => {
|
||||
const v = Number(sizeInput.value);
|
||||
if (!isNaN(v) && v > 0) {
|
||||
this.groundSize = v;
|
||||
elevationInput.addEventListener('input', () => {
|
||||
const v = Number(elevationInput.value);
|
||||
if (!isNaN(v)) {
|
||||
this.groundElevation = v;
|
||||
this.engineComponent?.setGroundElevation(v);
|
||||
}
|
||||
});
|
||||
// 聚焦时高亮边框
|
||||
sizeInput.addEventListener('focus', () => {
|
||||
sizeInput.style.borderColor = PRIMARY;
|
||||
elevationInput.addEventListener('focus', () => {
|
||||
elevationInput.style.borderColor = PRIMARY;
|
||||
});
|
||||
sizeInput.addEventListener('blur', () => {
|
||||
sizeInput.style.borderColor = BORDER_COLOR;
|
||||
elevationInput.addEventListener('blur', () => {
|
||||
elevationInput.style.borderColor = BORDER_COLOR;
|
||||
});
|
||||
|
||||
sizeRow.appendChild(sizeInput);
|
||||
section.appendChild(sizeRow);
|
||||
const unitLabel = document.createElement('span');
|
||||
unitLabel.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
unitLabel.textContent = t('setting.groundElevationUnit');
|
||||
|
||||
inputWrapper.appendChild(elevationInput);
|
||||
inputWrapper.appendChild(unitLabel);
|
||||
elevationRow.appendChild(inputWrapper);
|
||||
section.appendChild(elevationRow);
|
||||
|
||||
return section;
|
||||
}
|
||||
@@ -493,7 +535,7 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
onChange(select.value);
|
||||
});
|
||||
|
||||
// 聚焦高亮
|
||||
// 聚焦<EFBFBD><EFBFBD>亮
|
||||
select.addEventListener('focus', () => {
|
||||
select.style.borderColor = PRIMARY;
|
||||
});
|
||||
@@ -503,4 +545,61 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
return select;
|
||||
}
|
||||
|
||||
// ======================== 通用开关行 ========================
|
||||
|
||||
/**
|
||||
* 创建一个标签 + 开关的行
|
||||
* @param labelText 标签文本
|
||||
* @param enabled 当前开关状态
|
||||
* @param onChange 状态变化回调
|
||||
*/
|
||||
private createToggleRow(
|
||||
labelText: string,
|
||||
enabled: boolean,
|
||||
onChange: (enabled: boolean) => void
|
||||
): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.style.cssText = ROW_STYLE;
|
||||
|
||||
// 标签
|
||||
const label = document.createElement('span');
|
||||
label.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
label.textContent = labelText;
|
||||
row.appendChild(label);
|
||||
|
||||
// 开关容器
|
||||
const toggleOffBg = 'var(--bim-border-strong, #cbd5e1)';
|
||||
const toggleOnBg = PRIMARY;
|
||||
|
||||
let currentState = enabled;
|
||||
|
||||
const toggle = document.createElement('div');
|
||||
toggle.style.cssText = `
|
||||
width: 40px; height: 22px; border-radius: 11px; cursor: pointer;
|
||||
position: relative; transition: background 0.2s;
|
||||
background: ${currentState ? toggleOnBg : toggleOffBg};
|
||||
`;
|
||||
|
||||
// 滑块圆点
|
||||
const knob = document.createElement('div');
|
||||
knob.style.cssText = `
|
||||
width: 18px; height: 18px; border-radius: 50%;
|
||||
background: #fff; position: absolute; top: 2px;
|
||||
transition: left 0.2s;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||||
left: ${currentState ? '20px' : '2px'};
|
||||
`;
|
||||
toggle.appendChild(knob);
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
currentState = !currentState;
|
||||
toggle.style.background = currentState ? toggleOnBg : toggleOffBg;
|
||||
knob.style.left = currentState ? '20px' : '2px';
|
||||
onChange(currentState);
|
||||
});
|
||||
|
||||
row.appendChild(toggle);
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ export class WalkControlManager extends BaseManager {
|
||||
|
||||
// 打开漫游面板时,默认激活第一人称模式
|
||||
console.log('[WalkControl] 打开漫游面板,激活第一人称模式');
|
||||
this.registry.engine3d?.activateFirstPersonMode();
|
||||
this.engineComponent?.activateFirstPersonMode();
|
||||
|
||||
this.panel = new WalkControlPanel({
|
||||
onPlanViewToggle: (isActive) => {
|
||||
console.log('[WalkControl] 小地图:', isActive);
|
||||
this.registry.engine3d?.toggleMiniMap();
|
||||
this.engineComponent?.toggleMiniMap();
|
||||
this.emit('walk:plan-view-toggle', { isActive });
|
||||
},
|
||||
onPathModeToggle: (isActive) => {
|
||||
@@ -66,17 +66,17 @@ export class WalkControlManager extends BaseManager {
|
||||
onSpeedChange: (speed) => {
|
||||
console.log('[WalkControl] 速度变化:', speed);
|
||||
const engineSpeed = speed * 0.1;
|
||||
this.registry.engine3d?.setWalkSpeed(engineSpeed);
|
||||
this.engineComponent?.setWalkSpeed(engineSpeed);
|
||||
this.emit('walk:speed-change', { speed });
|
||||
},
|
||||
onGravityToggle: (enabled) => {
|
||||
console.log('[WalkControl] 重力:', enabled);
|
||||
this.registry.engine3d?.setWalkGravity(enabled);
|
||||
this.engineComponent?.setWalkGravity(enabled);
|
||||
this.emit('walk:gravity-toggle', { enabled });
|
||||
},
|
||||
onCollisionToggle: (enabled) => {
|
||||
console.log('[WalkControl] 碰撞:', enabled);
|
||||
this.registry.engine3d?.setWalkCollision(enabled);
|
||||
this.engineComponent?.setWalkCollision(enabled);
|
||||
this.emit('walk:collision-toggle', { enabled });
|
||||
},
|
||||
onCharacterModelChange: (model) => {
|
||||
@@ -111,7 +111,7 @@ export class WalkControlManager extends BaseManager {
|
||||
this.pathManager?.hide();
|
||||
|
||||
console.log('[WalkControl] 关闭漫游面板,退出第一人称模式');
|
||||
this.registry.engine3d?.deactivateFirstPersonMode();
|
||||
this.engineComponent?.deactivateFirstPersonMode();
|
||||
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
|
||||
Reference in New Issue
Block a user