mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
feat: findModelsByBaseClass
This commit is contained in:
parent
2a306d1813
commit
68b106da04
@ -162,22 +162,9 @@ QuickEditForm.registerFlow({
|
||||
}
|
||||
const collectionField = ctx.model.collection.getField(fieldPath) as CollectionField;
|
||||
if (collectionField) {
|
||||
let use = 'EditableFieldModel';
|
||||
if (collectionField.interface === 'number') {
|
||||
use = 'InputNumberFieldModel';
|
||||
}
|
||||
if (collectionField.interface === 'integer') {
|
||||
use = 'InputNumberFieldModel';
|
||||
}
|
||||
if (collectionField.interface === 'select') {
|
||||
use = 'SelectFieldModel';
|
||||
}
|
||||
if (collectionField.interface === 'textarea') {
|
||||
use = 'TextareaFieldModel';
|
||||
}
|
||||
if (collectionField.interface === 'datetime') {
|
||||
use = 'DateTimeFieldModel';
|
||||
}
|
||||
const FieldModels = collectionField.getMatchFieldModelsByBaseClass('EditableFieldModel');
|
||||
const use = [...FieldModels.keys()].shift() || 'EditableFieldModel';
|
||||
console.log('getMatchFieldModelsByBaseClass', FieldModels);
|
||||
ctx.model.addSubModel('fields', {
|
||||
use,
|
||||
stepParams: {
|
||||
|
87
packages/core/flow-engine/src/__tests__/flow-engine.test.ts
Normal file
87
packages/core/flow-engine/src/__tests__/flow-engine.test.ts
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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 { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { FlowEngine } from '../flowEngine';
|
||||
import { FlowModel } from '../models';
|
||||
|
||||
class BaseModel extends FlowModel {}
|
||||
class SubModelA extends BaseModel {}
|
||||
class SubModelB extends BaseModel {}
|
||||
class SubModelC extends SubModelA {}
|
||||
|
||||
describe('FlowEngine', () => {
|
||||
let engine: FlowEngine;
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new FlowEngine();
|
||||
engine.registerModels({
|
||||
BaseModel,
|
||||
SubModelA,
|
||||
SubModelB,
|
||||
SubModelC,
|
||||
});
|
||||
});
|
||||
|
||||
it('getModelClass should return correct class', () => {
|
||||
expect(engine.getModelClass('BaseModel')).toBe(BaseModel);
|
||||
expect(engine.getModelClass('SubModelA')).toBe(SubModelA);
|
||||
expect(engine.getModelClass('NotExist')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('findModelsByBaseClass should return all subclasses', () => {
|
||||
const result = engine.findModelsByBaseClass(BaseModel);
|
||||
expect(result.has('BaseModel')).toBe(false);
|
||||
expect(result.has('SubModelA')).toBe(true);
|
||||
expect(result.has('SubModelB')).toBe(true);
|
||||
expect(result.has('SubModelC')).toBe(true);
|
||||
});
|
||||
|
||||
it('findModelsByBaseClass should support filter', () => {
|
||||
const result = engine.findModelsByBaseClass(BaseModel, (ModelClass, name) => name.startsWith('SubModel'));
|
||||
expect(result.has('BaseModel')).toBe(false);
|
||||
expect(result.has('SubModelA')).toBe(true);
|
||||
expect(result.has('SubModelB')).toBe(true);
|
||||
expect(result.has('SubModelC')).toBe(true);
|
||||
});
|
||||
|
||||
it('findModelClass should find by predicate', () => {
|
||||
const found = engine.findModelClass((name) => name === 'SubModelB');
|
||||
expect(found).toBeDefined();
|
||||
expect(found?.[0]).toBe('SubModelB');
|
||||
expect(found?.[1]).toBe(SubModelB);
|
||||
});
|
||||
|
||||
it('filterModelClassByParent should return correct subclasses', () => {
|
||||
const result = engine.filterModelClassByParent(SubModelA);
|
||||
expect(result.has('SubModelA')).toBe(false);
|
||||
expect(result.has('SubModelC')).toBe(true);
|
||||
expect(result.has('BaseModel')).toBe(false);
|
||||
expect(result.has('SubModelB')).toBe(false);
|
||||
});
|
||||
|
||||
it('registerAction/getAction should work', () => {
|
||||
engine.registerAction('testAction', { handler: vi.fn(), name: 'testAction' });
|
||||
const action = engine.getAction('testAction');
|
||||
expect(action).toBeDefined();
|
||||
expect(action?.name).toBe('testAction');
|
||||
});
|
||||
|
||||
it('registerModels should overwrite existing model', () => {
|
||||
class NewBaseModel {}
|
||||
engine.registerModels({ BaseModel: NewBaseModel });
|
||||
expect(engine.getModelClass('BaseModel')).toBe(NewBaseModel);
|
||||
});
|
||||
|
||||
it('setContext/getContext should merge and return context', () => {
|
||||
engine.setContext({ a: 1 });
|
||||
engine.setContext({ b: 2 });
|
||||
expect(engine.getContext()).toMatchObject({ a: 1, b: 2 });
|
||||
});
|
||||
});
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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 { describe, expect, it } from 'vitest';
|
||||
import { isFieldInterfaceMatch } from '../data-source';
|
||||
|
||||
describe('isFieldInterfaceMatch', () => {
|
||||
it('should return true when fieldInterfaces is "*" (string)', () => {
|
||||
expect(isFieldInterfaceMatch('*', 'a')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when fieldInterfaces is a string not matching target', () => {
|
||||
expect(isFieldInterfaceMatch('b', 'a')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when fieldInterfaces is a string matching target', () => {
|
||||
expect(isFieldInterfaceMatch('a', 'a')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when fieldInterfaces is array not containing target', () => {
|
||||
expect(isFieldInterfaceMatch(['b', 'c'], 'a')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when fieldInterfaces is array containing target', () => {
|
||||
expect(isFieldInterfaceMatch(['a', 'b'], 'a')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when fieldInterfaces is array containing "*"', () => {
|
||||
expect(isFieldInterfaceMatch(['*', 'b'], 'a')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when fieldInterfaces is null', () => {
|
||||
expect(isFieldInterfaceMatch(null, 'a')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when fieldInterfaces is undefined', () => {
|
||||
expect(isFieldInterfaceMatch(undefined, 'a')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when targetInterface is empty string', () => {
|
||||
expect(isFieldInterfaceMatch('a', '')).toBe(false);
|
||||
expect(isFieldInterfaceMatch(['a', 'b'], '')).toBe(false);
|
||||
expect(isFieldInterfaceMatch('*', '')).toBe(true); // '*' 匹配任何字符串,包括空字符串
|
||||
});
|
||||
});
|
@ -455,4 +455,32 @@ export class CollectionField {
|
||||
const app = this.flowEngine.context.app;
|
||||
return app.dataSourceManager.collectionFieldInterfaceManager.getFieldInterface(this.interface);
|
||||
}
|
||||
|
||||
getMatchFieldModelsByBaseClass(baseClass: string) {
|
||||
return this.flowEngine.findModelsByBaseClass(baseClass, (M, name) => {
|
||||
console.log('getMatchFieldModelsByBaseClass', name, M['supportedFieldInterfaces'], this.interface);
|
||||
if (isFieldInterfaceMatch(M['supportedFieldInterfaces'], this.interface)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 fieldInterfaces 是否匹配 targetInterface
|
||||
* @param fieldInterfaces string | string[] | null
|
||||
* @param targetInterface string
|
||||
*/
|
||||
export function isFieldInterfaceMatch(
|
||||
fieldInterfaces: string | string[] | null | undefined,
|
||||
targetInterface: string,
|
||||
): boolean {
|
||||
if (!fieldInterfaces) return false;
|
||||
if (fieldInterfaces === '*') return true;
|
||||
if (typeof fieldInterfaces === 'string') return fieldInterfaces === targetInterface;
|
||||
if (Array.isArray(fieldInterfaces)) {
|
||||
return fieldInterfaces.includes('*') || fieldInterfaces.includes(targetInterface);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -213,6 +213,29 @@ export class FlowEngine {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父类过滤模型类(支持多层继承),可选自定义过滤器
|
||||
* @param {string | ModelConstructor} baseClass 父类名称或构造函数
|
||||
* @param {(ModelClass: ModelConstructor, className: string) => boolean} [filter] 过滤函数
|
||||
* @returns {Map<string, ModelConstructor>} 继承自指定父类且通过过滤的模型类映射
|
||||
*/
|
||||
public findModelsByBaseClass(
|
||||
baseClass: string | ModelConstructor,
|
||||
filter?: (ModelClass: ModelConstructor, className: string) => boolean,
|
||||
): Map<string, ModelConstructor> {
|
||||
const parentModelClass = typeof baseClass === 'string' ? this.getModelClass(baseClass) : baseClass;
|
||||
const result = new Map<string, ModelConstructor>();
|
||||
if (!parentModelClass) return result;
|
||||
for (const [className, ModelClass] of this.modelClasses) {
|
||||
if (isInheritedFrom(ModelClass, parentModelClass)) {
|
||||
if (!filter || filter(ModelClass, className)) {
|
||||
result.set(className, ModelClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并注册一个 Model 实例。
|
||||
* 如果具有相同 UID 的实例已存在,则返回现有实例。
|
||||
|
@ -4,7 +4,7 @@
|
||||
"displayName.zh-CN": "区块:代码",
|
||||
"description": "Create code blocks that execute custom JavaScript code with external library loading support.",
|
||||
"description.zh-CN": "创建代码区块,通过执行自定义 JavaScript 代码进行渲染,支持加载外部库。",
|
||||
"version": "1.8.0-beta.10",
|
||||
"version": "1.8.0-alpha.11",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "./dist/server/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
Loading…
x
Reference in New Issue
Block a user