TS 结合 Vue 实现动态注册和响应式管理

基础知识

1: 面向对象

面向对象编程是 JavaScript 中一种重要的编程范式,它帮助开发者通过类和对象组织代码,提高代码的复用性。

2: 装饰器

装饰器在 JavaScript 中是用于包装 class 或方法的高阶函数

为了统一术语,下面的内容会把装饰器讲成注解

在 oinone 平台中,无论是字段还是动作,都是通过 ts + vue 来实现的,ts 中是面向对象的写法,所有的属性、方法建议都在对应的 class 中,如果有通用的属性跟方法,可以放在一个公共的 class 中,然后通过继承来实现,这样便于维护。

<!-- FormString.vue -->
<template>
  <div>
    <p>用户名: {{userName}}</p>
    <button @click="updateUser({name: '王五'})">修改用户名</button>
  </div>
</template>
<script lang="ts">
  import { defineComponent } from 'vue';

  export default defineComponent({
    props: {
      userName: {
        type: String,
        default: ''
      },
      userInfo: {
        type: Object,
        default: () => ({})
      },
      updateUser: {
        type: Function,
        default: () => () => ({})
      }
    }
  });
</script>
import FormString from './FormString.vue'

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: [ViewType.Form, ViewType.Search],
    ttype: ModelFieldType.String
  })
)
export class FormCustomStringFieldWidget extends FormFieldWidget {
  public initialize(props) {
    super.initialize(props); // 调用父类方法,确保继承的属性和方法正常初始化
    this.setComponent(FormString); // 注册 Vue 组件,这样该 Widget 就会渲染 FormString 组件
    return this;
  }

  public otherInfo = {
    name:'张三'
  }

  @Widget.Reactive()
  public userInfo = {
    name:'李四'
  }

  @Widget.Reactive()
  public get userName() {
    return this.userInfo.name
  }

  @Widget.Method()
  public updateUser userName(user) {
     this.userInfo = user
  }

  public updateOtherUser userName(user) {
     this.otherUser = user
  }
}

这段代码定义了一个 FormCustomStringFieldWidget 类,用于处理表单中 String 类型字段的展示和交互。该类继承自 FormFieldWidget,并使用了多种注解和特性来实现不同功能。下面是对代码的详细讲解。

SPI 讲解

@SPI.ClassFactory()

无论是自定义字段还是动作,或者是自定义 mask、layout,都会用到@SPI.ClassFactory来注册,@SPI.ClassFactory 是一个注解,它标记了该类是通过工厂模式注册的。

在前端中,所有的注解(装饰器)本质上还是高阶函数,下面是一段伪代码。

const SPI = {
  ClassFactory(token) {
    return (targetClass) => {
      // todo something
    };
  }
};

class MyClass {}

SPI.ClassFactory(注册条件)(MyClass);

所以我们在使用@SPI.ClassFactory的时候,就会根据注册条件把对应的 class 注册到对应的工厂中。

注册条件

FormFieldWidget.Token({
  viewType: [ViewType.Form, ViewType.Search],
  ttype: ModelFieldType.String
});

这段代码调用FormFieldWidget.Token函数,生成一个 token,这个 token 会作为参数传递给@SPI.ClassFactory,然后@SPI.ClassFactory会根据这个 token 来注册对应的 class。

不同类型的 widget 调用的 token 函数不同:

注册条件集合

字段注册条件

  • 表单(详情、搜索、画廊)字段: FormFieldWidget.Token
  • 表格字段: BaseFieldWidget.Token

下面是参数描述和含义:

属性 类型 说明
viewType ViewType | ViewType[] 当前视图类型,支持单一或多个视图类型。
widget string | string[] 组件名称,可以是单个字符串或多个组件名称。
ttype ModelFieldType | ModelFieldType[] 字段业务类型,支持单一或多个业务类型。
multi boolean 是否多值,true 表示字段支持多个值。
model string | string[] 指定模型名称,可以是单一或多个模型。
viewName string | string[] 指定视图名称,可以是单一或多个视图名称。
name string 指定字段的 name 属性,用于业务逻辑识别。

当我们注册 字段 SPI 的时候,对应注册条件基本只会用到viewTypettypewidget、这三个属性,其他属性都是可选的。

