Class Component(ts)(v4)

Class Component

一种使用typescriptclass声明组件的方式。

IWidget类型声明

IWidget是平台内所有组件的统一接口定义,也是一个组件定义的最小集。

/**
 * 组件构造器
 */
export type WidgetConstructor<Props extends WidgetProps, Widget extends IWidget<Props>> = Constructor<Widget>;

/**
 * 组件属性
 */
export interface WidgetProps {
  [key: string]: unknown;
}

/**
 * 组件
 */
export interface IWidget<Props extends WidgetProps = WidgetProps> extends DisposableSupported {
  /**
   * 获取当前组件响应式对象
   * @return this
   */
  getOperator();

  /**
   * 组件初始化
   * @param props 属性
   */
  initialize(props: Props);

  /**
   * 创建子组件
   * @param constructor 子组件构造器
   * @param slotName 插槽名称
   * @param props 属性
   * @param specifiedIndex 插入/替换指定索引的子组件
   */
  createWidget<ChildProps extends WidgetProps = WidgetProps>(
    constructor: WidgetConstructor<ChildProps, IWidget<ChildProps>>,
    slotName?: string,
    props?: ChildProps,
    specifiedIndex?: number
  );
}

Widget

Widget是平台实现的类似于Class Component组件抽象基类,定义了包括渲染、生命周期、provider/inject、watch等相关功能。

export abstract class Widget<Props extends WidgetProps = WidgetProps, R = unknown> implements IWidget<Props> {
  /**
   *  添加事件监听
   *
   * @param  {string} path 监听的路径
   * @param  {deep?:boolean;immediate?:boolean} options?
   *
   * @example
   *
   * @Widget.Watch('formData.name', {deep: true, immediate: true})
   * private watchName(name: string) {
   *   ... todo
   * }
   *
   */
  protected static Watch(path: string, options?: { deep?: boolean; immediate?: boolean });

  /**
   * 可以用来处理不同widget之间的通讯,当被订阅的时候,会将默认值发送出去
   *
   * @param  {Symbol} name 唯一标示
   * @param  {unknown} value? 默认值
   *
   * @example
   *
   * const identifier = Symbol('example-sub')
   *
   * * field.ts * // 文件
   *
   * @Widget.BehaviorSubContext(identifier, {value: '这是默认值'})
   * private exampleSub!:WidgetBehaviorSubjection<{value: string}>
   *
   * onValueChange() {
   *   this.exampleSub.subject.next({value: '这是新的值'})
   * }
   *
   * * other-field.ts * // 文件
   * @Widget.BehaviorSubContext(identifier, {value: '这是默认值'})
   * private exampleSub!:WidgetBehaviorSubjection<{value: string}>
   *
   * mounted() {
   *  this.exampleSub.subscribe((value) => {
   *    ...todo
   *  })
   * }
   *
   */
  protected static BehaviorSubContext(name: Symbol, value?: unknown);

  /**
   * 与 `BehaviorSubContext` 一样,区别在于第一次不会发射默认值
   *
   * @param  {Symbol} name 唯一标示
   * @param  {unknown} value? 默认值
   */
  protected static SubContext(name: Symbol, value?: unknown);

  /**
   * 将数据绑定为响应式,并且组件中可以获取该值
   */
  protected static Reactive(params?: { displayName?: string; render?: boolean });

  /**
   * 将方法绑定为能够被组件获取的方法
   */
  protected static Method(displayName?: string);

  /**
   * 获取上级注入的依赖,与 Provide 一起使用
   *
   * @param  {string|Symbol} injectName? 被注入的name,如果不传递,那么取target中的name
   *
   * @example
   *
   * * children.ts * // 文件
   *
   * @Widget.Inject('InjectName')
   * private rootData!:
   *
   * 如果要将该值变为响应式,如果加上Reactive
   *
   *  @Widget.Reactive()
   *  @Widget.Inject('InjectName')
   *  private rootData!:;
   */
  protected static Inject(injectName?: string | Symbol);

  /**
   * 获取下级注入的依赖,与 Inject 一起使用
   *
   * @param  {string|Symbol} provideName? 被注入的name,如果不传递,那么取target中的name
   *
   * @example
   *
   * * parent.ts * // 文件
   *
   * @Widget.Provide('ProvideName')
   * private rootData!:
   *
   * 如果要将该值变为响应式,如果加上Reactive
   *
   *  @Widget.Reactive()
   *  @Widget.Provide('ProvideName')
   *  private rootData!:;
   */
  protected static Provide(provideName?: string | Symbol);

  /**
   * 通过唯一键获取对应组件
   * @param handle 唯一键
   */
  public static select(handle: string): Widget | undefined;

  public constructor(handle?: string);

  /**
   * 获取当前组件响应式对象
   */
  public getOperator();

