如何自定义 GraphQL 请求

在开发过程中,有时需要自定义 GraphQL 请求来实现更灵活的数据查询和操作。本文将介绍两种主要的自定义 GraphQL 请求方式:手写 GraphQL 请求和调用平台 API。

方式一:手写 GraphQL 请求

手写 GraphQL 请求是一种直接编写查询或变更语句的方式,适用于更复杂或特定的业务需求。以下分别是 querymutation 请求的示例。

1. 手写 Query 请求

以下是一个自定义 query 请求的示例,用于查询某个资源的语言信息列表。

const customQuery = async () => {
  const query = `{
    resourceLangQuery {
      queryListByEntity(query: {active: ACTIVE, installState: true}) {
        id
        name
        active
        installState
        code
        isoCode
      }
    }
  }`;

  const result = await http.query('resource', query);
  this.list = result.data['resourceLangQuery']['queryListByEntity'];
};

说明:

  • query 语句定义了一个请求,查询 resourceLangQuery 下的语言信息。
  • 查询的条件是 activeinstallState,只返回符合条件的结果。
  • 查询结果包括 id、name、active、installState 等字段。

2. 手写 Mutation 请求

以下是一个 mutation 请求的示例,用于创建新的资源分类信息。


const customMutation = async () => {
  const code = Date.now()
  const name = `测试${code}`

  const mutation = `mutation {
    resourceTaxKindMutation {
      create(data: {code: "${code}", name: "${name}"}) {
        id
        code
        name
        createDate
        writeDate
        createUid
        writeUid
      }
    }
  }`;

  const res = await http.mutate('resource', mutation);
  console.log(res);
};

说明:

  • mutation 语句用于创建一个新的资源分类。
  • create 操作的参数是一个对象,包含 code 和 name 字段。
  • 返回值包括 id、createDate 等字段。

方式二:调用平台的 API

平台 API 提供了简化的 GraphQL 调用方法,可以通过封装的函数来发送 query 和 mutation 请求。这种方式减少了手写 GraphQL 语句的复杂性,更加简洁和易于维护。

1. 调用平台的 Mutation API

使用平台的 customMutation 方法可以简化 Mutation 请求。

/**
 * 自定义请求方法
 * @param modelModel 模型编码
 * @param method 方法名或方法对象
 * @param records 请求参数,可以是单体对象或者对象的列表
 * @param requestFields 请求的字段配置,不传就是解析record内的所有字段
 * @param responseFields 响应的字段配置,不传就是所有字段都返回
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const customMutation = async (
  modelModel: string,
  method: string | { name: string; argumentName: string },
  records: ObjectValue | ListValue,
  requestFields?: IModelField[],
  responseFields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<any>
调用代码示例
// 1.customMutation 调用示例
const createTaxKind = async () => {
  const response = await customMutation(
    '模型编码',
    '模型方法',
    { code: '003', name: '测试3' }
  );
  console.log(response);
};

const createTaxKind2 = async () => {
  const response = await customMutation(
    '模型编码',
    {name:  '方法名', argumentName: '参数名'},
    { code: '003', name: '测试3' }
  );
  console.log(response);
};

2. 调用平台的 Query API

普通查询数据方法customQuery

/**
 * 自定义查询方法
 * @param modelModel 模型编码
 * @param method 方法名
 * @param record 请求参数,可以是单体对象或者对象的列表
 * @param requestFields 请求的字段配置,不传就是解析record内的所有字段
 * @param responseFields 响应的字段配置,不传就是所有字段都返回
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const customQuery = async <T>(
  modelModel: string,
  method: string,
  record: ObjectValue | ListValue | string = {},
  requestFields?: IModelField[],
  responseFields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<T>
调用代码示例
const fetchResourceLanguages = async () => {
  const response = await customQuery(
    '模型编码',
    '模型方法',
    { active: true },
  );
  console.log(response);
};

自定义分页类型接口查询方法

/**
 * 自定义分页类型接口查询方法
 * @param modelModel 模型编码
 * @param methodName 方法名
 * @param option 查询条件
 * @param requestFields 请求的字段配置,不传就是解析record内的所有字段
 * @param responseFields 响应的字段配置,不传就是所有字段都返回
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const customQueryPage = async <T = Record<string, unknown>>(
  modelModel: string,
  methodName: string,
  option: IQueryPageOption,
  requestFields?: IModelField[],
  responseFields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<IQueryPageResult<T>>
调用代码示例
// 存储字段的rsql查询条件
const rsql = `num > 1 and name =like='关键字'`;
// 查询条件对象
const condition = new Condition(rsql);
// 非存储字段的queryData查询条件
const queryData = { type: 'B2C' };
condition.setConditionBodyData(queryData)
const option = {
  // 当前页码
  currentPage: 1,
  // 每页条数
  pageSize: 20,
  // 查询条件对象,也可以是rsql字符串
  condition,
  // 自定义排序
  sort: [{sortField: 'id', direction: EDirection.ASC} as ISort]
} as IQueryPageOption;

const variables = {
  // 当调用的接口有权限相关提示可以设置该属性
  path: getSessionPath()
};
const page = await customQueryPage(
  'demo.demoItem',
  'queryPage',
  option,
  [],
  undefined,
  variables,
  { maxDepth: 1 }
);

标准分页接口查询方法

/**
 * 标准分页接口查询方法
 * @param modelModel 模型编码
 * @param option 查询条件
 * @param fields 请求和响应字段配置,不传就取当前模型内的所有字段
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const queryPage = async <T = Record<string, unknown>>(
  modelModel: string,
  option: IQueryPageOption,
  fields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<IQueryPageResult<T>> => {
  // 内部实际调用的也是customQueryPage
  return customQueryPage(modelModel, 'queryPage', option, fields, fields, variables, context);
};
调用方法示例

参考customQueryPage的调用示例

查询方法关键类型的定义

/**
 * 分页查询条件
 */
