From 227b9c59a1721928279eb806e6426159a071aa14 Mon Sep 17 00:00:00 2001 From: Katherine Date: Mon, 14 Oct 2024 17:47:33 +0800 Subject: [PATCH 001/307] style: center icon when action buttons use icon-only (#5413) --- .../src/schema-component/antd/action/Action.style.ts | 7 +++++++ .../client/src/schema-component/antd/action/Action.tsx | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/action/Action.style.ts b/packages/core/client/src/schema-component/antd/action/Action.style.ts index 1f4d4061b77..f283358e4c2 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.style.ts +++ b/packages/core/client/src/schema-component/antd/action/Action.style.ts @@ -47,6 +47,13 @@ const useStyles = genStyleHook('nb-action', (token) => { }, }, }, + + '.ant-btn-icon': { + marginInlineEnd: '0px !important', + }, + '.nb-action-title': { + marginInlineStart: '8px', + }, }, }; }); diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 3174959b976..a67ee33aefc 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -377,7 +377,6 @@ function RenderButton({ if (!designable && (field?.data?.hidden || !aclCtx)) { return null; } - return ( - {actionTitle} + {actionTitle && {actionTitle}} ); From 1523a9a0f7c42b839e4cb0eda91620094228cdc7 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Mon, 14 Oct 2024 18:18:09 +0800 Subject: [PATCH 002/307] fix(zIndex): fix edit profile drawer being covered by supage issue (#5409) * test: add e2e test * fix(zIndex): fix the issue of edit profile drawer being covered by subpages * fix: change password * chore: fix comment --- .../modules/popup/__e2e__/templatesOfBug.ts | 233 ++++++++++++++++++ .../src/modules/popup/__e2e__/zIndex.test.ts | 30 ++- .../antd/action/Action.Drawer.tsx | 4 +- .../antd/action/Action.Modal.tsx | 4 +- .../core/client/src/user/ChangePassword.tsx | 3 + packages/core/client/src/user/EditProfile.tsx | 3 + 6 files changed, 272 insertions(+), 5 deletions(-) diff --git a/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts b/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts index 2d71859186c..680585837d9 100644 --- a/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts +++ b/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts @@ -5124,3 +5124,236 @@ export const zIndexOfSubpage = { 'x-index': 1, }, }; +export const zIndexEditProfile = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-app-version': '1.3.32-beta', + properties: { + '17v3l3wra1q': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + 'x-app-version': '1.3.32-beta', + properties: { + tpentznu41j: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + '16wj734kw8c': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + gh2evps4nft: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'jlclfunyxlo', + 'x-async': false, + 'x-index': 1, + }, + juqs9wupfcc: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + onge8etkwkq: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + properties: { + '44abnpgy9hl': { + 'x-uid': '2m4j1wrmt2k', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'open subpage', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + iconColor: '#1677FF', + danger: false, + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + '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': 'popup:addTab', + 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': 'popup:common:addBlock', + 'x-uid': 'pncs8uoz02h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'i1n4wf3wqt8', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '537y7twqq1x', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'j652bs2ocx7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lmmfco4ti1k', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'qxsdgdxd7zd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rxecd82tr2t', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '8mdf3toqg65', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'x2e6vc3l16a', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'eqlzylp6c53', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '2e8trd4olie', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ay7xv8zc868', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/modules/popup/__e2e__/zIndex.test.ts b/packages/core/client/src/modules/popup/__e2e__/zIndex.test.ts index 3d05489c244..a77e96d27fe 100644 --- a/packages/core/client/src/modules/popup/__e2e__/zIndex.test.ts +++ b/packages/core/client/src/modules/popup/__e2e__/zIndex.test.ts @@ -8,7 +8,7 @@ */ import { expect, test } from '@nocobase/test/e2e'; -import { T2797, T2838, zIndexOfSubpage } from './templatesOfBug'; +import { T2797, T2838, zIndexEditProfile, zIndexOfSubpage } from './templatesOfBug'; test.describe('z-index of dialog', () => { test('edit block title', async ({ page, mockPage }) => { @@ -73,4 +73,32 @@ test.describe('z-index of dialog', () => { .click(); await expect(page.getByText('Add condition', { exact: true })).toBeVisible(); }); + + test('edit profile', async ({ page, mockPage }) => { + await mockPage(zIndexEditProfile).goto(); + + // open subpage, and then open the Edit Profile drawer + await page.getByLabel('action-Action.Link-open').click(); + await page.getByTestId('user-center-button').hover(); + await page.getByRole('menuitem', { name: 'Edit profile' }).click(); + await expect(page.getByTestId('drawer-Action.Drawer-Edit profile')).toBeVisible(); + + // click the Cancel button to close the drawer + await page.getByLabel('action-Action-Cancel').click(); + await expect(page.getByTestId('drawer-Action.Drawer-Edit profile')).not.toBeVisible(); + }); + + test('change password', async ({ page, mockPage }) => { + await mockPage(zIndexEditProfile).goto(); + + // open subpage, and then open the Change password drawer + await page.getByLabel('action-Action.Link-open').click(); + await page.getByTestId('user-center-button').hover(); + await page.getByRole('menuitem', { name: 'Change password' }).click(); + await expect(page.getByTestId('drawer-Action.Drawer-Change password')).toBeVisible(); + + // click the Cancel button to close the drawer + await page.getByLabel('action-Action-Cancel').click(); + await expect(page.getByTestId('drawer-Action.Drawer-Change password')).not.toBeVisible(); + }); }); diff --git a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx index 4a1c7da78f9..9847e5c9c2d 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx @@ -37,7 +37,7 @@ const openSizeWidthMap = new Map([ ]); export const InternalActionDrawer: React.FC = observer( (props) => { - const { footerNodeName = 'Action.Drawer.Footer', ...others } = props; + const { footerNodeName = 'Action.Drawer.Footer', zIndex: _zIndex, ...others } = props; const { visible, setVisible, openSize = 'middle', drawerProps, modalProps } = useActionContext(); const schema = useFieldSchema(); const field = useField(); @@ -63,7 +63,7 @@ export const InternalActionDrawer: React.FC = observer( useSetAriaLabelForDrawer(visible); } - const zIndex = parentZIndex + (props.level || 0); + const zIndex = _zIndex || parentZIndex + (props.level || 0); return ( diff --git a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx index bf054edf250..5478cc03179 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx @@ -39,7 +39,7 @@ const openSizeWidthMap = new Map([ export const InternalActionModal: React.FC> = observer( (props) => { - const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props; + const { footerNodeName = 'Action.Modal.Footer', width, zIndex: _zIndex, ...others } = props; const { visible, setVisible, openSize = 'middle', modalProps } = useActionContext(); const actualWidth = width ?? openSizeWidthMap.get(openSize); const schema = useFieldSchema(); @@ -71,7 +71,7 @@ export const InternalActionModal: React.FC> = obse useSetAriaLabelForModal(visible); } - const zIndex = parentZIndex + (props.level || 0); + const zIndex = _zIndex || parentZIndex + (props.level || 0); return ( diff --git a/packages/core/client/src/user/ChangePassword.tsx b/packages/core/client/src/user/ChangePassword.tsx index 226d9d9eb2c..88fc12b6f14 100644 --- a/packages/core/client/src/user/ChangePassword.tsx +++ b/packages/core/client/src/user/ChangePassword.tsx @@ -50,6 +50,9 @@ const schema: ISchema = { [uid()]: { 'x-decorator': 'Form', 'x-component': 'Action.Drawer', + 'x-component-props': { + zIndex: 10000, + }, type: 'void', title: '{{t("Change password")}}', properties: { diff --git a/packages/core/client/src/user/EditProfile.tsx b/packages/core/client/src/user/EditProfile.tsx index 9ac1849aec4..e60c78d87a0 100644 --- a/packages/core/client/src/user/EditProfile.tsx +++ b/packages/core/client/src/user/EditProfile.tsx @@ -71,6 +71,9 @@ const schema: ISchema = { useValues: '{{ useCurrentUserValues }}', }, 'x-component': 'Action.Drawer', + 'x-component-props': { + zIndex: 10000, + }, type: 'void', title: '{{t("Edit profile")}}', properties: { From 3e8326f94132db6347348a4945d6466c1095aba4 Mon Sep 17 00:00:00 2001 From: YANG QIA <2013xile@gmail.com> Date: Mon, 14 Oct 2024 23:25:19 +0800 Subject: [PATCH 003/307] fix(l10n): issue of publishing locale resources (#5416) --- packages/core/cache/src/cache-manager.ts | 2 +- packages/core/server/src/locale/locale.ts | 6 +++-- .../src/server/__tests__/actions.test.ts | 25 +++++++++++++++++++ .../plugin-localization/src/server/plugin.ts | 1 + .../src/server/resources.ts | 4 +-- 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/core/cache/src/cache-manager.ts b/packages/core/cache/src/cache-manager.ts index e39545b10e6..79ff7492459 100644 --- a/packages/core/cache/src/cache-manager.ts +++ b/packages/core/cache/src/cache-manager.ts @@ -100,7 +100,7 @@ export class CacheManager { async createCache(options: { name: string; prefix?: string; store?: string; [key: string]: any }) { const { name, prefix, store = this.defaultStore, ...config } = options; - if (!lodash.isEmpty(config)) { + if (!lodash.isEmpty(config) || store === 'memory') { const newStore = await this.createStore({ name, storeType: store, ...config }); return this.newCache({ name, prefix, store: newStore }); } diff --git a/packages/core/server/src/locale/locale.ts b/packages/core/server/src/locale/locale.ts index 4ec017eb005..3f7a39f2c1e 100644 --- a/packages/core/server/src/locale/locale.ts +++ b/packages/core/server/src/locale/locale.ts @@ -18,6 +18,7 @@ export interface ResourceStorer { getResources(lang: string): Promise<{ [ns: string]: Record; }>; + reset?: () => Promise; } export class Locale { @@ -45,14 +46,15 @@ export class Locale { name: 'locale', prefix: 'locale', store: 'memory', - max: 2000 }); await this.get(this.defaultLang); } async reload() { - await this.cache.reset(); + const storers = Array.from(this.resourceStorers.getValues()); + const promises = storers.map((storer) => storer.reset()); + await Promise.all([this.cache.reset(), ...promises]); } setLocaleFn(name: string, fn: (lang: string) => Promise) { diff --git a/packages/plugins/@nocobase/plugin-localization/src/server/__tests__/actions.test.ts b/packages/plugins/@nocobase/plugin-localization/src/server/__tests__/actions.test.ts index 57429325039..1f5b7fe04cd 100644 --- a/packages/plugins/@nocobase/plugin-localization/src/server/__tests__/actions.test.ts +++ b/packages/plugins/@nocobase/plugin-localization/src/server/__tests__/actions.test.ts @@ -27,9 +27,11 @@ describe('actions', () => { }; beforeAll(async () => { + process.env.APP_ENV = 'production'; app = await createMockServer({ plugins: ['localization'], }); + await app.emitAsync('afterLoad'); db = app.db; repo = db.getRepository('localizationTexts'); agent = app.agent(); @@ -106,5 +108,28 @@ describe('actions', () => { expect(res.body.data[0].translation).toBeUndefined(); }); }); + + it('publish', async () => { + await repo.create({ + values: [ + { + module: 'test', + text: 'text', + translations: [ + { + locale: 'en-US', + translation: 'translation', + }, + ], + }, + ], + }); + const { resources } = await app.localeManager.get('en-US'); + expect(resources.test).toBeUndefined(); + await agent.resource('localization').publish(); + const { resources: resources2 } = await app.localeManager.get('en-US'); + expect(resources2.test).toBeDefined(); + expect(resources2.test.text).toBe('translation'); + }); }); }); diff --git a/packages/plugins/@nocobase/plugin-localization/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-localization/src/server/plugin.ts index 5bfa09fcdbe..b2d4fc722ba 100644 --- a/packages/plugins/@nocobase/plugin-localization/src/server/plugin.ts +++ b/packages/plugins/@nocobase/plugin-localization/src/server/plugin.ts @@ -118,6 +118,7 @@ export class PluginLocalizationServer extends Plugin { this.app.localeManager.registerResourceStorer('plugin-localization', { getResources: (lang: string) => this.resources.getResources(lang), + reset: () => this.resources.reset(), }); } diff --git a/packages/plugins/@nocobase/plugin-localization/src/server/resources.ts b/packages/plugins/@nocobase/plugin-localization/src/server/resources.ts index bef751b3c5b..949829454b7 100644 --- a/packages/plugins/@nocobase/plugin-localization/src/server/resources.ts +++ b/packages/plugins/@nocobase/plugin-localization/src/server/resources.ts @@ -83,7 +83,7 @@ export default class Resources { await this.cache.set(`texts`, [...existTexts, ...newTexts]); } - async resetCache(locale: string) { - await this.cache.del(`translations:${locale}`); + async reset() { + await this.cache.reset(); } } From f92713bd6d8502d4c629bfba82ba1a176391af2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 08:36:09 +0800 Subject: [PATCH 004/307] chore(deps): bump @types/decompress from 4.2.4 to 4.2.7 (#5419) Bumps [@types/decompress](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/decompress) from 4.2.4 to 4.2.7. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/decompress) --- updated-dependencies: - dependency-name: "@types/decompress" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/core/server/package.json | 2 +- yarn.lock | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/core/server/package.json b/packages/core/server/package.json index 89672a884fb..8eb0b4ebe5f 100644 --- a/packages/core/server/package.json +++ b/packages/core/server/package.json @@ -22,7 +22,7 @@ "@nocobase/sdk": "1.3.32-beta", "@nocobase/telemetry": "1.3.32-beta", "@nocobase/utils": "1.3.32-beta", - "@types/decompress": "4.2.4", + "@types/decompress": "4.2.7", "@types/ini": "^1.3.31", "@types/koa-send": "^4.1.3", "@types/multer": "^1.4.5", diff --git a/yarn.lock b/yarn.lock index eabe1ba990d..812b6b0a376 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5655,10 +5655,10 @@ resolved "https://registry.npmmirror.com/@remix-run/router/-/router-1.14.0.tgz#9bc39a5a3a71b81bdb310eba6def5bc3966695b7" integrity sha512-WOHih+ClN7N8oHk9N4JUiMxQJmRVaOxcg8w7F/oHUXzJt920ekASLI/7cYX8XkntDWRhLZtsk6LbGrkgOAvi5A== -"@remix-run/router@1.19.2": - version "1.19.2" - resolved "https://registry.npmmirror.com/@remix-run/router/-/router-1.19.2.tgz#0c896535473291cb41f152c180bedd5680a3b273" - integrity sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA== +"@remix-run/router@1.20.0": + version "1.20.0" + resolved "https://registry.npmmirror.com/@remix-run/router/-/router-1.20.0.tgz#03554155b45d8b529adf635b2f6ad1165d70d8b4" + integrity sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg== "@restart/hooks@^0.4.7": version "0.4.15" @@ -6841,10 +6841,10 @@ dependencies: "@types/ms" "*" -"@types/decompress@4.2.4": - version "4.2.4" - resolved "https://registry.npmmirror.com/@types/decompress/-/decompress-4.2.4.tgz#dd2715d3ac1f566d03e6e302d1a26ffab59f8c5c" - integrity sha512-/C8kTMRTNiNuWGl5nEyKbPiMv6HA+0RbEXzFhFBEzASM6+oa4tJro9b8nj7eRlOFfuLdzUU+DS/GPDlvvzMOhA== +"@types/decompress@4.2.7": + version "4.2.7" + resolved "https://registry.npmmirror.com/@types/decompress/-/decompress-4.2.7.tgz#604f69b69d519ecb74dea1ea0829f159b85e1332" + integrity sha512-9z+8yjKr5Wn73Pt17/ldnmQToaFHZxK0N1GHysuk/JIPT8RIdQeoInM01wWPgypRcvb6VH1drjuFpQ4zmY437g== dependencies: "@types/node" "*" @@ -23047,11 +23047,11 @@ react-router-dom@6.3.0, react-router-dom@6.x, react-router-dom@^6.11.2, react-ro react-router "6.21.0" react-router@6.21.0, react-router@6.3.0, react-router@^6.11.2: - version "6.26.2" - resolved "https://registry.npmmirror.com/react-router/-/react-router-6.26.2.tgz#2f0a68999168954431cdc29dd36cec3b6fa44a7e" - integrity sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A== + version "6.27.0" + resolved "https://registry.npmmirror.com/react-router/-/react-router-6.27.0.tgz#db292474926c814c996c0ff3ef0162d1f9f60ed4" + integrity sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw== dependencies: - "@remix-run/router" "1.19.2" + "@remix-run/router" "1.20.0" react-side-effect@^2.1.0: version "2.1.2" From 2491658692ee798884e08e8adadda562e68e3194 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 08:39:50 +0800 Subject: [PATCH 005/307] chore(deps): bump vite-plugin-css-injected-by-js from 3.3.0 to 3.5.2 (#5420) Bumps [vite-plugin-css-injected-by-js](https://github.com/marco-prontera/vite-plugin-css-injected-by-js) from 3.3.0 to 3.5.2. - [Release notes](https://github.com/marco-prontera/vite-plugin-css-injected-by-js/releases) - [Commits](https://github.com/marco-prontera/vite-plugin-css-injected-by-js/compare/v3.3.0...v3.5.2) --- updated-dependencies: - dependency-name: vite-plugin-css-injected-by-js dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 812b6b0a376..6a1a13a9036 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27176,9 +27176,9 @@ vite-node@1.5.2: vite "^5.0.0" vite-plugin-css-injected-by-js@^3.2.1: - version "3.3.0" - resolved "https://registry.npmmirror.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.0.tgz#c19480a9e42a95c5bced976a9dde1446f9bd91ff" - integrity sha512-xG+jyHNCmUqi/TXp6q88wTJGeAOrNLSyUUTp4qEQ9QZLGcHWQQsCsSSKa59rPMQr8sOzfzmWDd8enGqfH/dBew== + version "3.5.2" + resolved "https://registry.npmmirror.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.2.tgz#1f75d16ad5c05b6b49bf18018099a189ec2e46ad" + integrity sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ== vite-plugin-lib-inject-css@1.2.0: version "1.2.0" From 3b04f010254a5f0ceeba46c6eea9442c9f304441 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 08:45:30 +0800 Subject: [PATCH 006/307] chore(deps): bump ws and @types/ws (#5421) Bumps [ws](https://github.com/websockets/ws) and [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws). These dependencies needed to be updated together. Updates `ws` from 8.15.1 to 8.18.0 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.15.1...8.18.0) Updates `@types/ws` from 8.5.10 to 8.5.12 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws) --- updated-dependencies: - dependency-name: ws dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: "@types/ws" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6a1a13a9036..db9f1b727e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7477,9 +7477,9 @@ integrity sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q== "@types/ws@^8.5.5": - version "8.5.10" - resolved "https://registry.npmmirror.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + version "8.5.12" + resolved "https://registry.npmmirror.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== dependencies: "@types/node" "*" @@ -27687,9 +27687,9 @@ ws@^7.0.0, ws@^7.4.6: integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.13.0: - version "8.15.1" - resolved "https://registry.npmmirror.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997" - integrity sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ== + version "8.18.0" + resolved "https://registry.npmmirror.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== ws@~7.4.0: version "7.4.6" From d705877eec5b3ad82fdfbcb9856d3f3243660698 Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 15 Oct 2024 09:23:24 +0800 Subject: [PATCH 007/307] fix(workflow): workflow node variables do not display inherited collection fields (#5415) * fix: missing fields in the inherited table * fix(client): fix hidden collection * refactor(client): add new api to get all fields on collection manager --------- Co-authored-by: mytharcher --- .../src/data-source/collection/Collection.ts | 4 ++++ .../data-source/collection/CollectionManager.ts | 4 ++++ .../antd/appends-tree-select/AppendsTreeSelect.tsx | 14 ++++++-------- .../plugin-workflow/src/client/variable.tsx | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/core/client/src/data-source/collection/Collection.ts b/packages/core/client/src/data-source/collection/Collection.ts index 977a6769c24..7eaf1daf9fc 100644 --- a/packages/core/client/src/data-source/collection/Collection.ts +++ b/packages/core/client/src/data-source/collection/Collection.ts @@ -250,6 +250,10 @@ export class Collection { return predicate ? filter(this.fields, predicate) : this.fields; } + getAllFields(predicate?: GetCollectionFieldPredicate) { + return this.getFields(predicate); + } + protected getFieldsMap() { if (!this.fieldsMap) { this.fieldsMap = this.getFields().reduce((memo, field) => { diff --git a/packages/core/client/src/data-source/collection/CollectionManager.ts b/packages/core/client/src/data-source/collection/CollectionManager.ts index 0d9265973f3..cb851399990 100644 --- a/packages/core/client/src/data-source/collection/CollectionManager.ts +++ b/packages/core/client/src/data-source/collection/CollectionManager.ts @@ -140,6 +140,10 @@ export class CollectionManager { return this.getCollection(collectionName)?.getFields(predicate) || []; } + getCollectionAllFields(collectionName: string, predicate?: GetCollectionFieldPredicate) { + return this.getCollection(collectionName)?.getAllFields(predicate) || []; + } + /** * @example * getFilterByTK('users', { id: 1 }); // 1 diff --git a/packages/core/client/src/schema-component/antd/appends-tree-select/AppendsTreeSelect.tsx b/packages/core/client/src/schema-component/antd/appends-tree-select/AppendsTreeSelect.tsx index 42e7e2daf07..e62aea2aac7 100644 --- a/packages/core/client/src/schema-component/antd/appends-tree-select/AppendsTreeSelect.tsx +++ b/packages/core/client/src/schema-component/antd/appends-tree-select/AppendsTreeSelect.tsx @@ -12,13 +12,7 @@ import { Tag, TreeSelect } from 'antd'; import type { DefaultOptionType, TreeSelectProps } from 'rc-tree-select/es/TreeSelect'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { - CollectionFieldOptions_deprecated, - parseCollectionName, - useApp, - useCollectionManager_deprecated, - useCompile, -} from '../../..'; +import { CollectionFieldOptions_deprecated, parseCollectionName, useApp, useCompile } from '../../..'; export type AppendsTreeSelectProps = { value: string[] | string; @@ -106,7 +100,11 @@ export const AppendsTreeSelect: React.FC { + const instance = collectionManager.getCollection(name); + // NOTE: condition for compatibility with hidden collections like "attachments" + return instance ? instance.getAllFields(predicate) : []; + }; const treeData = Object.values(optionsMap); const value: string | DefaultOptionType[] = useMemo(() => { if (props.multiple) { diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx index 30ed7804321..e86e19287c4 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx @@ -503,7 +503,7 @@ export function useGetCollectionFields(dataSourceName?) { const app = useApp(); const { collectionManager } = app.dataSourceManager.getDataSource(dataSourceName); - return useCallback((collectionName) => collectionManager.getCollectionFields(collectionName), [collectionManager]); + return useCallback((collectionName) => collectionManager.getCollectionAllFields(collectionName), [collectionManager]); } export function WorkflowVariableInput({ variableOptions, ...props }): JSX.Element { From f6af5ee220f323616496ef097ea68e7c4d5628b1 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Tue, 15 Oct 2024 10:31:34 +0800 Subject: [PATCH 008/307] fix(Collapse): fix undefined error --- packages/core/client/src/block-provider/hooks/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index cc15e0fe2ec..bf8090bfbef 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -1284,7 +1284,7 @@ export const useOptionalFieldList = () => { const isOptionalField = (field) => { const optionalInterfaces = ['select', 'multipleSelect', 'checkbox', 'checkboxGroup', 'chinaRegion']; - return optionalInterfaces.includes(field.interface) && field.uiSchema.enum; + return optionalInterfaces.includes(field?.interface) && field?.uiSchema?.enum; }; export const useAssociationFilterBlockProps = () => { @@ -1329,7 +1329,7 @@ export const useAssociationFilterBlockProps = () => { useEffect(() => { // 由于选项字段不需要触发当前请求,所以请求单独在关系字段的时候触发 - if (!isOptionalField(collectionField) && parseVariableLoading === false) { + if (collectionField && !isOptionalField(collectionField) && parseVariableLoading === false) { run(); } From c765b094bdf0bb8a1f8f33261a0565b918147bf9 Mon Sep 17 00:00:00 2001 From: Zhou Date: Tue, 15 Oct 2024 12:31:09 +0800 Subject: [PATCH 009/307] Update LICENSE.txt --- LICENSE.txt | 86 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 5f834534440..756cbeee6dc 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,8 +1,8 @@ -Updated Date: September 17, 2024 +Updated Date: October 15, 2024 NocoBase License Agreement -NOCOBASE PTE. LTD.,a Singaporean Exempt Private Company Limited by Shares with its principal place of business located at #03-01 ROBINSON 112 ("The Company") https://www.nocobase.com/ issues this License Agreement ("Agreement") to you. You, as an individual or a company ("User"), will be deemed to voluntarily accept all terms of this Agreement by using NocoBase (including but not limited to obtaining NocoBase source code or installation package in any form, installing and using NocoBase, purchasing NocoBase commercial license and services, purchasing NocoBase commercial plugins). If the User does not agree to any term of this Agreement, or cannot accurately understand our interpretation of the relevant terms, please stop using it immediately. +NOCOBASE PTE. LTD.,a Singaporean Exempt Private Company Limited by Shares with its principal place of business located at #03-01 ROBINSON 112 ("The Company") https://www.nocobase.com/ issues this License Agreement ("Agreement") to you. You, as an individual or a company ("the User"), will be deemed to voluntarily accept all terms of this Agreement by using NocoBase (including but not limited to obtaining NocoBase source code or installation package in any form, installing and using NocoBase, purchasing NocoBase commercial license and services, purchasing NocoBase commercial plugins). If the User does not agree to any term of this Agreement, or cannot accurately understand our interpretation of the relevant terms, please stop using it immediately. This Agreement applies to any use, quotation, contract, invoice, and all software delivered by the Company. The User and the Company or NocoBase's agents can no longer sign a separate license agreement for the sale and delivery of the software. @@ -14,15 +14,19 @@ The Company reserves the right to formulate and modify this Agreement from time 1.1 "Software" refers to the NocoBase kernel and plugins placed in the same code repository as the kernel, including their source code, installation packages, images, and all their modifications, updates, and upgrades. -1.2 "Marketplace" refers to the marketplace provided by the Company for selling Software plugins and solutions. +1.2 "Community Edition" refers to the free version of the Software provided to the User through public channels. -1.3 "Commercial Plugin" refers to the paid plugins sold in the Marketplace. +1.3 "Commercial Edition" refers to the paid version of the Software purchased by the User from the Company or its agents, downloaded through exclusive channels, and includes additional benefits. It consists of three versions: Standard Edition, Professional Edition, and Enterprise Edition. -1.4 "Upper Layer Application" refers to a specific business use case application serving internal or external customers of the User, developed based on Software and Commercial Plugins, such as ERP/CRM. +1.4 "Marketplace" refers to the marketplace provided by the Company for selling Software plugins and solutions. -1.5 "Customer" refers to the clients who purchase the User's Upper Layer Application. +1.5 "Commercial Plugin" refers to the paid plugins sold in the Marketplace. -1.6 "Third-Party Open Source Software" refers to open source software provided with Software and Commercial Plugins. They are licensed through various published open source software licenses or copyright notices accompanying such software. +1.6 "Upper Layer Application" refers to a specific business use case application serving internal or external customers of the User, developed based on Software and Commercial Plugins, such as ERP/CRM. + +1.7 "Customer" refers to the clients who purchase the User's Upper Layer Application. + +1.8 "Third-Party Open Source Software" refers to open source software provided with Software and Commercial Plugins. They are licensed through various published open source software licenses or copyright notices accompanying such software. =================================== 2. Intellectual Property Protection @@ -34,7 +38,7 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 3. Disclaimer ============= -3.1 Users shall not use the Software and Commercial Plugins to engage in activities that violate local laws and regulations, religious beliefs. All legal responsibilities and legal consequences arising from the use of Software and Commercial Plugins shall be borne by the User. +3.1 The User shall not use the Software and Commercial Plugins to engage in activities that violate local laws and regulations, religious beliefs. All legal responsibilities and legal consequences arising from the use of Software and Commercial Plugins shall be borne by the User. 3.2 The Company shall not be liable for any direct, indirect, special, incidental, or consequential damages (including but not limited to loss of profits, business interruption, data loss, or business information disclosure) caused by the User's use of the Software and Commercial Plugins, even if it has been previously informed of the possibility of such damages. @@ -44,7 +48,11 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 4.1 The Software uses a dual license type of AGPL-3.0 Open Source License https://www.gnu.org/licenses/agpl-3..en.htm and Commercial License. -4.2 Commercial Plugins use Commercial License. +4.2 The Community Edition uses the AGPL-3.0 Open Source License https://www.gnu.org/licenses/agpl-3.0.en.html. + +4.3 The Commercial Edition (including Standard, Professional, and Enterprise Editions) uses the Commercial License. + +4.4 Commercial Plugins use the Commercial Plugin License. ================================================ 5. Rights and Obligations of Open Source License @@ -52,9 +60,9 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 5.1 The Software can be used for commercial purposes. -5.2 User can sell plugins developed for the Software in the Marketplace. +5.2 The User can sell plugins developed for the Software in the Marketplace. -5.3 Outside the Marketplace, changes and plugins to the Software developed by User or third parties, and third-party applications developed based on the Software must all be open-sourced under the AGPL-3.0 license. +5.3 Outside the Marketplace, changes and plugins to the Software developed by the User or third parties, and third-party applications developed based on the Software must all be open-sourced under the AGPL-3.0 license. 5.4 It is not allowed to remove or change the brand, name, link, version number, license, and other information about NocoBase on the Software interface, except for the main LOGO in the upper left corner of the page. @@ -68,11 +76,11 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 6. Rights of Commercial License =============================== -6.1 Obtain a permanent commercial license of the Software or Commercial Plugin. +6.1 Obtain a permanent commercial license of the Software. 6.2 Get 12 months of upgrade and exclusive technical support. -6.3 The licensed Software and Commercial Plugins can be used for commercial purposes with no restrictions on the number of applications and users. +6.3 The licensed Software can be used for commercial purposes with no restrictions on the number of applications and users. 6.4 Changes and plugins to the Software, and applications integrated with the Software do not need to be open sourced. @@ -80,7 +88,7 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 6.6 Can sell plugins developed for Software in the Marketplace. -6.7 Users with a Professional or Enterprise Edition license can sell Upper Layer Application to their clients. +6.7 The User with a Professional or Enterprise Edition License can sell Upper Layer Application to their clients. 6.8 Not restricted by the AGPL-3.0 agreement. @@ -98,26 +106,54 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 7.4 It is not allowed to provide any form of no-code, zero-code, low-code platform SaaS products to the public using the original or modified Software. -7.5 It is not allowed for Users with a standard license to sell Upper Layer Application to clients without a commercial license. +7.5 It is not allowed for the User with a Standard Edition license to sell Upper Layer Application to clients without a Commercial license. -7.6 It is not allowed to use reverse engineering, decompilation, and other means to try to discover the source code of Commercial Plugins that have not obtained source code license. +7.6 It is not allowed to publicly sell plugins developed for Software outside of the Marketplace. -7.7 It is not allowed to disclose the source code of Commercial Plugins to any third party. +7.7 If there is a violation of the above obligations or the terms of this Agreement, the rights owned by the User will be immediately terminated, the paid fees will not be refunded, and the Company reserves the right to pursue the User's legal responsibility. -7.8 It is not allowed to publicly sell plugins developed for Software outside of the Marketplace. +7.8 If there are other agreements in the contract for the above obligations, the contract agreement shall prevail. -7.9 If there is a violation of the above obligations or the terms of this Agreement, the rights owned by the User will be immediately terminated, the paid fees will not be refunded, and the Company reserves the right to pursue the User's legal responsibility. +====================================== +8. Rights of Commercial Plugin License +====================================== -7.10 If there are other agreements in the contract for the above obligations, the contract agreement shall prevail. +8.1 Obtain a permanent Commercial Plugin License for the Commercial Plugin. + +8.2 Receive 12 months of upgrades and exclusive technical support. + +8.3 Can be used for commercial purposes without restrictions on the number of applications or users. + +8.4 The User with a Professional or Enterprise Edition License can use the Commercial Plugin in Upper Layer Applications sold to their customers. + +8.5 Not restricted by the AGPL-3.0 agreement. + +8.6 If there are other agreements in the contract regarding the above rights, the contract agreement shall prevail. + +=========================================== +9. Obligations of Commercial Plugin License +=========================================== + +9.1 It is not allowed to remove or change any intellectual property statements about NocoBase and the plugin authors in the code. + +9.2 It is not allowed to sell, transfer, lease, share, or gift the Commercial Plugin. + +9.3 It is not allowed to use reverse engineering, decompilation, or other methods to attempt to discover the source code of Commercial Plugins without obtaining a source code license. + +9.4 It is not allowed to disclose the source code of Commercial Plugins to any third party. + +9.5 If there is any violation of the above obligations or the terms of this Agreement, the rights owned by the User will be immediately terminated, the paid fees will not be refunded, and the Company reserves the right to pursue the User's legal responsibility. + +9.6 If there are other agreements in the contract regarding the above obligations, the contract agreement shall prevail. ============================================================= -8. Legal Jurisdiction, Interpretation, and Dispute Resolution +10. Legal Jurisdiction, Interpretation, and Dispute Resolution ============================================================= -8.1 Except for Mainland China, the interpretation, application, and all matters related to this agreement are subject to the jurisdiction of Singapore law. +10.1 Except for Mainland China, the interpretation, application, and all matters related to this agreement are subject to the jurisdiction of Singapore law. -8.2 Any dispute related to this Agreement should first be resolved through friendly negotiation. If the negotiation fails to resolve the dispute, the dispute should be submitted to the International Chamber of Commerce (ICC) for arbitration. The arbitration venue should be Singapore, conducted in English. +10.2 Any dispute related to this Agreement should first be resolved through friendly negotiation. If the negotiation fails to resolve the dispute, the dispute should be submitted to the International Chamber of Commerce (ICC) for arbitration. The arbitration venue should be Singapore, conducted in English. -8.3 All terms and conditions of this Agreement shall be deemed enforceable to the maximum extent permitted by applicable law. If any term of this Agreement is deemed invalid by any applicable law, the invalidity of that term does not affect the validity of any other term of this Agreement, and it should be deemed that the invalid term has been modified as much as possible to make it valid and enforceable, or if the term cannot be modified, it should be deemed to have been deleted from this Agreement. +10.3 All terms and conditions of this Agreement shall be deemed enforceable to the maximum extent permitted by applicable law. If any term of this Agreement is deemed invalid by any applicable law, the invalidity of that term does not affect the validity of any other term of this Agreement, and it should be deemed that the invalid term has been modified as much as possible to make it valid and enforceable, or if the term cannot be modified, it should be deemed to have been deleted from this Agreement. -8.4 The arbitration award is final, binding on both parties, and can be enforced in any court with jurisdiction. +10.4 The arbitration award is final, binding on both parties, and can be enforced in any court with jurisdiction. From 156d99c7247651a46eb4b093068a20b4321f865e Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 15 Oct 2024 16:16:58 +0800 Subject: [PATCH 010/307] feat: support for server-sent events (#5418) * feat: qr code uploader * fix: axios * fix: test error --- packages/core/app/client/.umirc.ts | 3 +++ packages/core/client/package.json | 2 +- .../data-block/DataBlockResourceProvider.tsx | 2 +- .../src/schema-component/antd/upload/Upload.tsx | 13 +++++++++++++ .../src/schema-component/antd/upload/index.ts | 4 +++- .../src/schema-component/antd/upload/style.ts | 2 ++ packages/core/create-nocobase-app/package.json | 2 +- packages/core/devtools/umiConfig.js | 9 +++++++++ packages/core/sdk/package.json | 2 +- packages/core/sdk/src/APIClient.ts | 4 ++-- packages/core/server/package.json | 2 +- .../src/server/__tests__/instruction.test.ts | 12 ++++++------ .../@nocobase/plugin-workflow-request/package.json | 2 +- .../src/server/__tests__/instruction.test.ts | 14 +++++++------- 14 files changed, 51 insertions(+), 22 deletions(-) diff --git a/packages/core/app/client/.umirc.ts b/packages/core/app/client/.umirc.ts index e0706354816..084f591a956 100644 --- a/packages/core/app/client/.umirc.ts +++ b/packages/core/app/client/.umirc.ts @@ -62,6 +62,9 @@ export default defineConfig({ edge: 79, safari: 12, }, + jsMinifierOptions: { + target: ['chrome80', 'es2020'], + }, codeSplitting: { jsStrategy: 'depPerChunk' }, diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 82f58fc9c03..145f3723329 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -33,7 +33,7 @@ "ahooks": "^3.7.2", "antd": "5.12.8", "antd-style": "3.4.5", - "axios": "^0.26.1", + "axios": "^1.7.0", "bignumber.js": "^9.1.2", "classnames": "^2.3.1", "cronstrue": "^2.11.0", diff --git a/packages/core/client/src/data-source/data-block/DataBlockResourceProvider.tsx b/packages/core/client/src/data-source/data-block/DataBlockResourceProvider.tsx index 9bb4d8df08b..2f1588c478f 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockResourceProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockResourceProvider.tsx @@ -8,8 +8,8 @@ */ import { IResource } from '@nocobase/sdk'; -import React, { FC, ReactNode, createContext, useContext, useMemo } from 'react'; import { isArray } from 'lodash'; +import React, { FC, ReactNode, createContext, useContext, useMemo } from 'react'; import { useAPIClient } from '../../api-client'; import { useCollectionManager } from '../collection'; import { CollectionRecord } from '../collection-record'; diff --git a/packages/core/client/src/schema-component/antd/upload/Upload.tsx b/packages/core/client/src/schema-component/antd/upload/Upload.tsx index b6efb38369a..44f989fa68f 100644 --- a/packages/core/client/src/schema-component/antd/upload/Upload.tsx +++ b/packages/core/client/src/schema-component/antd/upload/Upload.tsx @@ -20,6 +20,7 @@ import { useTranslation } from 'react-i18next'; import LightBox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; +import { useComponent } from '../../hooks'; import { useProps } from '../../hooks/useProps'; import { FILE_SIZE_LIMIT_DEFAULT, @@ -401,6 +402,8 @@ export function Uploader({ rules, ...props }: UploadProps) { }); }, []); + const QRCodeUploader = useComponent('QRCodeUploader'); + const { mimetype: accept, size } = rules ?? {}; const sizeHint = useSizeHint(size); const selectable = @@ -441,6 +444,16 @@ export function Uploader({ rules, ...props }: UploadProps) { + {selectable && QRCodeUploader && ( + { + // TODO + console.log(value); + // onChange(value); + }} + /> + )} ); } diff --git a/packages/core/client/src/schema-component/antd/upload/index.ts b/packages/core/client/src/schema-component/antd/upload/index.ts index ea7cfbdecc8..cd0ff5dd7fe 100644 --- a/packages/core/client/src/schema-component/antd/upload/index.ts +++ b/packages/core/client/src/schema-component/antd/upload/index.ts @@ -7,5 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -export * from './Upload'; export { attachmentFileTypes } from './shared'; +export { useUploadStyles } from './style'; + +export * from './Upload'; diff --git a/packages/core/client/src/schema-component/antd/upload/style.ts b/packages/core/client/src/schema-component/antd/upload/style.ts index 7180109f403..d413dfcc692 100644 --- a/packages/core/client/src/schema-component/antd/upload/style.ts +++ b/packages/core/client/src/schema-component/antd/upload/style.ts @@ -113,3 +113,5 @@ export const useStyles = genStyleHook('upload', (token) => { }, } as any; }); + +export const useUploadStyles = useStyles; diff --git a/packages/core/create-nocobase-app/package.json b/packages/core/create-nocobase-app/package.json index d0385785428..b26236b5f4c 100755 --- a/packages/core/create-nocobase-app/package.json +++ b/packages/core/create-nocobase-app/package.json @@ -5,7 +5,7 @@ "license": "AGPL-3.0", "dependencies": { "@umijs/utils": "3.5.20", - "axios": "^0.26.1", + "axios": "^1.7.0", "chalk": "^4.1.1", "commander": "^9.2.0", "tar": "6.1.11" diff --git a/packages/core/devtools/umiConfig.js b/packages/core/devtools/umiConfig.js index ff56d142fb1..bdccac8e8c5 100644 --- a/packages/core/devtools/umiConfig.js +++ b/packages/core/devtools/umiConfig.js @@ -53,6 +53,15 @@ function getUmiConfig() { target: PROXY_TARGET_URL, changeOrigin: true, pathRewrite: { [`^${API_BASE_PATH}`]: API_BASE_PATH }, + onProxyRes(proxyRes, req, res) { + if (req.headers.accept === 'text/event-stream') { + res.writeHead(res.statusCode, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-transform', + Connection: 'keep-alive', + }); + } + }, }, // for local storage ...getLocalStorageProxy(), diff --git a/packages/core/sdk/package.json b/packages/core/sdk/package.json index e88a5c0f8ea..12b15d8ff83 100644 --- a/packages/core/sdk/package.json +++ b/packages/core/sdk/package.json @@ -5,7 +5,7 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "dependencies": { - "axios": "^0.26.1", + "axios": "^1.7.0", "qs": "^6.10.1" }, "devDependencies": { diff --git a/packages/core/sdk/src/APIClient.ts b/packages/core/sdk/src/APIClient.ts index 1b0bbcc0ebb..e8987707819 100644 --- a/packages/core/sdk/src/APIClient.ts +++ b/packages/core/sdk/src/APIClient.ts @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import axios, { AxiosInstance, AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse } from 'axios'; +import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, RawAxiosRequestHeaders } from 'axios'; import qs from 'qs'; export interface ActionParams { @@ -347,7 +347,7 @@ export class APIClient { return this.axios.request(config); } - resource(name: string, of?: any, headers?: AxiosRequestHeaders, cancel?: boolean): IResource { + resource(name: string, of?: any, headers?: RawAxiosRequestHeaders, cancel?: boolean): IResource { const target = {}; const handler = { get: (_: any, actionName: string) => { diff --git a/packages/core/server/package.json b/packages/core/server/package.json index 97e5c351970..081d0a20250 100644 --- a/packages/core/server/package.json +++ b/packages/core/server/package.json @@ -26,7 +26,7 @@ "@types/ini": "^1.3.31", "@types/koa-send": "^4.1.3", "@types/multer": "^1.4.5", - "axios": "^0.26.1", + "axios": "^1.7.0", "chalk": "^4.1.1", "commander": "^9.2.0", "cron": "^2.4.4", diff --git a/packages/plugins/@nocobase/plugin-workflow-mailer/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-mailer/src/server/__tests__/instruction.test.ts index e5c8b883e45..7d8e36dd998 100644 --- a/packages/plugins/@nocobase/plugin-workflow-mailer/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-mailer/src/server/__tests__/instruction.test.ts @@ -15,7 +15,7 @@ import bodyParser from 'koa-bodyparser'; import Database from '@nocobase/database'; import { MockServer } from '@nocobase/test'; -import PluginWorkflow, { Processor, EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; +import PluginWorkflow, { EXECUTION_STATUS, JOB_STATUS, Processor } from '@nocobase/plugin-workflow'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; import { RequestConfig } from '../MailerInstruction'; @@ -206,8 +206,8 @@ describe('workflow > instructions > request', () => { expect(job.result).toMatchObject({ code: 'ECONNABORTED', - name: 'Error', - status: null, + name: 'AxiosError', + // status: null, message: 'timeout of 250ms exceeded', }); @@ -235,8 +235,8 @@ describe('workflow > instructions > request', () => { expect(job.status).toBe(JOB_STATUS.RESOLVED); expect(job.result).toMatchObject({ code: 'ECONNABORTED', - name: 'Error', - status: null, + name: 'AxiosError', + // status: null, message: 'timeout of 250ms exceeded', }); }); @@ -319,7 +319,7 @@ describe('workflow > instructions > request', () => { expect(job.result).toMatchObject({ code: 'ECONNRESET', name: 'Error', - status: null, + // status: null, message: 'socket hang up', }); }); diff --git a/packages/plugins/@nocobase/plugin-workflow-request/package.json b/packages/plugins/@nocobase/plugin-workflow-request/package.json index f864fb0630c..b809d0b3792 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-request/package.json @@ -11,7 +11,7 @@ "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow-request", "devDependencies": { "antd": "5.x", - "axios": "^0.26.1", + "axios": "^1.7.0", "react": "18.x", "react-i18next": "^11.15.1" }, diff --git a/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts index 4a8540f7131..2cc00be40d8 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts @@ -8,15 +8,15 @@ */ import { Server } from 'http'; -import type { AddressInfo } from 'net'; import jwt from 'jsonwebtoken'; import Koa from 'koa'; import bodyParser from 'koa-bodyparser'; +import type { AddressInfo } from 'net'; import Database from '@nocobase/database'; import { MockServer } from '@nocobase/test'; -import PluginWorkflow, { Processor, EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; +import PluginWorkflow, { EXECUTION_STATUS, JOB_STATUS, Processor } from '@nocobase/plugin-workflow'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; import { RequestConfig } from '../RequestInstruction'; @@ -227,8 +227,8 @@ describe('workflow > instructions > request', () => { expect(job.result).toMatchObject({ code: 'ECONNABORTED', - name: 'Error', - status: null, + name: 'AxiosError', + // status: null, message: 'timeout of 250ms exceeded', }); @@ -256,8 +256,8 @@ describe('workflow > instructions > request', () => { expect(job.status).toBe(JOB_STATUS.RESOLVED); expect(job.result).toMatchObject({ code: 'ECONNABORTED', - name: 'Error', - status: null, + name: 'AxiosError', + // status: null, message: 'timeout of 250ms exceeded', }); }); @@ -340,7 +340,7 @@ describe('workflow > instructions > request', () => { expect(job.result).toMatchObject({ code: 'ECONNRESET', name: 'Error', - status: null, + // status: null, message: 'socket hang up', }); }); From 5108c8fe7c735d53520a29f82eafb0d73fdacd0d Mon Sep 17 00:00:00 2001 From: YANG QIA <2013xile@gmail.com> Date: Tue, 15 Oct 2024 18:07:55 +0800 Subject: [PATCH 011/307] feat(client): allows to store client information into session storage (#5424) * feat(client): allows to store client information into session storage * fix: test error --------- Co-authored-by: chenos --- packages/core/app/client/.umirc.ts | 11 +++++----- packages/core/app/client/src/pages/index.tsx | 3 +++ packages/core/cli/src/util.js | 2 ++ packages/core/devtools/umiConfig.js | 3 ++- packages/core/sdk/src/APIClient.ts | 21 +++++++++++++------- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/core/app/client/.umirc.ts b/packages/core/app/client/.umirc.ts index 084f591a956..e3c887a4597 100644 --- a/packages/core/app/client/.umirc.ts +++ b/packages/core/app/client/.umirc.ts @@ -18,19 +18,20 @@ export default defineConfig({ devtool: process.env.NODE_ENV === 'development' ? 'source-map' : false, favicons: [`${appPublicPath}favicon/favicon.ico`], metas: [{ name: 'viewport', content: 'initial-scale=0.1' }], - links: [ - { rel: 'stylesheet', href: `${appPublicPath}global.css` }, - ], + links: [{ rel: 'stylesheet', href: `${appPublicPath}global.css` }], headScripts: [ { src: `${appPublicPath}browser-checker.js`, }, { - content: isDevCmd ? '' : ` + content: isDevCmd + ? '' + : ` window['__webpack_public_path__'] = '{{env.APP_PUBLIC_PATH}}'; window['__nocobase_public_path__'] = '{{env.APP_PUBLIC_PATH}}'; window['__nocobase_api_base_url__'] = '{{env.API_BASE_URL}}'; window['__nocobase_api_client_storage_prefix__'] = '{{env.API_CLIENT_STORAGE_PREFIX}}'; + window['__nocobase_api_client_storage_type__'] = '{{env.API_CLIENT_STORAGE_TYPE}}'; window['__nocobase_ws_url__'] = '{{env.WS_URL}}'; window['__nocobase_ws_path__'] = '{{env.WS_PATH}}'; `, @@ -66,7 +67,7 @@ export default defineConfig({ target: ['chrome80', 'es2020'], }, codeSplitting: { - jsStrategy: 'depPerChunk' + jsStrategy: 'depPerChunk', }, chainWebpack(config, { env }) { if (env === 'production') { diff --git a/packages/core/app/client/src/pages/index.tsx b/packages/core/app/client/src/pages/index.tsx index 2b7b2732287..2a27f6d8965 100644 --- a/packages/core/app/client/src/pages/index.tsx +++ b/packages/core/app/client/src/pages/index.tsx @@ -13,6 +13,9 @@ import devDynamicImport from '../.plugins/index'; export const app = new Application({ apiClient: { + storageType: + // @ts-ignore + window['__nocobase_api_client_storage_type__'] || process.env.API_CLIENT_STORAGE_TYPE || 'localStorage', // @ts-ignore storagePrefix: // @ts-ignore diff --git a/packages/core/cli/src/util.js b/packages/core/cli/src/util.js index 66c834b0474..4d58ecb894f 100644 --- a/packages/core/cli/src/util.js +++ b/packages/core/cli/src/util.js @@ -291,6 +291,7 @@ function buildIndexHtml(force = false) { const data = fs.readFileSync(tpl, 'utf-8'); const replacedData = data .replace(/\{\{env.APP_PUBLIC_PATH\}\}/g, process.env.APP_PUBLIC_PATH) + .replace(/\{\{env.API_CLIENT_STORAGE_TYPE\}\}/g, process.env.API_CLIENT_STORAGE_TYPE) .replace(/\{\{env.API_CLIENT_STORAGE_PREFIX\}\}/g, process.env.API_CLIENT_STORAGE_PREFIX) .replace(/\{\{env.API_BASE_URL\}\}/g, process.env.API_BASE_URL || process.env.API_BASE_PATH) .replace(/\{\{env.WS_URL\}\}/g, process.env.WEBSOCKET_URL || '') @@ -327,6 +328,7 @@ exports.initEnv = function initEnv() { APP_PORT: 13000, API_BASE_PATH: '/api/', API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_', + API_CLIENT_STORAGE_TYPE: 'localStorage', DB_DIALECT: 'sqlite', DB_STORAGE: 'storage/db/nocobase.sqlite', // DB_TIMEZONE: '+00:00', diff --git a/packages/core/devtools/umiConfig.js b/packages/core/devtools/umiConfig.js index bdccac8e8c5..f0a4f5fd2b3 100644 --- a/packages/core/devtools/umiConfig.js +++ b/packages/core/devtools/umiConfig.js @@ -8,7 +8,7 @@ const path = require('path'); console.log('VERSION: ', packageJson.version); function getUmiConfig() { - const { APP_PORT, API_BASE_URL, API_CLIENT_STORAGE_PREFIX, APP_PUBLIC_PATH } = process.env; + const { APP_PORT, API_BASE_URL, API_CLIENT_STORAGE_TYPE, API_CLIENT_STORAGE_PREFIX, APP_PUBLIC_PATH } = process.env; const API_BASE_PATH = process.env.API_BASE_PATH || '/api/'; const PROXY_TARGET_URL = process.env.PROXY_TARGET_URL || `http://127.0.0.1:${APP_PORT}`; const LOCAL_STORAGE_BASE_URL = 'storage/uploads/'; @@ -41,6 +41,7 @@ function getUmiConfig() { 'process.env.WS_PATH': process.env.WS_PATH, 'process.env.API_BASE_URL': API_BASE_URL || API_BASE_PATH, 'process.env.API_CLIENT_STORAGE_PREFIX': API_CLIENT_STORAGE_PREFIX, + 'process.env.API_CLIENT_STORAGE_TYPE': API_CLIENT_STORAGE_TYPE, 'process.env.APP_ENV': process.env.APP_ENV, 'process.env.VERSION': packageJson.version, 'process.env.WEBSOCKET_URL': process.env.WEBSOCKET_URL, diff --git a/packages/core/sdk/src/APIClient.ts b/packages/core/sdk/src/APIClient.ts index e8987707819..f110816aaef 100644 --- a/packages/core/sdk/src/APIClient.ts +++ b/packages/core/sdk/src/APIClient.ts @@ -267,6 +267,7 @@ export class MemoryStorage extends Storage { interface ExtendedOptions { authClass?: any; + storageType?: 'localStorage' | 'sessionStorage' | 'memory'; storageClass?: any; storagePrefix?: string; } @@ -300,10 +301,10 @@ export class APIClient { if (typeof instance === 'function') { this.axios = instance; } else { - const { authClass, storageClass, storagePrefix = 'NOCOBASE_', ...others } = instance || {}; + const { authClass, storageType, storageClass, storagePrefix = 'NOCOBASE_', ...others } = instance || {}; this.storagePrefix = storagePrefix; this.axios = axios.create(others); - this.initStorage(storageClass); + this.initStorage(storageClass, storageType); if (authClass) { this.auth = new authClass(this); } @@ -317,14 +318,20 @@ export class APIClient { this.interceptors(); } - private initStorage(storage?: any) { + private initStorage(storage?: any, storageType = 'localStorage') { if (storage) { this.storage = new storage(this); - } else if (typeof localStorage !== 'undefined') { - this.storage = localStorage; - } else { - this.storage = new MemoryStorage(); + return; } + if (storageType === 'localStorage' && typeof localStorage !== 'undefined') { + this.storage = localStorage; + return; + } + if (storageType === 'sessionStorage' && typeof sessionStorage !== 'undefined') { + this.storage = sessionStorage; + return; + } + this.storage = new MemoryStorage(); } interceptors() { From 01f7c39e9b7cd4e66efe08023ee57c535f972a93 Mon Sep 17 00:00:00 2001 From: Junyi Date: Tue, 15 Oct 2024 20:20:10 +0800 Subject: [PATCH 012/307] fix(plugin-workflow): fix hint of update node (#5426) --- .../@nocobase/plugin-workflow/src/client/nodes/update.tsx | 2 +- .../plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/update.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/update.tsx index e1d291faae9..46b0f42fc06 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/update.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/update.tsx @@ -73,7 +73,7 @@ export default class extends Instruction { { label: `{{t("Update in a batch", { ns: "${NAMESPACE}" })}}`, value: false, - tooltip: `{{t("Update all eligible data at one time, which has better performance when the amount of data is large. But the updated data will not trigger other workflows, and will not record audit logs.", { ns: "${NAMESPACE}" })}}`, + tooltip: `{{t("Update all eligible data at one time, which has better performance when the amount of data is large. But association fields are not supported (unless foreign key in current collection), and the updated data will not trigger other workflows.", { ns: "${NAMESPACE}" })}}`, }, { label: `{{t("Update one by one", { ns: "${NAMESPACE}" })}}`, diff --git a/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json index ed7565f3e2e..170bba5b2f8 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json @@ -169,8 +169,8 @@ "Update mode": "更新模式", "Update in a batch": "批量更新", "Update one by one": "逐条更新", - "Update all eligible data at one time, which has better performance when the amount of data is large. But the updated data will not trigger other workflows, and will not record audit logs.": - "一次性更新所有符合条件的数据,在数据量较大时有比较好的性能;但被更新的数据不会触发其他工作流,也不会记录更新日志。", + "Update all eligible data at one time, which has better performance when the amount of data is large. But association fields are not supported (unless foreign key in current collection), and the updated data will not trigger other workflows.": + "一次性更新所有符合条件的数据,在数据量较大时有比较好的性能;但不支持关系字段的更新(除非是在当前表中的外键),被更新的数据也不会触发其他工作流。", "The updated data can trigger other workflows, and the audit log will also be recorded. But it is usually only applicable to several or dozens of pieces of data, otherwise there will be performance problems.": "被更新的数据可以再次触发其他工作流,也会记录更新日志;但通常只适用于数条或数十条数据,否则会有性能问题。", "Query record": "查询数据", From c2a8dced55237736e274265bf364f7af33181435 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Wed, 16 Oct 2024 09:25:07 +0800 Subject: [PATCH 013/307] fix(Tabs): prevent mismatched activeKey (#5412) * test: add e2e test * fix(Tabs): prevent mismatched activeKey * fix(bulkEdit): prevent incorrect URL modification * Revert "fix(bulkEdit): prevent incorrect URL modification" This reverts commit 28f685ffebadee8b923474427ee48b66de48637b. * fix: avoid affecting the Tabs of the upper-level popup * fix(Duplicate): avoid affecting the Tabs of the upper-level popup * refactor: migrate e2e to plugin * test(Duplicate): add e2e test --- .../schema-component/antd/action/Action.tsx | 9 +- .../src/client/__e2e__/popup.test.ts | 32 + .../src/client/__e2e__/utils.ts | 702 +++++++++++ .../src/client/DuplicateAction.tsx | 24 +- .../src/client/__e2e__/popup.test.ts | 28 + .../src/client/__e2e__/templates.ts | 1028 +++++++++++++++++ 6 files changed, 1811 insertions(+), 12 deletions(-) create mode 100644 packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/popup.test.ts create mode 100644 packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/popup.test.ts diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 32676ec10ae..24c926c9c4f 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -15,7 +15,7 @@ import _, { default as lodash } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { useTranslation } from 'react-i18next'; -import { ErrorFallback, StablePopover, useActionContext } from '../..'; +import { ErrorFallback, StablePopover, TabsContextProvider, useActionContext } from '../..'; import { useDesignable } from '../../'; import { useACLActionParamsContext } from '../../../acl'; import { useCollectionParentRecordData, useCollectionRecordData, useDataBlockRequest } from '../../../data-source'; @@ -181,7 +181,7 @@ export const Action: ComposedAction = withDynamicSchemaProps( // return buttonElement; // } - const result = ( + let result = ( ); + if (isBulkEditAction(fieldSchema)) { + // Clear the context of Tabs to avoid affecting the Tabs of the upper-level popup + result = {result}; + } + // fix https://nocobase.height.app/T-3235/description if (addChild) { return wrapSSR( diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/popup.test.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/popup.test.ts new file mode 100644 index 00000000000..3531d0e9e8f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/popup.test.ts @@ -0,0 +1,32 @@ +/** + * 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 { expect, test } from '@nocobase/test/e2e'; +import { theAddBlockButtonInDrawerShouldBeVisible } from './utils'; + +test.describe('popup of bulk edit', () => { + test('the Add block button in drawer should be visible', async ({ page, mockPage }) => { + await mockPage(theAddBlockButtonInDrawerShouldBeVisible).goto(); + + // open subpage, anb then open the bulk edit drawer, the Add block in drawer should be visible + await page.getByLabel('action-Action.Link-open').click(); + await page.getByLabel('action-Action-Bulk edit 1-').click(); + await expect( + page.getByTestId('drawer-Action.Container-users-Bulk edit').getByLabel('schema-initializer-Grid-popup'), + ).toBeVisible(); + await page.getByLabel('drawer-Action.Container-users-Bulk edit-mask').click(); + + // click other tab, then open the bulk edit drawer, the Add block in drawer should be visible + await page.getByText('new tab').click(); + await page.getByLabel('action-Action-Bulk edit 2-').click(); + await expect( + page.getByTestId('drawer-Action.Container-users-Bulk edit').getByLabel('schema-initializer-Grid-popup'), + ).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts index 9fd147b4b06..3e67dc3ad24 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts @@ -541,3 +541,705 @@ export const oneEmptyTableBlockWithCustomizeActions: PageConfig = { 'x-async': true, }, }; + +export const theAddBlockButtonInDrawerShouldBeVisible = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-app-version': '1.3.32-beta', + properties: { + ydtgms2lmr4: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + 'x-app-version': '1.3.32-beta', + properties: { + jk8ix7ivmvb: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + fdohqzh304u: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + syp961jrbvs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'd9ciqysn98f', + 'x-async': false, + 'x-index': 1, + }, + bflk6vcqbda: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + '9fy397o5n7x': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + properties: { + ujxoicj1do2: { + 'x-uid': 'stie1dytwk9', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'open subpage', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + iconColor: '#1677FF', + danger: false, + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + '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': 'popup:addTab', + 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': 'popup:common:addBlock', + properties: { + gkbkej9bcfg: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + qsnxw1kyglw: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + rg5p0jxa04f: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + properties: { + yrh6fyeotth: { + 'x-uid': 'm9eykj7hcac', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'Bulk edit 1', + 'x-component': 'Action', + 'x-action': 'customize:bulkEdit', + 'x-action-settings': { + updateMode: 'selected', + }, + 'x-component-props': { + openMode: 'drawer', + icon: 'EditOutlined', + iconColor: '#1677FF', + danger: false, + type: 'default', + }, + 'x-align': 'right', + 'x-decorator': 'BulkEditActionDecorator', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:bulkEdit', + 'x-acl-action': 'update', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-app-version': '1.3.32-beta', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + 'x-app-version': '1.3.32-beta', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + 'x-initializer-props': { + gridInitializer: + 'popup:bulkEdit:addBlock', + }, + 'x-app-version': '1.3.32-beta', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.3.32-beta', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:bulkEdit:addBlock', + 'x-app-version': '1.3.32-beta', + 'x-uid': '86kvljipurj', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'osjgnzq0bxu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'nddbbs6e5vc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'r604iwattq7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'kdyyiruax36', + 'x-async': false, + 'x-index': 1, + }, + lodqxwvvrwu: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + '1gt1zh3wlhl': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'r3rllrh7wzk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dmx697o309f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'labwg68zvn6', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '0fn0tjzur2c', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'goinlb9fan9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '74qg393u7hr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hetu8763puj', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4dn6kb0cmwp', + 'x-async': false, + 'x-index': 1, + }, + gljvwj6dtl8: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'new tab', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.3.32-beta', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + 'x-app-version': '1.3.32-beta', + properties: { + c1yhos175hj: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + yhbbftd4j86: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + '4y4cdnzx645': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + properties: { + vp75269u5ai: { + 'x-uid': '3dup1gvthik', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'Bulk edit 2', + 'x-component': 'Action', + 'x-action': 'customize:bulkEdit', + 'x-action-settings': { + updateMode: 'selected', + }, + 'x-component-props': { + openMode: 'drawer', + icon: 'EditOutlined', + iconColor: '#1677FF', + danger: false, + type: 'default', + }, + 'x-align': 'right', + 'x-decorator': 'BulkEditActionDecorator', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:bulkEdit', + 'x-acl-action': 'update', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-app-version': '1.3.32-beta', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + 'x-app-version': '1.3.32-beta', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + 'x-initializer-props': { + gridInitializer: + 'popup:bulkEdit:addBlock', + }, + 'x-app-version': '1.3.32-beta', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.3.32-beta', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:bulkEdit:addBlock', + 'x-app-version': '1.3.32-beta', + 'x-uid': 'ev7zy0yictp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'n6f6k6fvukf', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xm9xscjd3am', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tpukizpyz29', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'mf55d2cpdpx', + 'x-async': false, + 'x-index': 1, + }, + p5i670h31zl: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + owyte0l5cgt: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'bvc3y7y3fem', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4xso516rjk9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dyyuo828wdo', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'um86bfrnd0n', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bwkvo3771d1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8ecnnc4yvgz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '6pmwqi2etjn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'e88jnt5tv4e', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '2u9lxm7qivt', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'kqabk7m6d7h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'eqyqzt5letc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7t4bewrinmr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ouboqexioby', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'e363l9dbg2g', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9p4tvagqpfd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'a68euip3d3s', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hklfkdijm58', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '536m19repzv', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx index 95907e04f7a..dfcd999ac78 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx @@ -15,6 +15,7 @@ import { FormBlockContext, PopupSettingsProvider, RecordProvider, + TabsContextProvider, fetchTemplateData, useACLActionParamsContext, useAPIClient, @@ -200,16 +201,19 @@ export const DuplicateAction = observer( {loading ? t('Duplicating') : children || t('Duplicate')} )} - - {/* 这里的 record 就是弹窗中创建表单的 sourceRecord */} - - - - - - - - + {/* Clear the context of Tabs to avoid affecting the Tabs of the upper-level popup */} + + + {/* 这里的 record 就是弹窗中创建表单的 sourceRecord */} + + + + + + + + + diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/popup.test.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/popup.test.ts new file mode 100644 index 00000000000..4e64200cf58 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/popup.test.ts @@ -0,0 +1,28 @@ +/** + * 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 { expect, test } from '@nocobase/test/e2e'; +import { theAddBlockButtonInDrawerShouldBeVisible } from './templates'; + +test.describe('popup of duplicate', () => { + test('the Add block button in drawer should be visible', async ({ page, mockPage }) => { + await mockPage(theAddBlockButtonInDrawerShouldBeVisible).goto(); + + // open subpage, anb then open the duplicate drawer, the Add block in drawer should be visible + await page.getByLabel('action-Action.Link-open').click(); + await page.getByLabel('action-Action.Link-Duplicate').click(); + await expect(page.getByLabel('block-item-Markdown.Void-').getByText('Duplicate markdown 1.')).toBeVisible(); + await page.getByLabel('drawer-Action.Container-users-Duplicate-mask').click(); + + // click other tab, then open the bulk edit drawer, the Add block in drawer should be visible + await page.getByText('new tab').click(); + await page.getByLabel('action-Action.Link-Duplicate').click(); + await expect(page.getByLabel('block-item-Markdown.Void-').getByText('Duplicate markdown 2.')).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/templates.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/templates.ts index b590fa97704..ff41fa6c6e7 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/templates.ts +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/templates.ts @@ -1039,3 +1039,1031 @@ export const T4546 = { 'x-async': true, }, }; +export const theAddBlockButtonInDrawerShouldBeVisible = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-app-version': '1.3.32-beta', + properties: { + ydtgms2lmr4: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + 'x-app-version': '1.3.32-beta', + properties: { + jk8ix7ivmvb: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + fdohqzh304u: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + syp961jrbvs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'd9ciqysn98f', + 'x-async': false, + 'x-index': 1, + }, + bflk6vcqbda: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + '9fy397o5n7x': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + properties: { + ujxoicj1do2: { + 'x-uid': 'stie1dytwk9', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'open subpage', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + iconColor: '#1677FF', + danger: false, + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + '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': 'popup:addTab', + 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': 'popup:common:addBlock', + properties: { + gkbkej9bcfg: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + qsnxw1kyglw: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + rg5p0jxa04f: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'kdyyiruax36', + 'x-async': false, + 'x-index': 1, + }, + lodqxwvvrwu: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + '1gt1zh3wlhl': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + properties: { + ertbfj6rncg: { + 'x-uid': '06gv2hdo5gn', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-action': 'duplicate', + 'x-acl-action': 'create', + title: 'Duplicate 1', + 'x-component': 'Action.Link', + 'x-decorator': + 'DuplicateActionDecorator', + 'x-component-props': { + openMode: 'drawer', + component: 'DuplicateAction', + type: 'primary', + duplicateMode: 'continueduplicate', + duplicateFields: ['nickname'], + duplicateCollection: 'users', + iconColor: '#1677FF', + danger: false, + }, + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': + 'actionSettings:duplicate', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Duplicate") }}', + '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': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Duplicate")}}', + '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': + 'popup:addNew:addBlock', + properties: { + '8688wl2mr4i': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.32-beta', + properties: { + f91h0y067hv: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.32-beta', + properties: { + pdsymaj9tip: { + 'x-uid': + 'sdya5bkm9c8', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + engine: + 'handlebars', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Duplicate markdown 1.', + }, + 'x-app-version': + '1.3.32-beta', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '5r3hc64n1ej', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '774qbcnt3qu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3v32xcbwr7m', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '0wttna5fpai', + 'x-async': false, + 'x-index': 1, + }, + wz1yhvrpmrv: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'new tab in drawer', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': + '1.3.32-beta', + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + 'x-app-version': + '1.3.32-beta', + properties: { + y5zjil8oaa3: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.32-beta', + properties: { + v0gws5adwan: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.32-beta', + properties: { + igm7sdsm3ur: { + 'x-uid': + 'estddzpckrg', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + engine: + 'handlebars', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'new tab in drawer markdown 1.', + }, + 'x-app-version': + '1.3.32-beta', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'r0ujr6t1rc1', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'veneg38qs5f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8mng9basz36', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'cmetycyfemg', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '7nlcloha97l', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '22om0z59x41', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'r3rllrh7wzk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dmx697o309f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'labwg68zvn6', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '0fn0tjzur2c', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'goinlb9fan9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '74qg393u7hr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hetu8763puj', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4dn6kb0cmwp', + 'x-async': false, + 'x-index': 1, + }, + gljvwj6dtl8: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'new tab', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.3.32-beta', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + 'x-app-version': '1.3.32-beta', + properties: { + c1yhos175hj: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.32-beta', + properties: { + yhbbftd4j86: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.32-beta', + properties: { + '4y4cdnzx645': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.32-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.32-beta', + 'x-uid': 'mf55d2cpdpx', + 'x-async': false, + 'x-index': 1, + }, + p5i670h31zl: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.32-beta', + 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-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.32-beta', + properties: { + owyte0l5cgt: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.32-beta', + properties: { + loi6el5pg6g: { + 'x-uid': 'a6072zadr0v', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-action': 'duplicate', + 'x-acl-action': 'create', + title: 'Duplicate 2', + 'x-component': 'Action.Link', + 'x-decorator': + 'DuplicateActionDecorator', + 'x-component-props': { + openMode: 'drawer', + component: 'DuplicateAction', + type: 'primary', + duplicateMode: 'continueduplicate', + duplicateFields: ['nickname'], + duplicateCollection: 'users', + iconColor: '#1677FF', + danger: false, + }, + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': + 'actionSettings:duplicate', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Duplicate") }}', + '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': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Duplicate")}}', + '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': + 'popup:addNew:addBlock', + properties: { + dx47nd4vr1y: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.32-beta', + properties: { + k0xrnil5pew: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.32-beta', + properties: { + l0bj7opq4ts: { + 'x-uid': + 'irw5rilaksj', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + engine: + 'handlebars', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Duplicate markdown 2.', + }, + 'x-app-version': + '1.3.32-beta', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '989e6tagpg7', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'wbgxmynn5mb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'q4tpvvow9ev', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5nyyobmuuyl', + 'x-async': false, + 'x-index': 1, + }, + k0dihwjrh0j: { + 'x-uid': '1m5bg14aiv9', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'new tab in drawer', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': + '1.3.32-beta', + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + 'x-app-version': + '1.3.32-beta', + properties: { + '84pz8mhlfi8': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.32-beta', + properties: { + dbm9wbvxfj5: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.32-beta', + properties: { + '5yhn7kfi2yw': + { + 'x-uid': + 'aa23f01rm3r', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + engine: + 'handlebars', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'new tab in drawer markdown 2.', + }, + 'x-app-version': + '1.3.32-beta', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'x2a3ivqab91', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'ekszy9o6ta0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8yjfgyrvord', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'w4k13f0dqpt', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7vaia1fkno1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bvc3y7y3fem', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4xso516rjk9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dyyuo828wdo', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'um86bfrnd0n', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bwkvo3771d1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8ecnnc4yvgz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '6pmwqi2etjn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'e88jnt5tv4e', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '2u9lxm7qivt', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'kqabk7m6d7h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'eqyqzt5letc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7t4bewrinmr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ouboqexioby', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'e363l9dbg2g', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9p4tvagqpfd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'a68euip3d3s', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hklfkdijm58', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '536m19repzv', + 'x-async': true, + 'x-index': 1, + }, +}; From 52ad41614008d7112273dc8c0e1e02d05eb8c0f4 Mon Sep 17 00:00:00 2001 From: Katherine Date: Wed, 16 Oct 2024 10:56:38 +0800 Subject: [PATCH 014/307] style: optimize expand/collapse button styles (#5431) --- .../antd/action/Action.style.ts | 2 +- .../antd/expand-action/Expand.Action.tsx | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/action/Action.style.ts b/packages/core/client/src/schema-component/antd/action/Action.style.ts index f283358e4c2..fcdf5315c29 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.style.ts +++ b/packages/core/client/src/schema-component/antd/action/Action.style.ts @@ -52,7 +52,7 @@ const useStyles = genStyleHook('nb-action', (token) => { marginInlineEnd: '0px !important', }, '.nb-action-title': { - marginInlineStart: '8px', + marginInlineStart: `${token.controlPaddingHorizontalSM} px`, }, }, }; diff --git a/packages/core/client/src/schema-component/antd/expand-action/Expand.Action.tsx b/packages/core/client/src/schema-component/antd/expand-action/Expand.Action.tsx index c12b5146d53..aca2e9edcdd 100644 --- a/packages/core/client/src/schema-component/antd/expand-action/Expand.Action.tsx +++ b/packages/core/client/src/schema-component/antd/expand-action/Expand.Action.tsx @@ -9,7 +9,7 @@ import { css } from '@emotion/css'; import { useFieldSchema } from '@formily/react'; -import { Button } from 'antd'; +import { Button, theme } from 'antd'; import React, { forwardRef, createRef } from 'react'; import { composeRef } from 'rc-util/lib/ref'; import { useCompile } from '../../hooks'; @@ -66,6 +66,9 @@ const InternalExpandAction = (props, ref) => { const compile = useCompile(); const internalRef = createRef(); const buttonRef = composeRef(ref, internalRef); + const { token } = theme.useToken(); + const actionTitle = ctx?.expandFlag ? compile(titleCollapse) : compile(titleExpand); + const icon = ctx?.expandFlag ? iconCollapse : iconExpand; return ( //@ts-ignore
}> @@ -77,9 +80,27 @@ const InternalExpandAction = (props, ref) => { icon={} type={props.type} style={props?.style} + className={css` + .ant-btn-icon { + margin-inline-end: 0px !important; + } + `} > {props.children?.[1]} - {ctx?.expandFlag ? compile(titleCollapse) : compile(titleExpand)} + + {actionTitle && ( + + {actionTitle} + + )} )}
From 7bd96fd154271ea4cf348e983704cd3e9c9ad752 Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Wed, 16 Oct 2024 14:25:00 +0800 Subject: [PATCH 015/307] fix: restore with datetime no tz (#5432) --- .../src/server/__tests__/dumper.test.ts | 39 +++++++++++++++++++ .../src/server/field-value-writer.ts | 27 +++++++++++-- .../src/server/restorer.ts | 2 +- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-backup-restore/src/server/__tests__/dumper.test.ts b/packages/plugins/@nocobase/plugin-backup-restore/src/server/__tests__/dumper.test.ts index 9e85ae37f87..2dc78f594fc 100644 --- a/packages/plugins/@nocobase/plugin-backup-restore/src/server/__tests__/dumper.test.ts +++ b/packages/plugins/@nocobase/plugin-backup-restore/src/server/__tests__/dumper.test.ts @@ -155,6 +155,45 @@ describe('dumper', () => { expect(items.length).toBe(1); }); + it('should dump and restore datetimeNoTz field', async () => { + await db.getRepository('collections').create({ + values: { + name: 'tests', + fields: [ + { + type: 'datetimeNoTz', + name: 'test_data', + }, + ], + }, + context: {}, + }); + + await db.getRepository('tests').create({ + values: { + test_data: '2013-10-10 12:11:00', + }, + }); + + const dumper = new Dumper(app); + + const result = await dumper.dump({ + groups: new Set(['required', 'custom']), + }); + + const restorer = new Restorer(app, { + backUpFilePath: result.filePath, + }); + + await restorer.restore({ + groups: new Set(['required', 'custom']), + }); + + const testCollection = app.db.getCollection('tests'); + const items = await testCollection.repository.find(); + expect(items.length).toBe(1); + }); + it('should dump and restore uuid field', async () => { await db.getRepository('collections').create({ values: { diff --git a/packages/plugins/@nocobase/plugin-backup-restore/src/server/field-value-writer.ts b/packages/plugins/@nocobase/plugin-backup-restore/src/server/field-value-writer.ts index 793368711da..fcd8bfefd06 100644 --- a/packages/plugins/@nocobase/plugin-backup-restore/src/server/field-value-writer.ts +++ b/packages/plugins/@nocobase/plugin-backup-restore/src/server/field-value-writer.ts @@ -7,10 +7,11 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { DataTypes, Field } from '@nocobase/database'; +import { Database, DataTypes, Field } from '@nocobase/database'; import lodash from 'lodash'; +import moment from 'moment/moment'; -type WriterFunc = (val: any) => any; +type WriterFunc = (val: any, database: Database) => any; const getMapFieldWriter = (field: Field) => { return (val) => { @@ -28,7 +29,7 @@ const getMapFieldWriter = (field: Field) => { export class FieldValueWriter { static writers = new Map(); - static write(field: Field, val) { + static write(field: Field, val, database) { if (val === null) return val; if (field.type == 'point' || field.type == 'lineString' || field.type == 'circle' || field.type === 'polygon') { @@ -36,10 +37,11 @@ export class FieldValueWriter { } const fieldType = field.typeToString(); + const writer = FieldValueWriter.writers[fieldType]; if (writer) { - val = writer(val); + val = writer(val, database); } return val; @@ -88,4 +90,21 @@ FieldValueWriter.registerWriter([DataTypes.JSON.toString(), DataTypes.JSONB.toSt } }); +FieldValueWriter.registerWriter('DatetimeNoTzTypeMySQL', (val, database) => { + // @ts-ignore + const timezone = database.options.rawTimezone || '+00:00'; + + if (typeof val === 'string' && isIso8601(val)) { + const momentVal = moment(val).utcOffset(timezone); + val = momentVal.format('YYYY-MM-DD HH:mm:ss'); + } + + return val; +}); + FieldValueWriter.registerWriter(DataTypes.BOOLEAN.toString(), (val) => Boolean(val)); + +function isIso8601(str) { + const iso8601StrictRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + return iso8601StrictRegex.test(str); +} diff --git a/packages/plugins/@nocobase/plugin-backup-restore/src/server/restorer.ts b/packages/plugins/@nocobase/plugin-backup-restore/src/server/restorer.ts index 1d5dddfb20f..f0481cf8954 100644 --- a/packages/plugins/@nocobase/plugin-backup-restore/src/server/restorer.ts +++ b/packages/plugins/@nocobase/plugin-backup-restore/src/server/restorer.ts @@ -339,7 +339,7 @@ export class Restorer extends AppMigrator { .map((val, index) => [columns[index], val]) .reduce((carry, [column, val]) => { const field = fieldAttributes[column]; - carry[column] = field ? FieldValueWriter.write(field, val) : val; + carry[column] = field ? FieldValueWriter.write(field, val, app.db) : val; return carry; }, {}), From cb8eb0d035edbb2fb01d3e5924ac67a64cd7a79c Mon Sep 17 00:00:00 2001 From: Katherine Date: Wed, 16 Oct 2024 15:18:09 +0800 Subject: [PATCH 016/307] fix: action button style (#5433) --- .../client/src/schema-component/antd/action/Action.style.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/client/src/schema-component/antd/action/Action.style.ts b/packages/core/client/src/schema-component/antd/action/Action.style.ts index fcdf5315c29..10ee19a305c 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.style.ts +++ b/packages/core/client/src/schema-component/antd/action/Action.style.ts @@ -52,7 +52,7 @@ const useStyles = genStyleHook('nb-action', (token) => { marginInlineEnd: '0px !important', }, '.nb-action-title': { - marginInlineStart: `${token.controlPaddingHorizontalSM} px`, + marginInlineStart: `${token.controlPaddingHorizontalSM}px`, }, }, }; From 0c2b07ea33bad0bc6db571d64b44387c5d97e312 Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 16 Oct 2024 15:35:14 +0800 Subject: [PATCH 017/307] fix(plugin-file-manager): fix storage field in template to load more storages (#5430) --- .../src/client/templates/file.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/client/templates/file.ts b/packages/plugins/@nocobase/plugin-file-manager/src/client/templates/file.ts index 7d06ed0ca06..6b9e36b0825 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/client/templates/file.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/client/templates/file.ts @@ -157,12 +157,24 @@ export class FileCollectionTemplate extends CollectionTemplate { }, ...getConfigurableProperties('category', 'description'), storage: { - title: `{{t("File storage", { ns: "${NAMESPACE}" })}}`, - type: 'hasOne', + type: 'string', name: 'storage', + title: `{{t("File storage", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', - 'x-component': 'Select', - 'x-reactions': ['{{useAsyncDataSource(loadStorages)}}'], + 'x-component': 'RemoteSelect', + 'x-component-props': { + service: { + resource: 'storages', + params: { + // pageSize: -1 + }, + }, + manual: false, + fieldNames: { + label: 'title', + value: 'name', + }, + }, }, ...getConfigurableProperties('presetFields'), }; From 05b970310187c5107801cea5f5e7a2767ed0e2b5 Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 16 Oct 2024 21:15:35 +0800 Subject: [PATCH 018/307] feat(plugin-workflow-loop): add more configuration (#5342) * feat(plugin-workflow-loop): add more configuration * fix(plugin-workflow-loop): fix scope variable on current loop node * refactor(plugin-workflow-loop): adjust order of exit options * chore(plugin-workflow-loop): add migration * fix(plugin-workflow): fix condition branch constant * fix(plugin-workflow): fix additionalScope argument in parsing variable * fix(plugin-workflow-loop): dependencies * fix(plugin-workflow-loop): fix type * fix(plugin-workflow-loop): fix client variable issues * fix(plugin-workflow-loop): only use basic calculation due to expression variable issues * refactor(plugin-workflow-loop): adjust configuration fields * fix(plugin-workflow-loop): fix locale * refactor(plugin-workflow-loop): adjust order of configuration fields * fix(plugin-workflow-loop): fix number type properties * fix(plugin-workflow-loop): fix null items in array are skipped --- .../antd/variable/TextArea.tsx | 4 +- .../plugin-workflow-loop/package.json | 1 + .../src/client/LoopInstruction.tsx | 353 ++++++-- .../plugin-workflow-loop/src/constants.ts | 24 + .../src/locale/zh-CN.json | 18 +- .../src/server/LoopInstruction.ts | 110 ++- .../src/server/__tests__/instruction.test.ts | 798 ++++++++++++++---- .../20240929212031-change-loop-result.ts | 45 + .../src/server/actions.ts | 22 +- .../src/locale/zh-CN.json | 4 +- .../src/server/instructions.ts | 9 + .../src/client/components/Calculation.tsx | 351 ++++++++ .../src/client/components/index.ts | 2 + .../src/client/nodes/condition.tsx | 338 +------- .../src/client/nodes/index.tsx | 21 +- .../plugin-workflow/src/server/Processor.ts | 14 +- .../plugin-workflow/src/server/index.ts | 1 + .../instructions/ConditionInstruction.ts | 113 +-- .../src/server/logicCalculate.ts | 127 +++ 19 files changed, 1631 insertions(+), 724 deletions(-) create mode 100644 packages/plugins/@nocobase/plugin-workflow-loop/src/constants.ts create mode 100644 packages/plugins/@nocobase/plugin-workflow-loop/src/server/migrations/20240929212031-change-loop-result.ts create mode 100644 packages/plugins/@nocobase/plugin-workflow/src/client/components/Calculation.tsx create mode 100644 packages/plugins/@nocobase/plugin-workflow/src/server/logicCalculate.ts diff --git a/packages/core/client/src/schema-component/antd/variable/TextArea.tsx b/packages/core/client/src/schema-component/antd/variable/TextArea.tsx index 47efceee962..1e91101d83a 100644 --- a/packages/core/client/src/schema-component/antd/variable/TextArea.tsx +++ b/packages/core/client/src/schema-component/antd/variable/TextArea.tsx @@ -41,7 +41,7 @@ function pasteHTML( if (indexes) { const children = Array.from(container.childNodes); if (indexes[0] === -1) { - if (indexes[1]) { + if (indexes[1] && children[indexes[1] - 1]) { range.setStartAfter(children[indexes[1] - 1]); } else { range.setStart(container, 0); @@ -51,7 +51,7 @@ function pasteHTML( } if (indexes[2] === -1) { - if (indexes[3]) { + if (indexes[3] && children[indexes[3] - 1]) { range.setEndAfter(children[indexes[3] - 1]); } else { range.setEnd(container, 0); diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/package.json b/packages/plugins/@nocobase/plugin-workflow-loop/package.json index 0c03dfd8b74..749398b6567 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-loop/package.json @@ -17,6 +17,7 @@ "peerDependencies": { "@nocobase/client": "1.x", "@nocobase/database": "1.x", + "@nocobase/evaluators": "1.x", "@nocobase/plugin-workflow": ">=0.17.0-alpha.3", "@nocobase/server": "1.x", "@nocobase/test": "1.x" diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/src/client/LoopInstruction.tsx b/packages/plugins/@nocobase/plugin-workflow-loop/src/client/LoopInstruction.tsx index 1de609447a6..6a26da0a399 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/src/client/LoopInstruction.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-loop/src/client/LoopInstruction.tsx @@ -7,11 +7,12 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { ArrowUpOutlined } from '@ant-design/icons'; - -import { css, cx, useCompile } from '@nocobase/client'; - +import { Card, Checkbox } from 'antd'; +import { FormLayout, FormItem } from '@formily/antd-v5'; +import { useForm } from '@formily/react'; +import { css, cx, SchemaComponent, useCompile, Variable } from '@nocobase/client'; import { NodeDefaultView, Branch, @@ -19,13 +20,23 @@ import { useStyles, VariableOption, WorkflowVariableInput, + WorkflowVariableTextArea, defaultFieldNames, nodesOptions, scopeOptions, triggerOptions, Instruction, + RadioWithTooltip, + renderEngineReference, + RadioWithTooltipOption, + CalculationConfig, + useWorkflowVariableOptions, + UseVariableOptions, + useNodeContext, } from '@nocobase/plugin-workflow/client'; + import { NAMESPACE, useLang } from '../locale'; +import { useTranslation } from 'react-i18next'; function findOption(options: VariableOption[], paths: string[]) { let opts = options; @@ -47,6 +58,189 @@ function findOption(options: VariableOption[], paths: string[]) { return option; } +function LoopCondition({ value, onChange }) { + const { t } = useTranslation(); + const onCheckpointChange = useCallback( + (ev) => { + onChange({ ...value, checkpoint: ev.target.value }); + }, + [value, onChange], + ); + const onContinueOnFalseChange = useCallback( + (ev) => { + onChange({ ...value, continueOnFalse: ev.target.value }); + }, + [value, onChange], + ); + const onCalculationChange = useCallback( + (calculation) => { + onChange({ ...value, calculation }); + }, + [value, onChange], + ); + return ( + <> + { + onChange( + ev.target.checked + ? { checkpoint: 0, continueOnFalse: false, calculation: { group: { type: 'and', calculations: [] } } } + : false, + ); + }} + > + {t('Enable loop condition', { ns: NAMESPACE })} + + {value ? ( + + + + + + + + + + + + + + ) : null} + + ); +} + +function useScopeVariables(node, options) { + const compile = useCompile(); + const langLoopTarget = useLang('Loop target'); + const langLoopIndex = useLang('Loop index (starts from 0)'); + const langLoopSequence = useLang('Loop sequence (starts from 1)'); + const langLoopLength = useLang('Loop length'); + const { target } = node.config; + if (target == null) { + return null; + } + + const { fieldNames = defaultFieldNames, scope } = options; + + // const { workflow } = useFlowContext(); + // const current = useNodeContext(); + // const upstreams = useAvailableUpstreams(current); + // find target data model by path described in `config.target` + // 1. get options from $context/$jobsMapByNodeKey + // 2. route to sub-options and use as loop target options + let targetOption: VariableOption = { + key: 'item', + [fieldNames.value]: 'item', + [fieldNames.label]: langLoopTarget, + }; + + if (typeof target === 'string' && target.startsWith('{{') && target.endsWith('}}')) { + const paths = target + .slice(2, -2) + .split('.') + .map((path) => path.trim()); + + const targetOptions = + scope ?? + [scopeOptions, nodesOptions, triggerOptions].map((item: any) => { + const opts = item.useOptions({ ...options, current: node }).filter(Boolean); + return { + [fieldNames.label]: compile(item.label), + [fieldNames.value]: item.value, + key: item.value, + [fieldNames.children]: opts, + disabled: opts && !opts.length, + }; + }); + + const found = findOption(targetOptions, paths); + + targetOption = Object.assign({}, found, targetOption); + } + + return [ + targetOption, + { key: 'index', [fieldNames.value]: 'index', [fieldNames.label]: langLoopIndex }, + { key: 'sequence', [fieldNames.value]: 'sequence', [fieldNames.label]: langLoopSequence }, + { key: 'length', [fieldNames.value]: 'length', [fieldNames.label]: langLoopLength }, + ]; +} + +function useVariableHook(options: UseVariableOptions = {}) { + const { values } = useForm(); + const node = useNodeContext(); + const current = { + ...node, + config: { + ...node.config, + target: values.target, + }, + }; + const result = useWorkflowVariableOptions(options); + const subOptions = useScopeVariables(current, { + ...options, + scope: result.filter((item) => ['$scopes', '$jobsMapByNodeKey', '$context'].includes(item.key)), + }); + const { fieldNames = defaultFieldNames } = options; + if (!subOptions) { + return result; + } + + const scope = result.find((item) => item[fieldNames.value] === '$scopes'); + if (scope) { + if (!scope[fieldNames.children]) { + scope[fieldNames.children] = []; + } + + scope[fieldNames.children].unshift({ + key: node.key, + [fieldNames.value]: node.key, + [fieldNames.label]: node.title ?? `#${node.id}`, + [fieldNames.children]: subOptions, + }); + scope.disabled = false; + } + + return [...result]; +} + +function LoopVariableTextArea({ variableOptions, ...props }): JSX.Element { + const scope = useVariableHook(variableOptions); + return ; +} + export default class extends Instruction { title = `{{t("Loop", { ns: "${NAMESPACE}" })}}`; type = 'loop'; @@ -61,7 +255,8 @@ export default class extends Instruction { 'x-component': 'WorkflowVariableInput', 'x-component-props': { changeOnSelect: true, - useTypedConstant: ['string', 'number', 'null'], + nullable: false, + useTypedConstant: ['string', ['number', { step: 1, min: 0, precision: 0 }]], className: css` width: 100%; @@ -75,10 +270,101 @@ export default class extends Instruction { `, }, required: true, + default: 1, + 'x-reactions': [ + { + target: 'calculation', + effects: ['onFieldValueChange'], + fulfill: { + state: { + value: null, + }, + }, + }, + { + target: 'expression', + effects: ['onFieldValueChange'], + fulfill: { + state: { + value: '', + }, + }, + }, + ], + }, + // startIndex: { + // type: 'number', + // title: `{{t("Start index", { ns: "${NAMESPACE}" })}}`, + // description: `{{t("The index number used in loop scope variable.", { ns: "${NAMESPACE}" })}}`, + // 'x-decorator': 'FormItem', + // 'x-component': 'RadioWithTooltip', + // 'x-component-props': { + // options: [ + // { + // label: `{{t("From 0", { ns: "${NAMESPACE}" })}}`, + // value: 0, + // tooltip: `{{t("Follow programming language conventions.", { ns: "${NAMESPACE}" })}}`, + // }, + // { + // label: `{{t("From 1", { ns: "${NAMESPACE}" })}}`, + // value: 1, + // tooltip: `{{t("Follow natural language conventions.", { ns: "${NAMESPACE}" })}}`, + // }, + // ], + // }, + // default: 0, + // }, + condition: { + type: 'boolean', + 'x-decorator': 'FormItem', + 'x-component': 'LoopCondition', + 'x-reactions': [ + { + dependencies: ['target'], + fulfill: { + state: { + visible: '{{Boolean($deps[0])}}', + }, + }, + }, + ], + default: false, + }, + exit: { + type: 'number', + title: `{{t("When node inside loop failed", { ns: "${NAMESPACE}" })}}`, + 'x-decorator': 'FormItem', + 'x-component': 'RadioWithTooltip', + 'x-component-props': { + direction: 'vertical', + options: [ + { + label: `{{t("Exit workflow", { ns: "${NAMESPACE}" })}}`, + value: 0, + }, + { + label: `{{t("Exit loop and continue workflow", { ns: "${NAMESPACE}" })}}`, + value: 1, + }, + { + label: `{{t("Continue loop on next item", { ns: "${NAMESPACE}" })}}`, + value: 2, + }, + ], + }, + default: 0, }, }; + scope = { + renderEngineReference, + }; components = { + LoopCondition, WorkflowVariableInput, + WorkflowVariableTextArea, + LoopVariableTextArea, + RadioWithTooltip, + CalculationConfig, }; Component({ data }) { // eslint-disable-next-line react-hooks/rules-of-hooks @@ -111,60 +397,5 @@ export default class extends Instruction { ); } - useScopeVariables(node, options) { - // eslint-disable-next-line react-hooks/rules-of-hooks - const compile = useCompile(); - // eslint-disable-next-line react-hooks/rules-of-hooks - const langLoopTarget = useLang('Loop target'); - // eslint-disable-next-line react-hooks/rules-of-hooks - const langLoopIndex = useLang('Loop index'); - // eslint-disable-next-line react-hooks/rules-of-hooks - const langLoopLength = useLang('Loop length'); - const { target } = node.config; - if (!target) { - return null; - } - - const { fieldNames = defaultFieldNames } = options; - - // const { workflow } = useFlowContext(); - // const current = useNodeContext(); - // const upstreams = useAvailableUpstreams(current); - // find target data model by path described in `config.target` - // 1. get options from $context/$jobsMapByNodeKey - // 2. route to sub-options and use as loop target options - let targetOption: VariableOption = { - key: 'item', - [fieldNames.value]: 'item', - [fieldNames.label]: langLoopTarget, - }; - - if (typeof target === 'string' && target.startsWith('{{') && target.endsWith('}}')) { - const paths = target - .slice(2, -2) - .split('.') - .map((path) => path.trim()); - - const targetOptions = [scopeOptions, nodesOptions, triggerOptions].map((item: any) => { - const opts = item.useOptions({ ...options, current: node }).filter(Boolean); - return { - [fieldNames.label]: compile(item.label), - [fieldNames.value]: item.value, - key: item.value, - [fieldNames.children]: opts, - disabled: opts && !opts.length, - }; - }); - - const found = findOption(targetOptions, paths); - - targetOption = Object.assign({}, found, targetOption); - } - - return [ - targetOption, - { key: 'index', [fieldNames.value]: 'index', [fieldNames.label]: langLoopIndex }, - { key: 'length', [fieldNames.value]: 'length', [fieldNames.label]: langLoopLength }, - ]; - } + useScopeVariables = useScopeVariables; } diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/src/constants.ts b/packages/plugins/@nocobase/plugin-workflow-loop/src/constants.ts new file mode 100644 index 00000000000..d62c0aa0d9f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-loop/src/constants.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ + +export const CHECKPOINT = { + BEFORE: 0, + AFTER: 1, +}; + +export const CONTINUE_ON_FALSE = { + BREAK: false, + CONTINUE: true, +}; + +export const EXIT = { + CONTINUE: 2, + BREAK: 1, + RETURN: 0, +}; diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-workflow-loop/src/locale/zh-CN.json index 19b014bd4f2..46084d64f0b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-workflow-loop/src/locale/zh-CN.json @@ -1,8 +1,22 @@ { "Loop": "循环", "Loop target": "循环对象", - "Loop index": "当前索引", + "Loop index (starts from 0)": "当前索引(从 0 开始)", + "Loop sequence (starts from 1)": "当前序号(从 1 开始)", "Loop length": "循环长度", "By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.": "使用循环节点可以对多条数据进行同样的操作,多条数据的来源可以是查询节点的多条结果,或者一条数据的多条关系数据。也可以用于一定次数的循环,或者对字符串中每一个字符的循环处理。循环次数过高可能引起性能问题,请谨慎使用。", - "A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "单一数字值将被视为循环次数,单一字符串值将被视为字符数组,其他非数组值将被转换为数组。达到循环次数,或者将数组循环完成后,循环节点结束。你也可以在循环中添加条件节点,以终止循环。" + "A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "单一数字值将被视为循环次数,单一字符串值将被视为字符数组,其他非数组值将被转换为数组。达到循环次数,或者将数组循环完成后,循环节点结束。你也可以在循环中添加条件节点,以终止循环。", + "Enable loop condition": "启用循环条件", + "Loop condition on each item": "循环每一项的条件", + "When to check": "检查时机", + "Before each starts": "每一轮开始之前", + "After each ends": "每一轮完成之后", + "When condition is not met on item": "当项不满足条件时", + "Exit loop": "退出循环", + "Continue on next item": "继续下一项", + "Condition": "条件", + "When node inside loop failed": "当循环内节点执行失败时", + "Continue loop on next item": "继续循环下一项", + "Exit loop and continue workflow": "退出循环并继续工作流", + "Exit workflow": "退出工作流" } diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/src/server/LoopInstruction.ts b/packages/plugins/@nocobase/plugin-workflow-loop/src/server/LoopInstruction.ts index b40e949d985..43d0ee527a4 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/src/server/LoopInstruction.ts +++ b/packages/plugins/@nocobase/plugin-workflow-loop/src/server/LoopInstruction.ts @@ -7,7 +7,22 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { Processor, Instruction, JOB_STATUS, FlowNodeModel, JobModel } from '@nocobase/plugin-workflow'; +import evaluators from '@nocobase/evaluators'; +import { Processor, Instruction, JOB_STATUS, FlowNodeModel, JobModel, logicCalculate } from '@nocobase/plugin-workflow'; +import { EXIT } from '../constants'; + +export type LoopInstructionConfig = { + target: any; + condition?: + | { + checkpoint?: number; + continueOnFalse?: boolean; + calculation?: any; + expression?: string; + } + | false; + exit?: number; +}; function getTargetLength(target) { let length = 0; @@ -17,39 +32,57 @@ function getTargetLength(target) { } length = Math.floor(target); } else { - const targets = (Array.isArray(target) ? target : [target]).filter((t) => t != null); + const targets = Array.isArray(target) ? target : [target].filter((t) => t != null); length = targets.length; } return length; } +function calculateCondition(node: FlowNodeModel, processor: Processor) { + const { engine, calculation, expression } = node.config.condition ?? {}; + const evaluator = evaluators.get(engine); + + return evaluator + ? evaluator(expression, processor.getScope(node.id, true)) + : logicCalculate(processor.getParsedValue(calculation, node.id, { includeSelfScope: true })); +} + export default class extends Instruction { async run(node: FlowNodeModel, prevJob: JobModel, processor: Processor) { const [branch] = processor.getBranches(node); const target = processor.getParsedValue(node.config.target, node.id); const length = getTargetLength(target); + const looped = 0; if (!branch || !length) { return { status: JOB_STATUS.RESOLVED, - result: 0, + result: { looped }, }; } + // NOTE: save job for condition calculation const job = await processor.saveJob({ status: JOB_STATUS.PENDING, - // save loop index - result: 0, + result: { looped }, nodeId: node.id, nodeKey: node.key, upstreamId: prevJob?.id ?? null, }); - // TODO: add loop scope to stack - // processor.stack.push({ - // label: node.title, - // value: node.id - // }); + if (node.config.condition) { + const { checkpoint, calculation, expression, continueOnFalse } = node.config.condition ?? {}; + if ((calculation || expression) && !checkpoint) { + const condition = calculateCondition(node, processor); + if (!condition && !continueOnFalse) { + job.set({ + status: JOB_STATUS.RESOLVED, + result: { looped, broken: true }, + }); + return job; + } + } + } await processor.run(branch, job); @@ -62,43 +95,74 @@ export default class extends Instruction { const [branch] = processor.getBranches(node); const { result, status } = job; - // if loop has been done (resolved / rejected), do not care newly executed branch jobs. + // NOTE: if loop has been done (resolved / rejected), do not care newly executed branch jobs. if (status !== JOB_STATUS.PENDING) { return processor.exit(); } - const nextIndex = result + 1; + const nextIndex = result.looped + 1; const target = processor.getParsedValue(loop.config.target, node.id); - // branchJob.status === JOB_STATUS.RESOLVED means branchJob is done, try next loop or exit as resolved - if (branchJob.status > JOB_STATUS.PENDING) { - job.set({ result: nextIndex }); + // NOTE: branchJob.status === JOB_STATUS.RESOLVED means branchJob is done, try next loop or exit as resolved + if ( + branchJob.status === JOB_STATUS.RESOLVED || + (branchJob.status < JOB_STATUS.PENDING && loop.config.exit === EXIT.CONTINUE) + ) { + job.set({ result: { looped: nextIndex } }); + await processor.saveJob(job); + + if (loop.config.condition) { + const { calculation, expression, continueOnFalse } = loop.config.condition ?? {}; + if (calculation || expression) { + const condition = calculateCondition(loop, processor); + if (!condition && !continueOnFalse) { + job.set({ + status: JOB_STATUS.RESOLVED, + result: { looped: nextIndex, broken: true }, + }); + return job; + } + } + } const length = getTargetLength(target); if (nextIndex < length) { await processor.saveJob(job); await processor.run(branch, job); - return null; + return processor.exit(); + } else { + job.set({ + status: JOB_STATUS.RESOLVED, + }); } + } else { + // NOTE: branchJob.status < JOB_STATUS.PENDING means branchJob is rejected, any rejection should cause loop rejected + job.set( + loop.config.exit + ? { + result: { looped: result.looped, broken: true }, + status: JOB_STATUS.RESOLVED, + } + : { + status: branchJob.status, + }, + ); } - // branchJob.status < JOB_STATUS.PENDING means branchJob is rejected, any rejection should cause loop rejected - job.set({ - status: branchJob.status, - }); - return job; } - getScope(node, index, processor) { + getScope(node, { looped }, processor) { const target = processor.getParsedValue(node.config.target, node.id); const targets = (Array.isArray(target) ? target : [target]).filter((t) => t != null); const length = getTargetLength(target); - const item = typeof target === 'number' ? index : targets[index]; + const index = looped; + const item = typeof target === 'number' ? index : targets[looped]; const result = { item, index, + sequence: index + 1, length, }; diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-loop/src/server/__tests__/instruction.test.ts index a9a72e73223..dd139f0938b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-loop/src/server/__tests__/instruction.test.ts @@ -13,6 +13,7 @@ import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; import Plugin from '..'; +import { EXIT } from '../../constants'; describe('workflow > instructions > loop', () => { let app: Application; @@ -66,7 +67,7 @@ describe('workflow > instructions > loop', () => { const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); expect(jobs.length).toBe(2); expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(0); + expect(jobs[0].result).toEqual({ looped: 0 }); }); it('should exit when branch meets error', async () => { @@ -99,209 +100,686 @@ describe('workflow > instructions > loop', () => { const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); expect(jobs.length).toBe(2); expect(jobs[0].status).toBe(JOB_STATUS.ERROR); - expect(jobs[0].result).toBe(0); + expect(jobs[0].result).toEqual({ looped: 0 }); expect(jobs[1].status).toBe(JOB_STATUS.ERROR); }); }); describe('config', () => { - it('no target just pass', async () => { - const n1 = await workflow.createNode({ - type: 'loop', + describe('target', () => { + it('no target just pass', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 0 }); }); - const n2 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - branchIndex: 0, + it('null target just pass', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: null, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 0 }); }); - const n3 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, + it('empty array just pass', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: [], + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 0 }); }); - await n1.setDownstream(n3); + it('null value in array will not be passed', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: [null], + }, + }); - const post = await PostRepo.create({ values: { title: 't1' } }); + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); - await sleep(500); + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); - const [execution] = await workflow.getExecutions(); - expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); - const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); - expect(jobs.length).toBe(2); - expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(0); + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + expect(jobs.length).toBe(3); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 1 }); + }); + + it('target is number, cycle number times', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2.5, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(4); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 2 }); + }); + + it('target is no array, set as an array', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: {}, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(3); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 1 }); + }); + + it('multiple targets', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: [1, 2], + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(4); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 2 }); + expect(jobs.filter((j) => j.nodeId === n2.id).length).toBe(2); + }); }); - it('null target just pass', async () => { - const n1 = await workflow.createNode({ - type: 'loop', - config: { - target: null, - }, + describe.skip('startIndex', () => { + it('startIndex as 0', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echoVariable', + config: { + variable: '{{$scopes.' + n1.key + '.item}}', + }, + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echoVariable', + config: { + variable: '{{$scopes.' + n1.key + '.index}}', + }, + upstreamId: n2.id, + }); + + await n2.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(5); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 2 }); + expect(jobs[1].result).toBe(0); + expect(jobs[2].result).toBe(0); + expect(jobs[3].result).toBe(1); + expect(jobs[4].result).toBe(1); }); - const n2 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - branchIndex: 0, + it('startIndex as 1', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + startIndex: 1, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echoVariable', + config: { + variable: `{{$scopes.${n1.key}.item}}`, + }, + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echoVariable', + config: { + variable: `{{$scopes.${n1.key}.index}}`, + }, + upstreamId: n2.id, + }); + + await n2.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(5); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 2 }); + expect(jobs[1].result).toBe(1); + expect(jobs[2].result).toBe(1); + expect(jobs[3].result).toBe(2); + expect(jobs[4].result).toBe(2); }); - - const n3 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - }); - - await n1.setDownstream(n3); - - const post = await PostRepo.create({ values: { title: 't1' } }); - - await sleep(500); - - const [execution] = await workflow.getExecutions(); - expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); - const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); - expect(jobs.length).toBe(2); - expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(0); }); - it('empty array just pass', async () => { - const n1 = await workflow.createNode({ - type: 'loop', - config: { - target: [], - }, + describe('condition', () => { + it('empty condition', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + condition: { + calculation: {}, + }, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(3); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 2 }); }); - const n2 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - branchIndex: 0, + it('condition engine basic before each: true with loop variable', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + }); + await n1.update({ + config: { + target: 2, + condition: { + calculation: { + group: { + type: 'and', + calculations: [ + { + calculator: 'equal', + operands: [`{{$scopes.${n1.key}.item}}`, 0], + }, + { + calculator: 'equal', + operands: [`{{$scopes.${n1.key}.index}}`, 0], + }, + ], + }, + }, + }, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 1, broken: true }); }); - const n3 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, + it('condition engine basic after each: true with loop variable', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + }); + await n1.update({ + config: { + target: 2, + condition: { + checkpoint: 1, + calculation: { + group: { + type: 'and', + calculations: [ + { + calculator: 'equal', + operands: [`{{$scopes.${n1.key}.item}}`, 0], + }, + { + calculator: 'equal', + operands: [`{{$scopes.${n1.key}.index}}`, 0], + }, + ], + }, + }, + }, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 1, broken: true }); }); - await n1.setDownstream(n3); + it('condition engine basic before each: first false', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + }); + await n1.update({ + config: { + target: 2, + condition: { + calculation: { + group: { + type: 'and', + calculations: [ + { + calculator: 'equal', + operands: [1, 0], + }, + ], + }, + }, + }, + }, + }); - const post = await PostRepo.create({ values: { title: 't1' } }); + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); - await sleep(500); + const post = await PostRepo.create({ values: { title: 't1' } }); - const [execution] = await workflow.getExecutions(); - expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); - const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); - expect(jobs.length).toBe(2); - expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(0); + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(1); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 0, broken: true }); + }); + + it('condition engine basic after each: first false', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + }); + await n1.update({ + config: { + target: 2, + condition: { + checkpoint: 1, + calculation: { + group: { + type: 'and', + calculations: [ + { + calculator: 'equal', + operands: [1, 0], + }, + ], + }, + }, + }, + }, + }); + + const n2 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + branchIndex: 0, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 1, broken: true }); + }); }); - it('target is number, cycle number times', async () => { - const n1 = await workflow.createNode({ - type: 'loop', - config: { - target: 2.5, - }, + describe('exit', () => { + it('exit not configured (legacy)', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + }, + }); + + const n2 = await workflow.createNode({ + type: 'error', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.ERROR); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.ERROR); + expect(jobs[0].result).toEqual({ looped: 0 }); }); - const n2 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - branchIndex: 0, + it('exit as failed', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + exit: EXIT.RETURN, + }, + }); + + const n2 = await workflow.createNode({ + type: 'error', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.ERROR); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(2); + expect(jobs[0].status).toBe(JOB_STATUS.ERROR); + expect(jobs[0].result).toEqual({ looped: 0 }); }); - const n3 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, + it('exit as break', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + exit: EXIT.BREAK, + }, + }); + + const n2 = await workflow.createNode({ + type: 'error', + upstreamId: n1.id, + branchIndex: 0, + }); + + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); + + await n1.setDownstream(n3); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(3); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 0, broken: true }); }); - await n1.setDownstream(n3); + it('exit as continue', async () => { + const n1 = await workflow.createNode({ + type: 'loop', + config: { + target: 2, + exit: EXIT.CONTINUE, + }, + }); - const post = await PostRepo.create({ values: { title: 't1' } }); + const n2 = await workflow.createNode({ + type: 'error', + upstreamId: n1.id, + branchIndex: 0, + }); - await sleep(500); + const n3 = await workflow.createNode({ + type: 'echo', + upstreamId: n1.id, + }); - const [execution] = await workflow.getExecutions(); - expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); - const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + await n1.setDownstream(n3); - expect(jobs.length).toBe(4); - expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(2); - }); + const post = await PostRepo.create({ values: { title: 't1' } }); - it('target is no array, set as an array', async () => { - const n1 = await workflow.createNode({ - type: 'loop', - config: { - target: {}, - }, + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); + + expect(jobs.length).toBe(4); + expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); + expect(jobs[0].result).toEqual({ looped: 2 }); }); - - const n2 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - branchIndex: 0, - }); - - const n3 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - }); - - await n1.setDownstream(n3); - - const post = await PostRepo.create({ values: { title: 't1' } }); - - await sleep(500); - - const [execution] = await workflow.getExecutions(); - expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); - const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); - - expect(jobs.length).toBe(3); - expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(1); - }); - - it('multiple targets', async () => { - const n1 = await workflow.createNode({ - type: 'loop', - config: { - target: [1, 2], - }, - }); - - const n2 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - branchIndex: 0, - }); - - const n3 = await workflow.createNode({ - type: 'echo', - upstreamId: n1.id, - }); - - await n1.setDownstream(n3); - - const post = await PostRepo.create({ values: { title: 't1' } }); - - await sleep(500); - - const [execution] = await workflow.getExecutions(); - expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); - const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); - - expect(jobs.length).toBe(4); - expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED); - expect(jobs[0].result).toBe(2); - expect(jobs.filter((j) => j.nodeId === n2.id).length).toBe(2); }); }); diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/src/server/migrations/20240929212031-change-loop-result.ts b/packages/plugins/@nocobase/plugin-workflow-loop/src/server/migrations/20240929212031-change-loop-result.ts new file mode 100644 index 00000000000..1b50d241b86 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-loop/src/server/migrations/20240929212031-change-loop-result.ts @@ -0,0 +1,45 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import { Migration } from '@nocobase/server'; + +export default class extends Migration { + async up() { + const { db, app } = this.context; + const JobRepo = db.getRepository('jobs'); + await db.sequelize.transaction(async (transaction) => { + const records = await JobRepo.find({ + filter: { + node: { + type: 'loop', + }, + }, + transaction, + }); + + app.logger.debug(`${records.length} records need to be migrated.`); + + for (const record of records) { + const { result } = record; + if (typeof result === 'number') { + await record.update({ result: { looped: result } }, { transaction }); + } + } + }); + } +} diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts index fcb1b9b6797..3a04e1d057f 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts @@ -67,16 +67,18 @@ export async function submit(context: Context, next) { return context.throw(403); } const presetValues = processor.getParsedValue(actionItem.values ?? {}, userJob.nodeId, { - // @deprecated - currentUser: currentUser, - // @deprecated - currentRecord: values.result[formKey], - // @deprecated - currentTime: new Date(), - $user: currentUser, - $nForm: values.result[formKey], - $nDate: { - now: new Date(), + additionalScope: { + // @deprecated + currentUser: currentUser, + // @deprecated + currentRecord: values.result[formKey], + // @deprecated + currentTime: new Date(), + $user: currentUser, + $nForm: values.result[formKey], + $nDate: { + now: new Date(), + }, }, }); diff --git a/packages/plugins/@nocobase/plugin-workflow-request/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-workflow-request/src/locale/zh-CN.json index b397b3f0528..a27dcc8b92b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-workflow-request/src/locale/zh-CN.json @@ -12,8 +12,8 @@ "Add key-value pairs": "添加键值对", "Format": "格式化", "Insert": "插入", - "Timeout config": "超时设置", - "ms": "毫秒", + "Timeout": "超时设置", + "Milliseconds": "毫秒", "Input request data": "输入请求数据", "Only support standard JSON data": "仅支持标准 JSON 数据", "\"Content-Type\" will be ignored from headers.": "请求头中配置的 \"Content-Type\" 将被忽略。", diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts index 772bb68e1bd..50c8855db23 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts @@ -19,6 +19,15 @@ export default { }, }, + echoVariable: { + run({ id, config = {} }: any, job, processor) { + return { + status: 1, + result: config.variable ? processor.getParsedValue(config.variable, id) : null, + }; + }, + }, + error: { run(node, input, processor) { throw new Error('definite error'); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/components/Calculation.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/components/Calculation.tsx new file mode 100644 index 00000000000..627ebdd93f3 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/components/Calculation.tsx @@ -0,0 +1,351 @@ +/** + * 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 { Registry } from '@nocobase/utils/client'; +import React, { createContext, useCallback, useContext } from 'react'; +import { lang, NAMESPACE } from '../locale'; +import { css, cx, useCompile, Variable } from '@nocobase/client'; +import { useWorkflowVariableOptions } from '../variable'; +import { Button, Select } from 'antd'; +import { Trans, useTranslation } from 'react-i18next'; +import { CloseCircleOutlined } from '@ant-design/icons'; + +interface Calculator { + name: string; + type: 'boolean' | 'number' | 'string' | 'date' | 'unknown' | 'null' | 'array'; + group: string; +} + +export const calculators = new Registry(); + +calculators.register('equal', { + name: '=', + type: 'boolean', + group: 'boolean', +}); +calculators.register('notEqual', { + name: '≠', + type: 'boolean', + group: 'boolean', +}); +calculators.register('gt', { + name: '>', + type: 'boolean', + group: 'boolean', +}); +calculators.register('gte', { + name: '≥', + type: 'boolean', + group: 'boolean', +}); +calculators.register('lt', { + name: '<', + type: 'boolean', + group: 'boolean', +}); +calculators.register('lte', { + name: '≤', + type: 'boolean', + group: 'boolean', +}); + +calculators.register('add', { + name: '+', + type: 'number', + group: 'number', +}); +calculators.register('minus', { + name: '-', + type: 'number', + group: 'number', +}); +calculators.register('multiple', { + name: '*', + type: 'number', + group: 'number', +}); +calculators.register('divide', { + name: '/', + type: 'number', + group: 'number', +}); +calculators.register('mod', { + name: '%', + type: 'number', + group: 'number', +}); + +calculators.register('includes', { + name: '{{t("contains")}}', + type: 'boolean', + group: 'string', +}); +calculators.register('notIncludes', { + name: '{{t("does not contain")}}', + type: 'boolean', + group: 'string', +}); +calculators.register('startsWith', { + name: '{{t("starts with")}}', + type: 'boolean', + group: 'string', +}); +calculators.register('notStartsWith', { + name: '{{t("not starts with")}}', + type: 'boolean', + group: 'string', +}); +calculators.register('endsWith', { + name: '{{t("ends with")}}', + type: 'boolean', + group: 'string', +}); +calculators.register('notEndsWith', { + name: '{{t("not ends with")}}', + type: 'boolean', + group: 'string', +}); +calculators.register('concat', { + name: `{{t("concat", { ns: "${NAMESPACE}" })}}`, + type: 'string', + group: 'string', +}); + +const calculatorGroups = [ + { + value: 'boolean', + title: '{{t("Comparision")}}', + }, + { + value: 'number', + title: `{{t("Arithmetic calculation", { ns: "${NAMESPACE}" })}}`, + }, + { + value: 'string', + title: `{{t("String operation", { ns: "${NAMESPACE}" })}}`, + }, + { + value: 'date', + title: `{{t("Date", { ns: "${NAMESPACE}" })}}`, + }, +]; + +function getGroupCalculators(group) { + return Array.from(calculators.getEntities()).filter(([key, value]) => value.group === group); +} + +function Calculation({ calculator, operands = [], onChange }) { + const compile = useCompile(); + const useVariableHook = useContext(VariableHookContext); + const leftOptions = useVariableHook(); + const rightOptions = useVariableHook(); + const leftOperandOnChange = useCallback( + (v) => onChange({ calculator, operands: [v, operands[1]] }), + [calculator, onChange, operands], + ); + const rightOperandOnChange = useCallback( + (v) => onChange({ calculator, operands: [operands[0], v] }), + [calculator, onChange, operands], + ); + const operatorOnChange = useCallback((v) => onChange({ operands, calculator: v }), [onChange, operands]); + + return ( +
+ + + +
+ ); +} + +function CalculationItem({ value, onChange, onRemove }) { + if (!value) { + return null; + } + + const { calculator, operands = [] } = value; + + return ( +
+ {value.group ? ( + onChange({ ...value, group })} /> + ) : ( + + )} +
+ ); +} + +function CalculationGroup({ value, onChange }) { + const { t } = useTranslation(); + const { type = 'and', calculations = [] } = value; + + const onAddSingle = useCallback(() => { + onChange({ + ...value, + calculations: [...calculations, { not: false, calculator: 'equal' }], + }); + }, [value, calculations, onChange]); + + const onAddGroup = useCallback(() => { + onChange({ + ...value, + calculations: [...calculations, { not: false, group: { type: 'and', calculations: [] } }], + }); + }, [value, calculations, onChange]); + + const onRemove = useCallback( + (i: number) => { + calculations.splice(i, 1); + onChange({ ...value, calculations: [...calculations] }); + }, + [value, calculations, onChange], + ); + + const onItemChange = useCallback( + (i: number, v) => { + calculations.splice(i, 1, v); + + onChange({ ...value, calculations: [...calculations] }); + }, + [value, calculations, onChange], + ); + + return ( +
+
+ + {'Meet '} + + {' conditions in the group'} + +
+
+ {calculations.map((calculation, i) => ( + onRemove(i)} + /> + ))} +
+
+ + +
+
+ ); +} + +const VariableHookContext = createContext(useWorkflowVariableOptions); + +export function CalculationConfig({ value, onChange, useVariableHook = useWorkflowVariableOptions }) { + const rule = value && Object.keys(value).length ? value : { group: { type: 'and', calculations: [] } }; + return ( + + onChange({ ...rule, group })} /> + + ); +} diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/components/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/components/index.ts index e2653ed9993..1336bed8a74 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/components/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/components/index.ts @@ -15,3 +15,5 @@ export * from './RadioWithTooltip'; export * from './CheckboxGroupWithTooltip'; export * from './ValueBlock'; export * from './SimpleDesigner'; +export * from './renderEngineReference'; +export * from './Calculation'; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx index 1393e28650f..a4c70cd4478 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx @@ -7,13 +7,9 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { CloseCircleOutlined } from '@ant-design/icons'; -import { css, cx, useCompile, Variable } from '@nocobase/client'; import { evaluators } from '@nocobase/evaluators/client'; -import { Registry } from '@nocobase/utils/client'; -import { Button, Select } from 'antd'; -import React, { useCallback } from 'react'; -import { Trans, useTranslation } from 'react-i18next'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Instruction, NodeDefaultView } from '.'; import { Branch } from '../Branch'; import { RadioWithTooltip, RadioWithTooltipOption } from '../components/RadioWithTooltip'; @@ -22,333 +18,7 @@ import { useFlowContext } from '../FlowContext'; import { lang, NAMESPACE } from '../locale'; import useStyles from '../style'; import { useWorkflowVariableOptions, WorkflowVariableTextArea } from '../variable'; - -interface Calculator { - name: string; - type: 'boolean' | 'number' | 'string' | 'date' | 'unknown' | 'null' | 'array'; - group: string; -} - -export const calculators = new Registry(); - -calculators.register('equal', { - name: '=', - type: 'boolean', - group: 'boolean', -}); -calculators.register('notEqual', { - name: '≠', - type: 'boolean', - group: 'boolean', -}); -calculators.register('gt', { - name: '>', - type: 'boolean', - group: 'boolean', -}); -calculators.register('gte', { - name: '≥', - type: 'boolean', - group: 'boolean', -}); -calculators.register('lt', { - name: '<', - type: 'boolean', - group: 'boolean', -}); -calculators.register('lte', { - name: '≤', - type: 'boolean', - group: 'boolean', -}); - -calculators.register('add', { - name: '+', - type: 'number', - group: 'number', -}); -calculators.register('minus', { - name: '-', - type: 'number', - group: 'number', -}); -calculators.register('multiple', { - name: '*', - type: 'number', - group: 'number', -}); -calculators.register('divide', { - name: '/', - type: 'number', - group: 'number', -}); -calculators.register('mod', { - name: '%', - type: 'number', - group: 'number', -}); - -calculators.register('includes', { - name: '{{t("contains")}}', - type: 'boolean', - group: 'string', -}); -calculators.register('notIncludes', { - name: '{{t("does not contain")}}', - type: 'boolean', - group: 'string', -}); -calculators.register('startsWith', { - name: '{{t("starts with")}}', - type: 'boolean', - group: 'string', -}); -calculators.register('notStartsWith', { - name: '{{t("not starts with")}}', - type: 'boolean', - group: 'string', -}); -calculators.register('endsWith', { - name: '{{t("ends with")}}', - type: 'boolean', - group: 'string', -}); -calculators.register('notEndsWith', { - name: '{{t("not ends with")}}', - type: 'boolean', - group: 'string', -}); -calculators.register('concat', { - name: `{{t("concat", { ns: "${NAMESPACE}" })}}`, - type: 'string', - group: 'string', -}); - -const calculatorGroups = [ - { - value: 'boolean', - title: '{{t("Comparision")}}', - }, - { - value: 'number', - title: `{{t("Arithmetic calculation", { ns: "${NAMESPACE}" })}}`, - }, - { - value: 'string', - title: `{{t("String operation", { ns: "${NAMESPACE}" })}}`, - }, - { - value: 'date', - title: `{{t("Date", { ns: "${NAMESPACE}" })}}`, - }, -]; - -function getGroupCalculators(group) { - return Array.from(calculators.getEntities()).filter(([key, value]) => value.group === group); -} - -function Calculation({ calculator, operands = [], onChange }) { - const compile = useCompile(); - const leftOptions = useWorkflowVariableOptions(); - const rightOptions = useWorkflowVariableOptions(); - const leftOperandOnChange = useCallback( - (v) => onChange({ calculator, operands: [v, operands[1]] }), - [calculator, onChange, operands], - ); - const rightOperandOnChange = useCallback( - (v) => onChange({ calculator, operands: [operands[0], v] }), - [calculator, onChange, operands], - ); - const operatorOnChange = useCallback((v) => onChange({ operands, calculator: v }), [onChange, operands]); - - return ( -
- - - -
- ); -} - -function CalculationItem({ value, onChange, onRemove }) { - if (!value) { - return null; - } - - const { calculator, operands = [] } = value; - - return ( -
- {value.group ? ( - onChange({ ...value, group })} /> - ) : ( - - )} -
- ); -} - -function CalculationGroup({ value, onChange }) { - const { t } = useTranslation(); - const { type = 'and', calculations = [] } = value; - - const onAddSingle = useCallback(() => { - onChange({ - ...value, - calculations: [...calculations, { not: false, calculator: 'equal' }], - }); - }, [value, calculations, onChange]); - - const onAddGroup = useCallback(() => { - onChange({ - ...value, - calculations: [...calculations, { not: false, group: { type: 'and', calculations: [] } }], - }); - }, [value, calculations, onChange]); - - const onRemove = useCallback( - (i: number) => { - calculations.splice(i, 1); - onChange({ ...value, calculations: [...calculations] }); - }, - [value, calculations, onChange], - ); - - const onItemChange = useCallback( - (i: number, v) => { - calculations.splice(i, 1, v); - - onChange({ ...value, calculations: [...calculations] }); - }, - [value, calculations, onChange], - ); - - return ( -
-
- - {'Meet '} - - {' conditions in the group'} - -
-
- {calculations.map((calculation, i) => ( - onRemove(i)} - /> - ))} -
-
- - -
-
- ); -} - -function CalculationConfig({ value, onChange }) { - const rule = value && Object.keys(value).length ? value : { group: { type: 'and', calculations: [] } }; - return onChange({ ...rule, group })} />; -} +import { CalculationConfig } from '../components/Calculation'; export default class extends Instruction { title = `{{t("Condition", { ns: "${NAMESPACE}" })}}`; @@ -390,7 +60,7 @@ export default class extends Instruction { default: 'basic', }, calculation: { - type: 'string', + type: 'object', title: `{{t("Condition", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'CalculationConfig', diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx index f2c7acbd9b1..296dcc6d52e 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx @@ -184,19 +184,16 @@ export function RemoveButton() { const current = useNodeContext(); const { modal } = App.useApp(); - if (!workflow) { - return null; - } const resource = api.resource('flow_nodes'); - async function onRemove() { - async function onOk() { - await resource.destroy?.({ - filterByTk: current.id, - }); - refresh(); - } + const onOk = useCallback(async () => { + await resource.destroy?.({ + filterByTk: current.id, + }); + refresh(); + }, [current.id, refresh, resource]); + const onRemove = useCallback(async () => { const usingNodes = nodes.filter((node) => { if (node === current) { return false; @@ -230,6 +227,10 @@ export function RemoveButton() { content: message, onOk, }); + }, [current, modal, nodes, onOk, t]); + + if (!workflow) { + return null; } return workflow.executed ? null : ( diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts index 420f7e08684..85909d06bf7 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts @@ -259,10 +259,8 @@ export default class Processor { // TODO(optimize) /** * @experimental - * @param {JobModel | Record} payload - * @returns {JobModel} */ - async saveJob(payload) { + async saveJob(payload: JobModel | Record): Promise { const { database } = this.execution.constructor; const { transaction } = this; const { model } = database.getCollection('jobs'); @@ -377,7 +375,7 @@ export default class Processor { /** * @experimental */ - public getScope(sourceNodeId: number) { + public getScope(sourceNodeId: number, includeSelfScope = false) { const node = this.nodesMap.get(sourceNodeId); const systemFns = {}; const scope = { @@ -389,9 +387,9 @@ export default class Processor { } const $scopes = {}; - for (let n = this.findBranchParentNode(node); n; n = this.findBranchParentNode(n)) { + for (let n = includeSelfScope ? node : this.findBranchParentNode(node); n; n = this.findBranchParentNode(n)) { const instruction = this.options.plugin.instructions.get(n.type); - if (typeof instruction.getScope === 'function') { + if (typeof instruction?.getScope === 'function') { $scopes[n.id] = $scopes[n.key] = instruction.getScope(n, this.jobsMapByNodeKey[n.key], this); } } @@ -407,9 +405,9 @@ export default class Processor { /** * @experimental */ - public getParsedValue(value, sourceNodeId: number, additionalScope?: object) { + public getParsedValue(value, sourceNodeId: number, { additionalScope = {}, includeSelfScope = false } = {}) { const template = parse(value); - const scope = Object.assign(this.getScope(sourceNodeId), additionalScope); + const scope = Object.assign(this.getScope(sourceNodeId, includeSelfScope), additionalScope); template.parameters.forEach(({ key }) => { appendArrayColumn(scope, key); }); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/index.ts index d7baac1bf24..0e679da7ff6 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/index.ts @@ -11,6 +11,7 @@ export * from './utils'; export * from './constants'; export * from './instructions'; export * from './functions'; +export * from './logicCalculate'; export { Trigger } from './triggers'; export { default as Processor } from './Processor'; export { default } from './Plugin'; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts index ab403523773..f9eaecd8469 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts @@ -8,13 +8,11 @@ */ import { evaluators } from '@nocobase/evaluators'; -import { Registry } from '@nocobase/utils'; import { Instruction } from '.'; import type Processor from '../Processor'; import { JOB_STATUS } from '../constants'; import type { FlowNodeModel, JobModel } from '../types'; - -type Comparer = (a: any, b: any) => boolean; +import { logicCalculate } from '../logicCalculate'; export const BRANCH_INDEX = { DEFAULT: null, @@ -22,115 +20,6 @@ export const BRANCH_INDEX = { ON_FALSE: 0, } as const; -export const calculators = new Registry(); - -// built-in functions -function equal(a, b) { - return a == b; -} - -function notEqual(a, b) { - return a != b; -} - -function gt(a, b) { - return a > b; -} - -function gte(a, b) { - return a >= b; -} - -function lt(a, b) { - return a < b; -} - -function lte(a, b) { - return a <= b; -} - -calculators.register('equal', equal); -calculators.register('notEqual', notEqual); -calculators.register('gt', gt); -calculators.register('gte', gte); -calculators.register('lt', lt); -calculators.register('lte', lte); - -calculators.register('==', equal); -calculators.register('!=', notEqual); -calculators.register('>', gt); -calculators.register('>=', gte); -calculators.register('<', lt); -calculators.register('<=', lte); - -function includes(a, b) { - return a.includes(b); -} - -function notIncludes(a, b) { - return !a.includes(b); -} - -function startsWith(a: string, b: string) { - return a.startsWith(b); -} - -function notStartsWith(a: string, b: string) { - return !a.startsWith(b); -} - -function endsWith(a: string, b: string) { - return a.endsWith(b); -} - -function notEndsWith(a: string, b: string) { - return !a.endsWith(b); -} - -calculators.register('includes', includes); -calculators.register('notIncludes', notIncludes); -calculators.register('startsWith', startsWith); -calculators.register('notStartsWith', notStartsWith); -calculators.register('endsWith', endsWith); -calculators.register('notEndsWith', notEndsWith); - -type CalculationItem = { - calculator?: string; - operands?: [any?, any?]; -}; - -type CalculationGroup = { - group: { - type: 'and' | 'or'; - calculations?: Calculation[]; - }; -}; - -type Calculation = CalculationItem | CalculationGroup; - -function calculate(calculation: CalculationItem = {}) { - type NewType = Comparer; - - let fn: NewType; - if (!(calculation.calculator && (fn = calculators.get(calculation.calculator)))) { - throw new Error(`no calculator function registered for "${calculation.calculator}"`); - } - return Boolean(fn(...(calculation.operands ?? []))); -} - -function logicCalculate(calculation?: Calculation) { - if (!calculation) { - return true; - } - - if (typeof calculation['group'] === 'object') { - const method = calculation['group'].type === 'and' ? 'every' : 'some'; - return (calculation['group'].calculations ?? [])[method]((item: Calculation) => logicCalculate(item)); - } - - return calculate(calculation as CalculationItem); -} - export class ConditionInstruction extends Instruction { async run(node: FlowNodeModel, prevJob, processor: Processor) { const { engine, calculation, expression, rejectOnFalse } = node.config || {}; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/logicCalculate.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/logicCalculate.ts new file mode 100644 index 00000000000..737ed335966 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/logicCalculate.ts @@ -0,0 +1,127 @@ +/** + * 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 { Registry } from '@nocobase/utils'; + +type Comparer = (a: any, b: any) => boolean; + +export const calculators = new Registry(); + +// built-in functions +function equal(a, b) { + return a == b; +} + +function notEqual(a, b) { + return a != b; +} + +function gt(a, b) { + return a > b; +} + +function gte(a, b) { + return a >= b; +} + +function lt(a, b) { + return a < b; +} + +function lte(a, b) { + return a <= b; +} + +calculators.register('equal', equal); +calculators.register('notEqual', notEqual); +calculators.register('gt', gt); +calculators.register('gte', gte); +calculators.register('lt', lt); +calculators.register('lte', lte); + +calculators.register('==', equal); +calculators.register('!=', notEqual); +calculators.register('>', gt); +calculators.register('>=', gte); +calculators.register('<', lt); +calculators.register('<=', lte); + +function includes(a, b) { + return a.includes(b); +} + +function notIncludes(a, b) { + return !a.includes(b); +} + +function startsWith(a: string, b: string) { + return a.startsWith(b); +} + +function notStartsWith(a: string, b: string) { + return !a.startsWith(b); +} + +function endsWith(a: string, b: string) { + return a.endsWith(b); +} + +function notEndsWith(a: string, b: string) { + return !a.endsWith(b); +} + +calculators.register('includes', includes); +calculators.register('notIncludes', notIncludes); +calculators.register('startsWith', startsWith); +calculators.register('notStartsWith', notStartsWith); +calculators.register('endsWith', endsWith); +calculators.register('notEndsWith', notEndsWith); + +type CalculationItem = { + calculator?: string; + operands?: [any?, any?]; +}; + +type CalculationGroup = { + group: { + type: 'and' | 'or'; + calculations?: Calculation[]; + }; +}; + +type Calculation = CalculationItem | CalculationGroup; + +function calculate(calculation: CalculationItem = {}): boolean { + let fn: Comparer; + if (!calculation.calculator || !calculation.operands?.length) { + return true; + } + if (!(fn = calculators.get(calculation.calculator))) { + throw new Error(`no calculator function registered for "${calculation.calculator}"`); + } + return Boolean(fn(...(calculation.operands ?? []))); +} + +const GroupTypeMethodMap = { + and: 'every', + or: 'some', +}; + +export function logicCalculate(calculation?: Calculation) { + if (!calculation) { + return true; + } + + if (typeof calculation['group'] === 'object') { + const method = GroupTypeMethodMap[calculation['group'].type]; + return (calculation['group'].calculations ?? [])[method]((item: Calculation) => logicCalculate(item)); + } + + return calculate(calculation as CalculationItem); +} From 379ae838626244ae198b480568c97b7a591e5791 Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 16 Oct 2024 22:39:20 +0800 Subject: [PATCH 019/307] feat(plugin-workflow): add test run for nodes (#5407) * feat(plugin-workflow): add test run for nodes * fix(plugin-workflow): fix locale * test(plugin-workflow): add test cases --- .../schema-component/antd/variable/Input.tsx | 1 + .../antd/variable/RawTextArea.tsx | 4 +- .../src/client/RequestInstruction.tsx | 3 +- .../src/server/RequestInstruction.ts | 25 +- .../src/server/__tests__/instruction.test.ts | 70 ++++- .../src/client/SQLInstruction.tsx | 1 + .../src/server/SQLInstruction.ts | 43 ++- .../src/server/__tests__/instruction.test.ts | 25 +- .../src/server/instructions.ts | 11 + .../src/client/nodes/index.tsx | 249 ++++++++++++++++-- .../plugin-workflow/src/locale/zh-CN.json | 5 +- .../plugin-workflow/src/server/Plugin.ts | 1 + .../server/__tests__/actions/nodes.test.ts | 29 ++ .../src/server/actions/index.ts | 1 + .../src/server/actions/nodes.ts | 21 ++ .../src/server/instructions/index.ts | 1 + 16 files changed, 455 insertions(+), 35 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/variable/Input.tsx b/packages/core/client/src/schema-component/antd/variable/Input.tsx index ac2182d3604..80b88914e50 100644 --- a/packages/core/client/src/schema-component/antd/variable/Input.tsx +++ b/packages/core/client/src/schema-component/antd/variable/Input.tsx @@ -480,6 +480,7 @@ export function Input(props: VariableInputProps) { margin-left: -1px; `)} type={variable ? 'primary' : 'default'} + disabled={disabled} /> )} diff --git a/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx b/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx index 258a1ef51c9..f37165bef86 100644 --- a/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx +++ b/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx @@ -25,7 +25,7 @@ function setNativeInputValue(input, value) { export function RawTextArea(props): JSX.Element { const inputRef = useRef(null); - const { changeOnSelect, component: Component = Input.TextArea, fieldNames, scope, ...others } = props; + const { changeOnSelect, component: Component = Input.TextArea, fieldNames, scope, buttonClass, ...others } = props; const dataScope = typeof scope === 'function' ? scope() : scope; const [options, setOptions] = useState(dataScope ? dataScope : []); @@ -66,7 +66,7 @@ export function RawTextArea(props): JSX.Element { > & { - headers: Array
; +export type RequestInstructionConfig = Pick & { + headers?: Header[]; contentType: string; - ignoreFail: boolean; + ignoreFail?: boolean; onlyData?: boolean; }; @@ -103,7 +103,7 @@ function responseFailure(error) { export default class extends Instruction { async run(node: FlowNodeModel, prevJob, processor: Processor) { - const config = processor.getParsedValue(node.config, node.id) as RequestConfig; + const config = processor.getParsedValue(node.config, node.id) as RequestInstructionConfig; const { workflow } = processor.execution; const sync = this.workflow.isWorkflowSync(workflow); @@ -171,10 +171,25 @@ export default class extends Instruction { } async resume(node: FlowNodeModel, job, processor: Processor) { - const { ignoreFail } = node.config as RequestConfig; + const { ignoreFail } = node.config as RequestInstructionConfig; if (ignoreFail) { job.set('status', JOB_STATUS.RESOLVED); } return job; } + + async test(config: RequestInstructionConfig) { + try { + const response = await request(config); + return { + status: JOB_STATUS.RESOLVED, + result: responseSuccess(response, config.onlyData), + }; + } catch (error) { + return { + status: config.ignoreFail ? JOB_STATUS.RESOLVED : JOB_STATUS.FAILED, + result: error.isAxiosError ? error.toJSON() : error.message, + }; + } + } } diff --git a/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts index 2cc00be40d8..c2c432955ab 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts @@ -19,7 +19,7 @@ import { MockServer } from '@nocobase/test'; import PluginWorkflow, { EXECUTION_STATUS, JOB_STATUS, Processor } from '@nocobase/plugin-workflow'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; -import { RequestConfig } from '../RequestInstruction'; +import RequestInstruction, { RequestConfig } from '../RequestInstruction'; const HOST = 'localhost'; @@ -111,6 +111,7 @@ describe('workflow > instructions > request', () => { let WorkflowModel; let workflow; let api: MockAPI; + let instruction: RequestInstruction; beforeEach(async () => { api = new MockAPI(); @@ -123,6 +124,9 @@ describe('workflow > instructions > request', () => { }); db = app.db; + + instruction = (app.pm.get(PluginWorkflow) as PluginWorkflow).instructions.get('request') as RequestInstruction; + WorkflowModel = db.getCollection('workflows').model; PostCollection = db.getCollection('posts'); PostRepo = PostCollection.repository; @@ -606,4 +610,68 @@ describe('workflow > instructions > request', () => { expect(job.result.status).toBe(404); }); }); + + describe('test run', () => { + it('invalid config', async () => { + const { status, result } = await instruction.test(Object.create({})); + expect(status).toBe(JOB_STATUS.FAILED); + expect(result).toBe("Cannot read properties of null (reading 'replace')"); + }); + + it('data url', async () => { + const { status, result } = await instruction.test({ + url: api.URL_DATA, + method: 'POST', + contentType: 'application/json', + data: { a: 1 }, + }); + expect(status).toBe(JOB_STATUS.RESOLVED); + expect(result.status).toBe(200); + expect(result.data).toEqual({ meta: {}, data: { a: 1 } }); + }); + + it('404', async () => { + const { status, result } = await instruction.test({ + url: api.URL_404, + method: 'GET', + contentType: '', + }); + expect(status).toBe(JOB_STATUS.FAILED); + expect(result.status).toBe(404); + }); + + it('timeout', async () => { + const { status, result } = await instruction.test({ + url: api.URL_TIMEOUT, + method: 'GET', + timeout: 1000, + contentType: '', + }); + expect(status).toBe(JOB_STATUS.FAILED); + expect(result.code).toBe('ECONNABORTED'); + }); + + it('ignoreFail', async () => { + const { status, result } = await instruction.test({ + url: api.URL_404, + method: 'GET', + ignoreFail: true, + contentType: '', + }); + expect(status).toBe(JOB_STATUS.RESOLVED); + expect(result.status).toBe(404); + }); + + it('timeout and ignoreFail', async () => { + const { status, result } = await instruction.test({ + url: api.URL_TIMEOUT, + method: 'GET', + timeout: 1000, + ignoreFail: true, + contentType: '', + }); + expect(status).toBe(JOB_STATUS.RESOLVED); + expect(result.code).toBe('ECONNABORTED'); + }); + }); }); diff --git a/packages/plugins/@nocobase/plugin-workflow-sql/src/client/SQLInstruction.tsx b/packages/plugins/@nocobase/plugin-workflow-sql/src/client/SQLInstruction.tsx index 158fba1e3bf..28762241afb 100644 --- a/packages/plugins/@nocobase/plugin-workflow-sql/src/client/SQLInstruction.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-sql/src/client/SQLInstruction.tsx @@ -80,4 +80,5 @@ export default class extends Instruction { [fieldNames.label]: title, }; } + testable = true; } diff --git a/packages/plugins/@nocobase/plugin-workflow-sql/src/server/SQLInstruction.ts b/packages/plugins/@nocobase/plugin-workflow-sql/src/server/SQLInstruction.ts index 124f08536c0..3198a1495a2 100644 --- a/packages/plugins/@nocobase/plugin-workflow-sql/src/server/SQLInstruction.ts +++ b/packages/plugins/@nocobase/plugin-workflow-sql/src/server/SQLInstruction.ts @@ -7,14 +7,20 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { SequelizeCollectionManager } from '@nocobase/data-source-manager'; import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '@nocobase/plugin-workflow'; +export type SQLInstructionConfig = { + dataSource?: string; + sql?: string; + withMeta?: boolean; +}; + export default class extends Instruction { async run(node: FlowNodeModel, input, processor: Processor) { const dataSourceName = node.config.dataSource || 'main'; - // @ts-ignore - const { db } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName).collectionManager; - if (!db) { + const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName); + if (!(collectionManager instanceof SequelizeCollectionManager)) { throw new Error(`type of data source "${node.config.dataSource}" is not database`); } const sql = processor.getParsedValue(node.config.sql || '', node.id).trim(); @@ -25,7 +31,7 @@ export default class extends Instruction { } const [result = null, meta = null] = - (await db.sequelize.query(sql, { + (await collectionManager.db.sequelize.query(sql, { transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction), // plain: true, // model: db.getCollection(node.config.collection).model @@ -36,4 +42,33 @@ export default class extends Instruction { status: JOB_STATUS.RESOLVED, }; } + + async test({ dataSource, sql, withMeta }: SQLInstructionConfig = {}) { + if (!sql) { + return { + result: null, + status: JOB_STATUS.RESOLVED, + }; + } + + const dataSourceName = dataSource || 'main'; + const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName); + if (!(collectionManager instanceof SequelizeCollectionManager)) { + throw new Error(`type of data source "${dataSource}" is not database`); + } + + try { + const [result = null, meta = null] = (await collectionManager.db.sequelize.query(sql)) ?? []; + + return { + result: withMeta ? [result, meta] : result, + status: JOB_STATUS.RESOLVED, + }; + } catch (error) { + return { + result: error.message, + status: JOB_STATUS.ERROR, + }; + } + } } diff --git a/packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/instruction.test.ts index 981543330e9..cc08b48bf63 100644 --- a/packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/instruction.test.ts @@ -9,10 +9,11 @@ import Database, { fn } from '@nocobase/database'; import { Application } from '@nocobase/server'; -import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; +import WorkflowPluginServer, { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; import Plugin from '..'; +import SQLInstruction from '../SQLInstruction'; const mysql = process.env.DB_DIALECT === 'mysql' ? describe : describe.skip; @@ -24,12 +25,13 @@ describe('workflow > instructions > sql', () => { let ReplyRepo; let WorkflowModel; let workflow; + let instruction: SQLInstruction; beforeEach(async () => { app = await getApp({ plugins: [Plugin], }); - + instruction = (app.pm.get(WorkflowPluginServer) as WorkflowPluginServer).instructions.get('sql') as SQLInstruction; db = app.db; WorkflowModel = db.getCollection('workflows').model; PostCollection = db.getCollection('posts'); @@ -334,4 +336,23 @@ describe('workflow > instructions > sql', () => { }); }); }); + + describe('test', () => { + it('empty sql', async () => { + const { status, result } = await instruction.test({}); + expect(status).toBe(JOB_STATUS.RESOLVED); + expect(result).toBe(null); + }); + + it('invalid sql', async () => { + const { status, result } = await instruction.test({ sql: '1' }); + expect(status).toBe(JOB_STATUS.ERROR); + }); + + it('valid sql', async () => { + const { status, result } = await instruction.test({ sql: 'select 1 as a' }); + expect(status).toBe(JOB_STATUS.RESOLVED); + expect(result).toEqual([{ a: 1 }]); + }); + }); }); diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts index 50c8855db23..cf6c9fa950c 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/server/instructions.ts @@ -17,6 +17,12 @@ export default { result: config.path == null ? result : lodash.get(result, config.path), }; }, + test(config = {}) { + return { + status: 1, + result: null, + }; + }, }, echoVariable: { @@ -40,6 +46,11 @@ export default { status: 0, }; }, + test() { + return { + status: 0, + }; + }, }, prompt: { diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx index 296dcc6d52e..4c8b68968ec 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx @@ -8,12 +8,12 @@ */ import { CloseOutlined, DeleteOutlined } from '@ant-design/icons'; -import { createForm } from '@formily/core'; +import { createForm, Field } from '@formily/core'; import { toJS } from '@formily/reactive'; -import { ISchema, useForm } from '@formily/react'; -import { Alert, App, Button, Dropdown, Input, Tag, Tooltip, message } from 'antd'; -import { cloneDeep, get } from 'lodash'; -import React, { useCallback, useContext, useMemo, useState } from 'react'; +import { ISchema, observer, useField, useForm } from '@formily/react'; +import { Alert, App, Button, Dropdown, Empty, Input, Space, Tag, Tooltip, message } from 'antd'; +import { cloneDeep, get, set } from 'lodash'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -21,10 +21,12 @@ import { FormProvider, SchemaComponent, SchemaInitializerItemType, + Variable, css, cx, useAPIClient, useActionContext, + useCancelAction, useCompile, usePlugin, useResourceActionContext, @@ -40,7 +42,7 @@ import { JobStatusOptionsMap } from '../constants'; import { useGetAriaLabelOfAddButton } from '../hooks/useGetAriaLabelOfAddButton'; import { lang } from '../locale'; import useStyles from '../style'; -import { UseVariableOptions, VariableOption } from '../variable'; +import { UseVariableOptions, VariableOption, WorkflowVariableInput } from '../variable'; export type NodeAvailableContext = { engine: WorkflowPlugin; @@ -58,7 +60,7 @@ export abstract class Instruction { * @experimental */ options?: { label: string; value: any; key: string }[]; - fieldset: { [key: string]: ISchema }; + fieldset: Record; /** * @experimental */ @@ -80,6 +82,7 @@ export abstract class Instruction { */ isAvailable?(ctx: NodeAvailableContext): boolean; end?: boolean | ((node) => boolean); + testable?: boolean; } function useUpdateAction() { @@ -304,6 +307,194 @@ function useFormProviderProps() { return { form: useForm() }; } +const useRunAction = () => { + const { values, query } = useForm(); + const node = useNodeContext(); + const api = useAPIClient(); + const ctx = useActionContext(); + const field = useField(); + return { + async run() { + const template = parse(node.config); + const config = template(toJS(values.config)); + const resultField = query('result').take() as Field; + resultField.setValue(null); + resultField.setFeedback({}); + + field.data = field.data || {}; + field.data.loading = true; + + try { + const { + data: { data }, + } = await api.resource('flow_nodes').test({ + values: { + config, + type: node.type, + }, + }); + + resultField.setFeedback({ + type: data.status > 0 ? 'success' : 'error', + messages: data.status > 0 ? [lang('Resolved')] : [lang('Failed')], + }); + resultField.setValue(data.result); + } catch (err) { + resultField.setFeedback({ + type: 'error', + messages: err.message, + }); + } + field.data.loading = false; + ctx.setFormValueChanged(false); + }, + }; +}; + +const VariableKeysContext = createContext([]); + +function VariableReplacer({ name, value, onChange }) { + return ( + + + + + ); +} + +function TestFormFieldset({ value, onChange }) { + const keys = useContext(VariableKeysContext); + + return keys.length ? ( + <> + {keys.map((key) => ( + { + set(value, key, v); + onChange(value); + }} + /> + ))} + + ) : ( + + ); +} + +function TestButton() { + const node = useNodeContext(); + const { values } = useForm(); + const template = parse(values); + const keys = template.parameters.map((item) => item.key); + const form = useMemo(() => createForm(), []); + + return ( + + + + + + ); +} + export function NodeDefaultView(props) { const { data, children } = props; const compile = useCompile(); @@ -515,25 +706,45 @@ export function NodeDefaultView(props) { }, properties: instruction.fieldset, }, - actions: workflow.executed + footer: workflow.executed ? null : { type: 'void', 'x-component': 'Action.Drawer.Footer', properties: { - cancel: { - title: '{{t("Cancel")}}', - 'x-component': 'Action', + actions: { + type: 'void', + 'x-component': 'ActionBar', 'x-component-props': { - useAction: '{{ cm.useCancelAction }}', + style: { + flexGrow: 1, + }, }, - }, - submit: { - title: '{{t("Submit")}}', - 'x-component': 'Action', - 'x-component-props': { - type: 'primary', - useAction: '{{ useUpdateAction }}', + properties: { + ...(instruction.testable + ? { + test: { + type: 'void', + 'x-component': observer(TestButton), + 'x-align': 'left', + }, + } + : {}), + cancel: { + title: '{{t("Cancel")}}', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ cm.useCancelAction }}', + }, + }, + submit: { + title: '{{t("Submit")}}', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: '{{ useUpdateAction }}', + }, + }, }, }, }, diff --git a/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json index 170bba5b2f8..015edafe892 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.json @@ -203,5 +203,8 @@ "End process": "结束流程", "End the process immediately, with set status.": "以设置的状态立即结束流程。", "End status": "结束状态", - "Succeeded": "成功" + "Succeeded": "成功", + "Test run": "测试执行", + "Test run will do the actual data manipulating or API calling, please use with caution.": "测试执行会进行实际的数据操作或 API 调用,请谨慎使用。", + "No variable": "无变量" } diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts index ddd5839d1d6..cf38febf512 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts @@ -251,6 +251,7 @@ export default class PluginWorkflowServer extends Plugin { 'executions:destroy', 'flow_nodes:update', 'flow_nodes:destroy', + 'flow_nodes:test', ], }); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/nodes.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/nodes.test.ts index 22ac417ea04..0bb667c5510 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/nodes.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/nodes.test.ts @@ -10,6 +10,7 @@ import { MockServer } from '@nocobase/test'; import Database from '@nocobase/database'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; +import { JOB_STATUS } from '../../constants'; describe('workflow > actions > workflows', () => { let app: MockServer; @@ -244,4 +245,32 @@ describe('workflow > actions > workflows', () => { expect(nodes.length).toBe(0); }); }); + + describe('test', () => { + it('test method not implemented', async () => { + const { status } = await agent.resource('flow_nodes').test({ values: { type: 'error' } }); + + expect(status).toBe(400); + }); + + it('test method implemented', async () => { + const { + status, + body: { data }, + } = await agent.resource('flow_nodes').test({ values: { type: 'echo' } }); + + expect(status).toBe(200); + expect(data.status).toBe(JOB_STATUS.RESOLVED); + }); + + it('test with pending status', async () => { + const { + status, + body: { data }, + } = await agent.resource('flow_nodes').test({ values: { type: 'pending' } }); + + expect(status).toBe(200); + expect(data.status).toBe(JOB_STATUS.PENDING); + }); + }); }); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/actions/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/actions/index.ts index 2c4d9e1bbca..e0b100700b0 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/actions/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/actions/index.ts @@ -30,6 +30,7 @@ export default function ({ app }) { ...make('flow_nodes', { update: nodes.update, destroy: nodes.destroy, + test: nodes.test, }), ...make('executions', executions), }); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/actions/nodes.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/actions/nodes.ts index 1c635b0606c..1cffbd2dbd6 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/actions/nodes.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/actions/nodes.ts @@ -9,6 +9,7 @@ import { Context, utils } from '@nocobase/actions'; import { MultipleRelationRepository, Op, Repository } from '@nocobase/database'; +import WorkflowPlugin from '..'; import type { WorkflowModel } from '../types'; export async function create(context: Context, next) { @@ -219,3 +220,23 @@ export async function update(context: Context, next) { await next(); } + +export async function test(context: Context, next) { + const { values = {} } = context.action.params; + const { type, config = {} } = values; + const plugin = context.app.pm.get(WorkflowPlugin) as WorkflowPlugin; + const instruction = plugin.instructions.get(type); + if (!instruction) { + context.throw(400, `instruction "${type}" not registered`); + } + if (typeof instruction.test !== 'function') { + context.throw(400, `test method of instruction "${type}" not implemented`); + } + try { + context.body = await instruction.test(config); + } catch (error) { + context.throw(500, error.message); + } + + next(); +} diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts index bd208b4ce06..ddea1cc15e4 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts @@ -29,6 +29,7 @@ export type InstructionInterface = { resume?: Runner; getScope?: (node: FlowNodeModel, data: any, processor: Processor) => any; duplicateConfig?: (node: FlowNodeModel, options: Transactionable) => object | Promise; + test?: (config: Record) => IJob | Promise; }; // what should a instruction do? From a78f2e170299fb935c34005a7e0e55da399de2d2 Mon Sep 17 00:00:00 2001 From: Chareice Date: Wed, 16 Oct 2024 23:24:10 +0800 Subject: [PATCH 020/307] chore: filter to date event --- packages/core/database/src/operators/date.ts | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/core/database/src/operators/date.ts b/packages/core/database/src/operators/date.ts index 9c0157e61c9..24b218a90a0 100644 --- a/packages/core/database/src/operators/date.ts +++ b/packages/core/database/src/operators/date.ts @@ -8,7 +8,7 @@ */ import { parseDate } from '@nocobase/utils'; -import { Op } from 'sequelize'; +import { Op, Sequelize } from 'sequelize'; import moment from 'moment'; function isDate(input) { @@ -17,7 +17,7 @@ function isDate(input) { const toDate = (date, options: any = {}) => { const { ctx } = options; - const val = isDate(date) ? date : new Date(date); + let val = isDate(date) ? date : new Date(date); const field = ctx.db.getFieldByPath(ctx.fieldPath); if (!field) { @@ -25,18 +25,25 @@ const toDate = (date, options: any = {}) => { } if (field.constructor.name === 'UnixTimestampField') { - return field.dateToValue(val); + val = field.dateToValue(val); } if (field.constructor.name === 'DatetimeNoTzField') { - return moment(val).utcOffset('+00:00').format('YYYY-MM-DD HH:mm:ss'); + val = moment(val).utcOffset('+00:00').format('YYYY-MM-DD HH:mm:ss'); } if (field.constructor.name === 'DateOnlyField') { - return moment(val).format('YYYY-MM-DD HH:mm:ss'); + val = moment(val).format('YYYY-MM-DD HH:mm:ss'); } - return val; + const eventObj = { + val, + fieldType: field.type, + }; + + ctx.db.emit('filterToDate', eventObj); + + return eventObj.val; }; function parseDateTimezone(ctx) { @@ -57,10 +64,6 @@ function parseDateTimezone(ctx) { return ctx.db.options.timezone; } -function isDatetimeString(str) { - return /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(str); -} - export default { $dateOn(value, ctx) { const r = parseDate(value, { From fb88833b44ab26bb1b8040a7234a5af084933c2f Mon Sep 17 00:00:00 2001 From: "nocobase[bot]" <179432756+nocobase[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:26:23 +0000 Subject: [PATCH 021/307] =?UTF-8?q?chore(versions):=20=F0=9F=98=8A=20publi?= =?UTF-8?q?sh=20v1.3.33-beta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lerna.json | 2 +- packages/core/acl/package.json | 6 +- packages/core/actions/package.json | 8 +- packages/core/app/package.json | 10 +- packages/core/auth/package.json | 12 +- packages/core/build/package.json | 2 +- packages/core/cache/package.json | 2 +- packages/core/cli/package.json | 6 +- packages/core/client/package.json | 8 +- .../core/create-nocobase-app/package.json | 2 +- .../core/data-source-manager/package.json | 12 +- packages/core/database/package.json | 6 +- packages/core/devtools/package.json | 8 +- packages/core/evaluators/package.json | 4 +- packages/core/logger/package.json | 2 +- packages/core/resourcer/package.json | 4 +- packages/core/sdk/package.json | 2 +- packages/core/server/package.json | 26 ++-- packages/core/telemetry/package.json | 4 +- packages/core/test/package.json | 4 +- packages/core/utils/package.json | 2 +- .../plugins/@nocobase/plugin-acl/package.json | 2 +- .../plugin-action-bulk-edit/package.json | 2 +- .../plugin-action-bulk-update/package.json | 2 +- .../plugin-action-custom-request/package.json | 2 +- .../plugin-action-duplicate/package.json | 2 +- .../plugin-action-export/package.json | 2 +- .../plugin-action-import/package.json | 2 +- .../plugin-action-print/package.json | 2 +- .../@nocobase/plugin-api-doc/package.json | 2 +- .../@nocobase/plugin-api-keys/package.json | 2 +- .../@nocobase/plugin-audit-logs/package.json | 2 +- .../@nocobase/plugin-auth-sms/package.json | 2 +- .../@nocobase/plugin-auth/package.json | 2 +- .../plugin-backup-restore/package.json | 2 +- .../plugin-block-iframe/package.json | 2 +- .../plugin-block-workbench/package.json | 2 +- .../@nocobase/plugin-calendar/package.json | 2 +- .../@nocobase/plugin-charts/package.json | 2 +- .../@nocobase/plugin-client/package.json | 2 +- .../plugin-collection-sql/package.json | 2 +- .../plugin-collection-tree/package.json | 2 +- .../plugin-data-source-main/package.json | 2 +- .../plugin-data-source-manager/package.json | 2 +- .../plugin-data-visualization/package.json | 2 +- .../plugin-disable-pm-add/package.json | 2 +- .../plugin-error-handler/package.json | 2 +- .../plugin-field-china-region/package.json | 2 +- .../plugin-field-formula/package.json | 2 +- .../plugin-field-m2m-array/package.json | 2 +- .../plugin-field-markdown-vditor/package.json | 2 +- .../plugin-field-sequence/package.json | 2 +- .../plugin-file-manager/package.json | 2 +- .../@nocobase/plugin-gantt/package.json | 2 +- .../package.json | 2 +- .../@nocobase/plugin-kanban/package.json | 2 +- .../plugin-localization/package.json | 2 +- .../@nocobase/plugin-logger/package.json | 2 +- .../plugins/@nocobase/plugin-map/package.json | 2 +- .../plugin-mobile-client/package.json | 2 +- .../@nocobase/plugin-mobile/package.json | 2 +- .../plugin-mock-collections/package.json | 2 +- .../plugin-multi-app-manager/package.json | 2 +- .../package.json | 2 +- .../plugin-notifications/package.json | 2 +- .../plugin-sample-hello/package.json | 2 +- .../plugin-snapshot-field/package.json | 2 +- .../plugin-system-settings/package.json | 2 +- .../plugin-theme-editor/package.json | 2 +- .../plugin-ui-schema-storage/package.json | 2 +- .../@nocobase/plugin-users/package.json | 2 +- .../plugin-verification/package.json | 2 +- .../package.json | 2 +- .../plugin-workflow-aggregate/package.json | 2 +- .../plugin-workflow-delay/package.json | 2 +- .../package.json | 2 +- .../plugin-workflow-loop/package.json | 2 +- .../plugin-workflow-mailer/package.json | 2 +- .../plugin-workflow-manual/package.json | 2 +- .../plugin-workflow-parallel/package.json | 2 +- .../plugin-workflow-request/package.json | 2 +- .../plugin-workflow-sql/package.json | 2 +- .../plugin-workflow-test/package.json | 2 +- .../@nocobase/plugin-workflow/package.json | 4 +- packages/presets/nocobase/package.json | 122 +++++++++--------- 85 files changed, 191 insertions(+), 191 deletions(-) diff --git a/lerna.json b/lerna.json index 4b2ca1331a3..0b2d3e4a2d6 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.3.32-beta", + "version": "1.3.33-beta", "npmClient": "yarn", "useWorkspaces": true, "npmClientArgs": [ diff --git a/packages/core/acl/package.json b/packages/core/acl/package.json index afa71fd629a..3f180f90974 100644 --- a/packages/core/acl/package.json +++ b/packages/core/acl/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/acl", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/resourcer": "1.3.32-beta", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/resourcer": "1.3.33-beta", + "@nocobase/utils": "1.3.33-beta", "minimatch": "^5.1.1" }, "repository": { diff --git a/packages/core/actions/package.json b/packages/core/actions/package.json index 4f7277770a2..089493e41d8 100644 --- a/packages/core/actions/package.json +++ b/packages/core/actions/package.json @@ -1,14 +1,14 @@ { "name": "@nocobase/actions", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/cache": "1.3.32-beta", - "@nocobase/database": "1.3.32-beta", - "@nocobase/resourcer": "1.3.32-beta" + "@nocobase/cache": "1.3.33-beta", + "@nocobase/database": "1.3.33-beta", + "@nocobase/resourcer": "1.3.33-beta" }, "repository": { "type": "git", diff --git a/packages/core/app/package.json b/packages/core/app/package.json index 6f7b7385ccf..0194f207a8d 100644 --- a/packages/core/app/package.json +++ b/packages/core/app/package.json @@ -1,17 +1,17 @@ { "name": "@nocobase/app", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/database": "1.3.32-beta", - "@nocobase/preset-nocobase": "1.3.32-beta", - "@nocobase/server": "1.3.32-beta" + "@nocobase/database": "1.3.33-beta", + "@nocobase/preset-nocobase": "1.3.33-beta", + "@nocobase/server": "1.3.33-beta" }, "devDependencies": { - "@nocobase/client": "1.3.32-beta" + "@nocobase/client": "1.3.33-beta" }, "repository": { "type": "git", diff --git a/packages/core/auth/package.json b/packages/core/auth/package.json index b2d24d16bbf..2960a1d69ba 100644 --- a/packages/core/auth/package.json +++ b/packages/core/auth/package.json @@ -1,16 +1,16 @@ { "name": "@nocobase/auth", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/actions": "1.3.32-beta", - "@nocobase/cache": "1.3.32-beta", - "@nocobase/database": "1.3.32-beta", - "@nocobase/resourcer": "1.3.32-beta", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/actions": "1.3.33-beta", + "@nocobase/cache": "1.3.33-beta", + "@nocobase/database": "1.3.33-beta", + "@nocobase/resourcer": "1.3.33-beta", + "@nocobase/utils": "1.3.33-beta", "@types/jsonwebtoken": "^8.5.8", "jsonwebtoken": "^8.5.1" }, diff --git a/packages/core/build/package.json b/packages/core/build/package.json index e9ada5df033..59d7d17b46d 100644 --- a/packages/core/build/package.json +++ b/packages/core/build/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/build", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "Library build tool based on rollup.", "main": "lib/index.js", "types": "./lib/index.d.ts", diff --git a/packages/core/cache/package.json b/packages/core/cache/package.json index a6a43fde1a8..5824c6d9bae 100644 --- a/packages/core/cache/package.json +++ b/packages/core/cache/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cache", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", diff --git a/packages/core/cli/package.json b/packages/core/cli/package.json index fe1c206099c..e0d791bc398 100644 --- a/packages/core/cli/package.json +++ b/packages/core/cli/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cli", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./src/index.js", @@ -8,7 +8,7 @@ "nocobase": "./bin/index.js" }, "dependencies": { - "@nocobase/app": "1.3.32-beta", + "@nocobase/app": "1.3.33-beta", "@types/fs-extra": "^11.0.1", "@umijs/utils": "3.5.20", "chalk": "^4.1.1", @@ -25,7 +25,7 @@ "tsx": "^4.19.0" }, "devDependencies": { - "@nocobase/devtools": "1.3.32-beta" + "@nocobase/devtools": "1.3.33-beta" }, "repository": { "type": "git", diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 35145ad1a42..a93bf9f5d8f 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/client", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "lib/index.js", "module": "es/index.mjs", @@ -26,9 +26,9 @@ "@formily/reactive-react": "^2.2.27", "@formily/shared": "^2.2.27", "@formily/validator": "^2.2.27", - "@nocobase/evaluators": "1.3.32-beta", - "@nocobase/sdk": "1.3.32-beta", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/evaluators": "1.3.33-beta", + "@nocobase/sdk": "1.3.33-beta", + "@nocobase/utils": "1.3.33-beta", "ahooks": "^3.7.2", "antd": "5.12.8", "antd-style": "3.4.5", diff --git a/packages/core/create-nocobase-app/package.json b/packages/core/create-nocobase-app/package.json index 2d581735115..1da13636a12 100755 --- a/packages/core/create-nocobase-app/package.json +++ b/packages/core/create-nocobase-app/package.json @@ -1,6 +1,6 @@ { "name": "create-nocobase-app", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "src/index.js", "license": "AGPL-3.0", "dependencies": { diff --git a/packages/core/data-source-manager/package.json b/packages/core/data-source-manager/package.json index bd81236752c..23598c69e9f 100644 --- a/packages/core/data-source-manager/package.json +++ b/packages/core/data-source-manager/package.json @@ -1,16 +1,16 @@ { "name": "@nocobase/data-source-manager", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/actions": "1.3.32-beta", - "@nocobase/cache": "1.3.32-beta", - "@nocobase/database": "1.3.32-beta", - "@nocobase/resourcer": "1.3.32-beta", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/actions": "1.3.33-beta", + "@nocobase/cache": "1.3.33-beta", + "@nocobase/database": "1.3.33-beta", + "@nocobase/resourcer": "1.3.33-beta", + "@nocobase/utils": "1.3.33-beta", "@types/jsonwebtoken": "^8.5.8", "jsonwebtoken": "^8.5.1" }, diff --git a/packages/core/database/package.json b/packages/core/database/package.json index 54f1b3802e6..d878f03c113 100644 --- a/packages/core/database/package.json +++ b/packages/core/database/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/database", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "main": "./lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", "dependencies": { - "@nocobase/logger": "1.3.32-beta", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/logger": "1.3.33-beta", + "@nocobase/utils": "1.3.33-beta", "async-mutex": "^0.3.2", "chalk": "^4.1.1", "cron-parser": "4.4.0", diff --git a/packages/core/devtools/package.json b/packages/core/devtools/package.json index ac0e207b139..9772829109a 100644 --- a/packages/core/devtools/package.json +++ b/packages/core/devtools/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/devtools", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./src/index.js", "dependencies": { - "@nocobase/build": "1.3.32-beta", - "@nocobase/client": "1.3.32-beta", - "@nocobase/test": "1.3.32-beta", + "@nocobase/build": "1.3.33-beta", + "@nocobase/client": "1.3.33-beta", + "@nocobase/test": "1.3.33-beta", "@types/koa": "^2.13.4", "@types/koa-bodyparser": "^4.3.4", "@types/lodash": "^4.14.177", diff --git a/packages/core/evaluators/package.json b/packages/core/evaluators/package.json index 000a14e9849..b11bce97da3 100644 --- a/packages/core/evaluators/package.json +++ b/packages/core/evaluators/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/evaluators", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "main": "./lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", "dependencies": { "@formulajs/formulajs": "4.2.0", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/utils": "1.3.33-beta", "mathjs": "^10.6.0" }, "repository": { diff --git a/packages/core/logger/package.json b/packages/core/logger/package.json index 2a1fcf7c0cd..270ebef0230 100644 --- a/packages/core/logger/package.json +++ b/packages/core/logger/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/logger", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "nocobase logging library", "license": "AGPL-3.0", "main": "./lib/index.js", diff --git a/packages/core/resourcer/package.json b/packages/core/resourcer/package.json index e6777195fdf..d272b2def9f 100644 --- a/packages/core/resourcer/package.json +++ b/packages/core/resourcer/package.json @@ -1,12 +1,12 @@ { "name": "@nocobase/resourcer", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "main": "./lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", "dependencies": { - "@nocobase/utils": "1.3.32-beta", + "@nocobase/utils": "1.3.33-beta", "deepmerge": "^4.2.2", "koa-compose": "^4.1.0", "lodash": "^4.17.21", diff --git a/packages/core/sdk/package.json b/packages/core/sdk/package.json index e302462502e..b5ea7bdce06 100644 --- a/packages/core/sdk/package.json +++ b/packages/core/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/sdk", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/core/server/package.json b/packages/core/server/package.json index 8eb0b4ebe5f..fd419574ebf 100644 --- a/packages/core/server/package.json +++ b/packages/core/server/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/server", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", @@ -10,18 +10,18 @@ "@koa/cors": "^3.1.0", "@koa/multer": "^3.0.2", "@koa/router": "^9.4.0", - "@nocobase/acl": "1.3.32-beta", - "@nocobase/actions": "1.3.32-beta", - "@nocobase/auth": "1.3.32-beta", - "@nocobase/cache": "1.3.32-beta", - "@nocobase/data-source-manager": "1.3.32-beta", - "@nocobase/database": "1.3.32-beta", - "@nocobase/evaluators": "1.3.32-beta", - "@nocobase/logger": "1.3.32-beta", - "@nocobase/resourcer": "1.3.32-beta", - "@nocobase/sdk": "1.3.32-beta", - "@nocobase/telemetry": "1.3.32-beta", - "@nocobase/utils": "1.3.32-beta", + "@nocobase/acl": "1.3.33-beta", + "@nocobase/actions": "1.3.33-beta", + "@nocobase/auth": "1.3.33-beta", + "@nocobase/cache": "1.3.33-beta", + "@nocobase/data-source-manager": "1.3.33-beta", + "@nocobase/database": "1.3.33-beta", + "@nocobase/evaluators": "1.3.33-beta", + "@nocobase/logger": "1.3.33-beta", + "@nocobase/resourcer": "1.3.33-beta", + "@nocobase/sdk": "1.3.33-beta", + "@nocobase/telemetry": "1.3.33-beta", + "@nocobase/utils": "1.3.33-beta", "@types/decompress": "4.2.7", "@types/ini": "^1.3.31", "@types/koa-send": "^4.1.3", diff --git a/packages/core/telemetry/package.json b/packages/core/telemetry/package.json index 3e8ddb76796..7fa9834621a 100644 --- a/packages/core/telemetry/package.json +++ b/packages/core/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/telemetry", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "nocobase telemetry library", "license": "AGPL-3.0", "main": "./lib/index.js", @@ -11,7 +11,7 @@ "directory": "packages/telemetry" }, "dependencies": { - "@nocobase/utils": "1.3.32-beta", + "@nocobase/utils": "1.3.33-beta", "@opentelemetry/api": "^1.7.0", "@opentelemetry/instrumentation": "^0.46.0", "@opentelemetry/resources": "^1.19.0", diff --git a/packages/core/test/package.json b/packages/core/test/package.json index 00b7b0185de..7e8e002180b 100644 --- a/packages/core/test/package.json +++ b/packages/core/test/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/test", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "lib/index.js", "module": "./src/index.ts", "types": "./lib/index.d.ts", @@ -51,7 +51,7 @@ }, "dependencies": { "@faker-js/faker": "8.1.0", - "@nocobase/server": "1.3.32-beta", + "@nocobase/server": "1.3.33-beta", "@playwright/test": "^1.45.3", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.0.0", diff --git a/packages/core/utils/package.json b/packages/core/utils/package.json index c2eda3f18c2..33c8f10baa4 100644 --- a/packages/core/utils/package.json +++ b/packages/core/utils/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/utils", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", diff --git a/packages/plugins/@nocobase/plugin-acl/package.json b/packages/plugins/@nocobase/plugin-acl/package.json index 6bc167344b3..eaae0fb77a2 100644 --- a/packages/plugins/@nocobase/plugin-acl/package.json +++ b/packages/plugins/@nocobase/plugin-acl/package.json @@ -4,7 +4,7 @@ "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.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/acl", diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json index 700b4dd6dfb..d3ace6367db 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-bulk-edit", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-bulk-edit", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit", diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/package.json b/packages/plugins/@nocobase/plugin-action-bulk-update/package.json index 9031a575dfe..540944d2e82 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-update/package.json +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-bulk-update", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-bulk-update", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update", diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/package.json b/packages/plugins/@nocobase/plugin-action-custom-request/package.json index 030bc39d993..4a94b6f8c5b 100644 --- a/packages/plugins/@nocobase/plugin-action-custom-request/package.json +++ b/packages/plugins/@nocobase/plugin-action-custom-request/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-custom-request", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-custom-request", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-custom-request", diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/package.json b/packages/plugins/@nocobase/plugin-action-duplicate/package.json index cc733e7f1f1..cec578984cd 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/package.json +++ b/packages/plugins/@nocobase/plugin-action-duplicate/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-duplicate", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-duplicate", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-duplicate", diff --git a/packages/plugins/@nocobase/plugin-action-export/package.json b/packages/plugins/@nocobase/plugin-action-export/package.json index 458f44645cf..a55574d3c0e 100644 --- a/packages/plugins/@nocobase/plugin-action-export/package.json +++ b/packages/plugins/@nocobase/plugin-action-export/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "操作:导出记录", "description": "Export filtered records to excel, you can configure which fields to export.", "description.zh-CN": "导出筛选后的记录到 Excel 中,可以配置导出哪些字段。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-export", diff --git a/packages/plugins/@nocobase/plugin-action-import/package.json b/packages/plugins/@nocobase/plugin-action-import/package.json index 3478831341e..ea74a6554be 100644 --- a/packages/plugins/@nocobase/plugin-action-import/package.json +++ b/packages/plugins/@nocobase/plugin-action-import/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "操作:导入记录", "description": "Import records using excel templates. You can configure which fields to import and templates will be generated automatically.", "description.zh-CN": "使用 Excel 模板导入数据,可以配置导入哪些字段,自动生成模板。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-import", diff --git a/packages/plugins/@nocobase/plugin-action-print/package.json b/packages/plugins/@nocobase/plugin-action-print/package.json index 8190d90560e..1608bc1465a 100644 --- a/packages/plugins/@nocobase/plugin-action-print/package.json +++ b/packages/plugins/@nocobase/plugin-action-print/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-print", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-print", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-print", diff --git a/packages/plugins/@nocobase/plugin-api-doc/package.json b/packages/plugins/@nocobase/plugin-api-doc/package.json index 37ef64cb917..1875ac87ecd 100644 --- a/packages/plugins/@nocobase/plugin-api-doc/package.json +++ b/packages/plugins/@nocobase/plugin-api-doc/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-api-doc", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "API documentation", "displayName.zh-CN": "API 文档", "description": "An OpenAPI documentation generator for NocoBase HTTP API.", diff --git a/packages/plugins/@nocobase/plugin-api-keys/package.json b/packages/plugins/@nocobase/plugin-api-keys/package.json index 64cb2d6fde8..1fa599d48a4 100644 --- a/packages/plugins/@nocobase/plugin-api-keys/package.json +++ b/packages/plugins/@nocobase/plugin-api-keys/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "认证:API 密钥", "description": "Allows users to use API key to access application's HTTP API", "description.zh-CN": "允许用户使用 API 密钥访问应用的 HTTP API", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/api-keys", diff --git a/packages/plugins/@nocobase/plugin-audit-logs/package.json b/packages/plugins/@nocobase/plugin-audit-logs/package.json index 790f64cdb42..7f7deb999ff 100644 --- a/packages/plugins/@nocobase/plugin-audit-logs/package.json +++ b/packages/plugins/@nocobase/plugin-audit-logs/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-audit-logs", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Audit logs (deprecated)", "displayName.zh-CN": "审计日志(废弃)", "description": "This plugin is deprecated. There will be a new audit log plugin in the future.", diff --git a/packages/plugins/@nocobase/plugin-auth-sms/package.json b/packages/plugins/@nocobase/plugin-auth-sms/package.json index 822402734df..1f92efda409 100644 --- a/packages/plugins/@nocobase/plugin-auth-sms/package.json +++ b/packages/plugins/@nocobase/plugin-auth-sms/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "认证:短信", "description": "SMS authentication.", "description.zh-CN": "通过短信验证码认证身份。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/auth-sms", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth-sms", diff --git a/packages/plugins/@nocobase/plugin-auth/package.json b/packages/plugins/@nocobase/plugin-auth/package.json index 53f6d64f4f1..168f4e607dc 100644 --- a/packages/plugins/@nocobase/plugin-auth/package.json +++ b/packages/plugins/@nocobase/plugin-auth/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-auth", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/auth", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth", diff --git a/packages/plugins/@nocobase/plugin-backup-restore/package.json b/packages/plugins/@nocobase/plugin-backup-restore/package.json index 28e968f589e..b7748afebe9 100644 --- a/packages/plugins/@nocobase/plugin-backup-restore/package.json +++ b/packages/plugins/@nocobase/plugin-backup-restore/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "应用的备份与还原(废弃)", "description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.", "description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/backup-restore", diff --git a/packages/plugins/@nocobase/plugin-block-iframe/package.json b/packages/plugins/@nocobase/plugin-block-iframe/package.json index c343e7fecef..f8b2603c06c 100644 --- a/packages/plugins/@nocobase/plugin-block-iframe/package.json +++ b/packages/plugins/@nocobase/plugin-block-iframe/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "区块:iframe", "description": "Create an iframe block on the page to embed and display external web pages or content.", "description.zh-CN": "在页面上创建和管理iframe,用于嵌入和展示外部网页或内容。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/block-iframe", diff --git a/packages/plugins/@nocobase/plugin-block-workbench/package.json b/packages/plugins/@nocobase/plugin-block-workbench/package.json index de1e47e781f..5c4c357f5a7 100644 --- a/packages/plugins/@nocobase/plugin-block-workbench/package.json +++ b/packages/plugins/@nocobase/plugin-block-workbench/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-block-workbench", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Block: Workbench", "displayName.zh-CN": "区块:工作台", "description": "Add buttons for actions, links, etc. in the workbench block to quickly initiate actions and jump pages.", diff --git a/packages/plugins/@nocobase/plugin-calendar/package.json b/packages/plugins/@nocobase/plugin-calendar/package.json index 84e79ff0205..cc246f80cf8 100644 --- a/packages/plugins/@nocobase/plugin-calendar/package.json +++ b/packages/plugins/@nocobase/plugin-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-calendar", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Calendar", "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.", diff --git a/packages/plugins/@nocobase/plugin-charts/package.json b/packages/plugins/@nocobase/plugin-charts/package.json index bcd4f23010d..2a27f981a77 100644 --- a/packages/plugins/@nocobase/plugin-charts/package.json +++ b/packages/plugins/@nocobase/plugin-charts/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "图表(废弃)", "description": "The plugin has been deprecated, please use the data visualization plugin instead.", "description.zh-CN": "已废弃插件,请使用数据可视化插件代替。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "license": "AGPL-3.0", "devDependencies": { diff --git a/packages/plugins/@nocobase/plugin-client/package.json b/packages/plugins/@nocobase/plugin-client/package.json index 8c6f36bdf8c..085a73ba334 100644 --- a/packages/plugins/@nocobase/plugin-client/package.json +++ b/packages/plugins/@nocobase/plugin-client/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "WEB 客户端", "description": "Provides a client interface for the NocoBase server", "description.zh-CN": "为 NocoBase 服务端提供客户端界面", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "license": "AGPL-3.0", "devDependencies": { diff --git a/packages/plugins/@nocobase/plugin-collection-sql/package.json b/packages/plugins/@nocobase/plugin-collection-sql/package.json index a46c6a5ccb9..52ef11e382e 100644 --- a/packages/plugins/@nocobase/plugin-collection-sql/package.json +++ b/packages/plugins/@nocobase/plugin-collection-sql/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据表: SQL", "description": "Provides SQL collection template", "description.zh-CN": "提供 SQL 数据表模板", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "homepage": "https://docs-cn.nocobase.com/handbook/collection-sql", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/collection-sql", "main": "dist/server/index.js", diff --git a/packages/plugins/@nocobase/plugin-collection-tree/package.json b/packages/plugins/@nocobase/plugin-collection-tree/package.json index 8e57d58c0c3..75efac86129 100644 --- a/packages/plugins/@nocobase/plugin-collection-tree/package.json +++ b/packages/plugins/@nocobase/plugin-collection-tree/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-collection-tree", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Collection: Tree", "displayName.zh-CN": "数据表:树", "description": "Provides tree collection template", diff --git a/packages/plugins/@nocobase/plugin-data-source-main/package.json b/packages/plugins/@nocobase/plugin-data-source-main/package.json index 3cd1657f4e5..cbfcabb4125 100644 --- a/packages/plugins/@nocobase/plugin-data-source-main/package.json +++ b/packages/plugins/@nocobase/plugin-data-source-main/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据源:主数据库", "description": "NocoBase main database, supports relational databases such as PostgreSQL, MySQL, MariaDB and so on.", "description.zh-CN": "NocoBase 主数据库,支持 PostgreSQL、MySQL、MariaDB 等关系型数据库。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/data-source-main", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/data-source-main", diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/package.json b/packages/plugins/@nocobase/plugin-data-source-manager/package.json index 467443230dc..84f4ff80fcc 100644 --- a/packages/plugins/@nocobase/plugin-data-source-manager/package.json +++ b/packages/plugins/@nocobase/plugin-data-source-manager/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-data-source-manager", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "displayName": "Data source manager", "displayName.zh-CN": "数据源管理", diff --git a/packages/plugins/@nocobase/plugin-data-visualization/package.json b/packages/plugins/@nocobase/plugin-data-visualization/package.json index 453a4c57eba..fa4d2862fc0 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/package.json +++ b/packages/plugins/@nocobase/plugin-data-visualization/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-data-visualization", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Data visualization", "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.", diff --git a/packages/plugins/@nocobase/plugin-disable-pm-add/package.json b/packages/plugins/@nocobase/plugin-disable-pm-add/package.json index 0cac7ef0ff9..987b276d92d 100644 --- a/packages/plugins/@nocobase/plugin-disable-pm-add/package.json +++ b/packages/plugins/@nocobase/plugin-disable-pm-add/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-disable-pm-add", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "peerDependencies": { "@nocobase/client": "1.x", diff --git a/packages/plugins/@nocobase/plugin-error-handler/package.json b/packages/plugins/@nocobase/plugin-error-handler/package.json index 669f1c340c5..9199e476ab6 100644 --- a/packages/plugins/@nocobase/plugin-error-handler/package.json +++ b/packages/plugins/@nocobase/plugin-error-handler/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "错误处理器", "description": "Handling application errors and exceptions.", "description.zh-CN": "处理应用程序中的错误和异常。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "devDependencies": { diff --git a/packages/plugins/@nocobase/plugin-field-china-region/package.json b/packages/plugins/@nocobase/plugin-field-china-region/package.json index 8868e487423..f937ab3a871 100644 --- a/packages/plugins/@nocobase/plugin-field-china-region/package.json +++ b/packages/plugins/@nocobase/plugin-field-china-region/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-field-china-region", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Administrative divisions of China", "displayName.zh-CN": "中国行政区划", "description": "Provides data and field type for administrative divisions of China.", diff --git a/packages/plugins/@nocobase/plugin-field-formula/package.json b/packages/plugins/@nocobase/plugin-field-formula/package.json index 93579e313e8..d5a37c07434 100644 --- a/packages/plugins/@nocobase/plugin-field-formula/package.json +++ b/packages/plugins/@nocobase/plugin-field-formula/package.json @@ -4,7 +4,7 @@ "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.zh-CN": "可以配置并存储同一条记录的多字段值之间的计算结果,支持 Math.js 和 Excel formula functions 两种引擎", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/field-formula", diff --git a/packages/plugins/@nocobase/plugin-field-m2m-array/package.json b/packages/plugins/@nocobase/plugin-field-m2m-array/package.json index 5253532c996..3cde20c380d 100644 --- a/packages/plugins/@nocobase/plugin-field-m2m-array/package.json +++ b/packages/plugins/@nocobase/plugin-field-m2m-array/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据表字段:多对多 (数组)", "description": "Allows to create many to many relationships between two models by storing an array of unique keys of the target model.", "description.zh-CN": "支持通过在数组中存储目标表唯一键的方式建立多对多关系。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "peerDependencies": { "@nocobase/client": "1.x", diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json b/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json index e6a3c611050..146346d80cb 100644 --- a/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据表字段:Markdown(Vditor)", "description": "Used to store Markdown and render it using Vditor editor, supports common Markdown syntax such as list, code, quote, etc., and supports uploading images, recordings, etc.It also allows for instant rendering, where what you see is what you get.", "description.zh-CN": "用于存储 Markdown,并使用 Vditor 编辑器渲染,支持常见 Markdown 语法,如列表,代码,引用等,并支持上传图片,录音等。同时可以做到即时渲染,所见即所得。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/field-markdown-vditor", diff --git a/packages/plugins/@nocobase/plugin-field-sequence/package.json b/packages/plugins/@nocobase/plugin-field-sequence/package.json index 8cd11781a0a..3e497a53aa2 100644 --- a/packages/plugins/@nocobase/plugin-field-sequence/package.json +++ b/packages/plugins/@nocobase/plugin-field-sequence/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据表字段:自动编码", "description": "Automatically generate codes based on configured rules, supporting combinations of dates, numbers, and text.", "description.zh-CN": "根据配置的规则自动生成编码,支持日期、数字、文本的组合。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/field-sequence", diff --git a/packages/plugins/@nocobase/plugin-file-manager/package.json b/packages/plugins/@nocobase/plugin-file-manager/package.json index ed0016caf94..2dffff4d300 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/package.json +++ b/packages/plugins/@nocobase/plugin-file-manager/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-file-manager", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "File manager", "displayName.zh-CN": "文件管理器", "description": "Provides files storage services with files collection template and attachment field.", diff --git a/packages/plugins/@nocobase/plugin-gantt/package.json b/packages/plugins/@nocobase/plugin-gantt/package.json index 1322089bc83..17abb295f78 100644 --- a/packages/plugins/@nocobase/plugin-gantt/package.json +++ b/packages/plugins/@nocobase/plugin-gantt/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-gantt", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "displayName": "Block: Gantt", "displayName.zh-CN": "区块:甘特图", "description": "Provides Gantt block.", diff --git a/packages/plugins/@nocobase/plugin-graph-collection-manager/package.json b/packages/plugins/@nocobase/plugin-graph-collection-manager/package.json index 74ff5701ce3..5ca76d6d8f0 100644 --- a/packages/plugins/@nocobase/plugin-graph-collection-manager/package.json +++ b/packages/plugins/@nocobase/plugin-graph-collection-manager/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "可视化数据表管理", "description": "An ER diagram-like tool. Currently only the Master database is supported.", "description.zh-CN": "类似 ER 图的工具,目前只支持主数据库。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/graph-collection-manager", diff --git a/packages/plugins/@nocobase/plugin-kanban/package.json b/packages/plugins/@nocobase/plugin-kanban/package.json index c570b4fdad3..82d0cff897c 100644 --- a/packages/plugins/@nocobase/plugin-kanban/package.json +++ b/packages/plugins/@nocobase/plugin-kanban/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-kanban", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/block-kanban", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/block-kanban", diff --git a/packages/plugins/@nocobase/plugin-localization/package.json b/packages/plugins/@nocobase/plugin-localization/package.json index 863c0f445e7..9af83d196cd 100644 --- a/packages/plugins/@nocobase/plugin-localization/package.json +++ b/packages/plugins/@nocobase/plugin-localization/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-localization", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/localization-management", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/localization-management", diff --git a/packages/plugins/@nocobase/plugin-logger/package.json b/packages/plugins/@nocobase/plugin-logger/package.json index e4adb35124d..89f304207d7 100644 --- a/packages/plugins/@nocobase/plugin-logger/package.json +++ b/packages/plugins/@nocobase/plugin-logger/package.json @@ -4,7 +4,7 @@ "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.zh-CN": "服务端日志,主要包括接口请求日志和系统运行日志,并支持打包和下载日志文件。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/logger", diff --git a/packages/plugins/@nocobase/plugin-map/package.json b/packages/plugins/@nocobase/plugin-map/package.json index a5e5b726115..4d65208c7bb 100644 --- a/packages/plugins/@nocobase/plugin-map/package.json +++ b/packages/plugins/@nocobase/plugin-map/package.json @@ -2,7 +2,7 @@ "name": "@nocobase/plugin-map", "displayName": "Block: Map", "displayName.zh-CN": "区块:地图", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "Map block, support Gaode map and Google map, you can also extend more map types.", "description.zh-CN": "地图区块,支持高德地图和 Google 地图,你也可以扩展更多地图类型。", "license": "AGPL-3.0", diff --git a/packages/plugins/@nocobase/plugin-mobile-client/package.json b/packages/plugins/@nocobase/plugin-mobile-client/package.json index 768d474eb3f..bdc1924b4e0 100644 --- a/packages/plugins/@nocobase/plugin-mobile-client/package.json +++ b/packages/plugins/@nocobase/plugin-mobile-client/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-mobile-client", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/mobile-client", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/mobile-client", diff --git a/packages/plugins/@nocobase/plugin-mobile/package.json b/packages/plugins/@nocobase/plugin-mobile/package.json index 170979dbc96..b6887cd16c0 100644 --- a/packages/plugins/@nocobase/plugin-mobile/package.json +++ b/packages/plugins/@nocobase/plugin-mobile/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-mobile", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/mobile", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/mobile", diff --git a/packages/plugins/@nocobase/plugin-mock-collections/package.json b/packages/plugins/@nocobase/plugin-mock-collections/package.json index ea4d76e2fdc..97c5661c896 100644 --- a/packages/plugins/@nocobase/plugin-mock-collections/package.json +++ b/packages/plugins/@nocobase/plugin-mock-collections/package.json @@ -2,7 +2,7 @@ "name": "@nocobase/plugin-mock-collections", "displayName": "mock-collections", "description": "mock-collections", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "license": "AGPL-3.0", "peerDependencies": { diff --git a/packages/plugins/@nocobase/plugin-multi-app-manager/package.json b/packages/plugins/@nocobase/plugin-multi-app-manager/package.json index ed48660bd81..80c4659ea30 100644 --- a/packages/plugins/@nocobase/plugin-multi-app-manager/package.json +++ b/packages/plugins/@nocobase/plugin-multi-app-manager/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "多应用管理器", "description": "Dynamically create multiple apps without separate deployments.", "description.zh-CN": "无需单独部署即可动态创建多个应用。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/multi-app-manager", diff --git a/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json b/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json index 91921301ae7..213818a164f 100644 --- a/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json +++ b/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "多应用数据表共享", "description": "", "description.zh-CN": "", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "devDependencies": { "@formily/react": "2.x", diff --git a/packages/plugins/@nocobase/plugin-notifications/package.json b/packages/plugins/@nocobase/plugin-notifications/package.json index bd798fdc29d..e2669cae73d 100644 --- a/packages/plugins/@nocobase/plugin-notifications/package.json +++ b/packages/plugins/@nocobase/plugin-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-notifications", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "description": "", "license": "AGPL-3.0", "main": "./dist/server/index.js", diff --git a/packages/plugins/@nocobase/plugin-sample-hello/package.json b/packages/plugins/@nocobase/plugin-sample-hello/package.json index ef9d8f8d849..65bbea7e639 100644 --- a/packages/plugins/@nocobase/plugin-sample-hello/package.json +++ b/packages/plugins/@nocobase/plugin-sample-hello/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-sample-hello", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "./dist/server/index.js", "displayName": "Hello", "displayName.zh-CN": "Hello", diff --git a/packages/plugins/@nocobase/plugin-snapshot-field/package.json b/packages/plugins/@nocobase/plugin-snapshot-field/package.json index b2bc7a0e0aa..5e9e800bb6d 100644 --- a/packages/plugins/@nocobase/plugin-snapshot-field/package.json +++ b/packages/plugins/@nocobase/plugin-snapshot-field/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据表字段:关系快照", "description": "When adding a new record, create a snapshot for its relational record and save in the new record. The snapshot will not be updated when the relational record is updated.", "description.zh-CN": "在添加数据时,为它的关系数据创建快照,并保存在当前的数据中。关系数据更新时,快照不会更新。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/field-snapshot", diff --git a/packages/plugins/@nocobase/plugin-system-settings/package.json b/packages/plugins/@nocobase/plugin-system-settings/package.json index e45361b1d4b..dc4a84010c3 100644 --- a/packages/plugins/@nocobase/plugin-system-settings/package.json +++ b/packages/plugins/@nocobase/plugin-system-settings/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "系统设置", "description": "Used to adjust the system title, logo, language, etc.", "description.zh-CN": "用于调整系统的标题、LOGO、语言等。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/system-settings", diff --git a/packages/plugins/@nocobase/plugin-theme-editor/package.json b/packages/plugins/@nocobase/plugin-theme-editor/package.json index 855bb5bea2c..218ec303572 100644 --- a/packages/plugins/@nocobase/plugin-theme-editor/package.json +++ b/packages/plugins/@nocobase/plugin-theme-editor/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-theme-editor", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/theme-editor", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/theme-editor", diff --git a/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json b/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json index 19b976fa322..8729c05ba3b 100644 --- a/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json +++ b/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "UI schema 存储服务", "description": "Provides centralized UI schema storage service.", "description.zh-CN": "提供中心化的 UI schema 存储服务。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/ui-schema-storage", diff --git a/packages/plugins/@nocobase/plugin-users/package.json b/packages/plugins/@nocobase/plugin-users/package.json index 78835212d79..b2952c9345d 100644 --- a/packages/plugins/@nocobase/plugin-users/package.json +++ b/packages/plugins/@nocobase/plugin-users/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "用户", "description": "Provides basic user model, as well as created by and updated by fields.", "description.zh-CN": "提供了基础的用户模型,以及创建人和最后更新人字段。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/users", diff --git a/packages/plugins/@nocobase/plugin-verification/package.json b/packages/plugins/@nocobase/plugin-verification/package.json index c69e6dbb14f..3929f83be68 100644 --- a/packages/plugins/@nocobase/plugin-verification/package.json +++ b/packages/plugins/@nocobase/plugin-verification/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "验证码", "description": "verification setting.", "description.zh-CN": "验证码配置。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/verification", diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json b/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json index 52ef0a4643b..57c41704506 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:操作后事件", "description": "Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or \"submit to workflow\". Suitable for data processing, sending notifications, etc., after actions are completed.", "description.zh-CN": "通过操作按钮或 API 发起请求并在执行完成后触发,比如新增、更新、删除数据或者“提交至工作流”之后。适用于在操作完成后进行数据处理、发送通知等。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/plugins/workflow-action-trigger", diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json index 941a19c16ab..a53a9871d86 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:聚合查询节点", "description": "Used to aggregate data against the database in workflow, such as: statistics, sum, average, etc.", "description.zh-CN": "可用于在工作流中对数据库进行聚合查询,如:统计数量、求和、平均值等。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-aggregate", diff --git a/packages/plugins/@nocobase/plugin-workflow-delay/package.json b/packages/plugins/@nocobase/plugin-workflow-delay/package.json index ccc69a245d7..f305549dc1b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-delay/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-delay/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:延时节点", "description": "Could be used in workflow parallel branch for waiting other branches.", "description.zh-CN": "可用于工作流并行分支中等待其他分支执行完成。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-delay", diff --git a/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json b/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json index 78212e6a265..45a59955611 100644 --- a/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:动态表达式计算节点", "description": "Useful plugin for doing dynamic calculation based on expression collection records in workflow.", "description.zh-CN": "用于在工作流中进行基于数据行的动态表达式计算。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-dynamic-calculation", diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/package.json b/packages/plugins/@nocobase/plugin-workflow-loop/package.json index 30c8d8ef28b..b72dc18325a 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-loop/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:循环节点", "description": "Used to repeat the sub-process processing of each value in an array, and can also be used for fixed times of sub-process processing.", "description.zh-CN": "用于对一个数组中的每个值进行重复的子流程处理,也可用于固定次数的重复子流程处理。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-loop", diff --git a/packages/plugins/@nocobase/plugin-workflow-mailer/package.json b/packages/plugins/@nocobase/plugin-workflow-mailer/package.json index f6861de60ea..4c2f231189a 100644 --- a/packages/plugins/@nocobase/plugin-workflow-mailer/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-mailer/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:邮件发送节点", "description": "Send email in workflow.", "description.zh-CN": "可用于在工作流中发送电子邮件。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-smtp-mailer", diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/package.json b/packages/plugins/@nocobase/plugin-workflow-manual/package.json index f384df098e7..73836520aaf 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-manual/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:人工处理节点", "description": "Could be used for workflows which some of decisions are made by users.", "description.zh-CN": "用于人工控制部分决策的流程。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-manual", diff --git a/packages/plugins/@nocobase/plugin-workflow-parallel/package.json b/packages/plugins/@nocobase/plugin-workflow-parallel/package.json index fb0e726c464..3a432890c88 100644 --- a/packages/plugins/@nocobase/plugin-workflow-parallel/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-parallel/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:并行分支节点", "description": "Could be used for parallel execution of branch processes in the workflow.", "description.zh-CN": "用于在工作流中需要并行执行的分支流程。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-parallel", diff --git a/packages/plugins/@nocobase/plugin-workflow-request/package.json b/packages/plugins/@nocobase/plugin-workflow-request/package.json index 5f6c4b15e72..5083b716e3c 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-request/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:HTTP 请求节点", "description": "Send HTTP requests to any HTTP service for data interaction in workflow.", "description.zh-CN": "可用于在工作流中向任意 HTTP 服务发送请求,进行数据交互。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-request", diff --git a/packages/plugins/@nocobase/plugin-workflow-sql/package.json b/packages/plugins/@nocobase/plugin-workflow-sql/package.json index b00c6881d7a..4c0a9bb0bf7 100644 --- a/packages/plugins/@nocobase/plugin-workflow-sql/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-sql/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:SQL 节点", "description": "Execute SQL statements in workflow.", "description.zh-CN": "可用于在工作流中对数据库执行任意 SQL 语句。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-sql", diff --git a/packages/plugins/@nocobase/plugin-workflow-test/package.json b/packages/plugins/@nocobase/plugin-workflow-test/package.json index 612c942fa85..7fa9843cd14 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-test/package.json @@ -2,7 +2,7 @@ "name": "@nocobase/plugin-workflow-test", "displayName": "Workflow: test kit", "displayName.zh-CN": "工作流:测试工具包", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "dist/server/index.js", "types": "./dist/server/index.d.ts", diff --git a/packages/plugins/@nocobase/plugin-workflow/package.json b/packages/plugins/@nocobase/plugin-workflow/package.json index 2e19dd46ca0..6fedff0fa79 100644 --- a/packages/plugins/@nocobase/plugin-workflow/package.json +++ b/packages/plugins/@nocobase/plugin-workflow/package.json @@ -4,13 +4,13 @@ "displayName.zh-CN": "工作流", "description": "A powerful BPM tool that provides foundational support for business automation, with the capability to extend unlimited triggers and nodes.", "description.zh-CN": "一个强大的 BPM 工具,为业务自动化提供基础支持,并且可任意扩展更多的触发器和节点。", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow", "dependencies": { - "@nocobase/plugin-workflow-test": "1.3.32-beta" + "@nocobase/plugin-workflow-test": "1.3.33-beta" }, "devDependencies": { "@ant-design/icons": "5.x", diff --git a/packages/presets/nocobase/package.json b/packages/presets/nocobase/package.json index 5ad3730f714..dec572debfa 100644 --- a/packages/presets/nocobase/package.json +++ b/packages/presets/nocobase/package.json @@ -1,70 +1,70 @@ { "name": "@nocobase/preset-nocobase", - "version": "1.3.32-beta", + "version": "1.3.33-beta", "license": "AGPL-3.0", "main": "./lib/server/index.js", "dependencies": { "@formily/json-schema": "2.x", - "@nocobase/plugin-acl": "1.3.32-beta", - "@nocobase/plugin-action-bulk-edit": "1.3.32-beta", - "@nocobase/plugin-action-bulk-update": "1.3.32-beta", - "@nocobase/plugin-action-custom-request": "1.3.32-beta", - "@nocobase/plugin-action-duplicate": "1.3.32-beta", - "@nocobase/plugin-action-export": "1.3.32-beta", - "@nocobase/plugin-action-import": "1.3.32-beta", - "@nocobase/plugin-action-print": "1.3.32-beta", - "@nocobase/plugin-api-doc": "1.3.32-beta", - "@nocobase/plugin-api-keys": "1.3.32-beta", - "@nocobase/plugin-audit-logs": "1.3.32-beta", - "@nocobase/plugin-auth": "1.3.32-beta", - "@nocobase/plugin-auth-sms": "1.3.32-beta", - "@nocobase/plugin-backup-restore": "1.3.32-beta", - "@nocobase/plugin-block-iframe": "1.3.32-beta", - "@nocobase/plugin-block-workbench": "1.3.32-beta", - "@nocobase/plugin-calendar": "1.3.32-beta", - "@nocobase/plugin-charts": "1.3.32-beta", - "@nocobase/plugin-client": "1.3.32-beta", - "@nocobase/plugin-collection-sql": "1.3.32-beta", - "@nocobase/plugin-collection-tree": "1.3.32-beta", - "@nocobase/plugin-data-source-main": "1.3.32-beta", - "@nocobase/plugin-data-source-manager": "1.3.32-beta", - "@nocobase/plugin-data-visualization": "1.3.32-beta", - "@nocobase/plugin-error-handler": "1.3.32-beta", - "@nocobase/plugin-field-china-region": "1.3.32-beta", - "@nocobase/plugin-field-formula": "1.3.32-beta", - "@nocobase/plugin-field-m2m-array": "1.3.32-beta", - "@nocobase/plugin-field-markdown-vditor": "1.3.32-beta", - "@nocobase/plugin-field-sequence": "1.3.32-beta", - "@nocobase/plugin-file-manager": "1.3.32-beta", - "@nocobase/plugin-gantt": "1.3.32-beta", - "@nocobase/plugin-graph-collection-manager": "1.3.32-beta", - "@nocobase/plugin-kanban": "1.3.32-beta", - "@nocobase/plugin-localization": "1.3.32-beta", - "@nocobase/plugin-logger": "1.3.32-beta", - "@nocobase/plugin-map": "1.3.32-beta", - "@nocobase/plugin-mobile": "1.3.32-beta", - "@nocobase/plugin-mobile-client": "1.3.32-beta", - "@nocobase/plugin-mock-collections": "1.3.32-beta", - "@nocobase/plugin-multi-app-manager": "1.3.32-beta", - "@nocobase/plugin-multi-app-share-collection": "1.3.32-beta", - "@nocobase/plugin-snapshot-field": "1.3.32-beta", - "@nocobase/plugin-system-settings": "1.3.32-beta", - "@nocobase/plugin-theme-editor": "1.3.32-beta", - "@nocobase/plugin-ui-schema-storage": "1.3.32-beta", - "@nocobase/plugin-users": "1.3.32-beta", - "@nocobase/plugin-verification": "1.3.32-beta", - "@nocobase/plugin-workflow": "1.3.32-beta", - "@nocobase/plugin-workflow-action-trigger": "1.3.32-beta", - "@nocobase/plugin-workflow-aggregate": "1.3.32-beta", - "@nocobase/plugin-workflow-delay": "1.3.32-beta", - "@nocobase/plugin-workflow-dynamic-calculation": "1.3.32-beta", - "@nocobase/plugin-workflow-loop": "1.3.32-beta", - "@nocobase/plugin-workflow-mailer": "1.3.32-beta", - "@nocobase/plugin-workflow-manual": "1.3.32-beta", - "@nocobase/plugin-workflow-parallel": "1.3.32-beta", - "@nocobase/plugin-workflow-request": "1.3.32-beta", - "@nocobase/plugin-workflow-sql": "1.3.32-beta", - "@nocobase/server": "1.3.32-beta", + "@nocobase/plugin-acl": "1.3.33-beta", + "@nocobase/plugin-action-bulk-edit": "1.3.33-beta", + "@nocobase/plugin-action-bulk-update": "1.3.33-beta", + "@nocobase/plugin-action-custom-request": "1.3.33-beta", + "@nocobase/plugin-action-duplicate": "1.3.33-beta", + "@nocobase/plugin-action-export": "1.3.33-beta", + "@nocobase/plugin-action-import": "1.3.33-beta", + "@nocobase/plugin-action-print": "1.3.33-beta", + "@nocobase/plugin-api-doc": "1.3.33-beta", + "@nocobase/plugin-api-keys": "1.3.33-beta", + "@nocobase/plugin-audit-logs": "1.3.33-beta", + "@nocobase/plugin-auth": "1.3.33-beta", + "@nocobase/plugin-auth-sms": "1.3.33-beta", + "@nocobase/plugin-backup-restore": "1.3.33-beta", + "@nocobase/plugin-block-iframe": "1.3.33-beta", + "@nocobase/plugin-block-workbench": "1.3.33-beta", + "@nocobase/plugin-calendar": "1.3.33-beta", + "@nocobase/plugin-charts": "1.3.33-beta", + "@nocobase/plugin-client": "1.3.33-beta", + "@nocobase/plugin-collection-sql": "1.3.33-beta", + "@nocobase/plugin-collection-tree": "1.3.33-beta", + "@nocobase/plugin-data-source-main": "1.3.33-beta", + "@nocobase/plugin-data-source-manager": "1.3.33-beta", + "@nocobase/plugin-data-visualization": "1.3.33-beta", + "@nocobase/plugin-error-handler": "1.3.33-beta", + "@nocobase/plugin-field-china-region": "1.3.33-beta", + "@nocobase/plugin-field-formula": "1.3.33-beta", + "@nocobase/plugin-field-m2m-array": "1.3.33-beta", + "@nocobase/plugin-field-markdown-vditor": "1.3.33-beta", + "@nocobase/plugin-field-sequence": "1.3.33-beta", + "@nocobase/plugin-file-manager": "1.3.33-beta", + "@nocobase/plugin-gantt": "1.3.33-beta", + "@nocobase/plugin-graph-collection-manager": "1.3.33-beta", + "@nocobase/plugin-kanban": "1.3.33-beta", + "@nocobase/plugin-localization": "1.3.33-beta", + "@nocobase/plugin-logger": "1.3.33-beta", + "@nocobase/plugin-map": "1.3.33-beta", + "@nocobase/plugin-mobile": "1.3.33-beta", + "@nocobase/plugin-mobile-client": "1.3.33-beta", + "@nocobase/plugin-mock-collections": "1.3.33-beta", + "@nocobase/plugin-multi-app-manager": "1.3.33-beta", + "@nocobase/plugin-multi-app-share-collection": "1.3.33-beta", + "@nocobase/plugin-snapshot-field": "1.3.33-beta", + "@nocobase/plugin-system-settings": "1.3.33-beta", + "@nocobase/plugin-theme-editor": "1.3.33-beta", + "@nocobase/plugin-ui-schema-storage": "1.3.33-beta", + "@nocobase/plugin-users": "1.3.33-beta", + "@nocobase/plugin-verification": "1.3.33-beta", + "@nocobase/plugin-workflow": "1.3.33-beta", + "@nocobase/plugin-workflow-action-trigger": "1.3.33-beta", + "@nocobase/plugin-workflow-aggregate": "1.3.33-beta", + "@nocobase/plugin-workflow-delay": "1.3.33-beta", + "@nocobase/plugin-workflow-dynamic-calculation": "1.3.33-beta", + "@nocobase/plugin-workflow-loop": "1.3.33-beta", + "@nocobase/plugin-workflow-mailer": "1.3.33-beta", + "@nocobase/plugin-workflow-manual": "1.3.33-beta", + "@nocobase/plugin-workflow-parallel": "1.3.33-beta", + "@nocobase/plugin-workflow-request": "1.3.33-beta", + "@nocobase/plugin-workflow-sql": "1.3.33-beta", + "@nocobase/server": "1.3.33-beta", "cronstrue": "^2.11.0" }, "repository": { From 57d179f21ec20957c853cff41000def9ea636dd6 Mon Sep 17 00:00:00 2001 From: "nocobase[bot]" <179432756+nocobase[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:32:47 +0000 Subject: [PATCH 022/307] docs: update changelogs --- CHANGELOG.md | 21 +++++++++++++++++++++ CHANGELOG.zh-CN.md | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 572505f6378..4c9e5cd2ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.3.33-beta](https://github.com/nocobase/nocobase/compare/v1.3.32-beta...v1.3.33-beta) - 2024-10-16 + +### 🚀 Improvements + +- **[Workflow]** add association field related hint to the batch mode of update node ([#5426](https://github.com/nocobase/nocobase/pull/5426)) by @mytharcher + +### 🐛 Bug Fixes + +- **[client]** + - fix the issue of Edit profile drawer being covered by subpage ([#5409](https://github.com/nocobase/nocobase/pull/5409)) by @zhangzhonghe + + - Workflow node variables do not display inherited collection fields ([#5415](https://github.com/nocobase/nocobase/pull/5415)) by @chenos + + - pagination not resetting after clearing filter data in table filtering block ([#5411](https://github.com/nocobase/nocobase/pull/5411)) by @katherinehhh + +- **[File manager]** remove the 20 items limit of loading storages in file template collection configuration ([#5430](https://github.com/nocobase/nocobase/pull/5430)) by @mytharcher + +- **[Action: Duplicate record]** Fix the issue where the bulk edit popup does not display content ([#5412](https://github.com/nocobase/nocobase/pull/5412)) by @zhangzhonghe + +- **[Data visualization]** Fix the issue of default values not displaying in the chart filter block ([#5405](https://github.com/nocobase/nocobase/pull/5405)) by @zhangzhonghe + ## [v1.3.32-beta](https://github.com/nocobase/nocobase/compare/v1.3.31-beta...v1.3.32-beta) - 2024-10-13 ### 🐛 Bug Fixes diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index c2f6095ceeb..032e59e952c 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -5,6 +5,27 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。 +## [v1.3.33-beta](https://github.com/nocobase/nocobase/compare/v1.3.32-beta...v1.3.33-beta) - 2024-10-16 + +### 🚀 优化 + +- **[工作流]** 对更新数据节点的批量模式增加关于关系字段的提示 ([#5426](https://github.com/nocobase/nocobase/pull/5426)) by @mytharcher + +### 🐛 修复 + +- **[client]** + - 修复个人资料配置弹窗被子页面遮挡住的问题 ([#5409](https://github.com/nocobase/nocobase/pull/5409)) by @zhangzhonghe + + - 工作流节点变量不显示继承表字段 ([#5415](https://github.com/nocobase/nocobase/pull/5415)) by @chenos + + - 使用筛选区块筛选表格数据时,清空筛选数据查询数据分页器没有跟着调整 ([#5411](https://github.com/nocobase/nocobase/pull/5411)) by @katherinehhh + +- **[文件管理器]** 移除文件表选择存储空间时仅加载 20 个的限制 ([#5430](https://github.com/nocobase/nocobase/pull/5430)) by @mytharcher + +- **[操作:复制记录]** 修复批量编辑弹窗不显示内容的问题 ([#5412](https://github.com/nocobase/nocobase/pull/5412)) by @zhangzhonghe + +- **[数据可视化]** 修复图表筛选区块中不显示默认值的问题 ([#5405](https://github.com/nocobase/nocobase/pull/5405)) by @zhangzhonghe + ## [v1.3.32-beta](https://github.com/nocobase/nocobase/compare/v1.3.31-beta...v1.3.32-beta) - 2024-10-13 ### 🐛 修复 From 54b7bb6d5245c22bffd7f22ea321dd89ef6dad1e Mon Sep 17 00:00:00 2001 From: Junyi Date: Thu, 17 Oct 2024 09:38:48 +0800 Subject: [PATCH 023/307] fix(plugin-file-manager): fix e2e due to component changed (#5437) --- .../plugin-data-source-main/src/client/__e2e__/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/utils.ts index f858128762a..5806ad18d22 100644 --- a/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/utils.ts +++ b/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/utils.ts @@ -331,7 +331,7 @@ export class CollectionSettings { } private async ['File storage'](value: string) { - await this.page.getByLabel('block-item-Select-collections-File storage').getByTestId('select-single').click(); + await this.page.getByLabel('block-item-RemoteSelect-collections-File storage').getByTestId('select-single').click(); await this.page.getByRole('option', { name: value }).click(); } From 737aa11fc5b97264ae593d0c462197cf8e036f6b Mon Sep 17 00:00:00 2001 From: Katherine Date: Thu, 17 Oct 2024 11:34:32 +0800 Subject: [PATCH 024/307] fix: allow table column title to be reset to default (#5439) --- .../data-blocks/table/tableColumnSettings.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx b/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx index d6b01c61d2e..6e08363070b 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx @@ -66,16 +66,15 @@ export const tableColumnSettings = new SchemaSettings({ }, } as ISchema, onSubmit: ({ title }) => { - if (title) { - field.title = title; - columnSchema.title = title; - dn.emit('patch', { - schema: { - 'x-uid': columnSchema['x-uid'], - title: columnSchema.title, - }, - }); - } + field.title = title; + columnSchema.title = title; + dn.emit('patch', { + schema: { + 'x-uid': columnSchema['x-uid'], + title: columnSchema.title, + }, + }); + dn.refresh(); }, }; From 2061f702a867b25ad3ff497dc968151ee7ff5238 Mon Sep 17 00:00:00 2001 From: Katherine Date: Thu, 17 Oct 2024 12:01:41 +0800 Subject: [PATCH 025/307] fix: data clearing bug when selecting association data with data scope in nested sub-table (#5441) --- .../antd/association-field/AssociationSelect.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx b/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx index 4097ce05916..65611d78cba 100644 --- a/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx @@ -13,6 +13,7 @@ import { RecursionField, connect, mapProps, observer, useField, useFieldSchema, import { uid } from '@formily/shared'; import { Space, message } from 'antd'; import { isFunction } from 'mathjs'; +import { last } from 'lodash'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ClearCollectionFieldContext, RecordProvider, useAPIClient, useCollectionRecordData } from '../../../'; @@ -82,7 +83,7 @@ const InternalAssociationSelect = observer( if ( linkageFields.includes(fieldPath?.props?.name) && field.value && - fieldPath?.indexes?.[0] === field?.indexes?.[0] && + last(fieldPath?.indexes) === last(field?.indexes) && fieldPath?.props?.name !== field.props.name ) { field.setValue(undefined); From 821d7901698ab45168a70d25f477fdcbc260ce43 Mon Sep 17 00:00:00 2001 From: gchust Date: Thu, 17 Oct 2024 13:38:40 +0800 Subject: [PATCH 026/307] fix: field component options has not been translated correctly (#5442) --- .../collection-field-interface/CollectionFieldInterface.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts b/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts index befd3def02e..8f0413c7c90 100644 --- a/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts +++ b/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts @@ -12,6 +12,7 @@ import { cloneDeep } from 'lodash'; import type { CollectionFieldOptions } from '../collection'; import { CollectionFieldInterfaceManager } from './CollectionFieldInterfaceManager'; import { defaultProps } from '../../collection-manager/interfaces/properties'; +import { tval } from '@nocobase/utils/client'; export type CollectionFieldInterfaceFactory = new ( collectionFieldInterfaceManager: CollectionFieldInterfaceManager, ) => CollectionFieldInterface; @@ -73,7 +74,7 @@ export abstract class CollectionFieldInterface { if (xComponent) { this.componentOptions = [ { - label: xComponent.split('.').pop(), + label: tval(xComponent.split('.').pop()), value: xComponent, useProps() { return componentProps || {}; From 997bdd16097b295c38911eeb4d2617cc043e7dd9 Mon Sep 17 00:00:00 2001 From: gchust Date: Thu, 17 Oct 2024 16:18:49 +0800 Subject: [PATCH 027/307] fix: missing translate for input component option (#5444) --- .../collection-field-interface/CollectionFieldInterface.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts b/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts index 8f0413c7c90..49eeddeb1d1 100644 --- a/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts +++ b/packages/core/client/src/data-source/collection-field-interface/CollectionFieldInterface.ts @@ -8,7 +8,7 @@ */ import type { ISchema } from '@formily/react'; -import { cloneDeep } from 'lodash'; +import { cloneDeep, capitalize } from 'lodash'; import type { CollectionFieldOptions } from '../collection'; import { CollectionFieldInterfaceManager } from './CollectionFieldInterfaceManager'; import { defaultProps } from '../../collection-manager/interfaces/properties'; @@ -72,9 +72,11 @@ export abstract class CollectionFieldInterface { const xComponent = this.default?.uiSchema?.['x-component']; const componentProps = this.default?.uiSchema?.['x-component-props']; if (xComponent) { + const schemaType = this.default?.uiSchema?.type || 'string'; + const label = tval(xComponent.startsWith('Input') ? capitalize(schemaType) : xComponent.split('.').pop()); this.componentOptions = [ { - label: tval(xComponent.split('.').pop()), + label, value: xComponent, useProps() { return componentProps || {}; From 9f6b16278267b1b0445cc64d3e74de049091d00b Mon Sep 17 00:00:00 2001 From: Junyi Date: Thu, 17 Oct 2024 18:00:58 +0800 Subject: [PATCH 028/307] fix(client): fix selected background color of table row (#5445) --- .../src/schema-component/antd/table-v2/Table.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx index 2d35c1456b7..38d106bc434 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx @@ -476,18 +476,15 @@ export const Table: any = withDynamicSchemaProps( const highlightRowCss = useMemo(() => { return css` & > td { - background-color: ${token.controlItemBgActiveHover} !important; + background-color: ${token.controlItemBgActive} !important; } &:hover > td { background-color: ${token.controlItemBgActiveHover} !important; } `; - }, [token.controlItemBgActiveHover]); + }, [token.controlItemBgActive, token.controlItemBgActiveHover]); - const highlightRow = useMemo( - () => (onClickRow ? highlightRowCss : ''), - [onClickRow, token.controlItemBgActiveHover], - ); + const highlightRow = useMemo(() => (onClickRow ? highlightRowCss : ''), [highlightRowCss, onClickRow]); const onRow = useMemo(() => { if (onClickRow) { @@ -581,7 +578,7 @@ export const Table: any = withDynamicSchemaProps( const BodyCellComponent = useCallback( (props) => { const isIndex = props.className?.includes('selection-column'); - const { record, schema, rowIndex } = props; + const { record, schema, rowIndex, ...others } = props; const { ref, inView } = useInView({ threshold: 0, triggerOnce: true, @@ -594,7 +591,7 @@ export const Table: any = withDynamicSchemaProps( // fix the problem of blank rows at the beginning of a table block if (rowIndex < 20) { return ( - + {props.children} ); From c8741e76560c592b9bab379399b5c5d647772c5b Mon Sep 17 00:00:00 2001 From: Junyi Date: Thu, 17 Oct 2024 18:30:09 +0800 Subject: [PATCH 029/307] fix(plugin-file-manager): fix calling rule hook on read-pretty fields (#5447) --- .../src/client/hooks/useStorageRules.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useStorageRules.ts b/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useStorageRules.ts index 5ffbcd6f059..942f5b812bf 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useStorageRules.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useStorageRules.ts @@ -7,16 +7,25 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useCollectionField, useCollectionManager, useRequest } from '@nocobase/client'; +import { useField } from '@formily/react'; +import { useAPIClient, useCollectionField, useCollectionManager, useRequest } from '@nocobase/client'; export function useStorageRules(storage) { const name = storage ?? ''; + const apiClient = useAPIClient(); + const field = useField(); const { loading, data } = useRequest( - { - url: `storages:getRules/${name}`, + async () => { + if (field.pattern !== 'editable') { + return null; + } + return apiClient.request({ + url: `storages:getRules/${name}`, + }); }, { refreshDeps: [name], + cacheKey: name, }, ); return (!loading && data?.data) || null; From 50056439d90ac38c6a97a349cdb70ae8e7186b73 Mon Sep 17 00:00:00 2001 From: Junyi Date: Thu, 17 Oct 2024 19:10:12 +0800 Subject: [PATCH 030/307] fix(plugin-workflow-request): fix error message for test request node (#5443) * fix(plugin-workflow-request): fix error message for test request node * fix(plugin-workflow-request): fix type --- .../src/server/__tests__/instruction.test.ts | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts index c2c432955ab..8692c48d74e 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-request/src/server/__tests__/instruction.test.ts @@ -19,7 +19,7 @@ import { MockServer } from '@nocobase/test'; import PluginWorkflow, { EXECUTION_STATUS, JOB_STATUS, Processor } from '@nocobase/plugin-workflow'; import { getApp, sleep } from '@nocobase/plugin-workflow-test'; -import RequestInstruction, { RequestConfig } from '../RequestInstruction'; +import RequestInstruction, { RequestInstructionConfig } from '../RequestInstruction'; const HOST = 'localhost'; @@ -155,7 +155,7 @@ describe('workflow > instructions > request', () => { url: api.URL_DATA, method: 'GET', params: [{ name: 'id', value: '{{$context.data.id}}' }], - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -175,7 +175,7 @@ describe('workflow > instructions > request', () => { url: api.URL_DATA, method: 'GET', onlyData: true, - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -195,7 +195,7 @@ describe('workflow > instructions > request', () => { config: { url: api.URL_DATA, method: 'GET', - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -218,7 +218,7 @@ describe('workflow > instructions > request', () => { url: api.URL_TIMEOUT, method: 'GET', timeout: 250, - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -248,7 +248,7 @@ describe('workflow > instructions > request', () => { method: 'GET', timeout: 250, ignoreFail: true, - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -272,7 +272,7 @@ describe('workflow > instructions > request', () => { config: { url: api.URL_400, method: 'GET', - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -291,7 +291,7 @@ describe('workflow > instructions > request', () => { config: { url: api.URL_400_MESSAGE, method: 'GET', - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -311,7 +311,7 @@ describe('workflow > instructions > request', () => { config: { url: api.URL_400_OBJECT, method: 'GET', - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -331,7 +331,7 @@ describe('workflow > instructions > request', () => { config: { url: api.URL_END, method: 'GET', - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -357,7 +357,7 @@ describe('workflow > instructions > request', () => { method: 'GET', timeout: 1000, ignoreFail: true, - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -377,7 +377,7 @@ describe('workflow > instructions > request', () => { url: api.URL_DATA, method: 'POST', data: { title: '{{$context.data.title}}' }, - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -398,7 +398,7 @@ describe('workflow > instructions > request', () => { url: api.URL_DATA, method: 'POST', data: { title: '{{$context.data.title}}' }, - } as RequestConfig, + } as RequestInstructionConfig, }); const title = 't1\n\nline 2'; @@ -541,7 +541,7 @@ describe('workflow > instructions > request', () => { url: `http://localhost:${(server.address() as AddressInfo).port}/api/categories`, method: 'POST', headers: [{ name: 'Authorization', value: `Bearer ${token}` }], - } as RequestConfig, + } as RequestInstructionConfig, }); await PostRepo.create({ values: { title: 't1' } }); @@ -576,7 +576,7 @@ describe('workflow > instructions > request', () => { config: { url: api.URL_DATA, method: 'GET', - } as RequestConfig, + } as RequestInstructionConfig, }); const workflowPlugin = app.pm.get(PluginWorkflow) as PluginWorkflow; @@ -598,7 +598,7 @@ describe('workflow > instructions > request', () => { url: api.URL_404, method: 'GET', ignoreFail: true, - } as RequestConfig, + } as RequestInstructionConfig, }); const workflowPlugin = app.pm.get(PluginWorkflow) as PluginWorkflow; @@ -615,7 +615,7 @@ describe('workflow > instructions > request', () => { it('invalid config', async () => { const { status, result } = await instruction.test(Object.create({})); expect(status).toBe(JOB_STATUS.FAILED); - expect(result).toBe("Cannot read properties of null (reading 'replace')"); + expect(result).toBe('Invalid URL'); }); it('data url', async () => { From d158585efd1c00d0ee05d8ee38a14628a933e4a7 Mon Sep 17 00:00:00 2001 From: gchust Date: Thu, 17 Oct 2024 21:18:25 +0800 Subject: [PATCH 031/307] fix: unit tests error, caused by #5442 (#5448) * fix: unit tests error, caused by #5442 * fix: unit test errors casued by #5442 --- .../src/application/__tests__/Application.test.tsx | 2 +- .../CollectionFieldInterfaceManager.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/client/src/application/__tests__/Application.test.tsx b/packages/core/client/src/application/__tests__/Application.test.tsx index 2db784f2d1c..8fdcb59b940 100644 --- a/packages/core/client/src/application/__tests__/Application.test.tsx +++ b/packages/core/client/src/application/__tests__/Application.test.tsx @@ -467,7 +467,7 @@ describe('Application', () => { .toMatchInlineSnapshot(` [ { - "label": "TestComponent", + "label": "{{t("TestComponent")}}", "useProps": [Function], "value": "TestComponent", }, diff --git a/packages/core/client/src/data-source/__tests__/collection-field-interface/CollectionFieldInterfaceManager.test.ts b/packages/core/client/src/data-source/__tests__/collection-field-interface/CollectionFieldInterfaceManager.test.ts index 69de8c8f97b..a849280f9f2 100644 --- a/packages/core/client/src/data-source/__tests__/collection-field-interface/CollectionFieldInterfaceManager.test.ts +++ b/packages/core/client/src/data-source/__tests__/collection-field-interface/CollectionFieldInterfaceManager.test.ts @@ -156,7 +156,7 @@ describe('CollectionFieldInterfaceManager', () => { expect(fieldInterface.componentOptions).toMatchInlineSnapshot(` [ { - "label": "A", + "label": "{{t("A")}}", "useProps": [Function], "value": "A", }, @@ -190,7 +190,7 @@ describe('CollectionFieldInterfaceManager', () => { expect(fieldInterface.componentOptions).toMatchInlineSnapshot(` [ { - "label": "A", + "label": "{{t("A")}}", "useProps": [Function], "value": "A", }, @@ -268,7 +268,7 @@ describe('CollectionFieldInterfaceManager', () => { expect(fieldInterface.componentOptions).toMatchInlineSnapshot(` [ { - "label": "B", + "label": "{{t("B")}}", "useProps": [Function], "value": "A.B", }, @@ -292,7 +292,7 @@ describe('CollectionFieldInterfaceManager', () => { }; componentOptions = [ { - label: 'A', + label: '{{t("A")}}', value: 'A', }, ]; @@ -308,7 +308,7 @@ describe('CollectionFieldInterfaceManager', () => { expect(fieldInterface.componentOptions).toMatchInlineSnapshot(` [ { - "label": "A", + "label": "{{t("A")}}", "value": "A", }, { From f2e6e025773c1919de9afb322b715bee2e551503 Mon Sep 17 00:00:00 2001 From: Katherine Date: Fri, 18 Oct 2024 09:09:32 +0800 Subject: [PATCH 032/307] feat: configure edit permissions for user profiles & change password (#5422) * feat: configure edit permissions for user profiles * fix: bug * fix: bug * refactor: locale improve * refactor: currentUser --- .../core/client/src/user/ChangePassword.tsx | 10 +- packages/core/client/src/user/CurrentUser.tsx | 2 +- packages/core/client/src/user/EditProfile.tsx | 30 +++++- .../src/client/UsersManagement.tsx | 94 +++++++++++++++++-- .../plugin-users/src/client/schemas/users.ts | 51 ++++++++++ .../plugin-users/src/locale/zh-CN.json | 5 +- .../src/server/collections/systemSettings.ts | 38 ++++++++ 7 files changed, 218 insertions(+), 12 deletions(-) create mode 100644 packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts diff --git a/packages/core/client/src/user/ChangePassword.tsx b/packages/core/client/src/user/ChangePassword.tsx index a290703fbaf..61ede07f95a 100644 --- a/packages/core/client/src/user/ChangePassword.tsx +++ b/packages/core/client/src/user/ChangePassword.tsx @@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next'; import { ActionContextProvider, DropdownVisibleContext, SchemaComponent, useActionContext } from '../'; import { useAPIClient } from '../api-client'; import { useNavigate } from 'react-router-dom'; +import { useSystemSettings } from '../'; const useCloseAction = () => { const { setVisible } = useActionContext(); @@ -132,8 +133,10 @@ export const useChangePassword = () => { const ctx = useContext(DropdownVisibleContext); const [visible, setVisible] = useState(false); const { t } = useTranslation(); + const { data } = useSystemSettings(); + const { enableChangePassword } = data?.data || {}; - return useMemo(() => { + const result = useMemo(() => { return { key: 'password', eventKey: 'ChangePassword', @@ -153,4 +156,9 @@ export const useChangePassword = () => { ), }; }, [visible]); + if (enableChangePassword === false) { + return null; + } + + return result; }; diff --git a/packages/core/client/src/user/CurrentUser.tsx b/packages/core/client/src/user/CurrentUser.tsx index ee369aa697f..fdf2284eaf7 100644 --- a/packages/core/client/src/user/CurrentUser.tsx +++ b/packages/core/client/src/user/CurrentUser.tsx @@ -125,7 +125,7 @@ export const SettingsMenu: React.FC<{ }, editProfile, changePassword, - { + (editProfile || changePassword) && { key: 'divider_2', type: 'divider', }, diff --git a/packages/core/client/src/user/EditProfile.tsx b/packages/core/client/src/user/EditProfile.tsx index e60c78d87a0..b2e24e979b7 100644 --- a/packages/core/client/src/user/EditProfile.tsx +++ b/packages/core/client/src/user/EditProfile.tsx @@ -19,6 +19,7 @@ import { useActionContext, useCurrentUserContext, useRequest, + useSystemSettings, } from '../'; import { useAPIClient } from '../api-client'; @@ -82,6 +83,11 @@ const schema: ISchema = { title: "{{t('Nickname')}}", 'x-decorator': 'FormItem', 'x-component': 'Input', + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, username: { type: 'string', @@ -90,6 +96,11 @@ const schema: ISchema = { 'x-component': 'Input', 'x-validator': { username: true }, required: true, + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, email: { type: 'string', @@ -97,12 +108,22 @@ const schema: ISchema = { 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, phone: { type: 'string', title: '{{t("Phone")}}', 'x-decorator': 'FormItem', 'x-component': 'Input', + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, footer: { 'x-component': 'Action.Drawer.Footer', @@ -134,8 +155,9 @@ export const useEditProfile = () => { const ctx = useContext(DropdownVisibleContext); const [visible, setVisible] = useState(false); const { t } = useTranslation(); - - return useMemo(() => { + const { data } = useSystemSettings(); + const { enableEditProfile } = data?.data || {}; + const result = useMemo(() => { return { key: 'profile', eventKey: 'EditProfile', @@ -158,4 +180,8 @@ export const useEditProfile = () => { ), }; }, [visible]); + if (enableEditProfile === false) { + return null; + } + return result; }; diff --git a/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx b/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx index 7178d7f3ce9..70909498f29 100644 --- a/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx +++ b/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx @@ -16,14 +16,17 @@ import { useDataBlockRequest, useDataBlockResource, useSchemaComponentContext, + useRequest, + useAPIClient, } from '@nocobase/client'; -import React, { useEffect, useMemo } from 'react'; -import { usersSchema } from './schemas/users'; -import { useUsersTranslation } from './locale'; -import { PasswordField } from './PasswordField'; -import { App } from 'antd'; +import React, { createContext, useEffect, useMemo, useContext } from 'react'; +import { App, Tabs, message } from 'antd'; import { useForm } from '@formily/react'; import { createForm } from '@formily/core'; +import { css } from '@emotion/css'; +import { usersSchema, usersSettingsSchema } from './schemas/users'; +import { useUsersTranslation } from './locale'; +import { PasswordField } from './PasswordField'; const useCancelActionProps = () => { const { setVisible } = useActionContext(); @@ -49,7 +52,6 @@ const useSubmitActionProps = () => { async onClick() { await form.submit(); const values = form.values; - console.log('values:', values); if (values[collection.filterTargetKey]) { await resource.update({ values, @@ -79,7 +81,7 @@ const useEditFormProps = () => { }; }; -export const UsersManagement: React.FC = () => { +const UsersManagementTab: React.FC = () => { const { t } = useUsersTranslation(); const scCtx = useSchemaComponentContext(); return ( @@ -92,3 +94,81 @@ export const UsersManagement: React.FC = () => { ); }; +const UsersSettingsContext = createContext({}); + +const UsersSettingsProvider = (props) => { + const result = useRequest({ + url: 'systemSettings:get/1', + }); + return {props.children}; +}; + +const UsersSettingsTab: React.FC = () => { + const { t } = useUsersTranslation(); + const scCtx = useSchemaComponentContext(); + const form = useForm(); + const useFormBlockProps = () => { + const result = useContext(UsersSettingsContext); + const { enableChangePassword, enableEditProfile } = result?.data?.data || {}; + useEffect(() => { + form?.setValues({ + enableChangePassword: enableChangePassword !== false, + enableEditProfile: enableEditProfile !== false, + }); + }, [result]); + return { + form: form, + }; + }; + + const useSubmitActionProps = () => { + const api = useAPIClient(); + const form = useForm(); + return { + type: 'primary', + async onClick() { + await form.submit(); + const values = form.values; + await api.request({ url: 'systemSettings:update/1', data: values, method: 'POST' }); + message.success(t('Saved successfully')); + window.location.reload(); + }, + }; + }; + return ( + + + + ); +}; + +export const UsersManagement: React.FC = () => { + const { t } = useUsersTranslation(); + return ( + , + }, + { + label: t('Settings'), + key: 'usersSettings', + children: , + }, + ]} + /> + ); +}; diff --git a/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts b/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts index 4f31d6f632e..0aeb6a2ddb6 100644 --- a/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts +++ b/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts @@ -482,6 +482,57 @@ export const usersSchema: ISchema = { }, }; +export const usersSettingsSchema: ISchema = { + type: 'object', + 'x-decorator': 'UsersSettingsProvider', + properties: { + usersSettings: { + type: 'void', + 'x-component': 'CardItem', + 'x-decorator': 'UsersSettingsProvider', + properties: { + form: { + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useFormBlockProps', + properties: { + enableEditProfile: { + type: 'string', + 'x-component': 'Checkbox', + 'x-decorator': 'FormItem', + default: true, + 'x-content': '{{t("Allow edit profile")}}', + }, + enableChangePassword: { + type: 'string', + 'x-component': 'Checkbox', + 'x-decorator': 'FormItem', + default: true, + 'x-content': '{{t("Allow change password")}}', + }, + }, + }, + footer: { + type: 'void', + 'x-component': 'div', + 'x-component-props': { + style: { + float: 'right', + }, + }, + properties: { + submit: { + title: 'Submit', + 'x-component': 'Action', + 'x-use-component-props': 'useSubmitActionProps', + }, + }, + }, + }, + }, + }, +}; + export const getRoleUsersSchema = (): ISchema => ({ type: 'void', properties: { diff --git a/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json index e6c5678174d..2153c4fe3d0 100644 --- a/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json @@ -3,5 +3,8 @@ "Add users": "添加用户", "Remove user": "移除用户", "Are you sure you want to remove it?": "你确定要移除吗?", - "Random password": "随机密码" + "Random password": "随机密码", + "Users manager": "用户管理", + "Allow edit profile": "允许修改个人资料", + "Allow change password": "允许修改密码" } diff --git a/packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts b/packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts new file mode 100644 index 00000000000..1d3029bc525 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts @@ -0,0 +1,38 @@ +/** + * 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 { extendCollection } from '@nocobase/database'; + +export default extendCollection({ + name: 'systemSettings', + fields: [ + { + interface: 'checkbox', + type: 'boolean', + name: 'enableEditProfile', + uiSchema: { + type: 'boolean', + 'x-component': 'Checkbox', + title: '{{t("Allow edit profile")}}', + default: true, + }, + }, + { + interface: 'checkbox', + type: 'boolean', + name: 'enableChangePassword', + uiSchema: { + type: 'boolean', + 'x-component': 'Checkbox', + title: '{{t("Allow Change Password")}}', + default: true, + }, + }, + ], +}); From c20374e14507e02e89e86261e057c9d6e0b6c6e3 Mon Sep 17 00:00:00 2001 From: chenos Date: Fri, 18 Oct 2024 10:03:27 +0800 Subject: [PATCH 033/307] chore: update yarn.lock --- yarn.lock | 145 +++++++++++++++++------------------------------------- 1 file changed, 45 insertions(+), 100 deletions(-) diff --git a/yarn.lock b/yarn.lock index 16946f58a73..60af2c87fe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3123,6 +3123,17 @@ "@emotion/weak-memoize" "^0.4.0" stylis "4.2.0" +"@emotion/css@11.x": + version "11.13.4" + resolved "https://registry.npmmirror.com/@emotion/css/-/css-11.13.4.tgz#a5128e34a23f5e2c891970b8ec98a60c5a2395e1" + integrity sha512-CthbOD5EBw+iN0rfM96Tuv5kaZN4nxPyYDvGUs0bc7wZBBiU/0mse+l+0O9RshW2d+v5HH1cme+BAbLJ/3Folw== + dependencies: + "@emotion/babel-plugin" "^11.12.0" + "@emotion/cache" "^11.13.0" + "@emotion/serialize" "^1.3.0" + "@emotion/sheet" "^1.4.0" + "@emotion/utils" "^1.4.0" + "@emotion/css@^11", "@emotion/css@^11.11.2", "@emotion/css@^11.7.1": version "11.13.0" resolved "https://registry.npmmirror.com/@emotion/css/-/css-11.13.0.tgz#3b44f008ce782dafa7cecff75b263af174d0c702" @@ -7336,20 +7347,13 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react-dom@17.x", "@types/react-dom@^17.0.0": +"@types/react-dom@17.x", "@types/react-dom@^17.0.0", "@types/react-dom@^18.0.0": version "17.0.25" resolved "https://registry.npmmirror.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== dependencies: "@types/react" "^17" -"@types/react-dom@^18.0.0": - version "18.3.0" - resolved "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== - dependencies: - "@types/react" "*" - "@types/react-redux@^7.1.20": version "7.1.33" resolved "https://registry.npmmirror.com/@types/react-redux/-/react-redux-7.1.33.tgz#53c5564f03f1ded90904e3c90f77e4bd4dc20b15" @@ -7360,7 +7364,7 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@17.x", "@types/react@>=16.9.11", "@types/react@^17", "@types/react@^17.0.0": +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@17.x", "@types/react@>=16.9.11", "@types/react@^17", "@types/react@^17.0.0", "@types/react@^18.0.0": version "17.0.73" resolved "https://registry.npmmirror.com/@types/react/-/react-17.0.73.tgz#23a663c803b18d8b7f4f2bb9b467f2f3fd70787a" integrity sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ== @@ -7369,14 +7373,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@^18.0.0": - version "18.3.5" - resolved "https://registry.npmmirror.com/@types/react/-/react-18.3.5.tgz#5f524c2ad2089c0ff372bbdabc77ca2c4dbadf8f" - integrity sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/readdir-glob@*": version "1.1.5" resolved "https://registry.npmmirror.com/@types/readdir-glob/-/readdir-glob-1.1.5.tgz#21a4a98898fc606cb568ad815f2a0eedc24d412a" @@ -7609,17 +7605,7 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^5.62.0": - version "5.62.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" - integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== - dependencies: - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - debug "^4.3.4" - -"@typescript-eslint/parser@^6.2.0": +"@typescript-eslint/parser@^5.62.0", "@typescript-eslint/parser@^6.2.0": version "6.21.0" resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== @@ -8723,7 +8709,7 @@ antd-token-previewer@^2.0.0-alpha.6: use-debouncy "~4.3.0" vanilla-jsoneditor "^0.16.1" -antd@5.x, antd@^5.5.1, antd@^5.x: +antd@5.12.8, antd@5.x, antd@^5.5.1, antd@^5.x: version "5.12.8" resolved "https://registry.npmmirror.com/antd/-/antd-5.12.8.tgz#94fea825e9bae535af7adf70417fa1f0dadc4c98" integrity sha512-R2CRcB+aaVZurb3J0IKpBRWq5kW4CLcSqDF58/QBsqYdzK7XjSvM8+eF3rWVRUDbSJfGmyW7I80ywNRYpW1+vA== @@ -9369,12 +9355,14 @@ axios@^0.21.0: dependencies: follow-redirects "^1.14.0" -axios@^0.26.1: - version "0.26.1" - resolved "https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" - integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== +axios@^1.7.0: + version "1.7.7" + resolved "https://registry.npmmirror.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== dependencies: - follow-redirects "^1.14.8" + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" b4a@^1.6.4: version "1.6.4" @@ -12492,7 +12480,7 @@ delegates@^1.0.0: resolved "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -denque@^2.0.1: +denque@^2.0.1, denque@^2.1.0: version "2.1.0" resolved "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== @@ -12975,22 +12963,6 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -echarts-for-react@^3.0.2: - version "3.0.2" - resolved "https://registry.npmmirror.com/echarts-for-react/-/echarts-for-react-3.0.2.tgz#ac5859157048a1066d4553e34b328abb24f2b7c1" - integrity sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA== - dependencies: - fast-deep-equal "^3.1.3" - size-sensor "^1.0.1" - -echarts@^5.5.0: - version "5.5.0" - resolved "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz#c13945a7f3acdd67c134d8a9ac67e917830113ac" - integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== - dependencies: - tslib "2.3.0" - zrender "5.5.0" - editions@^2.2.0: version "2.3.1" resolved "https://registry.npmmirror.com/editions/-/editions-2.3.1.tgz#3bc9962f1978e801312fbd0aebfed63b49bfe698" @@ -14588,11 +14560,16 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@^1.14.0, follow-redirects@^1.14.8: +follow-redirects@^1.14.0: version "1.15.3" resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.npmmirror.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -15910,7 +15887,7 @@ highlight.js@^10.1.0, highlight.js@^10.2.0: resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== -history@5.3.0, history@^5.2.0: +history@5.3.0: version "5.3.0" resolved "https://registry.npmmirror.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== @@ -18484,7 +18461,7 @@ lru-cache@6.0.0, lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@8.0.5: +lru-cache@8.0.5, lru-cache@^8.0.0: version "8.0.5" resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-8.0.5.tgz#983fe337f3e176667f8e567cfcce7cb064ea214e" integrity sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA== @@ -18494,6 +18471,11 @@ lru-cache@^10.0.2: resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.5" resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -19447,6 +19429,11 @@ mime@^1.4.1: resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^4.0.4: + version "4.0.4" + resolved "https://registry.npmmirror.com/mime/-/mime-4.0.4.tgz#9f851b0fc3c289d063b20a7a8055b3014b25664b" + integrity sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ== + mimer@1.1.0: version "1.1.0" resolved "https://registry.npmmirror.com/mimer/-/mimer-1.1.0.tgz#2cb67f7093998e772a0e62c090f77daa1b8a2dbe" @@ -20444,7 +20431,7 @@ number-is-nan@^1.0.0: resolved "https://registry.npmmirror.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -nwsapi@^2.2.0: +nwsapi@2.2.7, nwsapi@^2.2.0: version "2.2.7" resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== @@ -23014,15 +23001,7 @@ react-device-detect@2.2.3: dependencies: ua-parser-js "^1.0.33" -react-dom@18.1.0: - version "18.1.0" - resolved "https://registry.npmmirror.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f" - integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.22.0" - -react-dom@18.x, react-dom@^18.0.0, react-dom@^18.2.0: +react-dom@18.1.0, react-dom@18.x, react-dom@^18.0.0, react-dom@^18.2.0: version "18.2.0" resolved "https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -23214,15 +23193,7 @@ react-refresh@0.14.0, react-refresh@^0.14.0: resolved "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== -react-router-dom@6.3.0: - version "6.3.0" - resolved "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" - integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw== - dependencies: - history "^5.2.0" - react-router "6.3.0" - -react-router-dom@6.x, react-router-dom@^6.11.2, react-router-dom@^6.x: +react-router-dom@6.3.0, react-router-dom@6.x, react-router-dom@^6.11.2, react-router-dom@^6.x: version "6.21.0" resolved "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.21.0.tgz#aa4c6bc046a8e8723095bc09b3c0ab2254532712" integrity sha512-1dUdVj3cwc1npzJaf23gulB562ESNvxf7E4x8upNJycqyUm5BRRZ6dd3LrlzhtLaMrwOCO8R0zoiYxdaJx4LlQ== @@ -23254,14 +23225,7 @@ react-to-print@^2.14.7: resolved "https://registry.npmmirror.com/react-to-print/-/react-to-print-2.14.15.tgz#edb4ce8a92205cf37fd8c3d57de829034aa5c911" integrity sha512-SKnwOzU2cJ8eaAkoJO7+gNhvfEDmm+Y34IdcHsjtHioUevUPhprqbVtvNJlZ2JkGJ8ExK2QNWM9pXECTDR5D8w== -react@18.1.0: - version "18.1.0" - resolved "https://registry.npmmirror.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890" - integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ== - dependencies: - loose-envify "^1.1.0" - -react@18.x, react@^18.0.0, react@^18.2.0: +react@18.1.0, react@18.x, react@^18.0.0, react@^18.2.0: version "18.2.0" resolved "https://registry.npmmirror.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== @@ -24302,13 +24266,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.22.0: - version "0.22.0" - resolved "https://registry.npmmirror.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8" - integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ== - dependencies: - loose-envify "^1.1.0" - scheduler@^0.23.0: version "0.23.0" resolved "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -26318,11 +26275,6 @@ tslib@1.9.3: resolved "https://registry.npmmirror.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@2.3.0: - version "2.3.0" - resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== - tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -28166,13 +28118,6 @@ zip-stream@^4.1.0: compress-commons "^4.1.2" readable-stream "^3.6.0" -zrender@5.5.0: - version "5.5.0" - resolved "https://registry.npmmirror.com/zrender/-/zrender-5.5.0.tgz#54d0d6c4eda81a96d9f60a9cd74dc48ea026bc1e" - integrity sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w== - dependencies: - tslib "2.3.0" - zustand@^4.4.1: version "4.4.7" resolved "https://registry.npmmirror.com/zustand/-/zustand-4.4.7.tgz#355406be6b11ab335f59a66d2cf9815e8f24038c" From adc5ecf21674b34e169af45ee8a1588b6f98bed2 Mon Sep 17 00:00:00 2001 From: Katherine Date: Fri, 18 Oct 2024 10:22:23 +0800 Subject: [PATCH 034/307] refactor: reverseField (#5423) --- .../collection-manager/interfaces/properties/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/client/src/collection-manager/interfaces/properties/index.ts b/packages/core/client/src/collection-manager/interfaces/properties/index.ts index 82d08c2ad9d..b3cb9ff58b5 100644 --- a/packages/core/client/src/collection-manager/interfaces/properties/index.ts +++ b/packages/core/client/src/collection-manager/interfaces/properties/index.ts @@ -147,7 +147,6 @@ export const reverseFieldProperties: Record = { reverse: { type: 'void', 'x-component': 'div', - 'x-hidden': '{{ !showReverseFieldConfig }}', properties: { autoCreateReverseField: { type: 'boolean', @@ -198,6 +197,12 @@ export const reverseFieldProperties: Record = { }, }, }, + (field) => { + const values = field.form.values; + const { reverseField } = values; + field.value = !!reverseField?.key; + field.disabled = !!reverseField?.key; + }, ], }, 'reverseField.type': { @@ -211,6 +216,7 @@ export const reverseFieldProperties: Record = { required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', + 'x-disabled': '{{ !showReverseFieldConfig }}', }, 'reverseField.name': { type: 'string', @@ -219,6 +225,7 @@ export const reverseFieldProperties: Record = { 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'uid', + 'x-disabled': '{{ !showReverseFieldConfig }}', description: "{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}", }, From 495dde46abbbffaeeff0e5a2724c891510c8f044 Mon Sep 17 00:00:00 2001 From: xilesun <2013xile@gmail.com> Date: Fri, 18 Oct 2024 10:30:58 +0800 Subject: [PATCH 035/307] chore(ci): optimize --- .github/workflows/changelog-and-release.yml | 5 ++- .github/workflows/manual-build-pro-image.yml | 37 +++++++++++-------- .../manual-build-pro-plugin-image.yml | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/.github/workflows/changelog-and-release.yml b/.github/workflows/changelog-and-release.yml index ed7d78c2315..4030cce619a 100644 --- a/.github/workflows/changelog-and-release.yml +++ b/.github/workflows/changelog-and-release.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v4 with: repository: nocobase/nocobase - ref: main + ref: ${{ github.head_ref || github.ref_name }} token: ${{ steps.app-token.outputs.token }} persist-credentials: true fetch-depth: 0 @@ -47,6 +47,7 @@ jobs: with: repository: nocobase/pro-plugins path: packages/pro-plugins + ref: ${{ github.head_ref || github.ref_name }} fetch-depth: 0 token: ${{ steps.app-token.outputs.token }} persist-credentials: true @@ -55,7 +56,7 @@ jobs: run: | for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }} do - git clone -b main https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo + git clone -b ${{ github.head_ref || github.ref_name }} https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo done - name: Set user run: | diff --git a/.github/workflows/manual-build-pro-image.yml b/.github/workflows/manual-build-pro-image.yml index 1192c443d6f..17db19f1da3 100644 --- a/.github/workflows/manual-build-pro-image.yml +++ b/.github/workflows/manual-build-pro-image.yml @@ -1,19 +1,16 @@ name: Manual build pro image concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.pr_number }}-${{ github.event.inputs.nocobase_pr_number }} cancel-in-progress: true on: workflow_dispatch: inputs: - base_branch: - description: 'Please enter a base branch for main repo' - required: true - default: 'main' pr_number: - description: 'Please enter a pull request number' - required: true + description: 'Please enter the pr number of pro-plugins' + nocobase_pr_number: + description: 'Please enter the pr number of nocobase/nocobase repository' jobs: build-and-push: @@ -43,22 +40,32 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: ${{ github.event.inputs.base_branch }} + ref: ${{ github.head_ref || github.ref_name }} token: ${{ steps.app-token.outputs.token }} submodules: true - - name: Set PR branch - id: set_pro_pr_branch - if: inputs.pr_number != 'main' - run: echo "pr_branch=refs/pull/${{ github.event.inputs.pr_number }}/head" >> $GITHUB_OUTPUT - - name: Echo PR branch - run: echo "${{ steps.set_pro_pr_branch.outputs.pr_branch }}" + - name: Checkout nocobase/nocobase pr + if: ${{ inputs.nocobase_pr_number != '' }} + shell: bash + run: | + gh pr checkout ${{ inputs.nocobase_pr_number }} + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - name: Checkout pro-plugins uses: actions/checkout@v3 with: repository: nocobase/pro-plugins path: packages/pro-plugins - ref: ${{ steps.set_pro_pr_branch.outputs.pr_branch || 'main' }} + ref: ${{ github.head_ref || github.ref_name }} token: ${{ steps.app-token.outputs.token }} + - name: Checkout pr + if: ${{ inputs.pr_number != '' }} + shell: bash + run: | + cd ./packages/pro-plugins/ + gh pr checkout ${{ inputs.pr_number }} + cd ../../ + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - name: Clone pro repos shell: bash run: | diff --git a/.github/workflows/manual-build-pro-plugin-image.yml b/.github/workflows/manual-build-pro-plugin-image.yml index 3b87bcaf75b..3f304ada23c 100644 --- a/.github/workflows/manual-build-pro-plugin-image.yml +++ b/.github/workflows/manual-build-pro-plugin-image.yml @@ -1,7 +1,7 @@ name: Build pro plugin docker image concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.pro_plugin }}-${{ github.events.inputs.pr_number }}-${{ github.events.inputs.nocobase_pr_number }} cancel-in-progress: true on: From d120259c67ba11d975217766f91b60b63546ddc5 Mon Sep 17 00:00:00 2001 From: Katherine Date: Fri, 18 Oct 2024 11:48:40 +0800 Subject: [PATCH 036/307] refactor: use filterable property to control field filtering capability (#5455) --- .../src/schema-component/antd/filter/useFilterActionProps.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts index e193e2a2df2..f83489c6567 100644 --- a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts +++ b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts @@ -106,6 +106,9 @@ export const useFilterFieldOptions = (fields) => { if (!field.interface) { return; } + if (field.filterable === false) { + return; + } const fieldInterface = getInterface(field.interface); if (!fieldInterface?.filterable) { return; From eff3dfdd54cea064cafcf23a1f2f5fe2c67604e1 Mon Sep 17 00:00:00 2001 From: Katherine Date: Fri, 18 Oct 2024 12:01:14 +0800 Subject: [PATCH 037/307] feat: support configuring disassociation button in sub-tables (#5425) * feat: support configuring disassociation button in sub-tables * fix: bug * refactor: locale improve * fix: filter action test --- packages/core/client/src/locale/zh-CN.json | 3 +- .../subTablePopoverComponentFieldSettings.tsx | 41 ++++++++++++++++++- .../antd/association-field/SubTable.tsx | 23 ++++++++--- .../antd/filter/__e2e__/FilterAction.test.ts | 22 +++++----- .../schema-component/antd/table-v2/Table.tsx | 37 +++++++++-------- 5 files changed, 91 insertions(+), 35 deletions(-) diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index 707af0105d1..d5794e80781 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -1004,5 +1004,6 @@ "Switching the picker, the value and default value will be cleared":"切换选择器时,字段的值和默认值将会被清空", "Stay on the current popup or page":"停留在当前弹窗或页面", "Return to the previous popup or page":"返回上一层弹窗或页面", - "Action after successful submission":"提交成功后动作" + "Action after successful submission":"提交成功后动作", + "Allow disassociation":"允许解除已有数据关联" } diff --git a/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx b/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx index 06339a4bd59..4480dd7b034 100644 --- a/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx +++ b/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx @@ -104,6 +104,38 @@ const allowSelectExistingRecord = { }, }; +const allowDisassociation = { + name: 'allowDisassociation', + type: 'switch', + useVisible() { + const readPretty = useIsFieldReadPretty(); + return !readPretty; + }, + useComponentProps() { + const { t } = useTranslation(); + const field = useField(); + const fieldSchema = useFieldSchema(); + const { dn, refresh } = useDesignable(); + return { + title: t('Allow disassociation'), + checked: fieldSchema['x-component-props']?.allowDisassociation !== false, + onChange(value) { + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + field.componentProps.allowDisassociation = value; + fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props'].allowDisassociation = value; + schema['x-component-props'] = fieldSchema['x-component-props']; + dn.emit('patch', { + schema, + }); + refresh(); + }, + }; + }, +}; + export const setDefaultSortingRules = { name: 'SetDefaultSortingRules', type: 'modal', @@ -293,5 +325,12 @@ export const linkageRules = { export const subTablePopoverComponentFieldSettings = new SchemaSettings({ name: 'fieldSettings:component:SubTable', - items: [fieldComponent, allowAddNewData, allowSelectExistingRecord, setDefaultSortingRules, linkageRules], + items: [ + fieldComponent, + allowAddNewData, + allowSelectExistingRecord, + allowDisassociation, + setDefaultSortingRules, + linkageRules, + ], }); diff --git a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx index a700727e710..28ebb2b8ba9 100644 --- a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx @@ -99,9 +99,8 @@ export const SubTable: any = observer( const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label'); const recordV2 = useCollectionRecord(); const collection = useCollection(); - + const { allowSelectExistingRecord, allowAddnew, allowDisassociation } = field.componentProps; useSubTableSpecialCase({ field }); - const move = (fromIndex: number, toIndex: number) => { if (toIndex === undefined) return; if (!isArr(field.value)) return; @@ -153,7 +152,7 @@ export const SubTable: any = observer( const { selectedRows, setSelectedRows } = useContext(RecordPickerContext); return { onClick() { - selectedRows.map((v) => field.value.push(v)); + selectedRows.map((v) => field.value.push(markRecordAsNew(v))); field.onInput(field.value); field.initialValue = field.value; setSelectedRows([]); @@ -181,13 +180,25 @@ export const SubTable: any = observer( field={field} showIndex dragSort={false} - showDel={field.editable} + showDel={ + allowAddnew !== false || allowSelectExistingRecord !== false || allowDisassociation !== false + ? (record) => { + if (!field.editable) { + return false; + } + if (allowDisassociation !== false) { + return true; + } + return record?.__isNewRecord__; + } + : false + } pagination={false} rowSelection={{ type: 'none', hideSelectAll: true }} footer={() => field.editable && ( <> - {field.componentProps?.allowAddnew !== false && ( + {allowAddnew !== false && (