自定义指令

除了CabloyJS提供的基本指令,业务模块还可以提供与具体业务相关的自定义指令

下面,以模块test-party为例,实现两个自定义指令:

  1. partyOver:在Party结束时,通过点击按钮Party Over,把正式副本设置为Over状态
  2. partyOverBulk:在列表页面,对已选中的条目进行批量处理

自定义指令:代码逻辑

一个自定义指令的完整逻辑,需要前端和后端联动,这必然涉及到多个代码文件的创建与变更。为了进一步简化开发步骤,提升开发体验,CabloyJS内置了一个Cli命令,通过此命令可以快速创建前后端代码骨架

因此,在这里,我们先以模块test-party为例,来梳理一下自定义指令前端和后端的代码逻辑线索,然后再介绍如何通过Cli命令进行快速创建

后端代码

- 指令定义

在模块meta文件的base.atoms.[atomClass].actions节点添加指令定义

src/suite-vendor/test-party/modules/test-party/backend/src/meta.js

  1. 1base: {
  2. 2 atoms: {
  3. 3 party: {
  4. 4 actions: {
  5. 5 partyOver: {
  6. 6 code: 101,
  7. 7 title: 'PartyOver',
  8. 8 actionModule: moduleInfo.relativeName,
  9. 9 actionComponent: 'action',
  10. 10 icon: { material: 'check_circle_outline' },
  11. 11 enableOnOpened: true,
  12. 12 stage: 'formal',
  13. 13 },
  14. 14 partyOverBulk: {
  15. 15 code: 201,
  16. 16 title: 'PartyOver',
  17. 17 actionModule: moduleInfo.relativeName,
  18. 18 actionComponent: 'action',
  19. 19 icon: { material: 'check_circle_outline' },
  20. 20 bulk: true,
  21. 21 select: true,
  22. 22 },
  23. 23 },
  24. 24 },
  25. 25 },
  26. 26},
名称 默认值 说明
partyOver/partyOverBulk 指令名称
code 指令代码。自定义指令从101开始
title 指令的标题
actionModule 前端处理组件所属模块名: test-party
actionComponent 前端处理组件名: action
icon 图标
bulk false 批量指令与列表操作相关
select null 对于批量指令,true:只有选择了条目才有效,false:没有选择条目才有效,null:总是有效
enableOnOpened false 对于进入编辑状态的数据是否有效
stage 针对哪个原子阶段有效,在这里只针对formal副本有效

- API路由

定义两个API路由,用于前端调用

  1. 可根据实际业务决定是否需要设置中间件transaction
  2. 必须配置中间件right的meta参数

src/suite-vendor/test-party/modules/test-party/backend/src/routes.js

  1. 1{ method: 'post', path: 'party/over', controller: 'party', middlewares: 'transaction',
  2. 2 meta: { right: { type: 'atom', atomClass: 'test-party:party', action: 'partyOver' } },
  3. 3},
  4. 4{ method: 'post', path: 'party/overBulk', controller: 'party', middlewares: 'transaction',
  5. 5 meta: { right: { type: 'atom', atomClass: 'test-party:party', action: 'partyOverBulk' } },
  6. 6},

- Controller

添加API路由对应的Controller方法

src/suite-vendor/test-party/modules/test-party/backend/src/controller/party.js

  1. 1async over() {
  2. 2 const res = await this.ctx.service.party.over({
  3. 3 key: this.ctx.request.body.key,
  4. 4 user: this.ctx.state.user.op,
  5. 5 });
  6. 6 this.ctx.success(res);
  7. 7}
  8. 8
  9. 9async overBulk() {
  10. 10 const res = await this.ctx.service.party.overBulk({
  11. 11 keys: this.ctx.request.body.keys,
  12. 12 user: this.ctx.state.user.op,
  13. 13 });
  14. 14 this.ctx.success(res);
  15. 15}

- Service

在Service中执行具体的业务逻辑

