refactor: sync managers and section box actions

Wire section box scale/reverse/reset to clipping APIs and sync demo artifacts.
This commit is contained in:
yuding
2026-02-04 18:20:30 +08:00
parent b12940f49c
commit 191c571f40
64 changed files with 10569 additions and 7065 deletions

View File

@@ -4,7 +4,7 @@ import { IBimComponent } from '../../types/component';
import { localeManager, t } from '../../services/locale';
import { themeManager } from '../../services/theme';
import type { MeasureConfig, MeasurePanelOptions, MeasurePrecision, MeasureResult, MeasureUnit } from './types';
import { MEASURE_TYPES, MEASURE_MODES_ORDERED, type MeasureMode } from '../../types/measure';
import { MEASURE_TYPES, MEASURE_MODES_ORDERED, getValueType, type MeasureMode, type MeasureValueType } from '../../types/measure';
/**
* 测量面板组件(只做 UI不实现真实测量
@@ -55,6 +55,7 @@ export class MeasurePanel implements IBimComponent {
private toolButtons: Map<MeasureMode, HTMLButtonElement> = new Map();
private toggleBtn!: HTMLButtonElement;
private toggleTextEl!: HTMLElement;
private mainValueRowEl!: HTMLElement;
private mainValueValueEl!: HTMLElement;
private mainValueLabelEl!: HTMLElement;
private mainNumberEl!: HTMLElement;
@@ -428,6 +429,7 @@ export class MeasurePanel implements IBimComponent {
// 主结果值(随模式变化)
const mainValueRow = document.createElement('div');
mainValueRow.className = 'bim-measure-row';
this.mainValueRowEl = mainValueRow;
const mainValueLabel = document.createElement('span');
mainValueLabel.className = 'label';
this.mainValueLabelEl = mainValueLabel;
@@ -764,7 +766,7 @@ export class MeasurePanel implements IBimComponent {
private renderResult(): void {
// 1) 根据模式决定结果区显示规则
// 你给的规则:
// - 距离:显示数值 + xyz
// - 距离:显示数值
// - 最小距离:只显示数值
// - 角度:--°
// - 标高:--m固定 m
@@ -773,14 +775,10 @@ export class MeasurePanel implements IBimComponent {
// - 坡度:--%
// - 空间体积:--mm³单位随设置变动即 unit³
this.mainValueLabelEl.style.display = '';
this.mainValueLabelEl.textContent = t(this.getModeValueLabelI18nKey(this.activeMode));
const parts = this.formatMainValueParts(this.activeMode, this.result);
this.mainNumberEl.textContent = parts.numberText;
this.mainUnitEl.textContent = parts.unitText;
const showXyz = this.activeMode === 'distance' || this.activeMode === 'point';
if (showXyz) {
const isPointMode = this.activeMode === 'point';
if (isPointMode) {
this.mainValueRowEl.style.display = 'none';
this.xyzBoxEl.style.display = '';
const xyz = this.result?.xyz;
if (!xyz) {
@@ -789,12 +787,24 @@ export class MeasurePanel implements IBimComponent {
this.xyzZEl.textContent = '--';
return;
}
this.xyzXEl.textContent = this.formatNumberWithPrecision(xyz.x, this.config.precision);
this.xyzYEl.textContent = this.formatNumberWithPrecision(xyz.y, this.config.precision);
this.xyzZEl.textContent = this.formatNumberWithPrecision(xyz.z, this.config.precision);
this.xyzXEl.textContent = this.convertValue('length', xyz.x) + ' ' + this.getUnitText('length');
this.xyzYEl.textContent = this.convertValue('length', xyz.y) + ' ' + this.getUnitText('length');
this.xyzZEl.textContent = this.convertValue('length', xyz.z) + ' ' + this.getUnitText('length');
return;
}
this.mainValueRowEl.style.display = '';
this.mainValueLabelEl.textContent = t(this.getModeValueLabelI18nKey(this.activeMode));
const value = this.result ? (this.result as any)[MEASURE_TYPES[this.activeMode].callBackType] : undefined;
if (this.activeMode === 'slope'||this.activeMode === 'angle') {
this.mainNumberEl.textContent = value ?? '--';
this.mainUnitEl.textContent = '';
} else {
const valueType = getValueType(this.activeMode);
this.mainNumberEl.textContent = this.convertValue(valueType, value);
this.mainUnitEl.textContent = this.getUnitText(valueType);
}
this.xyzBoxEl.style.display = 'none';
}
@@ -809,99 +819,69 @@ export class MeasurePanel implements IBimComponent {
return `measure.labels.value.${mode}`;
}
// 注意:旧的 formatMainValue/formatWithFixedUnit 已被 formatMainValueParts 替代,
// 以支持“数值与单位分色显示”和“无数据时仍展示单位”。
/**
* 统一的数值转换方法
* @param type 测量值类型length(长度)、area(面积)、angle(角度)、percent(百分比)、point(坐标)
* @param value 原始数值(单位:长度为米,面积为平方米)
* @returns 转换后的格式化字符串,无效值返回 '--'
*/
private convertValue(type: MeasureValueType, value: number | undefined | null): string {
if (value === null || value === undefined || Number.isNaN(value)) {
return '--';
}
const unit = this.config.unit;
const precision = this.config.precision;
let converted: number;
switch (type) {
case 'length':
switch (unit) {
case 'mm': converted = value * 1000; break;
case 'cm': converted = value * 100; break;
case 'km': converted = value / 1000; break;
default: converted = value;
}
break;
case 'area':
switch (unit) {
case 'mm': converted = value * 1000 * 1000; break;
case 'cm': converted = value * 100 * 100; break;
case 'km': converted = value / 1000 / 1000; break;
default: converted = value;
}
break;
case 'angle':
case 'percent':
case 'point':
default:
converted = value;
}
return converted.toFixed(precision);
}
/**
* 基础数字格式化(按精度显示)
* 获取单位文本
* @param type 测量值类型
* @returns 对应的单位文本(如 mm、m²、°
*/
private formatNumberWithPrecision(value: number, precision: MeasurePrecision): string {
// 你要求精度可选0 / 0.0 / 0.00 / 0.000,因此这里不做 trim严格按 toFixed 输出
return value.toFixed(precision);
}
// 注意:旧的 formatLengthWithConfig 已被 formatLengthParts 替代。
private getUnitI18nKey(unit: MeasureUnit): string {
return `measure.units.${unit}`;
}
private formatMainValueParts(mode: MeasureMode, result: MeasureResult | null): { numberText: string; unitText: string } {
if (!result) {
return this.getEmptyValuePartsByMode(mode);
}
const config = MEASURE_TYPES[mode];
const value = (result as any)[config.resultField];
switch (config.valueType) {
case 'length':
case 'area':
return this.formatMeasureValue(value, config.valueType);
case 'angle':
return this.formatFixedUnitParts(value, t('measure.units.deg'));
case 'percent':
return this.formatFixedUnitParts(value, t('measure.units.percent'));
case 'point':
return { numberText: '--', unitText: '' };
default:
return { numberText: '--', unitText: '' };
}
}
private getEmptyValuePartsByMode(mode: MeasureMode): { numberText: string; unitText: string } {
const config = MEASURE_TYPES[mode];
switch (config.valueType) {
case 'length':
return { numberText: '--', unitText: t(this.getUnitI18nKey(this.config.unit)) };
case 'area':
return { numberText: '--', unitText: `${this.config.unit}²` };
case 'angle':
return { numberText: '--', unitText: t('measure.units.deg') };
case 'percent':
return { numberText: '--', unitText: t('measure.units.percent') };
case 'point':
return { numberText: '--', unitText: '' };
default:
return { numberText: '--', unitText: '' };
}
}
private formatFixedUnitParts(value: number | undefined, unitText: string): { numberText: string; unitText: string } {
if (value === null || value === undefined || Number.isNaN(value)) {
return { numberText: '--', unitText };
}
return { numberText: this.formatNumberWithPrecision(value, this.config.precision), unitText };
}
private formatMeasureValue(value: number | undefined, type: 'length' | 'area'): { numberText: string; unitText: string } {
private getUnitText(type: MeasureValueType): string {
const unit = this.config.unit;
const unitText = type === 'area' ? `${unit}²` : t(this.getUnitI18nKey(unit));
if (value === null || value === undefined || Number.isNaN(value)) {
return { numberText: '--', unitText };
switch (type) {
case 'length':
case 'point':
return unit;
case 'area':
return `${unit}²`;
case 'angle':
case 'percent':
return '°';
default:
return '';
}
let converted: number;
if (type === 'length') {
switch (unit) {
case 'mm': converted = value * 1000; break;
case 'cm': converted = value * 100; break;
case 'km': converted = value / 1000; break;
default: converted = value;
}
} else {
switch (unit) {
case 'mm': converted = value * 1000 * 1000; break;
case 'cm': converted = value * 100 * 100; break;
case 'km': converted = value / 1000 / 1000; break;
default: converted = value;
}
}
return { numberText: converted.toFixed(this.config.precision), unitText };
}
}