feat: enhance description component, update property panel with tabs, and refine docs

This commit is contained in:
yuding
2025-12-22 16:41:24 +08:00
parent ed0414c75b
commit e1bb5558ff
19 changed files with 2640 additions and 1856 deletions

62
.idea/workspace.xml generated
View File

@@ -4,7 +4,20 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="728b1ce9-7308-4507-bebd-62399c54bf21" name="更改" comment="添加测试信息" />
<list default="true" id="728b1ce9-7308-4507-bebd-62399c54bf21" name="更改" comment="添加折叠面板">
<change beforePath="$PROJECT_DIR$/dist/bim-engine-sdk.es.js" beforeDir="false" afterPath="$PROJECT_DIR$/dist/bim-engine-sdk.es.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/dist/bim-engine-sdk.es.js.map" beforeDir="false" afterPath="$PROJECT_DIR$/dist/bim-engine-sdk.es.js.map" afterDir="false" />
<change beforePath="$PROJECT_DIR$/dist/bim-engine-sdk.umd.js" beforeDir="false" afterPath="$PROJECT_DIR$/dist/bim-engine-sdk.umd.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/dist/bim-engine-sdk.umd.js.map" beforeDir="false" afterPath="$PROJECT_DIR$/dist/bim-engine-sdk.umd.js.map" afterDir="false" />
<change beforePath="$PROJECT_DIR$/dist/index.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/dist/index.d.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/collapse/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/collapse/index.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/tab/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/tab/index.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/locales/en-US.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/locales/en-US.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/locales/types.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/locales/types.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/locales/zh-CN.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/locales/zh-CN.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/managers/property-panel-manager.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/managers/property-panel-manager.ts" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -31,7 +44,7 @@
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/yuding/WORK/LYZ/project/bimEngine/engine&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/yuding/WORK/LYZ/project/bimEngine/engine/src/managers&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
@@ -42,6 +55,11 @@
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/managers" />
</key>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
@@ -63,6 +81,16 @@
<workItem from="1764923867307" duration="53000" />
<workItem from="1764923944573" duration="598000" />
<workItem from="1765159215556" duration="215000" />
<workItem from="1765276444696" duration="1275000" />
<workItem from="1765332689442" duration="125000" />
<workItem from="1765785106464" duration="10802000" />
<workItem from="1765857284634" duration="196000" />
<workItem from="1765880383085" duration="690000" />
<workItem from="1765936484546" duration="357000" />
<workItem from="1765943119364" duration="2464000" />
<workItem from="1766108012524" duration="1315000" />
<workItem from="1766371049964" duration="671000" />
<workItem from="1766385054791" duration="7532000" />
</task>
<task id="LOCAL-00001" summary="添加测试信息">
<option name="closed" value="true" />
@@ -88,7 +116,31 @@
<option name="project" value="LOCAL" />
<updated>1765159346641</updated>
</task>
<option name="localTasksCounter" value="4" />
<task id="LOCAL-00004" summary="添加测试信息">
<option name="closed" value="true" />
<created>1765857466168</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1765857466168</updated>
</task>
<task id="LOCAL-00005" summary="修改">
<option name="closed" value="true" />
<created>1766385084570</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1766385084570</updated>
</task>
<task id="LOCAL-00006" summary="添加折叠面板">
<option name="closed" value="true" />
<created>1766389199604</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1766389199604</updated>
</task>
<option name="localTasksCounter" value="7" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -96,7 +148,9 @@
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="添加测试信息" />
<option name="LAST_COMMIT_MESSAGE" value="添加测试信息" />
<MESSAGE value="修改" />
<MESSAGE value="添加折叠面板" />
<option name="LAST_COMMIT_MESSAGE" value="添加折叠面板" />
</component>
<component name="VgoProject">
<settings-migrated>true</settings-migrated>

View File

