【界面设计器】自定义字段组件实战——表格字段内嵌表格

阅读之前

此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。

如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录

业务背景

表格中的一对多(O2M)多对多(M2M)字段使用表格展开。

演示内容:在【商品】的表格中存在【库存信息】这一列,这一列的内容通过表格展示【尺码】和【数量】两列。

业务分析及实现思路

从需求来看,我们需要实现一个【内嵌表格】组件,并且该组件允许在【表格】视图中使用。与之前不同的是,这里我们需要支持两个业务类型一对多(O2M)多对多(M2M),即一个组件中包含两个元件。

在【内嵌表格】组件的属性面板中,我们需要再定义一个【内嵌表格配置】组件,用来选择内嵌表格中需要哪些字段进行组合,以及为每个组合提供一些基础配置。

这里需要理解一个基本概念,即【内嵌表格】的属性面板是【内嵌表格配置】的【执行页面】。所有组件的属性面板在【执行页面】时都是【表单】视图。

因此我们可以实现一个【内嵌表格配置】组件,并且该组件允许在【表单】视图中使用。其业务类型使用【文本】,我们在保存配置数据时,可以使用JSON数据结构来存储复杂结构。(这里的实现思路并非是最符合协议设定的,但可以满足绝大多数组件场景)

在【内嵌表格配置】组件中,我们可以允许用户添加/移除组合,并且每个组合有两个属性,【标题】和【字段】。

一些解释

看过【界面设计器】自定义字段组件实战——表格字段组合展示文章的读者可能很熟悉这一实现思路,会想当然的尝试将两个组件进行合并。这里我觉得有必要作出一些实现思路上的解释。

虽然在表面上看起来【组合列配置】和【内嵌表格配置】用到的属性完全一样,但在实现上,由于关联关系的查询需要在组件中特殊处理【透出字段(选项字段列表)】字段(【界面设计器】组件开发常见问题
中对该属性进行了解释),才能查询到相应的关联数据。

不仅如此,这两个组件所代表的含义也完全不同。【组合列配置】是在一列中配置需要展示的字段,它在未来可能会增加【颜色(根据条件判断展示不同的颜色)】、【动作(可点击的行为)】等等诸多与之相关的属性。【内嵌表格配置】是在一列中配置表格中的多列,它在未来可能会增加【行高(控制表格行高)】、【支持排序(表格列支持排序)】等等诸多与之相关的属性。

在这里希望读者可以理解一点:相似并不代表相关。组件的抽象与归纳整理的不同点在于,抽象更需要关心其本身所代表的含义,而不是仅关注其相似程度。将多个相似度高但含义不同的组件进行归纳整理得到的只是一个含义不明,无法适应变化的组件。

因此,我们仍然使用两个不同的组件进行实现。

准备工作

此处你应该已经准备好了【商品】和【库存】两个模型,并且可以完整执行【商品】模型的全部【增删改查】操作。

业务模型定义(此处模型定义并非业务中正常使用的模型定义,仅作为演示使用)

(以下仅展示本文章用到的模型字段,忽略其他无关字段。)

关联字段:-左侧表示当前模型中的字段API名称,右侧表示关联模型中的字段API名称。

商品(Item)
名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段
ID id 整数 - - -
编码 code 文本 128 - -
名称 name 文本 128 - -
库存信息 inventoryInfo 一对多 - 库存(Inventory) id - itemId
库存(Inventory)
名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段
ID id 整数 128 - -
商品 item 多对一 - 商品(Item) itemId - id
商品ID itemId 整数 - - -
尺码 size 文本 128 - -
库存 count 整数 - - -

PS:如果是使用【模型设计器】创建这两个模型,在创建关联关系字段时必须使用双向关联,才能正确建立关联关系。

实现页面效果展示

表格视图

image.png

表单视图

image.png

库存信息配置

image.png

创建组件、元件

准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。

以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。

创建内嵌表格组件

image.png

创建内嵌表格元件(一对多)

image.png

创建内嵌表格元件(多对多)

