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低代码应用平台体验

(0)
oinone的头像oinone
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 自定义组件之手动渲染弹出层(v4)

    阅读之前 你应该: 了解自定义组件相关内容。 自定义组件之手动渲染基础(v4) 弹出层组件 我们内置了两个弹出层组件,弹窗(Dialog)和抽屉(Drawer),以下所有内容全部围绕弹窗(Dialog)进行描述,抽屉相关内容与弹窗完全一致。 下面这个对照表格可以帮助你区分两个弹出层组件的异同: 弹出层相关功能 弹窗(Dialog) 抽屉(Drawer) API方法 Dialog#create Drawer#create 内置组件 DialogWidget DrawerWidget 内置插槽 header, default, footer header, default, footer 渲染弹出层的方式 我们提供了三种渲染弹出层组件的方式,根据不同的情况使用不同的方式可以让实现变得更简单。 使用Dialog#createAPI方法创建弹窗:一般用于简单场景,动作区无法进行自定义。 使用DSL渲染能力创建弹窗 调用ActionWidget#click方法打开弹窗(适用于自动渲染场景) 使用createWidget创建DialogWidget并打开弹窗(适用于手动渲染场景) 使用第三方组件创建弹窗:一般用于弹窗需要自定义的场景中,性能最优,可用于任何场景。 使用Dialog#createAPI方法创建弹窗 以下是一个自定义组件的完整示例,其使用ViewCache#compule方法获取视图。 view.ts export const template = `<view> <field data="id" invisible="true" /> <field data="code" label="编码" /> <field data="name" label="名称" /> </view>`; ManualDemoWidget.ts import { BaseElementWidget, createRuntimeContextForWidget, Dialog, FormWidget, MessageHub, RuntimeView, SPI, ViewCache, ViewType, Widget } from '@kunlun/dependencies'; import ManualDemo from './ManualDemo.vue'; import { template } from './view'; @SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' })) export class ManualDemoWidget extends BaseElementWidget { public initialize(props) { super.initialize(props); this.setComponent(ManualDemo); return this; } @Widget.Method() public async openPopup() { // 获取运行时视图 const view = await this.fetchViewByCompile(); if (!view) { console.error('Invalid view'); return; } // 创建运行时上下文 const runtimeContext = createRuntimeContextForWidget(view); const runtimeContextHandle = runtimeContext.handle; // 获取初始化数据 const formData = await runtimeContext.getInitialValue(); // 创建弹窗 const dialogWidget = Dialog.create(); // 设置弹窗属性 dialogWidget.setTitle('这是一个演示弹窗'); // 创建所需组件 dialogWidget.createWidget(FormWidget, undefined, { metadataHandle: runtimeContextHandle, rootHandle: runtimeContextHandle, dataSource: formData, activeRecords: formData, template: runtimeContext.viewTemplate, inline: true }); dialogWidget.on('ok', () => { MessageHub.info('click ok'); // 关闭弹窗 dialogWidget.onVisibleChange(false); }); dialogWidget.on('cancel', () => { MessageHub.info('click cancel'); // 关闭弹窗 dialogWidget.onVisibleChange(false); }); // 打开弹窗…

    前端 2023年11月1日
    91800
  • 【前端】工程结构最佳实践(v4)

    阅读之前 你应该: 了解node与npm相关内容 了解lerna包管理工具的相关内容 官方文档 了解git仓库的相关内容 了解rollup的相关内容 工程结构包示例 Vue项目结构包下载 工程结构详解 工程结构 ├── packages │   ├── kunlun-boot │   │   ├── package.json │   │   ├── public │   │   │   ├── favicon.ico │   │   │   └── index.html │   │   ├── src │   │   │   ├── main.ts │   │   │   └── shim-vue.d.ts │   │   ├── tsconfig.json │   │   └── vue.config.js │   ├── kunlun-module-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-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.0K00
  • 前端自定义左树右表中的树

    在 oinone 平台中,提供了默认的左树右表的视图,用户可以通过界面设计器配置,默认的树视图不一定满足所有需求,尤其当需要自定义功能或复杂的交互时,我们可以通过自定义视图来实现更灵活的展现。 本文将带你一步步了解如何自定义左树右表视图中的树组件。 自定义树视图 1. 使用界面设计器配置视图 首先,我们需要通过界面设计器生成基础的左树右表视图。界面设计器允许用户根据不同需求进行拖拽配置,快速创建可视化界面。 配置完视图之后,我们可以重写左侧的树组件。Oinone 的默认树组件是 TableSearchTreeWidget,通过自定义的方式,我们可以实现更高级的功能。 2. 重写 TableSearchTreeWidget import { BaseElementWidget, SPI, TableSearchTreeWidget, ViewType } from '@kunlun/dependencies'; import CustomTableSearchTree from './CustomTableSearchTree.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: [ViewType.Table, ViewType.Form], widget: 'tree', model: 'resource.k2.Model0000000100' // 改成自己的模型 }) ) export class CustomTableSearchTreeWidget extends TableSearchTreeWidget { public initialize(props) { super.initialize(props); this.setComponent(CustomTableSearchTree); return this; } } 3. 定义 Vue 树组件 接下来,我们来实现 CustomTableSearchTree.vue 组件。这个组件将处理树的数据加载、节点选中等逻辑。你可以根据项目的需要修改其中的交互逻辑或 UI 设计。 <template> <a-tree :load-data="onLoadData" :tree-data="treeData" @select="onSelected" /> </template> <script lang="ts"> import { OioTreeNode, TreeUtils } from '@kunlun/dependencies'; import { computed, defineComponent } from 'vue'; export default defineComponent({ props: { rootNode: { type: Object }, loadData: { type: Function, required: true }, onSelected: { type: Function, required: true } }, setup(props) { // // 计算树的数据源,使用 TreeUtils 处理 const treeData = computed(() => { return TreeUtils.fillLoadMoreAction([…(props.rootNode?.children || [])]); }); // 异步加载子节点 const onLoadData = async (node) => { return await props.loadData(node.dataRef); }; // 处理节点选中事件 const onSelected = ( selectedKeys: string[], e: { nativeEvent: PointerEvent; node: { dataRef: OioTreeNode }; selected: boolean } ) => { props.onSelected?.(e.node.dataRef, e.selected); }; return { treeData, onLoadData, onSelected }; } }); </script> 4. 自定义…

    2024年10月21日
    2.0K00
  • Oinone平台可视化调试工具

    为方便开发者定位问题,我们提供了可视化的调试工具。
    该文档将介绍可视化调试工具的基本使用方法。

    2024年4月13日
    1.0K00
  • 自定义视图组件(v4)

    阅读之前 你应该: 了解DSL相关内容。母版-布局-DSL 渲染基础(v4) 了解SPI机制相关内容。组件SPI机制(v4) 什么是视图组件 我们将一个视图中提供数据源的组件称为视图组件。 下面,我们将根据提供的示例布局进行进一步介绍。 示例布局(默认表格视图布局) <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: 视图标签;一个视图中的所有组件将共享数据源,视图的数据源通过视图组件进行提供。(在这个示例中,该视图的数据源通过widget="table"(TableWidget)提供) pack: 容器组件标签; element: 通用元素组件标签; xslot:dsl插槽; 根据标签性质,我们可以将这个示例布局进一步简化,只留下我们目前要关注的主要内容。 <view type="TABLE"> <element widget="table" slot="table"> <xslot name="fields" /> </element> </view> 在以上示例布局中,有且仅有一个组件会向视图提供数据源,那就是widget="table"(TableWidget)这个组件。我们接下来将对这个组件进行自定义,以实现业务中所需的列表(List)数据源展示方式。 1 平台组件简介 平台提供的基础组件有如下几种: 组件名称 描述 BaseElement element标签通用组件 BaseElementViewWidget 通用视图组件 BaseElementObjectViewWidget 对象(Object)数据源通用视图组件 BaseElementListViewWidget 列表(List)数据源通用组件 平台提供的内置组件有如下几种:(均使用element标签) 组件名称 标签 视图类型 描述 TableWidget widget="table" TABLE 内置表格组件 FormWidget widget="form" FORM 内置表单组件 DetailWidget widget="detail" DETAIL 内置详情组件 GallertWidget widget="gallery" GALLERY 内置画廊组件 TreeWidget/CardCascaderWidget widget="tree/cardCascader" TREE 内置树/卡片级联组件 我们可以根据业务场景,继承不同的组件,来实现自己的业务场景。在自定义过程中,我们建议尽可能的将逻辑控制在组件内部。如果场景是唯一且确定的,也可以进行一些特殊逻辑处理。 2 场景:实现一个虚拟滚动表格(不使用分页器) 2.1 确定组件名称 widget="VirtualTable" 通过布局设置自定义组件名称 我们将原表格布局中的widget="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="VirtualTable" slot="table"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" /> <element widget="rowActions" slot="rowActions" /> </element> </pack> </view> 通过DSL设置自定义组件名称 我们使用了slot="table"这个插槽,通过属性合并覆盖的方式,在DSL上直接指定我们所需要的自定义组件名称即可。 这种修改方式更适用于个别几个视图需要使用该组件的情况。 <view type="TABLE"> <template slot="table" widget="VirtualTable"> …… </template> </view> 2.2 简单实现一个基础功能的虚拟滚动表格 定义一个VirtualTable.vue文件,使用平台提供的oio-table组件。目前内部采用vxe-table封装,相关api文档 点击查看 props定义: showDataSource: 当前展示数据;通过平台内置BaseElementListViewWidget组件提供。 <template> <oio-table ref="table" border show-overflow height="400" :row-config="{ isHover: true…

    2023年11月1日
    79800

Leave a Reply

登录后才能评论