动作注册条件

  • 动作: ActionWidget.Token

下面是参数描述和含义:

属性 类型 说明
viewType ViewType | ViewType[] 当前视图类型,支持单一或多个视图类型。
actionType ActionType | AiewType[] 当前动作类型,支持单一或多个动作类型。
widget string | string[] 组件名称,可以是单个字符串或多个组件名称。
target ViewActionTarget | ViewActionTarget[] 打开方式 (视图动作专属)
viewName string | string[] 指定视图名称,可以是单一或多个视图名称。
model string | string[] 指定模型名称,可以是单一或多个模型。
name string 指定动作的 name 属性,用于业务逻辑识别。

当我们注册 动作 SPI 的时候,对应注册条件基本只会用到actionTypemodelname、这三个属性,其他属性都是可选的。

视图、layout 注册条件

  • 动作: BaseElementWidget.Token

下面是参数描述和含义:

属性 类型 说明
viewType ViewType | ViewType[] 当前视图类型,支持单一或多个视图类型。
inline boolean 当前是否为内嵌视图。
widget string | string[] 组件名称,可以是单个字符串或多个组件名称。
viewName string | string[] 指定视图名称,可以是单一或多个视图名称。
model string | string[] 指定模型名称,可以是单一或多个模型。

当我们注册 layout 或者视图 SPI 的时候,对应注册条件基本只会用到viewTypewidget、这两个个属性,其他属性都是可选的。

mask 注册条件

  • 动作: MaskWidget.Token

下面是参数描述和含义:

属性 类型 说明
widget string | string[] 组件名称,可以是单个字符串或多个组件名称。

当我们注册 mask SPI 的时候,对应注册条件只会用到widget

路由注册条件

  • 动作: RouterWidget.Token

下面是参数描述和含义:

属性 类型 说明
widget string | string[] 组件名称,可以是单个字符串或多个组件名称。

当我们注册 路由 SPI 的时候,对应注册条件只会用到widget

SPI 覆盖

在开发中,有时我们需要对平台底层已注册的 SPI 进行覆盖。这种情况通常出现在需要修改或扩展某些功能时。下面将介绍如何安全地覆盖 SPI。

以字段为例,加入我们需要覆盖平台默认的字段,这个字段是 Form 表单里面的String类型,我们希望这个字段可以支持业务扩展的能力,那么我们可以通过如下方式来扩展这个字段:

在 5.0.x 版本中,平台部分源码是开放的,所以我们可以看到平台默认的字段源码,FormStringFieldSingleWidget就是 Form 表单里面的String类型对应的 class,它的 SPI 是:

@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: [ViewType.Form, ViewType.Search],
    ttype: ModelFieldType.String
  })
)

如果想覆盖它,只需要 SPI 注册条件一致就行。

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: [ViewType.Form, ViewType.Search],
    ttype: ModelFieldType.String
  })
)
class ExistingStringFieldWidget extends FormStringFieldSingleWidget {
  // 平台已有实现
}

注册 vue 组件

FormCustomStringFieldWidget中,我们定义了initialize函数里面写了一点代码。

import FormString from './FormString.vue'

class FormCustomStringFieldWidget{
  public initialize(props) {
    super.initialize(props);
    this.setComponent(FormString);
    return this;
  }
}

initialize函数是固定写法,super.initialize(props)是用来调用父类的方法,this.setComponent(FormString)是将 vue 组件注册到当前 widget 中,当自定义的字段不需要重写 vue 组件的时候,那么就不需要重写initialize函数,只需要继承对应的 class 重写对应的属性、方法即可

当自定义的字段、动作、视图、mask 需要使用自己的 vue 组件时,才需要重写initialize函数

ts 与 vue 之间的联动

Widget.Reactive()

FormCustomStringFieldWidget中,我们定义了userInfouserName两个属性,通过Widget.Reactive()注解,将这两个属性变成响应式属性,这样在 vue 组件中中,就可以通过 props 来接受两个属性,otherInfo属性没有打上任何注解,说明它是一个普通的属性,在对应的 vue 文件里面是获取不到的。

当定义 userName 时,我们为其加上了 get 属性,这会将它转换为一个计算属性,方便在 Vue 组件中动态更新。

