From b929a1a351a3dfd49e88bfba70fc79dfa28d4e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=A4=9A=E7=9B=8A?= Date: Tue, 22 Mar 2022 12:24:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20api=20=E5=AF=B9=20?= =?UTF-8?q?graphql=20=E7=9A=84=E6=94=AF=E6=8C=81=20(#3819)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh-CN/components/crud.md | 8 +++ docs/zh-CN/types/api.md | 95 +++++++++++++++++++++++++++++++++++ mock/cfc/mock/sample.js | 6 +++ src/store/crud.ts | 10 ++++ src/types.ts | 2 + src/utils/api.ts | 16 ++++++ 6 files changed, 137 insertions(+) diff --git a/docs/zh-CN/components/crud.md b/docs/zh-CN/components/crud.md index 4925f6231..a681ab7c7 100755 --- a/docs/zh-CN/components/crud.md +++ b/docs/zh-CN/components/crud.md @@ -98,6 +98,14 @@ CRUD,即增删改查组件,主要用来展现数据列表,并支持各类 > 如果 api 地址中有变量,比如 `/api/mock2/sample/${id}`,amis 就不会自动加上分页参数,需要自己加上,改成 `/api/mock2/sample/${id}?page=${page}&perPage=${perPage}` +## 分页参数 + +默认的分页参数是 `page` 和 `perPage`,page 代表页数,比如第一页,perPage 代表每页显示几行。 + +如果要其它格式,比如转成 `limit` 和 `offset`,可以使用公式来转换,比如 + +`/api/mock2/sample?limit=${perPage}&offset=${(page - 1) * perPage}` + ## 功能 既然这个渲染器叫增删改查,那接下来分开介绍这几个功能吧。 diff --git a/docs/zh-CN/types/api.md b/docs/zh-CN/types/api.md index 6b3669527..5117b3444 100755 --- a/docs/zh-CN/types/api.md +++ b/docs/zh-CN/types/api.md @@ -938,6 +938,101 @@ Access-Control-Expose-Headers: Content-Disposition } ``` +## GraphQL + +1.7.0 及之前的版本需要通过配置 `data` 里的 `query` 和 `variables` 字段可以实现 GraphQL 查询 + +```schema: scope="body" +{ + "type": "form", + "api": { + "method": "post", + "url": "/api/mock2/form/saveForm", + "data": { + "query": "mutation AddUser($name: String!, $email: String!) { \ + insert_user(object: { title: $title, email: $email }) { \ + title \ + email \ + } \ + }", + "variables": { + "name": "${name}", + "email": "${email}" + } + } + }, + "body": [ + { + "type": "input-text", + "name": "name", + "label": "姓名:" + }, + { + "name": "email", + "type": "input-email", + "label": "邮箱:" + } + ] +} +``` + +1.8.0 及以上版本简化了 GraphQL 的支持,增加了 `graphql` 属性,如果配置了就会自动并自动将 data 当成 `variables`,上面的例子可以简化为下面的写法,除了简化之外还方便了可视化编辑器编辑 + +```schema: scope="body" +{ + "type": "form", + "api": { + "method": "post", + "url": "/api/mock2/form/saveForm", + "graphql": "mutation AddUser($name: String!, $email: String!) { \ + insert_user(object: { name: $name, email: $email }) { \ + name \ + email \ + } \ + }" + }, + "body": [ + { + "type": "input-text", + "name": "name", + "label": "姓名:" + }, + { + "name": "email", + "type": "input-email", + "label": "邮箱:" + } + ] +} +``` + +如果设置了 `data` 会被当成 `variables`,比如在 CRUD 里设置分页参数,比如下面的例子 + +```json +{ + "type": "crud", + "api": { + "url": "/api/mock2/sample", + "method": "post", + "graphql": "{ pages(page: $page, perPage: $perPage) { id, engine } }", + "data": { + "page": "${page}", + "perPage": "${perPage}" + } + }, + "columns": [ + { + "name": "id", + "label": "ID" + }, + { + "name": "engine", + "label": "Rendering engine" + } + ] +} +``` + ## 属性表 | 字段名 | 说明 | 类型 | 备注 | diff --git a/mock/cfc/mock/sample.js b/mock/cfc/mock/sample.js index 884798564..6b273ab76 100755 --- a/mock/cfc/mock/sample.js +++ b/mock/cfc/mock/sample.js @@ -25,6 +25,12 @@ module.exports = function (req, res) { return bulkUpdate2(req, res); } else if (pathname === 'sample/mirror') { return mirror(req, res); + } else if (pathname === 'sample/array') { + return res.json(DB.concat()); + } else if (pathname === 'sample/itemsHasAnother') { + return res.json({myItems: DB.concat()}); + } else if (pathname === 'sample/arrayInData') { + return res.json({data: DB.concat()}); } return index(req, res); diff --git a/src/store/crud.ts b/src/store/crud.ts index e881294b0..894a7ab1b 100644 --- a/src/store/crud.ts +++ b/src/store/crud.ts @@ -272,6 +272,16 @@ export const CRUDStore = ServiceStore.named('CRUDStore') items = result.items || result.rows; } + // 如果不按照 items 格式返回,就拿第一个数组当成 items + if (!Array.isArray(items)) { + for (const key of Object.keys(result)) { + if (result.hasOwnProperty(key) && Array.isArray(result[key])) { + items = result[key]; + break; + } + } + } + if (!Array.isArray(items)) { throw new Error(self.__('CRUD.invalidArray')); } else { diff --git a/src/types.ts b/src/types.ts index 8513ea619..df996e220 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,6 +6,8 @@ export interface ApiObject extends SchemaApiObject { withCredentials?: boolean; cancelExecutor?: (cancel: Function) => void; }; + graphql?: string; + operationName?: string; body?: PlainObject; query?: PlainObject; adaptor?: (payload: object, response: fetcherResult, api: ApiObject) => any; diff --git a/src/utils/api.ts b/src/utils/api.ts index 6ee33828d..6d7bfc8b1 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -172,6 +172,22 @@ export function buildApi( } } + if (api.graphql) { + if (api.method === 'get') { + api.query = api.data = {...api.query, query: api.graphql}; + } else if ( + api.method === 'post' || + api.method === 'put' || + api.method === 'patch' + ) { + api.body = api.data = { + query: api.graphql, + operationName: api.operationName, + variables: cloneObject(api.data) + }; + } + } + return api; }