@@ -527,6 +527,7 @@ const dialog = engine.dialog.create({
| `BimTree` | `src/components/tree/index.ts` | 通用树形组件 | `IBimComponent` |
| `BimTab` | `src/components/tab/index.ts` | 固定标签页组件 | `IBimComponent` |
| `BimCollapse` | `src/components/collapse/index.ts` | 折叠面板组件 | `IBimComponent` |
| `BimDescription` | `src/components/description/index.ts` | 描述列表组件 (Key-Value) | `IBimComponent` |
### 4.3 服务类清单

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

64
dist/index.d.ts vendored
View File

@@ -361,6 +361,51 @@ declare class ConstructTreeManagerBtn extends BimComponent {
setColors(colors: ButtonGroupColors): void;
}
/**
* 描述列表项配置
*/
export declare interface DescriptionItem {
/** 标签文本 (直接显示,组件内部不翻译) */
label: string;
/** 内容文本或元素 */
value: string | HTMLElement;
/** 行级自定义标签颜色 */
labelColor?: string;
/** 行级自定义内容颜色 */
valueColor?: string;
/** 自定义类名 */
className?: string;
}
/**
* 描述列表组件配置
*/
export declare interface DescriptionOptions {
/** 挂载容器 */
container: HTMLElement | string;
/** 数据项列表 */
items: DescriptionItem[];
/**
* 是否显示边框 (默认 false)
* 开启后,将显示行间分割线以及 Key-Value 之间的纵向分割线
*/
bordered?: boolean;
/** 标签固定宽度 (例如 '80px'),若不设置则自适应 */
labelWidth?: string;
/** 全局标签颜色 */
labelColor?: string;
/** 全局内容颜色 */
valueColor?: string;
/** 全局字体大小 */
fontSize?: string;
/** 标签内边距 (默认 '0 4px') */
labelPadding?: string;
/** 内容内边距 (默认 '0 4px') */
valuePadding?: string;
/** 自定义类名 */
className?: string;
}
/**
* 弹窗颜色配置
*/
@@ -608,7 +653,7 @@ export declare interface IBimComponent {
declare type Listener<T = any> = (payload: T) => void;
/**
* 语言码类型
* 语言<EFBFBD><EFBFBD>码类型
*/
declare type LocaleType = 'zh-CN' | 'en-US';
@@ -651,11 +696,28 @@ declare interface OptButton extends ButtonConfig {
children?: OptButton[];
}
/**
* 属性面板管理器
* 负责展示和管理属性面板弹窗 (演示 Tab + Collapse + Description 组件)
*/
declare class PropertyPanelManager extends BimComponent {
private dialogId;
constructor(engine: BimEngine);
init(): void;
/**
* 显示属性面板
*/
show(): void;
/**
* 创建"属性"标签页的内容 (包含 Collapse)
*/
private createPropsTabContent;
/**
* 创建"材质"标签页的内容 (包含 Collapse)
*/
private createMaterialTabContent;
private createBaseInfoContent;
private createAdvancedInfoContent;
private createMaterialContent;
destroy(): void;
}

View File

@@ -1,69 +1,97 @@
# Collapse 组件文档
> **注意**: 本组件为 UI 组件,必须通过 Manager如 `PropertyPanelManager`)封装后使用,不可直接在业务代码中实例化。
## 1. 组件概述
# Collapse 折叠面板组件
`BimCollapse` 是一个通用的折叠面板组件,支持手风琴模式、自定义内容和标题。常用于属性面板、设置菜单等场景。
## 2. API 参考
## 1. 组件概述
### 2.1 配置项 `CollapseOptions`
- **类名**: `BimCollapse`
- **文件路径**: `src/components/collapse/index.ts`
- **样式文件**: `src/components/collapse/index.css`
- **类型定义**: `src/components/collapse/types.ts`
该组件实现了 `IBimComponent` 接口,具备完整的生命周期管理、主题响应和国际化支持能力。
## 2. API 文档
### 2.1 构造函数
```typescript
interface CollapseOptions {
container: HTMLElement | string; // 挂载容器
items: CollapseItemConfig[]; // 面板项列表
accordion?: boolean; // 是否开启手风琴模式 (默认 false)
activeIds?: string[]; // 初始展开的 ID 列表
bordered?: boolean; // 是否显示边框 (默认 true)
ghost?: boolean; // 是否幽灵模式 (默认 false)
className?: string; // 自定义类名
onChange?: (activeIds: string[]) => void; // 切换回调
}
const collapse = new BimCollapse(options: CollapseOptions);
```
### 2.2 面板项配置 `CollapseItemConfig`
### 2.2 CollapseOptions 配置项
```typescript
interface CollapseItemConfig {
id: string; // 唯一标识
title: string; // 标题翻译键 (例如 'panel.base')
content: string | HTMLElement; // 内容
icon?: string; // 标题图标 (SVG)
extra?: string | HTMLElement; // 标题右侧额外内容
disabled?: boolean; // 是否禁用
className?: string; // 自定义类名
}
```
| 属性 | 类型 | 默认值 | 说明 |
|------|------|-------|------|
| `container` | `HTMLElement \| string` | - | **(必填)** 挂载容器或其 ID |
| `items` | `CollapseItemConfig[]` | - | **(必填)** 面板项列表 |
| `accordion` | `boolean` | `false` | 是否开启手风琴模式(一次只能展开一项) |
| `activeIds` | `string[]` | `[]` | 初始展开的面板 ID 列表 |
| `bordered` | `boolean` | `true` | 是否显示外边框 |
| `ghost` | `boolean` | `false` | 是否开启幽灵模式(无背景、无边框) |
| `className` | `string` | - | 自定义类名 |
| `onChange` | `(activeIds: string[]) => void` | - | 切换面板时的回调函数 |
### 2.3 方法
### 2.3 CollapseItemConfig 面板项配置
* `toggleItem(id: string)`: 切换指定面板的展开/折叠状态。
* `setLocales()`: 更新组件文本(通常自动调用)。
* `destroy()`: 销毁组件。
| 属性 | 类型 | 说明 |
|------|------|------|
| `id` | `string` | **(必填)** 唯一标识符 |
| `title` | `string` | **(必填)** 标题文本的**翻译键** (例如 `'panel.property.base'`) |
| `content` | `string \| HTMLElement` | **(必填)** 面板内容 |
| `icon` | `string` | 标题左侧图标 (SVG 字符串) |
| `extra` | `string \| HTMLElement` | 标题栏右侧额外内容 |
| `disabled` | `boolean` | 是否禁用该面板 |
| `className` | `string` | 自定义面板类名 |
## 3. 使用示例 (在 Manager 中)
### 2.4 实例方法
- **`toggleItem(id: string)`**: 切换指定面板的展开/折叠状态。
- **`setTheme(theme: ThemeConfig)`**: 设置组件主题 (CSS 变量映射)。
- **`setLocales()`**: 更新组件文本 (标题翻译)。
- **`destroy()`**: 销毁组件,清理资源。
## 3. 使用示例
```typescript
import { BimCollapse } from '../components/collapse/index';
// 在 Manager 的方法中
const collapse = new BimCollapse({
container: this.containerElement,
container: document.getElementById('panel'),
accordion: true,
activeIds: ['base'],
items: [
{
id: 'item1',
title: 'my.title.key', // 翻译键
content: '<div>Content Here</div>'
id: 'base',
title: 'panel.property.base', // 必须是翻译键
content: document.createElement('div'), // 或 HTML 字符串
icon: '<svg>...</svg>'
},
{
id: 'advanced',
title: 'panel.property.advanced',
content: 'Advanced Content',
disabled: true
}
],
onChange: (ids) => {
console.log('Current active panels:', ids);
}
]
});
```
## 4. 国际化
## 4. 主题支持
组件自动订阅 `localeManager`
* **标题**: `config.title` 必须是翻译键。
* **内容**: 如果内容包含文本,请确保内容生成时已翻译,或内容本身具有响应国际化的能力。
组件自动订阅主题变更,并通过 CSS 变量控制样式:
- **面板背景**: `theme.panelBackground`
- **边框颜色**: `theme.border`
- **文本颜色**: `theme.textPrimary`
- **标题栏背景**: `theme.componentHover` (默认) / `theme.componentBackground`
- **禁用态颜色**: `theme.textSecondary`
## 5. 国际化支持
组件自动订阅语言变更:
- **标题 (`title`)**: 会自动使用 `t(config.title)` 进行翻译。确保传入的是有效的翻译键。
- **内容 (`content`)**: 如果内容包含文本,请确保内容生成时已翻译,或内容本身具有响应国际化的能力(如使用 `BimDescription`)。

View File

@@ -0,0 +1,127 @@
# Description 描述列表组件
`BimDescription` 组件用于展示一组键值对Key-Value数据常用于详情页、属性面板等场景。
## 1. 组件概述
- **类名**: `BimDescription`
- **文件路径**: `src/components/description/index.ts`
- **样式文件**: `src/components/description/index.css`
- **类型定义**: `src/components/description/types.ts`
该组件是一个**纯展示组件**,其特点是:
- **不内置国际化**: 为了最大灵活性Label 和 Value 均直接显示传入的字符串/元素,不调用翻译函数。调用者应负责传入已翻译的文本。
- **高度定制化**: 支持全局或行级的颜色、Padding、字体大小定制。
- **布局灵活**: 支持普通列表模式和带边框的表格模式Key-Value 间有纵向分割线)。
## 2. API 文档
### 2.1 构造函数
```typescript
const description = new BimDescription(options: DescriptionOptions);
```
### 2.2 DescriptionOptions 配置项
| 属性 | 类型 | 默认值 | 说明 |
|------|------|-------|------|
| `container` | `HTMLElement \| string` | - | **(必填)** 挂载容器或其 ID |
| `items` | `DescriptionItem[]` | - | **(必填)** 数据项列表 |
| `bordered` | `boolean` | `false` | 是否显示边框。开启后会显示行间分割线以及 Key-Value 之间的纵向分割线 |
| `labelWidth` | `string` | - | 标签列的固定宽度 (如 `'80px'`),不设置则自适应 |
| `fontSize` | `string` | `'14px'` | 全局字体大小 (如 `'12px'`) |
| `labelColor` | `string` | `theme.textSecondary` | 全局标签颜色 |
| `valueColor` | `string` | `theme.textPrimary` | 全局内容颜色 |
| `labelPadding` | `string` | `'0 4px'` | 标签单元格内边距 (CSS padding 语法) |
| `valuePadding` | `string` | `'0 4px'` | 内容单元格内边距 (CSS padding 语法) |
| `className` | `string` | - | 自定义类名 |
### 2.3 DescriptionItem 数据项
| 属性 | 类型 | 说明 |
|------|------|------|
| `label` | `string` | 标签文本 (直接显示,在非 bordered 模式下会自动添加冒号) |
| `value` | `string \| HTMLElement` | 内容文本或 DOM 元素 |
| `labelColor` | `string` | 行级自定义标签颜色 (优先级高于全局) |
| `valueColor` | `string` | 行级自定义内容颜色 (优先级高于全局) |
| `className` | `string` | 自定义行类名 |
### 2.4 实例方法
- **`setItems(items: DescriptionItem[])`**: 动态更新数据列表。
- **`setTheme(theme: ThemeConfig)`**: <20><>置组件主题 (通常自动调用)。
- **`destroy()`**: 销毁组件,清理 DOM 和事件订阅。
## 3. 使用示例
### 3.1 基础使用
```typescript
new BimDescription({
container: document.getElementById('container'),
labelWidth: '80px',
items: [
{ label: 'Name', value: 'Wall-01' },
{ label: 'ID', value: 'E-1001' },
{ label: 'Level', value: 'Level 1' }
]
});
```
### 3.2 带边框的表格模式 (Bordered)
```typescript
new BimDescription({
container: document.getElementById('container'),
bordered: true,
fontSize: '12px',
labelPadding: '4px 8px',
valuePadding: '4px 8px',
items: [
{ label: 'Material', value: 'Concrete' },
{ label: 'Density', value: '2400 kg/m³' }
]
});
```
### 3.3 自定义样式
```typescript
new BimDescription({
container: 'container',
labelColor: '#999',
valueColor: '#333',
items: [
{
label: 'Status',
value: '<span style="color: green">Active</span>',
labelColor: 'blue' // 单独覆盖此行的标签颜色
}
]
});
```
## 4. 主题支持
组件会自动响应系统主题变更。默认映射关系如下:
- **文本颜色**: `theme.textPrimary`
- **标签颜色**: `theme.textSecondary`
- **边框颜色**: `theme.border`
- **标签背景 (仅 Bordered 模式)**: `theme.background` (用于轻微区分 Key 和 Value)
## 5. 国际化支持
**组件内部不进行翻译**。调用者应在传入 `label` 之前使用 `t()` 函数进行翻译。
```typescript
import { t } from 'bim-engine-sdk/services/locale';
new BimDescription({
// ...
items: [
{ label: t('panel.property.id'), value: '123' }
]
});
```

View File

@@ -117,5 +117,4 @@
}
.bim-collapse-content-box {
padding: 16px;
}

