feat: new version of the documentation (#95)

* feat: new version of the documentation

* feat: add English catalog translation

* Update quickstart.md

* Update quickstart.zh-CN.md

* Update quickstart.zh-CN.md

* Update quickstart.zh-CN.md

* Update quickstart.zh-CN.md

* feat: update quickstart

* update doc

* update pepository api doc

Co-authored-by: ChengLei Shao <chareice@live.com>
This commit is contained in:
chenos 2021-10-28 22:55:51 +08:00 committed by GitHub
parent 1408de55d2
commit ca7af9c8cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 7521 additions and 911 deletions

View File

@ -0,0 +1,5 @@
import React, { Redirect } from 'umi';
export default (props: any) => {
return <Redirect {...props}/>
};

View File

@ -79,6 +79,9 @@ const Layout: React.FC<IRouteComponentProps> = ({ children, location }) => {
setMenuCollapsed(true);
}}
>
<div className={'tip'} style={{ position: 'fixed', top: 64, left: 0, right: 0, textAlign: 'center', lineHeight: '36px', background: '#fffbe6', borderBottom: '1px solid rgba(60, 72, 88, 0.05)', zIndex: 100 }}>
{isCN ? '文档还在建设中,与实际代码存在差异' : 'The documentation is still under construction and differs somewhat from the actual code.'}
</div>
<Navbar
location={location}
navPrefix={<SearchBar />}

View File

