mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
Merge branch 'next' into feat/plugin-bulk-filter
This commit is contained in:
commit
3dc71ab222
24
CHANGELOG.md
24
CHANGELOG.md
@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14
|
||||||
|
|
||||||
|
### 🎉 New Features
|
||||||
|
|
||||||
|
- **[Departments]** Make Department, Attachment URL, and Workflow response message plugins free ([#6663](https://github.com/nocobase/nocobase/pull/6663)) by @chenos
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- **[client]**
|
||||||
|
- The filter form should not display the "Unsaved changes" prompt ([#6657](https://github.com/nocobase/nocobase/pull/6657)) by @zhangzhonghe
|
||||||
|
|
||||||
|
- "allow multiple" option not working for relation field ([#6661](https://github.com/nocobase/nocobase/pull/6661)) by @katherinehhh
|
||||||
|
|
||||||
|
- In the filter form, when the filter button is clicked, if there are fields that have not passed validation, the filtering is still triggered ([#6659](https://github.com/nocobase/nocobase/pull/6659)) by @zhangzhonghe
|
||||||
|
|
||||||
|
- Switching to the group menu should not jump to a page that has already been hidden in menu ([#6654](https://github.com/nocobase/nocobase/pull/6654)) by @zhangzhonghe
|
||||||
|
|
||||||
|
- **[File storage: S3(Pro)]**
|
||||||
|
- Organize language by @jiannx
|
||||||
|
|
||||||
|
- Individual baseurl and public settings, improve S3 pro storage config UX by @jiannx
|
||||||
|
|
||||||
|
- **[Migration manager]** the skip auto backup option becomes invalid if environment variable popup appears during migration by @gchust
|
||||||
|
|
||||||
## [v1.6.19](https://github.com/nocobase/nocobase/compare/v1.6.18...v1.6.19) - 2025-04-14
|
## [v1.6.19](https://github.com/nocobase/nocobase/compare/v1.6.18...v1.6.19) - 2025-04-14
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
@ -5,6 +5,30 @@
|
|||||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||||
并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。
|
并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。
|
||||||
|
|
||||||
|
## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14
|
||||||
|
|
||||||
|
### 🎉 新特性
|
||||||
|
|
||||||
|
- **[部门]** 商业插件部门、附件 URL、工作流响应消息改为免费提供 ([#6663](https://github.com/nocobase/nocobase/pull/6663)) by @chenos
|
||||||
|
|
||||||
|
### 🐛 修复
|
||||||
|
|
||||||
|
- **[client]**
|
||||||
|
- 筛选表单不应该显示“未保存修改”提示 ([#6657](https://github.com/nocobase/nocobase/pull/6657)) by @zhangzhonghe
|
||||||
|
|
||||||
|
- 筛选表单中关系字段的“允许多选”设置项不生效 ([#6661](https://github.com/nocobase/nocobase/pull/6661)) by @katherinehhh
|
||||||
|
|
||||||
|
- 筛选表单中,当点击筛选按钮时,如果有字段未校验通过,依然会触发筛选的问题 ([#6659](https://github.com/nocobase/nocobase/pull/6659)) by @zhangzhonghe
|
||||||
|
|
||||||
|
- 切换到分组菜单时,不应该跳转到已经在菜单中被隐藏的页面 ([#6654](https://github.com/nocobase/nocobase/pull/6654)) by @zhangzhonghe
|
||||||
|
|
||||||
|
- **[文件存储:S3 (Pro)]**
|
||||||
|
- 整理语言文案 by @jiannx
|
||||||
|
|
||||||
|
- baseurl 和 public 设置不再互相关联,改进 S3 pro 存储的配置交互体验 by @jiannx
|
||||||
|
|
||||||
|
- **[迁移管理]** 迁移时若弹出环境变量弹窗,跳过自动备份选项会失效 by @gchust
|
||||||
|
|
||||||
## [v1.6.19](https://github.com/nocobase/nocobase/compare/v1.6.18...v1.6.19) - 2025-04-14
|
## [v1.6.19](https://github.com/nocobase/nocobase/compare/v1.6.18...v1.6.19) - 2025-04-14
|
||||||
|
|
||||||
### 🐛 修复
|
### 🐛 修复
|
||||||
|
@ -6,7 +6,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
RUN cd /app \
|
RUN cd /app \
|
||||||
&& yarn config set network-timeout 600000 -g \
|
&& yarn config set network-timeout 600000 -g \
|
||||||
&& npx -y create-nocobase-app@${CNA_VERSION} my-nocobase-app -a -e APP_ENV=production \
|
&& npx -y create-nocobase-app@${CNA_VERSION} my-nocobase-app --skip-dev-dependencies -a -e APP_ENV=production \
|
||||||
&& cd /app/my-nocobase-app \
|
&& cd /app/my-nocobase-app \
|
||||||
&& yarn install --production
|
&& yarn install --production
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"npmClientArgs": ["--ignore-engines"],
|
"npmClientArgs": ["--ignore-engines"],
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/acl",
|
"name": "@nocobase/acl",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/resourcer": "1.7.0-beta.16",
|
"@nocobase/resourcer": "1.7.0-beta.18",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"minimatch": "^5.1.1"
|
"minimatch": "^5.1.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/actions",
|
"name": "@nocobase/actions",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/cache": "1.7.0-beta.16",
|
"@nocobase/cache": "1.7.0-beta.18",
|
||||||
"@nocobase/database": "1.7.0-beta.16",
|
"@nocobase/database": "1.7.0-beta.18",
|
||||||
"@nocobase/resourcer": "1.7.0-beta.16"
|
"@nocobase/resourcer": "1.7.0-beta.18"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/app",
|
"name": "@nocobase/app",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/database": "1.7.0-beta.16",
|
"@nocobase/database": "1.7.0-beta.18",
|
||||||
"@nocobase/preset-nocobase": "1.7.0-beta.16",
|
"@nocobase/preset-nocobase": "1.7.0-beta.18",
|
||||||
"@nocobase/server": "1.7.0-beta.16"
|
"@nocobase/server": "1.7.0-beta.18"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nocobase/client": "1.7.0-beta.16"
|
"@nocobase/client": "1.7.0-beta.18"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/auth",
|
"name": "@nocobase/auth",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/actions": "1.7.0-beta.16",
|
"@nocobase/actions": "1.7.0-beta.18",
|
||||||
"@nocobase/cache": "1.7.0-beta.16",
|
"@nocobase/cache": "1.7.0-beta.18",
|
||||||
"@nocobase/database": "1.7.0-beta.16",
|
"@nocobase/database": "1.7.0-beta.18",
|
||||||
"@nocobase/resourcer": "1.7.0-beta.16",
|
"@nocobase/resourcer": "1.7.0-beta.18",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"@types/jsonwebtoken": "^8.5.8",
|
"@types/jsonwebtoken": "^8.5.8",
|
||||||
"jsonwebtoken": "^8.5.1"
|
"jsonwebtoken": "^8.5.1"
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/build",
|
"name": "@nocobase/build",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "Library build tool based on rollup.",
|
"description": "Library build tool based on rollup.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"@lerna/project": "4.0.0",
|
"@lerna/project": "4.0.0",
|
||||||
"@rsbuild/plugin-babel": "^1.0.3",
|
"@rsbuild/plugin-babel": "^1.0.3",
|
||||||
"@rsdoctor/rspack-plugin": "^0.4.8",
|
"@rsdoctor/rspack-plugin": "^0.4.8",
|
||||||
"@rspack/core": "1.1.1",
|
"@rspack/core": "1.3.2",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@types/gulp": "^4.0.13",
|
"@types/gulp": "^4.0.13",
|
||||||
"@types/lerna__package": "5.1.0",
|
"@types/lerna__package": "5.1.0",
|
||||||
|
@ -347,6 +347,7 @@ export async function buildPluginClient(cwd: string, userConfig: UserConfig, sou
|
|||||||
umdNamedDefine: true,
|
umdNamedDefine: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
amd: {},
|
||||||
resolve: {
|
resolve: {
|
||||||
tsConfig: path.join(process.cwd(), 'tsconfig.json'),
|
tsConfig: path.join(process.cwd(), 'tsconfig.json'),
|
||||||
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.less', '.css'],
|
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.less', '.css'],
|
||||||
|
4
packages/core/cache/package.json
vendored
4
packages/core/cache/package.json
vendored
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/cache",
|
"name": "@nocobase/cache",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/lock-manager": "1.7.0-beta.16",
|
"@nocobase/lock-manager": "1.7.0-beta.18",
|
||||||
"bloom-filters": "^3.0.1",
|
"bloom-filters": "^3.0.1",
|
||||||
"cache-manager": "^5.2.4",
|
"cache-manager": "^5.2.4",
|
||||||
"cache-manager-redis-yet": "^4.1.2"
|
"cache-manager-redis-yet": "^4.1.2"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/cli",
|
"name": "@nocobase/cli",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./src/index.js",
|
"main": "./src/index.js",
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"nocobase": "./bin/index.js"
|
"nocobase": "./bin/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/app": "1.7.0-beta.16",
|
"@nocobase/app": "1.7.0-beta.18",
|
||||||
"@types/fs-extra": "^11.0.1",
|
"@types/fs-extra": "^11.0.1",
|
||||||
"@umijs/utils": "3.5.20",
|
"@umijs/utils": "3.5.20",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
@ -18,14 +18,13 @@
|
|||||||
"fast-glob": "^3.3.1",
|
"fast-glob": "^3.3.1",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.1.1",
|
||||||
"p-all": "3.0.0",
|
"p-all": "3.0.0",
|
||||||
"pm2": "^5.2.0",
|
"pm2": "^6.0.5",
|
||||||
"portfinder": "^1.0.28",
|
"portfinder": "^1.0.28",
|
||||||
"serve": "^13.0.2",
|
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
"tsx": "^4.19.0"
|
"tsx": "^4.19.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nocobase/devtools": "1.7.0-beta.16"
|
"@nocobase/devtools": "1.7.0-beta.18"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -460,8 +460,16 @@ exports.initEnv = function initEnv() {
|
|||||||
process.env.SOCKET_PATH = generateGatewayPath();
|
process.env.SOCKET_PATH = generateGatewayPath();
|
||||||
fs.mkdirpSync(dirname(process.env.SOCKET_PATH), { force: true, recursive: true });
|
fs.mkdirpSync(dirname(process.env.SOCKET_PATH), { force: true, recursive: true });
|
||||||
fs.mkdirpSync(process.env.PM2_HOME, { force: true, recursive: true });
|
fs.mkdirpSync(process.env.PM2_HOME, { force: true, recursive: true });
|
||||||
const pkgDir = resolve(process.cwd(), 'storage/plugins', '@nocobase/plugin-multi-app-manager');
|
const pkgs = [
|
||||||
fs.existsSync(pkgDir) && fs.rmdirSync(pkgDir, { recursive: true, force: true });
|
'@nocobase/plugin-multi-app-manager',
|
||||||
|
'@nocobase/plugin-departments',
|
||||||
|
'@nocobase/plugin-field-attachment-url',
|
||||||
|
'@nocobase/plugin-workflow-response-message',
|
||||||
|
];
|
||||||
|
for (const pkg of pkgs) {
|
||||||
|
const pkgDir = resolve(process.cwd(), 'storage/plugins', pkg);
|
||||||
|
fs.existsSync(pkgDir) && fs.rmdirSync(pkgDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generatePlugins = function () {
|
exports.generatePlugins = function () {
|
||||||
|
@ -234,6 +234,10 @@ export default defineConfig({
|
|||||||
"title": "Filter",
|
"title": "Filter",
|
||||||
"link": "/components/filter"
|
"link": "/components/filter"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "LinkageFilter",
|
||||||
|
"link": "/components/linkage-filter"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/client",
|
"name": "@nocobase/client",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"module": "es/index.mjs",
|
"module": "es/index.mjs",
|
||||||
@ -27,9 +27,9 @@
|
|||||||
"@formily/reactive-react": "^2.2.27",
|
"@formily/reactive-react": "^2.2.27",
|
||||||
"@formily/shared": "^2.2.27",
|
"@formily/shared": "^2.2.27",
|
||||||
"@formily/validator": "^2.2.27",
|
"@formily/validator": "^2.2.27",
|
||||||
"@nocobase/evaluators": "1.7.0-beta.16",
|
"@nocobase/evaluators": "1.7.0-beta.18",
|
||||||
"@nocobase/sdk": "1.7.0-beta.16",
|
"@nocobase/sdk": "1.7.0-beta.18",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"ahooks": "^3.7.2",
|
"ahooks": "^3.7.2",
|
||||||
"antd": "5.24.2",
|
"antd": "5.24.2",
|
||||||
"antd-style": "3.7.1",
|
"antd-style": "3.7.1",
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
||||||
import { DatePickerProvider, ActionBarProvider, SchemaComponentOptions } from '../schema-component';
|
import { FilterCollectionField } from '../modules/blocks/filter-blocks/FilterCollectionField';
|
||||||
|
import { ActionBarProvider, DatePickerProvider, SchemaComponentOptions } from '../schema-component';
|
||||||
import { DefaultValueProvider } from '../schema-settings';
|
import { DefaultValueProvider } from '../schema-settings';
|
||||||
import { CollectOperators } from './CollectOperators';
|
import { CollectOperators } from './CollectOperators';
|
||||||
import { FormBlockProvider } from './FormBlockProvider';
|
import { FormBlockProvider } from './FormBlockProvider';
|
||||||
import { FilterCollectionField } from '../modules/blocks/filter-blocks/FilterCollectionField';
|
|
||||||
|
|
||||||
export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
const filedSchema = useFieldSchema();
|
const filedSchema = useFieldSchema();
|
||||||
@ -35,7 +35,7 @@ export const FilterFormBlockProvider = withDynamicSchemaProps((props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DefaultValueProvider isAllowToSetDefaultValue={() => false}>
|
<DefaultValueProvider isAllowToSetDefaultValue={() => false}>
|
||||||
<FormBlockProvider name="filter-form" {...props}></FormBlockProvider>
|
<FormBlockProvider name="filter-form" {...props} confirmBeforeClose={false}></FormBlockProvider>
|
||||||
</DefaultValueProvider>
|
</DefaultValueProvider>
|
||||||
</ActionBarProvider>
|
</ActionBarProvider>
|
||||||
</DatePickerProvider>
|
</DatePickerProvider>
|
||||||
|
@ -546,9 +546,11 @@ export const useFilterBlockActionProps = () => {
|
|||||||
const { doFilter } = useDoFilter();
|
const { doFilter } = useDoFilter();
|
||||||
const actionField = useField();
|
const actionField = useField();
|
||||||
actionField.data = actionField.data || {};
|
actionField.data = actionField.data || {};
|
||||||
|
const form = useForm();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
|
await form.submit();
|
||||||
actionField.data.loading = true;
|
actionField.data.loading = true;
|
||||||
await doFilter();
|
await doFilter();
|
||||||
actionField.data.loading = false;
|
actionField.data.loading = false;
|
||||||
@ -1580,7 +1582,7 @@ export const getAppends = ({
|
|||||||
const fieldNames = getTargetField(item);
|
const fieldNames = getTargetField(item);
|
||||||
|
|
||||||
// 只应该收集关系字段,只有大于 1 的时候才是关系字段
|
// 只应该收集关系字段,只有大于 1 的时候才是关系字段
|
||||||
if (fieldNames.length > 1) {
|
if (fieldNames.length > 1 && !item.op) {
|
||||||
appends.add(fieldNames.join('.'));
|
appends.add(fieldNames.join('.'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,8 @@ import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/actio
|
|||||||
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
|
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
|
||||||
import { SchemaSettingsEnableChildCollections } from '../../../schema-settings/SchemaSettings';
|
import { SchemaSettingsEnableChildCollections } from '../../../schema-settings/SchemaSettings';
|
||||||
import { useOpenModeContext } from '../../popup/OpenModeProvider';
|
import { useOpenModeContext } from '../../popup/OpenModeProvider';
|
||||||
|
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
|
||||||
|
import { useDataBlockProps } from '../../../data-source';
|
||||||
|
|
||||||
export const addNewActionSettings = new SchemaSettings({
|
export const addNewActionSettings = new SchemaSettings({
|
||||||
name: 'actionSettings:addNew',
|
name: 'actionSettings:addNew',
|
||||||
@ -27,6 +29,16 @@ export const addNewActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'openMode',
|
name: 'openMode',
|
||||||
Component: SchemaSettingOpenModeSchemaItems,
|
Component: SchemaSettingOpenModeSchemaItems,
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
SecondConFirm,
|
SecondConFirm,
|
||||||
RefreshDataBlockRequest,
|
RefreshDataBlockRequest,
|
||||||
} from '../../../schema-component/antd/action/Action.Designer';
|
} from '../../../schema-component/antd/action/Action.Designer';
|
||||||
|
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
|
||||||
|
|
||||||
export const bulkDeleteActionSettings = new SchemaSettings({
|
export const bulkDeleteActionSettings = new SchemaSettings({
|
||||||
name: 'actionSettings:bulkDelete',
|
name: 'actionSettings:bulkDelete',
|
||||||
@ -27,6 +28,16 @@ export const bulkDeleteActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'secondConFirm',
|
name: 'secondConFirm',
|
||||||
Component: SecondConFirm,
|
Component: SecondConFirm,
|
||||||
|
@ -32,11 +32,9 @@ export const disassociateActionSettings = new SchemaSettings({
|
|||||||
name: 'linkageRules',
|
name: 'linkageRules',
|
||||||
Component: SchemaSettingsLinkageRules,
|
Component: SchemaSettingsLinkageRules,
|
||||||
useComponentProps() {
|
useComponentProps() {
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const { linkageRulesProps } = useSchemaToolbar();
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
return {
|
return {
|
||||||
...linkageRulesProps,
|
...linkageRulesProps,
|
||||||
collectionName: name,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -14,7 +14,7 @@ import { useDesignable } from '../../..';
|
|||||||
import { useSchemaToolbar } from '../../../application';
|
import { useSchemaToolbar } from '../../../application';
|
||||||
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
||||||
import { RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
import { RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
||||||
import { SchemaSettingsModalItem } from '../../../schema-settings';
|
import { SchemaSettingsModalItem, SchemaSettingsLinkageRules } from '../../../schema-settings';
|
||||||
|
|
||||||
function ButtonEditor() {
|
function ButtonEditor() {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
@ -110,6 +110,17 @@ export const expendableActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
sort: 100,
|
sort: 100,
|
||||||
|
@ -11,10 +11,9 @@ import { useField, useFieldSchema } from '@formily/react';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCollectionRecord, useDesignable } from '../../../';
|
import { useDesignable } from '../../../';
|
||||||
import { useSchemaToolbar } from '../../../application';
|
import { useSchemaToolbar } from '../../../application';
|
||||||
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
||||||
import { useCollection_deprecated } from '../../../collection-manager';
|
|
||||||
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
||||||
import {
|
import {
|
||||||
SchemaSettingsLinkageRules,
|
SchemaSettingsLinkageRules,
|
||||||
@ -22,6 +21,7 @@ import {
|
|||||||
SchemaSettingAccessControl,
|
SchemaSettingAccessControl,
|
||||||
} from '../../../schema-settings';
|
} from '../../../schema-settings';
|
||||||
import { useURLAndHTMLSchema } from './useURLAndHTMLSchema';
|
import { useURLAndHTMLSchema } from './useURLAndHTMLSchema';
|
||||||
|
import { useDataBlockProps } from '../../../data-source';
|
||||||
|
|
||||||
export const SchemaSettingsActionLinkItem: FC = () => {
|
export const SchemaSettingsActionLinkItem: FC = () => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
@ -94,16 +94,10 @@ export const customizeLinkActionSettings = new SchemaSettings({
|
|||||||
{
|
{
|
||||||
name: 'linkageRules',
|
name: 'linkageRules',
|
||||||
Component: SchemaSettingsLinkageRules,
|
Component: SchemaSettingsLinkageRules,
|
||||||
useVisible() {
|
|
||||||
const record = useCollectionRecord();
|
|
||||||
return !_.isEmpty(record?.data);
|
|
||||||
},
|
|
||||||
useComponentProps() {
|
useComponentProps() {
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const { linkageRulesProps } = useSchemaToolbar();
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
return {
|
return {
|
||||||
...linkageRulesProps,
|
...linkageRulesProps,
|
||||||
collectionName: name,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import { useSchemaToolbar } from '../../../application';
|
import { useSchemaToolbar } from '../../../application';
|
||||||
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
||||||
import { ButtonEditor, RemoveButton, SecondConFirm } from '../../../schema-component/antd/action/Action.Designer';
|
import { ButtonEditor, RemoveButton, SecondConFirm } from '../../../schema-component/antd/action/Action.Designer';
|
||||||
|
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
|
||||||
export const refreshActionSettings = new SchemaSettings({
|
export const refreshActionSettings = new SchemaSettings({
|
||||||
name: 'actionSettings:refresh',
|
name: 'actionSettings:refresh',
|
||||||
items: [
|
items: [
|
||||||
@ -22,6 +22,17 @@ export const refreshActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'secondConFirm',
|
name: 'secondConFirm',
|
||||||
Component: SecondConFirm,
|
Component: SecondConFirm,
|
||||||
|
@ -29,6 +29,7 @@ import { useCollectionState } from '../../../schema-settings/DataTemplates/hooks
|
|||||||
import { SchemaSettingsModalItem } from '../../../schema-settings/SchemaSettings';
|
import { SchemaSettingsModalItem } from '../../../schema-settings/SchemaSettings';
|
||||||
import { useParentPopupRecord } from '../../variable/variablesProvider/VariablePopupRecordProvider';
|
import { useParentPopupRecord } from '../../variable/variablesProvider/VariablePopupRecordProvider';
|
||||||
import { useDataBlockProps } from '../../../data-source';
|
import { useDataBlockProps } from '../../../data-source';
|
||||||
|
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
|
||||||
|
|
||||||
const Tree = connect(
|
const Tree = connect(
|
||||||
AntdTree,
|
AntdTree,
|
||||||
@ -149,6 +150,16 @@ export const createSubmitActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'secondConfirmation',
|
name: 'secondConfirmation',
|
||||||
Component: SecondConFirm,
|
Component: SecondConFirm,
|
||||||
|
@ -46,10 +46,6 @@ export const updateSubmitActionSettings = new SchemaSettings({
|
|||||||
collectionName: name,
|
collectionName: name,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
useVisible() {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
return !fieldSchema.parent['x-initializer'].includes('bulkEditForm');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'secondConfirmation',
|
name: 'secondConfirmation',
|
||||||
|
@ -6,17 +6,12 @@
|
|||||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useFieldSchema } from '@formily/react';
|
|
||||||
import { useSchemaToolbar } from '../../../application';
|
import { useSchemaToolbar } from '../../../application';
|
||||||
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
||||||
import { useCollection_deprecated } from '../../../collection-manager';
|
|
||||||
import { useCollection } from '../../../data-source';
|
|
||||||
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
||||||
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
|
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
|
||||||
import { SchemaSettingsLinkageRules, SchemaSettingAccessControl } from '../../../schema-settings';
|
import { SchemaSettingsLinkageRules, SchemaSettingAccessControl } from '../../../schema-settings';
|
||||||
import { useOpenModeContext } from '../../popup/OpenModeProvider';
|
import { useOpenModeContext } from '../../popup/OpenModeProvider';
|
||||||
import { useCurrentPopupRecord } from '../../variable/variablesProvider/VariablePopupRecordProvider';
|
|
||||||
|
|
||||||
export const customizePopupActionSettings = new SchemaSettings({
|
export const customizePopupActionSettings = new SchemaSettings({
|
||||||
name: 'actionSettings:popup',
|
name: 'actionSettings:popup',
|
||||||
@ -33,18 +28,11 @@ export const customizePopupActionSettings = new SchemaSettings({
|
|||||||
name: 'linkageRules',
|
name: 'linkageRules',
|
||||||
Component: SchemaSettingsLinkageRules,
|
Component: SchemaSettingsLinkageRules,
|
||||||
useComponentProps() {
|
useComponentProps() {
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const { linkageRulesProps } = useSchemaToolbar();
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
return {
|
return {
|
||||||
...linkageRulesProps,
|
...linkageRulesProps,
|
||||||
collectionName: name,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
useVisible() {
|
|
||||||
const { collection } = useCurrentPopupRecord() || {};
|
|
||||||
const currentCollection = useCollection();
|
|
||||||
return !collection || collection?.name === currentCollection?.name;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'openMode',
|
name: 'openMode',
|
||||||
|
@ -35,11 +35,16 @@ test.describe('linkage rules', () => {
|
|||||||
// 条件:singleLineText 字段的值包含 123 时
|
// 条件:singleLineText 字段的值包含 123 时
|
||||||
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
|
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
|
||||||
await page.getByText('Add condition', { exact: true }).click();
|
await page.getByText('Add condition', { exact: true }).click();
|
||||||
await page.getByTestId('select-filter-field').click();
|
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'singleLineText' }).click();
|
|
||||||
await page.getByLabel('Linkage rules').getByRole('tabpanel').getByRole('textbox').click();
|
|
||||||
await page.getByLabel('Linkage rules').getByRole('tabpanel').getByRole('textbox').fill('123');
|
|
||||||
|
|
||||||
|
await page.getByLabel('variable-button').first().click();
|
||||||
|
await page.getByText('Current form').last().click();
|
||||||
|
await page.getByText('Current form').last().click();
|
||||||
|
await page.getByRole('menuitemcheckbox', { name: 'singleLineText' }).locator('div').click();
|
||||||
|
|
||||||
|
// await page.getByRole('menuitemcheckbox', { name: 'singleLineText' }).click();
|
||||||
|
await page.getByTestId('right-filter-field').getByRole('textbox').click();
|
||||||
|
await page.getByTestId('right-filter-field').getByRole('textbox').fill('123');
|
||||||
|
await page.getByRole('tabpanel').getByRole('textbox').last().fill('123');
|
||||||
// action:禁用 longText 字段
|
// action:禁用 longText 字段
|
||||||
await page.getByText('Add property').click();
|
await page.getByText('Add property').click();
|
||||||
await page.getByTestId('select-linkage-property-field').click();
|
await page.getByTestId('select-linkage-property-field').click();
|
||||||
@ -81,7 +86,7 @@ test.describe('linkage rules', () => {
|
|||||||
// 修改第一组规则,使其条件中包含一个变量 --------------------------------------------------------------------------
|
// 修改第一组规则,使其条件中包含一个变量 --------------------------------------------------------------------------
|
||||||
// 当 singleLineText 字段的值包含 longText 字段的值时,禁用 longText 字段
|
// 当 singleLineText 字段的值包含 longText 字段的值时,禁用 longText 字段
|
||||||
await openLinkageRules();
|
await openLinkageRules();
|
||||||
await page.getByLabel('variable-button').click();
|
await page.getByLabel('variable-button').last().click();
|
||||||
await expectSupportedVariables(page, [
|
await expectSupportedVariables(page, [
|
||||||
'Constant',
|
'Constant',
|
||||||
'Current user',
|
'Current user',
|
||||||
@ -136,8 +141,13 @@ test.describe('linkage rules', () => {
|
|||||||
.getByText('Add condition', { exact: true })
|
.getByText('Add condition', { exact: true })
|
||||||
.last()
|
.last()
|
||||||
.click();
|
.click();
|
||||||
await page.getByRole('button', { name: 'Select field' }).click();
|
// await page.getByRole('button', { name: 'Select field' }).click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'number' }).click();
|
|
||||||
|
await page.getByTestId('left-filter-field').getByLabel('variable-button').last().click();
|
||||||
|
await page.getByText('Current form').last().click();
|
||||||
|
await page.getByText('Current form').last().click();
|
||||||
|
await page.getByRole('menuitemcheckbox', { name: 'number' }).locator('div').click();
|
||||||
|
|
||||||
await page.getByLabel('Linkage rules').getByRole('spinbutton').click();
|
await page.getByLabel('Linkage rules').getByRole('spinbutton').click();
|
||||||
await page.getByLabel('Linkage rules').getByRole('spinbutton').fill('123');
|
await page.getByLabel('Linkage rules').getByRole('spinbutton').fill('123');
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ test.describe('deprecated variables', () => {
|
|||||||
await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible();
|
await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible();
|
||||||
|
|
||||||
// 2. 但是变量列表中是禁用状态
|
// 2. 但是变量列表中是禁用状态
|
||||||
await page.locator('button').filter({ hasText: /^x$/ }).click();
|
await page.locator('button').filter({ hasText: /^x$/ }).last().click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).hover({ position: { x: 40, y: 12 } });
|
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).hover({ position: { x: 40, y: 12 } });
|
||||||
await expect(page.getByRole('tooltip', { name: 'This variable has been deprecated' })).toBeVisible();
|
await expect(page.getByRole('tooltip', { name: 'This variable has been deprecated' })).toBeVisible();
|
||||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toHaveClass(
|
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toHaveClass(
|
||||||
@ -45,11 +45,11 @@ test.describe('deprecated variables', () => {
|
|||||||
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
||||||
|
|
||||||
// 3. 当设置为其它变量后,再次打开,变量列表中的弃用变量不再显示
|
// 3. 当设置为其它变量后,再次打开,变量列表中的弃用变量不再显示
|
||||||
await page.locator('button').filter({ hasText: /^x$/ }).click();
|
await page.locator('button').filter({ hasText: /^x$/ }).last().click();
|
||||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current form right' })).toHaveCount(1);
|
await expect(page.getByRole('menuitemcheckbox', { name: 'Current form right' })).toHaveCount(1);
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
||||||
await expect(page.getByLabel('variable-tag').getByText('Current form / Nickname')).toBeVisible();
|
await expect(page.getByLabel('variable-tag').getByText('Current form / Nickname').last()).toBeVisible();
|
||||||
// 清空表达式
|
// 清空表达式
|
||||||
await page.getByLabel('textbox').clear();
|
await page.getByLabel('textbox').clear();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
@ -58,7 +58,7 @@ test.describe('deprecated variables', () => {
|
|||||||
await page.getByLabel('block-item-CardItem-users-form').hover();
|
await page.getByLabel('block-item-CardItem-users-form').hover();
|
||||||
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
|
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
||||||
await page.locator('button').filter({ hasText: /^x$/ }).click();
|
await page.locator('button').filter({ hasText: /^x$/ }).last().click();
|
||||||
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
|
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
|
||||||
// 使下拉菜单消失
|
// 使下拉菜单消失
|
||||||
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
|
||||||
|
@ -86,7 +86,6 @@ test.describe('configure fields', () => {
|
|||||||
await page.getByRole('menuitem', { name: 'manyToOne3' }).click();
|
await page.getByRole('menuitem', { name: 'manyToOne3' }).click();
|
||||||
await page.mouse.move(600, 0);
|
await page.mouse.move(600, 0);
|
||||||
await page.reload();
|
await page.reload();
|
||||||
|
|
||||||
await expect(page.getByLabel('block-item-CollectionField-general-form-general.manyToOne1-manyToOne1')).toHaveText(
|
await expect(page.getByLabel('block-item-CollectionField-general-form-general.manyToOne1-manyToOne1')).toHaveText(
|
||||||
`manyToOne1:${record.manyToOne1.id}`,
|
`manyToOne1:${record.manyToOne1.id}`,
|
||||||
);
|
);
|
||||||
|
@ -42,6 +42,7 @@ test.describe('where grid card block can be added', () => {
|
|||||||
await page.getByLabel('schema-initializer-Grid-').nth(1).hover();
|
await page.getByLabel('schema-initializer-Grid-').nth(1).hover();
|
||||||
await page.getByRole('menuitem', { name: 'Role name' }).click();
|
await page.getByRole('menuitem', { name: 'Role name' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
|
await page.reload();
|
||||||
await expect(page.getByText('Root')).toBeVisible();
|
await expect(page.getByText('Root')).toBeVisible();
|
||||||
await expect(page.getByText('Admin')).toBeVisible();
|
await expect(page.getByText('Admin')).toBeVisible();
|
||||||
await expect(page.getByText('Member')).toBeVisible();
|
await expect(page.getByText('Member')).toBeVisible();
|
||||||
|
@ -30,7 +30,6 @@ test('action linkage by row data', async ({ page, mockPage }) => {
|
|||||||
// 添加其他你需要的样式属性
|
// 添加其他你需要的样式属性
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(adminEditActionStyle.opacity).not.toBe('0.1');
|
expect(adminEditActionStyle.opacity).not.toBe('0.1');
|
||||||
expect(rootEditActionStyle.opacity).not.toBe('1');
|
expect(rootEditActionStyle.opacity).not.toBe('1');
|
||||||
});
|
});
|
||||||
|
@ -316,7 +316,8 @@ test.describe('actions schema settings', () => {
|
|||||||
|
|
||||||
// 添加一个条件:ID 等于 1
|
// 添加一个条件:ID 等于 1
|
||||||
await page.getByText('Add condition', { exact: true }).click();
|
await page.getByText('Add condition', { exact: true }).click();
|
||||||
await page.getByTestId('select-filter-field').click();
|
await page.getByTestId('left-filter-field').getByLabel('variable-button').click();
|
||||||
|
await page.getByText('Current record').last().click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
|
||||||
await page.getByRole('spinbutton').click();
|
await page.getByRole('spinbutton').click();
|
||||||
await page.getByRole('spinbutton').fill('1');
|
await page.getByRole('spinbutton').fill('1');
|
||||||
@ -340,7 +341,8 @@ test.describe('actions schema settings', () => {
|
|||||||
|
|
||||||
// 添加一个条件:ID 等于 1
|
// 添加一个条件:ID 等于 1
|
||||||
await page.getByRole('tabpanel').getByText('Add condition', { exact: true }).last().click();
|
await page.getByRole('tabpanel').getByText('Add condition', { exact: true }).last().click();
|
||||||
await page.getByRole('button', { name: 'Select field' }).click();
|
await page.getByTestId('left-filter-field').getByLabel('variable-button').last().click();
|
||||||
|
await page.getByText('Current record').last().click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
|
||||||
await page.getByRole('spinbutton').click();
|
await page.getByRole('spinbutton').click();
|
||||||
await page.getByRole('spinbutton').fill('1');
|
await page.getByRole('spinbutton').fill('1');
|
||||||
@ -902,7 +904,6 @@ test.describe('actions schema settings', () => {
|
|||||||
await page.getByRole('menuitem', { name: 'Submit' }).click();
|
await page.getByRole('menuitem', { name: 'Submit' }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
await page.getByRole('button', { name: 'Submit' }).click();
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
|
|
||||||
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-treeCollection').hover();
|
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-treeCollection').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Tree table' }).click();
|
await page.getByRole('menuitem', { name: 'Tree table' }).click();
|
||||||
|
|
||||||
@ -928,6 +929,7 @@ test.describe('actions schema settings', () => {
|
|||||||
await page.getByLabel('schema-initializer-Grid-form:').hover();
|
await page.getByLabel('schema-initializer-Grid-form:').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Parent', exact: true }).click();
|
await page.getByRole('menuitem', { name: 'Parent', exact: true }).click();
|
||||||
await page.mouse.move(300, 0);
|
await page.mouse.move(300, 0);
|
||||||
|
await page.reload();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
.getByLabel('block-item-CollectionField-')
|
.getByLabel('block-item-CollectionField-')
|
||||||
|
@ -27,7 +27,8 @@ test.describe('options of Select field in linkage rule', () => {
|
|||||||
await page.getByRole('switch', { name: 'On Off' }).click();
|
await page.getByRole('switch', { name: 'On Off' }).click();
|
||||||
await page.getByRole('button', { name: 'OK' }).click();
|
await page.getByRole('button', { name: 'OK' }).click();
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.getByRole('option', { name: 'option2' })).toBeVisible();
|
await page.getByLabel('block-item-CollectionField-').click();
|
||||||
|
await expect(page.getByRole('option', { name: 'option2' }).last()).toBeVisible();
|
||||||
await expect(page.getByRole('option', { name: 'option3' })).toBeVisible();
|
await expect(page.getByRole('option', { name: 'option3' })).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -215,7 +215,7 @@ test.describe('where to open a popup and what can be added to it', () => {
|
|||||||
await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
|
await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
|
||||||
|
|
||||||
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
|
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).click();
|
||||||
await expect(page.getByRole('menuitem', { name: 'Associated records' })).toHaveCount(1);
|
await expect(page.getByRole('menuitem', { name: 'Associated records' })).toHaveCount(1);
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'One to many' }).click();
|
await page.getByRole('menuitem', { name: 'One to many' }).click();
|
||||||
@ -282,7 +282,7 @@ test.describe('where to open a popup and what can be added to it', () => {
|
|||||||
await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
|
await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
|
||||||
|
|
||||||
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
|
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
await page.getByRole('menuitem', { name: 'Table right' }).click();
|
||||||
await expect(page.getByRole('menuitem', { name: 'Associated records' })).toHaveCount(1);
|
await expect(page.getByRole('menuitem', { name: 'Associated records' })).toHaveCount(1);
|
||||||
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
await page.getByRole('menuitem', { name: 'Associated records' }).hover();
|
||||||
await page.getByRole('menuitem', { name: 'One to many' }).click();
|
await page.getByRole('menuitem', { name: 'One to many' }).click();
|
||||||
|
@ -18,7 +18,7 @@ test.describe('variables', () => {
|
|||||||
await page.getByLabel('action-Action.Link-View-view-').hover();
|
await page.getByLabel('action-Action.Link-View-view-').hover();
|
||||||
await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:view-users').hover();
|
await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:view-users').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
||||||
await page.getByLabel('variable-button').click();
|
await page.getByTestId('left-filter-field').getByLabel('variable-button').click();
|
||||||
|
|
||||||
// 2. 断言应该显示的变量
|
// 2. 断言应该显示的变量
|
||||||
['Constant', 'Current user', 'Current role', 'API token', 'Date variables', 'Current record'].forEach(
|
['Constant', 'Current user', 'Current role', 'API token', 'Date variables', 'Current record'].forEach(
|
||||||
|
@ -22,14 +22,14 @@ test.describe('variable: Current Record', () => {
|
|||||||
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
||||||
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
|
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
|
||||||
await page.getByText('Add condition', { exact: true }).click();
|
await page.getByText('Add condition', { exact: true }).click();
|
||||||
await page.getByLabel('variable-button').click();
|
await page.getByLabel('variable-button').first().click();
|
||||||
|
|
||||||
// 当前表单中应该包含 “Nickname” 字段
|
// 当前表单中应该包含 “Nickname” 字段
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
||||||
|
|
||||||
// 当前对象中应该包含 “Role UID” 字段
|
// 当前对象中应该包含 “Role UID” 字段
|
||||||
await page.getByLabel('variable-button').click();
|
await page.getByLabel('variable-button').first().click();
|
||||||
await page.getByText('Current object').click();
|
await page.getByText('Current object').click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Current object right' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Current object right' }).click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Role UID' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Role UID' }).click();
|
||||||
@ -43,12 +43,12 @@ test.describe('variable: Current Record', () => {
|
|||||||
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
|
||||||
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
|
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
|
||||||
await page.getByText('Add condition', { exact: true }).click();
|
await page.getByText('Add condition', { exact: true }).click();
|
||||||
await page.getByLabel('variable-button').click();
|
await page.getByLabel('variable-button').first().click();
|
||||||
|
|
||||||
// 当前记录中应该包含 “Nickname” 字段
|
// 当前记录中应该包含 “Nickname” 字段
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).click();
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
|
||||||
await page.getByLabel('variable-button').click();
|
await page.getByLabel('variable-button').first().click();
|
||||||
|
|
||||||
// 当前对象中应该包含 “Role UID” 字段
|
// 当前对象中应该包含 “Role UID” 字段
|
||||||
await page.getByRole('menuitemcheckbox', { name: 'Current object right' }).click();
|
await page.getByRole('menuitemcheckbox', { name: 'Current object right' }).click();
|
||||||
|
@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { findFirstPageRoute, NocoBaseDesktopRouteType } from '..';
|
||||||
|
import { NocoBaseDesktopRoute } from '../convertRoutesToSchema';
|
||||||
|
|
||||||
|
describe('findFirstPageRoute', () => {
|
||||||
|
// 基本测试:空路由数组
|
||||||
|
it('should return undefined for empty routes array', () => {
|
||||||
|
const result = findFirstPageRoute([]);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 基本测试:undefined 路由数组
|
||||||
|
it('should return undefined for undefined routes', () => {
|
||||||
|
const result = findFirstPageRoute(undefined);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:只有一个页面路由
|
||||||
|
it('should find the first page route when there is only one page', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:多个页面路由
|
||||||
|
it('should find the first page route when there are multiple pages', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
schemaUid: 'page2',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:不同类型的路由混合
|
||||||
|
it('should find the first page route among mixed route types', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
schemaUid: 'link1',
|
||||||
|
type: NocoBaseDesktopRouteType.link,
|
||||||
|
title: 'Link 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:隐藏的菜单项
|
||||||
|
it('should ignore hidden menu items', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
hideInMenu: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
schemaUid: 'page2',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:嵌套路由
|
||||||
|
it('should find page route in nested group', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: NocoBaseDesktopRouteType.group,
|
||||||
|
title: 'Group 1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[0].children[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:多层嵌套路由
|
||||||
|
it('should find page route in deeply nested groups', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: NocoBaseDesktopRouteType.group,
|
||||||
|
title: 'Group 1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
type: NocoBaseDesktopRouteType.group,
|
||||||
|
title: 'Group 1-1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 111,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[0].children[0].children[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:复杂路由结构
|
||||||
|
it('should find the first visible page in a complex route structure', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: NocoBaseDesktopRouteType.group,
|
||||||
|
title: 'Group 1',
|
||||||
|
hideInMenu: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
type: NocoBaseDesktopRouteType.group,
|
||||||
|
title: 'Group 2',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
schemaUid: 'page2',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[1].children[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试:空组
|
||||||
|
it('should skip empty groups and find page in next group', () => {
|
||||||
|
const routes: NocoBaseDesktopRoute[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: NocoBaseDesktopRouteType.group,
|
||||||
|
title: 'Empty Group',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
schemaUid: 'page1',
|
||||||
|
type: NocoBaseDesktopRouteType.page,
|
||||||
|
title: 'Page 1',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = findFirstPageRoute(routes);
|
||||||
|
expect(result).toEqual(routes[1]);
|
||||||
|
});
|
||||||
|
});
|
@ -710,27 +710,11 @@ export const InternalAdminLayout = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function getDefaultPageUid(routes: NocoBaseDesktopRoute[]) {
|
|
||||||
// Find the first route of type "page"
|
|
||||||
for (const route of routes) {
|
|
||||||
if (route.type === NocoBaseDesktopRouteType.page) {
|
|
||||||
return route.schemaUid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.children?.length) {
|
|
||||||
const result = getDefaultPageUid(route.children);
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const NavigateToDefaultPage: FC = (props) => {
|
const NavigateToDefaultPage: FC = (props) => {
|
||||||
const { allAccessRoutes } = useAllAccessDesktopRoutes();
|
const { allAccessRoutes } = useAllAccessDesktopRoutes();
|
||||||
const location = useLocationNoUpdate();
|
const location = useLocationNoUpdate();
|
||||||
|
|
||||||
const defaultPageUid = getDefaultPageUid(allAccessRoutes);
|
const defaultPageUid = findFirstPageRoute(allAccessRoutes)?.schemaUid;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -962,16 +946,17 @@ function findRouteById(id: string, treeArray: any[]) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findFirstPageRoute(routes: NocoBaseDesktopRoute[]) {
|
export function findFirstPageRoute(routes: NocoBaseDesktopRoute[]) {
|
||||||
if (!routes) return;
|
if (!routes) return;
|
||||||
|
|
||||||
for (const route of routes) {
|
for (const route of routes.filter((item) => !item.hideInMenu)) {
|
||||||
if (route.type === NocoBaseDesktopRouteType.page) {
|
if (route.type === NocoBaseDesktopRouteType.page) {
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.children?.length) {
|
if (route.type === NocoBaseDesktopRouteType.group && route.children?.length) {
|
||||||
return findFirstPageRoute(route.children);
|
const result = findFirstPageRoute(route.children);
|
||||||
|
if (result) return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ import { useGetAriaLabelOfAction } from './hooks/useGetAriaLabelOfAction';
|
|||||||
import { ActionContextProps, ActionProps, ComposedAction } from './types';
|
import { ActionContextProps, ActionProps, ComposedAction } from './types';
|
||||||
import { linkageAction, setInitialActionState } from './utils';
|
import { linkageAction, setInitialActionState } from './utils';
|
||||||
import { NAMESPACE_UI_SCHEMA } from '../../../i18n/constant';
|
import { NAMESPACE_UI_SCHEMA } from '../../../i18n/constant';
|
||||||
|
import { BlockContext } from '../../../block-provider/BlockProvider';
|
||||||
|
|
||||||
// 这个要放到最下面,否则会导致前端单测失败
|
// 这个要放到最下面,否则会导致前端单测失败
|
||||||
import { useApp } from '../../../application';
|
import { useApp } from '../../../application';
|
||||||
@ -96,7 +97,9 @@ export const Action: ComposedAction = withDynamicSchemaProps(
|
|||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
const tarComponent = useComponent(component) || component;
|
const tarComponent = useComponent(component) || component;
|
||||||
const variables = useVariables();
|
const variables = useVariables();
|
||||||
const localVariables = useLocalVariables({ currentForm: { values: recordData, readPretty: false } as any });
|
const localVariables = useLocalVariables({
|
||||||
|
currentForm: { values: recordData, readPretty: false } as any,
|
||||||
|
});
|
||||||
const { visibleWithURL, setVisibleWithURL } = usePopupUtils();
|
const { visibleWithURL, setVisibleWithURL } = usePopupUtils();
|
||||||
const { setSubmitted } = useActionContext();
|
const { setSubmitted } = useActionContext();
|
||||||
const { getAriaLabel } = useGetAriaLabelOfAction(title);
|
const { getAriaLabel } = useGetAriaLabelOfAction(title);
|
||||||
@ -120,6 +123,7 @@ export const Action: ComposedAction = withDynamicSchemaProps(
|
|||||||
condition: v.condition,
|
condition: v.condition,
|
||||||
variables,
|
variables,
|
||||||
localVariables,
|
localVariables,
|
||||||
|
conditionType: v.conditionType,
|
||||||
},
|
},
|
||||||
app.jsonLogic,
|
app.jsonLogic,
|
||||||
);
|
);
|
||||||
@ -155,36 +159,38 @@ export const Action: ComposedAction = withDynamicSchemaProps(
|
|||||||
}, [onClick, fieldSchema, getAllDataBlocks]);
|
}, [onClick, fieldSchema, getAllDataBlocks]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InternalAction
|
<BlockContext.Provider value={{ name: 'action' }}>
|
||||||
containerRefKey={containerRefKey}
|
<InternalAction
|
||||||
fieldSchema={fieldSchema}
|
containerRefKey={containerRefKey}
|
||||||
designable={designable}
|
fieldSchema={fieldSchema}
|
||||||
field={field}
|
designable={designable}
|
||||||
icon={icon}
|
field={field}
|
||||||
loading={loading}
|
icon={icon}
|
||||||
handleMouseEnter={handleMouseEnter}
|
loading={loading}
|
||||||
tarComponent={tarComponent}
|
handleMouseEnter={handleMouseEnter}
|
||||||
className={className}
|
tarComponent={tarComponent}
|
||||||
type={props.type}
|
className={className}
|
||||||
Designer={Designer}
|
type={props.type}
|
||||||
onClick={handleClick}
|
Designer={Designer}
|
||||||
confirm={confirm}
|
onClick={onClick}
|
||||||
confirmTitle={confirmTitle}
|
confirm={confirm}
|
||||||
popover={popover}
|
confirmTitle={confirmTitle}
|
||||||
addChild={addChild}
|
popover={popover}
|
||||||
recordData={recordData}
|
addChild={addChild}
|
||||||
title={title}
|
recordData={recordData}
|
||||||
style={style}
|
title={title}
|
||||||
propsDisabled={propsDisabled}
|
style={style}
|
||||||
useAction={useAction}
|
propsDisabled={propsDisabled}
|
||||||
visibleWithURL={visibleWithURL}
|
useAction={useAction}
|
||||||
setVisibleWithURL={setVisibleWithURL}
|
visibleWithURL={visibleWithURL}
|
||||||
setSubmitted={setSubmitted}
|
setVisibleWithURL={setVisibleWithURL}
|
||||||
getAriaLabel={getAriaLabel}
|
setSubmitted={setSubmitted}
|
||||||
parentRecordData={parentRecordData}
|
getAriaLabel={getAriaLabel}
|
||||||
actionCallback={actionCallback}
|
parentRecordData={parentRecordData}
|
||||||
{...others}
|
actionCallback={actionCallback}
|
||||||
/>
|
{...others}
|
||||||
|
/>
|
||||||
|
</BlockContext.Provider>
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
{ displayName: 'Action' },
|
{ displayName: 'Action' },
|
||||||
|
@ -32,7 +32,7 @@ export const useGetAriaLabelOfAction = (title: string) => {
|
|||||||
let { name: blockName } = useBlockContext() || {};
|
let { name: blockName } = useBlockContext() || {};
|
||||||
const actionTitle = title || compile(fieldSchema.title);
|
const actionTitle = title || compile(fieldSchema.title);
|
||||||
collectionName = collectionName ? `-${collectionName}` : '';
|
collectionName = collectionName ? `-${collectionName}` : '';
|
||||||
blockName = blockName ? `-${blockName}` : '';
|
blockName = blockName && blockName !== 'action' ? `-${blockName}` : '';
|
||||||
action = action ? `-${action}` : '';
|
action = action ? `-${action}` : '';
|
||||||
recordName = recordName ? `-${recordName}` : '';
|
recordName = recordName ? `-${recordName}` : '';
|
||||||
|
|
||||||
|
@ -87,12 +87,14 @@ export const linkageAction = async (
|
|||||||
condition,
|
condition,
|
||||||
variables,
|
variables,
|
||||||
localVariables,
|
localVariables,
|
||||||
|
conditionType,
|
||||||
}: {
|
}: {
|
||||||
operator;
|
operator;
|
||||||
field;
|
field;
|
||||||
condition;
|
condition;
|
||||||
variables: VariablesContextType;
|
variables: VariablesContextType;
|
||||||
localVariables: VariableOption[];
|
localVariables: VariableOption[];
|
||||||
|
conditionType: 'advanced' | 'basic';
|
||||||
},
|
},
|
||||||
jsonLogic: any,
|
jsonLogic: any,
|
||||||
) => {
|
) => {
|
||||||
@ -101,7 +103,7 @@ export const linkageAction = async (
|
|||||||
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case ActionType.Visible:
|
case ActionType.Visible:
|
||||||
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) {
|
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) {
|
||||||
displayResult.push(operator);
|
displayResult.push(operator);
|
||||||
field.data = field.data || {};
|
field.data = field.data || {};
|
||||||
field.data.hidden = false;
|
field.data.hidden = false;
|
||||||
@ -113,7 +115,7 @@ export const linkageAction = async (
|
|||||||
field.display = last(displayResult);
|
field.display = last(displayResult);
|
||||||
break;
|
break;
|
||||||
case ActionType.Hidden:
|
case ActionType.Hidden:
|
||||||
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) {
|
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) {
|
||||||
field.data = field.data || {};
|
field.data = field.data || {};
|
||||||
field.data.hidden = true;
|
field.data.hidden = true;
|
||||||
} else {
|
} else {
|
||||||
@ -122,7 +124,7 @@ export const linkageAction = async (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ActionType.Disabled:
|
case ActionType.Disabled:
|
||||||
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) {
|
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) {
|
||||||
disableResult.push(true);
|
disableResult.push(true);
|
||||||
}
|
}
|
||||||
field.stateOfLinkageRules = {
|
field.stateOfLinkageRules = {
|
||||||
@ -133,7 +135,7 @@ export const linkageAction = async (
|
|||||||
field.componentProps['disabled'] = last(disableResult);
|
field.componentProps['disabled'] = last(disableResult);
|
||||||
break;
|
break;
|
||||||
case ActionType.Active:
|
case ActionType.Active:
|
||||||
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) {
|
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) {
|
||||||
disableResult.push(false);
|
disableResult.push(false);
|
||||||
} else {
|
} else {
|
||||||
disableResult.push(!!field.componentProps?.['disabled']);
|
disableResult.push(!!field.componentProps?.['disabled']);
|
||||||
|
@ -27,7 +27,7 @@ export const useGetAriaLabelOfBlockItem = (name?: string) => {
|
|||||||
let { name: blockName } = useBlockContext() || {};
|
let { name: blockName } = useBlockContext() || {};
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let { name: collectionName, getField } = useCollection_deprecated();
|
let { name: collectionName, getField } = useCollection_deprecated();
|
||||||
blockName = name || blockName;
|
blockName = name || (blockName !== 'action' ? blockName : '');
|
||||||
|
|
||||||
const title = compile(fieldSchema['title']) || compile(getField(fieldSchema.name)?.uiSchema?.title);
|
const title = compile(fieldSchema['title']) || compile(getField(fieldSchema.name)?.uiSchema?.title);
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ describe('CollectionSelect', () => {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="block-item-demo title"
|
aria-label="block-item-demo title"
|
||||||
class="nb-block-item nb-form-item css-9qorhu ant-nb-block-item css-dev-only-do-not-override-1rquknz"
|
class="nb-block-item nb-form-item css-1elzyjx ant-nb-block-item css-dev-only-do-not-override-1rquknz"
|
||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -191,7 +191,7 @@ describe('CollectionSelect', () => {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="block-item-demo title"
|
aria-label="block-item-demo title"
|
||||||
class="nb-block-item nb-form-item css-9qorhu ant-nb-block-item css-dev-only-do-not-override-1rquknz"
|
class="nb-block-item nb-form-item css-1elzyjx ant-nb-block-item css-dev-only-do-not-override-1rquknz"
|
||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -39,15 +39,13 @@ const formItemWrapCss = css`
|
|||||||
.ant-description-textarea img {
|
.ant-description-textarea img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
&.ant-formily-item-layout-horizontal.ant-formily-item-label-wrap {
|
&.ant-formily-item-layout-vertical .ant-formily-item-label {
|
||||||
.ant-formily-item-label {
|
display: inline;
|
||||||
|
.ant-formily-item-label-tooltip-icon {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.ant-formily-item-label-content {
|
||||||
display: inline;
|
display: inline;
|
||||||
padding-right: 5px;
|
|
||||||
|
|
||||||
.ant-formily-item-label-tooltip-icon,
|
|
||||||
.ant-formily-item-label-content {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -89,7 +87,7 @@ export const FormItem: any = withDynamicSchemaProps(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
t(field.description, { ns: NAMESPACE_UI_SCHEMA })
|
field.description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [field.description]);
|
}, [field.description]);
|
||||||
@ -114,9 +112,6 @@ export const FormItem: any = withDynamicSchemaProps(
|
|||||||
? '100% !important'
|
? '100% !important'
|
||||||
: null};
|
: null};
|
||||||
}
|
}
|
||||||
.ant-formily-item-control {
|
|
||||||
padding: ${showTitle === false ? '5px' : '0px'};
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -20,6 +20,7 @@ import { useAttach, useComponent } from '../..';
|
|||||||
import { useApp } from '../../../application';
|
import { useApp } from '../../../application';
|
||||||
import { getCardItemSchema } from '../../../block-provider';
|
import { getCardItemSchema } from '../../../block-provider';
|
||||||
import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider';
|
import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider';
|
||||||
|
import { useDataBlockProps } from '../../../data-source';
|
||||||
import { useDataBlockRequest } from '../../../data-source/data-block/DataBlockRequestProvider';
|
import { useDataBlockRequest } from '../../../data-source/data-block/DataBlockRequestProvider';
|
||||||
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
|
||||||
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||||
@ -150,12 +151,15 @@ const WithForm = (props: WithFormProps) => {
|
|||||||
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) || [];
|
||||||
|
|
||||||
|
// 关闭弹窗之前,如果有未保存的数据,是否要二次确认
|
||||||
|
const { confirmBeforeClose = true } = useDataBlockProps() || ({} as any);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = uid();
|
const id = uid();
|
||||||
|
|
||||||
form.addEffects(id, () => {
|
form.addEffects(id, () => {
|
||||||
onFormInputChange(() => {
|
onFormInputChange(() => {
|
||||||
setFormValueChanged?.(true);
|
setFormValueChanged?.(confirmBeforeClose);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -166,7 +170,7 @@ const WithForm = (props: WithFormProps) => {
|
|||||||
return () => {
|
return () => {
|
||||||
form.removeEffects(id);
|
form.removeEffects(id);
|
||||||
};
|
};
|
||||||
}, [form, props.disabled, setFormValueChanged]);
|
}, [form, props.disabled, setFormValueChanged, confirmBeforeClose]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@ -219,17 +223,19 @@ const WithForm = (props: WithFormProps) => {
|
|||||||
const WithoutForm = (props) => {
|
const WithoutForm = (props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { setFormValueChanged } = useActionContext();
|
const { setFormValueChanged } = useActionContext();
|
||||||
|
// 关闭弹窗之前,如果有未保存的数据,是否要二次确认
|
||||||
|
const { confirmBeforeClose = true } = useDataBlockProps() || ({} as any);
|
||||||
const form = useMemo(
|
const form = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createForm({
|
createForm({
|
||||||
disabled: props.disabled,
|
disabled: props.disabled,
|
||||||
effects() {
|
effects() {
|
||||||
onFormInputChange((form) => {
|
onFormInputChange((form) => {
|
||||||
setFormValueChanged?.(true);
|
setFormValueChanged?.(confirmBeforeClose);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[],
|
[confirmBeforeClose],
|
||||||
);
|
);
|
||||||
return fieldSchema['x-decorator'] === 'FormV2' ? (
|
return fieldSchema['x-decorator'] === 'FormV2' ? (
|
||||||
<FormDecorator form={form} {...props} />
|
<FormDecorator form={form} {...props} />
|
||||||
|
@ -66,4 +66,5 @@ export * from './unix-timestamp';
|
|||||||
export * from './upload';
|
export * from './upload';
|
||||||
export * from './variable';
|
export * from './variable';
|
||||||
export * from './form-drawer';
|
export * from './form-drawer';
|
||||||
|
export * from './linkageFilter';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createForm, onFieldValueChange } from '@formily/core';
|
||||||
|
import { FieldContext, FormContext } from '@formily/react';
|
||||||
|
import { merge } from '@formily/shared';
|
||||||
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
|
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
|
||||||
|
import { SchemaComponent } from '../../core';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
import { VariableInput, getShouldChange } from '../../../schema-settings/VariableInput/VariableInput';
|
||||||
|
import { useCollectionRecordData } from '../../../data-source';
|
||||||
|
import { useLocalVariables, useVariables } from '../../../variables';
|
||||||
|
import { useCollectionManager_deprecated } from '../../../collection-manager';
|
||||||
|
|
||||||
|
export interface DynamicComponentProps {
|
||||||
|
value: any;
|
||||||
|
/**
|
||||||
|
* `Filter` 组件左侧选择的字段
|
||||||
|
*/
|
||||||
|
collectionField: CollectionFieldOptions_deprecated;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
renderSchemaComponent: () => React.JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value: any;
|
||||||
|
collectionField?: CollectionFieldOptions_deprecated;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
componentProps?: any;
|
||||||
|
schema?: any;
|
||||||
|
setScopes?: any;
|
||||||
|
testid?: string;
|
||||||
|
nullable?: boolean;
|
||||||
|
constantAbel?: boolean;
|
||||||
|
changeOnSelect?: boolean;
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DynamicComponent = (props: Props) => {
|
||||||
|
const { setScopes, nullable, constantAbel, changeOnSelect, readOnly = false } = props;
|
||||||
|
const { disabled } = useContext(FilterContext) || {};
|
||||||
|
const record = useCollectionRecordData();
|
||||||
|
const variables = useVariables();
|
||||||
|
const localVariables = useLocalVariables();
|
||||||
|
const { getAllCollectionsInheritChain } = useCollectionManager_deprecated();
|
||||||
|
const { collectionField } = props;
|
||||||
|
const component = useCallback((props: DynamicComponentProps) => {
|
||||||
|
return (
|
||||||
|
<VariableInput
|
||||||
|
{...props}
|
||||||
|
form={form}
|
||||||
|
record={record}
|
||||||
|
setScopes={setScopes}
|
||||||
|
nullable={nullable}
|
||||||
|
constantAbel={constantAbel}
|
||||||
|
changeOnSelect={changeOnSelect}
|
||||||
|
shouldChange={getShouldChange({
|
||||||
|
collectionField,
|
||||||
|
variables,
|
||||||
|
localVariables,
|
||||||
|
getAllCollectionsInheritChain,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
const form = useMemo(() => {
|
||||||
|
return createForm({
|
||||||
|
values: {
|
||||||
|
value: props.value,
|
||||||
|
},
|
||||||
|
effects() {
|
||||||
|
onFieldValueChange('value', (field) => {
|
||||||
|
props?.onChange?.(field.value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
disabled,
|
||||||
|
});
|
||||||
|
}, [JSON.stringify(props.value), props.schema]);
|
||||||
|
const renderSchemaComponent: any = useCallback(() => {
|
||||||
|
const componentProps = merge(props?.schema?.['x-component-props'] || {}, props.componentProps || {});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldContext.Provider value={null}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
'x-component': 'Input',
|
||||||
|
...props.schema,
|
||||||
|
'x-component-props': merge(componentProps, {
|
||||||
|
style: {
|
||||||
|
minWidth: 150,
|
||||||
|
...props.style,
|
||||||
|
},
|
||||||
|
utc: false,
|
||||||
|
readOnly: readOnly,
|
||||||
|
}),
|
||||||
|
name: 'value',
|
||||||
|
'x-read-pretty': false,
|
||||||
|
'x-validator': undefined,
|
||||||
|
'x-decorator': undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
);
|
||||||
|
}, [props.schema]);
|
||||||
|
return (
|
||||||
|
<FormContext.Provider value={form}>
|
||||||
|
<div data-testid={props.testid}>
|
||||||
|
{React.createElement<DynamicComponentProps>(component, {
|
||||||
|
value: props.value,
|
||||||
|
collectionField: props.collectionField,
|
||||||
|
onChange: props?.onChange,
|
||||||
|
renderSchemaComponent,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</FormContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FilterDynamicComponent = DynamicComponent;
|
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { ObjectField as ObjectFieldModel } from '@formily/core';
|
||||||
|
import { ArrayField, connect, useField } from '@formily/react';
|
||||||
|
import { Select, Space } from 'antd';
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import { useToken } from '../__builtins__';
|
||||||
|
import { FilterItems } from './FilterItems';
|
||||||
|
import { FilterLogicContext, RemoveConditionContext } from './context';
|
||||||
|
|
||||||
|
export const FilterGroup = connect((props) => {
|
||||||
|
const { bordered = true, disabled } = props;
|
||||||
|
const field = useField<ObjectFieldModel>();
|
||||||
|
const remove = useContext(RemoveConditionContext);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { token } = useToken();
|
||||||
|
|
||||||
|
const keys = Object.keys(field.value || {});
|
||||||
|
const logic = keys.includes('$or') ? '$or' : '$and';
|
||||||
|
const setLogic = (value) => {
|
||||||
|
const obj = field.value || {};
|
||||||
|
field.value = {
|
||||||
|
[value]: [...(obj[logic] || [])],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const mergedDisabled = disabled || field.disabled;
|
||||||
|
return (
|
||||||
|
<FilterLogicContext.Provider value={logic}>
|
||||||
|
<div
|
||||||
|
style={
|
||||||
|
bordered
|
||||||
|
? {
|
||||||
|
position: 'relative',
|
||||||
|
border: `1px dashed ${token.colorBorder}`,
|
||||||
|
padding: token.paddingSM,
|
||||||
|
marginBottom: token.marginXS,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
position: 'relative',
|
||||||
|
marginBottom: token.marginXS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{remove && !mergedDisabled && (
|
||||||
|
<a role="button" aria-label="icon-close">
|
||||||
|
<CloseCircleOutlined
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 10,
|
||||||
|
top: 10,
|
||||||
|
color: '#bfbfbf',
|
||||||
|
}}
|
||||||
|
onClick={() => remove()}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
<div style={{ marginBottom: 8, color: token.colorText }}>
|
||||||
|
<Trans>
|
||||||
|
{'Meet '}
|
||||||
|
<Select
|
||||||
|
// @ts-ignore
|
||||||
|
role="button"
|
||||||
|
data-testid="filter-select-all-or-any"
|
||||||
|
style={{ width: 'auto' }}
|
||||||
|
value={logic}
|
||||||
|
onChange={(value) => {
|
||||||
|
setLogic(value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Option value={'$and'}>All</Select.Option>
|
||||||
|
<Select.Option value={'$or'}>Any</Select.Option>
|
||||||
|
</Select>
|
||||||
|
{' conditions in the group'}
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ArrayField name={`${logic}`} component={[FilterItems]} disabled={mergedDisabled} />
|
||||||
|
</div>
|
||||||
|
{!mergedDisabled && (
|
||||||
|
<Space size={16} style={{ marginTop: 8, marginBottom: 8 }}>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
const value = field.value || {};
|
||||||
|
const items = value[logic] || [];
|
||||||
|
items.push({});
|
||||||
|
field.value = {
|
||||||
|
[logic]: items,
|
||||||
|
};
|
||||||
|
field.initialValue = {
|
||||||
|
[logic]: items,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Add condition')}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
const value = field.value || {};
|
||||||
|
const items = value[logic] || [];
|
||||||
|
items.push({
|
||||||
|
$and: [{}],
|
||||||
|
});
|
||||||
|
field.value = {
|
||||||
|
[logic]: items,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Add condition group')}
|
||||||
|
</a>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</FilterLogicContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ArrayField as ArrayFieldModel } from '@formily/core';
|
||||||
|
import { ObjectField, observer, useField } from '@formily/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { FilterGroup } from './FilterGroup';
|
||||||
|
import { LinkageFilterItem } from './LinkageFilterItem';
|
||||||
|
import { RemoveConditionContext } from './context';
|
||||||
|
|
||||||
|
export const FilterItems = observer(
|
||||||
|
(props) => {
|
||||||
|
const field = useField<ArrayFieldModel>();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{field?.value?.filter(Boolean).map((item, index) => {
|
||||||
|
return (
|
||||||
|
<RemoveConditionContext.Provider key={index} value={() => field.remove(index)}>
|
||||||
|
<ObjectField name={index} component={[item.$and || item.$or ? FilterGroup : LinkageFilterItem]} />
|
||||||
|
</RemoveConditionContext.Provider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'FilterItems' },
|
||||||
|
);
|
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ObjectField as ObjectFieldModel } from '@formily/core';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { UseRequestOptions, useRequest } from '../../../api-client';
|
||||||
|
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
|
||||||
|
import { useProps } from '../../hooks/useProps';
|
||||||
|
import { FilterGroup } from './FilterGroup';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
|
||||||
|
const useDef = (options: UseRequestOptions) => {
|
||||||
|
const field = useField<ObjectFieldModel>();
|
||||||
|
return useRequest(() => Promise.resolve({ data: field.dataSource }), options);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LinkageFilter: any = withDynamicSchemaProps(
|
||||||
|
observer((props: any) => {
|
||||||
|
const { useDataSource = useDef } = props;
|
||||||
|
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
|
const { dynamicComponent, className, collectionName } = useProps(props);
|
||||||
|
const [scopes, setScopes] = useState([]);
|
||||||
|
|
||||||
|
const field = useField<ObjectFieldModel>();
|
||||||
|
const fieldSchema: any = useFieldSchema();
|
||||||
|
useDataSource({
|
||||||
|
onSuccess(data) {
|
||||||
|
field.dataSource = data?.data || [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (fieldSchema.defaultValue) {
|
||||||
|
field.initialValue = fieldSchema.defaultValue;
|
||||||
|
}
|
||||||
|
}, [fieldSchema.defaultValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<FilterContext.Provider
|
||||||
|
value={{
|
||||||
|
field,
|
||||||
|
fieldSchema,
|
||||||
|
dynamicComponent,
|
||||||
|
disabled: props.disabled,
|
||||||
|
collectionName,
|
||||||
|
scopes,
|
||||||
|
setScopes,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FilterGroup {...props} bordered={false} />
|
||||||
|
</FilterContext.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
{ displayName: 'LinkageFilter' },
|
||||||
|
);
|
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { observer } from '@formily/react';
|
||||||
|
import { Select, Space } from 'antd';
|
||||||
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useCompile } from '../../hooks';
|
||||||
|
import { DynamicComponent } from './DynamicComponent';
|
||||||
|
import { RemoveConditionContext } from './context';
|
||||||
|
import { useValues } from './useValues';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
|
||||||
|
export const LinkageFilterItem = observer(
|
||||||
|
(props: any) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const compile = useCompile();
|
||||||
|
const remove = useContext(RemoveConditionContext);
|
||||||
|
const { setScopes } = useContext(FilterContext) || {};
|
||||||
|
const { schema, operators, operator, setOperator, rightVar, leftVar, setLeftValue, setRightValue } = useValues();
|
||||||
|
const style = useMemo(() => ({ marginBottom: 8 }), []);
|
||||||
|
|
||||||
|
const onOperatorsChange = useCallback(
|
||||||
|
(value) => {
|
||||||
|
setOperator(value);
|
||||||
|
},
|
||||||
|
[setOperator],
|
||||||
|
);
|
||||||
|
const removeStyle = useMemo(() => ({ color: '#bfbfbf' }), []);
|
||||||
|
return (
|
||||||
|
// 添加 nc-filter-item 类名是为了帮助编写测试时更容易选中该元素
|
||||||
|
<div style={style} className="nc-filter-item">
|
||||||
|
<Space wrap>
|
||||||
|
<DynamicComponent
|
||||||
|
value={leftVar}
|
||||||
|
onChange={setLeftValue}
|
||||||
|
setScopes={setScopes}
|
||||||
|
testid="left-filter-field"
|
||||||
|
nullable={false}
|
||||||
|
constantAbel={false}
|
||||||
|
changeOnSelect={false}
|
||||||
|
readOnly={true}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
// @ts-ignore
|
||||||
|
role="button"
|
||||||
|
data-testid="select-filter-operator"
|
||||||
|
className={css`
|
||||||
|
min-width: 110px;
|
||||||
|
`}
|
||||||
|
popupMatchSelectWidth={false}
|
||||||
|
value={operator?.value}
|
||||||
|
options={compile(operators)}
|
||||||
|
onChange={onOperatorsChange}
|
||||||
|
placeholder={t('Comparision')}
|
||||||
|
/>
|
||||||
|
{!operator?.noValue ? (
|
||||||
|
<DynamicComponent value={rightVar} schema={schema} onChange={setRightValue} testid="right-filter-field" />
|
||||||
|
) : null}
|
||||||
|
{!props.disabled && (
|
||||||
|
<a role="button" aria-label="icon-close">
|
||||||
|
<CloseCircleOutlined onClick={remove} style={removeStyle} />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'FilterItem' },
|
||||||
|
);
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ObjectField } from '@formily/core';
|
||||||
|
import { Schema } from '@formily/react';
|
||||||
|
import { ComponentType, createContext } from 'react';
|
||||||
|
import { DynamicComponentProps } from './DynamicComponent';
|
||||||
|
|
||||||
|
export interface FilterContextProps {
|
||||||
|
field?: ObjectField & { collectionName?: string };
|
||||||
|
fieldSchema?: Schema;
|
||||||
|
dynamicComponent?: ComponentType<DynamicComponentProps>;
|
||||||
|
disabled?: boolean;
|
||||||
|
collectionName?: string;
|
||||||
|
scopes?: any[];
|
||||||
|
setScopes?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RemoveConditionContext = createContext(null);
|
||||||
|
RemoveConditionContext.displayName = 'RemoveConditionContext';
|
||||||
|
export const FilterContext = createContext<FilterContextProps>(null);
|
||||||
|
FilterContext.displayName = 'FilterContext';
|
||||||
|
export const FilterLogicContext = createContext(null);
|
||||||
|
FilterLogicContext.displayName = 'FilterLogicContext';
|
@ -0,0 +1,148 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useField, observer, ISchema } from '@formily/react';
|
||||||
|
import { FilterActionProps, useRequest, SchemaComponent, Plugin } from '@nocobase/client';
|
||||||
|
import { ArrayCollapse, FormLayout } from '@formily/antd-v5';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
|
import { mockApp } from '@nocobase/client/demo-utils';
|
||||||
|
|
||||||
|
const ShowFilterData = observer(({ children }) => {
|
||||||
|
const field = useField<any>();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<pre>{JSON.stringify(field.value, null, 2)}</pre>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const useFilterActionProps = (): FilterActionProps => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const { run } = useRequest({ url: 'test' }, { manual: true });
|
||||||
|
|
||||||
|
return {
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
console.log('onSubmit', values);
|
||||||
|
|
||||||
|
// request api
|
||||||
|
run(values);
|
||||||
|
|
||||||
|
field.setValue(values);
|
||||||
|
},
|
||||||
|
onReset: (values) => {
|
||||||
|
console.log('onReset', values);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
'x-decorator': 'FormV2',
|
||||||
|
'x-component': 'ShowFormData',
|
||||||
|
properties: {
|
||||||
|
rules: {
|
||||||
|
type: 'array',
|
||||||
|
default: [{}],
|
||||||
|
'x-component': 'ArrayCollapse',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component-props': {
|
||||||
|
accordion: true,
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'ArrayCollapse.CollapsePanel',
|
||||||
|
'x-component-props': {
|
||||||
|
// extra: 'linkage rule',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
layout: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'FormLayout',
|
||||||
|
'x-component-props': {
|
||||||
|
labelStyle: {
|
||||||
|
marginTop: '6px',
|
||||||
|
},
|
||||||
|
labelCol: 8,
|
||||||
|
wrapperCol: 16,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
conditions: {
|
||||||
|
'x-component': 'h4',
|
||||||
|
'x-content': '{{ t("Condition") }}',
|
||||||
|
},
|
||||||
|
condition: {
|
||||||
|
'x-component': 'LinkageFilter',
|
||||||
|
'x-use-component-props': () => {
|
||||||
|
return {
|
||||||
|
// options,
|
||||||
|
className: css`
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 10px;
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.Remove',
|
||||||
|
},
|
||||||
|
moveUp: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.MoveUp',
|
||||||
|
},
|
||||||
|
moveDown: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.MoveDown',
|
||||||
|
},
|
||||||
|
copy: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.Copy',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Add linkage rule") }}',
|
||||||
|
'x-component': 'ArrayCollapse.Addition',
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['rules'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
// disabled: '{{$deps[0].length >= 3}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponent
|
||||||
|
schema={schema}
|
||||||
|
components={{ ShowFilterData, ArrayCollapse, FormLayout }}
|
||||||
|
scope={{ useFilterActionProps }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DemoPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.add('root', { path: '/', Component: Demo });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = mockApp({
|
||||||
|
plugins: [DemoPlugin],
|
||||||
|
apis: {
|
||||||
|
test: { data: { data: 'ok' } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,81 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useField, observer } from '@formily/react';
|
||||||
|
import { FilterActionProps, ISchema, useDataBlockRequest } from '@nocobase/client';
|
||||||
|
import { mockApp } from '@nocobase/client/demo-utils';
|
||||||
|
import { SchemaComponent, Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
|
const ShowFilterData = observer(({ children }) => {
|
||||||
|
const field = useField<any>();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<pre>{JSON.stringify(field.value, null, 2)}</pre>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const useFilterActionProps = (): FilterActionProps => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const { run } = useDataBlockRequest(); // replace `useRequest`
|
||||||
|
|
||||||
|
return {
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
console.log('onSubmit', values);
|
||||||
|
|
||||||
|
// request api
|
||||||
|
run(values);
|
||||||
|
|
||||||
|
field.setValue(values);
|
||||||
|
},
|
||||||
|
onReset: (values) => {
|
||||||
|
console.log('onReset', values);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema: ISchema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'root',
|
||||||
|
'x-decorator': 'DataBlockProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: 'users',
|
||||||
|
action: 'list',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
test: {
|
||||||
|
name: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
title: 'Filter',
|
||||||
|
'x-decorator': 'ShowFilterData',
|
||||||
|
'x-component': 'Filter.Action',
|
||||||
|
'x-use-component-props': 'useFilterActionProps',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponent
|
||||||
|
schema={schema}
|
||||||
|
components={{ ShowFilterData }}
|
||||||
|
scope={{
|
||||||
|
useFilterActionProps,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DemoPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.add('root', { path: '/', Component: Demo });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = mockApp({
|
||||||
|
plugins: [DemoPlugin],
|
||||||
|
apis: {
|
||||||
|
test: { data: { data: 'ok' } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,17 @@
|
|||||||
|
# LinkageFilter
|
||||||
|
|
||||||
|
A component used for filtering data, commonly used to filter data in blocks.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type FilterActionProps<T = {}> = ActionProps & {
|
||||||
|
options?: any[];
|
||||||
|
form?: Form;
|
||||||
|
onSubmit?: (values: T) => void;
|
||||||
|
onReset?: (values: T) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
|
||||||
|
<code src="./demos/new-demos/basic.tsx"></code>
|
@ -0,0 +1,18 @@
|
|||||||
|
# LinkageFilter
|
||||||
|
|
||||||
|
用于前端联动规则中,用作条件配置
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type FilterActionProps<T = {}> = ActionProps & {
|
||||||
|
options?: any[];
|
||||||
|
form?: Form;
|
||||||
|
onSubmit?: (values: T) => void;
|
||||||
|
onReset?: (values: T) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
左侧支持变量,操作符、和右侧变量组件跟随左侧变量联动
|
||||||
|
|
||||||
|
<code src="./demos/new-demos/basic.tsx"></code>
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './LinkageFilter';
|
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前字段所支持的操作符列表
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useOperatorList = (): any[] => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
|
||||||
|
|
||||||
|
const res = useMemo(() => {
|
||||||
|
const fieldInterface = schema['x-designer-props']?.interface;
|
||||||
|
const collectionFields = getCollectionFields(name);
|
||||||
|
if (fieldInterface) {
|
||||||
|
return getInterface(fieldInterface)?.filterable?.operators || [];
|
||||||
|
}
|
||||||
|
const field = collectionFields.find((item) => item.name === schema.name);
|
||||||
|
const ops = getInterface(field?.interface)?.filterable?.operators || [];
|
||||||
|
return ops.filter((o) => typeof o.visible !== 'function' || o.visible(field));
|
||||||
|
}, [schema.name]);
|
||||||
|
return res;
|
||||||
|
};
|
@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useField } from '@formily/react';
|
||||||
|
import { merge } from '@formily/shared';
|
||||||
|
import { cloneDeep, last, uniqBy } from 'lodash';
|
||||||
|
import { useCallback, useContext, useEffect } from 'react';
|
||||||
|
import { FilterContext } from './context';
|
||||||
|
|
||||||
|
interface UseValuesReturn {
|
||||||
|
fields: any[];
|
||||||
|
collectionField: any;
|
||||||
|
dataIndex: string[];
|
||||||
|
operators: any[];
|
||||||
|
operator: any;
|
||||||
|
schema: any;
|
||||||
|
value: any;
|
||||||
|
setDataIndex: (dataIndex: string[]) => void;
|
||||||
|
setOperator: (operatorValue: string) => void;
|
||||||
|
setRightValue: (value: any) => void;
|
||||||
|
setLeftValue: (value: any) => void;
|
||||||
|
leftVar: any;
|
||||||
|
rightVar: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const findOption = (str, options) => {
|
||||||
|
if (!str) return null;
|
||||||
|
const match = str.match(/\{\{\$(.*?)\}\}/);
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
const [firstKey, ...subKeys] = match[1].split('.'); // 拆分层级
|
||||||
|
const keys = [`$${firstKey}`, ...subKeys]; // 第一层保留 `$`,后续不带 `$`
|
||||||
|
|
||||||
|
let currentOptions = options;
|
||||||
|
let option = null;
|
||||||
|
for (const key of keys) {
|
||||||
|
option = currentOptions.find((opt) => opt.value === key);
|
||||||
|
if (!option) return null;
|
||||||
|
|
||||||
|
// 进入下一层 children 查找
|
||||||
|
if (Array.isArray(option.children) || option.isLeaf === false) {
|
||||||
|
currentOptions = option.children;
|
||||||
|
} else {
|
||||||
|
return option; // 没有 children 直接返回
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return option;
|
||||||
|
};
|
||||||
|
const operators = [
|
||||||
|
{ label: '{{t("is empty")}}', value: '$empty', noValue: true },
|
||||||
|
{ label: '{{t("is not empty")}}', value: '$notEmpty', noValue: true },
|
||||||
|
];
|
||||||
|
export const useValues = (): UseValuesReturn => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const { scopes } = useContext(FilterContext) || {};
|
||||||
|
const { op, leftVar, rightVar } = field.value || {};
|
||||||
|
const data2value = useCallback(() => {
|
||||||
|
field.value = field.data.leftVar
|
||||||
|
? {
|
||||||
|
op: field.data.operator?.value,
|
||||||
|
leftVar: field.data.leftVar,
|
||||||
|
rightVar: field.data?.rightVar,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
}, [field]);
|
||||||
|
|
||||||
|
const value2data = () => {
|
||||||
|
/**
|
||||||
|
* 等待获取最新的scopes
|
||||||
|
*/
|
||||||
|
setTimeout(() => {
|
||||||
|
const option = findOption(leftVar, scopes);
|
||||||
|
field.data = field.data || {};
|
||||||
|
if (!field.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const combOperators = uniqBy([...(field.data.operators || []), ...(option?.operators || [])], 'value');
|
||||||
|
field.data.operators = combOperators.length ? combOperators : operators;
|
||||||
|
field.data.leftVar = leftVar;
|
||||||
|
field.data.rightVar = rightVar;
|
||||||
|
const operator = combOperators?.find((v) => v.value === op);
|
||||||
|
field.data.operator = field.data.operator || operator;
|
||||||
|
const s1 = cloneDeep(option?.schema);
|
||||||
|
const s2 = cloneDeep(operator?.schema);
|
||||||
|
field.data.schema = field.data?.schema || merge(s1, s2);
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(value2data, [field.value, scopes]);
|
||||||
|
|
||||||
|
const setLeftValue = useCallback(
|
||||||
|
(leftVar, paths) => {
|
||||||
|
const option: any = last(paths);
|
||||||
|
field.data = field.data || {};
|
||||||
|
field.data.operators = option?.operators || operators;
|
||||||
|
const operator = field.data.operators?.[0];
|
||||||
|
field.data.operator = operator;
|
||||||
|
const s1 = cloneDeep(option?.schema);
|
||||||
|
const s2 = cloneDeep(operator?.schema);
|
||||||
|
field.data.schema = merge(s1, s2);
|
||||||
|
field.data.leftVar = leftVar;
|
||||||
|
field.data.rightVar = operator?.noValue ? operator.default || true : undefined;
|
||||||
|
data2value();
|
||||||
|
},
|
||||||
|
[data2value, field],
|
||||||
|
);
|
||||||
|
|
||||||
|
const setOperator = useCallback(
|
||||||
|
(operatorValue) => {
|
||||||
|
const operator = field.data?.operators?.find?.((item) => item.value === operatorValue);
|
||||||
|
field.data.operator = operator;
|
||||||
|
const s1 = cloneDeep(field.data.schema);
|
||||||
|
const s2 = cloneDeep(operator?.schema);
|
||||||
|
field.data.schema = merge(s1, s2);
|
||||||
|
field.data.value = operator.noValue ? operator.default || true : undefined;
|
||||||
|
data2value();
|
||||||
|
},
|
||||||
|
[data2value, field.data],
|
||||||
|
);
|
||||||
|
|
||||||
|
const setRightValue = useCallback(
|
||||||
|
(rightVar) => {
|
||||||
|
field.data.rightVar = rightVar;
|
||||||
|
data2value();
|
||||||
|
},
|
||||||
|
[data2value, field.data],
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...(field?.data || {}),
|
||||||
|
setLeftValue,
|
||||||
|
setOperator,
|
||||||
|
setRightValue,
|
||||||
|
};
|
||||||
|
};
|
@ -48,6 +48,7 @@ export type RemoteSelectProps<P = any> = SelectProps<P, any> & {
|
|||||||
CustomDropdownRender?: (v: any) => any;
|
CustomDropdownRender?: (v: any) => any;
|
||||||
optionFilter?: (option: any) => boolean;
|
optionFilter?: (option: any) => boolean;
|
||||||
toOptionsItem?: (data) => any;
|
toOptionsItem?: (data) => any;
|
||||||
|
onSuccess?: (data) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InternalRemoteSelect = withDynamicSchemaProps(
|
const InternalRemoteSelect = withDynamicSchemaProps(
|
||||||
@ -68,6 +69,7 @@ const InternalRemoteSelect = withDynamicSchemaProps(
|
|||||||
dataSource: propsDataSource,
|
dataSource: propsDataSource,
|
||||||
toOptionsItem = (value) => value,
|
toOptionsItem = (value) => value,
|
||||||
popupMatchSelectWidth = false,
|
popupMatchSelectWidth = false,
|
||||||
|
onSuccess,
|
||||||
...others
|
...others
|
||||||
} = props;
|
} = props;
|
||||||
const dataSource = useDataSourceKey();
|
const dataSource = useDataSourceKey();
|
||||||
@ -178,6 +180,7 @@ const InternalRemoteSelect = withDynamicSchemaProps(
|
|||||||
{
|
{
|
||||||
manual,
|
manual,
|
||||||
debounceWait: wait,
|
debounceWait: wait,
|
||||||
|
onSuccess,
|
||||||
...(service.defaultParams ? { defaultParams: [service.defaultParams] } : {}),
|
...(service.defaultParams ? { defaultParams: [service.defaultParams] } : {}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -18,6 +18,8 @@ import {
|
|||||||
useDesigner,
|
useDesigner,
|
||||||
useFlag,
|
useFlag,
|
||||||
useSchemaComponentContext,
|
useSchemaComponentContext,
|
||||||
|
BlockContext,
|
||||||
|
useBlockContext,
|
||||||
} from '../../../';
|
} from '../../../';
|
||||||
import { useToken } from '../__builtins__';
|
import { useToken } from '../__builtins__';
|
||||||
import { designerCss } from './Table.Column.ActionBar';
|
import { designerCss } from './Table.Column.ActionBar';
|
||||||
@ -77,6 +79,7 @@ export const TableColumnDecorator = (props) => {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { isInSubTable } = useFlag() || {};
|
const { isInSubTable } = useFlag() || {};
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
|
const { name } = useBlockContext?.() || {};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (field.title) {
|
if (field.title) {
|
||||||
@ -110,11 +113,13 @@ export const TableColumnDecorator = (props) => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<CollectionFieldContext.Provider value={collectionField}>
|
<CollectionFieldContext.Provider value={collectionField}>
|
||||||
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField} />
|
<BlockContext.Provider value={{ name: isInSubTable ? name : 'taleColumn' }}>
|
||||||
<span role="button">
|
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField} />
|
||||||
{fieldSchema?.required && <span className="ant-formily-item-asterisk">*</span>}
|
<span role="button">
|
||||||
<span>{field?.title || compile(uiSchema?.title)}</span>
|
{fieldSchema?.required && <span className="ant-formily-item-asterisk">*</span>}
|
||||||
</span>
|
<span>{field?.title || compile(uiSchema?.title)}</span>
|
||||||
|
</span>
|
||||||
|
</BlockContext.Provider>
|
||||||
</CollectionFieldContext.Provider>
|
</CollectionFieldContext.Provider>
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
);
|
);
|
||||||
|
@ -186,6 +186,7 @@ export type VariableInputProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
parseOptions?: ParseOptions;
|
parseOptions?: ParseOptions;
|
||||||
hideVariableButton?: boolean;
|
hideVariableButton?: boolean;
|
||||||
|
constantAbel?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Input(props: VariableInputProps) {
|
export function Input(props: VariableInputProps) {
|
||||||
@ -202,6 +203,7 @@ export function Input(props: VariableInputProps) {
|
|||||||
fieldNames,
|
fieldNames,
|
||||||
parseOptions,
|
parseOptions,
|
||||||
hideVariableButton,
|
hideVariableButton,
|
||||||
|
constantAbel = true,
|
||||||
} = props;
|
} = props;
|
||||||
const scope = typeof props.scope === 'function' ? props.scope() : props.scope;
|
const scope = typeof props.scope === 'function' ? props.scope() : props.scope;
|
||||||
const { wrapSSR, hashId, componentCls, rootPrefixCls } = useStyles({ hideVariableButton });
|
const { wrapSSR, hashId, componentCls, rootPrefixCls } = useStyles({ hideVariableButton });
|
||||||
@ -233,6 +235,7 @@ export function Input(props: VariableInputProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const constantOption: DefaultOptionType & { component?: React.FC<any> } = useMemo(() => {
|
const constantOption: DefaultOptionType & { component?: React.FC<any> } = useMemo(() => {
|
||||||
|
if (!constantAbel) return null;
|
||||||
if (children) {
|
if (children) {
|
||||||
return {
|
return {
|
||||||
value: '$',
|
value: '$',
|
||||||
|
@ -57,6 +57,7 @@ export const getTargetField = (obj) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const result = keys.slice(0, index);
|
const result = keys.slice(0, index);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,72 +77,44 @@ function getAllKeys(obj) {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseVariableValue = async (targetVariable, variables, localVariables) => {
|
||||||
|
const parsingResult = isVariable(targetVariable)
|
||||||
|
? [variables.parseVariable(targetVariable, localVariables).then(({ value }) => value)]
|
||||||
|
: [targetVariable];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [value] = await Promise.all(parsingResult);
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in parseVariableValue:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const conditionAnalyses = async (
|
export const conditionAnalyses = async (
|
||||||
{
|
{
|
||||||
ruleGroup,
|
ruleGroup,
|
||||||
variables,
|
variables,
|
||||||
localVariables,
|
localVariables,
|
||||||
variableNameOfLeftCondition,
|
variableNameOfLeftCondition,
|
||||||
|
conditionType,
|
||||||
}: {
|
}: {
|
||||||
ruleGroup;
|
ruleGroup;
|
||||||
variables: VariablesContextType;
|
variables: VariablesContextType;
|
||||||
localVariables: VariableOption[];
|
localVariables: VariableOption[];
|
||||||
/**
|
|
||||||
* used to parse the variable name of the left condition value
|
|
||||||
* @default '$nForm'
|
|
||||||
*/
|
|
||||||
variableNameOfLeftCondition?: string;
|
variableNameOfLeftCondition?: string;
|
||||||
|
conditionType?: 'advanced' | 'basic';
|
||||||
},
|
},
|
||||||
jsonLogic: any,
|
jsonLogic: any,
|
||||||
) => {
|
) => {
|
||||||
const type = Object.keys(ruleGroup)[0] || '$and';
|
const type = Object.keys(ruleGroup)[0] || '$and';
|
||||||
const conditions = ruleGroup[type];
|
const conditions = ruleGroup[type];
|
||||||
let results = conditions.map(async (condition) => {
|
|
||||||
if ('$and' in condition || '$or' in condition) {
|
|
||||||
return await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic);
|
|
||||||
}
|
|
||||||
|
|
||||||
const logicCalculation = getInnermostKeyAndValue(condition);
|
const results = await Promise.all(
|
||||||
const operator = logicCalculation?.key;
|
conditions.map((condition) =>
|
||||||
|
processCondition(condition, variables, localVariables, variableNameOfLeftCondition, conditionType, jsonLogic),
|
||||||
if (!operator) {
|
),
|
||||||
return true;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const targetVariableName = targetFieldToVariableString(getTargetField(condition), variableNameOfLeftCondition);
|
|
||||||
const targetValue = variables
|
|
||||||
.parseVariable(targetVariableName, localVariables, {
|
|
||||||
doNotRequest: true,
|
|
||||||
})
|
|
||||||
.then(({ value }) => value);
|
|
||||||
|
|
||||||
const parsingResult = isVariable(logicCalculation?.value)
|
|
||||||
? [variables.parseVariable(logicCalculation?.value, localVariables).then(({ value }) => value), targetValue]
|
|
||||||
: [logicCalculation?.value, targetValue];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [value, targetValue] = await Promise.all(parsingResult);
|
|
||||||
const targetCollectionField = await variables.getCollectionField(targetVariableName, localVariables);
|
|
||||||
let currentInputValue = transformVariableValue(targetValue, { targetCollectionField });
|
|
||||||
const comparisonValue = transformVariableValue(value, { targetCollectionField });
|
|
||||||
if (
|
|
||||||
targetCollectionField?.type &&
|
|
||||||
['datetime', 'date', 'datetimeNoTz', 'dateOnly', 'unixTimestamp'].includes(targetCollectionField.type) &&
|
|
||||||
currentInputValue
|
|
||||||
) {
|
|
||||||
const picker = inferPickerType(comparisonValue);
|
|
||||||
const format = getPickerFormat(picker);
|
|
||||||
currentInputValue = dayjs(currentInputValue).format(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonLogic.apply({
|
|
||||||
[operator]: [currentInputValue, comparisonValue],
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
results = await Promise.all(results);
|
|
||||||
|
|
||||||
if (type === '$and') {
|
if (type === '$and') {
|
||||||
return every(results, (v) => v);
|
return every(results, (v) => v);
|
||||||
@ -153,6 +126,67 @@ export const conditionAnalyses = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const processCondition = async (
|
||||||
|
condition,
|
||||||
|
variables,
|
||||||
|
localVariables,
|
||||||
|
variableNameOfLeftCondition,
|
||||||
|
conditionType,
|
||||||
|
jsonLogic,
|
||||||
|
) => {
|
||||||
|
if ('$and' in condition || '$or' in condition) {
|
||||||
|
return await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic);
|
||||||
|
}
|
||||||
|
return conditionType === 'advanced'
|
||||||
|
? processAdvancedCondition(condition, variables, localVariables, jsonLogic)
|
||||||
|
: processBasicCondition(condition, variables, localVariables, variableNameOfLeftCondition, jsonLogic);
|
||||||
|
};
|
||||||
|
|
||||||
|
const processAdvancedCondition = async (condition, variables, localVariables, jsonLogic) => {
|
||||||
|
const operator = condition.op;
|
||||||
|
const rightValue = await parseVariableValue(condition.rightVar, variables, localVariables);
|
||||||
|
const leftValue = await parseVariableValue(condition.leftVar, variables, localVariables);
|
||||||
|
if (operator) {
|
||||||
|
return jsonLogic.apply({ [operator]: [leftValue, rightValue] });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const processBasicCondition = async (condition, variables, localVariables, variableNameOfLeftCondition, jsonLogic) => {
|
||||||
|
const logicCalculation = getInnermostKeyAndValue(condition);
|
||||||
|
const operator = logicCalculation?.key;
|
||||||
|
if (!operator) return true;
|
||||||
|
|
||||||
|
const targetVariableName = targetFieldToVariableString(getTargetField(condition), variableNameOfLeftCondition);
|
||||||
|
const targetValue = variables
|
||||||
|
.parseVariable(targetVariableName, localVariables, { doNotRequest: true })
|
||||||
|
.then(({ value }) => value);
|
||||||
|
|
||||||
|
const parsingResult = isVariable(logicCalculation?.value)
|
||||||
|
? [variables.parseVariable(logicCalculation?.value, localVariables).then(({ value }) => value), targetValue]
|
||||||
|
: [logicCalculation?.value, targetValue];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [value, resolvedTargetValue] = await Promise.all(parsingResult);
|
||||||
|
const targetCollectionField = await variables.getCollectionField(targetVariableName, localVariables);
|
||||||
|
let currentInputValue = transformVariableValue(resolvedTargetValue, { targetCollectionField });
|
||||||
|
const comparisonValue = transformVariableValue(value, { targetCollectionField });
|
||||||
|
|
||||||
|
if (
|
||||||
|
targetCollectionField?.type &&
|
||||||
|
['datetime', 'date', 'datetimeNoTz', 'dateOnly', 'unixTimestamp'].includes(targetCollectionField.type) &&
|
||||||
|
currentInputValue
|
||||||
|
) {
|
||||||
|
const picker = inferPickerType(comparisonValue);
|
||||||
|
const format = getPickerFormat(picker);
|
||||||
|
currentInputValue = dayjs(currentInputValue).format(format);
|
||||||
|
}
|
||||||
|
return jsonLogic.apply({ [operator]: [currentInputValue, comparisonValue] });
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转化成变量字符串,方便解析出值
|
* 转化成变量字符串,方便解析出值
|
||||||
* @param targetField
|
* @param targetField
|
||||||
|
@ -89,6 +89,7 @@ const InternalCreateRecordAction = (props: any, ref) => {
|
|||||||
condition: v.condition,
|
condition: v.condition,
|
||||||
variables,
|
variables,
|
||||||
localVariables,
|
localVariables,
|
||||||
|
conditionType: v.conditionType,
|
||||||
},
|
},
|
||||||
app.jsonLogic,
|
app.jsonLogic,
|
||||||
);
|
);
|
||||||
@ -208,6 +209,7 @@ export const CreateAction = observer(
|
|||||||
condition: v.condition,
|
condition: v.condition,
|
||||||
variables,
|
variables,
|
||||||
localVariables,
|
localVariables,
|
||||||
|
conditionType: v.conditionType,
|
||||||
},
|
},
|
||||||
app.jsonLogic,
|
app.jsonLogic,
|
||||||
);
|
);
|
||||||
|
@ -26,7 +26,7 @@ import { VariableInput, getShouldChange } from '../../../schema-settings/Variabl
|
|||||||
import { Option } from '../../../schema-settings/VariableInput/type';
|
import { Option } from '../../../schema-settings/VariableInput/type';
|
||||||
import { formatVariableScop } from '../../../schema-settings/VariableInput/utils/formatVariableScop';
|
import { formatVariableScop } from '../../../schema-settings/VariableInput/utils/formatVariableScop';
|
||||||
import { useLocalVariables, useVariables } from '../../../variables';
|
import { useLocalVariables, useVariables } from '../../../variables';
|
||||||
|
import { BlockContext, useBlockContext } from '../../../block-provider';
|
||||||
interface AssignedFieldProps {
|
interface AssignedFieldProps {
|
||||||
value: any;
|
value: any;
|
||||||
onChange: (value: any) => void;
|
onChange: (value: any) => void;
|
||||||
@ -93,7 +93,7 @@ export enum AssignedFieldValueType {
|
|||||||
DynamicValue = 'dynamicValue',
|
DynamicValue = 'dynamicValue',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AssignedField = (props: AssignedFieldProps) => {
|
export const AssignedFieldInner = (props: AssignedFieldProps) => {
|
||||||
const { value, onChange } = props;
|
const { value, onChange } = props;
|
||||||
const { getCollectionFields, getAllCollectionsInheritChain } = useCollectionManager_deprecated();
|
const { getCollectionFields, getAllCollectionsInheritChain } = useCollectionManager_deprecated();
|
||||||
const collection = useCollection_deprecated();
|
const collection = useCollection_deprecated();
|
||||||
@ -148,3 +148,13 @@ export const AssignedField = (props: AssignedFieldProps) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AssignedField = (props) => {
|
||||||
|
const { form } = useFormBlockContext();
|
||||||
|
const { name } = useBlockContext();
|
||||||
|
return (
|
||||||
|
<BlockContext.Provider value={{ name: form ? 'form' : name }}>
|
||||||
|
<AssignedFieldInner {...props} />
|
||||||
|
</BlockContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -728,7 +728,7 @@ export const useCustomFormItemInitializerFields = (options?: any) => {
|
|||||||
const remove = useRemoveGridFormItem();
|
const remove = useRemoveGridFormItem();
|
||||||
return currentFields
|
return currentFields
|
||||||
?.filter((field) => {
|
?.filter((field) => {
|
||||||
return field?.interface && field.interface !== 'snapshot' && field.type !== 'sequence';
|
return !field.inherit && field?.interface && field.interface !== 'snapshot' && field.type !== 'sequence';
|
||||||
})
|
})
|
||||||
?.map((field) => {
|
?.map((field) => {
|
||||||
const interfaceConfig = getInterface(field.interface);
|
const interfaceConfig = getInterface(field.interface);
|
||||||
|
@ -38,6 +38,7 @@ interface Props {
|
|||||||
*/
|
*/
|
||||||
variableNameOfLeftCondition?: string;
|
variableNameOfLeftCondition?: string;
|
||||||
action?: any;
|
action?: any;
|
||||||
|
conditionType?: 'advanced' | 'basic';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindLinkageRulesToFiled(
|
export function bindLinkageRulesToFiled(
|
||||||
@ -83,7 +84,6 @@ export function bindLinkageRulesToFiled(
|
|||||||
() => {
|
() => {
|
||||||
// 获取条件中的字段值
|
// 获取条件中的字段值
|
||||||
const fieldValuesInCondition = getFieldValuesInCondition({ linkageRules, formValues });
|
const fieldValuesInCondition = getFieldValuesInCondition({ linkageRules, formValues });
|
||||||
|
|
||||||
// 获取条件中的变量值
|
// 获取条件中的变量值
|
||||||
const variableValuesInCondition = getVariableValuesInCondition({ linkageRules, localVariables });
|
const variableValuesInCondition = getVariableValuesInCondition({ linkageRules, localVariables });
|
||||||
|
|
||||||
@ -132,20 +132,37 @@ function getVariableValuesInCondition({
|
|||||||
return linkageRules.map((rule) => {
|
return linkageRules.map((rule) => {
|
||||||
const type = Object.keys(rule.condition)[0] || '$and';
|
const type = Object.keys(rule.condition)[0] || '$and';
|
||||||
const conditions = rule.condition[type];
|
const conditions = rule.condition[type];
|
||||||
|
if (rule.conditionType === 'advanced') {
|
||||||
|
return conditions
|
||||||
|
.map((condition) => {
|
||||||
|
if (!condition) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return conditions
|
const resolveVariable = (varName) =>
|
||||||
.map((condition) => {
|
isVariable(varName) ? getVariableValue(varName, localVariables) : varName;
|
||||||
const jsonlogic = getInnermostKeyAndValue(condition);
|
|
||||||
if (!jsonlogic) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (isVariable(jsonlogic.value)) {
|
|
||||||
return getVariableValue(jsonlogic.value, localVariables);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonlogic.value;
|
return {
|
||||||
})
|
leftVar: resolveVariable(condition.leftVar),
|
||||||
.filter(Boolean);
|
rightVar: resolveVariable(condition.rightVar),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
} else {
|
||||||
|
return conditions
|
||||||
|
.map((condition) => {
|
||||||
|
const jsonlogic = getInnermostKeyAndValue(condition);
|
||||||
|
if (!jsonlogic) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isVariable(jsonlogic.value)) {
|
||||||
|
return getVariableValue(jsonlogic.value, localVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonlogic.value;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +233,7 @@ function getSubscriber(
|
|||||||
localVariables,
|
localVariables,
|
||||||
variableNameOfLeftCondition,
|
variableNameOfLeftCondition,
|
||||||
action,
|
action,
|
||||||
|
conditionType: rule.conditionType,
|
||||||
},
|
},
|
||||||
jsonLogic,
|
jsonLogic,
|
||||||
);
|
);
|
||||||
@ -327,7 +345,17 @@ function getFieldNameByOperator(operator: ActionType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const collectFieldStateOfLinkageRules = (
|
export const collectFieldStateOfLinkageRules = (
|
||||||
{ operator, value, field, condition, variables, localVariables, variableNameOfLeftCondition, action }: Props,
|
{
|
||||||
|
operator,
|
||||||
|
value,
|
||||||
|
field,
|
||||||
|
condition,
|
||||||
|
variables,
|
||||||
|
localVariables,
|
||||||
|
variableNameOfLeftCondition,
|
||||||
|
action,
|
||||||
|
conditionType,
|
||||||
|
}: Props,
|
||||||
jsonLogic: any,
|
jsonLogic: any,
|
||||||
) => {
|
) => {
|
||||||
const requiredResult = field?.stateOfLinkageRules?.required || [field?.initStateOfLinkageRules?.required];
|
const requiredResult = field?.stateOfLinkageRules?.required || [field?.initStateOfLinkageRules?.required];
|
||||||
@ -336,7 +364,13 @@ export const collectFieldStateOfLinkageRules = (
|
|||||||
const valueResult = field?.stateOfLinkageRules?.value || [field?.initStateOfLinkageRules?.value];
|
const valueResult = field?.stateOfLinkageRules?.value || [field?.initStateOfLinkageRules?.value];
|
||||||
const optionsResult = field?.stateOfLinkageRules?.dataSource || [field?.initStateOfLinkageRules?.dataSource];
|
const optionsResult = field?.stateOfLinkageRules?.dataSource || [field?.initStateOfLinkageRules?.dataSource];
|
||||||
const { evaluate } = evaluators.get('formula.js');
|
const { evaluate } = evaluators.get('formula.js');
|
||||||
const paramsToGetConditionResult = { ruleGroup: condition, variables, localVariables, variableNameOfLeftCondition };
|
const paramsToGetConditionResult = {
|
||||||
|
ruleGroup: condition,
|
||||||
|
variables,
|
||||||
|
localVariables,
|
||||||
|
variableNameOfLeftCondition,
|
||||||
|
conditionType,
|
||||||
|
};
|
||||||
const dateScopeResult = field?.stateOfLinkageRules?.dateScope || [field?.initStateOfLinkageRules?.dateScope];
|
const dateScopeResult = field?.stateOfLinkageRules?.dateScope || [field?.initStateOfLinkageRules?.dateScope];
|
||||||
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
|
@ -38,7 +38,12 @@ const getSatisfiedActions = async ({ rules, variables, localVariables }, jsonLog
|
|||||||
rules
|
rules
|
||||||
.filter((k) => !k.disabled)
|
.filter((k) => !k.disabled)
|
||||||
.map(async (rule) => {
|
.map(async (rule) => {
|
||||||
if (await conditionAnalyses({ ruleGroup: rule.condition, variables, localVariables }, jsonLogic)) {
|
if (
|
||||||
|
await conditionAnalyses(
|
||||||
|
{ ruleGroup: rule.condition, variables, localVariables, conditionType: rule.conditionType },
|
||||||
|
jsonLogic,
|
||||||
|
)
|
||||||
|
) {
|
||||||
return rule;
|
return rule;
|
||||||
} else return null;
|
} else return null;
|
||||||
}),
|
}),
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { observer, useFieldSchema } from '@formily/react';
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
import { useCollectionManager_deprecated, useCollection_deprecated } from '../../collection-manager';
|
||||||
import { useCollectionParentRecordData } from '../../data-source/collection-record/CollectionRecordProvider';
|
import { useCollectionParentRecordData } from '../../data-source/collection-record/CollectionRecordProvider';
|
||||||
import { CollectionProvider } from '../../data-source/collection/CollectionProvider';
|
import { CollectionProvider } from '../../data-source/collection/CollectionProvider';
|
||||||
import { withDynamicSchemaProps } from '../../hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../hoc/withDynamicSchemaProps';
|
||||||
@ -27,15 +27,75 @@ import { ArrayCollapse } from './components/LinkageHeader';
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
dynamicComponent: any;
|
dynamicComponent: any;
|
||||||
}
|
}
|
||||||
|
function extractFieldPath(obj, path = []) {
|
||||||
|
if (typeof obj !== 'object' || obj === null) return null;
|
||||||
|
|
||||||
|
const [key, value] = Object.entries(obj)[0] || [];
|
||||||
|
|
||||||
|
if (typeof value === 'object' && value !== null && !key.startsWith('$')) {
|
||||||
|
return extractFieldPath(value, [...path, key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [path.join('.'), obj];
|
||||||
|
}
|
||||||
|
type Condition = { [field: string]: { [op: string]: any } } | { $and: Condition[] } | { $or: Condition[] };
|
||||||
|
type TransformedCondition =
|
||||||
|
| { leftVar: string; op: string; rightVar: any }
|
||||||
|
| { $and: TransformedCondition[] }
|
||||||
|
| { $or: TransformedCondition[] };
|
||||||
|
function transformConditionData(condition: Condition, variableKey: '$nForm' | '$nRecord'): TransformedCondition {
|
||||||
|
if ('$and' in condition) {
|
||||||
|
return {
|
||||||
|
$and: condition.$and.map((c) => transformConditionData(c, variableKey)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('$or' in condition) {
|
||||||
|
return {
|
||||||
|
$or: condition.$or.map((c) => transformConditionData(c, variableKey)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const [field, expression] = extractFieldPath(condition || {}) || [];
|
||||||
|
|
||||||
|
const [op, value] = Object.entries(expression || {})[0] || [];
|
||||||
|
return {
|
||||||
|
leftVar: field ? `{{${variableKey}.${field}}}` : null,
|
||||||
|
op,
|
||||||
|
rightVar: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function getActiveContextName(contextList: { name: string; ctx: any }[]): string | null {
|
||||||
|
const priority = ['$nForm', '$nRecord'];
|
||||||
|
for (const name of priority) {
|
||||||
|
const item = contextList.find((ctx) => ctx.name === name && ctx.ctx);
|
||||||
|
if (item) return name;
|
||||||
|
}
|
||||||
|
return '$nRecord';
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformDefaultValue = (values, variableKey) => {
|
||||||
|
return values.map((v) => {
|
||||||
|
if (v.conditionType !== 'advanced') {
|
||||||
|
const condition = transformConditionData(v.condition, variableKey);
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
condition: variableKey ? condition : v.condition,
|
||||||
|
conditionType: variableKey ? 'advanced' : 'basic',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const FormLinkageRules = withDynamicSchemaProps(
|
export const FormLinkageRules = withDynamicSchemaProps(
|
||||||
observer((props: Props) => {
|
observer((props: Props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { options, defaultValues, collectionName, form, variables, localVariables, record, dynamicComponent } =
|
const { options, defaultValues, collectionName, form, variables, localVariables, record, dynamicComponent } =
|
||||||
useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
const { getAllCollectionsInheritChain } = useCollectionManager_deprecated();
|
const { getAllCollectionsInheritChain } = useCollectionManager_deprecated();
|
||||||
const parentRecordData = useCollectionParentRecordData();
|
const parentRecordData = useCollectionParentRecordData();
|
||||||
|
const variableKey = getActiveContextName(localVariables);
|
||||||
const components = useMemo(() => ({ ArrayCollapse }), []);
|
const components = useMemo(() => ({ ArrayCollapse }), []);
|
||||||
const schema = useMemo(
|
const schema = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@ -43,7 +103,7 @@ export const FormLinkageRules = withDynamicSchemaProps(
|
|||||||
properties: {
|
properties: {
|
||||||
rules: {
|
rules: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
default: defaultValues,
|
default: transformDefaultValue(defaultValues, variableKey),
|
||||||
'x-component': 'ArrayCollapse',
|
'x-component': 'ArrayCollapse',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -72,6 +132,20 @@ export const FormLinkageRules = withDynamicSchemaProps(
|
|||||||
'x-content': '{{ t("Condition") }}',
|
'x-content': '{{ t("Condition") }}',
|
||||||
},
|
},
|
||||||
condition: {
|
condition: {
|
||||||
|
'x-component': 'Input', // 仅作为数据存储
|
||||||
|
'x-hidden': true, // 不显示
|
||||||
|
'x-reactions': [
|
||||||
|
{
|
||||||
|
dependencies: ['.conditionType', '.conditionBasic', '.conditionAdvanced'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
value: '{{$deps[0] === "basic" ? $deps[1] : $deps[2]}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
conditionBasic: {
|
||||||
'x-component': 'Filter',
|
'x-component': 'Filter',
|
||||||
'x-use-component-props': () => {
|
'x-use-component-props': () => {
|
||||||
return {
|
return {
|
||||||
@ -83,6 +157,7 @@ export const FormLinkageRules = withDynamicSchemaProps(
|
|||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
'x-visible': '{{$deps[0] === "basic"}}',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
collectionName,
|
collectionName,
|
||||||
dynamicComponent: (props: DynamicComponentProps) => {
|
dynamicComponent: (props: DynamicComponentProps) => {
|
||||||
@ -102,6 +177,38 @@ export const FormLinkageRules = withDynamicSchemaProps(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'x-reactions': [
|
||||||
|
{
|
||||||
|
dependencies: ['.conditionType', '.condition'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
visible: '{{$deps[0] === "basic"}}',
|
||||||
|
value: '{{$deps[0] === "basic" ? $deps[1] : undefined}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
conditionAdvanced: {
|
||||||
|
'x-component': 'LinkageFilter',
|
||||||
|
'x-visible': '{{$deps[0] === "advanced"}}',
|
||||||
|
'x-reactions': [
|
||||||
|
{
|
||||||
|
dependencies: ['.conditionType', '.condition'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
visible: '{{$deps[0] === "advanced"}}',
|
||||||
|
value: '{{$deps[0] === "advanced" ? $deps[1] : undefined}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
conditionType: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
default: 'advanced',
|
||||||
|
'x-hidden': true,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
'x-component': 'h4',
|
'x-component': 'h4',
|
||||||
@ -168,10 +275,10 @@ export const FormLinkageRules = withDynamicSchemaProps(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
// 这里使用 SubFormProvider 包裹,是为了让子表格的联动规则中 “当前对象” 的配置显示正确
|
// 这里使用 SubFormProvider 包裹,是为了让子表格的联动规则中 “当前对象” 的配置显示正确
|
||||||
<SubFormProvider value={{ value: null, collection: { name: collectionName } as any }}>
|
<SubFormProvider value={{ value: null, collection: { name: collectionName || name } as any }}>
|
||||||
<RecordProvider record={record} parent={parentRecordData}>
|
<RecordProvider record={record} parent={parentRecordData}>
|
||||||
<FilterContext.Provider value={value}>
|
<FilterContext.Provider value={value}>
|
||||||
<CollectionProvider name={collectionName}>
|
<CollectionProvider name={collectionName || name} allowNull>
|
||||||
<SchemaComponent components={components} schema={schema} />
|
<SchemaComponent components={components} schema={schema} />
|
||||||
</CollectionProvider>
|
</CollectionProvider>
|
||||||
</FilterContext.Provider>
|
</FilterContext.Provider>
|
||||||
|
@ -32,9 +32,11 @@ export enum ActionType {
|
|||||||
export enum LinkageRuleCategory {
|
export enum LinkageRuleCategory {
|
||||||
default = 'default',
|
default = 'default',
|
||||||
style = 'style',
|
style = 'style',
|
||||||
|
button = 'button',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LinkageRuleDataKeyMap: Record<`${LinkageRuleCategory}`, string> = {
|
export const LinkageRuleDataKeyMap: Record<`${LinkageRuleCategory}`, string> = {
|
||||||
[LinkageRuleCategory.style]: 'x-linkage-style-rules',
|
[LinkageRuleCategory.style]: 'x-linkage-style-rules',
|
||||||
[LinkageRuleCategory.default]: 'x-linkage-rules',
|
[LinkageRuleCategory.default]: 'x-linkage-rules',
|
||||||
|
[LinkageRuleCategory.button]: 'x-linkage-rules',
|
||||||
};
|
};
|
||||||
|
@ -1122,7 +1122,7 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
|
|||||||
const getRules = useCallback(() => {
|
const getRules = useCallback(() => {
|
||||||
return gridSchema?.[dataKey] || fieldSchema?.[dataKey] || [];
|
return gridSchema?.[dataKey] || fieldSchema?.[dataKey] || [];
|
||||||
}, [gridSchema, fieldSchema, dataKey]);
|
}, [gridSchema, fieldSchema, dataKey]);
|
||||||
const title = titleMap[category];
|
const title = titleMap[category] || t('Linkage rules');
|
||||||
const schema = useMemo<ISchema>(
|
const schema = useMemo<ISchema>(
|
||||||
() => ({
|
() => ({
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@ -1155,7 +1155,7 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
|
|||||||
(v) => {
|
(v) => {
|
||||||
const rules = [];
|
const rules = [];
|
||||||
for (const rule of v.fieldReaction.rules) {
|
for (const rule of v.fieldReaction.rules) {
|
||||||
rules.push(_.pickBy(rule, _.identity));
|
rules.push(_.omit(_.pickBy(rule, _.identity), ['conditionBasic', 'conditionAdvanced']));
|
||||||
}
|
}
|
||||||
const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props']?.templateId;
|
const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props']?.templateId;
|
||||||
const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid'];
|
const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid'];
|
||||||
|
@ -11,7 +11,7 @@ import { Form } from '@formily/core';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Schema } from '@formily/json-schema';
|
import { Schema } from '@formily/json-schema';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CollectionFieldOptions_deprecated } from '../../collection-manager';
|
import { CollectionFieldOptions_deprecated } from '../../collection-manager';
|
||||||
import { Variable, useVariableScope } from '../../schema-component';
|
import { Variable, useVariableScope } from '../../schema-component';
|
||||||
@ -72,6 +72,10 @@ type Props = {
|
|||||||
*/
|
*/
|
||||||
noDisabled?: boolean;
|
noDisabled?: boolean;
|
||||||
hideVariableButton?: boolean;
|
hideVariableButton?: boolean;
|
||||||
|
setScopes?: any; //更新scopes
|
||||||
|
nullable?: boolean;
|
||||||
|
constantAbel?: boolean;
|
||||||
|
changeOnSelect?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,6 +102,10 @@ export const VariableInput = (props: Props) => {
|
|||||||
targetFieldSchema,
|
targetFieldSchema,
|
||||||
noDisabled,
|
noDisabled,
|
||||||
hideVariableButton,
|
hideVariableButton,
|
||||||
|
setScopes,
|
||||||
|
nullable = true,
|
||||||
|
constantAbel = true,
|
||||||
|
changeOnSelect = true,
|
||||||
} = props;
|
} = props;
|
||||||
const { name: blockCollectionName } = useBlockCollection();
|
const { name: blockCollectionName } = useBlockCollection();
|
||||||
const scope = useVariableScope();
|
const scope = useVariableScope();
|
||||||
@ -127,31 +135,37 @@ export const VariableInput = (props: Props) => {
|
|||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(value: any, optionPath: any[]) => {
|
(value: any, optionPath: any[]) => {
|
||||||
if (!shouldChange) {
|
if (!shouldChange) {
|
||||||
return onChange(value);
|
return onChange(value, optionPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `shouldChange` 这个函数的运算量比较大,会导致展开变量列表时有明显的卡顿感,在这里加个延迟能有效解决这个问题
|
// `shouldChange` 这个函数的运算量比较大,会导致展开变量列表时有明显的卡顿感,在这里加个延迟能有效解决这个问题
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
if (await shouldChange(value, optionPath)) {
|
if (await shouldChange(value, optionPath)) {
|
||||||
onChange(value);
|
onChange(value, optionPath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[onChange, shouldChange],
|
[onChange, shouldChange],
|
||||||
);
|
);
|
||||||
|
const scopes = returnScope(
|
||||||
|
compatOldVariables(_.isEmpty(scope) ? variableOptions : scope, {
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
setScopes?.(scopes);
|
||||||
|
}, [value, scope]);
|
||||||
return (
|
return (
|
||||||
<Variable.Input
|
<Variable.Input
|
||||||
className={className}
|
className={className}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
scope={returnScope(
|
scope={scopes}
|
||||||
compatOldVariables(_.isEmpty(scope) ? variableOptions : scope, {
|
|
||||||
value,
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
style={style}
|
style={style}
|
||||||
changeOnSelect
|
changeOnSelect={changeOnSelect}
|
||||||
hideVariableButton={hideVariableButton}
|
hideVariableButton={hideVariableButton}
|
||||||
|
nullable={nullable}
|
||||||
|
constantAbel={constantAbel}
|
||||||
>
|
>
|
||||||
<RenderSchemaComponent value={value} onChange={onChange} />
|
<RenderSchemaComponent value={value} onChange={onChange} />
|
||||||
</Variable.Input>
|
</Variable.Input>
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import { useAPIClient } from '../../../api-client/hooks/useAPIClient';
|
import { useAPIClient } from '../../../api-client/hooks/useAPIClient';
|
||||||
import { useBaseVariable } from './useBaseVariable';
|
import { useBaseVariable } from './useBaseVariable';
|
||||||
|
import { string } from '../../../collection-manager/interfaces/properties/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 变量:`当前 Token`
|
* 变量:`当前 Token`
|
||||||
@ -26,6 +27,7 @@ export const useAPITokenVariable = ({
|
|||||||
title: 'API token',
|
title: 'API token',
|
||||||
noDisabled,
|
noDisabled,
|
||||||
noChildren: true,
|
noChildren: true,
|
||||||
|
operators: string,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -87,6 +87,8 @@ interface BaseProps {
|
|||||||
*/
|
*/
|
||||||
deprecated?: boolean;
|
deprecated?: boolean;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
/**支持的操作符 */
|
||||||
|
operators?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BaseVariableProviderProps {
|
interface BaseVariableProviderProps {
|
||||||
@ -133,6 +135,8 @@ const getChildren = (
|
|||||||
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField })),
|
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField })),
|
||||||
isLeaf: true,
|
isLeaf: true,
|
||||||
depth,
|
depth,
|
||||||
|
operators: option?.operators,
|
||||||
|
schema: option?.schema,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +201,7 @@ export const useBaseVariable = ({
|
|||||||
returnFields = (fields) => fields,
|
returnFields = (fields) => fields,
|
||||||
deprecated,
|
deprecated,
|
||||||
tooltip,
|
tooltip,
|
||||||
|
operators = [],
|
||||||
}: BaseProps) => {
|
}: BaseProps) => {
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const getFilterOptions = useGetFilterOptions();
|
const getFilterOptions = useGetFilterOptions();
|
||||||
@ -276,6 +281,7 @@ export const useBaseVariable = ({
|
|||||||
children: [],
|
children: [],
|
||||||
disabled: !!deprecated,
|
disabled: !!deprecated,
|
||||||
deprecated,
|
deprecated,
|
||||||
|
operators,
|
||||||
} as Option;
|
} as Option;
|
||||||
}, [uiSchema?.['x-component']]);
|
}, [uiSchema?.['x-component']]);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useOperators } from '../../../block-provider/CollectOperators';
|
import { useOperators } from '../../../block-provider/CollectOperators';
|
||||||
import { useDatePickerContext } from '../../../schema-component/antd/date-picker/DatePicker';
|
import { useDatePickerContext } from '../../../schema-component/antd/date-picker/DatePicker';
|
||||||
import { getDateRanges } from '../../../schema-component/antd/date-picker/util';
|
import { getDateRanges } from '../../../schema-component/antd/date-picker/util';
|
||||||
|
import { datetime } from '../../../collection-manager/interfaces/properties/operators';
|
||||||
interface Props {
|
interface Props {
|
||||||
operator?: {
|
operator?: {
|
||||||
value: string;
|
value: string;
|
||||||
@ -45,132 +45,155 @@ export const useDateVariable = ({ operator, schema, noDisabled }: Props) => {
|
|||||||
value: 'now',
|
value: 'now',
|
||||||
label: t('Current time'),
|
label: t('Current time'),
|
||||||
disabled: noDisabled ? false : schema?.['x-component'] !== 'DatePicker' || operatorValue === '$dateBetween',
|
disabled: noDisabled ? false : schema?.['x-component'] !== 'DatePicker' || operatorValue === '$dateBetween',
|
||||||
|
operators: datetime,
|
||||||
|
schema: {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'yesterday',
|
key: 'yesterday',
|
||||||
value: 'yesterday',
|
value: 'yesterday',
|
||||||
label: t('Yesterday'),
|
label: t('Yesterday'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'today',
|
key: 'today',
|
||||||
value: 'today',
|
value: 'today',
|
||||||
label: t('Today'),
|
label: t('Today'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'tomorrow',
|
key: 'tomorrow',
|
||||||
value: 'tomorrow',
|
value: 'tomorrow',
|
||||||
label: t('Tomorrow'),
|
label: t('Tomorrow'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastIsoWeek',
|
key: 'lastIsoWeek',
|
||||||
value: 'lastIsoWeek',
|
value: 'lastIsoWeek',
|
||||||
label: t('Last week'),
|
label: t('Last week'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisIsoWeek',
|
key: 'thisIsoWeek',
|
||||||
value: 'thisIsoWeek',
|
value: 'thisIsoWeek',
|
||||||
label: t('This week'),
|
label: t('This week'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextIsoWeek',
|
key: 'nextIsoWeek',
|
||||||
value: 'nextIsoWeek',
|
value: 'nextIsoWeek',
|
||||||
label: t('Next week'),
|
label: t('Next week'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastMonth',
|
key: 'lastMonth',
|
||||||
value: 'lastMonth',
|
value: 'lastMonth',
|
||||||
label: t('Last month'),
|
label: t('Last month'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisMonth',
|
key: 'thisMonth',
|
||||||
value: 'thisMonth',
|
value: 'thisMonth',
|
||||||
label: t('This month'),
|
label: t('This month'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextMonth',
|
key: 'nextMonth',
|
||||||
value: 'nextMonth',
|
value: 'nextMonth',
|
||||||
label: t('Next month'),
|
label: t('Next month'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastQuarter',
|
key: 'lastQuarter',
|
||||||
value: 'lastQuarter',
|
value: 'lastQuarter',
|
||||||
label: t('Last quarter'),
|
label: t('Last quarter'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisQuarter',
|
key: 'thisQuarter',
|
||||||
value: 'thisQuarter',
|
value: 'thisQuarter',
|
||||||
label: t('This quarter'),
|
label: t('This quarter'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextQuarter',
|
key: 'nextQuarter',
|
||||||
value: 'nextQuarter',
|
value: 'nextQuarter',
|
||||||
label: t('Next quarter'),
|
label: t('Next quarter'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastYear',
|
key: 'lastYear',
|
||||||
value: 'lastYear',
|
value: 'lastYear',
|
||||||
label: t('Last year'),
|
label: t('Last year'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisYear',
|
key: 'thisYear',
|
||||||
value: 'thisYear',
|
value: 'thisYear',
|
||||||
label: t('This year'),
|
label: t('This year'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextYear',
|
key: 'nextYear',
|
||||||
value: 'nextYear',
|
value: 'nextYear',
|
||||||
label: t('Next year'),
|
label: t('Next year'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'last7Days',
|
key: 'last7Days',
|
||||||
value: 'last7Days',
|
value: 'last7Days',
|
||||||
label: t('Last 7 days'),
|
label: t('Last 7 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next7Days',
|
key: 'next7Days',
|
||||||
value: 'next7Days',
|
value: 'next7Days',
|
||||||
label: t('Next 7 days'),
|
label: t('Next 7 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'last30Days',
|
key: 'last30Days',
|
||||||
value: 'last30Days',
|
value: 'last30Days',
|
||||||
label: t('Last 30 days'),
|
label: t('Last 30 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next30Days',
|
key: 'next30Days',
|
||||||
value: 'next30Days',
|
value: 'next30Days',
|
||||||
label: t('Next 30 days'),
|
label: t('Next 30 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'last90Days',
|
key: 'last90Days',
|
||||||
value: 'last90Days',
|
value: 'last90Days',
|
||||||
label: t('Last 90 days'),
|
label: t('Last 90 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next90Days',
|
key: 'next90Days',
|
||||||
value: 'next90Days',
|
value: 'next90Days',
|
||||||
label: t('Next 90 days'),
|
label: t('Next 90 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -222,132 +245,154 @@ export const useDatetimeVariable = ({ operator, schema, noDisabled, targetFieldS
|
|||||||
value: 'now',
|
value: 'now',
|
||||||
label: t('Current time'),
|
label: t('Current time'),
|
||||||
disabled: noDisabled ? false : schema?.['x-component'] !== 'DatePicker' || operatorValue === '$dateBetween',
|
disabled: noDisabled ? false : schema?.['x-component'] !== 'DatePicker' || operatorValue === '$dateBetween',
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'yesterday',
|
key: 'yesterday',
|
||||||
value: 'yesterday',
|
value: 'yesterday',
|
||||||
label: t('Yesterday'),
|
label: t('Yesterday'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'today',
|
key: 'today',
|
||||||
value: 'today',
|
value: 'today',
|
||||||
label: t('Today'),
|
label: t('Today'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'tomorrow',
|
key: 'tomorrow',
|
||||||
value: 'tomorrow',
|
value: 'tomorrow',
|
||||||
label: t('Tomorrow'),
|
label: t('Tomorrow'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastIsoWeek',
|
key: 'lastIsoWeek',
|
||||||
value: 'lastIsoWeek',
|
value: 'lastIsoWeek',
|
||||||
label: t('Last week'),
|
label: t('Last week'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisIsoWeek',
|
key: 'thisIsoWeek',
|
||||||
value: 'thisIsoWeek',
|
value: 'thisIsoWeek',
|
||||||
label: t('This week'),
|
label: t('This week'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextIsoWeek',
|
key: 'nextIsoWeek',
|
||||||
value: 'nextIsoWeek',
|
value: 'nextIsoWeek',
|
||||||
label: t('Next week'),
|
label: t('Next week'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastMonth',
|
key: 'lastMonth',
|
||||||
value: 'lastMonth',
|
value: 'lastMonth',
|
||||||
label: t('Last month'),
|
label: t('Last month'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisMonth',
|
key: 'thisMonth',
|
||||||
value: 'thisMonth',
|
value: 'thisMonth',
|
||||||
label: t('This month'),
|
label: t('This month'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextMonth',
|
key: 'nextMonth',
|
||||||
value: 'nextMonth',
|
value: 'nextMonth',
|
||||||
label: t('Next month'),
|
label: t('Next month'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastQuarter',
|
key: 'lastQuarter',
|
||||||
value: 'lastQuarter',
|
value: 'lastQuarter',
|
||||||
label: t('Last quarter'),
|
label: t('Last quarter'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisQuarter',
|
key: 'thisQuarter',
|
||||||
value: 'thisQuarter',
|
value: 'thisQuarter',
|
||||||
label: t('This quarter'),
|
label: t('This quarter'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextQuarter',
|
key: 'nextQuarter',
|
||||||
value: 'nextQuarter',
|
value: 'nextQuarter',
|
||||||
label: t('Next quarter'),
|
label: t('Next quarter'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastYear',
|
key: 'lastYear',
|
||||||
value: 'lastYear',
|
value: 'lastYear',
|
||||||
label: t('Last year'),
|
label: t('Last year'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thisYear',
|
key: 'thisYear',
|
||||||
value: 'thisYear',
|
value: 'thisYear',
|
||||||
label: t('This year'),
|
label: t('This year'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nextYear',
|
key: 'nextYear',
|
||||||
value: 'nextYear',
|
value: 'nextYear',
|
||||||
label: t('Next year'),
|
label: t('Next year'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'last7Days',
|
key: 'last7Days',
|
||||||
value: 'last7Days',
|
value: 'last7Days',
|
||||||
label: t('Last 7 days'),
|
label: t('Last 7 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next7Days',
|
key: 'next7Days',
|
||||||
value: 'next7Days',
|
value: 'next7Days',
|
||||||
label: t('Next 7 days'),
|
label: t('Next 7 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'last30Days',
|
key: 'last30Days',
|
||||||
value: 'last30Days',
|
value: 'last30Days',
|
||||||
label: t('Last 30 days'),
|
label: t('Last 30 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next30Days',
|
key: 'next30Days',
|
||||||
value: 'next30Days',
|
value: 'next30Days',
|
||||||
label: t('Next 30 days'),
|
label: t('Next 30 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'last90Days',
|
key: 'last90Days',
|
||||||
value: 'last90Days',
|
value: 'last90Days',
|
||||||
label: t('Last 90 days'),
|
label: t('Last 90 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next90Days',
|
key: 'next90Days',
|
||||||
value: 'next90Days',
|
value: 'next90Days',
|
||||||
label: t('Next 90 days'),
|
label: t('Next 90 days'),
|
||||||
disabled,
|
disabled,
|
||||||
|
operators: datetime,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import { Form } from '@formily/core';
|
import { Form } from '@formily/core';
|
||||||
import { Schema } from '@formily/json-schema';
|
import { Schema } from '@formily/json-schema';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useBlockContext } from '../../../block-provider';
|
||||||
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
|
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
|
||||||
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
|
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
|
||||||
import { useDataBlockRequestData, useDataSource } from '../../../data-source';
|
import { useDataBlockRequestData, useDataSource } from '../../../data-source';
|
||||||
@ -62,14 +63,6 @@ export const useFormVariable = ({ collectionName, collectionField, schema, noDis
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useCurrentFormData = () => {
|
|
||||||
const data = useDataBlockRequestData();
|
|
||||||
if (data?.data?.length > 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return data?.data?.[0] || data?.data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 变量:`当前表单` 相关的 hook
|
* 变量:`当前表单` 相关的 hook
|
||||||
* @param param0
|
* @param param0
|
||||||
@ -78,14 +71,14 @@ const useCurrentFormData = () => {
|
|||||||
export const useCurrentFormContext = ({ form: _form }: Pick<Props, 'form'> = {}) => {
|
export const useCurrentFormContext = ({ form: _form }: Pick<Props, 'form'> = {}) => {
|
||||||
const { form } = useFormBlockContext();
|
const { form } = useFormBlockContext();
|
||||||
const { isVariableParsedInOtherContext } = useFlag();
|
const { isVariableParsedInOtherContext } = useFlag();
|
||||||
|
const { name } = useBlockContext?.() || {};
|
||||||
const formInstance = _form || form;
|
const formInstance = _form || form;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/** 变量值 */
|
/** 变量值 */
|
||||||
currentFormCtx: formInstance?.values,
|
currentFormCtx: formInstance?.values,
|
||||||
/** 用来判断是否可以显示`当前表单`变量 */
|
/** 用来判断是否可以显示`当前表单`变量 */
|
||||||
shouldDisplayCurrentForm: formInstance && !formInstance.readPretty && !isVariableParsedInOtherContext,
|
shouldDisplayCurrentForm:
|
||||||
|
name === 'form' && formInstance && !formInstance.readPretty && !isVariableParsedInOtherContext,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,7 +90,10 @@ export const useCurrentRecordContext = () => {
|
|||||||
/** 变量值 */
|
/** 变量值 */
|
||||||
currentRecordCtx: ctx?.recordData || formRecord?.data || recordData,
|
currentRecordCtx: ctx?.recordData || formRecord?.data || recordData,
|
||||||
/** 用于判断是否需要显示配置项 */
|
/** 用于判断是否需要显示配置项 */
|
||||||
shouldDisplayCurrentRecord: !_.isEmpty(_.omit(recordData, ['__collectionName', '__parent'])) || !!formRecord?.data,
|
shouldDisplayCurrentRecord:
|
||||||
|
!_.isEmpty(_.omit(recordData, ['__collectionName', '__parent'])) ||
|
||||||
|
!!formRecord?.data ||
|
||||||
|
blockType === 'taleColumn',
|
||||||
/** 当前记录对应的 collection name */
|
/** 当前记录对应的 collection name */
|
||||||
collectionName: realCollectionName,
|
collectionName: realCollectionName,
|
||||||
/** 块类型 */
|
/** 块类型 */
|
||||||
|
@ -13,6 +13,9 @@ import { useAPIClient } from '../../../api-client';
|
|||||||
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
|
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
|
||||||
import { CollectionFieldOptions } from '../../../data-source/collection/Collection';
|
import { CollectionFieldOptions } from '../../../data-source/collection/Collection';
|
||||||
import { useBaseVariable } from './useBaseVariable';
|
import { useBaseVariable } from './useBaseVariable';
|
||||||
|
import { string } from '../../../collection-manager/interfaces/properties/operators';
|
||||||
|
import { useCurrentUserContext } from '../../../user/CurrentUserProvider';
|
||||||
|
import { useCompile } from '../../../schema-component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -47,6 +50,7 @@ export const useRoleVariable = ({
|
|||||||
noDisabled,
|
noDisabled,
|
||||||
targetFieldSchema,
|
targetFieldSchema,
|
||||||
noChildren: true,
|
noChildren: true,
|
||||||
|
operators: string,
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -73,6 +77,9 @@ export const useCurrentRoleVariable = ({
|
|||||||
} = {}) => {
|
} = {}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const apiClient = useAPIClient();
|
const apiClient = useAPIClient();
|
||||||
|
const compile = useCompile();
|
||||||
|
const { data } = useCurrentUserContext() || {};
|
||||||
|
const roles = (data?.data?.roles || []).map(({ name, title }) => ({ name, title: compile(title) }));
|
||||||
const currentRoleSettings = useBaseVariable({
|
const currentRoleSettings = useBaseVariable({
|
||||||
collectionField,
|
collectionField,
|
||||||
uiSchema,
|
uiSchema,
|
||||||
@ -83,12 +90,13 @@ export const useCurrentRoleVariable = ({
|
|||||||
noDisabled,
|
noDisabled,
|
||||||
targetFieldSchema,
|
targetFieldSchema,
|
||||||
noChildren: true,
|
noChildren: true,
|
||||||
|
operators: string,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/** 变量配置项 */
|
/** 变量配置项 */
|
||||||
currentRoleSettings,
|
currentRoleSettings,
|
||||||
/** 变量的值 */
|
/** 变量的值 */
|
||||||
currentRoleCtx: apiClient.auth?.role,
|
currentRoleCtx: apiClient.auth?.role === '__union__' ? roles.map((v) => v.name) : apiClient.auth?.role,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,7 @@ export const useIsLoggedIn = () => {
|
|||||||
|
|
||||||
export const useCurrentRoles = () => {
|
export const useCurrentRoles = () => {
|
||||||
const { allowAnonymous } = useACLRoleContext();
|
const { allowAnonymous } = useACLRoleContext();
|
||||||
const { data } = useCurrentUserContext();
|
const { data } = useCurrentUserContext() || {};
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
const roles = (data?.data?.roles || []).map(({ name, title }) => ({ name, title: compile(title) }));
|
const roles = (data?.data?.roles || []).map(({ name, title }) => ({ name, title: compile(title) }));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "create-nocobase-app",
|
"name": "create-nocobase-app",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -8,6 +8,7 @@
|
|||||||
"axios": "^1.7.0",
|
"axios": "^1.7.0",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"commander": "^9.2.0",
|
"commander": "^9.2.0",
|
||||||
|
"fs-extra": "^11.3.0",
|
||||||
"tar": "6.1.11"
|
"tar": "6.1.11"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -19,6 +19,7 @@ const cli = new Command('create-nocobase');
|
|||||||
cli
|
cli
|
||||||
.arguments('<name>', 'directory of new NocoBase app')
|
.arguments('<name>', 'directory of new NocoBase app')
|
||||||
.option('--quickstart', 'quickstart app creation')
|
.option('--quickstart', 'quickstart app creation')
|
||||||
|
.option('--skip-dev-dependencies')
|
||||||
.option('-a, --all-db-dialect', 'install all database dialect dependencies')
|
.option('-a, --all-db-dialect', 'install all database dialect dependencies')
|
||||||
.option('-d, --db-dialect <dbDialect>', 'database dialect, current support sqlite/mysql/postgres', 'sqlite')
|
.option('-d, --db-dialect <dbDialect>', 'database dialect, current support sqlite/mysql/postgres', 'sqlite')
|
||||||
.option('-e, --env <env>', 'environment variables write into .env file', concat, [])
|
.option('-e, --env <env>', 'environment variables write into .env file', concat, [])
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const { existsSync } = require('fs');
|
const { existsSync, promises } = require('fs');
|
||||||
|
const fs = require('fs-extra');
|
||||||
const { join, resolve } = require('path');
|
const { join, resolve } = require('path');
|
||||||
const { Generator } = require('@umijs/utils');
|
const { Generator } = require('@umijs/utils');
|
||||||
const { downloadPackageFromNpm, updateJsonFile } = require('./util');
|
const { downloadPackageFromNpm, updateJsonFile } = require('./util');
|
||||||
@ -191,6 +192,13 @@ class AppGenerator extends Generator {
|
|||||||
|
|
||||||
this.checkDbEnv();
|
this.checkDbEnv();
|
||||||
|
|
||||||
|
const skipDevDependencies = this.args.skipDevDependencies;
|
||||||
|
if (skipDevDependencies) {
|
||||||
|
const json = await fs.readJSON(join(this.cwd, 'package.json'), 'utf8');
|
||||||
|
delete json['devDependencies'];
|
||||||
|
await fs.writeJSON(join(this.cwd, 'package.json'), json, { encoding: 'utf8', spaces: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(chalk.green(`$ cd ${name}`));
|
console.log(chalk.green(`$ cd ${name}`));
|
||||||
console.log(chalk.green(`$ yarn install`));
|
console.log(chalk.green(`$ yarn install`));
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"react-router-dom": "6.28.1",
|
"react-router-dom": "6.28.1",
|
||||||
"react-router": "6.28.1",
|
"react-router": "6.28.1",
|
||||||
"antd": "5.24.2",
|
"antd": "5.24.2",
|
||||||
|
"async": "3.2.6",
|
||||||
"rollup": "4.24.0"
|
"rollup": "4.24.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/data-source-manager",
|
"name": "@nocobase/data-source-manager",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/actions": "1.7.0-beta.16",
|
"@nocobase/actions": "1.7.0-beta.18",
|
||||||
"@nocobase/cache": "1.7.0-beta.16",
|
"@nocobase/cache": "1.7.0-beta.18",
|
||||||
"@nocobase/database": "1.7.0-beta.16",
|
"@nocobase/database": "1.7.0-beta.18",
|
||||||
"@nocobase/resourcer": "1.7.0-beta.16",
|
"@nocobase/resourcer": "1.7.0-beta.18",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"@types/jsonwebtoken": "^8.5.8",
|
"@types/jsonwebtoken": "^8.5.8",
|
||||||
"jsonwebtoken": "^8.5.1"
|
"jsonwebtoken": "^8.5.1"
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/database",
|
"name": "@nocobase/database",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/logger": "1.7.0-beta.16",
|
"@nocobase/logger": "1.7.0-beta.18",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"async-mutex": "^0.3.2",
|
"async-mutex": "^0.3.2",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"cron-parser": "4.4.0",
|
"cron-parser": "4.4.0",
|
||||||
@ -20,12 +20,12 @@
|
|||||||
"graphlib": "^2.1.8",
|
"graphlib": "^2.1.8",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mathjs": "^10.6.1",
|
"mathjs": "^10.6.1",
|
||||||
"nanoid": "^3.3.6",
|
"nanoid": "^3.3.11",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"node-sql-parser": "^4.18.0",
|
"node-sql-parser": "^4.18.0",
|
||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
"safe-json-stringify": "^1.2.0",
|
"safe-json-stringify": "^1.2.0",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.7.1",
|
||||||
"sequelize": "^6.26.0",
|
"sequelize": "^6.26.0",
|
||||||
"umzug": "^3.1.1",
|
"umzug": "^3.1.1",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/devtools",
|
"name": "@nocobase/devtools",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./src/index.js",
|
"main": "./src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/build": "1.7.0-beta.16",
|
"@nocobase/build": "1.7.0-beta.18",
|
||||||
"@nocobase/client": "1.7.0-beta.16",
|
"@nocobase/client": "1.7.0-beta.18",
|
||||||
"@nocobase/test": "1.7.0-beta.16",
|
"@nocobase/test": "1.7.0-beta.18",
|
||||||
"@types/koa": "^2.15.0",
|
"@types/koa": "^2.15.0",
|
||||||
"@types/koa-bodyparser": "^4.3.4",
|
"@types/koa-bodyparser": "^4.3.4",
|
||||||
"@types/lodash": "^4.14.177",
|
"@types/lodash": "^4.14.177",
|
||||||
@ -35,7 +35,7 @@
|
|||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
"rimraf": "^3.0.0",
|
"rimraf": "^3.0.0",
|
||||||
"serve": "^13.0.2",
|
"serve": "^14.2.4",
|
||||||
"ts-loader": "^7.0.4",
|
"ts-loader": "^7.0.4",
|
||||||
"ts-node": "9.1.1",
|
"ts-node": "9.1.1",
|
||||||
"ts-node-dev": "1.1.8",
|
"ts-node-dev": "1.1.8",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/evaluators",
|
"name": "@nocobase/evaluators",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formulajs/formulajs": "4.4.9",
|
"@formulajs/formulajs": "4.4.9",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"mathjs": "^10.6.0"
|
"mathjs": "^10.6.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/lock-manager",
|
"name": "@nocobase/lock-manager",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"async-mutex": "^0.5.0"
|
"async-mutex": "^0.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/logger",
|
"name": "@nocobase/logger",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "nocobase logging library",
|
"description": "nocobase logging library",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/resourcer",
|
"name": "@nocobase/resourcer",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"koa-compose": "^4.1.0",
|
"koa-compose": "^4.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/sdk",
|
"name": "@nocobase/sdk",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/server",
|
"name": "@nocobase/server",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
@ -10,19 +10,19 @@
|
|||||||
"@koa/cors": "^5.0.0",
|
"@koa/cors": "^5.0.0",
|
||||||
"@koa/multer": "^3.0.2",
|
"@koa/multer": "^3.0.2",
|
||||||
"@koa/router": "^9.4.0",
|
"@koa/router": "^9.4.0",
|
||||||
"@nocobase/acl": "1.7.0-beta.16",
|
"@nocobase/acl": "1.7.0-beta.18",
|
||||||
"@nocobase/actions": "1.7.0-beta.16",
|
"@nocobase/actions": "1.7.0-beta.18",
|
||||||
"@nocobase/auth": "1.7.0-beta.16",
|
"@nocobase/auth": "1.7.0-beta.18",
|
||||||
"@nocobase/cache": "1.7.0-beta.16",
|
"@nocobase/cache": "1.7.0-beta.18",
|
||||||
"@nocobase/data-source-manager": "1.7.0-beta.16",
|
"@nocobase/data-source-manager": "1.7.0-beta.18",
|
||||||
"@nocobase/database": "1.7.0-beta.16",
|
"@nocobase/database": "1.7.0-beta.18",
|
||||||
"@nocobase/evaluators": "1.7.0-beta.16",
|
"@nocobase/evaluators": "1.7.0-beta.18",
|
||||||
"@nocobase/lock-manager": "1.7.0-beta.16",
|
"@nocobase/lock-manager": "1.7.0-beta.18",
|
||||||
"@nocobase/logger": "1.7.0-beta.16",
|
"@nocobase/logger": "1.7.0-beta.18",
|
||||||
"@nocobase/resourcer": "1.7.0-beta.16",
|
"@nocobase/resourcer": "1.7.0-beta.18",
|
||||||
"@nocobase/sdk": "1.7.0-beta.16",
|
"@nocobase/sdk": "1.7.0-beta.18",
|
||||||
"@nocobase/telemetry": "1.7.0-beta.16",
|
"@nocobase/telemetry": "1.7.0-beta.18",
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"@types/decompress": "4.2.7",
|
"@types/decompress": "4.2.7",
|
||||||
"@types/ini": "^1.3.31",
|
"@types/ini": "^1.3.31",
|
||||||
"@types/koa-send": "^4.1.3",
|
"@types/koa-send": "^4.1.3",
|
||||||
@ -31,6 +31,7 @@
|
|||||||
"axios": "^1.7.0",
|
"axios": "^1.7.0",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"commander": "^9.2.0",
|
"commander": "^9.2.0",
|
||||||
|
"compression": "^1.8.0",
|
||||||
"cron": "^2.4.4",
|
"cron": "^2.4.4",
|
||||||
"cronstrue": "^2.11.0",
|
"cronstrue": "^2.11.0",
|
||||||
"dayjs": "^1.11.8",
|
"dayjs": "^1.11.8",
|
||||||
@ -45,9 +46,9 @@
|
|||||||
"koa-static": "^5.0.0",
|
"koa-static": "^5.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "^3.3.11",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.7.1",
|
||||||
"serve-handler": "^6.1.5",
|
"serve-handler": "^6.1.6",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.13.0",
|
||||||
"xpipe": "^1.0.5"
|
"xpipe": "^1.0.5"
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/telemetry",
|
"name": "@nocobase/telemetry",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"description": "nocobase telemetry library",
|
"description": "nocobase telemetry library",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
@ -11,7 +11,7 @@
|
|||||||
"directory": "packages/telemetry"
|
"directory": "packages/telemetry"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/utils": "1.7.0-beta.16",
|
"@nocobase/utils": "1.7.0-beta.18",
|
||||||
"@opentelemetry/api": "^1.7.0",
|
"@opentelemetry/api": "^1.7.0",
|
||||||
"@opentelemetry/instrumentation": "^0.46.0",
|
"@opentelemetry/instrumentation": "^0.46.0",
|
||||||
"@opentelemetry/resources": "^1.19.0",
|
"@opentelemetry/resources": "^1.19.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/test",
|
"name": "@nocobase/test",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"module": "./src/index.ts",
|
"module": "./src/index.ts",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
@ -51,7 +51,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@faker-js/faker": "8.1.0",
|
"@faker-js/faker": "8.1.0",
|
||||||
"@nocobase/server": "1.7.0-beta.16",
|
"@nocobase/server": "1.7.0-beta.18",
|
||||||
"@playwright/test": "^1.45.3",
|
"@playwright/test": "^1.45.3",
|
||||||
"@testing-library/jest-dom": "^6.4.2",
|
"@testing-library/jest-dom": "^6.4.2",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/utils",
|
"name": "@nocobase/utils",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"displayName.zh-CN": "权限控制",
|
"displayName.zh-CN": "权限控制",
|
||||||
"description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.",
|
"description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.",
|
||||||
"description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。",
|
"description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "./dist/server/index.js",
|
"main": "./dist/server/index.js",
|
||||||
"homepage": "https://docs.nocobase.com/handbook/acl",
|
"homepage": "https://docs.nocobase.com/handbook/acl",
|
||||||
|
@ -48,9 +48,9 @@ export async function setCurrentRole(ctx: Context, next) {
|
|||||||
const userRoles = Array.from(rolesMap.values());
|
const userRoles = Array.from(rolesMap.values());
|
||||||
ctx.state.currentUser.roles = userRoles;
|
ctx.state.currentUser.roles = userRoles;
|
||||||
const systemSettings = (await cache.wrap(`app:systemSettings`, () =>
|
const systemSettings = (await cache.wrap(`app:systemSettings`, () =>
|
||||||
ctx.db.getRepository('systemSettings').findOne(),
|
ctx.db.getRepository('systemSettings').findOne({ raw: true }),
|
||||||
)) as Model;
|
)) as Model;
|
||||||
const roleMode = systemSettings?.get('roleMode') || SystemRoleMode.default;
|
const roleMode = systemSettings?.roleMode || SystemRoleMode.default;
|
||||||
if ([currentRole, ctx.state.currentRole].includes(UNION_ROLE_KEY) && roleMode === SystemRoleMode.default) {
|
if ([currentRole, ctx.state.currentRole].includes(UNION_ROLE_KEY) && roleMode === SystemRoleMode.default) {
|
||||||
currentRole = userRoles[0].name;
|
currentRole = userRoles[0].name;
|
||||||
ctx.state.currentRole = userRoles[0].name;
|
ctx.state.currentRole = userRoles[0].name;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/plugin-action-bulk-edit",
|
"name": "@nocobase/plugin-action-bulk-edit",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "dist/server/index.js",
|
"main": "dist/server/index.js",
|
||||||
"homepage": "https://docs.nocobase.com/handbook/action-bulk-edit",
|
"homepage": "https://docs.nocobase.com/handbook/action-bulk-edit",
|
||||||
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit",
|
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit",
|
||||||
|
@ -21,9 +21,10 @@ import {
|
|||||||
SecondConFirm,
|
SecondConFirm,
|
||||||
AfterSuccess,
|
AfterSuccess,
|
||||||
RefreshDataBlockRequest,
|
RefreshDataBlockRequest,
|
||||||
|
SchemaSettingsLinkageRules,
|
||||||
|
useDataBlockProps,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { ModalProps } from 'antd';
|
import { ModalProps } from 'antd';
|
||||||
import { isValid } from '@formily/shared';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -96,6 +97,16 @@ export const deprecatedBulkEditActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'openMode',
|
name: 'openMode',
|
||||||
Component: SchemaInitializerOpenModeSchemaItems,
|
Component: SchemaInitializerOpenModeSchemaItems,
|
||||||
@ -138,6 +149,16 @@ export const bulkEditActionSettings = new SchemaSettings({
|
|||||||
return buttonEditorProps;
|
return buttonEditorProps;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'openMode',
|
name: 'openMode',
|
||||||
Component: SchemaInitializerOpenModeSchemaItems,
|
Component: SchemaInitializerOpenModeSchemaItems,
|
||||||
@ -158,6 +179,7 @@ export const bulkEditActionSettings = new SchemaSettings({
|
|||||||
name: 'updateMode',
|
name: 'updateMode',
|
||||||
Component: UpdateMode,
|
Component: UpdateMode,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
sort: 100,
|
sort: 100,
|
||||||
@ -191,6 +213,17 @@ export const bulkEditFormSubmitActionSettings = new SchemaSettings({
|
|||||||
name: 'afterSuccessfulSubmission',
|
name: 'afterSuccessfulSubmission',
|
||||||
Component: AfterSuccess,
|
Component: AfterSuccess,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'linkageRules',
|
||||||
|
Component: SchemaSettingsLinkageRules,
|
||||||
|
useComponentProps() {
|
||||||
|
const { linkageRulesProps } = useSchemaToolbar();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...linkageRulesProps,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'refreshDataBlockRequest',
|
name: 'refreshDataBlockRequest',
|
||||||
Component: RefreshDataBlockRequest,
|
Component: RefreshDataBlockRequest,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/plugin-action-bulk-update",
|
"name": "@nocobase/plugin-action-bulk-update",
|
||||||
"version": "1.7.0-beta.16",
|
"version": "1.7.0-beta.18",
|
||||||
"main": "dist/server/index.js",
|
"main": "dist/server/index.js",
|
||||||
"homepage": "https://docs.nocobase.com/handbook/action-bulk-update",
|
"homepage": "https://docs.nocobase.com/handbook/action-bulk-update",
|
||||||
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update",
|
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user