interface IQueryPageOption {
  pageSize?: number;
  currentPage?: number;
  sort?: ISort | ISort[];
  condition?: Condition | string;
  // condition 和 record 只能二选一
  record?: ObjectValue | ListValue;
  maxDepth?: number;
}

interface IQueryPageResult<T> {
  content: T[];
  totalElements: number;
  size: number;
  totalPages: number;
}

通用注意事项

默认情况下,平台 API 请求只会查询两层,如果要查询第三层,则需要传递往下查询深度的context.maxDepth属性,maxDepth=1为一共查询两层,maxDepth=2为一共查询三层;在平台底层maxDepth是从0开始的,0代表了第一层级,所以为1的时候,就是查两层。

/**
 * 响应字段的配置,不配置会返回模型下所有字段,
 * 建议按需求配置需要返回哪些字段,
 * 下面的配置等同于手写gql的
 * user {
 *   id
 *   name
 * }
 */
const responseFields = [
      {
        name: 'user',
        ttype: ModelFieldType.ManyToOne,
        modelFields: [
          { name: 'id', ttype: ModelFieldType.Long },
          { name: 'name', ttype: ModelFieldType.String }
        ] as IModelField[]
      }
    ] as IModelField[];

customMutation(
    '模型编码',
    '模型方法',
    { code: '003', name: '测试3' },
    undefined,
    responseFields,
    undefined,
    {
      maxDepth: 2
    }
  )

customQuery(
    '模型编码',
    '模型方法',
    { active: true },
    undefined,
    responseFields,
    undefined,
    {
      maxDepth: 2
    }
  )

对比

  • 手写 GraphQL 请求适用于请求参数比较简单的请求
  • 调用平台 API 适用于请求参数过于复杂、手动很难写

调用平台 API的弊端

调用平台 API会导致gql的请求体很大,因为底层会把当前模型所有的字段都作为响应体返回。如果请求的层级越深,那么gql请求体越大。

如果想通过手写Graphql的方法拼接复杂的请求参数,可以参考这边文章,里面有详细的讲解

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

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

Like (0)
汤乾华's avatar汤乾华数式员工
Previous 2024年9月21日 pm4:17
Next 2024年9月26日 am9:19