image.png

创建内嵌表格配置组件

image.png

创建内嵌表格配置元件

image.png

设计内嵌表格元件属性面板

(两个元件的属性面板可以完全一致)

创建tableConfig字段,并切换至【内嵌表格配置】组件。

image.png

image.png

再拖入【透出字段(选项字段列表)】,并设置为隐藏。

image.png

设计内嵌表格配置元件属性面板

image.png

启动SDK工程进行组件基本功能开发

开发步骤参考

  • 打开【表格】视图,将【库存信息】字段的组件切换为【内嵌表格】
  • 在属性面板中看到【内嵌表格配置】组件,并优先实现【内嵌表格配置】组件。这里的属性面板就是【内嵌表格配置】组件对应的【执行页面】。
  • 当【内嵌表格配置】组件可以按照预先设计的数据结构正确保存tableConfig属性时,可以在【内嵌表格】组件中的props定义中直接获取该属性,接下来就可以进行【内嵌表格】组件的开发。

代码实现参考

工程结构

image.png

typing.ts
export interface InlineTableConfig {
  key: string;
  label?: string;
  field?: string;
}
FieldService.ts
import { GenericFunctionService, IModelField, QueryPageResult } from '@kunlun/dependencies';

export class FieldService {
  private static readonly FIELD_MODEL = 'base.Field';

  public static async fetchFieldsByModel(model: string, filter?: string): Promise<IModelField[]> {
    let rsql = `model == "${model}"`;
    if (filter) {
      rsql = `${rsql} and ${filter}`;
    }
    const res = await GenericFunctionService.INSTANCE.simpleExecuteByName<QueryPageResult<IModelField>>(
      FieldService.FIELD_MODEL,
      'queryPage',
      {
        size: -1
      },
      {
        rsql
      }
    );
    return res?.content || [];
  }
}
InlineTableConfig.vue
<template>
  <div class="inline-table-config">
    <oio-form v-for="item in list" :data="item" :key="item.key">
      <oio-form-item label="标题" name="label">
        <oio-input v-model:value="item.label" />
      </oio-form-item>
      <oio-form-item label="字段" name="field">
        <a-select
          class="oio-select"
          dropdownClassName="oio-select-dropdown"
          v-model:value="item.field"
          :options="fields"
        />
      </oio-form-item>
      <oio-button type="link" @click="() => removeItem(item)">移除</oio-button>
    </oio-form>
    <oio-button type="primary" block @click="addItem">添加</oio-button>
  </div>
</template>
<script lang="ts">
import { uniqueKeyGenerator } from '@kunlun/dependencies';
import { OioButton, OioForm, OioFormItem, OioInput } from '@kunlun/vue-ui-antd';
import { Select as ASelect } from 'ant-design-vue';
import { defineComponent, onMounted, PropType, ref, watch } from 'vue';
import { InlineTableConfig } from '../../typing';
import { FieldService } from './service/FieldService';

interface SelectItem {
  label: string;
  value: string;
}

export default defineComponent({
  name: 'InlineTableConfig',
  components: {
    OioForm,
    OioFormItem,
    OioInput,
    OioButton,
    ASelect
  },
  props: {
    referenceModel: {
      type: String
    },
    value: {
      type: String
    },
    change: {
      type: Function
    },
    setOptionFields: {
      type: Function as PropType<(optionFields: string[]) => void>
    }
  },
  setup(props) {
    const list = ref<InlineTableConfig[]>([]);
    if (props.value) {
      list.value = JSON.parse(props.value);
    }

    const addItem = () => {
      list.value = [...list.value, { key: uniqueKeyGenerator() }];
    };

    const removeItem = (item: InlineTableConfig) => {
      const { key } = item;
      if (!key) {
        return;
      }
      const index = list.value.findIndex((v) => v.key === key);
      if (index >= 0) {
        list.value.splice(index, 1);
      }
    };

    const fields = ref<SelectItem[]>([]);

    onMounted(() => {
      if (props.referenceModel) {
        FieldService.fetchFieldsByModel(props.referenceModel, 'ttype =out= (O2O, O2M, M2O, M2M)').then((res) => {
          fields.value = res.map((v) => ({
            label: v.displayName || v.field!,
            value: v.field!
          }));
        });
      }
    });

    watch(
      list,
      (val) => {
        if (props.referenceModel) {
          props.setOptionFields?.(val.filter((v) => !!v.field).map((v) => v.field) as string[]);
        }
        props.change?.(JSON.stringify(val));
      },
      { deep: true }
    );

    return {
      list,
      addItem,
      removeItem,
      fields
    };
  }
});
</script>
FormStringInlineTableConfigFieldWidget.ts
import { FormFieldWidget, ModelFieldType, SPI, ViewType, Widget } from '@kunlun/dependencies';
import InlineTableConfig from './InlineTableConfig.vue';

