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

相关推荐

  • 根据固定的接口返回数据动态控制按钮的显隐

    在项目开发中,我们经常会面临这样的情况:当前按钮的显示与隐藏需要根据后端某个接口返回的数据来决定,而无法通过权限配置进行处理。为了解决这个问题,我们可以通过自定义的方式来处理这一功能。 首先,我们需要知道当前动作是什么类型的动作,例如「服务端动作、跳转动作、打开弹窗的动作、打开抽屉的动作」。 ServerActionWidget -> 服务端动作DialogViewActionWidget -> 打开弹窗动作DrawerViewActionWidget -> 打开抽屉动作RouterViewActionWidget -> 路由跳转动作 下面是一个示例代码,演示了如何通过自定义的方式处理按钮的显示与隐藏逻辑。 import { ActionType, ActionWidget, SPI, ServerActionWidget, Widget, http } from '@kunlun/dependencies'; @SPI.ClassFactory( ActionWidget.Token({ actionType: ActionType.Server, model: 'resource.k2.Model0000000100', name: 'create' }) ) export class MyAction extends ServerActionWidget { // 当前动作是服务端动作,继承的是 ServerActionWidget @Widget.Reactive() private needInvisible = false; @Widget.Reactive() public get invisible(): boolean { return this.needInvisible; } protected mounted(): void { super.mounted(); // 模拟接口 http.query(模块名, `graphql`).then(res => { if(res) { this.needInvisible = true } }) } } 在实际应用中,我们可以调用后端接口,根据返回的数据动态修改 needInvisible 这个值,从而实现按钮的动态显示与隐藏效果。这样的设计使得按钮的显示状态更加灵活,并且能够根据后端数据动态调整,提高了系统的可定制性。

    前端 2023年11月23日
    95900
  • 如何自定义指定页面的样式

    可以通过在layout上给页面元素加css的class来解决此问题 import { registerLayout, ViewType } from '@kunlun/dependencies'; export const install = () => { registerLayout( ` <!– 给视图加class –> <view type="FORM" class="my-form-view"> <!– 给动作条组件加class –> <element widget="actionBar" slot="actionBar" class="my-action-bar" slotSupport="action" > <xslot name="actions" slotSupport="action" /> </element> <!– 给表单组件加class –> <element widget="form" slot="form" class="my-form-widget"> <xslot name="fields" slotSupport="pack,field" /> </element> </view> `, { viewType: ViewType.Form, // 页面的模型编码,可在浏览器地址栏的model=xxxx获取 model: 'resource.k2.Model0000000109', // 页面的动作名称,可在浏览器地址栏的action=xxxx获取 actionName: 'uiViewb2de116be1754ff781e1ffa8065477fa' } ); }; install(); 这样我们就可以在浏览器的html标签中查看到给组件加的class,通过加上这个作用域,可以给当前页面写特定样式

    2024年8月16日
    73700
  • 自定义前端拦截器

    某种情况下,我们需要通过自定义请求拦截器来做自己的逻辑处理,平台内置了一些拦截器 登录拦截器LoginRedirectInterceptor 重定向到登录拦截器LoginRedirectInterceptor import { UrlHelper, IResponseErrorResult, LoginRedirectInterceptor } from '@kunlun/dependencies'; export class BizLoginRedirectInterceptor extends LoginRedirectInterceptor { /** * 重定向到登录页 * @param response 错误响应结果 * @return 是否重定向成功 */ public redirectToLogin(response: IResponseErrorResult): boolean { if (window.parent === window) { const redirect_url = location.pathname; // 可自定义跳转路径 location.href = `${UrlHelper.appendBasePath('login')}?redirect_url=${redirect_url}`; } else { // iframe页面的跳转 window.open(`${window.parent.location.origin}/#/login`, '_top'); } return true; } } 请求成功拦截器RequestSuccessInterceptor 请求失败拦截器 RequestErrorInterceptor 网络请求异常拦截器 NetworkErrorInterceptor 当我们需要重写某个拦截器的时候,只需要继承对应的拦截器,然后重写里面的方法即可 // 自定义登录拦截器 export class CustomLoginRedirectInterceptor extends LoginRedirectInterceptor{ public error(response: IResponseErrorResult) { // 自己的逻辑处理 return true // 必写 } } // 自定义请求成功拦截器 export class CustomRequestSuccessInterceptor extends RequestSuccessInterceptor{ public success(response: IResponseErrorResult) { // 自己的逻辑处理 return true // 必写 } } // 自定义请求失败拦截器 export class CustomRequestErrorInterceptor extends RequestErrorInterceptor{ public error(response: IResponseErrorResult) { const { errors } = response; if (errors && errors.length) { const notPermissionCodes = [ SystemErrorCode.NO_PERMISSION_ON_MODULE, SystemErrorCode.NO_PERMISSION_ON_VIEW, SystemErrorCode.NO_PERMISSION_ON_MODULE_ENTRY, SystemErrorCode.NO_PERMISSION_ON_HOMEPAGE ]; /** * 用来处理重复的错误提示 */ const executedMessages: string[] = []; for (const errorItem of errors) { const errorCode = errorItem.extensions?.errorCode; if (!notPermissionCodes.includes(errorCode as any)) { const errorMessage = errorItem.extensions?.messages?.[0]?.message || errorItem.message; if (!executedMessages.includes(errorMessage)) { // 自己的逻辑处理 } executedMessages.push(errorMessage); } } } return true; } } // 自定义网络请求异常拦截器…

    前端 2023年11月1日
    74901
  • 前端低无一体使用教程

    介绍 客户在使用oinone平台的时候,有一些个性化的前端展示或交互需求,oinone作为开发平台,不可能提前预置好一个跟客户需求一模一样的组件,这个时候我们提供了一个“低无一体”模块,可以反向生成API代码,生成对应的扩展工程和API依赖包,再由专业前端研发人员基于扩展工程(kunlun-sdk),利用API包进行开发并上传至平台,这样就可以在界面设计器的组件里切换为我们通过低无一体自定义的新组件。 低无一体的具体体现 “低”— 指低代码,在sdk扩展工程内编写的组件代码 “无”— 指无代码,在页面设计器的组件功能处新建的组件定义 低无一体的组件跟直接在自有工程内写组件的区别? 低无一体的组件复用性更高,可以在本工程其他页面和其他业务工程中再次使用。 组件、元件介绍 元件 — 指定视图类型(viewType) + 指定业务类型(ttype)字段的个性化交互展示。组件 — 同一类个性化交互展示的元件的集合。组件是一个大一点的概念,比如常用的 Input 组件,他的元件在表单视图有字符串输入元件、密码输入元件,在详情和表格展示的时候就是只读的,页面最终使用的其实是元件。通过组件+ttype+视图类型+是否多值+组件名可以找到符合条件的元件,组件下有多个元件会根据最优匹配规则找到最合适的具体元件。 如何使用低无一体 界面设计器组件管理页面添加组件 进入组件的元件管理页面 点击“添加元件” 设计元件的属性 这里以是否“显示清除按钮”作为自定义属性从左侧拖入到中间设计区域,然后发布 点击“返回组件” 鼠标悬浮到卡片的更多按钮的图标,弹出下拉弹出“低无一体”的按钮 在弹窗内点击“生成SDK”的按钮 生成完成后,点击“下载模板工程” 解压模板工程kunlun-sdk.zip 解压后先查看README.MD,了解一下工程运行要点,可以先运行 npm i 安装依赖 再看kunlun-plugin目录下已经有生成好的组件对应的ts和vue文件 下面在vue文件内增加自定义代码,可以运行 npm run dev 在开发模式下调试看效果 <template> <div class="my-form-string-input"> <oio-input :value="realValue" @update:value="change" > <template #prepend>MyPrepend</template> </oio-input> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; import { OioInput } from '@kunlun/vue-ui-antd'; export default defineComponent({ name: 'customField1', components: { OioInput }, props: { value: { type: String }, change: { type: Function }, }, setup(props) { const realValue = ref<string | null | undefined>(props.value); return { realValue }; } }); </script> <style lang="scss"> .my-form-string-input { border: 1px solid red; } </style> 确定改好代码后运行 npm run build,生成上传所需的js和css文件 可以看到 kunlun-plugin目录下多出了dist目录,我们需要的是 kunlun-plugin.umd.js 和 kunlun-plugin.css 这2个文件 再次回到组件的“低无一体”管理弹窗页面,上传上面生成的js和css文件,并点击“确定”保存,到这里我们的组件就新增完成了。 下面我们再到页面设计器的页面中使用上面设计的组件(这里的表单页面是提前准备好的,这里就不介绍如何新建表单页面了) 将左侧组件库拉直最底部,可以看到刚刚新建的组件,将其拖至中间设计区域,我们可以看到自定义组件的展示结果跟刚刚的代码是对应上的(ps: 如果样式未生效,请刷新页面查看,因为刚刚上传的js和css文件在页面初始加载的时候才会导入进来,刚刚上传的动作未导入新上传的代码文件),再次点击设计区域中的自定义组件,可以看到右侧属性设置面板也出现了元件设计时拖入的属性。 最后再去运行时的页面查看效果,与代码逻辑一致! 注意事项 为什么我上传了组件后页面未生效? 检查前端启动工程的低无一体是否配置正确

    2023年11月6日
    2.6K00
  • 字段组件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日
    1.0K00

Leave a Reply

登录后才能评论