diff --git a/.github/workflows/build-internal-image.yml b/.github/workflows/build-internal-image.yml new file mode 100644 index 0000000000..0601be438d --- /dev/null +++ b/.github/workflows/build-internal-image.yml @@ -0,0 +1,119 @@ +name: Build Image (Internal) + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + workflow_dispatch: + inputs: + ref_name: + description: 'Branch or tag name to release' + +jobs: + get-plugins: + uses: nocobase/nocobase/.github/workflows/get-plugins.yml@main + secrets: inherit + push-docker: + runs-on: ubuntu-latest + needs: get-plugins + services: + verdaccio: + image: verdaccio/verdaccio:5 + ports: + - 4873:4873 + steps: + - name: Set Node.js 20 + uses: actions/setup-node@v3 + with: + node-version: 20 + - name: Get info + id: get-info + shell: bash + run: | + if [[ "${{ inputs.ref_name || github.ref_name }}" =~ "beta" ]]; then + echo "defaultTag=$(echo 'beta')" >> $GITHUB_OUTPUT + echo "proRepos=$(echo '${{ needs.get-plugins.outputs.beta-plugins }}')" >> $GITHUB_OUTPUT + elif [[ "${{ inputs.ref_name || github.ref_name }}" =~ "alpha" ]]; then + echo "defaultTag=$(echo 'alpha')" >> $GITHUB_OUTPUT + echo "proRepos=$(echo '${{ needs.get-plugins.outputs.alpha-plugins }}')" >> $GITHUB_OUTPUT + else + # rc + echo "defaultTag=$(echo 'latest')" >> $GITHUB_OUTPUT + echo "proRepos=$(echo '${{ needs.get-plugins.outputs.rc-plugins }}')" >> $GITHUB_OUTPUT + fi + - 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: nocobase,pro-plugins,${{ join(fromJSON(steps.get-info.outputs.proRepos), ',') }},${{ join(fromJSON(needs.get-plugins.outputs.custom-plugins), ',') }} + skip-token-revoke: true + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref_name || github.ref_name }} + - name: yarn install + run: | + yarn install + - name: Checkout pro-plugins + uses: actions/checkout@v3 + with: + repository: nocobase/pro-plugins + path: packages/pro-plugins + ref: ${{ inputs.ref_name || github.ref_name }} + token: ${{ steps.app-token.outputs.token }} + - name: Clone pro repos + shell: bash + run: | + for repo in ${{ join(fromJSON(steps.get-info.outputs.proRepos), ' ') }} ${{ join(fromJSON(needs.get-plugins.outputs.custom-plugins), ' ') }} + do + git clone -b ${{ inputs.ref_name || github.ref_name }} https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo + done + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + driver-opts: network=host + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + nocobase/nocobase + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + - name: Login to Aliyun Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ secrets.ALI_DOCKER_REGISTRY }} + username: ${{ secrets.ALI_DOCKER_USERNAME }} + password: ${{ secrets.ALI_DOCKER_PASSWORD }} + - name: Set variables + run: | + target_directory="./packages/pro-plugins/@nocobase" + subdirectories=$(find "$target_directory" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | tr '\n' ' ') + trimmed_variable=$(echo "$subdirectories" | xargs) + packageNames="@nocobase/${trimmed_variable// / @nocobase/}" + pluginNames="${trimmed_variable//plugin-/}" + BEFORE_PACK_NOCOBASE="yarn add @nocobase/plugin-notifications @nocobase/plugin-disable-pm-add $packageNames -W --production" + APPEND_PRESET_LOCAL_PLUGINS="notifications,disable-pm-add,${pluginNames// /,}" + echo "var1=$BEFORE_PACK_NOCOBASE" >> $GITHUB_OUTPUT + echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + file: Dockerfile + build-args: | + VERDACCIO_URL=http://localhost:4873/ + COMMIT_HASH=${GITHUB_SHA} + PLUGINS_DIRS=pro-plugins + BEFORE_PACK_NOCOBASE=${{ steps.vars.outputs.var1 }} + APPEND_PRESET_LOCAL_PLUGINS=${{ steps.vars.outputs.var2 }} + push: true + tags: ${{ secrets.ALI_DOCKER_PUBLIC_REGISTRY }}/nocobase/nocobase:${{ steps.get-info.outputs.defaultTag }},${{ secrets.ALI_DOCKER_PUBLIC_REGISTRY }}/${{ steps.meta.outputs.tags }} 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/.github/workflows/nocobase-test-backend.yml b/.github/workflows/nocobase-test-backend.yml index f7956dddd7..c925ff3fb9 100644 --- a/.github/workflows/nocobase-test-backend.yml +++ b/.github/workflows/nocobase-test-backend.yml @@ -5,38 +5,40 @@ concurrency: cancel-in-progress: true on: - push: - branches: - - main - - next - - develop - paths: - - 'package.json' - - '**/yarn.lock' - - 'packages/core/acl/**' - - 'packages/core/auth/**' - - 'packages/core/actions/**' - - 'packages/core/database/**' - - 'packages/core/resourcer/**' - - 'packages/core/data-source-manager/**' - - 'packages/core/server/**' - - 'packages/core/utils/**' - - 'packages/plugins/**/src/server/**' - - '.github/workflows/nocobase-test-backend.yml' - pull_request: - paths: - - 'package.json' - - '**/yarn.lock' - - 'packages/core/acl/**' - - 'packages/core/auth/**' - - 'packages/core/actions/**' - - 'packages/core/database/**' - - 'packages/core/resourcer/**' - - 'packages/core/data-source-manager/**' - - 'packages/core/server/**' - - 'packages/core/utils/**' - - 'packages/plugins/**/src/server/**' - - '.github/workflows/nocobase-test-backend.yml' + workflow_dispatch: + + # push: + # branches: + # - main + # - next + # - develop + # paths: + # - 'package.json' + # - '**/yarn.lock' + # - 'packages/core/acl/**' + # - 'packages/core/auth/**' + # - 'packages/core/actions/**' + # - 'packages/core/database/**' + # - 'packages/core/resourcer/**' + # - 'packages/core/data-source-manager/**' + # - 'packages/core/server/**' + # - 'packages/core/utils/**' + # - 'packages/plugins/**/src/server/**' + # - '.github/workflows/nocobase-test-backend.yml' + # pull_request: + # paths: + # - 'package.json' + # - '**/yarn.lock' + # - 'packages/core/acl/**' + # - 'packages/core/auth/**' + # - 'packages/core/actions/**' + # - 'packages/core/database/**' + # - 'packages/core/resourcer/**' + # - 'packages/core/data-source-manager/**' + # - 'packages/core/server/**' + # - 'packages/core/utils/**' + # - 'packages/plugins/**/src/server/**' + # - '.github/workflows/nocobase-test-backend.yml' jobs: sqlite-test: @@ -59,7 +61,9 @@ jobs: node-version: ${{ matrix.node_version }} cache: 'yarn' - name: Install project dependencies - run: yarn install + run: | + yarn install + yarn add sqlite3 --no-save -W - name: Test with Sqlite run: yarn test --server --single-thread=false env: diff --git a/.github/workflows/nocobase-test-windows.yml b/.github/workflows/nocobase-test-windows.yml index c1d378a6ba..cd20394a83 100644 --- a/.github/workflows/nocobase-test-windows.yml +++ b/.github/workflows/nocobase-test-windows.yml @@ -63,7 +63,9 @@ jobs: ${{ runner.os }}-yarn- - name: Install project dependencies - run: yarn --prefer-offline + run: | + yarn --prefer-offline + yarn add sqlite3 --no-save -W - name: Test with Sqlite run: yarn test --server --single-thread=false diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f2ee38c2f..d7ab9406f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,173 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.6.25](https://github.com/nocobase/nocobase/compare/v1.6.24...v1.6.25) - 2025-04-29 + +### 🎉 New Features + +- **[undefined]** add publish ci for license kit ([#6786](https://github.com/nocobase/nocobase/pull/6786)) by @jiannx + +- **[Data visualization: ECharts]** Add "Y-Axis inverse" setting for bar charts by @2013xile + +### 🚀 Improvements + +- **[utils]** Increase the height of the filter button field list and sort/categorize the fields ([#6779](https://github.com/nocobase/nocobase/pull/6779)) by @zhangzhonghe + +- **[client]** optimize subtable add button style and align paginator on the same row ([#6790](https://github.com/nocobase/nocobase/pull/6790)) by @katherinehhh + +- **[File manager]** Add OSS timeout option default to 10min ([#6795](https://github.com/nocobase/nocobase/pull/6795)) by @mytharcher + +- **[Password policy]** Change default password expiration to never expire by @2013xile + +- **[WeCom]** Prioritize corporate email over personal email when updating the user's email by @2013xile + +### 🐛 Bug Fixes + +- **[client]** + - In the collapse block, clicking the clear button in the relationship field search box should not delete the data range ([#6782](https://github.com/nocobase/nocobase/pull/6782)) by @zhangzhonghe + + - association field not submitting data when displaying field from related collection ([#6798](https://github.com/nocobase/nocobase/pull/6798)) by @katherinehhh + + - Prohibit moving menus before or after page tabs ([#6777](https://github.com/nocobase/nocobase/pull/6777)) by @zhangzhonghe + + - Table block displays duplicate data when filtering ([#6792](https://github.com/nocobase/nocobase/pull/6792)) by @zhangzhonghe + + - In the filter form, switching the field operator and then refreshing the page causes an error ([#6781](https://github.com/nocobase/nocobase/pull/6781)) by @zhangzhonghe + +- **[database]** + - Avoid error thrown when data type is not string ([#6797](https://github.com/nocobase/nocobase/pull/6797)) by @mytharcher + + - add unavailableActions to sql collection and view collection ([#6765](https://github.com/nocobase/nocobase/pull/6765)) by @katherinehhh + +- **[create-nocobase-app]** Temporarily fix mariadb issue by downgrading to 2.5.6 ([#6762](https://github.com/nocobase/nocobase/pull/6762)) by @chenos + +- **[Authentication]** Disallow changing authenticator name ([#6808](https://github.com/nocobase/nocobase/pull/6808)) by @2013xile + +- **[Template print]** Fix: Correct permission validation logic to prevent unauthorized actions. by @sheldon66 + +- **[File storage: S3(Pro)]** access url expiration invalid by @jiannx + +- **[Block: Tree]** After connecting through a foreign key, clicking to trigger filtering results in empty filter conditions by @zhangzhonghe + +## [v1.6.24](https://github.com/nocobase/nocobase/compare/v1.6.23...v1.6.24) - 2025-04-24 + +### 🚀 Improvements + +- **[client]** Adjust upload message ([#6757](https://github.com/nocobase/nocobase/pull/6757)) by @mytharcher + +### 🐛 Bug Fixes + +- **[client]** + - only export action in view collection is support when writableView is false ([#6763](https://github.com/nocobase/nocobase/pull/6763)) by @katherinehhh + + - unexpected association data creation when displaying association field under sub-form/sub-table in create form ([#6727](https://github.com/nocobase/nocobase/pull/6727)) by @katherinehhh + + - Incorrect data retrieved for many-to-many array fields from related tables in forms ([#6744](https://github.com/nocobase/nocobase/pull/6744)) by @2013xile + +## [v1.6.23](https://github.com/nocobase/nocobase/compare/v1.6.22...v1.6.23) - 2025-04-23 + +### 🚀 Improvements + +- **[cli]** Optimize internal logic of the `nocobase upgrade` command ([#6754](https://github.com/nocobase/nocobase/pull/6754)) by @chenos + +- **[Template print]** Replaced datasource action control with client role-based access control. by @sheldon66 + +### 🐛 Bug Fixes + +- **[cli]** Auto-update package.json on upgrade ([#6747](https://github.com/nocobase/nocobase/pull/6747)) by @chenos + +- **[client]** + - missing filter for already associated data when adding association data ([#6750](https://github.com/nocobase/nocobase/pull/6750)) by @katherinehhh + + - tree table 'Add Child' button linkage rule missing 'current record' ([#6752](https://github.com/nocobase/nocobase/pull/6752)) by @katherinehhh + +- **[Action: Import records]** Fix the import and export exceptions that occur when setting field permissions. ([#6677](https://github.com/nocobase/nocobase/pull/6677)) by @aaaaaajie + +- **[Block: Gantt]** gantt chart block overlapping months in calendar header for month view ([#6753](https://github.com/nocobase/nocobase/pull/6753)) by @katherinehhh + +- **[Action: Export records Pro]** + - pro export button losing filter parameters after sorting table column by @katherinehhh + + - Fix the import and export exceptions that occur when setting field permissions. by @aaaaaajie + +- **[File storage: S3(Pro)]** Fix response data of uploaded file by @mytharcher + +- **[Workflow: Approval]** Fix preload association fields for records by @mytharcher + +## [v1.6.22](https://github.com/nocobase/nocobase/compare/v1.6.21...v1.6.22) - 2025-04-22 + +### 🚀 Improvements + +- **[create-nocobase-app]** Upgrade dependencies and remove SQLite support ([#6708](https://github.com/nocobase/nocobase/pull/6708)) by @chenos + +- **[File manager]** Expose utils API ([#6705](https://github.com/nocobase/nocobase/pull/6705)) by @mytharcher + +- **[Workflow]** Add date types to variable types set ([#6717](https://github.com/nocobase/nocobase/pull/6717)) by @mytharcher + +### 🐛 Bug Fixes + +- **[client]** + - The problem of mobile top navigation bar icons being difficult to delete ([#6734](https://github.com/nocobase/nocobase/pull/6734)) by @zhangzhonghe + + - After connecting through a foreign key, clicking to trigger filtering results in empty filter conditions ([#6634](https://github.com/nocobase/nocobase/pull/6634)) by @zhangzhonghe + + - picker switching issue in date field of filter button ([#6695](https://github.com/nocobase/nocobase/pull/6695)) by @katherinehhh + + - The issue of the collapse button in the left menu being obscured by the workflow pop-up window ([#6733](https://github.com/nocobase/nocobase/pull/6733)) by @zhangzhonghe + + - missing action option constraints when reopening linkage rules ([#6723](https://github.com/nocobase/nocobase/pull/6723)) by @katherinehhh + + - export button shown without export permission ([#6689](https://github.com/nocobase/nocobase/pull/6689)) by @katherinehhh + + - Required fields hidden by linkage rules should not affect form submission ([#6709](https://github.com/nocobase/nocobase/pull/6709)) by @zhangzhonghe + +- **[server]** appVersion incorrectly generated by create-migration ([#6740](https://github.com/nocobase/nocobase/pull/6740)) by @chenos + +- **[build]** Fix error thrown in tar command ([#6722](https://github.com/nocobase/nocobase/pull/6722)) by @mytharcher + +- **[Workflow]** Fix error thrown when execute schedule event in subflow ([#6721](https://github.com/nocobase/nocobase/pull/6721)) by @mytharcher + +- **[Workflow: Custom action event]** Support to execute in multiple records mode by @mytharcher + +- **[File storage: S3(Pro)]** Add multer make logic for server-side upload by @mytharcher + +## [v1.6.21](https://github.com/nocobase/nocobase/compare/v1.6.20...v1.6.21) - 2025-04-17 + +### 🚀 Improvements + +- **[client]** Add delay API for scenarios which open without delay ([#6681](https://github.com/nocobase/nocobase/pull/6681)) by @mytharcher + +- **[create-nocobase-app]** Upgrade some dependencies to latest versions ([#6673](https://github.com/nocobase/nocobase/pull/6673)) by @chenos + +### 🐛 Bug Fixes + +- **[client]** + - Fix error thrown when mouse hover on referenced template block in approval node configuration ([#6691](https://github.com/nocobase/nocobase/pull/6691)) by @mytharcher + + - custom association field not displaying field component settings ([#6692](https://github.com/nocobase/nocobase/pull/6692)) by @katherinehhh + + - Fix locale for upload component ([#6682](https://github.com/nocobase/nocobase/pull/6682)) by @mytharcher + + - lazy load missing ui component will cause render error ([#6683](https://github.com/nocobase/nocobase/pull/6683)) by @gchust + + - Add native Password component to HoC Input ([#6679](https://github.com/nocobase/nocobase/pull/6679)) by @mytharcher + + - inherited fields shown in current collection field assignment list ([#6666](https://github.com/nocobase/nocobase/pull/6666)) by @katherinehhh + +- **[database]** Fixed ci build error ([#6687](https://github.com/nocobase/nocobase/pull/6687)) by @aaaaaajie + +- **[build]** build output is incorrect when plugin depends on some AMD libraries ([#6665](https://github.com/nocobase/nocobase/pull/6665)) by @gchust + +- **[Action: Import records]** fixed an error importing xlsx time field ([#6672](https://github.com/nocobase/nocobase/pull/6672)) by @aaaaaajie + +- **[Workflow: Manual node]** Fix manual task status constant ([#6676](https://github.com/nocobase/nocobase/pull/6676)) by @mytharcher + +- **[Block: iframe]** vertical scrollbar appears when iframe block is set to full height ([#6675](https://github.com/nocobase/nocobase/pull/6675)) by @katherinehhh + +- **[Workflow: Custom action event]** Fix test cases by @mytharcher + +- **[Backup manager]** timeout error occurs when trying to restore an unecrypted backup with a password by @gchust + ## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14 ### 🎉 New Features diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 7bb43bdfb4..7c775842e9 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -5,6 +5,173 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。 +## [v1.6.25](https://github.com/nocobase/nocobase/compare/v1.6.24...v1.6.25) - 2025-04-29 + +### 🎉 新特性 + +- **[undefined]** 添加 license kit 发包ci ([#6786](https://github.com/nocobase/nocobase/pull/6786)) by @jiannx + +- **[数据可视化:EChrats]** 条形图支持“y轴反向”设置 by @2013xile + +### 🚀 优化 + +- **[utils]** 增加筛选按钮字段列表的高度,和对字段进行排序分类 ([#6779](https://github.com/nocobase/nocobase/pull/6779)) by @zhangzhonghe + +- **[client]** 优化子表格添加按钮样式,并将分页器与按钮排列在同一行 ([#6790](https://github.com/nocobase/nocobase/pull/6790)) by @katherinehhh + +- **[文件管理器]** 增加 OSS 存储引擎的超时时间配置项,且默认为 10 分钟 ([#6795](https://github.com/nocobase/nocobase/pull/6795)) by @mytharcher + +- **[密码策略]** 默认密码过期时间修改为不过期 by @2013xile + +- **[企业微信]** 更新用户邮箱时优先使用企业邮箱而不是个人邮箱 by @2013xile + +### 🐛 修复 + +- **[client]** + - 折叠面板区块中,当点击关系字段搜索框的清空按钮后,不应该删除数据范围 ([#6782](https://github.com/nocobase/nocobase/pull/6782)) by @zhangzhonghe + + - 关系字段,在显示关系表下的字段数据时不提交数据 ([#6798](https://github.com/nocobase/nocobase/pull/6798)) by @katherinehhh + + - 禁止将菜单移动到页面 tab 的前面和后面 ([#6777](https://github.com/nocobase/nocobase/pull/6777)) by @zhangzhonghe + + - 表格区块在筛选时重复显示数据 ([#6792](https://github.com/nocobase/nocobase/pull/6792)) by @zhangzhonghe + + - 筛选表单中,切换字段操作符后,刷新页面会报错 ([#6781](https://github.com/nocobase/nocobase/pull/6781)) by @zhangzhonghe + +- **[database]** + - 避免文本类型输入数据不是字符串时的报错 ([#6797](https://github.com/nocobase/nocobase/pull/6797)) by @mytharcher + + - 补充sql collection和view collection 的unavailableActions ([#6765](https://github.com/nocobase/nocobase/pull/6765)) by @katherinehhh + +- **[create-nocobase-app]** 回退 mariadb 版本至 2.5.6,解决兼容性问题 ([#6762](https://github.com/nocobase/nocobase/pull/6762)) by @chenos + +- **[用户认证]** 不允许修改认证器标识 ([#6808](https://github.com/nocobase/nocobase/pull/6808)) by @2013xile + +- **[模板打印]** 修复:修正权限校验逻辑,防止未授权操作。 by @sheldon66 + +- **[文件存储:S3 (Pro)]** 访问地址有效期无效 by @jiannx + +- **[区块:树]** 通过外键连接后,点击触发筛选,筛选条件为空 by @zhangzhonghe + +## [v1.6.24](https://github.com/nocobase/nocobase/compare/v1.6.23...v1.6.24) - 2025-04-24 + +### 🚀 优化 + +- **[client]** 调整上传文件的提示信息 ([#6757](https://github.com/nocobase/nocobase/pull/6757)) by @mytharcher + +### 🐛 修复 + +- **[client]** + - 视图表,无编辑权限时允许显示导出按钮 ([#6763](https://github.com/nocobase/nocobase/pull/6763)) by @katherinehhh + + - 新增表单中显示关系字段子表格/子表单时关系数据也被新增 ([#6727](https://github.com/nocobase/nocobase/pull/6727)) by @katherinehhh + + - 在表单中获取关联表中的多对多数组字段数据不正确 ([#6744](https://github.com/nocobase/nocobase/pull/6744)) by @2013xile + +## [v1.6.23](https://github.com/nocobase/nocobase/compare/v1.6.22...v1.6.23) - 2025-04-23 + +### 🚀 优化 + +- **[cli]** 优化 `nocobase upgrade` 命令的内部实现逻辑 ([#6754](https://github.com/nocobase/nocobase/pull/6754)) by @chenos + +- **[模板打印]** 用客户端角色访问控制替换了数据源操作权限控制。 by @sheldon66 + +### 🐛 修复 + +- **[cli]** 升级时自动更新项目的 package.json ([#6747](https://github.com/nocobase/nocobase/pull/6747)) by @chenos + +- **[client]** + - 添加关联表格时未过滤已关联的数据 ([#6750](https://github.com/nocobase/nocobase/pull/6750)) by @katherinehhh + + - 树表格中添加子记录按钮的联动规则缺失「当前记录」变量 ([#6752](https://github.com/nocobase/nocobase/pull/6752)) by @katherinehhh + +- **[操作:导入记录]** 修复设置字段权限时出现的导入导出异常。 ([#6677](https://github.com/nocobase/nocobase/pull/6677)) by @aaaaaajie + +- **[区块:甘特图]** 甘特图区块设置月份视图时,日历头部月份重叠 ([#6753](https://github.com/nocobase/nocobase/pull/6753)) by @katherinehhh + +- **[操作:导出记录 Pro]** + - pro导出按钮在点击表格排序后丢失过滤参数 by @katherinehhh + + - 修复设置字段权限时出现的导入导出异常。 by @aaaaaajie + +- **[文件存储:S3 (Pro)]** 修复已上传文件的响应数据 by @mytharcher + +- **[工作流:审批]** 修复预加载审批记录数据的关系字段 by @mytharcher + +## [v1.6.22](https://github.com/nocobase/nocobase/compare/v1.6.21...v1.6.22) - 2025-04-22 + +### 🚀 优化 + +- **[create-nocobase-app]** 更新依赖,移除 SQLite 支持 ([#6708](https://github.com/nocobase/nocobase/pull/6708)) by @chenos + +- **[文件管理器]** 暴露公共包 API ([#6705](https://github.com/nocobase/nocobase/pull/6705)) by @mytharcher + +- **[工作流]** 为变量的类型集合增加日期相关类型 ([#6717](https://github.com/nocobase/nocobase/pull/6717)) by @mytharcher + +### 🐛 修复 + +- **[client]** + - 移动端顶部的导航栏图标很难被删除的问题 ([#6734](https://github.com/nocobase/nocobase/pull/6734)) by @zhangzhonghe + + - 通过外键连接后,点击触发筛选,筛选条件为空 ([#6634](https://github.com/nocobase/nocobase/pull/6634)) by @zhangzhonghe + + - 筛选按钮中日期字段,切换picker 异常 ([#6695](https://github.com/nocobase/nocobase/pull/6695)) by @katherinehhh + + - 左侧菜单的收起按钮会被绑定工作流弹窗遮挡的问题 ([#6733](https://github.com/nocobase/nocobase/pull/6733)) by @zhangzhonghe + + - 重新打开联动规则时缺少操作选项约束 ([#6723](https://github.com/nocobase/nocobase/pull/6723)) by @katherinehhh + + - 未设置导出权限时仍显示导出按钮 ([#6689](https://github.com/nocobase/nocobase/pull/6689)) by @katherinehhh + + - 被联动规则隐藏的必填字段,不应该影响表单的提交 ([#6709](https://github.com/nocobase/nocobase/pull/6709)) by @zhangzhonghe + +- **[server]** create-migration 命令生成的 appVersion 不准确 ([#6740](https://github.com/nocobase/nocobase/pull/6740)) by @chenos + +- **[build]** 修复 tar 命令报错的问题 ([#6722](https://github.com/nocobase/nocobase/pull/6722)) by @mytharcher + +- **[工作流]** 修复子流程执行定时任务报错的问题 ([#6721](https://github.com/nocobase/nocobase/pull/6721)) by @mytharcher + +- **[工作流:自定义操作事件]** 支持多行记录模式的手动执行 by @mytharcher + +- **[文件存储:S3 (Pro)]** 增加 multer 逻辑用于服务端上传 by @mytharcher + +## [v1.6.21](https://github.com/nocobase/nocobase/compare/v1.6.20...v1.6.21) - 2025-04-17 + +### 🚀 优化 + +- **[client]** 为弹窗组件增加 delay API ([#6681](https://github.com/nocobase/nocobase/pull/6681)) by @mytharcher + +- **[create-nocobase-app]** 升级部分依赖的版本 ([#6673](https://github.com/nocobase/nocobase/pull/6673)) by @chenos + +### 🐛 修复 + +- **[client]** + - 修复审批节点配置中引用模板区块的添加按钮报错问题 ([#6691](https://github.com/nocobase/nocobase/pull/6691)) by @mytharcher + + - 自定义的关系字段没有显示关系字段组件 ([#6692](https://github.com/nocobase/nocobase/pull/6692)) by @katherinehhh + + - 修复上传组件语言问题 ([#6682](https://github.com/nocobase/nocobase/pull/6682)) by @mytharcher + + - 懒加载组件不存在时界面报错 ([#6683](https://github.com/nocobase/nocobase/pull/6683)) by @gchust + + - 补全原生的 Password 组件到封装过的输入组件 ([#6679](https://github.com/nocobase/nocobase/pull/6679)) by @mytharcher + + - 字段赋值本表字段列表中显示了继承表字段,应只显示本表字段 ([#6666](https://github.com/nocobase/nocobase/pull/6666)) by @katherinehhh + +- **[database]** 修复 CI 编译错误 ([#6687](https://github.com/nocobase/nocobase/pull/6687)) by @aaaaaajie + +- **[build]** 插件依赖 AMD 库时构建产物不正确 ([#6665](https://github.com/nocobase/nocobase/pull/6665)) by @gchust + +- **[操作:导入记录]** 修复导入包含时间字段的 xlsx 错误 ([#6672](https://github.com/nocobase/nocobase/pull/6672)) by @aaaaaajie + +- **[工作流:人工处理节点]** 修复人工节点任务状态常量 ([#6676](https://github.com/nocobase/nocobase/pull/6676)) by @mytharcher + +- **[区块:iframe]** iframe 区块设置全高时页面出现滚动条 ([#6675](https://github.com/nocobase/nocobase/pull/6675)) by @katherinehhh + +- **[工作流:自定义操作事件]** 修复测试用例 by @mytharcher + +- **[备份管理器]** 还原时若备份未设置密码,但用户输入了密码,还原会出现超时报错 by @gchust + ## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14 ### 🎉 新特性 diff --git a/docker/nocobase/Dockerfile b/docker/nocobase/Dockerfile index 3fb6bafc89..9c0a1bbfff 100644 --- a/docker/nocobase/Dockerfile +++ b/docker/nocobase/Dockerfile @@ -8,7 +8,11 @@ RUN cd /app \ && yarn config set network-timeout 600000 -g \ && npx -y create-nocobase-app@${CNA_VERSION} my-nocobase-app --skip-dev-dependencies -a -e APP_ENV=production \ && cd /app/my-nocobase-app \ - && yarn install --production + && yarn install --production \ + && rm -rf yarn.lock \ + && find node_modules -type f -name "yarn.lock" -delete \ + && find node_modules -type f -name "bower.json" -delete \ + && find node_modules -type f -name "composer.json" -delete RUN cd /app \ && rm -rf nocobase.tar.gz \ diff --git a/lerna.json b/lerna.json index caa46b0f70..9486c51939 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "npmClient": "yarn", "useWorkspaces": true, "npmClientArgs": ["--ignore-engines"], diff --git a/package.json b/package.json index b530c0c628..2520b58a91 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "ghooks": "^2.0.4", "lint-staged": "^13.2.3", "patch-package": "^8.0.0", + "pm2": "^6.0.5", "pretty-format": "^24.0.0", "pretty-quick": "^3.1.0", "react": "^18.0.0", diff --git a/packages/core/acl/package.json b/packages/core/acl/package.json index 20ca10f80e..3a0f35febf 100644 --- a/packages/core/acl/package.json +++ b/packages/core/acl/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/acl", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/resourcer": "1.7.0-beta.18", - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/resourcer": "1.7.0-beta.26", + "@nocobase/utils": "1.7.0-beta.26", "minimatch": "^5.1.1" }, "repository": { diff --git a/packages/core/actions/package.json b/packages/core/actions/package.json index 81ae052a0f..93e5f9b492 100644 --- a/packages/core/actions/package.json +++ b/packages/core/actions/package.json @@ -1,14 +1,14 @@ { "name": "@nocobase/actions", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/cache": "1.7.0-beta.18", - "@nocobase/database": "1.7.0-beta.18", - "@nocobase/resourcer": "1.7.0-beta.18" + "@nocobase/cache": "1.7.0-beta.26", + "@nocobase/database": "1.7.0-beta.26", + "@nocobase/resourcer": "1.7.0-beta.26" }, "repository": { "type": "git", diff --git a/packages/core/app/package.json b/packages/core/app/package.json index 0e849790ec..876d992f89 100644 --- a/packages/core/app/package.json +++ b/packages/core/app/package.json @@ -1,17 +1,17 @@ { "name": "@nocobase/app", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/database": "1.7.0-beta.18", - "@nocobase/preset-nocobase": "1.7.0-beta.18", - "@nocobase/server": "1.7.0-beta.18" + "@nocobase/database": "1.7.0-beta.26", + "@nocobase/preset-nocobase": "1.7.0-beta.26", + "@nocobase/server": "1.7.0-beta.26" }, "devDependencies": { - "@nocobase/client": "1.7.0-beta.18" + "@nocobase/client": "1.7.0-beta.26" }, "repository": { "type": "git", diff --git a/packages/core/auth/package.json b/packages/core/auth/package.json index e78fbe7da0..942e6a9b9a 100644 --- a/packages/core/auth/package.json +++ b/packages/core/auth/package.json @@ -1,18 +1,18 @@ { "name": "@nocobase/auth", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/actions": "1.7.0-beta.18", - "@nocobase/cache": "1.7.0-beta.18", - "@nocobase/database": "1.7.0-beta.18", - "@nocobase/resourcer": "1.7.0-beta.18", - "@nocobase/utils": "1.7.0-beta.18", - "@types/jsonwebtoken": "^8.5.8", - "jsonwebtoken": "^8.5.1" + "@nocobase/actions": "1.7.0-beta.26", + "@nocobase/cache": "1.7.0-beta.26", + "@nocobase/database": "1.7.0-beta.26", + "@nocobase/resourcer": "1.7.0-beta.26", + "@nocobase/utils": "1.7.0-beta.26", + "@types/jsonwebtoken": "^9.0.9", + "jsonwebtoken": "^9.0.2" }, "repository": { "type": "git", diff --git a/packages/core/build/package.json b/packages/core/build/package.json index 06aa9f32f5..5215de0e1f 100644 --- a/packages/core/build/package.json +++ b/packages/core/build/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/build", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "Library build tool based on rollup.", "main": "lib/index.js", "types": "./lib/index.d.ts", @@ -39,7 +39,7 @@ "postcss-preset-env": "^9.1.2", "react-imported-component": "^6.5.4", "style-loader": "^3.3.3", - "tar": "^6.2.0", + "tar": "^7.4.3", "tsup": "8.2.4", "typescript": "5.1.3", "update-notifier": "3.0.0", diff --git a/packages/core/build/src/tarPlugin.ts b/packages/core/build/src/tarPlugin.ts index 1d58224924..35b107e5aa 100644 --- a/packages/core/build/src/tarPlugin.ts +++ b/packages/core/build/src/tarPlugin.ts @@ -8,7 +8,7 @@ */ import path from 'path'; -import tar from 'tar'; +import { create } from 'tar'; import fg from 'fast-glob'; import fs from 'fs-extra'; @@ -38,5 +38,5 @@ export function tarPlugin(cwd: string, log: PkgLog) { fs.mkdirpSync(path.dirname(tarball)); fs.rmSync(tarball, { force: true }); - return tar.c({ gzip: true, file: tarball, cwd }, tarFiles); + return create({ gzip: true, file: tarball, cwd }, tarFiles); } diff --git a/packages/core/cache/package.json b/packages/core/cache/package.json index 13372d0c7f..809cbbd222 100644 --- a/packages/core/cache/package.json +++ b/packages/core/cache/package.json @@ -1,12 +1,12 @@ { "name": "@nocobase/cache", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/lock-manager": "1.7.0-beta.18", + "@nocobase/lock-manager": "1.7.0-beta.26", "bloom-filters": "^3.0.1", "cache-manager": "^5.2.4", "cache-manager-redis-yet": "^4.1.2" diff --git a/packages/core/cli/package.json b/packages/core/cli/package.json index ce7485f885..85d0d638f3 100644 --- a/packages/core/cli/package.json +++ b/packages/core/cli/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cli", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./src/index.js", @@ -8,11 +8,12 @@ "nocobase": "./bin/index.js" }, "dependencies": { - "@nocobase/app": "1.7.0-beta.18", + "@nocobase/app": "1.7.0-beta.26", "@types/fs-extra": "^11.0.1", "@umijs/utils": "3.5.20", "chalk": "^4.1.1", "commander": "^9.2.0", + "deepmerge": "^4.3.1", "dotenv": "^16.0.0", "execa": "^5.1.1", "fast-glob": "^3.3.1", @@ -20,11 +21,12 @@ "p-all": "3.0.0", "pm2": "^6.0.5", "portfinder": "^1.0.28", + "tar": "^7.4.3", "tree-kill": "^1.2.2", "tsx": "^4.19.0" }, "devDependencies": { - "@nocobase/devtools": "1.7.0-beta.18" + "@nocobase/devtools": "1.7.0-beta.26" }, "repository": { "type": "git", diff --git a/packages/core/cli/src/commands/dev.js b/packages/core/cli/src/commands/dev.js index c12c5df2d5..9caa52b0ff 100644 --- a/packages/core/cli/src/commands/dev.js +++ b/packages/core/cli/src/commands/dev.js @@ -8,7 +8,7 @@ */ const _ = require('lodash'); const { Command } = require('commander'); -const { generatePlugins, run, postCheck, nodeCheck, promptForTs, isPortReachable } = require('../util'); +const { generatePlugins, run, postCheck, nodeCheck, promptForTs, isPortReachable, checkDBDialect } = require('../util'); const { getPortPromise } = require('portfinder'); const chokidar = require('chokidar'); const { uid } = require('@formily/shared'); @@ -36,6 +36,7 @@ module.exports = (cli) => { .option('-i, --inspect [port]') .allowUnknownOption() .action(async (opts) => { + checkDBDialect(); let subprocess; const runDevClient = () => { console.log('starting client', 1 * clientPort); diff --git a/packages/core/cli/src/commands/e2e.js b/packages/core/cli/src/commands/e2e.js index 8daa5e6389..cf3bf6172a 100644 --- a/packages/core/cli/src/commands/e2e.js +++ b/packages/core/cli/src/commands/e2e.js @@ -8,7 +8,7 @@ */ const { Command } = require('commander'); -const { run, isPortReachable } = require('../util'); +const { run, isPortReachable, checkDBDialect } = require('../util'); const { execSync } = require('node:child_process'); const axios = require('axios'); const { pTest } = require('./p-test'); @@ -165,6 +165,7 @@ const filterArgv = () => { */ module.exports = (cli) => { const e2e = cli.command('e2e').hook('preAction', () => { + checkDBDialect(); if (process.env.APP_BASE_URL) { process.env.APP_BASE_URL = process.env.APP_BASE_URL.replace('localhost', '127.0.0.1'); console.log('APP_BASE_URL:', process.env.APP_BASE_URL); diff --git a/packages/core/cli/src/commands/global.js b/packages/core/cli/src/commands/global.js index 524ad3cd09..48bab4fb23 100644 --- a/packages/core/cli/src/commands/global.js +++ b/packages/core/cli/src/commands/global.js @@ -8,7 +8,7 @@ */ const { Command } = require('commander'); -const { run, isDev, isProd, promptForTs, downloadPro } = require('../util'); +const { run, isDev, isProd, promptForTs, downloadPro, checkDBDialect } = require('../util'); /** * @@ -21,6 +21,7 @@ module.exports = (cli) => { .option('-h, --help') .option('--ts-node-dev') .action(async (options) => { + checkDBDialect(); const cmd = process.argv.slice(2)?.[0]; if (cmd === 'install') { await downloadPro(); diff --git a/packages/core/cli/src/commands/index.js b/packages/core/cli/src/commands/index.js index 3647d07250..97a685f668 100644 --- a/packages/core/cli/src/commands/index.js +++ b/packages/core/cli/src/commands/index.js @@ -30,6 +30,7 @@ module.exports = (cli) => { require('./test')(cli); require('./test-coverage')(cli); require('./umi')(cli); + require('./update-deps')(cli); require('./upgrade')(cli); require('./postinstall')(cli); require('./pkg')(cli); diff --git a/packages/core/cli/src/commands/start.js b/packages/core/cli/src/commands/start.js index 968e8654b2..e0f0bc905c 100644 --- a/packages/core/cli/src/commands/start.js +++ b/packages/core/cli/src/commands/start.js @@ -8,7 +8,7 @@ */ const _ = require('lodash'); const { Command } = require('commander'); -const { run, postCheck, downloadPro, promptForTs } = require('../util'); +const { run, postCheck, downloadPro, promptForTs, checkDBDialect } = require('../util'); const { existsSync, rmSync } = require('fs'); const { resolve, isAbsolute } = require('path'); const chalk = require('chalk'); @@ -48,8 +48,10 @@ module.exports = (cli) => { .option('-i, --instances [instances]') .option('--db-sync') .option('--quickstart') + .option('--launch-mode [launchMode]') .allowUnknownOption() .action(async (opts) => { + checkDBDialect(); if (opts.quickstart) { await downloadPro(); } @@ -118,17 +120,27 @@ module.exports = (cli) => { ]); process.exit(); } else { - run( - 'pm2-runtime', - [ - 'start', - ...instancesArgs, - `${APP_PACKAGE_ROOT}/lib/index.js`, - NODE_ARGS ? `--node-args="${NODE_ARGS}"` : undefined, - '--', - ...process.argv.slice(2), - ].filter(Boolean), - ); + const launchMode = opts.launchMode || process.env.APP_LAUNCH_MODE || 'pm2'; + if (launchMode === 'pm2') { + run( + 'pm2-runtime', + [ + 'start', + ...instancesArgs, + `${APP_PACKAGE_ROOT}/lib/index.js`, + NODE_ARGS ? `--node-args="${NODE_ARGS}"` : undefined, + '--', + ...process.argv.slice(2), + ].filter(Boolean), + ); + } else { + run( + 'node', + [`${APP_PACKAGE_ROOT}/lib/index.js`, ...(NODE_ARGS || '').split(' '), ...process.argv.slice(2)].filter( + Boolean, + ), + ); + } } }); }; diff --git a/packages/core/cli/src/commands/test-coverage.js b/packages/core/cli/src/commands/test-coverage.js index ee6d50695d..8a26113203 100644 --- a/packages/core/cli/src/commands/test-coverage.js +++ b/packages/core/cli/src/commands/test-coverage.js @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -const { run } = require('../util'); +const { run, checkDBDialect } = require('../util'); const fg = require('fast-glob'); const coreClientPackages = ['packages/core/client', 'packages/core/sdk']; @@ -30,6 +30,7 @@ const getPackagesDir = (isClient) => { module.exports = (cli) => { cli.command('test-coverage:server').action(async () => { + checkDBDialect(); const packageRoots = getPackagesDir(false); for (const dir of packageRoots) { try { @@ -41,6 +42,7 @@ module.exports = (cli) => { }); cli.command('test-coverage:client').action(async () => { + checkDBDialect(); const packageRoots = getPackagesDir(true); for (const dir of packageRoots) { try { diff --git a/packages/core/cli/src/commands/test.js b/packages/core/cli/src/commands/test.js index d97a5f1026..0ec62ac4e8 100644 --- a/packages/core/cli/src/commands/test.js +++ b/packages/core/cli/src/commands/test.js @@ -8,7 +8,7 @@ */ const { Command } = require('commander'); -const { run } = require('../util'); +const { run, checkDBDialect } = require('../util'); const path = require('path'); /** @@ -29,6 +29,7 @@ function addTestCommand(name, cli) { .arguments('[paths...]') .allowUnknownOption() .action(async (paths, opts) => { + checkDBDialect(); if (name === 'test:server') { process.env.TEST_ENV = 'server-side'; } else if (name === 'test:client') { diff --git a/packages/core/cli/src/commands/update-deps.js b/packages/core/cli/src/commands/update-deps.js new file mode 100644 index 0000000000..ed54d06721 --- /dev/null +++ b/packages/core/cli/src/commands/update-deps.js @@ -0,0 +1,71 @@ +/** + * 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. + */ + +const chalk = require('chalk'); +const { Command } = require('commander'); +const { resolve } = require('path'); +const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode, checkDBDialect } = require('../util'); +const { existsSync, rmSync } = require('fs'); +const { readJSON, writeJSON } = require('fs-extra'); +const deepmerge = require('deepmerge'); + +const rmAppDir = () => { + // If ts-node is not installed, do not do the following + const appDevDir = resolve(process.cwd(), './storage/.app-dev'); + if (existsSync(appDevDir)) { + rmSync(appDevDir, { recursive: true, force: true }); + } +}; + +/** + * + * @param {Command} cli + */ +module.exports = (cli) => { + cli + .command('update-deps') + .option('--force') + .allowUnknownOption() + .action(async (options) => { + if (hasCorePackages() || !hasTsNode()) { + await downloadPro(); + return; + } + const pkg = require('../../package.json'); + let distTag = 'latest'; + if (pkg.version.includes('alpha')) { + distTag = 'alpha'; + } else if (pkg.version.includes('beta')) { + distTag = 'beta'; + } + const { stdout } = await run('npm', ['info', `@nocobase/cli@${distTag}`, 'version'], { + stdio: 'pipe', + }); + if (!options.force && pkg.version === stdout) { + await downloadPro(); + rmAppDir(); + return; + } + const descPath = resolve(process.cwd(), 'package.json'); + const descJson = await readJSON(descPath, 'utf8'); + const sourcePath = resolve(__dirname, '../../templates/create-app-package.json'); + const sourceJson = await readJSON(sourcePath, 'utf8'); + if (descJson['dependencies']?.['@nocobase/cli']) { + descJson['dependencies']['@nocobase/cli'] = stdout; + } + if (descJson['devDependencies']?.['@nocobase/devtools']) { + descJson['devDependencies']['@nocobase/devtools'] = stdout; + } + const json = deepmerge(descJson, sourceJson); + await writeJSON(descPath, json, { spaces: 2, encoding: 'utf8' }); + await run('yarn', ['install']); + await downloadPro(); + rmAppDir(); + }); +}; diff --git a/packages/core/cli/src/commands/upgrade.js b/packages/core/cli/src/commands/upgrade.js index 4774564e41..c2bb0736d3 100644 --- a/packages/core/cli/src/commands/upgrade.js +++ b/packages/core/cli/src/commands/upgrade.js @@ -10,15 +10,25 @@ const chalk = require('chalk'); const { Command } = require('commander'); const { resolve } = require('path'); -const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode } = require('../util'); +const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode, checkDBDialect } = require('../util'); const { existsSync, rmSync } = require('fs'); +const { readJSON, writeJSON } = require('fs-extra'); +const deepmerge = require('deepmerge'); + +async function updatePackage() { + const sourcePath = resolve(__dirname, '../../templates/create-app-package.json'); + const descPath = resolve(process.cwd(), 'package.json'); + const sourceJson = await readJSON(sourcePath, 'utf8'); + const descJson = await readJSON(descPath, 'utf8'); + const json = deepmerge(descJson, sourceJson); + await writeJSON(descPath, json, { spaces: 2, encoding: 'utf8' }); +} /** * * @param {Command} cli */ module.exports = (cli) => { - const { APP_PACKAGE_ROOT } = process.env; cli .command('upgrade') .allowUnknownOption() @@ -26,52 +36,12 @@ module.exports = (cli) => { .option('--next') .option('-S|--skip-code-update') .action(async (options) => { - if (hasTsNode()) promptForTs(); - if (hasCorePackages()) { - // await run('yarn', ['install']); - await downloadPro(); - await runAppCommand('upgrade'); - return; - } + checkDBDialect(); if (options.skipCodeUpdate) { - await downloadPro(); await runAppCommand('upgrade'); - return; + } else { + await run('nocobase', ['update-deps']); + await run('nocobase', ['upgrade', '--skip-code-update']); } - // await runAppCommand('upgrade'); - if (!hasTsNode()) { - await downloadPro(); - await runAppCommand('upgrade'); - return; - } - const rmAppDir = () => { - // If ts-node is not installed, do not do the following - const appDevDir = resolve(process.cwd(), './storage/.app-dev'); - if (existsSync(appDevDir)) { - rmSync(appDevDir, { recursive: true, force: true }); - } - }; - const pkg = require('../../package.json'); - let distTag = 'latest'; - if (pkg.version.includes('alpha')) { - distTag = 'alpha'; - } else if (pkg.version.includes('beta')) { - distTag = 'beta'; - } - // get latest version - const { stdout } = await run('npm', ['info', `@nocobase/cli@${distTag}`, 'version'], { - stdio: 'pipe', - }); - if (pkg.version === stdout) { - await downloadPro(); - await runAppCommand('upgrade'); - await rmAppDir(); - return; - } - await run('yarn', ['add', `@nocobase/cli@${distTag}`, `@nocobase/devtools@${distTag}`, '-W']); - await run('yarn', ['install']); - await downloadPro(); - await runAppCommand('upgrade'); - await rmAppDir(); }); }; diff --git a/packages/core/cli/src/util.js b/packages/core/cli/src/util.js index 1c760cd4ba..855acd317b 100644 --- a/packages/core/cli/src/util.js +++ b/packages/core/cli/src/util.js @@ -360,7 +360,7 @@ exports.initEnv = function initEnv() { API_BASE_PATH: '/api/', API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_', API_CLIENT_STORAGE_TYPE: 'localStorage', - DB_DIALECT: 'sqlite', + // DB_DIALECT: 'sqlite', DB_STORAGE: 'storage/db/nocobase.sqlite', // DB_TIMEZONE: '+00:00', DB_UNDERSCORED: parseEnv('DB_UNDERSCORED'), @@ -472,6 +472,12 @@ exports.initEnv = function initEnv() { } }; +exports.checkDBDialect = function () { + if (!process.env.DB_DIALECT) { + throw new Error('DB_DIALECT is required.'); + } +}; + exports.generatePlugins = function () { try { require.resolve('@nocobase/devtools/umiConfig'); diff --git a/packages/core/cli/templates/create-app-package.json b/packages/core/cli/templates/create-app-package.json new file mode 100644 index 0000000000..3caaa3882e --- /dev/null +++ b/packages/core/cli/templates/create-app-package.json @@ -0,0 +1,39 @@ +{ + "private": true, + "workspaces": ["packages/*/*", "packages/*/*/*"], + "engines": { + "node": ">=18" + }, + "scripts": { + "nocobase": "nocobase", + "pm": "nocobase pm", + "pm2": "nocobase pm2", + "dev": "nocobase dev", + "start": "nocobase start", + "clean": "nocobase clean", + "build": "nocobase build", + "test": "nocobase test", + "e2e": "nocobase e2e", + "tar": "nocobase tar", + "postinstall": "nocobase postinstall", + "lint": "eslint ." + }, + "resolutions": { + "cytoscape": "3.28.0", + "@types/react": "18.3.18", + "@types/react-dom": "^18.0.0", + "react-router-dom": "6.28.1", + "react-router": "6.28.1", + "async": "^3.2.6", + "antd": "5.12.8", + "rollup": "4.24.0", + "semver": "^7.7.1" + }, + "dependencies": { + "pm2": "^6.0.5", + "mysql2": "^3.14.0", + "mariadb": "^2.5.6", + "pg": "^8.14.1", + "pg-hstore": "^2.3.4" + } +} diff --git a/packages/core/client/package.json b/packages/core/client/package.json index bb51b5c8ef..61726eaf97 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/client", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "lib/index.js", "module": "es/index.mjs", @@ -27,9 +27,9 @@ "@formily/reactive-react": "^2.2.27", "@formily/shared": "^2.2.27", "@formily/validator": "^2.2.27", - "@nocobase/evaluators": "1.7.0-beta.18", - "@nocobase/sdk": "1.7.0-beta.18", - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/evaluators": "1.7.0-beta.26", + "@nocobase/sdk": "1.7.0-beta.26", + "@nocobase/utils": "1.7.0-beta.26", "ahooks": "^3.7.2", "antd": "5.24.2", "antd-style": "3.7.1", diff --git a/packages/core/client/src/acl/ACLProvider.tsx b/packages/core/client/src/acl/ACLProvider.tsx index 19d8433475..482bea60d7 100644 --- a/packages/core/client/src/acl/ACLProvider.tsx +++ b/packages/core/client/src/acl/ACLProvider.tsx @@ -314,15 +314,17 @@ export const ACLActionProvider = (props) => { const schema = useFieldSchema(); const currentUid = schema['x-uid']; let actionPath = schema['x-acl-action']; - const editablePath = ['create', 'update', 'destroy', 'importXlsx']; + // 只兼容这些数据表资源按钮 + const resourceActionPath = ['create', 'update', 'destroy', 'importXlsx', 'export']; + // 视图表无编辑权限时不支持的操作 + const writableViewCollectionAction = ['create', 'update', 'destroy', 'importXlsx', 'bulkDestroy', 'bulkUpdate']; - if (!actionPath && resource && schema['x-action'] && editablePath.includes(schema['x-action'])) { + if (!actionPath && resource && schema['x-action'] && resourceActionPath.includes(schema['x-action'])) { actionPath = `${resource}:${schema['x-action']}`; } if (actionPath && !actionPath?.includes(':')) { actionPath = `${resource}:${actionPath}`; } - const params = useMemo( () => actionPath && parseAction(actionPath, { schema, recordPkValue }), [parseAction, actionPath, schema, recordPkValue], @@ -339,16 +341,18 @@ export const ACLActionProvider = (props) => { if (!params) { return {props.children}; } - //视图表无编辑权限时不显示 - if (editablePath.includes(actionPath) || editablePath.includes(actionPath?.split(':')[1])) { + //视图表无编辑权限时不支持 writableViewCollectionAction 的按钮 + if ( + writableViewCollectionAction.includes(actionPath) || + writableViewCollectionAction.includes(actionPath?.split(':')[1]) + ) { if ((collection && collection.template !== 'view') || collection?.writableView) { return {props.children}; } - return null; + return {props.children}; } return {props.children}; }; - export const useACLFieldWhitelist = () => { const params = useContext(ACLActionParamsContext); const whitelist = useMemo(() => { diff --git a/packages/core/client/src/acl/Configuration/schemas/scopes.ts b/packages/core/client/src/acl/Configuration/schemas/scopes.ts index a202d7871d..5984e48225 100644 --- a/packages/core/client/src/acl/Configuration/schemas/scopes.ts +++ b/packages/core/client/src/acl/Configuration/schemas/scopes.ts @@ -236,7 +236,6 @@ export const scopesSchema: ISchema = { 'x-component': 'Action.Link', 'x-component-props': { openMode: 'drawer', - icon: 'EditOutlined', }, properties: { drawer: { diff --git a/packages/core/client/src/application/globalOperators.js b/packages/core/client/src/application/globalOperators.js index 57b5ae7a11..ce8f265eb1 100644 --- a/packages/core/client/src/application/globalOperators.js +++ b/packages/core/client/src/application/globalOperators.js @@ -167,7 +167,7 @@ export function getOperators() { const dateA = parseDate(a); const dateB = parseDate(b); if (!dateA || !dateB) { - throw new Error('Invalid date format'); + return false; } return dateA < dateB; }, @@ -651,10 +651,11 @@ function parseYear(dateStr) { } function parseDate(targetDateStr) { - let dateStr = Array.isArray(targetDateStr) ? targetDateStr[1] : targetDateStr; - if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(dateStr)) { - // ISO 8601 格式:YYYY-MM-DDTHH:mm:ss.sssZ - return new Date(dateStr); // 直接解析为 Date 对象 + let dateStr = Array.isArray(targetDateStr) ? targetDateStr[1] ?? targetDateStr[0] : targetDateStr; + if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(dateStr)) { + return new Date(dateStr); + } else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateStr)) { + return new Date(dateStr.replace(' ', 'T')); } else if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { // YYYY-MM-DD 格式 return parseFullDate(dateStr); @@ -668,5 +669,6 @@ function parseDate(targetDateStr) { // YYYY 格式 return parseYear(dateStr); } - return null; // Invalid format + + return null; } 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/FormBlockProvider.tsx b/packages/core/client/src/block-provider/FormBlockProvider.tsx index f5e9da87cb..48a9e017ff 100644 --- a/packages/core/client/src/block-provider/FormBlockProvider.tsx +++ b/packages/core/client/src/block-provider/FormBlockProvider.tsx @@ -50,6 +50,7 @@ const InternalFormBlockProvider = (props) => { const form = useMemo( () => createForm({ + validateFirst: true, readPretty, }), [readPretty], diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index d34a8d540e..f602f86ee5 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -97,6 +97,35 @@ const filterValue = (value) => { return obj; }; +function getFilteredFormValues(form) { + const values = _.cloneDeep(form.values); + const allFields = []; + form.query('*').forEach((field) => { + if (field) { + allFields.push(field); + } + }); + const readonlyPaths = _.uniq( + allFields + .filter((field) => field?.componentProps?.readOnlySubmit) + .map((field) => { + const segments = field.path?.segments || []; + if (segments.length <= 1) { + return segments.join('.'); + } + return segments.slice(0, -1).join('.'); + }), + ); + readonlyPaths.forEach((path, index) => { + if (index !== 0 || path.includes('.')) { + // 清空值,但跳过第一层 + _.unset(values, path); + } + }); + + return values; +} + export function getFormValues({ filterByTk, field, @@ -124,7 +153,7 @@ export function getFormValues({ } } - return form.values; + return getFilteredFormValues(form); } export function useCollectValuesToSubmit() { @@ -203,8 +232,8 @@ export function useCollectValuesToSubmit() { ]); } -function interpolateVariables(str: string, scope: Record): string { - return str.replace(/\{\{\s*([a-zA-Z0-9_$-.]+?)\s*\}\}/g, (_, key) => { +export function interpolateVariables(str: string, scope: Record): string { + return str.replace(/\{\{\s*([a-zA-Z0-9_$.-]+?)\s*\}\}/g, (_, key) => { return scope[key] !== undefined ? String(scope[key]) : ''; }); } @@ -1484,6 +1513,7 @@ export const useAssociationFilterBlockProps = () => { run, valueKey, labelKey, + dataScopeFilter: filter, }; }; async function doReset({ diff --git a/packages/core/client/src/collection-manager/mixins/InheritanceCollectionMixin.ts b/packages/core/client/src/collection-manager/mixins/InheritanceCollectionMixin.ts index 5399efe4c1..5648500ddc 100644 --- a/packages/core/client/src/collection-manager/mixins/InheritanceCollectionMixin.ts +++ b/packages/core/client/src/collection-manager/mixins/InheritanceCollectionMixin.ts @@ -7,9 +7,9 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { filter, unionBy, uniq } from 'lodash'; import type { CollectionFieldOptions, GetCollectionFieldPredicate } from '../../data-source'; import { Collection } from '../../data-source/collection/Collection'; -import _, { filter, unionBy, uniq } from 'lodash'; export class InheritanceCollectionMixin extends Collection { protected parentCollectionsName: string[]; @@ -22,6 +22,7 @@ export class InheritanceCollectionMixin extends Collection { protected parentCollectionFields: Record = {}; protected allCollectionsInheritChain: string[]; protected inheritCollectionsChain: string[]; + protected inheritChain: string[]; protected foreignKeyFields: CollectionFieldOptions[]; getParentCollectionsName() { @@ -233,6 +234,43 @@ export class InheritanceCollectionMixin extends Collection { return this.inheritCollectionsChain; } + /** + * 获取所有祖先数据表和后代数据表,不包括兄弟表。用于下面这些地方: + * - 筛选区块链接数据区块时使用 + */ + getInheritChain() { + if (this.inheritChain) { + return this.inheritChain.slice(); + } + + const ancestorChain = this.getInheritCollectionsChain(); + const descendantNames = this.getChildrenCollectionsName(); + + // 构建最终的链,首先包含祖先链(包括自身) + const inheritChain = [...ancestorChain]; + + // 再添加直接后代及其后代,但不包括兄弟表 + const addDescendants = (names: string[]) => { + for (const name of names) { + if (!inheritChain.includes(name)) { + inheritChain.push(name); + const childCollection = this.collectionManager.getCollection(name); + if (childCollection) { + // 递归添加每个后代的后代 + const childrenNames = childCollection.getChildrenCollectionsName(); + addDescendants(childrenNames); + } + } + } + }; + + // 从当前集合的直接后代开始添加 + addDescendants(descendantNames); + + this.inheritChain = inheritChain; + return this.inheritChain; + } + getAllFields(predicate?: GetCollectionFieldPredicate) { if (this.allFields) { return this.allFields.slice(); diff --git a/packages/core/client/src/collection-manager/mixins/__tests__/InheritanceCollectionMixin.test.ts b/packages/core/client/src/collection-manager/mixins/__tests__/InheritanceCollectionMixin.test.ts new file mode 100644 index 0000000000..b3bd5c4613 --- /dev/null +++ b/packages/core/client/src/collection-manager/mixins/__tests__/InheritanceCollectionMixin.test.ts @@ -0,0 +1,189 @@ +import { Application } from '@nocobase/client'; +import { CollectionManager } from '../../../data-source/collection/CollectionManager'; +import { InheritanceCollectionMixin } from '../InheritanceCollectionMixin'; + +describe('InheritanceCollectionMixin', () => { + let app: Application; + let collectionManager: CollectionManager; + + beforeEach(() => { + app = new Application({ + dataSourceManager: { + collectionMixins: [InheritanceCollectionMixin], + }, + }); + collectionManager = app.getCollectionManager(); + }); + + describe('getInheritChain', () => { + it('should return itself when there are no ancestors or descendants', () => { + const options = { + name: 'test', + fields: [{ name: 'field1', interface: 'input' }], + }; + + collectionManager.addCollections([options]); + const collection = collectionManager.getCollection('test'); + + const inheritChain = collection.getInheritChain(); + expect(inheritChain).toEqual(['test']); + }); + + it('should return a chain including all ancestor tables', () => { + // 创建三代数据表结构:grandparent -> parent -> child + const grandparentOptions = { + name: 'grandparent', + fields: [{ name: 'field1', interface: 'input' }], + }; + const parentOptions = { + name: 'parent', + inherits: ['grandparent'], + fields: [{ name: 'field2', interface: 'input' }], + }; + const childOptions = { + name: 'child', + inherits: ['parent'], + fields: [{ name: 'field3', interface: 'input' }], + }; + + // 先将所有集合添加到 collectionManager + collectionManager.addCollections([grandparentOptions, parentOptions, childOptions]); + + // 获取最终的集合实例以调用方法 + const child = collectionManager.getCollection('child'); + + // 测试 child 的继承链包含所有祖先表 + const inheritChain = child.getInheritChain(); + expect(inheritChain).toContain('child'); + expect(inheritChain).toContain('parent'); + expect(inheritChain).toContain('grandparent'); + expect(inheritChain.length).toBe(3); + }); + + it('should include all descendant tables, but not sibling tables', () => { + // 创建具有兄弟和后代关系的数据表结构 + // parent (祖先表) + // |-- child1 (子表) + // | |-- grandChild1 (孙表1) + // | |-- grandChild2 (孙表2) + // |-- child2 (兄弟表) + // |-- grandChild3 (兄弟的子表,不应该包括在测试集合的继承链中) + + const collections = [ + { + name: 'parent', + fields: [{ name: 'parentField', interface: 'input' }], + }, + { + name: 'child1', + inherits: ['parent'], + fields: [{ name: 'child1Field', interface: 'input' }], + }, + { + name: 'child2', + inherits: ['parent'], + fields: [{ name: 'child2Field', interface: 'input' }], + }, + { + name: 'grandChild1', + inherits: ['child1'], + fields: [{ name: 'grandChild1Field', interface: 'input' }], + }, + { + name: 'grandChild2', + inherits: ['child1'], + fields: [{ name: 'grandChild2Field', interface: 'input' }], + }, + { + name: 'grandChild3', + inherits: ['child2'], + fields: [{ name: 'grandChild3Field', interface: 'input' }], + }, + ]; + + // 一次性添加所有集合 + collectionManager.addCollections(collections); + + // 获取要测试的集合实例 + const child1 = collectionManager.getCollection('child1'); + + // 测试 child1 的继承链 + const child1InheritChain = child1.getInheritChain(); + + // 应该包含自身、父表和子表 + expect(child1InheritChain).toContain('child1'); + expect(child1InheritChain).toContain('parent'); + expect(child1InheritChain).toContain('grandChild1'); + expect(child1InheritChain).toContain('grandChild2'); + + // 不应该包含兄弟表及其子表 + expect(child1InheritChain).not.toContain('child2'); + expect(child1InheritChain).not.toContain('grandChild3'); + + // 检查总数量是否正确 (parent, child1, grandChild1, grandChild2) + expect(child1InheritChain.length).toBe(4); + }); + + it('should properly handle multiple inheritance', () => { + // 创建多重继承的数据表结构 + // parent1 parent2 + // \ / + // \ / + // child + // | + // grandChild + + const collections = [ + { + name: 'parent1', + fields: [{ name: 'parent1Field', interface: 'input' }], + }, + { + name: 'parent2', + fields: [{ name: 'parent2Field', interface: 'input' }], + }, + { + name: 'child', + inherits: ['parent1', 'parent2'], + fields: [{ name: 'childField', interface: 'input' }], + }, + { + name: 'grandChild', + inherits: ['child'], + fields: [{ name: 'grandChildField', interface: 'input' }], + }, + ]; + + // 一次性添加所有集合 + collectionManager.addCollections(collections); + + // 获取要测试的集合实例 + const child = collectionManager.getCollection('child'); + const grandChild = collectionManager.getCollection('grandChild'); + + // 测试 child 的继承链 + const childInheritChain = child.getInheritChain(); + + // 应该包含自身、两个父表和子表 + expect(childInheritChain).toContain('child'); + expect(childInheritChain).toContain('parent1'); + expect(childInheritChain).toContain('parent2'); + expect(childInheritChain).toContain('grandChild'); + + // 检查总数量是否正确 (child, parent1, parent2, grandChild) + expect(childInheritChain.length).toBe(4); + + // 测试 grandChild 的继承链 + const grandChildInheritChain = grandChild.getInheritChain(); + + // 应该包含自身及所有祖先表 + expect(grandChildInheritChain).toContain('grandChild'); + expect(grandChildInheritChain).toContain('child'); + expect(grandChildInheritChain).toContain('parent1'); + expect(grandChildInheritChain).toContain('parent2'); + + // 检查总数量是否正确 (grandChild, child, parent1, parent2) + expect(grandChildInheritChain.length).toBe(4); + }); + }); +}); 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/data-source/data-source/DataSourceManagerProvider.tsx b/packages/core/client/src/data-source/data-source/DataSourceManagerProvider.tsx index 547c038e0e..578b447868 100644 --- a/packages/core/client/src/data-source/data-source/DataSourceManagerProvider.tsx +++ b/packages/core/client/src/data-source/data-source/DataSourceManagerProvider.tsx @@ -29,7 +29,7 @@ export function useDataSourceManager() { } /** - * 获取当前 collection 继承链路上的所有 collection + * 获取当前 collection 继承链路上的所有 collection(不包括兄弟表) * @returns */ export function useAllCollectionsInheritChainGetter() { @@ -39,7 +39,7 @@ export function useAllCollectionsInheritChainGetter() { return dm ?.getDataSource(customDataSource) ?.collectionManager?.getCollection(collectionName) - ?.getAllCollectionsInheritChain(); + ?.getInheritChain(); }, [dm], ); diff --git a/packages/core/client/src/filter-provider/__tests__/utiles.test.ts b/packages/core/client/src/filter-provider/__tests__/utiles.test.ts index be25dce192..f8340bd2a5 100644 --- a/packages/core/client/src/filter-provider/__tests__/utiles.test.ts +++ b/packages/core/client/src/filter-provider/__tests__/utiles.test.ts @@ -67,6 +67,108 @@ describe('getSupportFieldsByAssociation', () => { }); describe('getSupportFieldsByForeignKey', () => { + it('should return foreign key fields matching both name and target collection', () => { + const filterBlockCollection = { + fields: [ + { id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' }, + { id: 2, name: 'field2', type: 'hasMany', foreignKey: 'fk2', target: 'collection2' }, + { id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'collection3' }, + ], + }; + + const block = { + foreignKeyFields: [ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 2, name: 'fk2', collectionName: 'collection2' }, + { id: 3, name: 'fk3', collectionName: 'collection3' }, + ], + }; + + const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any); + + expect(result).toEqual([ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 2, name: 'fk2', collectionName: 'collection2' }, + { id: 3, name: 'fk3', collectionName: 'collection3' }, + ]); + }); + + it("should not return foreign key fields when target collection doesn't match", () => { + const filterBlockCollection = { + fields: [ + { id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' }, + { id: 2, name: 'field2', type: 'hasMany', foreignKey: 'fk2', target: 'collectionX' }, // target不匹配 + { id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'collection3' }, + ], + }; + + const block = { + foreignKeyFields: [ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 2, name: 'fk2', collectionName: 'collection2' }, // 与field2的target不匹配 + { id: 3, name: 'fk3', collectionName: 'collection3' }, + ], + }; + + const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any); + + expect(result).toEqual([ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 3, name: 'fk3', collectionName: 'collection3' }, + ]); + }); + + it('should filter out belongsTo type fields', () => { + const filterBlockCollection = { + fields: [ + { id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' }, + { id: 2, name: 'field2', type: 'belongsTo', foreignKey: 'fk2', target: 'collection2' }, // belongsTo类型 + { id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'collection3' }, + ], + }; + + const block = { + foreignKeyFields: [ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 2, name: 'fk2', collectionName: 'collection2' }, + { id: 3, name: 'fk3', collectionName: 'collection3' }, + ], + }; + + const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any); + + expect(result).toEqual([ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 3, name: 'fk3', collectionName: 'collection3' }, + ]); + }); + + it('should handle when both name and target collection match', () => { + const filterBlockCollection = { + fields: [ + { id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' }, + { id: 2, name: 'field2', type: 'hasOne', foreignKey: 'fk2', target: 'collection2' }, + { id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'wrongCollection' }, // 目标表不匹配 + ], + }; + + const block = { + foreignKeyFields: [ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 2, name: 'fk2', collectionName: 'collection2' }, + { id: 3, name: 'fk3', collectionName: 'collection3' }, // 与field3的target不匹配 + ], + }; + + const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any); + + expect(result).toEqual([ + { id: 1, name: 'fk1', collectionName: 'collection1' }, + { id: 2, name: 'fk2', collectionName: 'collection2' }, + ]); + }); + + // 保留原有的通用测试用例 it("should return all foreign key fields matching the filter block collection's foreign key properties", () => { const filterBlockCollection = { fields: [ diff --git a/packages/core/client/src/filter-provider/utils.ts b/packages/core/client/src/filter-provider/utils.ts index 1b3c6920ba..c2759df6ed 100644 --- a/packages/core/client/src/filter-provider/utils.ts +++ b/packages/core/client/src/filter-provider/utils.ts @@ -49,9 +49,13 @@ export const getSupportFieldsByAssociation = (inheritCollectionsChain: string[], export const getSupportFieldsByForeignKey = (filterBlockCollection: Collection, block: DataBlock) => { return block.foreignKeyFields?.filter((foreignKeyField) => { - return filterBlockCollection.fields.some( - (field) => field.type !== 'belongsTo' && field.foreignKey === foreignKeyField.name, - ); + return filterBlockCollection.fields.some((field) => { + return ( + field.type !== 'belongsTo' && + field.foreignKey === foreignKeyField.name && // 1. 外键字段的 name 要一致 + field.target === foreignKeyField.collectionName // 2. 关系字段的目标表要和外键的数据表一致 + ); + }); }); }; @@ -193,19 +197,21 @@ export const useFilterAPI = () => { const doFilter = useCallback( ( - value: any | ((target: FilterTarget['targets'][0], block: DataBlock) => any), + value: any | ((target: FilterTarget['targets'][0], block: DataBlock, sourceKey?: string) => any), field: string | ((target: FilterTarget['targets'][0], block: DataBlock) => string) = 'id', operator: string | ((target: FilterTarget['targets'][0]) => string) = '$eq', ) => { + const currentBlock = dataBlocks.find((block) => block.uid === fieldSchema.parent['x-uid']); dataBlocks.forEach((block) => { + let key = field as string; const target = targets.find((target) => target.uid === block.uid); if (!target) return; if (_.isFunction(value)) { - value = value(target, block); + value = value(target, block, getSourceKey(currentBlock, target.field)); } if (_.isFunction(field)) { - field = field(target, block); + key = field(target, block); } if (_.isFunction(operator)) { operator = operator(target); @@ -219,7 +225,7 @@ export const useFilterAPI = () => { storedFilter[uid] = { $and: [ { - [field]: { + [key]: { [operator]: value, }, }, @@ -248,7 +254,7 @@ export const useFilterAPI = () => { ); }); }, - [dataBlocks, targets, uid], + [dataBlocks, targets, uid, fieldSchema], ); return { @@ -268,3 +274,8 @@ export const isInFilterFormBlock = (fieldSchema: Schema) => { } return false; }; + +function getSourceKey(currentBlock: DataBlock, field: string) { + const associationField = currentBlock?.associatedFields?.find((item) => item.foreignKey === field); + return associationField?.sourceKey || field?.split?.('.')?.[1]; +} diff --git a/packages/core/client/src/global-theme/type.ts b/packages/core/client/src/global-theme/type.ts index 319cf01086..1a2377962d 100644 --- a/packages/core/client/src/global-theme/type.ts +++ b/packages/core/client/src/global-theme/type.ts @@ -49,6 +49,8 @@ export interface CustomToken extends AliasToken { marginBlock: number; /** 区块的圆角 */ borderRadiusBlock: number; + + siderWidth: number; } export interface ThemeConfig extends _ThemeConfig { 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 01eb15ae86..dc001c1ca9 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 05a64aba8e..a8a75e7028 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 1909aaaa92..c9b15f6e99 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", "No pages yet, please configure first": "Ainda não há páginas, por favor configure primeiro", "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", diff --git a/packages/core/client/src/locale/ru-RU.json b/packages/core/client/src/locale/ru-RU.json index 22c864ac35..65340c38ee 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": "Полные права", "No pages yet, please configure first": "Пока нет страниц, пожалуйста, настройте сначала", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Нажмите на значок \"Редактор пользовательского интерфейса\" в правом верхнем углу, чтобы войти в режим редактора пользовательского интерфейса", diff --git a/packages/core/client/src/locale/tr-TR.json b/packages/core/client/src/locale/tr-TR.json index 390c4ca0f2..b6c66fe2c1 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", "No pages yet, please configure first": "Henüz sayfa yok, lütfen önce yapılandırın", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Kullanıcı arayüzü düzenleyici moduna girmek için sağ üst köşedeki \"Kullanıcı Arayüzü Düzenleyici\" simgesine tıklayın", diff --git a/packages/core/client/src/locale/uk-UA.json b/packages/core/client/src/locale/uk-UA.json index 91bf069e1d..8f698134ba 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": "Повні права", "No pages yet, please configure first": "Ще немає сторінок, будь ласка, спочатку налаштуйте", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Натисніть на значок \"Редактор користувацького інтерфейсу\" в правому верхньому куті, щоб увійти в режим редактора користувацького інтерфейсу.", diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index 8e27dc701d..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": "分组字段", @@ -821,7 +823,8 @@ "File size should not exceed {{size}}.": "文件大小不能超过 {{size}}", "File size exceeds the limit": "文件大小超过限制", "File type is not allowed": "文件类型不允许", - "Incomplete uploading files need to be resolved": "未完成上传的文件需要处理", + "Uploading": "上传中", + "Some files are not uploaded correctly, please check.": "部分文件未上传成功,请检查。", "Default title for each record": "用作数据的默认标题", "If collection inherits, choose inherited collections as templates": "当前表有继承关系时,可选择继承链路上的表作为模板来源", "Select an existing piece of data as the initialization data for the form": "选择一条已有的数据作为表单的初始化数据", @@ -1087,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": "日期范围", @@ -1102,5 +1104,7 @@ "Colon":"冒号", "No pages yet, please configure first": "暂无页面,请先配置", "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.": "提交成功后,会自动刷新这里选中的数据区块。" + "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 77a012fe3f..01c31a2a1d 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": "完全權限", "No pages yet, please configure first": "尚未配置頁面,請先配置", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "點擊右上角的 \"介面設定\" 圖示進入介面設定模式", 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/associate/AssociateActionProvider.tsx b/packages/core/client/src/modules/actions/associate/AssociateActionProvider.tsx index 18be2d76b9..5e2a49c7c4 100644 --- a/packages/core/client/src/modules/actions/associate/AssociateActionProvider.tsx +++ b/packages/core/client/src/modules/actions/associate/AssociateActionProvider.tsx @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React, { useState, useContext } from 'react'; +import React, { useState, useContext, useEffect } from 'react'; import { RecordPickerProvider, RecordPickerContext } from '../../../schema-component/antd/record-picker'; import { SchemaComponentOptions, @@ -41,9 +41,16 @@ const useTableSelectorProps = () => { export const AssociateActionProvider = (props) => { const [selectedRows, setSelectedRows] = useState([]); const collection = useCollection(); - const { resource, service, block, __parent } = useBlockRequestContext(); + const { resource, block, __parent } = useBlockRequestContext(); const actionCtx = useActionContext(); const { isMobile } = useOpenModeContext() || {}; + const [associationData, setAssociationData] = useState([]); + useEffect(() => { + resource?.list?.().then((res) => { + setAssociationData(res.data?.data || []); + }); + }, [resource]); + const pickerProps = { size: 'small', onChange: props?.onChange, @@ -73,8 +80,8 @@ export const AssociateActionProvider = (props) => { }; const getFilter = () => { const targetKey = collection?.filterTargetKey || 'id'; - if (service.data?.data) { - const list = service.data?.data.map((option) => option[targetKey]).filter(Boolean); + if (associationData) { + const list = associationData.map((option) => option[targetKey]).filter(Boolean); const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {}; return filter; } 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__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer.test.ts index 3c0d2ab0da..90b836587d 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer.test.ts @@ -309,6 +309,7 @@ test.describe('configure actions column', () => { await page.getByText('Actions', { exact: true }).hover({ force: true }); await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover(); await page.getByRole('menuitem', { name: 'Delete' }).click(); + await page.mouse.move(500, 0); // await page.getByText('Actions', { exact: true }).hover({ force: true }); // await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover(); 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 ac79a00269..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', @@ -37,7 +39,7 @@ const enabledIndexColumn: SchemaSettingsItemType = { const { dn } = useDesignable(); return { title: t('Enable index column'), - checked: field.decoratorProps.enableSelectColumn !== false, + checked: field.decoratorProps.enableIndexÏColumn !== false, onChange: async (enableIndexÏColumn) => { field.decoratorProps = field.decoratorProps || {}; field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn; @@ -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/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx b/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx index 55e1d28991..96da338b3f 100644 --- a/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx +++ b/packages/core/client/src/modules/fields/component/SubTable/subTablePopoverComponentFieldSettings.tsx @@ -28,6 +28,7 @@ import { isSubMode } from '../../../../schema-component/antd/association-field/u import { useIsAssociationField } from '../../../../schema-component/antd/form-item'; import { FormLinkageRules } from '../../../../schema-settings/LinkageRules'; import { SchemaSettingsLinkageRules } from '../../../../schema-settings/SchemaSettings'; +import { useColumnSchema } from '../../../../schema-component'; import { SchemaSettingsItemType } from '../../../../application'; const enabledIndexColumn: SchemaSettingsItemType = { @@ -338,11 +339,12 @@ export const linkageRules = { Component: SchemaSettingsLinkageRules, useComponentProps() { const field = useField(); - const fieldSchema = useFieldSchema(); + const schema = useFieldSchema(); + const { fieldSchema: columnSchema } = useColumnSchema(); + const fieldSchema = columnSchema || schema; const cm = useCollectionManager(); const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']); const { rerenderDataBlock } = useRerenderDataBlock(); - return { collectionName: collectionField?.target, Component: LinkageRulesComponent, diff --git a/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts index d684443399..ecbb9205a0 100644 --- a/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts @@ -57,6 +57,7 @@ test.describe('add blocks to the popup', () => { await page.getByRole('menuitem', { name: 'Details right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records' }).last().hover(); await page.getByRole('menuitem', { name: 'Roles' }).click(); + await page.mouse.move(300, 0); await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover(); await page.getByRole('menuitem', { name: 'Role UID' }).click(); await page.mouse.move(300, 0); diff --git a/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts b/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts index 4d48df81f3..e0bfad2db1 100644 --- a/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts +++ b/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts @@ -71,7 +71,7 @@ test.describe('sub page', () => { expect(page.url()).not.toContain('/popups/'); // 确认是否回到了主页面 - await page.getByText('Users单层子页面Configure').hover(); + // await page.getByText('Users单层子页面Configure').hover(); await expect( page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }), ).toBeVisible(); 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 6ffe8eb425..f2f3b95f2c 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'; @@ -146,7 +147,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 692c9e3f0c..b3a6a1c610 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 @@ -18,7 +18,6 @@ import { useTranslation } from 'react-i18next'; import { Link, Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom'; import { ACLRolesCheckProvider, - AppNotFound, CurrentAppInfoProvider, DndContext, Icon, @@ -46,6 +45,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'; @@ -503,6 +503,8 @@ const subMenuItemRender = (item, dom) => { }; const CollapsedButton: FC<{ collapsed: boolean }> = (props) => { + const { token } = useToken(); + return ( {(context) => @@ -515,7 +517,7 @@ const CollapsedButton: FC<{ collapsed: boolean }> = (props) => { // Fix the issue where the collapse/expand button is covered by subpages .ant-pro-sider-collapsed-button { top: 64px; - left: ${props.collapsed ? 52 : 188}px; + left: ${props.collapsed ? 52 : (token.siderWidth || 200) - 12}px; z-index: 200; transition: left 0.2s; } @@ -671,7 +673,7 @@ export const InternalAdminLayout = () => { { 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.Modal.tsx b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx index 1c43c5bc9e..9ccb215771 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx @@ -67,21 +67,24 @@ const ActionModalContent: FC<{ footerNodeName: string; field: any; schema: any } ); export function useDelayedVisible(visible: boolean, delay = 200) { - const [ready, setReady] = useState(false); + const [ready, setReady] = useState(delay === 0); useEffect(() => { + if (ready) { + return; + } if (visible) { const timer = setTimeout(() => setReady(true), delay); return () => clearTimeout(timer); } else { setReady(false); } - }, [visible]); + }, [delay, ready, visible]); return ready; } export const InternalActionModal: React.FC> = observer( (props) => { - const { footerNodeName = 'Action.Modal.Footer', width, zIndex: _zIndex, ...others } = props; + const { footerNodeName = 'Action.Modal.Footer', width, zIndex: _zIndex, delay = 200, ...others } = props; const { visible, setVisible, openSize = 'middle', modalProps } = useActionContext(); const actualWidth = width ?? openSizeWidthMap.get(openSize); const schema = useFieldSchema(); @@ -102,7 +105,7 @@ export const InternalActionModal: React.FC> = obse } const zIndex = getZIndex('modal', _zIndex || parentZIndex, props.level || 0); - const ready = useDelayedVisible(visible, 200); // 200ms 与 Modal 动画时间一致 + const ready = useDelayedVisible(visible, delay); // 200ms 与 Modal 动画时间一致 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 38b6802b4d..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'; @@ -57,7 +57,7 @@ import { useAllDataBlocks } from '../page/AllDataBlocksProvider'; const useA = () => { return { - async run() { }, + async run() {}, }; }; @@ -139,23 +139,26 @@ export const Action: ComposedAction = withDynamicSchemaProps( ); const handleClick = useMemo(() => { - return onClick && (async (e, callback) => { - await onClick?.(e, callback); + return ( + onClick && + (async (e, callback) => { + await onClick?.(e, callback); - // 执行完 onClick 之后,刷新数据区块 - const blocksToRefresh = fieldSchema['x-action-settings']?.onSuccess?.blocksToRefresh || [] - if (blocksToRefresh.length > 0) { - getAllDataBlocks().forEach((block) => { - if (blocksToRefresh.includes(block.uid)) { - try { - block.service?.refresh(); - } catch (error) { - console.error('Failed to refresh block:', block.uid, error); + // 执行完 onClick 之后,刷新数据区块 + const blocksToRefresh = fieldSchema['x-action-settings']?.onSuccess?.blocksToRefresh || []; + if (blocksToRefresh.length > 0) { + getAllDataBlocks().forEach((block) => { + if (blocksToRefresh.includes(block.uid)) { + try { + block.service?.refresh(); + } catch (error) { + console.error('Failed to refresh block:', block.uid, error); + } } - } - }); - } - }); + }); + } + }) + ); }, [onClick, fieldSchema, getAllDataBlocks]); return ( @@ -172,7 +175,7 @@ export const Action: ComposedAction = withDynamicSchemaProps( className={className} type={props.type} Designer={Designer} - onClick={onClick} + onClick={handleClick} confirm={confirm} confirmTitle={confirmTitle} popover={popover} @@ -351,11 +354,7 @@ const InternalAction: React.FC = observer(function Com(prop } if (addChild) { - return wrapSSR( - - {result} - , - ) as React.ReactElement; + return wrapSSR({result}) as React.ReactElement; } return wrapSSR(result) as React.ReactElement; @@ -370,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/hooks/useGetAfterSuccessVariablesOptions.ts b/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts index d89abc64bb..311c1b7be9 100644 --- a/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts +++ b/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts @@ -12,9 +12,9 @@ import { useCollection_deprecated, useCollectionFilterOptions } from '../../../. import { useCollectionRecordData } from '../../../../data-source'; import { useTranslation } from 'react-i18next'; import { useCompile } from '../../../'; -import { useBlockContext } from '../../../../block-provider/BlockProvider'; import { usePopupVariable } from '../../../../schema-settings/VariableInput/hooks'; import { useCurrentRoleVariable } from '../../../../schema-settings/VariableInput/hooks'; +import { useFormBlockContext } from '../../../../block-provider'; export const useAfterSuccessOptions = () => { const collection = useCollection_deprecated(); @@ -23,16 +23,15 @@ export const useAfterSuccessOptions = () => { const userFieldOptions = useCollectionFilterOptions('users', 'main'); const compile = useCompile(); const recordData = useCollectionRecordData(); - const { name: blockType } = useBlockContext() || {}; + const { form } = useFormBlockContext(); const [fields, userFields] = useMemo(() => { return [compile(fieldsOptions), compile(userFieldOptions)]; }, [fieldsOptions, userFieldOptions]); const { settings: popupRecordSettings, shouldDisplayPopupRecord } = usePopupVariable(); const { currentRoleSettings } = useCurrentRoleVariable(); - const record = useCollectionRecordData(); return useMemo(() => { return [ - (record || blockType === 'form') && { + form && { value: '$record', label: t('Response record', { ns: 'client' }), children: [...fields], @@ -62,5 +61,5 @@ export const useAfterSuccessOptions = () => { children: null, }, ].filter(Boolean); - }, [recordData, t, fields, blockType, userFields]); + }, [recordData, t, fields, form, userFields]); }; 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 3df496e1b4..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 & { @@ -92,6 +93,7 @@ export type ActionDrawerProps = T & { footerNodeName?: string; /** 当前弹窗嵌套的层级 */ level?: number; + delay?: number; }; export type ComposedActionDrawer = React.FC> & { diff --git a/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx b/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx index ed9969a523..03c67938c8 100644 --- a/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/AssociationSelect.tsx @@ -34,7 +34,7 @@ import useServiceOptions, { useAssociationFieldContext } from './hooks'; const removeIfKeyEmpty = (obj, filterTargetKey) => { if (!obj || typeof obj !== 'object' || !filterTargetKey || Array.isArray(obj)) return obj; - return !obj[filterTargetKey] ? null : obj; + return !obj[filterTargetKey] ? undefined : obj; }; export const AssociationFieldAddNewer = (props) => { @@ -106,8 +106,13 @@ const InternalAssociationSelect = observer( useEffect(() => { const initValue = isVariable(field.value) ? undefined : field.value; const value = Array.isArray(initValue) ? initValue.filter(Boolean) : initValue; - setInnerValue(value); - }, [field.value]); + const result = removeIfKeyEmpty(value, filterTargetKey); + setInnerValue(result); + if (!isEqual(field.value, result)) { + field.value = result; + } + }, [field.value, filterTargetKey]); + useEffect(() => { const id = uid(); form.addEffects(id, () => { diff --git a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx index 032446d4c6..5b15461293 100644 --- a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx @@ -70,6 +70,15 @@ const subTableContainer = css` .ant-table-thead .ant-table-cell { font-weight: normal; } + .ant-pagination { + position: absolute; + right: 0px; + bottom: 0px; + } + .ant-table-footer { + margin-top: 10px; + background: inherit; + } `; const tableClassName = css` @@ -218,7 +227,6 @@ export const SubTable: any = observer( }, }; }; - console.log(props); return (
@@ -253,37 +261,39 @@ export const SubTable: any = observer( emptyText: {field.editable ? t('Please add or select record') : t('No data')}, }} enableIndexÏColumn={enableIndexÏColumn !== false} + footer={() => ( +
+ {field.editable && ( + + {allowAddnew !== false && ( + + {t('Add new')} + + } + /> + )} + {allowSelectExistingRecord && ( + + {t('Select record')} + + } + /> + )} + + )} +
+ )} /> - {field.editable && ( - - {allowAddnew !== false && ( - - {t('Add new')} - - } - /> - )} - {allowSelectExistingRecord && ( - - {t('Select record')} - - } - /> - )} - - )} 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/date-picker/DatePicker.tsx b/packages/core/client/src/schema-component/antd/date-picker/DatePicker.tsx index debbd13e94..4a93093675 100644 --- a/packages/core/client/src/schema-component/antd/date-picker/DatePicker.tsx +++ b/packages/core/client/src/schema-component/antd/date-picker/DatePicker.tsx @@ -252,8 +252,9 @@ DatePicker.FilterWithPicker = function FilterWithPicker(props: any) { const value = Array.isArray(props.value) ? props.value[0] : props.value; const compile = useCompile(); const fieldSchema = useFieldSchema(); - const targetPicker = value ? inferPickerType(value, picker) : picker; - const targetDateFormat = getPickerFormat(targetPicker) || format; + const initPicker = value ? inferPickerType(value, picker) : picker; + const [targetPicker, setTargetPicker] = useState(initPicker); + const targetDateFormat = getPickerFormat(initPicker) || format; const newProps = { utc, inputReadOnly: isMobileMedia, @@ -271,13 +272,6 @@ DatePicker.FilterWithPicker = function FilterWithPicker(props: any) { }; const field: any = useField(); const [stateProps, setStateProps] = useState(newProps); - useEffect(() => { - newProps.picker = targetPicker; - const dateTimeFormat = getDateTimeFormat(targetPicker, targetDateFormat, showTime, timeFormat); - newProps.format = dateTimeFormat; - setStateProps(newProps); - }, [targetPicker]); - return ( + {editFlag && + React.createElement(DynamicComponent, { + value, + schema, + onChange: onChangeValue, + })} + {!props.disabled && ( + + + + )} + +
+ ); + }, + { displayName: 'FormButtonLinkageRuleAction' }, +); diff --git a/packages/core/client/src/schema-settings/LinkageRules/FieldStyleLinkageRuleAction.tsx b/packages/core/client/src/schema-settings/LinkageRules/components/FieldStyleLinkageRuleAction.tsx similarity index 94% rename from packages/core/client/src/schema-settings/LinkageRules/FieldStyleLinkageRuleAction.tsx rename to packages/core/client/src/schema-settings/LinkageRules/components/FieldStyleLinkageRuleAction.tsx index 94a470277c..d4e5968938 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/FieldStyleLinkageRuleAction.tsx +++ b/packages/core/client/src/schema-settings/LinkageRules/components/FieldStyleLinkageRuleAction.tsx @@ -10,15 +10,14 @@ import { CloseCircleOutlined } from '@ant-design/icons'; import { css } from '@emotion/css'; import { observer } from '@formily/react'; -import { uid } from '@formily/shared'; import { Select, Space } from 'antd'; import React, { useCallback, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useCompile } from '../..'; -import { ValueDynamicComponent } from './ValueDynamicComponent'; -import { RemoveActionContext } from './context'; -import { ActionType } from './type'; -import { useValues } from './useValues'; +import { useCompile } from '../../..'; +import { ValueDynamicComponent } from '../ValueDynamicComponent'; +import { RemoveActionContext } from '../context'; +import { ActionType } from '../type'; +import { useValues } from '../useValues'; const colorSchema = { type: 'string', diff --git a/packages/core/client/src/schema-settings/LinkageRules/index.tsx b/packages/core/client/src/schema-settings/LinkageRules/index.tsx index 03acb9eca3..530cb892ce 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/index.tsx +++ b/packages/core/client/src/schema-settings/LinkageRules/index.tsx @@ -20,10 +20,12 @@ import { SubFormProvider } from '../../schema-component/antd/association-field/h import { DynamicComponentProps } from '../../schema-component/antd/filter/DynamicComponent'; import { FilterContext } from '../../schema-component/antd/filter/context'; import { VariableInput, getShouldChange } from '../VariableInput/VariableInput'; +import { useCurrentFormContext } from '../VariableInput/hooks/useFormVariable'; import { LinkageRuleActionGroup } from './LinkageRuleActionGroup'; import { EnableLinkage } from './components/EnableLinkage'; import { ArrayCollapse } from './components/LinkageHeader'; - +import { useFlag } from '../../flag-provider'; +import { LinkageRuleCategory } from './type'; export interface Props { dynamicComponent: any; } @@ -64,11 +66,12 @@ function transformConditionData(condition: Condition, variableKey: '$nForm' | '$ rightVar: value, }; } -function getActiveContextName(contextList: { name: string; ctx: any }[]): string | null { - const priority = ['$nForm', '$nRecord']; - for (const name of priority) { - const item = contextList.find((ctx) => ctx.name === name && ctx.ctx); - if (item) return name; +function getActiveContextName(underNester, shouldDisplayCurrentForm): string | null { + if (underNester) { + return '$iteration'; + } + if (shouldDisplayCurrentForm) { + return '$nForm'; } return '$nRecord'; } @@ -90,13 +93,34 @@ const transformDefaultValue = (values, variableKey) => { export const FormLinkageRules = withDynamicSchemaProps( observer((props: Props) => { const fieldSchema = useFieldSchema(); - const { options, defaultValues, collectionName, form, variables, localVariables, record, dynamicComponent } = - useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema + const { + options, + defaultValues, + collectionName, + form, + variables, + localVariables, + record, + dynamicComponent, + category, + returnScope, + } = useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema const { name } = useCollection_deprecated(); const { getAllCollectionsInheritChain } = useCollectionManager_deprecated(); const parentRecordData = useCollectionParentRecordData(); - const variableKey = getActiveContextName(localVariables); + const { shouldDisplayCurrentForm } = useCurrentFormContext(); const components = useMemo(() => ({ ArrayCollapse }), []); + const { isInSubTable, isInSubForm } = useFlag(); + const variableKey = getActiveContextName(isInSubTable || isInSubForm, shouldDisplayCurrentForm); + const returnTargetScope = + returnScope ?? + ((options) => + options.filter((v) => { + if (category === LinkageRuleCategory.block) { + return !['$nForm', '$nRecord'].includes(v.value); + } + return true; + })); const schema = useMemo( () => ({ type: 'object', @@ -192,6 +216,9 @@ export const FormLinkageRules = withDynamicSchemaProps( conditionAdvanced: { 'x-component': 'LinkageFilter', 'x-visible': '{{$deps[0] === "advanced"}}', + 'x-component-props': { + returnScope: returnTargetScope, + }, 'x-reactions': [ { dependencies: ['.conditionType', '.condition'], diff --git a/packages/core/client/src/schema-settings/LinkageRules/type.ts b/packages/core/client/src/schema-settings/LinkageRules/type.ts index 05abd99617..59426ad3b0 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/type.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/type.ts @@ -33,10 +33,12 @@ export enum LinkageRuleCategory { default = 'default', style = 'style', button = 'button', + block = 'block', } export const LinkageRuleDataKeyMap: Record<`${LinkageRuleCategory}`, string> = { [LinkageRuleCategory.style]: 'x-linkage-style-rules', [LinkageRuleCategory.default]: 'x-linkage-rules', [LinkageRuleCategory.button]: 'x-linkage-rules', + [LinkageRuleCategory.block]: 'x-block-linkage-rules', }; diff --git a/packages/core/client/src/schema-settings/LinkageRules/useValues.ts b/packages/core/client/src/schema-settings/LinkageRules/useValues.ts index 0001622468..5a86fafcd0 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/useValues.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/useValues.ts @@ -34,7 +34,37 @@ export const useValues = (options) => { const dataIndex = field.data?.targetFields; const option = (dataIndex && findOption(dataIndex, options)) || {}; const operators = option?.operators || []; - field.data.operators = operators; + field.data.operators = operators?.filter((v) => { + const isOptionField = ['select', 'radioGroup', 'multipleSelect', 'checkboxGroup'].includes( + option?.interface || '', + ); + const isDateField = [ + 'date', + 'datetime', + 'dateOnly', + 'datetimeNoTz', + 'unixTimestamp', + 'createdAt', + 'updatedAt', + ].includes(option?.interface || ''); + + // 如果 多个字段,则排除 Value、DateScope、Options + if (dataIndex.length > 1 && [ActionType.Value, ActionType.DateScope, ActionType.Options].includes(v.value)) { + return false; + } + + // 非选项字段,去掉 Options + if (!isOptionField && v.value === ActionType.Options) { + return false; + } + + // 非时间字段,去掉 DateScope + if (!isDateField && v.value === ActionType.DateScope) { + return false; + } + + return true; + }); field.data.schema = option?.schema; }; useEffect(value2data, [logic]); diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx index c4154f68b4..a35d3ea95a 100644 --- a/packages/core/client/src/schema-settings/SchemaSettings.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx @@ -48,6 +48,7 @@ import { SchemaSettingsItemType, SchemaToolbarVisibleContext, VariablesContext, + getZIndex, useCollection, useCollectionManager, useZIndexContext, @@ -84,7 +85,7 @@ import { AssociationOrCollectionProvider, useDataBlockProps } from '../data-sour import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider'; import { useDataSourceKey } from '../data-source/data-source/DataSourceProvider'; import { useFilterBlock } from '../filter-provider/FilterProvider'; -import { FlagProvider } from '../flag-provider'; +import { FlagProvider, useFlag } from '../flag-provider'; import { useGlobalTheme } from '../global-theme'; import { useCollectMenuItem, useCollectMenuItems, useMenuItem } from '../hooks/useMenuItem'; import { @@ -341,13 +342,13 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) { } return ( { setVisible(false); const collection = collectionName && cm?.getCollection(collectionName); const gridSchema = findGridSchema(fieldSchema); const values = await FormDialog( - t('Save as template'), + t('Save as reference template'), () => { const componentTitle = { FormItem: t('Form'), @@ -365,8 +366,8 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) { required: true, default: collection ? `${compile(collection?.title || collection?.name)}_${t( - componentTitle[componentName] || componentName, - )}` + componentTitle[componentName] || componentName, + )}` : t(componentTitle[componentName] || componentName), 'x-decorator': 'FormItem', 'x-component': 'Input', @@ -413,7 +414,7 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) { dn.refresh(); }} > - {t('Save as block template')} + {t('Save as reference template')} ); }; @@ -567,7 +568,7 @@ export const SchemaSettingsRemove: FC = (props) => { export interface SchemaSettingsSelectItemProps extends Omit, - Omit { + Omit { value?: SelectWithTitleProps['defaultValue']; optionRender?: (option: any, info: { index: number }) => React.ReactNode; } @@ -697,7 +698,7 @@ export const SchemaSettingsActionModalItem: FC @@ -900,26 +901,32 @@ export const SchemaSettingsModalItem: FC = (props) > - + - + 576px - @media (min-width: 576px) { - min-width: 520px; - } + // screen > 576px + @media (min-width: 576px) { + min-width: 520px; + } - // screen <= 576px - @media (max-width: 576px) { - min-width: 320px; - } - `} + // screen <= 576px + @media (max-width: 576px) { + min-width: 320px; + } + `} > @@ -984,13 +991,13 @@ export const SchemaSettingsDefaultSortingRules = function DefaultSortingRules(pr const sort = defaultSort?.map((item: string) => { return item.startsWith('-') ? { - field: item.substring(1), - direction: 'desc', - } + field: item.substring(1), + direction: 'desc', + } : { - field: item, - direction: 'asc', - }; + field: item, + direction: 'asc', + }; }); const sortFields = useSortFields(props.name || collection?.name); @@ -1096,7 +1103,7 @@ export const SchemaSettingsDefaultSortingRules = function DefaultSortingRules(pr }; export const SchemaSettingsLinkageRules = function LinkageRules(props) { - const { collectionName, readPretty, Component, afterSubmit } = props; + const { collectionName, readPretty, Component, afterSubmit, title: settingTitle, returnScope } = props; const fieldSchema = useFieldSchema(); const { form } = useFormBlockContext(); const { dn } = useDesignable(); @@ -1122,7 +1129,8 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) { const getRules = useCallback(() => { return gridSchema?.[dataKey] || fieldSchema?.[dataKey] || []; }, [gridSchema, fieldSchema, dataKey]); - const title = titleMap[category] || t('Linkage rules'); + const title = settingTitle || titleMap[category] || t('Linkage rules'); + const flagVales = useFlag(); const schema = useMemo( () => ({ type: 'object', @@ -1143,6 +1151,7 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) { localVariables, record, formBlockType, + returnScope, }; }, }, @@ -1158,23 +1167,36 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) { rules.push(_.omit(_.pickBy(rule, _.identity), ['conditionBasic', 'conditionAdvanced'])); } const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props']?.templateId; - const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid']; + const uid = + category !== LinkageRuleCategory.block + ? (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid'] + : fieldSchema['x-uid']; const schema = { ['x-uid']: uid, }; gridSchema[dataKey] = rules; schema[dataKey] = rules; + fieldSchema[dataKey] = rules; dn.emit('patch', { schema, }); dn.refresh(); afterSubmit?.(); }, - [dn, getTemplateById, gridSchema, dataKey, afterSubmit], + [dn, getTemplateById, gridSchema, dataKey, afterSubmit, category], ); return ( - + { + return {props.children}; + }} + /> ); }; diff --git a/packages/core/client/src/schema-settings/SchemaSettingsTemplate.tsx b/packages/core/client/src/schema-settings/SchemaSettingsTemplate.tsx index aaaf338cff..66ec832370 100644 --- a/packages/core/client/src/schema-settings/SchemaSettingsTemplate.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettingsTemplate.tsx @@ -40,7 +40,7 @@ export function SchemaSettingsTemplate(props) { if (template) { return ( { const schema = await copyTemplateSchema(template); const removed = tdn.removeWithoutEmit(); @@ -59,12 +59,12 @@ export function SchemaSettingsTemplate(props) { } return ( { setVisible(false); const collection = collectionName && getCollection(collectionName); const values = await FormDialog( - t('Save as template'), + t('Save as reference template'), () => { return ( @@ -115,7 +115,7 @@ export function SchemaSettingsTemplate(props) { }); }} > - {t('Save as template')} + {t('Save as reference template')} ); } diff --git a/packages/core/client/src/schema-settings/VariableInput/__tests__/hooks/useURLSearchParamsVariable.test.ts b/packages/core/client/src/schema-settings/VariableInput/__tests__/hooks/useURLSearchParamsVariable.test.ts index 6635777047..3579de9a40 100644 --- a/packages/core/client/src/schema-settings/VariableInput/__tests__/hooks/useURLSearchParamsVariable.test.ts +++ b/packages/core/client/src/schema-settings/VariableInput/__tests__/hooks/useURLSearchParamsVariable.test.ts @@ -24,7 +24,7 @@ test('getURLSearchParamsChildren should return an array of options with expected const result = getURLSearchParamsChildren(queryParams); - expect(result).toEqual([ + expect(result).toMatchObject([ { label: 'param1', value: 'param1', diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/index.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/index.ts index ec9cd6da7f..88bb03b409 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/index.ts +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/index.ts @@ -15,3 +15,4 @@ export * from './useURLSearchParamsVariable'; export * from './useUserVariable'; export * from './useVariableOptions'; export * from './usePopupVariable'; +export * from './useContextAssociationFields'; diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx b/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx index 7ea69f241b..892c0e242c 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx @@ -46,7 +46,9 @@ const getChildren = ( return { key: option.name, value: option.name, + name: option.name, label: compile(option.title), + title: compile(option.title), disabled: disabled, isLeaf: true, depth, @@ -59,7 +61,9 @@ const getChildren = ( return { key: option.name, value: option.name, + name: option.name, label: compile(option.title), + title: compile(option.title), disabled: disabled, isLeaf: true, field: option, @@ -77,10 +81,10 @@ export const useContextAssociationFields = ({ contextCollectionName, collectionField, }: { - schema: any; + schema?: any; maxDepth?: number; contextCollectionName: string; - collectionField: CollectionFieldOptions_deprecated; + collectionField?: CollectionFieldOptions_deprecated; }) => { const { t } = useTranslation(); const compile = useCompile(); @@ -101,10 +105,17 @@ export const useContextAssociationFields = ({ const children = getChildren( getFilterOptions(collectionName).filter((v) => { - const isAssociationField = ['hasOne', 'hasMany', 'belongsTo', 'belongsToMany', 'belongsToArray'].includes( - v.type, - ); - return isAssociationField; + if (collectionField) { + const isAssociationField = [ + 'hasOne', + 'hasMany', + 'belongsTo', + 'belongsToMany', + 'belongsToArray', + ].includes(v.type); + return isAssociationField; + } + return true; }), { schema, diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useFormVariable.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/useFormVariable.ts index ddf2f76f1f..56cde061fa 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useFormVariable.ts +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useFormVariable.ts @@ -78,7 +78,10 @@ export const useCurrentFormContext = ({ form: _form }: Pick = {}) currentFormCtx: formInstance?.values, /** 用来判断是否可以显示`当前表单`变量 */ shouldDisplayCurrentForm: - name === 'form' && formInstance && !formInstance.readPretty && !isVariableParsedInOtherContext, + ['form', 'filter-form'].includes(name) && + formInstance && + !formInstance.readPretty && + !isVariableParsedInOtherContext, }; }; diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useURLSearchParamsVariable.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/useURLSearchParamsVariable.ts index 966e55f687..e0c87a2e35 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useURLSearchParamsVariable.ts +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useURLSearchParamsVariable.ts @@ -16,6 +16,7 @@ import { useLocationSearch } from '../../../application/CustomRouterContextProvi import { useFlag } from '../../../flag-provider/hooks/useFlag'; import { Option } from '../type'; import { getLabelWithTooltip } from './useBaseVariable'; +import { string } from '../../../collection-manager/interfaces/properties/operators'; export const getURLSearchParams = (search: string) => { if (search.startsWith('?')) { @@ -32,6 +33,7 @@ export const getURLSearchParamsChildren = (queryParams: Record): Op value: key, key, isLeaf: true, + operators: string, }; }); }; diff --git a/packages/core/client/src/schema-settings/__e2e__/linkageRules.test.ts b/packages/core/client/src/schema-settings/__e2e__/linkageRules.test.ts index f52d12eba1..ccf229c420 100644 --- a/packages/core/client/src/schema-settings/__e2e__/linkageRules.test.ts +++ b/packages/core/client/src/schema-settings/__e2e__/linkageRules.test.ts @@ -10,6 +10,7 @@ import { expect, test } from '@nocobase/test/e2e'; import { formFieldDependsOnSubtableFieldsWithLinkageRules, + whenARequiredFieldIsSetToHideRetainValueItShouldBeAbleToSubmitTheFormNormally, whenClearingARelationshipFieldTheValueOfTheAssociatedFieldShouldBeCleared, whenSetToHideRetainedValueItShouldNotImpactTheFieldSDefaultValueVariables, } from './template'; @@ -83,6 +84,28 @@ test.describe('linkage rules', () => { ).toBeVisible(); }); + test('When a required field is set to "Hide (retain value)", it should be able to submit the form normally', async ({ + mockPage, + page, + }) => { + await mockPage(whenARequiredFieldIsSetToHideRetainValueItShouldBeAbleToSubmitTheFormNormally).goto(); + + // 1. 输入昵称 + await page + .getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname') + .getByRole('textbox') + .fill('123456'); + + // 2. 点击提交 + await page.getByLabel('action-Action-Submit-submit-').click(); + + // 3. 应该能正常提交,不应该被拦截 + await page.reload(); + await expect( + page.getByLabel('block-item-CardItem-users-table').getByRole('cell', { name: '123456' }), + ).toBeVisible(); + }); + test('When clearing a relationship field, the value of the associated field should be cleared', async ({ page, mockPage, diff --git a/packages/core/client/src/schema-settings/__e2e__/template.ts b/packages/core/client/src/schema-settings/__e2e__/template.ts index af6bfdde87..d540b5ab2f 100644 --- a/packages/core/client/src/schema-settings/__e2e__/template.ts +++ b/packages/core/client/src/schema-settings/__e2e__/template.ts @@ -2215,3 +2215,376 @@ export const accessControlActionWithTable = { 'x-index': 1, }, }; +export const whenARequiredFieldIsSetToHideRetainValueItShouldBeAbleToSubmitTheFormNormally = { + tabSchema: { + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + phefo1qp4yu: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + e94p5oj6num: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + fpwdszedqqt: { + type: 'void', + version: '2.0', + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:createForm', + 'x-component': 'CardItem', + 'x-decorator': 'FormBlockProvider', + 'x-acl-action': 'users:create', + 'x-app-version': '1.6.21', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + }, + 'x-acl-action-props': { + skipScopeCheck: true, + }, + _isJSONSchemaObject: true, + 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', + properties: { + fy0innr5s9v: { + type: 'void', + version: '2.0', + 'x-component': 'FormV2', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + 'x-use-component-props': 'useCreateFormBlockProps', + properties: { + grid: { + type: 'void', + 'x-uid': '7wwn4d0722h', + version: '2.0', + 'x-component': 'Grid', + 'x-app-version': '1.6.21', + 'x-initializer': 'form:configureFields', + 'x-linkage-rules': [ + { + actions: [ + { + operator: 'hidden', + targetFields: ['username'], + }, + ], + condition: { + $and: [], + }, + }, + ], + _isJSONSchemaObject: true, + properties: { + t9ik3qkj7mv: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + szfeenblh7q: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + username: { + type: 'string', + version: '2.0', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-app-version': '1.6.21', + 'x-component-props': {}, + 'x-collection-field': 'users.username', + _isJSONSchemaObject: true, + 'x-uid': 'w7j6pbapyz2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4p4e653i4sg', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ojqn0csar3b', + 'x-async': false, + 'x-index': 1, + }, + ij66m020mn5: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + '95nih4mt2lf': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + nickname: { + type: 'string', + version: '2.0', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-app-version': '1.6.21', + 'x-component-props': {}, + 'x-collection-field': 'users.nickname', + _isJSONSchemaObject: true, + 'x-uid': 'rpmd8swoor7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'h0z7u62fwn3', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'b3v741bdvtt', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + o5pser9a4zy: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-app-version': '1.6.21', + 'x-initializer': 'createForm:configureActions', + 'x-component-props': { + layout: 'one-column', + }, + _isJSONSchemaObject: true, + properties: { + g5aat11143b: { + type: 'void', + title: '{{ t("Submit") }}', + version: '2.0', + 'x-action': 'submit', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:createSubmit', + 'x-component': 'Action', + 'x-app-version': '1.6.21', + 'x-action-settings': {}, + 'x-component-props': { + type: 'primary', + htmlType: 'submit', + }, + _isJSONSchemaObject: true, + 'x-use-component-props': 'useCreateActionProps', + 'x-uid': 'doarbo27x5f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'n5j6q3dfzst', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '1k05k8tv3fp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4bq4fts48ec', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tgjtas03gh6', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'raas2rrgtgq', + 'x-async': false, + 'x-index': 1, + }, + gqnjk39afuj: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + fvn883pa5lt: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + '0wlnuaz623s': { + type: 'void', + version: '2.0', + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-app-version': '1.6.21', + 'x-filter-targets': [], + 'x-decorator-props': { + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + dragSort: false, + showIndex: true, + collection: 'users', + dataSource: 'main', + }, + _isJSONSchemaObject: true, + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-app-version': '1.6.21', + 'x-initializer': 'table:configureActions', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + _isJSONSchemaObject: true, + 'x-uid': 'q7y8z49vkvs', + 'x-async': false, + 'x-index': 1, + }, + e7dmvrnhhhw: { + type: 'array', + version: '2.0', + 'x-component': 'TableV2', + 'x-app-version': '1.6.21', + 'x-initializer': 'table:configureColumns', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + _isJSONSchemaObject: true, + 'x-use-component-props': 'useTableBlockProps', + properties: { + actions: { + type: 'void', + title: '{{ t("Actions") }}', + version: '2.0', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-app-version': '1.6.21', + 'x-initializer': 'table:configureItemActions', + 'x-action-column': 'actions', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + _isJSONSchemaObject: true, + properties: { + wwt3qrk0ro0: { + type: 'void', + version: '2.0', + 'x-component': 'Space', + 'x-decorator': 'DndContext', + 'x-app-version': '1.6.21', + 'x-component-props': { + split: '|', + }, + _isJSONSchemaObject: true, + 'x-uid': '58ymkonaijm', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '46zml84998i', + 'x-async': false, + 'x-index': 1, + }, + d1qruxltt1t: { + type: 'void', + version: '2.0', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + 'x-decorator': 'TableV2.Column.Decorator', + 'x-app-version': '1.6.21', + _isJSONSchemaObject: true, + properties: { + nickname: { + version: '2.0', + 'x-component': 'CollectionField', + 'x-decorator': null, + 'x-app-version': '1.6.21', + 'x-read-pretty': true, + 'x-component-props': { + ellipsis: true, + }, + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-collection-field': 'users.nickname', + _isJSONSchemaObject: true, + 'x-uid': 'ut8m8l3qhzn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'c2592dzb9s1', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'nntvcjy39cg', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'o5gizqch1wr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8awil00a9wo', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'i1dnlv9n2k0', + 'x-async': false, + 'x-index': 2, + }, + }, + name: 'ri6dthungp3', + 'x-uid': 'b2u59skxpsy', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/schema-settings/index.ts b/packages/core/client/src/schema-settings/index.ts index 83b37a62f0..847e02df81 100644 --- a/packages/core/client/src/schema-settings/index.ts +++ b/packages/core/client/src/schema-settings/index.ts @@ -31,3 +31,5 @@ export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFil export * from './isPatternDisabled'; export { SchemaSettingsPlugin } from './SchemaSettingsPlugin'; export * from './VariableInput'; +export { replaceVariables } from './LinkageRules/bindLinkageRulesToFiled'; +export * from './LinkageRules/type'; diff --git a/packages/core/client/src/testUtils/mockAPIClient.ts b/packages/core/client/src/testUtils/mockAPIClient.ts index 5b77afe4ce..2bff3a3e8a 100644 --- a/packages/core/client/src/testUtils/mockAPIClient.ts +++ b/packages/core/client/src/testUtils/mockAPIClient.ts @@ -8,6 +8,7 @@ */ import { APIClient } from '../api-client'; +import { Application } from '../application'; class MockAPIClient extends APIClient { mockAdapter() { @@ -18,6 +19,9 @@ class MockAPIClient extends APIClient { export const mockAPIClient = () => { const apiClient = new MockAPIClient(); + const app = new Application(); + apiClient.app = app; + const mockRequest = apiClient.mockAdapter(); return { apiClient, mockRequest }; }; diff --git a/packages/core/client/src/variables/__tests__/useVariables.test.tsx b/packages/core/client/src/variables/__tests__/useVariables.test.tsx index b6852af739..4d3a839dd7 100644 --- a/packages/core/client/src/variables/__tests__/useVariables.test.tsx +++ b/packages/core/client/src/variables/__tests__/useVariables.test.tsx @@ -129,7 +129,7 @@ mockRequest.onGet('/users/0/belongsToField:get').reply(() => { }, ]; }); -mockRequest.onGet('/users/0/hasManyField:list?pageSize=9999').reply(() => { +mockRequest.onGet('/users/0/hasManyField:list?paginate=false').reply(() => { return [ 200, { @@ -142,7 +142,7 @@ mockRequest.onGet('/users/0/hasManyField:list?pageSize=9999').reply(() => { }, ]; }); -mockRequest.onGet('/users/0/belongsToManyField:list?pageSize=9999').reply(() => { +mockRequest.onGet('/users/0/belongsToManyField:list?paginate=false').reply(() => { return [ 200, { @@ -156,7 +156,7 @@ mockRequest.onGet('/users/0/belongsToManyField:list?pageSize=9999').reply(() => }, ]; }); -mockRequest.onGet('/test/0/hasManyField:list?pageSize=9999').reply(() => { +mockRequest.onGet('/test/0/hasManyField:list?paginate=false').reply(() => { return [ 200, { diff --git a/packages/core/client/src/variables/utils/getAction.tsx b/packages/core/client/src/variables/utils/getAction.tsx index 94dfc985c8..7645fbc963 100644 --- a/packages/core/client/src/variables/utils/getAction.tsx +++ b/packages/core/client/src/variables/utils/getAction.tsx @@ -8,11 +8,11 @@ */ const TYPE_TO_ACTION = { - hasMany: 'list?pageSize=9999', + hasMany: 'list?paginate=false', belongsTo: 'get', hasOne: 'get', - belongsToMany: 'list?pageSize=9999', - belongsToArray: 'get', + belongsToMany: 'list?paginate=false', + belongsToArray: 'list?paginate=false', }; export const getAction = (type: string) => { if (process.env.NODE_ENV !== 'production' && !(type in TYPE_TO_ACTION)) { diff --git a/packages/core/create-nocobase-app/package.json b/packages/core/create-nocobase-app/package.json index 92aacc28b8..4312768373 100755 --- a/packages/core/create-nocobase-app/package.json +++ b/packages/core/create-nocobase-app/package.json @@ -1,6 +1,6 @@ { "name": "create-nocobase-app", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "src/index.js", "license": "AGPL-3.0", "dependencies": { @@ -9,7 +9,7 @@ "chalk": "^4.1.1", "commander": "^9.2.0", "fs-extra": "^11.3.0", - "tar": "6.1.11" + "tar": "^7.4.3" }, "bin": { "create-nocobase-app": "./bin/index.js" diff --git a/packages/core/create-nocobase-app/src/cli.js b/packages/core/create-nocobase-app/src/cli.js index 0fd40eb008..28496f7c6b 100644 --- a/packages/core/create-nocobase-app/src/cli.js +++ b/packages/core/create-nocobase-app/src/cli.js @@ -21,7 +21,7 @@ cli .option('--quickstart', 'quickstart app creation') .option('--skip-dev-dependencies') .option('-a, --all-db-dialect', 'install all database dialect dependencies') - .option('-d, --db-dialect ', 'database dialect, current support sqlite/mysql/postgres', 'sqlite') + .option('-d, --db-dialect [dbDialect]', 'database dialect, current support postgres, mysql, mariadb, kingbase') .option('-e, --env ', 'environment variables write into .env file', concat, []) .description('create a new application') .action(async (name, options) => { diff --git a/packages/core/create-nocobase-app/src/generator.js b/packages/core/create-nocobase-app/src/generator.js index fc7ef0179d..6b8d700825 100644 --- a/packages/core/create-nocobase-app/src/generator.js +++ b/packages/core/create-nocobase-app/src/generator.js @@ -37,21 +37,6 @@ class AppGenerator extends Generator { return items; } - checkDbEnv() { - const dialect = this.args.dbDialect; - const env = this.env; - if (dialect === 'sqlite') { - return; - } - if (!env.DB_DATABASE || !env.DB_USER || !env.DB_PASSWORD) { - console.log( - chalk.red( - `Please set DB_HOST, DB_PORT, DB_DATABASE, DB_USER, DB_PASSWORD in .env file to complete database settings`, - ), - ); - } - } - checkProjectPath() { if (existsSync(this.cwd)) { console.log(chalk.red('Project directory already exists')); @@ -59,44 +44,14 @@ class AppGenerator extends Generator { } } - checkDialect() { - const dialect = this.args.dbDialect; - const supportDialects = ['mysql', 'mariadb', 'sqlite', 'postgres']; - if (!supportDialects.includes(dialect)) { - console.log( - `dialect ${chalk.red(dialect)} is not supported, currently supported dialects are ${chalk.green( - supportDialects.join(','), - )}.`, - ); - process.exit(1); - } - } - getContext() { const env = this.env; const envs = []; const dependencies = []; - const { dbDialect, allDbDialect } = this.args; - - if (allDbDialect) { - dependencies.push(`"mysql2": "^3.11.0"`); - dependencies.push(`"mariadb": "^2.5.6"`); - dependencies.push(`"pg": "^8.7.3"`); - dependencies.push(`"pg-hstore": "^2.3.4"`); - dependencies.push(`"sqlite3": "^5.0.8"`); - } + const { dbDialect } = this.args; switch (dbDialect) { - case 'sqlite': - if (!allDbDialect) { - dependencies.push(`"sqlite3": "^5.0.8"`); - } - envs.push(`DB_STORAGE=${env.DB_STORAGE || 'storage/db/nocobase.sqlite'}`); - break; case 'mysql': - if (!allDbDialect) { - dependencies.push(`"mysql2": "^3.11.0"`); - } envs.push(`DB_HOST=${env.DB_HOST || 'localhost'}`); envs.push(`DB_PORT=${env.DB_PORT || 3306}`); envs.push(`DB_DATABASE=${env.DB_DATABASE || ''}`); @@ -104,9 +59,6 @@ class AppGenerator extends Generator { envs.push(`DB_PASSWORD=${env.DB_PASSWORD || ''}`); break; case 'mariadb': - if (!allDbDialect) { - dependencies.push(`"mariadb": "^2.5.6"`); - } envs.push(`DB_HOST=${env.DB_HOST || 'localhost'}`); envs.push(`DB_PORT=${env.DB_PORT || 3306}`); envs.push(`DB_DATABASE=${env.DB_DATABASE || ''}`); @@ -115,10 +67,6 @@ class AppGenerator extends Generator { break; case 'kingbase': case 'postgres': - if (!allDbDialect) { - dependencies.push(`"pg": "^8.7.3"`); - dependencies.push(`"pg-hstore": "^2.3.4"`); - } envs.push(`DB_HOST=${env.DB_HOST || 'localhost'}`); envs.push(`DB_PORT=${env.DB_PORT || 5432}`); envs.push(`DB_DATABASE=${env.DB_DATABASE || ''}`); @@ -177,28 +125,33 @@ class AppGenerator extends Generator { async writing() { this.checkProjectPath(); - this.checkDialect(); const { name } = this.context; console.log(`Creating a new NocoBase application at ${chalk.green(name)}`); console.log('Creating files'); + const context = this.getContext(); + this.copyDirectory({ - context: this.getContext(), + context, path: join(__dirname, '../templates/app'), target: this.cwd, }); - this.checkDbEnv(); + const json = { + name: context.name, + ...(await fs.readJSON(join(this.cwd, 'package.json'), 'utf8')), + }; - const skipDevDependencies = this.args.skipDevDependencies; - if (skipDevDependencies) { - const json = await fs.readJSON(join(this.cwd, 'package.json'), 'utf8'); - delete json['devDependencies']; - await fs.writeJSON(join(this.cwd, 'package.json'), json, { encoding: 'utf8', spaces: 2 }); + json['dependencies']['@nocobase/cli'] = context.version; + + if (!this.args.skipDevDependencies) { + json['devDependencies'] = json['devDependencies'] || {}; + json['devDependencies']['@nocobase/devtools'] = context.version; } + await fs.writeJSON(join(this.cwd, 'package.json'), json, { encoding: 'utf8', spaces: 2 }); console.log(''); console.log(chalk.green(`$ cd ${name}`)); console.log(chalk.green(`$ yarn install`)); diff --git a/packages/core/create-nocobase-app/templates/app/package.json.tpl b/packages/core/create-nocobase-app/templates/app/package.json similarity index 78% rename from packages/core/create-nocobase-app/templates/app/package.json.tpl rename to packages/core/create-nocobase-app/templates/app/package.json index 6245ac2fd6..7613d58f4e 100644 --- a/packages/core/create-nocobase-app/templates/app/package.json.tpl +++ b/packages/core/create-nocobase-app/templates/app/package.json @@ -1,5 +1,4 @@ { - "name": "{{{name}}}", "private": true, "workspaces": [ "packages/*/*", @@ -29,14 +28,15 @@ "react-router-dom": "6.28.1", "react-router": "6.28.1", "antd": "5.24.2", - "async": "3.2.6", - "rollup": "4.24.0" + "async": "^3.2.6", + "rollup": "4.24.0", + "semver": "^7.7.1" }, "dependencies": { - "@nocobase/cli": "{{{version}}}", - {{{dependencies}}} - }, - "devDependencies": { - "@nocobase/devtools": "{{{version}}}" + "pm2": "^6.0.5", + "mysql2": "^3.14.0", + "mariadb": "^2.5.6", + "pg": "^8.14.1", + "pg-hstore": "^2.3.4" } -} +} \ No newline at end of file diff --git a/packages/core/data-source-manager/package.json b/packages/core/data-source-manager/package.json index a3cd0531b0..2894b08654 100644 --- a/packages/core/data-source-manager/package.json +++ b/packages/core/data-source-manager/package.json @@ -1,18 +1,18 @@ { "name": "@nocobase/data-source-manager", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/actions": "1.7.0-beta.18", - "@nocobase/cache": "1.7.0-beta.18", - "@nocobase/database": "1.7.0-beta.18", - "@nocobase/resourcer": "1.7.0-beta.18", - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/actions": "1.7.0-beta.26", + "@nocobase/cache": "1.7.0-beta.26", + "@nocobase/database": "1.7.0-beta.26", + "@nocobase/resourcer": "1.7.0-beta.26", + "@nocobase/utils": "1.7.0-beta.26", "@types/jsonwebtoken": "^8.5.8", - "jsonwebtoken": "^8.5.1" + "jsonwebtoken": "^9.0.2" }, "repository": { "type": "git", diff --git a/packages/core/database/package.json b/packages/core/database/package.json index 1ce49e8c91..aa1274a590 100644 --- a/packages/core/database/package.json +++ b/packages/core/database/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/database", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "main": "./lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", "dependencies": { - "@nocobase/logger": "1.7.0-beta.18", - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/logger": "1.7.0-beta.26", + "@nocobase/utils": "1.7.0-beta.26", "async-mutex": "^0.3.2", "chalk": "^4.1.1", "cron-parser": "4.4.0", diff --git a/packages/core/database/src/__tests__/fields/string-field.test.ts b/packages/core/database/src/__tests__/fields/string-field.test.ts index ff095bc862..88898d7553 100644 --- a/packages/core/database/src/__tests__/fields/string-field.test.ts +++ b/packages/core/database/src/__tests__/fields/string-field.test.ts @@ -119,4 +119,32 @@ describe('string field', () => { name: 'n1', }); }); + + it('trim when value is null should be null', async () => { + const collection = db.collection({ + name: 'tests', + fields: [{ type: 'string', name: 'name', trim: true }], + }); + await db.sync(); + const model = await collection.model.create({ + name: null, + }); + expect(model.toJSON()).toMatchObject({ + name: null, + }); + }); + + it('when value is number should be convert to string', async () => { + const collection = db.collection({ + name: 'tests', + fields: [{ type: 'string', name: 'name', trim: true }], + }); + await db.sync(); + const model = await collection.model.create({ + name: 123, + }); + expect(model.toJSON()).toMatchObject({ + name: '123', + }); + }); }); diff --git a/packages/core/database/src/__tests__/fields/text-field.test.ts b/packages/core/database/src/__tests__/fields/text-field.test.ts index 762269cb7f..1225367b9b 100644 --- a/packages/core/database/src/__tests__/fields/text-field.test.ts +++ b/packages/core/database/src/__tests__/fields/text-field.test.ts @@ -66,4 +66,32 @@ describe('text field', () => { name: 'n1', }); }); + + it('trim when value is null should be null', async () => { + const collection = db.collection({ + name: 'tests', + fields: [{ type: 'string', name: 'name', trim: true }], + }); + await db.sync(); + const model = await collection.model.create({ + name: null, + }); + expect(model.toJSON()).toMatchObject({ + name: null, + }); + }); + + it('when value is number should be convert to string', async () => { + const collection = db.collection({ + name: 'tests', + fields: [{ type: 'string', name: 'name', trim: true }], + }); + await db.sync(); + const model = await collection.model.create({ + name: 123, + }); + expect(model.toJSON()).toMatchObject({ + name: '123', + }); + }); }); diff --git a/packages/core/database/src/fields/string-field.ts b/packages/core/database/src/fields/string-field.ts index 627768beeb..1b0719af8f 100644 --- a/packages/core/database/src/fields/string-field.ts +++ b/packages/core/database/src/fields/string-field.ts @@ -24,7 +24,13 @@ export class StringField extends Field { return { set(value) { - this.setDataValue(name, trim ? value?.trim() : value); + if (value == null) { + return value; + } + if (typeof value !== 'string') { + value = value.toString(); + } + this.setDataValue(name, trim ? value.trim() : value); }, }; } diff --git a/packages/core/database/src/fields/text-field.ts b/packages/core/database/src/fields/text-field.ts index 39f1ef4670..4b045db75e 100644 --- a/packages/core/database/src/fields/text-field.ts +++ b/packages/core/database/src/fields/text-field.ts @@ -29,7 +29,13 @@ export class TextField extends Field { return { set(value) { - this.setDataValue(name, trim ? value?.trim() : value); + if (value == null) { + return value; + } + if (typeof value !== 'string') { + value = value.toString(); + } + this.setDataValue(name, trim ? value.trim() : value); }, }; } diff --git a/packages/core/database/src/interfaces/index.ts b/packages/core/database/src/interfaces/index.ts index 038ada52cc..aebd38d8d1 100644 --- a/packages/core/database/src/interfaces/index.ts +++ b/packages/core/database/src/interfaces/index.ts @@ -15,3 +15,4 @@ export * from './datetime-interface'; export * from './datetime-no-tz-interface'; export * from './boolean-interface'; export * from './date-interface'; +export * from './time-interface'; diff --git a/packages/core/database/src/interfaces/time-interface.ts b/packages/core/database/src/interfaces/time-interface.ts new file mode 100644 index 0000000000..6b14579b30 --- /dev/null +++ b/packages/core/database/src/interfaces/time-interface.ts @@ -0,0 +1,27 @@ +/** + * 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 dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import { BaseInterface } from './base-interface'; +dayjs.extend(utc); +export class TimeInterface extends BaseInterface { + toValue(value: any, ctx?: any) { + if (this.validate(value)) { + const result = dayjs.utc(value).format('HH:mm:ss'); + return result; + } + return value; + } + + validate(value) { + const result = dayjs(value).isValid(); + return result; + } +} diff --git a/packages/core/database/src/interfaces/utils.ts b/packages/core/database/src/interfaces/utils.ts index 170687f6e5..cc10790ad0 100644 --- a/packages/core/database/src/interfaces/utils.ts +++ b/packages/core/database/src/interfaces/utils.ts @@ -16,6 +16,7 @@ import { MultipleSelectInterface, PercentInterface, SelectInterface, + TimeInterface, } from './index'; import { ManyToOneInterface } from './many-to-one-interface'; import { ManyToManyInterface } from './many-to-many-interface'; @@ -50,6 +51,7 @@ const interfaces = { o2m: OneToManyInterface, m2o: ManyToOneInterface, m2m: ManyToManyInterface, + time: TimeInterface, }; export function registerInterfaces(db: Database) { diff --git a/packages/core/database/src/view-collection.ts b/packages/core/database/src/view-collection.ts index 1654423f66..4901d2d9ab 100644 --- a/packages/core/database/src/view-collection.ts +++ b/packages/core/database/src/view-collection.ts @@ -26,7 +26,7 @@ export class ViewCollection extends Collection { return []; } - return ['create', 'update', 'destroy']; + return ['create', 'update', 'destroy', 'importXlsx', 'destroyMany', 'updateMany']; } protected sequelizeModelOptions(): any { diff --git a/packages/core/devtools/package.json b/packages/core/devtools/package.json index 9bfee29a37..a90a804dee 100644 --- a/packages/core/devtools/package.json +++ b/packages/core/devtools/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/devtools", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "license": "AGPL-3.0", "main": "./src/index.js", "dependencies": { - "@nocobase/build": "1.7.0-beta.18", - "@nocobase/client": "1.7.0-beta.18", - "@nocobase/test": "1.7.0-beta.18", + "@nocobase/build": "1.7.0-beta.26", + "@nocobase/client": "1.7.0-beta.26", + "@nocobase/test": "1.7.0-beta.26", "@types/koa": "^2.15.0", "@types/koa-bodyparser": "^4.3.4", "@types/lodash": "^4.14.177", diff --git a/packages/core/evaluators/package.json b/packages/core/evaluators/package.json index 2312b20723..d34fd2c9c1 100644 --- a/packages/core/evaluators/package.json +++ b/packages/core/evaluators/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/evaluators", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "main": "./lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", "dependencies": { "@formulajs/formulajs": "4.4.9", - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/utils": "1.7.0-beta.26", "mathjs": "^10.6.0" }, "repository": { diff --git a/packages/core/lock-manager/package.json b/packages/core/lock-manager/package.json index d844a95dc1..3077f37f03 100644 --- a/packages/core/lock-manager/package.json +++ b/packages/core/lock-manager/package.json @@ -1,10 +1,10 @@ { "name": "@nocobase/lock-manager", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "lib/index.js", "license": "AGPL-3.0", "devDependencies": { - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/utils": "1.7.0-beta.26", "async-mutex": "^0.5.0" } } diff --git a/packages/core/logger/package.json b/packages/core/logger/package.json index ec387f5a49..a0198f4bb6 100644 --- a/packages/core/logger/package.json +++ b/packages/core/logger/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/logger", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "nocobase logging library", "license": "AGPL-3.0", "main": "./lib/index.js", diff --git a/packages/core/resourcer/package.json b/packages/core/resourcer/package.json index 41c7c03676..789dd59562 100644 --- a/packages/core/resourcer/package.json +++ b/packages/core/resourcer/package.json @@ -1,16 +1,16 @@ { "name": "@nocobase/resourcer", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "", "main": "./lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", "dependencies": { - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/utils": "1.7.0-beta.26", "deepmerge": "^4.2.2", "koa-compose": "^4.1.0", "lodash": "^4.17.21", - "path-to-regexp": "6.2.2", + "path-to-regexp": "^6.3.0", "qs": "^6.9.4" }, "repository": { diff --git a/packages/core/resourcer/src/resourcer.ts b/packages/core/resourcer/src/resourcer.ts index 6d750e4d20..53ed79a53c 100644 --- a/packages/core/resourcer/src/resourcer.ts +++ b/packages/core/resourcer/src/resourcer.ts @@ -367,7 +367,7 @@ export class ResourceManager { : params.resourceName; ctx.action.params.filterByTk = params.resourceIndex; const query = parseQuery(ctx.request.querystring); - if (pathToRegexp('/resourcer/{:associatedName.}?:resourceName{\\::actionName}').test(ctx.request.path)) { + if (pathToRegexp('/resourcer/:rest(.*)').test(ctx.request.path)) { ctx.action.mergeParams({ ...query, ...params, diff --git a/packages/core/resourcer/src/utils.ts b/packages/core/resourcer/src/utils.ts index 3ef70517e6..b8b9780e4d 100644 --- a/packages/core/resourcer/src/utils.ts +++ b/packages/core/resourcer/src/utils.ts @@ -67,17 +67,25 @@ export function parseRequest(request: ParseRequest, options: ParseOptions = {}): ...(options.accessors || {}), }; const keys = []; - const regexp = pathToRegexp('/resourcer/{:associatedName.}?:resourceName{\\::actionName}', keys); + + const regexp = pathToRegexp('/resourcer/:rest(.*)', keys); const reqPath = decodeURI(request.path); const matches = regexp.exec(reqPath); if (matches) { const params = {}; - keys.forEach((obj, index) => { - if (matches[index + 1] === undefined) { - return; + const [resource, action] = matches[1].split(':'); + const [res1, res2] = resource.split('.'); + if (res1) { + if (res2) { + params['associatedName'] = res1; + params['resourceName'] = res2; + } else { + params['resourceName'] = res1; } - params[obj.name] = matches[index + 1]; - }); + } + if (action) { + params['actionName'] = action; + } return params; } const defaults = { diff --git a/packages/core/sdk/package.json b/packages/core/sdk/package.json index f9954ed165..aad88d948c 100644 --- a/packages/core/sdk/package.json +++ b/packages/core/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/sdk", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/core/server/package.json b/packages/core/server/package.json index de319f6dff..baec2f35ed 100644 --- a/packages/core/server/package.json +++ b/packages/core/server/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/server", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", @@ -8,25 +8,25 @@ "@formily/json-schema": "2.x", "@hapi/topo": "^6.0.0", "@koa/cors": "^5.0.0", - "@koa/multer": "^3.0.2", - "@koa/router": "^9.4.0", - "@nocobase/acl": "1.7.0-beta.18", - "@nocobase/actions": "1.7.0-beta.18", - "@nocobase/auth": "1.7.0-beta.18", - "@nocobase/cache": "1.7.0-beta.18", - "@nocobase/data-source-manager": "1.7.0-beta.18", - "@nocobase/database": "1.7.0-beta.18", - "@nocobase/evaluators": "1.7.0-beta.18", - "@nocobase/lock-manager": "1.7.0-beta.18", - "@nocobase/logger": "1.7.0-beta.18", - "@nocobase/resourcer": "1.7.0-beta.18", - "@nocobase/sdk": "1.7.0-beta.18", - "@nocobase/telemetry": "1.7.0-beta.18", - "@nocobase/utils": "1.7.0-beta.18", + "@koa/multer": "^3.1.0", + "@koa/router": "^13.1.0", + "@nocobase/acl": "1.7.0-beta.26", + "@nocobase/actions": "1.7.0-beta.26", + "@nocobase/auth": "1.7.0-beta.26", + "@nocobase/cache": "1.7.0-beta.26", + "@nocobase/data-source-manager": "1.7.0-beta.26", + "@nocobase/database": "1.7.0-beta.26", + "@nocobase/evaluators": "1.7.0-beta.26", + "@nocobase/lock-manager": "1.7.0-beta.26", + "@nocobase/logger": "1.7.0-beta.26", + "@nocobase/resourcer": "1.7.0-beta.26", + "@nocobase/sdk": "1.7.0-beta.26", + "@nocobase/telemetry": "1.7.0-beta.26", + "@nocobase/utils": "1.7.0-beta.26", "@types/decompress": "4.2.7", "@types/ini": "^1.3.31", "@types/koa-send": "^4.1.3", - "@types/multer": "^1.4.5", + "@types/multer": "^1.4.12", "async-mutex": "^0.5.0", "axios": "^1.7.0", "chalk": "^4.1.1", @@ -45,7 +45,7 @@ "koa-send": "^5.0.1", "koa-static": "^5.0.0", "lodash": "^4.17.21", - "multer": "^1.4.2", + "multer": "^1.4.5-lts.2", "nanoid": "^3.3.11", "semver": "^7.7.1", "serve-handler": "^6.1.6", diff --git a/packages/core/server/src/commands/create-migration.ts b/packages/core/server/src/commands/create-migration.ts index 4b76620f63..e09a488e6f 100644 --- a/packages/core/server/src/commands/create-migration.ts +++ b/packages/core/server/src/commands/create-migration.ts @@ -29,14 +29,13 @@ export default (app: Application) => { 'migrations', `${dayjs().format('YYYYMMDDHHmmss')}-${name}.ts`, ); - const version = app.getVersion(); - // 匹配主版本号、次版本号、小版本号和后缀的正则表达式 + const version = app.getPackageVersion(); const regex = /(\d+)\.(\d+)\.(\d+)(-[\w.]+)?/; const nextVersion = version.replace(regex, (match, major, minor, patch, suffix) => { - // 将小版本号转换为整数并加1 - const newPatch = parseInt(patch) + 1; - // 返回新的版本号 - return `${major}.${minor}.${newPatch}${suffix || ''}`; + if (version.includes('beta') || version.includes('alpha')) { + return `${major}.${minor}.${patch}`; + } + return `${major}.${1 + 1 * minor}.0`; }); const from = pkg === '@nocobase/server' ? `../migration` : '@nocobase/server'; const data = `import { Migration } from '${from}'; diff --git a/packages/core/server/src/plugin-manager/deps.ts b/packages/core/server/src/plugin-manager/deps.ts index ba3365bb1b..f6565d65d6 100644 --- a/packages/core/server/src/plugin-manager/deps.ts +++ b/packages/core/server/src/plugin-manager/deps.ts @@ -15,7 +15,7 @@ const deps: Record = { '@formily': '2.x', '@formily/antd-v5': '1.x', - jsonwebtoken: '8.x', + jsonwebtoken: '9.x', 'cache-manager': '5.x', sequelize: '6.x', umzug: '3.x', @@ -26,7 +26,7 @@ const deps: Record = { 'winston-daily-rotate-file': '4.x', koa: '2.x', '@koa/cors': '5.x', - '@koa/router': '9.x', + '@koa/router': '13.x', multer: '1.x', '@koa/multer': '3.x', 'koa-bodyparser': '4.x', diff --git a/packages/core/telemetry/package.json b/packages/core/telemetry/package.json index d5a6e826b1..0dbb844f7c 100644 --- a/packages/core/telemetry/package.json +++ b/packages/core/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/telemetry", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "description": "nocobase telemetry library", "license": "AGPL-3.0", "main": "./lib/index.js", @@ -11,7 +11,7 @@ "directory": "packages/telemetry" }, "dependencies": { - "@nocobase/utils": "1.7.0-beta.18", + "@nocobase/utils": "1.7.0-beta.26", "@opentelemetry/api": "^1.7.0", "@opentelemetry/instrumentation": "^0.46.0", "@opentelemetry/resources": "^1.19.0", diff --git a/packages/core/test/package.json b/packages/core/test/package.json index 0cddbe03aa..b9b9cfa7f8 100644 --- a/packages/core/test/package.json +++ b/packages/core/test/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/test", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "lib/index.js", "module": "./src/index.ts", "types": "./lib/index.d.ts", @@ -51,7 +51,7 @@ }, "dependencies": { "@faker-js/faker": "8.1.0", - "@nocobase/server": "1.7.0-beta.18", + "@nocobase/server": "1.7.0-beta.26", "@playwright/test": "^1.45.3", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.0.0", @@ -69,7 +69,6 @@ "mysql2": "^3.11.0", "pg": "^8.7.3", "pg-hstore": "^2.3.4", - "sqlite3": "^5.0.8", "supertest": "^6.1.6", "vite": "^5.0.0", "vitest": "^1.5.0", diff --git a/packages/core/test/src/client/utils/utils.ts b/packages/core/test/src/client/utils/utils.ts index 67fe5c5a11..e956a8a70d 100644 --- a/packages/core/test/src/client/utils/utils.ts +++ b/packages/core/test/src/client/utils/utils.ts @@ -20,7 +20,7 @@ export const WaitApp = async () => { const loadError = screen.queryByText('App Error'); if (loadError) { - expectNoTsError(screen.queryByText('App Error')).not.toBeInTheDocument(); + // expectNoTsError(screen.queryByText('App Error')).not.toBeInTheDocument(); } const renderError = screen.queryByText('Render Failed'); diff --git a/packages/core/test/src/e2e/e2eUtils.ts b/packages/core/test/src/e2e/e2eUtils.ts index 05372aeee2..39dd40af23 100644 --- a/packages/core/test/src/e2e/e2eUtils.ts +++ b/packages/core/test/src/e2e/e2eUtils.ts @@ -31,6 +31,21 @@ function getPageMenuSchema({ pageSchemaUid, tabSchemaUid, tabSchemaName }) { }; } +function getPageMenuSchemaWithTabSchema({ tabSchema }) { + if (!tabSchema) { + return null; + } + + return { + type: 'void', + 'x-component': 'Page', + properties: { + [tabSchema.name]: tabSchema, + }, + 'x-uid': uid(), + }; +} + export * from '@playwright/test'; export { defineConfig }; @@ -193,10 +208,17 @@ export interface PageConfig { */ collections?: CollectionSetting[]; /** + * @deprecate 在菜单被重构之后,没有办法直接复制完整的页面 Schema 了。所以这个选项不推荐使用了。 + * 推荐使用 tabSchema,复制一个页面 tab 的 Schema 传给 tabSchema。 + * * 页面整体的 Schema * @default undefined */ pageSchema?: any; + /** + * 页面 Tab 的 Schema。当 pageSchema 和 tabSchema 都存在时,最终显示的会是 tabSchema 的内容 + */ + tabSchema?: any; /** 如果为 true 则表示不会更改 PageSchema 的 uid */ keepUid?: boolean; /** 在 URL 中的 uid,例如:/admin/0ig6xhe03u2 */ @@ -217,6 +239,7 @@ interface CreatePageOptions { url?: PageConfig['url']; name?: string; pageSchema?: any; + tabSchema?: any; /** 如果为 true 则表示不会更改 PageSchema 的 uid */ keepUid?: boolean; /** 在 URL 中的 uid,例如:/admin/0ig6xhe03u2 */ @@ -367,6 +390,7 @@ export class NocoPage { type: this.options?.type, name: this.options?.name, pageSchema: this.options?.pageSchema, + tabSchema: this.options?.tabSchema, url: this.options?.url, keepUid: this.options?.keepUid, pageUid: this.options?.pageUid, @@ -737,13 +761,16 @@ const updateUidOfPageSchema = (uiSchema: any) => { * 在 NocoBase 中创建一个页面 */ const createPage = async (options?: CreatePageOptions) => { - const { type = 'page', url, name, pageSchema, keepUid, pageUid: pageUidFromOptions } = options || {}; + const { type = 'page', url, name, pageSchema, tabSchema, keepUid, pageUid: pageUidFromOptions } = options || {}; const api = await request.newContext({ storageState: process.env.PLAYWRIGHT_AUTH_FILE, }); + + const schema = getPageMenuSchemaWithTabSchema({ tabSchema }) || pageSchema; + const state = await api.storageState(); const headers = getHeaders(state); - const newPageSchema = keepUid ? pageSchema : updateUidOfPageSchema(pageSchema); + const newPageSchema = keepUid ? schema : updateUidOfPageSchema(schema); const pageSchemaUid = newPageSchema?.['x-uid'] || uid(); const newTabSchemaUid = uid(); const newTabSchemaName = uid(); diff --git a/packages/core/test/src/scripts/test-db-creator.ts b/packages/core/test/src/scripts/test-db-creator.ts index e496ec1857..2d9bcb0998 100644 --- a/packages/core/test/src/scripts/test-db-creator.ts +++ b/packages/core/test/src/scripts/test-db-creator.ts @@ -7,13 +7,13 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import http from 'http'; -import url from 'url'; -import pg from 'pg'; import dotenv from 'dotenv'; -import path from 'path'; -import mysql from 'mysql2/promise'; +import http from 'http'; import mariadb from 'mariadb'; +import mysql from 'mysql2/promise'; +import path from 'path'; +import pg from 'pg'; +import url from 'url'; dotenv.config({ path: path.resolve(process.cwd(), '.env.test') }); diff --git a/packages/core/utils/package.json b/packages/core/utils/package.json index 26b9e7c2ad..3451b7d02e 100644 --- a/packages/core/utils/package.json +++ b/packages/core/utils/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/utils", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "lib/index.js", "types": "./lib/index.d.ts", "license": "AGPL-3.0", @@ -13,7 +13,7 @@ "flat-to-nested": "^1.1.1", "graphlib": "^2.1.8", "handlebars": "^4.7.8", - "multer": "^1.4.5-lts.1", + "multer": "^1.4.5-lts.2", "object-path": "^0.11.8" }, "gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1" diff --git a/packages/core/utils/src/__tests__/common.test.ts b/packages/core/utils/src/__tests__/common.test.ts index 1768547f79..6bcc3da6a4 100644 --- a/packages/core/utils/src/__tests__/common.test.ts +++ b/packages/core/utils/src/__tests__/common.test.ts @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { hasEmptyValue } from '../client'; +import { hasEmptyValue, sortTree } from '../client'; describe('hasEmptyValue', () => { it('should return false when there is no empty value', () => { @@ -80,3 +80,147 @@ describe('hasEmptyValue', () => { expect(hasEmptyValue(obj)).toBe(true); }); }); + +describe('sortTree', () => { + it('should return the original tree when tree is empty', () => { + expect(sortTree(null, 'order')).toBeNull(); + expect(sortTree([], 'order')).toEqual([]); + }); + + it('should sort tree nodes by a specific field in ascending order', () => { + const tree = [ + { id: 3, name: 'C', children: [] }, + { id: 1, name: 'A', children: [] }, + { id: 2, name: 'B', children: [] }, + ]; + + const result = sortTree(tree, 'id'); + + expect(result).toEqual([ + { id: 1, name: 'A', children: [] }, + { id: 2, name: 'B', children: [] }, + { id: 3, name: 'C', children: [] }, + ]); + }); + + it('should sort tree nodes by a specific field in descending order', () => { + const tree = [ + { id: 1, name: 'A', children: [] }, + { id: 3, name: 'C', children: [] }, + { id: 2, name: 'B', children: [] }, + ]; + + const result = sortTree(tree, 'id', 'children', false); + + expect(result).toEqual([ + { id: 3, name: 'C', children: [] }, + { id: 2, name: 'B', children: [] }, + { id: 1, name: 'A', children: [] }, + ]); + }); + + it('should sort tree nodes with nested children', () => { + const tree = [ + { + id: 3, + name: 'C', + items: [ + { id: 2, name: 'C-2' }, + { id: 1, name: 'C-1' }, + ], + }, + { id: 1, name: 'A', items: [] }, + { + id: 2, + name: 'B', + items: [ + { id: 3, name: 'B-3' }, + { id: 1, name: 'B-1' }, + ], + }, + ]; + + const result = sortTree(tree, 'id', 'items'); + + expect(result).toEqual([ + { id: 1, name: 'A', items: [] }, + { + id: 2, + name: 'B', + items: [ + { id: 1, name: 'B-1' }, + { id: 3, name: 'B-3' }, + ], + }, + { + id: 3, + name: 'C', + items: [ + { id: 1, name: 'C-1' }, + { id: 2, name: 'C-2' }, + ], + }, + ]); + }); + + it('should support sorting by function', () => { + const tree = [ + { id: 3, name: 'C', children: [] }, + { id: 1, name: 'A', children: [] }, + { id: 2, name: 'B', children: [] }, + ]; + + const sortByName = (node) => node.name; + const result = sortTree(tree, sortByName); + + expect(result).toEqual([ + { id: 1, name: 'A', children: [] }, + { id: 2, name: 'B', children: [] }, + { id: 3, name: 'C', children: [] }, + ]); + }); + + it('should handle complex nested structures', () => { + const tree = [ + { + id: 2, + name: 'B', + children: [ + { + id: 3, + name: 'B-3', + children: [ + { id: 2, name: 'B-3-2' }, + { id: 1, name: 'B-3-1' }, + ], + }, + { id: 1, name: 'B-1', children: [] }, + ], + }, + { id: 3, name: 'C', children: [] }, + { id: 1, name: 'A', children: [] }, + ]; + + const result = sortTree(tree, 'id'); + + expect(result).toEqual([ + { id: 1, name: 'A', children: [] }, + { + id: 2, + name: 'B', + children: [ + { id: 1, name: 'B-1', children: [] }, + { + id: 3, + name: 'B-3', + children: [ + { id: 1, name: 'B-3-1' }, + { id: 2, name: 'B-3-2' }, + ], + }, + ], + }, + { id: 3, name: 'C', children: [] }, + ]); + }); +}); diff --git a/packages/core/utils/src/common.ts b/packages/core/utils/src/common.ts index c4894af961..374b4d1cde 100644 --- a/packages/core/utils/src/common.ts +++ b/packages/core/utils/src/common.ts @@ -7,6 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import _ from 'lodash'; + export const isString = (value: any): value is string => { return typeof value === 'string'; }; @@ -54,3 +56,75 @@ export const hasEmptyValue = (objOrArr: object | any[]) => { export const nextTick = (fn: () => void) => { setTimeout(fn); }; + +/** + * Generic tree node depth-first traversal function + * @param {Object|Array} tree - The tree structure to traverse + * @param {Function} callback - The callback function executed for each node, stops traversing and returns the current node when a truthy value is returned + * @param {Object} options - Configuration options + * @param {string|Function} options.childrenKey - The property name of child nodes, defaults to 'children', can also be a function + * @returns {any|undefined} - The found node or undefined + */ +export function treeFind( + tree: T | T[], + callback: (node: T) => boolean, + options: { + childrenKey?: string | ((node: T) => T[] | undefined); + } = {}, +): T | undefined { + if (!tree) return undefined; + + const { childrenKey = 'children' } = options; + + // Handle case where the root node is an array + const nodes = Array.isArray(tree) ? [...tree] : [tree]; + + // Depth-first search + for (const node of nodes) { + // Call callback function on the current node + if (callback(node)) { + return node; + } + + // Get child nodes + const children = typeof childrenKey === 'function' ? childrenKey(node) : (node as any)[childrenKey]; + + // Recursively process child nodes + if (Array.isArray(children) && children.length > 0) { + const found = treeFind(children, callback, options); + if (found !== undefined) { + return found; + } + } + } + + return undefined; +} + +/** + * Sort a tree structure + * @param {Array} tree - Tree structure array + * @param {string|Function} sortBy - Sort field or sort function + * @param {string} childrenKey - The key name of child nodes, defaults to 'children' + * @param {boolean} isAsc - Whether to sort in ascending order, defaults to true + * @returns {Array} - The sorted tree structure + */ +export function sortTree(tree: any[], sortBy: string | Function, childrenKey = 'children', isAsc = true) { + if (!tree || !Array.isArray(tree) || tree.length === 0) { + return tree; + } + + // Sort nodes at the current level + const sortedTree = _.orderBy(tree, sortBy, isAsc ? 'asc' : 'desc'); + + // Recursively sort child nodes + return sortedTree.map((node) => { + if (node[childrenKey] && node[childrenKey].length > 0) { + return { + ...node, + [childrenKey]: sortTree(node[childrenKey], sortBy, childrenKey, isAsc), + }; + } + return node; + }); +} diff --git a/packages/core/utils/src/date.ts b/packages/core/utils/src/date.ts index 2153660a72..1baf179e1f 100644 --- a/packages/core/utils/src/date.ts +++ b/packages/core/utils/src/date.ts @@ -69,12 +69,13 @@ export const toLocal = (value: dayjs.Dayjs) => { }; const convertQuarterToFirstDay = (quarterStr) => { - if (dayjs(quarterStr).isValid()) { + try { const year = parseInt(quarterStr.slice(0, 4)); // 提取年份 const quarter = parseInt(quarterStr.slice(-1)); // 提取季度数字 return dayjs().quarter(quarter).year(year); + } catch (error) { + return null; } - return null; }; const toMoment = (val: any, options?: Str2momentOptions) => { @@ -235,3 +236,13 @@ export const getDateTimeFormat = (picker, format, showTime, timeFormat) => { } return format; }; + +export function getFormatFromDateStr(dateStr: string): string | null { + if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateStr)) return 'YYYY-MM-DD HH:mm:ss'; + if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) return 'YYYY-MM-DD'; + if (/^\d{4}-\d{2}$/.test(dateStr)) return 'YYYY-MM'; + if (/^\d{4}$/.test(dateStr)) return 'YYYY'; + if (/^\d{4}Q[1-4]$/.test(dateStr)) return 'YYYY[Q]Q'; + if (/^\d{4}-\d{2}-\d{2}T/.test(dateStr)) return 'YYYY-MM-DDTHH:mm:ss.SSSZ'; + return null; +} diff --git a/packages/plugins/@nocobase/plugin-acl/package.json b/packages/plugins/@nocobase/plugin-acl/package.json index b7e3d3da26..3280f7d826 100644 --- a/packages/plugins/@nocobase/plugin-acl/package.json +++ b/packages/plugins/@nocobase/plugin-acl/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "权限控制", "description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.", "description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/acl", @@ -13,8 +13,8 @@ "Users & permissions" ], "devDependencies": { - "@types/jsonwebtoken": "^8.5.8", - "jsonwebtoken": "^8.5.1", + "@types/jsonwebtoken": "^9.0.9", + "jsonwebtoken": "^9.0.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/plugins/@nocobase/plugin-acl/src/client/schemas/scopes.ts b/packages/plugins/@nocobase/plugin-acl/src/client/schemas/scopes.ts index e4195906c7..36f081596c 100644 --- a/packages/plugins/@nocobase/plugin-acl/src/client/schemas/scopes.ts +++ b/packages/plugins/@nocobase/plugin-acl/src/client/schemas/scopes.ts @@ -234,7 +234,6 @@ export const scopesSchema: ISchema = { 'x-component': 'Action.Link', 'x-component-props': { openMode: 'drawer', - icon: 'EditOutlined', }, properties: { drawer: { diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json index 2382df243e..c9859da976 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-bulk-edit", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-bulk-edit", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit", diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx index b126f527e8..7b9a1d2f3a 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx @@ -126,6 +126,16 @@ export const deprecatedBulkEditActionSettings = new SchemaSettings({ name: 'updateMode', Component: UpdateMode, }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + }; + }, + }, { name: 'remove', sort: 100, diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormBlockSettings.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormBlockSettings.tsx index 8add5a1911..8b3fad699e 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormBlockSettings.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormBlockSettings.tsx @@ -8,18 +8,17 @@ */ import { useFieldSchema } from '@formily/react'; +import { useTranslation } from 'react-i18next'; import { SchemaSettings, useBlockTemplateContext, SchemaSettingsLayoutItem, - SchemaSettingsDataTemplates, - useFormBlockContext, SchemaSettingsFormItemTemplate, useCollection, - useCollection_deprecated, SchemaSettingsBlockHeightItem, SchemaSettingsBlockTitleItem, SchemaSettingsLinkageRules, + LinkageRuleCategory, } from '@nocobase/client'; export const bulkEditFormBlockSettings = new SchemaSettings({ @@ -34,12 +33,23 @@ export const bulkEditFormBlockSettings = 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 { t } = useTranslation(); + return { + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, }; }, }, diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/package.json b/packages/plugins/@nocobase/plugin-action-bulk-update/package.json index 4192615bbb..6dea54b172 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-update/package.json +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-bulk-update", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-bulk-update", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update", diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx index 0d5b099677..6bdc3c1623 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx @@ -78,7 +78,7 @@ function AfterSuccess() { return ( { + const { params } = useCurrentPopupContext(); + const recordData = useCollectionRecordData(); + const tableBlockContextBasicValue = useTableBlockContext(); + if (recordData) { + return false; + } + + const popupTableBlockContext = getStoredPopupContext(params?.popupuid)?.tableBlockContext; + return !isEmpty(popupTableBlockContext) || !isEmpty(tableBlockContextBasicValue); +}; + export const useCustomRequestVariableOptions = () => { const collection = useCollection_deprecated(); const { t } = useTranslation(); @@ -33,6 +50,8 @@ export const useCustomRequestVariableOptions = () => { return [compile(fieldsOptions), compile(userFieldOptions)]; }, [fieldsOptions, userFieldOptions]); const environmentVariables = useGlobalVariable('$env'); + const contextVariable = useContextAssociationFields({ maxDepth: 2, contextCollectionName: collection.name }); + const shouldShowTableSelectVariable = useIsShowTableSelectRecord(); return useMemo(() => { return [ environmentVariables, @@ -61,6 +80,7 @@ export const useCustomRequestVariableOptions = () => { title: 'API token', children: null, }, + shouldShowTableSelectVariable && { ...contextVariable, name: '$nSelectedRecord', title: contextVariable.label }, ].filter(Boolean); - }, [recordData, t, fields, blockType, userFields]); + }, [recordData, t, fields, blockType, userFields, shouldShowTableSelectVariable]); }; diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts index a1580495e9..90de1760b4 100644 --- a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts +++ b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts @@ -16,6 +16,12 @@ import { useCompile, useDataSourceKey, useNavigateNoUpdate, + useBlockRequestContext, + useContextVariable, + useLocalVariables, + useVariables, + replaceVariables, + interpolateVariables, } from '@nocobase/client'; import { isURL } from '@nocobase/utils/client'; import { App } from 'antd'; @@ -25,20 +31,32 @@ export const useCustomizeRequestActionProps = () => { const apiClient = useAPIClient(); const navigate = useNavigateNoUpdate(); const actionSchema = useFieldSchema(); + const { field } = useBlockRequestContext(); const compile = useCompile(); const form = useForm(); const { name: blockType } = useBlockContext() || {}; - // const { getPrimaryKey } = useCollection_deprecated(); const recordData = useCollectionRecordData(); const fieldSchema = useFieldSchema(); const actionField = useField(); const { setVisible } = useActionContext(); const { modal, message } = App.useApp(); const dataSourceKey = useDataSourceKey(); + const { ctx } = useContextVariable(); + const localVariables = useLocalVariables(); + const variables = useVariables(); + return { async onClick(e?, callBack?) { + const selectedRecord = field?.data?.selectedRowData ? field?.data?.selectedRowData : ctx; const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {}; - const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {}; + const { + manualClose, + redirecting, + redirectTo, + successMessage: rawSuccessMessage, + actionAfterSuccess, + } = onSuccess || {}; + let successMessage = rawSuccessMessage; const xAction = actionSchema?.['x-action']; if (skipValidator !== true && xAction === 'customize:form:request') { await form.submit(); @@ -58,15 +76,27 @@ export const useCustomizeRequestActionProps = () => { method: 'POST', data: { currentRecord: { - // id: record[getPrimaryKey()], - // appends: result.params[0]?.appends, dataSourceKey, data: currentRecordData, }, $nForm: blockType === 'form' ? form.values : undefined, + $nSelectedRecord: selectedRecord, }, responseType: fieldSchema['x-response-type'] === 'stream' ? 'blob' : 'json', }); + try { + const { exp, scope: expScope } = await replaceVariables(successMessage, { + variables, + localVariables: [ + ...localVariables, + { name: '$nResponse', ctx: new Proxy({ ...res?.data?.data, ...res?.data }, {}) }, + ], + }); + successMessage = interpolateVariables(exp, expScope); + } catch (error) { + console.log(error); + } + if (res.headers['content-disposition']) { const contentDisposition = res.headers['content-disposition']; const utf8Match = contentDisposition.match(/filename\*=utf-8''([^;]+)/i); diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.ts b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.ts deleted file mode 100644 index 2a872ac122..0000000000 --- a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.ts +++ /dev/null @@ -1,92 +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 { useFieldSchema } from '@formily/react'; -import { - AfterSuccess, - ButtonEditor, - RefreshDataBlockRequest, - RemoveButton, - SchemaSettings, - SchemaSettingsLinkageRules, - SecondConFirm, - useCollection, - useCollectionRecord, - useSchemaToolbar, - SchemaSettingAccessControl, - useDataBlockProps, - useCollectionManager_deprecated, -} from '@nocobase/client'; -import { CustomRequestSettingsItem } from './components/CustomRequestActionDesigner'; - -export const customizeCustomRequestActionSettings = new SchemaSettings({ - name: 'actionSettings:customRequest', - items: [ - { - name: 'editButton', - Component: ButtonEditor, - useComponentProps() { - const fieldSchema = useFieldSchema(); - return { - isLink: fieldSchema['x-action'] === 'customize:table:request', - }; - }, - }, - { - name: 'linkageRules', - Component: SchemaSettingsLinkageRules, - useComponentProps() { - const { linkageRulesProps } = useSchemaToolbar(); - return { - ...linkageRulesProps, - }; - }, - }, - { - name: 'secondConFirm', - Component: SecondConFirm, - }, - { - name: 'afterSuccessfulSubmission', - Component: AfterSuccess, - }, - { - name: 'request settings', - Component: CustomRequestSettingsItem, - }, - { - ...SchemaSettingAccessControl, - useVisible() { - return true; - }, - }, - { - name: 'refreshDataBlockRequest', - Component: RefreshDataBlockRequest, - useComponentProps() { - return { - isPopupAction: false, - }; - }, - useVisible() { - const collection = useCollection(); - return !!collection; - }, - }, - { - name: 'delete', - sort: 100, - Component: RemoveButton as any, - useComponentProps() { - const { removeButtonProps } = useSchemaToolbar(); - return removeButtonProps; - }, - }, - ], -}); diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.tsx b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.tsx new file mode 100644 index 0000000000..9f2e680968 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.tsx @@ -0,0 +1,242 @@ +/** + * 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 { + ButtonEditor, + RefreshDataBlockRequest, + RemoveButton, + SchemaSettings, + SchemaSettingsLinkageRules, + SecondConFirm, + useCollection, + useSchemaToolbar, + SchemaSettingAccessControl, + useDesignable, + useGlobalVariable, + usePlugin, + SchemaSettingsModalItem, + useAfterSuccessOptions, + BlocksSelector, +} from '@nocobase/client'; +import React from 'react'; +import { ISchema, useFieldSchema } from '@formily/react'; +import { useTranslation } from 'react-i18next'; +import { CustomRequestSettingsItem } from './components/CustomRequestActionDesigner'; + +const useVariableOptions = () => { + const scopes = useAfterSuccessOptions(); + const { t } = useTranslation(); + return [ + { + value: '$nResponse', + label: t('Response', { ns: 'client' }), + children: null, + }, + ...scopes.filter((v: any) => ['currentUser', 'currentTime', '$nRole'].includes(v.value)), + ].filter(Boolean); +}; + +const useLinkVariableOptions = () => { + const scopes = useAfterSuccessOptions(); + const environmentVariables = useGlobalVariable('$env'); + return [...scopes.filter((v: any) => v.value !== '$record'), environmentVariables].filter(Boolean); +}; +const useLinkVariableProps = () => { + const scope = useLinkVariableOptions(); + return { + scope, + useTypedConstant: true, + }; +}; +export function AfterSuccess() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const { onSuccess } = fieldSchema?.['x-action-settings'] || {}; + const templatePlugin: any = usePlugin('@nocobase/plugin-block-template'); + const isInBlockTemplateConfigPage = templatePlugin?.isInBlockTemplateConfigPage?.(); + return ( + { + return { + tooltip: t('After successful submission, the selected data blocks will be automatically refreshed.'), + }; + }, + 'x-component': BlocksSelector, + 'x-hidden': isInBlockTemplateConfigPage, // 模板配置页面暂不支持该配置 + }, + }, + } as ISchema + } + onSubmit={(onSuccess) => { + fieldSchema['x-action-settings']['onSuccess'] = onSuccess; + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-action-settings': fieldSchema['x-action-settings'], + }, + }); + }} + /> + ); +} + +export const customizeCustomRequestActionSettings = new SchemaSettings({ + name: 'actionSettings:customRequest', + items: [ + { + name: 'editButton', + Component: ButtonEditor, + useComponentProps() { + const fieldSchema = useFieldSchema(); + return { + isLink: fieldSchema['x-action'] === 'customize:table:request', + }; + }, + }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + }; + }, + }, + { + name: 'secondConFirm', + Component: SecondConFirm, + }, + { + name: 'afterSuccessfulSubmission', + Component: AfterSuccess, + }, + { + name: 'request settings', + Component: CustomRequestSettingsItem, + }, + { + ...SchemaSettingAccessControl, + useVisible() { + return true; + }, + }, + { + name: 'refreshDataBlockRequest', + Component: RefreshDataBlockRequest, + useComponentProps() { + return { + isPopupAction: false, + }; + }, + useVisible() { + const collection = useCollection(); + return !!collection; + }, + }, + { + name: 'delete', + sort: 100, + Component: RemoveButton as any, + useComponentProps() { + const { removeButtonProps } = useSchemaToolbar(); + return removeButtonProps; + }, + }, + ], +}); diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/server/actions/send.ts b/packages/plugins/@nocobase/plugin-action-custom-request/src/server/actions/send.ts index 8cb73835be..f22ce9d294 100644 --- a/packages/plugins/@nocobase/plugin-action-custom-request/src/server/actions/send.ts +++ b/packages/plugins/@nocobase/plugin-action-custom-request/src/server/actions/send.ts @@ -15,6 +15,17 @@ import Application from '@nocobase/server'; import axios from 'axios'; import CustomRequestPlugin from '../plugin'; +function toJSON(value) { + if (typeof value === 'string') { + try { + return JSON.parse(value); + } catch (error) { + return value; + } + } + return value; +} + const getHeaders = (headers: Record) => { return Object.keys(headers).reduce((hds, key) => { if (key.toLocaleLowerCase().startsWith('x-')) { @@ -73,6 +84,7 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next) data: {}, }, $nForm, + $nSelectedRecord, } = values; // root role has all permissions @@ -154,6 +166,7 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next) $nToken: ctx.getBearerToken(), $nForm, $env: ctx.app.environment.getVariables(), + $nSelectedRecord, }; const axiosRequestConfig = { @@ -166,7 +179,7 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next) ...omitNullAndUndefined(getParsedValue(arrayToObject(headers), variables)), }, params: getParsedValue(arrayToObject(params), variables), - data: getParsedValue(data, variables), + data: getParsedValue(toJSON(data), variables), }; const requestUrl = axios.getUri(axiosRequestConfig); diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/package.json b/packages/plugins/@nocobase/plugin-action-duplicate/package.json index 4dea5f7bc8..a7d590c168 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/package.json +++ b/packages/plugins/@nocobase/plugin-action-duplicate/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-duplicate", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-duplicate", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-duplicate", diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts index 0b4b86d3f2..5f950ccea9 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts @@ -55,6 +55,7 @@ test.describe('direct duplicate & copy into the form and continue to fill in', ( await page.getByRole('menuitem', { name: 'oneToMany' }).click(); await page.getByRole('menuitem', { name: 'manyToOne', exact: true }).click(); await page.getByRole('menuitem', { name: 'manyToMany' }).click(); + await page.mouse.move(300, 0); await page.getByLabel('schema-initializer-ActionBar-createForm:configureActions-general').click(); await page.getByRole('menuitem', { name: 'Submit' }).click(); await page.getByLabel('drawer-Action.Container-general-Duplicate-mask').click(); diff --git a/packages/plugins/@nocobase/plugin-action-export/package.json b/packages/plugins/@nocobase/plugin-action-export/package.json index 6634081558..abca506a5f 100644 --- a/packages/plugins/@nocobase/plugin-action-export/package.json +++ b/packages/plugins/@nocobase/plugin-action-export/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "操作:导出记录", "description": "Export filtered records to excel, you can configure which fields to export.", "description.zh-CN": "导出筛选后的记录到 Excel 中,可以配置导出哪些字段。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-export", diff --git a/packages/plugins/@nocobase/plugin-action-export/src/server/services/base-exporter.ts b/packages/plugins/@nocobase/plugin-action-export/src/server/services/base-exporter.ts index a86eea3c3d..d699a96b5d 100644 --- a/packages/plugins/@nocobase/plugin-action-export/src/server/services/base-exporter.ts +++ b/packages/plugins/@nocobase/plugin-action-export/src/server/services/base-exporter.ts @@ -1,3 +1,12 @@ +/** + * 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 { FindOptions, ICollection, @@ -10,6 +19,7 @@ import EventEmitter from 'events'; import { deepGet } from '../utils/deep-get'; import path from 'path'; import os from 'os'; +import _ from 'lodash'; export type ExportOptions = { collectionManager: ICollectionManager; @@ -49,11 +59,11 @@ abstract class BaseExporter extends Eve const { collection, chunkSize, repository } = this.options; - const total = await (repository || collection.repository).count(this.getFindOptions()); + const total = await (repository || collection.repository).count(this.getFindOptions(ctx)); let current = 0; await (repository || collection.repository).chunk({ - ...this.getFindOptions(), + ...this.getFindOptions(ctx), chunkSize: chunkSize || 200, callback: async (rows, options) => { for (const row of rows) { @@ -71,31 +81,34 @@ abstract class BaseExporter extends Eve return this.finalize(); } - protected getAppendOptionsFromFields() { - return this.options.fields + protected getAppendOptionsFromFields(ctx?) { + const fields = this.options.fields.map((x) => x[0]); + const hasPermissionFields = _.isEmpty(ctx?.permission?.can?.params) + ? fields + : _.intersection(ctx?.permission?.can?.params?.appends || [], fields); + return hasPermissionFields .map((field) => { - const fieldInstance = this.options.collection.getField(field[0]); + const fieldInstance = this.options.collection.getField(field); if (!fieldInstance) { - throw new Error(`Field "${field[0]}" not found: , please check the fields configuration.`); + throw new Error(`Field "${field}" not found: , please check the fields configuration.`); } if (fieldInstance.isRelationField()) { - return field.join('.'); + return field; } return null; }) .filter(Boolean); } - - protected getFindOptions() { + protected getFindOptions(ctx?) { const { findOptions = {} } = this.options; if (this.limit) { findOptions.limit = this.limit; } - const appendOptions = this.getAppendOptionsFromFields(); + const appendOptions = this.getAppendOptionsFromFields(ctx); if (appendOptions.length) { return { diff --git a/packages/plugins/@nocobase/plugin-action-import/package.json b/packages/plugins/@nocobase/plugin-action-import/package.json index c0a5d30941..2a9630f17a 100644 --- a/packages/plugins/@nocobase/plugin-action-import/package.json +++ b/packages/plugins/@nocobase/plugin-action-import/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "操作:导入记录", "description": "Import records using excel templates. You can configure which fields to import and templates will be generated automatically.", "description.zh-CN": "使用 Excel 模板导入数据,可以配置导入哪些字段,自动生成模板。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-import", @@ -15,7 +15,7 @@ "@formily/core": "2.x", "@formily/react": "2.x", "@formily/shared": "2.x", - "@koa/multer": "^3.0.2", + "@koa/multer": "^3.1.0", "@types/node-xlsx": "^0.15.1", "antd": "5.x", "async-mutex": "^0.5.0", diff --git a/packages/plugins/@nocobase/plugin-action-import/src/client/index.ts b/packages/plugins/@nocobase/plugin-action-import/src/client/index.ts index 25a89e0419..6c452c3cb1 100644 --- a/packages/plugins/@nocobase/plugin-action-import/src/client/index.ts +++ b/packages/plugins/@nocobase/plugin-action-import/src/client/index.ts @@ -34,7 +34,7 @@ export class PluginActionImportClient extends Plugin { skipScopeCheck: true, }, }, - useVisible: () => useActionAvailable('import'), + useVisible: () => useActionAvailable('importXlsx'), }; const tableActionInitializers = this.app.schemaInitializerManager.get('table:configureActions'); diff --git a/packages/plugins/@nocobase/plugin-action-import/src/server/__tests__/xlsx-importer.test.ts b/packages/plugins/@nocobase/plugin-action-import/src/server/__tests__/xlsx-importer.test.ts index 85cb90ca9a..5f1fff7559 100644 --- a/packages/plugins/@nocobase/plugin-action-import/src/server/__tests__/xlsx-importer.test.ts +++ b/packages/plugins/@nocobase/plugin-action-import/src/server/__tests__/xlsx-importer.test.ts @@ -2155,4 +2155,123 @@ describe('xlsx importer', () => { expect(await Post.repository.count()).toBe(1); }); + + it('should filter no permission columns', async () => { + const User = app.db.collection({ + name: 'users', + fields: [ + { + type: 'string', + name: 'name', + }, + { + type: 'string', + name: 'email', + }, + ], + }); + + await app.db.sync(); + + const templateCreator = new TemplateCreator({ + collection: User, + explain: 'test', + columns: [ + { + dataIndex: ['name'], + defaultTitle: '姓名', + }, + { + dataIndex: ['email'], + defaultTitle: '邮箱', + }, + ], + }); + + const template = (await templateCreator.run({ returnXLSXWorkbook: true })) as XLSX.WorkBook; + + const worksheet = template.Sheets[template.SheetNames[0]]; + + XLSX.utils.sheet_add_aoa(worksheet, [['User1', 'test@test.com']], { + origin: 'A3', + }); + + const importer = new XlsxImporter({ + collectionManager: app.mainDataSource.collectionManager, + collection: User, + explain: 'test', + columns: [ + { + dataIndex: ['name'], + defaultTitle: '姓名', + }, + { + dataIndex: ['email'], + defaultTitle: '邮箱', + }, + ], + workbook: template, + }); + await importer.run({ + context: { + permission: { + can: { params: { fields: ['name'] } }, + }, + }, + }); + + expect(await User.repository.count()).toBe(1); + const user = await User.repository.findOne(); + expect(user.get('name')).toBe('User1'); + expect(user.get('email')).not.exist; + }); + + it('should import time field successfully', async () => { + const TimeCollection = app.db.collection({ + name: 'time_tests', + fields: [ + { + type: 'time', + name: 'brithtime', + }, + ], + }); + + await app.db.sync(); + const templateCreator = new TemplateCreator({ + collection: TimeCollection, + explain: 'test', + columns: [ + { + dataIndex: ['birthtime'], + defaultTitle: '出生时间', + }, + ], + }); + + const template = (await templateCreator.run({ returnXLSXWorkbook: true })) as XLSX.WorkBook; + + const worksheet = template.Sheets[template.SheetNames[0]]; + + XLSX.utils.sheet_add_aoa(worksheet, [['12:12:12']], { + origin: 'A3', + }); + + const importer = new XlsxImporter({ + collectionManager: app.mainDataSource.collectionManager, + collection: TimeCollection, + explain: 'test', + columns: [ + { + dataIndex: ['brithtime'], + defaultTitle: '出生时间', + }, + ], + workbook: template, + }); + + await importer.run(); + const count = await TimeCollection.repository.count(); + expect(count).toBe(1); + }); }); diff --git a/packages/plugins/@nocobase/plugin-action-import/src/server/actions/import-xlsx.ts b/packages/plugins/@nocobase/plugin-action-import/src/server/actions/import-xlsx.ts index 8f8b75516f..9d63a78935 100644 --- a/packages/plugins/@nocobase/plugin-action-import/src/server/actions/import-xlsx.ts +++ b/packages/plugins/@nocobase/plugin-action-import/src/server/actions/import-xlsx.ts @@ -36,6 +36,7 @@ async function importXlsxAction(ctx: Context, next: Next) { const workbook = XLSX.read(ctx.file.buffer, { type: 'buffer', sheetRows: readLimit, + cellDates: true, }); const repository = ctx.getCurrentRepository() as Repository; diff --git a/packages/plugins/@nocobase/plugin-action-import/src/server/services/xlsx-importer.ts b/packages/plugins/@nocobase/plugin-action-import/src/server/services/xlsx-importer.ts index 6641efcee6..1e850ab8cd 100644 --- a/packages/plugins/@nocobase/plugin-action-import/src/server/services/xlsx-importer.ts +++ b/packages/plugins/@nocobase/plugin-action-import/src/server/services/xlsx-importer.ts @@ -14,6 +14,8 @@ import { Collection as DBCollection, Database } from '@nocobase/database'; import { Transaction } from 'sequelize'; import EventEmitter from 'events'; import { ImportValidationError, ImportError } from '../errors'; +import { Context } from '@nocobase/actions'; +import _ from 'lodash'; export type ImportColumn = { dataIndex: Array; @@ -54,8 +56,9 @@ export class XlsxImporter extends EventEmitter { this.repository = options.repository ? options.repository : options.collection.repository; } - async validate() { - if (this.options.columns.length == 0) { + async validate(ctx?: Context) { + const columns = this.getColumnsByPermission(ctx); + if (columns.length == 0) { throw new ImportValidationError('Columns configuration is empty'); } @@ -66,7 +69,7 @@ export class XlsxImporter extends EventEmitter { } } - const data = await this.getData(); + const data = await this.getData(ctx); return data; } @@ -80,7 +83,7 @@ export class XlsxImporter extends EventEmitter { } try { - await this.validate(); + await this.validate(options.context); const imported = await this.performImport(options); // @ts-ignore @@ -111,7 +114,7 @@ export class XlsxImporter extends EventEmitter { } let hasImportedAutoIncrementPrimary = false; - for (const importedDataIndex of this.options.columns) { + for (const importedDataIndex of this.getColumnsByPermission(options?.context)) { if (importedDataIndex.dataIndex[0] === autoIncrementAttribute) { hasImportedAutoIncrementPrimary = true; break; @@ -150,9 +153,18 @@ export class XlsxImporter extends EventEmitter { this.emit('seqReset', { maxVal, seqName: autoIncrInfo.seqName }); } + private getColumnsByPermission(ctx: Context): ImportColumn[] { + const columns = this.options.columns; + return columns.filter((x) => + _.isEmpty(ctx?.permission?.can?.params) + ? true + : _.includes(ctx?.permission?.can?.params?.fields || [], x.dataIndex[0]), + ); + } + async performImport(options?: RunOptions): Promise { const transaction = options?.transaction; - const data = await this.getData(); + const data = await this.getData(options?.context); const chunks = lodash.chunk(data.slice(1), this.options.chunkSize || 200); let handingRowIndex = 1; @@ -271,25 +283,26 @@ export class XlsxImporter extends EventEmitter { return str; } - private getExpectedHeaders(): string[] { - return this.options.columns.map((col) => col.title || col.defaultTitle); + private getExpectedHeaders(ctx?: Context): string[] { + const columns = this.getColumnsByPermission(ctx); + return columns.map((col) => col.title || col.defaultTitle); } - async getData() { + async getData(ctx?: Context) { const workbook = this.options.workbook; const worksheet = workbook.Sheets[workbook.SheetNames[0]]; - const data = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: null }) as string[][]; + let data = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: null }) as string[][]; // Find and validate header row - const expectedHeaders = this.getExpectedHeaders(); - const { headerRowIndex, headers } = this.findAndValidateHeaders(data); + const expectedHeaders = this.getExpectedHeaders(ctx); + const { headerRowIndex, headers } = this.findAndValidateHeaders({ data, expectedHeaders }); if (headerRowIndex === -1) { throw new ImportValidationError('Headers not found. Expected headers: {{headers}}', { headers: expectedHeaders.join(', '), }); } - + data = this.alignWithHeaders({ data, expectedHeaders, headers }); // Extract data rows const rows = data.slice(headerRowIndex + 1); @@ -301,8 +314,18 @@ export class XlsxImporter extends EventEmitter { return [headers, ...rows]; } - private findAndValidateHeaders(data: string[][]): { headerRowIndex: number; headers: string[] } { - const expectedHeaders = this.getExpectedHeaders(); + private alignWithHeaders(params: { headers: string[]; expectedHeaders: string[]; data: string[][] }): string[][] { + const { expectedHeaders, headers, data } = params; + const keepCols = headers.map((x, i) => (expectedHeaders.includes(x) ? i : -1)).filter((i) => i > -1); + + return data.map((row) => keepCols.map((i) => row[i])); + } + + private findAndValidateHeaders(options: { expectedHeaders: string[]; data: string[][] }): { + headerRowIndex: number; + headers: string[]; + } { + const { data, expectedHeaders } = options; // Find header row and validate for (let rowIndex = 0; rowIndex < data.length; rowIndex++) { @@ -310,31 +333,11 @@ export class XlsxImporter extends EventEmitter { const actualHeaders = row.filter((cell) => cell !== null && cell !== ''); const allHeadersFound = expectedHeaders.every((header) => actualHeaders.includes(header)); - const noExtraHeaders = actualHeaders.length === expectedHeaders.length; - if (allHeadersFound && noExtraHeaders) { - const mismatchIndex = expectedHeaders.findIndex((title, index) => actualHeaders[index] !== title); - - if (mismatchIndex === -1) { - // All headers match - return { headerRowIndex: rowIndex, headers: actualHeaders }; - } else { - // Found potential header row but with mismatch - throw new ImportValidationError( - 'Header mismatch at column {{column}}: expected "{{expected}}", but got "{{actual}}"', - { - column: mismatchIndex + 1, - expected: expectedHeaders[mismatchIndex], - actual: actualHeaders[mismatchIndex] || 'empty', - }, - ); - } + if (allHeadersFound) { + const orderedHeaders = expectedHeaders.filter((h) => actualHeaders.includes(h)); + return { headerRowIndex: rowIndex, headers: orderedHeaders }; } } - - // No row with matching headers found - throw new ImportValidationError('Headers not found. Expected headers: {{headers}}', { - headers: expectedHeaders.join(', '), - }); } } diff --git a/packages/plugins/@nocobase/plugin-action-print/package.json b/packages/plugins/@nocobase/plugin-action-print/package.json index 40b75af87d..92f31edee5 100644 --- a/packages/plugins/@nocobase/plugin-action-print/package.json +++ b/packages/plugins/@nocobase/plugin-action-print/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-action-print", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/action-print", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-print", diff --git a/packages/plugins/@nocobase/plugin-ai/package.json b/packages/plugins/@nocobase/plugin-ai/package.json index 94a93ee045..479ff5188b 100644 --- a/packages/plugins/@nocobase/plugin-ai/package.json +++ b/packages/plugins/@nocobase/plugin-ai/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "AI 集成", "description": "Support integration with AI services, providing AI-related workflow nodes to enhance business processing capabilities.", "description.zh-CN": "支持接入 AI 服务,提供 AI 相关的工作流节点,增强业务处理能力。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "peerDependencies": { "@nocobase/client": "1.x", diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/schemas/llms.ts b/packages/plugins/@nocobase/plugin-ai/src/client/schemas/llms.ts index c945abc595..1a4cff6066 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/schemas/llms.ts +++ b/packages/plugins/@nocobase/plugin-ai/src/client/schemas/llms.ts @@ -187,7 +187,6 @@ export const llmsSchema = { 'x-component': 'Action.Link', 'x-component-props': { openMode: 'drawer', - icon: 'EditOutlined', }, properties: { drawer: { diff --git a/packages/plugins/@nocobase/plugin-api-doc/package.json b/packages/plugins/@nocobase/plugin-api-doc/package.json index 795f2c9920..e291d67237 100644 --- a/packages/plugins/@nocobase/plugin-api-doc/package.json +++ b/packages/plugins/@nocobase/plugin-api-doc/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-api-doc", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "displayName": "API documentation", "displayName.zh-CN": "API 文档", "description": "An OpenAPI documentation generator for NocoBase HTTP API.", diff --git a/packages/plugins/@nocobase/plugin-api-keys/package.json b/packages/plugins/@nocobase/plugin-api-keys/package.json index 4aaa4fea9d..07b344211a 100644 --- a/packages/plugins/@nocobase/plugin-api-keys/package.json +++ b/packages/plugins/@nocobase/plugin-api-keys/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "认证:API 密钥", "description": "Allows users to use API key to access application's HTTP API", "description.zh-CN": "允许用户使用 API 密钥访问应用的 HTTP API", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/api-keys", diff --git a/packages/plugins/@nocobase/plugin-async-task-manager/package.json b/packages/plugins/@nocobase/plugin-async-task-manager/package.json index 1b321ff11e..56565aadb8 100644 --- a/packages/plugins/@nocobase/plugin-async-task-manager/package.json +++ b/packages/plugins/@nocobase/plugin-async-task-manager/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "异步任务管理器", "description": "Manage and monitor asynchronous tasks such as data import/export. Support task progress tracking and notification.", "description.zh-CN": "管理和监控数据导入导出等异步任务。支持任务进度跟踪和通知。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "peerDependencies": { "@nocobase/client": "1.x", diff --git a/packages/plugins/@nocobase/plugin-audit-logs/package.json b/packages/plugins/@nocobase/plugin-audit-logs/package.json index 094180b6f6..b748321cb2 100644 --- a/packages/plugins/@nocobase/plugin-audit-logs/package.json +++ b/packages/plugins/@nocobase/plugin-audit-logs/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-audit-logs", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "displayName": "Audit logs (deprecated)", "displayName.zh-CN": "审计日志(废弃)", "description": "This plugin is deprecated. There will be a new audit log plugin in the future.", diff --git a/packages/plugins/@nocobase/plugin-auth-sms/package.json b/packages/plugins/@nocobase/plugin-auth-sms/package.json index 210a6f54f2..7021454a3b 100644 --- a/packages/plugins/@nocobase/plugin-auth-sms/package.json +++ b/packages/plugins/@nocobase/plugin-auth-sms/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "认证:短信", "description": "SMS authentication.", "description.zh-CN": "通过短信验证码认证身份。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/auth-sms", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth-sms", diff --git a/packages/plugins/@nocobase/plugin-auth/package.json b/packages/plugins/@nocobase/plugin-auth/package.json index 464f62a665..7a4ff3dcb8 100644 --- a/packages/plugins/@nocobase/plugin-auth/package.json +++ b/packages/plugins/@nocobase/plugin-auth/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-auth", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/auth", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth", diff --git a/packages/plugins/@nocobase/plugin-auth/src/client/settings/schemas/authenticators.ts b/packages/plugins/@nocobase/plugin-auth/src/client/settings/schemas/authenticators.ts index ab17b3a218..39ae8c175e 100644 --- a/packages/plugins/@nocobase/plugin-auth/src/client/settings/schemas/authenticators.ts +++ b/packages/plugins/@nocobase/plugin-auth/src/client/settings/schemas/authenticators.ts @@ -349,6 +349,7 @@ export const authenticatorsSchema: ISchema = { name: { 'x-component': 'CollectionField', 'x-decorator': 'FormItem', + 'x-disabled': true, }, authType: { 'x-component': 'CollectionField', diff --git a/packages/plugins/@nocobase/plugin-auth/src/server/basic-auth.ts b/packages/plugins/@nocobase/plugin-auth/src/server/basic-auth.ts index 5a378e9ddd..2b92203cef 100644 --- a/packages/plugins/@nocobase/plugin-auth/src/server/basic-auth.ts +++ b/packages/plugins/@nocobase/plugin-auth/src/server/basic-auth.ts @@ -47,7 +47,7 @@ export class BasicAuth extends BaseAuth { const valid = await field.verify(password, user.password); if (!valid) { ctx.throw(401, ctx.t('The username/email or password is incorrect, please re-enter', { ns: namespace }), { - code: 'INCORRECT_PASSWORD', + internalCode: 'INCORRECT_PASSWORD', user, }); } diff --git a/packages/plugins/@nocobase/plugin-backup-restore/package.json b/packages/plugins/@nocobase/plugin-backup-restore/package.json index 9bd545d7b0..086768bf44 100644 --- a/packages/plugins/@nocobase/plugin-backup-restore/package.json +++ b/packages/plugins/@nocobase/plugin-backup-restore/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "应用的备份与还原(废弃)", "description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.", "description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/backup-restore", @@ -28,7 +28,7 @@ "object-path": "^0.11.8", "react": "^18.2.0", "semver": "^7.7.1", - "tar": "^6.1.13" + "tar": "^7.4.3" }, "peerDependencies": { "@nocobase/actions": "1.x", diff --git a/packages/plugins/@nocobase/plugin-block-iframe/package.json b/packages/plugins/@nocobase/plugin-block-iframe/package.json index 7d1d74bc3a..590bd8a9fc 100644 --- a/packages/plugins/@nocobase/plugin-block-iframe/package.json +++ b/packages/plugins/@nocobase/plugin-block-iframe/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "区块:iframe", "description": "Create an iframe block on the page to embed and display external web pages or content.", "description.zh-CN": "在页面上创建和管理iframe,用于嵌入和展示外部网页或内容。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/block-iframe", diff --git a/packages/plugins/@nocobase/plugin-block-iframe/src/client/schemaSettings.tsx b/packages/plugins/@nocobase/plugin-block-iframe/src/client/schemaSettings.tsx index 1124cba449..04173b15df 100644 --- a/packages/plugins/@nocobase/plugin-block-iframe/src/client/schemaSettings.tsx +++ b/packages/plugins/@nocobase/plugin-block-iframe/src/client/schemaSettings.tsx @@ -19,6 +19,8 @@ import { useRecord, useURLAndHTMLSchema, useVariableOptions, + SchemaSettingsLinkageRules, + LinkageRuleCategory, } from '@nocobase/client'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -200,6 +202,17 @@ const commonOptions: any = { name: 'setTheBlockHeight', Component: SchemaSettingsBlockHeightItem, }, + { + name: 'blockLinkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { t } = useTranslation(); + return { + title: t('Block Linkage rules'), + category: LinkageRuleCategory.block, + }; + }, + }, { name: 'divider', type: 'divider', diff --git a/packages/plugins/@nocobase/plugin-block-template/package.json b/packages/plugins/@nocobase/plugin-block-template/package.json index 8d1786640e..b89dd36d0b 100644 --- a/packages/plugins/@nocobase/plugin-block-template/package.json +++ b/packages/plugins/@nocobase/plugin-block-template/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "区块:模板", "description": "Create and manage block templates for reuse on pages.", "description.zh-CN": "创建和管理区块模板,用于在页面中重复使用。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/block-template", diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/components/BlockTemplateMenusProvider.tsx b/packages/plugins/@nocobase/plugin-block-template/src/client/components/BlockTemplateMenusProvider.tsx index 192524154b..26109e7223 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/components/BlockTemplateMenusProvider.tsx +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/components/BlockTemplateMenusProvider.tsx @@ -68,7 +68,7 @@ export const BlockTemplateMenusProvider = ({ children }) => { method: 'get', params: { filter: { - configured: true, + configured: { $isTruly: true }, type: isMobile ? 'Mobile' : { $ne: 'Mobile' }, }, paginate: false, @@ -82,8 +82,8 @@ export const BlockTemplateMenusProvider = ({ children }) => { useEffect(() => { const isLeavingTemplatesPage = - previousPathRef.current.includes('/settings/block-templates') && - !location.pathname.includes('/settings/block-templates'); + previousPathRef.current.includes('/settings/block-templates/inherited') && + !location.pathname.includes('/settings/block-templates/inherited'); if (isLeavingTemplatesPage) { refresh(); } @@ -159,7 +159,7 @@ export const BlockTemplateMenusProvider = ({ children }) => { collectionName = field?.target; } const isDetails = name === 'details' || componentName === 'ReadPrettyFormItem'; - const children = data?.data + const children: SchemaInitializerItemType[] = data?.data ?.filter( (d) => (d.componentType === componentName || @@ -197,16 +197,7 @@ export const BlockTemplateMenusProvider = ({ children }) => { if (!children?.length) { return null; } - return [ - { - type: 'divider', - }, - { - type: 'itemGroup', - title: t('Block template'), - children, - }, - ] as SchemaInitializerItemType[]; + return children; }; registerInitializerMenusGenerator('block_template', generator); }, [data?.data, plugin.isInBlockTemplateConfigPage, handleTemplateClick, t, plugin]); diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConfigureLink.tsx b/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConfigureLink.tsx index b6d341eef2..eb98b59872 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConfigureLink.tsx +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConfigureLink.tsx @@ -15,9 +15,9 @@ export const ConfigureLink = () => { const value = useFilterByTk(); const recordData = useCollectionRecordData(); const t = useT(); - let to = `/admin/settings/block-templates/${value}`; + let to = `/admin/settings/block-templates/inherited/${value}`; if (recordData.type === 'Mobile') { - to = `/m/block-templates/${recordData.key}/${recordData.uid}`; + to = `/m/block-templates/inherited/${recordData.key}/${recordData.uid}`; } return {t('Configure')}; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConvertToNormalBlockSetting.tsx b/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConvertToNormalBlockSetting.tsx new file mode 100644 index 0000000000..2f1310fe8c --- /dev/null +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/components/ConvertToNormalBlockSetting.tsx @@ -0,0 +1,182 @@ +/** + * 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 { SchemaSettingsItem, useAPIClient, useDesignable, useFormBlockProps } from '@nocobase/client'; +import { useFieldSchema, useForm, useField } from '@formily/react'; +import { App } from 'antd'; +import React from 'react'; +import _ from 'lodash'; +import { Schema } from '@formily/json-schema'; +import { useT } from '../locale'; +import { uid } from '@nocobase/utils/client'; + +const findInsertPosition = (parentSchema, uid) => { + const postion = { + insertPosition: 'beforeBegin', + insertTarget: null, + }; + const properties = Object.values(parentSchema.properties || {}).sort((a, b) => { + return (a as any)['x-index'] - (b as any)['x-index']; + }); + for (let i = 0; i < properties.length; i++) { + const property = properties[i]; + if ((property as any)['x-uid'] === uid) { + postion.insertPosition = 'beforeBegin'; + if (i === properties.length - 1) { + postion.insertPosition = 'beforeEnd'; + postion.insertTarget = parentSchema['x-uid']; + } else { + postion.insertPosition = 'beforeBegin'; + postion.insertTarget = (properties[i + 1] as any)['x-uid']; + } + } + } + return postion; +}; + +const findParentRootTemplateSchema = (fieldSchema) => { + if (!fieldSchema) { + return null; + } + if (fieldSchema['x-template-root-uid']) { + return fieldSchema; + } else { + return findParentRootTemplateSchema(fieldSchema.parent); + } +}; + +// Create a copy of the schema with all template associations removed +const convertToNormalBlockSchema = (schema) => { + const newSchema = _.cloneDeep(schema); + + // Remove template associations from the schema + const removeTemplateAssociations = (s) => { + // Remove template-specific properties + delete s['x-template-uid']; + delete s['x-template-root-uid']; + delete s['x-template-version']; + delete s['x-block-template-key']; + delete s['x-template-root-ref']; + delete s['x-template-title']; + delete s['x-virtual']; + + if (s['x-toolbar-props']?.toolbarClassName?.includes('nb-in-template')) { + s['x-toolbar-props'].toolbarClassName = s['x-toolbar-props'].toolbarClassName.replace('nb-in-template', ''); + } + + if (s['x-uid']) { + s['x-uid'] = uid(); + } + // Process nested properties + if (s.properties) { + for (const key in s.properties) { + if (!s.properties[key]['x-template-root-uid']) { + removeTemplateAssociations(s.properties[key]); + } + } + } + }; + + removeTemplateAssociations(newSchema); + return newSchema; +}; + +export const ConvertToNormalBlockSetting = () => { + const { refresh } = useDesignable(); + const t = useT(); + const api = useAPIClient(); + const form = useForm(); + const field = useField(); + const { form: blockForm } = useFormBlockProps(); + const fieldSchema = useFieldSchema(); + const { modal, message } = App.useApp(); + const blockTemplatesResource = api.resource('blockTemplates'); + + const confirm = { + okText: t('Yes'), + cancelText: t('No'), + }; + + return ( + { + modal.confirm({ + title: t('Convert to normal block'), + content: t('Are you sure you want to convert this template block to a normal block?'), + ...confirm, + async onOk() { + const newSchema = convertToNormalBlockSchema(fieldSchema.toJSON()); + const position = findInsertPosition(fieldSchema.parent, fieldSchema['x-uid']); + // TODO: Remove old schema, and links + + // Remove old schema + await api.request({ + url: `/uiSchemas:remove/${fieldSchema['x-uid']}`, + }); + + // Insert new schema + const schema = new Schema(newSchema); + await api.request({ + url: `/uiSchemas:insertAdjacent/${position.insertTarget}?position=${position.insertPosition}`, + method: 'post', + data: { + schema, + }, + }); + + // Update the UI to show the new schema + fieldSchema.toJSON = () => { + const ret = schema.toJSON(); + return ret; + }; + + refresh({ refreshParentSchema: true }); + + // Update component properties + field['componentProps'] = { + ...field['componentProps'], + key: uid(), + }; + + if (field.parent?.['componentProps']) { + field.parent['componentProps'] = { + ...field.parent['componentProps'], + key: uid(), + }; + } + + // Update decorator properties + field['decoratorProps'] = { + ...field['decoratorProps'], + key: uid(), + }; + + if (field.parent?.['decoratorProps']) { + field.parent['decoratorProps'] = { + ...field.parent['decoratorProps'], + key: uid(), + }; + } + + // Reset forms + form.reset(); + blockForm?.reset(); + form.clearFormGraph('*', false); + blockForm?.clearFormGraph('*', false); + + message.success(t('Converted successfully'), 0.2); + }, + }); + }} + > + {t('Convert to normal block')} + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/components/SaveAsTemplateSetting.tsx b/packages/plugins/@nocobase/plugin-block-template/src/client/components/SaveAsTemplateSetting.tsx index 1ab2d6a7a5..d73d9c87a3 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/components/SaveAsTemplateSetting.tsx +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/components/SaveAsTemplateSetting.tsx @@ -58,11 +58,11 @@ export const SaveAsTemplateSetting = () => { return ( { key: { type: 'string', 'x-decorator': 'FormItem', - title: t('Key'), + title: t('Name'), 'x-component': 'Input', 'x-validator': 'uid', required: true, @@ -298,6 +298,10 @@ function getTemplateSchemaFromPage(schema: ISchema) { } _.set(t, `properties.['${key}']`, {}); traverseSchema(s.properties[key], t.properties[key]); + // array's key will be set to number when render, so we need to set the name to the key + if (s.type === 'array' && t['properties']?.[key]?.name) { + _.set(t, `properties.['${key}'].name`, key); + } } } }; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/hooks/useIsPageBlock.ts b/packages/plugins/@nocobase/plugin-block-template/src/client/hooks/useIsPageBlock.ts index 86a2e0e4e2..0bc04f372a 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/hooks/useIsPageBlock.ts +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/hooks/useIsPageBlock.ts @@ -22,8 +22,9 @@ export const useIsPageBlock = () => { const isPage = location.pathname.startsWith('/admin/') || location.pathname.startsWith('/page/'); const notInPopup = !location.pathname.includes('/popups/'); const notInSetting = !location.pathname.startsWith('/admin/settings/'); - const notInBlockTemplate = !location.pathname.startsWith('/block-templates/'); - return isPage && notInPopup && notInSetting && notInBlockTemplate; + const notInWorkflow = !location.pathname.startsWith('/admin/workflow/workflows/'); + const notInBlockTemplate = !location.pathname.startsWith('/block-templates/inherited/'); + return isPage && notInPopup && notInSetting && notInWorkflow && notInBlockTemplate; }, [location.pathname, fieldSchema]); return isPageBlock; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/index.tsx b/packages/plugins/@nocobase/plugin-block-template/src/client/index.tsx index f143dc158d..423442629e 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/index.tsx +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/index.tsx @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { Plugin } from '@nocobase/client'; +import { BlockTemplatesPane, Plugin, SchemaSettingsFormItemTemplate, SchemaSettingsTemplate } from '@nocobase/client'; import { templateBlockInitializerItem } from './initializers'; import { NAMESPACE } from './constants'; import { BlockTemplateList, BlockTemplatePage } from './components'; @@ -28,6 +28,8 @@ import { import { BlockTemplateMenusProvider } from './components/BlockTemplateMenusProvider'; import { disabledDeleteSettingItem } from './settings/disabledDeleteSetting'; import { saveAsTemplateSetting } from './settings/saveAsTemplateSetting'; +import { convertToNormalBlockSettingItem } from './settings/convertToNormalBlockSetting'; + export class PluginBlockTemplateClient extends Plugin { templateInfos = new Map(); templateschemacache = {}; @@ -89,10 +91,20 @@ export class PluginBlockTemplateClient extends Plugin { this.app.pluginSettingsManager.add('block-templates', { title: `{{t("Block templates", { ns: "${NAMESPACE}" })}}`, icon: 'ProfileOutlined', + // Component: BlockTemplateList, + }); + + this.app.pluginSettingsManager.add(`block-templates.reference`, { + title: `{{t("Reference template")}}`, + Component: BlockTemplatesPane, + }); + + this.app.pluginSettingsManager.add(`block-templates.inherited`, { + title: `{{t("Inherited template")}}`, Component: BlockTemplateList, }); - this.app.pluginSettingsManager.add(`block-templates/:key`, { + this.app.pluginSettingsManager.add(`block-templates/inherited/:key`, { title: false, pluginKey: 'block-templates', isTopLevel: false, @@ -101,14 +113,15 @@ export class PluginBlockTemplateClient extends Plugin { // add mobile router this.app.pluginManager.get('mobile')?.mobileRouter?.add('mobile.schema.blockTemplate', { - path: `/block-templates/:key/:pageSchemaUid`, + path: `/block-templates/inherited/:key/:pageSchemaUid`, Component: BlockTemplateMobilePage, }); } isInBlockTemplateConfigPage() { - const mobilePath = this.app.pluginManager.get('mobile')?.mobileBasename + '/block-templates'; - const desktopPath = 'admin/settings/block-templates'; + const mobilePath = + this.app.pluginManager.get('mobile')?.mobileBasename + '/block-templates/inherited'; + const desktopPath = 'admin/settings/block-templates/inherited'; return window.location.pathname.includes(desktopPath) || window.location.pathname.includes(mobilePath); } @@ -139,7 +152,7 @@ export class PluginBlockTemplateClient extends Plugin { // if not filter out fieldSettings:component:, we will show two revert setting item if (schemaSetting && !key.startsWith('fieldSettings:component:')) { for (let i = 0; i < schemaSetting.items.length; i++) { - // hide convert to block setting item + // hide reference template setting item hideConvertToBlockSettingItem( schemaSetting.items[i], schemaSetting.items[i - 1], @@ -158,9 +171,10 @@ export class PluginBlockTemplateClient extends Plugin { deleteItemIndex !== -1 && !schemaSetting.items.find((item) => item.name === 'template-revertSettingItem') ) { - schemaSetting.items.splice(deleteItemIndex, 0, revertSettingItem); + schemaSetting.items.splice(deleteItemIndex, 0, revertSettingItem, convertToNormalBlockSettingItem); } else { schemaSetting.add('template-revertSettingItem', revertSettingItem); + schemaSetting.add('template-convertToNormalBlockSettingItem', convertToNormalBlockSettingItem); } schemaSetting.add('template-disabledDeleteItem', disabledDeleteSettingItem); } @@ -186,13 +200,15 @@ export class PluginBlockTemplateClient extends Plugin { 'FormDetailsSettings', ]; if (blockSettings.includes(key)) { - // schemaSetting.add('template-saveAsTemplateItem', saveAsTemplateSetting); - // 放到template-revertSettingItem上方 - const revertItemIndex = schemaSetting.items.findIndex((item) => item.name === 'template-revertSettingItem'); - if (revertItemIndex !== -1) { - schemaSetting.items.splice(revertItemIndex, 0, saveAsTemplateSetting); - } else { - schemaSetting.items.push(saveAsTemplateSetting); + const referenceTemplateItemIndex = schemaSetting.items.findIndex( + (item) => + item['Component'] === SchemaSettingsTemplate || item['Component'] === SchemaSettingsFormItemTemplate, + ); + if (referenceTemplateItemIndex !== -1) { + schemaSetting.items.splice(referenceTemplateItemIndex + 1, 0, { + ...saveAsTemplateSetting, + sort: schemaSetting.items[referenceTemplateItemIndex].sort, + }); } } } diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/blockTemplates.ts b/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/blockTemplates.ts index 7fcec37f5a..808a9ad542 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/blockTemplates.ts +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/blockTemplates.ts @@ -88,9 +88,6 @@ export const blockTemplatesSchema: ISchema = { type: 'void', title: '{{ t("Title") }}', 'x-component': 'TableV2.Column', - 'x-component-props': { - width: 150, - }, properties: { title: { type: 'string', diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/editActionSchema.ts b/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/editActionSchema.ts index 9a4365844f..40fcb34290 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/editActionSchema.ts +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/schemas/editActionSchema.ts @@ -15,7 +15,6 @@ export const editActionSchema = { 'x-component': 'Action.Link', 'x-component-props': { openMode: 'drawer', - icon: 'EditOutlined', }, properties: { drawer: { diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/settings/convertToNormalBlockSetting.ts b/packages/plugins/@nocobase/plugin-block-template/src/client/settings/convertToNormalBlockSetting.ts new file mode 100644 index 0000000000..3bd7b284c2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/settings/convertToNormalBlockSetting.ts @@ -0,0 +1,22 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { useFieldSchema } from '@formily/react'; +import { ConvertToNormalBlockSetting } from '../components/ConvertToNormalBlockSetting'; +import { tStr } from '../locale'; + +export const convertToNormalBlockSettingItem = { + name: 'template-convertToNormalBlockSettingItem', + title: tStr('Convert to normal block'), + Component: ConvertToNormalBlockSetting, + useVisible: () => { + const fieldSchema = useFieldSchema(); + return fieldSchema?.['x-template-root-uid']; + }, +}; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/settings/saveAsTemplateSetting.ts b/packages/plugins/@nocobase/plugin-block-template/src/client/settings/saveAsTemplateSetting.ts index 2eef55a7da..1ab8c79671 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/settings/saveAsTemplateSetting.ts +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/settings/saveAsTemplateSetting.ts @@ -7,6 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { useSchemaSettings } from '@nocobase/client'; import { SaveAsTemplateSetting } from '../components/SaveAsTemplateSetting'; import { useIsPageBlock } from '../hooks/useIsPageBlock'; @@ -15,6 +16,7 @@ export const saveAsTemplateSetting = { Component: SaveAsTemplateSetting, useVisible: () => { const isPageBlock = useIsPageBlock(); - return isPageBlock; + const { template: deprecatedTemplate } = useSchemaSettings(); + return isPageBlock && !deprecatedTemplate; }, }; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/client/utils/setting.ts b/packages/plugins/@nocobase/plugin-block-template/src/client/utils/setting.ts index c3eb83b051..9ccd92c167 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/client/utils/setting.ts +++ b/packages/plugins/@nocobase/plugin-block-template/src/client/utils/setting.ts @@ -16,7 +16,6 @@ import { SchemaSettingsTemplate, SchemaSettingsConnectDataBlocks, usePlugin, - useSchemaSettings, } from '@nocobase/client'; export const hideConvertToBlockSettingItem = ( @@ -29,19 +28,21 @@ export const hideConvertToBlockSettingItem = ( settingItem['Component'] === SchemaSettingsFormItemTemplate ) { // hide covert to block setting item + const visible = settingItem['useVisible'] || (() => true); settingItem['useVisible'] = function useVisible() { - const { template: deprecatedTemplate } = useSchemaSettings(); - if (deprecatedTemplate && ['formItemTemplate', 'ConvertReferenceToDuplicate'].includes(settingItem['name'])) { - // still allow user to convert reference to duplicate, this way user can migrate to new template easily - return true; - } - return false; + return !useIsInTemplate() && visible(); }; if (preSettingItem?.['type'] === 'divider') { - preSettingItem['useVisible'] = () => false; + const preVisible = preSettingItem['useVisible'] || (() => true); + preSettingItem['useVisible'] = function useVisible() { + return !useIsInTemplate() && preVisible(); + }; } if (nextSettingItem?.['type'] === 'divider') { - nextSettingItem['useVisible'] = () => false; + const nextVisible = nextSettingItem['useVisible'] || (() => true); + nextSettingItem['useVisible'] = function useVisible() { + return !useIsInTemplate() && nextVisible(); + }; } } }; diff --git a/packages/plugins/@nocobase/plugin-block-template/src/locale/en-US.json b/packages/plugins/@nocobase/plugin-block-template/src/locale/en-US.json index 6acd6419ea..9f737f2a9c 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/locale/en-US.json +++ b/packages/plugins/@nocobase/plugin-block-template/src/locale/en-US.json @@ -12,7 +12,9 @@ "Mobile": "Mobile", "Current": "Current record", "Revert to template": "Revert to template", + "Convert to normal block": "Convert to normal block", "Are you sure you want to revert all changes from the template?": "Are you sure you want to revert all changes from the template?", + "Are you sure you want to convert this template block to a normal block?": "Are you sure you want to convert this template block to a normal block?", "Templates": "Templates", "Block templates": "Block templates", "Submit": "Submit", @@ -22,6 +24,7 @@ "Saved successfully": "Saved successfully", "Block template": "Block template", "Reset successfully": "Reset successfully", + "Converted successfully": "Converted successfully", "Delete successfully": "Delete successfully", "Template block settings": "Template block settings", "Filter": "Filter", diff --git a/packages/plugins/@nocobase/plugin-block-template/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-block-template/src/locale/zh-CN.json index be27745c26..57bdbbb754 100644 --- a/packages/plugins/@nocobase/plugin-block-template/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-block-template/src/locale/zh-CN.json @@ -12,7 +12,9 @@ "Mobile": "移动端", "Current": "当前记录", "Revert to template": "恢复到模板", + "Convert to normal block": "转换成普通区块", "Are you sure you want to revert all changes from the template?": "您确定要恢复所有对模板的更改吗?", + "Are you sure you want to convert this template block to a normal block?": "您确定要将此模板区块转换为普通区块吗?", "Templates": "模板", "Block templates": "区块模板", "Submit": "提交", @@ -22,6 +24,7 @@ "Saved successfully": "保存成功", "Block template": "区块模板", "Reset successfully": "重置成功", + "Converted successfully": "转换成功", "Delete successfully": "删除成功", "Template block settings": "模板区块设置", "Filter": "筛选", diff --git a/packages/plugins/@nocobase/plugin-block-workbench/package.json b/packages/plugins/@nocobase/plugin-block-workbench/package.json index 7a65efc524..7ca32035ad 100644 --- a/packages/plugins/@nocobase/plugin-block-workbench/package.json +++ b/packages/plugins/@nocobase/plugin-block-workbench/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-block-workbench", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "displayName": "Block: Action panel", "displayName.zh-CN": "区块:操作面板", "description": "Centrally manages and displays various actions, allowing users to efficiently perform tasks. It supports extensibility, with current action types including pop-ups, links, scanning, and custom requests.", diff --git a/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchAction.tsx b/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchAction.tsx index 56814c760d..ba13de2116 100644 --- a/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchAction.tsx +++ b/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchAction.tsx @@ -42,35 +42,39 @@ const useStyles = createStyles(({ token, css }) => ({ `, })); -function Button() { +function Button({ onlyIcon }) { const fieldSchema = useFieldSchema(); - const icon = fieldSchema['x-component-props']?.['icon']; - const backgroundColor = fieldSchema['x-component-props']?.['iconColor']; + const { icon, iconColor: backgroundColor } = fieldSchema['x-component-props'] || {}; const { layout, ellipsis = true } = useContext(WorkbenchBlockContext); const { styles, cx } = useStyles(); const { t } = useTranslation(); const title = t(fieldSchema.title, { ns: NAMESPACE_UI_SCHEMA }); - return layout === WorkbenchLayout.Grid ? ( -
- } /> -
- {title} + + const shouldShowTitle = !onlyIcon; + + if (layout === WorkbenchLayout.Grid) { + return ( +
+ } /> +
+ {shouldShowTitle && title} +
-
- ) : ( - {title} - ); + ); + } + + return {shouldShowTitle && title}; } export const WorkbenchAction = withDynamicSchemaProps((props) => { - const { className, ...others } = props; + const { className, targetComponent, iconColor, ...others } = props; const { styles, cx } = useStyles() as any; const fieldSchema = useFieldSchema(); const Component = useComponent(props?.targetComponent) || Action; @@ -79,9 +83,10 @@ export const WorkbenchAction = withDynamicSchemaProps((props) => { } + title={
); }; diff --git a/packages/plugins/@nocobase/plugin-theme-editor/src/client/antd-token-previewer/token-panel-pro/token-meta.json b/packages/plugins/@nocobase/plugin-theme-editor/src/client/antd-token-previewer/token-panel-pro/token-meta.json index 54ff50a523..5209098ea7 100644 --- a/packages/plugins/@nocobase/plugin-theme-editor/src/client/antd-token-previewer/token-panel-pro/token-meta.json +++ b/packages/plugins/@nocobase/plugin-theme-editor/src/client/antd-token-previewer/token-panel-pro/token-meta.json @@ -1592,6 +1592,14 @@ "type": "boolean", "source": "seed" }, + "siderWidth": { + "name": "侧边栏宽度(px)", + "nameEn": "Sider width (px)", + "desc": "调整侧边栏展示区域的宽度,宽度单位通常为像素(px)", + "descEn": "Adjust the width of the sidebar display area. The width is usually measured in pixels (px).", + "type": "number", + "source": "map" + }, "zIndexBase": { "name": "基础 zIndex", "nameEn": "Base zIndex", diff --git a/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json b/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json index 457c09e1fd..248aa9dd61 100644 --- a/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json +++ b/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "UI schema 存储服务", "description": "Provides centralized UI schema storage service.", "description.zh-CN": "提供中心化的 UI schema 存储服务。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/ui-schema-storage", diff --git a/packages/plugins/@nocobase/plugin-user-data-sync/package.json b/packages/plugins/@nocobase/plugin-user-data-sync/package.json index 47b204a6ce..a109b5a777 100644 --- a/packages/plugins/@nocobase/plugin-user-data-sync/package.json +++ b/packages/plugins/@nocobase/plugin-user-data-sync/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "用户数据同步", "description": "Reigster and manage extensible user data synchronization sources, with HTTP API provided by default. Support for synchronizing data to resources such as users and departments.", "description.zh-CN": "注册和管理可扩展的用户数据同步来源,默认提供 HTTP API。支持向用户和部门等资源同步数据。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "main": "dist/server/index.js", "peerDependencies": { "@nocobase/client": "1.x", diff --git a/packages/plugins/@nocobase/plugin-users/package.json b/packages/plugins/@nocobase/plugin-users/package.json index 3031a2e31b..cf7689c77f 100644 --- a/packages/plugins/@nocobase/plugin-users/package.json +++ b/packages/plugins/@nocobase/plugin-users/package.json @@ -4,14 +4,14 @@ "displayName.zh-CN": "用户", "description": "Provides basic user model, as well as created by and updated by fields.", "description.zh-CN": "提供了基础的用户模型,以及创建人和最后更新人字段。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/users", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/users", "devDependencies": { - "@types/jsonwebtoken": "^8.5.8", - "jsonwebtoken": "^8.5.1" + "@types/jsonwebtoken": "^9.0.9", + "jsonwebtoken": "^9.0.2" }, "peerDependencies": { "@nocobase/actions": "1.x", diff --git a/packages/plugins/@nocobase/plugin-verification/package.json b/packages/plugins/@nocobase/plugin-verification/package.json index 4190a22f8b..8b66a8d586 100644 --- a/packages/plugins/@nocobase/plugin-verification/package.json +++ b/packages/plugins/@nocobase/plugin-verification/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "验证", "description": "User identity verification management, including SMS, TOTP authenticator, with extensibility.", "description.zh-CN": "用户身份验证管理,包含短信、TOTP 认证器等,可扩展。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/verification", diff --git a/packages/plugins/@nocobase/plugin-verification/src/client/schemas/verificators.ts b/packages/plugins/@nocobase/plugin-verification/src/client/schemas/verificators.ts index 9115298ca0..f7393c4314 100644 --- a/packages/plugins/@nocobase/plugin-verification/src/client/schemas/verificators.ts +++ b/packages/plugins/@nocobase/plugin-verification/src/client/schemas/verificators.ts @@ -207,7 +207,6 @@ export const verficatorsSchema: ISchema = { 'x-component': 'Action.Link', 'x-component-props': { openMode: 'drawer', - icon: 'EditOutlined', }, properties: { drawer: { diff --git a/packages/plugins/@nocobase/plugin-verification/src/server/migrations/20250111192640-providers2verificators.ts b/packages/plugins/@nocobase/plugin-verification/src/server/migrations/20250111192640-providers2verificators.ts index 86f788fa69..4534038626 100644 --- a/packages/plugins/@nocobase/plugin-verification/src/server/migrations/20250111192640-providers2verificators.ts +++ b/packages/plugins/@nocobase/plugin-verification/src/server/migrations/20250111192640-providers2verificators.ts @@ -13,7 +13,7 @@ import { SMS_OTP_VERIFICATION_TYPE } from '../../constants'; export default class extends Migration { on = 'afterLoad'; // 'beforeLoad' or 'afterLoad' - appVersion = '<1.6.1'; + appVersion = '<1.7.0'; async up() { const providers = await this.db.getRepository('verifications_providers').find(); diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json b/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json index 9273c83917..a96f5cd057 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:操作后事件", "description": "Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or \"submit to workflow\". Suitable for data processing, sending notifications, etc., after actions are completed.", "description.zh-CN": "通过操作按钮或 API 发起请求并在执行完成后触发,比如新增、更新、删除数据或者“提交至工作流”之后。适用于在操作完成后进行数据处理、发送通知等。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/plugins/workflow-action-trigger", diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json index 201387fbdb..3a5a11ac35 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:聚合查询节点", "description": "Used to aggregate data against the database in workflow, such as: statistics, sum, average, etc.", "description.zh-CN": "可用于在工作流中对数据库进行聚合查询,如:统计数量、求和、平均值等。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-aggregate", diff --git a/packages/plugins/@nocobase/plugin-workflow-delay/package.json b/packages/plugins/@nocobase/plugin-workflow-delay/package.json index 874b59727c..6abadcdde0 100644 --- a/packages/plugins/@nocobase/plugin-workflow-delay/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-delay/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:延时节点", "description": "Could be used in workflow parallel branch for waiting other branches.", "description.zh-CN": "可用于工作流并行分支中等待其他分支执行完成。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-delay", diff --git a/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json b/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json index 4cfa89a145..75a17bb985 100644 --- a/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-dynamic-calculation/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:动态表达式计算节点", "description": "Useful plugin for doing dynamic calculation based on expression collection records in workflow.", "description.zh-CN": "用于在工作流中进行基于数据行的动态表达式计算。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-dynamic-calculation", diff --git a/packages/plugins/@nocobase/plugin-workflow-loop/package.json b/packages/plugins/@nocobase/plugin-workflow-loop/package.json index 8445cd6a5f..1a31dcbf3a 100644 --- a/packages/plugins/@nocobase/plugin-workflow-loop/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-loop/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:循环节点", "description": "Used to repeat the sub-process processing of each value in an array, and can also be used for fixed times of sub-process processing.", "description.zh-CN": "用于对一个数组中的每个值进行重复的子流程处理,也可用于固定次数的重复子流程处理。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-loop", diff --git a/packages/plugins/@nocobase/plugin-workflow-mailer/package.json b/packages/plugins/@nocobase/plugin-workflow-mailer/package.json index 45e9fe77d6..ff648c2439 100644 --- a/packages/plugins/@nocobase/plugin-workflow-mailer/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-mailer/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:邮件发送节点", "description": "Send email in workflow.", "description.zh-CN": "可用于在工作流中发送电子邮件。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-smtp-mailer", diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/package.json b/packages/plugins/@nocobase/plugin-workflow-manual/package.json index e5c80f4bb3..469dd411fe 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-manual/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:人工处理节点", "description": "Could be used for workflows which some of decisions are made by users.", "description.zh-CN": "用于人工控制部分决策的流程。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-manual", diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx index 491a82d89c..5a90d0daad 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx @@ -8,6 +8,7 @@ */ import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; import { useField, useFieldSchema, useForm } from '@formily/react'; import { FormLayout } from '@formily/antd-v5'; import { Button, Card, ConfigProvider, Descriptions, Space, Spin, Tag } from 'antd'; @@ -47,17 +48,14 @@ import WorkflowPlugin, { useAvailableUpstreams, useFlowContext, EXECUTION_STATUS, - JOB_STATUS, WorkflowTitle, - TASK_STATUS, usePopupRecordContext, } from '@nocobase/plugin-workflow/client'; import { NAMESPACE, useLang } from '../locale'; import { FormBlockProvider } from './instruction/FormBlockProvider'; import { ManualFormType, manualFormTypes } from './instruction/SchemaConfig'; -import { TaskStatusOptionsMap } from '../common/constants'; -import { useNavigate, useParams } from 'react-router-dom'; +import { TaskStatusOptionsMap, TASK_STATUS } from '../common/constants'; function TaskStatusColumn(props) { const recordData = useCollectionRecordData(); @@ -654,11 +652,11 @@ function TaskItem() { const StatusFilterMap = { pending: { - status: JOB_STATUS.PENDING, + status: TASK_STATUS.PENDING, 'execution.status': EXECUTION_STATUS.STARTED, }, completed: { - status: JOB_STATUS.RESOLVED, + status: [TASK_STATUS.RESOLVED, TASK_STATUS.REJECTED], }, }; diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/common/constants.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/common/constants.ts index dd4acc5d20..a59b843700 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/common/constants.ts +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/common/constants.ts @@ -14,7 +14,7 @@ export const MANUAL_TASK_TYPE = 'manual'; export const TASK_STATUS = { PENDING: 0, RESOLVED: 1, - REJECTED: -1, + REJECTED: -5, }; export const TaskStatusOptions = [ diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts index a63b5aba87..08e931293b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts @@ -51,7 +51,7 @@ export default class extends Plugin { }, }); - this.app.acl.allow('workflowManualTasks', ['list', 'get', 'submit'], 'loggedIn'); + this.app.acl.allow('workflowManualTasks', ['list', 'listMine', 'get', 'submit'], 'loggedIn'); const workflowPlugin = this.app.pm.get(WorkflowPlugin) as WorkflowPlugin; workflowPlugin.registerInstruction('manual', ManualInstruction); diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/__tests__/tasks.test.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/__tests__/tasks.test.ts new file mode 100644 index 0000000000..c866136331 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/__tests__/tasks.test.ts @@ -0,0 +1,150 @@ +/** + * 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 Database from '@nocobase/database'; +import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; +import { getApp, sleep } from '@nocobase/plugin-workflow-test'; +import { MockServer } from '@nocobase/test'; +import { TASK_STATUS } from '../../common/constants'; + +// NOTE: skipped because time is not stable on github ci, but should work in local +describe('workflow > instructions > manual > task center', () => { + let app: MockServer; + let agent; + let userAgents; + let db: Database; + let PostRepo; + let WorkflowModel; + let workflow; + let UserModel; + let users; + let UserJobModel; + + beforeEach(async () => { + app = await getApp({ + plugins: ['users', 'auth', 'workflow-manual'], + }); + // await app.getPlugin('auth').install(); + agent = app.agent(); + db = app.db; + WorkflowModel = db.getCollection('workflows').model; + PostRepo = db.getCollection('posts').repository; + UserModel = db.getCollection('users').model; + UserJobModel = db.getModel('workflowManualTasks'); + + users = await UserModel.bulkCreate([ + { id: 2, nickname: 'a' }, + { id: 3, nickname: 'b' }, + ]); + + userAgents = await Promise.all(users.map((user) => app.agent().login(user))); + + workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts', + }, + }); + }); + + afterEach(() => app.destroy()); + + describe('listMine', () => { + it('member', async () => { + const n1 = await workflow.createNode({ + type: 'manual', + config: { + assignees: [users[0].id], + forms: { + f1: { + type: 'update', + actions: [{ status: JOB_STATUS.RESOLVED, key: 'resolve' }], + collection: 'posts', + filter: { + id: '{{$context.data.id}}', + }, + }, + }, + }, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const UserJobModel = db.getModel('workflowManualTasks'); + const pendingJobs = await UserJobModel.findAll({ + order: [['userId', 'ASC']], + }); + expect(pendingJobs.length).toBe(1); + + const res1 = await userAgents[0].resource('workflowManualTasks').listMine(); + expect(res1.status).toBe(200); + expect(res1.body.data.length).toBe(1); + + const res2 = await userAgents[0].resource('workflowManualTasks').listMine({ + filter: { + status: TASK_STATUS.PENDING, + }, + }); + expect(res2.status).toBe(200); + expect(res2.body.data.length).toBe(1); + + const res3 = await userAgents[0].resource('workflowManualTasks').listMine({ + filter: { + status: TASK_STATUS.RESOLVED, + }, + }); + expect(res3.status).toBe(200); + expect(res3.body.data.length).toBe(0); + + const res4 = await userAgents[0].resource('workflowManualTasks').submit({ + filterByTk: pendingJobs[0].get('id'), + values: { + result: { f1: { title: 't2' }, _: 'resolve' }, + }, + }); + expect(res4.status).toBe(202); + + await sleep(500); + + const [e1] = await workflow.getExecutions({ order: [['createdAt', 'ASC']] }); + expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED); + const [j1] = await e1.getJobs(); + expect(j1.status).toBe(JOB_STATUS.RESOLVED); + expect(j1.result).toMatchObject({ f1: { title: 't2' } }); + + const posts = await PostRepo.find({ order: [['createdAt', 'ASC']] }); + expect(posts.length).toBe(1); + expect(posts[0]).toMatchObject({ title: 't2' }); + + const res5 = await userAgents[0].resource('workflowManualTasks').listMine({ + filter: { + status: TASK_STATUS.PENDING, + }, + }); + expect(res5.status).toBe(200); + expect(res5.body.data.length).toBe(0); + + const res6 = await userAgents[0].resource('workflowManualTasks').listMine({ + filter: { + status: TASK_STATUS.RESOLVED, + }, + }); + expect(res6.status).toBe(200); + expect(res6.body.data.length).toBe(1); + + const res7 = await userAgents[0].resource('workflowManualTasks').listMine(); + expect(res7.status).toBe(200); + expect(res7.body.data.length).toBe(1); + }); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow-notification/package.json b/packages/plugins/@nocobase/plugin-workflow-notification/package.json index 2474535dc5..920f2a8c8b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-notification/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-notification/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:通知节点", "description": "Send notification in workflow.", "description.zh-CN": "可用于在工作流中发送各类通知。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-smtp-mailer", diff --git a/packages/plugins/@nocobase/plugin-workflow-parallel/package.json b/packages/plugins/@nocobase/plugin-workflow-parallel/package.json index 3dd3024223..e35549f16f 100644 --- a/packages/plugins/@nocobase/plugin-workflow-parallel/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-parallel/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:并行分支节点", "description": "Could be used for parallel execution of branch processes in the workflow.", "description.zh-CN": "用于在工作流中需要并行执行的分支流程。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-parallel", diff --git a/packages/plugins/@nocobase/plugin-workflow-request/package.json b/packages/plugins/@nocobase/plugin-workflow-request/package.json index 88676beb3e..df10693f3f 100644 --- a/packages/plugins/@nocobase/plugin-workflow-request/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-request/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:HTTP 请求节点", "description": "Send HTTP requests to any HTTP service for data interaction in workflow.", "description.zh-CN": "可用于在工作流中向任意 HTTP 服务发送请求,进行数据交互。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-request", diff --git a/packages/plugins/@nocobase/plugin-workflow-response-message/package.json b/packages/plugins/@nocobase/plugin-workflow-response-message/package.json index f8833d6adc..8ee9b09265 100644 --- a/packages/plugins/@nocobase/plugin-workflow-response-message/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-response-message/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-workflow-response-message", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "displayName": "Workflow: Response message", "displayName.zh-CN": "工作流:响应消息", "description": "Used for assemble response message and showing to client in form event and request interception workflows.", diff --git a/packages/plugins/@nocobase/plugin-workflow-sql/package.json b/packages/plugins/@nocobase/plugin-workflow-sql/package.json index 2fb3bbba7f..47606dc7bd 100644 --- a/packages/plugins/@nocobase/plugin-workflow-sql/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-sql/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:SQL 节点", "description": "Execute SQL statements in workflow.", "description.zh-CN": "可用于在工作流中对数据库执行任意 SQL 语句。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-sql", diff --git a/packages/plugins/@nocobase/plugin-workflow-test/package.json b/packages/plugins/@nocobase/plugin-workflow-test/package.json index a2e2f95942..9d150ed275 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-test/package.json @@ -2,7 +2,7 @@ "name": "@nocobase/plugin-workflow-test", "displayName": "Workflow: test kit", "displayName.zh-CN": "工作流:测试工具包", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "dist/server/index.js", "types": "./dist/server/index.d.ts", diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts index 10709e0ae7..9dbbd3aded 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts @@ -882,7 +882,7 @@ export const builtinExpression = { value: 'formula.js', label: 'Formula.js', tooltip: '{{t("Formula.js supports most Microsoft Excel formula functions.")}}', - link: 'https://formulajs.info/functions/', + link: 'https://docs.nocobase.com/handbook/calculation-engines/formula', }, ], default: 'formula.js', diff --git a/packages/plugins/@nocobase/plugin-workflow/package.json b/packages/plugins/@nocobase/plugin-workflow/package.json index 401cbe5e6b..6a332c9519 100644 --- a/packages/plugins/@nocobase/plugin-workflow/package.json +++ b/packages/plugins/@nocobase/plugin-workflow/package.json @@ -4,13 +4,13 @@ "displayName.zh-CN": "工作流", "description": "A powerful BPM tool that provides foundational support for business automation, with the capability to extend unlimited triggers and nodes.", "description.zh-CN": "一个强大的 BPM 工具,为业务自动化提供基础支持,并且可任意扩展更多的触发器和节点。", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow", "dependencies": { - "@nocobase/plugin-workflow-test": "1.7.0-beta.18" + "@nocobase/plugin-workflow-test": "1.7.0-beta.26" }, "devDependencies": { "@ant-design/icons": "5.x", diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/components/TriggerCollectionRecordSelect.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/components/TriggerCollectionRecordSelect.tsx index ca1f085b11..42d964426c 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/components/TriggerCollectionRecordSelect.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/components/TriggerCollectionRecordSelect.tsx @@ -21,19 +21,19 @@ export function TriggerCollectionRecordSelect(props) { const [dataSourceName, collectionName] = parseCollectionName(workflow.config.collection); const { collectionManager } = app.dataSourceManager.getDataSource(dataSourceName); const collection = collectionManager.getCollection(collectionName); - const render = (props) => ( + const render = (p) => ( ); return ( @@ -42,6 +42,7 @@ export function TriggerCollectionRecordSelect(props) { onChange={props.onChange} nullable={false} changeOnSelect + {...props} render={render} /> ); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx index 87d358cc34..d70b3c4e82 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx @@ -125,20 +125,8 @@ export const systemOptions = { export const BaseTypeSets = { boolean: new Set(['checkbox']), number: new Set(['integer', 'number', 'percent']), - string: new Set([ - 'input', - 'password', - 'email', - 'phone', - 'select', - 'radioGroup', - 'text', - 'markdown', - 'richText', - 'expression', - 'time', - ]), - date: new Set(['date', 'createdAt', 'updatedAt']), + string: new Set(['input', 'password', 'email', 'phone', 'select', 'radioGroup', 'text', 'markdown', 'richText']), + date: new Set(['datetime', 'datetimeNoTz', 'dateOnly', 'createdAt', 'updatedAt']), }; // { type: 'reference', options: { collection: 'users', multiple: false } } diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/index.ts index 375ef784ab..a2343e8fb2 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/index.ts @@ -9,6 +9,7 @@ import Trigger from '..'; import type Plugin from '../../Plugin'; +import { WorkflowModel } from '../../types'; import DateFieldScheduleTrigger from './DateFieldScheduleTrigger'; import StaticScheduleTrigger from './StaticScheduleTrigger'; import { SCHEDULE_MODE } from './utils'; @@ -67,19 +68,15 @@ export default class ScheduleTrigger extends Trigger { // return !existed.length; // } - validateContext(values) { - if (!values?.mode) { - return { - mode: 'Mode is required', - }; - } - const trigger = this.getTrigger(values.mode); + validateContext(values, workflow: WorkflowModel) { + const { mode } = workflow.config; + const trigger = this.getTrigger(mode); if (!trigger) { return { mode: 'Mode in invalid', }; } - return trigger.validateContext?.(values); + return trigger.validateContext?.(values, workflow); } } diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/index.ts index 24c01d874e..50e462fd33 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/index.ts @@ -20,7 +20,7 @@ export abstract class Trigger { return true; } duplicateConfig?(workflow: WorkflowModel, options: Transactionable): object | Promise; - validateContext?(values: any): null | void | { [key: string]: string }; + validateContext?(values: any, workflow: WorkflowModel): null | void | { [key: string]: string }; sync?: boolean; execute?( workflow: WorkflowModel, diff --git a/packages/presets/nocobase/package.json b/packages/presets/nocobase/package.json index 9c5cfc3a73..479d5279b9 100644 --- a/packages/presets/nocobase/package.json +++ b/packages/presets/nocobase/package.json @@ -1,85 +1,85 @@ { "name": "@nocobase/preset-nocobase", - "version": "1.7.0-beta.18", + "version": "1.7.0-beta.26", "license": "AGPL-3.0", "main": "./lib/server/index.js", "dependencies": { "@formily/json-schema": "2.x", - "@nocobase/plugin-acl": "1.7.0-beta.18", - "@nocobase/plugin-action-bulk-edit": "1.7.0-beta.18", - "@nocobase/plugin-action-bulk-update": "1.7.0-beta.18", - "@nocobase/plugin-action-custom-request": "1.7.0-beta.18", - "@nocobase/plugin-action-duplicate": "1.7.0-beta.18", - "@nocobase/plugin-action-export": "1.7.0-beta.18", - "@nocobase/plugin-action-import": "1.7.0-beta.18", - "@nocobase/plugin-action-print": "1.7.0-beta.18", - "@nocobase/plugin-ai": "1.7.0-beta.18", - "@nocobase/plugin-api-doc": "1.7.0-beta.18", - "@nocobase/plugin-api-keys": "1.7.0-beta.18", - "@nocobase/plugin-async-task-manager": "1.7.0-beta.18", - "@nocobase/plugin-audit-logs": "1.7.0-beta.18", - "@nocobase/plugin-auth": "1.7.0-beta.18", - "@nocobase/plugin-auth-sms": "1.7.0-beta.18", - "@nocobase/plugin-backup-restore": "1.7.0-beta.18", - "@nocobase/plugin-block-iframe": "1.7.0-beta.18", - "@nocobase/plugin-block-template": "1.7.0-beta.18", - "@nocobase/plugin-block-workbench": "1.7.0-beta.18", - "@nocobase/plugin-calendar": "1.7.0-beta.18", - "@nocobase/plugin-charts": "1.7.0-beta.18", - "@nocobase/plugin-client": "1.7.0-beta.18", - "@nocobase/plugin-collection-sql": "1.7.0-beta.18", - "@nocobase/plugin-collection-tree": "1.7.0-beta.18", - "@nocobase/plugin-data-source-main": "1.7.0-beta.18", - "@nocobase/plugin-data-source-manager": "1.7.0-beta.18", - "@nocobase/plugin-data-visualization": "1.7.0-beta.18", - "@nocobase/plugin-departments": "1.7.0-beta.18", - "@nocobase/plugin-environment-variables": "1.7.0-beta.18", - "@nocobase/plugin-error-handler": "1.7.0-beta.18", - "@nocobase/plugin-field-attachment-url": "1.7.0-beta.18", - "@nocobase/plugin-field-china-region": "1.7.0-beta.18", - "@nocobase/plugin-field-formula": "1.7.0-beta.18", - "@nocobase/plugin-field-m2m-array": "1.7.0-beta.18", - "@nocobase/plugin-field-markdown-vditor": "1.7.0-beta.18", - "@nocobase/plugin-field-sequence": "1.7.0-beta.18", - "@nocobase/plugin-field-sort": "1.7.0-beta.18", - "@nocobase/plugin-file-manager": "1.7.0-beta.18", - "@nocobase/plugin-gantt": "1.7.0-beta.18", - "@nocobase/plugin-graph-collection-manager": "1.7.0-beta.18", - "@nocobase/plugin-kanban": "1.7.0-beta.18", - "@nocobase/plugin-locale-tester": "1.7.0-beta.18", - "@nocobase/plugin-localization": "1.7.0-beta.18", - "@nocobase/plugin-logger": "1.7.0-beta.18", - "@nocobase/plugin-map": "1.7.0-beta.18", - "@nocobase/plugin-mobile": "1.7.0-beta.18", - "@nocobase/plugin-mobile-client": "1.7.0-beta.18", - "@nocobase/plugin-mock-collections": "1.7.0-beta.18", - "@nocobase/plugin-multi-app-manager": "1.7.0-beta.18", - "@nocobase/plugin-multi-app-share-collection": "1.7.0-beta.18", - "@nocobase/plugin-notification-email": "1.7.0-beta.18", - "@nocobase/plugin-notification-in-app-message": "1.7.0-beta.18", - "@nocobase/plugin-notification-manager": "1.7.0-beta.18", - "@nocobase/plugin-public-forms": "1.7.0-beta.18", - "@nocobase/plugin-snapshot-field": "1.7.0-beta.18", - "@nocobase/plugin-system-settings": "1.7.0-beta.18", - "@nocobase/plugin-theme-editor": "1.7.0-beta.18", - "@nocobase/plugin-ui-schema-storage": "1.7.0-beta.18", - "@nocobase/plugin-user-data-sync": "1.7.0-beta.18", - "@nocobase/plugin-users": "1.7.0-beta.18", - "@nocobase/plugin-verification": "1.7.0-beta.18", - "@nocobase/plugin-workflow": "1.7.0-beta.18", - "@nocobase/plugin-workflow-action-trigger": "1.7.0-beta.18", - "@nocobase/plugin-workflow-aggregate": "1.7.0-beta.18", - "@nocobase/plugin-workflow-delay": "1.7.0-beta.18", - "@nocobase/plugin-workflow-dynamic-calculation": "1.7.0-beta.18", - "@nocobase/plugin-workflow-loop": "1.7.0-beta.18", - "@nocobase/plugin-workflow-mailer": "1.7.0-beta.18", - "@nocobase/plugin-workflow-manual": "1.7.0-beta.18", - "@nocobase/plugin-workflow-notification": "1.7.0-beta.18", - "@nocobase/plugin-workflow-parallel": "1.7.0-beta.18", - "@nocobase/plugin-workflow-request": "1.7.0-beta.18", - "@nocobase/plugin-workflow-response-message": "1.7.0-beta.18", - "@nocobase/plugin-workflow-sql": "1.7.0-beta.18", - "@nocobase/server": "1.7.0-beta.18", + "@nocobase/plugin-acl": "1.7.0-beta.26", + "@nocobase/plugin-action-bulk-edit": "1.7.0-beta.26", + "@nocobase/plugin-action-bulk-update": "1.7.0-beta.26", + "@nocobase/plugin-action-custom-request": "1.7.0-beta.26", + "@nocobase/plugin-action-duplicate": "1.7.0-beta.26", + "@nocobase/plugin-action-export": "1.7.0-beta.26", + "@nocobase/plugin-action-import": "1.7.0-beta.26", + "@nocobase/plugin-action-print": "1.7.0-beta.26", + "@nocobase/plugin-ai": "1.7.0-beta.26", + "@nocobase/plugin-api-doc": "1.7.0-beta.26", + "@nocobase/plugin-api-keys": "1.7.0-beta.26", + "@nocobase/plugin-async-task-manager": "1.7.0-beta.26", + "@nocobase/plugin-audit-logs": "1.7.0-beta.26", + "@nocobase/plugin-auth": "1.7.0-beta.26", + "@nocobase/plugin-auth-sms": "1.7.0-beta.26", + "@nocobase/plugin-backup-restore": "1.7.0-beta.26", + "@nocobase/plugin-block-iframe": "1.7.0-beta.26", + "@nocobase/plugin-block-template": "1.7.0-beta.26", + "@nocobase/plugin-block-workbench": "1.7.0-beta.26", + "@nocobase/plugin-calendar": "1.7.0-beta.26", + "@nocobase/plugin-charts": "1.7.0-beta.26", + "@nocobase/plugin-client": "1.7.0-beta.26", + "@nocobase/plugin-collection-sql": "1.7.0-beta.26", + "@nocobase/plugin-collection-tree": "1.7.0-beta.26", + "@nocobase/plugin-data-source-main": "1.7.0-beta.26", + "@nocobase/plugin-data-source-manager": "1.7.0-beta.26", + "@nocobase/plugin-data-visualization": "1.7.0-beta.26", + "@nocobase/plugin-departments": "1.7.0-beta.26", + "@nocobase/plugin-environment-variables": "1.7.0-beta.26", + "@nocobase/plugin-error-handler": "1.7.0-beta.26", + "@nocobase/plugin-field-attachment-url": "1.7.0-beta.26", + "@nocobase/plugin-field-china-region": "1.7.0-beta.26", + "@nocobase/plugin-field-formula": "1.7.0-beta.26", + "@nocobase/plugin-field-m2m-array": "1.7.0-beta.26", + "@nocobase/plugin-field-markdown-vditor": "1.7.0-beta.26", + "@nocobase/plugin-field-sequence": "1.7.0-beta.26", + "@nocobase/plugin-field-sort": "1.7.0-beta.26", + "@nocobase/plugin-file-manager": "1.7.0-beta.26", + "@nocobase/plugin-gantt": "1.7.0-beta.26", + "@nocobase/plugin-graph-collection-manager": "1.7.0-beta.26", + "@nocobase/plugin-kanban": "1.7.0-beta.26", + "@nocobase/plugin-locale-tester": "1.7.0-beta.26", + "@nocobase/plugin-localization": "1.7.0-beta.26", + "@nocobase/plugin-logger": "1.7.0-beta.26", + "@nocobase/plugin-map": "1.7.0-beta.26", + "@nocobase/plugin-mobile": "1.7.0-beta.26", + "@nocobase/plugin-mobile-client": "1.7.0-beta.26", + "@nocobase/plugin-mock-collections": "1.7.0-beta.26", + "@nocobase/plugin-multi-app-manager": "1.7.0-beta.26", + "@nocobase/plugin-multi-app-share-collection": "1.7.0-beta.26", + "@nocobase/plugin-notification-email": "1.7.0-beta.26", + "@nocobase/plugin-notification-in-app-message": "1.7.0-beta.26", + "@nocobase/plugin-notification-manager": "1.7.0-beta.26", + "@nocobase/plugin-public-forms": "1.7.0-beta.26", + "@nocobase/plugin-snapshot-field": "1.7.0-beta.26", + "@nocobase/plugin-system-settings": "1.7.0-beta.26", + "@nocobase/plugin-theme-editor": "1.7.0-beta.26", + "@nocobase/plugin-ui-schema-storage": "1.7.0-beta.26", + "@nocobase/plugin-user-data-sync": "1.7.0-beta.26", + "@nocobase/plugin-users": "1.7.0-beta.26", + "@nocobase/plugin-verification": "1.7.0-beta.26", + "@nocobase/plugin-workflow": "1.7.0-beta.26", + "@nocobase/plugin-workflow-action-trigger": "1.7.0-beta.26", + "@nocobase/plugin-workflow-aggregate": "1.7.0-beta.26", + "@nocobase/plugin-workflow-delay": "1.7.0-beta.26", + "@nocobase/plugin-workflow-dynamic-calculation": "1.7.0-beta.26", + "@nocobase/plugin-workflow-loop": "1.7.0-beta.26", + "@nocobase/plugin-workflow-mailer": "1.7.0-beta.26", + "@nocobase/plugin-workflow-manual": "1.7.0-beta.26", + "@nocobase/plugin-workflow-notification": "1.7.0-beta.26", + "@nocobase/plugin-workflow-parallel": "1.7.0-beta.26", + "@nocobase/plugin-workflow-request": "1.7.0-beta.26", + "@nocobase/plugin-workflow-response-message": "1.7.0-beta.26", + "@nocobase/plugin-workflow-sql": "1.7.0-beta.26", + "@nocobase/server": "1.7.0-beta.26", "cronstrue": "^2.11.0", "fs-extra": "^11.1.1" }, diff --git a/yarn.lock b/yarn.lock index a03e37b526..e2afcef6ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,15 +42,25 @@ ini "^1.3.5" kitx "^2.0.0" +"@alicloud/credentials@^2.4.2": + version "2.4.2" + resolved "https://registry.npmmirror.com/@alicloud/credentials/-/credentials-2.4.2.tgz#fa79602b03ba37a425f64732c8a58163a5bcbd0c" + integrity sha512-UbqUYlwOWKNxOemXM545HzQyCaChhyrne9cab4f67EqAkgrTjeMiTA7QK6sHtmcmJYowYQxxXoKPSe5GZstvbA== + dependencies: + "@alicloud/tea-typescript" "^1.8.0" + httpx "^2.3.3" + ini "^1.3.5" + kitx "^2.0.0" + "@alicloud/dingtalk@^2.1.23": - version "2.1.91" - resolved "https://registry.npmmirror.com/@alicloud/dingtalk/-/dingtalk-2.1.91.tgz#9c2aaf753c6b6c382506a45c52d296ba4f92a1e5" - integrity sha512-mmxz7JaEZRmGx2DKPrtNc4utWfy2z31HNxK8afezEnvKDybE77cpE/cPO9jS6f+sd6j9Sb0Obo370BjKLV0P1g== + version "2.1.98" + resolved "https://registry.npmmirror.com/@alicloud/dingtalk/-/dingtalk-2.1.98.tgz#2150efcd516191b0b152d298d004d970c4f72b89" + integrity sha512-qKlXk8D1E0506uLL2F3grTq/S2LmceRhVTM/WlGZLFtMMC/72QfYkQ77jqC/M8vDzj7POAXLE/7f5hwl3QuNwQ== dependencies: "@alicloud/endpoint-util" "^0.0.1" "@alicloud/gateway-dingtalk" "^1.0.2" "@alicloud/gateway-spi" "^0.0.8" - "@alicloud/openapi-client" "^0.4.12" + "@alicloud/openapi-client" "^0.4.14" "@alicloud/openapi-util" "^0.3.2" "@alicloud/tea-typescript" "^1.7.1" "@alicloud/tea-util" "^1.4.9" @@ -91,7 +101,7 @@ "@alicloud/credentials" "^2" "@alicloud/tea-typescript" "^1.7.1" -"@alicloud/openapi-client@0.4.12", "@alicloud/openapi-client@^0.4.1", "@alicloud/openapi-client@^0.4.12", "@alicloud/openapi-client@^0.4.7", "@alicloud/openapi-client@^0.4.8", "@alicloud/openapi-client@^0.4.9": +"@alicloud/openapi-client@0.4.12", "@alicloud/openapi-client@^0.4.1": version "0.4.12" resolved "https://registry.npmmirror.com/@alicloud/openapi-client/-/openapi-client-0.4.12.tgz#7a4f00e71b025d8a487cfbeeb8aa6f63d70b2054" integrity sha512-WuKfFqwY3/+wuNawzfJAirNA00XDI7fm9fUhWK7siGZEh0R2XJR0Y54MLW7WWItX06fAghUvnDhKWZH3AgN+yg== @@ -103,6 +113,18 @@ "@alicloud/tea-util" "^1.4.9" "@alicloud/tea-xml" "0.0.3" +"@alicloud/openapi-client@^0.4.14", "@alicloud/openapi-client@^0.4.7", "@alicloud/openapi-client@^0.4.8", "@alicloud/openapi-client@^0.4.9": + version "0.4.14" + resolved "https://registry.npmmirror.com/@alicloud/openapi-client/-/openapi-client-0.4.14.tgz#3f408a8e7e6ad7e1026ff96304fadaf9e8976e87" + integrity sha512-NiMDBszCyiH5HI9vHbkDhhDbFF3gMEJDHuPc2cAP0queLtrjPfU+d6/uhGVt44B9oC0q6f6vaJgptQ99fxxfnQ== + dependencies: + "@alicloud/credentials" "^2.4.2" + "@alicloud/gateway-spi" "^0.0.8" + "@alicloud/openapi-util" "^0.3.2" + "@alicloud/tea-typescript" "^1.7.1" + "@alicloud/tea-util" "1.4.9" + "@alicloud/tea-xml" "0.0.3" + "@alicloud/openapi-util@^0.2.9": version "0.2.9" resolved "https://registry.npmmirror.com/@alicloud/openapi-util/-/openapi-util-0.2.9.tgz#2379cd81f993dcab32066a2b892ddcbdd266d51c" @@ -139,7 +161,7 @@ "@alicloud/tea-typescript" "^1.5.1" kitx "^2.0.0" -"@alicloud/tea-util@^1.3.0", "@alicloud/tea-util@^1.4.4", "@alicloud/tea-util@^1.4.9": +"@alicloud/tea-util@1.4.9", "@alicloud/tea-util@^1.3.0", "@alicloud/tea-util@^1.4.4", "@alicloud/tea-util@^1.4.9": version "1.4.9" resolved "https://registry.npmmirror.com/@alicloud/tea-util/-/tea-util-1.4.9.tgz#aa1c4f566d530e7ffc6d9fac1aded06bb0ea45ca" integrity sha512-S0wz76rGtoPKskQtRTGqeuqBHFj8BqUn0Vh+glXKun2/9UpaaaWmuJwcmtImk6bJZfLYEShDF/kxDmDJoNYiTw== @@ -1496,9 +1518,9 @@ tslib "^2.6.2" "@azure/core-client@^1.3.0", "@azure/core-client@^1.5.0", "@azure/core-client@^1.9.2": - version "1.9.2" - resolved "https://registry.npmmirror.com/@azure/core-client/-/core-client-1.9.2.tgz#6fc69cee2816883ab6c5cdd653ee4f2ff9774f74" - integrity sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w== + version "1.9.3" + resolved "https://registry.npmmirror.com/@azure/core-client/-/core-client-1.9.3.tgz#9ca8f3bdc730d10d58f65c9c2c9ca992bc15bb67" + integrity sha512-/wGw8fJ4mdpJ1Cum7s1S+VQyXt1ihwKLzfabS1O/RDADnmzVc01dHn44qD0BvGH6KlZNzOMW95tEpKqhkCChPA== dependencies: "@azure/abort-controller" "^2.0.0" "@azure/core-auth" "^1.4.0" @@ -1535,9 +1557,9 @@ tslib "^2.6.2" "@azure/core-rest-pipeline@^1.17.0", "@azure/core-rest-pipeline@^1.19.0", "@azure/core-rest-pipeline@^1.8.0", "@azure/core-rest-pipeline@^1.8.1", "@azure/core-rest-pipeline@^1.9.1": - version "1.19.0" - resolved "https://registry.npmmirror.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.0.tgz#4cc60d3f2ee68cf0ef379851b4ed175f7932c8c5" - integrity sha512-bM3308LRyg5g7r3Twprtqww0R/r7+GyVxj4BafcmVPo4WQoGt5JXuaqxHEFjw2o3rvFZcUPiqJMg6WuvEEeVUA== + version "1.19.1" + resolved "https://registry.npmmirror.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.1.tgz#e740676444777a04dc55656d8660131dfd926924" + integrity sha512-zHeoI3NCs53lLBbWNzQycjnYKsA1CVKlnzSNuSFcUDwBp8HHVObePxrM7HaX+Ha5Ks639H7chNC9HOaIhNS03w== dependencies: "@azure/abort-controller" "^2.0.0" "@azure/core-auth" "^1.8.0" @@ -1564,9 +1586,9 @@ tslib "^2.6.2" "@azure/identity@^4.2.1": - version "4.7.0" - resolved "https://registry.npmmirror.com/@azure/identity/-/identity-4.7.0.tgz#b3bc57aec40432899108fd41177e8168e7bb6223" - integrity sha512-6z/S2KorkbKaZ0DgZFVRdu7RCuATmMSTjKpuhj7YpjxkJ0vnJ7kTM3cpNgzFgk9OPYfZ31wrBEtC/iwAS4jQDA== + version "4.9.0" + resolved "https://registry.npmmirror.com/@azure/identity/-/identity-4.9.0.tgz#226c21bcb706401d1e2425834158e448f8d01209" + integrity sha512-dz2ZvKxDFoTUmJgmkCBVcyuKckgqE1qVxrRPzUhyKN7FyvUbtNPUrGzqSllOAf1OL9TMGgYqZWbIyD0b/AE15g== dependencies: "@azure/abort-controller" "^2.0.0" "@azure/core-auth" "^1.9.0" @@ -1576,11 +1598,8 @@ "@azure/core-util" "^1.11.0" "@azure/logger" "^1.0.0" "@azure/msal-browser" "^4.2.0" - "@azure/msal-node" "^3.2.1" - events "^3.0.0" - jws "^4.0.0" + "@azure/msal-node" "^3.5.0" open "^10.1.0" - stoppable "^1.1.0" tslib "^2.2.0" "@azure/keyvault-common@^2.0.0": @@ -1623,21 +1642,21 @@ tslib "^2.6.2" "@azure/msal-browser@^4.2.0": - version "4.5.0" - resolved "https://registry.npmmirror.com/@azure/msal-browser/-/msal-browser-4.5.0.tgz#fbdc4f58f0f37a5487199f4706e5f8a04cd00234" - integrity sha512-H7mWmu8yI0n0XxhJobrgncXI6IU5h8DKMiWDHL5y+Dc58cdg26GbmaMUehbUkdKAQV2OTiFa4FUa6Fdu/wIxBg== + version "4.11.0" + resolved "https://registry.npmmirror.com/@azure/msal-browser/-/msal-browser-4.11.0.tgz#e9d9651d692969e68c78ef873ed9a69e02389a64" + integrity sha512-0p5Ut3wORMP+975AKvaSPIO4UytgsfAvJ7RxaTx+nkP+Hpkmm93AuiMkBWKI2x9tApU/SLgIyPz/ZwLYUIWb5Q== dependencies: - "@azure/msal-common" "15.2.0" + "@azure/msal-common" "15.5.1" "@azure/msal-common@14.16.0": version "14.16.0" resolved "https://registry.npmmirror.com/@azure/msal-common/-/msal-common-14.16.0.tgz#f3470fcaec788dbe50859952cd499340bda23d7a" integrity sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA== -"@azure/msal-common@15.2.0": - version "15.2.0" - resolved "https://registry.npmmirror.com/@azure/msal-common/-/msal-common-15.2.0.tgz#f4e38ba85c0a32208b7046e011c21ff627b6755c" - integrity sha512-HiYfGAKthisUYqHG1nImCf/uzcyS31wng3o+CycWLIM9chnYJ9Lk6jZ30Y6YiYYpTQ9+z/FGUpiKKekd3Arc0A== +"@azure/msal-common@15.5.1": + version "15.5.1" + resolved "https://registry.npmmirror.com/@azure/msal-common/-/msal-common-15.5.1.tgz#3b34c81013530e1425a1fad40f3ac1238e1780f8" + integrity sha512-oxK0khbc4Bg1bKQnqDr7ikULhVL2OHgSrIq0Vlh4b6+hm4r0lr6zPMQE8ZvmacJuh+ZZGKBM5iIObhF1q1QimQ== "@azure/msal-node@^2.15.0": version "2.16.2" @@ -1648,12 +1667,12 @@ jsonwebtoken "^9.0.0" uuid "^8.3.0" -"@azure/msal-node@^3.2.1": - version "3.2.3" - resolved "https://registry.npmmirror.com/@azure/msal-node/-/msal-node-3.2.3.tgz#c742d66d3a9183c1dfa4160632d8891c69af7bcc" - integrity sha512-0eaPqBIWEAizeYiXdeHb09Iq0tvHJ17ztvNEaLdr/KcJJhJxbpkkEQf09DB+vKlFE0tzYi7j4rYLTXtES/InEQ== +"@azure/msal-node@^3.5.0": + version "3.5.1" + resolved "https://registry.npmmirror.com/@azure/msal-node/-/msal-node-3.5.1.tgz#8bb233cbeeda83f64af4cc29569f1b5312c9b9ad" + integrity sha512-dkgMYM5B6tI88r/oqf5bYd93WkenQpaWwiszJDk7avVjso8cmuKRTW97dA1RMi6RhihZFLtY1VtWxU9+sW2T5g== dependencies: - "@azure/msal-common" "15.2.0" + "@azure/msal-common" "15.5.1" jsonwebtoken "^9.0.0" uuid "^8.3.0" @@ -2993,9 +3012,9 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime-corejs3@^7.15.4": - version "7.26.9" - resolved "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.26.9.tgz#8b73bae47453aa3dd2839ac52598581a7dd8332f" - integrity sha512-5EVjbTegqN7RSJle6hMWYxO4voo4rI+9krITk+DWR+diJgGrjZjrIBnJhjrHYYQsFgI7j1w1QnrvV7YSKBfYGg== + version "7.27.0" + resolved "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.27.0.tgz#c766df350ec7a2caf3ed64e3659b100954589413" + integrity sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew== dependencies: core-js-pure "^3.30.2" regenerator-runtime "^0.14.0" @@ -3204,17 +3223,7 @@ "@codemirror/view" "^6.17.0" "@lezer/common" "^1.0.0" -"@codemirror/commands@^6.0.0": - version "6.8.0" - resolved "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.8.0.tgz#92f200b66f852939bd6ebb90d48c2d9e9c813d64" - integrity sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ== - dependencies: - "@codemirror/language" "^6.0.0" - "@codemirror/state" "^6.4.0" - "@codemirror/view" "^6.27.0" - "@lezer/common" "^1.1.0" - -"@codemirror/commands@^6.1.0": +"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0": version "6.8.1" resolved "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.8.1.tgz#639f5559d2f33f2582a2429c58cb0c1b925c7a30" integrity sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw== @@ -3268,19 +3277,7 @@ "@lezer/highlight" "^1.0.0" "@lezer/lr" "^1.0.0" -"@codemirror/language@^6.0.0", "@codemirror/language@^6.6.0": - version "6.10.8" - resolved "https://registry.npmmirror.com/@codemirror/language/-/language-6.10.8.tgz#3e3a346a2b0a8cf63ee1cfe03349eb1965dce5f9" - integrity sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw== - dependencies: - "@codemirror/state" "^6.0.0" - "@codemirror/view" "^6.23.0" - "@lezer/common" "^1.1.0" - "@lezer/highlight" "^1.0.0" - "@lezer/lr" "^1.0.0" - style-mod "^4.0.0" - -"@codemirror/language@^6.11.0", "@codemirror/language@^6.8.0": +"@codemirror/language@^6.0.0", "@codemirror/language@^6.11.0", "@codemirror/language@^6.6.0", "@codemirror/language@^6.8.0": version "6.11.0" resolved "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.0.tgz#5ae90972601497f4575f30811519d720bf7232c9" integrity sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ== @@ -3300,9 +3297,9 @@ "@codemirror/language" "^6.0.0" "@codemirror/lint@^6.0.0", "@codemirror/lint@^6.8.2": - version "6.8.4" - resolved "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.8.4.tgz#7d8aa5d1a6dec89ffcc23ad45ddca2e12e90982d" - integrity sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A== + version "6.8.5" + resolved "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.8.5.tgz#9edaa808e764e28e07665b015951934c8ec3a418" + integrity sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA== dependencies: "@codemirror/state" "^6.0.0" "@codemirror/view" "^6.35.0" @@ -3335,9 +3332,9 @@ "@lezer/highlight" "^1.0.0" "@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.32.0", "@codemirror/view@^6.35.0": - version "6.36.4" - resolved "https://registry.npmmirror.com/@codemirror/view/-/view-6.36.4.tgz#d47d38b92a22cc40647bfb9cc97944e13d44942d" - integrity sha512-ZQ0V5ovw/miKEXTvjgzRyjnrk9TwriUB1k4R5p7uNnHR9Hus+D1SXHGdJshijEzPFjU25xea/7nhIeSqYFKdbA== + version "6.36.5" + resolved "https://registry.npmmirror.com/@codemirror/view/-/view-6.36.5.tgz#bb99b971322b9a3f8c7013f0ef6c4a511c0d750a" + integrity sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg== dependencies: "@codemirror/state" "^6.5.0" style-mod "^4.1.0" @@ -5096,6 +5093,13 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmmirror.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -5243,9 +5247,9 @@ "@jridgewell/sourcemap-codec" "^1.4.14" "@js-joda/core@^5.6.1": - version "5.6.4" - resolved "https://registry.npmmirror.com/@js-joda/core/-/core-5.6.4.tgz#abafce9b30302639bfe4a71bad5120fe39654f10" - integrity sha512-ChdLDTYMEoYoiKZMT90wZMEdGvZ2/QZMnhvjvEqeO5oLoxUfSiLzfe6Lhf3g88+MhZ+utbAu7PAxX1sZkLo5pA== + version "5.6.5" + resolved "https://registry.npmmirror.com/@js-joda/core/-/core-5.6.5.tgz#c766894b49eb8044480b91625fb7dc370e8182ef" + integrity sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ== "@jsep-plugin/assignment@^1.2.1", "@jsep-plugin/assignment@^1.3.0": version "1.3.0" @@ -5269,13 +5273,18 @@ dependencies: vary "^1.1.2" -"@koa/multer@^3.0.0", "@koa/multer@^3.0.2": +"@koa/multer@^3.0.2": version "3.0.2" resolved "https://registry.npmmirror.com/@koa/multer/-/multer-3.0.2.tgz#04ed1af502de5793a6052daf6c256e94ef13e3a4" integrity sha512-Q6WfPpE06mJWyZD1fzxM6zWywaoo+zocAn2YA9QYz4RsecoASr1h/kSzG0c5seDpFVKCMZM9raEfuM7XfqbRLw== dependencies: fix-esm "1.0.1" +"@koa/multer@^3.1.0": + version "3.1.0" + resolved "https://registry.npmmirror.com/@koa/multer/-/multer-3.1.0.tgz#64b12f173d9c8d88c4114574ed67d2c8f5acdba8" + integrity sha512-ETf4OLpOew9XE9lyU+5HIqk3TCmdGAw9pUXgxzrlYip+PkxLGoU4meiVTxiW4B6lxdBNijb3DFQ7M2woLcDL1g== + "@koa/router@^12.0.1": version "12.0.2" resolved "https://registry.npmmirror.com/@koa/router/-/router-12.0.2.tgz#286d51959ed611255faa944818a112e35567835a" @@ -5287,16 +5296,14 @@ methods "^1.1.2" path-to-regexp "^6.3.0" -"@koa/router@^9.4.0": - version "9.4.0" - resolved "https://registry.npmmirror.com/@koa/router/-/router-9.4.0.tgz#734b64c0ae566eb5af752df71e4143edc4748e48" - integrity sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A== +"@koa/router@^13.1.0": + version "13.1.0" + resolved "https://registry.npmmirror.com/@koa/router/-/router-13.1.0.tgz#43f4c554444ea4f4a148a5735a9525c6d16fd1b5" + integrity sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw== dependencies: - debug "^4.1.1" - http-errors "^1.7.3" + http-errors "^2.0.0" koa-compose "^4.1.0" - methods "^1.1.2" - path-to-regexp "^6.1.0" + path-to-regexp "^6.3.0" "@langchain/core@^0.3.39": version "0.3.42" @@ -6108,9 +6115,9 @@ "@lezer/lr" "^1.0.0" "@lezer/javascript@^1.0.0": - version "1.4.21" - resolved "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.4.21.tgz#8ebf7d1f891c70e3d00864f5a03ac42c75d19492" - integrity sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ== + version "1.5.0" + resolved "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.0.tgz#f332792c75af16809e09b6ed50cc7b69d25bd5e9" + integrity sha512-i/uZt1eoiojC3BRsjtiYZjT8DhzgZvWiKJjpXW3Jc2y4FkXY9YoBLKx+jSct+ynUINv5GtRxjTK7hBNhujPwrg== dependencies: "@lezer/common" "^1.2.0" "@lezer/highlight" "^1.1.3" @@ -6124,9 +6131,9 @@ "@lezer/common" "^1.0.0" "@lezer/python@^1.1.4": - version "1.1.17" - resolved "https://registry.npmmirror.com/@lezer/python/-/python-1.1.17.tgz#de0529e14605430ca8935fe4b3df0252ad173318" - integrity sha512-Iz0doICPko9uv2chIfSsViNSugNa4PWhxs17jtFd0ZMt+OieDq3wxtFOdmj7wtst3FWDeJkB0CxWNot0BlYixw== + version "1.1.18" + resolved "https://registry.npmmirror.com/@lezer/python/-/python-1.1.18.tgz#fa02fbf492741c82dc2dc98a0a042bd0d4d7f1d3" + integrity sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg== dependencies: "@lezer/common" "^1.2.0" "@lezer/highlight" "^1.0.0" @@ -6160,21 +6167,6 @@ resolved "https://registry.npmmirror.com/@makotot/ghostui/-/ghostui-2.0.0.tgz#ae035d405a9ed5100436158e953ed9480f1c09a7" integrity sha512-LD6OeMv+yGjpYZNjh34yDTCIE1NegqOtJq5gm4wX6op3QL7K5psTVzMjkWzseBoYj0XOD4g+UJVIZTprfoOPGg== -"@mapbox/node-pre-gyp@^1.0.0": - version "1.0.11" - resolved "https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@marijn/find-cluster-break@^1.0.0": version "1.0.2" resolved "https://registry.npmmirror.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8" @@ -8914,6 +8906,14 @@ dependencies: "@types/node" "*" +"@types/jsonwebtoken@^9.0.9": + version "9.0.9" + resolved "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz#a4c3a446c0ebaaf467a58398382616f416345fb3" + integrity sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ== + dependencies: + "@types/ms" "*" + "@types/node" "*" + "@types/keygrip@*": version "1.0.6" resolved "https://registry.npmmirror.com/@types/keygrip/-/keygrip-1.0.6.tgz#1749535181a2a9b02ac04a797550a8787345b740" @@ -8933,7 +8933,7 @@ dependencies: "@types/koa" "*" -"@types/koa-multer@^1.0.1": +"@types/koa-multer@^1.0.4": version "1.0.4" resolved "https://registry.npmmirror.com/@types/koa-multer/-/koa-multer-1.0.4.tgz#5576cb7aea56926e4d48faaa7b510b87bdfcb3db" integrity sha512-Aty7k5W/bVgvCtyc7iwzjP1QXp1CPsQE3klmIEVBphK2bthjJjUpgkvFNChckA8DPxaatzFmnotWZnXLAAnLog== @@ -9076,7 +9076,7 @@ resolved "https://registry.npmmirror.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/multer@^1.4.5": +"@types/multer@^1.4.12", "@types/multer@^1.4.5": version "1.4.12" resolved "https://registry.npmmirror.com/@types/multer/-/multer-1.4.12.tgz#da67bd0c809f3a63fe097c458c0d4af1fea50ab7" integrity sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg== @@ -9111,11 +9111,11 @@ undici-types "~6.20.0" "@types/node@>=18", "@types/node@^22.5.4": - version "22.13.9" - resolved "https://registry.npmmirror.com/@types/node/-/node-22.13.9.tgz#5d9a8f7a975a5bd3ef267352deb96fb13ec02eca" - integrity sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw== + version "22.14.1" + resolved "https://registry.npmmirror.com/@types/node/-/node-22.14.1.tgz#53b54585cec81c21eee3697521e31312d6ca1e6f" + integrity sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw== dependencies: - undici-types "~6.20.0" + undici-types "~6.21.0" "@types/node@^12.0.2": version "12.20.55" @@ -9181,9 +9181,9 @@ "@types/express" "*" "@types/pg@^8.10.9": - version "8.11.11" - resolved "https://registry.npmmirror.com/@types/pg/-/pg-8.11.11.tgz#3bdce0580e521a62a62339a53d110458d9eae6df" - integrity sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw== + version "8.11.13" + resolved "https://registry.npmmirror.com/@types/pg/-/pg-8.11.13.tgz#ab785528b173d9bf8623406d7611ee0c0c715914" + integrity sha512-6kXByGkvRvwXLuyaWzsebs2du6+XuAB2CuMsuzP7uaihQahshVgSmB22Pmh0vQMkQ1h5+PZU0d+Di1o+WpVWJg== dependencies: "@types/node" "*" pg-protocol "*" @@ -10797,7 +10797,7 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.npmmirror.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -"aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: +aproba@^2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== @@ -10882,22 +10882,6 @@ archy@^1.0.0: resolved "https://registry.npmmirror.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - -are-we-there-yet@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" - integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - are-we-there-yet@~1.1.2: version "1.1.7" resolved "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" @@ -11695,9 +11679,9 @@ bl@^4.0.3, bl@^4.1.0: readable-stream "^3.4.0" bl@^6.0.11: - version "6.0.20" - resolved "https://registry.npmmirror.com/bl/-/bl-6.0.20.tgz#70527afc27d2158b88db55a99ad8813ab7d82914" - integrity sha512-JMP0loH6ApbpT4Aa9oU5NqAkdDvcyc8koeuK8i5mYoBCVj3XCXG0uweGNN2m6DqaCO2yRHdm+MjCeTsR5VsmcA== + version "6.1.0" + resolved "https://registry.npmmirror.com/bl/-/bl-6.1.0.tgz#cc35ce7a2e8458caa8c8fb5deeed6537b73e4504" + integrity sha512-ClDyJGQkc8ZtzdAAbAwBmhMSpwN/sC9HA8jxdYm6nVUbCfZbe2mgza4qh7AuEYyEPB/c4Kznf9s66bnsKMQDjw== dependencies: "@types/readable-stream" "^4.0.0" buffer "^6.0.3" @@ -12100,14 +12084,6 @@ bundle-require@^5.0.0: dependencies: load-tsconfig "^0.2.3" -busboy@^0.2.11: - version "0.2.14" - resolved "https://registry.npmmirror.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" - integrity sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg== - dependencies: - dicer "0.2.5" - readable-stream "1.1.x" - busboy@^1.0.0: version "1.6.0" resolved "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -12662,6 +12638,11 @@ chownr@^2.0.0: resolved "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + ci-info@^1.5.0: version "1.6.0" resolved "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" @@ -13089,7 +13070,7 @@ color-string@^1.5.5, color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.2, color-support@^1.1.3: +color-support@^1.1.3: version "1.1.3" resolved "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== @@ -13394,7 +13375,7 @@ console-browserify@^1.1.0: resolved "https://registry.npmmirror.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== @@ -15042,11 +15023,6 @@ detect-libc@^1.0.3: resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== -detect-libc@^2.0.0: - version "2.0.2" - resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" - integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== - detect-newline@^4.0.0: version "4.0.1" resolved "https://registry.npmmirror.com/detect-newline/-/detect-newline-4.0.1.tgz#fcefdb5713e1fb8cb2839b8b6ee22e6716ab8f23" @@ -15070,14 +15046,6 @@ dezalgo@^1.0.0, dezalgo@^1.0.4: asap "^2.0.0" wrappy "1" -dicer@0.2.5: - version "0.2.5" - resolved "https://registry.npmmirror.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" - integrity sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg== - dependencies: - readable-stream "1.1.x" - streamsearch "0.1.2" - diff-match-patch@^1.0.5: version "1.0.5" resolved "https://registry.npmmirror.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" @@ -17671,35 +17639,6 @@ functions-have-names@^1.2.3: resolved "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.npmmirror.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - gauge@~2.7.3: version "2.7.4" resolved "https://registry.npmmirror.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -19153,7 +19092,7 @@ httpx@^2.2.0, httpx@^2.2.6: "@types/node" "^20" debug "^4.1.1" -httpx@^2.3.2: +httpx@^2.3.2, httpx@^2.3.3: version "2.3.3" resolved "https://registry.npmmirror.com/httpx/-/httpx-2.3.3.tgz#353d3d9161b7cc2be4c638873f2fe6b319354c89" integrity sha512-k1qv94u1b6e+XKCxVbLgYlOypVP9MPGpnN5G/vxFf6tDO4V3xpz3d6FUOY/s8NtPgaq5RBVVgSB+7IHpVxMYzw== @@ -19504,9 +19443,9 @@ invert-kv@^1.0.0: integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ== ioredis@^5.4.1: - version "5.5.0" - resolved "https://registry.npmmirror.com/ioredis/-/ioredis-5.5.0.tgz#ff2332e125ca2ac8e15472ddd14ecdffa6484a2a" - integrity sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw== + version "5.6.1" + resolved "https://registry.npmmirror.com/ioredis/-/ioredis-5.6.1.tgz#1ed7dc9131081e77342503425afceaf7357ae599" + integrity sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA== dependencies: "@ioredis/commands" "^1.1.1" cluster-key-slot "^1.1.0" @@ -20769,23 +20708,7 @@ jsonpath-plus@^9.0.0: "@jsep-plugin/regex" "^1.0.3" jsep "^1.3.8" -jsonwebtoken@^8.5.1: - version "8.5.1" - resolved "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^5.6.0" - -jsonwebtoken@^9.0.0: +jsonwebtoken@^9.0.0, jsonwebtoken@^9.0.2: version "9.0.2" resolved "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== @@ -21908,7 +21831,7 @@ make-fetch-happen@^8.0.9: socks-proxy-agent "^5.0.0" ssri "^8.0.0" -make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: +make-fetch-happen@^9.0.1: version "9.1.0" resolved "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== @@ -21985,9 +21908,9 @@ mariadb@^2.5.6: please-upgrade-node "^3.2.0" mariadb@^3.3.0: - version "3.4.0" - resolved "https://registry.npmmirror.com/mariadb/-/mariadb-3.4.0.tgz#3dc2e918f000b39df9e1c2139604a4b2ebaa8d1c" - integrity sha512-hdRPcAzs+MTxK5VG1thBW18gGTlw6yWBe9YnLB65GLo7q0fO5DWsgomIevV/pXSaWRmD3qi6ka4oSFRTExRiEQ== + version "3.4.1" + resolved "https://registry.npmmirror.com/mariadb/-/mariadb-3.4.1.tgz#7444ee942410bd48f0a713d02b1539785c3c83d8" + integrity sha512-h2q6JREwHrzPx7bVVdHCkMGCbCXZmUk6WhDPcNrmhgGoYe15Cbs24Giq/m43oXFO/6Gqz4MTaRrnipWCgtgetQ== dependencies: "@types/geojson" "^7946.0.14" "@types/node" "^22.5.4" @@ -22958,7 +22881,7 @@ minipass@^5.0.0: resolved "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -22978,6 +22901,13 @@ minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" +minizlib@^3.0.1: + version "3.0.2" + resolved "https://registry.npmmirror.com/minizlib/-/minizlib-3.0.2.tgz#f33d638eb279f664439aa38dc5f91607468cb574" + integrity sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA== + dependencies: + minipass "^7.1.2" + mississippi@^1.2.0, mississippi@^1.3.0: version "1.3.1" resolved "https://registry.npmmirror.com/mississippi/-/mississippi-1.3.1.tgz#2a8bb465e86550ac8b36a7b6f45599171d78671e" @@ -23049,6 +22979,11 @@ mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: dependencies: minimist "^1.2.6" +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + mlly@^1.2.0, mlly@^1.4.2: version "1.4.2" resolved "https://registry.npmmirror.com/mlly/-/mlly-1.4.2.tgz#7cf406aa319ff6563d25da6b36610a93f2a8007e" @@ -23088,6 +23023,13 @@ module-details-from-path@^1.0.3: resolved "https://registry.npmmirror.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== +moment-timezone@0.5.48, moment-timezone@^0.5.45: + version "0.5.48" + resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.48.tgz#111727bb274734a518ae154b5ca589283f058967" + integrity sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw== + dependencies: + moment "^2.29.4" + moment-timezone@^0.5.34, moment-timezone@^0.5.40, moment-timezone@^0.5.43: version "0.5.43" resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790" @@ -23095,13 +23037,6 @@ moment-timezone@^0.5.34, moment-timezone@^0.5.40, moment-timezone@^0.5.43: dependencies: moment "^2.29.4" -moment-timezone@^0.5.45: - version "0.5.47" - resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.47.tgz#d4d1a21b78372d914d6d69ae285454732a429749" - integrity sha512-UbNt/JAWS0m/NJOebR0QMRHBk0hu03r5dx9GK8Cs0AS3I81yDcOc9k+DytPItgVvBP7J6Mf6U2n3BPAacAV9oA== - dependencies: - moment "^2.29.4" - moment@^2.29.1, moment@^2.29.4: version "2.29.4" resolved "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" @@ -23171,24 +23106,10 @@ multer-s3@^3.0.1: html-comment-regex "^1.1.2" run-parallel "^1.1.6" -multer@^1.4.2: - version "1.4.4" - resolved "https://registry.npmmirror.com/multer/-/multer-1.4.4.tgz#e2bc6cac0df57a8832b858d7418ccaa8ebaf7d8c" - integrity sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw== - dependencies: - append-field "^1.0.0" - busboy "^0.2.11" - concat-stream "^1.5.2" - mkdirp "^0.5.4" - object-assign "^4.1.1" - on-finished "^2.3.0" - type-is "^1.6.4" - xtend "^4.0.0" - -multer@^1.4.5-lts.1: - version "1.4.5-lts.1" - resolved "https://registry.npmmirror.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" - integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== +multer@^1.4.5-lts.2: + version "1.4.5-lts.2" + resolved "https://registry.npmmirror.com/multer/-/multer-1.4.5-lts.2.tgz#340af065d8685dda846ec9e3d7655fcd50afba2d" + integrity sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A== dependencies: append-field "^1.0.0" busboy "^1.0.0" @@ -23291,12 +23212,12 @@ nanoid@^2.1.0: resolved "https://registry.npmmirror.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== -nanoid@^3.3.11: +nanoid@^3.3.11, nanoid@^3.3.6: version "3.3.11" resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== -nanoid@^3.3.6, nanoid@^3.3.7: +nanoid@^3.3.7: version "3.3.7" resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== @@ -23402,11 +23323,6 @@ node-abort-controller@^3.0.1: resolved "https://registry.npmmirror.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== -node-addon-api@^4.2.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== - node-domexception@1.0.0, node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -23437,22 +23353,6 @@ node-fetch@^3.2.0: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-gyp@8.x: - version "8.4.1" - resolved "https://registry.npmmirror.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - node-gyp@^10.2.0: version "10.3.1" resolved "https://registry.npmmirror.com/node-gyp/-/node-gyp-10.3.1.tgz#1dd1a1a1c6c5c59da1a76aea06a062786b2c8a1a" @@ -23808,26 +23708,6 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.npmmirror.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - -npmlog@^6.0.0: - version "6.0.2" - resolved "https://registry.npmmirror.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== - dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.3" - set-blocking "^2.0.0" - nprogress@^0.2.0: version "0.2.0" resolved "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" @@ -24046,9 +23926,9 @@ oidc-token-hash@^5.0.3: integrity sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA== ollama@^0.5.12: - version "0.5.14" - resolved "https://registry.npmmirror.com/ollama/-/ollama-0.5.14.tgz#bf1b72998491636ee89766bb0e3163e3c50a6970" - integrity sha512-pvOuEYa2WkkAumxzJP0RdEYHkbZ64AYyyUszXVX7ruLvk5L+EiO2G71da2GqEQ4IAk4j6eLoUbGk5arzFT1wJA== + version "0.5.15" + resolved "https://registry.npmmirror.com/ollama/-/ollama-0.5.15.tgz#34a4549af3c4b819d39c81d313d911f6b8a9ef6e" + integrity sha512-TSaZSJyP7MQJFjSmmNsoJiriwa3U+/UJRw6+M8aucs5dTsaWNZsBIGpDb5rXnW6nXxJBB/z79gZY8IaiIQgelQ== dependencies: whatwg-fetch "^3.6.20" @@ -24125,9 +24005,9 @@ only@~0.0.2: integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== open@^10.1.0: - version "10.1.0" - resolved "https://registry.npmmirror.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" - integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + version "10.1.1" + resolved "https://registry.npmmirror.com/open/-/open-10.1.1.tgz#5fd814699e47ae3e1a09962d39f4f4441cae6c22" + integrity sha512-zy1wx4+P3PfhXSEPJNtZmJXfhkkIaxU1VauWIrDZw1O7uJRDRJtKr9n3Ic4NgbA16KyOxOXO2ng9gYwCdXuSXA== dependencies: default-browser "^5.2.1" define-lazy-prop "^3.0.0" @@ -24893,21 +24773,11 @@ path-to-regexp@3.3.0: resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b" integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw== -path-to-regexp@6.2.2: - version "6.2.2" - resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36" - integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw== - path-to-regexp@8.2.0: version "8.2.0" resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== -path-to-regexp@^6.1.0: - version "6.2.1" - resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" - integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== - path-to-regexp@^6.3.0: version "6.3.0" resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" @@ -25015,9 +24885,9 @@ pg-pool@^3.6.1: integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og== pg-protocol@*: - version "1.7.1" - resolved "https://registry.npmmirror.com/pg-protocol/-/pg-protocol-1.7.1.tgz#aad61a6f927b51e89dcf721408b76c0e536d43dc" - integrity sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ== + version "1.8.0" + resolved "https://registry.npmmirror.com/pg-protocol/-/pg-protocol-1.8.0.tgz#c707101dd07813868035a44571488e4b98639d48" + integrity sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g== pg-protocol@^1.6.0: version "1.6.0" @@ -25958,9 +25828,9 @@ postgres-array@~2.0.0: integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== postgres-array@~3.0.1: - version "3.0.2" - resolved "https://registry.npmmirror.com/postgres-array/-/postgres-array-3.0.2.tgz#68d6182cb0f7f152a7e60dc6a6889ed74b0a5f98" - integrity sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog== + version "3.0.4" + resolved "https://registry.npmmirror.com/postgres-array/-/postgres-array-3.0.4.tgz#4efcaf4d2c688d8bcaa8620ed13f35f299f7528c" + integrity sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ== postgres-bytea@~1.0.0: version "1.0.0" @@ -27457,16 +27327,6 @@ readable-stream@1.1: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - "readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: version "3.6.2" resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -28628,9 +28488,9 @@ sequelize@^6.26.0: wkx "^0.5.0" sequelize@^6.35.0: - version "6.37.5" - resolved "https://registry.npmmirror.com/sequelize/-/sequelize-6.37.5.tgz#2711ab97d0e0fe49c652368946a7312eb0f11cd7" - integrity sha512-10WA4poUb3XWnUROThqL2Apq9C2NhyV1xHPMZuybNMCucDsbbFuKg51jhmyvvAUyUqCiimwTZamc3AHhMoBr2Q== + version "6.37.7" + resolved "https://registry.npmmirror.com/sequelize/-/sequelize-6.37.7.tgz#55a6f8555ae76c1fbd4bce76b2ac5fcc0a1e6eb6" + integrity sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA== dependencies: "@types/debug" "^4.1.8" "@types/validator" "^13.7.17" @@ -29345,17 +29205,6 @@ sprintf-js@~1.0.2: resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sqlite3@^5.0.8: - version "5.1.6" - resolved "https://registry.npmmirror.com/sqlite3/-/sqlite3-5.1.6.tgz#1d4fbc90fe4fbd51e952e0a90fd8f6c2b9098e97" - integrity sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - node-addon-api "^4.2.0" - tar "^6.1.11" - optionalDependencies: - node-gyp "8.x" - sqlstring@^2.3.2: version "2.3.3" resolved "https://registry.npmmirror.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" @@ -29471,11 +29320,6 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -stoppable@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" - integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== - stream-browserify@3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" @@ -29537,11 +29381,6 @@ stream-wormhole@^1.0.4: resolved "https://registry.npmmirror.com/stream-wormhole/-/stream-wormhole-1.1.0.tgz#300aff46ced553cfec642a05251885417693c33d" integrity sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew== -streamsearch@0.1.2: - version "0.1.2" - resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA== - streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -30211,18 +30050,6 @@ tar-stream@^3.1.5: fast-fifo "^1.2.0" streamx "^2.15.0" -tar@6.1.11: - version "6.1.11" - resolved "https://registry.npmmirror.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - tar@^4.4.12: version "4.4.19" resolved "https://registry.npmmirror.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" @@ -30236,7 +30063,7 @@ tar@^4.4.12: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.13, tar@^6.1.2, tar@^6.2.0: +tar@^6.0.2, tar@^6.1.0, tar@^6.1.11: version "6.2.0" resolved "https://registry.npmmirror.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== @@ -30248,7 +30075,7 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.13, tar@^6.1.2, tar@^6.2.0: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.2.1: +tar@^6.1.13, tar@^6.2.1: version "6.2.1" resolved "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -30260,6 +30087,18 @@ tar@^6.2.1: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^7.4.3: + version "7.4.3" + resolved "https://registry.npmmirror.com/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571" + integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.0.1" + mkdirp "^3.0.1" + yallist "^5.0.0" + tedious@^18.2.4: version "18.6.1" resolved "https://registry.npmmirror.com/tedious/-/tedious-18.6.1.tgz#1c4a3f06c891be67a032117e2e25193286d44496" @@ -31260,6 +31099,11 @@ undici-types@~6.20.0: resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + unescape@^1.0.1: version "1.0.1" resolved "https://registry.npmmirror.com/unescape/-/unescape-1.0.1.tgz#956e430f61cad8a4d57d82c518f5e6cc5d0dda96" @@ -32290,7 +32134,7 @@ why-is-node-running@^2.2.2: siginfo "^2.0.0" stackback "0.0.2" -wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: +wide-align@^1.1.0: version "1.1.5" resolved "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -32543,9 +32387,9 @@ xlsx@^0.17.0: resolved "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz#0f64eeed3f1a46e64724620c3553f2dbd3cd2d7d" xml-crypto@^3.0.1: - version "3.2.0" - resolved "https://registry.npmmirror.com/xml-crypto/-/xml-crypto-3.2.0.tgz#a9debab572c8e895cff5fb351a8d8be3f6e1962e" - integrity sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg== + version "3.2.1" + resolved "https://registry.npmmirror.com/xml-crypto/-/xml-crypto-3.2.1.tgz#df2511a95275b43e18924693ff932af7de3217a4" + integrity sha512-0GUNbPtQt+PLMsC5HoZRONX+K6NBJEqpXe/lsvrFj0EqfpGPpVfJKGE7a5jCg8s2+Wkrf/2U1G41kIH+zC9eyQ== dependencies: "@xmldom/xmldom" "^0.8.8" xpath "0.0.32" @@ -32667,6 +32511,11 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: resolved "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== + yaml@2.3.1: version "2.3.1" resolved "https://registry.npmmirror.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" @@ -32854,11 +32703,16 @@ zip-stream@^5.0.1: compress-commons "^5.0.1" readable-stream "^3.6.0" -zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.24.1: +zod-to-json-schema@^3.22.3: version "3.24.3" resolved "https://registry.npmmirror.com/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz#5958ba111d681f8d01c5b6b647425c9b8a6059e7" integrity sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A== +zod-to-json-schema@^3.24.1: + version "3.24.5" + resolved "https://registry.npmmirror.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3" + integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g== + zod@^3.22.4, zod@^3.24.1: version "3.24.2" resolved "https://registry.npmmirror.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"