UNPKG

@truenewx/tnxcore

Version:

互联网技术解决方案:JavaScript核心扩展支持

181 lines (165 loc) 5.68 kB
/** * 枚举类型 * 注意1:子类声明后务必确保init()被调用一次,方可正常使用 * 注意2:由于枚举常量作为对象可能被代理,所以枚举常量的比较不要使用===操作符,而应该用equals()方法 */ import * as meta from '../api/meta.ts'; const DEFAULT_KEYS = ['ordinal', 'name', 'caption', 'scenes']; export default abstract class Enum { /** * 枚举类型名。为避免压缩混淆后类型名丢失而设立,由子类覆写 */ static type = 'Enum'; ordinal = 0; name = ''; caption = ''; scenes: Record<string, string> = {}; /** * @param caption 显示名称 * @param {Object|Array} [scenes] 场景集 */ protected constructor(caption: string, scenes?: string[] | Record<string, string>) { this.caption = caption; if (scenes) { if (Array.isArray(scenes)) { for (let scene of scenes) { this.scenes[scene] = this.caption; } } else if (typeof scenes === 'object') { Object.assign(this.scenes, scenes); } } } static init(metaApi = meta): void { let sceneNameSet = new Set<string>(); this.forEach((constant: Enum, ordinal: number, name: string) => { constant.ordinal = ordinal; constant.name = name; Object.keys(constant.scenes).forEach(sceneName => sceneNameSet.add(sceneName)); }); metaApi.addEnumType({ name: this.type, items: this.getItems(), }); for (let sceneName of sceneNameSet) { metaApi.addEnumType({ name: this.type, subname: sceneName, items: this.getItems(sceneName), }); } } static forEach(consumer: (constant: Enum, ordinal: number, name: string) => void): void { let names = Object.keys(this); let ordinal = 0; for (let name of names) { const constant = this[name]; if (constant.constructor === this) { consumer(constant, ordinal++, name); } } } /** * @param {?String} [sceneName] 场景名称 */ static values(sceneName?: string) { let values: Enum[] = []; this.forEach((constant: Enum) => { if (!sceneName || constant.scenes[sceneName]) { values.push(constant); } }); return values; } static valueOf(name: string): Enum | undefined { let result: Enum | undefined = undefined; this.forEach((constant: Enum) => { if (constant.name === name) { result = constant; } }); return result; } /** * 校验指定常量合法性 * @param {String|Enum} constant 常量或常量名 * @param {?String} [sceneName] 场景名称 * @return {Enum} 常量 */ static validate(constant: string | Enum, sceneName?: string): Enum | undefined { let result: Enum | undefined = undefined; if (constant) { if (typeof constant === 'string') { result = this.valueOf(constant); } else if (constant.constructor === this) { result = constant as Enum; } else if (typeof constant.name === 'string') { result = this.validate(constant.name); } if (result && sceneName) { if (!result.scenes[sceneName]) { result = undefined; } } } if (!result) { let message = `无效的 ${this.name} 常量: `; if (constant && typeof constant['name'] === 'string') { message += constant['name']; } else { message += constant; } if (sceneName) { message += `,于场景 ${sceneName} 中`; } throw new Error(message); } return result; } /** * @param {?String} [sceneName] 场景名称 */ static getItems(sceneName?: string): meta.EnumItem[] { let items = []; this.forEach((constant: Enum) => { let caption = constant.caption; if (sceneName) { let sceneCaption = constant.scenes[sceneName]; if (sceneCaption) { caption = sceneCaption; } else { return; } } let item: meta.EnumItem = { key: constant.name, caption: caption, }; Object.keys(constant).forEach(key => { if (!DEFAULT_KEYS.includes(key)) { item[key] = constant[key]; } }); items.push(item); }); return items; } toString(): string { return this.name; } equals(other: Enum): boolean { return !!(other && other.constructor === this.constructor && other.name === this.name); } supports(sceneName: string): boolean { return !!this.scenes[sceneName]; } localeCompare(other: Enum): number { if (this.ordinal < other.ordinal) { return -1; } else if (this.ordinal > other.ordinal) { return 1; } else { return 0; } } }