  /**
   * 组件初始化
   * @param props 属性
   */
  public initialize(props: Props = {} as Props);

  /**
   * 获取组件唯一键
   */
  public getHandle(): string;

  /**
   * 创建一个widget
   *
   * @param constructor 对应的widget
   * @param name 插槽
   * @param config widget中initialize方法接收的参数
   * @param specifiedIndex 插入到对应位置的索引
   */
  public createWidget<T extends Widget>(
    constructor: WidgetConstructor<T['config'], T>,
    name?: string,
    config?: T['config'],
    specifiedIndex?: number
  );

  /**
   * 销毁当前widget
   */
  public dispose();

  /**
   * 获取父widget
   */
  public getParent(): Widget | undefined;

  /**
   * 获取所有的children
   */
  public getChildren(): Widget[];

  /**
   * 组件渲染
   * @param args 任意渲染参数
   * @return 框架VDom
   */
  public abstract render(...args): R;

  /**
   * beforeCreated 钩子函数
   * @protected
   */
  protected beforeCreated(): void;

  /**
   * created 钩子函数
   * @protected
   */
  protected created(): void;

  /**
   * beforeMount 钩子函数
   * @protected
   */
  protected beforeMount(): void;

  /**
   * mounted 钩子函数
   * @protected
   */
  protected mounted(): void;

  /**
   * beforeUpdate 钩子函数
   * @protected
   */
  protected beforeUpdate(): void;

  /**
   * updated 钩子函数
   * @protected
   */
  protected updated(): void;

  /**
   * activated 钩子函数
   * @protected
   */
  protected activated(): void;

  /**
   * deactivated 钩子函数
   * @protected
   */
  protected deactivated(): void;

  /**
   * beforeUnmount 钩子函数
   * @protected
   */
  protected beforeUnmount(): void;

  /**
   * unmounted 钩子函数
   * @protected
   */
  protected unmounted(): void;
}

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

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

Like (0)
oinone's avataroinone
Previous 2023年6月20日 pm4:07
Next 2023年11月2日 pm1:58

