前端自定义字段与视图最佳方案

自定义视图获取数据

在某些情况下,oinone 提供的默认视图无法满足需求,这时我们就需要自定义视图。通常,虽然视图的 UI 不足以满足要求,但数据结构是不变的。此时,重点是修改页面 UI,数据的请求与回填可以利用平台默认的能力。

如何实现?

  1. 界面设计器的使用
    1. 你可以通过界面设计器先配置页面,平台在运行时会根据设计器生成对应的 GraphQL 请求,并自动回填数据。
  2. 视图的数据结构
    1. 视图的数据类型分为可以分为 Object 跟 List
      1. Object 代表当前视图的数据结构是对象
      2. List 代表当前视图的数据结构是数组。

如果我们将 Object 跟 List 分的更细一点就变成了这样:

1: Object: 对象,代表当前视图的数据结构是单个对象,例如:表单视图、详情视图
1: List: 对象数组,代表当前视图的数据结构数组,例如:表格视图、卡片视图、画廊视图

视图类型 平台组件 数据属性
表格视图 TableWidget dataSource
画廊视图 GalleryWidget dataSource
表单视图 FormWidget formData
详情视图 DetailWidget formData

自定义视图时,需要先确认当前视图的类型,再通过界面设计器进行页面配置。前端部分只需继承相应的组件,平台底层会自动处理接口数据的获取与回填。

表单视图示例:

import Form from './Form.vue';

// 自定义表单视图
@SPI.ClassFactory(
  BaseElementWidget.Token({
    widget: 'custom-form'
  })
)
export class CustomForm extends FormWidget {
  public initialize(props: Props) {
    super.initialize(props);
    this.setComponent(Form);
    return this;
  }
}

Vue 组件:

<template></template>
<script lang="ts">
export default defineComponent({
  props: {
    formData: {
      // 当前表单的数据
      type: Object,
      default: () => ({})
    }
  }
});
</script>

自定义layout

// 原始的layout模版
<view type="FORM">
    <element widget="actionBar" slot="actionBar" slotSupport="action">
        <xslot name="actions" slotSupport="action" />
    </element>
    <element widget="form" slot="form">
        <xslot name="fields" slotSupport="pack,field" />
    </element>
</view>

//自定义的layout模版
<view type="FORM">
    <element widget="actionBar" slot="actionBar" slotSupport="action">
        <xslot name="actions" slotSupport="action" />
    </element>
    <element widget="custom-form" slot="form">
        <xslot name="fields" slotSupport="pack,field" />
    </element>
</view>

其实就是把 widget="form" 改成 widget="custom-form"

表格视图示例:

import Table from './Table.vue';

// 自定义表格视图
@SPI.ClassFactory(
  BaseElementWidget.Token({
    widget: 'custom-table'
  })
)
export class CustomTable extends TableWidget {
  public initialize(props: Props) {
    super.initialize(props);
    this.setComponent(Table);
    return this;
  }
}

Vue 组件:

<template></template>
<script lang="ts">
export default defineComponent({
  props: {
    dataSource: {
      // 当前列表的数据
      type: Object,
      default: () => ({})
    }
  }
});
</script>

自定义layout

// 原始的layout模版
<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" slotSupport="field">
                <xslot name="searchFields" slotSupport="field" />
            </element>
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar" slotSupport="action">
            <xslot name="actions" slotSupport="action" />
        </element>
        <element widget="table" slot="table" slotSupport="field">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" slotSupport="field" />
            <element widget="rowActions" slot="rowActions" slotSupport="action" />
        </element>
    </pack>
</view>

//自定义的layout模版
<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" slotSupport="field">
                <xslot name="searchFields" slotSupport="field" />
            </element>
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar" slotSupport="action">
            <xslot name="actions" slotSupport="action" />
        </element>
        <element widget="custom-table" slot="table" slotSupport="field">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" slotSupport="field" />
            <element widget="rowActions" slot="rowActions" slotSupport="action" />
        </element>
    </pack>
</view>

其实就是把 widget="table" 改成 widget="custom-table"

以上代码展示了如何继承对应的 widget,平台会自动调用接口获取数据。如果你需要自定义接口调用,请参考然后参考这篇文章,调用接口

自定义字段获取数据、提交数据

在 oinone 中,常见的自定义字段分为两种,一种是表单字段,一种是列表字段。

表单字段

1.  所有的表单字段都有 value 属性,用来存储当前字段对应的值
2.  所有的表单字段都有 formData 属性,用来获取其他字段的值,也可以修改其他字段的值
3.  所有的表单字段都有 change 方法,用来修改当前字段对应的值

示例:

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: [ViewType.Form, ViewType.Search],
    ttype: ModelFieldType.String
  })
)
export class CustomFormFieldWidget extends FormFieldWidget {
  mounted() {
    console.log(this.value); // 获取当前字段的值
    console.log(this.formData); // 获取当前表单的值
    this.change('hello'); // 修改当前字段的值
  }
}

Vue 组件:

<template>
  <div @click="change('hi')">{{ value }}</div>
</template>
<script lang="ts">
export default defineComponent({
  props: {
    value: {
      type: String
    },
    change: {
      type: Function
    }
  }
});
</script>

列表字段

  1. 所有的列表字段都有 dataSource 属性,用来获取当前列表的数据
  2. 所有的列表字段中的 renderDefaultSlot 方法,用来渲染单元格组件

