前端视图的元数据与数据的传递、交互

在阅读本篇文章之前,您需要学习以下知识点:

1: 元数据

视图的元数据

在日常开发中,我们会经常遇到自定义的字段、动作、视图需要界面设计器配置的数据,这些数据可能是当前页面的字段,也有可能动作,那么如何获取呢?

视图元数据分为两种:
1: 当前视图(metadataRuntimeContext)
2: 根视图(rootRuntimeContext)

那么这两种类型怎么区分呢?

举个例子:
1: 如果当前字段是在表单中,那么当前视图就是表单,根视图就表单的父级视图,如果只有一个表单视图,那么当前视图就是根视图。
2: 如果当前视图是表单,但是表单里面有个表格,对于表格字段而言,当前视图就是表格,根视图就是表单。

当前视图的元数据(metadataRuntimeContext)

在前端,我们通过 metadataRuntimeContext 来获取视图的元数据,例如:

export class CustomFormStringFieldSingleWidget extends FormStringFieldSingleWidget {
  protected mounted(): void {
    console.log(this.metadataRuntimeContext);
  }

    /**
  * 界面设计器配置的动作
  */
  @Widget.Reactive()
  protected get modelActions() {
    return this.metadataRuntimeContext.model.modelActions
  }

   /**
  * 界面设计器配置的字段
  */
  @Widget.Reactive()
  protected get modelFields() {
    return this.metadataRuntimeContext.model.modelFields
  }
}
属性名 类型 可选性 描述
viewAction RuntimeViewAction 运行时跳转动作(通过跳转动作创建的运行时上下文具备该属性)
module RuntimeModule 运行时模块
model RuntimeModel 运行时模型
view RuntimeView 运行时视图
viewLayout DslDefinition \| undefined 视图布局 DSL,从运行时视图解析获得
viewDsl DslDefinition \| undefined 视图模板 DSL,从运行时视图解析获得
viewTemplate DslDefinition 视图最终执行 DSL,从运行时视图解析获得或根据布局 DSL 和模板 DSL 合并生成
getModel (model: string, isBelong?: boolean) => GetModelResult \| undefined 获取模型,返回获取的模型和所在的运行时上下文
getModelField (data: string, isBelong?: boolean) => GetModelFieldResult \| undefined 获取模型字段,返回获取的模型字段和所在的运行时上下文
getRequestModelFields (options?: GetRequestModelFieldsOptions) => RequestModelField[] 获取请求字段
getDefaultValue () => Promise<Record<string, unknown>> 获取默认值
getInitialValue () => Promise<Record<string, unknown>> 获取初始值

运行时模型(model)

属性名 类型 可选性 描述
id string 模型 id
model string 模型编码
name string 技术名称
modelFields RuntimeModelField[] 模型字段
modelActions RuntimeAction[] 模型动作
type ModelType 模型类型
module string 模块编码
moduleName string 模块名称
moduleDefinition RuntimeModule 模块定义
pks string[] 主键
uniques string[][] 唯一键
indexes string[][] 索引
sorting string 排序
label string 显示标题
labelFields string[] 标题字段

可通过modelFieldsmodelActions获取界面设计器配置的模型字段、模型动作。

当前视图的 dsl(viewDsl)

视图的 DSL,从运行时视图解析获得,如果模型中的modelFieldsmodelActions获取不到的对应的字段、动作,那么可从该属性中获取

默认值(getDefaultValue)

用来获字段的默认值(界面配置器配置的)

根视图的元数据(rootRuntimeContext)

根视图的元数据结构跟当前视图的元数据类似。

视图数据

视图数据是贯穿于应用程序前端和视图层的关键数据,它承载了应用逻辑到用户界面之间的桥梁作用。

在 oinone 前端中,常见的视图数据分为如下几种:

1: 表单数据(formData)
2: 列表数据(dataSource)
3: 根视图数据(rootData)
4: 当前字段、动作所在的区域数据(activeRecords)
5: 按钮打开弹窗、抽屉的时候,按钮所在的区域数据(openerActiveRecords)

formData

formData 是一个对象,对象中的每个属性对应一个表单项,表单项的值就是该属性的值。所有的表单字段都可以通过this.formData来访问,也可以通过this.formData.xxx来获取某个字段的值。

dataSource

dataSource 是一个数组,数组中的每个元素对应一个列表项,列表项的值就是该元素。所有的列表字段都可以通过this.dataSource来访问,也可以通过this.dataSource[0].xxx来获取某个列表字段的值。

rootData

rootData 是一个数组,如果当前视图是列表,那么 rootData 是列表的数据源。如果当前视图是表单,那么 rootData 是表单的数据源,其中的第 0 项是当前表单的数据。

activeRecords

activeRecords 是一个数组

1: 表单视图的动作
    1.1: activeRecords 是表单的数据源,其中的第 0 项是当前表单的数据。
2: 表格视图上方的动作
    2.1: activeRecords 是当前选中的行数据,其中的第 0 项是对应的数据。
3: 表格的行内动作
    3.1: activeRecords 是当前行数据,其中的第 0 项是对应的数据。

openerActiveRecords

