@truenewx/tnxcore
Version:
互联网技术解决方案:JavaScript核心扩展支持
181 lines (165 loc) • 5.68 kB
text/typescript
/**
* 枚举类型
* 注意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;
}
}
}