View File

@@ -0,0 +1,53 @@
.bim-description {
display: flex;
flex-direction: column;
width: 100%;
/* 默认字体大小和颜色 */
font-size: var(--bim-desc-font-size, 14px);
color: var(--bim-text-color, #333);
/* 严格移除容器本身的 padding */
padding: 0;
}
.bim-description-item {
display: flex;
align-items: stretch;
/* 严格移除 item 的 padding完全由 label/value padding 控制 */
padding: 0;
line-height: 1.5;
}
/* 边框模式 */
.bim-description.is-bordered {
border-bottom: none; /* 最后一项会补齐 */
}
.bim-description.is-bordered .bim-description-item {
border-bottom: 1px solid var(--bim-border-color, #eee);
}
/* 标签样式 */
.bim-description-label {
color: var(--bim-desc-label-color, var(--bim-label-color, #666));
flex-shrink: 0;
/* 默认 padding: 0 4px */
padding: var(--bim-desc-label-padding, 4px 4px);
display: flex;
align-items: center; /* 垂直居中 */
}
/* 边框模式下的标签样式 */
.bim-description.is-bordered .bim-description-label {
border-right: 1px solid var(--bim-border-color, #eee);
}
/* 内容样式 */
.bim-description-value {
color: var(--bim-desc-value-color, var(--bim-value-color, #333));
flex: 1;
word-break: break-all;
/* 默认 padding: 0 4px */
padding: var(--bim-desc-value-padding, 4px 4px);
display: flex;
align-items: center;
}

View File

@@ -0,0 +1,160 @@
import './index.css';
import { DescriptionOptions, DescriptionItem } from './types';
import { IBimComponent } from '../../types/component';
import { themeManager } from '../../services/theme';
import type { ThemeConfig } from '../../themes/types';
/**
* 描述列表组件
* 用于展示一组 Key-Value 数据
* 注意:本组件为纯展示组件,不处理国际化,请在外部传入处理好的文本。
*/
export class BimDescription implements IBimComponent {
private element: HTMLElement;
private options: DescriptionOptions;
private unsubscribeTheme: (() => void) | null = null;
constructor(options: DescriptionOptions) {
this.options = {
bordered: false,
...options
};
this.element = this.createDom();
const container = typeof this.options.container === 'string'
? document.getElementById(this.options.container)
: this.options.container;
if (container) {
container.appendChild(this.element);
}
this.init();
}
public init(): void {
this.applyCustomStyles();
this.renderItems();
// 订阅主题变更
this.unsubscribeTheme = themeManager.subscribe((theme) => {
this.setTheme(theme);
});
// 初始应用主题
this.setTheme(themeManager.getTheme());
}
private createDom(): HTMLElement {
const el = document.createElement('div');
el.className = `bim-description ${this.options.className || ''}`;
if (this.options.bordered) el.classList.add('is-bordered');
return el;
}
private applyCustomStyles() {
const style = this.element.style;
// 应用全局字体大小
if (this.options.fontSize) {
style.setProperty('--bim-desc-font-size', this.options.fontSize);
}
// 应用全局 Label 颜色
if (this.options.labelColor) {
style.setProperty('--bim-desc-label-color', this.options.labelColor);
}
// 应用全局 Value 颜色
if (this.options.valueColor) {
style.setProperty('--bim-desc-value-color', this.options.valueColor);
}
// 应用 Padding 配置
if (this.options.labelPadding) {
style.setProperty('--bim-desc-label-padding', this.options.labelPadding);
}
if (this.options.valuePadding) {
style.setProperty('--bim-desc-value-padding', this.options.valuePadding);
}
}
private renderItems() {
this.element.innerHTML = ''; // 清空现有内容
this.options.items.forEach(item => {
const itemEl = document.createElement('div');
itemEl.className = `bim-description-item ${item.className || ''}`;
// 1. Label
const labelEl = document.createElement('div');
labelEl.className = 'bim-description-label';
// 行级颜色覆盖全局颜色
if (item.labelColor) {
labelEl.style.color = item.labelColor;
}
// 设置固定宽度
if (this.options.labelWidth) {
labelEl.style.width = this.options.labelWidth;
}
// 直接显示文本
// bordered 模式移除冒号,普通模式保留
labelEl.textContent = this.options.bordered ? item.label : (item.label + ':');
// 2. Value
const valueEl = document.createElement('div');
valueEl.className = 'bim-description-value';
// 行级颜色覆盖全局颜色
if (item.valueColor) {
valueEl.style.color = item.valueColor;
}
if (typeof item.value === 'string') {
valueEl.innerHTML = item.value;
} else {
valueEl.appendChild(item.value);
}
itemEl.appendChild(labelEl);
itemEl.appendChild(valueEl);
this.element.appendChild(itemEl);
});
}
/**
* 动态更新数据
*/
public setItems(items: DescriptionItem[]) {
this.options.items = items;
this.renderItems();
}
public setTheme(theme: ThemeConfig): void {
const style = this.element.style;
// 设置基础主题变量 (作为 fallback 或默认值)
style.setProperty('--bim-text-color', theme.textPrimary);
style.setProperty('--bim-label-color', theme.textSecondary);
style.setProperty('--bim-value-color', theme.textPrimary);
style.setProperty('--bim-border-color', theme.border);
}
public setLocales(): void {
// 本组件不处理国际化
}
public destroy(): void {
if (this.unsubscribeTheme) {
this.unsubscribeTheme();
this.unsubscribeTheme = null;
}
this.element.remove();
}
}

View File

@@ -0,0 +1,58 @@
/**
* 描述列表项配置
*/
export interface DescriptionItem {
/** 标签文本 (直接显示,组件内部不翻译) */
label: string;
/** 内容文本或元素 */
value: string | HTMLElement;
/** 行级自定义标签颜色 */
labelColor?: string;
/** 行级自定义内容颜色 */
valueColor?: string;
/** 自定义类名 */
className?: string;
}
/**
* 描述列表组件配置
*/
export interface DescriptionOptions {
/** 挂载容器 */
container: HTMLElement | string;
/** 数据项列表 */
items: DescriptionItem[];
/**
* 是否显示边框 (默认 false)
* 开启后,将显示行间分割线以及 Key-Value 之间的纵向分割线
*/
bordered?: boolean;
/** 标签固定宽度 (例如 '80px'),若不设置则自适应 */
labelWidth?: string;
/** 全局标签颜色 */
labelColor?: string;
/** 全局内容颜色 */
valueColor?: string;
/** 全局字体大小 */
fontSize?: string;
/** 标签内边距 (默认 '0 4px') */
labelPadding?: string;
/** 内容内边距 (默认 '0 4px') */
valuePadding?: string;
/** 自定义类名 */
className?: string;
}

View File

@@ -11,8 +11,6 @@
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 4px 0;
background: transparent;
}
@@ -21,22 +19,25 @@
align-items: center;
justify-content: center;
gap: 6px;
/* 恢复原样式上下4px左右0 */
padding: 4px 0;
border: none;
border-radius: 0;
border-radius: 0; /* 恢复直角 */
background: transparent;
color: var(--bim-tab-text, #e6e6e6);
cursor: pointer;
transition: color 0.2s ease, border-color 0.2s ease;
transition: all 0.2s ease;
font-size: 14px;
border-bottom: 2px solid transparent;
border-bottom: 4px solid transparent;
}
.bim-tab__item:hover:not(.is-disabled):not(.is-active) {
.bim-tab__item:hover {
color: var(--bim-tab-text, #e6e6e6);
border-bottom-color: var(--bim-tab-border, rgba(255, 255, 255, 0.15));
background-color: var(--bim-tab-hover-bg, rgba(255, 255, 255, 0.05));
border-bottom-color: var(--bim-tab-hover-bg, rgba(255, 255, 255, 0.15));
}
/* Active 状态 */
.bim-tab__item.is-active {
color: var(--bim-tab-text-active, #4da3ff);
border-bottom-color: var(--bim-tab-text-active, #4da3ff);
@@ -105,4 +106,3 @@
.construct-tab__panel-content .bim-tree {
flex: 1;
}

View File

@@ -12,5 +12,6 @@ export type { DialogOptions, DialogPosition } from './components/dialog/index.ty
export type { ButtonConfig, ButtonGroupOptions } from './components/button-group/index.type';
export type { TreeOptions, TreeNodeConfig, TreeNodeCheckState, NodeClickAction } from './components/tree/types';
export type { CollapseOptions, CollapseItemConfig } from './components/collapse/types';
export type { DescriptionOptions, DescriptionItem } from './components/description/types';
// Note: Component classes are intentionally NOT exported to enforce Manager pattern usage.

View File

@@ -39,11 +39,14 @@ export const enUS: TranslationDictionary = {
},
panel: {
property: {
title: 'Property Panel',
title: 'Component Details',
base: 'Basic Info',
material: 'Material',
advanced: 'Advanced'
advanced: 'Advanced',
tab: {
props: 'Properties',
material: 'Material'
}
}
}
};

View File

@@ -26,6 +26,10 @@ export interface TranslationDictionary {
base: string;
material: string;
advanced: string;
tab: {
props: string;
material: string;
}
}
};
dialog: {
@@ -50,6 +54,6 @@ export interface TranslationDictionary {
}
/**
* 语言码类型
* 语言<EFBFBD><EFBFBD>码类型
*/
export type LocaleType = 'zh-CN' | 'en-US';

View File

@@ -39,10 +39,14 @@ export const zhCN: TranslationDictionary = {
},
panel: {
property: {
title: '属性面板',
title: '构件详情',
base: '基本属性',
material: '材质信息',
advanced: '高级设置'
advanced: '高级设置',
tab: {
props: '属性',
material: '材质'
}
}
}
};

View File

@@ -1,92 +1,200 @@
import { BimComponent } from '../core/component';
import { BimEngine } from '../bim-engine';
import { BimCollapse } from '../components/collapse/index';
import { BimDescription } from '../components/description/index';
import { BimTab } from '../components/tab/index';
/**
* 属性面板管理器
* 负责展示和管理属性面板弹窗 (演示 Tab + Collapse + Description 组件)
*/
export class PropertyPanelManager extends BimComponent {
private dialogId = 'property-panel-dialog';
constructor(engine: BimEngine) {
super(engine);
}
public init(): void {
// 监听来自 Demo 的打开属性面板事件
document.addEventListener('bim-demo:open-property-panel', () => {
this.show();
});
}
/**
* 显示属性面板
*/
public show() {
if (!this.engine.dialog) {
console.warn('Dialog manager is not initialized');
return;
}
const dialog = this.engine.dialog.create({
title: 'panel.property.title', // '属性面板'
minWidth: 320,
height: 420,
position: 'top-right',
resizable: false
});
// 2. Create Content Container
// 1. 创建弹窗
const width = 360; // 稍微加宽一点以容纳 Tab
const x = document.body.clientWidth - width - 40;
console.log('x', x)
const dialog = this.engine.dialog.create({
id: this.dialogId,
title: 'panel.property.title', // '构件详情'
content: '',
width: `${width}px`,
height: '500px',
position: { x, y: 20 },
showMask: false,
resizable: true
} as any);
// 2. 创建内容容器
const contentContainer = document.createElement('div');
contentContainer.style.height = '100%';
contentContainer.style.overflowY = 'auto';
contentContainer.style.display = 'flex';
contentContainer.style.flexDirection = 'column';
// Use public API to set content
dialog.setContent(contentContainer);
// 3. Create Collapse inside the Dialog
new BimCollapse({
// 3. 创建标签页组件
const tab = new BimTab({
container: contentContainer,
tabs: [
{
id: 'props',
title: 'panel.property.tab.props', // '属性'
content: this.createPropsTabContent()
},
{
id: 'material',
title: 'panel.property.tab.material', // '材质'
content: this.createMaterialTabContent()
}
]
});
tab.init();
}
/**
* 创建"属性"标签页的内容 (包含 Collapse)
*/
private createPropsTabContent(): HTMLElement {
const container = document.createElement('div');
container.style.height = '100%';
container.style.overflowY = 'auto'; // 内容区域滚动
new BimCollapse({
container: container,
accordion: true,
activeIds: ['base'],
activeIds: ['base', 'location'],
items: [
{
id: 'base',
title: 'panel.property.base', // '基本属性'
content: this.createBaseInfoContent(),
icon: '<svg viewBox="0 0 1024 1024"><path d="M512 64q190.4 0 326.4 136T974.4 526.4 838.4 852.8 512 988.8 185.6 852.8 49.6 526.4 185.6 200 512 64m0-64C229.6 0 0 229.6 0 512s229.6 512 512 512 512-229.6 512-512S794.4 0 512 0z" fill="currentColor"/></svg>'
},
{
id: 'material',
title: 'panel.property.material', // '材质信息'
content: this.createMaterialContent(),
icon: '<svg viewBox="0 0 1024 1024"><path d="M128 128h768v768H128z" fill="none" stroke="currentColor" stroke-width="64"/></svg>'
},
{
id: 'advanced',
title: 'panel.property.advanced', // '高级设置'
content: '<div>Loading...</div>', // Placeholder
disabled: true
content: this.createAdvancedInfoContent(), // 新增一个内容
disabled: false
}
]
});
return container;
}
/**
* 创建"材质"标签页的内容 (包含 Collapse)
*/
private createMaterialTabContent(): HTMLElement {
const container = document.createElement('div');
container.style.height = '100%';
container.style.overflowY = 'auto';
new BimCollapse({
container: container,
accordion: true,
activeIds: ['material'],
items: [
{
id: 'material',
title: 'panel.property.material', // '材质信息'
content: this.createMaterialContent(),
}
]
});
return container;
}
private createBaseInfoContent(): HTMLElement {
const div = document.createElement('div');
div.style.padding = '8px 0';
div.innerHTML = `
<div style="margin-bottom:8px"><b>ID:</b> <span style="color:#666">E-2023001</span></div>
<div style="margin-bottom:8px"><b>Name:</b> <span style="color:#666">Wall-01</span></div>
<div style="margin-bottom:8px"><b>Level:</b> <span style="color:#666">F1</span></div>
`;
return div;
const container = document.createElement('div');
new BimDescription({
container: container,
labelWidth: '80px',
bordered: true,
items: [
{ label: 'Guid', value: '<span style="color:#666">1f8d-4a2e-9c</span>' },
{ label: 'Name', value: '<b>Basic Wall: Generic - 200mm</b>' },
{ label: 'Type', value: 'Basic Wall' },
{ label: 'Level', value: 'Trane - Centrifugal Water Chiller - CVHF 2 Stage direct drive TAG(BP-RHS-1100RT) 0202104531 1' }
]
});
return container;
}
private createAdvancedInfoContent(): HTMLElement {
const container = document.createElement('div');
new BimDescription({
container: container,
labelWidth: '100px',
bordered: true,
items: [
{ label: 'Area', value: '32.5 m²' },
{ label: 'Volume', value: '6.5 m³' },
{ label: 'Length', value: '5000 mm' },
{ label: 'Phase', value: 'New Construction' }
]
});
return container;
}
private createMaterialContent(): HTMLElement {
const div = document.createElement('div');
div.innerHTML = `
<div style="display:flex;align-items:center;margin-bottom:8px">
<div style="width:20px;height:20px;background:#ccc;margin-right:8px;border:1px solid #999"></div>
<span>Concrete</span>
</div>
<div>Density: 2400 kg/m³</div>
const container = document.createElement('div');
// 材质预览块
const preview = document.createElement('div');
preview.style.display = 'flex';
preview.style.alignItems = 'center';
preview.style.marginBottom = '4px';
preview.innerHTML = `
<div style="width:24px;height:24px;background:#9ca3af;margin-right:8px;border:1px solid #6b7280;border-radius:2px;"></div>
<span>Concrete - Cast-in-Place Gray</span>
`;
return div;
const descContainer = document.createElement('div');
new BimDescription({
container: descContainer,
items: [
{ label: 'Preview', value: preview },
{ label: 'Class', value: 'Concrete' },
{ label: 'Density', value: '2400 kg/m³' },
{ label: 'Thermal', value: '0.6 W/(m·K)' }
]
});
container.appendChild(descContainer);
return container;
}
public destroy(): void {
// Cleanup if needed
// 如果有需要清理的资源
}
}