Merge remote-tracking branch 'upstream/main' into @hera/dev
This commit is contained in:
commit
fd656387ea
@ -17,6 +17,7 @@ services:
|
|||||||
- DB_USER=nocobase
|
- DB_USER=nocobase
|
||||||
- DB_PASSWORD=nocobase
|
- DB_PASSWORD=nocobase
|
||||||
- DB_TIMEZONE=+08:00
|
- DB_TIMEZONE=+08:00
|
||||||
|
- DB_UNDERSCORED=true
|
||||||
volumes:
|
volumes:
|
||||||
- ./storage:/app/nocobase/storage
|
- ./storage:/app/nocobase/storage
|
||||||
ports:
|
ports:
|
||||||
@ -29,5 +30,7 @@ services:
|
|||||||
MYSQL_PASSWORD: nocobase
|
MYSQL_PASSWORD: nocobase
|
||||||
MYSQL_ROOT_PASSWORD: nocobase
|
MYSQL_ROOT_PASSWORD: nocobase
|
||||||
restart: always
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./storage/db/mariadb:/var/lib/mysql
|
||||||
networks:
|
networks:
|
||||||
- nocobase
|
- nocobase
|
@ -29,5 +29,7 @@ services:
|
|||||||
MYSQL_PASSWORD: nocobase
|
MYSQL_PASSWORD: nocobase
|
||||||
MYSQL_ROOT_PASSWORD: nocobase
|
MYSQL_ROOT_PASSWORD: nocobase
|
||||||
restart: always
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./storage/db/mysql:/var/lib/mysql
|
||||||
networks:
|
networks:
|
||||||
- nocobase
|
- nocobase
|
||||||
|
@ -28,5 +28,7 @@ services:
|
|||||||
POSTGRES_USER: nocobase
|
POSTGRES_USER: nocobase
|
||||||
POSTGRES_DB: nocobase
|
POSTGRES_DB: nocobase
|
||||||
POSTGRES_PASSWORD: nocobase
|
POSTGRES_PASSWORD: nocobase
|
||||||
|
volumes:
|
||||||
|
- ./storage/db/postgres:/var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
- nocobase
|
- nocobase
|
@ -18,6 +18,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"nocobase": "nocobase",
|
"nocobase": "nocobase",
|
||||||
"pm": "nocobase pm",
|
"pm": "nocobase pm",
|
||||||
|
"pm2": "nocobase pm2",
|
||||||
"dev": "nocobase dev",
|
"dev": "nocobase dev",
|
||||||
"dev-server": "nocobase dev --server",
|
"dev-server": "nocobase dev --server",
|
||||||
"start": "nocobase start",
|
"start": "nocobase start",
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
getCjsPackages,
|
getCjsPackages,
|
||||||
getPresetsPackages,
|
getPresetsPackages,
|
||||||
ROOT_PATH,
|
ROOT_PATH,
|
||||||
|
ESM_PACKAGES,
|
||||||
} from './constant';
|
} from './constant';
|
||||||
import { buildClient } from './buildClient';
|
import { buildClient } from './buildClient';
|
||||||
import { buildCjs } from './buildCjs';
|
import { buildCjs } from './buildCjs';
|
||||||
@ -18,6 +19,7 @@ import { PkgLog, getPkgLog, toUnixPath, getPackageJson, getUserConfig, UserConfi
|
|||||||
import { getPackages } from './utils/getPackages';
|
import { getPackages } from './utils/getPackages';
|
||||||
import { Package } from '@lerna/package';
|
import { Package } from '@lerna/package';
|
||||||
import { tarPlugin } from './tarPlugin'
|
import { tarPlugin } from './tarPlugin'
|
||||||
|
import { buildEsm } from './buildEsm';
|
||||||
|
|
||||||
const BUILD_ERROR = 'build-error';
|
const BUILD_ERROR = 'build-error';
|
||||||
|
|
||||||
@ -51,6 +53,8 @@ export async function build(pkgs: string[]) {
|
|||||||
if (clientCore) {
|
if (clientCore) {
|
||||||
await buildPackage(clientCore, 'es', buildClient);
|
await buildPackage(clientCore, 'es', buildClient);
|
||||||
}
|
}
|
||||||
|
const esmPackages = cjsPackages.filter(pkg => ESM_PACKAGES.includes(pkg.name));
|
||||||
|
await buildPackages(esmPackages, 'es', buildEsm);
|
||||||
|
|
||||||
// plugins/*、samples/*
|
// plugins/*、samples/*
|
||||||
await buildPackages(pluginPackages, 'dist', buildPlugin);
|
await buildPackages(pluginPackages, 'dist', buildPlugin);
|
||||||
|
@ -19,14 +19,14 @@ export async function buildClient(cwd: string, userConfig: UserConfig, sourcemap
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
await buildEsm(cwd, userConfig, sourcemap, external, log);
|
await buildClientEsm(cwd, userConfig, sourcemap, external, log);
|
||||||
await buildLib(cwd, userConfig, sourcemap, external, log);
|
await buildClientLib(cwd, userConfig, sourcemap, external, log);
|
||||||
await buildLocale(cwd, userConfig, log);
|
await buildLocale(cwd, userConfig, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
type External = (id: string) => boolean;
|
type External = (id: string) => boolean;
|
||||||
|
|
||||||
export function buildEsm(cwd: string, userConfig: UserConfig, sourcemap: boolean, external: External, log: PkgLog) {
|
function buildClientEsm(cwd: string, userConfig: UserConfig, sourcemap: boolean, external: External, log: PkgLog) {
|
||||||
log('build client esm');
|
log('build client esm');
|
||||||
const entry = path.join(cwd, 'src/index.ts').replaceAll(/\\/g, '/');
|
const entry = path.join(cwd, 'src/index.ts').replaceAll(/\\/g, '/');
|
||||||
const outDir = path.resolve(cwd, 'es');
|
const outDir = path.resolve(cwd, 'es');
|
||||||
@ -61,7 +61,7 @@ export function buildEsm(cwd: string, userConfig: UserConfig, sourcemap: boolean
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buildLib(
|
async function buildClientLib(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
userConfig: UserConfig,
|
userConfig: UserConfig,
|
||||||
sourcemap: boolean,
|
sourcemap: boolean,
|
||||||
|
66
packages/core/build/src/buildEsm.ts
Normal file
66
packages/core/build/src/buildEsm.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import { PkgLog, UserConfig } from './utils';
|
||||||
|
import { build as viteBuild } from 'vite';
|
||||||
|
import fg from 'fast-glob';
|
||||||
|
|
||||||
|
export async function buildEsm(cwd: string, userConfig: UserConfig, sourcemap: boolean = false, log: PkgLog) {
|
||||||
|
log('build esm');
|
||||||
|
|
||||||
|
const indexEntry = path.join(cwd, 'src/index.ts').replaceAll(/\\/g, '/');
|
||||||
|
const outDir = path.resolve(cwd, 'es');
|
||||||
|
|
||||||
|
await build(cwd, indexEntry, outDir, userConfig, sourcemap, log);
|
||||||
|
|
||||||
|
const clientEntry = fg.sync(['src/client/index.ts', 'src/client.ts'], { cwd, absolute: true, onlyFiles: true })?.[0]?.replaceAll(/\\/g, '/');
|
||||||
|
const clientOutDir = path.resolve(cwd, 'es/client');
|
||||||
|
if (clientEntry) {
|
||||||
|
await build(cwd, clientEntry, clientOutDir, userConfig, sourcemap, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkg = require(path.join(cwd, 'package.json'));
|
||||||
|
if (pkg.name === '@nocobase/test') {
|
||||||
|
const e2eEntry = path.join(cwd, 'src/e2e/index.ts').replaceAll(/\\/g, '/');
|
||||||
|
const e2eOutDir = path.resolve(cwd, 'es/e2e');
|
||||||
|
await build(cwd, e2eEntry, e2eOutDir, userConfig, sourcemap, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function build(cwd: string, entry: string, outDir: string, userConfig: UserConfig, sourcemap: boolean = false, log: PkgLog) {
|
||||||
|
const cwdWin = cwd.replaceAll(/\\/g, '/');
|
||||||
|
const cwdUnix = cwd.replaceAll(/\//g, '\\');
|
||||||
|
const external = function (id: string) {
|
||||||
|
if (id.startsWith('.') || id.startsWith(cwdUnix) || id.startsWith(cwdWin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return viteBuild(
|
||||||
|
userConfig.modifyViteConfig({
|
||||||
|
mode: process.env.NODE_ENV || 'production',
|
||||||
|
define: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
|
||||||
|
'process.env.__TEST__': false,
|
||||||
|
'process.env.__E2E__': process.env.__E2E__ ? true : false,
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
minify: false,
|
||||||
|
outDir,
|
||||||
|
cssCodeSplit: true,
|
||||||
|
emptyOutDir: true,
|
||||||
|
sourcemap,
|
||||||
|
lib: {
|
||||||
|
entry,
|
||||||
|
formats: ['es'],
|
||||||
|
fileName: 'index',
|
||||||
|
},
|
||||||
|
target: ['node16'],
|
||||||
|
rollupOptions: {
|
||||||
|
cache: true,
|
||||||
|
treeshake: true,
|
||||||
|
external,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
@ -39,6 +39,7 @@ export const getPresetsPackages = (packages: Package[]) =>
|
|||||||
packages.filter((item) => item.location.startsWith(PRESETS_DIR));
|
packages.filter((item) => item.location.startsWith(PRESETS_DIR));
|
||||||
export const CORE_APP = path.join(PACKAGES_PATH, 'core/app');
|
export const CORE_APP = path.join(PACKAGES_PATH, 'core/app');
|
||||||
export const CORE_CLIENT = path.join(PACKAGES_PATH, 'core/client');
|
export const CORE_CLIENT = path.join(PACKAGES_PATH, 'core/client');
|
||||||
|
export const ESM_PACKAGES = ['@nocobase/test'];
|
||||||
export const CJS_EXCLUDE_PACKAGES = [
|
export const CJS_EXCLUDE_PACKAGES = [
|
||||||
path.join(PACKAGES_PATH, 'core/build'),
|
path.join(PACKAGES_PATH, 'core/build'),
|
||||||
path.join(PACKAGES_PATH, 'core/cli'),
|
path.join(PACKAGES_PATH, 'core/cli'),
|
||||||
|
@ -16,6 +16,7 @@ module.exports = (cli) => {
|
|||||||
require('./e2e')(cli);
|
require('./e2e')(cli);
|
||||||
require('./clean')(cli);
|
require('./clean')(cli);
|
||||||
require('./doc')(cli);
|
require('./doc')(cli);
|
||||||
|
require('./pm2')(cli);
|
||||||
require('./test')(cli);
|
require('./test')(cli);
|
||||||
require('./umi')(cli);
|
require('./umi')(cli);
|
||||||
require('./upgrade')(cli);
|
require('./upgrade')(cli);
|
||||||
|
28
packages/core/cli/src/commands/pm2.js
Normal file
28
packages/core/cli/src/commands/pm2.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const chalk = require('chalk');
|
||||||
|
const { Command } = require('commander');
|
||||||
|
const { run, isDev } = require('../util');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Command} cli
|
||||||
|
*/
|
||||||
|
module.exports = (cli) => {
|
||||||
|
cli
|
||||||
|
.command('pm2')
|
||||||
|
.allowUnknownOption()
|
||||||
|
.action(() => {
|
||||||
|
run('pm2', process.argv.slice(3));
|
||||||
|
});
|
||||||
|
cli
|
||||||
|
.command('pm2-restart')
|
||||||
|
.allowUnknownOption()
|
||||||
|
.action(() => {
|
||||||
|
run('pm2', ['restart', 'all']);
|
||||||
|
});
|
||||||
|
cli
|
||||||
|
.command('pm2-stop')
|
||||||
|
.allowUnknownOption()
|
||||||
|
.action(() => {
|
||||||
|
run('pm2', ['stop', 'all']);
|
||||||
|
});
|
||||||
|
};
|
@ -175,6 +175,9 @@ export class Application {
|
|||||||
return this.dataSourceManager.getDataSource(dataSource)?.collectionManager;
|
return this.dataSourceManager.getDataSource(dataSource)?.collectionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
getComposeProviders() {
|
getComposeProviders() {
|
||||||
const Providers = compose(...this.providers)(BlankComponent);
|
const Providers = compose(...this.providers)(BlankComponent);
|
||||||
Providers.displayName = 'Providers';
|
Providers.displayName = 'Providers';
|
||||||
@ -280,6 +283,9 @@ export class Application {
|
|||||||
return React.createElement(this.getComponent(Component), props);
|
return React.createElement(this.getComponent(Component), props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal use addComponents({ SomeComponent }) instead
|
||||||
|
*/
|
||||||
protected addComponent(component: ComponentType, name?: string) {
|
protected addComponent(component: ComponentType, name?: string) {
|
||||||
const componentName = name || component.displayName || component.name;
|
const componentName = name || component.displayName || component.name;
|
||||||
if (!componentName) {
|
if (!componentName) {
|
||||||
|
@ -26,6 +26,9 @@ export class PluginManager {
|
|||||||
this.initPlugins = this.init(_plugins);
|
this.initPlugins = this.init(_plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
async init(_plugins: PluginType[]) {
|
async init(_plugins: PluginType[]) {
|
||||||
await this.initStaticPlugins(_plugins);
|
await this.initStaticPlugins(_plugins);
|
||||||
if (this.loadRemotePlugins) {
|
if (this.loadRemotePlugins) {
|
||||||
@ -84,6 +87,9 @@ export class PluginManager {
|
|||||||
return new plugin(opts, this.app);
|
return new plugin(opts, this.app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
async load() {
|
async load() {
|
||||||
await this.initPlugins;
|
await this.initPlugins;
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ export class RouterManager {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
getRoutesTree(): RouteObject[] {
|
getRoutesTree(): RouteObject[] {
|
||||||
type RouteTypeWithChildren = RouteType & { children?: RouteTypeWithChildren };
|
type RouteTypeWithChildren = RouteType & { children?: RouteTypeWithChildren };
|
||||||
const routes: Record<string, RouteTypeWithChildren> = {};
|
const routes: Record<string, RouteTypeWithChildren> = {};
|
||||||
@ -106,6 +109,9 @@ export class RouterManager {
|
|||||||
this.options.basename = basename;
|
this.options.basename = basename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
getRouterComponent() {
|
getRouterComponent() {
|
||||||
const { type = 'browser', ...opts } = this.options;
|
const { type = 'browser', ...opts } = this.options;
|
||||||
const Routers = {
|
const Routers = {
|
||||||
|
@ -9,11 +9,17 @@ import { SchemaInitializerOptions } from '../types';
|
|||||||
|
|
||||||
export * from './useAriaAttributeOfMenuItem';
|
export * from './useAriaAttributeOfMenuItem';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function useSchemaInitializerMenuItems(items: any[], name?: string, onClick?: (args: any) => void) {
|
export function useSchemaInitializerMenuItems(items: any[], name?: string, onClick?: (args: any) => void) {
|
||||||
const getMenuItems = useGetSchemaInitializerMenuItems(onClick);
|
const getMenuItems = useGetSchemaInitializerMenuItems(onClick);
|
||||||
return useMemo(() => getMenuItems(items, name), [getMenuItems, items, name]);
|
return useMemo(() => getMenuItems(items, name), [getMenuItems, items, name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function useGetSchemaInitializerMenuItems(onClick?: (args: any) => void) {
|
export function useGetSchemaInitializerMenuItems(onClick?: (args: any) => void) {
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useSchemaInitializerSubMenuContext } from '../components/SchemaInitializerSubMenu';
|
import { useSchemaInitializerSubMenuContext } from '../components/SchemaInitializerSubMenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const useAriaAttributeOfMenuItem = () => {
|
export const useAriaAttributeOfMenuItem = () => {
|
||||||
const { isInMenu } = useSchemaInitializerSubMenuContext();
|
const { isInMenu } = useSchemaInitializerSubMenuContext();
|
||||||
// 在 Menu 中,每一项的 role 已经被标记为 menuitem 了,不需要再次标记;
|
// 在 Menu 中,每一项的 role 已经被标记为 menuitem 了,不需要再次标记;
|
||||||
|
@ -7,6 +7,9 @@ import React from 'react';
|
|||||||
import { useDesignable } from '../../../schema-component';
|
import { useDesignable } from '../../../schema-component';
|
||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const SchemaSettingsWrapper: FC<SchemaSettingOptions<any>> = (props) => {
|
export const SchemaSettingsWrapper: FC<SchemaSettingOptions<any>> = (props) => {
|
||||||
const { items, Component = SchemaSettingsIcon, name, componentProps, style, ...others } = props;
|
const { items, Component = SchemaSettingsIcon, name, componentProps, style, ...others } = props;
|
||||||
const { dn } = useDesignable();
|
const { dn } = useDesignable();
|
||||||
|
@ -35,6 +35,9 @@ import * as nocobaseClient from '../../index';
|
|||||||
|
|
||||||
import type { RequireJS } from './requirejs';
|
import type { RequireJS } from './requirejs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function defineGlobalDeps(requirejs: RequireJS) {
|
export function defineGlobalDeps(requirejs: RequireJS) {
|
||||||
// react
|
// react
|
||||||
requirejs.define('react', () => React);
|
requirejs.define('react', () => React);
|
||||||
|
@ -3,12 +3,18 @@ import type { PluginData } from '../PluginManager';
|
|||||||
import type { RequireJS } from './requirejs';
|
import type { RequireJS } from './requirejs';
|
||||||
import type { DevDynamicImport } from '../Application';
|
import type { DevDynamicImport } from '../Application';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function defineDevPlugins(plugins: Record<string, typeof Plugin>) {
|
export function defineDevPlugins(plugins: Record<string, typeof Plugin>) {
|
||||||
Object.entries(plugins).forEach(([packageName, plugin]) => {
|
Object.entries(plugins).forEach(([packageName, plugin]) => {
|
||||||
window.define(`${packageName}/client`, () => plugin);
|
window.define(`${packageName}/client`, () => plugin);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function definePluginClient(packageName: string) {
|
export function definePluginClient(packageName: string) {
|
||||||
window.define(`${packageName}/client`, ['exports', packageName], function (_exports: any, _pluginExports: any) {
|
window.define(`${packageName}/client`, ['exports', packageName], function (_exports: any, _pluginExports: any) {
|
||||||
Object.defineProperty(_exports, '__esModule', {
|
Object.defineProperty(_exports, '__esModule', {
|
||||||
@ -27,6 +33,9 @@ export function definePluginClient(packageName: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function configRequirejs(requirejs: any, pluginData: PluginData[]) {
|
export function configRequirejs(requirejs: any, pluginData: PluginData[]) {
|
||||||
requirejs.requirejs.config({
|
requirejs.requirejs.config({
|
||||||
waitSeconds: 120,
|
waitSeconds: 120,
|
||||||
@ -37,6 +46,9 @@ export function configRequirejs(requirejs: any, pluginData: PluginData[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function processRemotePlugins(pluginData: PluginData[], resolve: (plugins: [string, typeof Plugin][]) => void) {
|
export function processRemotePlugins(pluginData: PluginData[], resolve: (plugins: [string, typeof Plugin][]) => void) {
|
||||||
return (...pluginModules: (typeof Plugin & { default?: typeof Plugin })[]) => {
|
return (...pluginModules: (typeof Plugin & { default?: typeof Plugin })[]) => {
|
||||||
const res: [string, typeof Plugin][] = pluginModules
|
const res: [string, typeof Plugin][] = pluginModules
|
||||||
@ -58,6 +70,9 @@ export function processRemotePlugins(pluginData: PluginData[], resolve: (plugins
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function getRemotePlugins(
|
export function getRemotePlugins(
|
||||||
requirejs: any,
|
requirejs: any,
|
||||||
pluginData: PluginData[] = [],
|
pluginData: PluginData[] = [],
|
||||||
@ -80,6 +95,9 @@ interface GetPluginsOption {
|
|||||||
devDynamicImport?: DevDynamicImport;
|
devDynamicImport?: DevDynamicImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export async function getPlugins(options: GetPluginsOption): Promise<Array<[string, typeof Plugin]>> {
|
export async function getPlugins(options: GetPluginsOption): Promise<Array<[string, typeof Plugin]>> {
|
||||||
const { requirejs, pluginData, devDynamicImport } = options;
|
const { requirejs, pluginData, devDynamicImport } = options;
|
||||||
if (pluginData.length === 0) return [];
|
if (pluginData.length === 0) return [];
|
||||||
|
@ -10,6 +10,9 @@ export interface RequireJS {
|
|||||||
define: RequireDefine
|
define: RequireDefine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function getRequireJs(): RequireJS {
|
export function getRequireJs(): RequireJS {
|
||||||
var requirejs, require, define;
|
var requirejs, require, define;
|
||||||
var req, s, head, baseElement, dataMain, src,
|
var req, s, head, baseElement, dataMain, src,
|
||||||
|
@ -24,6 +24,13 @@ import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table
|
|||||||
import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps';
|
import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps';
|
||||||
import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps';
|
import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps';
|
||||||
import { useCollapseBlockDecoratorProps } from '../modules/blocks/filter-blocks/collapse/hooks/useCollapseBlockDecoratorProps';
|
import { useCollapseBlockDecoratorProps } from '../modules/blocks/filter-blocks/collapse/hooks/useCollapseBlockDecoratorProps';
|
||||||
|
import { useFilterFormBlockProps } from '../modules/blocks/filter-blocks/form/hooks/useFilterFormBlockProps';
|
||||||
|
import { useFilterFormBlockDecoratorProps } from '../modules/blocks/filter-blocks/form/hooks/useFilterFormBlockDecoratorProps';
|
||||||
|
import { useGridCardBlockDecoratorProps } from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps';
|
||||||
|
import { useDetailsProps } from '../modules/blocks/data-blocks/details-single/hooks/useDetailsProps';
|
||||||
|
import { useDetailsWithPaginationProps } from '../modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationProps';
|
||||||
|
import { useDetailsDecoratorProps } from '../modules/blocks/data-blocks/details-single/hooks/useDetailsDecoratorProps';
|
||||||
|
import { useDetailsWithPaginationDecoratorProps } from '../modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationDecoratorProps';
|
||||||
|
|
||||||
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
||||||
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
||||||
@ -51,6 +58,10 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
|||||||
useEditFormBlockProps,
|
useEditFormBlockProps,
|
||||||
useFormFieldProps,
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
|
useDetailsProps,
|
||||||
|
useDetailsWithPaginationProps,
|
||||||
|
useDetailsDecoratorProps,
|
||||||
|
useDetailsWithPaginationDecoratorProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
useTableSelectorProps,
|
useTableSelectorProps,
|
||||||
@ -58,6 +69,9 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
|||||||
useListBlockDecoratorProps,
|
useListBlockDecoratorProps,
|
||||||
useTableSelectorDecoratorProps,
|
useTableSelectorDecoratorProps,
|
||||||
useCollapseBlockDecoratorProps,
|
useCollapseBlockDecoratorProps,
|
||||||
|
useFilterFormBlockProps,
|
||||||
|
useFilterFormBlockDecoratorProps,
|
||||||
|
useGridCardBlockDecoratorProps,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
@ -102,6 +116,10 @@ export class BlockSchemaComponentPlugin extends Plugin {
|
|||||||
useEditFormBlockProps,
|
useEditFormBlockProps,
|
||||||
useFormFieldProps,
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
|
useDetailsProps,
|
||||||
|
useDetailsWithPaginationProps,
|
||||||
|
useDetailsDecoratorProps,
|
||||||
|
useDetailsWithPaginationDecoratorProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
useTableSelectorProps,
|
useTableSelectorProps,
|
||||||
@ -109,6 +127,9 @@ export class BlockSchemaComponentPlugin extends Plugin {
|
|||||||
useListBlockDecoratorProps,
|
useListBlockDecoratorProps,
|
||||||
useTableSelectorDecoratorProps,
|
useTableSelectorDecoratorProps,
|
||||||
useCollapseBlockDecoratorProps,
|
useCollapseBlockDecoratorProps,
|
||||||
|
useFilterFormBlockProps,
|
||||||
|
useFilterFormBlockDecoratorProps,
|
||||||
|
useGridCardBlockDecoratorProps,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,12 @@ import { createForm } from '@formily/core';
|
|||||||
import { useField } from '@formily/react';
|
import { useField } from '@formily/react';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';
|
||||||
import { useCollectionParentRecord } from '../data-source/collection-record/CollectionRecordProvider';
|
import { useCollectionParentRecord } from '../data-source/collection-record/CollectionRecordProvider';
|
||||||
import { RecordProvider } from '../record-provider';
|
import { RecordProvider } from '../record-provider';
|
||||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
import { useParsedFilter } from './hooks';
|
import { useParsedFilter } from './hooks';
|
||||||
|
import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const DetailsBlockContext = createContext<any>({});
|
export const DetailsBlockContext = createContext<any>({});
|
||||||
DetailsBlockContext.displayName = 'DetailsBlockContext';
|
DetailsBlockContext.displayName = 'DetailsBlockContext';
|
||||||
@ -24,6 +25,7 @@ const InternalDetailsBlockProvider = (props) => {
|
|||||||
const { resource, service } = useBlockRequestContext();
|
const { resource, service } = useBlockRequestContext();
|
||||||
const parentRecord = useCollectionParentRecord();
|
const parentRecord = useCollectionParentRecord();
|
||||||
const currentRecord = (action === 'list' ? service?.data?.data?.[0] : service?.data?.data) || {};
|
const currentRecord = (action === 'list' ? service?.data?.data?.[0] : service?.data?.data) || {};
|
||||||
|
const formBlockRef = useRef();
|
||||||
const detailsBLockValue = useMemo(() => {
|
const detailsBLockValue = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
action,
|
action,
|
||||||
@ -31,6 +33,7 @@ const InternalDetailsBlockProvider = (props) => {
|
|||||||
field,
|
field,
|
||||||
service,
|
service,
|
||||||
resource,
|
resource,
|
||||||
|
formBlockRef,
|
||||||
};
|
};
|
||||||
}, [action, field, form, resource, service]);
|
}, [action, field, form, resource, service]);
|
||||||
|
|
||||||
@ -50,20 +53,22 @@ const InternalDetailsBlockProvider = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DetailsBlockContext.Provider value={detailsBLockValue}>
|
<DetailsBlockContext.Provider value={detailsBLockValue}>
|
||||||
|
<div ref={formBlockRef}>
|
||||||
<RecordProvider isNew={false} record={currentRecord} parent={parentRecord?.data}>
|
<RecordProvider isNew={false} record={currentRecord} parent={parentRecord?.data}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</RecordProvider>
|
</RecordProvider>
|
||||||
|
</div>
|
||||||
</DetailsBlockContext.Provider>
|
</DetailsBlockContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DetailsBlockProvider = (props) => {
|
export const DetailsBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
return (
|
return (
|
||||||
<BlockProvider name="details" {...props}>
|
<BlockProvider name="details" {...props}>
|
||||||
<InternalDetailsBlockProvider {...props} />
|
<InternalDetailsBlockProvider {...props} />
|
||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -72,6 +77,11 @@ export const useDetailsBlockContext = () => {
|
|||||||
return useContext(DetailsBlockContext);
|
return useContext(DetailsBlockContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 即将废弃,请用 useDetailsWithPaginationProps 或者 useDetailsProps
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export const useDetailsBlockProps = () => {
|
export const useDetailsBlockProps = () => {
|
||||||
const ctx = useDetailsBlockContext();
|
const ctx = useDetailsBlockContext();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -2,8 +2,9 @@ import React from 'react';
|
|||||||
import { DatePickerProvider } from '../schema-component';
|
import { DatePickerProvider } from '../schema-component';
|
||||||
import { DefaultValueProvider } from '../schema-settings';
|
import { DefaultValueProvider } from '../schema-settings';
|
||||||
import { FormBlockProvider } from './FormBlockProvider';
|
import { FormBlockProvider } from './FormBlockProvider';
|
||||||
|
import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const FilterFormBlockProvider = (props) => {
|
export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
return (
|
return (
|
||||||
<DatePickerProvider value={{ utc: false }}>
|
<DatePickerProvider value={{ utc: false }}>
|
||||||
<DefaultValueProvider isAllowToSetDefaultValue={() => false}>
|
<DefaultValueProvider isAllowToSetDefaultValue={() => false}>
|
||||||
@ -11,4 +12,4 @@ export const FilterFormBlockProvider = (props) => {
|
|||||||
</DefaultValueProvider>
|
</DefaultValueProvider>
|
||||||
</DatePickerProvider>
|
</DatePickerProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -6,4 +6,5 @@ export * from './FormFieldProvider';
|
|||||||
export * from './TableBlockProvider';
|
export * from './TableBlockProvider';
|
||||||
export * from './TableFieldProvider';
|
export * from './TableFieldProvider';
|
||||||
export * from './TableSelectorProvider';
|
export * from './TableSelectorProvider';
|
||||||
|
export * from './DetailsBlockProvider';
|
||||||
export * from './hooks';
|
export * from './hooks';
|
||||||
|
@ -17,7 +17,7 @@ export class UUIDFieldInterface extends CollectionFieldInterface {
|
|||||||
'x-validator': 'uuid',
|
'x-validator': 'uuid',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
availableTypes = ['string', 'uid'];
|
availableTypes = ['string', 'uid', 'uuid'];
|
||||||
properties = {
|
properties = {
|
||||||
'uiSchema.title': {
|
'uiSchema.title': {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -110,7 +110,7 @@ export class InheritanceCollectionMixin extends Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// override Collection
|
// override Collection
|
||||||
getFieldsMap() {
|
protected getFieldsMap() {
|
||||||
if (this.fieldsMap) {
|
if (this.fieldsMap) {
|
||||||
return this.fieldsMap;
|
return this.fieldsMap;
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,7 @@ import { Field } from '@formily/core';
|
|||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { useIsFileField } from '../schema-component/antd/form-item/FormItem.Settings';
|
import { useIsFileField } from '../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useColumnSchema } from '../schema-component/antd/table-v2/Table.Column.Decorator';
|
import { useColumnSchema } from '../schema-component/antd/table-v2/Table.Column.Decorator';
|
||||||
import { useCollectionManager_deprecated, useCollection_deprecated } from '../collection-manager';
|
import { useCollectionField } from '../data-source';
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取字段相关的配置信息
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function useCollectionField() {
|
|
||||||
const { getCollectionJoinField } = useCollectionManager_deprecated();
|
|
||||||
const { getField } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
|
|
||||||
return collectionField;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useFieldComponentName(): string {
|
export function useFieldComponentName(): string {
|
||||||
const { fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema();
|
const { fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema();
|
||||||
|
@ -13,7 +13,10 @@ type Props = {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: 初步适配
|
/**
|
||||||
|
* TODO: 初步适配
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const CollectionFieldInternalField: React.FC = (props: Props) => {
|
export const CollectionFieldInternalField: React.FC = (props: Props) => {
|
||||||
const { component } = props;
|
const { component } = props;
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
@ -22,7 +22,7 @@ export const CollectionFieldProvider: FC<CollectionFieldProviderProps> = (props)
|
|||||||
const collectionManager = useCollectionManager();
|
const collectionManager = useCollectionManager();
|
||||||
|
|
||||||
const value = useMemo(() => {
|
const value = useMemo(() => {
|
||||||
if (!collection || allowNull) return null;
|
if (!collection) return null;
|
||||||
const field = fieldSchema?.['x-component-props']?.['field'];
|
const field = fieldSchema?.['x-component-props']?.['field'];
|
||||||
return (
|
return (
|
||||||
collectionManager.getCollectionField(fieldSchema?.['x-collection-field']) ||
|
collectionManager.getCollectionField(fieldSchema?.['x-collection-field']) ||
|
||||||
@ -31,7 +31,7 @@ export const CollectionFieldProvider: FC<CollectionFieldProviderProps> = (props)
|
|||||||
);
|
);
|
||||||
}, [collection, fieldSchema, name, collectionManager]);
|
}, [collection, fieldSchema, name, collectionManager]);
|
||||||
|
|
||||||
if (!collection || allowNull) {
|
if (!value && allowNull) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,8 @@ export class Collection {
|
|||||||
getFields(predicate?: GetCollectionFieldPredicate) {
|
getFields(predicate?: GetCollectionFieldPredicate) {
|
||||||
return predicate ? filter(this.fields, predicate) : this.fields;
|
return predicate ? filter(this.fields, predicate) : this.fields;
|
||||||
}
|
}
|
||||||
getFieldsMap() {
|
|
||||||
|
protected getFieldsMap() {
|
||||||
if (!this.fieldsMap) {
|
if (!this.fieldsMap) {
|
||||||
this.fieldsMap = this.getFields().reduce((memo, field) => {
|
this.fieldsMap = this.getFields().reduce((memo, field) => {
|
||||||
memo[field.name] = field;
|
memo[field.name] = field;
|
||||||
|
@ -131,6 +131,9 @@ export class CollectionManager {
|
|||||||
return this.getCollection(collectionName)?.getFields(predicate) || [];
|
return this.getCollection(collectionName)?.getFields(predicate) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
clone(collections: CollectionOptions[] = []) {
|
clone(collections: CollectionOptions[] = []) {
|
||||||
const collectionManager = new CollectionManager([], this.dataSource);
|
const collectionManager = new CollectionManager([], this.dataSource);
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import type { Application } from '../../application/Application';
|
import type { Application } from '../../application/Application';
|
||||||
import type { CollectionOptions } from './Collection';
|
import type { CollectionOptions } from './Collection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const collectionTransform = (collection: CollectionOptions, app: Application) => {
|
export const collectionTransform = (collection: CollectionOptions, app: Application) => {
|
||||||
const { rawTitle, title, fields = [], ...rest } = collection;
|
const { rawTitle, title, fields = [], ...rest } = collection;
|
||||||
return {
|
return {
|
||||||
@ -26,6 +29,9 @@ export const collectionTransform = (collection: CollectionOptions, app: Applicat
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export function applyMixins(Cls: any, mixins: any[], options?: any, collectionManager?: any) {
|
export function applyMixins(Cls: any, mixins: any[], options?: any, collectionManager?: any) {
|
||||||
const instance = new Cls(options, collectionManager);
|
const instance = new Cls(options, collectionManager);
|
||||||
mixins.forEach((MixinClass) => {
|
mixins.forEach((MixinClass) => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { App, Button, Result, Typography } from 'antd';
|
import { App, Button, Result, Typography } from 'antd';
|
||||||
import React, { FC, useMemo } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CardItem, useCompile, useDesignable } from '../../schema-component';
|
import { CardItem, EllipsisWithTooltip, useCompile, useDesignable } from '../../schema-component';
|
||||||
import { useDataSource } from '../data-source/DataSourceProvider';
|
import { useDataSource } from '../data-source/DataSourceProvider';
|
||||||
import { useDataSourceManager } from '../data-source';
|
import { useDataSourceManager } from '../data-source';
|
||||||
import { DEFAULT_DATA_SOURCE_KEY } from '../../data-source/data-source/DataSourceManager';
|
import { DEFAULT_DATA_SOURCE_KEY } from '../../data-source/data-source/DataSourceManager';
|
||||||
@ -15,6 +15,9 @@ export interface CollectionDeletedPlaceholderProps {
|
|||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const CollectionDeletedPlaceholder: FC<CollectionDeletedPlaceholderProps> = ({ type, name, message }) => {
|
export const CollectionDeletedPlaceholder: FC<CollectionDeletedPlaceholderProps> = ({ type, name, message }) => {
|
||||||
const { designable, dn } = useDesignable();
|
const { designable, dn } = useDesignable();
|
||||||
const { modal } = App.useApp();
|
const { modal } = App.useApp();
|
||||||
@ -63,7 +66,11 @@ export const CollectionDeletedPlaceholder: FC<CollectionDeletedPlaceholderProps>
|
|||||||
|
|
||||||
if (designable) {
|
if (designable) {
|
||||||
if (type === 'Field') {
|
if (type === 'Field') {
|
||||||
return <Text type="secondary">{messageValue}</Text>;
|
return (
|
||||||
|
<EllipsisWithTooltip ellipsis>
|
||||||
|
<Text type="secondary">{messageValue}</Text>
|
||||||
|
</EllipsisWithTooltip>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,9 @@ interface DataSourceApplicationProviderProps extends CollectionManagerProviderPr
|
|||||||
dataSourceManager: DataSourceManager;
|
dataSourceManager: DataSourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const DataSourceApplicationProvider: FC<DataSourceApplicationProviderProps> = ({
|
export const DataSourceApplicationProvider: FC<DataSourceApplicationProviderProps> = ({
|
||||||
children,
|
children,
|
||||||
dataSourceManager,
|
dataSourceManager,
|
||||||
|
@ -103,6 +103,9 @@ export interface DataBlockContextValue<T extends {} = {}> {
|
|||||||
export const DataBlockContext = createContext<DataBlockContextValue<any>>({} as any);
|
export const DataBlockContext = createContext<DataBlockContextValue<any>>({} as any);
|
||||||
DataBlockContext.displayName = 'DataBlockContext';
|
DataBlockContext.displayName = 'DataBlockContext';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export const AssociationOrCollectionProvider = (props: {
|
export const AssociationOrCollectionProvider = (props: {
|
||||||
collection: string | CollectionOptions;
|
collection: string | CollectionOptions;
|
||||||
association: string;
|
association: string;
|
||||||
|
@ -7,6 +7,7 @@ import { AllDataBlockProps, useDataBlockProps } from './DataBlockProvider';
|
|||||||
import { useDataBlockResource } from './DataBlockResourceProvider';
|
import { useDataBlockResource } from './DataBlockResourceProvider';
|
||||||
import { useDataSourceHeaders } from '../utils';
|
import { useDataSourceHeaders } from '../utils';
|
||||||
import { useDataLoadingMode } from '../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem';
|
import { useDataLoadingMode } from '../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem';
|
||||||
|
import { useCollection, useCollectionManager } from '../collection';
|
||||||
|
|
||||||
export const BlockRequestContext = createContext<UseRequestResult<any>>(null);
|
export const BlockRequestContext = createContext<UseRequestResult<any>>(null);
|
||||||
BlockRequestContext.displayName = 'BlockRequestContext';
|
BlockRequestContext.displayName = 'BlockRequestContext';
|
||||||
@ -55,6 +56,7 @@ function useCurrentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
|
|||||||
function useParentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
|
function useParentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
|
||||||
const { sourceId, association, parentRecord } = options;
|
const { sourceId, association, parentRecord } = options;
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
|
const cm = useCollectionManager();
|
||||||
const dataBlockProps = useDataBlockProps();
|
const dataBlockProps = useDataBlockProps();
|
||||||
const headers = useDataSourceHeaders(dataBlockProps.dataSource);
|
const headers = useDataSourceHeaders(dataBlockProps.dataSource);
|
||||||
return useRequest<T>(
|
return useRequest<T>(
|
||||||
@ -63,8 +65,12 @@ function useParentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
|
|||||||
if (!association) return Promise.resolve({ data: undefined });
|
if (!association) return Promise.resolve({ data: undefined });
|
||||||
// "association": "Collection.Field"
|
// "association": "Collection.Field"
|
||||||
const arr = association.split('.');
|
const arr = association.split('.');
|
||||||
// <collection>:get/<filterByTk>
|
const field = cm.getCollectionField(association);
|
||||||
const url = `${arr[0]}:get/${sourceId}`;
|
const isM2O = field.interface === 'm2o';
|
||||||
|
const filterTargetKey = cm.getCollection(arr[0]).getOption('filterTargetKey');
|
||||||
|
const filterKey = isM2O ? filterTargetKey : field.sourceKey;
|
||||||
|
// <collection>:get?filter[filterKey]=sourceId
|
||||||
|
const url = `${arr[0]}:get?filter[${filterKey}]=${sourceId}`;
|
||||||
const res = await api.request({ url, headers });
|
const res = await api.request({ url, headers });
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
@ -88,6 +94,7 @@ export const BlockRequestProvider: FC = ({ children }) => {
|
|||||||
requestOptions,
|
requestOptions,
|
||||||
requestService,
|
requestService,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const currentRequest = useCurrentRequest<{ data: any }>({
|
const currentRequest = useCurrentRequest<{ data: any }>({
|
||||||
action,
|
action,
|
||||||
sourceId,
|
sourceId,
|
||||||
|
@ -83,6 +83,9 @@ export abstract class DataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
export class LocalDataSource extends DataSource {
|
export class LocalDataSource extends DataSource {
|
||||||
getDataSource() {
|
getDataSource() {
|
||||||
return {
|
return {
|
||||||
|
@ -53,6 +53,10 @@ export const detailsActionInitializers_deprecated = new CompatibleSchemaInitiali
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 已弃用,请使用 readPrettyFormActionInitializers 代替
|
||||||
|
*/
|
||||||
export const detailsActionInitializers = new CompatibleSchemaInitializer(
|
export const detailsActionInitializers = new CompatibleSchemaInitializer(
|
||||||
{
|
{
|
||||||
name: 'detailsWithPaging:configureActions',
|
name: 'detailsWithPaging:configureActions',
|
||||||
|
@ -2,9 +2,9 @@ import { TableOutlined } from '@ant-design/icons';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||||
import { createDetailsBlockSchema } from '../../../../schema-initializer/utils';
|
|
||||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||||
|
import { createDetailsWithPaginationUISchema } from './createDetailsWithPaginationUISchema';
|
||||||
|
|
||||||
export const DetailsBlockInitializer = ({
|
export const DetailsBlockInitializer = ({
|
||||||
filterCollections,
|
filterCollections,
|
||||||
@ -51,15 +51,14 @@ export const DetailsBlockInitializer = ({
|
|||||||
|
|
||||||
const { item } = options;
|
const { item } = options;
|
||||||
const collection = getCollection(item.name, item.dataSource);
|
const collection = getCollection(item.name, item.dataSource);
|
||||||
const schema = createDetailsBlockSchema({
|
const schema = createDetailsWithPaginationUISchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
actionInitializers:
|
hideActionInitializer: !(
|
||||||
(collection.template !== 'view' || collection?.writableView) &&
|
(collection.template !== 'view' || collection?.writableView) &&
|
||||||
collection.template !== 'sql' &&
|
collection.template !== 'sql'
|
||||||
'detailsWithPaging:configureActions',
|
),
|
||||||
settings: 'blockSettings:multiDataDetails',
|
|
||||||
});
|
});
|
||||||
insert(schema);
|
insert(schema);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { createDetailsWithPaginationUISchema } from '../createDetailsWithPaginationUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => ({
|
||||||
|
uid: vi.fn().mockReturnValue('mocked-uid'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('createDetailsBlockWithPaginationUISchema', () => {
|
||||||
|
it('should return the correct schema', () => {
|
||||||
|
const schema = createDetailsWithPaginationUISchema({
|
||||||
|
collectionName: 'users',
|
||||||
|
dataSource: 'users',
|
||||||
|
rowKey: 'id',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "details:configureFields",
|
||||||
|
},
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "details:configureActions",
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Pagination",
|
||||||
|
"x-use-component-props": "useDetailsPaginationProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Details",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useDetailsWithPaginationProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "users:view",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "DetailsBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"association": undefined,
|
||||||
|
"collection": "users",
|
||||||
|
"dataSource": "users",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 1,
|
||||||
|
},
|
||||||
|
"readPretty": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:detailsWithPagination",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useDetailsWithPaginationDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,70 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export function createDetailsWithPaginationUISchema(options: {
|
||||||
|
dataSource: string;
|
||||||
|
rowKey: string;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
hideActionInitializer?: boolean;
|
||||||
|
}): ISchema {
|
||||||
|
const { collectionName, dataSource, association, templateSchema, hideActionInitializer, rowKey } = options;
|
||||||
|
const resourceName = association || collectionName;
|
||||||
|
|
||||||
|
if (!dataSource || !rowKey) {
|
||||||
|
throw new Error('dataSource and rowKey are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${resourceName}:view`,
|
||||||
|
'x-decorator': 'DetailsBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useDetailsWithPaginationDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
dataSource,
|
||||||
|
collection: collectionName,
|
||||||
|
association,
|
||||||
|
readPretty: true,
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:detailsWithPagination',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Details',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useDetailsWithPaginationProps',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': hideActionInitializer ? undefined : 'details:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Pagination',
|
||||||
|
'x-use-component-props': 'useDetailsPaginationProps',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
import { ArrayItems } from '@formily/antd-v5';
|
||||||
|
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
||||||
|
import { useFormBlockContext } from '../../../../block-provider';
|
||||||
|
import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider';
|
||||||
|
import { useCollection_deprecated, useSortFields } from '../../../../collection-manager';
|
||||||
|
import { removeNullCondition, useDesignable } from '../../../../schema-component';
|
||||||
|
import { SchemaSettingsBlockTitleItem, SchemaSettingsTemplate } from '../../../../schema-settings';
|
||||||
|
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
|
||||||
|
import { setDataLoadingModeSettingsItem, useDataLoadingMode } from './setDataLoadingModeSettingsItem';
|
||||||
|
import { SchemaSettingsItemType } from '../../../../application/schema-settings/types';
|
||||||
|
|
||||||
|
const commonItems: SchemaSettingsItemType[] = [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dataScope',
|
||||||
|
Component: SchemaSettingsDataScope,
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { form } = useFormBlockContext();
|
||||||
|
const field = useField();
|
||||||
|
const { service } = useDetailsBlockContext();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
const dataLoadingMode = useDataLoadingMode();
|
||||||
|
return {
|
||||||
|
collectionName: name,
|
||||||
|
defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {},
|
||||||
|
form,
|
||||||
|
onSubmit: ({ filter }) => {
|
||||||
|
filter = removeNullCondition(filter);
|
||||||
|
const params = field.decoratorProps.params || {};
|
||||||
|
params.filter = filter;
|
||||||
|
field.decoratorProps.params = params;
|
||||||
|
fieldSchema['x-decorator-props']['params'] = params;
|
||||||
|
|
||||||
|
if (dataLoadingMode === 'auto') {
|
||||||
|
service.run({ ...service.params?.[0], filter });
|
||||||
|
}
|
||||||
|
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sortingRules',
|
||||||
|
type: 'modal',
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = useField();
|
||||||
|
const { service } = useDetailsBlockContext();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
const sortFields = useSortFields(name);
|
||||||
|
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||||
|
const sort = defaultSort?.map((item: string) => {
|
||||||
|
return item.startsWith('-')
|
||||||
|
? {
|
||||||
|
field: item.substring(1),
|
||||||
|
direction: 'desc',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
field: item,
|
||||||
|
direction: 'asc',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
title: t('Set default sorting rules'),
|
||||||
|
components: {
|
||||||
|
ArrayItems,
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
title: t('Set default sorting rules'),
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'array',
|
||||||
|
default: sort,
|
||||||
|
'x-component': 'ArrayItems',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
space: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Space',
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayItems.SortHandle',
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
type: 'string',
|
||||||
|
enum: sortFields,
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
width: 260,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Radio.Group',
|
||||||
|
'x-component-props': {
|
||||||
|
optionType: 'button',
|
||||||
|
},
|
||||||
|
enum: [
|
||||||
|
{
|
||||||
|
label: t('ASC'),
|
||||||
|
value: 'asc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('DESC'),
|
||||||
|
value: 'desc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayItems.Remove',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: t('Add sort field'),
|
||||||
|
'x-component': 'ArrayItems.Addition',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ISchema,
|
||||||
|
onSubmit({ sort }) {
|
||||||
|
const sortArr = sort.map((item) => {
|
||||||
|
return item.direction === 'desc' ? `-${item.field}` : item.field;
|
||||||
|
});
|
||||||
|
const params = field.decoratorProps.params || {};
|
||||||
|
params.sort = sortArr;
|
||||||
|
field.decoratorProps.params = params;
|
||||||
|
fieldSchema['x-decorator-props']['params'] = params;
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
service.run({ ...service.params?.[0], sort: sortArr });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setDataLoadingModeSettingsItem,
|
||||||
|
{
|
||||||
|
name: 'template',
|
||||||
|
Component: SchemaSettingsTemplate,
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const defaultResource =
|
||||||
|
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
||||||
|
return {
|
||||||
|
componentName: 'Details',
|
||||||
|
collectionName: name,
|
||||||
|
resourceName: defaultResource,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
breakRemoveOn: {
|
||||||
|
'x-component': 'Grid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 已弃用,请使用 detailsWithPaginationSettings 代替
|
||||||
|
*/
|
||||||
|
export const multiDataDetailsBlockSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:multiDataDetails',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const detailsWithPaginationSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:detailsWithPagination',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
|
||||||
|
export function useDetailsWithPaginationDecoratorProps(props) {
|
||||||
|
let sourceId;
|
||||||
|
|
||||||
|
// association 的值是固定不变的,所以可以在条件中使用 hooks
|
||||||
|
if (props.association) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
sourceId = useDataBlockSourceId({ association: props.association });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useDetailsBlockProps } from '../../../../../block-provider/DetailsBlockProvider';
|
||||||
|
|
||||||
|
export function useDetailsWithPaginationProps() {
|
||||||
|
return useDetailsBlockProps();
|
||||||
|
}
|
@ -1,204 +0,0 @@
|
|||||||
import { ArrayItems } from '@formily/antd-v5';
|
|
||||||
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
|
||||||
import { useFormBlockContext } from '../../../../block-provider';
|
|
||||||
import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider';
|
|
||||||
import { useCollection_deprecated, useSortFields } from '../../../../collection-manager';
|
|
||||||
import { removeNullCondition, useDesignable } from '../../../../schema-component';
|
|
||||||
import { SchemaSettingsBlockTitleItem, SchemaSettingsTemplate } from '../../../../schema-settings';
|
|
||||||
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
|
|
||||||
import { setDataLoadingModeSettingsItem, useDataLoadingMode } from './setDataLoadingModeSettingsItem';
|
|
||||||
|
|
||||||
export const multiDataDetailsBlockSettings = new SchemaSettings({
|
|
||||||
name: 'blockSettings:multiDataDetails',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
Component: SchemaSettingsBlockTitleItem,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dataScope',
|
|
||||||
Component: SchemaSettingsDataScope,
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { form } = useFormBlockContext();
|
|
||||||
const field = useField();
|
|
||||||
const { service } = useDetailsBlockContext();
|
|
||||||
const { dn } = useDesignable();
|
|
||||||
const dataLoadingMode = useDataLoadingMode();
|
|
||||||
return {
|
|
||||||
collectionName: name,
|
|
||||||
defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {},
|
|
||||||
form,
|
|
||||||
onSubmit: ({ filter }) => {
|
|
||||||
filter = removeNullCondition(filter);
|
|
||||||
const params = field.decoratorProps.params || {};
|
|
||||||
params.filter = filter;
|
|
||||||
field.decoratorProps.params = params;
|
|
||||||
fieldSchema['x-decorator-props']['params'] = params;
|
|
||||||
|
|
||||||
if (dataLoadingMode === 'auto') {
|
|
||||||
service.run({ ...service.params?.[0], filter });
|
|
||||||
}
|
|
||||||
|
|
||||||
dn.emit('patch', {
|
|
||||||
schema: {
|
|
||||||
['x-uid']: fieldSchema['x-uid'],
|
|
||||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sortingRules',
|
|
||||||
type: 'modal',
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const field = useField();
|
|
||||||
const { service } = useDetailsBlockContext();
|
|
||||||
const { dn } = useDesignable();
|
|
||||||
const sortFields = useSortFields(name);
|
|
||||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
|
||||||
const sort = defaultSort?.map((item: string) => {
|
|
||||||
return item.startsWith('-')
|
|
||||||
? {
|
|
||||||
field: item.substring(1),
|
|
||||||
direction: 'desc',
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
field: item,
|
|
||||||
direction: 'asc',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
title: t('Set default sorting rules'),
|
|
||||||
components: {
|
|
||||||
ArrayItems,
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
type: 'object',
|
|
||||||
title: t('Set default sorting rules'),
|
|
||||||
properties: {
|
|
||||||
sort: {
|
|
||||||
type: 'array',
|
|
||||||
default: sort,
|
|
||||||
'x-component': 'ArrayItems',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
space: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Space',
|
|
||||||
properties: {
|
|
||||||
sort: {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'ArrayItems.SortHandle',
|
|
||||||
},
|
|
||||||
field: {
|
|
||||||
type: 'string',
|
|
||||||
enum: sortFields,
|
|
||||||
required: true,
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Select',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
width: 260,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
direction: {
|
|
||||||
type: 'string',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Radio.Group',
|
|
||||||
'x-component-props': {
|
|
||||||
optionType: 'button',
|
|
||||||
},
|
|
||||||
enum: [
|
|
||||||
{
|
|
||||||
label: t('ASC'),
|
|
||||||
value: 'asc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('DESC'),
|
|
||||||
value: 'desc',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
remove: {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'ArrayItems.Remove',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
add: {
|
|
||||||
type: 'void',
|
|
||||||
title: t('Add sort field'),
|
|
||||||
'x-component': 'ArrayItems.Addition',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as ISchema,
|
|
||||||
onSubmit({ sort }) {
|
|
||||||
const sortArr = sort.map((item) => {
|
|
||||||
return item.direction === 'desc' ? `-${item.field}` : item.field;
|
|
||||||
});
|
|
||||||
const params = field.decoratorProps.params || {};
|
|
||||||
params.sort = sortArr;
|
|
||||||
field.decoratorProps.params = params;
|
|
||||||
fieldSchema['x-decorator-props']['params'] = params;
|
|
||||||
dn.emit('patch', {
|
|
||||||
schema: {
|
|
||||||
['x-uid']: fieldSchema['x-uid'],
|
|
||||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
service.run({ ...service.params?.[0], sort: sortArr });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setDataLoadingModeSettingsItem,
|
|
||||||
{
|
|
||||||
name: 'template',
|
|
||||||
Component: SchemaSettingsTemplate,
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const defaultResource =
|
|
||||||
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
|
||||||
return {
|
|
||||||
componentName: 'Details',
|
|
||||||
collectionName: name,
|
|
||||||
resourceName: defaultResource,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'divider',
|
|
||||||
type: 'divider',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'remove',
|
|
||||||
type: 'remove',
|
|
||||||
componentProps: {
|
|
||||||
removeParentsIfNoChildren: true,
|
|
||||||
breakRemoveOn: {
|
|
||||||
'x-component': 'Grid',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
@ -47,6 +47,7 @@ const AssociatedFields = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
|
* 已弃用,请使用 readPrettyFormItemInitializers 代替
|
||||||
*/
|
*/
|
||||||
export const readPrettyFormItemInitializers_deprecated = new CompatibleSchemaInitializer({
|
export const readPrettyFormItemInitializers_deprecated = new CompatibleSchemaInitializer({
|
||||||
name: 'ReadPrettyFormItemInitializers',
|
name: 'ReadPrettyFormItemInitializers',
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { FormOutlined } from '@ant-design/icons';
|
import { FormOutlined } from '@ant-design/icons';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider';
|
import { useBlockAssociationContext } from '../../../../block-provider';
|
||||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||||
import { useSchemaTemplateManager } from '../../../../schema-templates';
|
import { useSchemaTemplateManager } from '../../../../schema-templates';
|
||||||
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
|
import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
|
||||||
|
import { createDetailsUISchema } from './createDetailsUISchema';
|
||||||
|
|
||||||
export const RecordReadPrettyFormBlockInitializer = () => {
|
export const RecordReadPrettyFormBlockInitializer = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -24,29 +25,27 @@ export const RecordReadPrettyFormBlockInitializer = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function useCreateSingleDetailsSchema() {
|
export function useCreateSingleDetailsSchema() {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||||
const association = useBlockAssociationContext();
|
const association = useBlockAssociationContext();
|
||||||
const { block } = useBlockRequestContext();
|
|
||||||
const actionInitializers =
|
|
||||||
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
|
|
||||||
|
|
||||||
const templateWrap = useCallback(
|
const templateWrap = useCallback(
|
||||||
(templateSchema, options) => {
|
(templateSchema, options) => {
|
||||||
const { item } = options;
|
const { item } = options;
|
||||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||||
const blockSchema = createDetailsBlockSchema({
|
const blockSchema = createDetailsUISchema(
|
||||||
actionInitializers,
|
association
|
||||||
|
? {
|
||||||
association,
|
association,
|
||||||
collection: item.collectionName || item.name,
|
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
action: 'get',
|
templateSchema: templateSchema,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
}
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
: {
|
||||||
template: templateSchema,
|
collectionName: item.collectionName || item.name,
|
||||||
settings: 'blockSettings:singleDataDetails',
|
dataSource: item.dataSource,
|
||||||
});
|
templateSchema: templateSchema,
|
||||||
|
},
|
||||||
|
);
|
||||||
if (item.mode === 'reference') {
|
if (item.mode === 'reference') {
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
}
|
}
|
||||||
@ -55,7 +54,7 @@ export function useCreateSingleDetailsSchema() {
|
|||||||
return templateSchema;
|
return templateSchema;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actionInitializers, association],
|
[association],
|
||||||
);
|
);
|
||||||
|
|
||||||
const createSingleDetailsSchema = useCallback(
|
const createSingleDetailsSchema = useCallback(
|
||||||
@ -65,20 +64,21 @@ export function useCreateSingleDetailsSchema() {
|
|||||||
insert(templateWrap(template, { item }));
|
insert(templateWrap(template, { item }));
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsUISchema(
|
||||||
actionInitializers,
|
association
|
||||||
|
? {
|
||||||
association,
|
association,
|
||||||
collection: item.collectionName || item.name,
|
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
action: 'get',
|
}
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
: {
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
collectionName: item.collectionName || item.name,
|
||||||
settings: 'blockSettings:singleDataDetails',
|
dataSource: item.dataSource,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actionInitializers, association, getTemplateSchemaByMode, insert, templateWrap],
|
[association, getTemplateSchemaByMode, insert, templateWrap],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { createSingleDetailsSchema, templateWrap };
|
return { createSingleDetailsSchema, templateWrap };
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
import { createDetailsUISchema } from '../createDetailsUISchema';
|
||||||
|
|
||||||
|
// Mock uid to always return a fixed value
|
||||||
|
vi.mock('@formily/shared', () => ({
|
||||||
|
uid: () => 'fixed-uid',
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('createDetailsBlockWithoutPagingUISchema', () => {
|
||||||
|
it('should create a valid schema with mandatory fields', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'users',
|
||||||
|
dataSource: 'usersDataSource',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = createDetailsUISchema(options);
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"fixed-uid": {
|
||||||
|
"properties": {
|
||||||
|
"fixed-uid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "details:configureActions",
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "details:configureFields",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Details",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useDetailsProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "users:get",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "DetailsBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "get",
|
||||||
|
"association": undefined,
|
||||||
|
"collection": "users",
|
||||||
|
"dataSource": "usersDataSource",
|
||||||
|
"readPretty": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:details",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useDetailsDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,60 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export function createDetailsUISchema(options: {
|
||||||
|
dataSource: string;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
}): ISchema {
|
||||||
|
const { collectionName, dataSource, association, templateSchema } = options;
|
||||||
|
const resourceName = association || collectionName;
|
||||||
|
|
||||||
|
if (!dataSource) {
|
||||||
|
throw new Error('dataSource are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${resourceName}:get`,
|
||||||
|
'x-decorator': 'DetailsBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useDetailsDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
dataSource,
|
||||||
|
collection: collectionName,
|
||||||
|
association,
|
||||||
|
readPretty: true,
|
||||||
|
action: 'get',
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:details',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Details',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useDetailsProps',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'details:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
||||||
|
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||||
|
import { SchemaSettingsBlockTitleItem, SchemaSettingsFormItemTemplate } from '../../../../schema-settings';
|
||||||
|
import { SchemaSettingsItemType } from '../../../../application/schema-settings/types';
|
||||||
|
|
||||||
|
const commonItems: SchemaSettingsItemType[] = [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'formItemTemplate',
|
||||||
|
Component: SchemaSettingsFormItemTemplate,
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const defaultResource =
|
||||||
|
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
||||||
|
return {
|
||||||
|
insertAdjacentPosition: 'beforeEnd',
|
||||||
|
componentName: 'ReadPrettyFormItem',
|
||||||
|
collectionName: name,
|
||||||
|
resourceName: defaultResource,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
breakRemoveOn: {
|
||||||
|
'x-component': 'Grid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 已弃用,请使用 detailsBlockSettings 代替
|
||||||
|
*/
|
||||||
|
export const singleDataDetailsBlockSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:singleDataDetails',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const detailsBlockSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:details',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider';
|
||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
|
||||||
|
export function useDetailsDecoratorProps(props) {
|
||||||
|
const params = useParamsFromRecord();
|
||||||
|
let sourceId;
|
||||||
|
|
||||||
|
// association 的值是固定不变的,所以可以在条件中使用 hooks
|
||||||
|
if (props.association) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
sourceId = useDataBlockSourceId({ association: props.association });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useDetailsBlockProps } from '../../../../../block-provider/DetailsBlockProvider';
|
||||||
|
|
||||||
|
export function useDetailsProps() {
|
||||||
|
return useDetailsBlockProps();
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
import { useFieldSchema } from '@formily/react';
|
|
||||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
|
||||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
|
||||||
import { SchemaSettingsBlockTitleItem, SchemaSettingsFormItemTemplate } from '../../../../schema-settings';
|
|
||||||
|
|
||||||
export const singleDataDetailsBlockSettings = new SchemaSettings({
|
|
||||||
name: 'blockSettings:singleDataDetails',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
Component: SchemaSettingsBlockTitleItem,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'formItemTemplate',
|
|
||||||
Component: SchemaSettingsFormItemTemplate,
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const defaultResource =
|
|
||||||
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
|
||||||
return {
|
|
||||||
insertAdjacentPosition: 'beforeEnd',
|
|
||||||
componentName: 'ReadPrettyFormItem',
|
|
||||||
collectionName: name,
|
|
||||||
resourceName: defaultResource,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'divider',
|
|
||||||
type: 'divider',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'remove',
|
|
||||||
type: 'remove',
|
|
||||||
componentProps: {
|
|
||||||
removeParentsIfNoChildren: true,
|
|
||||||
breakRemoveOn: {
|
|
||||||
'x-component': 'Grid',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
@ -9,7 +9,7 @@ import {
|
|||||||
oneTableBlockWithAddNewAndViewAndEditAndBasicFields,
|
oneTableBlockWithAddNewAndViewAndEditAndBasicFields,
|
||||||
test,
|
test,
|
||||||
} from '@nocobase/test/e2e';
|
} from '@nocobase/test/e2e';
|
||||||
import { T2165, T2174, T3251 } from './templatesOfBug';
|
import { T2165, T2174, T3251, T3806 } from './templatesOfBug';
|
||||||
|
|
||||||
const clickOption = async (page: Page, optionName: string) => {
|
const clickOption = async (page: Page, optionName: string) => {
|
||||||
await page.getByLabel('block-item-CardItem-general-form').hover();
|
await page.getByLabel('block-item-CardItem-general-form').hover();
|
||||||
@ -254,6 +254,47 @@ test.describe('creation form block schema settings', () => {
|
|||||||
page.getByLabel('block-item-CollectionField-users-form-users.email-Email').getByRole('textbox'),
|
page.getByLabel('block-item-CollectionField-users-form-users.email-Email').getByRole('textbox'),
|
||||||
).toBeEditable();
|
).toBeEditable();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://nocobase.height.app/T-3806
|
||||||
|
test('after save as block template', async ({ page, mockPage }) => {
|
||||||
|
await mockPage(T3806).goto();
|
||||||
|
|
||||||
|
// 1. 一开始联动规则应该正常
|
||||||
|
await page
|
||||||
|
.getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname')
|
||||||
|
.getByRole('textbox')
|
||||||
|
.fill('123');
|
||||||
|
await expect(
|
||||||
|
page.getByLabel('block-item-CollectionField-users-form-users.username-Username').getByRole('textbox'),
|
||||||
|
).toHaveValue('123');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. 将表单区块保存为模板后
|
||||||
|
await page.getByLabel('block-item-CardItem-users-form').hover();
|
||||||
|
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover();
|
||||||
|
await page.getByRole('menuitem', { name: 'Save as block template' }).click();
|
||||||
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
// 3. 联动规则应该依然是正常的
|
||||||
|
await page
|
||||||
|
.getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname')
|
||||||
|
.getByRole('textbox')
|
||||||
|
.fill('456');
|
||||||
|
await expect(
|
||||||
|
page.getByLabel('block-item-CollectionField-users-form-users.username-Username').getByRole('textbox'),
|
||||||
|
).toHaveValue('456');
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
// 4. 把创建的模板删除
|
||||||
|
await page.goto('/admin/settings/ui-schema-storage');
|
||||||
|
await page.getByLabel('Select all').check();
|
||||||
|
await page.getByLabel('action-Action-Delete-destroy-').click();
|
||||||
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
|
await expect(page.getByRole('row', { name: 'Users_Form' }).first()).toBeHidden();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Save as block template & convert reference to duplicate', async ({ page, mockPage }) => {
|
test('Save as block template & convert reference to duplicate', async ({ page, mockPage }) => {
|
||||||
|
@ -6066,3 +6066,204 @@ export const T3529: PageConfig = {
|
|||||||
'x-index': 1,
|
'x-index': 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const T3806: PageConfig = {
|
||||||
|
pageSchema: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Page',
|
||||||
|
properties: {
|
||||||
|
'6w8tkq3665g': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'page:addBlock',
|
||||||
|
properties: {
|
||||||
|
'0k0yifwylj8': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
v5cdqnvyh3z: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
p9pszqbz1n5: {
|
||||||
|
_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-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:createForm',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
'578cg3ao40s': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'FormV2',
|
||||||
|
'x-use-component-props': 'useCreateFormBlockProps',
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
'x-uid': 'xpdqjz634mt',
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'form:configureFields',
|
||||||
|
'x-linkage-rules': [
|
||||||
|
{
|
||||||
|
condition: {
|
||||||
|
$and: [],
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
targetFields: ['username'],
|
||||||
|
operator: 'value',
|
||||||
|
value: {
|
||||||
|
mode: 'express',
|
||||||
|
value: '{{$nForm.nickname}}',
|
||||||
|
result: '{{$nForm.nickname}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: {
|
||||||
|
gjck905cyws: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
i041myizzph: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
nickname: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'string',
|
||||||
|
'x-toolbar': 'FormItemSchemaToolbar',
|
||||||
|
'x-settings': 'fieldSettings:FormItem',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'users.nickname',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-uid': 'afnfoxl5cbi',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'a3o7l08zz6e',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'zoakp82dymw',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
qffoo7lqua1: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
nq0rbuti1d9: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
username: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'string',
|
||||||
|
'x-toolbar': 'FormItemSchemaToolbar',
|
||||||
|
'x-settings': 'fieldSettings:FormItem',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'users.username',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-uid': 'mq80n8nyuq8',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'hf9bq4vjm80',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'ua08izsl514',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
'006ip4mnr75': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'createForm:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
layout: 'one-column',
|
||||||
|
style: {
|
||||||
|
marginTop: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'u6d026lia1x',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'p7ucl8ixcx4',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'hz9xoui3o9d',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'j1q201o2wju',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 't0olm6ygvas',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'aq4i1y0nf00',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'px8uapyslvw',
|
||||||
|
'x-async': true,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@ -2,7 +2,7 @@ import { OrderedListOutlined } from '@ant-design/icons';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||||
import { createGridCardBlockSchema } from '../../../../schema-initializer/utils';
|
import { createGridCardBlockSchema } from './createGridCardBlockSchema';
|
||||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||||
|
|
||||||
@ -48,10 +48,9 @@ export const GridCardBlockInitializer = ({
|
|||||||
|
|
||||||
const collection = getCollection(item.name, item.dataSource);
|
const collection = getCollection(item.name, item.dataSource);
|
||||||
const schema = createGridCardBlockSchema({
|
const schema = createGridCardBlockSchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
settings: 'blockSettings:gridCard',
|
|
||||||
});
|
});
|
||||||
insert(schema);
|
insert(schema);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
import { createGridCardBlockSchema } from '../createGridCardBlockSchema';
|
||||||
|
|
||||||
|
describe('createGridCardBlockSchema', () => {
|
||||||
|
test('should return the correct schema', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'testCollection',
|
||||||
|
dataSource: 'testDataSource',
|
||||||
|
association: 'testAssociation',
|
||||||
|
templateSchema: { type: 'string' },
|
||||||
|
rowKey: 'testRowKey',
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = createGridCardBlockSchema(options);
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "gridCard:configureActions",
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
},
|
||||||
|
"x-initializer": "gridCard:configureItemActions",
|
||||||
|
"x-use-component-props": "useGridCardActionBarProps",
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"x-component": "GridCard.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useGridCardItemProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "GridCard",
|
||||||
|
"x-use-component-props": "useGridCardBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "testAssociation:view",
|
||||||
|
"x-component": "BlockItem",
|
||||||
|
"x-decorator": "GridCard.Decorator",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"association": "testAssociation",
|
||||||
|
"collection": "testCollection",
|
||||||
|
"dataSource": "testDataSource",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 12,
|
||||||
|
},
|
||||||
|
"readPretty": true,
|
||||||
|
"rowKey": "testRowKey",
|
||||||
|
"runWhenParamsChanged": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:gridCard",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-component-props": "useGridCardBlockItemProps",
|
||||||
|
"x-use-decorator-props": "useGridCardBlockDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return the correct schema when templateSchema is empty', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'testCollection',
|
||||||
|
dataSource: 'testDataSource',
|
||||||
|
association: 'testAssociation',
|
||||||
|
rowKey: 'testRowKey',
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = createGridCardBlockSchema(options);
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "gridCard:configureActions",
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
},
|
||||||
|
"x-initializer": "gridCard:configureItemActions",
|
||||||
|
"x-use-component-props": "useGridCardActionBarProps",
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "details:configureFields",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"x-component": "GridCard.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useGridCardItemProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "GridCard",
|
||||||
|
"x-use-component-props": "useGridCardBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "testAssociation:view",
|
||||||
|
"x-component": "BlockItem",
|
||||||
|
"x-decorator": "GridCard.Decorator",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"association": "testAssociation",
|
||||||
|
"collection": "testCollection",
|
||||||
|
"dataSource": "testDataSource",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 12,
|
||||||
|
},
|
||||||
|
"readPretty": true,
|
||||||
|
"rowKey": "testRowKey",
|
||||||
|
"runWhenParamsChanged": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:gridCard",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-component-props": "useGridCardBlockItemProps",
|
||||||
|
"x-use-decorator-props": "useGridCardBlockDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,81 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
|
||||||
|
export const createGridCardBlockSchema = (options: {
|
||||||
|
dataSource: string;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
rowKey?: string;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, association, templateSchema, dataSource, rowKey } = options;
|
||||||
|
const resourceName = association || collectionName;
|
||||||
|
|
||||||
|
if (!dataSource) {
|
||||||
|
throw new Error('dataSource are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${resourceName}:view`,
|
||||||
|
'x-decorator': 'GridCard.Decorator',
|
||||||
|
'x-use-decorator-props': 'useGridCardBlockDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: collectionName,
|
||||||
|
association,
|
||||||
|
dataSource,
|
||||||
|
readPretty: true,
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 12,
|
||||||
|
},
|
||||||
|
runWhenParamsChanged: true,
|
||||||
|
rowKey,
|
||||||
|
},
|
||||||
|
'x-component': 'BlockItem',
|
||||||
|
'x-use-component-props': 'useGridCardBlockItemProps',
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:gridCard',
|
||||||
|
properties: {
|
||||||
|
actionBar: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'gridCard:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 'var(--nb-spacing)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'GridCard',
|
||||||
|
'x-use-component-props': 'useGridCardBlockProps',
|
||||||
|
properties: {
|
||||||
|
item: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'GridCard.Item',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useGridCardItemProps',
|
||||||
|
properties: {
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
},
|
||||||
|
actionBar: {
|
||||||
|
type: 'void',
|
||||||
|
'x-align': 'left',
|
||||||
|
'x-initializer': 'gridCard:configureItemActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-use-component-props': 'useGridCardActionBarProps',
|
||||||
|
'x-component-props': {
|
||||||
|
layout: 'one-column',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
import { useGridCardBlockParams } from './useGridCardBlockParams';
|
||||||
|
|
||||||
|
export function useGridCardBlockDecoratorProps(props) {
|
||||||
|
const params = useGridCardBlockParams(props);
|
||||||
|
let sourceId;
|
||||||
|
|
||||||
|
// 因为 association 是固定的,所以可以在条件中使用 hooks
|
||||||
|
if (props.association) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
sourceId = useDataBlockSourceId({ association: props.association });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
export function useGridCardBlockParams(props) {
|
||||||
|
const { params } = props;
|
||||||
|
const { filter: parsedFilter } = useParsedFilter({
|
||||||
|
filterOption: params?.filter,
|
||||||
|
});
|
||||||
|
const paramsWithFilter = useMemo(() => {
|
||||||
|
return {
|
||||||
|
...params,
|
||||||
|
filter: parsedFilter,
|
||||||
|
};
|
||||||
|
}, [parsedFilter, params]);
|
||||||
|
|
||||||
|
return paramsWithFilter;
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { FormOutlined } from '@ant-design/icons';
|
import { FormOutlined } from '@ant-design/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { createFilterFormBlockSchema } from '../../../../schema-initializer/utils';
|
import { createFilterFormBlockSchema } from './createFilterFormBlockSchema';
|
||||||
import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer';
|
import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer';
|
||||||
import { Collection, CollectionFieldOptions } from '../../../../data-source';
|
import { Collection, CollectionFieldOptions } from '../../../../data-source';
|
||||||
|
|
||||||
@ -25,10 +25,9 @@ export const FilterFormBlockInitializer = ({
|
|||||||
componentType={'FilterFormItem'}
|
componentType={'FilterFormItem'}
|
||||||
templateWrap={(templateSchema, { item }) => {
|
templateWrap={(templateSchema, { item }) => {
|
||||||
const s = createFilterFormBlockSchema({
|
const s = createFilterFormBlockSchema({
|
||||||
template: templateSchema,
|
templateSchema: templateSchema,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
collection: item.name || item.collectionName,
|
collectionName: item.name || item.collectionName,
|
||||||
settings: 'blockSettings:filterForm',
|
|
||||||
});
|
});
|
||||||
if (item.template && item.mode === 'reference') {
|
if (item.template && item.mode === 'reference') {
|
||||||
s['x-template-key'] = item.template.key;
|
s['x-template-key'] = item.template.key;
|
||||||
@ -38,9 +37,8 @@ export const FilterFormBlockInitializer = ({
|
|||||||
onCreateBlockSchema={({ item }) => {
|
onCreateBlockSchema={({ item }) => {
|
||||||
return insert(
|
return insert(
|
||||||
createFilterFormBlockSchema({
|
createFilterFormBlockSchema({
|
||||||
collection: item.collectionName || item.name,
|
collectionName: item.collectionName || item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
settings: 'blockSettings:filterForm',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,106 @@
|
|||||||
|
import { createFilterFormBlockSchema } from '../createFilterFormBlockSchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => {
|
||||||
|
return {
|
||||||
|
uid: () => 'mocked-uid',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createFilterFormBlockSchema', () => {
|
||||||
|
test('should return the correct schema', () => {
|
||||||
|
const schema = createFilterFormBlockSchema({
|
||||||
|
collectionName: 'myCollection',
|
||||||
|
dataSource: 'myDataSource',
|
||||||
|
templateSchema: { type: 'string' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"mocked-uid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"float": "right",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "filterForm:configureActions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-use-component-props": "useFilterFormBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "FilterFormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "myCollection",
|
||||||
|
"dataSource": "myDataSource",
|
||||||
|
},
|
||||||
|
"x-filter-operators": {},
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"x-settings": "blockSettings:filterForm",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useFilterFormBlockDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return the correct schema without templateSchema', () => {
|
||||||
|
const schema = createFilterFormBlockSchema({
|
||||||
|
collectionName: 'myCollection',
|
||||||
|
dataSource: 'myDataSource',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "filterForm:configureFields",
|
||||||
|
},
|
||||||
|
"mocked-uid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"float": "right",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "filterForm:configureActions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-use-component-props": "useFilterFormBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "FilterFormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "myCollection",
|
||||||
|
"dataSource": "myDataSource",
|
||||||
|
},
|
||||||
|
"x-filter-operators": {},
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"x-settings": "blockSettings:filterForm",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useFilterFormBlockDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,57 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export const createFilterFormBlockSchema = (options: {
|
||||||
|
collectionName: string;
|
||||||
|
dataSource: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
}) => {
|
||||||
|
const { collectionName, dataSource, templateSchema } = options;
|
||||||
|
|
||||||
|
if (!collectionName || !dataSource) {
|
||||||
|
throw new Error('collectionName and dataSource are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FilterFormBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useFilterFormBlockDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
dataSource,
|
||||||
|
collection: collectionName,
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:filterForm',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
// 保存当前筛选区块所能过滤的数据区块
|
||||||
|
'x-filter-targets': [],
|
||||||
|
// 用于存储用户设置的每个字段的运算符,目前仅筛选表单区块支持自定义
|
||||||
|
'x-filter-operators': {},
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'FormV2',
|
||||||
|
'x-use-component-props': 'useFilterFormBlockProps',
|
||||||
|
properties: {
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'filterForm:configureFields',
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'filterForm:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
layout: 'one-column',
|
||||||
|
style: {
|
||||||
|
float: 'right',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return schema;
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export function useFilterFormBlockDecoratorProps() {}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useFormBlockProps } from '../../../../../block-provider/FormBlockProvider';
|
||||||
|
|
||||||
|
export function useFilterFormBlockProps() {
|
||||||
|
return useFormBlockProps();
|
||||||
|
}
|
@ -88,7 +88,7 @@ test.describe('where to open a popup and what can be added to it', () => {
|
|||||||
// 删除已创建的 blocks,腾出页面空间
|
// 删除已创建的 blocks,腾出页面空间
|
||||||
// delete details block
|
// delete details block
|
||||||
await page.getByText('GeneralConfigure actionsConfigure fields').hover();
|
await page.getByText('GeneralConfigure actionsConfigure fields').hover();
|
||||||
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:singleDataDetails-general').hover();
|
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:details-general').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
// delete form block
|
// delete form block
|
||||||
|
@ -6,7 +6,7 @@ import { useFieldComponentName } from '../../../../common/useFieldComponentName'
|
|||||||
import { useDesignable, useFieldModeOptions, useIsAddNewForm } from '../../../../schema-component';
|
import { useDesignable, useFieldModeOptions, useIsAddNewForm } from '../../../../schema-component';
|
||||||
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
|
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
|
||||||
import { useTitleFieldOptions } from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
import { useTitleFieldOptions } from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useCollectionField } from '../utils';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
|
|
||||||
const fieldComponent: any = {
|
const fieldComponent: any = {
|
||||||
name: 'fieldComponent',
|
name: 'fieldComponent',
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
useIsFieldReadPretty,
|
useIsFieldReadPretty,
|
||||||
useIsFormReadPretty,
|
useIsFormReadPretty,
|
||||||
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useCollectionField } from '../utils';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
import { useFormBlockType } from '../../../../block-provider';
|
import { useFormBlockType } from '../../../../block-provider';
|
||||||
|
|
||||||
const allowMultiple: any = {
|
const allowMultiple: any = {
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
useTitleFieldOptions,
|
useTitleFieldOptions,
|
||||||
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
||||||
import { useCollectionField } from '../utils';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
|
|
||||||
export const titleField: any = {
|
export const titleField: any = {
|
||||||
name: 'titleField',
|
name: 'titleField',
|
||||||
|
@ -6,7 +6,7 @@ import { useFieldComponentName } from '../../../../common/useFieldComponentName'
|
|||||||
import { useDesignable, useFieldModeOptions, useIsAddNewForm } from '../../../../schema-component';
|
import { useDesignable, useFieldModeOptions, useIsAddNewForm } from '../../../../schema-component';
|
||||||
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
|
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
|
||||||
import { useIsFieldReadPretty } from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
import { useIsFieldReadPretty } from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useCollectionField } from '../utils';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
||||||
import { titleField } from '../Picker/recordPickerComponentFieldSettings';
|
import { titleField } from '../Picker/recordPickerComponentFieldSettings';
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSetti
|
|||||||
import { SchemaSettingsSortingRule } from '../../../../schema-settings/SchemaSettingsSortingRule';
|
import { SchemaSettingsSortingRule } from '../../../../schema-settings/SchemaSettingsSortingRule';
|
||||||
import { useIsShowMultipleSwitch } from '../../../../schema-settings/hooks/useIsShowMultipleSwitch';
|
import { useIsShowMultipleSwitch } from '../../../../schema-settings/hooks/useIsShowMultipleSwitch';
|
||||||
import { useLocalVariables, useVariables } from '../../../../variables';
|
import { useLocalVariables, useVariables } from '../../../../variables';
|
||||||
import { useCollectionField } from '../utils';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
|
|
||||||
const enableLink = {
|
const enableLink = {
|
||||||
name: 'enableLink',
|
name: 'enableLink',
|
||||||
|
@ -8,7 +8,7 @@ import { isSubMode } from '../../../../schema-component/antd/association-field/u
|
|||||||
import { useTitleFieldOptions } from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
import { useTitleFieldOptions } from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useColorFields } from '../../../../schema-component/antd/table-v2/Table.Column.Designer';
|
import { useColorFields } from '../../../../schema-component/antd/table-v2/Table.Column.Designer';
|
||||||
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
|
||||||
import { useCollectionField } from '../utils';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
|
|
||||||
const enableLink = {
|
const enableLink = {
|
||||||
name: 'enableLink',
|
name: 'enableLink',
|
||||||
@ -140,9 +140,12 @@ export const tagComponentFieldSettings = new SchemaSettings({
|
|||||||
useComponentProps() {
|
useComponentProps() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const fieldSchema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
|
const targetCollectionField = useCollectionField();
|
||||||
|
const { fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema();
|
||||||
|
const fieldSchema = tableColumnSchema || schema;
|
||||||
|
const collectionField = tableColumnField || targetCollectionField;
|
||||||
const { dn } = useDesignable();
|
const { dn } = useDesignable();
|
||||||
const collectionField = useCollectionField();
|
|
||||||
const colorFieldOptions = useColorFields(collectionField?.target ?? collectionField?.targetCollection);
|
const colorFieldOptions = useColorFields(collectionField?.target ?? collectionField?.targetCollection);
|
||||||
return {
|
return {
|
||||||
title: t('Tag color field'),
|
title: t('Tag color field'),
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import { useFieldSchema } from '@formily/react';
|
|
||||||
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取字段相关的配置信息
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function useCollectionField() {
|
|
||||||
const { getCollectionJoinField } = useCollectionManager_deprecated();
|
|
||||||
const { getField } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
|
|
||||||
return collectionField;
|
|
||||||
}
|
|
@ -33,7 +33,7 @@ export const PoweredBy = () => {
|
|||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: parseHTML(
|
__html: parseHTML(
|
||||||
customBrandPlugin?.options?.options?.brand ||
|
customBrandPlugin?.options?.options?.brand ||
|
||||||
`Powered by <a href="${urls[i18n.language] || urls['en-US']}">NocoBase</a>`,
|
`Powered by <a href="${urls[i18n.language] || urls['en-US']}" target="__blank">NocoBase</a>`,
|
||||||
{ appVersion },
|
{ appVersion },
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
@ -5,9 +5,12 @@ import { SortableItem } from '../../common';
|
|||||||
import { useDesigner, useProps } from '../../hooks';
|
import { useDesigner, useProps } from '../../hooks';
|
||||||
import { useGetAriaLabelOfBlockItem } from './hooks/useGetAriaLabelOfBlockItem';
|
import { useGetAriaLabelOfBlockItem } from './hooks/useGetAriaLabelOfBlockItem';
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const BlockItem: React.FC<any> = (props) => {
|
export const BlockItem: React.FC<any> = withDynamicSchemaProps((props) => {
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { className, children } = useProps(props);
|
const { className, children } = useProps(props);
|
||||||
|
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { getAriaLabel } = useGetAriaLabelOfBlockItem(props.name);
|
const { getAriaLabel } = useGetAriaLabelOfBlockItem(props.name);
|
||||||
@ -69,4 +72,4 @@ export const BlockItem: React.FC<any> = (props) => {
|
|||||||
{children}
|
{children}
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -3,8 +3,9 @@ import { FormV2 } from '../form-v2';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Empty } from 'antd';
|
import { Empty } from 'antd';
|
||||||
import { useDataBlockRequest } from '../../../data-source';
|
import { useDataBlockRequest } from '../../../data-source';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const Details = (props) => {
|
export const Details = withDynamicSchemaProps((props) => {
|
||||||
const request = useDataBlockRequest();
|
const request = useDataBlockRequest();
|
||||||
|
|
||||||
if (!request?.loading && _.isEmpty(request?.data?.data)) {
|
if (!request?.loading && _.isEmpty(request?.data?.data)) {
|
||||||
@ -12,4 +13,4 @@ export const Details = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <FormV2 {...props} />;
|
return <FormV2 {...props} />;
|
||||||
};
|
});
|
||||||
|
@ -67,16 +67,15 @@ export const FormItem: any = observer(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}, [showTitle]);
|
}, [showTitle]);
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<CollectionFieldProvider allowNull={true}>
|
||||||
<BlockItem className={'nb-form-item'}>
|
<BlockItem className={'nb-form-item'}>
|
||||||
<CollectionFieldProvider name={fieldSchema.name} allowNull={!fieldSchema['x-collection-field']}>
|
|
||||||
<ACLCollectionFieldProvider>
|
<ACLCollectionFieldProvider>
|
||||||
<Item className={className} {...props} extra={extra} />
|
<Item className={className} {...props} extra={extra} />
|
||||||
</ACLCollectionFieldProvider>
|
</ACLCollectionFieldProvider>
|
||||||
</CollectionFieldProvider>
|
|
||||||
</BlockItem>
|
</BlockItem>
|
||||||
|
</CollectionFieldProvider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ displayName: 'FormItem' },
|
{ displayName: 'FormItem' },
|
||||||
|
@ -19,6 +19,7 @@ import { getInnermostKeyAndValue, getTargetField } from '../../common/utils/uitl
|
|||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
import { collectFieldStateOfLinkageRules, getTempFieldState } from './utils';
|
import { collectFieldStateOfLinkageRules, getTempFieldState } from './utils';
|
||||||
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider';
|
||||||
|
|
||||||
export interface FormProps {
|
export interface FormProps {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
@ -87,6 +88,7 @@ const WithForm = (props: WithFormProps) => {
|
|||||||
const { setFormValueChanged } = useActionContext();
|
const { setFormValueChanged } = useActionContext();
|
||||||
const variables = useVariables();
|
const variables = useVariables();
|
||||||
const localVariables = useLocalVariables({ currentForm: form });
|
const localVariables = useLocalVariables({ currentForm: form });
|
||||||
|
const { templateFinshed } = useTemplateBlockContext();
|
||||||
const linkageRules: any[] =
|
const linkageRules: any[] =
|
||||||
(getLinkageRules(fieldSchema) || fieldSchema.parent?.['x-linkage-rules'])?.filter((k) => !k.disabled) || [];
|
(getLinkageRules(fieldSchema) || fieldSchema.parent?.['x-linkage-rules'])?.filter((k) => !k.disabled) || [];
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ const WithForm = (props: WithFormProps) => {
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
getSubscriber(action, field, rule, variables, localVariables),
|
getSubscriber(action, field, rule, variables, localVariables),
|
||||||
{ fireImmediately: true },
|
{ fireImmediately: false },
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -166,7 +168,7 @@ const WithForm = (props: WithFormProps) => {
|
|||||||
dispose();
|
dispose();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [linkageRules]);
|
}, [linkageRules, templateFinshed]);
|
||||||
|
|
||||||
return fieldSchema['x-decorator'] === 'FormV2' ? <FormDecorator {...props} /> : <FormComponent {...props} />;
|
return fieldSchema['x-decorator'] === 'FormV2' ? <FormDecorator {...props} /> : <FormComponent {...props} />;
|
||||||
};
|
};
|
||||||
|
@ -101,6 +101,7 @@ export const Form: React.FC<FormProps> & { Designer?: any } = observer(
|
|||||||
async onSuccess(data) {
|
async onSuccess(data) {
|
||||||
await form.reset();
|
await form.reset();
|
||||||
form.setValues(data?.data);
|
form.setValues(data?.data);
|
||||||
|
form.setInitialValues(data?.data);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props,
|
props,
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { FormLayout } from '@formily/antd-v5';
|
import { FormLayout } from '@formily/antd-v5';
|
||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { FormContext, useField } from '@formily/react';
|
import { FormContext, useField, useFieldSchema } from '@formily/react';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
||||||
import useStyles from './GridCard.Decorator.style';
|
import useStyles from './GridCard.Decorator.style';
|
||||||
|
import { useGridCardBlockParams } from '../../../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockParams';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const GridCardBlockContext = createContext<any>({});
|
export const GridCardBlockContext = createContext<any>({});
|
||||||
GridCardBlockContext.displayName = 'GridCardBlockContext';
|
GridCardBlockContext.displayName = 'GridCardBlockContext';
|
||||||
|
|
||||||
@ -41,24 +44,31 @@ const InternalGridCardBlockProvider = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GridCardBlockProvider = (props) => {
|
/**
|
||||||
const { params } = props;
|
* 用于兼容旧版 UISchema(旧版本没有 x-use-decorator-props)
|
||||||
const { filter: parsedFilter } = useParsedFilter({
|
* @param props
|
||||||
filterOption: params?.filter,
|
*/
|
||||||
});
|
const useCompatGridCardBlockParams = (props) => {
|
||||||
const paramsWithFilter = useMemo(() => {
|
const schema = useFieldSchema();
|
||||||
return {
|
|
||||||
...params,
|
// 因为 x-use-decorator-props 的值是固定的,所以可以在条件中使用 hooks
|
||||||
filter: parsedFilter,
|
if (schema['x-use-decorator-props']) {
|
||||||
|
return props.params;
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
return useGridCardBlockParams(props);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [parsedFilter, params]);
|
|
||||||
|
export const GridCardBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
|
const params = useCompatGridCardBlockParams(props);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BlockProvider name="grid-card" {...props} params={paramsWithFilter}>
|
<BlockProvider name="grid-card" {...props} params={params}>
|
||||||
<InternalGridCardBlockProvider {...props} />
|
<InternalGridCardBlockProvider {...props} />
|
||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export const useGridCardBlockContext = () => {
|
export const useGridCardBlockContext = () => {
|
||||||
return useContext(GridCardBlockContext);
|
return useContext(GridCardBlockContext);
|
||||||
|
@ -5,6 +5,7 @@ import { Card } from 'antd';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
|
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
|
||||||
import { RecordProvider } from '../../../record-provider';
|
import { RecordProvider } from '../../../record-provider';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
const itemCss = css`
|
const itemCss = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -15,7 +16,7 @@ const itemCss = css`
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GridCardItem = (props) => {
|
export const GridCardItem = withDynamicSchemaProps((props) => {
|
||||||
const field = useField<ObjectField>();
|
const field = useField<ObjectField>();
|
||||||
const parentRecordData = useCollectionParentRecordData();
|
const parentRecordData = useCollectionParentRecordData();
|
||||||
return (
|
return (
|
||||||
@ -40,4 +41,4 @@ export const GridCardItem = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -11,6 +11,7 @@ import { GridCardDesigner } from './GridCard.Designer';
|
|||||||
import { GridCardItem } from './GridCard.Item';
|
import { GridCardItem } from './GridCard.Item';
|
||||||
import { useGridCardActionBarProps } from './hooks';
|
import { useGridCardActionBarProps } from './hooks';
|
||||||
import { defaultColumnCount, pageSizeOptions } from './options';
|
import { defaultColumnCount, pageSizeOptions } from './options';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
const rowGutter = {
|
const rowGutter = {
|
||||||
md: 12,
|
md: 12,
|
||||||
@ -56,7 +57,9 @@ const designerCss = css`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const InternalGridCard = (props) => {
|
const InternalGridCard = (props) => {
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { columnCount: columnCountProp, pagination } = useProps(props);
|
const { columnCount: columnCountProp, pagination } = useProps(props);
|
||||||
|
|
||||||
const { service, columnCount: _columnCount = defaultColumnCount } = useGridCardBlockContext();
|
const { service, columnCount: _columnCount = defaultColumnCount } = useGridCardBlockContext();
|
||||||
const columnCount = columnCountProp || _columnCount;
|
const columnCount = columnCountProp || _columnCount;
|
||||||
const { run, params } = service;
|
const { run, params } = service;
|
||||||
@ -145,7 +148,7 @@ const InternalGridCard = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GridCard = InternalGridCard as typeof InternalGridCard & {
|
export const GridCard = withDynamicSchemaProps(InternalGridCard) as typeof InternalGridCard & {
|
||||||
Item: typeof GridCardItem;
|
Item: typeof GridCardItem;
|
||||||
Designer: typeof GridCardDesigner;
|
Designer: typeof GridCardDesigner;
|
||||||
Decorator: typeof GridCardBlockProvider;
|
Decorator: typeof GridCardBlockProvider;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { render } from '@nocobase/test/client';
|
|
||||||
import React from 'react';
|
|
||||||
import { Pagination } from '../index';
|
|
||||||
|
|
||||||
describe('Pagination', () => {
|
|
||||||
it('should render correctly', () => {
|
|
||||||
render(<Pagination />);
|
|
||||||
expect(document.querySelector('.ant-pagination')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hidden', () => {
|
|
||||||
render(<Pagination hidden />);
|
|
||||||
expect(document.querySelector('.ant-pagination')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
@ -2,10 +2,14 @@ import { observer } from '@formily/react';
|
|||||||
import { Pagination as AntdPagination } from 'antd';
|
import { Pagination as AntdPagination } from 'antd';
|
||||||
import React, { KeyboardEventHandler } from 'react';
|
import React, { KeyboardEventHandler } from 'react';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const Pagination = observer(
|
export const Pagination = withDynamicSchemaProps(
|
||||||
|
observer(
|
||||||
(props: any) => {
|
(props: any) => {
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { hidden, ...others } = useProps(props);
|
const { hidden, ...others } = useProps(props);
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -22,4 +26,5 @@ export const Pagination = observer(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ displayName: 'Pagination' },
|
{ displayName: 'Pagination' },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -52,7 +52,8 @@ const InternalRemoteSelect = connect(
|
|||||||
const { getField } = useCollection_deprecated();
|
const { getField } = useCollection_deprecated();
|
||||||
const searchData = useRef(null);
|
const searchData = useRef(null);
|
||||||
const { getCollectionJoinField, getInterface } = useCollectionManager_deprecated();
|
const { getCollectionJoinField, getInterface } = useCollectionManager_deprecated();
|
||||||
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema.name as string);
|
const colletionFieldName = fieldSchema['x-collection-field'] || fieldSchema.name;
|
||||||
|
const collectionField = getField(colletionFieldName) || getCollectionJoinField(colletionFieldName);
|
||||||
const targetField =
|
const targetField =
|
||||||
_targetField ||
|
_targetField ||
|
||||||
(collectionField?.target &&
|
(collectionField?.target &&
|
||||||
|
@ -123,7 +123,6 @@ export * from './buttons';
|
|||||||
export * from './items';
|
export * from './items';
|
||||||
export {
|
export {
|
||||||
createDetailsBlockSchema,
|
createDetailsBlockSchema,
|
||||||
createFilterFormBlockSchema,
|
|
||||||
createFormBlockSchema,
|
createFormBlockSchema,
|
||||||
createReadPrettyFormBlockSchema,
|
createReadPrettyFormBlockSchema,
|
||||||
createTableBlockSchema,
|
createTableBlockSchema,
|
||||||
|
@ -4,7 +4,8 @@ import React, { useCallback } from 'react';
|
|||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
||||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../collection-manager';
|
||||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||||
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||||
|
import { createDetailsWithPaginationUISchema } from '../../modules/blocks/data-blocks/details-multi/createDetailsWithPaginationUISchema';
|
||||||
|
|
||||||
export const RecordAssociationDetailsBlockInitializer = () => {
|
export const RecordAssociationDetailsBlockInitializer = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -25,13 +26,10 @@ export const RecordAssociationDetailsBlockInitializer = () => {
|
|||||||
insert(s);
|
insert(s);
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsWithPaginationUISchema({
|
||||||
collection: field.target,
|
|
||||||
resource,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: resource,
|
association: resource,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
settings: 'blockSettings:multiDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -51,12 +49,10 @@ export function useCreateAssociationDetailsBlock() {
|
|||||||
const collection = getCollection(field.target);
|
const collection = getCollection(field.target);
|
||||||
|
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsWithPaginationUISchema({
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
settings: 'blockSettings:multiDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,8 @@ import React, { useCallback } from 'react';
|
|||||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../collection-manager';
|
||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
||||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||||
import { createGridCardBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||||
|
import { createGridCardBlockSchema } from '../../modules/blocks/data-blocks/grid-card/createGridCardBlockSchema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -31,11 +32,8 @@ export const RecordAssociationGridCardBlockInitializer = () => {
|
|||||||
insert(
|
insert(
|
||||||
createGridCardBlockSchema({
|
createGridCardBlockSchema({
|
||||||
rowKey: collection.filterTargetKey,
|
rowKey: collection.filterTargetKey,
|
||||||
collection: field.target,
|
|
||||||
resource,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: resource,
|
association: resource,
|
||||||
settings: 'blockSettings:gridCard',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -57,10 +55,8 @@ export function useCreateAssociationGridCardBlock() {
|
|||||||
insert(
|
insert(
|
||||||
createGridCardBlockSchema({
|
createGridCardBlockSchema({
|
||||||
rowKey: collection.filterTargetKey,
|
rowKey: collection.filterTargetKey,
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
settings: 'blockSettings:gridCard',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -2,10 +2,10 @@ import { FormOutlined } from '@ant-design/icons';
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
||||||
import { useBlockRequestContext } from '../../block-provider';
|
|
||||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||||
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../collection-manager';
|
||||||
|
import { createDetailsUISchema } from '../../modules/blocks/data-blocks/details-single/createDetailsUISchema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -22,8 +22,6 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
const collection = getCollection(collectionName);
|
const collection = getCollection(collectionName);
|
||||||
|
|
||||||
const resource = `${field.collectionName}.${field.name}`;
|
const resource = `${field.collectionName}.${field.name}`;
|
||||||
const { block } = useBlockRequestContext();
|
|
||||||
const actionInitializers = block !== 'TableField' ? 'details:configureActions' : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaInitializerItem
|
<SchemaInitializerItem
|
||||||
@ -33,17 +31,10 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
if (item.template) {
|
if (item.template) {
|
||||||
const s = await getTemplateSchemaByMode(item);
|
const s = await getTemplateSchemaByMode(item);
|
||||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||||
const blockSchema = createDetailsBlockSchema({
|
const blockSchema = createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: collectionName,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
resource,
|
|
||||||
association: resource,
|
association: resource,
|
||||||
action: 'get',
|
templateSchema: s,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
template: s,
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
});
|
});
|
||||||
if (item.mode === 'reference') {
|
if (item.mode === 'reference') {
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
@ -54,16 +45,9 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: collectionName,
|
|
||||||
resource,
|
|
||||||
association: resource,
|
association: resource,
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
action: 'get',
|
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -76,8 +60,6 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
export function useCreateAssociationDetailsWithoutPagination() {
|
export function useCreateAssociationDetailsWithoutPagination() {
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
const { getCollection } = useCollectionManager_deprecated();
|
const { getCollection } = useCollectionManager_deprecated();
|
||||||
const { block } = useBlockRequestContext();
|
|
||||||
const actionInitializers = block !== 'TableField' ? 'details:configureActions' : null;
|
|
||||||
|
|
||||||
const createAssociationDetailsWithoutPagination = useCallback(
|
const createAssociationDetailsWithoutPagination = useCallback(
|
||||||
({ item }) => {
|
({ item }) => {
|
||||||
@ -85,19 +67,13 @@ export function useCreateAssociationDetailsWithoutPagination() {
|
|||||||
const collection = getCollection(field.target);
|
const collection = getCollection(field.target);
|
||||||
|
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
action: 'get',
|
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[actionInitializers, getCollection, insert],
|
[getCollection, insert],
|
||||||
);
|
);
|
||||||
|
|
||||||
const templateWrap = useCallback(
|
const templateWrap = useCallback(
|
||||||
@ -106,16 +82,10 @@ export function useCreateAssociationDetailsWithoutPagination() {
|
|||||||
const collection = getCollection(field.target);
|
const collection = getCollection(field.target);
|
||||||
|
|
||||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||||
const blockSchema = createDetailsBlockSchema({
|
const blockSchema = createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
action: 'get',
|
templateSchema: templateSchema,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
template: templateSchema,
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
});
|
});
|
||||||
if (item.mode === 'reference') {
|
if (item.mode === 'reference') {
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
@ -125,7 +95,7 @@ export function useCreateAssociationDetailsWithoutPagination() {
|
|||||||
return templateSchema;
|
return templateSchema;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actionInitializers, getCollection],
|
[getCollection],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { createAssociationDetailsWithoutPagination, templateWrap };
|
return { createAssociationDetailsWithoutPagination, templateWrap };
|
||||||
|
@ -893,7 +893,18 @@ export const useCollectionDataSourceItems = ({
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createDetailsBlockSchema = (options) => {
|
export const createDetailsBlockSchema = (options: {
|
||||||
|
collection: string;
|
||||||
|
dataSource: string;
|
||||||
|
rowKey?: string;
|
||||||
|
formItemInitializers?: string;
|
||||||
|
actionInitializers?: string;
|
||||||
|
association?: string;
|
||||||
|
template?: any;
|
||||||
|
settings?: string;
|
||||||
|
action?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}) => {
|
||||||
const {
|
const {
|
||||||
formItemInitializers = 'details:configureFields',
|
formItemInitializers = 'details:configureFields',
|
||||||
actionInitializers = 'detailsWithPaging:configureActions',
|
actionInitializers = 'detailsWithPaging:configureActions',
|
||||||
@ -972,97 +983,6 @@ export const createDetailsBlockSchema = (options) => {
|
|||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createGridCardBlockSchema = (options) => {
|
|
||||||
const {
|
|
||||||
formItemInitializers = 'details:configureFields',
|
|
||||||
actionInitializers = 'gridCard:configureActions',
|
|
||||||
itemActionInitializers = 'gridCard:configureItemActions',
|
|
||||||
collection,
|
|
||||||
association,
|
|
||||||
template,
|
|
||||||
dataSource,
|
|
||||||
settings,
|
|
||||||
...others
|
|
||||||
} = options;
|
|
||||||
const resourceName = association || collection;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-acl-action': `${resourceName}:view`,
|
|
||||||
'x-decorator': 'GridCard.Decorator',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection,
|
|
||||||
association,
|
|
||||||
dataSource,
|
|
||||||
readPretty: true,
|
|
||||||
action: 'list',
|
|
||||||
params: {
|
|
||||||
pageSize: 12,
|
|
||||||
},
|
|
||||||
runWhenParamsChanged: true,
|
|
||||||
...others,
|
|
||||||
},
|
|
||||||
'x-component': 'BlockItem',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useGridCardBlockItemProps }}',
|
|
||||||
},
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
'x-settings': settings,
|
|
||||||
properties: {
|
|
||||||
actionBar: {
|
|
||||||
type: 'void',
|
|
||||||
'x-initializer': actionInitializers,
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
marginBottom: 'var(--nb-spacing)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
list: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'GridCard',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useGridCardBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
item: {
|
|
||||||
type: 'object',
|
|
||||||
'x-component': 'GridCard.Item',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useGridCardItemProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
grid: template || {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-initializer': formItemInitializers,
|
|
||||||
'x-initializer-props': {
|
|
||||||
useProps: '{{ useGridCardItemInitializerProps }}',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
actionBar: {
|
|
||||||
type: 'void',
|
|
||||||
'x-align': 'left',
|
|
||||||
'x-initializer': itemActionInitializers,
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useGridCardActionBarProps }}',
|
|
||||||
layout: 'one-column',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* 已弃用,请使用 createCreateFormBlockUISchema 或者 createEditFormBlockUISchema 替代
|
* 已弃用,请使用 createCreateFormBlockUISchema 或者 createEditFormBlockUISchema 替代
|
||||||
@ -1153,71 +1073,6 @@ export const createFormBlockSchema = (options: {
|
|||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createFilterFormBlockSchema = (options) => {
|
|
||||||
const {
|
|
||||||
formItemInitializers = 'filterForm:configureFields',
|
|
||||||
actionInitializers = 'filterForm:configureActions',
|
|
||||||
collection,
|
|
||||||
resource,
|
|
||||||
association,
|
|
||||||
dataSource,
|
|
||||||
action,
|
|
||||||
template,
|
|
||||||
settings,
|
|
||||||
...others
|
|
||||||
} = options;
|
|
||||||
const resourceName = resource || association || collection;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'FilterFormBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
...others,
|
|
||||||
action,
|
|
||||||
resource: resourceName,
|
|
||||||
dataSource,
|
|
||||||
collection,
|
|
||||||
association,
|
|
||||||
},
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
...(settings ? { 'x-settings': settings } : { 'x-designer': 'FormV2.FilterDesigner' }),
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
// 保存当前筛选区块所能过滤的数据区块
|
|
||||||
'x-filter-targets': [],
|
|
||||||
// 用于存储用户设置的每个字段的运算符,目前仅筛选表单区块支持自定义
|
|
||||||
'x-filter-operators': {},
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'FormV2',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useFormBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
grid: template || {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-initializer': formItemInitializers,
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-initializer': actionInitializers,
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
layout: 'one-column',
|
|
||||||
style: {
|
|
||||||
float: 'right',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* 已弃用,可以使用 createDetailsBlockSchema 替换
|
* 已弃用,可以使用 createDetailsBlockSchema 替换
|
||||||
|
@ -42,11 +42,11 @@ import {
|
|||||||
ActionContextProvider,
|
ActionContextProvider,
|
||||||
AssociationOrCollectionProvider,
|
AssociationOrCollectionProvider,
|
||||||
CollectionFieldOptions_deprecated,
|
CollectionFieldOptions_deprecated,
|
||||||
|
CollectionRecordProvider,
|
||||||
DataSourceApplicationProvider,
|
DataSourceApplicationProvider,
|
||||||
Designable,
|
Designable,
|
||||||
FormDialog,
|
FormDialog,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
CollectionRecordProvider,
|
|
||||||
RemoteSchemaComponent,
|
RemoteSchemaComponent,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaComponentContext,
|
SchemaComponentContext,
|
||||||
@ -56,6 +56,7 @@ import {
|
|||||||
useAPIClient,
|
useAPIClient,
|
||||||
useBlockRequestContext,
|
useBlockRequestContext,
|
||||||
useCollectionManager_deprecated,
|
useCollectionManager_deprecated,
|
||||||
|
useCollectionRecord,
|
||||||
useCollection_deprecated,
|
useCollection_deprecated,
|
||||||
useCompile,
|
useCompile,
|
||||||
useDataBlockProps,
|
useDataBlockProps,
|
||||||
@ -63,7 +64,6 @@ import {
|
|||||||
useFilterBlock,
|
useFilterBlock,
|
||||||
useGlobalTheme,
|
useGlobalTheme,
|
||||||
useLinkageCollectionFilterOptions,
|
useLinkageCollectionFilterOptions,
|
||||||
useCollectionRecord,
|
|
||||||
useRecord,
|
useRecord,
|
||||||
useSchemaSettingsItem,
|
useSchemaSettingsItem,
|
||||||
useSortFields,
|
useSortFields,
|
||||||
@ -84,9 +84,9 @@ import {
|
|||||||
useFormActiveFields,
|
useFormActiveFields,
|
||||||
} from '../block-provider/hooks';
|
} from '../block-provider/hooks';
|
||||||
import { SelectWithTitle, SelectWithTitleProps } from '../common/SelectWithTitle';
|
import { SelectWithTitle, SelectWithTitleProps } from '../common/SelectWithTitle';
|
||||||
|
import { useNiceDropdownMaxHeight } from '../common/useNiceDropdownHeight';
|
||||||
import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
|
import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
|
||||||
import { useDataSourceKey } from '../data-source/data-source/DataSourceProvider';
|
import { useDataSourceKey } from '../data-source/data-source/DataSourceProvider';
|
||||||
import { useNiceDropdownMaxHeight } from '../common/useNiceDropdownHeight';
|
|
||||||
import {
|
import {
|
||||||
FilterBlockType,
|
FilterBlockType,
|
||||||
getSupportFieldsByAssociation,
|
getSupportFieldsByAssociation,
|
||||||
@ -574,7 +574,7 @@ export const SchemaSettingsRemove: FC<SchemaSettingsRemoveProps> = (props) => {
|
|||||||
field.required = false;
|
field.required = false;
|
||||||
fieldSchema['required'] = false;
|
fieldSchema['required'] = false;
|
||||||
}
|
}
|
||||||
if (template && ctx?.dn) {
|
if (template && template.uid === fieldSchema['x-uid'] && ctx?.dn) {
|
||||||
await ctx?.dn.remove(null, options);
|
await ctx?.dn.remove(null, options);
|
||||||
} else {
|
} else {
|
||||||
await dn.remove(null, options);
|
await dn.remove(null, options);
|
||||||
|
@ -15,8 +15,14 @@ import { customizeUpdateRecordActionSettings } from '../modules/actions/update-r
|
|||||||
import { customizePopupActionSettings } from '../modules/actions/view-edit-popup/customizePopupActionSettings';
|
import { customizePopupActionSettings } from '../modules/actions/view-edit-popup/customizePopupActionSettings';
|
||||||
import { editActionSettings } from '../modules/actions/view-edit-popup/editActionSettings';
|
import { editActionSettings } from '../modules/actions/view-edit-popup/editActionSettings';
|
||||||
import { viewActionSettings } from '../modules/actions/view-edit-popup/viewActionSettings';
|
import { viewActionSettings } from '../modules/actions/view-edit-popup/viewActionSettings';
|
||||||
import { multiDataDetailsBlockSettings } from '../modules/blocks/data-blocks/details-multi/multiDataDetailsBlockSettings';
|
import {
|
||||||
import { singleDataDetailsBlockSettings } from '../modules/blocks/data-blocks/details-single/singleDataDetailsBlockSettings';
|
detailsWithPaginationSettings,
|
||||||
|
multiDataDetailsBlockSettings,
|
||||||
|
} from '../modules/blocks/data-blocks/details-multi/detailsWithPaginationSettings';
|
||||||
|
import {
|
||||||
|
detailsBlockSettings,
|
||||||
|
singleDataDetailsBlockSettings,
|
||||||
|
} from '../modules/blocks/data-blocks/details-single/detailsBlockSettings';
|
||||||
import {
|
import {
|
||||||
createFormBlockSettings,
|
createFormBlockSettings,
|
||||||
creationFormBlockSettings,
|
creationFormBlockSettings,
|
||||||
@ -57,7 +63,9 @@ export class SchemaSettingsPlugin extends Plugin {
|
|||||||
this.schemaSettingsManager.add(filterFormBlockSettings);
|
this.schemaSettingsManager.add(filterFormBlockSettings);
|
||||||
this.schemaSettingsManager.add(filterFormItemFieldSettings);
|
this.schemaSettingsManager.add(filterFormItemFieldSettings);
|
||||||
this.schemaSettingsManager.add(multiDataDetailsBlockSettings);
|
this.schemaSettingsManager.add(multiDataDetailsBlockSettings);
|
||||||
|
this.schemaSettingsManager.add(detailsWithPaginationSettings);
|
||||||
this.schemaSettingsManager.add(singleDataDetailsBlockSettings);
|
this.schemaSettingsManager.add(singleDataDetailsBlockSettings);
|
||||||
|
this.schemaSettingsManager.add(detailsBlockSettings);
|
||||||
this.schemaSettingsManager.add(tableSelectorBlockSettings);
|
this.schemaSettingsManager.add(tableSelectorBlockSettings);
|
||||||
this.schemaSettingsManager.add(listBlockSettings);
|
this.schemaSettingsManager.add(listBlockSettings);
|
||||||
this.schemaSettingsManager.add(gridCardBlockSettings);
|
this.schemaSettingsManager.add(gridCardBlockSettings);
|
||||||
|
@ -111,6 +111,8 @@ export const Help = observer(
|
|||||||
<Popover
|
<Popover
|
||||||
// nb-about 的样式定义在 plugin-custom-brand 插件中
|
// nb-about 的样式定义在 plugin-custom-brand 插件中
|
||||||
rootClassName="nb-about"
|
rootClassName="nb-about"
|
||||||
|
placement="bottomRight"
|
||||||
|
arrow={false}
|
||||||
content={<div dangerouslySetInnerHTML={{ __html: content }}></div>}
|
content={<div dangerouslySetInnerHTML={{ __html: content }}></div>}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import { defineConfig } from '@nocobase/test';
|
import { defineConfig } from '@nocobase/test/vitest.mjs';
|
||||||
|
|
||||||
export default defineConfig();
|
export default defineConfig();
|
||||||
|
@ -29,12 +29,12 @@ const availableActions: {
|
|||||||
update: {
|
update: {
|
||||||
displayName: '{{t("Edit")}}',
|
displayName: '{{t("Edit")}}',
|
||||||
type: 'old-data',
|
type: 'old-data',
|
||||||
aliases: ['update', 'move'],
|
aliases: ['update', 'move', 'add', 'set', 'remove', 'toggle'],
|
||||||
allowConfigureFields: true,
|
allowConfigureFields: true,
|
||||||
},
|
},
|
||||||
destroy: {
|
destroy: {
|
||||||
displayName: '{{t("Delete")}}',
|
displayName: '{{t("Delete")}}',
|
||||||
aliases: ['destroy', 'remove'],
|
aliases: ['destroy'],
|
||||||
type: 'old-data',
|
type: 'old-data',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -357,19 +357,25 @@ export class PluginManager {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
let sourceDir = basename(dirname(__dirname)) === 'src' ? 'src' : 'dist';
|
|
||||||
const packageNames: string[] = items.map((item) => item.packageName);
|
const packageNames: string[] = items.map((item) => item.packageName);
|
||||||
const source = [];
|
const source = [];
|
||||||
for (const packageName of packageNames) {
|
for (const packageName of packageNames) {
|
||||||
const directory = join(packageName, sourceDir, 'server/commands/*.' + (sourceDir === 'src' ? 'ts' : 'js'));
|
const file = require.resolve(packageName);
|
||||||
source.push(directory);
|
const sourceDir = basename(dirname(file)) === 'src' ? 'src' : 'dist';
|
||||||
|
const directory = join(
|
||||||
|
packageName,
|
||||||
|
sourceDir,
|
||||||
|
'server/commands/*.' + (basename(dirname(file)) === 'src' ? 'ts' : 'js'),
|
||||||
|
);
|
||||||
|
source.push(directory.replaceAll(sep, '/'));
|
||||||
}
|
}
|
||||||
sourceDir = basename(dirname(__dirname)) === 'src' ? 'src' : 'lib';
|
|
||||||
for (const plugin of this.options.plugins || []) {
|
for (const plugin of this.options.plugins || []) {
|
||||||
if (typeof plugin === 'string') {
|
if (typeof plugin === 'string') {
|
||||||
const packageName = await PluginManager.getPackageName(plugin);
|
const packageName = await PluginManager.getPackageName(plugin);
|
||||||
|
const file = require.resolve(packageName);
|
||||||
|
const sourceDir = basename(dirname(file)) === 'src' ? 'src' : 'lib';
|
||||||
const directory = join(packageName, sourceDir, 'server/commands/*.' + (sourceDir === 'src' ? 'ts' : 'js'));
|
const directory = join(packageName, sourceDir, 'server/commands/*.' + (sourceDir === 'src' ? 'ts' : 'js'));
|
||||||
source.push(directory);
|
source.push(directory.replaceAll(sep, '/'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const files = await fg(source, {
|
const files = await fg(source, {
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
"default": "./lib/index.js"
|
"default": "./lib/index.js"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"types": "./vitest.d.ts",
|
"types": "./es/index.d.ts",
|
||||||
"default": "./vitest.mjs"
|
"default": "./es/index.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"./client": {
|
"./client": {
|
||||||
@ -22,8 +22,8 @@
|
|||||||
"default": "./lib/client/index.js"
|
"default": "./lib/client/index.js"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"types": "./lib/client/index.d.ts",
|
"types": "./es/client/index.d.ts",
|
||||||
"default": "./lib/client/index.js"
|
"default": "./es/client/index.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"./e2e": {
|
"./e2e": {
|
||||||
@ -32,11 +32,12 @@
|
|||||||
"default": "./lib/e2e/index.js"
|
"default": "./lib/e2e/index.js"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"types": "./lib/e2e/index.d.ts",
|
"types": "./es/e2e/index.d.ts",
|
||||||
"default": "./lib/e2e/index.js"
|
"default": "./es/e2e/index.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json",
|
||||||
|
"./vitest.mjs": "./vitest.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@faker-js/faker": "8.1.0",
|
"@faker-js/faker": "8.1.0",
|
||||||
@ -45,6 +46,7 @@
|
|||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@testing-library/user-event": "^14.4.3",
|
"@testing-library/user-event": "^14.4.3",
|
||||||
|
"@testing-library/jest-dom": "^6.4.2",
|
||||||
"@types/supertest": "^2.0.11",
|
"@types/supertest": "^2.0.11",
|
||||||
"@vitejs/plugin-react": "^4.0.0",
|
"@vitejs/plugin-react": "^4.0.0",
|
||||||
"jsdom": "^16.0.0",
|
"jsdom": "^16.0.0",
|
||||||
@ -58,7 +60,6 @@
|
|||||||
"supertest": "^6.1.6",
|
"supertest": "^6.1.6",
|
||||||
"vite": "^5.0.0",
|
"vite": "^5.0.0",
|
||||||
"vitest": "^1.4.0",
|
"vitest": "^1.4.0",
|
||||||
"vitest-dom": "^0.1.1",
|
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"
|
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import '@testing-library/jest-dom/vitest';
|
||||||
import { configure } from '@testing-library/react';
|
import { configure } from '@testing-library/react';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import 'vitest-dom/extend-expect';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解决 TypeError: URL.createObjectURL is not a function
|
* 解决 TypeError: URL.createObjectURL is not a function
|
||||||
|
22
packages/core/test/src/defineConfig.d.ts
vendored
22
packages/core/test/src/defineConfig.d.ts
vendored
@ -1,22 +0,0 @@
|
|||||||
declare global {
|
|
||||||
const suite: (typeof import('vitest'))['suite'];
|
|
||||||
const test: (typeof import('vitest'))['test'];
|
|
||||||
const describe: (typeof import('vitest'))['describe'];
|
|
||||||
const it: (typeof import('vitest'))['it'];
|
|
||||||
const expectTypeOf: (typeof import('vitest'))['expectTypeOf'];
|
|
||||||
const assertType: (typeof import('vitest'))['assertType'];
|
|
||||||
const expect: (typeof import('vitest'))['expect'];
|
|
||||||
const assert: (typeof import('vitest'))['assert'];
|
|
||||||
const vitest: (typeof import('vitest'))['vitest'];
|
|
||||||
const vi: (typeof import('vitest'))['vitest'];
|
|
||||||
const beforeAll: (typeof import('vitest'))['beforeAll'];
|
|
||||||
const afterAll: (typeof import('vitest'))['afterAll'];
|
|
||||||
const beforeEach: (typeof import('vitest'))['beforeEach'];
|
|
||||||
const afterEach: (typeof import('vitest'))['afterEach'];
|
|
||||||
}
|
|
||||||
import { type UserConfig } from 'vitest/config';
|
|
||||||
export declare const defineConfig: (
|
|
||||||
config?: UserConfig & {
|
|
||||||
server: boolean;
|
|
||||||
},
|
|
||||||
) => UserConfig;
|
|
@ -1,2 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
export * from '../vitest';
|
|
@ -53,7 +53,6 @@ export interface CollectionSetting {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
inherit?: boolean;
|
inherit?: boolean;
|
||||||
inherits?: string[];
|
|
||||||
category?: any[];
|
category?: any[];
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
description?: string;
|
description?: string;
|
||||||
@ -120,6 +119,23 @@ interface AclRoleSetting {
|
|||||||
key?: string;
|
key?: string;
|
||||||
//菜单权限配置
|
//菜单权限配置
|
||||||
menuUiSchemas?: string[];
|
menuUiSchemas?: string[];
|
||||||
|
dataSourceKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DatabaseSetting {
|
||||||
|
database: string;
|
||||||
|
host: string;
|
||||||
|
port: string;
|
||||||
|
schema?: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
interface DataSourceSetting {
|
||||||
|
key: string;
|
||||||
|
displayName: string;
|
||||||
|
type: string;
|
||||||
|
options: DatabaseSetting;
|
||||||
|
enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageConfig {
|
export interface PageConfig {
|
||||||
@ -229,6 +245,15 @@ interface ExtendUtils {
|
|||||||
* 更新角色权限配置
|
* 更新角色权限配置
|
||||||
*/
|
*/
|
||||||
updateRole: <T = any>(roleSetting: AclRoleSetting) => Promise<T>;
|
updateRole: <T = any>(roleSetting: AclRoleSetting) => Promise<T>;
|
||||||
|
/**
|
||||||
|
* 创建一个外部数据源(pg)
|
||||||
|
*/
|
||||||
|
mockExternalDataSource: <T = any>(DataSourceSetting: DataSourceSetting) => Promise<T>;
|
||||||
|
/**
|
||||||
|
* 删除外部数据源
|
||||||
|
* @param key 外部数据源key
|
||||||
|
*/
|
||||||
|
destoryExternalDataSource: <T = any>(key: string) => Promise<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PORT = process.env.APP_PORT || 20000;
|
const PORT = process.env.APP_PORT || 20000;
|
||||||
@ -437,6 +462,20 @@ const _test = base.extend<ExtendUtils>({
|
|||||||
|
|
||||||
await use(updateRole);
|
await use(updateRole);
|
||||||
},
|
},
|
||||||
|
mockExternalDataSource: async ({ browser }, use) => {
|
||||||
|
const mockExternalDataSource = async (DataSourceSetting: DataSourceSetting) => {
|
||||||
|
return createExternalDataSource(DataSourceSetting);
|
||||||
|
};
|
||||||
|
|
||||||
|
await use(mockExternalDataSource);
|
||||||
|
},
|
||||||
|
destoryExternalDataSource: async ({ browser }, use) => {
|
||||||
|
const destoryDataSource = async (key: string) => {
|
||||||
|
return destoryExternalDataSource(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
await use(destoryDataSource);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const test = Object.assign(_test, {
|
export const test = Object.assign(_test, {
|
||||||
@ -581,10 +620,6 @@ const deleteCollections = async (collectionNames: string[]) => {
|
|||||||
|
|
||||||
const result = await api.post(`/api/collections:destroy?${params}`, {
|
const result = await api.post(`/api/collections:destroy?${params}`, {
|
||||||
headers,
|
headers,
|
||||||
params: {
|
|
||||||
// 自动删除依赖于集合的对象(如视图),进而删除依赖于这些对象的所有对象
|
|
||||||
cascade: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.ok()) {
|
if (!result.ok()) {
|
||||||
@ -696,8 +731,12 @@ const updateRole = async (roleSetting: AclRoleSetting) => {
|
|||||||
const state = await api.storageState();
|
const state = await api.storageState();
|
||||||
const headers = getHeaders(state);
|
const headers = getHeaders(state);
|
||||||
const name = roleSetting.name;
|
const name = roleSetting.name;
|
||||||
|
const dataSourceKey = roleSetting.dataSourceKey;
|
||||||
|
|
||||||
const result = await api.post(`/api/roles:update?filterByTk=${name}`, {
|
const url = !dataSourceKey
|
||||||
|
? `/api/roles:update?filterByTk=${name}`
|
||||||
|
: `/api/dataSources/${dataSourceKey}/roles:update?filterByTk=${name}`;
|
||||||
|
const result = await api.post(url, {
|
||||||
headers,
|
headers,
|
||||||
data: { ...roleSetting },
|
data: { ...roleSetting },
|
||||||
});
|
});
|
||||||
@ -724,7 +763,48 @@ const setDefaultRole = async (name) => {
|
|||||||
data: { roleName: name },
|
data: { roleName: name },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 创建外部数据源
|
||||||
|
* @paramn
|
||||||
|
*/
|
||||||
|
const createExternalDataSource = async (dataSourceSetting: DataSourceSetting) => {
|
||||||
|
const api = await request.newContext({
|
||||||
|
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
|
||||||
|
});
|
||||||
|
const state = await api.storageState();
|
||||||
|
const headers = getHeaders(state);
|
||||||
|
const result = await api.post(`/api/dataSources:create`, {
|
||||||
|
headers,
|
||||||
|
data: { ...dataSourceSetting },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok()) {
|
||||||
|
throw new Error(await result.text());
|
||||||
|
}
|
||||||
|
const dataSourceData = (await result.json()).data;
|
||||||
|
return dataSourceData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除外部数据源
|
||||||
|
* @paramn
|
||||||
|
*/
|
||||||
|
const destoryExternalDataSource = async (key) => {
|
||||||
|
const api = await request.newContext({
|
||||||
|
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
|
||||||
|
});
|
||||||
|
const state = await api.storageState();
|
||||||
|
const headers = getHeaders(state);
|
||||||
|
const result = await api.post(`/api/dataSources:destroy?filterByTk=${key}`, {
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok()) {
|
||||||
|
throw new Error(await result.text());
|
||||||
|
}
|
||||||
|
const dataSourceData = (await result.json()).data;
|
||||||
|
return dataSourceData;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 根据 collection 的配置生成 Faker 数据
|
* 根据 collection 的配置生成 Faker 数据
|
||||||
* @param collectionSetting
|
* @param collectionSetting
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user