Files
bim_engine/demo-next/components/BimViewer.tsx

263 lines
7.4 KiB
TypeScript
Raw Normal View History

2026-04-21 15:07:49 +08:00
'use client';
import { useEffect, useRef, useState, useCallback } from 'react';
import ControlPanel from './ControlPanel';
declare global {
interface Window {
IflowEngine: any;
}
}
const DEFAULT_MODEL_URL = 'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/8634e556-a94e-4ba7-be3e-2ea1507cced5/';
export default function BimViewer() {
const containerRef = useRef<HTMLDivElement>(null);
const engineRef = useRef<any>(null);
const [engineStatus, setEngineStatus] = useState<string>('未初始化');
const [isLoading, setIsLoading] = useState(false);
const updateStatus = useCallback((status: string) => {
setEngineStatus(status);
}, []);
useEffect(() => {
if (!containerRef.current || typeof window === 'undefined') return;
if (!window.IflowEngine) {
console.error('SDK not found');
updateStatus('SDK 未加载');
return;
}
const Engine = window.IflowEngine.BimEngine;
const engine = new Engine(containerRef.current, { locale: 'zh-CN' });
engineRef.current = engine;
engine.rightKey.registerHandler((event: any) => {
const { x, y } = event;
return [
{
id: 'home',
label: 'toolbar.home',
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>',
onClick: () => {
console.log('Go Home');
}
},
{
id: 'log-pos',
label: '打印坐标',
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>',
onClick: () => {
console.log(`Clicked at: ${x}, ${y}`);
}
}
];
});
return () => {
if (engineRef.current) {
engineRef.current.destroy();
engineRef.current = null;
}
};
}, [updateStatus]);
const initEngine = useCallback(() => {
const engine = engineRef.current;
if (!engine || !engine.engine) {
alert('引擎未创建,请先等待页面加载完成');
return;
}
if (engine.engine.isInitialized()) {
alert('3D 引擎已经初始化过了');
updateStatus('已初始化');
return;
}
try {
const success = engine.engine.initialize({
backgroundColor: 0x333333,
version: 'v2',
showStats: false,
showViewCube: true
});
if (success) {
updateStatus('已初始化');
console.log('3D 引擎初始化成功');
} else {
updateStatus('初始化失败');
console.error('3D 引擎初始化失败');
}
} catch (error: any) {
updateStatus('初始化错误');
console.error('3D 引擎初始化错误:', error);
}
}, [updateStatus]);
const loadModel = useCallback((url?: string) => {
const engine = engineRef.current;
if (!engine || !engine.engine) {
alert('引擎未创建');
return;
}
if (!engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
const modelUrl = url || DEFAULT_MODEL_URL;
if (!modelUrl.trim()) {
alert('请输入模型 URL');
return;
}
setIsLoading(true);
try {
engine.engine.loadModel([modelUrl], {
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1]
});
console.log('模型加载请求已发送:', modelUrl);
} catch (error: any) {
console.error('模型加载错误:', error);
} finally {
setTimeout(() => setIsLoading(false), 2000);
}
}, []);
const setLang = useCallback((lang: 'zh-CN' | 'en-US') => {
if (engineRef.current) {
engineRef.current.setLocale(lang);
}
}, []);
const setTheme = useCallback((themeName: 'dark' | 'light') => {
if (engineRef.current) {
engineRef.current.setTheme(themeName);
}
}, []);
const setCustomTheme = useCallback(() => {
if (!engineRef.current) return;
engineRef.current.setCustomTheme({
name: 'red-alert',
primary: '#d32f2f',
primaryHover: '#b71c1c',
background: '#ffebee',
panelBackground: 'rgba(255, 255, 255, 0.9)',
textPrimary: '#b71c1c',
textSecondary: '#e57373',
border: '#ffcdd2',
icon: '#d32f2f',
iconActive: '#b71c1c',
componentBackground: 'rgba(255, 205, 210, 0.3)',
componentHover: 'rgba(255, 205, 210, 0.8)',
componentActive: '#e57373'
});
}, []);
const openTestDialog = useCallback(() => {
if (!engineRef.current || !engineRef.current.dialog) return;
engineRef.current.dialog.create({
title: 'dialog.testTitle',
width: 350,
height: 200,
position: 'center',
draggable: true,
resizable: true
});
}, []);
const openInfoDialog = useCallback(() => {
if (!engineRef.current || !engineRef.current.dialog) return;
engineRef.current.dialog.showInfoDialog();
}, []);
const openTreeDialog = useCallback(() => {
if (!engineRef.current || !engineRef.current.modelTree) return;
engineRef.current.modelTree.showStructTree();
}, []);
const toggleToolbar = useCallback((visible: boolean) => {
if (!engineRef.current || !engineRef.current.toolbar) return;
engineRef.current.toolbar.setVisible(visible);
}, []);
const toggleLabel = useCallback((visible: boolean) => {
if (!engineRef.current || !engineRef.current.toolbar) return;
engineRef.current.toolbar.setShowLabel(visible);
}, []);
const pauseRendering = useCallback(() => {
if (!engineRef.current?.engine?.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
engineRef.current.engine.pauseRendering();
console.log('渲染已暂停');
}, []);
const resumeRendering = useCallback(() => {
if (!engineRef.current?.engine?.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
engineRef.current.engine.resumeRendering();
console.log('渲染已恢复');
}, []);
return (
<div style={{ display: 'flex', width: '100%', height: '100%', overflow: 'hidden' }}>
<ControlPanel
engineStatus={engineStatus}
isLoading={isLoading}
onInitEngine={initEngine}
onLoadModel={loadModel}
onSetLang={setLang}
onSetTheme={setTheme}
onSetCustomTheme={setCustomTheme}
onOpenTestDialog={openTestDialog}
onOpenInfoDialog={openInfoDialog}
onOpenTreeDialog={openTreeDialog}
onToggleToolbar={toggleToolbar}
onToggleLabel={toggleLabel}
onPauseRendering={pauseRendering}
onResumeRendering={resumeRendering}
/>
<div
ref={containerRef}
style={{
flex: 1,
position: 'relative',
background: '#1a1a1a',
overflow: 'hidden'
}}
/>
{isLoading && (
<div
style={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
background: 'rgba(0, 0, 0, 0.8)',
color: '#fff',
padding: '20px 40px',
borderRadius: '8px',
zIndex: 9999,
fontSize: '16px'
}}
>
...
</div>
)}
</div>
);
}