@nocobase/plugin-ui-templates 2.0.0-alpha.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +172 -0
- package/build.config.ts +12 -0
- package/client.js +1 -0
- package/dist/client/collections/flowModelTemplates.d.ts +67 -0
- package/dist/client/components/FlowModelTemplatesPage.d.ts +12 -0
- package/dist/client/components/TemplateSelectOption.d.ts +20 -0
- package/dist/client/constants.d.ts +9 -0
- package/dist/client/hooks/useFlowModelTemplateActions.d.ts +24 -0
- package/dist/client/index.d.ts +13 -0
- package/dist/client/index.js +10 -0
- package/dist/client/locale.d.ts +18 -0
- package/dist/client/menuExtensions.d.ts +9 -0
- package/dist/client/models/ReferenceBlockModel.d.ts +47 -0
- package/dist/client/models/ReferenceFormGridModel.d.ts +38 -0
- package/dist/client/models/SubModelTemplateImporterModel.d.ts +55 -0
- package/dist/client/models/referenceShared.d.ts +23 -0
- package/dist/client/openViewActionExtensions.d.ts +10 -0
- package/dist/client/schemas/flowModelTemplates.d.ts +11 -0
- package/dist/client/subModelMenuExtensions.d.ts +10 -0
- package/dist/client/utils/infiniteSelect.d.ts +28 -0
- package/dist/client/utils/refHost.d.ts +20 -0
- package/dist/client/utils/templateCompatibility.d.ts +91 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +42 -0
- package/dist/externalVersion.js +24 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +48 -0
- package/dist/locale/de-DE.json +14 -0
- package/dist/locale/en-US.json +72 -0
- package/dist/locale/es-ES.json +14 -0
- package/dist/locale/fr-FR.json +14 -0
- package/dist/locale/hu-HU.json +14 -0
- package/dist/locale/id-ID.json +14 -0
- package/dist/locale/it-IT.json +14 -0
- package/dist/locale/ja-JP.json +14 -0
- package/dist/locale/ko-KR.json +14 -0
- package/dist/locale/nl-NL.json +14 -0
- package/dist/locale/pt-BR.json +14 -0
- package/dist/locale/ru-RU.json +14 -0
- package/dist/locale/tr-TR.json +14 -0
- package/dist/locale/uk-UA.json +14 -0
- package/dist/locale/vi-VN.json +14 -0
- package/dist/locale/zh-CN.json +71 -0
- package/dist/locale/zh-TW.json +14 -0
- package/dist/server/collections/flowModelTemplateUsages.d.ts +11 -0
- package/dist/server/collections/flowModelTemplateUsages.js +71 -0
- package/dist/server/collections/flowModelTemplates.d.ts +11 -0
- package/dist/server/collections/flowModelTemplates.js +96 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +42 -0
- package/dist/server/plugin.d.ts +17 -0
- package/dist/server/plugin.js +242 -0
- package/dist/server/resources/flowModelTemplateUsages.d.ts +19 -0
- package/dist/server/resources/flowModelTemplateUsages.js +91 -0
- package/dist/server/resources/flowModelTemplates.d.ts +20 -0
- package/dist/server/resources/flowModelTemplates.js +267 -0
- package/package.json +37 -0
- package/server.js +1 -0
- package/src/client/__tests__/openViewActionExtensions.test.ts +1208 -0
- package/src/client/collections/flowModelTemplates.ts +131 -0
- package/src/client/components/FlowModelTemplatesPage.tsx +78 -0
- package/src/client/components/TemplateSelectOption.tsx +106 -0
- package/src/client/constants.ts +10 -0
- package/src/client/hooks/useFlowModelTemplateActions.tsx +137 -0
- package/src/client/index.ts +54 -0
- package/src/client/locale.ts +40 -0
- package/src/client/menuExtensions.tsx +1033 -0
- package/src/client/models/ReferenceBlockModel.tsx +793 -0
- package/src/client/models/ReferenceFormGridModel.tsx +302 -0
- package/src/client/models/SubModelTemplateImporterModel.tsx +634 -0
- package/src/client/models/__tests__/ReferenceBlockModel.test.tsx +482 -0
- package/src/client/models/__tests__/ReferenceFormGridModel.test.tsx +175 -0
- package/src/client/models/__tests__/SubModelTemplateImporterModel.test.ts +447 -0
- package/src/client/models/referenceShared.tsx +99 -0
- package/src/client/openViewActionExtensions.tsx +981 -0
- package/src/client/schemas/flowModelTemplates.ts +264 -0
- package/src/client/subModelMenuExtensions.ts +103 -0
- package/src/client/utils/infiniteSelect.ts +150 -0
- package/src/client/utils/refHost.ts +44 -0
- package/src/client/utils/templateCompatibility.ts +374 -0
- package/src/client.ts +10 -0
- package/src/index.ts +11 -0
- package/src/locale/de-DE.json +14 -0
- package/src/locale/en-US.json +72 -0
- package/src/locale/es-ES.json +14 -0
- package/src/locale/fr-FR.json +14 -0
- package/src/locale/hu-HU.json +14 -0
- package/src/locale/id-ID.json +14 -0
- package/src/locale/it-IT.json +14 -0
- package/src/locale/ja-JP.json +14 -0
- package/src/locale/ko-KR.json +14 -0
- package/src/locale/nl-NL.json +14 -0
- package/src/locale/pt-BR.json +14 -0
- package/src/locale/ru-RU.json +14 -0
- package/src/locale/tr-TR.json +14 -0
- package/src/locale/uk-UA.json +14 -0
- package/src/locale/vi-VN.json +14 -0
- package/src/locale/zh-CN.json +71 -0
- package/src/locale/zh-TW.json +14 -0
- package/src/server/__tests__/template-usage.test.ts +351 -0
- package/src/server/collections/flowModelTemplateUsages.ts +51 -0
- package/src/server/collections/flowModelTemplates.ts +76 -0
- package/src/server/index.ts +10 -0
- package/src/server/plugin.ts +236 -0
- package/src/server/resources/flowModelTemplateUsages.ts +61 -0
- package/src/server/resources/flowModelTemplates.ts +251 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Block UID": "Block UID",
|
|
3
|
+
"Block UID is already set and cannot be modified": "Block UID is already set and cannot be modified",
|
|
4
|
+
"Copy": "Copy",
|
|
5
|
+
"Other blocks": "Other blocks",
|
|
6
|
+
"Please configure target block": "Please configure target block",
|
|
7
|
+
"Reference": "Reference",
|
|
8
|
+
"Reference block": "Reference block",
|
|
9
|
+
"Reference mode": "Reference mode",
|
|
10
|
+
"Reference settings": "Reference settings",
|
|
11
|
+
"Some configurations using uid may need to be reconfigured": "Some configurations using uid may need to be reconfigured",
|
|
12
|
+
"Target UID": "Target UID",
|
|
13
|
+
"Target block is invalid": "Target block is invalid"
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Block UID": "Block UID",
|
|
3
|
+
"Block UID is already set and cannot be modified": "Block UID is already set and cannot be modified",
|
|
4
|
+
"Copy": "Copy",
|
|
5
|
+
"Other blocks": "Other blocks",
|
|
6
|
+
"Please configure target block": "Please configure target block",
|
|
7
|
+
"Reference": "Reference",
|
|
8
|
+
"Reference block": "Reference block",
|
|
9
|
+
"Reference mode": "Reference mode",
|
|
10
|
+
"Reference settings": "Reference settings",
|
|
11
|
+
"Some configurations using uid may need to be reconfigured": "Some configurations using uid may need to be reconfigured",
|
|
12
|
+
"Target UID": "Target UID",
|
|
13
|
+
"Target block is invalid": "Target block is invalid"
|
|
14
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"UI templates": "界面模板",
|
|
3
|
+
"Block templates (v2)": "区块模板 (v2)",
|
|
4
|
+
"Popup templates (v2)": "弹窗模板 (v2)",
|
|
5
|
+
"Actions": "操作",
|
|
6
|
+
"Are you sure you want to delete this item? This action cannot be undone.": "确定要删除此项吗?此操作不可撤销。",
|
|
7
|
+
"Block UID": "区块 UID",
|
|
8
|
+
"Block UID is already set and cannot be modified": "当前已经指定区块UID,不允许修改",
|
|
9
|
+
"Block template": "区块模板",
|
|
10
|
+
"Select block template": "选择区块模板",
|
|
11
|
+
"Block templates": "区块模板",
|
|
12
|
+
"Duplicate": "复制",
|
|
13
|
+
"Delete template": "删除模板",
|
|
14
|
+
"Deleted": "已删除",
|
|
15
|
+
"Edit": "编辑",
|
|
16
|
+
"Edit template": "编辑模板",
|
|
17
|
+
"Mode": "模式",
|
|
18
|
+
"Other blocks": "其他区块",
|
|
19
|
+
"Please configure target block": "请先配置目标区块",
|
|
20
|
+
"Reference": "引用",
|
|
21
|
+
"Saved": "已保存",
|
|
22
|
+
"Search templates": "搜索模板",
|
|
23
|
+
"Some configurations using uid may need to be reconfigured": "部分使用uid的配置可能需要重新配置",
|
|
24
|
+
"Target block is invalid": "目标区块无效",
|
|
25
|
+
"Template": "模板",
|
|
26
|
+
"Cannot resolve template parameter {{param}}": "缺少 {{param}},无法使用",
|
|
27
|
+
"Template description": "模板描述",
|
|
28
|
+
"Template name": "模板名称",
|
|
29
|
+
"Template name is required": "模板名称必填",
|
|
30
|
+
"Template settings": "模板配置",
|
|
31
|
+
"Template is in use and cannot be deleted": "当前模板正在被使用中,无法删除",
|
|
32
|
+
"Usage count": "引用数量",
|
|
33
|
+
"Using reference fields will remove existing fields": "使用引用字段会将当前已经添加的字段移除",
|
|
34
|
+
"This block already references field template, please convert fields to copy first": "当前区块已引用字段模板,请先将引用字段转换为复制",
|
|
35
|
+
"Convert reference to duplicate": "将引用转换为复制",
|
|
36
|
+
"Save as template": "保存为模板",
|
|
37
|
+
"Convert reference to duplicate is unavailable": "将引用转换为复制不可用",
|
|
38
|
+
"Are you sure to convert this template block to copy mode?": "确定将此模板区块转换为复制模式吗?",
|
|
39
|
+
"Convert reference fields to duplicate": "将引用字段转换为复制",
|
|
40
|
+
"Field template": "字段模板",
|
|
41
|
+
"Are you sure to convert referenced fields to copy mode?": "确定将引用字段转换为复制模式吗?",
|
|
42
|
+
"Template missing data source/collection info": "模板缺少数据源/数据表信息",
|
|
43
|
+
"Template collection mismatch": "数据表不匹配:当前为 \"{{expected}}\",模板适用于 \"{{actual}}\"",
|
|
44
|
+
"Template data source mismatch": "数据源不匹配:当前为 \"{{expected}}\",模板适用于 \"{{actual}}\"",
|
|
45
|
+
"No association": "(无)",
|
|
46
|
+
"Template association mismatch": "关联名不匹配:当前为 \"{{expected}}\",模板适用于 \"{{actual}}\"",
|
|
47
|
+
"Popup template": "弹窗模板",
|
|
48
|
+
"Popup templates": "弹窗模板",
|
|
49
|
+
"Select popup template": "选择弹窗模板",
|
|
50
|
+
"Replace current block with reference template": "用模板引用替换当前区块",
|
|
51
|
+
"Replace current popup with reference template": "用模板引用替换当前弹窗",
|
|
52
|
+
"Are you sure to convert this pop-up to copy mode?": "确定将此弹窗转换为复制模式吗?",
|
|
53
|
+
"This pop-up is not using a popup template": "当前弹窗未使用弹窗模板",
|
|
54
|
+
"Converted": "已转换",
|
|
55
|
+
"Only Popup can be saved as popup template": "仅支持将「弹窗」保存为弹窗模板",
|
|
56
|
+
"Popup template not found": "弹窗模板不存在",
|
|
57
|
+
"Failed to duplicate pop-up": "复制弹窗失败",
|
|
58
|
+
"Failed to copy popup from template": "从弹窗模板复制失败",
|
|
59
|
+
"Template field section description": "将使用指定区块模板的字段部分",
|
|
60
|
+
"Reference mode description": "修改区块所有引用该模板的地方都会被修改。",
|
|
61
|
+
"Duplicate mode description": "基于模板生成新内容,所有 UID(包括子节点的 UID)都会重新生成。涉及跨区块的 UID 引用在复制后将不再生效。",
|
|
62
|
+
"Save mode": "保存模式",
|
|
63
|
+
"Convert current block to template": "将当前区块转为模板",
|
|
64
|
+
"Convert current block to template description": "当前区块本身会变成模板引用",
|
|
65
|
+
"Duplicate current block as template": "复制当前区块为模板",
|
|
66
|
+
"Duplicate current block as template description": "保留原区块,复制一份作为模板",
|
|
67
|
+
"Convert current popup to template": "将当前弹窗转为模板",
|
|
68
|
+
"Convert current popup to template description": "当前弹窗本身会变成模板引用",
|
|
69
|
+
"Duplicate current popup as template": "复制当前弹窗为模板",
|
|
70
|
+
"Duplicate current popup as template description": "保留原弹窗,复制一份作为模板"
|
|
71
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Block UID": "Block UID",
|
|
3
|
+
"Block UID is already set and cannot be modified": "Block UID is already set and cannot be modified",
|
|
4
|
+
"Copy": "Copy",
|
|
5
|
+
"Other blocks": "Other blocks",
|
|
6
|
+
"Please configure target block": "Please configure target block",
|
|
7
|
+
"Reference": "Reference",
|
|
8
|
+
"Reference block": "Reference block",
|
|
9
|
+
"Reference mode": "Reference mode",
|
|
10
|
+
"Reference settings": "Reference settings",
|
|
11
|
+
"Some configurations using uid may need to be reconfigured": "Some configurations using uid may need to be reconfigured",
|
|
12
|
+
"Target UID": "Target UID",
|
|
13
|
+
"Target block is invalid": "Target block is invalid"
|
|
14
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createMockServer, MockServer } from '@nocobase/test';
|
|
11
|
+
import { vi } from 'vitest';
|
|
12
|
+
import FlowModelRepository from '../../../../plugin-flow-engine/src/server/repository';
|
|
13
|
+
|
|
14
|
+
describe('ui templates and usages', () => {
|
|
15
|
+
let app: MockServer | null;
|
|
16
|
+
let flowRepo: FlowModelRepository;
|
|
17
|
+
let usageRepo: any;
|
|
18
|
+
|
|
19
|
+
const buildOptions = (templateUid?: string, useTemplateExtra: Record<string, any> = {}) => {
|
|
20
|
+
return {
|
|
21
|
+
use: 'ReferenceBlockModel',
|
|
22
|
+
stepParams: {
|
|
23
|
+
referenceSettings: {
|
|
24
|
+
target: {
|
|
25
|
+
targetUid: 'target-block',
|
|
26
|
+
},
|
|
27
|
+
...(templateUid
|
|
28
|
+
? {
|
|
29
|
+
useTemplate: {
|
|
30
|
+
templateUid,
|
|
31
|
+
templateName: `name-${templateUid}`,
|
|
32
|
+
templateDescription: `desc-${templateUid}`,
|
|
33
|
+
...useTemplateExtra,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
: {}),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const createReferenceBlock = async (uid: string, templateUid: string) => {
|
|
43
|
+
return await flowRepo.create({
|
|
44
|
+
values: {
|
|
45
|
+
uid,
|
|
46
|
+
options: buildOptions(templateUid),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const updateReferenceBlockOptions = (uid: string, templateUid: string) => {
|
|
52
|
+
return flowRepo.update({
|
|
53
|
+
filter: { uid },
|
|
54
|
+
values: { options: buildOptions(templateUid) },
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const destroyBlock = (uid: string) => {
|
|
59
|
+
return flowRepo.destroy({
|
|
60
|
+
filter: { uid },
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const getBlockOptions = async (uid: string) => {
|
|
65
|
+
const record = await flowRepo.findOne({ filter: { uid } });
|
|
66
|
+
return FlowModelRepository.optionsToJson(record?.get('options'));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const countUsage = (filter: Record<string, any>) => {
|
|
70
|
+
return usageRepo.count({ filter });
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
beforeEach(async () => {
|
|
74
|
+
app = await createMockServer({
|
|
75
|
+
plugins: ['flow-engine', 'ui-templates'],
|
|
76
|
+
registerActions: true,
|
|
77
|
+
});
|
|
78
|
+
expect(app.pm.get('ui-templates')).toBeTruthy();
|
|
79
|
+
flowRepo = app.db.getRepository('flowModels') as FlowModelRepository;
|
|
80
|
+
usageRepo = app.db.getRepository('flowModelTemplateUsages');
|
|
81
|
+
await flowRepo.create({ values: { uid: 'target-block', options: { use: 'TargetBlock' } } });
|
|
82
|
+
expect(app.db.listeners('flowModels.afterSaveWithAssociations').length).toBeGreaterThan(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
afterEach(async () => {
|
|
86
|
+
vi.restoreAllMocks();
|
|
87
|
+
if (app) {
|
|
88
|
+
await app.destroy();
|
|
89
|
+
app = null;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should create usage and switch template updates usage records', async () => {
|
|
94
|
+
const block = await createReferenceBlock('ref-1', 'tpl-1');
|
|
95
|
+
expect(await countUsage({ templateUid: 'tpl-1', modelUid: 'ref-1' })).toBe(1);
|
|
96
|
+
|
|
97
|
+
await updateReferenceBlockOptions(block.get('uid'), 'tpl-2');
|
|
98
|
+
expect(await countUsage({ templateUid: 'tpl-1', modelUid: 'ref-1' })).toBe(0);
|
|
99
|
+
expect(await countUsage({ templateUid: 'tpl-2', modelUid: 'ref-1' })).toBe(1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should remove usages when reference block is destroyed', async () => {
|
|
103
|
+
const block = await createReferenceBlock('ref-3', 'tpl-destroy');
|
|
104
|
+
expect(await countUsage({ modelUid: 'ref-3' })).toBe(1);
|
|
105
|
+
await destroyBlock(block.get('uid'));
|
|
106
|
+
expect(await countUsage({ modelUid: 'ref-3' })).toBe(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should return usageCount and block deletion guard via resources', async () => {
|
|
110
|
+
const agent = app.agent();
|
|
111
|
+
const tplResp = await agent.resource('flowModelTemplates').create({
|
|
112
|
+
values: {
|
|
113
|
+
uid: 'tpl-resource',
|
|
114
|
+
name: 'Template Resource',
|
|
115
|
+
targetUid: 'target-block',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
expect(tplResp.status).toBe(200);
|
|
119
|
+
const createdTpl = tplResp.body?.data || tplResp.body;
|
|
120
|
+
expect(createdTpl?.usageCount).toBe(0);
|
|
121
|
+
|
|
122
|
+
const block = await createReferenceBlock('ref-4', 'tpl-resource');
|
|
123
|
+
expect(block).toBeTruthy();
|
|
124
|
+
expect(await countUsage({ templateUid: 'tpl-resource', modelUid: 'ref-4' })).toBe(1);
|
|
125
|
+
|
|
126
|
+
const getResp = await agent.resource('flowModelTemplates').get({
|
|
127
|
+
filterByTk: 'tpl-resource',
|
|
128
|
+
});
|
|
129
|
+
expect(getResp.status).toBe(200);
|
|
130
|
+
const gotTpl = getResp.body?.data || getResp.body;
|
|
131
|
+
expect(gotTpl?.usageCount).toBe(1);
|
|
132
|
+
|
|
133
|
+
const destroyResp = await agent.resource('flowModelTemplates').destroy({
|
|
134
|
+
filterByTk: 'tpl-resource',
|
|
135
|
+
});
|
|
136
|
+
expect(destroyResp.status).not.toBe(200);
|
|
137
|
+
const blockedTpl = await agent.resource('flowModelTemplates').get({
|
|
138
|
+
filterByTk: 'tpl-resource',
|
|
139
|
+
});
|
|
140
|
+
const blockedTplData = blockedTpl.body?.data || blockedTpl.body;
|
|
141
|
+
expect(blockedTplData?.uid).toBe('tpl-resource');
|
|
142
|
+
expect(blockedTplData?.usageCount).toBe(1);
|
|
143
|
+
|
|
144
|
+
await destroyBlock(block.get('uid'));
|
|
145
|
+
expect(await countUsage({ templateUid: 'tpl-resource', modelUid: 'ref-4' })).toBe(0);
|
|
146
|
+
|
|
147
|
+
const destroyResp2 = await agent.resource('flowModelTemplates').destroy({
|
|
148
|
+
filterByTk: 'tpl-resource',
|
|
149
|
+
});
|
|
150
|
+
expect(destroyResp2.status).toBe(200);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should sync template name/description to reference blocks on template update', async () => {
|
|
154
|
+
const agent = app.agent();
|
|
155
|
+
await agent.resource('flowModelTemplates').create({
|
|
156
|
+
values: {
|
|
157
|
+
uid: 'tpl-sync',
|
|
158
|
+
name: 'Template Old',
|
|
159
|
+
description: 'Desc Old',
|
|
160
|
+
targetUid: 'target-block',
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await createReferenceBlock('ref-sync', 'tpl-sync');
|
|
165
|
+
|
|
166
|
+
const updateResp = await agent.resource('flowModelTemplates').update({
|
|
167
|
+
filterByTk: 'tpl-sync',
|
|
168
|
+
values: {
|
|
169
|
+
name: 'Template New',
|
|
170
|
+
description: 'Desc New',
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
expect(updateResp.status).toBe(200);
|
|
174
|
+
|
|
175
|
+
const options = await getBlockOptions('ref-sync');
|
|
176
|
+
const useTemplate = options?.stepParams?.referenceSettings?.useTemplate;
|
|
177
|
+
expect(useTemplate?.templateUid).toBe('tpl-sync');
|
|
178
|
+
expect(useTemplate?.templateName).toBe('Template New');
|
|
179
|
+
expect(useTemplate?.templateDescription).toBe('Desc New');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should persist associationName in flowModelTemplates', async () => {
|
|
183
|
+
const agent = app.agent();
|
|
184
|
+
const tplResp = await agent.resource('flowModelTemplates').create({
|
|
185
|
+
values: {
|
|
186
|
+
uid: 'tpl-assoc',
|
|
187
|
+
name: 'Template Assoc',
|
|
188
|
+
targetUid: 'target-block',
|
|
189
|
+
dataSourceKey: 'main',
|
|
190
|
+
collectionName: 'bad',
|
|
191
|
+
associationName: 'users.roles',
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
expect(tplResp.status).toBe(200);
|
|
195
|
+
const createdTpl = tplResp.body?.data || tplResp.body;
|
|
196
|
+
expect(createdTpl?.associationName).toBe('users.roles');
|
|
197
|
+
|
|
198
|
+
const getResp = await agent.resource('flowModelTemplates').get({
|
|
199
|
+
filterByTk: 'tpl-assoc',
|
|
200
|
+
});
|
|
201
|
+
expect(getResp.status).toBe(200);
|
|
202
|
+
const gotTpl = getResp.body?.data || getResp.body;
|
|
203
|
+
expect(gotTpl?.associationName).toBe('users.roles');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should create usage for ReferenceFormGridModel', async () => {
|
|
207
|
+
const agent = app.agent();
|
|
208
|
+
await agent.resource('flowModelTemplates').create({
|
|
209
|
+
values: {
|
|
210
|
+
uid: 'tpl-sub',
|
|
211
|
+
name: 'Template Sub',
|
|
212
|
+
targetUid: 'target-block',
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await flowRepo.create({
|
|
217
|
+
values: {
|
|
218
|
+
uid: 'block-sub',
|
|
219
|
+
options: {
|
|
220
|
+
use: 'FormBlockModel',
|
|
221
|
+
stepParams: {},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await flowRepo.create({
|
|
227
|
+
values: {
|
|
228
|
+
uid: 'grid-sub',
|
|
229
|
+
options: {
|
|
230
|
+
use: 'ReferenceFormGridModel',
|
|
231
|
+
parentId: 'block-sub',
|
|
232
|
+
subKey: 'grid',
|
|
233
|
+
subType: 'object',
|
|
234
|
+
stepParams: {
|
|
235
|
+
referenceSettings: {
|
|
236
|
+
useTemplate: {
|
|
237
|
+
templateUid: 'tpl-sub',
|
|
238
|
+
templateName: 'Template Sub',
|
|
239
|
+
targetUid: 'target-block',
|
|
240
|
+
targetPath: 'subModels.grid',
|
|
241
|
+
mode: 'reference',
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expect(await countUsage({ templateUid: 'tpl-sub', modelUid: 'grid-sub' })).toBe(1);
|
|
250
|
+
|
|
251
|
+
await flowRepo.update({
|
|
252
|
+
filter: { uid: 'grid-sub' },
|
|
253
|
+
values: {
|
|
254
|
+
options: {
|
|
255
|
+
use: 'ReferenceFormGridModel',
|
|
256
|
+
parentId: 'block-sub',
|
|
257
|
+
subKey: 'grid',
|
|
258
|
+
subType: 'object',
|
|
259
|
+
stepParams: {},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
expect(await countUsage({ modelUid: 'grid-sub' })).toBe(0);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should maintain usages via flowModels:save/destroy actions', async () => {
|
|
268
|
+
const agent = app.agent();
|
|
269
|
+
await agent.resource('flowModelTemplates').create({
|
|
270
|
+
values: {
|
|
271
|
+
uid: 'tpl-save',
|
|
272
|
+
name: 'Template Save',
|
|
273
|
+
targetUid: 'target-block',
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
await agent.resource('flowModelTemplates').create({
|
|
277
|
+
values: {
|
|
278
|
+
uid: 'tpl-save-2',
|
|
279
|
+
name: 'Template Save 2',
|
|
280
|
+
targetUid: 'target-block',
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const saveResp = await agent.resource('flowModels').save({
|
|
285
|
+
values: {
|
|
286
|
+
uid: 'ref-save',
|
|
287
|
+
...buildOptions('tpl-save'),
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
expect(saveResp.status).toBe(200);
|
|
291
|
+
expect(await countUsage({ templateUid: 'tpl-save', modelUid: 'ref-save' })).toBe(1);
|
|
292
|
+
|
|
293
|
+
const listResp = await agent.resource('flowModelTemplates').list();
|
|
294
|
+
expect(listResp.status).toBe(200);
|
|
295
|
+
const listData = listResp.body?.data ?? listResp.body;
|
|
296
|
+
const rows = Array.isArray(listData) ? listData : Array.isArray(listData?.rows) ? listData.rows : [];
|
|
297
|
+
const row = (rows as Array<{ uid?: string; usageCount?: number }>).find((r) => r?.uid === 'tpl-save');
|
|
298
|
+
expect(row?.usageCount).toBe(1);
|
|
299
|
+
|
|
300
|
+
const saveResp2 = await agent.resource('flowModels').save({
|
|
301
|
+
values: {
|
|
302
|
+
uid: 'ref-save',
|
|
303
|
+
...buildOptions('tpl-save-2'),
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
expect(saveResp2.status).toBe(200);
|
|
307
|
+
expect(await countUsage({ templateUid: 'tpl-save', modelUid: 'ref-save' })).toBe(0);
|
|
308
|
+
expect(await countUsage({ templateUid: 'tpl-save-2', modelUid: 'ref-save' })).toBe(1);
|
|
309
|
+
|
|
310
|
+
// uid 省略时后端应补齐,并能正确维护 usages
|
|
311
|
+
const saveResp3 = await agent.resource('flowModels').save({
|
|
312
|
+
values: {
|
|
313
|
+
...buildOptions('tpl-save'),
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
expect(saveResp3.status).toBe(200);
|
|
317
|
+
const createdUid = saveResp3.body?.data ?? saveResp3.body;
|
|
318
|
+
expect(typeof createdUid).toBe('string');
|
|
319
|
+
expect(await countUsage({ templateUid: 'tpl-save', modelUid: createdUid })).toBe(1);
|
|
320
|
+
|
|
321
|
+
// openView 的 step key 不一定是 "openView",应能扫描 popupSettings 下所有 stepParams
|
|
322
|
+
const saveResp4 = await agent.resource('flowModels').save({
|
|
323
|
+
values: {
|
|
324
|
+
uid: 'popup-save',
|
|
325
|
+
use: 'PopupActionModel',
|
|
326
|
+
stepParams: {
|
|
327
|
+
popupSettings: {
|
|
328
|
+
openViewX: {
|
|
329
|
+
popupTemplateUid: 'tpl-save',
|
|
330
|
+
popupTemplateMode: 'reference',
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
expect(saveResp4.status).toBe(200);
|
|
337
|
+
expect(await countUsage({ templateUid: 'tpl-save', modelUid: 'popup-save' })).toBe(1);
|
|
338
|
+
|
|
339
|
+
const destroyResp = await agent.resource('flowModels').destroy({ filterByTk: 'ref-save' });
|
|
340
|
+
expect(destroyResp.status).toBe(200);
|
|
341
|
+
expect(await countUsage({ modelUid: 'ref-save' })).toBe(0);
|
|
342
|
+
|
|
343
|
+
const destroyResp2 = await agent.resource('flowModels').destroy({ filterByTk: createdUid });
|
|
344
|
+
expect(destroyResp2.status).toBe(200);
|
|
345
|
+
expect(await countUsage({ modelUid: createdUid })).toBe(0);
|
|
346
|
+
|
|
347
|
+
const destroyResp3 = await agent.resource('flowModels').destroy({ filterByTk: 'popup-save' });
|
|
348
|
+
expect(destroyResp3.status).toBe(200);
|
|
349
|
+
expect(await countUsage({ modelUid: 'popup-save' })).toBe(0);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { CollectionOptions } from '@nocobase/database';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
dumpRules: 'required',
|
|
14
|
+
migrationRules: ['overwrite', 'schema-only'],
|
|
15
|
+
name: 'flowModelTemplateUsages',
|
|
16
|
+
autoGenId: false,
|
|
17
|
+
timestamps: true,
|
|
18
|
+
fields: [
|
|
19
|
+
{
|
|
20
|
+
type: 'uid',
|
|
21
|
+
name: 'uid',
|
|
22
|
+
primaryKey: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: 'string',
|
|
26
|
+
name: 'templateUid',
|
|
27
|
+
allowNull: false,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'string',
|
|
31
|
+
name: 'modelUid',
|
|
32
|
+
allowNull: false,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
indexes: [
|
|
36
|
+
{
|
|
37
|
+
fields: ['uid'],
|
|
38
|
+
unique: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
fields: ['templateUid', 'modelUid'],
|
|
42
|
+
unique: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
fields: ['templateUid'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
fields: ['modelUid'],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
} as CollectionOptions;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { CollectionOptions } from '@nocobase/database';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
dumpRules: 'required',
|
|
14
|
+
migrationRules: ['overwrite', 'schema-only'],
|
|
15
|
+
name: 'flowModelTemplates',
|
|
16
|
+
autoGenId: false,
|
|
17
|
+
timestamps: true,
|
|
18
|
+
fields: [
|
|
19
|
+
{
|
|
20
|
+
type: 'uid',
|
|
21
|
+
name: 'uid',
|
|
22
|
+
primaryKey: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: 'string',
|
|
26
|
+
name: 'name',
|
|
27
|
+
allowNull: false,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'text',
|
|
31
|
+
name: 'description',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: 'string',
|
|
35
|
+
name: 'targetUid',
|
|
36
|
+
allowNull: false,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: 'string',
|
|
40
|
+
name: 'useModel',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'string',
|
|
44
|
+
name: 'type',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: 'string',
|
|
48
|
+
name: 'dataSourceKey',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'string',
|
|
52
|
+
name: 'collectionName',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'string',
|
|
56
|
+
name: 'associationName',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'string',
|
|
60
|
+
name: 'filterByTk',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'string',
|
|
64
|
+
name: 'sourceId',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
indexes: [
|
|
68
|
+
{
|
|
69
|
+
fields: ['uid'],
|
|
70
|
+
unique: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
fields: ['targetUid'],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
} as CollectionOptions;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { default } from './plugin';
|