提交代码
This commit is contained in:
@@ -11,7 +11,7 @@ import { themeManager } from './services/theme';
|
||||
import type { LocaleType } from './locales/types';
|
||||
import type { ThemeType } from './themes/types';
|
||||
import type { EngineEvents } from './types/events';
|
||||
|
||||
import './iflow-engine-base.css';
|
||||
/**
|
||||
* BimEngine2d 构造选项
|
||||
* 合并引擎配置与主题/语言设置
|
||||
|
||||
@@ -11,6 +11,7 @@ import { themeManager } from './services/theme';
|
||||
import type { LocaleType } from './locales/types';
|
||||
import type { ThemeType } from './themes/types';
|
||||
import type { EngineEvents } from './types/events';
|
||||
import './iflow-engine-base.css';
|
||||
|
||||
/**
|
||||
* BimEngine720 构造选项
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
declare const __APP_VERSION__: string;
|
||||
import './bim-engine.css';
|
||||
import './iflow-engine-base.css';
|
||||
import { DialogManager } from './managers/dialog-manager';
|
||||
import { EngineManager } from './managers/engine-manager';
|
||||
import { RightKeyManager } from './managers/right-key-manager';
|
||||
|
||||
@@ -16,8 +16,6 @@ import type { SectionBoxRange } from '../section-box-panel/types';
|
||||
import type { ManagerRegistry } from '../../core/manager-registry';
|
||||
// 导入第三方 SDK 的 createEngine 函数(从 npm 包引入)
|
||||
import { createEngine as createEngineSDK } from 'iflow-engine-base';
|
||||
//import { createEngine as createEngineSDK } from '../../../../bim_engine_base/dist/bim-engine-sdk.es';
|
||||
import "../../../../bim_engine_base/dist/iflow-engine-base.css"
|
||||
|
||||
export type {
|
||||
EngineOptions,
|
||||
@@ -177,6 +175,22 @@ export class Engine implements IBimComponent {
|
||||
console.warn('[Engine] 底层引擎不支持 events.on 方法,无法监听点击事件');
|
||||
}
|
||||
|
||||
const oneClickEncoding = (this.engine as any).oneClickEncoding;
|
||||
if (oneClickEncoding?.on) {
|
||||
oneClickEncoding.on('encoding-start', (data: any) => {
|
||||
console.log('[Engine] 底层 encoding-start 事件触发:', data);
|
||||
this.registry.emit('encoding:start', data);
|
||||
});
|
||||
oneClickEncoding.on('encoding-complete', (data: any) => {
|
||||
console.log('[Engine] 底层 encoding-complete 事件触发:', data);
|
||||
this.registry.emit('encoding:complete', data);
|
||||
});
|
||||
oneClickEncoding.on('encoding-error', (data: any) => {
|
||||
console.log('[Engine] 底层 encoding-error 事件触发:', data);
|
||||
this.registry.emit('encoding:error', data);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Engine] Failed to initialize engine:', error);
|
||||
this._isInitialized = false;
|
||||
@@ -252,6 +266,36 @@ export class Engine implements IBimComponent {
|
||||
this.engine?.events?.off(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅一键编码事件
|
||||
* @param event 事件名称:'encoding-start' | 'encoding-complete' | 'encoding-error'
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public onOneClickEncodingEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.oneClickEncoding?.on(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅一键编码事件
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public offOneClickEncodingEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.oneClickEncoding?.off(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动一键编码
|
||||
* @remarks 调用底层 engine.oneClickEncoding.start(),需先订阅相关事件
|
||||
*/
|
||||
public startOneClickEncoding(): void {
|
||||
if (!this._isInitialized || !this.engine?.oneClickEncoding) {
|
||||
console.warn('[Engine] oneClickEncoding not available.');
|
||||
return;
|
||||
}
|
||||
this.engine.oneClickEncoding.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停渲染
|
||||
*/
|
||||
@@ -631,6 +675,32 @@ export class Engine implements IBimComponent {
|
||||
this.engine.clipping.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置剖切面填充开关
|
||||
* @param enabled 是否启用填充
|
||||
* @remarks 对接底层 `engine.clipping.setFillCutFace(enabled)`
|
||||
*/
|
||||
public setFillCutFace(enabled: boolean): void {
|
||||
if (!this._isInitialized || !this.engine?.clipping) {
|
||||
console.error('[Engine] Cannot set fill cut face: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
console.log('[Engine] Setting fill cut face:', enabled);
|
||||
this.engine.clipping.setFillCutFace(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剖切面填充状态
|
||||
* @returns true=已启用填充,false=未启用
|
||||
* @remarks 对接底层 `engine.clipping.getFillCutFace()`
|
||||
*/
|
||||
public getFillCutFace(): boolean {
|
||||
if (!this._isInitialized || !this.engine?.clipping) {
|
||||
return false;
|
||||
}
|
||||
return this.engine.clipping.getFillCutFace?.() ?? false;
|
||||
}
|
||||
|
||||
// ==================== 结束:剖切功能 ====================
|
||||
|
||||
// ==================== 相机切换 ====================
|
||||
@@ -1767,6 +1837,49 @@ export class Engine implements IBimComponent {
|
||||
|
||||
// ==================== 结束:构件操作 ====================
|
||||
|
||||
/**
|
||||
* 读取所有已加载模型的缓存编码数据
|
||||
* @remarks 遍历 engine.models,对每个模型调用 oneClickEncoding.readModelCodeFormStoge(url)
|
||||
*/
|
||||
public readModelCodeFormStoge(): void {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.warn('[Engine] Cannot read model code form stoge: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
let count = 0;
|
||||
this.engine.models.forEach((m: any) => {
|
||||
this.engine.oneClickEncoding.readModelCodeFormStoge(m.url);
|
||||
count++;
|
||||
});
|
||||
console.log('readModelCodeFormStoge-success:', count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所有已加载模型是否已有编码数据
|
||||
* @returns 当所有模型都存在编码时返回 true,否则返回 false
|
||||
*/
|
||||
public hasModelCode(): boolean {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.warn('[Engine] Cannot check model code: engine not initialized.');
|
||||
return false;
|
||||
}
|
||||
const models = (this.engine as any).models;
|
||||
if (!Array.isArray(models) || models.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const oneClickEncoding = (this.engine as any).oneClickEncoding;
|
||||
if (!oneClickEncoding || typeof oneClickEncoding.exitModelCode !== 'function') {
|
||||
console.warn('[Engine] oneClickEncoding.exitModelCode is not available.');
|
||||
return false;
|
||||
}
|
||||
return models.every((m: any) => {
|
||||
if (!m || typeof m.url !== 'string') {
|
||||
return false;
|
||||
}
|
||||
return oneClickEncoding.exitModelCode(m.url) === true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁组件 (接口实现)
|
||||
* 清理资源、取消订阅、销毁引擎实例
|
||||
|
||||
@@ -25,29 +25,29 @@ export class RadialToolbar implements IBimComponent {
|
||||
private maxInteractiveRadius = 220;
|
||||
|
||||
private readonly closeDelay: number;
|
||||
private readonly itemsPerRing: number;
|
||||
|
||||
// 主按钮直径(px)
|
||||
private readonly MAIN_BUTTON_SIZE = 60;
|
||||
// 子按钮直径(px)
|
||||
private readonly SUB_BUTTON_SIZE = 46;
|
||||
// 第一环“子按钮中心”到“主按钮中心”的距离(px)
|
||||
// 不做防重叠兜底,允许在小半径时出现重叠。
|
||||
// 第一环"子按钮中心"到"主按钮中心"的距离(px)
|
||||
private readonly BASE_RADIUS = 80;
|
||||
// 多环时,相邻两环的中心半径差(px)
|
||||
private readonly RING_GAP = 40;
|
||||
// 扇形展开角度范围:当前 180~270(实际就是 90 度)
|
||||
private readonly RING_GAP = 60;
|
||||
// 扇形展开角度范围 (180为正左边/下,270为正上)
|
||||
private readonly FAN_START_DEG = 170;
|
||||
private readonly FAN_END_DEG = 280;
|
||||
// 扇形边缘留白(px),防止按钮贴边或裁切
|
||||
private readonly CANVAS_PADDING = 28;
|
||||
|
||||
// 【自定义每环数量】:你可以随意修改这里的数字!多出来的按钮会自动折叠到更外围的新环。
|
||||
private readonly FIXED_RING_CAPACITIES = [4, 6, 8];
|
||||
|
||||
constructor(options: RadialToolbarOptions) {
|
||||
this.container = options.container;
|
||||
this.items = options.items === undefined ? this.createDefaultItems() : [...options.items];
|
||||
this.mainButtonLabel = options.mainButtonLabel ?? 'toolbar.home';
|
||||
this.onMainButtonClick = options.onMainButtonClick;
|
||||
this.itemsPerRing = Math.max(3, options.itemsPerRing ?? 5);
|
||||
this.closeDelay = Math.max(100, options.closeDelay ?? 260);
|
||||
|
||||
this.wrapper = this.createWrapper();
|
||||
@@ -223,38 +223,69 @@ export class RadialToolbar implements IBimComponent {
|
||||
return btn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据预设的数组计算容量(不足自动向后排)
|
||||
*/
|
||||
private getRingCapacities(total: number): number[] {
|
||||
const capacities: number[] = [];
|
||||
let remaining = total;
|
||||
|
||||
for (const cap of this.FIXED_RING_CAPACITIES) {
|
||||
if (remaining <= 0) break;
|
||||
const actualCount = Math.min(cap, remaining);
|
||||
capacities.push(actualCount);
|
||||
remaining -= actualCount;
|
||||
}
|
||||
|
||||
// 如果配置的环数不够用,剩下的全部折叠到最后一环
|
||||
if (remaining > 0) {
|
||||
capacities.push(remaining);
|
||||
}
|
||||
|
||||
return capacities;
|
||||
}
|
||||
|
||||
private updateItemPositions(): void {
|
||||
const total = this.itemElements.length;
|
||||
if (total === 0) return;
|
||||
|
||||
const fanSpan = this.FAN_END_DEG - this.FAN_START_DEG;
|
||||
const ringCapacities = this.getRingCapacities(total);
|
||||
|
||||
this.itemElements.forEach((btn, globalIndex) => {
|
||||
const ringIndex = Math.floor(globalIndex / this.itemsPerRing);
|
||||
const ringStart = ringIndex * this.itemsPerRing;
|
||||
const ringCount = Math.min(this.itemsPerRing, total - ringStart);
|
||||
const ringLocalIndex = globalIndex - ringStart;
|
||||
const ratio = ringCount === 1 ? 0.5 : ringLocalIndex / (ringCount - 1);
|
||||
const angleDeg = this.FAN_START_DEG + fanSpan * ratio;
|
||||
const angleRad = (angleDeg * Math.PI) / 180;
|
||||
const radius = this.getBaseRadius(ringCount) + ringIndex * this.RING_GAP;
|
||||
let globalIndex = 0;
|
||||
ringCapacities.forEach((ringCount, ringIndex) => {
|
||||
const radius = this.BASE_RADIUS + ringIndex * this.RING_GAP;
|
||||
|
||||
const x = Math.cos(angleRad) * radius;
|
||||
const y = Math.sin(angleRad) * radius;
|
||||
for (let ringLocalIndex = 0; ringLocalIndex < ringCount; ringLocalIndex++) {
|
||||
const btn = this.itemElements[globalIndex];
|
||||
|
||||
const openDelay = (ringLocalIndex + ringIndex * 0.5) * 0.045;
|
||||
const closeDelay = (ringCount - 1 - ringLocalIndex + ringIndex * 0.4) * 0.032;
|
||||
// 【核心逻辑】:首尾固定:第一个按钮在最下面(180),最后一个在最上面(270),中间按剩余空间均分
|
||||
// 如果该环只有1个按钮,让它待在180度的起始位置
|
||||
const ratio = ringCount <= 1 ? 0 : ringLocalIndex / (ringCount - 1);
|
||||
|
||||
btn.style.setProperty('--rt-x', `${x.toFixed(2)}px`);
|
||||
btn.style.setProperty('--rt-y', `${y.toFixed(2)}px`);
|
||||
btn.style.setProperty('--rt-open-delay', `${openDelay.toFixed(3)}s`);
|
||||
btn.style.setProperty('--rt-close-delay', `${closeDelay.toFixed(3)}s`);
|
||||
const angleDeg = this.FAN_START_DEG + fanSpan * ratio;
|
||||
const angleRad = (angleDeg * Math.PI) / 180;
|
||||
|
||||
const x = Math.cos(angleRad) * radius;
|
||||
const y = Math.sin(angleRad) * radius;
|
||||
|
||||
const openDelay = (ringLocalIndex + ringIndex * 0.5) * 0.045;
|
||||
const closeDelay = (ringCount - 1 - ringLocalIndex + ringIndex * 0.4) * 0.032;
|
||||
|
||||
btn.style.setProperty('--rt-x', `${x.toFixed(2)}px`);
|
||||
btn.style.setProperty('--rt-y', `${y.toFixed(2)}px`);
|
||||
btn.style.setProperty('--rt-open-delay', `${openDelay.toFixed(3)}s`);
|
||||
btn.style.setProperty('--rt-close-delay', `${closeDelay.toFixed(3)}s`);
|
||||
globalIndex++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateLayoutMetrics(): void {
|
||||
const total = this.items.length;
|
||||
const ringCount = Math.max(1, Math.ceil(total / this.itemsPerRing));
|
||||
const maxItemsPerRing = Math.max(1, Math.min(total, this.itemsPerRing));
|
||||
const maxRadius = this.getBaseRadius(maxItemsPerRing) + (ringCount - 1) * this.RING_GAP;
|
||||
const ringCapacities = this.getRingCapacities(total);
|
||||
const ringCount = ringCapacities.length;
|
||||
const maxRadius = this.BASE_RADIUS + (ringCount - 1) * this.RING_GAP;
|
||||
const size = Math.ceil(maxRadius + this.MAIN_BUTTON_SIZE + this.SUB_BUTTON_SIZE + this.CANVAS_PADDING * 2);
|
||||
this.maxInteractiveRadius = maxRadius + this.SUB_BUTTON_SIZE * 0.7;
|
||||
|
||||
@@ -265,10 +296,6 @@ export class RadialToolbar implements IBimComponent {
|
||||
this.wrapper.style.setProperty('--rt-main-offset', `${(this.MAIN_BUTTON_SIZE - this.SUB_BUTTON_SIZE) / 2}px`);
|
||||
}
|
||||
|
||||
private getBaseRadius(_itemsInRing: number): number {
|
||||
return this.BASE_RADIUS;
|
||||
}
|
||||
|
||||
private scheduleCollapse(): void {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
@@ -354,7 +381,7 @@ export class RadialToolbar implements IBimComponent {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const text = t(item.label);
|
||||
const text = item.title ?? t(item.label);
|
||||
el.title = text;
|
||||
el.setAttribute('aria-label', text);
|
||||
this.applyItemActiveClass(el, item);
|
||||
@@ -362,7 +389,7 @@ export class RadialToolbar implements IBimComponent {
|
||||
if (!item.icon) {
|
||||
const iconEl = el.querySelector('.radial-sub-btn-icon');
|
||||
if (iconEl) {
|
||||
iconEl.textContent = this.getFallbackLabel(item.label);
|
||||
iconEl.textContent = this.getFallbackLabel(item.title ?? item.label);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -431,6 +458,12 @@ export class RadialToolbar implements IBimComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public addItem(item: RadialMenuItem): void {
|
||||
this.items.push(item);
|
||||
this.renderItems();
|
||||
this.updateLayoutMetrics();
|
||||
}
|
||||
|
||||
public init(): void { }
|
||||
|
||||
public setLocales(): void {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export interface RadialMenuItem {
|
||||
id: string;
|
||||
label: string;
|
||||
/** 直接显示的文本(优先级高于 label 的国际化翻译) */
|
||||
title?: string;
|
||||
icon?: string;
|
||||
onClick?: (item: RadialMenuItem) => void;
|
||||
isToggle?: boolean;
|
||||
@@ -14,6 +16,5 @@ export interface RadialToolbarOptions {
|
||||
mainButtonIcon?: string;
|
||||
mainButtonLabel?: string;
|
||||
onMainButtonClick?: () => void;
|
||||
itemsPerRing?: number;
|
||||
closeDelay?: number;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,16 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
public element: HTMLElement;
|
||||
private options: SectionAxisPanelOptions;
|
||||
|
||||
// 状态
|
||||
private isHidden: boolean = false;
|
||||
private isFilled: boolean = false;
|
||||
private activeAxis: SectionAxis = 'x';
|
||||
|
||||
// DOM 引用 - 第一行
|
||||
private hideBtn!: HTMLButtonElement;
|
||||
private reverseBtn!: HTMLButtonElement;
|
||||
private fillBtn!: HTMLButtonElement;
|
||||
private hideLabelEl!: HTMLElement;
|
||||
private reverseLabelEl!: HTMLElement;
|
||||
private fillLabelEl!: HTMLElement;
|
||||
|
||||
// DOM 引用 - 第二行
|
||||
private axisXBtn!: HTMLButtonElement;
|
||||
@@ -37,6 +38,7 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
constructor(options: SectionAxisPanelOptions = {}) {
|
||||
this.options = options;
|
||||
this.isHidden = options.defaultHidden ?? false;
|
||||
this.isFilled = options.defaultFill ?? false;
|
||||
this.activeAxis = options.defaultAxis ?? 'x';
|
||||
this.element = this.createDom();
|
||||
}
|
||||
@@ -60,7 +62,7 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
this.setTheme(themeManager.getTheme());
|
||||
|
||||
// 初始化按钮状态
|
||||
this.updateHideButtonState();
|
||||
this.updateButtonStates();
|
||||
this.updateAxisButtonsState();
|
||||
}
|
||||
|
||||
@@ -86,41 +88,39 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
public setLocales(): void {
|
||||
this.hideLabelEl.textContent = t('sectionAxis.actions.hide');
|
||||
this.reverseLabelEl.textContent = t('sectionAxis.actions.reverse');
|
||||
// XYZ按钮的文字不需要国际化,保持为单个字母
|
||||
this.fillLabelEl.textContent = t('sectionAxis.actions.fill');
|
||||
|
||||
this.hideBtn.title = t('sectionAxis.actions.hide');
|
||||
this.reverseBtn.title = t('sectionAxis.actions.reverse');
|
||||
this.fillBtn.title = t('sectionAxis.actions.fill');
|
||||
this.axisXBtn.title = t('sectionAxis.actions.axisX');
|
||||
this.axisYBtn.title = t('sectionAxis.actions.axisY');
|
||||
this.axisZBtn.title = t('sectionAxis.actions.axisZ');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置隐藏状态
|
||||
*/
|
||||
public setHiddenState(isHidden: boolean): void {
|
||||
this.isHidden = isHidden;
|
||||
this.updateHideButtonState();
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隐藏状态
|
||||
*/
|
||||
public getHiddenState(): boolean {
|
||||
return this.isHidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置激活的轴向
|
||||
*/
|
||||
public setFillState(isFilled: boolean): void {
|
||||
this.isFilled = isFilled;
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
public getFillState(): boolean {
|
||||
return this.isFilled;
|
||||
}
|
||||
|
||||
public setActiveAxis(axis: SectionAxis): void {
|
||||
this.activeAxis = axis;
|
||||
this.updateAxisButtonsState();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取激活的轴向
|
||||
*/
|
||||
public getActiveAxis(): SectionAxis {
|
||||
return this.activeAxis;
|
||||
}
|
||||
@@ -151,6 +151,12 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
const row1 = document.createElement('div');
|
||||
row1.className = 'section-axis-row-1';
|
||||
|
||||
this.fillBtn = this.createButton(
|
||||
'fill',
|
||||
getIcon('填充'),
|
||||
() => this.handleFillToggle()
|
||||
);
|
||||
|
||||
this.hideBtn = this.createButton(
|
||||
'hide',
|
||||
getIcon('隐藏'),
|
||||
@@ -163,6 +169,7 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
() => this.handleReverse()
|
||||
);
|
||||
|
||||
row1.appendChild(this.fillBtn);
|
||||
row1.appendChild(this.hideBtn);
|
||||
row1.appendChild(this.reverseBtn);
|
||||
|
||||
@@ -188,7 +195,7 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
* 创建按钮(带图标)
|
||||
*/
|
||||
private createButton(
|
||||
type: 'hide' | 'reverse',
|
||||
type: 'hide' | 'reverse' | 'fill',
|
||||
iconSvg: string,
|
||||
onClick: () => void
|
||||
): HTMLButtonElement {
|
||||
@@ -207,11 +214,12 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
label.className = 'section-axis-btn-label';
|
||||
btn.appendChild(label);
|
||||
|
||||
// 保存 label 引用
|
||||
if (type === 'hide') {
|
||||
this.hideLabelEl = label;
|
||||
} else if (type === 'reverse') {
|
||||
this.reverseLabelEl = label;
|
||||
} else if (type === 'fill') {
|
||||
this.fillLabelEl = label;
|
||||
}
|
||||
|
||||
// 点击事件
|
||||
@@ -249,16 +257,22 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
*/
|
||||
private handleHideToggle(): void {
|
||||
this.isHidden = !this.isHidden;
|
||||
this.updateHideButtonState();
|
||||
this.updateButtonStates();
|
||||
|
||||
if (this.options.onHideToggle) {
|
||||
this.options.onHideToggle(this.isHidden);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理反向按钮点击
|
||||
*/
|
||||
private handleFillToggle(): void {
|
||||
this.isFilled = !this.isFilled;
|
||||
this.updateButtonStates();
|
||||
|
||||
if (this.options.onFillToggle) {
|
||||
this.options.onFillToggle(this.isFilled);
|
||||
}
|
||||
}
|
||||
|
||||
private handleReverse(): void {
|
||||
if (this.options.onReverse) {
|
||||
this.options.onReverse();
|
||||
@@ -281,15 +295,9 @@ export class SectionAxisPanel implements IBimComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新隐藏按钮状态
|
||||
*/
|
||||
private updateHideButtonState(): void {
|
||||
if (this.isHidden) {
|
||||
this.hideBtn.classList.add('active');
|
||||
} else {
|
||||
this.hideBtn.classList.remove('active');
|
||||
}
|
||||
private updateButtonStates(): void {
|
||||
this.hideBtn.classList.toggle('active', this.isHidden);
|
||||
this.fillBtn.classList.toggle('active', this.isFilled);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,30 +7,11 @@ export type SectionAxis = 'x' | 'y' | 'z';
|
||||
* 轴向剖切面板配置选项
|
||||
*/
|
||||
export interface SectionAxisPanelOptions {
|
||||
/**
|
||||
* 隐藏按钮切换回调
|
||||
* @param isHidden 是否隐藏
|
||||
*/
|
||||
onHideToggle?: (isHidden: boolean) => void;
|
||||
|
||||
/**
|
||||
* 反向按钮回调
|
||||
*/
|
||||
onFillToggle?: (isFilled: boolean) => void;
|
||||
onReverse?: () => void;
|
||||
|
||||
/**
|
||||
* 轴向切换回调
|
||||
* @param axis 当前激活的轴向
|
||||
*/
|
||||
onAxisChange?: (axis: SectionAxis) => void;
|
||||
|
||||
/**
|
||||
* 默认激活的轴向(默认 'x')
|
||||
*/
|
||||
defaultAxis?: SectionAxis;
|
||||
|
||||
/**
|
||||
* 初始隐藏状态(默认 false)
|
||||
*/
|
||||
defaultHidden?: boolean;
|
||||
defaultFill?: boolean;
|
||||
}
|
||||
|
||||
@@ -18,15 +18,18 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
|
||||
private isHidden: boolean = false;
|
||||
private isReversed: boolean = false;
|
||||
private isFilled: boolean = false;
|
||||
private range: SectionBoxRange;
|
||||
|
||||
private hideBtn!: HTMLButtonElement;
|
||||
private fitBtn!: HTMLButtonElement;
|
||||
private resetBtn!: HTMLButtonElement;
|
||||
private fillBtn!: HTMLButtonElement;
|
||||
|
||||
private hideLabelEl!: HTMLElement;
|
||||
private fitLabelEl!: HTMLElement;
|
||||
private resetLabelEl!: HTMLElement;
|
||||
private fillLabelEl!: HTMLElement;
|
||||
private xLabelEl!: HTMLElement;
|
||||
private yLabelEl!: HTMLElement;
|
||||
private zLabelEl!: HTMLElement;
|
||||
@@ -61,6 +64,7 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
this.options = options;
|
||||
this.isHidden = options.defaultHidden ?? false;
|
||||
this.isReversed = options.defaultReversed ?? false;
|
||||
this.isFilled = options.defaultFill ?? false;
|
||||
this.range = JSON.parse(JSON.stringify(options.defaultRange ?? DEFAULT_RANGE));
|
||||
}
|
||||
|
||||
@@ -96,6 +100,15 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
return this.isReversed;
|
||||
}
|
||||
|
||||
public setFillState(isFilled: boolean): void {
|
||||
this.isFilled = isFilled;
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
public getFillState(): boolean {
|
||||
return this.isFilled;
|
||||
}
|
||||
|
||||
public setRange(range: Partial<SectionBoxRange>): void {
|
||||
if (range.x) this.range.x = { ...this.range.x, ...range.x };
|
||||
if (range.y) this.range.y = { ...this.range.y, ...range.y };
|
||||
@@ -126,6 +139,12 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
const buttonsContainer = document.createElement('div');
|
||||
buttonsContainer.className = 'section-box-row-buttons';
|
||||
|
||||
this.fillBtn = this.createButton('fill', t('sectionBox.actions.fill'), () => {
|
||||
this.isFilled = !this.isFilled;
|
||||
this.updateButtonStates();
|
||||
this.options.onFillToggle?.(this.isFilled);
|
||||
}, 'fill');
|
||||
|
||||
this.hideBtn = this.createButton('hide', t('sectionBox.actions.hide'), () => {
|
||||
this.isHidden = !this.isHidden;
|
||||
this.updateButtonStates();
|
||||
@@ -138,7 +157,7 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
|
||||
this.resetBtn = this.createButton('reset', t('sectionBox.actions.reset'), () => this.reset(), 'reset');
|
||||
|
||||
[this.hideBtn, this.fitBtn, this.resetBtn].forEach(btn => buttonsContainer.appendChild(btn));
|
||||
[this.fillBtn, this.hideBtn, this.fitBtn, this.resetBtn].forEach(btn => buttonsContainer.appendChild(btn));
|
||||
|
||||
const slidersContainer = document.createElement('div');
|
||||
slidersContainer.className = 'section-box-sliders';
|
||||
@@ -164,7 +183,8 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
hide: '隐藏',
|
||||
reverse: '反向',
|
||||
fit: '适应到模型',
|
||||
reset: '重置'
|
||||
reset: '重置',
|
||||
fill: '填充'
|
||||
};
|
||||
|
||||
const icon = document.createElement('div');
|
||||
@@ -178,6 +198,7 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
if (ref === 'hide') this.hideLabelEl = labelEl;
|
||||
else if (ref === 'fit') this.fitLabelEl = labelEl;
|
||||
else if (ref === 'reset') this.resetLabelEl = labelEl;
|
||||
else if (ref === 'fill') this.fillLabelEl = labelEl;
|
||||
|
||||
btn.appendChild(icon);
|
||||
btn.appendChild(labelEl);
|
||||
@@ -320,12 +341,14 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
|
||||
public setLocales(): void {
|
||||
if (!this.hideLabelEl) return;
|
||||
this.fillLabelEl.textContent = t('sectionBox.actions.fill');
|
||||
this.hideLabelEl.textContent = t('sectionBox.actions.hide');
|
||||
this.fitLabelEl.textContent = t('sectionBox.actions.fitToModel');
|
||||
this.resetLabelEl.textContent = t('sectionBox.actions.reset');
|
||||
this.xLabelEl.textContent = t('sectionBox.axes.x');
|
||||
this.yLabelEl.textContent = t('sectionBox.axes.y');
|
||||
this.zLabelEl.textContent = t('sectionBox.axes.z');
|
||||
this.fillBtn.title = t('sectionBox.actions.fill');
|
||||
this.hideBtn.title = t('sectionBox.actions.hide');
|
||||
this.fitBtn.title = t('sectionBox.actions.fitToModel');
|
||||
this.resetBtn.title = t('sectionBox.actions.reset');
|
||||
|
||||
@@ -24,46 +24,14 @@ export interface SectionBoxRange {
|
||||
* 剖切盒面板配置选项
|
||||
*/
|
||||
export interface SectionBoxPanelOptions {
|
||||
/**
|
||||
* 隐藏按钮切换回调
|
||||
* @param isHidden 是否隐藏剖切盒
|
||||
*/
|
||||
onHideToggle?: (isHidden: boolean) => void;
|
||||
|
||||
/**
|
||||
* 反向按钮切换回调
|
||||
* @param isReversed 是否反向
|
||||
*/
|
||||
onReverseToggle?: (isReversed: boolean) => void;
|
||||
|
||||
/**
|
||||
* 适应到模型按钮回调
|
||||
*/
|
||||
onFitToModel?: () => void;
|
||||
|
||||
/**
|
||||
* 重置按钮回调
|
||||
*/
|
||||
onReset?: () => void;
|
||||
|
||||
/**
|
||||
* 范围变化回调
|
||||
* @param range 当前范围值
|
||||
*/
|
||||
onRangeChange?: (range: SectionBoxRange) => void;
|
||||
|
||||
/**
|
||||
* 默认隐藏状态(默认 false)
|
||||
*/
|
||||
onFillToggle?: (isFilled: boolean) => void;
|
||||
defaultHidden?: boolean;
|
||||
|
||||
/**
|
||||
* 默认反向状态(默认 false)
|
||||
*/
|
||||
defaultReversed?: boolean;
|
||||
|
||||
/**
|
||||
* 默认范围值
|
||||
*/
|
||||
defaultFill?: boolean;
|
||||
defaultRange?: SectionBoxRange;
|
||||
}
|
||||
|
||||
@@ -11,16 +11,18 @@ export interface SectionDockPanelOptions {
|
||||
defaultType?: SectionDockType | null;
|
||||
defaultAxis?: SectionDockAxis;
|
||||
defaultHidden?: boolean;
|
||||
defaultFill?: boolean;
|
||||
onTypeChange?: (type: SectionDockType, axis: SectionDockAxis) => void;
|
||||
onAxisChange?: (axis: SectionDockAxis) => void;
|
||||
onHideToggle?: (hidden: boolean) => void;
|
||||
onReverse?: () => void;
|
||||
onReset?: (type: SectionDockType, axis: SectionDockAxis) => void;
|
||||
onFitToModel?: () => void;
|
||||
onFillToggle?: (isFilled: boolean) => void;
|
||||
}
|
||||
|
||||
interface ToolDefinition {
|
||||
key: 'hide' | 'reverse' | 'reset' | 'fit';
|
||||
key: 'hide' | 'reverse' | 'reset' | 'fit' | 'fill';
|
||||
iconName: string;
|
||||
textKey: string;
|
||||
onClick: () => void;
|
||||
@@ -40,12 +42,14 @@ export class SectionDockPanel implements IBimComponent {
|
||||
private activeType: SectionDockType | null;
|
||||
private activeAxis: SectionDockAxis;
|
||||
private isHidden: boolean;
|
||||
private isFilled: boolean;
|
||||
|
||||
constructor(options: SectionDockPanelOptions = {}) {
|
||||
this.options = options;
|
||||
this.activeType = options.defaultType ?? null;
|
||||
this.activeAxis = options.defaultAxis ?? 'x';
|
||||
this.isHidden = options.defaultHidden ?? false;
|
||||
this.isFilled = options.defaultFill ?? false;
|
||||
|
||||
const { root, toolContainer, axisPanel, divider } = this.createDom();
|
||||
this.element = root;
|
||||
@@ -112,6 +116,15 @@ export class SectionDockPanel implements IBimComponent {
|
||||
return this.activeAxis;
|
||||
}
|
||||
|
||||
public setFillState(isFilled: boolean): void {
|
||||
this.isFilled = isFilled;
|
||||
this.renderTools();
|
||||
}
|
||||
|
||||
public getFillState(): boolean {
|
||||
return this.isFilled;
|
||||
}
|
||||
|
||||
private createDom(): { root: HTMLElement; toolContainer: HTMLElement; axisPanel: HTMLElement; divider: HTMLElement } {
|
||||
const root = document.createElement('div');
|
||||
root.className = 'section-dock-panel';
|
||||
@@ -257,6 +270,18 @@ export class SectionDockPanel implements IBimComponent {
|
||||
}
|
||||
|
||||
private getToolsForType(type: SectionDockType): ToolDefinition[] {
|
||||
const fillTool: ToolDefinition = {
|
||||
key: 'fill',
|
||||
iconName: '填充',
|
||||
textKey: type === 'box' ? 'sectionBox.actions.fill' : type === 'axis' ? 'sectionAxis.actions.fill' : 'sectionPlane.actions.fill',
|
||||
isActive: this.isFilled,
|
||||
onClick: () => {
|
||||
this.isFilled = !this.isFilled;
|
||||
this.options.onFillToggle?.(this.isFilled);
|
||||
this.renderTools();
|
||||
}
|
||||
};
|
||||
|
||||
const hideTool: ToolDefinition = {
|
||||
key: 'hide',
|
||||
iconName: '隐藏',
|
||||
@@ -280,6 +305,7 @@ export class SectionDockPanel implements IBimComponent {
|
||||
|
||||
if (type === 'axis') {
|
||||
return [
|
||||
fillTool,
|
||||
hideTool,
|
||||
reverseTool,
|
||||
{
|
||||
@@ -297,6 +323,7 @@ export class SectionDockPanel implements IBimComponent {
|
||||
|
||||
if (type === 'box') {
|
||||
return [
|
||||
fillTool,
|
||||
hideTool,
|
||||
{
|
||||
key: 'fit',
|
||||
@@ -320,6 +347,7 @@ export class SectionDockPanel implements IBimComponent {
|
||||
}
|
||||
|
||||
return [
|
||||
fillTool,
|
||||
hideTool,
|
||||
reverseTool,
|
||||
{
|
||||
|
||||
@@ -15,14 +15,17 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
private options: SectionPlanePanelOptions;
|
||||
|
||||
private isHidden: boolean = false;
|
||||
private isFilled: boolean = false;
|
||||
|
||||
// DOM 引用
|
||||
private hideBtn!: HTMLButtonElement;
|
||||
private reverseBtn!: HTMLButtonElement;
|
||||
private resetBtn!: HTMLButtonElement;
|
||||
private fillBtn!: HTMLButtonElement;
|
||||
private hideLabelEl!: HTMLElement;
|
||||
private reverseLabelEl!: HTMLElement;
|
||||
private resetLabelEl!: HTMLElement;
|
||||
private fillLabelEl!: HTMLElement;
|
||||
|
||||
// 订阅清理
|
||||
private unsubscribeLocale: (() => void) | null = null;
|
||||
@@ -31,6 +34,7 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
constructor(options: SectionPlanePanelOptions = {}) {
|
||||
this.options = options;
|
||||
this.isHidden = options.defaultHidden ?? false;
|
||||
this.isFilled = options.defaultFill ?? false;
|
||||
this.element = this.createDom();
|
||||
}
|
||||
|
||||
@@ -43,8 +47,18 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
return this.isHidden;
|
||||
}
|
||||
|
||||
public setFillState(isFilled: boolean): void {
|
||||
this.isFilled = isFilled;
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
public getFillState(): boolean {
|
||||
return this.isFilled;
|
||||
}
|
||||
|
||||
private updateButtonStates(): void {
|
||||
this.hideBtn?.classList.toggle('active', this.isHidden);
|
||||
this.fillBtn?.classList.toggle('active', this.isFilled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,10 +102,12 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
this.hideLabelEl.textContent = t('sectionPlane.actions.hide');
|
||||
this.reverseLabelEl.textContent = t('sectionPlane.actions.reverse');
|
||||
this.resetLabelEl.textContent = t('sectionPlane.actions.reset');
|
||||
this.fillLabelEl.textContent = t('sectionPlane.actions.fill');
|
||||
|
||||
this.hideBtn.title = t('sectionPlane.actions.hide');
|
||||
this.reverseBtn.title = t('sectionPlane.actions.reverse');
|
||||
this.resetBtn.title = t('sectionPlane.actions.reset');
|
||||
this.fillBtn.title = t('sectionPlane.actions.fill');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,6 +132,17 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
const root = document.createElement('div');
|
||||
root.className = 'section-plane-panel';
|
||||
|
||||
// 填充按钮
|
||||
this.fillBtn = this.createButton(
|
||||
'fill',
|
||||
getIcon('填充'),
|
||||
() => {
|
||||
this.isFilled = !this.isFilled;
|
||||
this.updateButtonStates();
|
||||
this.options.onFillToggle?.(this.isFilled);
|
||||
}
|
||||
);
|
||||
|
||||
// 隐藏按钮
|
||||
this.hideBtn = this.createButton(
|
||||
'hide',
|
||||
@@ -149,6 +176,7 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
}
|
||||
);
|
||||
|
||||
root.appendChild(this.fillBtn);
|
||||
root.appendChild(this.hideBtn);
|
||||
root.appendChild(this.reverseBtn);
|
||||
root.appendChild(this.resetBtn);
|
||||
@@ -159,7 +187,7 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
/**
|
||||
* 创建按钮
|
||||
*/
|
||||
private createButton(type: 'hide' | 'reverse' | 'reset', iconSvg: string, onClick: () => void): HTMLButtonElement {
|
||||
private createButton(type: 'hide' | 'reverse' | 'reset' | 'fill', iconSvg: string, onClick: () => void): HTMLButtonElement {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'section-plane-btn';
|
||||
@@ -175,11 +203,12 @@ export class SectionPlanePanel implements IBimComponent {
|
||||
label.className = 'section-plane-btn-label';
|
||||
btn.appendChild(label);
|
||||
|
||||
// 保存 label 引用
|
||||
if (type === 'hide') {
|
||||
this.hideLabelEl = label;
|
||||
} else if (type === 'reverse') {
|
||||
this.reverseLabelEl = label;
|
||||
} else if (type === 'fill') {
|
||||
this.fillLabelEl = label;
|
||||
} else {
|
||||
this.resetLabelEl = label;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,23 @@ export interface SectionPlanePanelOptions {
|
||||
*/
|
||||
defaultHidden?: boolean;
|
||||
|
||||
/**
|
||||
* 初始填充状态
|
||||
*/
|
||||
defaultFill?: boolean;
|
||||
|
||||
/**
|
||||
* 隐藏状态切换回调
|
||||
* @param isHidden 是否隐藏
|
||||
*/
|
||||
onHideToggle?: (isHidden: boolean) => void;
|
||||
|
||||
/**
|
||||
* 填充状态切换回调
|
||||
* @param isFilled 是否填充
|
||||
*/
|
||||
onFillToggle?: (isFilled: boolean) => void;
|
||||
|
||||
/**
|
||||
* 反向按钮回调
|
||||
*/
|
||||
|
||||
1
src/iflow-engine-base.css
Normal file
1
src/iflow-engine-base.css
Normal file
File diff suppressed because one or more lines are too long
@@ -135,7 +135,8 @@ export const enUS: TranslationDictionary = {
|
||||
actions: {
|
||||
hide: 'Hide',
|
||||
reverse: 'Reverse',
|
||||
reset: 'Reset'
|
||||
reset: 'Reset',
|
||||
fill: 'Fill'
|
||||
}
|
||||
},
|
||||
sectionAxis: {
|
||||
@@ -146,7 +147,8 @@ export const enUS: TranslationDictionary = {
|
||||
reset: 'Reset',
|
||||
axisX: 'X',
|
||||
axisY: 'Y',
|
||||
axisZ: 'Z'
|
||||
axisZ: 'Z',
|
||||
fill: 'Fill'
|
||||
}
|
||||
},
|
||||
sectionBox: {
|
||||
@@ -155,7 +157,8 @@ export const enUS: TranslationDictionary = {
|
||||
hide: 'Hide',
|
||||
reverse: 'Reverse',
|
||||
fitToModel: 'Fit',
|
||||
reset: 'Reset'
|
||||
reset: 'Reset',
|
||||
fill: 'Fill'
|
||||
},
|
||||
axes: {
|
||||
x: 'X',
|
||||
|
||||
@@ -147,6 +147,7 @@ export interface TranslationDictionary {
|
||||
hide: string;
|
||||
reverse: string;
|
||||
reset: string;
|
||||
fill: string;
|
||||
};
|
||||
};
|
||||
sectionAxis: {
|
||||
@@ -158,6 +159,7 @@ export interface TranslationDictionary {
|
||||
axisX: string;
|
||||
axisY: string;
|
||||
axisZ: string;
|
||||
fill: string;
|
||||
};
|
||||
};
|
||||
sectionBox: {
|
||||
@@ -167,6 +169,7 @@ export interface TranslationDictionary {
|
||||
reverse: string;
|
||||
fitToModel: string;
|
||||
reset: string;
|
||||
fill: string;
|
||||
};
|
||||
axes: {
|
||||
x: string;
|
||||
|
||||
@@ -135,7 +135,8 @@ export const zhCN: TranslationDictionary = {
|
||||
actions: {
|
||||
hide: '隐藏',
|
||||
reverse: '反向',
|
||||
reset: '重置'
|
||||
reset: '重置',
|
||||
fill: '填充'
|
||||
}
|
||||
},
|
||||
sectionAxis: {
|
||||
@@ -146,7 +147,8 @@ export const zhCN: TranslationDictionary = {
|
||||
reset: '重置',
|
||||
axisX: 'X',
|
||||
axisY: 'Y',
|
||||
axisZ: 'Z'
|
||||
axisZ: 'Z',
|
||||
fill: '填充'
|
||||
}
|
||||
},
|
||||
sectionBox: {
|
||||
@@ -155,7 +157,8 @@ export const zhCN: TranslationDictionary = {
|
||||
hide: '隐藏',
|
||||
reverse: '反向',
|
||||
fitToModel: '适应',
|
||||
reset: '重置'
|
||||
reset: '重置',
|
||||
fill: '填充'
|
||||
},
|
||||
axes: {
|
||||
x: 'X',
|
||||
|
||||
@@ -135,7 +135,8 @@ export const zhTW: TranslationDictionary = {
|
||||
actions: {
|
||||
hide: '隱藏',
|
||||
reverse: '反向',
|
||||
reset: '重設'
|
||||
reset: '重設',
|
||||
fill: '填充'
|
||||
}
|
||||
},
|
||||
sectionAxis: {
|
||||
@@ -146,7 +147,8 @@ export const zhTW: TranslationDictionary = {
|
||||
reset: '重設',
|
||||
axisX: 'X',
|
||||
axisY: 'Y',
|
||||
axisZ: 'Z'
|
||||
axisZ: 'Z',
|
||||
fill: '填充'
|
||||
}
|
||||
},
|
||||
sectionBox: {
|
||||
@@ -155,7 +157,8 @@ export const zhTW: TranslationDictionary = {
|
||||
hide: '隱藏',
|
||||
reverse: '反向',
|
||||
fitToModel: '適應',
|
||||
reset: '重設'
|
||||
reset: '重設',
|
||||
fill: '填充'
|
||||
},
|
||||
axes: {
|
||||
x: 'X',
|
||||
|
||||
@@ -12,7 +12,6 @@ import { createWalkRadialButton } from '../components/radial-toolbar/buttons/wal
|
||||
|
||||
export interface RadialToolbarManagerOptions {
|
||||
items?: RadialMenuItem[];
|
||||
itemsPerRing?: number;
|
||||
}
|
||||
|
||||
export class RadialToolbarManager extends BaseManager {
|
||||
@@ -24,7 +23,6 @@ export class RadialToolbarManager extends BaseManager {
|
||||
this.toolbar = new RadialToolbar({
|
||||
container,
|
||||
items: options?.items ?? this.createDefaultItems(),
|
||||
itemsPerRing: options?.itemsPerRing ?? 4,
|
||||
mainButtonIcon: getIcon('主视角'),
|
||||
mainButtonLabel: 'toolbar.home',
|
||||
onMainButtonClick: () => {
|
||||
@@ -61,6 +59,10 @@ export class RadialToolbarManager extends BaseManager {
|
||||
this.toolbar.setItemActive('walk', dock.isOpen('walk'));
|
||||
}
|
||||
|
||||
public addItem(item: RadialMenuItem): void {
|
||||
this.toolbar?.addItem(item);
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
if (this.unsubscribeDockState) {
|
||||
this.unsubscribeDockState();
|
||||
|
||||
@@ -61,6 +61,7 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
this.panel = new SectionAxisPanel({
|
||||
defaultAxis: 'x',
|
||||
defaultHidden: false,
|
||||
defaultFill: this.engineComponent?.getFillCutFace() ?? false,
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionAxisDialogManager] 隐藏切换:', isHidden);
|
||||
if (isHidden) {
|
||||
@@ -69,6 +70,9 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onFillToggle: (isFilled) => {
|
||||
this.engineComponent?.setFillCutFace(isFilled);
|
||||
},
|
||||
onReverse: () => {
|
||||
this.engineComponent?.reverseSection();
|
||||
},
|
||||
|
||||
@@ -53,6 +53,7 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
this.panel = new SectionBoxPanel({
|
||||
defaultHidden: false,
|
||||
defaultReversed: false,
|
||||
defaultFill: this.engineComponent?.getFillCutFace() ?? false,
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionBoxDialogManager] 隐藏切换:', isHidden);
|
||||
if (isHidden) {
|
||||
@@ -61,6 +62,9 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onFillToggle: (isFilled) => {
|
||||
this.engineComponent?.setFillCutFace(isFilled);
|
||||
},
|
||||
onFitToModel: () => {
|
||||
// 对接底层 scaleBox():缩放剖切盒到场景整体包围盒
|
||||
this.engineComponent?.scaleSectionBox();
|
||||
@@ -70,7 +74,7 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
// UI 侧会自行将滑块强制恢复到 0-100,并将隐藏/反向按钮恢复为关闭状态。
|
||||
this.engineComponent?.deactivateSection();
|
||||
this.engineComponent?.activeSection('box');
|
||||
// 确保剖切可见(避免上一次处于隐藏状态导致“看起来没重置”)
|
||||
// 确保剖切可见(避免上一次处于隐藏状态导致"看起来没重置")
|
||||
this.engineComponent?.recoverSection();
|
||||
},
|
||||
onRangeChange: (range) => {
|
||||
|
||||
@@ -91,6 +91,10 @@ export class SectionDockManager extends BaseManager {
|
||||
},
|
||||
onFitToModel: () => {
|
||||
this.engineComponent?.scaleSectionBox();
|
||||
},
|
||||
defaultFill: this.engineComponent?.getFillCutFace() ?? false,
|
||||
onFillToggle: (isFilled) => {
|
||||
this.engineComponent?.setFillCutFace(isFilled);
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
|
||||
@@ -53,6 +53,7 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new SectionPlanePanel({
|
||||
defaultHidden: false,
|
||||
defaultFill: this.engineComponent?.getFillCutFace() ?? false,
|
||||
onHideToggle: (isHidden) => {
|
||||
console.log('[SectionPlaneDialogManager] 隐藏切换:', isHidden);
|
||||
if (isHidden) {
|
||||
@@ -61,6 +62,9 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onFillToggle: (isFilled) => {
|
||||
this.engineComponent?.setFillCutFace(isFilled);
|
||||
},
|
||||
onReverse: () => {
|
||||
this.engineComponent?.reverseSection();
|
||||
},
|
||||
|
||||
@@ -10,6 +10,10 @@ export interface EngineEvents {
|
||||
'engine:model-loading-completed': {};
|
||||
'engine:object-clicked': { objectId: string; position: { x: number; y: number; z: number } };
|
||||
|
||||
'encoding:start': { data?: any };
|
||||
'encoding:complete': { data?: any };
|
||||
'encoding:error': { data?: any };
|
||||
|
||||
// 树组件事件
|
||||
'ui:tree-node-check': { id: string; checked: boolean; node: any };
|
||||
'ui:tree-node-select': { id: string; selected: boolean; node: any };
|
||||
|
||||
@@ -27,6 +27,7 @@ const ICONS: Record<string, string> = {
|
||||
适应到模型: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><g transform="translate(-84.667 -84.667)"><path d="M87.667,97V87.667H97V85.333H86.733a1.4,1.4,0,0,0-1.4,1.4V97Zm0,23.333H85.333V130.6a1.4,1.4,0,0,0,1.4,1.4H97v-2.333H87.667v-9.333Zm32.667,9.333V132H130.6a1.4,1.4,0,0,0,1.4-1.4V120.333h-2.333v9.333h-9.333ZM129.667,97H132V86.733a1.4,1.4,0,0,0-1.4-1.4H120.333v2.333h9.333Z"/><path d="M270.857,243.5l11.3-6.652V223.387l-11.3,6.361V243.5Zm-1.8,0v-13.75l-11.3-6.361v13.456l11.3,6.652Zm-10.278-21.621,11.177,6.284,11.177-6.284L269.958,215.3l-11.177,6.573Zm11.622-8.426,13.1,7.709a.919.919,0,0,1,.448.793v15.419a.926.926,0,0,1-.448.793l-13.1,7.709a.887.887,0,0,1-.9,0l-13.1-7.709a.905.905,0,0,1-.448-.793V221.954a.919.919,0,0,1,.448-.793l13.1-7.709A.887.887,0,0,1,270.4,213.452Z" transform="translate(-161.292 -120.997)"/></g></svg>',
|
||||
隐藏: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path d="M61.274,76.178l0-.007-.007,0a32.458,32.458,0,0,0-7.014-9.981L51.53,68.91a28.382,28.382,0,0,1,6.124,8.635c-4.472,9.25-10.809,13.636-19.472,13.636A20.615,20.615,0,0,1,30.6,89.838l-2.937,2.933a23.916,23.916,0,0,0,10.514,2.277q15.472,0,23.088-16.116a3.229,3.229,0,0,0,0-2.755ZM57.839,58.351l-2.274-2.277a.429.429,0,0,0-.608,0l-6.265,6.262a23.775,23.775,0,0,0-10.51-2.281q-15.472,0-23.088,16.116v.007a3.236,3.236,0,0,0,0,2.765,32.617,32.617,0,0,0,7.014,9.985L16.7,94.32a.429.429,0,0,0,0,.608l2.274,2.277a.429.429,0,0,0,.608,0L57.839,58.949a.426.426,0,0,0,0-.6ZM32.113,78.915a6.011,6.011,0,0,1,7.22-7.22l-7.223,7.22Zm9.9-9.9A9.452,9.452,0,0,0,29.43,81.6l-4.6,4.6a28.424,28.424,0,0,1-6.124-8.635c4.475-9.25,10.816-13.636,19.472-13.636a20.615,20.615,0,0,1,7.577,1.343Zm-4.046,14.55a5.752,5.752,0,0,1-1.01-.086l-2.744,2.741A9.446,9.446,0,0,0,46.638,73.794L43.9,76.538a6.018,6.018,0,0,1-5.932,7.024Z" transform="translate(-14.182 -52.64)"/></svg>',
|
||||
重置: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path d="M32.936,32.658a15.914,15.914,0,0,1-9.6,3.064c-.451-.006-.9-.042-1.342-.077-.182-.018-.357-.047-.539-.077-.352-.047-.7-.089-1.037-.164-.211-.036-.422-.1-.621-.148-.334-.077-.668-.154-.99-.254-.153-.059-.3-.11-.457-.177-.381-.124-.755-.26-1.113-.414-.082-.036-.162-.071-.24-.1-.422-.2-.844-.408-1.248-.627-.017-.012-.035-.018-.054-.03a16.485,16.485,0,0,1-3.732-2.9c-.017-.018-.035-.042-.054-.059-.34-.355-.668-.721-.979-1.118-.065-.083-.123-.16-.194-.254A17.286,17.286,0,0,1,7.149,18.751h3.98L6.318,9.076,0,18.874H3.942a20.809,20.809,0,0,0,3.5,11.57.871.871,0,0,0,.075.14c.225.34.486.648.724.961.1.113.177.232.274.362.354.443.746.869,1.133,1.285.043.043.075.076.108.113a19.778,19.778,0,0,0,4.436,3.446c.043.027.08.043.128.076.467.259.949.5,1.432.719.124.055.242.113.359.164.419.184.847.335,1.277.493.2.076.4.14.611.21.378.113.762.21,1.153.3.257.065.509.129.772.184a2.252,2.252,0,0,0,.316.076c.37.07.735.1,1.1.151.134.027.27.049.4.065.66.065,1.314.11,1.974.11,4.006,0,8.852-1.3,11.85-4.177a1.908,1.908,0,0,0,.333-2.484,2.228,2.228,0,0,0-2.952.023ZM44.053,20.424a20.728,20.728,0,0,0-3.47-11.537c-.032-.055-.054-.113-.08-.164-.284-.405-.574-.778-.869-1.161a1.289,1.289,0,0,1-.1-.14A19.86,19.86,0,0,0,32.168,1.7c-.086-.032-.155-.076-.242-.11-.456-.195-.922-.362-1.394-.53C30.37,1,30.2.937,30.033.883,29.62.754,29.212.651,28.792.548c-.231-.055-.467-.113-.7-.164-.113-.022-.22-.055-.338-.081-.311-.055-.617-.081-.933-.124C26.6.151,26.392.119,26.18.1,25.655.042,25.135.021,24.616.016c-.1,0-.188-.016-.284-.016-.016,0-.032.005-.049.005A19.124,19.124,0,0,0,13.02,3.695a1.682,1.682,0,0,0-.466,2.515,1.777,1.777,0,0,0,2.309.3A15.251,15.251,0,0,1,24.374,3.45c.486.006.971.03,1.441.077.147.012.287.036.432.059a11.3,11.3,0,0,1,1.16.189c.162.03.334.077.493.11.381.089.744.178,1.107.3a2.577,2.577,0,0,1,.34.124c.422.142.832.29,1.23.467.047.012.082.047.123.059a16.492,16.492,0,0,1,6.182,4.8.367.367,0,0,0,.024.036A17.308,17.308,0,0,1,40.629,20.43H36.645l5.048,9.787L48,20.424Zm0,0" transform="translate(0 4.349)"/></svg>',
|
||||
填充: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path d="M24 6L10 22h10v18h8V22h10L24 6z" opacity="0.35"/><path d="M8 24h32v4H8z"/></svg>',
|
||||
|
||||
// ========== 测量相关图标 (32x32) ==========
|
||||
标高: '<svg width="32" height="32" viewBox="0 0 32 32"><path fill="currentColor" d="M84.131,193.119a1.056,1.056,0,0,1,1.116.982v7.857a1.056,1.056,0,0,1-1.116.982H54.367a1.056,1.056,0,0,1-1.116-.982V194.1a1.056,1.056,0,0,1,1.116-.982Zm-1.116,1.964H55.483v5.893H83.015Zm1.116-13.749a1.064,1.064,0,0,1,1.114.935,1.032,1.032,0,0,1-1.007,1.025l-.107,0H71.2l-7.858,6.914a1.227,1.227,0,0,1-1.578,0l-8.185-7.2-.018-.016-.032-.031.049.047a1.107,1.107,0,0,1-.092-.092l-.011-.014a.869.869,0,0,1-.182-.857l0-.008L53.31,182l.012-.029.02-.045.019-.035a1.1,1.1,0,0,1,.891-.552h.007q.053,0,.107,0ZM68.043,183.3H57.06l5.492,4.831Z" transform="translate(-53.247 -176.136)"/></svg>',
|
||||
|
||||
Reference in New Issue
Block a user