nocobase/docs/zh-CN/api/actions.md
jack zhang d76e8fb87f
refactor: upgrade umi, react and react-router-dom (#1921)
* refactor: update umi version 3.x to version 4.x

* refactor: update react-router-dom version to 6.x

* refactor(react-router-dom): change Layout Component `props.children` to `<Outlet />`

* refactor(react-router-dom): change <Route /> props and <RouteSwitch /> correct

* refactor(react-router-dom): replace `<Redirect />` to `<Navigate replace />`

* refactor(react-router-dom): replace `useHistory` to `useNavigate`

* refactor(react-router-dom): replace `useRouteMatch` to `useParams`

* refactor(react-router-dom & dumi): fix <RouteSwitch /> & umi document bug

* refactor(react-router-dom): `useRoutes` Optimize `<RouteSwitch />` code

* refactor(react-router-dom): update `Route` types and docs

* refactor(react-router-dom): optimize RouteSwitch code

* refactor(react-router-dom): `useLocation` no generics type

* refactor(react-router-dom): add `less v3.9.0` to `resolutions` to solve the error of `gulp-less`

* refactor(react-router-dom): fix `<RouteSwitch />`  `props.routes` as an array is not handled

* chore: upgrade `dumi` and refactor docs

* fix: completed code review, add `targets` to solve browser compatibility & removed `chainWebpack`

* refactor(dumi): upgraded dumi under `packages/core/client`

* refactor(dumi): delete `packages/core/dumi-theme-nocobase`

* refactor(dumi): degrade `react`  & replace `dumi-theme-antd` to `dumi-theme-nocobase`

* refactor(dumi): solve conflicts between multiple dumi applications

* fix: login page error in react 17

* refactor(dumi): remove less resolutions

* refactor(dumi): umi add `msfu: true` config

* fix: merge bug

* fix: self code review

* fix: code reivew and test bug

* refactor: upgrade react to 18

* refactor: degrade react types to 17

* chore: fix ci error

* fix: support routerBase & fix workflow page params

* fix(doc): menu externel link

* fix: build error

* fix: delete

* fix: vitest error

* fix: react-router new code replace

* fix: vitest markdown error

* fix: title is none when refresh

* fix: merge error

* fix: sidebar width is wrong

* fix: useProps error

* fix: side-menu-width

* fix: menu selectId is wrong & useProps is string

* fix: menu selected first default & side menu hide when change

* fix: test error & v0.10 change log

* fix: new compnent doc modify

* fix: set umi `fastRefresh=false`

* refactor: application v2

* fix: improve code

* fix: bug

* fix: page = 0 error

* fix: workflow navigate error

* feat: plugin manager

* fix: afterAdd

* feat: update docs

* feat: update docs

* fix: page tab change not work

* fix: login redirect query param doesn't work

* fix: bug and doc

* feat: update docs

* fix: ci error

* fix: merge main

* feat: update docs

* feat: update docs

* feat: update docs

* chore(versions): 😊 publish v0.10.0-alpha.1

* fix: translations

* chore: backend node test max old space size

* docs: add useSearchParams

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
Co-authored-by: ChengLei Shao <chareice@live.com>
2023-06-20 11:48:02 +08:00

10 KiB
Raw Blame History

@nocobase/actions

概览

针对常用的 CRUD 等数据资源的操作NocoBase 内置了对应操作方法,并通过数据表资源自动映射相关的操作。

import { Application } from "@nocobase/server";

const app = new Application({
  database: {
    dialect: 'sqlite',
    storage: './db.sqlite',
  },
  registerActions: true // 注册内置资源操作,默认为 True
});

内置的操作方法都是注册在 application 中的 resourcer 实例上。 通常情况下无需直接调用内置的 action 方法,在需要扩展默认操作行为时,可以在自定义的操作方法内调用默认方法。

资源操作

list()

获取数据列表。对应资源操作的 URL 为 GET /api/<resource>:list

参数

参数名 类型 默认值 描述
filter Filter - 过滤参数
fields string[] - 要获取的字段
except string[] - 要排除的字段
appends string[] - 要附加的关系字段
sort string[] - 排序参数
page number 1 分页
pageSize number 20 每页数据条数

示例

当需要提供一个查询数据列表的接口,但不是默认以 JSON 格式输出时,可以基于内置默认方法进行扩展:

import actions from '@nocobase/actions';

app.actions({
  async ['books:list'](ctx, next) {
    ctx.action.mergeParams({
      except: ['content']
    });

    await actions.list(ctx, async () => {
      const { rows } = ctx.body;
      // transform JSON to CSV output
      ctx.body = rows.map(row => Object.keys(row).map(key => row[key]).join(',')).join('\n');
      ctx.type = 'text/csv';

      await next();
    });
  }
});

请求示例,将获得 CSV 格式文件的返回:

curl -X GET http://localhost:13000/api/books:list

get()

获取单条数据。对应资源操作的 URL 为 GET /api/<resource>:get

参数

参数名 类型 默认值 描述
filterByTk number | string - 过滤主键
filter Filter - 过滤参数
fields string[] - 要获取的字段
except string[] - 要排除的字段
appends string[] - 要附加的关系字段
sort string[] - 排序参数
page number 1 分页
pageSize number 20 每页数据条数

示例

基于 NocoBase 内置的文件管理插件,可以扩展当客户端请求以资源标识下载一个文件时,返回文件流:

import path from 'path';
import actions from '@nocobase/actions';
import { STORAGE_TYPE_LOCAL } from '@nocobase/plugin-file-manager';

app.actions({
  async ['attachments:get'](ctx, next) {
    ctx.action.mergeParams({
      appends: ['storage'],
    });

    await actions.get(ctx, async () => {
      if (ctx.accepts('json', 'application/octet-stream') === 'json') {
        return next();
      }

      const { body: attachment } = ctx;
      const { storage } = attachment;

      if (storage.type !== STORAGE_TYPE_LOCAL) {
        return ctx.redirect(attachment.url);
      }

      ctx.body = fs.createReadStream(path.resolve(storage.options.documentRoot?, storage.path));
      ctx.attachment(attachment.filename);
      ctx.type = 'application/octet-stream';

      await next();
    });
  }
});

请求示例,将获得文件流的返回:

curl -X GET -H "Accept: application/octet-stream" http://localhost:13000/api/attachments:get?filterByTk=1

create()

创建单条数据。对应资源操作的 URL 为 POST /api/<resource>:create

参数

参数名 类型 默认值 描述
values Object - 要创建的数据

示例

类似文件管理插件,创建带有二进制内容的数据作为上传文件的附件:

import multer from '@koa/multer';
import actions from '@nocobase/actions';

app.actions({
  async ['files:create'](ctx, next) {
    if (ctx.request.type === 'application/json') {
      return actions.create(ctx, next);
    }

    if (ctx.request.type !== 'multipart/form-data') {
      return ctx.throw(406);
    }

    // 文件保存处理仅用 multer() 作为示例,不代表完整的逻辑
    multer().single('file')(ctx, async () => {
      const { file, body } = ctx.request;
      const { filename, mimetype, size, path } = file;

      ctx.action.mergeParams({
        values: {
          filename,
          mimetype,
          size,
          path: file.path,
          meta: typeof body.meta === 'string' ? JSON.parse(body.meta) : {};
        }
      });

      await actions.create(ctx, next);
    });
  }
});

请求示例,可以创建文件表的普通数据,也可以含附件一起提交:

# 仅创建普通数据
curl -X POST -H "Content-Type: application/json" -d '{"filename": "some-file.txt", "mimetype": "text/plain", "size": 5, "url": "https://cdn.yourdomain.com/some-file.txt"}' "http://localhost:13000/api/files:create"

# 含附件一起提交
curl -X POST -F "file=@/path/to/some-file.txt" -F 'meta={"length": 100}' "http://localhost:13000/api/files:create"

update()

更新一条或多条数据。对应的 URL 为 PUT /api/<resource>:update

参数

参数名 类型 默认值 描述
filter Filter - 过滤参数
filterByTk number | string - 过滤主键
values Object - 更新数据值

注:参数中的 filterfilterByTk 至少提供一项。

示例

类似 create() 的例子,更新文件记录可以扩展为可携带二进制内容的数据作为更新的文件:

import multer from '@koa/multer';
import actions from '@nocobase/actions';

app.actions({
  async ['files:update'](ctx, next) {
    if (ctx.request.type === 'application/json') {
      return actions.update(ctx, next);
    }

    if (ctx.request.type !== 'multipart/form-data') {
      return ctx.throw(406);
    }

    // 文件保存处理仅用 multer() 作为示例,不代表完整的逻辑
    multer().single('file')(ctx, async () => {
      const { file, body } = ctx.request;
      const { filename, mimetype, size, path } = file;

      ctx.action.mergeParams({
        values: {
          filename,
          mimetype,
          size,
          path: file.path,
          meta: typeof body.meta === 'string' ? JSON.parse(body.meta) : {};
        }
      });

      await actions.update(ctx, next);
    });
  }
});

请求示例,可以创建文件表的普通数据,也可以含附件一起提交:

# 仅创建普通数据
curl -X PUT -H "Content-Type: application/json" -d '{"filename": "some-file.txt", "mimetype": "text/plain", "size": 5, "url": "https://cdn.yourdomain.com/some-file.txt"}' "http://localhost:13000/api/files:update"

# 含附件一起提交
curl -X PUT -F "file=@/path/to/some-file.txt" -F 'meta={"length": 100}' "http://localhost:13000/api/files:update"

destroy()

删除一条或多条数据。对应的 URL 为 DELETE /api/<resource>:destroy

参数

参数名 类型 默认值 描述
filter Filter - 过滤参数
filterByTk number | string - 过滤主键

注:参数中的 filterfilterByTk 至少提供一项。

示例

类似对文件管理插件扩展一个删除文件数据也需要同时删除对应文件的操作处理:

import actions from '@nocobase/actions';

app.actions({
  async ['files:destroy'](ctx, next) {
    // const repository = getRepositoryFromParams(ctx);

    // const { filterByTk, filter } = ctx.action.params;

    // const items = await repository.find({
    //   fields: [repository.collection.filterTargetKey],
    //   appends: ['storage'],
    //   filter,
    //   filterByTk,
    //   context: ctx,
    // });

    // await items.reduce((promise, item) => promise.then(async () => {
    //   await item.removeFromStorage();
    //   await item.destroy();
    // }), Promise.resolve());

    await actions.destroy(ctx, async () => {
      // do something
      await next();
    });
  }
});

move()

对应的 URL 为 POST /api/<resource>:move

此方法用于移动数据,调整数据的排序。例如在页面中,拖拽一个元素到另一个元素的上方或下方,可调用此方法实现顺序调整。

参数

参数名 类型 默认值 描述
sourceId targetKey - 移动的元素ID
targetId targetKey - 与移动元素交换位置的元素ID
sortField string sort 排序存储的字段名
targetScope string - 排序的scope一个 resource 可以按照不同的 scope 排序
sticky boolean - 是否置顶移动的元素
method insertAfter | prepend - 插入类型,插入目标元素之前还是之后

关系资源资源操作

add()

添加与对象的关联关系,对应的 URL 为 POST /api/<resource.assocition>:add。适用于 hasManybelongsToMany 关联。

参数

参数名 类型 默认值 描述
values TargetKey | TargetKey[] - 添加的关联对象ID

remove()

移除与对象的关联关系,对应的 URL 为 POST /api/<resource.assocition>:remove

参数

参数名 类型 默认值 描述
values TargetKey | TargetKey[] - 移除的关联对象ID

set()

设置关联的关联对象,对应的 URL 为 POST /api/<resource.assocition>:set

参数

参数名 类型 默认值 描述
values TargetKey | TargetKey[] - 设置的关联对象的ID

toggle()

切换关联的关联对象,对应的 URL 为 POST /api/<resource.assocition>:toggletoggle 在内部判断关联对象是否已经存在,如果存在则移除,如果不存在则添加。

参数

参数名 类型 默认值 描述
values TargetKey - 切换的关联对象的ID