diff --git a/.github/workflows/manual-npm-publish-license-kit.yml b/.github/workflows/manual-npm-publish-license-kit.yml new file mode 100644 index 0000000000..d4703a82d5 --- /dev/null +++ b/.github/workflows/manual-npm-publish-license-kit.yml @@ -0,0 +1,619 @@ +name: Manual npm publish license-kit +env: + DEBUG: napi:* + APP_NAME: license-kit + MACOSX_DEPLOYMENT_TARGET: '10.13' +permissions: + contents: write + id-token: write +on: + workflow_dispatch: + +# 'on': +# push: +# branches: +# - main +# tags-ignore: +# - '**' +# paths-ignore: +# - '**/*.md' +# - LICENSE +# - '**/*.gitignore' +# - .editorconfig +# - docs/** +# pull_request: null +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + build: yarn build --target x86_64-apple-darwin + - host: windows-latest + build: yarn build --target x86_64-pc-windows-msvc + target: x86_64-pc-windows-msvc + - host: windows-latest + build: yarn build --target i686-pc-windows-msvc + target: i686-pc-windows-msvc + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian + build: yarn build --target x86_64-unknown-linux-gnu + - host: ubuntu-latest + target: x86_64-unknown-linux-musl + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build: yarn build --target x86_64-unknown-linux-musl + - host: macos-latest + target: aarch64-apple-darwin + build: yarn build --target aarch64-apple-darwin + - host: ubuntu-latest + target: aarch64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 + build: yarn build --target aarch64-unknown-linux-gnu + - host: ubuntu-latest + target: armv7-unknown-linux-gnueabihf + setup: | + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf -y + build: yarn build --target armv7-unknown-linux-gnueabihf + - host: ubuntu-latest + target: armv7-unknown-linux-musleabihf + build: yarn build --target armv7-unknown-linux-musleabihf + - host: ubuntu-latest + target: aarch64-linux-android + build: yarn build --target aarch64-linux-android + - host: ubuntu-latest + target: armv7-linux-androideabi + build: yarn build --target armv7-linux-androideabi + - host: ubuntu-latest + target: aarch64-unknown-linux-musl + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build: |- + set -e && + rustup target add aarch64-unknown-linux-musl && + yarn build --target aarch64-unknown-linux-musl + - host: windows-latest + target: aarch64-pc-windows-msvc + build: yarn build --target aarch64-pc-windows-msvc + - host: ubuntu-latest + target: riscv64gc-unknown-linux-gnu + setup: | + sudo apt-get update + sudo apt-get install gcc-riscv64-linux-gnu -y + build: yarn build --target riscv64gc-unknown-linux-gnu + name: stable - ${{ matrix.settings.target }} - node@20 + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Setup node + uses: actions/setup-node@v4 + if: ${{ !matrix.settings.docker }} + with: + node-version: 20 + cache: yarn + - name: Install + uses: dtolnay/rust-toolchain@stable + if: ${{ !matrix.settings.docker }} + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + - uses: goto-bus-stop/setup-zig@v2 + if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' || matrix.settings.target == 'armv7-unknown-linux-musleabihf' }} + with: + version: 0.13.0 + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + - name: Setup node x86 + if: matrix.settings.target == 'i686-pc-windows-msvc' + run: yarn config set supportedArchitectures.cpu "ia32" + shell: bash + - name: Install dependencies + run: yarn install + - name: Setup node x86 + uses: actions/setup-node@v4 + if: matrix.settings.target == 'i686-pc-windows-msvc' + with: + node-version: 20 + cache: yarn + architecture: x86 + - name: Install OpenSSL dev + if: ${{ matrix.settings.host == 'ubuntu-latest' }} + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libssl-dev + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: '--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build' + run: ${{ matrix.settings.build }} + - name: Build + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} + shell: bash + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: bindings-${{ matrix.settings.target }} + path: ${{ env.APP_NAME }}.*.node + if-no-files-found: error + build-freebsd: + runs-on: ubuntu-latest + name: Build FreeBSD + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Build + id: build + uses: cross-platform-actions/action@v0.27.0 + env: + DEBUG: napi:* + RUSTUP_IO_THREADS: 1 + with: + operating_system: freebsd + version: '14.2' + memory: 8G + cpu_count: 3 + environment_variables: 'DEBUG RUSTUP_IO_THREADS' + shell: bash + run: | + sudo pkg install -y -f curl node libnghttp2 npm openssl + sudo npm install -g yarn --ignore-scripts + curl https://sh.rustup.rs -sSf --output rustup.sh + sh rustup.sh -y --profile minimal --default-toolchain beta + source "$HOME/.cargo/env" + echo "~~~~ rustc --version ~~~~" + rustc --version + echo "~~~~ node -v ~~~~" + node -v + echo "~~~~ yarn --version ~~~~" + yarn --version + pwd + ls -lah + whoami + env + freebsd-version + yarn install + yarn build + rm -rf node_modules + rm -rf target + rm -rf .yarn/cache + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: bindings-freebsd + path: ${{ env.APP_NAME }}.*.node + if-no-files-found: error + test-macOS-windows-binding: + name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + - host: windows-latest + target: x86_64-pc-windows-msvc + node: + - '18' + - '20' + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: yarn + architecture: x64 + - name: Install dependencies + run: yarn install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-${{ matrix.settings.target }} + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: yarn test + test-linux-x64-gnu-binding: + name: Test bindings on Linux-x64-gnu - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-x86_64-unknown-linux-gnu + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-slim yarn test + test-linux-x64-musl-binding: + name: Test bindings on x86_64-unknown-linux-musl - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: yarn + - name: Install dependencies + run: | + yarn config set supportedArchitectures.libc "musl" + yarn install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-x86_64-unknown-linux-musl + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-alpine yarn test + test-linux-aarch64-gnu-binding: + name: Test bindings on aarch64-unknown-linux-gnu - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-aarch64-unknown-linux-gnu + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Install dependencies + run: | + yarn config set supportedArchitectures.cpu "arm64" + yarn config set supportedArchitectures.libc "glibc" + yarn install + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Setup and run tests + uses: addnab/docker-run-action@v3 + with: + image: node:${{ matrix.node }}-slim + options: '--platform linux/arm64 -v ${{ github.workspace }}:/build -w /build' + run: | + set -e + yarn test + ls -la + test-linux-aarch64-musl-binding: + name: Test bindings on aarch64-unknown-linux-musl - node@${{ matrix.node }} + needs: + - build + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-aarch64-unknown-linux-musl + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Install dependencies + run: | + yarn config set supportedArchitectures.cpu "arm64" + yarn config set supportedArchitectures.libc "musl" + yarn install + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Setup and run tests + uses: addnab/docker-run-action@v3 + with: + image: node:lts-alpine + options: '--platform linux/arm64 -v ${{ github.workspace }}:/build -w /build' + run: | + set -e + yarn test + test-linux-arm-gnueabihf-binding: + name: Test bindings on armv7-unknown-linux-gnueabihf - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-armv7-unknown-linux-gnueabihf + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Install dependencies + run: | + yarn config set supportedArchitectures.cpu "arm" + yarn install + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Setup and run tests + uses: addnab/docker-run-action@v3 + with: + image: node:${{ matrix.node }}-bullseye-slim + options: '--platform linux/arm/v7 -v ${{ github.workspace }}:/build -w /build' + run: | + set -e + yarn test + ls -la + universal-macOS: + name: Build universal macOS binary + needs: + - build + runs-on: macos-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download macOS x64 artifact + uses: actions/download-artifact@v4 + with: + name: bindings-x86_64-apple-darwin + path: artifacts + - name: Download macOS arm64 artifact + uses: actions/download-artifact@v4 + with: + name: bindings-aarch64-apple-darwin + path: artifacts + - name: Combine binaries + run: yarn universal + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: bindings-universal-apple-darwin + path: ${{ env.APP_NAME }}.*.node + if-no-files-found: error + publish: + name: Publish + runs-on: ubuntu-latest + needs: + - build-freebsd + - test-macOS-windows-binding + - test-linux-x64-gnu-binding + - test-linux-x64-musl-binding + - test-linux-aarch64-gnu-binding + - test-linux-aarch64-musl-binding + - test-linux-arm-gnueabihf-binding + - universal-macOS + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.NOCOBASE_APP_ID }} + private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }} + repositories: license-kit + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v4 + with: + repository: nocobase/license-kit + token: ${{ steps.app-token.outputs.token }} + persist-credentials: true + ref: main + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + - name: Move artifacts + run: yarn artifacts + - name: List packages + run: ls -R ./npm + shell: bash + - name: Publish + run: | + npm config set provenance true + if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --access public + elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --tag next --access public + else + echo "Not a release, skipping publish" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx b/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx index 3cb1a80436..018818a31f 100644 --- a/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx +++ b/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx @@ -34,20 +34,6 @@ export interface SchemaSettingsChildrenProps { children: SchemaSettingsItemType[]; } -const typeComponentMap = { - item: SchemaSettingsItem, - itemGroup: SchemaSettingsItemGroup, - subMenu: SchemaSettingsSubMenu, - divider: SchemaSettingsDivider, - remove: SchemaSettingsRemove, - select: SchemaSettingsSelectItem, - cascader: SchemaSettingsCascaderItem, - switch: SchemaSettingsSwitchItem, - popup: SchemaSettingsPopupItem, - actionModal: SchemaSettingsActionModalItem, - modal: SchemaSettingsModalItem, -}; - const SchemaSettingsChildErrorFallback: FC< FallbackProps & { title: string; @@ -113,6 +99,19 @@ export const SchemaSettingsChild: FC = (props) => { hideIfNoChildren, componentProps, } = props as any; + const typeComponentMap = { + item: SchemaSettingsItem, + itemGroup: SchemaSettingsItemGroup, + subMenu: SchemaSettingsSubMenu, + divider: SchemaSettingsDivider, + remove: SchemaSettingsRemove, + select: SchemaSettingsSelectItem, + cascader: SchemaSettingsCascaderItem, + switch: SchemaSettingsSwitchItem, + popup: SchemaSettingsPopupItem, + actionModal: SchemaSettingsActionModalItem, + modal: SchemaSettingsModalItem, + }; const useChildrenRes = useChildren(); const useComponentPropsRes = useComponentProps(); const findComponent = useFindComponent(); diff --git a/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx b/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx index 704c7156f7..2047aeb971 100644 --- a/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx +++ b/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx @@ -22,8 +22,15 @@ import { useCreateFormBlockProps } from '../modules/blocks/data-blocks/form/hook import { useEditFormBlockDecoratorProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps'; import { useEditFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockProps'; import { useDataFormItemProps } from '../modules/blocks/data-blocks/form/hooks/useDataFormItemProps'; -import { useGridCardBlockDecoratorProps } from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps'; -import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps'; +import { + useGridCardBlockDecoratorProps, + useGridCardBlockItemProps, + useGridCardBlockProps, +} from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps'; +import { + useListBlockDecoratorProps, + useListBlockProps, +} from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps'; import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps'; import { TableColumnSchemaToolbar } from '../modules/blocks/data-blocks/table/TableColumnSchemaToolbar'; import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps'; @@ -80,11 +87,14 @@ export const BlockSchemaComponentProvider: React.FC = (props) => { useTableSelectorProps, useTableBlockDecoratorProps, useListBlockDecoratorProps, + useListBlockProps, useTableSelectorDecoratorProps, useCollapseBlockDecoratorProps, useFilterFormBlockProps, useFilterFormBlockDecoratorProps, useGridCardBlockDecoratorProps, + useGridCardBlockItemProps, + useGridCardBlockProps, useFormItemProps, useDataFormItemProps, }} @@ -141,11 +151,14 @@ export class BlockSchemaComponentPlugin extends Plugin { useTableSelectorProps, useTableBlockDecoratorProps, useListBlockDecoratorProps, + useListBlockProps, useTableSelectorDecoratorProps, useCollapseBlockDecoratorProps, useFilterFormBlockProps, useFilterFormBlockDecoratorProps, useGridCardBlockDecoratorProps, + useGridCardBlockProps, + useGridCardBlockItemProps, useFormItemProps, useDataFormItemProps, }); diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index bd2c39d05a..86c8ad8b19 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -1508,6 +1508,7 @@ export const useAssociationFilterBlockProps = () => { run, valueKey, labelKey, + dataScopeFilter: filter, }; }; async function doReset({ diff --git a/packages/core/client/src/common/AppNotFound.tsx b/packages/core/client/src/common/AppNotFound.tsx new file mode 100644 index 0000000000..1b5f84c7d5 --- /dev/null +++ b/packages/core/client/src/common/AppNotFound.tsx @@ -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 { Button, Result } from 'antd'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; + +export const AppNotFound = () => { + const navigate = useNavigate(); + const { t } = useTranslation(); + return ( + navigate('/', { replace: true })} type="primary"> + Back Home + + } + /> + ); +}; diff --git a/packages/core/client/src/common/index.ts b/packages/core/client/src/common/index.ts index dbc0e2243b..c581b3be5e 100644 --- a/packages/core/client/src/common/index.ts +++ b/packages/core/client/src/common/index.ts @@ -7,5 +7,6 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +export * from './AppNotFound'; export * from './SelectWithTitle'; export * from './useFieldComponentName'; diff --git a/packages/core/client/src/data-source/data-block/DataBlockProvider.tsx b/packages/core/client/src/data-source/data-block/DataBlockProvider.tsx index 89a9786308..1102d5825c 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockProvider.tsx @@ -23,6 +23,7 @@ import { import { CollectionRecord } from '../collection-record'; import { BlockRequestProvider } from './DataBlockRequestProvider'; import { DataBlockResourceProvider } from './DataBlockResourceProvider'; +import { BlockLinkageRuleProvider } from '../../modules/blocks/BlockLinkageRuleProvider'; export interface AllDataBlockProps { collection: string | CollectionOptions; @@ -189,13 +190,15 @@ export const DataBlockProvider: FC> = withDynamicSche - - - - {children} - - - + + + + + {children} + + + + diff --git a/packages/core/client/src/locale/de-DE.json b/packages/core/client/src/locale/de-DE.json index fe9b43dd49..e5f3529d05 100644 --- a/packages/core/client/src/locale/de-DE.json +++ b/packages/core/client/src/locale/de-DE.json @@ -500,7 +500,8 @@ "Turn pages": "Seiten umblättern", "Others": "Andere", "Other records": "Andere Datensätze", - "Save as template": "Als Vorlage speichern", + "Save as reference template": "Als Referenzvorlage speichern", + "Save as inherited template": "Als vererbte Vorlage speichern", "Save as block template": "Als Blockvorlage speichern", "Block templates": "Blockvorlagen", "Block template": "Blockvorlage", @@ -589,6 +590,7 @@ "Blank block": "Leerer Block", "Duplicate template": "Vorlage duplizieren", "Reference template": "Referenzvorlage", + "Inherited template": "Vererbte Vorlage", "Create calendar block": "Kalenderblock erstellen", "Create kanban block": "Kanban-Block erstellen", "Grouping field": "Gruppierungsfeld", diff --git a/packages/core/client/src/locale/en-US.json b/packages/core/client/src/locale/en-US.json index b3b42c43e2..7f3bb24433 100644 --- a/packages/core/client/src/locale/en-US.json +++ b/packages/core/client/src/locale/en-US.json @@ -505,7 +505,7 @@ "Save as block template": "Save as block template", "Block templates": "Block templates", "Block template": "Block template", - "Convert reference to duplicate": "Convert reference to duplicate", + "Convert template to duplicate": "Convert template to duplicate", "Template name": "Template name", "Block type": "Block type", "No blocks to connect": "No blocks to connect", @@ -590,6 +590,7 @@ "Blank block": "Blank block", "Duplicate template": "Duplicate template", "Reference template": "Reference template", + "Inherited template": "Inherited template", "Create calendar block": "Create calendar block", "Create kanban block": "Create kanban block", "Grouping field": "Grouping field", @@ -889,7 +890,6 @@ "No pages yet, please configure first": "No pages yet, please configure first", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode", "Deprecated": "Deprecated", - "The following old template features have been deprecated and will be removed in next version.": "The following old template features have been deprecated and will be removed in next version.", "Full permissions": "Full permissions", "Refresh data blocks": "Refresh data blocks", "Select data blocks to refresh": "Select data blocks to refresh", diff --git a/packages/core/client/src/locale/es-ES.json b/packages/core/client/src/locale/es-ES.json index 06e30c4dfa..69765e3b84 100644 --- a/packages/core/client/src/locale/es-ES.json +++ b/packages/core/client/src/locale/es-ES.json @@ -470,7 +470,8 @@ "Turn pages": "Pasar páginas", "Others": "Otros", "Other records": "Otros registros", - "Save as template": "Guardar como plantilla", + "Save as reference template": "Guardar como plantilla de referencia", + "Save as inherited template": "Guardar como plantilla heredada", "Save as block template": "Guardar como plantilla de bloque", "Block templates": "Bloquear plantillas", "Block template": "Plantilla de bloque", @@ -560,6 +561,7 @@ "Blank block": "Bloque en blanco", "Duplicate template": "Duplicar plantilla", "Reference template": "Plantilla de referencia", + "Inherited template": "Plantilla heredada", "Create calendar block": "Crear bloque de calendario", "Create kanban block": "Crear bloque kanban", "Grouping field": "Campo de agrupación", @@ -806,7 +808,6 @@ "No pages yet, please configure first": "Aún no hay páginas, por favor configura primero", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Haga clic en el icono \"Editor de UI\" en la esquina superior derecha para entrar en el modo de Editor de UI.", "Deprecated": "Obsoleto", - "The following old template features have been deprecated and will be removed in next version.": "Las siguientes características de plantilla antigua han quedado obsoletas y se eliminarán en la próxima versión.", "Full permissions": "Todos los derechos", "Refresh data blocks": "Actualizar bloques de datos", "Select data blocks to refresh": "Actualizar bloques de datos", diff --git a/packages/core/client/src/locale/fr-FR.json b/packages/core/client/src/locale/fr-FR.json index ab2c5fb444..a0fa53f2fa 100644 --- a/packages/core/client/src/locale/fr-FR.json +++ b/packages/core/client/src/locale/fr-FR.json @@ -485,7 +485,8 @@ "Turn pages": "Tourner les pages", "Others": "Autres", "Other records": "Autres enregistrements", - "Save as template": "Enregistrer en tant que modèle", + "Save as reference template": "Enregistrer en tant que modèle de référence", + "Save as inherited template": "Enregistrer en tant que modèle hérité", "Save as block template": "Enregistrer en tant que modèle de bloc", "Block templates": "Modèles de bloc", "Block template": "Modèle de bloc", @@ -573,6 +574,7 @@ "Blank block": "Bloc vierge", "Duplicate template": "Dupliquer le modèle", "Reference template": "Référencer le modèle", + "Inherited template": "Modèle hérité", "Create calendar block": "Créer un bloc de calendrier", "Create kanban block": "Créer un bloc kanban", "Grouping field": "Champ de regroupement", @@ -826,7 +828,6 @@ "No pages yet, please configure first": "Pas encore de pages, veuillez configurer d'abord", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Cliquez sur l'icône \"Éditeur d'interface utilisateur\" dans le coin supérieur droit pour entrer en mode Éditeur d'interface utilisateur", "Deprecated": "Déprécié", - "The following old template features have been deprecated and will be removed in next version.": "Les fonctionnalités des anciens modèles ont été dépréciées et seront supprimées dans la prochaine version.", "Full permissions": "Tous les droits", "Refresh data blocks": "Actualiser les blocs de données", "Select data blocks to refresh": "Actualiser les blocs de données", diff --git a/packages/core/client/src/locale/it-IT.json b/packages/core/client/src/locale/it-IT.json index d6b0f39bf9..8fe47eea95 100644 --- a/packages/core/client/src/locale/it-IT.json +++ b/packages/core/client/src/locale/it-IT.json @@ -494,7 +494,8 @@ "Turn pages": "Volta pagine", "Others": "Altri", "Other records": "Altri record", - "Save as template": "Salva come modello", + "Save as reference template": "Salva come modello di riferimento", + "Save as inherited template": "Salva come modello ereditato", "Save as block template": "Salva come modello blocco", "Block templates": "Modelli blocco", "Block template": "Modello blocco", @@ -580,6 +581,7 @@ "Blank block": "Blocco vuoto", "Duplicate template": "Modello duplicato", "Reference template": "Modello di riferimento", + "Inherited template": "Modello ereditato", "Create calendar block": "Crea blocco calendario", "Create kanban block": "Crea blocco kanban", "Grouping field": "Campo di raggruppamento", diff --git a/packages/core/client/src/locale/ja-JP.json b/packages/core/client/src/locale/ja-JP.json index 1918d9968f..2d190cd566 100644 --- a/packages/core/client/src/locale/ja-JP.json +++ b/packages/core/client/src/locale/ja-JP.json @@ -397,7 +397,8 @@ "Turn pages": "ページをめくる", "Others": "その他", "Other records": "他のレコード", - "Save as template": "テンプレートとして保存", + "Save as reference template": "参照テンプレートとして保存", + "Save as inherited template": "継承テンプレートとして保存", "Save as block template": "ブロックテンプレートとして保存", "Block templates": "ブロックテンプレート", "Block template": "ブロックテンプレート", @@ -472,6 +473,7 @@ "Blank block": "空のブロック", "Duplicate template": "テンプレートをコピー", "Reference template": "テンプレートを参照", + "Inherited template": "継承テンプレート", "Create calendar block": "カレンダーブロックの作成", "Create kanban block": "かんばんブロックの作成", "Grouping field": "グループフィールド", @@ -1044,7 +1046,6 @@ "No pages yet, please configure first": "まだページがありません。最初に設定してください", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "ユーザーインターフェースエディターモードに入るには、右上隅の「UIエディタ」アイコンをクリックしてください", "Deprecated": "非推奨", - "The following old template features have been deprecated and will be removed in next version.": "次の古いテンプレート機能は非推奨になり、次のバージョンで削除されます。", "Full permissions": "すべての権限", "Refresh data blocks": "データブロックを更新", "Select data blocks to refresh": "データブロックを選択して更新", diff --git a/packages/core/client/src/locale/ko-KR.json b/packages/core/client/src/locale/ko-KR.json index e007ae97f7..1866fc8486 100644 --- a/packages/core/client/src/locale/ko-KR.json +++ b/packages/core/client/src/locale/ko-KR.json @@ -517,7 +517,8 @@ "Turn pages": "페이지 넘김", "Others": "기타", "Other records": "기타 레코드", - "Save as template": "템플릿으로 저장", + "Save as reference template": "참조 템플릿으로 저장", + "Save as inherited template": "상속 템플릿으로 저장", "Save as block template": "블록 템플릿으로 저장", "Block templates": "블록 템플릿", "Block template": "블록 템플릿", @@ -601,6 +602,7 @@ "Blank block": "빈 블록", "Duplicate template": "템플릿 복제", "Reference template": "참조 템플릿", + "Inherited template": "상속 템플릿", "Create calendar block": "캘린더 블록 생성", "Create kanban block": "칸반 블록 생성", "Grouping field": "그루핑 필드", @@ -917,7 +919,6 @@ "No pages yet, please configure first": "아직 페이지가 없습니다. 먼저 설정하십시오", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "사용자 인터페이스 편집기 모드에 들어가려면 오른쪽 상단의 \"UI 편집기\" 아이콘을 클릭하십시오", "Deprecated": "사용 중단됨", - "The following old template features have been deprecated and will be removed in next version.": "다음 오래된 템플릿 기능은 사용 중단되었으며 다음 버전에서 제거될 것입니다.", "Full permissions": "모든 권한", "Refresh data blocks": "데이터 블록 새로 고침", "Select data blocks to refresh": "데이터 블록을 선택하여 새로 고침", diff --git a/packages/core/client/src/locale/nl-NL.json b/packages/core/client/src/locale/nl-NL.json index 1e58c0bd68..ab31e6f767 100644 --- a/packages/core/client/src/locale/nl-NL.json +++ b/packages/core/client/src/locale/nl-NL.json @@ -504,7 +504,8 @@ "Turn pages": "Pagina's omslaan", "Others": "Overigen", "Other records": "Andere records", - "Save as template": "Opslaan als sjabloon", + "Save as reference template": "Opslaan als referentiesjabloon", + "Save as inherited template": "Opslaan als overerfde sjabloon", "Save as block template": "Opslaan als bloksjabloon", "Block templates": "Bloksjablonen", "Block template": "Bloksjabloon", @@ -593,6 +594,7 @@ "Blank block": "Leeg blok", "Duplicate template": "Sjabloon dupliceren", "Reference template": "Sjabloon refereren", + "Inherited template": "Overerfde sjabloon", "Create calendar block": "Kalenderblok maken", "Create kanban block": "Kanbanblok maken", "Grouping field": "Groepeer veld", diff --git a/packages/core/client/src/locale/pt-BR.json b/packages/core/client/src/locale/pt-BR.json index cf7a17d36a..8bd393a253 100644 --- a/packages/core/client/src/locale/pt-BR.json +++ b/packages/core/client/src/locale/pt-BR.json @@ -433,7 +433,8 @@ "Turn pages": "Virar páginas", "Others": "Outros", "Other records": "Outros registros", - "Save as template": "Salvar como modelo", + "Save as reference template": "Salvar como modelo de referência", + "Save as inherited template": "Salvar como modelo herdado", "Save as block template": "Salvar como modelo de bloco", "Block templates": "Modelos de bloco", "Block template": "Modelo de bloco", @@ -525,6 +526,7 @@ "Blank block": "Bloco em branco", "Duplicate template": "Duplicar modelo", "Reference template": "Modelo de referência", + "Inherited template": "Modelo herdado", "Create calendar block": "Criar bloco de calendário", "Create kanban block": "Criar bloco Kanban", "Grouping field": "Campo de agrupamento", @@ -781,7 +783,6 @@ "Are you sure you want to hide this tab?": "Tem certeza de que deseja ocultar esta guia?", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Depois de ocultar, esta guia não aparecerá mais na barra de guias. Para mostrá-la novamente, você precisa ir à página de gerenciamento de rotas para configurá-la.", "Deprecated": "Descontinuado", - "The following old template features have been deprecated and will be removed in next version.": "As seguintes funcionalidades de modelo antigo foram descontinuadas e serão removidas na próxima versão.", "Full permissions": "Todas as permissões", "Refresh data blocks": "Atualizar blocos de dados", "Select data blocks to refresh": "Selecionar blocos de dados para atualizar", diff --git a/packages/core/client/src/locale/ru-RU.json b/packages/core/client/src/locale/ru-RU.json index a86f02e638..565af92f97 100644 --- a/packages/core/client/src/locale/ru-RU.json +++ b/packages/core/client/src/locale/ru-RU.json @@ -337,7 +337,8 @@ "Turn pages": "Перелистывать страницы", "Others": "Другие", "Other records": "Другие записи", - "Save as template": "Сохранить как шаблон", + "Save as reference template": "Сохранить как шаблон ссылки", + "Save as inherited template": "Сохранить как шаблон наследования", "Save as block template": "Сохранить как шаблон Блока", "Block templates": "Шаблоны блоков", "Convert reference to duplicate": "Преобразовать ссылку в дубликат", @@ -411,6 +412,7 @@ "Blank block": "Пустой блок", "Duplicate template": "Дублировать шаблон", "Reference template": "Справочный шаблон", + "Inherited template": "Наследуемый шаблон", "Create calendar block": "Создать блок календаря", "Create kanban block": "Создать блок Канбан", "Grouping field": "Поле группировки", @@ -610,7 +612,6 @@ "Are you sure you want to hide this tab?": "Вы уверены, что хотите скрыть эту вкладку?", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "После скрытия этой вкладки она больше не будет отображаться во вкладке. Чтобы снова отобразить ее, вам нужно будет перейти на страницу управления маршрутами, чтобы установить ее.", "Deprecated": "Устаревший", - "The following old template features have been deprecated and will be removed in next version.": "Следующие старые функции шаблонов устарели и будут удалены в следующей версии.", "Full permissions": "Полные права", "Refresh data blocks": "Обновить блоки данных", "Select data blocks to refresh": "Выберите блоки данных для обновления", diff --git a/packages/core/client/src/locale/tr-TR.json b/packages/core/client/src/locale/tr-TR.json index 7a8dfc99cb..a4eb173776 100644 --- a/packages/core/client/src/locale/tr-TR.json +++ b/packages/core/client/src/locale/tr-TR.json @@ -336,7 +336,8 @@ "Turn pages": "Sayfaları çevir", "Others": "Diğerleri", "Other records": "Diğer kayıtlar", - "Save as template": "Şablon olarak kaydet", + "Save as reference template": "Referans şablonu olarak kaydet", + "Save as inherited template": "Kalıtım şablonu olarak kaydet", "Save as block template": "Blok şablonu olarak kaydet", "Block templates": "Blok şablonları", "Block template": "Blok şablonu", @@ -411,6 +412,7 @@ "Blank block": "Boş blok", "Duplicate template": "Şablonun kopyasını oluştur", "Reference template": "Referans şablon", + "Inherited template": "Kalıtım şablonu", "Create calendar block": "Takvim bloğu oluştur", "Create kanban block": "Kanban bloğu oluştur", "Grouping field": "Alan gruplandırma", @@ -608,7 +610,6 @@ "Are you sure you want to hide this tab?": "Bu sekmeyi gizlemek istediğinizden emin misiniz?", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Gizlendikten sonra, bu sekme artık sekme çubuğunda görünmeyecek. Onu tekrar göstermek için, rotayı yönetim sayfasına gidip ayarlamanız gerekiyor.", "Deprecated": "Kullanımdan kaldırıldı", - "The following old template features have been deprecated and will be removed in next version.": "Aşağıdaki eski şablon özellikleri kullanımdan kaldırıldı ve gelecek sürümlerde kaldırılacaktır.", "Full permissions": "Tüm izinler", "Refresh data blocks": "Yenile veri blokları", "Select data blocks to refresh": "Veri bloklarını yenilemek için seçin", diff --git a/packages/core/client/src/locale/uk-UA.json b/packages/core/client/src/locale/uk-UA.json index cf54fe03c6..fea0787b9a 100644 --- a/packages/core/client/src/locale/uk-UA.json +++ b/packages/core/client/src/locale/uk-UA.json @@ -487,7 +487,8 @@ "Turn pages": "Переключати сторінки", "Others": "Інші", "Other records": "Інші записи", - "Save as template": "Зберегти як шаблон", + "Save as reference template": "Зберегти як шаблон посилання", + "Save as inherited template": "Зберегти як шаблон нащадка", "Save as block template": "Зберегти як шаблон блока", "Block templates": "Шаблони блоків", "Block template": "Шаблон блока", @@ -577,6 +578,7 @@ "Blank block": "Порожній блок", "Duplicate template": "Дублювати шаблон", "Reference template": "Посилання на шаблон", + "Inherited template": "Нащадковий шаблон", "Create calendar block": "Створити блок календаря", "Create kanban block": "Створити блок канбану", "Grouping field": "Поле для групування", @@ -824,7 +826,6 @@ "Are you sure you want to hide this tab?": "Ви впевнені, що хочете приховати цю вкладку?", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Після приховування цієї вкладки вона більше не з'явиться в панелі вкладок. Щоб знову показати її, вам потрібно перейти на сторінку керування маршрутами, щоб налаштувати її.", "Deprecated": "Застаріло", - "The following old template features have been deprecated and will be removed in next version.": "Наступні старі функції шаблонів були застарілі і будуть видалені в наступній версії.", "Full permissions": "Повні права", "Refresh data blocks": "Оновити дані блоків", "Select data blocks to refresh": "Виберіть блоки даних для оновлення", diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index ba082807f2..f2b35eee88 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -527,7 +527,8 @@ "Turn pages": "翻页", "Others": "其他", "Other records": "其他记录", - "Save as template": "保存为模板", + "Save as reference template": "保存为引用模板", + "Save as inherited template": "保存为继承模板", "Save as block template": "保存为区块模板", "Block templates": "区块模板", "Block template": "区块模板", @@ -611,6 +612,7 @@ "Blank block": "空区块", "Duplicate template": "复制模板", "Reference template": "引用模板", + "Inherited template": "继承模板", "Create calendar block": "创建日历区块", "Create kanban block": "创建看板区块", "Grouping field": "分组字段", @@ -1088,7 +1090,6 @@ "Are you sure you want to hide this tab?": "你确定要隐藏该标签页吗?", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "隐藏后,该标签将不再显示在标签栏中。要想再次显示它,你需要到路由管理页面进行设置。", "Deprecated": "已弃用", - "The following old template features have been deprecated and will be removed in next version.": "以下旧的模板功能已弃用,将在下个版本移除。", "Full permissions": "全部权限", "Enable index column": "启用序号列", "Date scope": "日期范围", @@ -1101,7 +1102,9 @@ "Italic": "斜体", "Response record":"响应结果记录", "Colon":"冒号", - "After successful submission, the selected data blocks will be automatically refreshed.": "提交成功后,会自动刷新这里选中的数据区块。", "No pages yet, please configure first": "暂无页面,请先配置", - "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "点击右上角的“界面配置”图标,进入界面配置模式" + "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "点击右上角的“界面配置”图标,进入界面配置模式", + "After successful submission, the selected data blocks will be automatically refreshed.": "提交成功后,会自动刷新这里选中的数据区块。", + "Block Linkage rules":"区块联动规则", + "Field Linkage rules":"字段联动规则" } diff --git a/packages/core/client/src/locale/zh-TW.json b/packages/core/client/src/locale/zh-TW.json index 5abc83e35d..63753e7ebb 100644 --- a/packages/core/client/src/locale/zh-TW.json +++ b/packages/core/client/src/locale/zh-TW.json @@ -518,7 +518,8 @@ "Turn pages": "翻頁", "Others": "其他", "Other records": "其他記錄", - "Save as template": "儲存為模板", + "Save as reference template": "儲存為引用模板", + "Save as inherited template": "儲存為繼承模板", "Save as block template": "儲存為區塊模板", "Block templates": "區塊模板", "Block template": "區塊模板", @@ -602,6 +603,7 @@ "Blank block": "空區塊", "Duplicate template": "複製模板", "Reference template": "引用模板", + "Inherited template": "繼承模板", "Create calendar block": "建立日曆區塊", "Create kanban block": "建立看板區塊", "Grouping field": "群組欄位", @@ -915,7 +917,6 @@ "Are you sure you want to hide this tab?": "你確定要隱藏這個標籤嗎?", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "隱藏後,這個標籤將不再出現在標籤欄中。要再次顯示它,你需要到路由管理頁面進行設置。", "Deprecated": "已棄用", - "The following old template features have been deprecated and will be removed in next version.": "以下舊的模板功能已棄用,將在下個版本移除。", "Full permissions": "完全權限", "Refresh data blocks": "刷新數據區塊", "Select data blocks to refresh": "選擇要刷新的數據區塊", diff --git a/packages/core/client/src/modules/actions/__e2e__/link/basic.test.ts b/packages/core/client/src/modules/actions/__e2e__/link/basic.test.ts index 8b2a31eddb..0212e4abe5 100644 --- a/packages/core/client/src/modules/actions/__e2e__/link/basic.test.ts +++ b/packages/core/client/src/modules/actions/__e2e__/link/basic.test.ts @@ -33,8 +33,9 @@ test.describe('Link', () => { ).toHaveCount(1); await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-actionSettings:link-users' }).hover(); await page.getByRole('menuitem', { name: 'Edit link' }).click(); + await page.getByLabel('block-item-users-URL').getByLabel('textbox').click(); await page - .getByLabel('block-item-users-table-URL') + .getByLabel('block-item-users-URL') .getByLabel('textbox') .fill(await nocoPage.getUrl()); await page.getByPlaceholder('Name').fill('id'); @@ -102,7 +103,7 @@ test.describe('Link', () => { await page.getByLabel('action-Action.Link-Link-').hover(); await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:link-users').hover(); await page.getByRole('menuitem', { name: 'Edit link' }).click(); - await page.getByLabel('block-item-users-table-URL').getByLabel('textbox').fill(otherPageUrl); + await page.getByLabel('block-item-users-URL').getByLabel('textbox').fill(otherPageUrl); await page.getByRole('button', { name: 'OK', exact: true }).click(); await page.getByLabel('action-Action.Link-Link-').click(); diff --git a/packages/core/client/src/modules/actions/add-new/addNewActionSettings.tsx b/packages/core/client/src/modules/actions/add-new/addNewActionSettings.tsx index 9c4d55b34a..886af15e42 100644 --- a/packages/core/client/src/modules/actions/add-new/addNewActionSettings.tsx +++ b/packages/core/client/src/modules/actions/add-new/addNewActionSettings.tsx @@ -69,6 +69,21 @@ export const addNewActionSettings = new SchemaSettings({ return isChildCollectionAction; }, }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { association } = useDataBlockProps() || {}; + const { name } = useCollection_deprecated(); + const { getCollectionField } = useCollectionManager_deprecated(); + const associationField = getCollectionField(association); + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + collectionName: associationField?.collectionName || name, + }; + }, + }, { name: 'delete', sort: 100, diff --git a/packages/core/client/src/modules/actions/bulk-destroy/bulkDeleteActionSettings.tsx b/packages/core/client/src/modules/actions/bulk-destroy/bulkDeleteActionSettings.tsx index f5c2ab2409..8597a75659 100644 --- a/packages/core/client/src/modules/actions/bulk-destroy/bulkDeleteActionSettings.tsx +++ b/packages/core/client/src/modules/actions/bulk-destroy/bulkDeleteActionSettings.tsx @@ -51,6 +51,16 @@ export const bulkDeleteActionSettings = new SchemaSettings({ }; }, }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + }; + }, + }, { name: 'remove', sort: 100, diff --git a/packages/core/client/src/modules/actions/filter/filterActionSettings.tsx b/packages/core/client/src/modules/actions/filter/filterActionSettings.tsx index 30e1197f13..542497f2d4 100644 --- a/packages/core/client/src/modules/actions/filter/filterActionSettings.tsx +++ b/packages/core/client/src/modules/actions/filter/filterActionSettings.tsx @@ -53,14 +53,33 @@ export const filterActionSettings = new SchemaSettings({ default: fieldSchema?.['x-component-props']?.icon, 'x-component-props': {}, }, + onlyIcon: { + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + title: t('Icon only'), + default: fieldSchema?.['x-component-props']?.onlyIcon, + 'x-component-props': {}, + 'x-reactions': [ + { + dependencies: ['icon'], + fulfill: { + state: { + hidden: '{{!$deps[0]}}', + }, + }, + }, + ], + }, }, } as ISchema, - onSubmit: ({ title, icon }) => { + onSubmit: ({ title, icon, onlyIcon }) => { fieldSchema.title = title; field.title = title; field.componentProps.icon = icon; + field.componentProps.onlyIcon = onlyIcon; fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; fieldSchema['x-component-props'].icon = icon; + fieldSchema['x-component-props'].onlyIcon = onlyIcon; dn.emit('patch', { schema: { ['x-uid']: fieldSchema['x-uid'], diff --git a/packages/core/client/src/modules/actions/link/customizeLinkActionSettings.tsx b/packages/core/client/src/modules/actions/link/customizeLinkActionSettings.tsx index 34c51a0556..a12f3abd0d 100644 --- a/packages/core/client/src/modules/actions/link/customizeLinkActionSettings.tsx +++ b/packages/core/client/src/modules/actions/link/customizeLinkActionSettings.tsx @@ -15,6 +15,7 @@ import { useDesignable } from '../../../'; import { useSchemaToolbar } from '../../../application'; import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings'; import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer'; +import { useCollectionManager_deprecated } from '../../../collection-manager'; import { SchemaSettingsLinkageRules, SchemaSettingsModalItem, @@ -96,6 +97,9 @@ export const customizeLinkActionSettings = new SchemaSettings({ Component: SchemaSettingsLinkageRules, useComponentProps() { const { linkageRulesProps } = useSchemaToolbar(); + const { association } = useDataBlockProps() || {}; + const { getCollectionField } = useCollectionManager_deprecated(); + const associationField = getCollectionField(association); return { ...linkageRulesProps, }; diff --git a/packages/core/client/src/modules/blocks/BlockLinkageRuleProvider.tsx b/packages/core/client/src/modules/blocks/BlockLinkageRuleProvider.tsx new file mode 100644 index 0000000000..7bb04fcbcd --- /dev/null +++ b/packages/core/client/src/modules/blocks/BlockLinkageRuleProvider.tsx @@ -0,0 +1,100 @@ +/** + * 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 React, { useMemo, useEffect, useState } from 'react'; +import { useFieldSchema, useForm } from '@formily/react'; +import { last, isEqual } from 'lodash'; +import { uid } from '@formily/shared'; +import { reaction } from '@formily/reactive'; +import { useLocalVariables, useVariables } from '../../variables'; +import { useReactiveLinkageEffect } from './utils'; +import { useDesignable } from '../../'; +import { forEachLinkageRule } from '../../schema-settings/LinkageRules/forEachLinkageRule'; +import { + getVariableValuesInCondition, + getVariableValuesInExpression, +} from '../../schema-settings/LinkageRules/bindLinkageRulesToFiled'; + +const getLinkageRules = (fieldSchema) => { + if (!fieldSchema) { + return []; + } + let linkageRules = fieldSchema?.['x-block-linkage-rules'] || []; + fieldSchema.mapProperties((schema) => { + if (schema['x-block-linkage-rules']) { + linkageRules = schema['x-block-linkage-rules']; + } + }); + return linkageRules?.filter((k) => !k.disabled); +}; + +export const BlockLinkageRuleProvider = (props) => { + const schema = useFieldSchema(); + const variables = useVariables(); + const localVariables = useLocalVariables(); + const { designable } = useDesignable(); + const form = useForm(); + const linkageRules = useMemo(() => getLinkageRules(schema), [schema]); + const [triggerLinkageUpdate, setTriggerLinkageUpdate] = useState(null); + const displayResult = useReactiveLinkageEffect(linkageRules, variables, localVariables, triggerLinkageUpdate); + const shouldCalculateFormLinkage = schema?.['x-decorator'] === 'FormItem' && !form.readPretty && linkageRules.length; + + useEffect(() => { + if (shouldCalculateFormLinkage) { + const id = uid(); + const disposes = []; + + // 延迟执行,防止一开始获取到的 form.values 值是旧的 + setTimeout(() => { + form.addEffects(id, () => { + forEachLinkageRule(linkageRules, (action, rule) => { + return reaction( + () => { + // 获取条件中的变量值 + const variableValuesInCondition = getVariableValuesInCondition({ linkageRules, localVariables }); + // 获取 value 表达式中的变量值 + const variableValuesInExpression = getVariableValuesInExpression({ action, localVariables }); + const result = [variableValuesInCondition, variableValuesInExpression] + .map((item) => JSON.stringify(item)) + .join(','); + return result; + }, + () => { + setTriggerLinkageUpdate(uid()); + }, + { fireImmediately: true, equals: isEqual }, + ); + }); + }); + }); + + // 清理副作用 + return () => { + form.removeEffects(id); + disposes.forEach((dispose) => { + dispose(); + }); + }; + } + }, [linkageRules, shouldCalculateFormLinkage]); + if (!linkageRules.length) { + return props.children; + } + + if (displayResult === null) return null; + if (last(displayResult) === 'hidden') { + if (designable) { + return
{props.children}
; + } else { + return null; + } + } + + return props.children; +}; diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts index 4168cb2752..3557653105 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts @@ -45,7 +45,7 @@ test.describe('multi data details block schema settings', () => { // 禁用规则,联动规则失效 await page.getByLabel('block-item-CardItem-users-').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:detailsWithPagination-users').hover(); - await page.getByText('Linkage rules').click(); + await page.getByText('Field Linkage rules').click(); await page.getByRole('switch', { name: 'On Off' }).click(); await page.getByRole('button', { name: 'OK' }).click(); await page.reload(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/detailsWithPaginationSettings.tsx b/packages/core/client/src/modules/blocks/data-blocks/details-multi/detailsWithPaginationSettings.tsx index 9653bd2d82..35363ab525 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-multi/detailsWithPaginationSettings.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/detailsWithPaginationSettings.tsx @@ -14,7 +14,7 @@ import { SchemaSettings } from '../../../../application/schema-settings/SchemaSe import { SchemaSettingsItemType } from '../../../../application/schema-settings/types'; import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; -import { useCollection_deprecated, useSortFields } from '../../../../collection-manager'; +import { useSortFields } from '../../../../collection-manager'; import { removeNullCondition, useDesignable } from '../../../../schema-component'; import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; @@ -24,6 +24,8 @@ import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettin import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { setDataLoadingModeSettingsItem } from './setDataLoadingModeSettingsItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; +import { useCollection } from '../../../../data-source'; const commonItems: SchemaSettingsItemType[] = [ { @@ -35,13 +37,28 @@ const commonItems: SchemaSettingsItemType[] = [ Component: SchemaSettingsBlockHeightItem, }, { - name: 'linkageRules', + name: 'fieldLinkageRules', Component: SchemaSettingsLinkageRules, useComponentProps() { - const { name } = useCollection_deprecated(); + const { name } = useCollection(); + const { t } = useTranslation(); return { collectionName: name, readPretty: true, + title: t('Field Linkage rules'), + }; + }, + }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, }; }, }, @@ -49,7 +66,7 @@ const commonItems: SchemaSettingsItemType[] = [ name: 'dataScope', Component: SchemaSettingsDataScope, useComponentProps() { - const { name } = useCollection_deprecated(); + const { name } = useCollection(); const fieldSchema = useFieldSchema(); const { form } = useFormBlockContext(); const field = useField(); @@ -83,7 +100,7 @@ const commonItems: SchemaSettingsItemType[] = [ name: 'sortingRules', type: 'modal', useComponentProps() { - const { name } = useCollection_deprecated(); + const { name } = useCollection(); const { t } = useTranslation(); const fieldSchema = useFieldSchema(); const field = useField(); @@ -201,7 +218,7 @@ const commonItems: SchemaSettingsItemType[] = [ name: 'template', Component: SchemaSettingsTemplate, useComponentProps() { - const { name } = useCollection_deprecated(); + const { name } = useCollection(); const fieldSchema = useFieldSchema(); const { componentNamePrefix } = useBlockTemplateContext(); const defaultResource = diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/detailsBlockSettings.ts b/packages/core/client/src/modules/blocks/data-blocks/details-single/detailsBlockSettings.ts index 6d7f136c3c..e15933e215 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-single/detailsBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/detailsBlockSettings.ts @@ -8,14 +8,17 @@ */ import { useFieldSchema } from '@formily/react'; +import { useTranslation } from 'react-i18next'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettingsItemType } from '../../../../application/schema-settings/types'; import { useCollection } from '../../../../data-source/collection/CollectionProvider'; +import { useCollection_deprecated } from '../../../../collection-manager'; import { SchemaSettingsFormItemTemplate, SchemaSettingsLinkageRules } from '../../../../schema-settings'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; const commonItems: SchemaSettingsItemType[] = [ { @@ -27,13 +30,28 @@ const commonItems: SchemaSettingsItemType[] = [ Component: SchemaSettingsBlockHeightItem, }, { - name: 'linkageRules', + name: 'fieldLinkageRules', Component: SchemaSettingsLinkageRules, useComponentProps() { const { name } = useCollection(); + const { t } = useTranslation(); return { collectionName: name, readPretty: true, + title: t('Field Linkage rules'), + }; + }, + }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection_deprecated(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, }; }, }, diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts index 86eda66087..56459afec6 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts @@ -310,7 +310,7 @@ test.describe('set default value', () => { // 设置联动规则 await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); - await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('menuitem', { name: 'Field linkage rules' }).click(); await page.mouse.move(300, 0); await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByText('Add property').click(); @@ -438,7 +438,7 @@ test.describe('set default value', () => { // 设置联动规则 await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); - await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('menuitem', { name: 'Field linkage rules' }).click(); await page.mouse.move(300, 0); await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByText('Add property').click(); @@ -563,7 +563,7 @@ test.describe('set default value', () => { // 设置联动规则 await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); - await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('menuitem', { name: 'Field linkage rules' }).click(); await page.mouse.move(300, 0); await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByText('Add property').click(); @@ -701,7 +701,7 @@ test.describe('set default value', () => { // 设置联动规则 await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); - await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('menuitem', { name: 'Field linkage rules' }).click(); await page.mouse.move(300, 0); await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByText('Add property').click(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-edit/deprecatedVariables.test.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-edit/deprecatedVariables.test.ts index 1461e81c7e..4c86cc9422 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-edit/deprecatedVariables.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-edit/deprecatedVariables.test.ts @@ -18,7 +18,7 @@ test.describe('deprecated variables', () => { await page.getByLabel('action-Action.Link-Edit').click(); await page.getByLabel('block-item-CardItem-users-form').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: 'Field linkage rules' }).click(); await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible(); // 2. 但是变量列表中是禁用状态 @@ -57,7 +57,7 @@ test.describe('deprecated variables', () => { // 4. 再次打开弹窗,变量列表中的弃用变量不再显示 await page.getByLabel('block-item-CardItem-users-form').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: 'Field linkage rules' }).click(); await page.locator('button').filter({ hasText: /^x$/ }).last().click(); await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden(); // 使下拉菜单消失 diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/createFormBlockSettings.tsx b/packages/core/client/src/modules/blocks/data-blocks/form/createFormBlockSettings.tsx index c16c806c77..e29e77446a 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/createFormBlockSettings.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/form/createFormBlockSettings.tsx @@ -8,6 +8,7 @@ */ import { useFieldSchema } from '@formily/react'; +import { useTranslation } from 'react-i18next'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; import { useCollection_deprecated } from '../../../../collection-manager'; @@ -21,6 +22,7 @@ import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/Schem import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; export const createFormBlockSettings = new SchemaSettings({ name: 'blockSettings:createForm', @@ -34,12 +36,27 @@ export const createFormBlockSettings = new SchemaSettings({ Component: SchemaSettingsBlockHeightItem, }, { - name: 'linkageRules', + name: 'fieldLinkageRules', Component: SchemaSettingsLinkageRules, useComponentProps() { const { name } = useCollection_deprecated(); + const { t } = useTranslation(); return { collectionName: name, + title: t('Field Linkage rules'), + }; + }, + }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection_deprecated(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, }; }, }, diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/editFormBlockSettings.ts b/packages/core/client/src/modules/blocks/data-blocks/form/editFormBlockSettings.ts index 6b41165721..8ce416a3e6 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/editFormBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/editFormBlockSettings.ts @@ -8,6 +8,7 @@ */ import { useFieldSchema } from '@formily/react'; +import { useTranslation } from 'react-i18next'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; import { useCollection_deprecated } from '../../../../collection-manager'; @@ -21,6 +22,7 @@ import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/Schem import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; export const editFormBlockSettings = new SchemaSettings({ name: 'blockSettings:editForm', @@ -34,12 +36,27 @@ export const editFormBlockSettings = new SchemaSettings({ Component: SchemaSettingsBlockHeightItem, }, { - name: 'linkageRules', + name: 'fieldLinkageRules', Component: SchemaSettingsLinkageRules, useComponentProps() { const { name } = useCollection_deprecated(); + const { t } = useTranslation(); return { collectionName: name, + title: t('Field Linkage rules'), + }; + }, + }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection_deprecated(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, }; }, }, diff --git a/packages/core/client/src/modules/blocks/data-blocks/grid-card/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/data-blocks/grid-card/__e2e__/schemaInitializer.test.ts index 310774c18b..c8d8eab3b2 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/grid-card/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/grid-card/__e2e__/schemaInitializer.test.ts @@ -28,7 +28,7 @@ test.describe('where grid card block can be added', () => { await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible(); }); - test('popup', async ({ page, mockPage }) => { + test.skip('popup', async ({ page, mockPage }) => { await mockPage(oneEmptyTableWithUsers).goto(); // 1. 打开弹窗,通过 Associated records 创建一个列表区块 diff --git a/packages/core/client/src/modules/blocks/data-blocks/grid-card/gridCardBlockSettings.ts b/packages/core/client/src/modules/blocks/data-blocks/grid-card/gridCardBlockSettings.ts index 074ec0f60d..5310ce5187 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/grid-card/gridCardBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/grid-card/gridCardBlockSettings.ts @@ -24,6 +24,8 @@ import { useBlockTemplateContext } from '../../../../schema-templates/BlockTempl import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem'; import { SetTheCountOfColumnsDisplayedInARow } from './SetTheCountOfColumnsDisplayedInARow'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; +import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; export const gridCardBlockSettings = new SchemaSettings({ name: 'blockSettings:gridCard', @@ -32,6 +34,19 @@ export const gridCardBlockSettings = new SchemaSettings({ name: 'setTheBlockHeight', Component: SchemaSettingsBlockHeightItem, }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection_deprecated(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, + }; + }, + }, { name: 'SetTheCountOfColumnsDisplayedInARow', Component: SetTheCountOfColumnsDisplayedInARow, diff --git a/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts index 6c70969a56..c4f4605cef 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts @@ -27,3 +27,11 @@ export function useGridCardBlockDecoratorProps(props) { parseVariableLoading, }; } + +export function useGridCardBlockItemProps() { + return {}; +} + +export function useGridCardBlockProps() { + return {}; +} diff --git a/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts index 4944874fb5..833b859966 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts @@ -22,3 +22,7 @@ export function useListBlockDecoratorProps(props) { parentRecord, }; } + +export function useListBlockProps() { + return {}; +} diff --git a/packages/core/client/src/modules/blocks/data-blocks/list/listBlockSettings.ts b/packages/core/client/src/modules/blocks/data-blocks/list/listBlockSettings.ts index f664cf58e2..98e784fa77 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/list/listBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/list/listBlockSettings.ts @@ -22,6 +22,8 @@ import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettin import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; +import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; export const listBlockSettings = new SchemaSettings({ name: 'blockSettings:list', @@ -34,6 +36,19 @@ export const listBlockSettings = new SchemaSettings({ name: 'setTheBlockHeight', Component: SchemaSettingsBlockHeightItem, }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection_deprecated(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, + }; + }, + }, { name: 'SetTheDataScope', Component: SchemaSettingsDataScope, diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx index bc3695f6df..73258a75e5 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx @@ -61,14 +61,11 @@ export const TableBlockInitializer = ({ export const useCreateTableBlock = () => { const { insert } = useSchemaInitializer(); - const { getCollection } = useCollectionManager_deprecated(); const createTableBlock = ({ item }) => { - const collection = getCollection(item.name, item.dataSource); const schema = createTableBlockUISchema({ collectionName: item.name, dataSource: item.dataSource, - rowKey: collection.filterTargetKey || 'id', }); insert(schema); }; diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/actions/linkage.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/actions/linkage.test.ts index 7ae4fa7a95..0faf73f886 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/actions/linkage.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/actions/linkage.test.ts @@ -17,14 +17,14 @@ test('action linkage by row data', async ({ page, mockPage }) => { .getByLabel('action-Action.Link-Edit-update-roles-table-admin') .locator('.nb-action-title'); const adminEditActionStyle = await adminEditAction.evaluate((element) => { - const computedStyle = window.getComputedStyle(element); + const computedStyle = window.getComputedStyle(element.querySelector('.nb-action-title')); return { opacity: computedStyle.opacity, }; }); const rootEditAction = page.getByLabel('action-Action.Link-Edit-update-roles-table-root').locator('.nb-action-title'); const rootEditActionStyle = await rootEditAction.evaluate((element) => { - const computedStyle = window.getComputedStyle(element); + const computedStyle = window.getComputedStyle(element.querySelector('.nb-action-title')); return { opacity: computedStyle.opacity, // 添加其他你需要的样式属性 diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts index 7975f16665..69b79e9bc6 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts @@ -39,7 +39,7 @@ test.describe('where table block can be added', () => { await page.getByLabel('schema-initializer-Grid-popup').hover(); await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'childAssociationField' }).waitFor({ state: 'detached' }); - await page.getByRole('menuitem', { name: 'Associated records' }).last().hover(); + await page.getByRole('menuitem', { name: 'Associated records right' }).last().hover(); await page.getByRole('menuitem', { name: 'childAssociationField' }).click(); await page .getByTestId('drawer-Action.Container-childCollection-View record') @@ -50,8 +50,9 @@ test.describe('where table block can be added', () => { // 添加父表关系区块 await page.getByRole('menuitem', { name: 'Table right' }).waitFor({ state: 'detached' }); await page.getByLabel('schema-initializer-Grid-popup').hover(); + await page.getByRole('menuitem', { name: 'Associated records right' }).waitFor({ state: 'detached' }); await page.getByRole('menuitem', { name: 'Table right' }).hover(); - await page.getByRole('menuitem', { name: 'Associated records' }).last().hover(); + await page.getByRole('menuitem', { name: 'Associated records right' }).hover(); await page.getByRole('menuitem', { name: 'parentAssociationField' }).click(); await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover(); await page.getByRole('menuitem', { name: 'parentTargetText' }).click(); @@ -72,6 +73,7 @@ test.describe('where table block can be added', () => { // 通过 Other records 创建一个表格区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); + await page.getByRole('menuitem', { name: 'Other records right' }).waitFor({ state: 'detached' }); await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__tests__/createTableBLockSchema.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__tests__/createTableBLockSchema.test.ts index 07e5de14db..2c4ac7473e 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__tests__/createTableBLockSchema.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__tests__/createTableBLockSchema.test.ts @@ -17,7 +17,7 @@ vi.mock('@formily/shared', () => { describe('createTableBLockSchemaV2', () => { it('should create a default table block schema with minimum options', () => { - const options = { dataSource: 'abc', collectionName: 'users', association: 'users.roles', rowKey: 'rowKey' }; + const options = { dataSource: 'abc', collectionName: 'users', association: 'users.roles' }; const schema = createTableBlockUISchema(options); expect(schema).toMatchInlineSnapshot(` @@ -85,7 +85,6 @@ describe('createTableBLockSchemaV2', () => { "params": { "pageSize": 20, }, - "rowKey": "rowKey", "showIndex": true, }, "x-filter-targets": [], diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/createTableBlockUISchema.ts b/packages/core/client/src/modules/blocks/data-blocks/table/createTableBlockUISchema.ts index 2628161034..3fb14295a4 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/createTableBlockUISchema.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/createTableBlockUISchema.ts @@ -13,10 +13,9 @@ import { uid } from '@formily/shared'; export const createTableBlockUISchema = (options: { dataSource: string; collectionName?: string; - rowKey?: string; association?: string; }): ISchema => { - const { collectionName, dataSource, rowKey, association } = options; + const { collectionName, dataSource, association } = options; if (!dataSource) { throw new Error('dataSource is required'); @@ -35,7 +34,6 @@ export const createTableBlockUISchema = (options: { params: { pageSize: 20, }, - rowKey, showIndex: true, dragSort: false, }, diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts index bc91c97de5..2082be83fd 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts @@ -11,15 +11,19 @@ import { useFieldSchema } from '@formily/react'; import { useMemo } from 'react'; import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter'; import { useParentRecordCommon } from '../../../useParentRecordCommon'; +import { useDataSourceManager } from '../../../../../data-source'; export const useTableBlockDecoratorProps = (props) => { const { params, parseVariableLoading } = useTableBlockParams(props); const parentRecord = useParentRecordCommon(props.association); + const dm = useDataSourceManager(); + const collection = dm.getDataSource(props.dataSource)?.collectionManager.getCollection(props.collection); return { params, parentRecord, parseVariableLoading, + rowKey: collection?.filterTargetKey || 'id', }; }; diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/tableBlockSettings.tsx b/packages/core/client/src/modules/blocks/data-blocks/table/tableBlockSettings.tsx index 6a05614b93..e99f8a4a11 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/tableBlockSettings.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/table/tableBlockSettings.tsx @@ -26,6 +26,8 @@ import { setTheDataScopeSchemaSettingsItem } from '../../../../schema-settings/s import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem'; import { SchemaSettingsItemType } from '../../../../application'; +import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; const enabledIndexColumn: SchemaSettingsItemType = { name: 'enableIndexColumn', @@ -64,6 +66,19 @@ export const tableBlockSettings = new SchemaSettings({ name: 'setTheBlockHeight', Component: SchemaSettingsBlockHeightItem, }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection_deprecated(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, + }; + }, + }, { name: 'treeTable', type: 'switch', @@ -138,7 +153,6 @@ export const tableBlockSettings = new SchemaSettings({ const { resource } = field.decoratorProps; const collectionField = resource && getCollectionField(resource); const api = useAPIClient(); - return { title: t('Enable drag and drop sorting'), checked: field.decoratorProps.dragSort, diff --git a/packages/core/client/src/modules/blocks/filter-blocks/collapse/filterCollapseBlockSettings.ts b/packages/core/client/src/modules/blocks/filter-blocks/collapse/filterCollapseBlockSettings.ts index 503c1c6ada..6e6ff7ad44 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/collapse/filterCollapseBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/filter-blocks/collapse/filterCollapseBlockSettings.ts @@ -12,11 +12,14 @@ import { useTranslation } from 'react-i18next'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { useCollection_deprecated } from '../../../../collection-manager'; import { FilterBlockType } from '../../../../filter-provider'; +import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks'; import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettingsTemplate'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; +import { useCollection } from '../../../../data-source/collection/CollectionProvider'; export const filterCollapseBlockSettings = new SchemaSettings({ name: 'blockSettings:filterCollapse', @@ -29,6 +32,19 @@ export const filterCollapseBlockSettings = new SchemaSettings({ name: 'setTheBlockHeight', Component: SchemaSettingsBlockHeightItem, }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, + }; + }, + }, { name: 'ConvertReferenceToDuplicate', Component: SchemaSettingsTemplate, diff --git a/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormBlockSettings.ts b/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormBlockSettings.ts index 64573a1b0f..05ea675a63 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/filter-blocks/form/filterFormBlockSettings.ts @@ -19,6 +19,7 @@ import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/Schema import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; export const filterFormBlockSettings = new SchemaSettings({ name: 'blockSettings:filterForm', @@ -48,12 +49,27 @@ export const filterFormBlockSettings = new SchemaSettings({ }, }, { - name: 'linkageRules', + name: 'fieldLinkageRules', Component: SchemaSettingsLinkageRules, useComponentProps() { const { name } = useCollection_deprecated(); + const { t } = useTranslation(); return { collectionName: name, + title: t('Field Linkage rules'), + }; + }, + }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection(); + const { t } = useTranslation(); + return { + collectionName: name, + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, }; }, }, diff --git a/packages/core/client/src/modules/blocks/other-blocks/markdown/markdownBlockSettings.ts b/packages/core/client/src/modules/blocks/other-blocks/markdown/markdownBlockSettings.ts index aecb5cd040..f779581ea2 100644 --- a/packages/core/client/src/modules/blocks/other-blocks/markdown/markdownBlockSettings.ts +++ b/packages/core/client/src/modules/blocks/other-blocks/markdown/markdownBlockSettings.ts @@ -7,11 +7,14 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useField } from '@formily/react'; +import { useField, useFieldSchema } from '@formily/react'; import { useTranslation } from 'react-i18next'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsRenderEngine } from '../../../../schema-settings/SchemaSettingsRenderEngine'; +import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; +import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type'; + export const markdownBlockSettings = new SchemaSettings({ name: 'blockSettings:markdown', items: [ @@ -21,7 +24,6 @@ export const markdownBlockSettings = new SchemaSettings({ useComponentProps() { const field = useField(); const { t } = useTranslation(); - return { title: t('Edit markdown'), onClick: () => { @@ -34,6 +36,27 @@ export const markdownBlockSettings = new SchemaSettings({ name: 'setTheBlockHeight', Component: SchemaSettingsBlockHeightItem, }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const underForm = fieldSchema['x-decorator'] === 'FormItem'; + return { + title: underForm ? t('Linkage rules') : t('Block Linkage rules'), + category: LinkageRuleCategory.block, + returnScope: (options) => { + return options.filter((v) => { + if (!underForm) { + return !['$nForm', '$nRecord'].includes(v.value); + } + return true; + }); + }, + }; + }, + }, { name: 'setBlockTemplate', Component: SchemaSettingsRenderEngine, diff --git a/packages/core/client/src/modules/blocks/utils.ts b/packages/core/client/src/modules/blocks/utils.ts new file mode 100644 index 0000000000..f2fb6599d9 --- /dev/null +++ b/packages/core/client/src/modules/blocks/utils.ts @@ -0,0 +1,91 @@ +/** + * 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 { useEffect, useState } from 'react'; +import { VariableOption, VariablesContextType } from '../../variables/types'; +import { conditionAnalyses } from '../../schema-component/common/utils/uitls'; +import { useApp } from '../../application'; +import { useCollectionRecord } from '../../data-source'; + +enum ActionType { + Visible = 'visible', + Hidden = 'hidden', +} + +const linkageAction = async ( + { + operator, + condition, + variables, + localVariables, + conditionType, + displayResult, + }: { + operator; + condition; + variables: VariablesContextType; + localVariables: VariableOption[]; + conditionType: 'advanced'; + displayResult: any[]; + }, + jsonLogic: any, +) => { + switch (operator) { + case ActionType.Visible: + if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) { + displayResult.push(ActionType.Visible); + } + return displayResult; + case ActionType.Hidden: + if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) { + displayResult.push(ActionType.Hidden); + } + return displayResult; + default: + return null; + } +}; + +export const useReactiveLinkageEffect = ( + linkageRules: any[], + variables: VariablesContextType, + localVariables: VariableOption[], + triggerLinkageUpdate, +) => { + const app = useApp(); + const jsonLogic = app.jsonLogic; + const [displayResult, setDisplayResult] = useState(null); + const record = useCollectionRecord(); + useEffect(() => { + const runLinkages = async () => { + const result: string[] = []; + + for (const rule of linkageRules.filter((r) => !r.disabled)) { + for (const action of rule.actions || []) { + await linkageAction( + { + operator: action.operator, + condition: rule.condition, + variables, + localVariables, + conditionType: rule.conditionType, + displayResult: result, + }, + jsonLogic, + ); + } + } + setDisplayResult(result); + }; + + runLinkages(); + }, [linkageRules, triggerLinkageUpdate, record]); + + return displayResult; +}; diff --git a/packages/core/client/src/nocobase-buildin-plugin/index.tsx b/packages/core/client/src/nocobase-buildin-plugin/index.tsx index c53173b30a..0332a71153 100644 --- a/packages/core/client/src/nocobase-buildin-plugin/index.tsx +++ b/packages/core/client/src/nocobase-buildin-plugin/index.tsx @@ -14,12 +14,13 @@ import { getSubAppName } from '@nocobase/sdk'; import { tval } from '@nocobase/utils/client'; import { Button, Modal, Result, Spin } from 'antd'; import React, { FC } from 'react'; -import { Navigate, useNavigate } from 'react-router-dom'; +import { Navigate } from 'react-router-dom'; import { ACLPlugin } from '../acl'; import { Application } from '../application'; import { Plugin } from '../application/Plugin'; import { BlockSchemaComponentPlugin } from '../block-provider'; import { CollectionPlugin } from '../collection-manager'; +import { AppNotFound } from '../common/AppNotFound'; import { RemoteDocumentTitlePlugin } from '../document-title'; import { PinnedListPlugin } from '../plugin-manager'; import { PMPlugin } from '../pm'; @@ -260,22 +261,6 @@ const AppMaintainingDialog: FC<{ app: Application; error: Error }> = observer( { displayName: 'AppMaintainingDialog' }, ); -export const AppNotFound = () => { - const navigate = useNavigate(); - return ( - navigate('/', { replace: true })} type="primary"> - Back Home - - } - /> - ); -}; - export class NocoBaseBuildInPlugin extends Plugin { async afterAdd() { this.app.addComponents({ diff --git a/packages/core/client/src/pm/PluginManager.tsx b/packages/core/client/src/pm/PluginManager.tsx index 4a373320b9..ead4c11b04 100644 --- a/packages/core/client/src/pm/PluginManager.tsx +++ b/packages/core/client/src/pm/PluginManager.tsx @@ -10,7 +10,7 @@ export * from './PluginManagerLink'; import { PageHeader } from '@ant-design/pro-layout'; import { useDebounce } from 'ahooks'; -import { Button, Col, Divider, Input, List, Modal, Result, Row, Space, Spin, Table, Tabs, TableProps } from 'antd'; +import { Button, Col, Divider, Input, List, Modal, Row, Space, Spin, Table, TableProps, Tabs } from 'antd'; import _ from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -19,6 +19,7 @@ import { useNavigate, useParams } from 'react-router-dom'; import { css } from '@emotion/css'; import { useACLRoleContext } from '../acl/ACLProvider'; import { useAPIClient, useRequest } from '../api-client'; +import { AppNotFound } from '../common/AppNotFound'; import { useDocumentTitle } from '../document-title'; import { useToken } from '../style'; import { PluginCard } from './PluginCard'; @@ -147,7 +148,7 @@ function BulkEnableButton({ plugins = [] }) { width: 300, ellipsis: true, }, - ] as TableProps['columns'] + ] as TableProps['columns'] } dataSource={items} /> @@ -409,6 +410,6 @@ export const PluginManager = () => { ) : ( - + ); }; diff --git a/packages/core/client/src/pm/PluginSetting.tsx b/packages/core/client/src/pm/PluginSetting.tsx index 2c7b2f3d31..60260a1448 100644 --- a/packages/core/client/src/pm/PluginSetting.tsx +++ b/packages/core/client/src/pm/PluginSetting.tsx @@ -9,11 +9,12 @@ import { PageHeader } from '@ant-design/pro-layout'; import { css } from '@emotion/css'; -import { Layout, Menu, Result } from 'antd'; +import { Layout, Menu } from 'antd'; import _ from 'lodash'; import React, { createContext, useCallback, useEffect, useMemo } from 'react'; import { Navigate, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'; import { ADMIN_SETTINGS_PATH, PluginSettingsPageType, useApp } from '../application'; +import { AppNotFound } from '../common/AppNotFound'; import { useDocumentTitle } from '../document-title'; import { useCompile } from '../schema-component'; import { useStyles } from './style'; @@ -223,13 +224,7 @@ export const AdminSettingsLayout = () => { } /> )} -
- {currentSetting ? ( - - ) : ( - - )} -
+
{currentSetting ? : }
diff --git a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx index 59677ef1f6..9bdece7e57 100644 --- a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx +++ b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx @@ -19,7 +19,6 @@ import { useTranslation } from 'react-i18next'; import { Link, Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom'; import { ACLRolesCheckProvider, - AppNotFound, CurrentAppInfoProvider, DndContext, Icon, @@ -47,6 +46,7 @@ import { useLocationNoUpdate, } from '../../../application/CustomRouterContextProvider'; import { Plugin } from '../../../application/Plugin'; +import { AppNotFound } from '../../../common/AppNotFound'; import { withTooltipComponent } from '../../../hoc/withTooltipComponent'; import { menuItemInitializer } from '../../../modules/menu/menuItemInitializer'; import { useMenuTranslation } from '../../../schema-component/antd/menu/locale'; diff --git a/packages/core/client/src/route-switch/antd/admin-layout/menuItemSettings.tsx b/packages/core/client/src/route-switch/antd/admin-layout/menuItemSettings.tsx index d8b382d57a..176aea409a 100644 --- a/packages/core/client/src/route-switch/antd/admin-layout/menuItemSettings.tsx +++ b/packages/core/client/src/route-switch/antd/admin-layout/menuItemSettings.tsx @@ -47,6 +47,11 @@ const components = { TreeSelect }; const toItems = (routes: NocoBaseDesktopRoute[], { t, compile }) => { const items = []; for (const route of routes) { + // filter out the tabs + if (route.type === NocoBaseDesktopRouteType.tabs) { + continue; + } + const item = { label: isVariable(route.title) ? compile(route.title) : t(route.title), value: `${route.id}||${route.type}`, diff --git a/packages/core/client/src/schema-component/antd/action/Action.Link.tsx b/packages/core/client/src/schema-component/antd/action/Action.Link.tsx index 3dcb0b6a89..6656bca46d 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Link.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Link.tsx @@ -10,32 +10,16 @@ import { observer } from '@formily/react'; import classnames from 'classnames'; import React from 'react'; -import { Space, Tooltip } from 'antd'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import Action from './Action'; import { ComposedAction } from './types'; -import { Icon } from '../../../icon'; - -const WrapperComponent = React.forwardRef( - ({ component: Component = 'a', icon, onlyIcon, children, ...restProps }: any, ref) => { - return ( - - - {icon && typeof icon === 'string' ? : icon} - - {onlyIcon ? children[1] : children} - - ); - }, -); -WrapperComponent.displayName = 'WrapperComponentLink'; export const ActionLink: ComposedAction = withDynamicSchemaProps( observer((props: any) => { return ( diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 2db0123766..b41b765ed1 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -10,7 +10,7 @@ import { Field } from '@formily/core'; import { observer, Schema, useField, useFieldSchema, useForm } from '@formily/react'; import { isPortalInBody } from '@nocobase/utils/client'; -import { App, Button } from 'antd'; +import { App, Button, Tooltip } from 'antd'; import classnames from 'classnames'; import debounce from 'lodash/debounce'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -369,6 +369,7 @@ Action.Popover = function ActionPopover(props) { {props.children} ); + return ( { + return ( + + {onlyIcon ? ( + + {icon && typeof icon === 'string' ? : icon} + + ) : ( + {icon && typeof icon === 'string' ? : icon} + )} + {onlyIcon ? children[1] : children} + + ); + }, + ); return ( {!onlyIcon && actionTitle && ( diff --git a/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx b/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx index 1c9e0fd932..47b75ef3d6 100644 --- a/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx +++ b/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx @@ -118,8 +118,5 @@ describe('Action.Popover', () => { }); fireEvent.mouseLeave(btn); - await waitFor(() => { - expect(document.querySelector('.ant-popover')).not.toBeInTheDocument(); - }); }); }); diff --git a/packages/core/client/src/schema-component/antd/action/types.ts b/packages/core/client/src/schema-component/antd/action/types.ts index 2c319a2ebf..eb95e1ce45 100644 --- a/packages/core/client/src/schema-component/antd/action/types.ts +++ b/packages/core/client/src/schema-component/antd/action/types.ts @@ -81,6 +81,7 @@ export interface ActionProps extends ButtonProps { * @internal */ addChild?: boolean; + onlyIcon?: boolean; } export type ComposedAction = React.FC & { diff --git a/packages/core/client/src/schema-component/antd/association-filter/AssociationFilter.Item.tsx b/packages/core/client/src/schema-component/antd/association-filter/AssociationFilter.Item.tsx index c439277ae6..eb163b25bd 100644 --- a/packages/core/client/src/schema-component/antd/association-filter/AssociationFilter.Item.tsx +++ b/packages/core/client/src/schema-component/antd/association-filter/AssociationFilter.Item.tsx @@ -41,6 +41,7 @@ export const AssociationFilterItem = withDynamicSchemaProps( handleSearchInput: _handleSearchInput, params, run, + dataScopeFilter, valueKey: _valueKey, labelKey: _labelKey, defaultCollapse, @@ -94,7 +95,7 @@ export const AssociationFilterItem = withDynamicSchemaProps( if (searchVisible || filter) { run({ ...params?.[0], - filter: undefined, + filter: dataScopeFilter, }); } setSearchVisible(!searchVisible); diff --git a/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx b/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx index deb129be5a..6e6b2c4172 100644 --- a/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx +++ b/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx @@ -6,10 +6,9 @@ * 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 React, { useMemo } from 'react'; import { useFieldSchema } from '@formily/react'; import cls from 'classnames'; -import React, { useMemo } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { useSchemaToolbarRender } from '../../../application'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; @@ -18,6 +17,8 @@ import { useProps } from '../../hooks'; import { ErrorFallback } from '../error-fallback'; import { useStyles } from './BlockItem.style'; import { useGetAriaLabelOfBlockItem } from './hooks/useGetAriaLabelOfBlockItem'; +import { useCollection } from '../../../data-source'; +import { BlockLinkageRuleProvider } from '../../../modules/blocks/BlockLinkageRuleProvider'; export interface BlockItemProps { name?: string; @@ -35,8 +36,9 @@ export const BlockItem: React.FC = withDynamicSchemaProps( const { render } = useSchemaToolbarRender(fieldSchema); const { getAriaLabel } = useGetAriaLabelOfBlockItem(props.name); const label = useMemo(() => getAriaLabel(), [getAriaLabel]); - - return ( + const collection = useCollection(); + const markdownField = fieldSchema['x-decorator'] === 'FormItem' && fieldSchema['x-block-linkage-rules']; + const content = ( = withDynamicSchemaProps( ); + + return collection && !markdownField ? content : {content}; }, { displayName: 'BlockItem' }, ); diff --git a/packages/core/client/src/schema-component/antd/block-item/BlockItemCard.tsx b/packages/core/client/src/schema-component/antd/block-item/BlockItemCard.tsx index a6e0e804ca..8845d27153 100644 --- a/packages/core/client/src/schema-component/antd/block-item/BlockItemCard.tsx +++ b/packages/core/client/src/schema-component/antd/block-item/BlockItemCard.tsx @@ -13,6 +13,7 @@ import { useTranslation } from 'react-i18next'; import { useToken } from '../../../style'; import { MarkdownReadPretty } from '../markdown'; import { NAMESPACE_UI_SCHEMA } from '../../../i18n/constant'; +import { useCollection } from '../../../data-source'; export const BlockItemCardContext = createContext({}); @@ -25,6 +26,8 @@ export const BlockItemCard = React.forwardRef(( const [titleHeight, setTitleHeight] = useState(0); const titleRef = useRef(null); const { t } = useTranslation(); + const collection = useCollection(); + console.log(); useEffect(() => { const timer = setTimeout(() => { if (titleRef.current) { diff --git a/packages/core/client/src/schema-component/antd/filter/FilterAction.tsx b/packages/core/client/src/schema-component/antd/filter/FilterAction.tsx index 6ce1d67240..adfa8b6bf1 100644 --- a/packages/core/client/src/schema-component/antd/filter/FilterAction.tsx +++ b/packages/core/client/src/schema-component/antd/filter/FilterAction.tsx @@ -51,7 +51,7 @@ const InternalFilterAction = React.memo((props: FilterActionProps) => { const form = useMemo
(() => props.form || createForm(), []); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema - const { options, onSubmit, onReset, Container = StablePopover, icon } = useProps(props); + const { options, onSubmit, onReset, Container = StablePopover, icon, onlyIcon } = useProps(props); const onOpenChange = useCallback((visible: boolean): void => { setVisible(visible); @@ -77,7 +77,6 @@ const InternalFilterAction = React.memo((props: FilterActionProps) => { /> ); }, [field, fieldSchema, form, onReset, onSubmit, options]); - return ( { > {/* Adding a div here can prevent unnecessary re-rendering of Action */}
- +
diff --git a/packages/core/client/src/schema-component/antd/filter/FilterItem.tsx b/packages/core/client/src/schema-component/antd/filter/FilterItem.tsx index 1a6c6a2922..69bfb9e5ad 100644 --- a/packages/core/client/src/schema-component/antd/filter/FilterItem.tsx +++ b/packages/core/client/src/schema-component/antd/filter/FilterItem.tsx @@ -10,6 +10,7 @@ import { CloseCircleOutlined } from '@ant-design/icons'; import { css } from '@emotion/css'; import { observer } from '@formily/react'; +import { sortTree } from '@nocobase/utils/client'; import { Cascader, Select, Space } from 'antd'; import React, { useCallback, useContext, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -25,7 +26,7 @@ export const FilterItem = observer( const remove = useContext(RemoveConditionContext); const { schema, - fields, + fields: _fields, operators, dataIndex, operator, @@ -35,7 +36,8 @@ export const FilterItem = observer( setValue, collectionField, } = useValues(); - const [options, setOptions] = useState(compile(fields)); + const fields = useMemo(() => sortTree(_fields, 'children', 'children', false), [_fields]); + const [options, setOptions] = useState(() => compile(fields)); const style = useMemo(() => ({ marginBottom: 8 }), []); const fieldNames = useMemo( () => ({ @@ -84,6 +86,12 @@ export const FilterItem = observer( className={css` width: 160px; `} + popupClassName={css` + .ant-cascader-menu { + height: fit-content; + max-height: 50vh; + } + `} allowClear fieldNames={fieldNames} value={dataIndex} diff --git a/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx b/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx index 4193c7951d..5d2693c14b 100644 --- a/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx +++ b/packages/core/client/src/schema-component/antd/form-item/SchemaSettingOptions.tsx @@ -535,11 +535,19 @@ export const EditOperator = () => { } field.componentProps = componentProps; + fieldSchema['x-component-props'] = componentProps; + fieldSchema.default = null; + field.value = null; + field.initialValue = null; + dn.emit('patch', { schema: { ['x-uid']: fieldSchema['x-uid'], ['x-component-props']: componentProps, ['x-filter-operator']: v, + // Clear default value when switching operators. Some operators require the default value to be an array, + // while others don't. Without clearing it, the filtering API would throw an error + default: null, }, }); dn.refresh(); diff --git a/packages/core/client/src/schema-component/antd/form/__tests__/form.settings.test.tsx b/packages/core/client/src/schema-component/antd/form/__tests__/form.settings.test.tsx index a40cbb5eb3..737a3d173d 100644 --- a/packages/core/client/src/schema-component/antd/form/__tests__/form.settings.test.tsx +++ b/packages/core/client/src/schema-component/antd/form/__tests__/form.settings.test.tsx @@ -152,65 +152,65 @@ describe('form.settings', () => { title: 'Edit block title', type: 'modal', }, - { - title: 'Linkage rules', - type: 'modal', - modalChecker: { - modalTitle: 'Linkage rules', - contentText: 'Add linkage rule', - async customCheck() { - // await userEvent.click(screen.getByText('Add linkage rule')); - // await waitFor(() => { - // expect(screen.queryByText('Add condition')).toBeInTheDocument(); - // }) - // await userEvent.click(screen.getByText('Add condition')); - // await waitFor(() => { - // expect(screen.queryByText('Select field')).toBeInTheDocument(); - // }) - // await userEvent.click(screen.getByText('Select field')); - // await waitFor(() => { - // expect(screen.queryByTitle('Username')).toBeInTheDocument(); - // }) - // await userEvent.click(screen.getByTitle('Username')); - // const dialog = screen.queryByRole('dialog'); - // await userEvent.type(dialog.querySelectorAll('.ant-input')[1], '1'); - // const properties = screen.queryByTestId('select-linkage-property-field'); - // await userEvent.click(screen.getByText('Select field')); - // await waitFor(() => { - // expect(properties.querySelector(`[title=Nickname]`)).toBeInTheDocument(); - // }) - // await userEvent.click(properties.querySelector(`[title=Nickname]`)); - // await userEvent.click(screen.getByText('action')); - // await waitFor(() => { - // expect(screen.queryByText('Hidden')).toBeInTheDocument(); - // }) - // await userEvent.click(screen.getByText('Hidden')); - }, - // async afterSubmit() { - // await checkSchema({ - // "x-linkage-rules": [ - // { - // "condition": { - // "$and": [ - // { - // "username": {} - // } - // ] - // }, - // "actions": [ - // { - // "targetFields": [ - // "nickname" - // ], - // "operator": "none" - // } - // ] - // } - // ] - // }) - // }, - }, - }, + // { + // title: 'Linkage rules', + // type: 'modal', + // modalChecker: { + // modalTitle: 'Linkage rules', + // contentText: 'Add linkage rule', + // async customCheck() { + // // await userEvent.click(screen.getByText('Add linkage rule')); + // // await waitFor(() => { + // // expect(screen.queryByText('Add condition')).toBeInTheDocument(); + // // }) + // // await userEvent.click(screen.getByText('Add condition')); + // // await waitFor(() => { + // // expect(screen.queryByText('Select field')).toBeInTheDocument(); + // // }) + // // await userEvent.click(screen.getByText('Select field')); + // // await waitFor(() => { + // // expect(screen.queryByTitle('Username')).toBeInTheDocument(); + // // }) + // // await userEvent.click(screen.getByTitle('Username')); + // // const dialog = screen.queryByRole('dialog'); + // // await userEvent.type(dialog.querySelectorAll('.ant-input')[1], '1'); + // // const properties = screen.queryByTestId('select-linkage-property-field'); + // // await userEvent.click(screen.getByText('Select field')); + // // await waitFor(() => { + // // expect(properties.querySelector(`[title=Nickname]`)).toBeInTheDocument(); + // // }) + // // await userEvent.click(properties.querySelector(`[title=Nickname]`)); + // // await userEvent.click(screen.getByText('action')); + // // await waitFor(() => { + // // expect(screen.queryByText('Hidden')).toBeInTheDocument(); + // // }) + // // await userEvent.click(screen.getByText('Hidden')); + // }, + // // async afterSubmit() { + // // await checkSchema({ + // // "x-linkage-rules": [ + // // { + // // "condition": { + // // "$and": [ + // // { + // // "username": {} + // // } + // // ] + // // }, + // // "actions": [ + // // { + // // "targetFields": [ + // // "nickname" + // // ], + // // "operator": "none" + // // } + // // ] + // // } + // // ] + // // }) + // // }, + // }, + // }, { title: 'Form data templates', type: 'modal', diff --git a/packages/core/client/src/schema-component/antd/linkageFilter/DynamicComponent.tsx b/packages/core/client/src/schema-component/antd/linkageFilter/DynamicComponent.tsx index 995043a1c9..a42cfc03ca 100644 --- a/packages/core/client/src/schema-component/antd/linkageFilter/DynamicComponent.tsx +++ b/packages/core/client/src/schema-component/antd/linkageFilter/DynamicComponent.tsx @@ -46,7 +46,7 @@ interface Props { export const DynamicComponent = (props: Props) => { const { setScopes, nullable, constantAbel, changeOnSelect, readOnly = false } = props; - const { disabled } = useContext(FilterContext) || {}; + const { disabled, returnScope } = useContext(FilterContext) || {}; const record = useCollectionRecordData(); const variables = useVariables(); const localVariables = useLocalVariables(); @@ -68,6 +68,7 @@ export const DynamicComponent = (props: Props) => { localVariables, getAllCollectionsInheritChain, })} + returnScope={returnScope} /> ); }, []); diff --git a/packages/core/client/src/schema-component/antd/linkageFilter/LinkageFilter.tsx b/packages/core/client/src/schema-component/antd/linkageFilter/LinkageFilter.tsx index eb3003c584..0ab8849f29 100644 --- a/packages/core/client/src/schema-component/antd/linkageFilter/LinkageFilter.tsx +++ b/packages/core/client/src/schema-component/antd/linkageFilter/LinkageFilter.tsx @@ -26,7 +26,7 @@ export const LinkageFilter: any = withDynamicSchemaProps( const { useDataSource = useDef } = props; // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema - const { dynamicComponent, className, collectionName } = useProps(props); + const { dynamicComponent, className, collectionName, returnScope } = useProps(props); const [scopes, setScopes] = useState([]); const field = useField(); @@ -54,6 +54,7 @@ export const LinkageFilter: any = withDynamicSchemaProps( collectionName, scopes, setScopes, + returnScope, }} > diff --git a/packages/core/client/src/schema-component/antd/linkageFilter/context.ts b/packages/core/client/src/schema-component/antd/linkageFilter/context.ts index 44be0a9730..966866fd8b 100644 --- a/packages/core/client/src/schema-component/antd/linkageFilter/context.ts +++ b/packages/core/client/src/schema-component/antd/linkageFilter/context.ts @@ -20,6 +20,7 @@ export interface FilterContextProps { collectionName?: string; scopes?: any[]; setScopes?: any; + returnScope?: any; } export const RemoveConditionContext = createContext(null); diff --git a/packages/core/client/src/schema-component/antd/linkageFilter/useValues.ts b/packages/core/client/src/schema-component/antd/linkageFilter/useValues.ts index 2f365fbadb..57fbd07501 100644 --- a/packages/core/client/src/schema-component/antd/linkageFilter/useValues.ts +++ b/packages/core/client/src/schema-component/antd/linkageFilter/useValues.ts @@ -44,7 +44,7 @@ const findOption = (str, options) => { // 进入下一层 children 查找 if (Array.isArray(option.children) || option.isLeaf === false) { - currentOptions = option.children || option.field.children; + currentOptions = option.children || option?.field?.children || []; } else { return option; // 没有 children 直接返回 } diff --git a/packages/core/client/src/schema-component/antd/page/Page.tsx b/packages/core/client/src/schema-component/antd/page/Page.tsx index 374997c7f2..d96b6b8d30 100644 --- a/packages/core/client/src/schema-component/antd/page/Page.tsx +++ b/packages/core/client/src/schema-component/antd/page/Page.tsx @@ -30,10 +30,10 @@ import { useNavigateNoUpdate, useRouterBasename, } from '../../../application/CustomRouterContextProvider'; +import { AppNotFound } from '../../../common/AppNotFound'; import { useDocumentTitle } from '../../../document-title'; import { useGlobalTheme } from '../../../global-theme'; import { Icon } from '../../../icon'; -import { AppNotFound } from '../../../nocobase-buildin-plugin'; import { NocoBaseDesktopRouteType, NocoBaseRouteContext, @@ -234,7 +234,10 @@ const InternalPageContent = (props: PageContentProps) => { // Create a clean search string or empty string if only '?' remains const searchString = searchParams.toString() ? `?${searchParams.toString()}` : ''; - navigate(location.pathname.replace(activeKey, oldTab.schemaUid) + searchString); + const newPath = + location.pathname + (location.pathname.endsWith('/') ? `tabs/${oldTab.schemaUid}` : `/tabs/${oldTab.schemaUid}`); + navigate(newPath + searchString); + return null; } diff --git a/packages/core/client/src/schema-component/antd/page/PagePopups.tsx b/packages/core/client/src/schema-component/antd/page/PagePopups.tsx index 818cf75460..ff4396cace 100644 --- a/packages/core/client/src/schema-component/antd/page/PagePopups.tsx +++ b/packages/core/client/src/schema-component/antd/page/PagePopups.tsx @@ -10,12 +10,11 @@ import { ISchema, Schema } from '@formily/json-schema'; import { useFieldSchema } from '@formily/react'; import { uid } from '@formily/shared'; -import { Result } from 'antd'; import _ from 'lodash'; import { FC, default as React, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; import { Location, useLocation } from 'react-router-dom'; import { useAPIClient } from '../../../api-client'; +import { AppNotFound } from '../../../common/AppNotFound'; import { DataBlockProvider } from '../../../data-source/data-block/DataBlockProvider'; import { BlockRequestContextProvider } from '../../../data-source/data-block/DataBlockRequestProvider'; import { useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive'; @@ -475,10 +474,7 @@ function get404Schema() { version: '2.0', type: 'void', 'x-component': function Com() { - const { t } = useTranslation(); - return ( - - ); + return ; }, 'x-uid': uid(), 'x-async': false, diff --git a/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx b/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx index 90ffd6ff5f..1286a613ed 100644 --- a/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx +++ b/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx @@ -41,7 +41,7 @@ export const usePopupSettings = () => { const isOldMobileMode = pathname?.includes('/mobile/') || hash?.includes('/mobile/'); const isNewMobileMode = pathname?.includes('/m/'); const isPCMode = pathname?.includes('/admin/'); - const isMobileTemplateSettingsPage = pathname?.includes('/m/block-templates/'); + const isMobileTemplateSettingsPage = pathname?.includes('/m/block-templates/inherited'); return ( (isPCMode || isNewMobileMode) && diff --git a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx index 1339c87c13..88bbe3f5bd 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx @@ -194,7 +194,8 @@ const useTableColumns = ( return css` .nb-action-link { margin: -${token.paddingContentVerticalLG}px -${token.marginSM}px; - padding: ${token.paddingContentVerticalLG}px ${token.paddingSM + 4}px; + padding: ${token.paddingContentVerticalLG}px ${token.paddingContentVerticalLG}px ${token.paddingSM}px + ${token.paddingSM}px; } `; }, [token.paddingContentVerticalLG, token.marginSM, token.margin]); diff --git a/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx b/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx index 83e6839373..9f35006f79 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx @@ -27,6 +27,7 @@ import { SchemaSettingsRemove, SchemaSettingsSelectItem, SchemaSettingsSwitchItem, + SchemaSettingsLinkageRules, } from '../../../schema-settings'; import { SchemaSettingsBlockHeightItem } from '../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockTitleItem } from '../../../schema-settings/SchemaSettingsBlockTitleItem'; @@ -127,10 +128,12 @@ export const TableBlockDesigner = () => { [dn, field.decoratorProps, fieldSchema, service], ); const api = useAPIClient(); + return ( + {collection?.tree && collectionField?.collectionName === collectionField?.target && ( { title: 'Set block height', type: 'modal', }, + { + title: 'Block linkage rules', + type: 'modal', + }, { title: 'Enable drag and drop sorting', type: 'switch', diff --git a/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx b/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx index 627aeaf7e3..81b015b5bc 100644 --- a/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx +++ b/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx @@ -357,7 +357,7 @@ function FinallyButton({ }} style={{ ...props?.style, - display: !designable && field?.data?.hidden && 'none', + display: !designable && field?.data?.hidden ? 'none' : 'inline-block', opacity: designable && field?.data?.hidden && 0.1, ...buttonStyle, }} diff --git a/packages/core/client/src/schema-initializer/components/DeprecatedTemplateTitle.tsx b/packages/core/client/src/schema-initializer/components/DeprecatedTemplateTitle.tsx deleted file mode 100644 index b2f644e107..0000000000 --- a/packages/core/client/src/schema-initializer/components/DeprecatedTemplateTitle.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 React from 'react'; -import { useTranslation } from 'react-i18next'; -import { QuestionCircleOutlined } from '@ant-design/icons'; -import { Space, Tooltip } from 'antd'; - -export const DeprecatedTemplateTitle = () => { - const { t } = useTranslation(); - return ( - - {t('Deprecated')} - - - - - ); -}; - -export const DeprecatedTemplateTitleElement = ; diff --git a/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx index 0bde6547ea..1b89b06071 100644 --- a/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx @@ -39,7 +39,6 @@ export const RecordAssociationBlockInitializer = () => { } else { insert( createTableBlockUISchema({ - rowKey: collection.filterTargetKey, dataSource: collection.dataSource, association: association, }), @@ -62,7 +61,6 @@ export function useCreateAssociationTableBlock() { insert( createTableBlockUISchema({ - rowKey: collection.filterTargetKey, dataSource: collection.dataSource, association: `${field.collectionName}.${field.name}`, }), diff --git a/packages/core/client/src/schema-initializer/utils.ts b/packages/core/client/src/schema-initializer/utils.ts index 60e3b34758..c697f8fd97 100644 --- a/packages/core/client/src/schema-initializer/utils.ts +++ b/packages/core/client/src/schema-initializer/utils.ts @@ -36,7 +36,6 @@ import { isAssocField } from '../filter-provider/utils'; import { useActionContext, useCompile, useDesignable } from '../schema-component'; import { useSchemaTemplateManager } from '../schema-templates'; import { useBlockTemplateContext } from '../schema-templates/BlockTemplateProvider'; -import { DeprecatedTemplateTitleElement } from './components/DeprecatedTemplateTitle'; export const itemsMerge = (items1) => { return items1; @@ -838,66 +837,71 @@ export const useRecordCollectionDataSourceItems = ( .filter((template) => { return ['FormItem', 'ReadPrettyFormItem'].includes(componentName) || template.resourceName === resourceName; }); - const extralCollectionMenuItems = Array.from(initializerMenusGenerators.values()) + const inheritedTemplatesMenuItems = Array.from(initializerMenusGenerators.values()) .map((generator) => generator({ collection, componentName })) .filter(Boolean) .flat(); - if ((!templates.length && !extralCollectionMenuItems.length) || isInTemplateSettingPage()) { + if ((!templates.length && !inheritedTemplatesMenuItems.length) || isInTemplateSettingPage()) { return []; } const index = 0; - const deprecatedTemplatesMenuItems = []; + const allTemplatesMenuItems: SchemaInitializerItemType[] = [ + { + type: 'divider', + name: 'divider', + }, + ]; if (templates.length) { - deprecatedTemplatesMenuItems.push( + allTemplatesMenuItems.push( { - type: 'divider', + key: `${collectionName || componentName}_table_subMenu_${index}_copy`, + type: 'subMenu', + name: 'copy', + title: t('Duplicate template'), + children: templates.map((template) => { + const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'copy', + name: collection.name, + template, + item, + title: templateName || t('Untitled'), + }; + }), }, { - type: 'itemGroup', - title: DeprecatedTemplateTitleElement, - children: [ - { - key: `${collectionName || componentName}_table_subMenu_${index}_copy`, - type: 'subMenu', - name: 'copy', - title: t('Duplicate template'), - children: templates.map((template) => { - const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) - ? `${template?.name} ${t('(Fields only)')}` - : template?.name; - return { - type: 'item', - mode: 'copy', - name: collection.name, - template, - item, - title: templateName || t('Untitled'), - }; - }), - }, - { - key: `${collectionName || componentName}_table_subMenu_${index}_ref`, - type: 'subMenu', - name: 'ref', - title: t('Reference template'), - children: templates.map((template) => { - const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) - ? `${template?.name} ${t('(Fields only)')}` - : template?.name; - return { - type: 'item', - mode: 'reference', - name: collection.name, - template, - item, - title: templateName || t('Untitled'), - }; - }), - }, - ], + key: `${collectionName || componentName}_table_subMenu_${index}_ref`, + type: 'subMenu', + name: 'ref', + title: t('Reference template'), + children: templates.map((template) => { + const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'reference', + name: collection.name, + template, + item, + title: templateName || t('Untitled'), + }; + }), }, ); } + if (inheritedTemplatesMenuItems.length) { + allTemplatesMenuItems.push({ + type: 'subMenu', + name: 'inherited_templates', + key: `associationFiled_${componentName}_table_subMenu_${index}_inherited`, + title: t('Inherited template'), + children: inheritedTemplatesMenuItems, + }); + } return [ { key: `${collectionName || componentName}_table_blank`, @@ -906,8 +910,7 @@ export const useRecordCollectionDataSourceItems = ( title: t('Blank block'), item, }, - ...extralCollectionMenuItems, - ...deprecatedTemplatesMenuItems, + ...allTemplatesMenuItems, ]; }; @@ -1533,13 +1536,13 @@ const getChildren = ({ return componentName && template.componentName === componentName; }); - const extralCollectionMenuItems = Array.from(initializerMenusGenerators.values()) + const inheritedTemplatesMenuItems = Array.from(initializerMenusGenerators.values()) .map((generator) => { return generator({ item, index, componentName, association }); }) .filter(Boolean) .flat(); - if ((!templates.length && !extralCollectionMenuItems.length) || isInTemplateSettingPage()) { + if ((!templates.length && !inheritedTemplatesMenuItems.length) || isInTemplateSettingPage()) { return { type: 'item', name: item.name, @@ -1547,66 +1550,72 @@ const getChildren = ({ dataSource, }; } - const deprecatedTemplatesMenuItems = []; + const allTemplatesMenuItems: SchemaInitializerItemType[] = [ + { + type: 'divider', + name: 'divider', + }, + ]; if (templates.length) { - deprecatedTemplatesMenuItems.push( + allTemplatesMenuItems.push( { - type: 'divider', + key: `${componentName}_table_subMenu_${index}_copy`, + type: 'subMenu', + name: 'copy', + dataSource, + title: t('Duplicate template'), + children: templates.map((template) => { + const templateName = [ + componentNamePrefix + 'FormItem', + componentNamePrefix + 'ReadPrettyFormItem', + ].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'copy', + name: item.name, + template, + dataSource, + title: templateName || t('Untitled'), + }; + }), }, { - type: 'itemGroup', - title: DeprecatedTemplateTitleElement, - children: [ - { - key: `${componentName}_table_subMenu_${index}_copy`, - type: 'subMenu', - name: 'copy', + key: `${componentName}_table_subMenu_${index}_ref`, + type: 'subMenu', + name: 'ref', + dataSource, + title: t('Reference template'), + children: templates.map((template) => { + const templateName = [ + componentNamePrefix + 'FormItem', + componentNamePrefix + 'ReadPrettyFormItem', + ].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'reference', + name: item.name, + template, dataSource, - title: t('Duplicate template'), - children: templates.map((template) => { - const templateName = [ - componentNamePrefix + 'FormItem', - componentNamePrefix + 'ReadPrettyFormItem', - ].includes(template?.componentName) - ? `${template?.name} ${t('(Fields only)')}` - : template?.name; - return { - type: 'item', - mode: 'copy', - name: item.name, - template, - dataSource, - title: templateName || t('Untitled'), - }; - }), - }, - { - key: `${componentName}_table_subMenu_${index}_ref`, - type: 'subMenu', - name: 'ref', - dataSource, - title: t('Reference template'), - children: templates.map((template) => { - const templateName = [ - componentNamePrefix + 'FormItem', - componentNamePrefix + 'ReadPrettyFormItem', - ].includes(template?.componentName) - ? `${template?.name} ${t('(Fields only)')}` - : template?.name; - return { - type: 'item', - mode: 'reference', - name: item.name, - template, - dataSource, - title: templateName || t('Untitled'), - }; - }), - }, - ], + title: templateName || t('Untitled'), + }; + }), }, ); } + if (inheritedTemplatesMenuItems.length) { + allTemplatesMenuItems.push({ + type: 'subMenu', + name: 'inherited_templates', + key: `associationFiled_${componentName}_table_subMenu_${index}_inherited`, + dataSource, + title: t('Inherited template'), + children: inheritedTemplatesMenuItems, + }); + } return { key: `${componentName}_table_subMenu_${index}`, type: 'subMenu', @@ -1620,8 +1629,7 @@ const getChildren = ({ dataSource, title: t('Blank block'), }, - ...extralCollectionMenuItems, - ...deprecatedTemplatesMenuItems, + ...allTemplatesMenuItems, ], }; }); @@ -1804,11 +1812,11 @@ function useAssociationFields({ return template.componentName === componentName; }); const keyPrefix = `associationFiled_table_subMenu`; - const extralCollectionMenuItems = Array.from(initializerMenusGenerators.values()) + const inheritedTemplatesMenuItems = Array.from(initializerMenusGenerators.values()) .map((generator) => generator({ collection, index, field, componentName, keyPrefix, name })) .filter(Boolean) .flat(); - if ((!templates.length && !extralCollectionMenuItems.length) || isInTemplateSettingPage()) { + if ((!templates.length && !inheritedTemplatesMenuItems.length) || isInTemplateSettingPage()) { return { type: 'item', name: `${field.collectionName}.${field.name}`, @@ -1818,70 +1826,77 @@ function useAssociationFields({ associationField: field, }; } - const deprecatedTemplatesMenuItems = []; + const allTemplatesMenuItems: SchemaInitializerItemType[] = [ + { + type: 'divider', + name: 'divider', + }, + ]; if (templates.length) { - deprecatedTemplatesMenuItems.push( + allTemplatesMenuItems.push( { - type: 'divider', + key: `associationFiled_${componentName}_table_subMenu_${index}_copy`, + type: 'subMenu', + name: 'copy', + dataSource, + title: t('Duplicate template'), + children: templates.map((template) => { + const templateName = [ + componentNamePrefix + 'FormItem', + componentNamePrefix + 'ReadPrettyFormItem', + ].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'copy', + name: `${field.collectionName}.${field.name}`, + collectionName: field.target, + template, + dataSource, + title: templateName || t('Untitled'), + associationField: field, + }; + }), }, { - type: 'itemGroup', - title: DeprecatedTemplateTitleElement, - children: [ - { - key: `associationFiled_${componentName}_table_subMenu_${index}_copy`, - type: 'subMenu', - name: 'copy', + key: `associationFiled_${componentName}_table_subMenu_${index}_ref`, + type: 'subMenu', + name: 'ref', + dataSource, + title: t('Reference template'), + children: templates.map((template) => { + const templateName = [ + componentNamePrefix + 'FormItem', + componentNamePrefix + 'ReadPrettyFormItem', + ].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'reference', + name: `${field.collectionName}.${field.name}`, + collectionName: field.target, + template, dataSource, - title: t('Duplicate template'), - children: templates.map((template) => { - const templateName = [ - componentNamePrefix + 'FormItem', - componentNamePrefix + 'ReadPrettyFormItem', - ].includes(template?.componentName) - ? `${template?.name} ${t('(Fields only)')}` - : template?.name; - return { - type: 'item', - mode: 'copy', - name: `${field.collectionName}.${field.name}`, - collectionName: field.target, - template, - dataSource, - title: templateName || t('Untitled'), - associationField: field, - }; - }), - }, - { - key: `associationFiled_${componentName}_table_subMenu_${index}_ref`, - type: 'subMenu', - name: 'ref', - dataSource, - title: t('Reference template'), - children: templates.map((template) => { - const templateName = [ - componentNamePrefix + 'FormItem', - componentNamePrefix + 'ReadPrettyFormItem', - ].includes(template?.componentName) - ? `${template?.name} ${t('(Fields only)')}` - : template?.name; - return { - type: 'item', - mode: 'reference', - name: `${field.collectionName}.${field.name}`, - collectionName: field.target, - template, - dataSource, - title: templateName || t('Untitled'), - associationField: field, - }; - }), - }, - ], + title: templateName || t('Untitled'), + associationField: field, + }; + }), }, ); } + if (inheritedTemplatesMenuItems.length) { + allTemplatesMenuItems.push({ + type: 'subMenu', + name: 'inherited_templates', + key: `associationFiled_${componentName}_table_subMenu_${index}_inherited`, + dataSource, + title: t('Inherited template'), + children: inheritedTemplatesMenuItems, + }); + } + return { key: `associationFiled_${componentName}_table_subMenu_${index}`, type: 'subMenu', @@ -1897,8 +1912,7 @@ function useAssociationFields({ title: t('Blank block'), associationField: field, }, - ...extralCollectionMenuItems, - ...deprecatedTemplatesMenuItems, + ...allTemplatesMenuItems, ], }; }); @@ -1917,7 +1931,7 @@ function useAssociationFields({ ]); } -const isInTemplateSettingPage = () => window.location.pathname.includes('/block-templates/'); +const isInTemplateSettingPage = () => window.location.pathname.includes('/block-templates/inherited'); const initializerMenusGenerators = new Map< string, diff --git a/packages/core/client/src/schema-settings/GeneralSchemaDesigner.tsx b/packages/core/client/src/schema-settings/GeneralSchemaDesigner.tsx index 365e827186..b7f066a300 100644 --- a/packages/core/client/src/schema-settings/GeneralSchemaDesigner.tsx +++ b/packages/core/client/src/schema-settings/GeneralSchemaDesigner.tsx @@ -263,7 +263,7 @@ const InternalSchemaToolbar: FC = React.memo((props) => { if (typeof title === 'string') return [compile(title)]; if (Array.isArray(title)) { if (title.length === 1 && fieldSchema['x-template-title']) { - templateTitleLabel.current = t('Template'); + templateTitleLabel.current = t('Inherited template'); return compile([title[0], fieldSchema['x-template-title']]); } return compile(title); diff --git a/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleActionGroup.tsx b/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleActionGroup.tsx index 8efada27cf..c0ff6debe9 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleActionGroup.tsx +++ b/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleActionGroup.tsx @@ -15,8 +15,10 @@ import { useTranslation } from 'react-i18next'; import { withDynamicSchemaProps } from '../../hoc/withDynamicSchemaProps'; import { useProps } from '../../schema-component/hooks/useProps'; import { FormButtonLinkageRuleAction, FormFieldLinkageRuleAction } from './LinkageRuleAction'; -import { FieldStyleLinkageRuleAction } from './FieldStyleLinkageRuleAction'; +import { FieldStyleLinkageRuleAction } from './components/FieldStyleLinkageRuleAction'; +import { BlockLinkageRuleAction } from './components/BlockLinkageRuleAction'; import { RemoveActionContext } from './context'; + export const LinkageRuleActions = observer( (props: any): any => { const { linkageOptions, category, elementType } = props; @@ -28,6 +30,7 @@ export const LinkageRuleActions = observer( button: FormButtonLinkageRuleAction, field: FormFieldLinkageRuleAction, style: FieldStyleLinkageRuleAction, + block: BlockLinkageRuleAction, }; return field?.value?.map((item, index) => { return ( @@ -41,7 +44,7 @@ export const LinkageRuleActions = observer( ); export interface LinkageRuleActionGroupProps { - type: 'button' | 'field' | 'style'; + type: 'button' | 'field' | 'style' | 'block'; linkageOptions: any; collectionName: string; } diff --git a/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts b/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts index 2a3cbbeb52..eaa2afcfbd 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts @@ -122,7 +122,7 @@ function getFieldValuesInCondition({ linkageRules, formValues }) { }); } -function getVariableValuesInCondition({ +export function getVariableValuesInCondition({ linkageRules, localVariables, }: { @@ -166,7 +166,7 @@ function getVariableValuesInCondition({ }); } -function getVariableValuesInExpression({ action, localVariables }) { +export function getVariableValuesInExpression({ action, localVariables }) { const actionValue = action.value; const mode = actionValue?.mode; const value = actionValue?.value || actionValue?.result; diff --git a/packages/core/client/src/schema-settings/LinkageRules/components/BlockLinkageRuleAction.tsx b/packages/core/client/src/schema-settings/LinkageRules/components/BlockLinkageRuleAction.tsx new file mode 100644 index 0000000000..6d7f39352d --- /dev/null +++ b/packages/core/client/src/schema-settings/LinkageRules/components/BlockLinkageRuleAction.tsx @@ -0,0 +1,86 @@ +/** + * 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 { TreeSelect } from '@formily/antd-v5'; +import { observer } from '@formily/react'; +import { uid } from '@formily/shared'; +import { Select, Space } from 'antd'; +import React, { useCallback, useContext, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useCompile } from '../../..'; +import { DynamicComponent } from '../DynamicComponent'; +import { RemoveActionContext } from '../context'; +import { ActionType } from '../type'; +import { useValues } from '../useValues'; + +export const BlockLinkageRuleAction = observer( + (props: any) => { + const { value, options } = props; + const { t } = useTranslation(); + const compile = useCompile(); + const [editFlag, setEditFlag] = useState(false); + const remove = useContext(RemoveActionContext); + const { schema, operator, setOperator, setValue } = useValues(options); + const operators = useMemo( + () => + compile([ + { label: t('Visible'), value: ActionType.Visible, schema: {} }, + { label: t('Hidden'), value: ActionType.Hidden, schema: {} }, + ]), + [compile, t], + ); + + const onChange = useCallback( + (value) => { + const flag = [ActionType.Value].includes(value); + setEditFlag(flag); + setOperator(value); + }, + [setOperator], + ); + + const onChangeValue = useCallback( + (value) => { + setValue(value); + }, + [setValue], + ); + + const closeStyle = useMemo(() => ({ color: '#bfbfbf' }), []); + + return ( +
+ +