母版-布局-DSL 渲染基础(v4)

概述

不论是母版布局还是DSL,我们统一使用XML进行定义,可以更好的提供结构化表述。

参考文档:

下面文档中未介绍到的Mask母版Layout布局,可以去数据库中base库的表base_layout_definitionbase_mask_definitiontemplate字段查看

母版

确定了主题、非主内容分发区域所使用组件和主内容分发区域联动方式的页面配置。

母版内容分为主内容分发区域与非主内容分发区域。非主内容分发区域一般包含顶部栏、底部栏和侧边栏。侧边栏可以放置菜单,菜单与主内容分发区域内容进行联动。

image.png

默认母板

<mask>
    <multi-tabs />
    <header>
        <widget widget="app-switcher" />
        <block>
            <widget widget="notification" />
            <widget widget="divider" />
            <widget widget="language" />
            <widget widget="divider" />
            <widget widget="user" />
        </block>
    </header>
    <container>
        <sidebar>
            <widget widget="nav-menu" height="100%" />
        </sidebar>
        <content>
            <breadcrumb />
            <block width="100%">
                <widget width="100%" widget="main-view" />
            </block>
        </content>
    </container>
</mask>

该模板中包含了如下几个组件:

  • mask:母版根标签
  • multi-tabs:多选项卡
  • header:顶部栏
  • container:容器
  • sldebar:侧边栏
    • nav-menu:导航菜单
  • content:主内容
    • breadcrumb:面包屑
    • block:div块
    • main-view:主视图;用于渲染布局和DSL等相关内容;

母版将整个页面的大体框架进行了描述,接下来将主要介绍布局和DSL是如何在main-view中进行渲染的。关于自定义母版组件的相关内容 点击查看

布局

布局是将页面拆分成一个一个的小单元,按照从上到下、从左到右进行顺序排列

布局主要用于控制页面中元素的展示的相对位置,原则上不建议将元数据相关内容在布局中进行使用,可最大化布局的利用率。

