import { action, excludedInJSON, immutable } from '../decorators'; import { LEVEL_ENUM, vertexsMap, utils, EventEmitter } from '..'; import { at } from 'lodash'; import { findUsageService } from '../../service/common'; import { v4 as uuidv4 } from 'uuid'; /** * 顶点类 * 属性均为只读,仅在初始化和 load 的情况下可以修改 */ export class Vertex extends EventEmitter { /** * 可修改标志,内部属性 * 标记在构造函数结束后是否可修改 */ @excludedInJSON() protected _mutable = true; /** * 概念类型 */ @immutable() public readonly level: LEVEL_ENUM = LEVEL_ENUM.vertex; /** * toJSON 时需要去除的键,为了避免出现 CircularJSON */ @immutable() protected readonly JSON_EXCLUDED_KEYS: Set; /** * 顶点 id */ @immutable() public readonly id: string = undefined; /** * 唯一 uuid * 目前主要用于前端 Debug 区分两个实例 */ @excludedInJSON() public readonly _uuid: string = uuidv4(); /** * 前端记录缓存的时间戳 */ @excludedInJSON() public readonly _cacheTimestamp: string = undefined; /** * 名称 */ @immutable() public readonly name: string; /** * 创建人 */ @immutable() public readonly createdBy: string = undefined; /** * 创建时间 */ @immutable() public readonly createdTime: string = undefined; /** * 修改人 */ @immutable() public readonly updatedBy: string = undefined; /** * 修改时间 */ @immutable() public readonly updatedTime: string = undefined; /** * IDE 版本 */ @immutable() public readonly ideVersion: string = undefined; /** * 是否可编辑 */ public editable: boolean = true; /** * 是否正在编辑 * 前端 UI 状态 */ @excludedInJSON() public editing: boolean = false; /** * 是否正在请求 * 前端 UI 状态 */ @excludedInJSON() public loading: boolean = false; /** * 节点是否为展开状态 * 前端 UI 状态 */ @excludedInJSON() public expanded: boolean = false; /** * 是否为叶子节点 * 前端 UI 状态 */ @excludedInJSON() public isLeaf: boolean = false; /** * @param source 需要合并的部分参数 */ constructor(source?: Partial) { super(); if (typeof window !== 'undefined') { const globalData = (window as any).globalData; this.ideVersion = globalData && globalData.ideVersion; } else { this.ideVersion = '1.2'; } source && this.assign(source); if (this.hasOwnProperty('JSON_EXCLUDED_KEYS') && this.JSON_EXCLUDED_KEYS === undefined) delete (this as any).JSON_EXCLUDED_KEYS; } assign(source: any) { this._mutable = true; const oldId = this.id; Object.assign(this, source); oldId && vertexsMap.delete(oldId); this.id && vertexsMap.set(this.id, this); this.emitVertexIdToNameChange(); this._mutable = false; } emitVertexIdToNameChange() { if (typeof window === 'undefined') return; const { id, name } = this; const service = window && (window as any).$data && (window as any).$data.app && (window as any).$data.app.firstMicroService; if (service) service.emit('vertexIdToNameChange', id, name); } plainAssign(source: any) { this._mutable = true; const oldId = this.id; Object.keys(source).forEach((key) => { const value = source[key]; if (typeof value !== 'object' && typeof value !== 'function') (this as any)[key] = value; }); oldId && vertexsMap.delete(oldId); this.id && vertexsMap.set(this.id, this); this.emitVertexIdToNameChange(); this._mutable = false; } pick(source: any, keys: Array = []) { const obj: { [name: string]: any } = {}; keys.forEach((key) => obj[key] = source[key]); // 这里统一用 assign,集中处理 id 的问题 this.assign(obj); } /** * 从对象中深度获取 * @param source * @param keys * @example 比如一般后端返回只是添加了个 id * ``` * this.deepPick(result, ['id']) */ deepPick(source: any, keys: Array = []) { utils.traverse((current) => { const subSource = current.jsonPath ? at(source, [current.jsonPath])[0] : source; const target = current.jsonPath ? at(this as any, [current.jsonPath])[0] : this; if (!target) return; const obj: { [name: string]: any } = {}; keys.forEach((key) => { if (subSource.hasOwnProperty(key)) { obj[key] = subSource[key]; } }); // 这里统一用 assign,集中处理 id 的问题 target.assign ? target.assign(obj) : Object.assign(target, obj); }, { node: source }, { mode: 'anyObject' }); } /** * 去除循环依赖,转为纯 JSON * @param parentKey 外面的 key,提供给 JSON.stringify 使用 * @param excludedKeys 需要额外排除的 keys */ toJSON(parentKey?: string, excludedKeys: Array = []): any { const JSON_EXCLUDED_KEYS = new Set(this.JSON_EXCLUDED_KEYS || []); // Object.getPrototypeOf(this).constructor.JSON_EXCLUDED_KEYS || []; excludedKeys.forEach((key) => JSON_EXCLUDED_KEYS.add(key)); const temp: { [prop: string]: any } = {}; Object.keys(this).forEach((key) => { if (!JSON_EXCLUDED_KEYS.has(key)) temp[key] = (this as any)[key]; }); return JSON.parse(JSON.stringify(temp)); } /** * 转为单层的 JSON * @param parentKey 外面的 key,提供给 JSON.stringify 使用 * @param excludedKeys 需要额外排除的 keys */ toPlainJSON(parentKey?: string, excludedKeys: Array = []): any { const JSON_EXCLUDED_KEYS = new Set(this.JSON_EXCLUDED_KEYS || []); // Object.getPrototypeOf(this).constructor.JSON_EXCLUDED_KEYS || []; excludedKeys.forEach((key) => JSON_EXCLUDED_KEYS.add(key)); const temp: { [prop: string]: any } = {}; Object.keys(this).forEach((key) => { const value = (this as any)[key]; if (!JSON_EXCLUDED_KEYS.has(key) && typeof value !== 'object' && typeof value !== 'function') temp[key] = value; }); return JSON.parse(JSON.stringify(temp)); } /** * 查找引用 */ @action('查找引用') async findUsage() { const result = await findUsageService.findUsage({ query: { id: this.id, }, }); return result; } /** * 销毁 * 从 Map 中删除点和子节点 */ destroy() { utils.traverse((current) => { vertexsMap.delete(current.node.id); }, { node: this }, { mode: 'anyObject', excludedKeySet: this.JSON_EXCLUDED_KEYS, }); } /** * 通过 Ref 去查找点 * @param ref */ public static getVertexByRef(ref: string) { const arr = ref.split('/'); const id = arr[arr.length - 1]; return vertexsMap.get(id); } } export default Vertex;