interface InternalMetadata {
  modelReferences?: {
    model?: string;
  };
}

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: ViewType.Form,
    ttype: ModelFieldType.String,
    widget: 'InlineTableConfig',
    multi: false
  })
)
export class FormStringInlineTableConfigFieldWidget extends FormFieldWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(InlineTableConfig);
    return this;
  }

  @Widget.Reactive()
  protected get referenceModel(): string | undefined {
    return (this.formData._metadata as InternalMetadata)?.modelReferences?.model;
  }

  @Widget.Method()
  public setOptionFields(optionFields: string[]) {
    this.formData.optionFields = optionFields;
  }
}
InlineTable.vue(需新增文件)
<template>
  <a-table
    class="column-inline-table"
    :data-source="dataSource"
    :columns="columns"
    :pagination="false"
    size="small"
    :scroll="{ y: 140 }"
  />
</template>
<script lang="ts">
import { ActiveRecord } from '@kunlun/dependencies';
import { Table as ATable } from 'ant-design-vue';
import { computed, defineComponent, PropType } from 'vue';
import { InlineTableConfig } from '../../typing';

export default defineComponent({
  name: 'InlineTable',
  inheritAttrs: false,
  components: {
    ATable
  },
  props: {
    dataSource: {
      type: Array as PropType<ActiveRecord[]>
    },
    fields: {
      type: Array as PropType<InlineTableConfig[]>
    }
  },
  setup(props) {
    const dataSource = computed(() => props.dataSource || []);

    const columns = computed(() =>
      (props.fields || []).map((v) => ({
        key: v.key,
        title: v.label,
        dataIndex: v.field
      }))
    );

    return {
      dataSource,
      columns
    };
  }
});
</script>
<style lang="scss">
.column-inline-table.ant-table-wrapper {
  height: 100%;
}
</style>

PS:此处由于平台使用了inline-table的class名称,为了避免冲突,使用column-inline-table

AbstractInlineTableFieldWidget.ts
import { RowContext, TableListFieldWidget, Widget } from '@kunlun/dependencies';
import { createVNode, VNode } from 'vue';
import { InlineTableConfig } from '../../typing';
import InlineTable from './InlineTable.vue';

export class AbstractInlineTableFieldWidget extends TableListFieldWidget {
  @Widget.Reactive()
  protected get tableConfig(): InlineTableConfig[] {
    const { tableConfig } = this.getDsl();
    if (tableConfig) {
      return JSON.parse(tableConfig);
    }
    return [];
  }

  protected getFields() {
    return this.tableConfig.filter((v) => !!v.field);
  }

  @Widget.Method()
  public renderDefaultSlot(context: RowContext): VNode[] | string {
    return [
      createVNode(InlineTable, {
        dataSource: this.compute(context),
        fields: this.getFields()
      })
    ];
  }
}
TableO2mMultiInlineTableFieldWidget.ts
import { BaseFieldWidget, ModelFieldType, SPI, ViewType } from '@kunlun/dependencies';
import { AbstractInlineTableFieldWidget } from './AbstractInlineTableFieldWidget';

