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:平台内置了多种QueryMutation定义,通过模型继承关系将自动生成,无需手动定义。比如Query定义包括queryOnequeryPage等;Mutation定义包括createupdate等。特殊情况下,默认逻辑无法满足时,服务端通常采用函数重载的方式进行替换,客户端则无需关心。

生成规则

  • type DemoModel:通过模型编码demo.DemoModel.分隔后的最后一位,并转换为大驼峰格式。字段与声明类型一致。
  • type DemoModelInput动作入参定义,未做特殊声明的情况下与模型定义一致。
  • type DemoModelQuerytype DemoModelMutationQueryMutation为固定后缀,分别生成动作相关类型。当函数类型为QUERY时,使用Query后缀,其他情况使用Mutation后缀。

(此处仅做简单解释,详细生成规则过于复杂,客户端无需关心)

Query类型的GQL示例

query {
  demoModelQuery {
    queryOne(query: { id: ${id} }) {
      id
      name
      isEnabled
    }
  }
}

Mutation类型的GQL示例

mutation {
  demoModelMutation {
    enable(data: { id: ${id} }) {
      id
      name
      isEnabled
    }
  }
}

GQL请求规则

  • querymutationGQL默认关键字,若为query则可缺省。
  • demoModelMutation:通过DemoModelMutation类型定义转换为小驼峰获取。Query类似。
  • enable:通过服务端定义的函数名称获取,以上示例中函数名称Java函数名称一致,特殊情况下,服务端可使用@Function(name = "")注解或@Action.Advanced(name = "")注解进行修改。disable类似。
  • (data: { id: ${id} })data通过服务端定义的参数名称获取,与Java函数参数名称一致。值类型与服务端声明类型一致,对象类型采用{}包裹,对象中的每个属性类型与服务端声明类型同样一致。此处服务端声明的data名称的入参仅包含DemoModelInput中定义的属性。GQL是通过参数名称进行参数映射的,与参数位置无关。
  • 响应字段声明:idnameisEnabled为本次请求后获取到的相关字段,不在响应字段声明中的属性无法被获取。

PS:GQL在客户端发起的请求中提供了VariablesFragmentInline Fragment等方式进行GQL的复用。感兴趣的同学可在GQL官网自行学习。

TypeScript最佳实践

通常情况下,我们是不需要手动发起GQL的,但在某些特殊场景(无法获取元数据上下文等)中,这是无法避免的。

在我们学习如何发起GQL请求之前,我们需要达成一个基本共识,即ts类型声明API服务归集

  • ts类型声明:需按照模型定义类型声明,通常我们将这些类型声明放在typing.ts文件中进行管理。
  • API服务归集:需按照模型对应的服务分别定义相关API服务的调用方法,并将这些服务放在service.ts文件中进行管理。具体的方法根据实际情况进行判断,但不建议使用复杂入参定义服务方法。
  • 模块名称归集:需将所有API中用到的模块名称统一放在module-name.ts文件中进行管理。

工程结构

service目录将为该工程提供全部的API服务。

这是我们建议的工程结构,具体工程结构也可以根据自身情况进行相应变更。接下来,我们会在通过HttpClient发起一个GQL请求章节提供相关的示例代码作为参考。

├── src
│   ├── service
│   │   ├── demo-model
│   │   │   ├── index.ts
│   │   │   ├── service.ts
│   │   │   └── typing.ts
│   │   ├── model.ts
│   │   ├── module-name.ts
│   │   └── index.ts
│   ├── main.ts
│   └── shim-vue.d.ts
├── tsconfig.json
└── vue.config.js

通过HttpClient发起一个GQL请求

service/module-name.ts
export const DEMO = "demo";
service/index.ts
export * from './demo-model';
service/demo-model/typing.ts
export interface DemoModel {
    id?: string;
    name?: string;
    isEnabled?: string;
}
service/demo-model/service.ts
import { HttpClient } from '@kunlun/dependencies';
import { DEMO } from '../module-name';
import { DemoModel } from './typing';

const http = HttpClient.getInstance();

export class DemoModelService {
  public static async enable(id: string): Promise<DemoModel | undefined> {
    const gql = `mutation {
  demoModelMutation {
    enable(data: {id: "${id}"}) {
      id
      name
      isEnabled
    }
  }
}`;
    const res = await http.mutate<DemoModel>(DEMO, gql);
    return res.data.demoModelMutation.enable;
  }
}
service/demo-model/index.ts
export * from './typing';
export * from './service';

