设置界面

This commit is contained in:
YHH
2025-10-16 13:07:19 +08:00
parent 6bcfd48a2f
commit 1ec7892338
11 changed files with 1141 additions and 6 deletions

View File

@@ -0,0 +1,192 @@
import { Injectable, IService } from '@esengine/ecs-framework';
export type SettingType = 'string' | 'number' | 'boolean' | 'select' | 'color' | 'range';
export interface SettingOption {
label: string;
value: any;
}
export interface SettingValidator {
validate: (value: any) => boolean;
errorMessage: string;
}
export interface SettingDescriptor {
key: string;
label: string;
type: SettingType;
defaultValue: any;
description?: string;
placeholder?: string;
options?: SettingOption[];
validator?: SettingValidator;
min?: number;
max?: number;
step?: number;
}
export interface SettingSection {
id: string;
title: string;
description?: string;
icon?: string;
settings: SettingDescriptor[];
}
export interface SettingCategory {
id: string;
title: string;
description?: string;
sections: SettingSection[];
}
@Injectable()
export class SettingsRegistry implements IService {
private categories: Map<string, SettingCategory> = new Map();
public dispose(): void {
this.categories.clear();
}
public registerCategory(category: SettingCategory): void {
if (this.categories.has(category.id)) {
console.warn(`[SettingsRegistry] Category ${category.id} already registered, overwriting`);
}
this.categories.set(category.id, category);
}
public registerSection(categoryId: string, section: SettingSection): void {
let category = this.categories.get(categoryId);
if (!category) {
category = {
id: categoryId,
title: categoryId,
sections: []
};
this.categories.set(categoryId, category);
}
const existingIndex = category.sections.findIndex(s => s.id === section.id);
if (existingIndex >= 0) {
category.sections[existingIndex] = section;
console.warn(`[SettingsRegistry] Section ${section.id} in category ${categoryId} already exists, overwriting`);
} else {
category.sections.push(section);
}
}
public registerSetting(categoryId: string, sectionId: string, setting: SettingDescriptor): void {
let category = this.categories.get(categoryId);
if (!category) {
category = {
id: categoryId,
title: categoryId,
sections: []
};
this.categories.set(categoryId, category);
}
let section = category.sections.find(s => s.id === sectionId);
if (!section) {
section = {
id: sectionId,
title: sectionId,
settings: []
};
category.sections.push(section);
}
const existingIndex = section.settings.findIndex(s => s.key === setting.key);
if (existingIndex >= 0) {
section.settings[existingIndex] = setting;
console.warn(`[SettingsRegistry] Setting ${setting.key} in section ${sectionId} already exists, overwriting`);
} else {
section.settings.push(setting);
}
}
public unregisterCategory(categoryId: string): void {
this.categories.delete(categoryId);
}
public unregisterSection(categoryId: string, sectionId: string): void {
const category = this.categories.get(categoryId);
if (category) {
category.sections = category.sections.filter(s => s.id !== sectionId);
if (category.sections.length === 0) {
this.categories.delete(categoryId);
}
}
}
public getCategory(categoryId: string): SettingCategory | undefined {
return this.categories.get(categoryId);
}
public getAllCategories(): SettingCategory[] {
return Array.from(this.categories.values());
}
public getSetting(categoryId: string, sectionId: string, key: string): SettingDescriptor | undefined {
const category = this.categories.get(categoryId);
if (!category) return undefined;
const section = category.sections.find(s => s.id === sectionId);
if (!section) return undefined;
return section.settings.find(s => s.key === key);
}
public getAllSettings(): Map<string, SettingDescriptor> {
const allSettings = new Map<string, SettingDescriptor>();
for (const category of this.categories.values()) {
for (const section of category.sections) {
for (const setting of section.settings) {
allSettings.set(setting.key, setting);
}
}
}
return allSettings;
}
public validateSetting(setting: SettingDescriptor, value: any): boolean {
if (setting.validator) {
return setting.validator.validate(value);
}
switch (setting.type) {
case 'number':
if (typeof value !== 'number') return false;
if (setting.min !== undefined && value < setting.min) return false;
if (setting.max !== undefined && value > setting.max) return false;
return true;
case 'boolean':
return typeof value === 'boolean';
case 'string':
return typeof value === 'string';
case 'select':
if (!setting.options) return false;
return setting.options.some(opt => opt.value === value);
case 'range':
if (typeof value !== 'number') return false;
if (setting.min !== undefined && value < setting.min) return false;
if (setting.max !== undefined && value > setting.max) return false;
return true;
case 'color':
return typeof value === 'string' && /^#[0-9A-Fa-f]{6}$/.test(value);
default:
return true;
}
}
}

View File

@@ -18,5 +18,6 @@ export * from './Services/ProjectService';
export * from './Services/ComponentDiscoveryService';
export * from './Services/ComponentLoaderService';
export * from './Services/LogService';
export * from './Services/SettingsRegistry';
export * from './Types/UITypes';