相关推荐

  • 自定义表格支持合并或列、表头分组

    本文将讲解如何通过自定义实现表格支持单元格合并和表头分组。 点击下载对应的代码 在学习该文章之前,你需要先了解: 1: 自定义视图2: 自定义视图、字段只修改 UI,不修改数据和逻辑3: 自定义视图动态渲染界面设计器配置的视图、动作 1. 自定义 widget 创建自定义的 MergeTableWidget,用于支持合并单元格和表头分组。 // MergeTableWidget.ts import { BaseElementWidget, SPI, ViewType, TableWidget, Widget, DslRender } from '@kunlun/dependencies'; import MergeTable from './MergeTable.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'MergeTableWidget' }) ) export class MergeTableWidget extends TableWidget { public initialize(props) { super.initialize(props); this.setComponent(MergeTable); return this; } /** * 表格展示字段 */ @Widget.Reactive() public get currentModelFields() { return this.metadataRuntimeContext.model.modelFields.filter((f) => !f.invisible); } /** * 渲染行内动作VNode */ @Widget.Method() protected renderRowActionVNodes() { const table = this.metadataRuntimeContext.viewDsl!; const rowAction = table?.widgets.find((w) => w.slot === 'rowActions'); if (rowAction) { return rowAction.widgets.map((w) => DslRender.render(w)); } return null; } } 2. 创建对应的 Vue 组件 定义一个支持合并单元格与表头分组的 Vue 组件。 <!– MergeTable.vue –> <template> <vxe-table border height="500" :column-config="{ resizable: true }" :merge-cells="mergeCells" :data="showDataSource" @checkbox-change="checkboxChange" @checkbox-all="checkedAllChange" > <vxe-column type="checkbox" width="50"></vxe-column> <!– 渲染界面设计器配置的字段 –> <vxe-column v-for="field in currentModelFields" :key="field.name" :field="field.name" :title="field.label" ></vxe-column> <!– 表头分组 https://vxetable.cn/v4.6/#/table/base/group –> <vxe-colgroup title="更多信息"> <vxe-column field="role" title="Role"></vxe-column> <vxe-colgroup title="详细信息"> <vxe-column field="sex" title="Sex"></vxe-column> <vxe-column field="age" title="Age"></vxe-column> </vxe-colgroup> </vxe-colgroup> <vxe-column title="操作" width="120"> <template #default="{ row, $rowIndex }"> <!– 渲染界面设计器配置的行内动作 –> <row-action-render :renderRowActionVNodes="renderRowActionVNodes" :row="row" :rowIndex="$rowIndex" :parentHandle="currentHandle" ></row-action-render> </template> </vxe-column> </vxe-table> <!– 分页 –> <oio-pagination :pageSizeOptions="pageSizeOptions" :currentPage="pagination.current"…

    2025年1月9日
    2.0K00
  • 前端打包文件上传到oss

    打包dist文件上传到oss教程 1: 确保boot工程里面安装了「webpack-aliyun-oss」依赖, 如果没有安装,可以手动安装下 2: package.json -> devDependencies -> {"webpack-aliyun-oss": "0.5.2"} 3: 在vue.config.js文件中添加对应的plugin // vue.config.js文件 // 新增代码 let BASE_URL = '/'; const { DEPLOY, OSS_REGION, OSS_DIST, OSS_URL, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, OSS_BUCKET } = process.env; const UNIQUE_KEY = Math.random(); switch (DEPLOY) { case 'online': BASE_URL = `${OSS_URL}${UNIQUE_KEY}/`; break; default: BASE_URL = '/'; } module.exports = { …原先的代码, publicPath: BASE_URL, plugins: [ …原先的插件, DEPLOY === 'online' ? new WebpackAliyunOss({ from: ['./dist/**/**', '!./dist/**/*.html'], // build目录下除html之外的所有文件 dist: `${OSS_DIST}/${UNIQUE_KEY}`, // oss上传目录 region: OSS_REGION, // 地区, 比如 oss-cn-hangzhou accessKeyId: OSS_ACCESS_KEY_ID, accessKeySecret: OSS_ACCESS_KEY_SECRET, bucket: OSS_BUCKET, timeout: 1200000, deleteOrigin: true, deleteEmptyDir: true, overwrite: true }) : () => {} ] } 4: 在vue.config.js同级目录下面新增「.env」文件, 写入对应的配置, 配置对应的值按照自己的oss配置来 DEPLOY=online OSS_BUCKET=xxx OSS_DIST=/oinone/product OSS_URL=https://xxxxxx/oinone/product/ OSS_REGION=oss-cn-hangzhou OSS_ACCESS_KEY_ID=xxxx OSS_ACCESS_KEY_SECRET=xxxx

    2023年11月1日
    1.5K00
  • 字段组件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.7K00
  • oinone的GraphQL使用指南

    如果之前没了解过GraphQL,可以先查看GraphQL的文档 为什么oinone要选用GraphQL? 我们先看一下oinone独特的元数据设计 介绍信息来源于Oinone 7天从入门到精通,如提示无权限,则需要申请 再看一下GraphQl的介绍 我们可以看出,GraphQL提供的特性可以满足我们对元数据的描述需求,因此我们选用GraphQL。 相关工具推荐 可视化gql请求工具: 官方下载地址 oinone工具包-win版 oinone工具包-mac版 模拟登录请求 点击“Refresh Schema”按钮手动同步后端的gql文档数据 点击“show Documentation”按钮查看gql文档,可以在搜索框内输入关键字查询相关文档

    2023年11月1日
    1.6K00
  • 上下文在字段和动作中的应用

    上下文在字段和动作中的应用 在业务场景中,常常需要在打开弹窗或跳转到新页面时携带当前页面数据。此时,我们需要配置相关「动作」中的上下文信息。 在 oinone 平台中,上下文主要分为以下三种: activeRecord:当前视图数据 rootRecord:主视图数据 openerRecord:触发弹窗的对象 参考文档:oinone内的主视图数据和当前视图数据使用介绍 activeRecord 表示当前视图的数据。例如,若动作配置在表单上,则指代当前表单的数据;若配置在 o2m、m2m 字段表格上,则指代选中的行数据。 rootRecord 表示根视图的数据。若当前视图是表单页,则代表表单的数据;若为表格页,则代表表格的数据。 openerRecord 表示触发弹窗的对象。例如,在弹窗内的字段或动作中,可通过 openerRecord 获取触发弹窗的信息。 这三者均为对象 (Object) 类型。 界面设计器配置 在 o2m、m2m 表格字段弹窗中携带当前视图数据 假设我们设计了一个包含 o2m、m2m 表格字段的表单页面。打开相关弹窗时,需将表单中的 code 数据传递至弹窗中。 选择相应的「动作」,如创建或添加。在右侧属性面板底部找到「上下文」,添加格式为对象 {} 的上下文信息。 以键值对的格式添加上下文信息:{code: rootRecord.code}。 设计弹窗时,将 code 字段拖入弹窗中。 完成设计后保存并发布。 大家可以看到,上下文中的key是 code,但是value是rootRecord.code,这里取的是rootRecord而不是activeRecord,因为我们上面讲过如果当前动作配置在o2m、m2m的字段表格上面,那么activeRecord就是表格选中的行,我们现在要取的是表单上的code字段,所以需要用rootRecord。 注意点:key需要是提交模型【前端视图】存在的字段才能传递。

    2023年11月8日
    2.8K10

Leave a Reply

Please Login to Comment