PS:对于GQL字符串的构造,平台还提供了另一种构造方式,开发者可根据自身喜好进行选择。

service/model.ts(模型名称归集,与模块名称归集类似)
export const DEMO_MODEL_NAME = 'demoModel';
service/demo-model/service.ts
import { GQL } from '@kunlun/dependencies';
import { DEMO_MODEL_NAME } from '../model';
import { DEMO } from '../module-name';
import { DemoModel } from './typing';

export class DemoModelService {
  public static async enable(id: string): Promise<DemoModel | undefined> {
    return GQL.mutation(DEMO_MODEL_NAME, 'enable')
      .buildRequest((builder) => builder.buildObjectParameter('data', (builder) => builder.stringParameter('id', id)))
      .buildResponse((builder) => builder.parameter('id', 'name', 'isEnabled'))
      .request(DEMO);
  }
}
使用对应服务
import { DemoService } from './service';

// 1. async/await (推荐)
const res = await DemoService.enable('1');
console.log(res);

// 2. Promise#then
DemoService.enable('1').then((res) => {
    console.log(res);
});

至此,我们已经可以发起任何GQL请求了。

通过window.open使用GET方式发起一个GQL请求

使用HttpClient发起的请求不论是使用query还是mutate,都是POST方式的请求。对于文件下载等需要使用GET方式的请求,我们通常使用window.open方法,并打开新的浏览器窗口来实现这一功能。

const gql = ${GQL};

window.open(UrlHelper.appendBasePath(/pamirs/${MODULE_NAME}?query=${encodeURIComponent(gql)}), '_blank');

客户端泛化调用任意API服务

GQL中定义的模型字段过多时,我们往往会发觉直接手写一个GQL的难度是相当高的,为此,我们提供了一个较为简单的调用服务方式,以辅助生成我们所需的GQL

该方法采用动态获取元数据的方式实现的泛化调用,仅限于开发环境使用,不适合生产环境使用。获取元数据的相关API服务相对于业务来说是一些无意义的损耗,因此,我们通常建议在开发时通过该方式生成GQL后,从请求体中拷贝该GQL,并改为通过HttpClient方式进行调用。一般而言,生成一次的GQL后,在未来的变更中仅会有个别字段的变化,不会产生过大的变化。

service/model.ts(模型名称归集,与模块名称归集类似)
export const DEMO_MODEL = 'demo.DemoModel';
service/demo-model/service.ts
import { GenericFunctionService } from '@kunlun/dependencies';
import { DEMO_MODEL } from '../model';
import { DemoModel } from './typing';

export class DemoModelService {
  public static async enable(id: string): Promise<DemoModel | undefined> {
    return GenericFunctionService.INSTANCE.executeByName(DEMO_MODEL, 'enable', { deep: 1 }, { id });
  }
}

这种方式需要使用模型编码函数名称作为入参,deep参数控制响应字段声明的层级,最后的不定长参数作为函数的入参。

由于泛化调用服务会动态获取模型关联模型函数等相关信息,函数名称参数名称等信息无法提前预知,也就无法明确定义参数名称,因此需要根据参数位置进行入参匹配。

小结

以上两种方式均是在没有元数据的情况下使用的请求方式,从之前的文章中我们可以了解到,元数据仅在main-view组件中才进行了收集,其他的情况是无法正常获取元数据的。

比如我们通常在mask中定义的组件,app-switchermenu等,这些组件需要发起相关请求,但其元数据无法在页面中获取。

不仅如此,这种请求方式只有在业务场景确定模型不再发生变更,并且确定该服务与权限相关功能完全无关时才可以使用。

由于我们无法提前预知该服务的相关变化,这些问题是无法避免的:

  • 当模型字段发生变化时,在代码中定义的GQL也需要发生相应的变更。
  • 当权限限制该用户发起该请求时,请求结果只会报错,我们无法预知服务是否可以正常调用。

客户端通过运行时上下文RuntimeContext发起GQL请求

在可以获取到元数据的情况下,我们可以用下面这种方式发起GQL请求。

service/demo-model/service.ts
import {
  GenericFunctionService,
  isRuntimeServerAction,
  RuntimeContext,
  RuntimeServerAction
} from '@kunlun/dependencies';
import { DemoModel } from './typing';