所有可以理解成:

 @Widget.Reactive()
 public userInfo = {}
 等价于 ↓
 const  userInfo = ref({})

  @Widget.Reactive()
  public get userName() {
    return this.userInfo.name
  }
   等价于  ↓
  const  userName = computed(() => userInfo.name)

Widget.Method()

在上述代码中,我们还定义了updateUserupdateOtherUser方法,通过updateUser上使用了Widget.Method()注解,将这个方法变成一个响应式方法,这样在 vue 组件中,就可以通过 props 来接受这个方法,调用这个方法,实现数据的更新,updateOtherUser是一个普通的方法,在 vue 文件中是获取不到的。

最终我们可以得出一个结论,在 ts 中,如果定义的属性跟方法需要在 vue 中获取,那么就要加上Widget.Reactive()注解,如果是方法,就加上Widget.Method()注解,如果该属性是一个计算属性,就加上get属性。

伪代码实现:

import { ref, computed } from 'vue';

const Widget = {
  Reactive(target, key, description) {
    if (description.get) {
      target[key] = computed(description.get);
    } else {
      target[key] = ref(target[key]);
    }
  },

  Method(target, key) {
    target[key] = ref(target[key]);
  }
};

完整案例:用户信息管理

场景:我们将创建一个用户信息管理的小模块,允许用户查看和修改他们的个人信息,包括姓名和邮箱地址。我们将用 Vue 组件展示这些信息,并使用 TS 中的类来管理逻辑。此案例将展示如何使用 @Widget.Reactive() 和 @Widget.Method() 注解来确保数据和方法的响应式更新。

第一步:Vue 组件定义

我们首先定义一个简单的 Vue 组件,用于显示和更新用户信息。

<!-- UserInfo.vue -->
<template>
  <div>
    <p>姓名: {{ userName }}</p>
    <p>邮箱: {{ userEmail }}</p>
    <button @click="updateUserInfo({ name: '李四', email: 'li.si@example.com' })">修改用户信息</button>
  </div>
</template>

<script lang="ts">
  import { defineComponent } from 'vue';

  export default defineComponent({
    props: {
      userName: {
        type: String,
        default: ''
      },
      userEmail: {
        type: String,
        default: ''
      },
      updateUserInfo: {
        type: Function,
        default: () => () => ({})
      }
    }
  });
</script>

第二步:TypeScript 类定义

我们在 TypeScript 中定义一个类 FormStringUserFieldWidget,用于管理用户信息。我们使用 @Widget.Reactive() 和 @Widget.Method() 来确保数据的响应式更新和方法的可用性。

import UserInfo from './UserInfo.vue';

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: [ViewType.Form],
    ttype: ModelFieldType.String,
    widget: 'FormStringUserFieldWidget'
  })
)
export class FormStringUserFieldWidget extends FormFieldWidget {
  // 注册 Vue 组件
  public initialize(props: any) {
    super.initialize(props);
    this.setComponent(UserInfo); // 将 UserInfo.vue 组件注册到当前 Widget
    return this;
  }

  // 用户基础信息
  @Widget.Reactive()
  public userInfo = {
    name: '张三',
    email: 'zhang.san@example.com'
  };

  // 返回用户姓名的计算属性
  @Widget.Reactive()
  public get userName() {
    return this.userInfo.name;
  }

  // 返回用户邮箱的计算属性
  @Widget.Reactive()
  public get userEmail() {
    return this.userInfo.email;
  }

  // 更新用户信息的方法
  @Widget.Method()
  public updateUserInfo(newInfo: { name: string; email: string }) {
    this.userInfo = { ...this.userInfo, ...newInfo };
  }
}

第三步:案例解析

  • initialize 方法:
    • 我们使用 initialize 方法来注册 Vue 组件 UserInfo.vue。当这个类被实例化时,Vue 组件会被渲染,并且与类中的响应式数据和方法建立连接。
  • userInfo 属性:
    • 这是用户的基本信息对象,包含 name 和 email 两个字段。我们使用 @Widget.Reactive() 注解将 userInfo 变成响应式数据,这样在 Vue 组件中可以随时获取和更新。
  • userName 和 userEmail 计算属性:
    • 我们使用 get 关键字为 userName 和 userEmail 定义了计算属性,并使用 @Widget.Reactive() 注解,这样 Vue 组件可以根据 userInfo 的变化自动更新显示内容。
  • updateUserInfo 方法:
    • 这是用于更新用户信息的响应式方法。通过 @Widget.Method() 注解,我们确保 Vue 组件能够调用此方法。点击 Vue 组件中的按钮后,会触发 updateUserInfo 方法更新用户的姓名和邮箱。
  • Vue 组件
    • 我们通过 props 接收了从 TypeScript 类传递过来的 userName、userEmail 和 updateUserInfo 方法。点击按钮后,updateUserInfo 方法会被调用,用户信息将被更新。