openerActiveRecords 是一个数组,用于保存当前页面的 opener 页面选中的数据。

1: 比如 行内动作 点击打开弹窗,弹窗里面的字段或者动作可以通过 openerActiveRecords 获取 行内 数据.
2: 比如 表单中的动作 点击打开弹窗,弹窗里面的字段或者动作可以通过 openerActiveRecords 表单 数据.

特殊数据

activeTreeContext 树组件的选中数据

activeTreeContext 树组件的选中数据,可以获取到选中的数据,以及选中的数据的父级数据,用于左树右表的视图.

常见场景:

场景 1: 左树右表的视图,表格上方的创建按钮点击打开弹窗弹窗里面要获取左树选中的数据,用于创建数据.
方案:点击弹窗里面的创建按钮时,通过 this.metadataRuntimeContext.view?.activeTreeContext 获取到左树选中的数据

class CustomActon extends ServerActionWidget {
  protected async executeAction(action: RuntimeServerAction, submitValue: SubmitValue){
    const treeContext = this.metadataRuntimeContext.view?.activeTreeContext || {}
    submitValue.records = {
      ...submitValue.records,
      ...treeContext
    }
    super.executeAction(action, submitValue)
  }
}

场景 2: 左树右表的视图,表格上方的创建按钮点击打开新页面新页面里面要获取左树选中的数据,用于创建数据.
方案:点击新页面里面的创建按钮时,可通过URL参数获取到左树选中的数据

class CustomActon extends ServerActionWidget {
  protected async executeAction(action: RuntimeServerAction, submitValue: SubmitValue){
    const treeContext = JSON.parse(this.getUrlParameters().context).activeTreeContext || {}
    submitValue.records = {
      ...submitValue.records,
      ...treeContext
    }
    super.executeAction(action, submitValue)
  }
}

searchBody 搜索条件、searchConditions 固定查询条件

参考文章

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

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

(0)
汤乾华的头像汤乾华数式员工
上一篇 2024年10月8日 am10:37
下一篇 2024年10月8日 pm5:55

