diff --git a/src/application/logic/base.logic.ts b/src/application/logic/base.logic.ts index b12f0dd..6508f26 100644 --- a/src/application/logic/base.logic.ts +++ b/src/application/logic/base.logic.ts @@ -2,16 +2,32 @@ * Copyright (c) 2020. bmy */ -import { VueCustomize } from "@logic/base.type"; +import VueRouter, { Route } from 'vue-router' + +export interface Methods { + StartUp?(): void; + $route: Route; + $router: VueRouter; + // 可添加其他需要的Vue属性,如$el/$data/$refs等 + $el?: HTMLElement; + $data?: Record; + $refs?: Record; +} /** * logic 层的父类 * 主要实现 页面公共参数和ajax方法的地方 */ -export class BaseLogic { +export class BaseLogic implements Methods { + // 泛型参数T表示Vue实例的类型 + protected _?: T; // 保留this._以便向后兼容 + + $route!: Route; + $router!: VueRouter; + $el?: HTMLElement | undefined; + $data?: Record | undefined; + - // 保存Vue对象 - public _!: any; public page: number = 1; public total_page: number = 1; @@ -26,14 +42,19 @@ export class BaseLogic { // 请求参数 public PageParams: { limit: number, page: number } = { - limit : 10, page : 1 + limit: 10, page: 1 } - /** - * 【页面中公共的多次被用到的接口】可以在这个父类中定义, - * 子类logic中直接使用,避免方法的冗余 - * @constructor - */ - public async DepartmentListRequest(): Promise { + constructor() { } + + // 组件初始化完成后调用 + StartUp?(): void; + + // 合并Vue实例到Logic实例 + mergeVueInstance(vueInstance: T) { + // 保存Vue实例的引用,以便在Logic实例中可以访问到Vue实例 + this._ = vueInstance; + } + } diff --git a/src/application/logic/page/about.logic.ts b/src/application/logic/page/about.logic.ts index 1b3e2ab..6bedc0e 100644 --- a/src/application/logic/page/about.logic.ts +++ b/src/application/logic/page/about.logic.ts @@ -6,13 +6,13 @@ import { Autowired } from "@ann/ioc.annotation"; import { BaseLogic } from "@logic/base.logic"; import { Methods, VueCustomize } from "@logic/base.type"; -export class AboutLogic extends BaseLogic implements Methods { - +export class AboutLogic extends BaseLogic implements Methods { + constructor(_: T) { super(); - this._ = _ + (this as any)._ = _; // 使用类型断言来解决TypeScript的类型检查问题 } - public async StartUp (): Promise { + public async StartUp(): Promise { } } diff --git a/src/application/logic/page/home.logic.ts b/src/application/logic/page/home.logic.ts index 04725aa..dc0585d 100644 --- a/src/application/logic/page/home.logic.ts +++ b/src/application/logic/page/home.logic.ts @@ -8,43 +8,44 @@ import { UserService, ResponseType } from "@service/user.service"; import { BaseLogic } from "@logic/base.logic"; import { Methods, VueCustomize } from "@logic/base.type"; -export class HomeLogic extends BaseLogic implements Methods { +export class HomeLogic extends BaseLogic implements Methods { @Autowired() public readonly UserService!: UserService; public title: string = "12222"; - - public List: ResponseType[] = []; - // constructor() { - // super(); - // } + public List: ResponseType[] = [ + { user_id: 1, user_name: '测试1' }, + { user_id: 2, user_name: '测试2' }, + ]; - constructor(_: T) { + constructor() { super(); - console.log("constructor params: ", _); - this._ = _ } /** - * 在这里面调用需要打开页面就执行的ajax请求 - * @constructor + * 页面初始化调用的方法 */ - public async StartUp (): Promise { + public async StartUp(): Promise { + // 在StartUp方法中访问$route和$router + console.log("StartUp - this: ", this); + console.log("StartUp - this.$route: ", this.$route); + console.log("StartUp - this.$router: ", this.$router); + await this.getData(); } /** * 请求首页数据 */ - public async getData (): Promise { - this.List = await this.UserService.UserList({ type: 1, page: 1 }); - console.log("this.List: ", this.List); + public async getData(): Promise { + // this.List = await this.UserService.UserList({ type: 1, page: 1 }); + // console.log("this.List: ", this.List); // let res = await this.UserService.login({ type: 2, page: 2 }); // console.log("this.res: ", res); - + // // await this._.$router.push('/About') } diff --git a/src/application/page/about.tsx b/src/application/page/about.tsx index 37e47e8..9b220a6 100644 --- a/src/application/page/about.tsx +++ b/src/application/page/about.tsx @@ -9,9 +9,9 @@ import { VueCustomize, RouterMeta } from "@logic/base.type"; import { Autowired } from "@ann/ioc.annotation"; @Component -export default class About extends VueCustomize { +export default class About extends VueCustomize { - @Autowired(About) + @Autowired() private logic!: AboutLogic private RouterMeta: RouterMeta = { @@ -20,16 +20,16 @@ export default class About extends VueCustomize { isLogin: true }; - public async created () { + public async created() { await this.logic.StartUp(); }; // 路由拦截器 - beforeRouteEnter (to: Route, from: Route, next: (to?: RawLocation | false | ((vm: Vue) => void)) => void) { + beforeRouteEnter(to: Route, from: Route, next: (to?: RawLocation | false | ((vm: Vue) => void)) => void) { next(); } - protected render () { + protected render() { return (
About diff --git a/src/application/page/home.tsx b/src/application/page/home.tsx index 0c08f3f..8c323f8 100644 --- a/src/application/page/home.tsx +++ b/src/application/page/home.tsx @@ -17,14 +17,22 @@ import { VueCustomize, RouterMeta } from "@logic/base.type"; @Component export default class Home extends VueCustomize { - + + // 可选:手动声明data(双重保障,也可仅靠装饰器自动声明) + public data() { + return { + logic: {}, // 确保Vue2初始化时劫持该属性 + Utils: {} + }; + } + public RouterMeta: RouterMeta = { title: '首页', showNav: true, isLogin: true } - @Autowired(Home) + @Autowired() private logic!: HomeLogic @Autowired() @@ -49,6 +57,7 @@ export default class Home extends VueCustomize { } public back() { + this.logic.title = "44444"; console.log("qqqq"); } @@ -61,6 +70,15 @@ export default class Home extends VueCustomize {

{this.logic.title}

+ + {/* 显示$route和$router信息 */} +
+

路由信息:

+

路由路径: {this.$route?.path}

+

路由名称: {this.$route?.name}

+

Logic实例路由路径: {(this.logic as any).$route?.path}

+
+ {this.ClickButton()}
    { diff --git a/src/core/annotation/ioc.annotation.ts b/src/core/annotation/ioc.annotation.ts index c5635cf..9b5ba03 100644 --- a/src/core/annotation/ioc.annotation.ts +++ b/src/core/annotation/ioc.annotation.ts @@ -1,14 +1,184 @@ -/* - * Copyright (c) 2020. bmy - */ +import Vue from 'vue'; -export function Autowired(params?: any) { - return (target: any, propertyKey: string) => { - let typeClass = Reflect.getMetadata('design:type', target, propertyKey); - Object.defineProperty(target, propertyKey, { - value: params ? new typeClass(new params) : new typeClass(), - writable: true, - configurable: true - }); - } +// 1. 定义最宽松的基础类型(兼容所有场景,避开symbol索引签名限制) +type AutowiredTarget = { + [key: string]: any; +}; + +// 2. Vue组件实例专属类型(仅声明string/number索引,运行时兼容symbol) +interface VueComponentInstance extends AutowiredTarget { + created?: (...args: any[]) => Promise | void; + $router?: any; + $route?: any; + $el?: HTMLElement; + $data?: Record; } + +// 3. Logic实例类型 +interface LogicInstance extends AutowiredTarget { + mergeVueInstance?: (vueInstance: T) => void; +} + +// 4. 通用构造函数类型 +type Constructor = 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 +): void { + // 保存原始created钩子 + const originalCreated: (...args: any[]) => Promise | void = target.created || (() => { }); + + // 重写created钩子(显式指定this类型) + target.created = async function (this: VueComponentInstance): Promise { + 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(['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 { + const props = new Set(); + 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 + }); +} \ No newline at end of file diff --git a/vue.config.js b/vue.config.js index 9f32ae3..0bbf1b8 100644 --- a/vue.config.js +++ b/vue.config.js @@ -56,25 +56,25 @@ module.exports = { errors: true }, // before 这块代码都可以删除,仅仅为脚手架提供一个用于测试的接口 - before: function (app, server) { - app.all('*', function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Headers', '*'); - res.header('Access-Control-Allow-Methods', '*'); - res.header('Content-Type', 'application/json;charset=utf-8'); - next(); - }); - app.get('/UserList', function (req, res) { - res.json({ - status: 200, - message: '请求成功', - result: [ - { id: 1, name: '张三' }, - { id: 2, name: '李四' } - ] - }); - }); - }, + // before: function (app, server) { + // app.all('*', function (req, res, next) { + // res.header('Access-Control-Allow-Origin', '*'); + // res.header('Access-Control-Allow-Headers', '*'); + // res.header('Access-Control-Allow-Methods', '*'); + // res.header('Content-Type', 'application/json;charset=utf-8'); + // next(); + // }); + // app.get('/UserList', function (req, res) { + // res.json({ + // status: 200, + // message: '请求成功', + // result: [ + // { id: 1, name: '张三' }, + // { id: 2, name: '李四' } + // ] + // }); + // }); + // }, // proxy: { // '/api': { // target: 'http://115.159.153.142:9010/h5', @@ -111,7 +111,7 @@ module.exports = { plugins: [ new ProgressPlugin({ handler: (percentage, message, ...args) => { - console.log(`${chalk.yellow("进度: ")}${chalk.green.bold(~~(percentage * 100) + "%")} ${args[0] ? chalk.black(args[0]) : " " } ${chalk.blue.bold(message)}`) + console.log(`${chalk.yellow("进度: ")}${chalk.green.bold(~~(percentage * 100) + "%")} ${args[0] ? chalk.black(args[0]) : " "} ${chalk.blue.bold(message)}`) }, }) ]