Files
bim_engine/src/managers/component-detail-manager.ts

283 lines
9.0 KiB
TypeScript
Raw Normal View History

import { BaseManager } from '../core/base-manager';
import { BimCollapse } from '../components/collapse/index';
import { BimTab } from '../components/tab';
import { t } from '../services/locale';
export class ComponentDetailManager extends BaseManager {
private dialogId = 'component-detail-dialog';
private dialog: any = null;
private currentSelection: { url: string; id: string } | null = null;
private unsubscribeSelected: (() => void) | null = null;
private unsubscribeDeselected: (() => void) | null = null;
private tabInstance: BimTab | null = null;
private propertiesData: any = null;
constructor() {
super();
}
public init(): void {
this.unsubscribeSelected = this.registry.on('component:selected', (payload) => {
this.currentSelection = payload;
if (this.isOpen()) {
this.loadAndRenderContent();
}
});
this.unsubscribeDeselected = this.registry.on('component:deselected', () => {
this.currentSelection = null;
if (this.isOpen()) {
this.renderNoSelection();
}
});
}
public show(): void {
if (!this.registry.dialog) {
console.warn('[ComponentDetailManager] Dialog manager not initialized');
return;
}
if (!this.isOpen()) {
this.createDialog();
}
if (this.currentSelection) {
this.loadAndRenderContent();
} else {
this.renderNoSelection();
}
}
private createDialog(): void {
const width = 300;
const x = document.body.clientWidth - width - 40;
this.dialog = this.registry.dialog?.create({
id: this.dialogId,
title: 'panel.componentDetail.title',
content: '',
width: `${width}px`,
height: '500px',
position: { x, y: 20 },
resizable: true,
onClose: () => this.hide()
} as any);
}
private loadAndRenderContent(): void {
if (!this.dialog || !this.currentSelection) return;
this.showLoading();
this.registry.engine3d?.getComponentProperties(
this.currentSelection.url,
this.currentSelection.id,
(data) => {
this.propertiesData = data;
this.renderTabbedContent();
}
);
}
private showLoading(): void {
const container = document.createElement('div');
container.style.cssText = 'height:100%;display:flex;align-items:center;justify-content:center;';
container.textContent = '加载中...';
this.dialog?.setContent(container);
}
private renderNoSelection(): void {
if (!this.dialog) return;
const container = document.createElement('div');
container.style.cssText = 'height:100%;display:flex;align-items:center;justify-content:center;color:var(--bim-text-secondary,#999);';
container.textContent = t('panel.componentDetail.noSelection') || '请先选中构件';
this.dialog.setContent(container);
}
private renderTabbedContent(): void {
if (!this.dialog) return;
if (this.tabInstance) {
this.tabInstance.destroy();
this.tabInstance = null;
}
const container = document.createElement('div');
container.style.cssText = 'height:100%;display:flex;flex-direction:column;';
const propertiesPanel = this.createPropertiesPanel();
const materialsPanel = this.createMaterialsPanel();
this.tabInstance = new BimTab({
container,
activeId: 'properties',
tabs: [
{
id: 'properties',
title: 'panel.property.tab.props',
content: propertiesPanel
},
{
id: 'materials',
title: 'panel.property.tab.material',
content: materialsPanel
}
]
});
this.tabInstance.init();
this.dialog.setContent(container);
}
private createPropertiesPanel(): HTMLElement {
const container = document.createElement('div');
container.style.cssText = 'height:100%;overflow-y:auto;';
const properties = this.propertiesData?.properties || [];
if (properties.length === 0) {
container.innerHTML = '<div style="padding:20px;text-align:center;color:var(--bim-text-secondary,#999);">无属性数据</div>';
return container;
}
const collapseItems = properties.map((category: any, index: number) => ({
id: `category-${index}`,
title: category.name || `分类 ${index + 1}`,
content: this.createCategoryContent(category.children || [])
}));
new BimCollapse({
container,
accordion: false,
ghost: true,
activeIds: collapseItems.length > 0 ? [collapseItems[0].id] : [],
items: collapseItems
});
const style = document.createElement('style');
style.textContent = `
#${this.dialogId} .bim-collapse-header {
background-color: var(--bim-component-bg-hover) !important;
}
#${this.dialogId} .bim-collapse-header:hover {
background-color: var(--bim-component-bg-active) !important;
}
`;
container.appendChild(style);
return container;
}
private createMaterialsPanel(): HTMLElement {
const container = document.createElement('div');
container.style.cssText = 'height:100%;overflow-y:auto;';
const materials = this.propertiesData?.materials || [];
if (materials.length === 0) {
container.innerHTML = '<div style="padding:20px;text-align:center;color:var(--bim-text-secondary,#999);">无材质数据</div>';
return container;
}
const collapseItems = materials.map((material: any, index: number) => ({
id: `material-${index}`,
title: material.name || `材质 ${index + 1}`,
content: this.createCategoryContent(material.children || material.properties || [])
}));
new BimCollapse({
container,
accordion: false,
ghost: true,
activeIds: collapseItems.length > 0 ? [collapseItems[0].id] : [],
items: collapseItems
});
const style = document.createElement('style');
style.textContent = `
#${this.dialogId} .bim-collapse-header {
background-color: var(--bim-component-bg-hover) !important;
}
#${this.dialogId} .bim-collapse-header:hover {
background-color: var(--bim-component-bg-active) !important;
}
`;
container.appendChild(style);
return container;
}
private createCategoryContent(items: any[]): HTMLElement {
const container = document.createElement('div');
items.forEach((item: any, index: number) => {
const row = document.createElement('div');
row.style.cssText = `
display: flex;
border-bottom: 1px solid var(--bim-border-default, rgba(255,255,255,0.15));
`;
if (index === items.length - 1) {
row.style.borderBottom = 'none';
}
const label = document.createElement('div');
label.style.cssText = `
width: 120px;
flex-shrink: 0;
color: var(--bim-text-secondary, #999);
font-size: 13px;
padding: 8px 12px;
border-right: 1px solid var(--bim-border-default, rgba(255,255,255,0.15));
`;
label.textContent = item.name || '-';
const value = document.createElement('div');
value.style.cssText = `
flex: 1;
color: var(--bim-text-primary, #fff);
font-size: 13px;
padding: 8px 12px;
word-break: break-all;
`;
value.textContent = String(item.value ?? '-');
row.appendChild(label);
row.appendChild(value);
container.appendChild(row);
});
return container;
}
public isOpen(): boolean {
return this.dialog !== null;
}
public hide(): void {
if (this.tabInstance) {
this.tabInstance.destroy();
this.tabInstance = null;
}
if (this.dialog) {
this.dialog.destroy();
this.dialog = null;
}
}
public destroy(): void {
if (this.unsubscribeSelected) {
this.unsubscribeSelected();
this.unsubscribeSelected = null;
}
if (this.unsubscribeDeselected) {
this.unsubscribeDeselected();
this.unsubscribeDeselected = null;
}
this.hide();
super.destroy();
}
}