原子
是CabloyJS最基本的要素,如文章、公告、请假单,等等
通过原子
的组合,就可以实现任何想要的功能,如CMS、OA、CRM、ERP等等
正由于从各种业务模型
中抽象出来一个通用的原子
概念,因而,CabloyJS为原子
实现了许多通用的特性和功能,从而可以便利的为各类实际业务赋能
更详细的原子
概念请参见:Cabloy:原子基本概念
在这里,主要通过原子类型party
介绍最基本的概念与用法
业务模块与原子类型
当我们创建一个业务模块时,就会自动创建一个原子类型。比如,模块test-party
中的party
原子类型。由于模块test-party
已经存在,在这里仅把创建模块的命令列出来,并不需要再次执行
- 1$ cd /path/to/project
- 2$ npm run cli :create:module test-party -- [--template=module-business] [--suite=test-party]
名称 | 说明 |
---|---|
moduleName | 模块名称,比如test-party |
template | 模版名称,比如module-business |
suite | 套件名称,比如test-party 。test-party套件 包含许多模块,test-party模块 仅仅是其中一个 |
声明原子类型
原子类型
是原子
对应的元数据信息,也是在模块的meta.js
文件中设置
src/suite-vendor/test-party/modules/test-party/backend/src/meta.js
- 1const meta = {
- 2 base: {
- 3 atoms: {
- 4 party: {
- 5 info: {
- 6 bean: 'party',
- 7 title: 'Party',
- 8 tableName: 'testParty',
- 9 tableNameModes: {
- 10 default: 'testPartyView',
- 11 },
- 12 language: false,
- 13 category: true,
- 14 tag: true,
- 15 },
- 16 actions: {
- 17 },
- 18 validator: 'party',
- 19 search: {
- 20 validator: 'partySearch',
- 21 },
- 22 },
- 23 },
- 24 },
- 25};
名称 | 说明 |
---|---|
info.bean | 原子类型对应的Bean组件名称 |
info.title | 原子类型的名称 |
info.tableName | 原子类型对应的业务数据表名称 |
info.tableNameModes.default | 可以根据需要指定数据视图 |
info.language | 是否启用本地化 |
info.category | 是否启用目录 |
info.tag | 是否启用标签 |
validator | 原子类型对应的验证器 ,用于渲染业务表单,并验证表单数据 |
search.validator | 与搜索相关的验证器 ,用于渲染自定义的搜索字段 |
原子业务数据表
与模块相关的数据架构变更管理,都在Bean组件version.manager
中
src/suite-vendor/test-party/modules/test-party/backend/src/bean/version.manager.js
- 1async update(options) {
- 2 if (options.version === 1) {
- 3 // create table: testParty
- 4 let sql = `
- 5 CREATE TABLE testParty (
- 6 id int(11) NOT NULL AUTO_INCREMENT,
- 7 createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- 8 updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- 9 deleted int(11) DEFAULT '0',
- 10 iid int(11) DEFAULT '0',
- 11 atomId int(11) DEFAULT '0',
- 12 personCount int(11) DEFAULT '0',
- 13 partyTypeId int(11) DEFAULT '0',
- 14 PRIMARY KEY (id)
- 15 )
- 16 `;
- 17 await this.ctx.model.query(sql);
- 18 }
- 19}
src/suite-vendor/test-party/modules/test-party/backend/src/beans.js
- 1const versionManager = require('./bean/version.manager.js');
- 2
- 3module.exports = app => {
- 4 const beans = {
- 5 // version
- 6 'version.manager': {
- 7 mode: 'app',
- 8 bean: versionManager,
- 9 },
- 10 };
- 11 return beans;
- 12};
model对象
定义与业务数据表对应的model对象,可以更便利的操作数据
src/suite-vendor/test-party/modules/test-party/backend/src/model/party.js
- 1module.exports = app => {
- 2 class Party extends app.meta.Model {
- 3 constructor(ctx) {
- 4 super(ctx, { table: 'testParty', options: { disableDeleted: false } });
- 5 }
- 6 }
- 7 return Party;
- 8};
名称 | 默认值 | 说明 |
---|---|---|
table | model对象对应的数据表名 | |
disableDeleted | false | 是否禁用软删除 特性 |
验证器
CabloyJS的验证
机制底层采用ajv
,建议您对ajv有初步的了解
使用验证器
,我们只需定义好与业务相关的JSON Schema
,就可以自动渲染表单,同时还可以自动验证表单数据,如果表单数据不符合预期,会自动把错误信息显示出来
关于表单验证
的更详细信息请参见:CabloyJS:表单验证
src/suite-vendor/test-party/modules/test-party/backend/src/config/validation/schemas.js
- 1module.exports = app => {
- 2 const schemas = {};
- 3 // party
- 4 schemas.party = {
- 5 type: 'object',
- 6 properties: {
- 7 atomName: {
- 8 type: 'string',
- 9 ebType: 'text',
- 10 ebTitle: 'Party Name',
- 11 notEmpty: true,
- 12 },
- 13 personCount: {
- 14 type: 'number',
- 15 ebType: 'text',
- 16 ebTitle: 'Person Count',
- 17 minimum: 1,
- 18 notEmpty: true,
- 19 },
- 20 partyTypeId: {
- 21 type: 'number',
- 22 ebType: 'select',
- 23 ebTitle: 'Party Type',
- 24 ebOptionsUrl: '/test/party/party/types',
- 25 ebOptionTitleKey: 'name',
- 26 ebOptionValueKey: 'id',
- 27 ebOptionsBlankAuto: true,
- 28 notEmpty: true,
- 29 },
- 30 atomCategoryId: {
- 31 type: 'number',
- 32 ebType: 'category',
- 33 ebTitle: 'Category',
- 34 },
- 35 atomTags: {
- 36 type: [ 'string', 'null' ],
- 37 ebType: 'tags',
- 38 ebTitle: 'Tags',
- 39 },
- 40 },
- 41 };
- 42 // party search
- 43 schemas.partySearch = {
- 44 type: 'object',
- 45 properties: {
- 46 partyTypeId: {
- 47 type: 'number',
- 48 ebType: 'select',
- 49 ebTitle: 'Party Type',
- 50 ebOptionsUrl: '/test/party/party/types',
- 51 ebOptionTitleKey: 'name',
- 52 ebOptionValueKey: 'id',
- 53 ebOptionsBlankAuto: true,
- 54 },
- 55 },
- 56 };
- 57 return schemas;
- 58};
原子Bean与原子指令
CabloyJS将所有业务数据的操作称为原子指令
,主要分两类:
基本指令
:create、read、write、delete扩展指令
:与具体业务相关的操作
只需为原子类型提供一个Bean组件,即可封装所有原子指令的业务逻辑
src/suite-vendor/test-party/modules/test-party/backend/src/bean/atom.party.js
- 1module.exports = app => {
- 2
- 3 const gPartyTypeEmojis = {
- 4 Birthday: '🎂',
- 5 Dance: '💃',
- 6 Garden: '🏡',
- 7 };
- 8
- 9 class Atom extends app.meta.AtomBase {
- 10
- 11 async create({ atomClass, item, user }) {
- 12 // super
- 13 const key = await super.create({ atomClass, item, user });
- 14 // add party
- 15 const res = await this.ctx.model.party.insert({
- 16 atomId: key.atomId,
- 17 });
- 18 return { atomId: key.atomId, itemId: res.insertId };
- 19 }
- 20
- 21 async read({ atomClass, options, key, user }) {
- 22 // super
- 23 const item = await super.read({ atomClass, options, key, user });
- 24 if (!item) return null;
- 25 // read
- 26 await this._getMeta(item, options);
- 27 // ok
- 28 return item;
- 29 }
- 30
- 31 async select({ atomClass, options, items, user }) {
- 32 // super
- 33 await super.select({ atomClass, options, items, user });
- 34 // select
- 35 for (const item of items) {
- 36 await this._getMeta(item, options);
- 37 }
- 38 }
- 39
- 40 async write({ atomClass, target, key, item, options, user }) {
- 41 // super
- 42 await super.write({ atomClass, target, key, item, options, user });
- 43 // update party
- 44 const data = await this.ctx.model.party.prepareData(item);
- 45 data.id = key.itemId;
- 46 await this.ctx.model.party.update(data);
- 47 }
- 48
- 49 async delete({ atomClass, key, user }) {
- 50 // delete party
- 51 await this.ctx.model.party.delete({
- 52 id: key.itemId,
- 53 });
- 54 // super
- 55 await super.delete({ atomClass, key, user });
- 56 }
- 57
- 58 async _getMeta(item, options) {
- 59 // layout: list/table/mobile/pc
- 60 const layout = options && options.layout;
- 61 // meta
- 62 const meta = this._ensureItemMeta(item);
- 63 // meta.flags
- 64 if (item.partyOver) {
- 65 meta.flags.push(this.ctx.text('PartyOverFlag'));
- 66 }
- 67 if (layout !== 'table' && item.personCount) {
- 68 meta.flags.push(item.personCount + 'P');
- 69 }
- 70 // meta.summary
- 71 if (item.partyTypeCode) {
- 72 const dictItem = await this.ctx.bean.dict.findItem({
- 73 dictKey: 'test-party:dictPartyType',
- 74 code: item.partyTypeCode,
- 75 });
- 76 meta.summary = `${dictItem.options.emoji}${dictItem.titleLocaleFull}`;
- 77 }
- 78 }
- 79
- 80 }
- 81
- 82 return Atom;
- 83};
名称 | 说明 |
---|---|
create | 新建party 时调用 |
read | 查询单条party 时调用 |
select | 查询多条party 时调用 |
write | 修改party 时调用 |
delete | 删除party 时调用 |
src/suite-vendor/test-party/modules/test-party/backend/src/beans.js
- 1const atomParty = require('./bean/atom.party.js');
- 2
- 3module.exports = app => {
- 4 const beans = {
- 5 // atom
- 6 'atom.party': {
- 7 mode: 'app',
- 8 bean: atomParty,
- 9 },
- 10 };
- 11 return beans;
- 12};
评论: