263 lines
7.4 KiB
TypeScript
263 lines
7.4 KiB
TypeScript
'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>
|
|
);
|
|
}
|