export class DemoModelService {
  public static async enable(runtimeContext: RuntimeContext, id: string): Promise<DemoModel | undefined> {
    const action = runtimeContext.model.modelActions.find(
      (v) => isRuntimeServerAction(v) && v.functionDefinition?.name === 'enable'
    ) as RuntimeServerAction;

    const functionDefinition = action?.functionDefinition;

    if (!functionDefinition) {
      return;
    }

    const requestModelFields = runtimeContext.getRequestModelFields();

    return GenericFunctionService.INSTANCE.execute(
      functionDefinition,
      {
        model: runtimeContext.model,
        requestFields: requestModelFields
      },
      { id }
    );
  }
}

在这个示例中,我们通过runtimeContext.model.modelActions查找所需调用的动作,如果该动作在当前运行时上下文中未找到,那我们就能知道这个动作可能在当前页面中未配置无权限访问。并且从运行时上下文中获取到的请求模型字段,可以完全按照页面配置进行按需请求。这也是我们推荐使用的调用方式之一。

需要注意的是,在GenericFunctionService#execute可选项中requestFields属性优先于deep属性。

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

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

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

相关推荐

  • 【界面设计器】自定义字段组件实战——千分位输入框

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 用户在输入【金额】字段时,实时展示千分位格式。 业务分析 从需求来看,我们需要实现一个【千分位】组件,并且该组件允许在【表单】视图中使用。 扩展实现:该组件虽然仅要求在【表单】中使用,但也可以在【搜索】中使用完全相同的实现,因此这里我们在注册时会增加【搜索】视图,将【千分位】组件应用在【搜索】中。对于【表格】、【详情】和【画廊】来说,该组件是没有【输入】行为的展示组件,在这里我们不进行演示。 准备工作 此处你应该已经在某个业务模型下,可以完整执行当前模型的全部【增删改查】操作。 业务模型定义 (以下仅展示本文章用到的模型字段,忽略其他无关字段。) 名称 API名称 业务类型 是否多值 长度(单值长度) 编码 code 文本 否 128 名称 name 文本 否 128 金额 money 金额 否 – 创建组件、元件 准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。 以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。 创建千分位组件 创建千分位元件 启动SDK工程进行组件基本功能开发 (npm相关操作请自行查看SDK工程中内置的README.MD) 关键点详解 数据交互类型的字段组件(以下简称展示组件)与仅展示类型的字段组件(以下简称交互组件)有一些差别。 通常情况下,在展示组件中仅需使用value属性即可展示相关内容。在交互组件中除了value用于展示外,还需使用change、focus以及blur处理用户输入时的基本交互逻辑。 数据交互方法主要功能说明: change方法:当值发生变更时调用,根据字段相关配置将值回填至表单中。 focus方法:当组件获取焦点时调用,记录当前值,并在调用blur方法时进行处理。 blur方法:当前组件失去焦点时调用,根据focus方法记录的值,进行失焦触发逻辑的执行。 这里的三个数据交互方法仅仅是对用户行为的抽象,而并非限定其使用场景。 通常来说,这三个方法的调用顺序为:focus –> change –> blur。 如在日期时间组件中,面板打开时调用了focus方法,面板值发生变更时,调用了change方法,面板关闭时调用了blur方法。 如在地图组件中,选中地图上的某个点时仅会调用change方法,用户交互上并不能体现出focus和blur的行为。因此对于这个组件而言,只会有change方法被执行。 代码实现参考 PS:oio-input-number样式必须升级到4.6.x以上的最新版本才支持 Thousandth.vue <template> <a-input-number class="oio-input-number" :value="inputValue" :formatter="formatter" :parser="parser" @update:value="change" @focus="focus" @blur="blur" /> </template> <script lang="ts"> import { InputNumber as AInputNumber } from 'ant-design-vue'; import { computed, defineComponent } from 'vue'; export default defineComponent({ name: 'Thousandth', components: { AInputNumber }, props: { value: { type: [String, Number] }, change: { type: Function }, focus: { type: Function }, blur: { type: Function } }, setup(props) { const inputValue = computed(() => { return props.value; }); const formatter = (value) => { return `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','); }; const parser = (value) => { return value.replace(/\$\s?|(,*)/g, ''); }; return { inputValue, formatter, parser }; } }); </script> FormMoneyThousandthFieldWidget.ts import { FormFieldWidget, ModelFieldType, SPI, ViewType } from '@kunlun/dependencies'; import Thousandth from './Thousandth.vue'; @SPI.ClassFactory( FormFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.Currency, widget:…

    2024年4月19日
    1.0K00
  • 从一个方法开始:浅析页面渲染流程

    渲染前的准备 渲染前的准备,在 Vue 渲染框架下,会先安装所有所支持的默认组件,比如 Mask,Header 等,这些组件支持 XML 默认模版的 Vue 框架下的渲染,详情可见 main.ts 中,maskInstall 与 install,这两个函数将平台内部支持的组件进行了注册,随后将整个 Vue 挂载为运行时 App,随后进行初始化。 渲染的起步 OioProvider 方法是整个应用的入口,我们忽略掉一些配置方法,将注意力集中到 initializeServices 从名称中我们可以看出来内部保存的都是初始化服务,其中提供了渲染服务等,我们当前使用的是 Vue 框架,所以当前其渲染的 Root 节点为 Vue, 以此,我们视野可以暂时转移到 admin-base中的 Root.vue以及 RootWidget上, 其实现了整个 Vue 框架下的 Root 节点如何渲染,其中定义了多个 widget,比如登陆页,首页,忘记密码已经重置密码等页面, 在本文中我们着重关注渲染首页的能力,RootWidget将 DefaultMetadataMainViewWidget作为渲染 Props中的 page即首页提供给下层组件使用, 渐入佳境 DefaultMetadataMainViewWidget从名称中可以看到,其为元数据主视图,主要做两件事,1:提供 Mask 的渲染2:提供元数据上下文初始化 该组件主要通过观察路由变化触发上面两个动作,当路由发生变化,该组件 reloadPage将被调用,reloadPage方法通过组装路由参数构成一个唯一 key 向后段查询当前路由所对应的渲染信息, 随后将获取到的信息进行处理,初始化,即 元数据上下文初始化,在初始化后,会将获取到的数据注入到 MetadataViewWidget中, Mask 的渲染 关于 Mask 的渲染,在获取到数据后,将生成 maskTemplate,并将其赋值, DefaultMetadataMainView.vue文件将渲染该模板,并渲染到页面中 数据的变更 当上面两件任务完成后,将开始主视图的渲染,上文提到,DefaultMetadataMainViewWidget只负责 mask 的渲染和上下文的初始化,所以 DefaultMetadataMainViewWidget通过触发事件的方式来实现主视图的渲染, DefaultMetadataMainViewWidget将必要信息作为事件参数触发,MultiTabsContainerWidget接收到 reloadMainViewCallChaining事件后,开启主视图渲染, MultiTabsContainerWidget会刷新运行时上下文,即 refreshRuntimeContext,该方法将尝试查询并通过 createOrUpdateEnterTab方法创建 Tab 页,createOrUpdateEnterTab最终生成一个 MultiTabItem格式的对象,该对象描述了 Tab 的相关信息,随后调用 createTabContainerWidget创建 tab 的容器即新建了一个 MultiTabContainerWidget组件即单个 tab 的容器,随后调用 setActiveTabItem, 并获取其绑定的 Vue 组件,并将其组件放置在 KeepAlive内部,触发更新, 主视图的渲染 MultiTabContainerWidget继承自MetadataViewWidget,当 MetadataViewWidget数据发生变更, 其绑定的 Vue 组件将解析 viewTemplate, 获取到与该模板 dslNodeType想匹配的 Vue 组件,当前例子中为 View.vue,随后 View.vue开始渲染,View.vue文件只是一个纯粹的容器,当 View.vue被挂载时,其内部的 template属性包含了整个页面的描述信息,View.vue需要做的就是将这个 template翻译并渲染成 DOM 展现在浏览器上, 渲染整个页面 当 View.vue被挂载时,其内部的 template属性包含了整个页面的描述信息,View.vue主要做了两个事情,一:将 template 中的 widget转换为组件,二:根据当前的 template信息生成 slot信息, const currentSlots = computed<Slots | undefined>( () => DslRender.fetchVNodeSlots(props.dslDefinition) || (Object.keys(context.slots).length ? context.slots : undefined) ); const renderWidget = createCustomWidget(InternalWidget.View, { …context.attrs, type: props.type || viewType.value, template: props.dslDefinition, metadataHandle: props.metadataHandle || metadataHandle.value, rootHandle: props.rootHandle || rootHandle.value, parentHandle: props.parentHandle || parentHandle.value, slotName: props.slotName, inline: inlineProp } as ViewWidgetProps); 生成这两部分信息后,View.vue会将这两部分挂载到页面上,这两部分从代码中可以看出,主要靠 fetchVNodeSlots,createCustomWidget两个函数, export function createCustomWidget( widget: string, props: CustomWidgetProps ): RenderWidget | undefined public static fetchVNodeSlots(dsl: DslDefinition | undefined, supportedSlotNames?: string[]):…

    2025年3月19日
    45700
  • 上下文在字段和动作中的应用

    上下文在字段和动作中的应用 在业务场景中,常常需要在打开弹窗或跳转到新页面时携带当前页面数据。此时,我们需要配置相关「动作」中的上下文信息。 在 oinone 平台中,上下文主要分为以下三种: activeRecord:当前视图数据 rootRecord:主视图数据 openerRecord:触发弹窗的对象 参考文档:oinone内的主视图数据和当前视图数据使用介绍 activeRecord 表示当前视图的数据。例如,若动作配置在表单上,则指代当前表单的数据;若配置在 o2m、m2m 字段表格上,则指代选中的行数据。 rootRecord 表示根视图的数据。若当前视图是表单页,则代表表单的数据;若为表格页,则代表表格的数据。 openerRecord 表示触发弹窗的对象。例如,在弹窗内的字段或动作中,可通过 openerRecord 获取触发弹窗的信息。 这三者均为对象 (Object) 类型。 界面设计器配置 在 o2m、m2m 表格字段弹窗中携带当前视图数据 假设我们设计了一个包含 o2m、m2m 表格字段的表单页面。打开相关弹窗时,需将表单中的 code 数据传递至弹窗中。 选择相应的「动作」,如创建或添加。在右侧属性面板底部找到「上下文」,添加格式为对象 {} 的上下文信息。 以键值对的格式添加上下文信息:{code: rootRecord.code}。 设计弹窗时,将 code 字段拖入弹窗中。 完成设计后保存并发布。 大家可以看到,上下文中的key是 code,但是value是rootRecord.code,这里取的是rootRecord而不是activeRecord,因为我们上面讲过如果当前动作配置在o2m、m2m的字段表格上面,那么activeRecord就是表格选中的行,我们现在要取的是表单上的code字段,所以需要用rootRecord。 注意点:key需要是提交模型【前端视图】存在的字段才能传递。

    2023年11月8日
    2.1K10
  • 【界面设计器】自定义字段组件基础

    阅读之前 本文档属于高阶实战文档,已假定你了解了所有必读文档中的全部内容,并了解过界面设计器的一些基本操作。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 概述 平台提供的字段组件(以下简称组件)是通过SPI机制进行查找并最终渲染在页面中。虽然平台内置了众多组件,但无法避免的是,对于业务场景复杂多变的实际情况下,我们无法完全提供所有组件。 面对这样的困境,我们提供了外部注册组件的方式。在之前文章中,我们了解到组件注册后需要应用到页面中,需要配合DSL才能实现。其实,在平台中还提供了一种SAAS化的组件注册方式,配合界面设计器的设计能力,可以将组件在设计器页面中引入,从而更近一步的满足不同的业务场景。 通过界面设计器可以创建自定义组件,并为组件添加对应的元件。 界面设计器可以为元件设计其在指定视图下的属性面板,在页面设计时,可以使用该属性面板为元件设置相关属性。 在界面设计器的设计页面中拖入的组件,将通过SPI机制获取到一个唯一的元件,并渲染在页面中,提供给业务使用。 界面设计器-组件管理 名词解释 页面设计:使用界面设计器设计页面的页面。 属性面板设计:使用界面设计器设计属性面板的页面。 设计页面:页面设计和属性面板设计的统称。 组件库:展示在设计页面左侧的全部可拖拽组件。 组件:在组件库中可拖拽的最小单元。 元件:一个组件中的具体实现,是组件的最小单元。 属性面板:展示在设计页面右侧的元件属性。 页面设计 属性面板设计 组件管理入口 进入界面设计器后,可通过上方标签页切换至【组件】管理页面。 创建第一个组件 点击【添加组件】,在弹窗中输入组件名称【文本输入框】后,点击【确定】。 创建第一个元件 点击【组件卡片】或点击【管理元件】按钮进入【元件】管理页面。 点击【添加元件】,可以看到如下【创建元件】弹窗。 表单字段解释 元件名称:显示名称,仅在管理页面做展示使用。 API名称:SPI中的widget属性。 支持字段业务类型:SPI中的ttype属性。 支持多值:SPI中的multi属性。 支持视图类型:SPI中的viewType属性。 元件描述:元件功能描述内容,仅在管理页面做展示使用。 填入以下内容,并点击【确定】。 设计元件属性面板 点击【元件卡片】或点击【设计元件属性】按钮进入【属性面板设计】页面。 从【模型】中搜索【标题】,将【标题】和【隐藏标题】拖放至设计区域。如果想实现的相对美观,可以额外添加【分组】组件拖放至设计区域,并修改标题为【基础】,如下图所示。 点击【发布】按钮进行页面的发布。 至此,我们设计了第一个元件属性面板,接下来,我们需要在页面设计中使用这个组件。 在页面中使用【文本输入框】 由于我们之前选择的支持的视图类型是【表单】,因此我们在【表单】页面进行接下来的操作,此处略去创建视图的过程。 从【模型】中将【名称】拖放至设计区域。并通过点击【切换】按钮切换至我们的组件【文本输入框】,并且将标题改为【这是文本输入框组件】查看其展示效果。 属性变化 在组件切换后,属性面板发生了变化,原有属性会根据当前属性面板中现有字段进行【裁剪】,相同属性名称(字段)的值会被保留,其他属性值会被丢弃。 由于我们并没有在当前属性面板添加【宽度】属性,因此原有属性的宽度被丢弃,组件会自动变成默认【宽度】,默认宽度为1。 组件变化 由于我们并没有在【低无一体】中上传对应元件的代码实现,因此展示了默认的【单行文本】组件,目前组件的展示效果不会发生变化。 组件可切换规则 只有【组件】中包含与【当前选中字段】匹配的【元件】,才会将对应【组件】的名称展示在【可切换列表】中。 【当前选中字段】中包含了如下三个属性,这三个属性和【创建元件】时设置的属性一一对应。 (右侧属性面板切换至字段后,可查看当前选中字段的相关元数据信息。) 所在视图类型:根据字段在视图中的位置进行推断,当前所在位置为【表单】。当【支持视图类型】包含【表单】时条件成立。 字段业务类型:文本。当【支持字段业务类型】包含【文本】时条件成立。 是否多值:否。当【支持多值】相同时条件成立。 使用低无一体为组件上传代码实现 进入【组件】管理页面,点击【低无一体】,打开【低无一体】弹窗。 按照步骤,在【生成SDK】后,可以【下载模板工程】。在本地进行npm相关操作后,会在packages/kunlun-plugin目录下生成dist目录。在dist目录中,会有对应的kunlun-sdk.umd.js文件,使用【上传JS文件】进行上传。如果工程中包含了css,使用【上传CSS文件】进行上传。上传完成后点击【确定】进行保存。 PS:在模板工程中,我们提供了最简化的Hello World示例,即使不添加任何代码也可以看到组件的具体效果,为了方便演示,我们暂时不介绍代码实现的相关内容,仅需直接上传对应js文件,看到效果即可。如果遇到相关问题,请点击查看【前端】低无一体部署常见问题。 结语 至此,我们已经完整体验了从【创建组件】到【属性面板设计】再到【使用组件】以及【实现组件】的全部流程。 通过这一流程我们不难发现,【自定义组件】并非仅仅用于【页面设计】,在【属性面板设计】时,我们同样可以使用【自定义组件】来设计【自定义组件】的属性面板。这样便形成了一个完整的设计闭环,使得开发者可以更大程度的发挥自身创造力,开发出符合业务需求的【自定义组件】。

    2023年11月1日
    2.1K00
  • 组件生命周期(v4)

    阅读之前: 你应该: 了解DSL相关内容。母版-布局-DSL 渲染基础(v4) 对第三方框架的组件生命周期有所了解。如Vue组件生命周期 了解平台实现的Class Component(ts)相关内容。Class Component(ts)(v4) 组件生命周期 任何一个Widget其标准生命周期应当包括beforeCreated、created、beforeMount、mounted、beforeUnmount、unmounted这六个基本的生命周期函数、以及beforeUpdate和updated在响应式更新时会进行调用的生命周期函数。特别的,还有activated和deactivated在组件搭配keep-alive特性时使用的生命周期函数。 具体的生命周期执行过程在这里不再进行赘述,这里的基本逻辑与Vue组件生命周期基本完全一致,感兴趣的读者可以阅读Vue相关文档进行学习。

    2023年11月1日
    1.1K10

Leave a Reply

登录后才能评论