Merge pull request 'merge_from_upstream' (#358) from merge_from_upstream into @hera/dev

Reviewed-on: daoyoucloud/tachycode#358
This commit is contained in:
sealday 2024-03-13 12:17:53 +08:00
commit 4bd5a1beda
121 changed files with 1152 additions and 252 deletions

View File

@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
## [v0.20.0-alpha.9](https://github.com/nocobase/nocobase/compare/v0.20.0-alpha.8...v0.20.0-alpha.9) - 2024-03-12
### Merged
- refactor: add child in inheritance of tree collection [`#3676`](https://github.com/nocobase/nocobase/pull/3676)
- fix: data source add field refresh(T-3253) [`#3645`](https://github.com/nocobase/nocobase/pull/3645)
- chore: escape underscore char in include query [`#3681`](https://github.com/nocobase/nocobase/pull/3681)
- fix: upgrade app after restore [`#3680`](https://github.com/nocobase/nocobase/pull/3680)
- fix: view collection association field foreignkey should be select [`#3671`](https://github.com/nocobase/nocobase/pull/3671)
- fix: acl should return true when resource allowed [`#3675`](https://github.com/nocobase/nocobase/pull/3675)
- fix: init scope value when all data is null value [`#3674`](https://github.com/nocobase/nocobase/pull/3674)
- fix: inheritance cache bug [`#3669`](https://github.com/nocobase/nocobase/pull/3669)
### Commits
- chore(versions): 😊 publish v0.20.0-alpha.9 [`2e7da6e`](https://github.com/nocobase/nocobase/commit/2e7da6e29bc75def06f59d8d3bb991e7e238c64b)
- fix: pagination error in roles collections resourcer [`023096b`](https://github.com/nocobase/nocobase/commit/023096b1a9ea6ccae364c81ba93ca32b077ccecc)
- chore: update changelog [`56fd24e`](https://github.com/nocobase/nocobase/commit/56fd24ef4b30717e12b2c0574d4df92210bac81c)
## [v0.20.0-alpha.8](https://github.com/nocobase/nocobase/compare/v0.20.0-alpha.7...v0.20.0-alpha.8) - 2024-03-11 ## [v0.20.0-alpha.8](https://github.com/nocobase/nocobase/compare/v0.20.0-alpha.7...v0.20.0-alpha.8) - 2024-03-11
### Merged ### Merged
@ -597,7 +616,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- fix: load field when source collection not found [`95bec22`](https://github.com/nocobase/nocobase/commit/95bec2278ff2f53dfb1307e47bc8aca90d5a606a) - fix: load field when source collection not found [`95bec22`](https://github.com/nocobase/nocobase/commit/95bec2278ff2f53dfb1307e47bc8aca90d5a606a)
- chore: update changelog [`34e026c`](https://github.com/nocobase/nocobase/commit/34e026cec5742c1a1347f2969a03446fa98c44c3) - chore: update changelog [`34e026c`](https://github.com/nocobase/nocobase/commit/34e026cec5742c1a1347f2969a03446fa98c44c3)
## [v0.17.0-alpha.1](https://github.com/nocobase/nocobase/compare/v0.16.0-alpha.7...v0.17.0-alpha.1) - 2023-12-04 ## [v0.17.0-alpha.1](https://github.com/nocobase/nocobase/compare/v0.16.0-alpha.6...v0.17.0-alpha.1) - 2023-12-04
### Merged ### Merged
@ -606,16 +625,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
### Commits ### Commits
- chore(versions): 😊 publish v0.17.0-alpha.1 [`1757a96`](https://github.com/nocobase/nocobase/commit/1757a96b51c7bb17f607b61467ab867e5add6567) - chore(versions): 😊 publish v0.17.0-alpha.1 [`1757a96`](https://github.com/nocobase/nocobase/commit/1757a96b51c7bb17f607b61467ab867e5add6567)
- chore(versions): 😊 publish v0.16.0-alpha.6 [`9d64430`](https://github.com/nocobase/nocobase/commit/9d644304c10be0f8404f2f3370a43d2dc00e8aed)
- chore: update changelog [`69abfc9`](https://github.com/nocobase/nocobase/commit/69abfc98327b36bbc6b7e225294835bb457518d6) - chore: update changelog [`69abfc9`](https://github.com/nocobase/nocobase/commit/69abfc98327b36bbc6b7e225294835bb457518d6)
## [v0.16.0-alpha.7](https://github.com/nocobase/nocobase/compare/v0.16.0-alpha.6...v0.16.0-alpha.7) - 2023-12-04
### Commits
- chore(versions): 😊 publish v0.16.0-alpha.7 [`d077a17`](https://github.com/nocobase/nocobase/commit/d077a17ee8cafc901a995f1ec7271ca78e4c32c3)
- chore(versions): 😊 publish v [`7810332`](https://github.com/nocobase/nocobase/commit/78103320eb6ee01559609784e7ba1a4493399c2c)
## [v0.16.0-alpha.6](https://github.com/nocobase/nocobase/compare/v0.16.0-alpha.5...v0.16.0-alpha.6) - 2023-12-04 ## [v0.16.0-alpha.6](https://github.com/nocobase/nocobase/compare/v0.16.0-alpha.5...v0.16.0-alpha.6) - 2023-12-04
### Merged ### Merged

View File

@ -1,5 +1,5 @@
{ {
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"npmClientArgs": ["--ignore-engines"], "npmClientArgs": ["--ignore-engines"],

View File

@ -1,13 +1,13 @@
{ {
"name": "@nocobase/acl", "name": "@nocobase/acl",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/resourcer": "0.20.0-alpha.8", "@nocobase/resourcer": "0.20.0-alpha.9",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"minimatch": "^5.1.1" "minimatch": "^5.1.1"
}, },
"repository": { "repository": {

View File

@ -1,14 +1,14 @@
{ {
"name": "@nocobase/actions", "name": "@nocobase/actions",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/cache": "0.20.0-alpha.8", "@nocobase/cache": "0.20.0-alpha.9",
"@nocobase/database": "0.20.0-alpha.8", "@nocobase/database": "0.20.0-alpha.9",
"@nocobase/resourcer": "0.20.0-alpha.8" "@nocobase/resourcer": "0.20.0-alpha.9"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,17 +1,17 @@
{ {
"name": "@nocobase/app", "name": "@nocobase/app",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/database": "0.20.0-alpha.8", "@nocobase/database": "0.20.0-alpha.9",
"@nocobase/preset-nocobase": "0.20.0-alpha.8", "@nocobase/preset-nocobase": "0.20.0-alpha.9",
"@nocobase/server": "0.20.0-alpha.8" "@nocobase/server": "0.20.0-alpha.9"
}, },
"devDependencies": { "devDependencies": {
"@nocobase/client": "0.20.0-alpha.8" "@nocobase/client": "0.20.0-alpha.9"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,16 +1,16 @@
{ {
"name": "@nocobase/auth", "name": "@nocobase/auth",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/actions": "0.20.0-alpha.8", "@nocobase/actions": "0.20.0-alpha.9",
"@nocobase/cache": "0.20.0-alpha.8", "@nocobase/cache": "0.20.0-alpha.9",
"@nocobase/database": "0.20.0-alpha.8", "@nocobase/database": "0.20.0-alpha.9",
"@nocobase/resourcer": "0.20.0-alpha.8", "@nocobase/resourcer": "0.20.0-alpha.9",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^8.5.8",
"jsonwebtoken": "^8.5.1" "jsonwebtoken": "^8.5.1"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/build", "name": "@nocobase/build",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "Library build tool based on rollup.", "description": "Library build tool based on rollup.",
"main": "lib/index.js", "main": "lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",

View File

@ -34,6 +34,7 @@ const external = [
'@nocobase/cache', '@nocobase/cache',
'@nocobase/client', '@nocobase/client',
'@nocobase/database', '@nocobase/database',
'@nocobase/data-source-manager',
'@nocobase/evaluators', '@nocobase/evaluators',
'@nocobase/logger', '@nocobase/logger',
'@nocobase/resourcer', '@nocobase/resourcer',

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/cache", "name": "@nocobase/cache",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/cli", "name": "@nocobase/cli",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./src/index.js", "main": "./src/index.js",
@ -8,7 +8,7 @@
"nocobase": "./bin/index.js" "nocobase": "./bin/index.js"
}, },
"dependencies": { "dependencies": {
"@nocobase/app": "0.20.0-alpha.8", "@nocobase/app": "0.20.0-alpha.9",
"@types/fs-extra": "^11.0.1", "@types/fs-extra": "^11.0.1",
"@umijs/utils": "3.5.20", "@umijs/utils": "3.5.20",
"chalk": "^4.1.1", "chalk": "^4.1.1",
@ -25,7 +25,7 @@
"tsx": "^4.6.2" "tsx": "^4.6.2"
}, },
"devDependencies": { "devDependencies": {
"@nocobase/devtools": "0.20.0-alpha.8" "@nocobase/devtools": "0.20.0-alpha.9"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/client", "name": "@nocobase/client",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "lib/index.js", "main": "lib/index.js",
"module": "es/index.mjs", "module": "es/index.mjs",
@ -26,9 +26,9 @@
"@formily/reactive-react": "^2.2.27", "@formily/reactive-react": "^2.2.27",
"@formily/shared": "^2.2.27", "@formily/shared": "^2.2.27",
"@formily/validator": "^2.2.27", "@formily/validator": "^2.2.27",
"@nocobase/evaluators": "0.20.0-alpha.8", "@nocobase/evaluators": "0.20.0-alpha.9",
"@nocobase/sdk": "0.20.0-alpha.8", "@nocobase/sdk": "0.20.0-alpha.9",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"ahooks": "^3.7.2", "ahooks": "^3.7.2",
"antd": "^5.12.8", "antd": "^5.12.8",
"antd-style": "3.4.5", "antd-style": "3.4.5",

View File

@ -986,6 +986,11 @@ export const useBulkDestroyActionProps = () => {
filterByTk: field.data?.selectedRowKeys, filterByTk: field.data?.selectedRowKeys,
}); });
field.data.selectedRowKeys = []; field.data.selectedRowKeys = [];
const currentPage = service.params[0]?.page;
const totalPage = service.data?.meta?.totalPage;
if (currentPage === totalPage) {
service.params[0].page = currentPage - 1;
}
service?.refresh?.(); service?.refresh?.();
}, },
}; };

View File

@ -132,10 +132,10 @@ export const useSelfAndChildrenCollections = (collectionName: string) => {
return options; return options;
}; };
export const useCollectionFilterOptions = (collection: any) => { export const useCollectionFilterOptions = (collection: any, dataSource?: string) => {
const { getCollectionFields, getInterface } = useCollectionManager_deprecated(); const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
return useMemo(() => { return useMemo(() => {
const fields = getCollectionFields(collection); const fields = getCollectionFields(collection, dataSource);
const field2option = (field, depth) => { const field2option = (field, depth) => {
if (!field.interface) { if (!field.interface) {
return; return;
@ -165,7 +165,7 @@ export const useCollectionFilterOptions = (collection: any) => {
option['children'] = children; option['children'] = children;
} }
if (nested) { if (nested) {
const targetFields = getCollectionFields(field.target); const targetFields = getCollectionFields(field.target, dataSource);
const options = getOptions(targetFields, depth + 1).filter(Boolean); const options = getOptions(targetFields, depth + 1).filter(Boolean);
option['children'] = option['children'] || []; option['children'] = option['children'] || [];
option['children'].push(...options); option['children'].push(...options);
@ -184,7 +184,7 @@ export const useCollectionFilterOptions = (collection: any) => {
}; };
const options = getOptions(fields, 1); const options = getOptions(fields, 1);
return options; return options;
}, [_.isString(collection) ? collection : collection?.name]); }, [_.isString(collection) ? collection : collection?.name, dataSource]);
}; };
export const useCollectionFilterOptionsV2 = (collection: any) => { export const useCollectionFilterOptionsV2 = (collection: any) => {

View File

@ -40,6 +40,9 @@ import {
UpdatedByFieldInterface, UpdatedByFieldInterface,
UrlFieldInterface, UrlFieldInterface,
SortFieldInterface, SortFieldInterface,
UUIDFieldInterface,
NanoidFieldInterface,
UnixTimestampFieldInterface,
} from './interfaces'; } from './interfaces';
import { import {
GeneralCollectionTemplate, GeneralCollectionTemplate,
@ -155,6 +158,9 @@ export class CollectionPlugin extends Plugin {
UpdatedByFieldInterface, UpdatedByFieldInterface,
UrlFieldInterface, UrlFieldInterface,
SortFieldInterface, SortFieldInterface,
UUIDFieldInterface,
NanoidFieldInterface,
UnixTimestampFieldInterface,
]); ]);
} }

View File

@ -25,3 +25,4 @@ export * from './collectionPlugin';
export * from './mixins/InheritanceCollectionMixin'; export * from './mixins/InheritanceCollectionMixin';
export * from './sub-table'; export * from './sub-table';
export * from './CollectionHistoryProvider'; export * from './CollectionHistoryProvider';
export * from './utils';

View File

@ -34,3 +34,6 @@ export * from './updatedAt';
export * from './updatedBy'; export * from './updatedBy';
export * from './url'; export * from './url';
export * from './sort'; export * from './sort';
export * from './uuid';
export * from './nanoid';
export * from './unixTimestamp';

View File

@ -0,0 +1,56 @@
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
import { operators } from './properties';
export class NanoidFieldInterface extends CollectionFieldInterface {
name = 'nanoid';
type = 'object';
group = 'advanced';
order = 0;
title = '{{t("Nano ID")}}';
hidden = false;
sortable = true;
default = {
type: 'nanoid',
uiSchema: {
type: 'string',
'x-component': 'NanoIDInput',
},
};
availableTypes = ['string', 'uid'];
properties = {
'uiSchema.title': {
type: 'string',
title: '{{t("Field display name")}}',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
title: '{{t("Field name")}}',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
description:
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
},
customAlphabet: {
type: 'string',
title: '{{t("Alphabet")}}',
default: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
size: {
type: 'number',
title: '{{t("Length")}}',
default: 21,
'x-decorator': 'FormItem',
'x-component': 'InputNumber',
},
};
filterable = {
operators: operators.string,
};
titleUsable = true;
}

View File

@ -0,0 +1,42 @@
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
import { dateTimeProps, defaultProps, operators } from './properties';
export class UnixTimestampFieldInterface extends CollectionFieldInterface {
name = 'unixTimestamp';
type = 'object';
group = 'datetime';
order = 1;
title = '{{t("Unix Timestamp")}}';
sortable = true;
default = {
type: 'bigInt',
uiSchema: {
type: 'number',
'x-component': 'UnixTimestamp',
'x-component-props': {
accuracy: 'millisecond',
showTime: true,
},
},
};
availableTypes = ['integet', 'bigInt'];
hasDefaultValue = true;
properties = {
...defaultProps,
'uiSchema.x-component-props.accuracy': {
type: 'string',
title: '{{t("Accuracy")}}',
'x-component': 'Radio.Group',
'x-decorator': 'FormItem',
default: 'millisecond',
enum: [
{ value: 'millisecond', label: '{{t("Millisecond")}}' },
{ value: 'second', label: '{{t("Second")}}' },
],
},
};
filterable = {
operators: operators.number,
};
titleUsable = true;
}

View File

@ -0,0 +1,44 @@
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
import { operators } from './properties';
export class UUIDFieldInterface extends CollectionFieldInterface {
name = 'uuid';
type = 'object';
group = 'advanced';
order = 0;
title = '{{t("UUID")}}';
hidden = false;
sortable = true;
default = {
type: 'uuid',
uiSchema: {
type: 'string',
'x-component': 'Input',
'x-validator': 'uuid',
},
};
availableTypes = ['string', 'uid'];
properties = {
'uiSchema.title': {
type: 'string',
title: '{{t("Field display name")}}',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
title: '{{t("Field name")}}',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
description:
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
},
};
filterable = {
operators: operators.string,
};
titleUsable = true;
}

View File

@ -0,0 +1,16 @@
export function parseCollectionName(collection: string) {
if (!collection) {
return [];
}
const dataSourceCollection = collection.split(':');
const collectionName = dataSourceCollection.pop();
const dataSourceName = dataSourceCollection[0] ?? 'main';
return [dataSourceName, collectionName];
}
export function joinCollectionName(dataSourceName: string, collectionName: string) {
if (!dataSourceName || dataSourceName === 'main') {
return collectionName;
}
return `${dataSourceName}:${collectionName}`;
}

View File

@ -114,7 +114,7 @@ export const BlockRequestProvider: FC = ({ children }) => {
<BlockRequestContext.Provider value={currentRequest}> <BlockRequestContext.Provider value={currentRequest}>
{action !== 'list' ? ( {action !== 'list' ? (
<CollectionRecordProvider <CollectionRecordProvider
isNew={action === undefined} isNew={action == null}
record={currentRequest.data?.data} record={currentRequest.data?.data}
parentRecord={memoizedParentRecord} parentRecord={memoizedParentRecord}
> >

View File

@ -851,7 +851,7 @@
"loading": "加载中", "loading": "加载中",
"name is required": "名称不能为空", "name is required": "名称不能为空",
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" 可能已被删除。请删除当前{{blockType}}.", "The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" 可能已被删除。请删除当前{{blockType}}.",
"data source": "数据源", "Data source": "数据源",
"DataSource": "数据源", "DataSource": "数据源",
"Data model": "数据模型", "Data model": "数据模型",
"Security": "认证与安全", "Security": "认证与安全",
@ -891,5 +891,12 @@
"Owners": "负责人", "Owners": "负责人",
"Plugin settings": "插件设置", "Plugin settings": "插件设置",
"Menu": "菜单", "Menu": "菜单",
"Drag and drop sorting field": "拖拽排序字段" "Drag and drop sorting field": "拖拽排序字段",
"Alphabet": "字符",
"Accuracy": "精确度",
"Millisecond": "毫秒",
"Second": "秒",
"Unix Timestamp": "Unix 时间戳",
"Field value do not meet the requirements": "字符不符合要求",
"Field value size is": "字符长度要求"
} }

View File

@ -0,0 +1,21 @@
import { test, expect } from '@nocobase/test/e2e';
import { T3529 } from './templatesOfBug';
test.describe('association form block', () => {
// https://nocobase.height.app/T-3529
test('should be created instead of updated', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T3529).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByLabel('action-Action.Link-View-view-general-table-0').click();
void page.getByLabel('action-Action-Submit-submit-').click();
const request = await page.waitForRequest((request) => {
return request.url().includes('m2mField0:create');
});
// 应该有包含 :create 的请求
expect(request).toBeTruthy();
});
});

View File

@ -1,4 +1,4 @@
import { PageConfig, generalWithM2oSingleSelect } from '@nocobase/test/e2e'; import { PageConfig, generalWithM2oSingleSelect, generalWithMultiLevelM2mFields } from '@nocobase/test/e2e';
export const T2165 = { export const T2165 = {
pageSchema: { pageSchema: {
@ -5701,3 +5701,374 @@ export const T3469: PageConfig = {
'x-async': true, 'x-async': true,
}, },
}; };
// https://nocobase.height.app/T-3529
export const T3529: PageConfig = {
collections: generalWithMultiLevelM2mFields,
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
'5i47muli5of': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
l62athlwjbe: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
gv12rstt0n6: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
'44sjepqege8': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'general:list',
'x-decorator-props': {
collection: 'general',
dataSource: 'main',
resource: 'general',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
disableTemplate: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'TableActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-uid': 'gcqzwfc98cz',
'x-async': false,
'x-index': 1,
},
cndkuudjlrn: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'TableColumnInitializers',
'x-component': 'TableV2',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
useProps: '{{ useTableBlockProps }}',
},
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'TableActionColumnInitializers',
properties: {
'7glkmum3znh': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
properties: {
'34feo0sq1b1': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View") }}',
'x-action': 'view',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:view',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
properties: {
'5ubuvb82lbu': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
pe6vly71p50: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
ls6i68y4q5r: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-acl-action': 'general.m2mField0:create',
'x-decorator': 'FormBlockProvider',
'x-decorator-props': {
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
action: null,
dataSource: 'main',
resource: 'general.m2mField0',
collection: 'm2mField1',
association: 'general.m2mField0',
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:createForm',
'x-component': 'CardItem',
'x-component-props': {},
properties: {
'4g1wyduvzzq': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'FormV2',
'x-component-props': {
useProps: '{{ useFormBlockProps }}',
},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-uid': 'aigmsbzdyeb',
'x-async': false,
'x-index': 1,
},
v5tx6a8ya3q: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'CreateFormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
properties: {
dhmchgjzu2d: {
_isJSONSchemaObject: true,
version: '2.0',
title: '{{ t("Submit") }}',
'x-action': 'submit',
'x-component': 'Action',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:createSubmit',
'x-component-props': {
type: 'primary',
htmlType: 'submit',
useProps: '{{ useCreateActionProps }}',
},
'x-action-settings': {
triggerWorkflows: [],
},
type: 'void',
'x-uid': 'bgi90aunfbx',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '6m4zb9wz65e',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'sbl64rh18y4',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'z76kadyn2d8',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'kb89ln0xf6j',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zyqb93i3dfg',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zxthz54u0wr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '4vis0296ryj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'p23nq2w7s15',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zaqeevlcwap',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'r1tzkph9sg3',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2k7344qkff4',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jozmqdix3a6',
'x-async': false,
'x-index': 1,
},
exdcwiv8fnb: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-toolbar': 'TableColumnSchemaToolbar',
'x-settings': 'fieldSettings:TableColumn',
'x-component': 'TableV2.Column',
properties: {
m2mField0: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 'general.m2mField0',
'x-component': 'CollectionField',
'x-component-props': {
fieldNames: {
value: 'id',
label: 'id',
},
ellipsis: true,
size: 'small',
},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
'x-uid': 'gzktad04dhq',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fulrpp42vr2',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '6rucf9up8f4',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'izfl6h66ch4',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'i8m1l6l6qg5',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'v0y71zujt89',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'vpa330z2xp5',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'l4kzvh7mdoa',
'x-async': true,
'x-index': 1,
},
};

View File

@ -0,0 +1,22 @@
import { useFieldSchema } from '@formily/react';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { SchemaSettingsDateFormat } from '../../../../schema-settings/SchemaSettingsDateFormat';
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
export const unixTimestampComponentFieldSettings = new SchemaSettings({
name: 'fieldSettings:component:UnixTimestamp',
items: [
{
name: 'dateDisplayFormat',
Component: SchemaSettingsDateFormat as any,
useComponentProps() {
const schema = useFieldSchema();
const { fieldSchema: tableColumnSchema } = useColumnSchema();
const fieldSchema = tableColumnSchema || schema;
return {
fieldSchema,
};
},
},
],
});

View File

@ -1,18 +1,20 @@
import { connect, mapReadPretty, observer, useField } from '@formily/react'; import { connect, mapReadPretty, observer, useField } from '@formily/react';
import { Select, SelectProps, Tag } from 'antd'; import { Cascader, Select, SelectProps, Tag } from 'antd';
import React, { useContext } from 'react'; import React, { useCallback, useContext, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelfAndChildrenCollections } from '../../../collection-manager/action-hooks'; import { useSelfAndChildrenCollections } from '../../../collection-manager/action-hooks';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager/hooks'; import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager/hooks';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
import { FilterContext } from '../filter/context'; import { FilterContext } from '../filter/context';
import { useDataSourceManager } from '../../../data-source';
import { parseCollectionName } from '../../../collection-manager';
export type CollectionSelectProps = SelectProps<any, any> & { export type CollectionSelectProps = SelectProps<any, any> & {
filter?: (item: any, index: number, array: any[]) => boolean; filter?: (item: any, index: number, array: any[]) => boolean;
isTableOid?: boolean; isTableOid?: boolean;
}; };
function useOptions({ filter, isTableOid }: CollectionSelectProps) { function useCollectionOptions({ filter, isTableOid }: CollectionSelectProps) {
const compile = useCompile(); const compile = useCompile();
const field: any = useField(); const field: any = useField();
const ctx: any = useContext(FilterContext); const ctx: any = useContext(FilterContext);
@ -29,20 +31,30 @@ function useOptions({ filter, isTableOid }: CollectionSelectProps) {
typeof filter === 'function' typeof filter === 'function'
? ((inheritCollections || currentCollections) as any[]).filter(filter) ? ((inheritCollections || currentCollections) as any[]).filter(filter)
: inheritCollections || currentCollections; : inheritCollections || currentCollections;
return filtered return useMemo(
.filter((item) => !item.hidden) () =>
.map((item) => ({ filtered
label: compile(item.title || item.label), .filter((item) => !item.hidden)
value: item.name || item.value, .map((item) => ({
color: item.category?.color, label: compile(item.title || item.label),
})); value: item.name || item.value,
color: item.category?.color,
})),
[filtered],
);
} }
export const CollectionSelect = connect( export const CollectionSelect = connect(
(props: CollectionSelectProps) => { (props: CollectionSelectProps) => {
const { filter, ...others } = props; const { filter, ...others } = props;
const options = useOptions(props); const options = useCollectionOptions(props);
const { t } = useTranslation(); const { t } = useTranslation();
const optionFilter = useCallback(
(input, option) =>
(option?.label.toLowerCase() ?? '').includes(input.toLocaleLowerCase()) ||
(option?.value.toString().toLowerCase() ?? '').includes(input.toLocaleLowerCase()),
[],
);
return ( return (
<Select <Select
// @ts-ignore // @ts-ignore
@ -52,10 +64,7 @@ export const CollectionSelect = connect(
popupMatchSelectWidth={false} popupMatchSelectWidth={false}
{...others} {...others}
showSearch showSearch
filterOption={(input, option) => filterOption={optionFilter}
(option?.label.toLowerCase() ?? '').includes(input.toLocaleLowerCase()) ||
(option?.value.toString().toLowerCase() ?? '').includes(input.toLocaleLowerCase())
}
options={options} options={options}
/> />
); );
@ -65,7 +74,7 @@ export const CollectionSelect = connect(
(props: CollectionSelectProps) => { (props: CollectionSelectProps) => {
const { mode } = props; const { mode } = props;
const compile = useCompile(); const compile = useCompile();
const options = useOptions(props).filter((option) => { const options = useCollectionOptions(props).filter((option) => {
if (mode === 'multiple') { if (mode === 'multiple') {
return (props.value ?? []).includes(option.value); return (props.value ?? []).includes(option.value);
} }
@ -86,3 +95,88 @@ export const CollectionSelect = connect(
), ),
), ),
); );
export type DataSourceSelectProps = SelectProps<any, any> & {
filter?: (item: any, index: number, array: any[]) => boolean;
};
function useDataSourceOptions({ filter }: DataSourceSelectProps) {
const compile = useCompile();
const dataSourceManager = useDataSourceManager();
const dataSources = dataSourceManager.getDataSources();
return useMemo(
() => [
{
label: compile('Main'),
value: 'main',
},
...(typeof filter === 'function' ? dataSources.filter(filter) : dataSources).map((item) => ({
label: item.displayName,
value: item.key,
})),
],
[dataSources, filter],
);
}
export const DataSourceSelect = connect((props: DataSourceSelectProps) => {
const { filter, ...others } = props;
const options = useDataSourceOptions(props);
const { t } = useTranslation();
const optionFilter = useCallback(
(input, option) =>
(option?.label.toLowerCase() ?? '').includes(input.toLocaleLowerCase()) ||
(option?.value.toString().toLowerCase() ?? '').includes(input.toLocaleLowerCase()),
[],
);
return (
<Select
// @ts-ignore
role="button"
data-testid="select-datasource"
placeholder={t('Select data source')}
popupMatchSelectWidth={false}
{...others}
showSearch
filterOption={optionFilter}
options={options}
/>
);
});
export const DataSourceCollectionCascader = connect((props) => {
const dataSourceManager = useDataSourceManager();
const compile = useCompile();
const { value, onChange, dataSourceFilter, collectionFilter, ...others } = props;
const [dataSourceName, collectionName] = parseCollectionName(value);
const path = [dataSourceName, collectionName].filter(Boolean);
const dataSources = dataSourceManager.getDataSources();
const options = useMemo(() => {
return (dataSourceFilter ? dataSources.filter(dataSourceFilter) : dataSources).map((dataSource) => {
return {
label: compile(dataSource.displayName),
value: dataSource.key,
children: dataSource.collectionManager.collectionInstancesArr
.filter(collectionFilter ?? ((collection) => !collection.hidden))
.map((collection) => {
return {
label: compile(collection.title),
value: collection.name,
};
}),
};
});
}, [dataSources, dataSourceFilter, collectionFilter]);
const handleChange = useCallback(
(value) => {
if (!value) {
return onChange(value);
}
onChange(value[0] === 'main' ? value[1] : value.join(':'));
},
[onChange],
);
return <Cascader showSearch {...others} options={options} value={path} onChange={handleChange} />;
});

View File

@ -38,6 +38,7 @@ const InternalRangePicker = connect(
export const DatePicker = (props) => { export const DatePicker = (props) => {
const { utc = true } = useDatePickerContext(); const { utc = true } = useDatePickerContext();
const value = Array.isArray(props.value) ? props.value[0] : props.value; const value = Array.isArray(props.value) ? props.value[0] : props.value;
console.log(value);
props = { utc, ...props }; props = { utc, ...props };
return <InternalDatePicker {...props} value={value} />; return <InternalDatePicker {...props} value={value} />;
}; };

View File

@ -51,5 +51,7 @@ export * from './tree-select';
export * from './upload'; export * from './upload';
export * from './variable'; export * from './variable';
export * from './custom-cascader'; export * from './custom-cascader';
export * from './unixTimestamp';
export * from './nanoIDInput';
import './index.less'; import './index.less';

View File

@ -0,0 +1,46 @@
import { customAlphabet as Alphabet } from 'nanoid';
import React, { useEffect } from 'react';
import { LoadingOutlined } from '@ant-design/icons';
import { connect, mapProps, mapReadPretty, useForm } from '@formily/react';
import { Input as AntdInput } from 'antd';
import { ReadPretty } from '../input';
import { useCollectionField } from '../../../data-source/collection-field/CollectionFieldProvider';
import { useTranslation } from 'react-i18next';
export const NanoIDInput = Object.assign(
connect(
AntdInput,
mapProps((props: any, field: any) => {
const { size, customAlphabet } = useCollectionField();
const { t } = useTranslation();
const form = useForm();
function isValidNanoid(value) {
if (value.length !== size) {
return t('Field value size is') + ` ${size}`;
}
for (let i = 0; i < value.length; i++) {
if (customAlphabet.indexOf(value[i]) === -1) {
return t(`Field value do not meet the requirements`);
}
}
}
useEffect(() => {
if (!field.initialValue) {
field.setInitialValue(Alphabet(customAlphabet, size)());
}
form.setFieldState(field.props.name, (state) => {
state.validator = isValidNanoid;
});
}, []);
return {
...props,
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
};
}),
mapReadPretty(ReadPretty.Input),
),
{
ReadPretty: ReadPretty.Input,
},
);

View File

@ -0,0 +1 @@
export * from './NanoIDInput';

View File

@ -21,9 +21,17 @@ Radio.__ANT_RADIO = true;
Radio.Group = connect( Radio.Group = connect(
AntdRadio.Group, AntdRadio.Group,
mapProps({ mapProps(
dataSource: 'options', {
}), dataSource: 'options',
},
(props) => {
return {
...props,
value: props.value && typeof props.value !== 'boolean' ? props.value.toString() : props.value,
};
},
),
mapReadPretty((props) => { mapReadPretty((props) => {
if (!isValid(props.value)) { if (!isValid(props.value)) {
return <div></div>; return <div></div>;

View File

@ -9,11 +9,11 @@ import React from 'react';
const options = [ const options = [
{ {
label: '男', label: '男',
value: 1, value: '1',
}, },
{ {
label: '女', label: '女',
value: 2, value: '2',
}, },
]; ];

View File

@ -8,12 +8,12 @@ import React from 'react';
const options = [ const options = [
{ {
label: '男', label: '男',
value: 1, value: '1',
color: 'blue', color: 'blue',
}, },
{ {
label: '女', label: '女',
value: 2, value: '2',
color: 'red', color: 'red',
}, },
]; ];

View File

@ -122,7 +122,7 @@ const InternalSelect = connect(
} }
return undefined; return undefined;
} }
return v; return v ? v.toString() : v;
}; };
return ( return (
<AntdSelect <AntdSelect

View File

@ -44,7 +44,10 @@ export function getCurrentOptions(values: string | string[], dataSource: any[],
if (!options) return []; if (!options) return [];
const current: Option[] = []; const current: Option[] = [];
for (const value of arrValues) { for (const value of arrValues) {
const option = options.find((v) => v[fieldNames.value] === value) || { value, label: value }; const option = options.find((v) => v[fieldNames.value] == value) || {
value,
label: value ? value.toString() : value,
};
current.push(option); current.push(option);
} }
return current; return current;

View File

@ -0,0 +1,49 @@
import { connect, mapReadPretty } from '@formily/react';
import React, { useMemo } from 'react';
import { DatePicker } from '../date-picker';
import dayjs from 'dayjs';
const toValue = (value: any, accuracy) => {
if (value) {
return timestampToDate(value, accuracy);
}
return null;
};
function timestampToDate(timestamp, accuracy = 'millisecond') {
if (accuracy === 'second') {
timestamp *= 1000; // 如果精确度是秒级则将时间戳乘以1000转换为毫秒级
}
return dayjs(timestamp);
}
function getTimestamp(date, accuracy = 'millisecond') {
if (accuracy === 'second') {
return dayjs(date).unix();
} else {
return dayjs(date).valueOf(); // 默认返回毫秒级时间戳
}
}
export const UnixTimestamp = connect(
(props) => {
const { value, onChange, accuracy } = props;
const v = useMemo(() => toValue(value, accuracy), [value]);
return (
<DatePicker
{...props}
value={v}
onChange={(v: any) => {
if (onChange) {
onChange(getTimestamp(v, accuracy));
}
}}
/>
);
},
mapReadPretty((props) => {
const { value, accuracy } = props;
const v = useMemo(() => toValue(value, accuracy), [value]);
return <DatePicker.ReadPretty {...props} value={v} />;
}),
);

View File

@ -0,0 +1 @@
export * from './UnixTimestamp';

View File

@ -43,6 +43,7 @@ import { subformPopoverComponentFieldSettings } from '../modules/fields/componen
import { selectComponentFieldSettings } from '../modules/fields/component/Select/selectComponentFieldSettings'; import { selectComponentFieldSettings } from '../modules/fields/component/Select/selectComponentFieldSettings';
import { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings'; import { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings';
import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings'; import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings';
import { unixTimestampComponentFieldSettings } from '../modules/fields/component/UnixTimestamp/unixTimestampComponentFieldSettings';
export class SchemaSettingsPlugin extends Plugin { export class SchemaSettingsPlugin extends Plugin {
async load() { async load() {
@ -91,6 +92,8 @@ export class SchemaSettingsPlugin extends Plugin {
this.schemaSettingsManager.add(subformPopoverComponentFieldSettings); this.schemaSettingsManager.add(subformPopoverComponentFieldSettings);
this.schemaSettingsManager.add(subTablePopoverComponentFieldSettings); this.schemaSettingsManager.add(subTablePopoverComponentFieldSettings);
this.schemaSettingsManager.add(datePickerComponentFieldSettings); this.schemaSettingsManager.add(datePickerComponentFieldSettings);
this.schemaSettingsManager.add(unixTimestampComponentFieldSettings);
this.schemaSettingsManager.add(fileManagerComponentFieldSettings); this.schemaSettingsManager.add(fileManagerComponentFieldSettings);
this.schemaSettingsManager.add(tagComponentFieldSettings); this.schemaSettingsManager.add(tagComponentFieldSettings);
this.schemaSettingsManager.add(cascadeSelectComponentFieldSettings); this.schemaSettingsManager.add(cascadeSelectComponentFieldSettings);

View File

@ -1,6 +1,6 @@
{ {
"name": "create-nocobase-app", "name": "create-nocobase-app",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "src/index.js", "main": "src/index.js",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {

View File

@ -1,16 +1,16 @@
{ {
"name": "@nocobase/data-source-manager", "name": "@nocobase/data-source-manager",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/actions": "0.20.0-alpha.8", "@nocobase/actions": "0.20.0-alpha.9",
"@nocobase/cache": "0.20.0-alpha.8", "@nocobase/cache": "0.20.0-alpha.9",
"@nocobase/database": "0.20.0-alpha.8", "@nocobase/database": "0.20.0-alpha.9",
"@nocobase/resourcer": "0.20.0-alpha.8", "@nocobase/resourcer": "0.20.0-alpha.9",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^8.5.8",
"jsonwebtoken": "^8.5.1" "jsonwebtoken": "^8.5.1"
}, },

View File

@ -9,3 +9,4 @@ export * from './load-default-actions';
export * from './types'; export * from './types';
export * from './data-source-with-database'; export * from './data-source-with-database';
export * from './utils';

View File

@ -0,0 +1,9 @@
export function parseCollectionName(collection: string) {
if (!collection) {
return [];
}
const dataSourceCollection = collection.split(':');
const collectionName = dataSourceCollection.pop();
const dataSourceName = dataSourceCollection[0] ?? 'main';
return [dataSourceName, collectionName];
}

View File

@ -1,13 +1,13 @@
{ {
"name": "@nocobase/database", "name": "@nocobase/database",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@nocobase/logger": "0.20.0-alpha.8", "@nocobase/logger": "0.20.0-alpha.9",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"async-mutex": "^0.3.2", "async-mutex": "^0.3.2",
"chalk": "^4.1.1", "chalk": "^4.1.1",
"cron-parser": "4.4.0", "cron-parser": "4.4.0",

View File

@ -0,0 +1,39 @@
import { mockDatabase } from '../';
import { Database } from '../../database';
describe('nanoid field', () => {
let db: Database;
beforeEach(async () => {
db = mockDatabase();
await db.clean({ drop: true });
});
afterEach(async () => {
await db.close();
});
it('should create nanoid field type', async () => {
const Test = db.collection({
name: 'tests',
autoGenId: false,
fields: [
{
type: 'nanoid',
name: 'id',
primaryKey: true,
size: 21,
customAlphabet: '1234567890abcdef',
},
{
type: 'nanoid',
name: 'id2',
},
],
});
await Test.sync();
const test = await Test.model.create();
expect(test.id).toHaveLength(21);
expect(test.id2).toHaveLength(12);
});
});

View File

@ -25,6 +25,7 @@ import { TimeFieldOptions } from './time-field';
import { UidFieldOptions } from './uid-field'; import { UidFieldOptions } from './uid-field';
import { UUIDFieldOptions } from './uuid-field'; import { UUIDFieldOptions } from './uuid-field';
import { VirtualFieldOptions } from './virtual-field'; import { VirtualFieldOptions } from './virtual-field';
import { NanoidFieldOptions } from './nanoid-field';
export * from './array-field'; export * from './array-field';
export * from './belongs-to-field'; export * from './belongs-to-field';
@ -48,6 +49,7 @@ export * from './time-field';
export * from './uid-field'; export * from './uid-field';
export * from './uuid-field'; export * from './uuid-field';
export * from './virtual-field'; export * from './virtual-field';
export * from './nanoid-field';
export type FieldOptions = export type FieldOptions =
| BaseFieldOptions | BaseFieldOptions
@ -70,6 +72,7 @@ export type FieldOptions =
| DateFieldOptions | DateFieldOptions
| UidFieldOptions | UidFieldOptions
| UUIDFieldOptions | UUIDFieldOptions
| NanoidFieldOptions
| PasswordFieldOptions | PasswordFieldOptions
| ContextFieldOptions | ContextFieldOptions
| BelongsToFieldOptions | BelongsToFieldOptions

View File

@ -0,0 +1,40 @@
import { DataTypes } from 'sequelize';
import { BaseColumnFieldOptions, Field } from './field';
import { customAlphabet, nanoid } from 'nanoid';
const DEFAULT_SIZE = 12;
export class NanoidField extends Field {
get dataType() {
return DataTypes.STRING;
}
init() {
const { name, size, customAlphabet: customAlphabetOptions } = this.options;
this.listener = async (instance) => {
const value = instance.get(name);
if (!value) {
const nanoIdFunc = customAlphabetOptions ? customAlphabet(customAlphabetOptions) : nanoid;
instance.set(name, nanoIdFunc(size || DEFAULT_SIZE));
}
};
}
bind() {
super.bind();
this.on('beforeCreate', this.listener);
this.on('beforeUpdate', this.listener);
}
unbind() {
super.unbind();
this.off('beforeCreate', this.listener);
this.off('beforeUpdate', this.listener);
}
}
export interface NanoidFieldOptions extends BaseColumnFieldOptions {
type: 'nanoid';
size?: number;
customAlphabet?: string;
}

View File

@ -1,9 +1,10 @@
const postgres = { const postgres = {
'character varying': 'string', 'character varying': ['string', 'uuid', 'nanoid'],
varchar: 'string', varchar: ['string', 'uuid', 'nanoid'],
char: ['string', 'uuid', 'nanoid'],
character: 'string', character: 'string',
text: 'text', text: 'text',
char: 'string',
oid: 'string', oid: 'string',
name: 'string', name: 'string',
@ -29,7 +30,7 @@ const postgres = {
path: 'json', path: 'json',
polygon: 'json', polygon: 'json',
circle: 'json', circle: 'json',
uuid: 'string', uuid: 'uuid',
}; };
const mysql = { const mysql = {
@ -41,10 +42,10 @@ const mysql = {
'tinyint unsigned': ['integer', 'boolean', 'sort'], 'tinyint unsigned': ['integer', 'boolean', 'sort'],
'mediumint unsigned': ['integer', 'boolean', 'sort'], 'mediumint unsigned': ['integer', 'boolean', 'sort'],
char: 'string', char: ['string', 'uuid', 'nanoid'],
varchar: ['string', 'uuid', 'nanoid'],
date: 'date', date: 'date',
time: 'time', time: 'time',
varchar: 'string',
text: 'text', text: 'text',
longtext: 'text', longtext: 'text',
int: ['integer', 'sort'], int: ['integer', 'sort'],
@ -64,7 +65,7 @@ const mysql = {
const sqlite = { const sqlite = {
text: 'text', text: 'text',
varchar: 'string', varchar: ['string', 'uuid', 'nanoid'],
integer: 'integer', integer: 'integer',
real: 'real', real: 'real',

View File

@ -1,13 +1,13 @@
{ {
"name": "@nocobase/devtools", "name": "@nocobase/devtools",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./src/index.js", "main": "./src/index.js",
"dependencies": { "dependencies": {
"@nocobase/build": "0.20.0-alpha.8", "@nocobase/build": "0.20.0-alpha.9",
"@nocobase/client": "0.20.0-alpha.8", "@nocobase/client": "0.20.0-alpha.9",
"@nocobase/test": "0.20.0-alpha.8", "@nocobase/test": "0.20.0-alpha.9",
"@types/koa": "^2.13.4", "@types/koa": "^2.13.4",
"@types/koa-bodyparser": "^4.3.4", "@types/koa-bodyparser": "^4.3.4",
"@types/lodash": "^4.14.177", "@types/lodash": "^4.14.177",

View File

@ -1,13 +1,13 @@
{ {
"name": "@nocobase/evaluators", "name": "@nocobase/evaluators",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@formulajs/formulajs": "4.2.0", "@formulajs/formulajs": "4.2.0",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"mathjs": "^10.6.0" "mathjs": "^10.6.0"
}, },
"repository": { "repository": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/logger", "name": "@nocobase/logger",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "nocobase logging library", "description": "nocobase logging library",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",

View File

@ -1,12 +1,12 @@
{ {
"name": "@nocobase/resourcer", "name": "@nocobase/resourcer",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"koa-compose": "^4.1.0", "koa-compose": "^4.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/sdk", "name": "@nocobase/sdk",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/server", "name": "@nocobase/server",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "lib/index.js", "main": "lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -10,18 +10,18 @@
"@koa/cors": "^3.1.0", "@koa/cors": "^3.1.0",
"@koa/multer": "^3.0.2", "@koa/multer": "^3.0.2",
"@koa/router": "^9.4.0", "@koa/router": "^9.4.0",
"@nocobase/acl": "0.20.0-alpha.8", "@nocobase/acl": "0.20.0-alpha.9",
"@nocobase/actions": "0.20.0-alpha.8", "@nocobase/actions": "0.20.0-alpha.9",
"@nocobase/auth": "0.20.0-alpha.8", "@nocobase/auth": "0.20.0-alpha.9",
"@nocobase/cache": "0.20.0-alpha.8", "@nocobase/cache": "0.20.0-alpha.9",
"@nocobase/data-source-manager": "0.20.0-alpha.8", "@nocobase/data-source-manager": "0.20.0-alpha.9",
"@nocobase/database": "0.20.0-alpha.8", "@nocobase/database": "0.20.0-alpha.9",
"@nocobase/evaluators": "0.20.0-alpha.8", "@nocobase/evaluators": "0.20.0-alpha.9",
"@nocobase/logger": "0.20.0-alpha.8", "@nocobase/logger": "0.20.0-alpha.9",
"@nocobase/resourcer": "0.20.0-alpha.8", "@nocobase/resourcer": "0.20.0-alpha.9",
"@nocobase/sdk": "0.20.0-alpha.8", "@nocobase/sdk": "0.20.0-alpha.9",
"@nocobase/telemetry": "0.20.0-alpha.8", "@nocobase/telemetry": "0.20.0-alpha.9",
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"@types/decompress": "4.2.4", "@types/decompress": "4.2.4",
"@types/ini": "^1.3.31", "@types/ini": "^1.3.31",
"@types/koa-send": "^4.1.3", "@types/koa-send": "^4.1.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/telemetry", "name": "@nocobase/telemetry",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "nocobase telemetry library", "description": "nocobase telemetry library",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
@ -11,7 +11,7 @@
"directory": "packages/telemetry" "directory": "packages/telemetry"
}, },
"dependencies": { "dependencies": {
"@nocobase/utils": "0.20.0-alpha.8", "@nocobase/utils": "0.20.0-alpha.9",
"@opentelemetry/api": "^1.7.0", "@opentelemetry/api": "^1.7.0",
"@opentelemetry/instrumentation": "^0.46.0", "@opentelemetry/instrumentation": "^0.46.0",
"@opentelemetry/resources": "^1.19.0", "@opentelemetry/resources": "^1.19.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/test", "name": "@nocobase/test",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "lib/index.js", "main": "lib/index.js",
"module": "./src/index.ts", "module": "./src/index.ts",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
@ -40,7 +40,7 @@
}, },
"dependencies": { "dependencies": {
"@faker-js/faker": "8.1.0", "@faker-js/faker": "8.1.0",
"@nocobase/server": "0.20.0-alpha.8", "@nocobase/server": "0.20.0-alpha.9",
"@playwright/test": "^1.42.1", "@playwright/test": "^1.42.1",
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/utils", "name": "@nocobase/utils",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "lib/index.js", "main": "lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "权限控制", "displayName.zh-CN": "权限控制",
"description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.", "description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.",
"description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。", "description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/acl", "homepage": "https://docs.nocobase.com/handbook/acl",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-action-bulk-edit", "name": "@nocobase/plugin-action-bulk-edit",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-bulk-edit", "homepage": "https://docs.nocobase.com/handbook/action-bulk-edit",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-action-bulk-update", "name": "@nocobase/plugin-action-bulk-update",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-bulk-update", "homepage": "https://docs.nocobase.com/handbook/action-bulk-update",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-action-duplicate", "name": "@nocobase/plugin-action-duplicate",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-duplicate", "homepage": "https://docs.nocobase.com/handbook/action-duplicate",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-duplicate", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-duplicate",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-action-print", "name": "@nocobase/plugin-action-print",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-print", "homepage": "https://docs.nocobase.com/handbook/action-print",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-print", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-print",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-api-doc", "name": "@nocobase/plugin-api-doc",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "API documentation", "displayName": "API documentation",
"displayName.zh-CN": "API 文档", "displayName.zh-CN": "API 文档",
"description": "An OpenAPI documentation generator for NocoBase HTTP API.", "description": "An OpenAPI documentation generator for NocoBase HTTP API.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "认证API 密钥", "displayName.zh-CN": "认证API 密钥",
"description": "Allows users to use API key to access application's HTTP API", "description": "Allows users to use API key to access application's HTTP API",
"description.zh-CN": "允许用户使用 API 密钥访问应用的 HTTP API", "description.zh-CN": "允许用户使用 API 密钥访问应用的 HTTP API",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/api-keys", "homepage": "https://docs.nocobase.com/handbook/api-keys",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-audit-logs", "name": "@nocobase/plugin-audit-logs",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "Audit logs (deprecated)", "displayName": "Audit logs (deprecated)",
"displayName.zh-CN": "审计日志(废弃)", "displayName.zh-CN": "审计日志(废弃)",
"description": "This plugin is deprecated. There will be a new audit log plugin in the future.", "description": "This plugin is deprecated. There will be a new audit log plugin in the future.",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-auth", "name": "@nocobase/plugin-auth",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/auth", "homepage": "https://docs.nocobase.com/handbook/auth",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "应用的备份与还原", "displayName.zh-CN": "应用的备份与还原",
"description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.", "description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.",
"description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。", "description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/backup-restore", "homepage": "https://docs.nocobase.com/handbook/backup-restore",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-calendar", "name": "@nocobase/plugin-calendar",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "Calendar", "displayName": "Calendar",
"displayName.zh-CN": "日历", "displayName.zh-CN": "日历",
"description": "Provides callendar collection template and block for managing date data, typically for date/time related information such as events, appointments, tasks, and so on.", "description": "Provides callendar collection template and block for managing date data, typically for date/time related information such as events, appointments, tasks, and so on.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "认证CAS", "displayName.zh-CN": "认证CAS",
"description": "CAS authentication.", "description": "CAS authentication.",
"description.zh-CN": "通过 CAS 协议认证身份。", "description.zh-CN": "通过 CAS 协议认证身份。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/auth-cas", "homepage": "https://docs.nocobase.com/handbook/auth-cas",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "图表(废弃)", "displayName.zh-CN": "图表(废弃)",
"description": "The plugin has been deprecated, please use the data visualization plugin instead.", "description": "The plugin has been deprecated, please use the data visualization plugin instead.",
"description.zh-CN": "已废弃插件,请使用数据可视化插件代替。", "description.zh-CN": "已废弃插件,请使用数据可视化插件代替。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"devDependencies": { "devDependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-china-region", "name": "@nocobase/plugin-china-region",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "Administrative divisions of China", "displayName": "Administrative divisions of China",
"displayName.zh-CN": "中国行政区划", "displayName.zh-CN": "中国行政区划",
"description": "Provides data and field type for administrative divisions of China.", "description": "Provides data and field type for administrative divisions of China.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "WEB 客户端", "displayName.zh-CN": "WEB 客户端",
"description": "Provides a client interface for the NocoBase server", "description": "Provides a client interface for the NocoBase server",
"description.zh-CN": "为 NocoBase 服务端提供客户端界面", "description.zh-CN": "为 NocoBase 服务端提供客户端界面",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"devDependencies": { "devDependencies": {

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据源:主数据库", "displayName.zh-CN": "数据源:主数据库",
"description": "NocoBase main database, supports relational databases such as MySQL, PostgreSQL, SQLite and so on.", "description": "NocoBase main database, supports relational databases such as MySQL, PostgreSQL, SQLite and so on.",
"description.zh-CN": "NocoBase 主数据库,支持 MySQL、PostgreSQL、SQLite 等关系型数据库。", "description.zh-CN": "NocoBase 主数据库,支持 MySQL、PostgreSQL、SQLite 等关系型数据库。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/data-source-main", "homepage": "https://docs.nocobase.com/handbook/data-source-main",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/data-source-main", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/data-source-main",

View File

@ -496,23 +496,16 @@ test.describe('form item & view form', () => {
}); });
}); });
test('field component', async ({ page, mockPage, mockRecords }) => { test('field component', async ({ page, mockPage, mockRecord }) => {
await (async (mockPage, mockRecords) => { const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit(); await mockRecord('general');
const record = (await mockRecords('general', 1))[0]; await nocoPage.goto();
await nocoPage.goto();
return record; await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(mockPage, mockRecords); await page.getByLabel(`block-item-CollectionField-general-form-general.oneToMany-oneToMany`).hover();
await (async (page: Page) => { await page
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click(); .getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.oneToMany`)
})(page); .hover();
await (async (page: Page, fieldName: string) => {
await page.getByLabel(`block-item-CollectionField-general-form-general.${fieldName}-${fieldName}`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.${fieldName}`)
.hover();
})(page, 'oneToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click(); await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项 // 断言支持的选项

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-custom-request", "name": "@nocobase/plugin-custom-request",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-custom-request", "homepage": "https://docs.nocobase.com/handbook/action-custom-request",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-custom-request", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-custom-request",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-data-source-manager", "name": "@nocobase/plugin-data-source-manager",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"displayName": "Data source manager", "displayName": "Data source manager",
"displayName.zh-CN": "数据源管理", "displayName.zh-CN": "数据源管理",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-data-visualization", "name": "@nocobase/plugin-data-visualization",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "Data visualization", "displayName": "Data visualization",
"displayName.zh-CN": "数据可视化", "displayName.zh-CN": "数据可视化",
"description": "Provides data visualization feature, including chart block and chart filter block, support line charts, area charts, bar charts and more than a dozen kinds of charts, you can also extend more chart types.", "description": "Provides data visualization feature, including chart block and chart filter block, support line charts, area charts, bar charts and more than a dozen kinds of charts, you can also extend more chart types.",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-disable-pm-add", "name": "@nocobase/plugin-disable-pm-add",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"peerDependencies": { "peerDependencies": {
"@nocobase/client": "0.x", "@nocobase/client": "0.x",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "错误处理器", "displayName.zh-CN": "错误处理器",
"description": "Handling application errors and exceptions.", "description": "Handling application errors and exceptions.",
"description.zh-CN": "处理应用程序中的错误和异常。", "description.zh-CN": "处理应用程序中的错误和异常。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"devDependencies": { "devDependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-excel-formula-field", "name": "@nocobase/plugin-excel-formula-field",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "操作:导出记录", "displayName.zh-CN": "操作:导出记录",
"description": "Export filtered records to excel, you can configure which fields to export.", "description": "Export filtered records to excel, you can configure which fields to export.",
"description.zh-CN": "导出筛选后的记录到 Excel 中,可以配置导出哪些字段。", "description.zh-CN": "导出筛选后的记录到 Excel 中,可以配置导出哪些字段。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-export", "homepage": "https://docs.nocobase.com/handbook/action-export",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-file-manager", "name": "@nocobase/plugin-file-manager",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "File manager", "displayName": "File manager",
"displayName.zh-CN": "文件管理器", "displayName.zh-CN": "文件管理器",
"description": "Provides files storage services with files collection template and attachment field.", "description": "Provides files storage services with files collection template and attachment field.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表字段:公式", "displayName.zh-CN": "数据表字段:公式",
"description": "Configure and store the results of calculations between multiple field values in the same record, supporting both Math.js and Excel formula functions.", "description": "Configure and store the results of calculations between multiple field values in the same record, supporting both Math.js and Excel formula functions.",
"description.zh-CN": "可以配置并存储同一条记录的多字段值之间的计算结果,支持 Math.js 和 Excel formula functions 两种引擎", "description.zh-CN": "可以配置并存储同一条记录的多字段值之间的计算结果,支持 Math.js 和 Excel formula functions 两种引擎",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/field-formula", "homepage": "https://docs.nocobase.com/handbook/field-formula",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-gantt", "name": "@nocobase/plugin-gantt",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"displayName": "Block: Gantt", "displayName": "Block: Gantt",
"displayName.zh-CN": "区块:甘特图", "displayName.zh-CN": "区块:甘特图",
"description": "Provides Gantt block.", "description": "Provides Gantt block.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "可视化数据表管理", "displayName.zh-CN": "可视化数据表管理",
"description": "An ER diagram-like tool. Currently only the Master database is supported.", "description": "An ER diagram-like tool. Currently only the Master database is supported.",
"description.zh-CN": "类似 ER 图的工具,目前只支持主数据库。", "description.zh-CN": "类似 ER 图的工具,目前只支持主数据库。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/graph-collection-manager", "homepage": "https://docs.nocobase.com/handbook/graph-collection-manager",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "区块iframe", "displayName.zh-CN": "区块iframe",
"description": "Create an iframe block on the page to embed and display external web pages or content.", "description": "Create an iframe block on the page to embed and display external web pages or content.",
"description.zh-CN": "在页面上创建和管理iframe用于嵌入和展示外部网页或内容。", "description.zh-CN": "在页面上创建和管理iframe用于嵌入和展示外部网页或内容。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/block-iframe", "homepage": "https://docs.nocobase.com/handbook/block-iframe",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "操作:导入记录", "displayName.zh-CN": "操作:导入记录",
"description": "Import records using excel templates. You can configure which fields to import and templates will be generated automatically.", "description": "Import records using excel templates. You can configure which fields to import and templates will be generated automatically.",
"description.zh-CN": "使用 Excel 模板导入数据,可以配置导入哪些字段,自动生成模板。", "description.zh-CN": "使用 Excel 模板导入数据,可以配置导入哪些字段,自动生成模板。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-import", "homepage": "https://docs.nocobase.com/handbook/action-import",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-kanban", "name": "@nocobase/plugin-kanban",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/block-kanban", "homepage": "https://docs.nocobase.com/handbook/block-kanban",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/block-kanban", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/block-kanban",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-localization-management", "name": "@nocobase/plugin-localization-management",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/localization-management", "homepage": "https://docs.nocobase.com/handbook/localization-management",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/localization-management", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/localization-management",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "日志", "displayName.zh-CN": "日志",
"description": "Server-side logs, mainly including API request logs and system runtime logs, and allows to package and download log files.", "description": "Server-side logs, mainly including API request logs and system runtime logs, and allows to package and download log files.",
"description.zh-CN": "服务端日志,主要包括接口请求日志和系统运行日志,并支持打包和下载日志文件。", "description.zh-CN": "服务端日志,主要包括接口请求日志和系统运行日志,并支持打包和下载日志文件。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/logger", "homepage": "https://docs.nocobase.com/handbook/logger",

View File

@ -2,7 +2,7 @@
"name": "@nocobase/plugin-map", "name": "@nocobase/plugin-map",
"displayName": "Block: Map", "displayName": "Block: Map",
"displayName.zh-CN": "区块:地图", "displayName.zh-CN": "区块:地图",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "Map block, support Gaode map and Google map, you can also extend more map types.", "description": "Map block, support Gaode map and Google map, you can also extend more map types.",
"description.zh-CN": "地图区块,支持高德地图和 Google 地图,你也可以扩展更多地图类型。", "description.zh-CN": "地图区块,支持高德地图和 Google 地图,你也可以扩展更多地图类型。",
"license": "AGPL-3.0", "license": "AGPL-3.0",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数学公式字段", "displayName.zh-CN": "数学公式字段",
"description": "A powerful mathematical formula field plugin designed to provide flexible mathematical and formula calculation capabilities for databases or data management systems. It seamlessly integrates with various database systems such as MySQL, PostgreSQL, and offers an intuitive user interface for defining and executing mathematical formula fields.", "description": "A powerful mathematical formula field plugin designed to provide flexible mathematical and formula calculation capabilities for databases or data management systems. It seamlessly integrates with various database systems such as MySQL, PostgreSQL, and offers an intuitive user interface for defining and executing mathematical formula fields.",
"description.zh-CN": "一个功能强大的数学公式字段插件旨在为数据库或数据管理系统提供灵活的数学计算和公式计算功能。它可以与各种数据库系统如MySQL、PostgreSQL无缝集成并提供直观的用户界面来定义和执行数学公式字段。", "description.zh-CN": "一个功能强大的数学公式字段插件旨在为数据库或数据管理系统提供灵活的数学计算和公式计算功能。它可以与各种数据库系统如MySQL、PostgreSQL无缝集成并提供直观的用户界面来定义和执行数学公式字段。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"devDependencies": { "devDependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-mobile-client", "name": "@nocobase/plugin-mobile-client",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/mobile-client", "homepage": "https://docs.nocobase.com/handbook/mobile-client",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/mobile-client", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/mobile-client",

View File

@ -2,7 +2,7 @@
"name": "@nocobase/plugin-mock-collections", "name": "@nocobase/plugin-mock-collections",
"displayName": "mock-collections", "displayName": "mock-collections",
"description": "mock-collections", "description": "mock-collections",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"peerDependencies": { "peerDependencies": {

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "多应用管理器", "displayName.zh-CN": "多应用管理器",
"description": "Dynamically create multiple apps without separate deployments.", "description": "Dynamically create multiple apps without separate deployments.",
"description.zh-CN": "无需单独部署即可动态创建多个应用。", "description.zh-CN": "无需单独部署即可动态创建多个应用。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/multi-app-manager", "homepage": "https://docs.nocobase.com/handbook/multi-app-manager",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "多应用数据表共享", "displayName.zh-CN": "多应用数据表共享",
"description": "", "description": "",
"description.zh-CN": "", "description.zh-CN": "",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"devDependencies": { "devDependencies": {
"@formily/react": "2.x", "@formily/react": "2.x",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-notifications", "name": "@nocobase/plugin-notifications",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"description": "", "description": "",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "认证OIDC", "displayName.zh-CN": "认证OIDC",
"description": "OIDC (OpenID Connect) authentication.", "description": "OIDC (OpenID Connect) authentication.",
"description.zh-CN": "通过 OIDC (OpenID Connect) 协议认证身份。", "description.zh-CN": "通过 OIDC (OpenID Connect) 协议认证身份。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/auth-oidc", "homepage": "https://docs.nocobase.com/handbook/auth-oidc",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "认证SAML 2.0", "displayName.zh-CN": "认证SAML 2.0",
"description": "SAML 2.0 authentication.", "description": "SAML 2.0 authentication.",
"description.zh-CN": "通过 SAML 2.0 协议认证身份。", "description.zh-CN": "通过 SAML 2.0 协议认证身份。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/auth-saml", "homepage": "https://docs.nocobase.com/handbook/auth-saml",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-sample-hello", "name": "@nocobase/plugin-sample-hello",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"displayName": "Hello", "displayName": "Hello",
"displayName.zh-CN": "Hello", "displayName.zh-CN": "Hello",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表字段:自动编码", "displayName.zh-CN": "数据表字段:自动编码",
"description": "Automatically generate codes based on configured rules, supporting combinations of dates, numbers, and text.", "description": "Automatically generate codes based on configured rules, supporting combinations of dates, numbers, and text.",
"description.zh-CN": "根据配置的规则自动生成编码,支持日期、数字、文本的组合。", "description.zh-CN": "根据配置的规则自动生成编码,支持日期、数字、文本的组合。",
"version": "0.20.0-alpha.8", "version": "0.20.0-alpha.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./dist/server/index.js", "main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/field-sequence", "homepage": "https://docs.nocobase.com/handbook/field-sequence",

Some files were not shown because too many files have changed in this diff Show More