mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
fix: flow model repository
This commit is contained in:
parent
f4a92e4959
commit
724b5e9e62
@ -9,6 +9,7 @@
|
||||
|
||||
import { FlowModel, IFlowModelRepository } from '@nocobase/flow-engine';
|
||||
import _ from 'lodash';
|
||||
import { Application } from '../application';
|
||||
|
||||
export class MockFlowModelRepository implements IFlowModelRepository<FlowModel> {
|
||||
get models() {
|
||||
@ -99,3 +100,32 @@ export class MockFlowModelRepository implements IFlowModelRepository<FlowModel>
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class FlowModelRepository implements IFlowModelRepository<FlowModel> {
|
||||
constructor(private app: Application) {}
|
||||
async findOne(query) {
|
||||
const response = await this.app.apiClient.request({
|
||||
url: 'flowModels:findOne',
|
||||
params: _.pick(query, ['uid', 'parentId']),
|
||||
});
|
||||
return response.data?.data;
|
||||
}
|
||||
|
||||
async save(model: FlowModel) {
|
||||
const response = await this.app.apiClient.request({
|
||||
method: 'POST',
|
||||
url: 'flowModels:save',
|
||||
data: model.serialize(),
|
||||
});
|
||||
return response.data?.data;
|
||||
}
|
||||
|
||||
async destroy(uid: string) {
|
||||
await this.app.apiClient.request({
|
||||
method: 'POST',
|
||||
url: 'flowModels:destroy',
|
||||
params: { filterByTk: uid },
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ function InternalFlowPage({ uid, sharedContext }) {
|
||||
|
||||
export const FlowRoute = () => {
|
||||
const params = useParams();
|
||||
return <FlowPage uid={params.name} />;
|
||||
return <FlowPage uid={`r_${params.name}`} />;
|
||||
};
|
||||
|
||||
export const FlowPage = (props) => {
|
||||
|
@ -12,7 +12,7 @@ import _ from 'lodash';
|
||||
import { Plugin } from '../application/Plugin';
|
||||
import * as actions from './actions';
|
||||
import { FlowEngineRunner } from './FlowEngineRunner';
|
||||
import { MockFlowModelRepository } from './FlowModelRepository';
|
||||
import { FlowModelRepository, MockFlowModelRepository } from './FlowModelRepository';
|
||||
import { FlowRoute } from './FlowPage';
|
||||
import { DateTimeFormat } from './flowSetting/DateTimeFormat';
|
||||
import * as models from './models';
|
||||
@ -20,7 +20,8 @@ import * as models from './models';
|
||||
export class PluginFlowEngine extends Plugin {
|
||||
async load() {
|
||||
this.app.addComponents({ FlowRoute });
|
||||
this.app.flowEngine.setModelRepository(new MockFlowModelRepository());
|
||||
// this.app.flowEngine.setModelRepository(new MockFlowModelRepository());
|
||||
this.app.flowEngine.setModelRepository(new FlowModelRepository(this.app));
|
||||
const filteredModels = Object.fromEntries(
|
||||
Object.entries(models).filter(
|
||||
([, ModelClass]) => typeof ModelClass === 'function' && ModelClass.prototype instanceof FlowModel,
|
||||
|
@ -0,0 +1,386 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Collection, Database } from '@nocobase/database';
|
||||
import { MockServer, createMockServer } from '@nocobase/test';
|
||||
import subModel from 'packages/core/client/docs/zh-CN/core/flow-models/demos/sub-model';
|
||||
import { SchemaNode } from '../dao/ui_schema_node_dao';
|
||||
import UiSchemaRepository from '../repository';
|
||||
|
||||
describe('ui_schema repository', () => {
|
||||
let app: MockServer;
|
||||
let db: Database;
|
||||
let repository: UiSchemaRepository;
|
||||
|
||||
let treePathCollection: Collection;
|
||||
|
||||
afterEach(async () => {
|
||||
await app.destroy();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await createMockServer({
|
||||
registerActions: true,
|
||||
plugins: ['ui-schema-storage'],
|
||||
});
|
||||
|
||||
db = app.db;
|
||||
repository = db.getCollection('uiSchemas').repository as UiSchemaRepository;
|
||||
treePathCollection = db.getCollection('uiSchemaTreePath');
|
||||
});
|
||||
|
||||
it('should insert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
};
|
||||
const model2 = await repository.insertModel(model1);
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
});
|
||||
|
||||
it('should insert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
uid: 'sub1',
|
||||
use: 'TestSubModel',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
uid: 'sub2-1',
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
uid: 'sub2-2',
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const model2 = await repository.insertModel(model1);
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).toBeDefined();
|
||||
expect(model2.subModels.sub1.use).toBe('TestSubModel');
|
||||
expect(model2.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub2.length).toBe(2);
|
||||
expect(model2.subModels.sub2[0].use).toBe('TestSubModel2');
|
||||
expect(model2.subModels.sub2[1].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub2[0].uid).toBe('sub2-1');
|
||||
expect(model2.subModels.sub2[1].uid).toBe('sub2-2');
|
||||
});
|
||||
|
||||
it('should insert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
uid: 'sub1',
|
||||
use: 'TestSubModel',
|
||||
subModels: {
|
||||
sub2: [
|
||||
{
|
||||
uid: 'sub2-1',
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
uid: 'sub2-2',
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
sub3: {
|
||||
uid: 'sub3',
|
||||
use: 'TestSubModel4',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const model2 = await repository.insertModel(model1);
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).toBeDefined();
|
||||
expect(model2.subModels.sub1.uid).toBe('sub1');
|
||||
expect(model2.subModels.sub1.use).toBe('TestSubModel');
|
||||
expect(model2.subModels.sub1.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub1.subModels.sub2.length).toBe(2);
|
||||
expect(model2.subModels.sub1.subModels.sub2[0].uid).toBe('sub2-1');
|
||||
expect(model2.subModels.sub1.subModels.sub2[0].use).toBe('TestSubModel2');
|
||||
expect(model2.subModels.sub1.subModels.sub2[1].uid).toBe('sub2-2');
|
||||
expect(model2.subModels.sub1.subModels.sub2[1].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub1.subModels.sub3).toBeDefined();
|
||||
expect(model2.subModels.sub1.subModels.sub3.uid).toBe('sub3');
|
||||
expect(model2.subModels.sub1.subModels.sub3.use).toBe('TestSubModel4');
|
||||
});
|
||||
|
||||
it('should insert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
use: 'TestSubModel',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const model2 = await repository.insertModel(model1);
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).toBeDefined();
|
||||
expect(model2.subModels.sub1.use).toBe('TestSubModel');
|
||||
expect(model2.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub2.length).toBe(2);
|
||||
expect(model2.subModels.sub2[0].use).toBe('TestSubModel2');
|
||||
expect(model2.subModels.sub2[1].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub2[0].uid).toBeDefined();
|
||||
expect(model2.subModels.sub2[1].uid).toBeDefined();
|
||||
});
|
||||
|
||||
it('should insert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
async: true, // 模拟异步加载
|
||||
use: 'TestSubModel',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
async: true, // 模拟异步加载
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const model2 = await repository.insertModel(model1);
|
||||
console.log(JSON.stringify(model2, null, 2));
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).not.toBeDefined();
|
||||
expect(model2.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub2.length).toBe(1);
|
||||
expect(model2.subModels.sub2[0].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub2[0].uid).toBeDefined();
|
||||
});
|
||||
|
||||
it('findModelById includeAsyncNode', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
async: true, // 模拟异步加载
|
||||
use: 'TestSubModel',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
async: true, // 模拟异步加载
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
await repository.insertModel(model1);
|
||||
const model2 = await repository.findModelById('uid1', { includeAsyncNode: true });
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).toBeDefined();
|
||||
expect(model2.subModels.sub1.use).toBe('TestSubModel');
|
||||
expect(model2.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub2.length).toBe(2);
|
||||
expect(model2.subModels.sub2[0].use).toBe('TestSubModel2');
|
||||
expect(model2.subModels.sub2[1].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub2[0].uid).toBeDefined();
|
||||
expect(model2.subModels.sub2[1].uid).toBeDefined();
|
||||
});
|
||||
|
||||
it('should upsert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
uid: 'sub1',
|
||||
use: 'TestSubModel',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
uid: 'sub2-1',
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
uid: 'sub2-2',
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const uid = await repository.upsertModel(model1);
|
||||
expect(uid).toBe('uid1');
|
||||
const model2 = await repository.findModelById('uid1');
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).toBeDefined();
|
||||
expect(model2.subModels.sub1.use).toBe('TestSubModel');
|
||||
expect(model2.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub2.length).toBe(2);
|
||||
expect(model2.subModels.sub2[0].use).toBe('TestSubModel2');
|
||||
expect(model2.subModels.sub2[1].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub2[0].uid).toBe('sub2-1');
|
||||
expect(model2.subModels.sub2[1].uid).toBe('sub2-2');
|
||||
const model3 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel_1',
|
||||
subModels: {
|
||||
sub1: {
|
||||
uid: 'sub1',
|
||||
use: 'TestSubModel_1',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
uid: 'sub2-1',
|
||||
use: 'TestSubModel2_1',
|
||||
},
|
||||
{
|
||||
uid: 'sub2-2',
|
||||
use: 'TestSubModel3_1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
await repository.upsertModel(model3);
|
||||
const model4 = await repository.findModelById('uid1');
|
||||
expect(model4).toBeDefined();
|
||||
expect(model4.uid).toBe('uid1');
|
||||
expect(model4.use).toBe('TestModel_1');
|
||||
expect(model4.subModels).toBeDefined();
|
||||
expect(model4.subModels.sub1).toBeDefined();
|
||||
expect(model4.subModels.sub1.use).toBe('TestSubModel_1');
|
||||
expect(model4.subModels.sub2).toBeDefined();
|
||||
expect(model4.subModels.sub2.length).toBe(2);
|
||||
expect(model4.subModels.sub2[0].use).toBe('TestSubModel2_1');
|
||||
expect(model4.subModels.sub2[1].use).toBe('TestSubModel3_1');
|
||||
expect(model4.subModels.sub2[0].uid).toBe('sub2-1');
|
||||
expect(model4.subModels.sub2[1].uid).toBe('sub2-2');
|
||||
await repository.upsertModel({
|
||||
uid: 'sub2-2',
|
||||
use: 'TestSubModel3_2',
|
||||
});
|
||||
const model5 = await repository.findModelById('uid1');
|
||||
expect(model5.subModels.sub2[1].use).toBe('TestSubModel3_2');
|
||||
});
|
||||
|
||||
it('should upsert model', async () => {
|
||||
const model1 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel',
|
||||
subModels: {
|
||||
sub1: {
|
||||
uid: 'sub1',
|
||||
use: 'TestSubModel',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
uid: 'sub2-1',
|
||||
use: 'TestSubModel2',
|
||||
},
|
||||
{
|
||||
uid: 'sub2-2',
|
||||
use: 'TestSubModel3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const uid = await repository.upsertModel(model1);
|
||||
expect(uid).toBe('uid1');
|
||||
const model2 = await repository.findModelById('uid1');
|
||||
expect(model2).toBeDefined();
|
||||
expect(model2.uid).toBe('uid1');
|
||||
expect(model2.use).toBe('TestModel');
|
||||
expect(model2.subModels).toBeDefined();
|
||||
expect(model2.subModels.sub1).toBeDefined();
|
||||
expect(model2.subModels.sub1.use).toBe('TestSubModel');
|
||||
expect(model2.subModels.sub2).toBeDefined();
|
||||
expect(model2.subModels.sub2.length).toBe(2);
|
||||
expect(model2.subModels.sub2[0].use).toBe('TestSubModel2');
|
||||
expect(model2.subModels.sub2[1].use).toBe('TestSubModel3');
|
||||
expect(model2.subModels.sub2[0].uid).toBe('sub2-1');
|
||||
expect(model2.subModels.sub2[1].uid).toBe('sub2-2');
|
||||
const model3 = {
|
||||
uid: 'uid1',
|
||||
use: 'TestModel_1',
|
||||
subModels: {
|
||||
sub1: {
|
||||
uid: 'sub1',
|
||||
use: 'TestSubModel_1',
|
||||
},
|
||||
sub2: [
|
||||
{
|
||||
uid: 'sub2-1',
|
||||
use: 'TestSubModel2_1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
await repository.upsertModel(model3);
|
||||
const model4 = await repository.findModelById('uid1');
|
||||
expect(model4).toBeDefined();
|
||||
expect(model4.uid).toBe('uid1');
|
||||
expect(model4.use).toBe('TestModel_1');
|
||||
expect(model4.subModels).toBeDefined();
|
||||
expect(model4.subModels.sub1).toBeDefined();
|
||||
expect(model4.subModels.sub1.use).toBe('TestSubModel_1');
|
||||
expect(model4.subModels.sub2).toBeDefined();
|
||||
expect(model4.subModels.sub2.length).toBe(2);
|
||||
expect(model4.subModels.sub2[0].use).toBe('TestSubModel2_1');
|
||||
expect(model4.subModels.sub2[0].uid).toBe('sub2-1');
|
||||
await repository.upsertModel({
|
||||
uid: 'sub2-2',
|
||||
parentId: 'uid1',
|
||||
subType: 'array',
|
||||
subKey: 'sub2',
|
||||
use: 'TestSubModel3_1',
|
||||
});
|
||||
const model5 = await repository.findModelById('uid1');
|
||||
expect(model5.subModels.sub2[1].use).toBe('TestSubModel3_1');
|
||||
});
|
||||
});
|
@ -39,7 +39,7 @@ interface InsertAdjacentOptions extends removeParentOptions {
|
||||
|
||||
const nodeKeys = ['properties', 'definitions', 'patternProperties', 'additionalProperties', 'items'];
|
||||
|
||||
function transaction(transactionAbleArgPosition?: number) {
|
||||
export function transaction(transactionAbleArgPosition?: number) {
|
||||
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
@ -1112,7 +1112,7 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
|
||||
return lodash.pick(schema, ['type', 'properties']);
|
||||
}
|
||||
|
||||
private async doGetJsonSchema(uid: string, options?: GetJsonSchemaOptions) {
|
||||
async findNodesById(uid: string, options?: GetJsonSchemaOptions) {
|
||||
const db = this.database;
|
||||
|
||||
const treeTable = this.uiSchemaTreePathTableName;
|
||||
@ -1138,10 +1138,15 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
|
||||
});
|
||||
|
||||
if (nodes[0].length == 0) {
|
||||
return {};
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.nodesToSchema(nodes[0], uid);
|
||||
return nodes[0];
|
||||
}
|
||||
|
||||
private async doGetJsonSchema(uid: string, options?: GetJsonSchemaOptions) {
|
||||
const nodes = await this.findNodesById(uid, options);
|
||||
return this.nodesToSchema(nodes, uid);
|
||||
}
|
||||
|
||||
private ignoreSchemaProperties(schemaProperties) {
|
||||
@ -1223,6 +1228,173 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
|
||||
|
||||
return { uid, name, async, childOptions };
|
||||
}
|
||||
|
||||
static modelToSingleNodes(model, parentChildOptions = null): SchemaNode[] {
|
||||
const { uid: oldUid, async, subModels, ...rest } = _.cloneDeep(model);
|
||||
const currentUid = oldUid || uid();
|
||||
const node = {
|
||||
'x-uid': currentUid,
|
||||
'x-async': async || false,
|
||||
name: currentUid,
|
||||
...rest,
|
||||
};
|
||||
if (parentChildOptions) {
|
||||
node.childOptions = parentChildOptions;
|
||||
}
|
||||
const nodes = [node];
|
||||
if (Object.keys(subModels || {}).length > 0) {
|
||||
for (const [subKey, subItems] of Object.entries(subModels)) {
|
||||
const items = _.castArray<any>(subItems);
|
||||
let sort = 0;
|
||||
for (const item of items) {
|
||||
item.subKey = subKey;
|
||||
item.subType = Array.isArray(subItems) ? 'array' : 'object';
|
||||
const childOptions = {
|
||||
parentUid: currentUid,
|
||||
parentPath: [currentUid, ...(parentChildOptions?.parentPath || [])].filter(Boolean),
|
||||
type: 'properties',
|
||||
sort: ++sort,
|
||||
};
|
||||
const children = this.modelToSingleNodes(item, childOptions);
|
||||
nodes.push(...children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static nodeToModel(node) {
|
||||
const { 'x-uid': uid, name, schema } = node;
|
||||
const model = {
|
||||
uid,
|
||||
...schema,
|
||||
};
|
||||
return model;
|
||||
}
|
||||
|
||||
static nodesToModel(nodes: any[], rootUid: string) {
|
||||
// 1. 建立 uid 到 node 的映射
|
||||
const nodeMap = new Map<string, any>();
|
||||
for (const node of nodes) {
|
||||
nodeMap.set(node['x-uid'], node);
|
||||
}
|
||||
|
||||
// 2. 找到 root 节点
|
||||
const rootNode = nodeMap.get(rootUid);
|
||||
if (!rootNode) return null;
|
||||
|
||||
// 3. 找到所有子节点
|
||||
const children = nodes.filter((n) => n.parent === rootUid);
|
||||
|
||||
// 4. 按 subKey 分组并递归
|
||||
const subModels: Record<string, any> = {};
|
||||
|
||||
for (const child of children) {
|
||||
const { subKey, subType } = child.schema;
|
||||
if (!subKey) continue;
|
||||
// 递归处理子节点
|
||||
const model = UiSchemaRepository.nodesToModel(nodes, child['x-uid']) || {
|
||||
uid: child['x-uid'],
|
||||
...child.schema,
|
||||
sortIndex: child.sort,
|
||||
};
|
||||
// 保证 sortIndex
|
||||
model.sortIndex = child.sort;
|
||||
if (subType === 'array') {
|
||||
if (!subModels[subKey]) subModels[subKey] = [];
|
||||
subModels[subKey].push(model);
|
||||
} else {
|
||||
subModels[subKey] = model;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 对数组类型的 subModels 排序
|
||||
for (const key in subModels) {
|
||||
if (Array.isArray(subModels[key])) {
|
||||
subModels[key].sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 过滤掉空对象 subModels
|
||||
const filteredSubModels: Record<string, any> = {};
|
||||
for (const key in subModels) {
|
||||
const value = subModels[key];
|
||||
if (Array.isArray(value) && value.length === 0) continue;
|
||||
if (!Array.isArray(value) && typeof value === 'object' && value !== null && Object.keys(value).length === 0)
|
||||
continue;
|
||||
filteredSubModels[key] = value;
|
||||
}
|
||||
|
||||
// 7. 返回最终 model
|
||||
return {
|
||||
uid: rootNode['x-uid'],
|
||||
...rootNode.schema,
|
||||
...(Object.keys(filteredSubModels).length > 0 ? { subModels: filteredSubModels } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@transaction()
|
||||
async insertModel(model: any, options?: Transactionable) {
|
||||
const nodes = UiSchemaRepository.modelToSingleNodes(model);
|
||||
const rootUid = nodes[0]['x-uid'];
|
||||
await this.insertNodes(nodes, options);
|
||||
return await this.findModelById(rootUid, options);
|
||||
}
|
||||
|
||||
@transaction()
|
||||
async updateSingleNode(node: SchemaNode, options?: Transactionable) {
|
||||
const instance = await this.model.findByPk(node['x-uid'], {
|
||||
transaction: options?.transaction,
|
||||
});
|
||||
if (instance) {
|
||||
// @ts-ignore
|
||||
await instance.update(
|
||||
{
|
||||
schema: {
|
||||
...(instance.get('schema') as any),
|
||||
...lodash.omit(node, ['x-async', 'name', 'x-uid', 'childOptions']),
|
||||
},
|
||||
},
|
||||
{
|
||||
hooks: false,
|
||||
transaction: options?.transaction,
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@transaction()
|
||||
async upsertModel(model: any, options?: Transactionable) {
|
||||
let childOptions: ChildOptions = null;
|
||||
if (model.parentId) {
|
||||
childOptions = {
|
||||
parentUid: model.parentId,
|
||||
type: 'properties',
|
||||
position: 'last',
|
||||
};
|
||||
}
|
||||
const nodes = UiSchemaRepository.modelToSingleNodes(model, childOptions);
|
||||
const rootUid = nodes[0]['x-uid'];
|
||||
for (const node of nodes) {
|
||||
const exists = await this.updateSingleNode(node, options);
|
||||
if (!exists) {
|
||||
await this.insertSingleNode(node, options);
|
||||
}
|
||||
}
|
||||
return rootUid;
|
||||
}
|
||||
|
||||
async findModelById(uid: string, options?: GetJsonSchemaOptions) {
|
||||
const nodes = await this.findNodesById(uid, options);
|
||||
return UiSchemaRepository.nodesToModel(nodes, uid);
|
||||
}
|
||||
|
||||
async findModelByParentId(parentUid: string, options?: GetJsonSchemaOptions) {
|
||||
const model = await this.findModelById(parentUid, options);
|
||||
return Object.values(model.subModels || {}).shift();
|
||||
}
|
||||
}
|
||||
|
||||
export default UiSchemaRepository;
|
||||
|
@ -8,10 +8,9 @@
|
||||
*/
|
||||
|
||||
import { MagicAttributeModel } from '@nocobase/database';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
import PluginLocalizationServer from '@nocobase/plugin-localization';
|
||||
import { tval } from '@nocobase/utils';
|
||||
import { uid } from '@nocobase/utils';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
import { tval, uid } from '@nocobase/utils';
|
||||
import path, { resolve } from 'path';
|
||||
import { uiSchemaActions } from './actions/ui-schema-action';
|
||||
import { UiSchemaModel } from './model';
|
||||
@ -71,6 +70,11 @@ export class PluginUISchemaStorageServer extends Plugin {
|
||||
actions: ['uiSchemas:*', 'uiSchemas.roles:list', 'uiSchemas.roles:set'],
|
||||
});
|
||||
|
||||
this.app.acl.registerSnippet({
|
||||
name: 'ui.flowModels',
|
||||
actions: ['flowModels:*'],
|
||||
});
|
||||
|
||||
db.on('uiSchemas.beforeCreate', function setUid(model) {
|
||||
if (!model.get('name')) {
|
||||
model.set('name', uid());
|
||||
@ -114,11 +118,44 @@ export class PluginUISchemaStorageServer extends Plugin {
|
||||
});
|
||||
});
|
||||
|
||||
this.app.resourcer.define({
|
||||
this.app.resourceManager.define({
|
||||
name: 'uiSchemas',
|
||||
actions: uiSchemaActions,
|
||||
});
|
||||
|
||||
this.app.resourceManager.define({
|
||||
name: 'flowModels',
|
||||
actions: {
|
||||
findOne: async (ctx, next) => {
|
||||
const { uid, parentId } = ctx.action.params;
|
||||
const repository = ctx.db.getRepository('uiSchemas') as UiSchemaRepository;
|
||||
if (uid) {
|
||||
ctx.body = await repository.findModelById(uid);
|
||||
} else if (parentId) {
|
||||
ctx.body = await repository.findModelByParentId(parentId);
|
||||
}
|
||||
await next();
|
||||
},
|
||||
save: async (ctx, next) => {
|
||||
const { values } = ctx.action.params;
|
||||
const repository = ctx.db.getRepository('uiSchemas') as UiSchemaRepository;
|
||||
const uid = await repository.upsertModel(values);
|
||||
ctx.body = uid;
|
||||
// ctx.body = await repository.findModelById(uid);
|
||||
await next();
|
||||
},
|
||||
destroy: async (ctx, next) => {
|
||||
const { filterByTk } = ctx.action.params;
|
||||
const repository = ctx.db.getRepository('uiSchemas') as UiSchemaRepository;
|
||||
await repository.remove(filterByTk);
|
||||
ctx.body = 'ok';
|
||||
await next();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.app.acl.allow('flowModels', ['findOne'], 'loggedIn');
|
||||
|
||||
this.app.acl.allow(
|
||||
'uiSchemas',
|
||||
['getProperties', 'getJsonSchema', 'getParentJsonSchema', 'initializeActionContext'],
|
||||
|
Loading…
x
Reference in New Issue
Block a user