diff --git a/website/i18n/zh-CN.json b/website/i18n/zh-CN.json index 089faf1..8582e0d 100644 --- a/website/i18n/zh-CN.json +++ b/website/i18n/zh-CN.json @@ -38,4 +38,4 @@ "Edit this Doc|recruitment message asking to edit the doc source": "Edit", "Translate this Doc|recruitment message asking to translate the docs": "Translate" } -} \ No newline at end of file +} diff --git a/website/translated_docs/zh-CN/actions.md b/website/translated_docs/zh-CN/actions.md index 97b0b31..d6ef802 100644 --- a/website/translated_docs/zh-CN/actions.md +++ b/website/translated_docs/zh-CN/actions.md @@ -2,6 +2,7 @@ id: actions title: 编写 Action --- + Admin 界面通常必须提供自定义操作,而不仅仅是简单的 CRUD。 例如, 在管理评论中, "Approve" 按钮 (允许更新` is_approved` 属性, 并在一次单击中保存更新的记录)-是必须具有的。 如何使用 react-admin 添加这样的自定义动作? 答案是双重的,react-admin 如何使用 Redux 和 redux-saga,学习并正确的做到这一点会给你更好的理解。 diff --git a/website/translated_docs/zh-CN/admin-component.md b/website/translated_docs/zh-CN/admin-component.md index 49b802c..8e0806a 100644 --- a/website/translated_docs/zh-CN/admin-component.md +++ b/website/translated_docs/zh-CN/admin-component.md @@ -2,6 +2,7 @@ id: admin-component title: Admin 组件 --- + `<Admin>` 组件创建一个具有自己的状态、路由和控制器逻辑的应用程序。 `<Admin>` 只需要一个 `dataProvider` 属性, 并且至少有一个子 `<Resource>` 就能工作: ```jsx diff --git a/website/translated_docs/zh-CN/authentication.md b/website/translated_docs/zh-CN/authentication.md index 1bd88a8..663418f 100644 --- a/website/translated_docs/zh-CN/authentication.md +++ b/website/translated_docs/zh-CN/authentication.md @@ -2,6 +2,7 @@ id: authentication title: 身份验证 --- +  React-admin 可以通过您选择的身份验证策略来保护您的admin app。 由于有许多不同的可能策略(Basic Auth,JWT,OAuth等),react-admin 只需提供钩子来执行您自己的验证代码。 diff --git a/website/translated_docs/zh-CN/authorization.md b/website/translated_docs/zh-CN/authorization.md index 0ab80e7..7400e5a 100644 --- a/website/translated_docs/zh-CN/authorization.md +++ b/website/translated_docs/zh-CN/authorization.md @@ -2,6 +2,7 @@ id: authorization title: 授权 --- + 某些应用程序可能需要确定特定身份验证的用户对安全资源的访问级别。 由于有许多不同的可能策略 (单个角色、多个角色或权限等), 因此, react-admin 只需提供 hooks 即可执行您自己的授权代码。 默认情况下, react-admin 应用程序不需要授权。 但是, 如果需要, 它将依赖于 [Authentication](./Authentication.html) 部分中引入的 `authProvider`。 diff --git a/website/translated_docs/zh-CN/creat-edit-view-components.md b/website/translated_docs/zh-CN/creat-edit-view-components.md index 8949c7f..d59b971 100644 --- a/website/translated_docs/zh-CN/creat-edit-view-components.md +++ b/website/translated_docs/zh-CN/creat-edit-view-components.md @@ -2,6 +2,7 @@ layout: creat-edit-view-components title: Create 和 Edit 视图组件 --- + “创建”和“编辑”视图都显示一个表单,使用空记录初始化的(对于“创建视图”)或具有一条从API(对于“编辑”视图)获取的记录。 `<Create>` 和 `<Edit>` 组件然后将表单的实际渲染委托给表单组件 - 通常是 `<SimpleForm>`。 此表单组件使用其子级 ([`<Input>`](./Inputs.md) 组件) 来呈现每个 from input。  diff --git a/website/translated_docs/zh-CN/custom-app.md b/website/translated_docs/zh-CN/custom-app.md index 1403d54..ff4a4b8 100644 --- a/website/translated_docs/zh-CN/custom-app.md +++ b/website/translated_docs/zh-CN/custom-app.md @@ -2,6 +2,7 @@ id: custom-app title: 在其它 App 中包含 Admin --- + `<Admin>` 标签是一个很好的快捷方式,在几分钟内就可以启动和运行react-admin。 但是,在许多情况下,您需要将 admin 嵌入到另一个应用程序中,或者深入定制 admin。 幸运的是,您可以在任何 React 应用程序中执行 `<Admin>` 所做的所有工作。 请注意,您需要进一步了解 [redux](http://redux.js.org/),[react-router](https://github.com/reactjs/react-router) 和 [redux-saga](https://github.com/yelouafi/redux-saga)。 diff --git a/website/translated_docs/zh-CN/data-providers.md b/website/translated_docs/zh-CN/data-providers.md index 5d38e45..b25e12b 100644 --- a/website/translated_docs/zh-CN/data-providers.md +++ b/website/translated_docs/zh-CN/data-providers.md @@ -2,6 +2,7 @@ id: data-providers title: 数据提供程序 --- + 无论使用何种规范,REST,GraphQL还是SOAP。React-admin都可以与它的API进行通信。 对于 REST 服务器, 它可以是 [JSON API](http://jsonapi.org/), [HAL](http://stateless.co/hal_specification.html), [OData](http://www.odata.org/) 或自定义规范。 React-admin 只需要一个 Data Provider 函数。 这是将数据查询转换为 http 请求的地方, 以及对数据响应的 http 响应。  diff --git a/website/translated_docs/zh-CN/ecosystem.md b/website/translated_docs/zh-CN/ecosystem.md index 6c27fe6..a8cfb7e 100644 --- a/website/translated_docs/zh-CN/ecosystem.md +++ b/website/translated_docs/zh-CN/ecosystem.md @@ -2,6 +2,7 @@ id: ecosystem title: 生态 --- + - [Input 组件和 Field 组件](#inputs-and-fields) - [翻译](#translations) - [Data Providers](#data-providers) diff --git a/website/translated_docs/zh-CN/faq.md b/website/translated_docs/zh-CN/faq.md index c3d8e81..220def5 100644 --- a/website/translated_docs/zh-CN/faq.md +++ b/website/translated_docs/zh-CN/faq.md @@ -2,6 +2,7 @@ id: faq title: 常见问题 --- + - [是否可以为资源提供自定义标识符/主键?](#can-i-have-custom-identifiersprimary-keys-for-my-resources) - [我收到关于数组中子项的唯一键的警告](#i-get-warning-about-unique-key-for-child-in-array) - [呈现时冻结验证的表单](#a-form-with-validation-freezes-when-rendering) diff --git a/website/translated_docs/zh-CN/field-components.md b/website/translated_docs/zh-CN/field-components.md index 78dddda..59a94b1 100644 --- a/website/translated_docs/zh-CN/field-components.md +++ b/website/translated_docs/zh-CN/field-components.md @@ -2,6 +2,7 @@ layout: field-components title: Field 组件 --- + `Field` 组件显示 REST 资源的给定属性。 此类组件在 `List` 视图中使用,但也可以针对只读字段在 `Edit` 和` Create` 视图中使用。 所有 Field 组件中最常见的是 `<TextField>`: ```jsx diff --git a/website/translated_docs/zh-CN/input-components.md b/website/translated_docs/zh-CN/input-components.md index f3fec12..7932898 100644 --- a/website/translated_docs/zh-CN/input-components.md +++ b/website/translated_docs/zh-CN/input-components.md @@ -2,6 +2,7 @@ id: input-components title: Input 组件 --- + `Input` 组件显示一个输入, 或一个下拉列表, 一个单选按钮列表, 等等。 这些组件允许编辑记录属性,并且在 `<Edit>`, `<Create>` 和 `<Filter>` 视图中是常见的。 ```jsx diff --git a/website/translated_docs/zh-CN/intro.md b/website/translated_docs/zh-CN/intro.md index d66938d..05d7fbc 100644 --- a/website/translated_docs/zh-CN/intro.md +++ b/website/translated_docs/zh-CN/intro.md @@ -2,9 +2,10 @@ id: intro title: 简介 --- + 一个运行在浏览器中,在REST/GraphQL API之上,用于构建admin(中后台)应用程序的前端框架。它使用了ES6,[React](https://facebook.github.io/react/) 和 [Material Design](https://material.io/)进行编写。 它以前的名字叫做 [admin-on-rest](https://github.com/marmelab/admin-on-rest)。 由[marmelab](https://marmelab.com/)开源和维护。 -[Demo](https://marmelab.com/react-admin-demo/) - [Source](https://github.com/marmelab/react-admin) - [Releases](https://github.com/marmelab/react-admin/releases) - [Support](http://stackoverflow.com/questions/tagged/react-admin) <iframe src="https://player.vimeo.com/video/268958716?byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style="display:block;margin:0 auto" mark="crwd-mark"></iframe> +[Demo](https://marmelab.com/react-admin-demo/) - [Source](https://github.com/marmelab/react-admin) - [Releases](https://github.com/marmelab/react-admin/releases) - [Support](http://stackoverflow.com/questions/tagged/react-admin) <iframe src="https://player.vimeo.com/video/268958716?byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style="display:block;margin:0 auto"></iframe> ## 功能 diff --git a/website/translated_docs/zh-CN/list-view-component.md b/website/translated_docs/zh-CN/list-view-component.md index 1eb5452..05ae7a9 100644 --- a/website/translated_docs/zh-CN/list-view-component.md +++ b/website/translated_docs/zh-CN/list-view-component.md @@ -2,6 +2,7 @@ layout: list-view-component title: List 视图组件 --- + 列表视图显示从 REST API 获取的记录列表。 此视图的入口点是`<List>`组件,它负责获取数据。 然后,它将数据传递到迭代器视图-通常是 `<Datagrid`,然后将每个记录属性的渲染委托给 [`<Field>`](./Fields.html) 组件。  diff --git a/website/translated_docs/zh-CN/reference.md b/website/translated_docs/zh-CN/reference.md index 2bb092e..b559b3e 100644 --- a/website/translated_docs/zh-CN/reference.md +++ b/website/translated_docs/zh-CN/reference.md @@ -2,6 +2,7 @@ id: reference title: 引用 --- + <div style="column-count:3"> <ul> <li> diff --git a/website/translated_docs/zh-CN/resource-component.md b/website/translated_docs/zh-CN/resource-component.md index 1f8a4be..a7b13a8 100644 --- a/website/translated_docs/zh-CN/resource-component.md +++ b/website/translated_docs/zh-CN/resource-component.md @@ -2,6 +2,7 @@ id: resource-component title: Resource 组件 --- + 一个 `<Resource>` 组件将一个API端点映射到一个CRUD界面。 例如,下面的 admin app 为 JSONPlaceholder API 公开在[`http://jsonplaceholder.ode.com/posts`](http://jsonplaceholder.typicode.com/posts) 和 [`http://jsonplaceholder.ode.com/users`](http://jsonplaceholder.typicode.com/users) 的资源提供了一个只读界面: ```jsx diff --git a/website/translated_docs/zh-CN/show-view-component.md b/website/translated_docs/zh-CN/show-view-component.md index ed99fd1..c97af59 100644 --- a/website/translated_docs/zh-CN/show-view-component.md +++ b/website/translated_docs/zh-CN/show-view-component.md @@ -2,6 +2,7 @@ id: show-view-component title: Show 视图组件 --- + Show 视图以只读方式显示从 API 获取的记录。 它将记录的实际呈现方式委托给一个布局组件——通常是 `<SimpleShowLayout>`。 此布局组件使用其子级 ([`<Fields>`](./Fields.md) 组件) 来呈现每个记录字段。  diff --git a/website/translated_docs/zh-CN/theming.md b/website/translated_docs/zh-CN/theming.md index 26aa501..f9ef45e 100644 --- a/website/translated_docs/zh-CN/theming.md +++ b/website/translated_docs/zh-CN/theming.md @@ -2,6 +2,7 @@ id: theming title: 主题 --- + 无论您需要为单个组件调整CSS规则,还是更改整个应用程序中的标签颜色,都可以! ## 重写组件样式 diff --git a/website/translated_docs/zh-CN/translation.md b/website/translated_docs/zh-CN/translation.md index f8b418c..18bbed7 100644 --- a/website/translated_docs/zh-CN/translation.md +++ b/website/translated_docs/zh-CN/translation.md @@ -2,6 +2,8 @@ id: translation title: 翻译 --- + + React-admin 界面使用英语作为默认语言。但是它也支持任何其他语言,归功于[polyglot.js](http://airbnb.io/polyglot.js/)库。 ## 改变区域设置 diff --git a/website/translated_docs/zh-CN/tutorial.md b/website/translated_docs/zh-CN/tutorial.md index e1c02c1..bcf125a 100644 --- a/website/translated_docs/zh-CN/tutorial.md +++ b/website/translated_docs/zh-CN/tutorial.md @@ -3,11 +3,11 @@ id: tutorial title: Tutorial --- -这 15 分钟的教程将揭示如何创建一个新的管理应用程序基于现有的 REST API。<video width="100%" controls> <source src="http://static.marmelab.com/react-admin/react-admin.mp4" type="video/mp4"> Your browser does not support the video tag. </video> +这15分钟的教程将揭示如何创建一个新的管理应用程序基于现有的 REST API。<video width="800" height="600" controls> <source src="http://static.marmelab.com/react-admin/react-admin.mp4" type="video/mp4"> Your browser does not support the video tag. </video> ## 设置 -React-Admin 使用 React。 我们将使用 Facebook 的 [create-react-app](https://github.com/facebookincubator/create-react-app) 创建一个空的 React app, 并安装 `react-admin` 软件包: +React-Admin使用React。 我们将使用 Facebook 的 [create-react-app](https://github.com/facebookincubator/create-react-app) 创建一个空的React app, 并安装 `react-admin` 软件包: ```sh npm install -g create-react-app @@ -17,115 +17,116 @@ yarn add react-admin yarn start ``` -您应该是在 3000 端口上启动并运行着一个空的 React 应用程序。 +您应该是在3000端口上启动并运行着一个空的React应用程序。 ## 使用 API 作为数据源 -React-admin 运行在浏览器中, 并使用 API 获取和存储数据。 +React-admin运行在浏览器中, 并使用 API 获取和存储数据。 我们将使用 [JSONPlaceholder](http://jsonplaceholder.typicode.com/), 一个用于测试和原型设计的假 REST API, 作为管理员的数据源。下面是它的样子: curl http://jsonplaceholder.typicode.com/posts/12 + ```json { "id": 12, "title": "in quibusdam tempore odit est dolorem", - "body": - "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + "body": "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", "userId": 2 } ``` -JSONPlaceholder 为帖子,评论和用户提供端点(API)。 我们建立的管理员将允许创建,检索,更新和删除(CRUD)这些资源。 +JSONPlaceholder为帖子,评论和用户提供端点(API)。 我们建立的管理员将允许创建,检索,更新和删除(CRUD)这些资源。 -## 使用 Data Provider 与 API 进行联系 +## 使用Data Provider与API进行联系 -通过以下代码替换`src/App.js`来引导 admin app: +通过以下代码替换`src/App.js`来引导admin app: ```jsx // in src/App.js -import React from "react"; -import { Admin, Resource } from "react-admin"; -import jsonServerProvider from "ra-data-json-server"; +import React from 'react'; +import { Admin, Resource } from 'react-admin'; +import jsonServerProvider from 'ra-data-json-server'; -const dataProvider = jsonServerProvider("http://jsonplaceholder.typicode.com"); +const dataProvider = jsonServerProvider('http://jsonplaceholder.typicode.com'); const App = () => <Admin dataProvider={dataProvider} />; export default App; ``` -`App` 组件现在渲染了一个 `<Admin>` 组件, 它是 react-admin 应用程序的根组件。 该组件需要一个`dataProvider` 属性- 一个能够从 API 获取数据的函数。 由于计算机之间并没有数据交换的标准,因此您可能必须编写自定义提供程序(Provider)以便将 react-admin 连接到您自己的 API- 稍后我们会深入了解数据提供程序(Data Providers)。 现在, 让我们利用 `ra-data-json-server` 数据提供程序, 它将适用于 JSONPlaceholder 。 +`App` 组件现在渲染了一个 `<Admin>` 组件, 它是react-admin应用程序的根组件。 该组件需要一个`dataProvider` 属性- 一个能够从API获取数据的函数。 由于计算机之间并没有数据交换的标准,因此您可能必须编写自定义提供程序(Provider)以便将react-admin连接到您自己的API- 稍后我们会深入了解数据提供程序(Data Providers)。 现在, 让我们利用 `ra-data-json-server` 数据提供程序, 它将适用于JSONPlaceholder 。 ```sh yarn add ra-data-json-server ``` -这足以让 react-admin 运行一个空的应用程序。现在是添加功能的时候了! +这足以让react-admin运行一个空的应用程序。现在是添加功能的时候了! -## 使用 Resource 映射 API 端点 +## 使用Resource映射API端点 -`<Admin>` 组件需要一个或多个 `<Resource>` 子级组件。 每个 Resource 映射一个名称到 API 中的端点。 编辑 `App.js` 文件来添加一个`posts`资源: +`<Admin>` 组件需要一个或多个 `<Resource>` 子级组件。 每个Resource映射一个名称到 API 中的端点。 编辑 `App.js` 文件来添加一个`posts`资源: ```jsx // in src/App.js -import { PostList } from "./posts"; +import { PostList } from './posts'; const App = () => ( - <Admin dataProvider={dataProvider}> - <Resource name="posts" list={PostList} /> - </Admin> + <Admin dataProvider={dataProvider}> + <Resource name="posts" list={PostList} /> + </Admin> ); ``` **提示**: 在下一节中, 我们将定义 `<PostList>` 组件。 -`<Resource name="posts" />` 这一行通知 react-admin 从 [http://jsonplaceholder.typicode.com/posts ](http://jsonplaceholder.typicode.com/posts) URL 上获取 "posts" 记录。 +`<Resource name="posts" />` 这一行通知react-admin从 [http://jsonplaceholder.typicode.com/posts ](http://jsonplaceholder.typicode.com/posts) URL上获取 "posts" 记录。 ## 显示记录列表 -`<Resource>` 还定义了每个 CRUD 操作所使用的 React 组件 (`列表(list)`, `创建(create)`, `编辑(edit)`, `显示(show)`)。 `list={PostList}`属性意味着 react-admin 应该使用 `<PostList>` 组件来显示帖子列表。 按如下方式创建该组件: +`<Resource>` 还定义了每个 CRUD 操作所使用的React组件 (`列表(list)`, `创建(create)`, `编辑(edit)`, `显示(show)`)。 `list={PostList}`属性意味着react-admin应该使用 `<PostList>` 组件来显示帖子列表。 按如下方式创建该组件: ```jsx // in src/posts.js -import React from "react"; -import { List, Datagrid, TextField } from "react-admin"; - -export const PostList = props => ( - <List {...props}> - <Datagrid> - <TextField source="id" /> - <TextField source="title" /> - <TextField source="body" /> - </Datagrid> - </List> +import React from 'react'; +import { List, Datagrid, TextField } from 'react-admin'; + +export const PostList = (props) => ( + <List {...props}> + <Datagrid> + <TextField source="id" /> + <TextField source="title" /> + <TextField source="body" /> + </Datagrid> + </List> ); ``` -PostList 的主要组件是 `<List>` 组件,负责从 API 中抓取信息,显示页面标题,以及处理页。 然后, 该 List 将实际 posts 列表的显示委托给其子级。 在这种情况下, 这是一个 `<Datagrid>` 组件, 它渲染为一张表,一行一条记录。 datagrid 使用其子组件 (此处为 `<TextField>`) 来确定要呈现的列。 每个域组件在 API 响应中映射一个不同的域, 由 `source`属性指定。 +PostList的主要组件是 `<List>` 组件,负责从 API 中抓取信息,显示页面标题,以及处理页。 然后, 该List将实际posts列表的显示委托给其子级。 在这种情况下, 这是一个 `<Datagrid>` 组件, 它渲染为一张表,一行一条记录。 datagrid 使用其子组件 (此处为 `<TextField>`) 来确定要呈现的列。 每个域组件在 API 响应中映射一个不同的域, 由 `source`属性指定。 -这足以显示 Post 列表: +这足以显示Post列表:  -如果你在浏览器开发者工具中查看网络选项卡时, 则会注意到应用程序拉取了 `http://jsonplaceholder.typicode.com/posts` URL, 然后使用结果(数据)生成 datagrid。 这些基本上解释了 react-admin 是如何工作的。 +如果你在浏览器开发者工具中查看网络选项卡时, 则会注意到应用程序拉取了 `http://jsonplaceholder.typicode.com/posts` URL, 然后使用结果(数据)生成 datagrid。 这些基本上解释了react-admin是如何工作的。 该列表已经功能化: 您可以通过单击列标题来重新排序, 或者使用底部分页控件更改页面。 `ra-data-json-server` 数据提供程序(Data Provider)将这些操作转换为 JSONPlaceholder 理解的查询字符串。 ## 使用字段类型 -你刚看到`<TextField>`组件,React-admin 提供了许多字段组件去映射各种内容类型: number,date, image,HTML,,array,reference 等。 +你刚看到`<TextField>`组件,React-admin提供了许多字段组件去映射各种内容类型: number,date, image,HTML,,array,reference等。 -例如, [ `/user` API 端点在 JSONPlaceholder](http://jsonplaceholder.typicode.com/users) 中包含电子邮件。 +例如, [ `/user` API端点在 JSONPlaceholder](http://jsonplaceholder.typicode.com/users) 中包含电子邮件。 curl http://jsonplaceholder.typicode.com/users/2 + ```json { "id": 2, "name": "Ervin Howell", "username": "Antonette", - "email": "Shanna@melissa.tv" + "email": "Shanna@melissa.tv", } ``` @@ -133,14 +134,14 @@ PostList 的主要组件是 `<List>` 组件,负责从 API 中抓取信息, ```jsx // in src/App.js -import { PostList } from "./posts"; -import { UserList } from "./users"; +import { PostList } from './posts'; +import { UserList } from './users'; const App = () => ( - <Admin dataProvider={dataProvider}> - <Resource name="posts" list={PostList} /> - <Resource name="users" list={UserList} /> - </Admin> + <Admin dataProvider={dataProvider}> + <Resource name="posts" list={PostList} /> + <Resource name="users" list={UserList} /> + </Admin> ); ``` @@ -148,18 +149,18 @@ const App = () => ( ```jsx // in src/users.js -import React from "react"; -import { List, Datagrid, EmailField, TextField } from "react-admin"; - -export const UserList = props => ( - <List title="All users" {...props}> - <Datagrid> - <TextField source="id" /> - <TextField source="name" /> - <TextField source="username" /> - <EmailField source="email" /> - </Datagrid> - </List> +import React from 'react'; +import { List, Datagrid, EmailField, TextField } from 'react-admin'; + +export const UserList = (props) => ( + <List title="All users" {...props}> + <Datagrid> + <TextField source="id" /> + <TextField source="name" /> + <TextField source="username" /> + <EmailField source="email" /> + </Datagrid> + </List> ); ``` @@ -167,22 +168,23 @@ export const UserList = props => ( 边栏现在提供了对第二个资源“users”的访问。你可以点击它,它工作了!“users” 列表将电子邮件地址显示为 `<a href="mailto:">` 标记。 -在 react-admin 中, Field 是简单的 react 组件。 在运行时, 它们接收从 API 获取的 `记录` (例如, `{ "id": 2, "name": "Ervin Howell", "username": "Antonette", "email": "Shanna@melissa.tv" }`) 并且他们应该显示`source`所指定的字段 (如:`email`)。 +在react-admin中, Field是简单的react 组件。 在运行时, 它们接收从 API 获取的 `记录` (例如, `{ "id": 2, "name": "Ervin Howell", "username": "Antonette", "email": "Shanna@melissa.tv" }`) 并且他们应该显示`source`所指定的字段 (如:`email`)。 这意味着编写自定义字段组件非常简单。例如,去创建一个`UrlField`: ```jsx // in src/MyUrlField.js -import React from "react"; -import PropTypes from "prop-types"; +import React from 'react'; +import PropTypes from 'prop-types'; -const UrlField = ({ record = {}, source }) => ( - <a href={record[source]}>{record[source]}</a> -); +const UrlField = ({ record = {}, source }) => + <a href={record[source]}> + {record[source]} + </a>; UrlField.propTypes = { - record: PropTypes.object, - source: PropTypes.string.isRequired + record: PropTypes.object, + source: PropTypes.string.isRequired, }; export default UrlField; @@ -194,39 +196,31 @@ export default UrlField; ```json { - "id": 1, - "title": - "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", - "body": - "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", - "userId": 1 + "id": 1, + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + "userId": 1 } ``` -React-admin 知道如何利用这些外键来获取引用。例如,在这些 post 列表中包含用户名,使用`<ReferenceField>`: +React-admin知道如何利用这些外键来获取引用。例如,在这些post列表中包含用户名,使用`<ReferenceField>`: ```jsx // in src/posts.js -import React from "react"; -import { - List, - Datagrid, - TextField, - EmailField, - ReferenceField -} from "react-admin"; - -export const PostList = props => ( - <List {...props}> - <Datagrid> - <TextField source="id" /> - <ReferenceField label="User" source="userId" reference="users"> - <TextField source="name" /> - </ReferenceField> - <TextField source="title" /> - <TextField source="body" /> - </Datagrid> - </List> +import React from 'react'; +import { List, Datagrid, TextField, EmailField, ReferenceField } from 'react-admin'; + +export const PostList = (props) => ( + <List {...props}> + <Datagrid> + <TextField source="id" /> + <ReferenceField label="User" source="userId" reference="users"> + <TextField source="name" /> + </ReferenceField> + <TextField source="title" /> + <TextField source="body" /> + </Datagrid> + </List> ); ``` @@ -236,7 +230,7 @@ export const PostList = props => ( **提示**:单独的 `<ReferenceField>` 组件不显示任何内容。 它只提取引用数据, 并将其作为 `record` 传递给其子组件。 就像 `<List>` 组件, 所有 `<Reference>` 组件只负责获取和准备数据, 并将呈现委托给其子级。 -**提示**:再次查看浏览器的网络选项卡:react-admin 对 users 的请求进行重复删除,并聚合它们,以便对于整个 datagrid 向 `/ users` 端点只发出*一个*HTTP 请求。 这是保持 UI 快速响应的众多优化之一。 +**提示**:再次查看浏览器的网络选项卡:react-admin对users的请求进行重复删除,并聚合它们,以便对于整个datagrid向 `/ users` 端点只发出*一个*HTTP请求。 这是保持UI快速响应的众多优化之一。 ## 添加创建和编辑功能 @@ -244,92 +238,73 @@ export const PostList = props => ( ```jsx // in src/posts.js -import React from "react"; -import { - List, - Edit, - Create, - Datagrid, - ReferenceField, - TextField, - EditButton, - DisabledInput, - LongTextInput, - ReferenceInput, - SelectInput, - SimpleForm, - TextInput -} from "react-admin"; - -export const PostList = props => ( - <List {...props}> - <Datagrid> - <TextField source="id" /> - <ReferenceField label="User" source="userId" reference="users"> - <TextField source="name" /> - </ReferenceField> - <TextField source="title" /> - <TextField source="body" /> - <EditButton /> - </Datagrid> - </List> +import React from 'react'; +import { List, Edit, Create, Datagrid, ReferenceField, TextField, EditButton, DisabledInput, LongTextInput, ReferenceInput, SelectInput, SimpleForm, TextInput } from 'react-admin'; + +export const PostList = (props) => ( + <List {...props}> + <Datagrid> + <TextField source="id" /> + <ReferenceField label="User" source="userId" reference="users"> + <TextField source="name" /> + </ReferenceField> + <TextField source="title" /> + <TextField source="body" /> + <EditButton /> + </Datagrid> + </List> ); const PostTitle = ({ record }) => { - return <span>Post {record ? `"${record.title}"` : ""}</span>; + return <span>Post {record ? `"${record.title}"` : ''}</span>; }; -export const PostEdit = props => ( - <Edit title={<PostTitle />} {...props}> - <SimpleForm> - <DisabledInput source="id" /> - <ReferenceInput label="User" source="userId" reference="users"> - <SelectInput optionText="name" /> - </ReferenceInput> - <TextInput source="title" /> - <LongTextInput source="body" /> - </SimpleForm> - </Edit> +export const PostEdit = (props) => ( + <Edit title={<PostTitle />} {...props}> + <SimpleForm> + <DisabledInput source="id" /> + <ReferenceInput label="User" source="userId" reference="users"> + <SelectInput optionText="name" /> + </ReferenceInput> + <TextInput source="title" /> + <LongTextInput source="body" /> + </SimpleForm> + </Edit> ); -export const PostCreate = props => ( - <Create {...props}> - <SimpleForm> - <ReferenceInput label="User" source="userId" reference="users"> - <SelectInput optionText="name" /> - </ReferenceInput> - <TextInput source="title" /> - <LongTextInput source="body" /> - </SimpleForm> - </Create> +export const PostCreate = (props) => ( + <Create {...props}> + <SimpleForm> + <ReferenceInput label="User" source="userId" reference="users"> + <SelectInput optionText="name" /> + </ReferenceInput> + <TextInput source="title" /> + <LongTextInput source="body" /> + </SimpleForm> + </Create> ); ``` 如果你已经理解`<List>`组件,这个`<Edit>`和`<Create>`组件将不足为奇。 他们负责获取记录(或者在`<Create>`的情况下初始化空记录),并显示页面标题。 它们传递纪录到这个`<SimpleForm>`组件,它负责表单布局,默认值,和验证。 就像`<Datagrid>`,`<SimpleForm>`用它的子组件来确定要显示的表单输入项。 它期望*input components*作为字组件。 `<DisabledInput>`,`<TextInput>`,`<LongTextInput>`和`<ReferenceInput>`都是这样的组件。 -至于`<ReferenceInput>`,它采用相同的属性作为`<ReferenceField>`(在早先的列表页用过)。 `<ReferenceInput>`用这些属性去获取可能引用关联到当前纪录的 API(在这个例子中, 可能 `users`关联到了当前的`post`)。 它然后传递这些可能的引用到子组件(`<SelectInput>`),它是负责显示它们(在这种情况通过它们的`name`字段),并且让用户选择一个。 在 HTML 中`<SelectInput>`渲染为一个`<select>`标签。 +至于`<ReferenceInput>`,它采用相同的属性作为`<ReferenceField>`(在早先的列表页用过)。 `<ReferenceInput>`用这些属性去获取可能引用关联到当前纪录的API(在这个例子中, 可能 `users`关联到了当前的`post`)。 它然后传递这些可能的引用到子组件(`<SelectInput>`),它是负责显示它们(在这种情况通过它们的`name`字段),并且让用户选择一个。 在HTML中`<SelectInput>`渲染为一个`<select>`标签。 **提示**:`<Edit>`和`<Create>`组件使用了几乎相同的子表单,但`<Edit>`中的附加`id` input 除外。 在大多数情况下, 创建和编辑记录的表单会有点不同。 但是, 如果它们相同, 则可以在两者之间共享一个常见的表单组件。 注意在`<PostList>`子组件中增加的`<EditButton>`字段:这是给予获得编辑页的访问。 另外, `<Edit>` 组件使用自定义 `<PostTitle>` 组件作为标题, 它显示了自定义给定页的标题的方式。 -要在 posts 资源中使用新的`<PostEdit>`和`<PostCreate>`组件,只需将它们添加成为`<Resource>`组件的 edit 和 create 属性: +要在posts资源中使用新的`<PostEdit>`和`<PostCreate>`组件,只需将它们添加成为`<Resource>`组件的edit和create属性: ```jsx // in src/App.js -import { PostList, PostEdit, PostCreate } from "./posts"; -import { UserList } from "./users"; +import { PostList, PostEdit, PostCreate } from './posts'; +import { UserList } from './users'; const App = () => ( - <Admin dataProvider={dataProvider}> - <Resource - name="posts" - list={PostList} - edit={PostEdit} - create={PostCreate} - /> - // ... - </Admin> + <Admin dataProvider={dataProvider}> + <Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} /> + // ... + </Admin> ); ``` @@ -337,47 +312,47 @@ const App = () => (  -这个表单在创建和编辑页中已经是可使用了的。在提交上它分别发送`POST`和`PUT`请求到 REST API。 +这个表单在创建和编辑页中已经是可使用了的。在提交上它分别发送`POST`和`PUT`请求到REST API。  -**注释**:JSONPlaceholder 是一个只读的 api;虽然它似乎接受`POST`和`PUT`请求,但它并没有考虑账户的新建和编辑 - 这就是为什么,在这种特定情况下,你将在创建后看到错误,并且在保存之后将不会看到你的编辑。 这只是 JSONPlaceholder 的一个假象。 +**注释**:JSONPlaceholder是一个只读的 api;虽然它似乎接受`POST`和`PUT`请求,但它并没有考虑账户的新建和编辑 - 这就是为什么,在这种特定情况下,你将在创建后看到错误,并且在保存之后将不会看到你的编辑。 这只是 JSONPlaceholder 的一个假象。 -React-admin 使用 _积极(optimistic )呈现_。 这意味着, 当您编辑记录并点击 “保存” 按钮时, UI 将显示一个确认, 并在*将更新查询发送到服务器之前*显示更新后的数据 。 这不仅使界面超快,而且还允许“Undo”功能。 它已经在 admin 中作为一个功能点了。 尝试编辑记录,然后在滑出之前点击黑色确认栏中的“Undo”链接。 您将看到应用程序不会将 `UPDATE` 查询发送到 API,并显示未修改的数据。 +React-admin使用 *积极(optimistic )呈现*。 这意味着, 当您编辑记录并点击 “保存” 按钮时, UI 将显示一个确认, 并在*将更新查询发送到服务器之前*显示更新后的数据 。 这不仅使界面超快,而且还允许“Undo”功能。 它已经在admin中作为一个功能点了。 尝试编辑记录,然后在滑出之前点击黑色确认栏中的“Undo”链接。 您将看到应用程序不会将 `UPDATE` 查询发送到 API,并显示未修改的数据。 **注意**:添加编辑项目的功能时,还可以添加删除项目的功能。 编辑视图中的 “Delete” 按钮完全开箱即用。 ## 向列表中添加搜索和筛选器 -让我们回到 Post 列表一分钟。它提供排序和分页, 但缺少一个功能: 搜索内容的能力。 +让我们回到Post列表一分钟。它提供排序和分页, 但缺少一个功能: 搜索内容的能力。 -React-admin 可以使用输入组件在列表视图中创建多标准搜索引擎。 首先, 创建一个 `<Filter>` 组件, 就像您将编写一个 `<SimpleForm>` 组件, 使用输入组件作为子级。 然后, 使用 `filters` 属性将其添加到 List 中: +React-admin可以使用输入组件在列表视图中创建多标准搜索引擎。 首先, 创建一个 `<Filter>` 组件, 就像您将编写一个 `<SimpleForm>` 组件, 使用输入组件作为子级。 然后, 使用 `filters` 属性将其添加到List中: ```jsx // in src/posts.js -import { Filter, ReferenceInput, SelectInput, TextInput } from "react-admin"; - -const PostFilter = props => ( - <Filter {...props}> - <TextInput label="Search" source="q" alwaysOn /> - <ReferenceInput label="User" source="userId" reference="users" allowEmpty> - <SelectInput optionText="name" /> - </ReferenceInput> - </Filter> +import { Filter, ReferenceInput, SelectInput, TextInput } from 'react-admin'; + +const PostFilter = (props) => ( + <Filter {...props}> + <TextInput label="Search" source="q" alwaysOn /> + <ReferenceInput label="User" source="userId" reference="users" allowEmpty> + <SelectInput optionText="name" /> + </ReferenceInput> + </Filter> ); -export const PostList = props => ( - <List {...props} filters={<PostFilter />}> - // ... - </List> +export const PostList = (props) => ( + <List {...props} filters={<PostFilter />}> + // ... + </List> ); ``` -第一个过滤器 ‘q’ 利用了 JSONPlaceholder 提供的全文功能。 它是 `alwaysOn`,所以它总是出现在屏幕上。 第二个筛选器,‘userId’ 可以通过位于列表顶部的 “add filter” 按钮来添加。 因为它是一个`<ReferenceInput>`,所以它已经填充了可能的用户。 它可以由终端用户关闭。 +第一个过滤器 ‘q’ 利用了JSONPlaceholder提供的全文功能。 它是 `alwaysOn`,所以它总是出现在屏幕上。 第二个筛选器,‘userId’ 可以通过位于列表顶部的 “add filter” 按钮来添加。 因为它是一个`<ReferenceInput>`,所以它已经填充了可能的用户。 它可以由终端用户关闭。  -过滤器是 “search-as-you-type”,这意味着当用户在筛选表单中输入新值时,列表会立即刷新 (通过 API 请求)。 +过滤器是 “search-as-you-type”,这意味着当用户在筛选表单中输入新值时,列表会立即刷新 (通过API请求)。 ## 自定义菜单图标 @@ -385,20 +360,14 @@ export const PostList = props => ( ```jsx // in src/App.js -import PostIcon from "@material-ui/icons/Book"; -import UserIcon from "@material-ui/icons/Group"; +import PostIcon from '@material-ui/icons/Book'; +import UserIcon from '@material-ui/icons/Group'; const App = () => ( - <Admin dataProvider={dataProvider}> - <Resource - name="posts" - list={PostList} - edit={PostEdit} - create={PostCreate} - icon={PostIcon} - /> - <Resource name="users" list={UserList} icon={UserIcon} /> - </Admin> + <Admin dataProvider={dataProvider}> + <Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} icon={PostIcon} /> + <Resource name="users" list={UserList} icon={UserIcon} /> + </Admin> ); ``` @@ -406,31 +375,31 @@ const App = () => ( ## 使用自定义主页 -默认情况下,admin-on-rest 显示第一个资源列表页为主页。 如果你想要显示一个自定义组件来代替,在 `<Admin>` 组件中作为 `dashboard` 属性传递它。 +默认情况下,admin-on-rest显示第一个资源列表页为主页。 如果你想要显示一个自定义组件来代替,在 `<Admin>` 组件中作为 `dashboard` 属性传递它。 ```jsx // in src/Dashboard.js -import React from "react"; -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import CardHeader from "@material-ui/core/CardHeader"; +import React from 'react'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader from '@material-ui/core/CardHeader'; export default () => ( - <Card> - <CardHeader title="Welcome to the administration" /> - <CardContent>Lorem ipsum sic dolor amet...</CardContent> - </Card> + <Card> + <CardHeader title="Welcome to the administration" /> + <CardContent>Lorem ipsum sic dolor amet...</CardContent> + </Card> ); ``` ```jsx // in src/App.js -import Dashboard from "./Dashboard"; +import Dashboard from './Dashboard'; const App = () => ( - <Admin dashboard={Dashboard} dataProvider={dataProvider}> - // ... - </Admin> + <Admin dashboard={Dashboard} dataProvider={dataProvider}> + // ... + </Admin> ); ``` @@ -438,67 +407,63 @@ const App = () => ( ## 添加登录页 -大多数 admin app 都需要身份验证。React-admin 可以在显示页面之前检查用户凭据,并在 REST API 返回 403 错误代码时重定向到登录表单。 +大多数admin app都需要身份验证。React-admin 可以在显示页面之前检查用户凭据,并在REST API返回403错误代码时重定向到登录表单。 这些凭据是*什么*以及*如何*获取它们是您作为开发人员必须回答的问题。 React-admin 不会对您的身份验证策略(basic auth, OAuth, custom route, 等)做出任何假设,但会通过调用 authProvider 函数为您提供在正确位置插入逻辑的钩子。 -对于本教程,由于没有我们能使用的公共认证 API,让我们使用一个假身份验证提供程序,接受每一个登录请求,并存储 `username` 在 `localStorage` 中。 每个页面更改都要求 `localStorage` 包含 `username名` 项。 +对于本教程,由于没有我们能使用的公共认证API,让我们使用一个假身份验证提供程序,接受每一个登录请求,并存储 `username` 在 `localStorage` 中。 每个页面更改都要求 `localStorage` 包含 `username名` 项。 `authProvider` 是一个简单的函数, 它必须返回 `Promise`: ```jsx // in src/authProvider.js -import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from "react-admin"; +import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin'; export default (type, params) => { - // called when the user attempts to log in - if (type === AUTH_LOGIN) { - const { username } = params; - localStorage.setItem("username", username); - // accept all username/password combinations - return Promise.resolve(); - } - // called when the user clicks on the logout button - if (type === AUTH_LOGOUT) { - localStorage.removeItem("username"); - return Promise.resolve(); - } - // called when the API returns an error - if (type === AUTH_ERROR) { - const { status } = params; - if (status === 401 || status === 403) { - localStorage.removeItem("username"); - return Promise.reject(); + // called when the user attempts to log in + if (type === AUTH_LOGIN) { + const { username } = params; + localStorage.setItem('username', username); + // accept all username/password combinations + return Promise.resolve(); } - return Promise.resolve(); - } - // called when the user navigates to a new location - if (type === AUTH_CHECK) { - return localStorage.getItem("username") - ? Promise.resolve() - : Promise.reject(); - } - return Promise.reject("Unknown method"); + // called when the user clicks on the logout button + if (type === AUTH_LOGOUT) { + localStorage.removeItem('username'); + return Promise.resolve(); + } + // called when the API returns an error + if (type === AUTH_ERROR) { + const { status } = params; + if (status === 401 || status === 403) { + localStorage.removeItem('username'); + return Promise.reject(); + } + return Promise.resolve(); + } + // called when the user navigates to a new location + if (type === AUTH_CHECK) { + return localStorage.getItem('username') + ? Promise.resolve() + : Promise.reject(); + } + return Promise.reject('Unknown method'); }; ``` **提示**: 由于 `dataProvider` 响应是异步的, 因此您可以非常容易地在这里 fetch 一个身份验证服务。 -要启用此身份验证策略, 请将 client 作为 `authProvider` 属性在 `<Admin>` 组件中传递: +要启用此身份验证策略, 请将 client 作为 `authProvider` 属性在 `<Admin> ` 组件中传递: ```jsx // in src/App.js -import Dashboard from "./Dashboard"; -import authProvider from "./authProvider"; +import Dashboard from './Dashboard'; +import authProvider from './authProvider'; const App = () => ( - <Admin - dashboard={Dashboard} - authProvider={authProvider} - dataProvider={dataProvider} - > - // ... - </Admin> + <Admin dashboard={Dashboard} authProvider={authProvider} dataProvider={dataProvider}> + // ... + </Admin> ); ``` @@ -510,25 +475,23 @@ const App = () => ( react-admin 布局已是响应式。尝试调整浏览器大小来查看侧栏如何切换到较小屏幕上的抽屉样式。 -但是一个响应式布局不足以去做一个响应式的 app。datagrid 组件在桌面上工作良好,但绝对不能适应移动设备。 如果您的 Admin 必须在移动设备上使用, 您必须为小屏幕提供一个替代组件。 +但是一个响应式布局不足以去做一个响应式的app。datagrid组件在桌面上工作良好,但绝对不能适应移动设备。 如果您的Admin必须在移动设备上使用, 您必须为小屏幕提供一个替代组件。 首先, 您应该知道您不必使用 `<Datagrid>` 组件作为 `<List>` 子级。 您可以使用您喜欢的任何其他组件。 例如, `<SimpleList>` 组件: ```jsx // in src/posts.js -import React from "react"; -import { List, SimpleList } from "react-admin"; - -export const PostList = props => ( - <List {...props}> - <SimpleList - primaryText={record => record.title} - secondaryText={record => `${record.views} views`} - tertiaryText={record => - new Date(record.published_at).toLocaleDateString() - } - /> - </List> +import React from 'react'; +import { List, SimpleList } from 'react-admin'; + +export const PostList = (props) => ( + <List {...props}> + <SimpleList + primaryText={record => record.title} + secondaryText={record => `${record.views} views`} + tertiaryText={record => new Date(record.published_at).toLocaleDateString()} + /> + </List> ); ``` @@ -536,91 +499,81 @@ export const PostList = props => ( <img src="https://marmelab.com/react-admin/img/mobile-post-list.png" alt="Mobile post list" style="display:block;margin:2em auto;box-shadow:none;filter:drop-shadow(13px 12px 7px rgba(0,0,0,0.5));" /> -注意:我们切换到这些屏幕截图的自定义 API,以演示如何使用一些 SimpleList 组件属性。 +注意:我们切换到这些屏幕截图的自定义API,以演示如何使用一些SimpleList组件属性。 这在移动设备上运行良好,但现在桌面用户体验更糟。 最好的折衷方案是在小屏幕上使用`<SimpleList>`,在其他屏幕上使用`<Datagrid>`。 这就是 `<Responsive>` 组件的用武之地: ```jsx // in src/posts.js -import React from "react"; -import { - List, - Responsive, - SimpleList, - Datagrid, - TextField, - ReferenceField, - EditButton -} from "react-admin"; - -export const PostList = props => ( - <List {...props}> - <Responsive - small={ - <SimpleList - primaryText={record => record.title} - secondaryText={record => `${record.views} views`} - tertiaryText={record => - new Date(record.published_at).toLocaleDateString() - } +import React from 'react'; +import { List, Responsive, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; + +export const PostList = (props) => ( + <List {...props}> + <Responsive + small={ + <SimpleList + primaryText={record => record.title} + secondaryText={record => `${record.views} views`} + tertiaryText={record => new Date(record.published_at).toLocaleDateString()} + /> + } + medium={ + <Datagrid> + <TextField source="id" /> + <ReferenceField label="User" source="userId" reference="users"> + <TextField source="name" /> + </ReferenceField> + <TextField source="title" /> + <TextField source="body" /> + <EditButton /> + </Datagrid> + } /> - } - medium={ - <Datagrid> - <TextField source="id" /> - <ReferenceField label="User" source="userId" reference="users"> - <TextField source="name" /> - </ReferenceField> - <TextField source="title" /> - <TextField source="body" /> - <EditButton /> - </Datagrid> - } - /> - </List> + </List> ); ``` -这完全按你期望的方式工作。这里的经验是 react-admin 针对布局注意了响应式的 web 设计,但它是你的工作-在页面中用 `<Responsive>` 。 +这完全按你期望的方式工作。这里的经验是react-admin针对布局注意了响应式的web设计,但它是你的工作-在页面中用 `<Responsive>` 。  ## 连接到真正的 API -这是本教程的重点。 在现实世界的项目中,你的 API 的规范(REST? GraphQL? 别的?不符合 JSONPLaceholder 规范。 编写 Data Provider 可能是您必须做的第一件事,以使 react-admin 工作。 根据您的 API, 这可能需要几个小时的额外工作。 +这是本教程的重点。 在现实世界的项目中,你的API的规范(REST? GraphQL? 别的?不符合 JSONPLaceholder 规范。 编写 Data Provider 可能是您必须做的第一件事,以使react-admin工作。 根据您的 API, 这可能需要几个小时的额外工作。 -React-admin 将每个数据查询委托给 Data Provider 函数。 这个函数必须针对结果返回一个 promise。 这为映射任何 API 规范,添加身份验证头,使用来自多个域的 API 端点等提供了极大的自由度。 +React-admin 将每个数据查询委托给 Data Provider 函数。 这个函数必须针对结果返回一个promise。 这为映射任何 API 规范,添加身份验证头,使用来自多个域的 API 端点等提供了极大的自由度。 例如,让我们假设您必须使用 `my.api.url` REST api,它需要以下参数: -| Action | Expected API request | -| ------------------- | ------------------------------------------------------------------------------------- | +| Action | Expected API request | +| ------------------- | --------------------------------------------------------------------------------------------- | | Get list | `GET http://my.api.url/posts?sort=['title','ASC']&range=[0, 24]&filter={title:'bar'}` | -| Get one record | `GET http://my.api.url/posts/123` | -| Get several records | `GET http://my.api.url/posts?filter={ids:[123,456,789]}` | -| Update a record | `PUT http://my.api.url/posts/123` | -| Create a record | `POST http://my.api.url/posts/123` | -| Delete a record | `DELETE http://my.api.url/posts/123` | +| Get one record | `GET http://my.api.url/posts/123` | +| Get several records | `GET http://my.api.url/posts?filter={ids:[123,456,789]}` | +| Update a record | `PUT http://my.api.url/posts/123` | +| Create a record | `POST http://my.api.url/posts/123` | +| Delete a record | `DELETE http://my.api.url/posts/123` | -React-admin 为这个列表的每个动作定义了自定义动词。 就像 HTTP 动词(GET,POST 等)一样,react-admin 动词限定一个 request 到一个 data provider 。 React-admin 动词被叫做 `GET_LIST`, `GET_ONE`,`GET_MANY`,`CREATE`,`UPDATE` 和 `DELETE`。 Data Provider 将得映射这些动词的每一个对应一个(或多个)HTTP 请求。 +React-admin 为这个列表的每个动作定义了自定义动词。 就像 HTTP 动词(GET,POST 等)一样,react-admin 动词限定一个 request 到一个 data provider 。 React-admin 动词被叫做 `GET_LIST`, `GET_ONE`,`GET_MANY`,`CREATE`,`UPDATE` 和 `DELETE`。 Data Provider 将得映射这些动词的每一个对应一个(或多个)HTTP 请求。 `my.api.url` API 的 Data Provider 代码如下: ```jsx // in src/dataProvider import { - GET_LIST, - GET_ONE, - GET_MANY, - GET_MANY_REFERENCE, - CREATE, - UPDATE, - DELETE, - fetchUtils -} from "react-admin"; -import { stringify } from "query-string"; - -const API_URL = "my.api.url"; + GET_LIST, + GET_ONE, + GET_MANY, + GET_MANY_REFERENCE, + CREATE, + UPDATE, + DELETE, + fetchUtils, +} from 'react-admin'; +import { stringify } from 'query-string'; + +const API_URL = 'my.api.url'; /** * @param {String} type One of the constants appearing at the top if this file, e.g. 'UPDATE' @@ -629,53 +582,53 @@ const API_URL = "my.api.url"; * @returns {Object} { url, options } The HTTP request parameters */ const convertDataProviderRequestToHTTP = (type, resource, params) => { - switch (type) { + switch (type) { case GET_LIST: { - const { page, perPage } = params.pagination; - const { field, order } = params.sort; - const query = { - sort: JSON.stringify([field, order]), - range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]), - filter: JSON.stringify(params.filter) - }; - return { url: `${API_URL}/${resource}?${stringify(query)}` }; + const { page, perPage } = params.pagination; + const { field, order } = params.sort; + const query = { + sort: JSON.stringify([field, order]), + range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]), + filter: JSON.stringify(params.filter), + }; + return { url: `${API_URL}/${resource}?${stringify(query)}` }; } case GET_ONE: - return { url: `${API_URL}/${resource}/${params.id}` }; + return { url: `${API_URL}/${resource}/${params.id}` }; case GET_MANY: { - const query = { - filter: JSON.stringify({ id: params.ids }) - }; - return { url: `${API_URL}/${resource}?${stringify(query)}` }; + const query = { + filter: JSON.stringify({ id: params.ids }), + }; + return { url: `${API_URL}/${resource}?${stringify(query)}` }; } case GET_MANY_REFERENCE: { - const { page, perPage } = params.pagination; - const { field, order } = params.sort; - const query = { - sort: JSON.stringify([field, order]), - range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]), - filter: JSON.stringify({ ...params.filter, [params.target]: params.id }) - }; - return { url: `${API_URL}/${resource}?${stringify(query)}` }; + const { page, perPage } = params.pagination; + const { field, order } = params.sort; + const query = { + sort: JSON.stringify([field, order]), + range: JSON.stringify([(page - 1) * perPage, (page * perPage) - 1]), + filter: JSON.stringify({ ...params.filter, [params.target]: params.id }), + }; + return { url: `${API_URL}/${resource}?${stringify(query)}` }; } case UPDATE: - return { - url: `${API_URL}/${resource}/${params.id}`, - options: { method: "PUT", body: JSON.stringify(params.data) } - }; + return { + url: `${API_URL}/${resource}/${params.id}`, + options: { method: 'PUT', body: JSON.stringify(params.data) }, + }; case CREATE: - return { - url: `${API_URL}/${resource}`, - options: { method: "POST", body: JSON.stringify(params.data) } - }; + return { + url: `${API_URL}/${resource}`, + options: { method: 'POST', body: JSON.stringify(params.data) }, + }; case DELETE: - return { - url: `${API_URL}/${resource}/${params.id}`, - options: { method: "DELETE" } - }; + return { + url: `${API_URL}/${resource}/${params.id}`, + options: { method: 'DELETE' }, + }; default: - throw new Error(`Unsupported fetch action type ${type}`); - } + throw new Error(`Unsupported fetch action type ${type}`); + } }; /** @@ -685,30 +638,19 @@ const convertDataProviderRequestToHTTP = (type, resource, params) => { * @param {Object} params The Data Provider request params, depending on the type * @returns {Object} Data Provider response */ -const convertHTTPResponseToDataProvider = ( - response, - type, - resource, - params -) => { - const { headers, json } = response; - switch (type) { +const convertHTTPResponseToDataProvider = (response, type, resource, params) => { + const { headers, json } = response; + switch (type) { case GET_LIST: - return { - data: json.map(x => x), - total: parseInt( - headers - .get("content-range") - .split("/") - .pop(), - 10 - ) - }; + return { + data: json.map(x => x), + total: parseInt(headers.get('content-range').split('/').pop(), 10), + }; case CREATE: - return { data: { ...params.data, id: json.id } }; + return { data: { ...params.data, id: json.id } }; default: - return { data: json }; - } + return { data: json }; + } }; /** @@ -718,31 +660,30 @@ const convertHTTPResponseToDataProvider = ( * @returns {Promise} the Promise for response */ export default (type, resource, params) => { - const { fetchJson } = fetchUtils; - const { url, options } = convertDataProviderRequestToHTTP( - type, - resource, - params - ); - return fetchJson(url, options).then(response => - convertHTTPResponseToDataProvider(response, type, resource, params) - ); + const { fetchJson } = fetchUtils; + const { url, options } = convertDataProviderRequestToHTTP(type, resource, params); + return fetchJson(url, options) + .then(response => convertHTTPResponseToDataProvider(response, type, resource, params)); }; ``` -**提示**:`fetchJson()`只是 `fetch().then(r => r.json())` 的快捷方式。加上对 HTTP 响应代码的控制,以便在 4xx 或 5xx 响应的情况下抛出 `HTTPError`。 如果不符合您的需要, 请随意使用 `fetch()`。 +**提示**:` fetchJson() `只是 `fetch().then(r => r.json())` 的快捷方式。加上对 HTTP 响应代码的控制,以便在 4xx 或 5xx 响应的情况下抛出 `HTTPError`。 如果不符合您的需要, 请随意使用 `fetch()`。 使用此提供程序代替以前的 `jsonServerProvider` 只是切换函数的问题: ```jsx // in src/app.js -import dataProvider from "./dataProvider"; +import dataProvider from './dataProvider'; -const App = () => <Admin dataProvider={dataProvider}>// ...</Admin>; +const App = () => ( + <Admin dataProvider={dataProvider}> + // ... + </Admin> +); ``` ## 结论 -React-admin 是以考虑定制为基础构建的。 您可以将任何 react-admin 组件替换为你自己的组件, 例如, 显示自定义列表布局或为一个给定的 resource 不同的编辑表单。 +React-admin 是以考虑定制为基础构建的。 您可以将任何 react-admin 组件替换为你自己的组件, 例如, 显示自定义列表布局或为一个给定的resource不同的编辑表单。 -现在, 您已经完成了教程, 请继续阅读 [react-admin 文档](http://marmelab.com/react-admin/), 并阅读 [Material UI 组件文档](http://www.material-ui.com/#/)。 +现在, 您已经完成了教程, 请继续阅读 [react-admin文档](http://marmelab.com/react-admin/), 并阅读 [Material UI 组件文档](http://www.material-ui.com/#/)。 \ No newline at end of file