@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Table,
    ttype: ModelFieldType.OneToMany,
    widget: 'InlineTable',
    multi: true
  })
)
export class TableO2mMultiInlineTableFieldWidget extends AbstractInlineTableFieldWidget {}
TableM2mMultiInlineTableFieldWidget.ts
import { BaseFieldWidget, ModelFieldType, SPI, ViewType } from '@kunlun/dependencies';
import { AbstractInlineTableFieldWidget } from './AbstractInlineTableFieldWidget';

@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Table,
    ttype: ModelFieldType.ManyToMany,
    widget: 'InlineTable',
    multi: true
  })
)
export class TableM2mMultiInlineTableFieldWidget extends AbstractInlineTableFieldWidget {}

实现效果展示

配置内嵌表格

image.png

表格展示

image.png

内嵌表格属性面板

image.png

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

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

(0)
数式-海波的头像数式-海波数式管理员
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 列表视图、卡片视图切换

    在日常项目开发中,我们可能会遇到当前视图是个表格,通过某个操作按钮将它变成卡片的形式. 这篇文章将带大家实现这种功能。通过上面两张图片可以看到出来不管是表格还是卡片,它都在当前的视图里面,所以我们需要一个视图容器来包裹表格跟卡片,并且表格可以使用平台默认的表格渲染,我们只需要自定义卡片即可。 我们用资源模块下面的国家菜单来实现这么一个功能这是对应的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.6K01
  • 前端自定义组件之多页面步骤条

    本文将讲解如何通过自定义,实现多页面的步骤条组件。其中每个步骤的元素里都对应界面设计器的一个页面。以下是代码实现和原理分析。 代码实现 NextStepWidget 多页面步骤条 ts 组件 import { CallChaining, createRuntimeContextByView, customMutation, customQuery, RuntimeView, SPI, ViewCache, Widget, DefaultTabsWidget, BasePackWidget } from '@oinone/kunlun-dependencies'; import { isEmpty } from 'lodash-es'; import { MyMetadataViewWidget } from './MyMetadataViewWidget'; import NextStep from './NextStep.vue'; import { IStepConfig, StepDirection } from './type'; @SPI.ClassFactory(BasePackWidget.Token({ widget: 'NextStep' })) export class NextStepWidget extends DefaultTabsWidget { public initialize(props) { this.titles = props.template?.widgets?.map((item) => item.title) || []; props.template && (props.template.widgets = []); super.initialize(props); this.setComponent(NextStep); return this; } @Widget.Reactive() public get invisible() { return false; } // 配置的每一步名称 @Widget.Reactive() public titles = []; // region 上一步下一步配置 // 步骤配置,切换的顺序就是数组的顺序,模型没有限制 @Widget.Reactive() public get stepJsonConfig() { let data = JSON.parse( this.getDsl().stepJsonConfig || '[{"model":"resource.ResourceCountry","viewName":"国家form"},{"model":"resource.ResourceProvince","viewName":"省form"},{"model":"resource.ResourceCity","viewName":"市form"}]' ); return data as IStepConfig[]; } // 切换上一步下一步 @Widget.Method() public async onStepChange(stepDirection: StepDirection) { // 没有激活的,说明是初始化,取第一步 if (!this.activeStepKey) { const step = this.stepJsonConfig[0]; if (step) { this.activeStepKey = `${step.model}_${step.viewName}`; await this.initStepView(step); } return; } // 获取当前步骤的索引 if (this.currentActiveKeyIndex > -1) { await this.onSave(); // 获取下一步索引 const nextStepIndex = stepDirection === StepDirection.NEXT ? this.currentActiveKeyIndex + 1 : this.currentActiveKeyIndex – 1; // 在索引范围内,则渲染视图 if (nextStepIndex >= 0 && nextStepIndex < this.stepJsonConfig.length) { const nextStep = this.stepJsonConfig[nextStepIndex];…

    2025年7月21日
    48700
  • 如何自定义点击导出动作绑定指定模板

    介绍 平台默认的导出会打开弹窗,然后在弹窗内的视图选择是用模板方式导出还是选字段导出,但是有时候有部分场景希望点击导出动作后直接进入导出流程,导出指定的某个模板,我们可以通过覆写打开弹窗的动作来实现该功能。 本文档参考了 表格页自定义按钮如何获取搜索区域的查询条件 代码示例 以下代码大部分场景只需要修改其中excelTplName更换模板即可,另外如何想增加复用性,还可以将该属性改为从元数据的配置中获取。 import { ActionType, BaseActionWidget, BaseElementListViewWidget, BooleanHelper, ClickResult, Condition, ExcelExportTask, FILE_MODULE_NAME, getSessionPath, GraphqlHelper, http, IQueryPageResult, ISort, queryDslWidget, ReturnPromise, RuntimeServerAction, ServerActionWidget, SPI, SubmitValue, SYSTEM_MODULE_NAME, translateValueByKey, UrlHelper, ViewActionTarget, Widget } from '@kunlun/dependencies'; import { OioNotification } from '@kunlun/vue-ui-antd'; @SPI.ClassFactory( BaseActionWidget.Token({ actionType: [ActionType.View], target: [ViewActionTarget.Dialog], model: 'ys0328.k2.Model0000000453', name: 'internalGotoListExportDialog' }) ) export class DemoExportActionWidget extends ServerActionWidget { /** * excel导出模板名称 * @protected */ protected excelTplName = '演示抽屉跳转链接导出'; /** * 导出任务的模型编码 * @protected */ protected exportTaskModel = 'excelExportTask'; /** * 导出任务的方法 * @protected */ protected exportTaskFun = 'createExportTask'; /** * * 是否是同步导出 */ @Widget.Reactive() protected get syncExport() { return BooleanHelper.isTrue(this.getDsl().sync) || !true; } protected async executeAction(action: RuntimeServerAction, parameters: SubmitValue): Promise<ClickResult> { const workbookId = await this.getWorkbookId(); if (!workbookId) { return false; } let task = { workbookDefinition: { id: workbookId } } as ExcelExportTask; // 从平台内置的方法获取搜索区域的条件 const { condition } = this.getSearchRsqlAndQueryParams(); // 排序规则 let sortList = [] as ISort[]; const baseViewWidget = Widget.select(this.rootHandle); const listViewWidget = queryDslWidget(baseViewWidget?.getChildrenInstance(), BaseElementListViewWidget); if (listViewWidget) { sortList = listViewWidget.sortList; } return this.export(task, condition, sortList); } protected getUploadBodyGql(id: string, condition: string | Condition, sortList:…

    2024年10月9日
    1.1K00
  • oio-modal 对话框

    API 参数 说明 类型 默认值 版本 cancelText 取消按钮文字 string| slot 取消 closable 是否显示右上角的关闭按钮 boolean true closeIcon 自定义关闭图标 VNode | slot – confirmLoading 确定按钮 loading boolean 无 destroyOnClose 关闭时销毁 Modal 里的子元素 boolean false footer 底部内容,当不需要默认底部按钮时,可以设为 :footerInvisible="true" slot 确定取消按钮 getTriggerContainer 指定 Modal 挂载的 HTML 节点 (instance): HTMLElement () => document.body keyboard 是否支持键盘 esc 关闭 boolean true mask 是否展示遮罩 boolean true maskClosable 点击蒙层是否允许关闭 boolean true enterText 确认按钮文字 string 确定 title 标题 string|slot 无 visible(v-model:visible) 对话框是否可见 boolean 无 width 宽度 string|number 520 wrapClassName 对话框外层容器的类名 string – zIndex 设置 Modal 的 z-index number 1000 cancelCallback 点击遮罩层或右上角叉或取消按钮的回调, return true则关闭弹窗 function(e) enterCallback 点击确定回调 function(e)

    2023年12月18日
    80700
  • 前端视图的元数据与数据的传递、交互

    在阅读本篇文章之前,您需要学习以下知识点: 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 是…

    2024年10月8日
    2.0K00

Leave a Reply

登录后才能评论