相关推荐

  • 前端自定义组件之左右滑动

    本文将讲解如何通过自定义,实现容器内的左右两个元素,通过左右拖拽分隔线,灵活调整宽度。其中左右元素里的内容都是界面设计器拖出来的。 实现路径 1. 界面设计器拖出页面 我们界面设计器拖个布局容器,然后在左右容器里拖拽任意元素。完成后点击右上角九宫格,选中布局容器,填入组件 api 名称,作用是把布局容器切换成我们自定义的左右滑动组件,这里的 api 名称和自定义组件的 widget 对应。最后发布页面,并绑定菜单。 2. 组件实现 widget 组件重写了布局容器,核心函数 renderLeft、renderRight,通过 DslRender.render 方法渲染界面设计器拖拽的元素。 import { BasePackWidget, DefaultContainersWidget, DslDefinition, DslRender, SPI, Widget } from '@oinone/kunlun-dependencies'; import LeftRightSlide from './LeftRightSlide.vue'; // 拿到界面设计器配置的子容器元素 function fetchContainerChildren(widgets?: DslDefinition[], level = 3): DslDefinition[] { if (!widgets) { return []; } const children: DslDefinition[] = []; for (const widget of widgets) { if (widget.widget === 'container') { children.push(widget); } else if (level >= 1) { fetchContainerChildren(widget.widgets, level – 1).forEach((child) => children.push(child)); } } return children; } @SPI.ClassFactory(BasePackWidget.Token({ widget: 'LeftRightSlide' })) export class LeftRightSlideWidget extends DefaultContainersWidget { public initialize(props) { super.initialize(props); this.setComponent(LeftRightSlide); return this; } // 获取容器的子元素 public get containerChildren(): DslDefinition[] { return fetchContainerChildren(this.template?.widgets); } // 初始宽度配置 @Widget.Reactive() public get initialLeftWidth() { return this.getDsl().initialLeftWidth || 400; } // 最小左宽度配置 @Widget.Reactive() public get minLeftWidth() { return this.getDsl().minLeftWidth || 200; } // 最小右宽度配置 @Widget.Reactive() public get minRightWidth() { return this.getDsl().minRightWidth || 200; } // 根据容器子元素渲染左侧 @Widget.Method() public renderLeft() { // 把容器的第一个元素作为左侧 const containerLeft = this.containerChildren[0]; if (containerLeft) { return DslRender.render(containerLeft); } } // 根据容器子元素渲染右侧 @Widget.Method() public renderRight() { // 把容器的第二个元素作为右侧 const containerRight = this.containerChildren[1]; if…

    2025年7月8日
    52200
  • oio-dropdown 下拉菜单

    向下弹出的列表。 何时使用 当页面上的操作命令过多时,用此组件可以收纳操作元素。点击或移入触点,会出现一个下拉菜单。可在列表中进行选择,并执行相应的命令。 用于收罗一组命令操作。 Select 用于选择,而 Dropdown 是命令集合。 API 属性如下 参数 说明 类型 默认值 destroyOnHide 关闭后是否销毁 Dropdown boolean false disabled 菜单是否禁用 boolean – getTriggerContainer 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 Function(triggerNode) () => document.body overlay(v-slot) 菜单 Menu – overlayClassName 下拉根元素的类名称 string – overlayStyle 下拉根元素的样式 object – placement 菜单弹出位置 bottomLeft | bottom | bottomRight | topLeft | top | topRight bottomLeft trigger 触发下拉的行为, 移动端不支持 hover Array<click|hover|contextmenu> ['hover'] visible(v-model:visible) 菜单是否显示 boolean – 事件 事件名称 说明 回调参数 onUpdateValue 菜单显示状态改变时调用,参数为 visible。点击菜单按钮导致的消失不会触发 function(visible)

    2023年12月18日
    74400
  • 列表视图、卡片视图切换

    在日常项目开发中,我们可能会遇到当前视图是个表格,通过某个操作按钮将它变成卡片的形式. 这篇文章将带大家实现这种功能。通过上面两张图片可以看到出来不管是表格还是卡片,它都在当前的视图里面,所以我们需要一个视图容器来包裹表格跟卡片,并且表格可以使用平台默认的表格渲染,我们只需要自定义卡片即可。 我们用资源模块下面的国家菜单来实现这么一个功能这是对应的URL: http://localhost:8080/page;module=resource;viewType=TABLE;model=resource.ResourceCountry;action=resource%23%E5%9B%BD%E5%AE%B6;scene=resource%23%E5%9B%BD%E5%AE%B6;target=OPEN_WINDOW;menu=%7B%22selectedKeys%22:%5B%22%E5%9B%BD%E5%AE%B6%22%5D,%22openKeys%22:%5B%22%E5%9C%B0%E5%9D%80%E5%BA%93%22,%22%E5%9C%B0%E5%8C%BA%22%5D%7D 源码下载 views 创建外层的视图容器 刚刚我们讲过,不管是表格还是卡片,它都在当前的视图里面,所以我们需要写一个视图容器来包裹它们,并且对应的容器里面允许拆入表格跟卡片,我们先创建TableWithCardViewWidget.ts // TableWithCardViewWidget.ts import { BaseElementWidget, SPI, Widget } from '@kunlun/dependencies'; import TableWithCardView from './TableWithCardView.vue'; enum ListViewType { TABLE = 'table', CARD = 'card' } @SPI.ClassFactory( BaseElementWidget.Token({ widget: 'TableWithCardViewWidget' }) ) export class TableWithCardViewWidget extends BaseElementWidget { @Widget.Reactive() private listViewType: ListViewType = ListViewType.TABLE; // 当前视图展示的类型,是展示卡片还是表格 public initialize(props) { if (!props.slotNames) { props.slotNames = ['tableWidget', 'cardWidget']; } super.initialize(props); this.setComponent(TableWithCardView); return this; } } 在TableWithCardViewWidget中的initialize函数中,我们定义了两个插槽: tableWidget、cardWidget,所以需要在对应的vue文件里面里接收这两个插槽 <template> <div class="list-view-wrapper"> <!– 表格插槽 –> <div style="height: 100%" v-if="listViewType === 'table'"> <slot name="tableWidget" /> </div> <!– 卡片插槽 –> <div v-if="listViewType === 'card'"> <slot name="cardWidget"></slot> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ props: ['listViewType', 'onChangeViewType'], inheritAttrs: false, setup(props, context) { const onChangeViewType = (listViewType) => { props.onChangeViewType(listViewType); }; } }); </script> 这样一来,我们就定义好了视图容器,接下来就是通过自定义layout的方式注册该容器 layout注册 import { registerLayout, ViewType } from '@kunlun/dependencies'; registerLayout( `<view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" cols="4" slot="search"/> </view> </pack> <pack widget="group" slot="tableGroup" style="position: relative"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="TableWithCardViewWidget"> <template slot="tableWidget"> <element widget="table" slot="table" datasource-provider="true"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" />…

    2024年10月16日
    1.7K01
  • Oinone平台可视化调试工具

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

    2024年4月13日
    1.0K00
  • 自定义mutation时出现校验不过时,如何排查

    场景描述 用户在自定义接口提供给前端调用时 @Action(displayName = "注册", bindingType = ViewTypeEnum.CUSTOM) public BaseResponse register(UserZhgl data) { //…逻辑 return result; } import java.io.Serializable; public class BaseResponse implements Serializable { private String code; private String msg; public BaseResponse() { } public BaseResponse(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } gql执行时出现报错 { "errors":[ { "message":"Validation error of type SubSelectionNotAllowed: Sub selection not allowed on leaf type Object of field register @ 'zhglMutation/register'", "locations":[ { "line":3, "column":3 } ], "extensions":{ "classification":"ValidationError" } } ] } 解决方案 1.返回对象不为空时,对象必须是模型,否则无法解析返回参数2.前端调用GQL异常时,可以用Insomnia工具对GQL进行测试,根据错误提示对GQL进行修改和排查3.GQL正常情况下,执行以后可根据后端日志进行错误排查

    2023年11月1日
    92600

Leave a Reply

登录后才能评论