id-scanner-lib
Version:
Browser-based ID card, QR code, and face recognition scanner with liveness detection
268 lines (234 loc) • 6.33 kB
text/typescript
/**
* @file 配置管理器
* @description 提供全局配置管理功能
* @module core/config
*/
/**
* 配置管理器
* 负责存储和管理应用程序的配置
*/
export class ConfigManager {
/** 单例实例 */
private static instance: ConfigManager;
/** 配置存储 */
private config: Record<string, any> = {};
/** 配置变更回调 */
private changeCallbacks: Map<string, Array<(value: any, oldValue: any) => void>> = new Map();
/**
* 私有构造函数
*/
private constructor() {
// 设置默认配置
this.config = {
debug: false,
logLevel: 'info',
camera: {
resolution: {
width: 1280,
height: 720
},
frameRate: 30,
facingMode: 'environment'
},
performance: {
useCache: true
}
};
}
/**
* 获取单例实例
*/
public static getInstance(): ConfigManager {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
/**
* 获取配置值
* @param key 配置键,支持点号分隔的路径
* @param defaultValue 默认值
*/
get<T = any>(key: string, defaultValue?: T): T {
const value = this.getNestedValue(this.config, key);
return (value !== undefined) ? value : (defaultValue as T);
}
/**
* 设置配置值
* @param key 配置键,支持点号分隔的路径
* @param value 配置值
*/
set<T = any>(key: string, value: T): void {
const oldValue = this.get(key);
// 如果值相同,不做任何事
if (oldValue === value) {
return;
}
this.setNestedValue(this.config, key, value);
// 触发变更回调
this.triggerChangeCallbacks(key, value, oldValue);
}
/**
* 批量更新配置
* @param config 配置对象
*/
updateConfig(config: Record<string, any>): void {
Object.entries(config).forEach(([key, value]) => {
this.set(key, value);
});
}
/**
* 重置为默认配置
*/
reset(): void {
const oldConfig = { ...this.config };
// 重新创建默认配置
this.config = {
debug: false,
logLevel: 'info',
camera: {
resolution: {
width: 1280,
height: 720
},
frameRate: 30,
facingMode: 'environment'
},
performance: {
useCache: true
}
};
// 触发所有回调
Object.keys(oldConfig).forEach(key => {
this.triggerChangeCallbacks(key, this.get(key), oldConfig[key]);
});
}
/**
* 注册配置变更回调
* @param key 配置键
* @param callback 回调函数
*/
onConfigChange<T = any>(key: string, callback: (value: T, oldValue: T) => void): void {
if (!this.changeCallbacks.has(key)) {
this.changeCallbacks.set(key, []);
}
this.changeCallbacks.get(key)!.push(callback);
}
/**
* 移除配置变更回调
* @param key 配置键
* @param callback 特定回调函数,如不提供则移除所有
*/
offConfigChange<T = any>(key: string, callback?: (value: T, oldValue: T) => void): void {
if (!this.changeCallbacks.has(key)) {
return;
}
if (callback) {
// 移除特定回调
const callbacks = this.changeCallbacks.get(key)!;
const index = callbacks.indexOf(callback as any);
if (index !== -1) {
callbacks.splice(index, 1);
}
// 如果没有回调,删除键
if (callbacks.length === 0) {
this.changeCallbacks.delete(key);
}
} else {
// 移除所有回调
this.changeCallbacks.delete(key);
}
}
/**
* 获取嵌套值
* @param obj 对象
* @param path 路径
*/
private getNestedValue(obj: Record<string, any>, path: string): any {
// 处理根路径
if (!path) {
return obj;
}
// 处理嵌套路径
const parts = path.split('.');
let current = obj;
for (const part of parts) {
if (current === undefined || current === null) {
return undefined;
}
current = current[part];
}
return current;
}
/**
* 设置嵌套值
* @param obj 对象
* @param path 路径
* @param value 值
*/
private setNestedValue(obj: Record<string, any>, path: string, value: any): void {
// 处理根路径
if (!path) {
return;
}
// 处理嵌套路径
const parts = path.split('.');
let current = obj;
// 遍历路径,直到倒数第二部分
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
// 如果不存在,创建新对象
if (current[part] === undefined || current[part] === null || typeof current[part] !== 'object') {
current[part] = {};
}
current = current[part];
}
// 设置最终值
current[parts[parts.length - 1]] = value;
}
/**
* 触发变更回调
* @param key 配置键
* @param value 新值
* @param oldValue 旧值
*/
private triggerChangeCallbacks(key: string, value: any, oldValue: any): void {
// 触发特定键的回调
if (this.changeCallbacks.has(key)) {
const callbacks = this.changeCallbacks.get(key)!;
callbacks.forEach(callback => {
try {
callback(value, oldValue);
} catch (error) {
console.error(`Error in config change callback for key ${key}:`, error);
}
});
}
// 触发父路径的回调
const parts = key.split('.');
while (parts.length > 1) {
parts.pop();
const parentKey = parts.join('.');
if (this.changeCallbacks.has(parentKey)) {
const parentValue = this.get(parentKey);
this.changeCallbacks.get(parentKey)!.forEach(callback => {
try {
callback(parentValue, parentValue);
} catch (error) {
console.error(`Error in config change callback for parent key ${parentKey}:`, error);
}
});
}
}
}
}
/**
* 模块配置接口
* 定义所有模块共享的基础配置属性
*/
export interface ModuleConfig {
/** 是否启用该模块 */
enabled: boolean;
/** 其他模块特定配置 */
[key: string]: any;
}