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 的时候,对应注册条件基本只会用到viewType、ttype、widget、这三个属性,其他属性都是可选的。

动作注册条件

  • 动作: ActionWidget.Token

下面是参数描述和含义:

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

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

视图、layout 注册条件

  • 动作: BaseElementWidget.Token

下面是参数描述和含义:

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

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

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中,我们定义了userInfo和userName两个属性,通过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()

在上述代码中,我们还定义了updateUser跟updateOtherUser方法,通过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低代码应用平台体验

Like (0)
汤乾华's avatar汤乾华数式员工
Previous 2024年9月21日 am10:32
Next 2024年9月21日 pm5:51

相关推荐

  • 如何编写自定义字段组件的校验逻辑

    介绍 自定义字段组件的时候,我们可能会遇到有复杂校验规则或者业务上特殊的校验提示信息的场景,这时候可以通过覆写字段的校验方法validator来实现。 示例代码 import { SPI, ValidatorInfo, FormStringFieldWidget, isEmptyValue, isValidatorSuccess, FormFieldWidget, ViewType, ModelFieldType } from '@kunlun/dependencies' @SPI.ClassFactory(FormFieldWidget.Token({ viewType: [ViewType.Form], ttype: ModelFieldType.String, widget: 'DemoPhone' })) export class DemoFormPhoneFieldWidget extends FormStringFieldWidget { // 字段校验方法 public async validator(): Promise<ValidatorInfo> { // 建议先调用平台内置的通用校验逻辑 const res = await super.validator(); if (!isValidatorSuccess(res)) { // 校验失败直接返回 return res; } // 编写自有校验逻辑 if (!isEmptyValue(this.value) && !/^1[3456789]\d{9}$/.test(this.value as string)) { // 通过内置的validatorError方法提示校验提示信息 return this.validatorError('手机号格式错误'); } // 无异常,用内置的validatorSuccess返回校验通过的信息 return this.validatorSuccess(); } } 扩展学习 自定义字段组件如何处理vue组件内的表单校验

    2024年8月23日
    2.8K00
  • 弹窗生命周期实践

    在oinone平台中,弹窗、抽屉是用户界面设计中最为常见的,而对于开发者而言,能够监听弹窗的生命周期事件通常是十分重要的,因为它提供了一个机会去执行一些逻辑。在这篇文章中,我们将深入探讨如何监听弹窗、抽屉生命周期事件,并讨论一些可能的应用场景。 首先,我们来实现一个监听弹窗销毁的事件。 让我们看一下提供的代码片段: // 1: 自定义打开弹窗的动作 @SPI.ClassFactory( BaseActionWidget.Token({ actionType: [ActionType.View], target: [ViewActionTarget.Dialog], model: 'model', name: 'name' }) ) export class MyDialogViewActionWidget extends DialogViewActionWidget { protected subscribePopupDispose = (manager: IPopupManager, instance: IPopupInstance, action) => { // 自定义销毁弹窗后的逻辑 }; protected mounted() { PopupManager.INSTANCE.onDispose(this.subscribePopupDispose.bind(this)); } protected unmounted() { PopupManager.INSTANCE.clearOnDispose(this.subscribePopupDispose.bind(this)); } } 在上面的代码中,我们自定义了打开弹窗的动作,并且监听了弹窗销毁事件。 让我们逐步解析这段代码: 1: subscribePopupDispose 是一个函数,作为弹窗销毁事件的处理程序。它接收三个参数:manager、instance 和 action。 manager: 弹窗事件管理器 instance: 弹窗实例 action: 操作弹窗的动作,如果是点击弹窗右上角的关闭按钮,那action为null 2: 组件挂载的时候,进行监听. 4: 最后组件销毁的时候需要清除对应的监听 那么,如果监听到弹窗销毁,我们可以执行什么样的逻辑呢? 1: 更新相关组件状态: 弹窗销毁后,可能需要更新其他组件的状态。通过 popupWidget 可以获取到弹窗相关的信息,进而执行一些状态更新操作。 2: 处理弹窗销毁时的数据或动作: 在 subscribePopupDispose 函数中,action 参数含一些关于弹窗销毁时的动作信息,如果是点击弹窗右上角的销毁按钮,那action为null。我们可以根据这些信息执行相应的逻辑,例如更新界面状态、保存用户输入等 3: 触发其他操作: 弹窗销毁后,可能需要触发一些后续操作,比如显示另一个弹窗、发起网络请求等。 完整的生命周期 方法名 功能描述 onPush(fn) 监听 弹出窗口被推入时的事件处理器 clearOnPush(fn) 清除onPush事件的监听 onCreated(fn) 监听 弹出窗口创建时的事件处理器 clearOnCreated(fn) 清除onCreated事件的监听 onOpen(fn) 监听 弹出窗口打开时的事件处理器 clearOnOpen(fn) 清除onOpen事件的监听 onClose(fn) 监听 弹出窗口关闭时的事件处理器 clearOnClose(fn) 清除onClose事件的监听 onDispose(fn) 监听 弹出窗口被销毁时的事件处理器 clearOnDispose(fn) 清除onDispose事件的监听 onDisposeAll(fn) 监听 所有弹出窗口被销毁时的事件处理器 clearOnDisposeAll(fn) 清除onDisposeAll事件的监听 结语 开发者可以更灵活地响应用户操作,提升用户体验。在实际项目中,根据应用需求和设计,可以根据以上优化逻辑定制具体的处理流程。希望这篇文章为你提供了更深入的理解。

    2023年11月17日
    2.2K00
  • 字段组件submit方法详解

    场景介绍 在日常开发调试表单页的过程中,细心的小伙伴应该注意到,视图内的数据(通过vue调试工具看到的formData就是视图的数据)和最终通过服务端动作提交的数据不总是一致的,本文将带领大家解开疑惑。 为什么会出现这种现象? 出现这种情况都是当前模型上有关联关系字段的场景,以多对一(M2O)场景为例,由于当前模型的关联关系字段是通过字段配置中的referenceFields属性和当前模型的relationFields属性进行关联的,所以提交数据的时候只需要拿到relationFields配置的字段就可以了,没有必要再去多拿关联关系字段本身的数据。 结合业务场景说明 这里以商品模型和类目模型举例,商品模型内有个类目的m2o字段category和对应的relationFields字段categoryId,数据提交到后端的时候前端默认会根据字段配置只获取categoryId,而category的整个对象都不会被提交。 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.model.DemoItemCategory; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.common.CodeModel; @Model.model(DemoItem.MODEL_MODEL) @Model(displayName = "测试商品") public class DemoItem extends CodeModel { private static final long serialVersionUID = -5104390780952631397L; public static final String MODEL_MODEL = "demo.DemoItem"; @Field.String @Field(displayName = "商品名称") private String name; @Field.Integer @Field(displayName = "类目ID") private Long categoryId; @Field.many2one @Field.Relation(relationFields = {"categoryId"}, referenceFields = {"id"}) @Field(displayName = "商品类目") private DemoItemCategory category; } 前端是如何处理数据的 前端的字段组件提供了submit()方法来让我们可以有就会在提交数据的时候改变数据。 // 字段组件基类 export class BaseFormItemWidget< Value = unknown, Props extends BaseFormItemWidgetProps = BaseFormItemWidgetProps > extends BaseDataWidget<Props> { /** * 数据提交的方法,例如:m2o字段user(假设其关系字段为userId)的值{id: 1, name: 'xxx'},但是实际后端数据只需要其中的id,所以用m2o对应的关系字段userId提交数据就可以了 * @param submitValue */ public submit(submitValue: SubmitValue): ReturnPromise<Record<string, unknown> | SubmitRelationValue | undefined> { return undefined; } } 这里先以FormStringFieldSingleWidget组件处理密码类型的字段讲解。密码一般在输入的时候是明文,为了提高提交到后端的安全性,可以将这个密码加密后再传到后端,后端再做进一步处理,这个场景中,视图中的密码和提交给后端的密码就出现了不一致的情况, @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.String }) ) export class FormStringFieldSingleWidget extends FormStringFieldWidget { public submit(submitValue: SubmitValue) { let finalValue = this.value; /** * 数据提交的时候,如果判断当前字段是否需要加密,需要加密的情况用encrypt函数做加密处理 */ if (this.crypto && finalValue) { finalValue = encrypt(finalValue); } return SubmitHandler.DEFAULT(this.field, this.itemName, submitValue, finalValue); } 注意:关系字段配置的透出字段只影响该字段的查询数据方法的返回值,不会因为此配置就在提交数据里加上这部分配置的字段 字段需要提交关联关系字段内的所有数据如何处理? 我们可以在自定义组件里覆写submit()方法,直接将this.value内的数据返回这里以覆写多对多m2m字段为例 import { BaseFieldWidget, FormM2MFieldSelectWidget, ModelFieldType, SPI, SubmitValue, ViewType } from '@kunlun/dependencies'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: ViewType.Form, ttype: ModelFieldType.ManyToMany, widget: 'Select', model: 'xxx.yyyyy', name: 'fileName01',…

    2024年9月10日
    2.4K00
  • oio-pagination 分页

    API 参数 说明 类型 默认值 版本 currentPage(v-model:currentPage) 当前页数 number – defaultPageSize 默认的每页条数 number 15 disabled 禁用分页 boolean – pageSize 每页条数 number – pageSizeOptions 指定每页可以显示多少条 string[] [’10’, ’15’, ’30’, ’50’, ‘100’, ‘200’] showQuickJumper 是否可以快速跳转至某页 boolean false showSizeChanger 是否展示 pageSize 切换器,当 total 大于 50 时默认为 true boolean – total 数据总数 number 0 事件 事件名称 说明 回调参数 change 页码或 pageSize 改变的回调,参数是改变后的页码及每页条数 Function(page, pageSize) noop

    2023年12月18日
    2.0K00
  • 自定义组件之手动渲染基础(v4)

    阅读之前 你应该: 了解DSL相关内容。母版-布局-DSL 渲染基础(v4) 了解SPI机制相关内容。组件SPI机制(v4.3.0) 了解组件相关内容。 Class Component(ts)(v4) 自定义组件之自动渲染(组件插槽的使用)(v4) 为什么需要手动渲染 在自定义组件之自动渲染(组件插槽的使用)(v4)文章中,我们介绍了带有具名插槽的组件可以使用DSL模板进行自动化渲染,并且可以用相对简单的方式与元数据进行结合。 虽然自动化渲染在实现基本业务逻辑的情况下,有着良好的表现,但自动化渲染方式也有着不可避免的局限性。 比如:当需要多个视图在同一个位置进行切换。 在我们的平台中,界面设计器的设计页面,在任何一个组件在选中后,需要渲染对应的右侧属性面板。每个面板的视图信息是保存在对应的元件中的。根据元件的不同,找到对应的视图进行渲染。在单个视图中使用自动化渲染是无法处理这一问题的,我们需要一种可以局部渲染指定视图的方式,来解决这一问题。 获取一个视图 使用ViewCache获取视图 export class ViewCache { /** * 通过模型编码和名称获取视图 * @param model 模型编码 * @param name 名称 * @param force 强制查询 * @return 运行时视图 */ public static async get(model: string, name: string, force = false): Promise<RuntimeView | undefined> /** * 通过模型编码、自定义名称和模板获取编译后的视图(此视图非完整视图,仅用于自定义渲染使用) * @param model 模型编码 * @param name 名称(用作缓存key) * @param template 视图模板 * @param force 强制查询 * @return 运行时视图 */ public static async compile( model: string, name: string, template: string, force = false ): Promise<RuntimeView | undefined> } ViewCache#get:用于服务端定义视图,客户端直接获取完整视图信息。 ViewCache#compile:用于客户端定义视图,通过服务端编译填充元数据相关信息,但不包含视图其他信息。 自定义一个带有具名插槽的组件,并提供切换视图的相关按钮 以下是一个自定义组件的完整示例,其使用ViewCache#compile方法获取视图。 view.ts const template1 = `<view> <field data="id" invisible="true" /> <field data="code" label="编码" /> <field data="name" label="名称" /> </view>`; const template2 = `<view> <field data="id" invisible="true" /> <field data="name" label="名称" /> <field data="code" label="编码" /> </view>`; export const templates = { template1, template2 }; ManualDemoWidget.ts import { BaseElementWidget, createRuntimeContextForWidget, FormWidget, RuntimeView, SPI, ViewCache, ViewType, Widget } from '@kunlun/dependencies'; import ManualDemo from './ManualDemo.vue'; import { templates } from './view'; @SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' })) export class ManualDemoWidget extends BaseElementWidget { private formWidget: FormWidget | undefined; public…

    2023年11月1日
    1.4K00

Leave a Reply

Please Login to Comment