@ -19,9 +19,12 @@ body {
padding: 16px (@s-content-margin + @s-toc-width) 50px @s-menu-width + @s-content-margin;
@media @mobile {
padding-top: 66px !important;
padding-top: 98px !important;
padding-left: 16px !important;
padding-right: 16px !important;
.tip {
top: 50px !important;
}
}
&[data-gapless='true'] {

View File

@ -3,7 +3,8 @@
.markdown {
color: @c-text;
font-size: 15px;
line-height: 1.60625;
// line-height: 1.60625;
line-height: 2;
&:not(:first-child):empty {
min-height: 32px;

View File

@ -4,6 +4,7 @@ on:
push:
branches:
- develop
- feature/docs
jobs:
deploy:

View File

@ -16,21 +16,51 @@ export default defineConfig({
'pathRewrite': { '^/api/': '/api/' },
},
},
resolve: {
includes: ['docs'],
},
styles: [
`
.__dumi-default-navbar-logo {
height: 100%;
width: 100px;
}
.__dumi-default-layout .__dumi-default-navbar {
box-shadow: 0 0 3px rgb(60 72 88 / 15%);
}
.__dumi-default-layout[data-site-mode='true'] {
padding-top: 150px !important;
}
@media only screen and (max-width: 767px) {
.__dumi-default-layout[data-site-mode="true"] {
padding-top: 128px !important;
}
}
@media only screen and (min-width: 768px) {
.__dumi-default-menu[data-mode='site'] {
top: 100px !important;
}
.__dumi-default-layout[data-site-mode='true'] .__dumi-default-layout-toc {
top: 150px !important;
}
}
`
],
// mfsu: {},
// ssr: {},
// exportStatic: {},
mode: 'doc',
logo: 'https://www.nocobase.com/dist/images/logo.png',
mode: 'site',
logo: 'https://www.nocobase.com/images/logo.png',
navs: {
'en-US': [
null,
{ title: 'GitHub', path: 'https://github.com/nocobase/nocobase' },
{ title: 'Changelog', path: 'https://github.com/nocobase/nocobase/releases' },
// { title: 'Changelog', path: 'https://github.com/nocobase/nocobase/releases' },
],
'zh-CN': [
null,
{ title: 'GitHub', path: 'https://github.com/nocobase/nocobase' },
{ title: '更新日志', path: 'https://github.com/nocobase/nocobase/releases' },
// { title: '更新日志', path: 'https://github.com/nocobase/nocobase/releases' },
],
},
// more config: https://d.umijs.org/config

10
docs/api/action.md Normal file
View File

@ -0,0 +1,10 @@
---
toc: menu
---
# Action
## action.params
## action.mergeParams()

80
docs/api/application.md Normal file
View File

@ -0,0 +1,80 @@
---
nav:
title: API
order: 3
toc: menu
---
# Application
## app.db
数据库实例
## app.resourcer
资源实例
## app.pm
插件管理器
## app.i18n
国际化
## app.constructor()
构造器
## app.use()
中间件
## app.on()
事件
## app.emit()
## app.emitAsync()
## app.collection()
等同于 app.db.collection()
## app.actions()
等同于 app.resourcer.registerActions()
## app.resource()
等同于 app.resourcer.define()
## app.parse()
等同于 app.cli.parse()
## app.load()
加载配置
## app.init()
初始化
## app.start()
启动应用
## app.stop()
停止应用
## app.command()
等同于 app.cli.command()
## app.plugin()
等同于 app.pm.add()

13
docs/api/cli.md Normal file
View File

@ -0,0 +1,13 @@
---
toc: menu
---
# CLI
## init
## start
## db:sync
## pm:download
## pm:enable
## pm:disable
## pm:remove

16
docs/api/client.md Normal file
View File

@ -0,0 +1,16 @@
---
toc: menu
---
# Client
## APIClient
## createRouteSwitch
## createCollectionField
## createSchemaComponent
## i18n

17
docs/api/collection.md Normal file
View File

@ -0,0 +1,17 @@
---
toc: menu
---
# Collection
## collection.constructor()
## collection.mergeOptions()
## collection.sync()
## collection.hasField()
## collection.getField()
## collection.addField()
## collection.setFields()
## collection.mergeField()
## collection.removeField()
## collection.forEachField()
## collection.findField()

16
docs/api/context.md Normal file
View File

@ -0,0 +1,16 @@
---
toc: menu
---
# Context
## ctx.db
## ctx.resourcer
## ctx.action
## ctx.i18n
## ctx.t()

18
docs/api/database.md Normal file
View File

@ -0,0 +1,18 @@
---
toc: menu
---
# Database
## db.constructor()
## db.collection()
## db.sync()
## db.close()
## db.registerFieldTypes()
## db.import()
## db.on()
## db.emitAsync()
## db.emit()
## db.registerModels()
## db.registerRepositories()
## db.registerOperators()

30
docs/api/field-types.md Normal file
View File

@ -0,0 +1,30 @@
---
toc: menu
---
# Field Types
## Field - abstract
## RelationField - abstract
## HasOneField
## HasManyField
## BelongsToField
## BelongsToManyField
## StringField
## TextField
## IntegerField
## FloatField
## DoubleField
## RealField
## DecimalField
## DateField
## TimeField
## JsonField
## JsonbField
## VirtualField
## SortField
## PasswordField
## RadioField
## UidField
## CreatedByField
## UpdatedByField

50
docs/api/operators.md Normal file
View File

@ -0,0 +1,50 @@
---
toc: menu
---
# Operators
## string
- includes
- notIncludes
- eq
- ne
- null
- notNull
## number
- eq
- ne
- gt
- gte
- lt
- lte
- between
- null
- notNull
## select
- eq
- ne
- in
- notIn
- null
- notNull
## multipleSelect
- match
- notMatch
- anyOf
- noneOf
- null
- notNull
## date
- dateOn
- dateNotOn
- dateBefore
- dateAfter
- dateNotBefore
- dateNotAfter
- null
- notNull
## association
- fieldName.$name
- exists
- notExists

View File

@ -0,0 +1,19 @@
---
toc: menu
---
# PluginManager
## pm.constructor()
## pm.forEach()
## pm.load()
## pm.download()
## pm.enable()
## pm.disable()
## pm.remove()

11
docs/api/plugin.md Normal file
View File

@ -0,0 +1,11 @@
---
toc: menu
---
# Plugin
## plugin.constructor()
## plugin.enable()
## plugin.disable()
## plugin.upgrade()
## plugin.remove()

370
docs/api/repository.md Normal file
View File

@ -0,0 +1,370 @@
---
toc: menu
---
# Repository
## `repository.findMany()`
查询数据,返回数组。无数据时为空数组,不返回 count。如果需要请使用 [repository.paginate()](#repositorypaginate) 方法。
##### Definition
```ts
interface findMany<M extends Sequelize.Model> {
(options: FindManyOptions): Promise<M[]>
}
interface FindManyOptions extends Sequelize.FindOptions {
// 数据过滤
filter?: FilterOptions;
// 输出结果显示哪些字段
fields?: string[];
// 输出结果不显示哪些字段
expect?: string[];
// 附加字段,用于控制关系字段的输出
appends?: string[];
// 排序,字段前面加上 “-” 表示降序
sort?: string[];
}
// 待补充
type FilterOptions = any;
```
##### Examples
###### 全览
```ts
repository.findMany({
// 过滤
filter: {
$and: [{ a: 5 }, { b: 6 }], // (a = 5) AND (b = 6)
$or: [{ a: 5 }, { b: 6 }], // (a = 5) OR (b = 6)
someAttribute: {
// Basics
$eq: 3, // = 3
$ne: 20, // != 20
$is: null, // IS NULL
$not: true, // IS NOT TRUE
$gt: 6, // > 6
$gte: 6,
},
// 支持使用逗号间隔
'someAttribute.$eq': 3,
// 内嵌的,一般是关系数据
nested: {
someAttribute: {},
},
// 同上,也支持使用逗号间隔
'nested.someAttribute': {
// 同上
},
},
// 字段白名单
fields: [],
// 附加字段,主要用于附加关系字段
appends: [],
// 字段黑名单
expect: [],
// 排序
sort: ['-createdAt', 'updatedAt'],
});
```
###### filter 参数示例说明
以文章和标签为例,文章和标签的 collection 如下:
```ts
const Tag = db.collection({
name: 'tags',
fields: [
{ type: 'string', name: 'name' },
],
});
const Post = db.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'name' },
{ type: 'text', name: 'content' },
{ type: 'belongsToMany', name: 'tags' },
],
});
```
最简单的筛选过滤
```ts
await Post.repository.findMany({
filter: {
'name': 'post1',
},
});
```
支持多种 Operators`$` 开头。[更多内容,查阅 Operators 章节](operators.md)
```ts
await Post.repository.findMany({
filter: {
'name.$includes': 'post1',
// 等同于
name: {
$includes: 'post1',
},
},
});
```
支持关系字段过滤,可以使用 dot 来表示层级结构
```ts
await Post.repository.findMany({
filter: {
'tags.name': 'tag1',
// 等同于
tags: {
name: 'tag1',
},
},
});
```
多个同一关系字段的过滤可以写在一起
```ts
await Post.repository.findMany({
filter: {
'tags': {
'name.$includes': 'tag1',
'createdAt.$dateOn': '2020-10-28',
},
},
});
```
同时也支持 and、or 逻辑运算符
```ts
await Post.repository.findMany({
filter: {
$and: [
// 一个 Object 只写一个条件
{ name: 'post1' },
// 支持关系字段(非常重要)
{ 'tags.name.$includes': 'tag1' },
{ 'tags.name.$includes': 'tag2' },
],
},
});
```
###### sort 参数示例说明
指定一组数据的排序,倒序时在字段前加上减号 `-`
```ts
await Post.repository.findMany({
// 创建日期倒序
sort: ['-createdAt'],
});
```
可以设置多个排序规则
```ts
await Post.repository.findMany({
// 创建日期倒序ID 正序
sort: ['-createdAt', 'id'],
});
```
也可以是关系表的字段
```ts
await Post.repository.findMany({
// 标签名正序,文章创建日期倒序
sort: ['tags.name', '-createdAt'],
});
```
###### fields 参数示例说明
- `fields` 显示哪些字段
- `expect` 不显示哪些字段
- `appends` 附加哪些字段
如果并未指定 fields输出所有 AttributesAssociations 字段并不输出
```ts
await Post.repository.findMany();
// [{ id, name, content, createdAt, updatedAt }]
```
只输出指定字段时,可以用 fields
```ts
await Post.repository.findMany({
fields: ['name'],
});
// [{ name }]
```
当 fields 里有关系字段时,按默认情况输出
```ts
await Post.repository.findMany({
fields: ['id', 'name', 'tags'],
});
//
// [{ id, name, createdAt, updatedAt, tags: [{ id, name, createdAt, updatedAt }] }]
```
可以只输出关系数据的某个字段
```ts
await Post.repository.findMany({
fields: ['id', 'name', 'tags.name'],
});
// [{ id, name, tags: [{ name }] }]
```
排除某些字段时,可以使用 expect
```ts
await Post.repository.findMany({
expect: ['content'],
});
// [{ id, name, createdAt, updatedAt }]
```
Attributes 不变,只附加 Associations 进来时,可以使用 appends
```ts
await Post.repository.findMany({
appends: ['tags'],
});
// [{ id, name, content, createdAt, updatedAt, tags: [{ id, name, createdAt, updatedAt }] }]
```
如果某个字段只用在 filter 里,但并没有出现在 fields 里,不应该被输出
```ts
await Post.repository.findMany({
filter: {
'tags.name': 'tag1',
},
});
// 输出所有的 Attributes但不输出 tags
// [{ id, name, content, createdAt, updatedAt }]
```
如果某个字段只用在 sort 里,但并没有出现在 fields 里,也不应该被输出
```ts
await Post.repository.findMany({
sort: ['-tags.createdAt']
});
// 输出所有的 Attributes但不输出 tags
// [{ id, name, content, createdAt, updatedAt }]
```
## `repository.paginate()`
按分页查询数据,并返回所有符合的数据总数。
##### Definition
```ts
interface paginate<M extends Sequelize.Model> {
(options: PaginateOptions): Promise<[ M[], number ]>
}
interface PaginateOptions extends Sequelize.FindAndCountOptions {
// 数据过滤
filter?: FilterOptions;
// 输出结果显示哪些字段
fields?: string[];
// 输出结果不显示哪些字段
expect?: string[];
// 附加字段,用于控制关系字段的输出
appends?: string[];
// 排序,字段前面加上 “-” 表示降序
sort?: string[];
// 当前页,默认为 1
page?: number;
// 当前页最大数量,默认为 20
pageSize?: number;
}
```
##### Examples
大部分参数与 [repository.findMany()](#repositoryfindmany) 一致,所以这里只列举 page 和 pageSize 的例子。
不填写参数时,默认 page=1pageSize=20。
```ts
repository.paginate();
// [[{ id, name, content, createdAt, updatedAt }], 50]
const [models, count] = await repository.paginate();
```
## `repository.findOne()`
##### Definition
```ts
interface findOne<M extends Sequelize.Model> {
(options: FindOneOptions): Promise<[ M[], number ]>
}
interface FindOneOptions extends FindManyOptions {
// 数据过滤
filter?: FilterOptions;
// 输出结果显示哪些字段
fields?: string[];
// 输出结果不显示哪些字段
expect?: string[];
// 附加字段,用于控制关系字段的输出
appends?: string[];
// 排序,字段前面加上 “-” 表示降序
sort?: string[];
// 通过 pk 过滤
filterByPk?: number | string;
}
```
##### Examples
大部分参数与 [repository.findMany()](#repositoryfindmany) 一致。这里只列举 filterByPk 的例子。
```ts
await repository.findOne({
filterByPk: 1,
// 等同于
filter: {
[Model.primaryKeyAttribute]: 1,
}
});
```
## repository.create()
## repository.update()
## repository.destroy()
## repository.relation().of()
### findMany()
### findOne()
### create()
### update()
### destroy()
### set()
### add()
### remove()
### toggle()

0
docs/api/resourcer.md Normal file
View File

1
docs/api/rest-api.md Normal file
View File

@ -0,0 +1 @@
# REST API

View File

@ -0,0 +1,6 @@
---
group:
title: Collection Fields
path: /components/collection-fields
order: 3
---

View File

@ -0,0 +1,6 @@
---
group:
title: 字段组件
path: /zh-CN/components/collection-fields
order: 3
---

View File

@ -0,0 +1,4 @@
---
group:
path: /components/collection-fields
---

15
docs/components/index.md Normal file
View File

@ -0,0 +1,15 @@
---
title: Overview
order: 0
nav:
title: Components
order: 4
---
# Components
NocoBase 的客户端组件总共有三类:
- 通过 createRouteSwitch 创建的路由组件,如 Layou、Page
- 通过 createCollectionField 创建的字段组件,用于扩展字段
- 通过 createSchemaComponent 创建的 JSON Schema 组件可以是任意东西比如表格、表单、日历、看板等。Schema Component 可用于 Route Component 或 Collection Field 中。

View File

@ -0,0 +1,15 @@
---
title: 概述
order: 0
nav:
title: 组件
order: 4
---
NocoBase 的客户端组件总共有三类:
- 通过 createRouteSwitch 创建的路由组件,如 Layou、Page
- 通过 createCollectionField 创建的字段组件,用于扩展字段
- 通过 createSchemaComponent 创建的 JSON Schema 组件,可以是任意东西,比如表格、表单、日历、看板等。
Schema Component 可用于 Route Component 或 Collection Field 中。

View File

@ -0,0 +1,10 @@
---
order: 3
title: AdminLayout
group:
title: Route Components
path: /components/route-components
order: 1
---
# AdminLayout

View File

@ -0,0 +1,10 @@
---
order: 3
title: AdminLayout
group:
title: 路由组件
path: /zh-CN/components/route-components
order: 1
---
# AdminLayout

View File

@ -0,0 +1,9 @@
---
order: 2
title: AuthLayout
group:
path: /components/route-components
order: 1
---
# AuthLayout

View File

@ -0,0 +1,8 @@
---
group:
title: Schema Components
path: /components/schema-components
order: 2
---
# Action

View File

@ -0,0 +1,8 @@
---
group:
title: Schema 组件
path: /zh-CN/components/schema-components
order: 2
---
# Action

View File

@ -0,0 +1 @@
# AddNew

View File

@ -0,0 +1,5 @@
---
title: Input
group:
path: /components/schema-components
---

8
docs/examples/index.md Normal file
View File

@ -0,0 +1,8 @@
---
sidemenu: false
nav:
title: Examples
order: 99
---
Coming soon...

View File

@ -0,0 +1,8 @@
---
sidemenu: false
nav:
title: 示例
order: 99
---
待补充...

View File

@ -0,0 +1,324 @@
---
order: 2
---
# Client Components
为了让更多非开发人员也能参与进来NocoBase 提供了配套的客户端 —— 无代码的可视化界面。客户端界面非常灵活,由不同组件构成,分为了三类:
- 通过 createRouteSwitch 创建的路由组件,如 Layout、Page
- 通过 createCollectionField 创建的字段组件,用于扩展字段
- 通过 createSchemaComponent 创建的 JSON Schema 组件,可以是任意东西,比如表格、表单、日历、看板等
[更多组件内容,查看组件章节](#)
## 组件树结构
界面是由组件构成的组件树,结构如下:
<pre lang="tsx">
// 布局
<Layout>
// 页面
<Page>
// 栅格
<Grid>
// 区块,以表格为例
<Table>
// 配置工具栏
<Table.DesignableBar/>
// 操作栏
<Table.ActionBar>
// 操作
<Action/>
<Action/>
</Table.ActionBar>
// 内容区
<Table.Content>
<Table.Column>
// 表格列的字段
<CollectionField />
</Table.Column>
<Table.Column>
<CollectionField />
</Table.Column>
</Table.Content>
</Table>
</Grid>
<AddNew />
</Page>
</Layout>
</pre>
注:以上例子只为表达组件树的结构和组件之间的关系,实际代码并不如此。
接下来,我们来详细的介绍各部分的概念。
## 布局和页面
页面是可以通过地址访问的网页,不同页面之间可能具有相同的页眉、页脚和导航,通常我们会把这些公共的内容放在布局组件里。例如,初始化的 NocoBase 提供了两个布局组件,如图所示:
- AuthLayout无需登录就能访问一般用于嵌入登录、注册、忘记密码等页面。
- AdminLayout需要登录管理后台的所有页面。
布局和页面组件通过 createRouteSwitch 注册,更多扩展内容点此查看。
## 页面内容排版
对开发者来说,页面内容的编写是自由的,不过为了方便对页面内容进行排版,提供了两种排版方式:
### 简易的上下结构
<pre lang="tsx">
<Page>
<BlockItem />
<BlockItem />
<BlockItem />
</Page>
</pre>
例子如下:
```js
// 示例
```
### 可拖拽的栅格
<pre lang="tsx">
<Page>
<Grid>
<Grid.Row>
<Grid.Col>
<BlockItem />
</Grid.Col>
<Grid.Col>
<BlockItem />
</Grid.Col>
</Grid.Row>
<Grid.Row>
<Grid.Col>
<BlockItem />
</Grid.Col>
<Grid.Col>
<BlockItem />
</Grid.Col>
</Grid.Row>
</Grid>
</Page>
</pre>
栅格组件 Grid 基于行Grid.Row和列Grid.Col来定义区块的外部框架。例子如下
```js
// 示例
```
## AddNew
AddNew 是页面可视化配置最重要的操作按钮,更多关于 AddNew 的内容点此查看
## 区块 - Block
区块一般放在页面里,可以是任意东西,包括文字、附件、表格、表单、日历、看板等等。一个完整的区块由三部分组成:
- 内容区 Content区块的主体
- 操作栏 ActionBar可以放置各种操作按钮用于操作区块数据可选
- 配置工具栏 DesignableBar操作区块配置的按钮可选
以表格区块为例,组件结构如下:
<pre lang="tsx">
<Table>
<Table.DesignableBar />
<Table.ActionBar />
<Table.Content />
</Table>
</pre>
具体形态:
```js
//示例(这里放上一个表示区块结构的示例)
```
区块有几种类型:
- 数据类型,用于展示数据表的数据,如表格、日历、看板、表单、详情等。
- 多媒体,用于丰富页面内容,如文本段、附件等。暂时只有一个简易的 Markdown。
- 图表,用于展示数据统计。
- 模板,可直接将某些成品模板化,直接应用到页面上。
区块可以任意扩展,如何扩展查看 createSchemaComponent 章节。
## 操作栏 - ActionBar
操作栏是一系列操作的集合,一般用于区块内部。用户发出操作指令,程序做出改变,并将结果响应在区块内容区。
例如:
表格,内容区是一个表格,操作区会放置一些操作按钮,如筛选、新增、删除、导出等
```js
// 示例(放一个简易的表格,把操作栏重点突出一下)
```
详情,内容区是详情数据,操作区会放置编辑、导出等按钮
```js
// 示例(放一个简易的详情,把操作栏重点突出一下)
```
不同的区块,操作栏的按钮可能不同。操作栏的按钮也是可以自定义的,具体内容查看操作章节。
## 操作 - Action
操作是封装的一段指令,一般需要用户参与。
例如:
- 删除数据,需要用户选中待删除数据,再触发删除指令
- 筛选数据,需要用户填写筛选项,再触发筛选指令
- 新增数据,需要用户填写数据之后提交,触发新增操作指令
- 查看详情,用户点击操作按钮,弹窗查看详情或当前窗口打开详情页查看
最简单的操作,只需要绑定一段指令即可,简单来说就是指定一段函数,无需传参。组件结构如下:
<pre lang="tsx">
<Action useAction={useAction} />
</pre>
大部分的操作指令需要用户提供参数,如新增数据操作,需要用户填写数据,填写数据一般需要弹出表单,用户填写完数据,点击提交,才触发操作指令。组件结构如下:
<pre lang="tsx">
<Action useAction={useAction}>
{/* 这是个弹窗表单,内置提交按钮,点击提交触发操作指令,具体代码省略 */}
<Action.Modal x-decorator={'Form'}></Action.Modal>
</Action>
</pre>
一个完整的操作大概分为两步:
- 为 Action 绑定一段指令
- 如果指令需要用户提供参数,需要提供交互界面,目前内置的有:
- Action.Drawer抽屉
- Action.Modal对话框
- Action.Popover气泡
操作是 NocoBase 里非常重要的一个概念,更多详情点此查看
## 配置工具栏 - DesignableBar
所有的 Schema Component 都可以绑定自己的配置工具栏DesignableBar用于修改当前组件的 Schema。
**什么是 Schema Component**
通过 Schema 协议编写的类 JSON Schema 格式的组件,如:
```js
{
type: 'void',
'x-Component': 'Hello',
'x-designable-bar': 'Hello.DesignableBar',
'x-dect': 'CardItem',
}
```
举几个例子,如:
表单字段的 JSON Schema
```js
const schema = {
type: 'string',
'x-component': 'Input',
'x-decorator': 'FormItem',
'x-designable-bar': 'Form.Field.DesignableBar',
};
```
表单项的配置工具栏 `Form.Field.DesignableBar` 的效果
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1304394/1634135895582-57b6ce7d-af08-4c11-ad14-19c75acf8f8a.png#clientId=u67b0cc68-db77-4&from=paste&height=183&id=m0SAT&margin=%5Bobject%20Object%5D&name=image.png&originHeight=366&originWidth=636&originalType=binary&ratio=1&size=49669&status=done&style=none&taskId=udd86527b-c0f4-46b9-9970-d7c3b23e624&width=318)
表格的 JSON Schema
```js
const schema = {
type: 'array',
'x-component': 'Table',
'x-decorator': 'CardItem',
'x-designable-bar': 'Table.DesignableBar',
};
```
表格配置工具栏 `Table.DesignableBar` 的效果
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1304394/1634134283034-a3660288-f903-4f19-8334-fda34f0bbe61.png#clientId=u67b0cc68-db77-4&from=paste&height=249&id=ud9d383da&margin=%5Bobject%20Object%5D&name=image.png&originHeight=498&originWidth=440&originalType=binary&ratio=1&size=51402&status=done&style=none&taskId=u94bfca27-5467-42f9-b7a3-ec00a9c688f&width=220)
菜单项的 JSON Schema
```js
const schema = {
type: 'array',
'x-component': 'Menu.Item',
'x-designable-bar': 'Menu.Item.DesignableBar',
};
```
菜单项配置工具栏 `Menu.Item.DesignableBar` 的效果
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1304394/1634134190121-f4b028ec-93bf-4a65-8ba8-dcc78886deae.png)
更多配置工具栏详情点此查看
## 字段组件 - CollectionField
字段组件的配置参数可能非常多在不同数据区块里也可能用到同一个字段组件为了减少代码重复NocoBase 里,将字段组件的配置交由数据表统一管理。一处配置,多处使用。数据区块里直接引用字段组件,如果有其他不同参数再另行扩展。
<pre lang="tsx">
<Table>
// 原生态的写法
<Table.Column title={'姓名'}>
<Input {...others} name="name" readPretty={true}/>
</Table.Column>
// 简化之后的字段引用
<Table.Column>
<CollectionField name="name"/>
</Table.Column>
</Table>
<Form>
// 如果在表格里也用到,再写一遍
<FormItem title={'姓名'}>
<Input {...others} name="name"/>
</FormItem>
// 字段引用,只需要提供 name 即可
<FormItem>
<CollectionField name="name"/>
</FormItem>
</Form>
</pre>
字段组件有三种显示状态:
- 可填写 - editable
- 不可填写 - disabled
- 阅读模式 - read-pretty
以单行文本Input为例
```js
// 示例Input 的三种显示状态)
// 示例待补充
```
**为什么字段有多种显示状态?**
- 在表单中一般情况字段为可填写状态editable但如果只供查看这时候就会把字段设置为 disabled 或 read-pretty。
- 在表格中一般情况字段为阅读模式read-pretty但如果需要在表格内快捷编辑又可以动态的将某个字段激活为 editable。
字段组件可以任意扩展,如何扩展查看 createCollectionField 章节。

View File

@ -0,0 +1,324 @@
---
order: 2
---
# 客户端组件
为了让更多非开发人员也能参与进来NocoBase 提供了配套的客户端 —— 无代码的可视化界面。客户端界面非常灵活,由不同组件构成,分为了三类:
- 通过 createRouteSwitch 创建的路由组件,如 Layout、Page
- 通过 createCollectionField 创建的字段组件,用于扩展字段
- 通过 createSchemaComponent 创建的 JSON Schema 组件,可以是任意东西,比如表格、表单、日历、看板等
[更多组件内容,查看组件章节](#)
## 组件树结构
界面是由组件构成的组件树,结构如下:
<pre lang="tsx">
// 布局
<Layout>
// 页面
<Page>
// 栅格
<Grid>
// 区块,以表格为例
<Table>
// 配置工具栏
<Table.DesignableBar/>
// 操作栏
<Table.ActionBar>
// 操作
<Action/>
<Action/>
</Table.ActionBar>
// 内容区
<Table.Content>
<Table.Column>
// 表格列的字段
<CollectionField />
</Table.Column>
<Table.Column>
<CollectionField />
</Table.Column>
</Table.Content>
</Table>
</Grid>
<AddNew />
</Page>
</Layout>
</pre>
注:以上例子只为表达组件树的结构和组件之间的关系,实际代码并不如此。
接下来,我们来详细的介绍各部分的概念。
## 布局和页面
页面是可以通过地址访问的网页,不同页面之间可能具有相同的页眉、页脚和导航,通常我们会把这些公共的内容放在布局组件里。例如,初始化的 NocoBase 提供了两个布局组件,如图所示:
- AuthLayout无需登录就能访问一般用于嵌入登录、注册、忘记密码等页面。
- AdminLayout需要登录管理后台的所有页面。
布局和页面组件通过 createRouteSwitch 注册,更多扩展内容点此查看。
## 页面内容排版
对开发者来说,页面内容的编写是自由的,不过为了方便对页面内容进行排版,提供了两种排版方式:
### 简易的上下结构
<pre lang="tsx">
<Page>
<BlockItem />
<BlockItem />
<BlockItem />
</Page>
</pre>
例子如下:
```js
// 示例
```
### 可拖拽的栅格
<pre lang="tsx">
<Page>
<Grid>
<Grid.Row>
<Grid.Col>
<BlockItem />
</Grid.Col>
<Grid.Col>
<BlockItem />
</Grid.Col>
</Grid.Row>
<Grid.Row>
<Grid.Col>
<BlockItem />
</Grid.Col>
<Grid.Col>
<BlockItem />
</Grid.Col>
</Grid.Row>
</Grid>
</Page>
</pre>
栅格组件 Grid 基于行Grid.Row和列Grid.Col来定义区块的外部框架。例子如下
```js
// 示例
```
## AddNew
AddNew 是页面可视化配置最重要的操作按钮,更多关于 AddNew 的内容点此查看
## 区块 - Block
区块一般放在页面里,可以是任意东西,包括文字、附件、表格、表单、日历、看板等等。一个完整的区块由三部分组成:
- 内容区 Content区块的主体
- 操作栏 ActionBar可以放置各种操作按钮用于操作区块数据可选
- 配置工具栏 DesignableBar操作区块配置的按钮可选
以表格区块为例,组件结构如下:
<pre lang="tsx">
<Table>
<Table.DesignableBar />
<Table.ActionBar />
<Table.Content />
</Table>
</pre>
具体形态:
```js
//示例(这里放上一个表示区块结构的示例)
```
区块有几种类型:
- 数据类型,用于展示数据表的数据,如表格、日历、看板、表单、详情等。
- 多媒体,用于丰富页面内容,如文本段、附件等。暂时只有一个简易的 Markdown。
- 图表,用于展示数据统计。
- 模板,可直接将某些成品模板化,直接应用到页面上。
区块可以任意扩展,如何扩展查看 createSchemaComponent 章节。
## 操作栏 - ActionBar
操作栏是一系列操作的集合,一般用于区块内部。用户发出操作指令,程序做出改变,并将结果响应在区块内容区。
例如:
表格,内容区是一个表格,操作区会放置一些操作按钮,如筛选、新增、删除、导出等
```js
// 示例(放一个简易的表格,把操作栏重点突出一下)
```
详情,内容区是详情数据,操作区会放置编辑、导出等按钮
```js
// 示例(放一个简易的详情,把操作栏重点突出一下)
```
不同的区块,操作栏的按钮可能不同。操作栏的按钮也是可以自定义的,具体内容查看操作章节。
## 操作 - Action
操作是封装的一段指令,一般需要用户参与。
例如:
- 删除数据,需要用户选中待删除数据,再触发删除指令
- 筛选数据,需要用户填写筛选项,再触发筛选指令
- 新增数据,需要用户填写数据之后提交,触发新增操作指令
- 查看详情,用户点击操作按钮,弹窗查看详情或当前窗口打开详情页查看
最简单的操作,只需要绑定一段指令即可,简单来说就是指定一段函数,无需传参。组件结构如下:
<pre lang="tsx">
<Action useAction={useAction} />
</pre>
大部分的操作指令需要用户提供参数,如新增数据操作,需要用户填写数据,填写数据一般需要弹出表单,用户填写完数据,点击提交,才触发操作指令。组件结构如下:
<pre lang="tsx">
<Action useAction={useAction}>
{/* 这是个弹窗表单,内置提交按钮,点击提交触发操作指令,具体代码省略 */}
<Action.Modal x-decorator={'Form'}></Action.Modal>
</Action>
</pre>
一个完整的操作大概分为两步:
- 为 Action 绑定一段指令
- 如果指令需要用户提供参数,需要提供交互界面,目前内置的有:
- Action.Drawer抽屉
- Action.Modal对话框
- Action.Popover气泡
操作是 NocoBase 里非常重要的一个概念,更多详情点此查看
## 配置工具栏 - DesignableBar
所有的 Schema Component 都可以绑定自己的配置工具栏DesignableBar用于修改当前组件的 Schema。
**什么是 Schema Component**
通过 Schema 协议编写的类 JSON Schema 格式的组件,如:
```js
{
type: 'void',
'x-Component': 'Hello',
'x-designable-bar': 'Hello.DesignableBar',
'x-dect': 'CardItem',
}
```
举几个例子,如:
表单字段的 JSON Schema
```js
const schema = {
type: 'string',
'x-component': 'Input',
'x-decorator': 'FormItem',
'x-designable-bar': 'Form.Field.DesignableBar',
};
```
表单项的配置工具栏 `Form.Field.DesignableBar` 的效果
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1304394/1634135895582-57b6ce7d-af08-4c11-ad14-19c75acf8f8a.png#clientId=u67b0cc68-db77-4&from=paste&height=183&id=m0SAT&margin=%5Bobject%20Object%5D&name=image.png&originHeight=366&originWidth=636&originalType=binary&ratio=1&size=49669&status=done&style=none&taskId=udd86527b-c0f4-46b9-9970-d7c3b23e624&width=318)
表格的 JSON Schema
```js
const schema = {
type: 'array',
'x-component': 'Table',
'x-decorator': 'CardItem',
'x-designable-bar': 'Table.DesignableBar',
};
```
表格配置工具栏 `Table.DesignableBar` 的效果
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1304394/1634134283034-a3660288-f903-4f19-8334-fda34f0bbe61.png#clientId=u67b0cc68-db77-4&from=paste&height=249&id=ud9d383da&margin=%5Bobject%20Object%5D&name=image.png&originHeight=498&originWidth=440&originalType=binary&ratio=1&size=51402&status=done&style=none&taskId=u94bfca27-5467-42f9-b7a3-ec00a9c688f&width=220)
菜单项的 JSON Schema
```js
const schema = {
type: 'array',
'x-component': 'Menu.Item',
'x-designable-bar': 'Menu.Item.DesignableBar',
};
```
菜单项配置工具栏 `Menu.Item.DesignableBar` 的效果
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1304394/1634134190121-f4b028ec-93bf-4a65-8ba8-dcc78886deae.png)
更多配置工具栏详情点此查看
## 字段组件 - CollectionField
字段组件的配置参数可能非常多在不同数据区块里也可能用到同一个字段组件为了减少代码重复NocoBase 里,将字段组件的配置交由数据表统一管理。一处配置,多处使用。数据区块里直接引用字段组件,如果有其他不同参数再另行扩展。
<pre lang="tsx">
<Table>
// 原生态的写法
<Table.Column title={'姓名'}>
<Input {...others} name="name" readPretty={true}/>
</Table.Column>
// 简化之后的字段引用
<Table.Column>
<CollectionField name="name"/>
</Table.Column>
</Table>
<Form>
// 如果在表格里也用到,再写一遍
<FormItem title={'姓名'}>
<Input {...others} name="name"/>
</FormItem>
// 字段引用,只需要提供 name 即可
<FormItem>
<CollectionField name="name"/>
</FormItem>
</Form>
</pre>
字段组件有三种显示状态:
- 可填写 - editable
- 不可填写 - disabled
- 阅读模式 - read-pretty
以单行文本Input为例
```js
// 示例Input 的三种显示状态)
// 示例待补充
```
**为什么字段有多种显示状态?**
- 在表单中一般情况字段为可填写状态editable但如果只供查看这时候就会把字段设置为 disabled 或 read-pretty。
- 在表格中一般情况字段为阅读模式read-pretty但如果需要在表格内快捷编辑又可以动态的将某个字段激活为 editable。
字段组件可以任意扩展,如何扩展查看 createCollectionField 章节。

View File

@ -0,0 +1,82 @@
---
order: 1
group:
title: Basic Concepts
path: /guide/basic
order: 4
---
# Collections & Fields
NocoBase 的数据表由字段(列)和记录(行)组成。数据表的概念与关系型数据库的数据表概念相近,但是字段的概念并不相同。
## 字段
NocoBase 里最常见的字段具有组件形态单行文本、多行文本、单选框。这些组件都有数值value可交由用户填写称为有值组件。结构如下
```ts
{
interface: 'textarea',
type: 'text',
name: 'description',
uiSchema: {
type: 'string',
title: '描述',
'x-component': 'Input.TextArea',
'x-decorator': 'FormItem',
},
}
```
上述是一个描述字段的配置:
- type 表示字段的存储类型,为 text 长文本类型
- uiSchema 为字段的组件参数
- uiSchema.type 为字段组件的数值类型
- uiSchema.x-component 表示组件类型,为多行输入框
- 绑定了组件的字段,都要设置一个 interface表示当前字段的类型例子描述字段为多行文本类型
除了常见的绑定了组件的字段以外,还有一些无需绑定组件的字段,如 token 字段,这类组件并不会显示在界面上。无组件字段的结构如下:
```ts
{
type: 'string',
name: 'token',
}
```
**为什么字段要区分存储类型和组件类型?**
其一:存储类型和组件类型是多对多关系,并不适合合并处理。
同一组件的 value 的类型(存储类型)可能并不相同,比如 select 的 value 可能是 string 或者 integer。同一存储类型也可能以不同的组件呈现如 string 绑定的组件可能是 Input也可能是 Select。
其二:有限的存储类型和组件类型可以组合出无数种字段类型。
单行文本、电子邮件、网址、手机号这些字段的存储类型和组件类型虽然都相同,但是校验参数并不相同,只需要调整 validate 参数即可创建出无数种字段。
## 字段的类型
| 名称 | Interface | Type | Component | 备注 |
| :------- | :-------- | :----- | :------------- | :---------------- |
| 单行文本 | string | string | Input | |
| 多行文本 | textarea | text | Input.TextArea | |
| 邮箱 | email | string | Input | validate: 'email' |
| 手机号 | phone | string | Input | validate: 'phone' |
## 可以做什么?
### 快速建模
与专业的建模工具不同NocoBase 提供了一种更利于普通用户理解的数据表配置方法。
- 可以直接通过 app.collection() 直接写代码里,多用于配置底层系统表。
- 也可以通过无代码平台的数据表配置入口配置数据表,多用于配置业务表。
### 创建数据区块
配置好的数据表可用于创建对应的数据区块,如以表格的形式展示某个数据表的内容。表格里可以选择哪些字段作为表格列显示出来。
更多关于区块的内容可以查看客户端组件章节。
### HTTP API
跨平台也可以通过 HTTP API 的方式操作数据表(增删改查配置等),更多内容查看 SDK 章节。

View File

@ -0,0 +1,82 @@
---
order: 1
group:
title: 基础概念
path: /zh-CN/guide/basic
order: 4
---
# 数据表和字段
NocoBase 的数据表由字段(列)和记录(行)组成。数据表的概念与关系型数据库的数据表概念相近,但是字段的概念并不相同。
## 字段
NocoBase 里最常见的字段具有组件形态单行文本、多行文本、单选框。这些组件都有数值value可交由用户填写称为有值组件。结构如下
```ts
{
interface: 'textarea',
type: 'text',
name: 'description',
uiSchema: {
type: 'string',
title: '描述',
'x-component': 'Input.TextArea',
'x-decorator': 'FormItem',
},
}
```
上述是一个描述字段的配置:
- type 表示字段的存储类型,为 text 长文本类型
- uiSchema 为字段的组件参数
- uiSchema.type 为字段组件的数值类型
- uiSchema.x-component 表示组件类型,为多行输入框
- 绑定了组件的字段,都要设置一个 interface表示当前字段的类型例子描述字段为多行文本类型
除了常见的绑定了组件的字段以外,还有一些无需绑定组件的字段,如 token 字段,这类组件并不会显示在界面上。无组件字段的结构如下:
```ts
{
type: 'string',
name: 'token',
}
```
**为什么字段要区分存储类型和组件类型?**
其一:存储类型和组件类型是多对多关系,并不适合合并处理。
同一组件的 value 的类型(存储类型)可能并不相同,比如 select 的 value 可能是 string 或者 integer。同一存储类型也可能以不同的组件呈现如 string 绑定的组件可能是 Input也可能是 Select。
其二:有限的存储类型和组件类型可以组合出无数种字段类型。
单行文本、电子邮件、网址、手机号这些字段的存储类型和组件类型虽然都相同,但是校验参数并不相同,只需要调整 validate 参数即可创建出无数种字段。
## 字段的类型
| 名称 | Interface | Type | Component | 备注 |
| :------- | :-------- | :----- | :------------- | :---------------- |
| 单行文本 | string | string | Input | |
| 多行文本 | textarea | text | Input.TextArea | |
| 邮箱 | email | string | Input | validate: 'email' |
| 手机号 | phone | string | Input | validate: 'phone' |
## 可以做什么?
### 快速建模
与专业的建模工具不同NocoBase 提供了一种更利于普通用户理解的数据表配置方法。
- 可以直接通过 app.collection() 直接写代码里,多用于配置底层系统表。
- 也可以通过无代码平台的数据表配置入口配置数据表,多用于配置业务表。
### 创建数据区块
配置好的数据表可用于创建对应的数据区块,如以表格的形式展示某个数据表的内容。表格里可以选择哪些字段作为表格列显示出来。
更多关于区块的内容可以查看客户端组件章节。
### HTTP API
跨平台也可以通过 HTTP API 的方式操作数据表(增删改查配置等),更多内容查看 SDK 章节。

6
docs/guide/deployment.md Normal file
View File

@ -0,0 +1,6 @@
---
order: 3
---
# Deployment

View File

@ -0,0 +1,5 @@
---
order: 3
---
# 部署

42
docs/guide/index.md Normal file
View File

@ -0,0 +1,42 @@
---
nav:
title: Guide
order: 1
order: 1
title: Introducation
---
## What is NocoBase
NocoBase is a scalability-first, open-source no-code development platform. No programming required, build your own collaboration platform, management system with NocoBase in minutes.
## When to use NocoBase
- **SMEs and organizations build business platforms and management systems for themselves or for their industry**
- Want the price to be low enough or even free
- Can be flexibly customized without programming knowledge
- Need full control of source code and data
- Can freely distribute and sell as their own products
- **Service providers and outsourcing teams develop collaboration platforms and management systems for their clients**
- Want to keep development costs as low as possible
- Need the most user-friendly secondary development experience
- Must be deployed privately as a standalone product for the client
- Can be freely distributed and sold by the client
## Why choose NocoBase
- **Open source and free**
- Unrestricted commercial use under the MIT license
- Full code ownership, private deployment, private and secure data
- Free to expand and develop for actual needs
- Good ecological support
- **Strong no-code capability**
- WYSIWYG visual configuration
- Separation of data structure configuration from interface configuration
- Rich combination of blocks and operations
- Role-based access control
- **Developer-friendly**
- Microkernel architecture, flexible and easy to extend, with a robust plug-in system
- Node.js-based, with popular frameworks and technologies, including Koa, Sequelize, React, Formily, Ant Design, etc.
- Progressive development, easy for getting-started, friendly to newcomers
- No binding, no strong dependencies, can be used in any combination or extensions, can be used in existing projects

42
docs/guide/index.zh-CN.md Normal file
View File

@ -0,0 +1,42 @@
---
nav:
title: 指南
order: 1
order: 1
title: 介绍
---
## NocoBase 是什么
NocoBase 是一个极易扩展的开源无代码开发平台。无需编程,使用 NocoBase 搭建自己的协作平台、管理系统,只需要几分钟时间。
## 哪些场景适合使用 NocoBase
- 中小企业和组织为自己或者为所在行业搭建业务平台和管理系统
- 希望价格足够低,甚至免费
- 不懂编程也可以灵活定制
- 需要完全掌控源代码和数据
- 可以以自有产品的形态自由分发和销售
- 服务商和外包团队为客户开发协作平台和管理系统
- 希望尽可能降低开发成本
- 需要极致友好的二次开发体验
- 必须以独立产品的形态为客户私有部署
- 客户可以自由分发和销售
## 为什么选择 NocoBase
- 开源免费
- 采用 MIT 许可协议,不限制商业使用
- 拥有全部代码,私有化部署,保障数据私有和安全
- 针对实际需求自由扩展开发
- 具备良好的生态支持
- 无代码能力强
- 所见即所得的可视化配置
- 数据结构配置与界面配置分离
- 丰富的区块和操作任意组合
- 基于角色的访问权限
- 对开发者友好
- 微内核,灵活易扩展,具备健全的插件体系
- 基于 Node.js使用主流框架和技术包括 Koa、Sequelize、React、Formily、Ant Design 等
- 渐进式开发,上手难度低,对新人友好
- 不绑架、不强依赖,可任意组合使用或扩展,可用于现有项目中

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
---
order: 2
---
# Client-side Kernel
为了让更多非开发人员也能参与进来NocoBase 提供了配套的客户端插件 —— 无代码的可视化配置界面。这部分的核心就是 @nocobase/client,理想状态可以用在任意前端构建工具或框架内,如:
- umijs
- create-react-app
- icejs
- vite
- snowpack
- nextjs
- 其他
暂时只支持 umijs打包编译还有些问题未来会逐步支持以上罗列的各个框架。
客户端主要的组成部分包括:
## 请求
- API Client
- Request Hook
```ts
const api = new APIClient({
request,
});
api.auth();
api.get();
api.post();
api.resource('collections').create();
api.resource('collections').findOne({});
api.resource('collections').findMany({});
api.resource('collections').relation('fields').of(1).create();
```
以下细节待定,特殊的资源
```js
api.collections.create();
api.uiSchemas.create();
```
Request Hook
[https://www.npmjs.com/package/@ahooksjs/use-request](https://www.npmjs.com/package/@ahooksjs/use-request)
```js
const { data } = useRequest(() => api.resource('users').findMany());
```
## 路由
- createRouteSwitch
```js
const RouteSwitch = createRouteSwitch({
components: {},
});
<RouteSwitch routes={[]} />
```
## Schema 组件
- createSchemaComponent
```js
function Hello() {
return <div>Hello Word</div>
}
const SchemaComponent = createSchemaComponent({
scope,
components: {
Hello
},
});
const schema = {
type: 'void',
'x-component': 'Hello',
};
<SchemaComponent schema={schema} />
```
## 怎么组装起来?
<pre lang="tsx">
import { I18nextProvider } from 'react-i18next';
import { createRouteSwitch, APIClient } from '@nocobase/client';
const apiClient = new APIClient();
const i18n = i18next.createInstance();
const Hello = () => {
return <div>Hello</div>;
}
const SchemaComponent = createSchemaComponent({
components: {
Hello,
},
});
const PageTemplate = () => {
const schema = {
type: 'void',
'x-component': 'Hello',
};
return (
<SchemaComponent schema={schema}/>
);
}
const RouteSwitch = createRouteSwitch({
components: {
PageTemplate,
},
});
const routes = [
{ path: '/hello', component: 'Hello' },
];
function AntdProvider(props) {
// 可以根据 i18next 的情况动态处理这里的 locale
return (
<ConfigProvider locale={locale}>{props.children}</ConfigProvider>
);
}
const App = () => {
return (
<APIClientProvider client={apiClient}>
<I18nextProvider i18n={i18n}>
<AntdProvider>
<Router>
<RouteSwitch routes=[routes]/>
</Router>
</AntdProvider>
</I18nextProvider>
</APIClientProvider>
);
}
</pre>
- APIClientProvider提供 APIClient
- I18nextProvider国际化
- AntdProvider处理 antd 组件的国际化,需要放在 I18nextProvider 里
- Router路由驱动
- RouteSwitch路由分发
上面代码看似有些啰嗦,实际各部分的功能和作用并不一样,不适合过度封装。如果需要可以根据实际情况,再进一步封装。

View File

@ -0,0 +1,158 @@
---
order: 2
---
# 客户端内核
为了让更多非开发人员也能参与进来NocoBase 提供了配套的客户端插件 —— 无代码的可视化配置界面。这部分的核心就是 @nocobase/client,理想状态可以用在任意前端构建工具或框架内,如:
- umijs
- create-react-app
- icejs
- vite
- snowpack
- nextjs
- 其他
暂时只支持 umijs打包编译还有些问题未来会逐步支持以上罗列的各个框架。
客户端主要的组成部分包括:
## 请求
- API Client
- Request Hook
```ts
const api = new APIClient({
request,
});
api.auth();
api.get();
api.post();
api.resource('collections').create();
api.resource('collections').findOne({});
api.resource('collections').findMany({});
api.resource('collections').relation('fields').of(1).create();
```
以下细节待定,特殊的资源
```js
api.collections.create();
api.uiSchemas.create();
```
Request Hook
[https://www.npmjs.com/package/@ahooksjs/use-request](https://www.npmjs.com/package/@ahooksjs/use-request)
```js
const { data } = useRequest(() => api.resource('users').findMany());
```
## 路由
- createRouteSwitch
```js
const RouteSwitch = createRouteSwitch({
components: {},
});
<RouteSwitch routes={[]} />
```
## Schema 组件
- createSchemaComponent
```js
function Hello() {
return <div>Hello Word</div>
}
const SchemaComponent = createSchemaComponent({
scope,
components: {
Hello
},
});
const schema = {
type: 'void',
'x-component': 'Hello',
};
<SchemaComponent schema={schema} />
```
## 怎么组装起来?
<pre lang="tsx">
import { I18nextProvider } from 'react-i18next';
import { createRouteSwitch, APIClient } from '@nocobase/client';
const apiClient = new APIClient();
const i18n = i18next.createInstance();
const Hello = () => {
return <div>Hello</div>;
}
const SchemaComponent = createSchemaComponent({
components: {
Hello,
},
});
const PageTemplate = () => {
const schema = {
type: 'void',
'x-component': 'Hello',
};
return (
<SchemaComponent schema={schema}/>
);
}
const RouteSwitch = createRouteSwitch({
components: {
PageTemplate,
},
});
const routes = [
{ path: '/hello', component: 'Hello' },
];
function AntdProvider(props) {
// 可以根据 i18next 的情况动态处理这里的 locale
return (
<ConfigProvider locale={locale}>{props.children}</ConfigProvider>
);
}
const App = () => {
return (
<APIClientProvider client={apiClient}>
<I18nextProvider i18n={i18n}>
<AntdProvider>
<Router>
<RouteSwitch routes=[routes]/>
</Router>
</AntdProvider>
</I18nextProvider>
</APIClientProvider>
);
}
</pre>
- APIClientProvider提供 APIClient
- I18nextProvider国际化
- AntdProvider处理 antd 组件的国际化,需要放在 I18nextProvider 里
- Router路由驱动
- RouteSwitch路由分发
上面代码看似有些啰嗦,实际各部分的功能和作用并不一样,不适合过度封装。如果需要可以根据实际情况,再进一步封装。

View File

@ -0,0 +1,73 @@
---
order: 3
---
# Installation and Startup Process
## 项目安装
```bash
yarn nocobase init
```
- app.constructor()
- app.parse()
- yarn nocobase init
初始化安装
- app.load()
加载配置
- app.emitAsync('beforeLoad')
所有配置加载之前的钩子
- app.pluginManager.load()
按顺序载入所有激活的插件的配置
- 加载 plugin-collections 的配置
- 添加 app.on('init') 监听
- db.getModel('collections').load()
把 collections 表的配置都导入 db.table()
- app.db.sync({force: false})
再执行 sync创建 collections 表里配置的数据表
- app.emitAsync('afterLoad')
所有配置加载之后的钩子
- app.db.sync({force: true})
根据配置生成数据表、字段、索引等
- app.emitAsync('init')
执行所有 init listeners一般是初始化的数据操作
- 触发 plugin-collections 的 init 事件,数据表就创建好了
- app.stop()
结束
## 项目启动
```bash
yarn nocobase start --init --sync
# --init 用于启动时快捷安装
# --sync 开发环境时,当 app.collection() 有更新时快速建表或更新表
```
- app.constructor()
- app.parse()
- yarn nocobase start
初始化安装
- app.load()
加载配置
- app.emitAsync('beforeLoad')
所有配置加载之前的钩子
- app.pluginManager.load()
按顺序载入所有激活的插件的配置
- 加载 plugin-collections 的配置
- 添加 app.on('start') 监听
- db.getModel('collections').load()
把 collections 表的配置都导入 db.table(),在 start 流程里不需要再 db.sync
- app.emitAsync('afterLoad')
所有配置加载之后的钩子
- app.db.sync({force: false})
yarn nocobase start --sync 有更新时快速建表或更新表
yarn nocobase start --init 快捷 init
- app.emitAsync('init')
yarn nocobase start --init 快捷 init
- app.emitAsync('start')
执行所有 start listeners一般是从数据表里读取一些必要的数据
- app.listen()
启动 http server

View File

@ -0,0 +1,73 @@
---
order: 3
---
# 项目安装和启动流程
## 项目安装
```bash
yarn nocobase init
```
- app.constructor()
- app.parse()
- yarn nocobase init
初始化安装
- app.load()
加载配置
- app.emitAsync('beforeLoad')
所有配置加载之前的钩子
- app.pluginManager.load()
按顺序载入所有激活的插件的配置
- 加载 plugin-collections 的配置
- 添加 app.on('init') 监听
- db.getModel('collections').load()
把 collections 表的配置都导入 db.table()
- app.db.sync({force: false})
再执行 sync创建 collections 表里配置的数据表
- app.emitAsync('afterLoad')
所有配置加载之后的钩子
- app.db.sync({force: true})
根据配置生成数据表、字段、索引等
- app.emitAsync('init')
执行所有 init listeners一般是初始化的数据操作
- 触发 plugin-collections 的 init 事件,数据表就创建好了
- app.stop()
结束
## 项目启动
```bash
yarn nocobase start --init --sync
# --init 用于启动时快捷安装
# --sync 开发环境时,当 app.collection() 有更新时快速建表或更新表
```
- app.constructor()
- app.parse()
- yarn nocobase start
初始化安装
- app.load()
加载配置
- app.emitAsync('beforeLoad')
所有配置加载之前的钩子
- app.pluginManager.load()
按顺序载入所有激活的插件的配置
- 加载 plugin-collections 的配置
- 添加 app.on('start') 监听
- db.getModel('collections').load()
把 collections 表的配置都导入 db.table(),在 start 流程里不需要再 db.sync
- app.emitAsync('afterLoad')
所有配置加载之后的钩子
- app.db.sync({force: false})
yarn nocobase start --sync 有更新时快速建表或更新表
yarn nocobase start --init 快捷 init
- app.emitAsync('init')
yarn nocobase start --init 快捷 init
- app.emitAsync('start')
执行所有 start listeners一般是从数据表里读取一些必要的数据
- app.listen()
启动 http server

View File

@ -0,0 +1,13 @@
---
order: 0
group:
title: Kernel Principle
path: /guide/kernel-principle
order: 6
---
# Microkernel Architecture
<img src="../../images/NocoBase.png" style="max-width: 800px; width: 100%;">
NocoBase 采用微内核架构,各类功能以插件形式扩展,所以微内核架构也叫插件化架构,由内核和插件两部分组成。内核提供了最小功能的 WEB 服务器,还提供了各种插件化接口;插件是按功能划分的各种独立模块,通过接口适配,具有可插拔的特点。插件化的设计降低了模块之间的耦合度,提高了复用率。随着插件库的不断扩充,常见的场景只需要组合插件即可完成基础搭建,这种设计理念非常适合无代码平台。

View File

@ -0,0 +1,13 @@
---
order: 0
group:
title: 内核原理
path: /zh-CN/guide/kernel-principle
order: 6
---
# 微内核架构
<img src="../../images/NocoBase.png" style="max-width: 800px; width: 100%;">
NocoBase 采用微内核架构,各类功能以插件形式扩展,所以微内核架构也叫插件化架构,由内核和插件两部分组成。内核提供了最小功能的 WEB 服务器,还提供了各种插件化接口;插件是按功能划分的各种独立模块,通过接口适配,具有可插拔的特点。插件化的设计降低了模块之间的耦合度,提高了复用率。随着插件库的不断扩充,常见的场景只需要组合插件即可完成基础搭建,这种设计理念非常适合无代码平台。

View File

@ -0,0 +1,903 @@
---
order: 1
---
# Server-side Kernel
## 微服务 - Microservices
为了更快的理解 NocoBase我们先创建一个应用新建一个 app.js 文件,代码如下:
```ts
const { Application } = require('@nocobase/server');
const app = new Application({
// 省略配置信息
});
// 配置一张 users 表
app.collection({
name: 'users',
fields: [
{ type: 'string', name: 'username' },
{ type: 'password', name: 'password' }
],
});
// 解析 argv 参数,终端通过命令行进行不同操作
app.parse(process.argv);
```
终端运行
```bash
# 根据配置生成数据库表结构
node app.js db:sync
# 启动应用
node app.js start --port=3000
```
相关 users 表的 REST API 就生成了
```bash
GET http://localhost:3000/api/users
POST http://localhost:3000/api/users
GET http://localhost:3000/api/users/1
PUT http://localhost:3000/api/users/1
DELETE http://localhost:3000/api/users/1
```
以上示例,只用了 10 行左右的代码就创建了真实可用的 REST API 服务。除了内置的 REST API 以外,还可以通过 `app.actions()` 自定义其他操作,如登录、注册、注销等。
```ts
app.actions({
async login(ctx, next) {},
async register(ctx, next) {},
async logout(ctx, next) {},
}, {
resourceName: 'users', // 属于 users 资源
});
```
以上自定义操作的 HTTP API 为:
```bash
POST http://localhost:3000/api/users:login
POST http://localhost:3000/api/users:register
POST http://localhost:3000/api/users:logout
```
自定义的 HTTP API 依旧保持 REST API 的风格,以 `<resourceName>:<actionName>` 格式表示。实际上 REST API 也可以显式指定 `actionName`,当指定了 `actionName`,无所谓使用什么请求方法,如:
```bash
# 更新操作
PUT http://localhost:3000/api/users/1
# 等同于
POST http://localhost:3000/api/users:update/1
# 删除操作
DELETE http://localhost:3000/api/users/1
# 等同于
GET http://localhost:3000/api/users:destroy/1
# 等同于
POST http://localhost:3000/api/users:destroy/1
```
NocoBase 的路由Resourcer基于资源Resource和操作Action设计将 REST 和 RPC 结合起来,提供更为灵活且统一的 Resource Action API。结合客户端 SDK 是这样的:
```ts
const { ClientSDK } = require('@nocobase/sdk');
const api = new ClientSDK({
// 可以适配不同 request
request(params) => Promise.resolve({}),
});
await api.resource('users').list();
await api.resource('users').create();
await api.resource('users').get();
await api.resource('users').update();
await api.resource('users').destroy();
await api.resource('users').login();
await api.resource('users').register();
await api.resource('users').logout();
```
## 应用 - Application
NocoBase 的 Application 继承了 Koa集成了 DB 和 CLI添加了一些必要的 API这里列一些重点
- `app.db`:数据库实例,每个 app 都有自己的 db。
- `db.getCollection()` 数据表/数据集
- `collection.repository` 数据仓库
- `collection.model` 数据模型
- `db.on()` 添加事件监听,由 EventEmitter 提供
- `db.emit()` 触发事件,由 EventEmitter 提供
- `db.emitAsync()` 触发异步事件
- `app.cli`Commander 实例,提供命令行操作
- `app.context`,上下文
- `ctx.db`
- `ctx.action` 当前资源操作实例
- `action.params` 操作参数
- `action.mergeParams()` 参数合并方法
- `app.constructor()` 初始化
- `app.collection()` 定义数据 Schema等同于 `app.db.collection()`
- `app.resource()` 定义资源
- `app.actions()` 定义资源的操作方法
- `app.on()` 添加事件监听,由 EventEmitter 提供
- `app.emit()` 触发事件,由 EventEmitter 提供
- `app.emitAsync()` 触发异步事件
- `app.use()` 添加中间件,由 Koa 提供
- `app.command()` 自定义命令行,等同于 `app.cli.command()`
- `app.plugin()` 添加插件
- `app.load()` 载入配置,主要用于载入插件
- `app.parse()` 解析 argv 参数,写在最后,等同于 `app.cli.parseAsync()`
## 数据集 - Collection
NocoBase 通过 `app.collection()` 方法定义数据的 SchemaSchema 的类型包括:
属性 Attribute
- Boolean 布尔型
- String 字符串
- Text 长文本
- Integer 整数型
- Float 浮点型
- Decimal 货币
- Json/Jsonb/Array 不同数据库的 JSON 类型不一致,存在兼容性问题
- Time 时间
- Date 日期
- Virtual 虚拟字段
- Reference 引用
- Formula 计算公式
- Context 上下文
- Password 密码
- Sort 排序
关系 Association/Realtion
- HasOne 一对一
- HasMany 一对多
- BelongsTo 多对一
- BelongsToMany 多对多
- Polymorphic 多态
比如一个微型博客的表结构可以这样设计:
```ts
// 用户
app.collection({
name: 'users',
fields: {
username: { type: 'string', unique: true },
password: { type: 'password', unique: true },
posts: { type: 'hasMany' },
},
});
// 文章
app.collection({
name: 'posts',
fields: {
title: 'string',
content: 'text',
tags: 'belongsToMany',
comments: 'hasMany',
author: { type: 'belongsTo', target: 'users' },
},
});
// 标签
app.collection({
name: 'tags',
fields: [
{ type: 'string', name: 'name' },
{ type: 'belongsToMany', name: 'posts' },
],
});
// 评论
app.collection({
name: 'comments',
fields: [
{ type: 'text', name: 'content' },
{ type: 'belongsTo', name: 'user' },
],
});
```
除了通过 `app.collection()` 配置 schema也可以直接调用 api 插入或修改 schemacollection 的核心 API 有:
- `collection` 当前 collection 的数据结构
- `collection.hasField()` 判断字段是否存在
- `collection.addField()` 添加字段配置
- `collection.getField()` 获取字段配置
- `collection.removeField()` 移除字段配置
- `collection.sync()` 与数据库表结构同步
- `collection.repository` 当前 collection 的数据仓库
- `repository.findMany()`
- `repository.findOne()`
- `repository.create()`
- `repository.update()`
- `repository.destroy()`
- `repository.relatedQuery().for()`
- `create()`
- `update()`
- `destroy()`
- `findMany()`
- `findOne()`
- `set()`
- `add()`
- `remove()`
- `toggle()`
- `collection.model` 当前 collection 的数据模型
Collection 示例:
```ts
const collection = app.db.getCollection('posts');
collection.hasField('title');
collection.getField('title');
// 添加或更新
collection.addField({
type: 'string',
name: 'content',
});
// 移除
collection.removeField('content');
// 添加、或指定 key path 替换
collection.mergeField({
name: 'content',
type: 'string',
});
除了全局的 `db.sync()`,也有 `collection.sync()` 方法。
await collection.sync();
```
`db:sync` 是非常常用的命令行之一,数据库根据 collection 的 schema 生成表结构。更多详情见 CLI 章节。`db:sync` 之后,就可以往表里写入数据了,可以使用 Repository 或 Model 操作。
- Repository 初步提供了 findAll、findOne、create、update、destroy 核心操作方法。
- Model 为 Sequelize.Model详细使用说明可以查看 Sequelize 文档。
- Model 取决于适配的 ORMRepository 基于 Model 提供统一的接口。
通过 Repository 创建数据
```ts
const User = app.db.getCollection('users');
const user = await User.repository.create({
title: 't1',
content: 'c1',
author: 1,
tags: [1,2,3],
}, {
whitelist: [],
blacklist: [],
});
await User.repository.findMany({
filter: {
title: 't1',
},
fields: ['id', 'title', 'content'],
sort: '-created_at',
page: 1,
perPage: 20,
});
await User.repository.findOne({
filter: {
title: 't1',
},
fields: ['id', 'title', 'content'],
sort: '-created_at',
page: 1,
perPage: 20,
});
await User.repository.update({
title: 't1',
content: 'c1',
author: 1,
tags: [1,2,3],
}, {
filter: {},
whitelist: [],
blacklist: [],
});
await User.repository.destroy({
filter: {},
});
```
通过 Model 创建数据
```ts
const User = db.getCollection('users');
const user = await User.model.create({
title: 't1',
content: 'c1',
});
```
## 资源 & 操作 - Resource & Action
Resource 是互联网资源,互联网资源都对应一个地址。客户端请求资源地址,服务器响应请求,在这里「请求」就是一种「操作」,在 REST 里通过判断请求方法GET/POST/PUT/DELETE来识别具体的操作但是请求方法局限性比较大如上文提到的登录、注册、注销就无法用 REST API 的方式表示。为了解决这类问题NocoBase 以 `<resourceName>:<actionName>` 格式表示资源的操作。在关系模型的世界里关系无处不在基于关系NocoBase 又延伸了关系资源的概念,对应关系资源的操作的格式为 `<associatedName>.<resourceName>:<actionName>`
Collection 会自动同步给 Resource如上文 Collection 章节定义的 Schema可以提炼的资源有
- `users`
- `users.posts`
- `posts`
- `posts.tags`
- `posts.comments`
- `posts.author`
- `tags`
- `tags.posts`
- `comments`
- `comments.user`
<Alert title="Collection 和 Resource 的关系与区别" type="warning">
- Collection 定义数据的 schema结构和关系
- Resource 定义数据的 action操作方法
- Resource 请求和响应的数据结构由 Collection 定义
- Collection 默认自动同步给 Resource
- Resource 的概念更大,除了对接 Collection 以外,也可以对接外部数据或其他自定义
</Alert>
资源相关 API 有:
- `app.resource()`
- `app.actions()`
- `ctx.action`
一个资源可以有多个操作。
```ts
// 数据类
app.resource({
name: 'users',
actions: {
async list(ctx, next) {},
async get(ctx, next) {},
async create(ctx, next) {},
async update(ctx, next) {},
async destroy(ctx, next) {},
},
});
// 非数据类
app.resource({
name: 'server',
actions: {
// 获取服务器时间
getTime(ctx, next) {},
// 健康检测
healthCheck(ctx, next) {},
},
});
```
常规操作可以用于不同资源
```ts
app.actions({
async list(ctx, next) {},
async get(ctx, next) {},
async create(ctx, next) {},
async update(ctx, next) {},
async destroy(ctx, next) {},
}, {
// 不指定 resourceName 时,全局共享
resourceNames: ['posts', 'comments', 'users'],
});
```
在资源内部定义的 action 不会共享,常规类似增删改查的操作建议设置为全局,`app.resource()` 只设置参数,如:
```ts
app.resource({
name: 'users',
actions: {
list: {
fields: ['id', 'username'], // 只输出 id 和 username 字段
filter: {
'username.$ne': 'admin', // 数据范围筛选过滤 username != admin
},
sort: ['-created_at'], // 创建时间倒序
perPage: 50,
},
get: {
fields: ['id', 'username'], // 只输出 id 和 username 字段
filter: {
'username.$ne': 'admin', // 数据范围筛选过滤 username != admin
},
},
create: {
fields: ['username'], // 白名单
},
update: {
fields: ['username'], // 白名单
},
destroy: {
filter: { // 不能删除 admin
'username.$ne': 'admin',
},
},
},
});
// app 默认已经内置了 list, get, create, update, destroy 操作
app.actions({
async list(ctx, next) {},
async get(ctx, next) {},
async create(ctx, next) {},
async update(ctx, next) {},
async destroy(ctx, next) {},
});
```
在 Middleware Handler 和 Action Handler 里,都可以通过 `ctx.action` 获取到当前 action 实例,提供了两个非常有用的 API
- `ctx.action.params`:获取操作对应的参数
- `ctx.action.mergeParams()`:处理多来源参数合并
`ctx.action.params` 有:
- 定位资源和操作
- `actionName`
- `resourceName`
- `associatedName`
- 定位资源 ID
- `resourceId`
- `associatedId`
- request query
- `filter`
- `fields`
- `sort`
- `page`
- `perPage`
- 其他 query 值
- request body
- `values`
示例:
```ts
async function (ctx, next) {
const { resourceName, resourceId, filter, fields } = ctx.action.params;
// ...
}
```
`ctx.action.mergeParams()` 主要用于多来源参数合并,以 `filter` 参数为例。如:客户端请求日期 2021-09-15 创建的文章
```bash
GET /api/posts:list?filter={"created_at": "2021-09-15"}
```
资源设置锁定只能查看已发布的文章
```ts
app.resource({
name: 'posts',
actions: {
list: {
filter: { status: 'publish' }, // 只能查看已发布文章
},
},
})
```
权限设定,只能查看自己创建的文章
```ts
app.use(async (ctx, next) => {
const { resourceName, actionName } = ctx.action.params;
if (resourceName === 'posts' && actionName === 'list') {
ctx.action.mergeParams({
filter: {
created_by_id: ctx.state.currentUser.id,
},
});
}
await next();
});
```
以上客户端、资源配置、中间件内我们都指定了 filter 参数,三个来源的参数最终会合并在一起作为最终的过滤条件:
```ts
async function list(ctx, next) {
// list 操作中获取到的 filter
console.log(ctx.params.filter);
// filter 是特殊的 and 合并
// {
// and: [
// { created_at: '2021-09-15' },
// { status: 'publish' },
// { created_by_id: 1, }
// ]
// }
}
```
## 事件 - Event
在操作执行前、后都放置了相关事件监听器,可以通过 `app.db.on()``app.on()` 添加。区别在于:
- `app.db.on()` 添加数据库层面的监听器
- `app.on()` 添加服务器应用层面的监听器
`users:login` 为例,在数据库里为「查询」操作,在应用里为「登录」操作。也就是说,如果需要记录登录操作日志,要在 `app.on()` 里处理。
```ts
// 创建数据时,执行 User.create() 时触发
app.db.on('users.beforeCreate', async (model) => {});
// 客户端 `POST /api/users:login` 时触发
app.on('users.beforeLogin', async (ctx, next) => {});
// 客户端 `POST /api/users` 时触发
app.on('users.beforeCreate', async (ctx, next) => {});
```
## 中间件 - Middleware
Server Application 基于 Koa所有 Koa 的插件(中间件)都可以直接使用,可以通过 `app.use()` 添加。如:
```ts
const responseTime = require('koa-response-time');
app.use(responseTime());
app.use(async (ctx, next) => {
await next();
});
```
`koa.use(middleware)` 略有不同,`app.use(middleware, options)` 多了个 options 参数,可以用于限定 resource 和 action也可以用于控制中间件的插入位置。
```ts
import { middleware } from '@nocobase/server';
app.use(async (ctx, next) => {}, {
name: 'middlewareName1',
resourceNames: [], // 作用于资源内所有 actions
actionNames: [
'list', // 全部 list action
'users:list', // 仅 users 资源的 list action,
],
insertBefore: '',
insertAfter: '',
});
```
## 命令行 - CLI
Application 除了可以做 HTTP Server 以外,也是 CLI内置了 Commander。目前内置的命令有
- `init` 初始化
- `db:sync --force` 用于配置与数据库表结构同步
- `start --port` 启动应用
- `plugin:**` 插件相关
自定义:
```ts
app.command('foo').action(async () => {
console.log('foo...');
});
```
## 插件 - Plugin
上文,讲述了核心的扩展接口,包括但不局限于:
- Database/Collection
- `app.db` database 实例
- `app.collection()` 等同于 `app.db.collection()`
- Resource/Action
- `app.resource()` 等同于 `app.resourcer.define()`
- `app.actions()` 等同于 `app.resourcer.registerActions()`
- Hook/Event
- `app.on()` 添加服务器监听器
- `app.db.on()` 添加数据库监听器
- Middleware
- `app.use()` 添加中间件
- CLI
- `app.cli` commander 实例
- `app.command()` 等同于 `app.cli.command()`
基于以上扩展接口,进一步提供了模块化、可插拔的插件,可以通过 `app.plugin()` 添加。插件的流程包括安装、升级、激活、载入、禁用、卸载,不需要的流程可缺失。如:
**最简单的插件**
```ts
app.plugin(function pluginName1() {
});
```
这种方式添加的插件会直接载入,无需安装。
**JSON 风格**
```ts
const plugin = app.plugin({
enable: false, // 默认为 true不需要启用时可以禁用。
name: 'plugin-name1',
displayName: '插件名称',
version: '1.2.3',
dependencies: {
pluginName2: '1.x',
pluginName3: '1.x',
},
async install() {},
async upgrade() {},
async activate() {},
async bootstrap() {},
async deactivate() {},
async unstall() {},
});
// 通过 api 激活插件
plugin.activate();
```
**OOP 风格**
```ts
class MyPlugin extends Plugin {
async install() {}
async upgrade() {}
async bootstrap() {}
async activate() {}
async deactivate() {}
async unstall() {}
}
app.plugin(MyPlugin);
// 或
app.plugin({
name: 'plugin-name1',
displayName: '插件名称',
version: '1.2.3',
dependencies: {
pluginName2: '1.x',
pluginName3: '1.x',
},
plugin: MyPlugin,
});
```
**引用独立的 Package**
```ts
app.plugin('@nocobase/plugin-action-logs');
```
插件信息也可以直接写在 `package.json`
```js
{
name: 'pluginName1',
displayName: '插件名称',
version: '1.2.3',
dependencies: {
pluginName2: '1.x',
pluginName3: '1.x',
},
}
```
**插件 CLI**
```bash
plugin:install pluginName1
plugin:unstall pluginName1
plugin:activate pluginName1
plugin:deactivate pluginName1
```
目前已有的插件:
- @nocobase/plugin-collections 提供数据表配置接口,可通过 HTTP API 管理数据表。
- @nocobase/plugin-action-logs 操作日志
- @nocobase/plugin-automations 自动化(未升级 v0.5,暂不能使用)
- @nocobase/plugin-china-region 中国行政区
- @nocobase/plugin-client 提供客户端,无代码的可视化配置界面,需要与 @nocobase/client 配合使用
- @nocobase/plugin-export 导出
- @nocobase/plugin-file-manager 文件管理器
- @nocobase/plugin-permissions 角色和权限
- @nocobase/plugin-system-settings 系统配置
- @nocobase/plugin-ui-router 前端路由配置
- @nocobase/plugin-ui-schema ui 配置
- @nocobase/plugin-users 用户模块
## 测试 - Testing
有代码就需要测试,@nocobase/test 提供了 mockDatabase 和 mockServer 用于数据库和服务器的测试,如:
```ts
import { mockServer, MockServer } from '@nocobase/test';
describe('mock server', () => {
let api: MockServer;
beforeEach(() => {
api = mockServer({
dataWrapping: false,
});
api.actions({
list: async (ctx, next) => {
ctx.body = [1, 2];
await next();
},
});
api.resource({
name: 'test',
});
});
afterEach(async () => {
return api.destroy();
});
it('agent.get', async () => {
const response = await api.agent().get('/test');
expect(response.body).toEqual([1, 2]);
});
it('agent.resource', async () => {
const response = await api.agent().resource('test').list();
expect(response.body).toEqual([1, 2]);
});
});
```
## 客户端 - Client
为了让更多非开发人员也能参与进来NocoBase 提供了配套的客户端插件 —— 无代码的可视化配置界面。客户端插件需要与 @nocobase/client 配合使用,可以直接使用,也可以自行改造。
插件配置
```ts
app.plugin('@nocobase/plugin-client', {
// 自定义 dist 路径
dist: path.resolve(__dirname, './node_modules/@nocobase/client/app'),
});
```
为了满足各类场景需求,客户端 `@nocobase/client` 提供了丰富的基础组件:
- Action - 操作
- Action.Window 当前浏览器窗口/标签里打开
- Action.Drawer 打开抽屉(默认右侧划出)
- Action.Modal 打开对话框
- Action.Dropdown 下拉菜单
- Action.Popover 气泡卡片
- Action.Group 按钮分组
- Action.Bar 操作栏
- AddNew 「添加」模块
- AddNew.CardItem - 添加区块
- AddNew.PaneItem - 添加区块(查看面板,与当前查看的数据相关)
- AddNew.FormItem - 添加字段
- BlockItem/CardItem/FormItem - 装饰器
- BlockItem - 普通装饰器(无包装效果)
- CardItem - 卡片装饰器
- FormItem - 字段装饰器
- Calendar - 日历
- Cascader - 级联选择
- Chart - 图表
- Checkbox - 勾选
- Checkbox.Group - 多选框
- Collection - 数据表配置
- Collection.Field - 数据表字段
- ColorSelect - 颜色选择器
- DatePicker - 日期选择器
- DesignableBar - 配置工具栏
- Filter - 筛选器
- Form - 表单
- Grid - 栅格布局
- IconPicker - 图标选择器
- Input - 输入框
- Input.TextArea - 多行输入框
- InputNumber - 数字框
- Kanban - 看板
- ListPicker - 列表选择器(用于选择、展示关联数据)
- Markdown 编辑器
- Menu - 菜单
- Password - 密码
- Radio - 单选框
- Select - 选择器
- Table - 表格
- Tabs - 标签页
- TimePicker - 时间选择器
- Upload - 上传
可以自行扩展组件,以上组件基于 Formily 构建,怎么自定义组件大家查看相关组件源码或 Formily 文档,这里说点不一样的。
- 如何扩展数据库字段?
- 如何将第三方区块添加到 AddNew 模块中?
- 如何在操作栏里添加更多的内置操作?
- 如何自定义配置工具栏?
除了组件具备灵活的扩展以外,客户端也可以在任意前端框架中使用,可以自定义 Request 和 Router
<pre lang="tsx">
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ClientSDK, Application } from '@nocobase/client';
// 初始化 client 实例
const client = new ClientSDK({
request: (options) => Promise.resolve({}),
});
// 适配 Route Component
const RouteSwitch = createRouteSwitch({
components: {
AdminLayout,
AuthLayout,
RouteSchemaRenderer,
},
});
ReactDOM.render(
<ClientProvider client={client}>
<MemoryRouter initialEntries={['/admin']}>
<RouteSwitch routes={[]}/>
</MemoryRouter>
</ClientProvider>,
document.getElementById('root'),
);
</pre>
更多细节,可以通过 `create-nocobase-app` 初始化项目脚手架并体验。
```bash
yarn create nocobase-app my-nocobase-project
```
nocobase-app 默认使用 umijs 作为项目构建工具,并集成了 Server 作数据接口,初始化的目录结构如下:
```bash
|- src
|- pages
|- apis
|- .env
|- .umirc.ts
|- package.json
```
## 场景 - Cases
小型管理信息系统,具备完整的前后端。
<img src="../../images/MiniMIS.png" style="max-width: 300px; width: 100%;">
API 服务,无客户端,提供纯后端接口。
<img src="../../images/API.png" style="max-width: 280px; width: 100%;">
小程序 + 后台管理,只需要一套数据库,但有两套用户和权限,一套用于后台用户,一套用于小程序用户。
<img src="../../images/MiniProgram.png" style="max-width: 600px; width: 100%;">
SaaS 服务共享用户每个应用有自己配套的数据库各应用数据完全隔离。应用不需要用户和权限模块SaaS 主站全局共享了。
<img src="../../images/SaaS2.png" style="max-width: 450px; width: 100%;">
SaaS 服务(独立用户),每个应用有自己的独立用户模块和权限,应用可以绑定自己的域名。
<img src="../../images/SaaS1.png" style="max-width: 450px; width: 100%;">

Some files were not shown because too many files have changed in this diff Show More