默认表格视图(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="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" />
            <element widget="rowActions" slot="rowActions" />
        </element>
    </pack>
</view>

该模板中包含了如下几个组件:

  • view:视图;用于定义当前视图类型,不同的视图类型会有不同的数据交互,以及渲染不同的组件。
  • pack:容器类型相关组件。
  • element:元素组件;包含各种各样的组件,根据组件实现有不同的作用。
  • xslot:DSL插槽;用于将DSL中定义的模板分别插入到对应的槽中;

特别的,任何XML标签上的slot属性都具备DSL插槽的全部能力。当学习完DSL相关内容后,我们将会对DSL插槽有比较清晰的理解。

PS:在下面的内容中,将使用该布局进行描述。

DSL

准备工作

为了方便描述DSL和元数据之间的关系,我们需要先定义一个简单模型,这个模型里面包含字段和动作。这些通常是服务端定义的。(对服务端不感兴趣的同学可以跳过代码部分)

DemoModel.java
@Model.model(DemoModel.MODEL_MODEL)
@Model(displayName = "演示模型", labelFields = {"name"})
public class DemoModel extends IdModel {

    private static final long serialVersionUID = -7211802945795866154L;

    public static final String MODEL_MODEL = "demo.DemoModel";

    @Field(displayName = "名称")
    private String name;

    @Field(displayName = "是否启用")
    private Boolean isEnabled;
}
DemoModelAction.java
@Model.model(DemoModel.MODEL_MODEL)
@UxRouteButton(
        action = @UxAction(name = "redirectCreatePage", displayName = "创建", contextType = ActionContextTypeEnum.CONTEXT_FREE),
        value = @UxRoute(model = DemoModel.MODEL_MODEL, viewName = "演示模型form"))
@UxRouteButton(
        action = @UxAction(name = "redirectUpdatePage", displayName = "编辑", contextType = ActionContextTypeEnum.SINGLE),
        value = @UxRoute(model = DemoModel.MODEL_MODEL, viewName = "演示模型form"))
public class DemoModelAction {

    @Action(displayName = "启用")
    public DemoModel enable(DemoModel data) {
        data.setIsEnabled(true);
        data.updateById();
        return data;
    }

    @Action(displayName = "禁用")
    public DemoModel disable(DemoModel data) {
        data.setIsEnabled(false);
        data.updateById();
        return data;
    }
}

上面的java代码定义了演示模型字段动作

  • 字段 field
    • id:ID 整数 Integer
    • name:名称 字符串 String
    • isEnabled:是否启用 布尔 Boolean
  • 动作 action
    • redirectCreatePage:创建;目标视图为"演示模型form" 跳转动作 ViewAction
    • redirectUpdatePage:编辑;目标视图为"演示模型form" 跳转动作 ViewAction
    • enable:启用 提交动作 ServerAction
    • disable:禁用 提交动作 ServerAction

根据上面的元数据内容,我们可以定义一个如下所示的DSL模板,来使用所有的元数据将其渲染到页面中:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="search">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="actions">
        <action name="redirectCreatePage" />
    </template>
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

该模板中包含的标签含义:

  • view:视图;与布局模板中的view标签作用一致。
  • template:向DSL插槽中插入一个模板片段
  • field:字段元数据标签;data属性与元数据中字段API名称(field)对应。
  • action:动作元数据标签;name属性与元数据中动作API名称(name)对应。

DSL模板本质上是对布局模板的补充,它使用布局模板中已经定义好的DSL插槽,对其进行相应规则的合并替换,最终构成一个可被执行的视图模板再经过客户端渲染展示给用户。单独解释DSL模板的结构化含义是完全没有意义的。

接下来,我们会分别定义不同的DSL模板,并结合默认表格视图的布局模板进行详细说明。

模板编译

服务端模板编译基础

进入页面时,客户端将通过加载跳转动作(ViewActionLoad)接口获取对应的布局模板和DSL模板,得到经过服务端编译JSON数据,类似于如下数据结构:(DSL模板的JSON结构)

{
    "dslNodeType": "VIEW",
    "type": "TABLE",
    ......,
    "widgets": [
        {
            "dslNodeType": "TEMPLATE",
            "slot": "search",
            "widgets": [
                ......
            ]
        },
        {
            "dslNodeType": "TEMPLATE",
            "slot": "actions",
            "widgets": [
                ......
            ]
        }
    ]
}

服务端编译过程可以简单的理解为以下几个内容:(实际要复杂很多,在这里我们仅需要关注最终拿到的JSON结果即可)

  • XML转换为JSON结构
  • XML标签转换为dslNodeType属性,XML子标签转换为widgets属性,其他属性保持不变
  • field标签(字段)action标签(动作)对应的元数据补充完整。如上所示,我们仅定义了字段的data属性,返回的JSON中将包含字段动作的全部元数据。字段会补充字段类型、显示名称、是否存储等元数据属性,动作会补充动作类型、显示名称等元数据属性。

示例模板

表格视图-布局模板
<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>
表格视图-DSL模板
<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="search">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="actions">
        <action name="redirectCreatePage" />
    </template>
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

以上所示的两个模板(布局模板DSL模板)会作为布局和DSL的模板合并相关内容介绍的演示模板。更多的布局模板点击查看

布局和DSL的模板合并

当我们从服务端获取了布局模板DSL模板对应的JSON后,客户端将对这两个JSON进行合并处理。

合并操作主要是将DSL模板中定义的根标签下定义的所有template标签,向布局模板中定义的DSL插槽进行属性合并和子节点替换。

标准合并

将上述两个模板进行合并后,将得到如下结果:(为了方便观察结构,将使用XML结构进行解释,实际运行时为JSON结构)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search">
                <!-- slot="search" -->
                <field data="id" invisible="true" />
                <field data="name" />
                <field data="isEnabled" />
            </element>
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <!-- slot="actions" -->
            <action name="redirectCreatePage" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <!-- slot="fields" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,DSL模板定义的template标签下的内容被合并到了布局模板的对应位置中。接下来就可以进行模板渲染操作了。

子标签替换和属性合并

一般的,我们将处理布局模板中任何标签上的slot属性,以及xslot标签。

遵循就近原则,模板合并将取路径最短的模板进行替换和插入。

当我们需要设置表格所有字段都允许排序时,我们可以使用sortable="true"属性,设置在widget="table"组件上。

使用slot="fields"插槽是无法向widget="table"组件添加额外属性的,此时我们需要使用slot="table"这个插槽来实现我们的需求。

一个错误的DSL模板可能被定义成如下内容:(下面的示例舍弃了部分插槽内容)

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<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" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
        </element>
    </pack>
</view>

由此可见,我们优先使用了widget="table"上的slot="table"属性作为插槽,将原本定义在widget="table"下的所有节点全部换成了在DSL模板中定义的内容,并且成功的添加了sortable="true"这一属性。

但是,由于子标签被完全替换了,因此丢弃了widget="rowActions"组件,导致slot="rowActions"中定义的内容,找不到对应的插槽,这部分DSL模板内容将被丢弃。

反向合并

由于上面的合并操作丢弃了widget="rowActions"组件,但我们的本意并不是想要丢弃这个组件,而是仍然希望能以某种规则将这部分内容继续保留。为了解决这个问题,我们提供了反向合并操作,但前提是,DOM结构必须是同一层级

基于这一特性,我们将上述模板稍加修改:(将slot="rowActions"放在slot="table"下面)

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
        <template slot="rowActions">
            <action name="redirectUpdatePage" />
            <action name="enable" />
            <action name="disable" />
        </template>
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<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" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,原本定义在布局模板上的widget="rowActions"被找回来了,并且正确填充了定义在DSL模板中的内容。

不足的是,由于widget="table"下面的所有内容都被替换了一次,我们无法确定原本的widget="rowActions"组件应该如何正确放置,因此使用了DSL模板中定义的位置。

这可能会带来一些弊端,比如:将某一元素必须被放在第一个节点处。针对这种情况,布局模板本来是可以做到限定作用的,但由于使用了更大范围的插槽,使得这一特性丢失。

但也带来了一些灵活性,比如:大多数情况下,某一元素需要放在第一个节点处,但在特殊情况下,该元素需要放在最后一个节点处。

我们希望插槽可以设计的尽可能的灵活,以满足各种不同场景的需求。

属性插槽

当我们希望使用属性合并,但不希望替换子标签时,我们可以使用属性插槽来解决这个问题。

当我们希望向widget="group"组件上添加title="这是一个示例标题"属性时,我们可以使用slot="tableGroup"插槽。

我们可以使用如下所示的方式来定义DSL模板:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="tableGroup" title="这是一个示例标题" />
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
        <template slot="rowActions">
            <action name="redirectUpdatePage" />
            <action name="enable" />
            <action name="disable" />
        </template>
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup" title="这是一个示例标题">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,当DSL模板中的template标签下没有任何子标签时,我们仅会处理属性合并,但不会处理在原布局模板下定义的子标签。

根据这一特性,我们也可以定义这样的DSL模板来实现上面给widget="table"添加sortable="true"的需求:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="tableGroup" title="这是一个示例标题" />
    <template slot="table" sortable="true" />
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:(与之前的合并结果完全一致)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup" title="这是一个示例标题">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由于属性插槽本身没有子标签,因此我们建议将所有的属性插槽配置在view标签子标签的最前面进行统一定义。

模板渲染

模板渲染是将一个包含dslNodeType属性的对象,生成对应框架的VDom对象,通过该VDom对象执行一段创建ts组件的逻辑,并通过ts组件渲染该组件对应的VDom对象

例如:使用Vue框架时,该VDom对象通常为VNode

(以下内容均围绕Vue框架进行描述,其他框架基本一致)

为了将模板渲染尽可能的接近第三方框架的渲染逻辑,我们采用了Tag(Vue组件) - Class Component(ts) - Component(Vue组件)这样的三层结构进行模板渲染的实现。

  • Tag(Vue组件):使用resolveComponent方法,将一个包含dslNodeType属性的对象,通过既定规则进行解析,获取一个Vue组件作为入口。主要处理模板标签和Vue组件之间的对应关系。
  • Class Component(ts):使用ts面向对象的特性,可以对组件逻辑进行重载、继承等处理,增加组件的灵活性。
  • Component(Vue组件):主要实现数据渲染。

具体的渲染逻辑不在此处进行介绍,该篇文章主要是为了让读者对模板以及整体框架有一个基本的概念,使用这套模板渲染的最终目的是为了让组件复用率更高,开发更加灵活。

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

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

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

相关推荐

  • 创建与编辑一体化

    在业务操作中,用户通常期望能够在创建页面后立即进行编辑,以减少频繁切换页面的步骤。我们可以充分利用Oinone平台提供的创建与编辑一体化功能,使操作更加高效便捷。 通过拖拽实现表单页面设计 在界面设计器中,我们首先需要设计出对应的页面。完成页面设计后,将需要的动作拖入设计好的页面。这个动作的关键在于支持一个功能,即根据前端传入的数据是否包含id来判断是创建操作还是编辑操作。 动作的属性配置如下: 前端自定义动作 一旦页面配置完成,前端需要对这个动作进行自定义。以下是一个示例的代码: @SPI.ClassFactory( ActionWidget.Token({ actionType: [ActionType.Server], model: '模型', name: '动作的名称' }) ) export class CreateOrUpdateServerActionWidget extends ServerActionWidget { @Widget.Reactive() protected get updateData(): boolean { return true; } } 通过以上步骤,我们实现了一个更智能的操作流程,使用户能够在创建页面的同时进行即时的编辑,从而提高了整体操作效率。这种创建与编辑一体化的功能不仅使操作更加顺畅,同时也为用户提供了更灵活的工作流程。

    2023年11月21日
    1.7K00
  • 左树右表默认选择第一行

    import { BaseElementWidget, Widget, SPI, ViewType, TableSearchTreeWidget } from '@kunlun/dependencies'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'tree', model: '改成当前视图的模型' }) ) export class CustomTableSearchTreeWidget extends TableSearchTreeWidget { protected hasExe = false; @Widget.Watch('rootNode.children.length') protected watchRootNode(len) { if (len && !this.hasExe) { this.hasExe = true; const firstChild = this.rootNode?.children?.[0]; if (firstChild) { this.onNodeSelected(firstChild); this.selectedKeys = [firstChild.key]; } } } }

    2024年11月26日
    1.2K00
  • 如何在表格的字段内添加动作

    介绍 在日常的业务中,我们经常需要在表格内直接点击动作完成一些操作,而不是只能在操作栏中,例如:订单的表格内点击商品名称或者里面的按钮跳转到商品详情页面,这里我们将带来大家来通过自定义表格字段来实现这个功能。 1.编写表格字段组件 组件ts文件TableBtnFieldWidget.ts import { ActionWidget, ActiveRecordExtendKeys, BaseFieldWidget, BaseListView, ModelFieldType, queryDslWidget, queryRowActionBar, RowContext, SPI, TableStringFieldWidget, BaseElementListViewWidget, ViewType, Widget } from '@kunlun/dependencies'; import { createVNode, VNode } from 'vue'; import { toString } from 'lodash-es'; import TableBtnField from './TableBtnField.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: ViewType.Table, ttype: [ModelFieldType.String, ModelFieldType.Text], // widget: 'StringLink', // 以下3行配置代码测试用,生产建议在界面设计器自定义组件,widget填自定义组件的api名称 model: 'resource.k2.Model0000000109', name: 'name' }) ) export class TableBtnFieldWidget extends TableStringFieldWidget { @Widget.Reactive() private get triggerActionLabel() { // 界面设计器组件内设计该属性 return this.getDsl().triggerActionLabel || '更新'; } private getTriggerAction() { return this.model.modelActions.find((a) => a.label === this.triggerActionLabel); } private getTriggerActionWidget(widgetHandle: string, draftId: string, triggerActionLabel: string): ActionWidget | undefined { const listView = Widget.select(widgetHandle) as unknown as BaseListView; const listWidget = queryDslWidget(listView?.getChildrenInstance(), BaseElementListViewWidget); if (!listWidget) { return undefined; } const rowActionBar = queryRowActionBar(listWidget.getChildrenInstance(), draftId); const actionWidget = rowActionBar?.getChildrenInstance().find((a) => (a as ActionWidget).action.label === triggerActionLabel); return actionWidget as ActionWidget; } protected clickAction(context: RowContext) { const draftId = context.data[ActiveRecordExtendKeys.DRAFT_ID] as unknown as string; const actionWidget = this.getTriggerActionWidget(this.getRootHandle()!, draftId, this.triggerActionLabel); if (!actionWidget) { console.error('未找到action', this.triggerActionLabel); return; } actionWidget.click(); } @Widget.Method() public renderDefaultSlot(context: RowContext): VNode[] | string { const value = toString(this.compute(context)); if (value) { return [ createVNode(TableBtnField,…

    2024年5月16日
    1.3K00
  • GraphQL请求详解(v4)

    阅读之前 什么是GraphQL? Oinone官方解读GraphQL入门 可视化请求工具 insomnia下载 概述 (以下内容简称GQL) 众所周知,平台的所有功能都是通过一系列元数据定义来驱动的,而GQL作为服务端和客户端交互协议,其重要性不言而喻。下面会从以下几个方面介绍GQL在平台中是如何运作的: 服务端定义元数据生成GQL对应的schema 通过HttpClient发起一个GQL请求 通过window.open使用GET方式发起一个GQL请求 客户端泛化调用任意API服务 客户端通过运行时上下文RuntimeContext发起GQL请求 准备工作 在开始介绍GQL之前,我们需要定义一个可以被GQL请求的服务端函数,以此来介绍我们的相关内容。(对服务端不感兴趣的同学可以跳过代码部分) DemoModel.java @Model.model(DemoModel.MODEL_MODEL) @Model(displayName = "演示模型", labelFields = {"name"}) public class DemoModel extends IdModel { private static final long serialVersionUID = -7211802945795866154L; public static final String MODEL_MODEL = "demo.DemoModel"; @Field(displayName = "名称") private String name; @Field(displayName = "是否启用") private Boolean isEnabled; } DemoModelAction.java @Model.model(DemoModel.MODEL_MODEL) public class DemoModelAction { @Action(displayName = "启用") public DemoModel enable(DemoModel data) { data.setIsEnabled(true); data.updateById(); return data; } @Action(displayName = "禁用") public DemoModel disable(DemoModel data) { data.setIsEnabled(false); data.updateById(); return data; } } 上面的java代码定义了演示模型的字段和动作: 字段 field id:ID 整数 Integer name:名称 字符串 String isEnabled:是否启用 布尔 Boolean 动作 action enable:启用 提交动作 ServerAction disable:禁用 提交动作 ServerAction 服务端定义元数据生成GQL对应的schema 模型和字段 type DemoModel { id: Long name: String isEnabled: Boolean } 动作 type DemoModelInput { id: Long name: String isEnabled: Boolean } type DemoModelQuery { queryOne(query: DemoModelInput): DemoModel …… } type DemoModelMutation { enable(data: DemoModelInput): DemoModel disable(data: DemoModelInput): DemoModel …… } PS:平台内置了多种Query和Mutation定义,通过模型继承关系将自动生成,无需手动定义。比如Query定义包括queryOne、queryPage等;Mutation定义包括create、update等。特殊情况下,默认逻辑无法满足时,服务端通常采用函数重载的方式进行替换,客户端则无需关心。 生成规则 type DemoModel:通过模型编码demo.DemoModel取.分隔后的最后一位,并转换为大驼峰格式。字段与声明类型一致。 type DemoModelInput:动作入参定义,未做特殊声明的情况下与模型定义一致。 type DemoModelQuery和type DemoModelMutation:Query和Mutation为固定后缀,分别生成动作相关类型。当函数类型为QUERY时,使用Query后缀,其他情况使用Mutation后缀。 (此处仅做简单解释,详细生成规则过于复杂,客户端无需关心) Query类型的GQL示例 query { demoModelQuery { queryOne(query: { id: ${id} }) { id name isEnabled } } } Mutation类型的GQL示例 mutation…

    2023年11月1日
    2.3K00
  • 登录页自定义配置如何使用

    介绍 为满足大家对登录页面不同的展示需求,oinone在登录页面提供了丰富的配置项以支持大家在业务上的个性化需求 配置方式 在manifest.js内新增以下配置选项 manifest.js文件如何配置的参考文档 runtimeConfigResolve({ login: { /** * 登录按钮label */ loginLabel: '登录', /** * 是否显示忘记密码按钮 */ forgetPassword: true, /** * 忘记密码按钮Label */ forgetPasswordLabel: '忘记密码', /** * 是否显示注册按钮 */ register: true, /** * 注册按钮Label */ registerLabel: '注册', /** * 是否显示验证码登录Tab */ codeLogin: true, /** * 账号登录Tab Label */ accountLoginLabel: '账号', /** * 验证码登录Tab Label */ codeLoginLabel: '验证码', /** * 账号登录-账号输入框Placeholder */ accountPlaceholder: '请输入账号', /** * 账号登录-密码输入框Placeholder */ passwordPlaceholder: '前输入密码', /** * 验证码登录-手机号输入框Placeholder */ phonePlaceholder: '请输入手机号', /** * 验证码登录-验证码输入框Placeholder */ codePlaceholder: '请输入验证码', } });

    2024年4月24日
    1.4K00

Leave a Reply

登录后才能评论