diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ffcb1664f..425a4cf312 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,6 +21,19 @@ "skipFiles": ["/**"], "type": "node" }, + { + "type": "node", + "request": "launch", + "name": "Debug Tests watch mode", + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "test", "-w", "${file}"], + "skipFiles": ["/**", "**/node_modules/**", "**/dist/**", "**/lib/**", "**/es/**"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "runtimeArgs": ["run", "test", "${relativeFile}"] + } + }, { "type": "node", "request": "launch", @@ -34,6 +47,19 @@ "runtimeArgs": ["run", "test", "${relativeFile}"] } }, + { + "type": "node", + "request": "launch", + "name": "Debug E2E Tests UI", + "runtimeExecutable": "yarn", + "runtimeArgs": ["e2e", "test", "${file}", "--ui"], + "skipFiles": ["/**", "**/node_modules/**", "**/dist/**", "**/lib/**", "**/es/**"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "runtimeArgs": ["e2e", "test", "${fileBasename}", "--ui"] + } + }, { "type": "node", "request": "launch", diff --git a/packages/core/cli/src/util.js b/packages/core/cli/src/util.js index 5764e5f605..52dc2c3ff8 100644 --- a/packages/core/cli/src/util.js +++ b/packages/core/cli/src/util.js @@ -217,6 +217,7 @@ exports.genTsConfigPaths = function genTsConfigPaths() { if (packageJsonName === '@nocobase/test') { paths[`${packageJsonName}/server`] = [`${relativePath}/src/server`]; paths[`${packageJsonName}/e2e`] = [`${relativePath}/src/e2e`]; + paths[`${packageJsonName}/web`] = [`${relativePath}/src/web`]; } if (packageJsonName === '@nocobase/plugin-workflow-test') { paths[`${packageJsonName}/e2e`] = [`${relativePath}/src/e2e`]; diff --git a/packages/core/client/.dumirc.ts b/packages/core/client/.dumirc.ts index dd0c2198ea..ebb48d33a9 100644 --- a/packages/core/client/.dumirc.ts +++ b/packages/core/client/.dumirc.ts @@ -1,3 +1,6 @@ +import path from 'path'; +import glob from 'glob'; +import _ from 'lodash' import { getUmiConfig } from '@nocobase/devtools/umiConfig'; import { defineConfig } from 'dumi'; import { defineThemeConfig } from 'dumi-theme-nocobase'; @@ -8,6 +11,19 @@ const lang = process.env.DOC_LANG; console.log('process.env.DOC_LANG', lang); +const componentsDir = 'src/schema-component/antd'; + +function getComponentsMenu() { + const cwd = path.join(__dirname, componentsDir); + const ignores = ['table/index.md', 'form/index.md']; // 老版本,不需要展示 + const files = glob.sync('*/index.md', { cwd, ignore: ignores }); + + return files.map((file) => ({ + title: _.upperFirst(_.camelCase(file.replace('/index.md', ''))), + link: `/components/${file.replace('/index.md', '')}`, + })); +} + export default defineConfig({ hash: true, alias: { @@ -21,7 +37,10 @@ export default defineConfig({ cacheDirectoryPath: `node_modules/.docs-client-${lang}-cache`, outputPath: `./dist/${lang}`, resolve: { - docDirs: [`./docs/${lang}`] + docDirs: [`./docs/${lang}`], + atomDirs: [ + { type: 'component', dir: componentsDir }, + ], }, locales: [ { id: 'en-US', name: 'English' }, @@ -38,6 +57,10 @@ export default defineConfig({ title: 'API', link: '/core/application/application', }, + { + title: 'Components', + link: '/components/action', + } // { // title: 'UI Schema', // link: '/ui-schema', @@ -202,6 +225,7 @@ export default defineConfig({ ] } ], + '/components': getComponentsMenu(), // '/ui-schema': [ // { // title: 'Overview', diff --git a/packages/core/client/docs/en-US/Demo.tsx b/packages/core/client/docs/en-US/Demo.tsx new file mode 100644 index 0000000000..f7abd778ce --- /dev/null +++ b/packages/core/client/docs/en-US/Demo.tsx @@ -0,0 +1,212 @@ +import { + getApp, + getAppComponent, + getAppComponentWithSchemaSettings, + getReadPrettyAppComponent, + withSchema, +} from '@nocobase/test/web'; +import { + ACLMenuItemProvider, + AdminLayout, + BlockSchemaComponentPlugin, + CurrentUserProvider, + DocumentTitleProvider, + EditComponent, + EditDefaultValue, + EditOperator, + EditPattern, + EditTitle, + EditTitleField, + EditValidationRules, + FilterFormBlockProvider, + FixedBlock, + Form, + FormBlockProvider, + FormItem, + FormV2, + Grid, + IconPicker, + Input, + InternalAdminLayout, + NanoIDInput, + Page, + RouteSchemaComponent, + SchemaInitializerPlugin, + TableBlockProvider, + TableV2, + VariablesProvider, + fieldSettingsFormItem, + tableActionColumnInitializers, + tableActionInitializers, + tableColumnInitializers, + useTableBlockDecoratorProps, +} from '@nocobase/client'; +import { observer } from '@formily/reactive-react'; +import React, { ComponentType } from 'react'; +import { useField, useFieldSchema } from '@formily/react'; +import axios from 'axios'; +import { pick } from 'lodash'; + +const App = getAppComponent({ + designable: true, + schema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-acl-action': 'users:create', + 'x-decorator': 'FormBlockProvider', + 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'users', + }, + 'x-component': 'div', + 'x-app-version': '0.21.0-alpha.10', + properties: { + '0s3tm262rre': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useCreateFormBlockProps', + 'x-app-version': '0.21.0-alpha.10', + properties: { + grid: { + 'x-uid': 'h38s9pa4ik5', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-app-version': '0.21.0-alpha.10', + 'x-linkage-rules': [ + { + condition: { + $and: [ + { + username: { + $eq: 'test', + }, + }, + ], + }, + actions: [ + { + targetFields: ['nickname'], + operator: 'none', + }, + ], + }, + ], + properties: { + udpf3e45i3d: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '0.21.0-alpha.10', + properties: { + hhc0bsk1roi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '0.21.0-alpha.10', + properties: { + username: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'users.username', + 'x-component-props': {}, + 'x-app-version': '0.21.0-alpha.10', + 'x-uid': '71x74r4t4g0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ophjdttgmo5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ta1vq3qr1sd', + 'x-async': false, + 'x-index': 3, + }, + row_rpkxgfonud3: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-index': 4, + properties: { + mmo2k17b0q1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + nickname: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'users.nickname', + 'x-component-props': {}, + 'x-app-version': '0.21.0-alpha.10', + 'x-uid': 'bcowga6nzzy', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'l1awt5at07z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'y1tdyhcwhhi', + 'x-async': false, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + '0m1r08p58e9': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + style: { + marginTop: 24, + }, + }, + 'x-app-version': '0.21.0-alpha.10', + 'x-uid': 't4gxf0xxaxc', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'yk2fivh9hgb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'aqbi3avt3kb', + 'x-async': false, + 'x-index': 1, + }, + appOptions: { + plugins: [BlockSchemaComponentPlugin], + providers: [VariablesProvider], + }, +}); + +export default App; diff --git a/packages/core/client/docs/en-US/test.md b/packages/core/client/docs/en-US/test.md new file mode 100644 index 0000000000..bc70498d6c --- /dev/null +++ b/packages/core/client/docs/en-US/test.md @@ -0,0 +1,2 @@ + + diff --git a/packages/core/client/docs/zh-CN/Demo.tsx b/packages/core/client/docs/zh-CN/Demo.tsx new file mode 100644 index 0000000000..14c9ab2dc2 --- /dev/null +++ b/packages/core/client/docs/zh-CN/Demo.tsx @@ -0,0 +1,183 @@ +import { + getApp, + getAppComponent, + getAppComponentWithSchemaSettings, + getReadPrettyAppComponent, + withSchema, +} from '@nocobase/test/web'; +import { + ACLMenuItemProvider, + AdminLayout, + BlockSchemaComponentPlugin, + CurrentUserProvider, + DocumentTitleProvider, + EditComponent, + EditDefaultValue, + EditOperator, + EditPattern, + EditTitle, + EditTitleField, + EditValidationRules, + FilterFormBlockProvider, + FixedBlock, + Form, + FormBlockProvider, + FormItem, + FormV2, + Grid, + IconPicker, + Input, + InternalAdminLayout, + NanoIDInput, + Page, + RouteSchemaComponent, + SchemaInitializerPlugin, + TableBlockProvider, + TableV2, + VariablesProvider, + fieldSettingsFormItem, + tableActionColumnInitializers, + tableActionInitializers, + tableColumnInitializers, + useTableBlockDecoratorProps, +} from '@nocobase/client'; +import { observer } from '@formily/reactive-react'; +import React, { ComponentType } from 'react'; +import { useField, useFieldSchema } from '@formily/react'; +import axios from 'axios'; +import { pick } from 'lodash'; + +const FormBlockProviderWithSchema = withSchema(FormBlockProvider); + +const App = getAppComponent({ + designable: true, + schema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-acl-action': 'users:create', + 'x-decorator': 'FormBlockProviderWithSchema', + 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'users', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:createForm', + 'x-component': 'CardItem', + properties: { + grid: { + 'x-uid': 'h38s9pa4ik5', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-app-version': '0.21.0-alpha.10', + properties: { + udpf3e45i3d: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '0.21.0-alpha.10', + properties: { + hhc0bsk1roi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '0.21.0-alpha.10', + properties: { + username: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'users.username', + 'x-component-props': {}, + 'x-app-version': '0.21.0-alpha.10', + 'x-uid': '71x74r4t4g0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ophjdttgmo5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ta1vq3qr1sd', + 'x-async': false, + 'x-index': 3, + }, + row_rpkxgfonud3: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-index': 4, + properties: { + mmo2k17b0q1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + nickname: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'users.nickname', + 'x-component-props': {}, + 'x-app-version': '0.21.0-alpha.10', + 'x-uid': 'bcowga6nzzy', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'l1awt5at07z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'y1tdyhcwhhi', + 'x-async': false, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + '0m1r08p58e9': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + style: { + marginTop: 24, + }, + }, + 'x-app-version': '0.21.0-alpha.10', + 'x-uid': 't4gxf0xxaxc', + 'x-async': false, + 'x-index': 2, + }, + }, + }, + appOptions: { + plugins: [BlockSchemaComponentPlugin], + providers: [VariablesProvider], + components: { + FormBlockProviderWithSchema, + }, + }, +}); + +export default App; diff --git a/packages/core/client/docs/zh-CN/test.md b/packages/core/client/docs/zh-CN/test.md new file mode 100644 index 0000000000..bc70498d6c --- /dev/null +++ b/packages/core/client/docs/zh-CN/test.md @@ -0,0 +1,2 @@ + + diff --git a/packages/core/client/src/acl/ACLProvider.tsx b/packages/core/client/src/acl/ACLProvider.tsx index b51843eaac..c3e2913ac9 100644 --- a/packages/core/client/src/acl/ACLProvider.tsx +++ b/packages/core/client/src/acl/ACLProvider.tsx @@ -198,8 +198,9 @@ export function useACLRoleContext() { export const ACLCollectionProvider = (props) => { const { allowAll, parseAction } = useACLRoleContext(); + const app = useApp(); const schema = useFieldSchema(); - if (allowAll) { + if (allowAll || app.disableAcl) { return props.children; } let actionPath = schema?.['x-acl-action'] || props.actionPath; diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index 351ef737ac..50012869b2 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -60,6 +60,7 @@ export interface ApplicationOptions { loadRemotePlugins?: boolean; devDynamicImport?: DevDynamicImport; dataSourceManager?: DataSourceManagerOptions; + disableAcl?: boolean; } export class Application { @@ -93,6 +94,9 @@ export class Application { get pm() { return this.pluginManager; } + get disableAcl() { + return this.options.disableAcl; + } constructor(protected options: ApplicationOptions = {}) { this.initRequireJs(); diff --git a/packages/core/client/src/application/RouterManager.tsx b/packages/core/client/src/application/RouterManager.tsx index cf16954dde..74989567db 100644 --- a/packages/core/client/src/application/RouterManager.tsx +++ b/packages/core/client/src/application/RouterManager.tsx @@ -24,6 +24,7 @@ export interface MemoryRouterOptions extends Omit } export type RouterOptions = (HashRouterOptions | BrowserRouterOptions | MemoryRouterOptions) & { renderComponent?: RenderComponentType; + routes?: Record; }; export type ComponentTypeAndString = ComponentType | string; export interface RouteType extends Omit { @@ -39,6 +40,7 @@ export class RouterManager { constructor(options: RouterOptions = {}, app: Application) { this.options = options; this.app = app; + this.routes = options.routes || {}; } /** diff --git a/packages/core/client/src/application/utils/requirejs.ts b/packages/core/client/src/application/utils/requirejs.ts index 10aeddfcf4..01984e5946 100644 --- a/packages/core/client/src/application/utils/requirejs.ts +++ b/packages/core/client/src/application/utils/requirejs.ts @@ -1,4 +1,4 @@ -/* istanbul ignore file */ +/* istanbul ignore file -- @preserve */ // @ts-nocheck /* eslint-disable */ /* prettier-ignore */ diff --git a/packages/core/client/src/collection-manager/collectionPlugin.ts b/packages/core/client/src/collection-manager/collectionPlugin.ts index 93b0bf2b00..f46162d408 100644 --- a/packages/core/client/src/collection-manager/collectionPlugin.ts +++ b/packages/core/client/src/collection-manager/collectionPlugin.ts @@ -82,7 +82,11 @@ export class CollectionPlugin extends Plugin { this.addCollectionTemplates(); this.addFieldInterfaces(); this.addFieldInterfaceGroups(); + this.addMainDataSource(); + } + addMainDataSource() { + if (this.options?.config?.enableRemoteDataSource === false) return; this.dataSourceManager.addDataSource(MainDataSource, { key: DEFAULT_DATA_SOURCE_KEY, displayName: DEFAULT_DATA_SOURCE_TITLE, diff --git a/packages/core/client/src/common/SelectWithTitle.tsx b/packages/core/client/src/common/SelectWithTitle.tsx index 9e515d1aac..6e2a48b922 100644 --- a/packages/core/client/src/common/SelectWithTitle.tsx +++ b/packages/core/client/src/common/SelectWithTitle.tsx @@ -28,10 +28,12 @@ export function SelectWithTitle({ title, defaultValue, onChange, options, fieldN {title}