相关推荐

  • 【界面设计器】自定义字段组件实战——表格字段组合展示

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 表格中的一列使用多个字段组合展示。 演示内容:表格中存在两列,【编码】和【基础信息】。将【名称】、【创建时间】、【更新时间】在【基础信息】一列展示。 业务分析及实现思路 从需求来看,我们需要实现一个【组合列】组件,并且该组件允许在【表格】视图中使用。由于【组合列】本身也是一个字段,因此这里需要选择需要组合字段中的其中一个字段作为组件切换的基础字段,比如我们可以选择【名称】字段作为基础字段。 在【组合列】组件的属性面板中,我们需要再自定义一个【组合列配置】组件,用来选择需要将哪些字段进行组合,以及为每个组合提供一些基础配置。 这里需要理解一个基本概念,即【组合列】的属性面板是【组合列配置】的【执行页面】。所有组件的属性面板在【执行页面】时都是【表单】视图。 因此我们可以实现一个【组合列配置】组件,并且该组件允许在【表单】视图中使用。其业务类型使用【文本】,我们在保存配置数据时,可以使用JSON数据结构来存储复杂结构。(这里的实现思路并非是最符合协议设定的,但可以满足绝大多数组件场景) 在【组合列配置】组件中,我们可以允许用户添加/移除组合,并且每个组合有两个属性,【标题】和【字段】。 准备工作 此处你应该已经在某个业务模型下,可以完整执行当前模型的全部【增删改查】操作。 业务模型定义 (以下仅展示本文章用到的模型字段,忽略其他无关字段。) 名称 API名称 业务类型 是否多值 长度(单值长度) 编码 code 文本 否 128 名称 name 文本 否 128 创建时间 createDate 日期时间 否 – 更新时间 updateDate 日期时间 否 – 实现页面效果展示 表格视图 创建组件、元件 准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。 以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。 创建组合列组件 创建组合列元件 创建组合列配置组件 创建组合列配置元件 设计组合列元件属性面板 创建compositeConfig字段,并切换至【组合配置】组件。 设计组合列配置元件属性面板 启动SDK工程进行组件基本功能开发 PS:这里由于我们创建了两个组件,因此,将SDK分开下载后,然后将组合列配置SDK中的演示代码(kunlun-plugin/src)移动到组合列SDK中,在同一工程中进行开发,最后只需将相关JS文件和CSS文件上传到组合列组件中即可,组合列配置组件可以不进行上传。这里需要注意的是,上传多个包含相同组件功能的JS文件和CSS文件可能在运行时导致无法正常替换、冲突等问题。 (npm相关操作请自行查看SDK工程中内置的README.MD) 开发步骤参考 打开【表格】视图,将【名称】字段的组件切换为【组合列】组件。 在属性面板中看到【组合列配置】组件,并优先实现【组合列配置】组件。这里的属性面板就是【组合列配置】对应的【执行页面】。 当【组合列配置】组件可以按照预先设计的数据结构正确保存compositeConfig属性时,可以在【组合列】组件中的props定义中直接获取该属性,接下来就可以进行【组合列】组件的开发。 代码实现参考 工程结构 typing.ts export interface CompositeConfig { key: string; label?: string; field?: string; value?: string; } CompositeColumnConfig.vue <template> <div class="composite-column-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 { WidgetInstance } from '@kunlun/ui-designer-dependencies'; import { OioButton, OioForm, OioFormItem, OioInput } from '@kunlun/vue-ui-antd'; import { Select as ASelect } from 'ant-design-vue'; import { computed, defineComponent, PropType, ref, watch } from 'vue'; import { CompositeConfig } from '../../typing'; export default defineComponent({ name: 'CompositeColumnConfig', components: { OioForm, OioFormItem, OioInput, OioButton, ASelect }, props: { currentInstance: { type:…

    2023年11月1日
    1.7K00
  • 前端6.2.x升级指南

    升级背景 从 6.2.x 版本开始,平台底层核心代码已全面开源(设计器相关代码除外),本次升级涉及包名变更和配置调整,以下是详细的升级步骤 1. 更新 .npmrc 配置 修改当前项目根目录 .npmrc 文件,内容如下: registry=https://registry.npmmirror.com/ @oinone:registry=http://nexus.shushi.pro/repository/kunlun/ @kunlun:registry=http://nexus.shushi.pro/repository/kunlun/ shamefully-hoist=true legacy-peer-deps=true link-workspace-packages = true prefer-workspace-packages = true recursive-install = true lockfile=true 2. 包名变更 以前的包是 @kunlun/xxxx,现在的包名是@oinone/kunlun开头,所以需要全部替换修改。 旧包名 新包名 @kunlun/dependencies @oinone/kunlun-dependencies @kunlun/vue-ui-antd @oinone/kunlun-vue-ui-antd 3. 升级依赖版本 修改所有 package.json 中更新所有平台相关依赖: – "@kunlun/dependencies": "~5.7.0" + "@oinone/kunlun-dependencies": "~6.2.0" boot工程的package.json新增devDependencies依赖 "devDependencies": { "@types/lodash": "4.14.182", "@types/lodash-es": "4.17.6", ….. } 4. 更新代码引用 全局搜索并替换代码中的所有 @kunlun/ 为 @oinone/kunlun- 完整的 main.ts import 'ant-design-vue/dist/antd.min.css'; import 'element-plus/dist/index.css'; import '@oinone/kunlun-vue-ui-antd/dist/oinone-kunlun-vue-ui-antd.css'; import '@oinone/kunlun-vue-ui-el/dist/oinone-kunlun-vue-ui-el.css'; import '@oinone/kunlun-designer-common/dist/oinone-kunlun-designer-common.css'; import '@oinone/kunlun-model-designer/dist/oinone-kunlun-model-designer.css'; import '@oinone/kunlun-workflow-designer/dist/oinone-kunlun-workflow-designer.css'; import '@oinone/kunlun-workflow/dist/oinone-kunlun-workflow.css'; import '@oinone/kunlun-data-designer/dist/oinone-kunlun-data-designer.css'; import '@oinone/kunlun-microflow-designer/dist/oinone-kunlun-microflow-designer.css'; import 'reflect-metadata'; import ElementPlus from 'element-plus'; import { App } from 'vue'; import { VueOioProvider, RuntimeContextManager } from '@oinone/kunlun-dependencies'; // 设计器 import '@oinone/kunlun-ui-designer-dependencies'; import '@oinone/kunlun-print-designer-dependencies'; import '@oinone/kunlun-workflow'; import '@oinone/kunlun-workflow-designer'; import '@oinone/kunlun-model-designer'; import '@oinone/kunlun-data-designer'; import '@oinone/kunlun-data-designer-open-pc'; import '@oinone/kunlun-eip-dependencies'; import '@oinone/kunlun-ai-dependencies'; import '@oinone/kunlun-microflow-designer'; import '@oinone/kunlun-designer-common-ee'; // 下面三个依赖是当前工程的包,请根据实际场景修改 import '@ss/oinone'; import '@ss/admin-widget'; import '@ss/project'; VueOioProvider( { browser: { title: 'Oinone – 构你想象!', favicon: 'https://pamirs.oss-cn-hangzhou.aliyuncs.com/pamirs/image/default_favicon.ico' } }, [ () => { /// 获取当前vue实例 const app = RuntimeContextManager.createOrReplace().frameworkInstance as App; } ] ); 当配置全部修改完成后,重写安装依赖 # 清理旧依赖 pnpm clean # 安装新依赖 pnpm install

    2025年6月17日
    76800
  • 多对多的表格 点击添加按钮打开一个表单弹窗

    多对多的表格 点击添加按钮打开一个表单弹窗 默认情况下,多对多的表格上方的添加按钮点击后,打开的是个表格 ,如果您期望点击添加按钮打开的是个表单页面,那么可以按照下方的操作来 1: 先从界面设计器拖一个多对多的字段进来 2: 将该字段切换成表格,并拖入一些字段到表格上 3: 选中添加按钮,将其隐藏 4: 从组件区域的动作分组中拖一个跳转动作,并且进行如下的配置 5: 属性填写好后进行保存,然后在设计弹窗 6: 拖入对应的字段到弹窗中, 当弹窗界面设计完成后,再把保存的按钮拖入进来 这样多对多的添加弹窗就变成了表单

    2023年11月9日
    2.0K00
  • 自定义字段组件如何处理vue组件内的表单校验

    介绍 本示例以字符串字段为业务场景,将输入框用element-plus的组件实现了一遍,vue组件内在onMounted生命周期内将ElForm表单实例通过ts组件内提供到props的setFormInstance方法设置到了ts组件的属性formInstance上,这样就可以在ts组件校验方法validator()触发的时候直接调用表单组件实例formInstance的校验方法validate() 适用场景 当前字段存储了动态表单的配置json,vue组件内自行实现了一套表单渲染逻辑,需要在vue组件和ts组件内同时触发校验 参考文档 element-plus表单组件文档 如何编写自定义字段组件的校验逻辑 示例代码 ts组件 import { BaseFieldWidget, FormStringFieldSingleWidget, isValidatorSuccess, ModelFieldType, SPI, ValidatorInfo, ViewType, Widget } from '@kunlun/dependencies'; import { FormInstance } from 'element-plus'; import MyFormStringField from './MyFormStringField.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.String, widget: 'Input', model: 'resource.k2.Model0000000109', name: 'code', }) ) export class MyFormStringFieldWidget extends FormStringFieldSingleWidget { public initialize(props) { super.initialize(props); this.setComponent(MyFormStringField); return this; } /** * ElementPlus的表单vue组件实例 * @private */ private formInstance?: FormInstance; @Widget.Method() private setFormInstance(formInstance: FormInstance | undefined) { this.formInstance = formInstance; } /** * 字段校验方法 */ public async validator(): Promise<ValidatorInfo> { const validRes = await this.formInstance?.validate((valid, fields) => {}); console.log('validRes', validRes) if (!validRes) { return this.validatorError('校验失败'); } const res = await super.validator(); if (!isValidatorSuccess(res)) { return res; } if (this.value == null) { return this.validatorSuccess(); } return this.validateLength(this.value); } } vue组件 <template> <ElForm ref="formInstance" :model="model" :rules="rules"> <ElFormItem label="编码" prop="code"> <ElInput v-model="model.code" @input="onValueChange"></ElInput> </ElFormItem> </ElForm> </template> <script lang="ts"> import { defineComponent, reactive, ref, onMounted, watch } from 'vue'; import { ElForm, ElFormItem, ElInput, FormInstance } from 'element-plus'; export default defineComponent({ name: 'MyFormStringField', components: { ElForm, ElFormItem, ElInput }, props: ['value', 'setFormInstance', 'onChange'],…

    2024年9月6日
    2.2K00
  • 自定义字段的数据联动

    某种情况下,开发人员期望自定以的字段发生变化后,需要修改其他的字段,这篇文章从两个维度来讲解如果处理数据的联动 界面设计器配置 1: 在界面设计器页面中的的组件区域找到自定义的字段,设计元件 2: 在模型区域,搜索提交方式,如果找到了,就把该字段拖拽进来, 如果找不到,就在元件中的组件区域,拖拽一个文本字段,按照下面的配置进行配置,然后保存 图一是找到了对应的字段图二是找不到对应的字段 【图一】 【图二】 图二的字段编码必须是constructDataTrigger 3: 从模型区搜索联动函数,将其拖拽进来 3: 从模型区搜索提交数据,将其拖拽进来4: 从模型区搜索提交字段,将其拖拽进来 5: 发布 (记得刷新页面哦) 最后再到对应的设计器页面,选中该字段,进行配置 提交方式为blur或者change , 需要开发者手动调用该方法 this.blur()或者this.change(value) // 字段对应的ts文件 class MyField extends FormFieldWidget { onChangeValue(val) { // this.change(val) // this.blur() } } 联动函数就是要调用的后端函数 提交数据分为:变更字段 -> 发生变化后的字段当前视图字段 -> 当前视图所有的字段指定字段 -> 指定字段,如果配置的指定字段,那么提交字段的配置就要输入对应的字段 代码配置 平台也支持通过代码的方式修改字段 // 字段对应的ts文件 class MyField extends FormFieldWidget { onChangeValue(val) { // 修改字段本身的值 this.change(val) // 修改其他字段的值 this.formData.otherField = 'value' this.reloadFormData$.subject.next(true); } }

    2023年11月9日
    2.7K00

Leave a Reply

Please Login to Comment