示例:

@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Table,
    ttype: [ModelFieldType.String, ModelFieldType.Phone, ModelFieldType.Email]
  })
)
export class CustomTableFieldWidget extends BaseFieldWidget {
  @Widget.Method()
  public renderDefaultSlot(context): VNode[] | string {
    const currentValue = this.compute(context) as string[];
    return [createVNode(TableVue, { value: currentValue })];
  }
}

Vue 组件:

// Table.vue
<template>
  <div>{{ value }}</div>
</template>
<script lang="ts">
export default defineComponent({
  props: {
    value: {
      type: String
    }
  }
});
</script>

通过这些自定义实现,你可以灵活地控制视图和字段的数据获取与修改,并且能够利用平台的 GraphQL 能力来实现更简便的数据操作。

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

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

(2)
汤乾华的头像汤乾华数式员工
上一篇 2024年10月16日 pm4:56
下一篇 2024年10月17日 pm9:13

相关推荐

  • 自定义的复杂字段配置透出字段

    学习这篇文章之前,需要先学会使用在界面设计器自定义一个前端组件,如果您还不会,可以先看这篇文章 默认情况下,当开前端发人员自定义了一个复杂字段,比如M2O、O2M、M2M的字段,那么Graphql查询的时候,只会查询id跟name这两个字段,如果还想查询字段的字段,那么可以通过配置化的方式来处理 1: 在界面设计器的组件区域中新增对应的字段 2: 设计元件,在模型区域中搜索选项字段列表,拖到设计区域,然后保存 3: 去对应的设计页面,刷新下页面,选中对应的字段,可以看到右侧有选项字段列表4: 输入期望Graphql查询字段,保存发布

    2023年11月9日
    1.4K00
  • [前端]平台内置的基类

    前端平台内置了多个基类,允许开发者通过继承的方式来实现字段、视图以及动作。以下是一些常见的基类: 视图基类 通用视图基类 BaseElementWidget BaseElementWidget 是所有视图的通用基类,无论是何种视图,都可以继承这个基类。它封装了一系列属性和API,帮助开发者更轻松地创建各种视图组件。 表单类型的视图基类 BaseElementObjectViewWidget BaseElementObjectViewWidget 是表单视图的基类,它是BaseElementWidget的扩展。这个基类内部自动处理请求发起,以及数据刷新等一系列操作。 表格类型的视图基类 BaseElementListViewWidget BaseElementListViewWidget 是表格视图的基类,同样也是基于BaseElementWidget的扩展。它内部处理自动请求发起和数据刷新等操作,与BaseElementObjectViewWidget类似。 字段基类 表单字段基类 FormFieldWidget FormFieldWidget 是表单字段的基类,它封装了一系列属性和API,用于简化表单字段的开发。 表格字段基类 BaseTableFieldWidget BaseTableFieldWidget 是表格字段的基类,它封装了一系列属性和API,有助于开发者更轻松地创建表格字段。 动作基类 服务端动作基类 ServerActionWidget 跳转动作基类 RouterViewActionWidget 跳转动作基类(打开抽屉) DrawerViewActionWidget 跳转动作基类(打开抽屉) DrawerViewActionWidget 通过使用这些基类,开发者可以提高代码的可重用性和可维护性,从而更高效地开发前端应用。这些基类旨在帮助开发者更轻松地构建功能丰富的应用程序。

    2023年11月15日
    1.1K00
  • Class Component(ts)(v4)

    Class Component 一种使用typescript的class声明组件的方式。 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 *…

    2023年11月1日
    1.3K00
  • 如何增加页面消息通知轮询的间隔或者关闭轮询

    场景 oinone的前端页面默认自带了消息通知功能,在顶部状态栏可以看到消息的查看入口,默认每隔5秒查询一次最新的消息,我们可以通过自定义消息组件增加该间隔或者是关闭轮询 示例代码 修改轮询间隔 import { MaskWidget, NotificationWidget, SPI } from '@kunlun/dependencies'; @SPI.ClassFactory(MaskWidget.Token({ widget: 'notification' })) export class DemoNotificationWidget extends NotificationWidget { /** * 轮询间隔时间,单位: 秒 * @protected */ protected msgDelay = 30000; } 关闭轮询 import { MaskWidget, NotificationWidget, SPI } from '@kunlun/dependencies'; @SPI.ClassFactory(MaskWidget.Token({ widget: 'notification' })) export class DemoNotificationWidget extends NotificationWidget { protected mounted() { super.mounted(); // 清除轮询的定时器 this.msgTimer && clearInterval(this.msgTimer); // 挂载后手动查询一次消息 this.getMsgTotal(); } }

    2024年8月20日
    1.2K00
  • 打开弹窗的action,传入默认的查询条件不生效

    场景 form视图中的action,点击后打开table的弹窗的,xml中配置的filter,但是table查询的时候没有带上查询条件: <action name=”action_name” label=”打开tabel弹窗视图” filter=”id==${activeRecord.id}” /> 解决方案 将xml中的activeRecord修改成openerRecord即可。 <action name=”action_name” label=”打开tabel弹窗视图” filter=”id==${openerRecord.id}” />

    2023年11月1日
    1.3K00

Leave a Reply

登录后才能评论