通过这个完整的案例,我们展示了如何将 TypeScript 与 Vue 结合,通过 @Widget.Reactive() 和 @Widget.Method() 实现数据的响应式联动和方法调用。

Oinone社区 作者:汤乾华原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/17632.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
汤乾华的头像汤乾华数式员工
上一篇 2024年9月21日 am10:32
下一篇 2024年9月21日 pm5:51

相关推荐

  • 「前端」获取系统配置

    「前端」获取系统配置 简介 系统配置对于前端开发至关重要,它包含了许多关键信息,通过调用「systemMajorConfig」API,可以轻松地获取这些关键配置信息。除了主要的系统配置外,底层还提供了一些快捷的API,比如获取当前主题、当前主题大小、登录页面主题、版权状态和默认浏览器信息。 使用步骤 调用「systemMajorConfig」API获取系统配置数据。 使用返回的数据对象来访问特定的系统配置参数,如企业名称、企业官网等。 使用底层提供的快捷API来获取与系统配置相关的特定信息。 系统配置参数 logo (string): 应用logo(未折叠状态) appSideLogo (string): 应用logo(折叠状态) smallLogo (string): 小型logo slogan (string): 企业slogan favicon (string): 浏览器logo browserTitle (string): 浏览器标题 loginPageLogo (string): 登录页logo loginBackground (string): 登录页背景 loginLayoutType (any): 登录页布局主题 mode (any): 主题模式 size (string): 主题大小 快捷API列表 getCurrentTheme: 获取当前主题信息。 getCurrentThemeSize: 获取当前主题大小。 getLoginTheme: 获取登录页面主题信息。 getCopyrightStatus: 获取版权状态信息。 getDefaultBrowser: 获取默认浏览器信息。 示例代码 import { systemConfig, getCurrentTheme } from ‘@kunlun/dependencies’ // 访问特定系统配置参数 console.log(systemConfig.logo); // 输出企业名称 // 使用快捷API获取特定信息 console.log(getCurrentTheme());

    2023年11月1日
    1.1K00
  • 表格字段API

    BaseTableFieldWidget 表格字段的基类. 示例 class MyTableFieldClass extends BaseTableFieldWidget{ } 内置常见的属性 dataSource 当前表格数据 rootData 根视图数据 activeRecords 当前选中行 userPrefer 用户偏好 width 单元格宽度 minWidth 单元格最小宽度 align 内容对齐方式 headerAlign 头部内容对齐方式 metadataRuntimeContext 当前视图运行时的上下文,可以获取当前模型、字段、动作、视图等所有的数据 urlParameters 获取当前的url field 当前字段 详细信息 用来获取当前字段的元数据 model 当前模型 详细信息 用来获取当前模型的元数据 view 当前视图 详细信息 界面设计器配置的视图dsl disabled 是否禁用 详细信息 来源于界面设计器的配置 invisible 当前字段是否不可见 详细信息 来源于界面设计器的配置,true -> 不可见, false -> 可见 required 是否必填 详细信息 来源于界面设计器的配置,如果当前字段是在详情页,那么是false readonly 是否只读 详细信息 来源于界面设计器的配置,如果当前字段是在详情页、搜索,那么是false label 当前字段的标题 详细信息 用来获取当前字段的标题 内置常见的方法 renderDefaultSlot 渲染单元格内容 示例 @Widget.Method() public renderDefaultSlot(context): VNode[] | string { // 当前单元格的数据 const currentValue = this.compute(context) as string[]; return [createVNode('div', { class: 'table-string-tag' }, currentValue)]; } renderHeaderSlot 自定义渲染头部 示例 @Widget.Method() public renderHeaderSlot(context: RowContext): VNode[] | string { const children = [createVNode('span', { class: 'oio-column-header-title' }, this.label)]; return children; } getTableInstance 获取当前表格实例(vxe-table) getDsl 获取界面设计器的配置

    2023年11月16日
    1.1K00
  • 【前端】移动端工程结构最佳实践(v4/v5)

    阅读之前 你应该: 了解node与npm相关内容 了解lerna包管理工具的相关内容 官方文档 了解git仓库的相关内容 了解rollup的相关内容 工程结构包示例 Vue项目结构包下载-v4.7.xVue项目结构包下载-v5.2.x 工程结构详解 工程结构 ├── packages │   ├── kunlun-mobile-boot │   │   ├── package.json │   │   ├── public │   │   │   ├── favicon.ico │   │   │   └── index.html │   │   ├── src │   │   │   ├── main.ts │   │   │   └── shim-vue.d.ts │   │   ├── tsconfig.json │   │   └── vue.config.js │   ├── kunlun-module-mobile-demo │   │   ├── scripts │   │   │   ├── postpublish.js │   │   │   └── prepublish-only.js │   │   ├── src │   │   │   ├── index.ts │   │   │   └── shim-vue.d.ts │   │   ├── index.ts │   │   ├── package.json │   │   ├── rollup.config.js │   │   └── tsconfig.json │   └── kunlun-modules-mobile-demo │   ├── scripts │   │   ├── build.config.js │   │   ├── postpublish.js │   │   └── prepublish-only.js │   ├── packages │   │   ├── module-demo1 │   │   │   ├── index.ts │   │   │   ├── package.json │   │   │   ├── rollup.config.js │   │   │   └── src │   │   │   ├── index.ts │   │   │   └── shim-vue.d.ts │   │   ├── module-demo2 │   │   │   ├── index.ts │   │   │   ├── package.json │   │   │   ├── rollup.config.js │   │   │  …

    前端 2023年11月1日
    1.6K00
  • 母版-布局-DSL 渲染基础(v4)

    概述 不论是母版、布局还是DSL,我们统一使用XML进行定义,可以更好的提供结构化表述。 参考文档: XML百度百科 XML语法参考 下面文档中未介绍到的Mask母版和Layout布局,可以去数据库中base库的表base_layout_definition和base_mask_definition的template字段查看 母版 确定了主题、非主内容分发区域所使用组件和主内容分发区域联动方式的页面配置。 母版内容分为主内容分发区域与非主内容分发区域。非主内容分发区域一般包含顶部栏、底部栏和侧边栏。侧边栏可以放置菜单,菜单与主内容分发区域内容进行联动。 默认母板 <mask> <multi-tabs /> <header> <widget widget="app-switcher" /> <block> <widget widget="notification" /> <widget widget="divider" /> <widget widget="language" /> <widget widget="divider" /> <widget widget="user" /> </block> </header> <container> <sidebar> <widget widget="nav-menu" height="100%" /> </sidebar> <content> <breadcrumb /> <block width="100%"> <widget width="100%" widget="main-view" /> </block> </content> </container> </mask> 该模板中包含了如下几个组件: mask:母版根标签 multi-tabs:多选项卡 header:顶部栏 container:容器 sldebar:侧边栏 nav-menu:导航菜单 content:主内容 breadcrumb:面包屑 block:div块 main-view:主视图;用于渲染布局和DSL等相关内容; 母版将整个页面的大体框架进行了描述,接下来将主要介绍布局和DSL是如何在main-view中进行渲染的。关于自定义母版组件的相关内容 点击查看 布局 布局是将页面拆分成一个一个的小单元,按照从上到下、从左到右进行顺序排列 布局主要用于控制页面中元素的展示的相对位置,原则上不建议将元数据相关内容在布局中进行使用,可最大化布局的利用率。 默认表格视图(TABLE) <view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" /> </view> </pack> <pack widget="group" slot="tableGroup"> <element widget="actionBar" slot="actionBar"> <xslot name="actions" /> </element> <element widget="table" slot="table"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" /> <element widget="rowActions" slot="rowActions" /> </element> </pack> </view> 该模板中包含了如下几个组件: view:视图;用于定义当前视图类型,不同的视图类型会有不同的数据交互,以及渲染不同的组件。 pack:容器类型相关组件。 element:元素组件;包含各种各样的组件,根据组件实现有不同的作用。 xslot:DSL插槽;用于将DSL中定义的模板分别插入到对应的槽中; 特别的,任何XML标签上的slot属性都具备DSL插槽的全部能力。当学习完DSL相关内容后,我们将会对DSL插槽有比较清晰的理解。 PS:在下面的内容中,将使用该布局进行描述。 DSL 准备工作 为了方便描述DSL和元数据之间的关系,我们需要先定义一个简单模型,这个模型里面包含字段和动作。这些通常是服务端定义的。(对服务端不感兴趣的同学可以跳过代码部分) DemoModel.java @Model.model(DemoModel.MODEL_MODEL) @Model(displayName = "演示模型", labelFields = {"name"}) public class DemoModel extends IdModel { private static final long serialVersionUID = -7211802945795866154L; public static final String MODEL_MODEL = "demo.DemoModel"; @Field(displayName = "名称") private String name; @Field(displayName = "是否启用") private Boolean isEnabled; } DemoModelAction.java @Model.model(DemoModel.MODEL_MODEL) @UxRouteButton( action = @UxAction(name = "redirectCreatePage", displayName = "创建", contextType = ActionContextTypeEnum.CONTEXT_FREE), value = @UxRoute(model =…

    2023年11月1日
    2.5K10
  • 如何在多标签页切换时自动刷新视图

    在日常项目中,常常会遇到多视图(Multi-View)标签的场景,用户在切换不同视图时,可能需要刷新当前活动标签内的视图数据或状态。本文将详细解析下面这段代码,并说明如何利用它在视图切换时刷新对应的视图。 下列代码写在ss-boot里面的main.ts import { VueOioProvider } from '@kunlun/dependencies'; import { delay } from 'lodash-es'; VueOioProvider( { … 自己的配置 }, [ () => { setTimeout(() => { subscribeRoute( (route) => { const page = route.segmentParams.page || {}; // 如果不是表格类型,则不刷新(根据自己的需求判断) if (page.viewType !== ViewType.Table) { return; } const { model, action } = page; const multiTabsManager = MultiTabsManager.INSTANCE; delay(() => { const tab = multiTabsManager.getActiveTab(); if (tab?.key && tab.stack.some((s) => s.parameters?.model === model && s.parameters?.action === action)) { multiTabsManager.refresh(tab.key); } }, 200); }, { distinct: true } ); }, 1000); } ] ); 1. VueOioProvider 及其作用 首先,代码通过 VueOioProvider 初始化应用程序或组件,并传入两部分参数: 配置对象:可以根据实际业务需求进行自定义配置; 回调函数数组:这里传入了一个匿名函数,用于在应用初始化后执行额外的逻辑 2. 延时执行与路由监听 在回调函数中,使用了 setTimeout 延时 1000 毫秒执行,目的通常是为了确保其他组件或全局状态已经初始化完毕,再开始进行路由监听。 随后,代码调用 subscribeRoute 来监听路由的变化。subscribeRoute 接收两个参数: 回调函数:每次路由变化时都会触发该函数,并将最新的 route 对象传递给它; 配置对象:此处使用 { distinct: true } 来避免重复的触发,提高性能。 3. 判断视图类型 在路由回调函数内部,首先通过 route.segmentParams.page 获取当前页面的配置信息。通过判断 page.viewType 是否等于 ViewType.Table,代码可以确定当前视图是否为“表格类型”: 如果不是表格类型:则直接返回,不做刷新操作; 如果是表格类型:则继续执行后续刷新逻辑。 这种判断机制保证了只有特定类型的视图(例如表格)在切换时才会触发刷新,避免了不必要的操作 4. 多视图标签的刷新逻辑 当确认当前视图为表格类型后,从 MultiTabsManager 中获取当前活动标签: MultiTabsManager.INSTANCE.getActiveTab() 返回当前活动的标签对象; 如果 key 存在,并且激活的标签内部存储的action跟url一致, 就调用 multiTabsManager.refresh(key) 方法来刷新当前标签内的视图。

    2025年3月13日
    1.0K00

Leave a Reply

登录后才能评论