224 lines
6.1 KiB
TypeScript
224 lines
6.1 KiB
TypeScript
|
|
'use client';
|
|||
|
|
|
|||
|
|
import { useState } from 'react';
|
|||
|
|
|
|||
|
|
interface ControlPanelProps {
|
|||
|
|
engineStatus: string;
|
|||
|
|
isLoading: boolean;
|
|||
|
|
onInitEngine: () => void;
|
|||
|
|
onLoadModel: (url?: string) => void;
|
|||
|
|
onSetLang: (lang: 'zh-CN' | 'en-US') => void;
|
|||
|
|
onSetTheme: (theme: 'dark' | 'light') => void;
|
|||
|
|
onSetCustomTheme: () => void;
|
|||
|
|
onOpenTestDialog: () => void;
|
|||
|
|
onOpenInfoDialog: () => void;
|
|||
|
|
onOpenTreeDialog: () => void;
|
|||
|
|
onToggleToolbar: (visible: boolean) => void;
|
|||
|
|
onToggleLabel: (visible: boolean) => void;
|
|||
|
|
onPauseRendering: () => void;
|
|||
|
|
onResumeRendering: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function ControlPanel({
|
|||
|
|
engineStatus,
|
|||
|
|
onInitEngine,
|
|||
|
|
onLoadModel,
|
|||
|
|
onSetLang,
|
|||
|
|
onSetTheme,
|
|||
|
|
onSetCustomTheme,
|
|||
|
|
onOpenTestDialog,
|
|||
|
|
onOpenInfoDialog,
|
|||
|
|
onOpenTreeDialog,
|
|||
|
|
onToggleToolbar,
|
|||
|
|
onToggleLabel,
|
|||
|
|
onPauseRendering,
|
|||
|
|
onResumeRendering
|
|||
|
|
}: ControlPanelProps) {
|
|||
|
|
const [modelUrl, setModelUrl] = useState('');
|
|||
|
|
const [isToolbarVisible, setIsToolbarVisible] = useState(true);
|
|||
|
|
const [isLabelVisible, setIsLabelVisible] = useState(true);
|
|||
|
|
|
|||
|
|
const handleToggleToolbar = () => {
|
|||
|
|
const newVisible = !isToolbarVisible;
|
|||
|
|
setIsToolbarVisible(newVisible);
|
|||
|
|
onToggleToolbar(newVisible);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleToggleLabel = () => {
|
|||
|
|
const newVisible = !isLabelVisible;
|
|||
|
|
setIsLabelVisible(newVisible);
|
|||
|
|
onToggleLabel(newVisible);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleLoadModel = () => {
|
|||
|
|
const url = modelUrl.trim();
|
|||
|
|
onLoadModel(url || undefined);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const getStatusColor = () => {
|
|||
|
|
switch (engineStatus) {
|
|||
|
|
case '已初始化':
|
|||
|
|
return '#28a745';
|
|||
|
|
case '初始化失败':
|
|||
|
|
case '初始化错误':
|
|||
|
|
case 'SDK 未加载':
|
|||
|
|
return '#dc3545';
|
|||
|
|
default:
|
|||
|
|
return '#666';
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<aside
|
|||
|
|
style={{
|
|||
|
|
width: '320px',
|
|||
|
|
background: '#fff',
|
|||
|
|
borderRight: '1px solid #e0e0e0',
|
|||
|
|
padding: '20px',
|
|||
|
|
overflowY: 'auto',
|
|||
|
|
display: 'flex',
|
|||
|
|
flexDirection: 'column',
|
|||
|
|
gap: '20px',
|
|||
|
|
boxShadow: '2px 0 5px rgba(0, 0, 0, 0.05)',
|
|||
|
|
zIndex: 10
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<h1 style={{ fontSize: '1.4rem', color: '#333', marginBottom: '10px' }}>
|
|||
|
|
iFlow Engine Demo
|
|||
|
|
</h1>
|
|||
|
|
<p style={{ fontSize: '0.85rem', color: '#666', marginBottom: '10px' }}>
|
|||
|
|
Next.js + React
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<ControlGroup title="🌍 语言 (Language)">
|
|||
|
|
<ButtonGroup>
|
|||
|
|
<button className="btn-primary" onClick={() => onSetLang('zh-CN')}>中文</button>
|
|||
|
|
<button className="btn-primary" onClick={() => onSetLang('en-US')}>English</button>
|
|||
|
|
</ButtonGroup>
|
|||
|
|
</ControlGroup>
|
|||
|
|
|
|||
|
|
<ControlGroup title="🪟 弹窗 (Dialog)">
|
|||
|
|
<ButtonGroup>
|
|||
|
|
<button onClick={onOpenTestDialog}>测试弹窗</button>
|
|||
|
|
<button onClick={onOpenInfoDialog}>信息弹窗</button>
|
|||
|
|
<button onClick={onOpenTreeDialog}>树组件</button>
|
|||
|
|
</ButtonGroup>
|
|||
|
|
</ControlGroup>
|
|||
|
|
|
|||
|
|
<ControlGroup title="🛠️ 工具栏 (Toolbar)">
|
|||
|
|
<ButtonGroup>
|
|||
|
|
<button onClick={handleToggleToolbar}>
|
|||
|
|
{isToolbarVisible ? '隐藏工具栏' : '显示工具栏'}
|
|||
|
|
</button>
|
|||
|
|
<button onClick={handleToggleLabel}>
|
|||
|
|
{isLabelVisible ? '隐藏标签' : '显示标签'}
|
|||
|
|
</button>
|
|||
|
|
</ButtonGroup>
|
|||
|
|
</ControlGroup>
|
|||
|
|
|
|||
|
|
<ControlGroup title="🎨 样式 (Theme)">
|
|||
|
|
<ButtonGroup>
|
|||
|
|
<button onClick={() => onSetTheme('dark')}>深色</button>
|
|||
|
|
<button onClick={() => onSetTheme('light')}>浅色</button>
|
|||
|
|
<button onClick={onSetCustomTheme}>自定义红</button>
|
|||
|
|
</ButtonGroup>
|
|||
|
|
</ControlGroup>
|
|||
|
|
|
|||
|
|
<ControlGroup title="🎮 3D 引擎 (Engine)">
|
|||
|
|
<ButtonGroup>
|
|||
|
|
<button className="btn-primary" onClick={onInitEngine}>初始化引擎</button>
|
|||
|
|
<button className="btn-primary" onClick={handleLoadModel}>加载模型</button>
|
|||
|
|
</ButtonGroup>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={modelUrl}
|
|||
|
|
onChange={(e) => setModelUrl(e.target.value)}
|
|||
|
|
placeholder="输入模型 URL(可选)"
|
|||
|
|
style={{
|
|||
|
|
width: '100%',
|
|||
|
|
padding: '6px 8px',
|
|||
|
|
fontSize: '0.85rem',
|
|||
|
|
border: '1px solid #ddd',
|
|||
|
|
borderRadius: '4px',
|
|||
|
|
fontFamily: 'Consolas, Monaco, monospace',
|
|||
|
|
outline: 'none',
|
|||
|
|
marginTop: '8px'
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
<ButtonGroup style={{ marginTop: '8px' }}>
|
|||
|
|
<button onClick={onPauseRendering}>暂停渲染</button>
|
|||
|
|
<button onClick={onResumeRendering}>恢复渲染</button>
|
|||
|
|
</ButtonGroup>
|
|||
|
|
<div style={{ marginTop: '10px', fontSize: '0.85rem', color: '#666' }}>
|
|||
|
|
状态: <span style={{ color: getStatusColor() }}>{engineStatus}</span>
|
|||
|
|
</div>
|
|||
|
|
</ControlGroup>
|
|||
|
|
|
|||
|
|
<style jsx>{`
|
|||
|
|
button {
|
|||
|
|
padding: 6px 12px;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
border: 1px solid #ddd;
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
button:hover {
|
|||
|
|
background: #f0f0f0;
|
|||
|
|
border-color: #ccc;
|
|||
|
|
}
|
|||
|
|
.btn-primary {
|
|||
|
|
background: #0078d4;
|
|||
|
|
color: white;
|
|||
|
|
border-color: #0063b1;
|
|||
|
|
}
|
|||
|
|
.btn-primary:hover {
|
|||
|
|
background: #0063b1;
|
|||
|
|
}
|
|||
|
|
`}</style>
|
|||
|
|
</aside>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function ControlGroup({ title, children }: { title: string; children: React.ReactNode }) {
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
background: '#f9f9f9',
|
|||
|
|
padding: '15px',
|
|||
|
|
borderRadius: '8px',
|
|||
|
|
border: '1px solid #eee'
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<h2
|
|||
|
|
style={{
|
|||
|
|
fontSize: '1rem',
|
|||
|
|
color: '#555',
|
|||
|
|
marginBottom: '10px',
|
|||
|
|
paddingBottom: '5px',
|
|||
|
|
borderBottom: '2px solid #e0e0e0'
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
{title}
|
|||
|
|
</h2>
|
|||
|
|
{children}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function ButtonGroup({ children, style }: { children: React.ReactNode; style?: React.CSSProperties }) {
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
display: 'flex',
|
|||
|
|
flexWrap: 'wrap',
|
|||
|
|
gap: '8px',
|
|||
|
|
...style
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
{children}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|