Files
TsxVueClassApi/src/core/annotation/ioc.annotation.ts
2025-12-08 13:54:20 +08:00

184 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Vue from 'vue';
// 1. 定义最宽松的基础类型(兼容所有场景,避开symbol索引签名限制)
type AutowiredTarget = {
[key: string]: any;
};
// 2. Vue组件实例专属类型(仅声明string/number索引,运行时兼容symbol)
interface VueComponentInstance extends AutowiredTarget {
created?: (...args: any[]) => Promise<void> | void;
$router?: any;
$route?: any;
$el?: HTMLElement;
$data?: Record<string, any>;
}
// 3. Logic实例类型
interface LogicInstance extends AutowiredTarget {
mergeVueInstance?: <T extends VueComponentInstance>(vueInstance: T) => void;
}
// 4. 通用构造函数类型
type Constructor<T = any> = new (...args: any[]) => T;
/**
* 自动注入实例装饰器
* 适配所有场景:
* - Vue组件的logic属性:自动创建Logic实例并合并Vue所有属性
* - Logic类/普通类的属性:直接创建实例并挂载
*/
export function Autowired(params?: any) {
return function (target: AutowiredTarget, propertyKey: string | symbol): void {
// 获取属性对应的类构造函数
const typeClass = Reflect.getMetadata('design:type', target, propertyKey) as Constructor;
// 仅对Vue组件的logic属性做特殊处理(运行时判断)
if (propertyKey === 'logic') {
enhanceVueLogicProperty(target as VueComponentInstance, propertyKey, typeClass);
} else {
// 所有其他场景(包括Logic类的属性):直接挂载实例
mountNormalInstance(target, propertyKey, typeClass, params);
}
};
}
/**
* 挂载普通实例(适配所有非Vue组件场景)
*/
function mountNormalInstance(
target: AutowiredTarget,
propertyKey: string | symbol,
typeClass: Constructor,
params?: any
): void {
if (!typeClass) {
console.warn(`类构造函数不存在,属性${String(propertyKey)}注入失败`);
return;
}
// 创建实例并挂载(统一用defineProperty兼容symbol)
const instance = params ? new typeClass(params) : new typeClass();
Object.defineProperty(target, propertyKey, {
value: instance,
writable: true,
configurable: true,
enumerable: true
});
}
/**
* 增强Vue组件的logic属性:创建实例并合并Vue所有属性
*/
function enhanceVueLogicProperty(
target: VueComponentInstance,
propertyKey: string | symbol,
typeClass: Constructor<LogicInstance>
): void {
// 保存原始created钩子
const originalCreated: (...args: any[]) => Promise<void> | void = target.created || (() => { });
// 重写created钩子(显式指定this类型)
target.created = async function (this: VueComponentInstance): Promise<void> {
const vueInstance = this;
// 防御性判断
if (!typeClass) {
console.warn(`Logic类构造函数不存在,属性${String(propertyKey)}注入失败`);
await originalCreated.call(this);
return;
}
// 创建Logic实例
const logicInstance = Vue.observable(new typeClass());
// 核心修复:用Object.defineProperty兼容symbol,避开TS索引检查
Object.defineProperty(this, propertyKey, {
value: logicInstance,
writable: true,
configurable: true,
enumerable: true
});
// 合并Vue属性(仅当存在mergeVueInstance方法时)
if (typeof logicInstance.mergeVueInstance === 'function') {
logicInstance.mergeVueInstance(vueInstance);
mergeVueProperties(logicInstance, vueInstance);
resetLogicConstructor(logicInstance);
}
// 执行原始created钩子
await originalCreated.call(this);
};
}
/**
* 合并Vue实例的所有属性到Logic实例
*/
function mergeVueProperties(logicInstance: LogicInstance, vueInstance: VueComponentInstance): void {
const allProps = getAllVueProperties(vueInstance);
const excludeProps = new Set<string | symbol>(['constructor', 'prototype', '__proto__', 'mergeVueInstance']);
allProps.forEach(prop => {
if (excludeProps.has(prop)) return;
const logicPropDesc = Object.getOwnPropertyDescriptor(logicInstance, prop);
if (logicPropDesc && !logicPropDesc.configurable) return;
try {
const vuePropDesc =
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(vueInstance), prop) ||
Object.getOwnPropertyDescriptor(vueInstance, prop);
if (vuePropDesc) {
Object.defineProperty(logicInstance, prop, {
...vuePropDesc,
enumerable: true,
configurable: true
});
} else {
// 兜底:用defineProperty赋值,兼容symbol
Object.defineProperty(logicInstance, prop, {
value: vueInstance[prop as keyof typeof vueInstance],
enumerable: true,
configurable: true,
writable: true
});
}
} catch (e) {
if (typeof prop === 'string' && !['__proto__', 'constructor'].includes(prop)) {
console.warn(`合并属性 ${String(prop)} 失败:`, e);
}
}
});
}
/**
* 收集Vue实例的所有属性(包括原型链、Symbol)
*/
function getAllVueProperties(obj: any): Set<string | symbol> {
const props = new Set<string | symbol>();
let currentObj: any = obj;
while (currentObj && currentObj !== Object.prototype) {
Object.getOwnPropertyNames(currentObj).forEach(prop => props.add(prop));
Object.getOwnPropertySymbols(currentObj).forEach(prop => props.add(prop));
currentObj = Object.getPrototypeOf(currentObj);
}
return props;
}
/**
* 重置Logic实例的constructor指向
*/
function resetLogicConstructor(logicInstance: LogicInstance): void {
Object.defineProperty(logicInstance, 'constructor', {
value: logicInstance.constructor,
configurable: true,
enumerable: false,
writable: true
});
}