src/suite-vendor/test-party/modules/test-party/backend/src/service/party.js

  1. 1async over({ key, user }) {
  2. 2 await this.ctx.model.party.update({
  3. 3 id: key.itemId,
  4. 4 partyOver: 1,
  5. 5 });
  6. 6}
  7. 7
  8. 8async overBulk({ keys, user }) {
  9. 9 const resKeys = [];
  10. 10 for (const key of keys) {
  11. 11 const res = await this._overBulk_item({ key, user });
  12. 12 if (res) {
  13. 13 resKeys.push(key);
  14. 14 }
  15. 15 }
  16. 16 return { keys: resKeys };
  17. 17}
  18. 18
  19. 19async _overBulk_item({ key, user }) {
  20. 20 // check right
  21. 21 const res = await this.ctx.bean.atom.checkRightAction({
  22. 22 atom: { id: key.atomId }, action: 'partyOver', user,
  23. 23 });
  24. 24 if (!res) return false;
  25. 25 // over
  26. 26 await this.over({ key, user });
  27. 27 // ok
  28. 28 return true;
  29. 29}

- 原子授权

自定义指令必须进行授权,相应的用户才能在前端使用该指令对应的按钮

src/suite-vendor/test-party/modules/test-party/backend/src/bean/version.manager.js

  1. 1async init(options) {
  2. 2 // only in test/local
  3. 3 if (!app.meta.isTest && !app.meta.isLocal) return;
  4. 4
  5. 5 // init
  6. 6 if (options.version === 1) {
  7. 7 // add role rights
  8. 8 const roleRights = [
  9. 9 // custom
  10. 10 { roleName: 'system', action: 'partyOver', scopeNames: 0 },
  11. 11 { roleName: 'system', action: 'partyOverBulk' },
  12. 12 ];
  13. 13 await this.ctx.bean.role.addRoleRightBatch({ atomClassName: 'party', roleRights });
  14. 14 }
  15. 15
  16. 16}

前端代码

- 前端组件定义

在后端定义的自定义指令,会自动在前端渲染成按钮。当点击按钮时,系统会自动调用指令定义中约定的前端组件test-party:action

因此,需要在前端创建一个组件,该组件提供一个约定的方法onAction,用于处理前端逻辑

src/suite-vendor/test-party/modules/test-party/front/src/components/action.js

  1. 1export default {
  2. 2 meta: {
  3. 3 global: false,
  4. 4 },
  5. 5 methods: {
  6. 6 async onAction({ ctx, action, item }) {
  7. 7 if (action.name === 'partyOver') {
  8. 8 const key = { atomId: item.atomId, itemId: item.itemId };
  9. 9 return await this._onActionPartyOver({ ctx, key });
  10. 10 } else if (action.name === 'partyOverBulk') {
  11. 11 return await this._onActionPartyOverBulk({ ctx, item });
  12. 12 }
  13. 13 },
  14. 14 async _onActionPartyOver({ ctx, key }) {
  15. 15 await ctx.$api.post('/test/party/party/over', { key });
  16. 16 ctx.$meta.eventHub.$emit('atom:action', { key, action: { name: 'save' } });
  17. 17 },
  18. 18 async _onActionPartyOverBulk({ ctx, item }) {
  19. 19 // confirm
  20. 20 await ctx.$view.dialog.confirm();
  21. 21 // atomClass
  22. 22 const atomClass = { id: item.atomClassId };
  23. 23 // keys
  24. 24 const selectedAtoms = ctx.bulk.selectedAtoms;
  25. 25 const keys = selectedAtoms.map(item => {
  26. 26 return { atomId: item.atomId, itemId: item.itemId };
  27. 27 });
  28. 28 // overBulk
  29. 29 const res = await ctx.$api.post('/test/party/party/overBulk', { atomClass, keys });
  30. 30 // change
  31. 31 for (const key of res.keys) {
  32. 32 // action
  33. 33 ctx.$meta.eventHub.$emit('atom:action', { key, action: { name: 'save' } });
  34. 34 }
  35. 35 // clear selection
  36. 36 ctx.bulk_clearSelectedAtoms();
  37. 37 // check result
  38. 38 if (res.keys.length === keys.length) return true;
  39. 39 return this.$text('PartyOverBulkNotAllDone');
  40. 40 },
  41. 41 },
  42. 42};

- 前端组件注册

由于该组件被跨模块使用,因此需要进行注册,注册名称为action

src/suite-vendor/test-party/modules/test-party/front/src/components.js

  1. 1import action from './components/action.js';
  2. 2
  3. 3export default {
  4. 4 action,
  5. 5};