Merge branch 'next' into T-4910
@ -37,7 +37,7 @@ CLUSTER_MODE=
|
||||
|
||||
################# DATABASE #################
|
||||
|
||||
# postgres | msysql | mariadb | sqlite
|
||||
# postgres | mysql | mariadb | sqlite
|
||||
DB_DIALECT=postgres
|
||||
DB_TABLE_PREFIX=
|
||||
DB_HOST=localhost
|
||||
|
22
.github/workflows/auto-merge.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: auto-merge
|
||||
name: Auto merge main -> next
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -13,17 +13,29 @@ jobs:
|
||||
push-commit:
|
||||
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: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: nocobase/nocobase
|
||||
ssh-key: ${{ secrets.NOCOBASE_DEPLOY_KEY }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
fetch-depth: 0
|
||||
- name: main -> next(nocobase)
|
||||
run: |
|
||||
git config --global user.email "actions@github.com"
|
||||
git config --global user.name "GitHub Actions Bot"
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout next
|
||||
@ -33,7 +45,7 @@ jobs:
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
branch: next
|
||||
ssh: true
|
||||
github_token: ${{ steps.app-token.outputs.token }}
|
||||
repository: nocobase/nocobase
|
||||
tags: true
|
||||
atomic: true
|
||||
|
4
.github/workflows/build-docker-image.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build Docker Image
|
||||
name: Build docker image
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -93,7 +93,7 @@ jobs:
|
||||
file: Dockerfile
|
||||
build-args: |
|
||||
VERDACCIO_URL=http://localhost:4873/
|
||||
COMMIT_HASH=${GITHUB_SHA}
|
||||
COMMIT_HASH=${{ github.sha }}
|
||||
push: true
|
||||
tags: ${{ steps.set-tags.outputs.tags }}
|
||||
|
||||
|
51
.github/workflows/build-pro-image.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build Pro Image
|
||||
name: Build pro image
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -12,16 +12,12 @@ on:
|
||||
- 'next'
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile.pro'
|
||||
- '.github/workflows/build-pro-image.yml'
|
||||
|
||||
jobs:
|
||||
app-token:
|
||||
if: github.event.pull_request.head.repo.fork != true
|
||||
uses: nocobase/nocobase/.github/workflows/get-nocobase-app-token.yml@main
|
||||
secrets: inherit
|
||||
build-and-push:
|
||||
needs: app-token
|
||||
if: github.event.pull_request.head.repo.fork != true
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
verdaccio:
|
||||
@ -29,14 +25,21 @@ jobs:
|
||||
ports:
|
||||
- 4873:4873
|
||||
steps:
|
||||
- name: Decrypt app token
|
||||
id: app-token
|
||||
shell: bash
|
||||
- name: Get pro plugins
|
||||
id: get-pro-plugins
|
||||
run: |
|
||||
ENCRYPTED_SECRET=${{ needs.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$ENCRYPTED_SECRET" | base64 --decode);
|
||||
APP_TOKEN=$(echo -n "$BINARY_ENCRYPTED_SECRET" | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
echo "token=$APP_TOKEN" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.head_ref || github.ref_name }}" == "main" ]]; then
|
||||
echo "proRepos=$(echo '${{ vars.PRO_PLUGIN_REPOS }}')" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "proRepos=$(echo '${{ vars.NEXT_PRO_PLUGIN_REPOS }}')" >> $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-pro-plugins.outputs.proRepos), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@ -50,13 +53,6 @@ jobs:
|
||||
path: packages/pro-plugins
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
git clone -b main https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
- run: |
|
||||
cd packages/pro-plugins &&
|
||||
if git show-ref --quiet refs/remotes/origin/${{ github.head_ref || github.ref_name }}; then
|
||||
@ -68,8 +64,15 @@ jobs:
|
||||
git checkout main
|
||||
fi
|
||||
fi
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(steps.get-pro-plugins.outputs.proRepos), ' ') }}
|
||||
do
|
||||
git clone -b main https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
- run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(steps.get-pro-plugins.outputs.proRepos), ' ') }}
|
||||
do
|
||||
cd ./packages/pro-plugins/@nocobase/$repo
|
||||
if git show-ref --quiet refs/remotes/origin/${{ github.head_ref || github.ref_name }}; then
|
||||
@ -86,7 +89,7 @@ jobs:
|
||||
- name: rm .git
|
||||
run: |
|
||||
rm -rf packages/pro-plugins/.git
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(steps.get-pro-plugins.outputs.proRepos), ' ') }}
|
||||
do
|
||||
rm -rf packages/pro-plugins/@nocobase/$repo/.git
|
||||
done
|
||||
@ -137,7 +140,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
file: Dockerfile.pro
|
||||
build-args: |
|
||||
VERDACCIO_URL=http://localhost:4873/
|
||||
COMMIT_HASH=${GITHUB_SHA}
|
||||
|
82
.github/workflows/changelog-and-release.yml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
name: Write changelog and create release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
type: choice
|
||||
description: Please choose a version
|
||||
options:
|
||||
- beta
|
||||
- alpha
|
||||
default: beta
|
||||
push:
|
||||
tags:
|
||||
- 'v*-beta'
|
||||
|
||||
jobs:
|
||||
write-changelog-and-release:
|
||||
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: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: nocobase/nocobase
|
||||
ref: main
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
fetch-depth: 0
|
||||
- name: Checkout pro-plugins
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: nocobase/pro-plugins
|
||||
path: packages/pro-plugins
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
git clone -b main https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
- name: Set user
|
||||
run: |
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
- name: Set Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Run script
|
||||
shell: bash
|
||||
run: |
|
||||
node scripts/release/changelogAndRelease.js --ver ${{ inputs.version }} --cmsURL ${{ secrets.CMS_URL }} --cmsToken ${{ secrets.CMS_TOKEN }}
|
||||
env:
|
||||
PRO_PLUGIN_REPOS: ${{ vars.PRO_PLUGIN_REPOS }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git pull origin main
|
||||
git add .
|
||||
git commit -m "docs: update changelogs"
|
||||
git push origin main
|
56
.github/workflows/deploy-client-docs.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: deploy client docs
|
||||
name: Deploy client docs
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -25,30 +25,30 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- name: Build zh-CN
|
||||
run: yarn doc build core/client --lang=zh-CN
|
||||
- name: Build en-US
|
||||
run: yarn doc build core/client --lang=en-US
|
||||
- name: Set tags
|
||||
id: set-tags
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "::set-output name=tags::${{ github.ref_name }}"
|
||||
else
|
||||
echo "::set-output name=tags::pr-${{ github.event.pull_request.number }}"
|
||||
fi
|
||||
- name: copy files via ssh - ${{ steps.set-tags.outputs.tags }}
|
||||
uses: appleboy/scp-action@v0.1.4
|
||||
with:
|
||||
host: ${{ secrets.CN_CLIENT_HOST }}
|
||||
username: ${{ secrets.CN_CLIENT_USERNAME }}
|
||||
key: ${{ secrets.CN_CLIENT_KEY }}
|
||||
port: ${{ secrets.CN_CLIENT_PORT }}
|
||||
source: "packages/core/client/dist/*"
|
||||
target: ${{ secrets.CN_CLIENT_TARGET }}/${{ steps.set-tags.outputs.tags }}
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- name: Build zh-CN
|
||||
run: yarn doc build core/client --lang=zh-CN
|
||||
- name: Build en-US
|
||||
run: yarn doc build core/client --lang=en-US
|
||||
- name: Set tags
|
||||
id: set-tags
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "::set-output name=tags::${{ github.ref_name }}"
|
||||
else
|
||||
echo "::set-output name=tags::pr-${{ github.event.pull_request.number }}"
|
||||
fi
|
||||
- name: copy files via ssh - ${{ steps.set-tags.outputs.tags }}
|
||||
uses: appleboy/scp-action@v0.1.4
|
||||
with:
|
||||
host: ${{ secrets.CN_CLIENT_HOST }}
|
||||
username: ${{ secrets.CN_CLIENT_USERNAME }}
|
||||
key: ${{ secrets.CN_CLIENT_KEY }}
|
||||
port: ${{ secrets.CN_CLIENT_PORT }}
|
||||
source: 'packages/core/client/dist/*'
|
||||
target: ${{ secrets.CN_CLIENT_TARGET }}/${{ steps.set-tags.outputs.tags }}
|
||||
|
40
.github/workflows/get-nocobase-app-token.yml
vendored
@ -1,40 +0,0 @@
|
||||
name: Get nocobase app github token
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
token:
|
||||
value: ${{ jobs.get-app-token.outputs.token }}
|
||||
user-id:
|
||||
value: ${{ jobs.get-app-token.outputs.user-id }}
|
||||
app-slug:
|
||||
value: ${{ jobs.get-app-token.outputs.app-slug }}
|
||||
|
||||
jobs:
|
||||
get-app-token:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
token: ${{ steps.encrypt-token.outputs.token }}
|
||||
app-slug: ${{ steps.app-token.outputs.app-slug }}
|
||||
user-id: ${{ steps.get-user-id.outputs.user-id }}
|
||||
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: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Encrypt token
|
||||
id: encrypt-token
|
||||
shell: bash
|
||||
run: |
|
||||
APP_TOKEN=${{ steps.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$APP_TOKEN" | openssl enc -aes-256-cbc -pbkdf2 -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
ENCRYPTED_SECRET=$(echo -n "$BINARY_ENCRYPTED_SECRET" | base64 -w 0);
|
||||
echo "token=$ENCRYPTED_SECRET" >> $GITHUB_OUTPUT
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
@ -1,4 +1,4 @@
|
||||
name: manual-build-pr-docker-image
|
||||
name: Manual build pr docker image
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
35
.github/workflows/manual-build-pro-image.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: manual-build-pro-image
|
||||
name: Manual build pro image
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -16,12 +16,8 @@ on:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
app-token:
|
||||
if: github.event.pull_request.head.repo.fork != true
|
||||
uses: nocobase/nocobase/.github/workflows/get-nocobase-app-token.yml@main
|
||||
secrets: inherit
|
||||
build-and-push:
|
||||
needs: app-token
|
||||
if: github.event.pull_request.head.repo.fork != true
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
verdaccio:
|
||||
@ -29,14 +25,21 @@ jobs:
|
||||
ports:
|
||||
- 4873:4873
|
||||
steps:
|
||||
- name: Decrypt app token
|
||||
id: app-token
|
||||
shell: bash
|
||||
- name: Get pro plugins
|
||||
id: get-pro-plugins
|
||||
run: |
|
||||
ENCRYPTED_SECRET=${{ needs.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$ENCRYPTED_SECRET" | base64 --decode);
|
||||
APP_TOKEN=$(echo -n "$BINARY_ENCRYPTED_SECRET" | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
echo "token=$APP_TOKEN" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.head_ref || github.ref_name }}" == "main" ]]; then
|
||||
echo "proRepos=$(echo '${{ vars.PRO_PLUGIN_REPOS }}')" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "proRepos=$(echo '${{ vars.NEXT_PRO_PLUGIN_REPOS }}')" >> $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-pro-plugins.outputs.proRepos), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@ -59,14 +62,14 @@ jobs:
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(steps.get-pro-plugins.outputs.proRepos), ' ') }}
|
||||
do
|
||||
git clone -b ${{ steps.set_pro_pr_branch.outputs.pr_branch || 'main' }} https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
git clone -b ${{ github.head_ref || github.ref_name }} https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
- name: rm .git
|
||||
run: |
|
||||
rm -rf packages/pro-plugins/.git
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(steps.get-pro-plugins.outputs.proRepos), ' ') }}
|
||||
do
|
||||
rm -rf packages/pro-plugins/@nocobase/$repo/.git
|
||||
done
|
||||
|
131
.github/workflows/manual-build-pro-plugin-image.yml
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
name: Build pro plugin docker image
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pro_plugin:
|
||||
description: 'Please enter a pro plugin name'
|
||||
required: true
|
||||
pr_number:
|
||||
description: 'Please enter the pr number of pro plugin repository'
|
||||
required: false
|
||||
nocobase_pr_number:
|
||||
description: 'Please enter the pr number of nocobase/nocobase repository'
|
||||
required: false
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
verdaccio:
|
||||
image: verdaccio/verdaccio:latest
|
||||
ports:
|
||||
- 4873:4873
|
||||
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: nocobase,pro-plugins,plugin-${{ inputs.pro_plugin }},${{ join(fromJSON(vars.NEXT_PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Checkout nocobase/nocobase
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
fetch-depth: 0
|
||||
- name: Checkout nocobase/nocobase pr
|
||||
if: ${{ inputs.nocobase_pr_number != '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
gh pr checkout ${{ inputs.nocobase_pr_number }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Checkout plugin
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
repository: nocobase/plugin-${{ inputs.pro_plugin }}
|
||||
path: packages/pro-plugins/@nocobase/plugin-${{ inputs.pro_plugin }}
|
||||
- name: Checkout pr
|
||||
if: ${{ inputs.pr_number != '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd ./packages/pro-plugins/@nocobase/plugin-${{ inputs.pro_plugin }}
|
||||
gh pr checkout ${{ inputs.pr_number }}
|
||||
cd ../../../../
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: rm .git
|
||||
run: rm -rf packages/pro-plugins/@nocobase/plugin-${{ inputs.pro_plugin }}/.git && git config --global user.email "you@example.com" && git config --global user.name "Your Name" && git add -A && git commit -m "tmp commit"
|
||||
- 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: 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: Get tag
|
||||
id: get-tag
|
||||
run: |
|
||||
if [ "${{ inputs.pr_number }}" != "" ]; then
|
||||
echo "tag=pr-${{ inputs.pr_number }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=${{ github.head_ref || github.ref_name }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Set tags
|
||||
id: set-tags
|
||||
run: |
|
||||
echo "::set-output name=tags::${{ secrets.ALI_DOCKER_REGISTRY }}/nocobase/nocobase:${{ steps.get-tag.outputs.tag }}-${{ inputs.pro_plugin }}"
|
||||
- name: IMAGE_TAG
|
||||
env:
|
||||
IMAGE_TAG: ${{ steps.get-tag.outputs.tag }}
|
||||
run: |
|
||||
echo $IMAGE_TAG
|
||||
- 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 $packageNames -W"
|
||||
APPEND_PRESET_LOCAL_PLUGINS="${pluginNames// /,}"
|
||||
echo "var1=$BEFORE_PACK_NOCOBASE" >> $GITHUB_OUTPUT
|
||||
echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT
|
||||
id: vars
|
||||
- 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: ${{ steps.set-tags.outputs.tags }}
|
||||
- name: Deploy NocoBase
|
||||
env:
|
||||
IMAGE_TAG: ${{ steps.get-tag.outputs.tag }}
|
||||
run: |
|
||||
echo $IMAGE_TAG
|
||||
export APP_NAME=$(echo $IMAGE_TAG | cut -d ":" -f 2)-${{ inputs.pro_plugin }}
|
||||
echo $APP_NAME
|
||||
curl --retry 2 --location --request POST "${{secrets.NOCOBASE_DEPLOY_HOST}}$APP_NAME" \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d "{
|
||||
\"tag\": \"$APP_NAME\",
|
||||
\"dialect\": \"postgres\"
|
||||
}"
|
2
.github/workflows/manual-e2e.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: manual-e2e
|
||||
name: Manual e2e
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
59
.github/workflows/manual-release.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: manual-release
|
||||
name: Manual release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -12,27 +12,28 @@ on:
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
app-token:
|
||||
uses: nocobase/nocobase/.github/workflows/get-nocobase-app-token.yml@main
|
||||
secrets: inherit
|
||||
pre-merge-main-into-next:
|
||||
runs-on: ubuntu-latest
|
||||
needs: app-token
|
||||
strategy:
|
||||
matrix:
|
||||
repo:
|
||||
- 'nocobase'
|
||||
- 'pro-plugins'
|
||||
- ${{ fromJSON(vars.PRO_PLUGIN_REPOS) }}
|
||||
- ${{ fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS) }}
|
||||
steps:
|
||||
- name: Decrypt app token
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
shell: bash
|
||||
run: |
|
||||
ENCRYPTED_SECRET=${{ needs.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$ENCRYPTED_SECRET" | base64 --decode);
|
||||
APP_TOKEN=$(echo -n "$BINARY_ENCRYPTED_SECRET" | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
echo "token=$APP_TOKEN" >> $GITHUB_OUTPUT
|
||||
with:
|
||||
app-id: ${{ vars.NOCOBASE_APP_ID }}
|
||||
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
|
||||
repositories: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@ -42,8 +43,8 @@ jobs:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- name: main -> next (nocobase/${{ matrix.repo }})
|
||||
run: |
|
||||
git config --global user.name '${{ needs.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ needs.app-token.outputs.user-id }}+${{ needs.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout next
|
||||
@ -51,18 +52,21 @@ jobs:
|
||||
git push origin next --tags --atomic
|
||||
update-version:
|
||||
needs:
|
||||
- app-token
|
||||
- pre-merge-main-into-next
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Decrypt app token
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
shell: bash
|
||||
run: |
|
||||
ENCRYPTED_SECRET=${{ needs.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$ENCRYPTED_SECRET" | base64 --decode);
|
||||
APP_TOKEN=$(echo -n "$BINARY_ENCRYPTED_SECRET" | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
echo "token=$APP_TOKEN" >> $GITHUB_OUTPUT
|
||||
with:
|
||||
app-id: ${{ vars.NOCOBASE_APP_ID }}
|
||||
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
|
||||
repositories: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@ -81,7 +85,7 @@ jobs:
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }} ${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
git clone -b main https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
@ -96,23 +100,24 @@ jobs:
|
||||
run: |
|
||||
cd ./packages/pro-plugins
|
||||
git checkout main
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }} ${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
echo "@nocobase/$repo" >> .git/info/exclude
|
||||
done
|
||||
echo "$(<.git/info/exclude )"
|
||||
cd ./../..
|
||||
git checkout main
|
||||
git config --global user.name '${{ needs.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ needs.app-token.outputs.user-id }}+${{ needs.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
echo "packages/pro-plugins" >> .git/info/exclude
|
||||
bash release.sh $IS_FEAT
|
||||
env:
|
||||
IS_FEAT: ${{ inputs.is_feat && '--is-feat' || '' }}
|
||||
PRO_PLUGIN_REPOS: ${{ vars.PRO_PLUGIN_REPOS }}
|
||||
CUSTOM_PRO_PLUGIN_REPOS: ${{ vars.CUSTOM_PRO_PLUGIN_REPOS }}
|
||||
- name: Push and merge into next
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }} ${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
cd ./packages/pro-plugins/@nocobase/$repo
|
||||
git push origin main --atomic --tags
|
||||
|
2
.github/workflows/nocobase-test-backend.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: NocoBase Backend Test
|
||||
name: NocoBase backend test
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
4
.github/workflows/nocobase-test-frontend.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: NocoBase FrontEnd Test
|
||||
name: NocoBase frontEnd test
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -33,7 +33,7 @@ jobs:
|
||||
frontend-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: [ '18' ]
|
||||
node_version: ['18']
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
steps:
|
||||
|
58
.github/workflows/release-next.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Release Next
|
||||
name: Release next
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -8,26 +8,27 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
app-token:
|
||||
uses: nocobase/nocobase/.github/workflows/get-nocobase-app-token.yml@main
|
||||
secrets: inherit
|
||||
publish-npm:
|
||||
runs-on: ubuntu-latest
|
||||
container: node:18
|
||||
needs: app-token
|
||||
steps:
|
||||
- name: Decrypt app token
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
shell: bash
|
||||
run: |
|
||||
ENCRYPTED_SECRET=${{ needs.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$ENCRYPTED_SECRET" | base64 --decode);
|
||||
APP_TOKEN=$(echo -n "$BINARY_ENCRYPTED_SECRET" | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
echo "token=$APP_TOKEN" >> $GITHUB_OUTPUT
|
||||
with:
|
||||
app-id: ${{ vars.NOCOBASE_APP_ID }}
|
||||
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
|
||||
repositories: nocobase,pro-plugins,${{ join(fromJSON(vars.NEXT_PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
fetch-depth: 0
|
||||
- name: Send curl request and parse response
|
||||
env:
|
||||
PKG_USERNAME: ${{ secrets.PKG_USERNAME }}
|
||||
@ -76,8 +77,8 @@ jobs:
|
||||
- name: publish npmjs.org
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git config --global user.email "test@mail.com"
|
||||
git config --global user.name "test"
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||
git config --global --add safe.directory /__w/nocobase/nocobase
|
||||
npm config set access public
|
||||
npm config set registry https://registry.npmjs.org/
|
||||
@ -99,11 +100,12 @@ jobs:
|
||||
repository: nocobase/pro-plugins
|
||||
path: packages/pro-plugins
|
||||
ref: next
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(vars.NEXT_PRO_PLUGIN_REPOS), ' ') }} ${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
git clone -b next https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
@ -124,3 +126,29 @@ jobs:
|
||||
bash generate-npmignore.sh ignore-src
|
||||
npm config set //pkg-src.nocobase.com/:_authToken=${{ env.PKG_SRC_NOCOBASE_TOKEN }}
|
||||
yarn release:force --no-verify-access --no-git-reset --registry https://pkg-src.nocobase.com --dist-tag=next
|
||||
- name: Tag
|
||||
run: |
|
||||
git reset --hard HEAD~
|
||||
git tag v${{ env.NEWVERSION }}
|
||||
git push origin v${{ env.NEWVERSION }}
|
||||
cd ./packages/pro-plugins
|
||||
git reset --hard
|
||||
git tag v${{ env.NEWVERSION }}
|
||||
git push origin v${{ env.NEWVERSION }}
|
||||
cd ../../
|
||||
for repo in ${{ join(fromJSON(vars.NEXT_PRO_PLUGIN_REPOS), ' ') }} ${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
cd ./packages/pro-plugins/@nocobase/$repo
|
||||
git reset --hard
|
||||
git tag v${{ env.NEWVERSION }}
|
||||
git push origin v${{ env.NEWVERSION }}
|
||||
cd ../../../../
|
||||
done
|
||||
- name: Run release script
|
||||
shell: bash
|
||||
run: |
|
||||
git fetch
|
||||
node scripts/release/changelogAndRelease.js --ver alpha --cmsURL ${{ secrets.CMS_URL }} --cmsToken ${{ secrets.CMS_TOKEN }}
|
||||
env:
|
||||
PRO_PLUGIN_REPOS: ${{ vars.NEXT_PRO_PLUGIN_REPOS }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
|
19
.github/workflows/release.yml
vendored
@ -10,22 +10,17 @@ on:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
app-token:
|
||||
uses: nocobase/nocobase/.github/workflows/get-nocobase-app-token.yml@main
|
||||
secrets: inherit
|
||||
publish-npm:
|
||||
runs-on: ubuntu-latest
|
||||
container: node:18
|
||||
needs: app-token
|
||||
steps:
|
||||
- name: Decrypt app token
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
shell: bash
|
||||
run: |
|
||||
ENCRYPTED_SECRET=${{ needs.app-token.outputs.token }};
|
||||
BINARY_ENCRYPTED_SECRET=$(echo -n "$ENCRYPTED_SECRET" | base64 --decode);
|
||||
APP_TOKEN=$(echo -n "$BINARY_ENCRYPTED_SECRET" | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}");
|
||||
echo "token=$APP_TOKEN" >> $GITHUB_OUTPUT
|
||||
with:
|
||||
app-id: ${{ vars.NOCOBASE_APP_ID }}
|
||||
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
|
||||
repositories: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
|
||||
skip-token-revoke: true
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Send curl request and parse response
|
||||
@ -76,7 +71,7 @@ jobs:
|
||||
- name: Clone pro repos
|
||||
shell: bash
|
||||
run: |
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }}
|
||||
for repo in ${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ' ') }} ${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ' ') }}
|
||||
do
|
||||
git clone -b main https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
|
||||
done
|
||||
|
1
.gitignore
vendored
@ -32,6 +32,7 @@ storage/plugins
|
||||
storage/tar
|
||||
storage/tmp
|
||||
storage/app.watch.ts
|
||||
storage/.upgrading
|
||||
storage/logs-e2e
|
||||
storage/uploads-e2e
|
||||
storage/.pm2-*
|
||||
|
397
CHANGELOG.md
@ -5,7 +5,402 @@ 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).
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
## [v1.3.32-beta](https://github.com/nocobase/nocobase/compare/v1.3.31-beta...v1.3.32-beta) - 2024-10-13
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** required relational field still triggers validation error after selecting a value with a variable in data scope ([#5399](https://github.com/nocobase/nocobase/pull/5399)) by @katherinehhh
|
||||
|
||||
## [v1.3.31-beta](https://github.com/nocobase/nocobase/compare/v1.3.30-beta...v1.3.31-beta) - 2024-10-11
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** Fix the issue where using the chinaRegion field in the filter form fails to correctly filter out values ([#5390](https://github.com/nocobase/nocobase/pull/5390)) by @zhangzhonghe
|
||||
|
||||
- **[Action: Import records]** fix import error with wps file ([#5397](https://github.com/nocobase/nocobase/pull/5397)) by @chareice
|
||||
|
||||
## [v1.3.30-beta](https://github.com/nocobase/nocobase/compare/v1.3.29-beta...v1.3.30-beta) - 2024-10-11
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- Fix the rendering error that occurs when displaying file collection relationship fields on mobile devices ([#5387](https://github.com/nocobase/nocobase/pull/5387)) by @zhangzhonghe
|
||||
|
||||
- Fix Create Block menu not loading more data collections ([#5388](https://github.com/nocobase/nocobase/pull/5388)) by @zhangzhonghe
|
||||
|
||||
- **[Workflow: Custom action event]**
|
||||
- Fix custom workflow event did not redirect after successful submission by @katherinehhh
|
||||
|
||||
- Fix custom workflow event did not redirect after successful submission by @katherinehhh
|
||||
|
||||
## [v1.3.29-beta](https://github.com/nocobase/nocobase/compare/v1.3.28-beta...v1.3.29-beta) - 2024-10-10
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]** Date variables are also not prohibited in create form ([#5376](https://github.com/nocobase/nocobase/pull/5376)) by @zhangzhonghe
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[Workflow: SQL node]** fix error when no result when calling stored procedure in SQL instruction ([#5385](https://github.com/nocobase/nocobase/pull/5385)) by @mytharcher
|
||||
|
||||
- **[Workflow]** fix date field based schedule trigger caused app crash, and also support other data source ([#5364](https://github.com/nocobase/nocobase/pull/5364)) by @mytharcher
|
||||
|
||||
## [v1.3.28-beta](https://github.com/nocobase/nocobase/compare/v1.3.27-beta...v1.3.28-beta) - 2024-10-09
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]** Save cdn links as local resources to prevent requesting external resources when deploying on the intranet ([#5375](https://github.com/nocobase/nocobase/pull/5375)) by @zhangzhonghe
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- Fix the issue where popups opened in the "Users & Permissions" configuration page are obscured by other popups ([#5373](https://github.com/nocobase/nocobase/pull/5373)) by @zhangzhonghe
|
||||
|
||||
- Fix the problem that after deleting a tab in a subpage, it does not take effect after opening it again ([#5362](https://github.com/nocobase/nocobase/pull/5362)) by @zhangzhonghe
|
||||
|
||||
- Fix the issue where inherited collection association fields cannot properly use variables ([#5346](https://github.com/nocobase/nocobase/pull/5346)) by @zhangzhonghe
|
||||
|
||||
- Fix the issue of current and association collection fields affecting each other in configuration ([#5343](https://github.com/nocobase/nocobase/pull/5343)) by @katherinehhh
|
||||
|
||||
- **[Action: Import records]** fixed issue with incorrect results for importing large dates ([#5356](https://github.com/nocobase/nocobase/pull/5356)) by @chareice
|
||||
|
||||
- **[Workflow]** fix switching component of association field in assigned fields caused page crash in create/update node ([#5366](https://github.com/nocobase/nocobase/pull/5366)) by @mytharcher
|
||||
|
||||
- **[Block: Gantt]** Fix the issue where opening a popup in the Gantt block and then closing it causes the subpage to also close ([#5370](https://github.com/nocobase/nocobase/pull/5370)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.27-beta](https://github.com/nocobase/nocobase/compare/v1.3.26-beta...v1.3.27-beta) - 2024-09-30
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** Fix variable "Table selected records" ([#5337](https://github.com/nocobase/nocobase/pull/5337)) by @zhangzhonghe
|
||||
|
||||
- **[Workflow: Custom action event]** fix custom action event not triggers in association block by @mytharcher
|
||||
|
||||
## [v1.3.26-beta](https://github.com/nocobase/nocobase/compare/v1.3.25-beta...v1.3.26-beta) - 2024-09-29
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]** Hide scrollbars on mobile ([#5339](https://github.com/nocobase/nocobase/pull/5339)) by @zhangzhonghe
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- Fix the issue of not being able to open sub-pages in embedded pages ([#5335](https://github.com/nocobase/nocobase/pull/5335)) by @zhangzhonghe
|
||||
|
||||
- Fix the issue of pop-up windows being obscured ([#5338](https://github.com/nocobase/nocobase/pull/5338)) by @zhangzhonghe
|
||||
|
||||
- Fix the issue of abnormal style when creating blocks with data templates in mobile subpages ([#5340](https://github.com/nocobase/nocobase/pull/5340)) by @zhangzhonghe
|
||||
|
||||
- Fix the issue of not refreshing the page block data when closing a subpage via the page menu ([#5331](https://github.com/nocobase/nocobase/pull/5331)) by @zhangzhonghe
|
||||
|
||||
- **[Action: Export records]** fix export format for decimal type fields ([#5316](https://github.com/nocobase/nocobase/pull/5316)) by @chareice
|
||||
|
||||
- **[Block: Kanban]** Fix the issue that the popup window could not be opened after clicking on the Kanban card in the embedded page ([#5326](https://github.com/nocobase/nocobase/pull/5326)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.25-beta](https://github.com/nocobase/nocobase/compare/v1.3.24-beta...v1.3.25-beta) - 2024-09-25
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]** update and improve Japanese translations in ja_JP files ([#5292](https://github.com/nocobase/nocobase/pull/5292)) by @Albert-mah
|
||||
|
||||
- **[Workflow]** add error handling for unregistered node type ([#5319](https://github.com/nocobase/nocobase/pull/5319)) by @mytharcher
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** Fix for not displaying full fields in variables ([#5310](https://github.com/nocobase/nocobase/pull/5310)) by @zhangzhonghe
|
||||
|
||||
- **[Workflow]** fix non-existed field in collection trigger causes error ([#5318](https://github.com/nocobase/nocobase/pull/5318)) by @mytharcher
|
||||
|
||||
- **[Action: Export records]** Fix fields from assicated tables are not processed by the field interface ([#5296](https://github.com/nocobase/nocobase/pull/5296)) by @gchust
|
||||
|
||||
## [v1.3.24-beta](https://github.com/nocobase/nocobase/compare/v1.3.23-beta...v1.3.24-beta) - 2024-09-23
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- markdown report error with using `#each` in handlebars ([#5305](https://github.com/nocobase/nocobase/pull/5305)) by @katherinehhh
|
||||
|
||||
- Fix issue where the collection from external data source does not support sorting on table columns ([#5293](https://github.com/nocobase/nocobase/pull/5293)) by @katherinehhh
|
||||
|
||||
- **[Data visualization]** Fix style issues of chart blocks when using dark mode themes ([#5302](https://github.com/nocobase/nocobase/pull/5302)) by @2013xile
|
||||
|
||||
## [v1.3.23-beta](https://github.com/nocobase/nocobase/compare/v1.3.22-beta...v1.3.23-beta) - 2024-09-19
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[Users]** Optimize performance for rendering the user management table ([#5276](https://github.com/nocobase/nocobase/pull/5276)) by @2013xile
|
||||
|
||||
- **[Departments]** Optimize performance for rendering the user table in department management by @2013xile
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- Fix incorrect `rowKey` of the `General action permissions table` in Users & Permissions page ([#5287](https://github.com/nocobase/nocobase/pull/5287)) by @gchust
|
||||
|
||||
- Fix the issue where setting a date variable for the date field in the filter form results in incorrect filter results ([#5257](https://github.com/nocobase/nocobase/pull/5257)) by @zhangzhonghe
|
||||
|
||||
- column width issue with scroll.y when table has no data ([#5256](https://github.com/nocobase/nocobase/pull/5256)) by @katherinehhh
|
||||
|
||||
- Fix the problem of blank rows at the beginning of a table block ([#5284](https://github.com/nocobase/nocobase/pull/5284)) by @zhangzhonghe
|
||||
|
||||
- **[create-nocobase-app]** Fix the issue where the popup for configuring Sequence rules lacked a submit button when adding a new Sequence field ([#5281](https://github.com/nocobase/nocobase/pull/5281)) by @zhangzhonghe
|
||||
|
||||
- **[database]** import with checkbox field ([#4992](https://github.com/nocobase/nocobase/pull/4992)) by @chareice
|
||||
|
||||
- **[evaluators]** Fix error caused by `Matrix` type from mathjs ([#5270](https://github.com/nocobase/nocobase/pull/5270)) by @mytharcher
|
||||
|
||||
- **[Calendar]** Cannot select the option to delete the schedule popup ([#5274](https://github.com/nocobase/nocobase/pull/5274)) by @katherinehhh
|
||||
|
||||
- **[Action: Export records]** Fix missing request context when generating data sheet in export action ([#5286](https://github.com/nocobase/nocobase/pull/5286)) by @gchust
|
||||
|
||||
## [v1.3.22-beta](https://github.com/nocobase/nocobase/compare/v1.3.21-beta...v1.3.22-beta) - 2024-09-12
|
||||
|
||||
### 🎉 New Features
|
||||
|
||||
- **[Action: Custom request]** Support for API token variables in the "Custom Request Button" configuration ([#5263](https://github.com/nocobase/nocobase/pull/5263)) by @zhangzhonghe
|
||||
Reference: [Custom request](https://docs.nocobase.com/handbook/action-custom-request)
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[Collection field: Markdown(Vditor)]** Support Vidtor when selecting fields in the UI for external data sources ([#5246](https://github.com/nocobase/nocobase/pull/5246)) by @katherinehhh
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[Calendar]** issue where the calendar block cannot display correctly when the end date crosses months ([#5239](https://github.com/nocobase/nocobase/pull/5239)) by @katherinehhh
|
||||
|
||||
## [v1.3.21-beta](https://github.com/nocobase/nocobase/compare/v1.3.20-beta...v1.3.21-beta) - 2024-09-10
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** Fix error when using linkage rules (NocoBase installed via create-nocobase-app) ([#5249](https://github.com/nocobase/nocobase/pull/5249)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.20-beta](https://github.com/nocobase/nocobase/compare/v1.3.19-beta...v1.3.20-beta) - 2024-09-10
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]** Support for displaying deeper level association fields in data blocks ([#5243](https://github.com/nocobase/nocobase/pull/5243)) by @zhangzhonghe
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- Menu modifications do not take effect in real-time ([#5207](https://github.com/nocobase/nocobase/pull/5207)) by @katherinehhh
|
||||
|
||||
- Support association field preloading in Handlebars templates ([#5236](https://github.com/nocobase/nocobase/pull/5236)) by @katherinehhh
|
||||
|
||||
- **[Data visualization]** Fix incorrect data source context when multiple data sources exist ([#5237](https://github.com/nocobase/nocobase/pull/5237)) by @2013xile
|
||||
|
||||
## [v1.3.19-beta](https://github.com/nocobase/nocobase/compare/v1.3.18-beta...v1.3.19-beta) - 2024-09-08
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** Fix URL anomalies caused by using popups together with Link buttons ([#5219](https://github.com/nocobase/nocobase/pull/5219)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.18-beta](https://github.com/nocobase/nocobase/compare/v1.3.17-beta...v1.3.18-beta) - 2024-09-08
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[Collection field: Many to many (array)]** Fix the error when deleting a collection contains m2m array fields ([#5231](https://github.com/nocobase/nocobase/pull/5231)) by @2013xile
|
||||
|
||||
## [v1.3.17-beta](https://github.com/nocobase/nocobase/compare/v1.3.16-beta...v1.3.17-beta) - 2024-09-07
|
||||
|
||||
### 🎉 New Features
|
||||
|
||||
- **[client]** Supports configuration of linkage rules in sub-forms and sub-forms. ([#5159](https://github.com/nocobase/nocobase/pull/5159)) by @zhangzhonghe
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]**
|
||||
- default time for display is 00:00:00 ([#5226](https://github.com/nocobase/nocobase/pull/5226)) by @chenos
|
||||
|
||||
- plugins can also be enabled when the plugin dependency version is inconsistent ([#5225](https://github.com/nocobase/nocobase/pull/5225)) by @chenos
|
||||
|
||||
- **[server]** provide more user-friendly application-level error messages ([#5220](https://github.com/nocobase/nocobase/pull/5220)) by @chenos
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]** Fix the "Maximum call stack size exceeded" error that occurs in the details block ([#5228](https://github.com/nocobase/nocobase/pull/5228)) by @zhangzhonghe
|
||||
|
||||
- **[Collection field: Many to many (array)]** Fix the error where setting a field of `uid` type as target key for a many to many (array) field ([#5229](https://github.com/nocobase/nocobase/pull/5229)) by @2013xile
|
||||
|
||||
- **[UI schema storage]** Fix the issue that member roles clicking the button reported no permission ([#5206](https://github.com/nocobase/nocobase/pull/5206)) by @zhangzhonghe
|
||||
|
||||
- **[Workflow]** Fix trigger type column showing wrong text after new workflow created ([#5222](https://github.com/nocobase/nocobase/pull/5222)) by @mytharcher
|
||||
|
||||
- **[Users]** Remove phone format validation when editing user phones in user management ([#5221](https://github.com/nocobase/nocobase/pull/5221)) by @2013xile
|
||||
|
||||
## [v1.3.16-beta](https://github.com/nocobase/nocobase/compare/v1.3.15-beta...v1.3.16-beta) - 2024-09-06
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[client]**
|
||||
- Placeholder added when the user has UI configuration permissions but no view permissions for the collection ([#5208](https://github.com/nocobase/nocobase/pull/5208)) by @katherinehhh
|
||||
|
||||
- Display system title when logo is missing. ([#5175](https://github.com/nocobase/nocobase/pull/5175)) by @maoyutofu
|
||||
|
||||
- **[Authentication]** support line break in system title ([#5211](https://github.com/nocobase/nocobase/pull/5211)) by @chenos
|
||||
|
||||
- **[Workflow: SQL node]** Change result data structure of SQL node to only contains data. ([#5189](https://github.com/nocobase/nocobase/pull/5189)) by @mytharcher
|
||||
Reference: [SQL Operation](https://docs.nocobase.com/handbook/workflow/nodes/sql)
|
||||
- **[Access control]** Make the `Permissions` Tab pannel of the `Users & Permissions` configuration page expandable. ([#5216](https://github.com/nocobase/nocobase/pull/5216)) by @zhangzhonghe
|
||||
Reference: [Development Guide](https://docs.nocobase.com/handbook/acl#development-guide)
|
||||
- **[Action: Batch edit]** batch updated and batch edit, change 'All' to 'Entire collection" ([#5200](https://github.com/nocobase/nocobase/pull/5200)) by @katherinehhh
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[client]**
|
||||
- component display error when switching assignment types in linkage rules ([#5180](https://github.com/nocobase/nocobase/pull/5180)) by @katherinehhh
|
||||
|
||||
- Fix an issue where using variables in data scope reported an error. ([#5195](https://github.com/nocobase/nocobase/pull/5195)) by @zhangzhonghe
|
||||
|
||||
- issue with custom request refreshDataBlockRequest ([#5188](https://github.com/nocobase/nocobase/pull/5188)) by @katherinehhh
|
||||
|
||||
- **[Data visualization]** Fixed the issue of getting wrong value when aggregating select fields ([#5214](https://github.com/nocobase/nocobase/pull/5214)) by @2013xile
|
||||
|
||||
- **[Data source manager]** Fixed incorrect `rowKey` of the datasource table in `Users & Permissions` page ([#5215](https://github.com/nocobase/nocobase/pull/5215)) by @gchust
|
||||
|
||||
- **[Workflow: HTTP request node]** Fix error when using non-string variable in request parameters. ([#5204](https://github.com/nocobase/nocobase/pull/5204)) by @mytharcher
|
||||
|
||||
- **[Collection field: Formula]** fix formula field serve test ([#5197](https://github.com/nocobase/nocobase/pull/5197)) by @katherinehhh
|
||||
|
||||
- **[App backup & restore (deprecated)]** fix test case errors ([#5201](https://github.com/nocobase/nocobase/pull/5201)) by @chenos
|
||||
|
||||
- **[Data source: REST API]**
|
||||
- collection name should be disabled in rest-api collection by @katherinehhh
|
||||
|
||||
- Rest api locale improve by @katherinehhh
|
||||
|
||||
## [v1.3.15-beta](https://github.com/nocobase/nocobase/compare/v1.3.14-beta...v1.3.15-beta) - 2024-09-04
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[Workflow]** Fix missed fields in workflow variables. ([#5187](https://github.com/nocobase/nocobase/pull/5187)) by @mytharcher
|
||||
|
||||
- **[Collection field: Markdown(Vditor)]** issue with markdown(Vditor) ([#5176](https://github.com/nocobase/nocobase/pull/5176)) by @katherinehhh
|
||||
|
||||
## [v1.3.14-beta](https://github.com/nocobase/nocobase/compare/v1.3.13-beta...v1.3.14-beta) - 2024-09-03
|
||||
|
||||
### 🎉 New Features
|
||||
|
||||
- **[client]** Add support for many-to-many association fields. ([#5178](https://github.com/nocobase/nocobase/pull/5178)) by @zhangzhonghe
|
||||
|
||||
### 🚀 Improvements
|
||||
|
||||
- **[Action: Custom request]** remove linkageRule for custom request in create form ([#5179](https://github.com/nocobase/nocobase/pull/5179)) by @katherinehhh
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[Collection field: Formula]** formula field adaptation time field ([#5168](https://github.com/nocobase/nocobase/pull/5168)) by @katherinehhh
|
||||
|
||||
## [v1.3.13-beta](https://github.com/nocobase/nocobase/compare/v1.3.12-beta...v1.3.13-beta) - 2024-09-03
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[Action: Export records]** Fixed incorrect export of relational data ([#5170](https://github.com/nocobase/nocobase/pull/5170)) by @chareice
|
||||
|
||||
## [v1.3.12-beta](https://github.com/nocobase/nocobase/compare/v1.3.11-beta...v1.3.12-beta) - 2024-09-01
|
||||
|
||||
### Merged
|
||||
|
||||
- fix(mobile): fix permission [`#5163`](https://github.com/nocobase/nocobase/pull/5163)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.12-beta [`774c296`](https://github.com/nocobase/nocobase/commit/774c2961d47aa17d1a9da7a595bb070f34aee11b)
|
||||
- chore: update changelog [`7f9a116`](https://github.com/nocobase/nocobase/commit/7f9a11698f3126257529ce4a91670239900f2ec3)
|
||||
- chore: update e2e test [`49db3e4`](https://github.com/nocobase/nocobase/commit/49db3e490821cd59aaba2f58ed2bb78051a86ad9)
|
||||
|
||||
## [v1.3.11-beta](https://github.com/nocobase/nocobase/compare/v1.3.10-beta...v1.3.11-beta) - 2024-08-31
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.11-beta [`517e199`](https://github.com/nocobase/nocobase/commit/517e199ed7a8e7dc81a06c50389ef41b6891b133)
|
||||
- chore: update changelog [`373f517`](https://github.com/nocobase/nocobase/commit/373f51773b772886cc8db3cb50184562113c62eb)
|
||||
|
||||
## [v1.3.10-beta](https://github.com/nocobase/nocobase/compare/v1.3.9-beta...v1.3.10-beta) - 2024-08-31
|
||||
|
||||
### Merged
|
||||
|
||||
- fix: issue with association select data scope linkage in sub-table [`#5160`](https://github.com/nocobase/nocobase/pull/5160)
|
||||
- fix: issue in data selector other block should display Markdown, not 'Add Text' [`#5161`](https://github.com/nocobase/nocobase/pull/5161)
|
||||
- fix(data-vi): issue of parsing variables in filter block [`#5157`](https://github.com/nocobase/nocobase/pull/5157)
|
||||
- fix(data-vi): transform the values of decimal fields from type string to number [`#5155`](https://github.com/nocobase/nocobase/pull/5155)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.10-beta [`5afac9c`](https://github.com/nocobase/nocobase/commit/5afac9cf82c78db4a7ee8ddb01a60597939ac82d)
|
||||
- chore: update changelog [`6fceac1`](https://github.com/nocobase/nocobase/commit/6fceac15827a10b6fba65e98314c37f3f9e697ba)
|
||||
- chore: update comment [`6e45780`](https://github.com/nocobase/nocobase/commit/6e4578056556c1c60ac721ff990a81ed37339074)
|
||||
|
||||
## [v1.3.9-beta](https://github.com/nocobase/nocobase/compare/v1.3.8-beta...v1.3.9-beta) - 2024-08-29
|
||||
|
||||
### Merged
|
||||
|
||||
- fix(mobile): should not force redirect to mobile page [`#5152`](https://github.com/nocobase/nocobase/pull/5152)
|
||||
- chore: support year data type in mysql [`#5123`](https://github.com/nocobase/nocobase/pull/5123)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.9-beta [`bf5011f`](https://github.com/nocobase/nocobase/commit/bf5011f75a7a9b26db7fef7aa4be28d7e4e077b4)
|
||||
- chore: update changelog [`b2fc646`](https://github.com/nocobase/nocobase/commit/b2fc646e5aa64d2ade03ce6fca78753cfddc26ec)
|
||||
|
||||
## [v1.3.8-beta](https://github.com/nocobase/nocobase/compare/v1.3.7-beta...v1.3.8-beta) - 2024-08-29
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.8-beta [`39d021a`](https://github.com/nocobase/nocobase/commit/39d021a9aa29bef9cf15d4af546060fc4b1dbd10)
|
||||
- chore: update changelog [`9f66c14`](https://github.com/nocobase/nocobase/commit/9f66c14968639d90b399d087eefac7a0c4ea4383)
|
||||
|
||||
## [v1.3.7-beta](https://github.com/nocobase/nocobase/compare/v1.3.6-beta...v1.3.7-beta) - 2024-08-29
|
||||
|
||||
### Merged
|
||||
|
||||
- fix: add text support handlebars [`#5150`](https://github.com/nocobase/nocobase/pull/5150)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.7-beta [`f429d13`](https://github.com/nocobase/nocobase/commit/f429d1326433e7f290e552ca91548d21b5af92e4)
|
||||
- chore: update changelog [`b41e477`](https://github.com/nocobase/nocobase/commit/b41e47757ec0d1f7b0af917e25ff5b4a436042aa)
|
||||
|
||||
## [v1.3.6-beta](https://github.com/nocobase/nocobase/compare/v1.3.5-beta...v1.3.6-beta) - 2024-08-29
|
||||
|
||||
### Merged
|
||||
|
||||
- fix: association select data scope linkage should support edit form [`#5149`](https://github.com/nocobase/nocobase/pull/5149)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.6-beta [`39c7ce4`](https://github.com/nocobase/nocobase/commit/39c7ce4741801819b98970b95c1663915a8c3bff)
|
||||
- chore: update changelog [`cfbc2a6`](https://github.com/nocobase/nocobase/commit/cfbc2a6c15a5dfb8c0684051df1cf01499ff30ac)
|
||||
|
||||
## [v1.3.5-beta](https://github.com/nocobase/nocobase/compare/v1.3.4-beta...v1.3.5-beta) - 2024-08-28
|
||||
|
||||
### Merged
|
||||
|
||||
- fix: association select data scope linkage should be supported in sub-form [`#5146`](https://github.com/nocobase/nocobase/pull/5146)
|
||||
- fix(mobile): resovle redirect issue [`#5145`](https://github.com/nocobase/nocobase/pull/5145)
|
||||
- feat(plugin-workflow): allow to delete execution in list [`#5135`](https://github.com/nocobase/nocobase/pull/5135)
|
||||
- fix(defaultValue): ignores variable values that do not match the current field [`#5122`](https://github.com/nocobase/nocobase/pull/5122)
|
||||
- chore(deps-dev): bump eslint-plugin-jest-dom from 5.1.0 to 5.4.0 [`#5138`](https://github.com/nocobase/nocobase/pull/5138)
|
||||
- chore(deps): bump @ant-design/pro-layout from 7.17.16 to 7.19.12 [`#5137`](https://github.com/nocobase/nocobase/pull/5137)
|
||||
- fix(template): fix error on form block submission [`#5133`](https://github.com/nocobase/nocobase/pull/5133)
|
||||
- feat: add support for opening via URL [`#5098`](https://github.com/nocobase/nocobase/pull/5098)
|
||||
- fix(release): decrypt token error occasionally [`#5143`](https://github.com/nocobase/nocobase/pull/5143)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.5-beta [`35e8f89`](https://github.com/nocobase/nocobase/commit/35e8f89c75800a612db27485c96196555f922273)
|
||||
- Revert "chore(deps): bump @ant-design/pro-layout from 7.17.16 to 7.19.12 (#5137)" [`3f461ad`](https://github.com/nocobase/nocobase/commit/3f461ad8f079b4c2cf5975c1e26271f55021e08a)
|
||||
- fix(release): pro image ci [`e45d450`](https://github.com/nocobase/nocobase/commit/e45d45015792138e7378741bdaf488de714b365d)
|
||||
|
||||
## [v1.3.4-beta](https://github.com/nocobase/nocobase/compare/v1.3.3-beta...v1.3.4-beta) - 2024-08-27
|
||||
|
||||
### Merged
|
||||
|
||||
- refactor: set remainsTheSame as the default value for field editing in bulk editing action [`#5124`](https://github.com/nocobase/nocobase/pull/5124)
|
||||
|
||||
### Commits
|
||||
|
||||
- chore(versions): 😊 publish v1.3.4-beta [`a011748`](https://github.com/nocobase/nocobase/commit/a0117480e037e48a23f59921110003047a1a174b)
|
||||
- chore: update changelog [`3403e8d`](https://github.com/nocobase/nocobase/commit/3403e8d76684950d6962a6110a4440eb95856a35)
|
||||
|
||||
## [v1.3.3-beta](https://github.com/nocobase/nocobase/compare/v1.3.2-beta...v1.3.3-beta) - 2024-08-27
|
||||
|
||||
|
298
CHANGELOG.zh-CN.md
Normal file
@ -0,0 +1,298 @@
|
||||
# 更新日志
|
||||
|
||||
本项目的所有重要更改都将记录在此文件中。
|
||||
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。
|
||||
|
||||
## [v1.3.32-beta](https://github.com/nocobase/nocobase/compare/v1.3.31-beta...v1.3.32-beta) - 2024-10-13
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 关系字段设置必填,数据范围中设置变量后,选中值却报字段必填不通过 ([#5399](https://github.com/nocobase/nocobase/pull/5399)) by @katherinehhh
|
||||
|
||||
## [v1.3.31-beta](https://github.com/nocobase/nocobase/compare/v1.3.30-beta...v1.3.31-beta) - 2024-10-11
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 修复在筛选表单中使用行政区划字段无法正确筛选出值的问题 ([#5390](https://github.com/nocobase/nocobase/pull/5390)) by @zhangzhonghe
|
||||
|
||||
- **[操作:导入记录]** 修复导入 wps 文件报错的问题 ([#5397](https://github.com/nocobase/nocobase/pull/5397)) by @chareice
|
||||
|
||||
## [v1.3.30-beta](https://github.com/nocobase/nocobase/compare/v1.3.29-beta...v1.3.30-beta) - 2024-10-11
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- 修复在移动端中,显示文件表关系字段时报渲染错误的问题 ([#5387](https://github.com/nocobase/nocobase/pull/5387)) by @zhangzhonghe
|
||||
|
||||
- 修复创建区块菜单无法加载更多数据表的问题 ([#5388](https://github.com/nocobase/nocobase/pull/5388)) by @zhangzhonghe
|
||||
|
||||
- **[工作流:自定义操作事件]**
|
||||
- 修复 自定义工作流事件提交成功后跳转不生效 by @katherinehhh
|
||||
|
||||
- 自定义工作流事件提交成功后跳转不生效 by @katherinehhh
|
||||
|
||||
## [v1.3.29-beta](https://github.com/nocobase/nocobase/compare/v1.3.28-beta...v1.3.29-beta) - 2024-10-10
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]** 创建表单中也不禁用日期变量 ([#5376](https://github.com/nocobase/nocobase/pull/5376)) by @zhangzhonghe
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[工作流:SQL 节点]** 修复在 SQL 节点中调用存储过程没有返回结果时导致错误的问题 ([#5385](https://github.com/nocobase/nocobase/pull/5385)) by @mytharcher
|
||||
|
||||
- **[工作流]** 修复基于时间字段的定时任务导致报错的问题,并支持其他数据库数据源 ([#5364](https://github.com/nocobase/nocobase/pull/5364)) by @mytharcher
|
||||
|
||||
## [v1.3.28-beta](https://github.com/nocobase/nocobase/compare/v1.3.27-beta...v1.3.28-beta) - 2024-10-09
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]** 将 cdn 链接保存为本地资源,以防止在内网部署时请求外部资源 ([#5375](https://github.com/nocobase/nocobase/pull/5375)) by @zhangzhonghe
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- 修复在“用户和权限”配置页打开的弹窗被其它弹窗遮挡的问题 ([#5373](https://github.com/nocobase/nocobase/pull/5373)) by @zhangzhonghe
|
||||
|
||||
- 修复在子页面中删除 tab 页后,再次打开后未生效的问题 ([#5362](https://github.com/nocobase/nocobase/pull/5362)) by @zhangzhonghe
|
||||
|
||||
- 修复继承表关系字段无法正常使用变量的问题 ([#5346](https://github.com/nocobase/nocobase/pull/5346)) by @zhangzhonghe
|
||||
|
||||
- 修复字段配置中当前数据表字段与关系表字段互相影响缺陷 ([#5343](https://github.com/nocobase/nocobase/pull/5343)) by @katherinehhh
|
||||
|
||||
- **[操作:导入记录]** 修复导入大日期结果不正确的问题 ([#5356](https://github.com/nocobase/nocobase/pull/5356)) by @chareice
|
||||
|
||||
- **[工作流]** 修复新增、更新节点中配置关系字段赋值时切换组件导致的页面崩溃 ([#5366](https://github.com/nocobase/nocobase/pull/5366)) by @mytharcher
|
||||
|
||||
- **[区块:甘特图]** 修复在甘特图中打开弹窗,然后再关闭,导致子页面也被关闭的问题 ([#5370](https://github.com/nocobase/nocobase/pull/5370)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.27-beta](https://github.com/nocobase/nocobase/compare/v1.3.26-beta...v1.3.27-beta) - 2024-09-30
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 修复变量“表格中选中的记录” ([#5337](https://github.com/nocobase/nocobase/pull/5337)) by @zhangzhonghe
|
||||
|
||||
- **[工作流:自定义操作事件]** 修复自定义操作事件在关系区块中不触发的问题 by @mytharcher
|
||||
|
||||
## [v1.3.26-beta](https://github.com/nocobase/nocobase/compare/v1.3.25-beta...v1.3.26-beta) - 2024-09-29
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]** 隐藏移动端的滚动条 ([#5339](https://github.com/nocobase/nocobase/pull/5339)) by @zhangzhonghe
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- 修复在嵌入页面中无法打开子页面的问题 ([#5335](https://github.com/nocobase/nocobase/pull/5335)) by @zhangzhonghe
|
||||
|
||||
- 修复弹窗被遮挡的问题 ([#5338](https://github.com/nocobase/nocobase/pull/5338)) by @zhangzhonghe
|
||||
|
||||
- 修复移动端子页面中,使用数据模板创建区块时,样式异常的问题 ([#5340](https://github.com/nocobase/nocobase/pull/5340)) by @zhangzhonghe
|
||||
|
||||
- 修复通过页面菜单关闭子页面时,不刷新页面区块数据的问题 ([#5331](https://github.com/nocobase/nocobase/pull/5331)) by @zhangzhonghe
|
||||
|
||||
- **[操作:导出记录]** 修复 decimal 类型字段的导出格式 ([#5316](https://github.com/nocobase/nocobase/pull/5316)) by @chareice
|
||||
|
||||
- **[区块:看板]** 修复在嵌入页面中,点击看板卡片后,无法打开弹窗的问题 ([#5326](https://github.com/nocobase/nocobase/pull/5326)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.25-beta](https://github.com/nocobase/nocobase/compare/v1.3.24-beta...v1.3.25-beta) - 2024-09-25
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]** 增加日语本地化翻译 ([#5292](https://github.com/nocobase/nocobase/pull/5292)) by @Albert-mah
|
||||
|
||||
- **[工作流]** 增加对未注册的节点类型导致错误的跟踪报错 ([#5319](https://github.com/nocobase/nocobase/pull/5319)) by @mytharcher
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 修复变量中没有显示完整字段的问题 ([#5310](https://github.com/nocobase/nocobase/pull/5310)) by @zhangzhonghe
|
||||
|
||||
- **[工作流]** 修复数据表事件中发生改变的字段被删除后报错的问题 ([#5318](https://github.com/nocobase/nocobase/pull/5318)) by @mytharcher
|
||||
|
||||
- **[操作:导出记录]** 修复导出操作时,关联表中的字段未执行interface渲染逻辑 ([#5296](https://github.com/nocobase/nocobase/pull/5296)) by @gchust
|
||||
|
||||
## [v1.3.24-beta](https://github.com/nocobase/nocobase/compare/v1.3.23-beta...v1.3.24-beta) - 2024-09-23
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- markdown 的handlebars 模板使用#each 渲染数组数据时数据没有正常显示 ([#5305](https://github.com/nocobase/nocobase/pull/5305)) by @katherinehhh
|
||||
|
||||
- 外部数据库数据源表格列头不支持排序的问题 ([#5293](https://github.com/nocobase/nocobase/pull/5293)) by @katherinehhh
|
||||
|
||||
- **[数据可视化]** 修复图表区块在暗黑主题下的样式问题 ([#5302](https://github.com/nocobase/nocobase/pull/5302)) by @2013xile
|
||||
|
||||
## [v1.3.23-beta](https://github.com/nocobase/nocobase/compare/v1.3.22-beta...v1.3.23-beta) - 2024-09-19
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[用户]** 优化用户管理表格的渲染速度 ([#5276](https://github.com/nocobase/nocobase/pull/5276)) by @2013xile
|
||||
|
||||
- **[部门]** 优化部门管理中的用户表格的渲染速度 by @2013xile
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- 修复用户和权限设置页面中`通用操作权限表格`的`rowKey`不正确问题 ([#5287](https://github.com/nocobase/nocobase/pull/5287)) by @gchust
|
||||
|
||||
- 修复在筛选表单中,为日期字段设置日期变量后,导致的筛选结果不正确的问题 ([#5257](https://github.com/nocobase/nocobase/pull/5257)) by @zhangzhonghe
|
||||
|
||||
- 表格没有数据时且设置了区块高度时无法设置列宽 ([#5256](https://github.com/nocobase/nocobase/pull/5256)) by @katherinehhh
|
||||
|
||||
- 修复表格区块在一开始出现空白行的问题 ([#5284](https://github.com/nocobase/nocobase/pull/5284)) by @zhangzhonghe
|
||||
|
||||
- **[create-nocobase-app]** 修复在新增自动编码字段时,配置编码规则的弹窗缺少提交按钮的问题 ([#5281](https://github.com/nocobase/nocobase/pull/5281)) by @zhangzhonghe
|
||||
|
||||
- **[database]** 导入支持勾选字段 ([#4992](https://github.com/nocobase/nocobase/pull/4992)) by @chareice
|
||||
|
||||
- **[evaluators]** 修复 Math.js 计算输出矩阵类型导致的问题 ([#5270](https://github.com/nocobase/nocobase/pull/5270)) by @mytharcher
|
||||
|
||||
- **[日历]** 删除日程弹窗选项不能选择 ([#5274](https://github.com/nocobase/nocobase/pull/5274)) by @katherinehhh
|
||||
|
||||
- **[操作:导出记录]** 修复在导出操作中,生成数据表格时,缺少上下文的问题 ([#5286](https://github.com/nocobase/nocobase/pull/5286)) by @gchust
|
||||
|
||||
## [v1.3.22-beta](https://github.com/nocobase/nocobase/compare/v1.3.21-beta...v1.3.22-beta) - 2024-09-12
|
||||
|
||||
### 🎉 新特性
|
||||
|
||||
- **[操作:自定义请求]** 自定义请求按钮的配置中,支持使用 API token 变量 ([#5263](https://github.com/nocobase/nocobase/pull/5263)) by @zhangzhonghe
|
||||
参考文档:[自定义请求-变量](https://docs-cn.nocobase.com/handbook/action-custom-request#%E5%8F%98%E9%87%8F)
|
||||
### 🚀 优化
|
||||
|
||||
- **[数据表字段:Markdown(Vditor)]** 在外部数据源中选字段 UI 的时支持 Vidtor ([#5246](https://github.com/nocobase/nocobase/pull/5246)) by @katherinehhh
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[日历]** 日历区块结束日期跨月时无法正确显示的问题 ([#5239](https://github.com/nocobase/nocobase/pull/5239)) by @katherinehhh
|
||||
|
||||
## [v1.3.21-beta](https://github.com/nocobase/nocobase/compare/v1.3.20-beta...v1.3.21-beta) - 2024-09-10
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 修复在使用联动规则时报错的问题(通过 create-nocobase-app 安装的 NocoBase) ([#5249](https://github.com/nocobase/nocobase/pull/5249)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.20-beta](https://github.com/nocobase/nocobase/compare/v1.3.19-beta...v1.3.20-beta) - 2024-09-10
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]** 数据区块中支持显示更深层级的关系字段 ([#5243](https://github.com/nocobase/nocobase/pull/5243)) by @zhangzhonghe
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- 修改菜单标题时没有实时生效 ([#5207](https://github.com/nocobase/nocobase/pull/5207)) by @katherinehhh
|
||||
|
||||
- 支持 Handlebars 模板中关系字段的预加载 ([#5236](https://github.com/nocobase/nocobase/pull/5236)) by @katherinehhh
|
||||
|
||||
- **[数据可视化]** 修复存在多个数据源时,图表的数据源上下文不正确的问题 ([#5237](https://github.com/nocobase/nocobase/pull/5237)) by @2013xile
|
||||
|
||||
## [v1.3.19-beta](https://github.com/nocobase/nocobase/compare/v1.3.18-beta...v1.3.19-beta) - 2024-09-08
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 修复因弹窗与 Link 按钮一起使用,所导致的 URL 异常的问题 ([#5219](https://github.com/nocobase/nocobase/pull/5219)) by @zhangzhonghe
|
||||
|
||||
## [v1.3.18-beta](https://github.com/nocobase/nocobase/compare/v1.3.17-beta...v1.3.18-beta) - 2024-09-08
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[数据表字段:多对多 (数组)]** 修复删除包含多对多(数组)字段的数据表时出现的错误 ([#5231](https://github.com/nocobase/nocobase/pull/5231)) by @2013xile
|
||||
|
||||
## [v1.3.17-beta](https://github.com/nocobase/nocobase/compare/v1.3.16-beta...v1.3.17-beta) - 2024-09-07
|
||||
|
||||
### 🎉 新特性
|
||||
|
||||
- **[client]** 支持在子表单和子表格中配置联动规则。 ([#5159](https://github.com/nocobase/nocobase/pull/5159)) by @zhangzhonghe
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]**
|
||||
- 显示时间时默认时间为 00:00:00 ([#5226](https://github.com/nocobase/nocobase/pull/5226)) by @chenos
|
||||
|
||||
- 插件依赖版本不一致时也可以激活插件 ([#5225](https://github.com/nocobase/nocobase/pull/5225)) by @chenos
|
||||
|
||||
- **[server]** 提供更友好的应用级错误提示 ([#5220](https://github.com/nocobase/nocobase/pull/5220)) by @chenos
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]** 修复在详情区块中出现的 “Maximum call stack size exceeded” 错误 ([#5228](https://github.com/nocobase/nocobase/pull/5228)) by @zhangzhonghe
|
||||
|
||||
- **[数据表字段:多对多 (数组)]** 修复将 `uid` 类型的字段设置为多对多(数组)字段的目标键时出现的报错 ([#5229](https://github.com/nocobase/nocobase/pull/5229)) by @2013xile
|
||||
|
||||
- **[UI schema 存储服务]** 修复 member 角色点击按钮报无权限的问题 ([#5206](https://github.com/nocobase/nocobase/pull/5206)) by @zhangzhonghe
|
||||
|
||||
- **[工作流]** 修复创建工作流后类型列展示错误文字的问题 ([#5222](https://github.com/nocobase/nocobase/pull/5222)) by @mytharcher
|
||||
|
||||
- **[用户]** 移除在用户管理中编辑用户资料时的手机号格式验证 ([#5221](https://github.com/nocobase/nocobase/pull/5221)) by @2013xile
|
||||
|
||||
## [v1.3.16-beta](https://github.com/nocobase/nocobase/compare/v1.3.15-beta...v1.3.16-beta) - 2024-09-06
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[client]**
|
||||
- 有UI配置权限但没有数据表查看权限时添加占位 ([#5208](https://github.com/nocobase/nocobase/pull/5208)) by @katherinehhh
|
||||
|
||||
- 当缺少 logo 时,显示系统标题。 ([#5175](https://github.com/nocobase/nocobase/pull/5175)) by @maoyutofu
|
||||
|
||||
- **[用户认证]** 系统标题支持换行 ([#5211](https://github.com/nocobase/nocobase/pull/5211)) by @chenos
|
||||
|
||||
- **[工作流:SQL 节点]** 将 SQL 操作节点的结果数据结构调整为仅包含数据部分。 ([#5189](https://github.com/nocobase/nocobase/pull/5189)) by @mytharcher
|
||||
Reference: [SQL 操作](https://docs-cn.nocobase.com/handbook/workflow/nodes/sql)
|
||||
- **[权限控制]** 使 `Users & Permissions` 配置页的 `Permissions` Tab 面板可扩展。 ([#5216](https://github.com/nocobase/nocobase/pull/5216)) by @zhangzhonghe
|
||||
Reference: [开发指南](https://docs-cn.nocobase.com/handbook/acl#%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97)
|
||||
- **[操作:批量编辑]** 批量更新、批量编辑的 文案 ,“所有” 改成 “全表” ([#5200](https://github.com/nocobase/nocobase/pull/5200)) by @katherinehhh
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[client]**
|
||||
- 修复联动规则中切换赋值类型时组件显示错误 ([#5180](https://github.com/nocobase/nocobase/pull/5180)) by @katherinehhh
|
||||
|
||||
- 修复数据范围中使用变量报错的问题。 ([#5195](https://github.com/nocobase/nocobase/pull/5195)) by @zhangzhonghe
|
||||
|
||||
- 自定义请求按钮的请求后刷新数据设置不生效 ([#5188](https://github.com/nocobase/nocobase/pull/5188)) by @katherinehhh
|
||||
|
||||
- **[数据可视化]** 修复聚合选项字段时,获取结果不正确的问题 ([#5214](https://github.com/nocobase/nocobase/pull/5214)) by @2013xile
|
||||
|
||||
- **[数据源管理]** 修复`用户和权限`设置页面中数据源表格`rowKey`不正确问题 ([#5215](https://github.com/nocobase/nocobase/pull/5215)) by @gchust
|
||||
|
||||
- **[工作流:HTTP 请求节点]** 修复请求节点参数使用非字符串变量时的问题。 ([#5204](https://github.com/nocobase/nocobase/pull/5204)) by @mytharcher
|
||||
|
||||
- **[数据表字段:公式]** 修复公式字段时间类型测试用例 ([#5197](https://github.com/nocobase/nocobase/pull/5197)) by @katherinehhh
|
||||
|
||||
- **[应用的备份与还原(废弃)]** 修复测试用例报错 ([#5201](https://github.com/nocobase/nocobase/pull/5201)) by @chenos
|
||||
|
||||
- **[数据源:REST API]**
|
||||
- rest api 数据表 标识不可编辑 by @katherinehhh
|
||||
|
||||
- Rest api 多语言调整 by @katherinehhh
|
||||
|
||||
## [v1.3.15-beta](https://github.com/nocobase/nocobase/compare/v1.3.14-beta...v1.3.15-beta) - 2024-09-04
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[工作流]** 修复工作流变量中缺少部分字段可选的问题。 ([#5187](https://github.com/nocobase/nocobase/pull/5187)) by @mytharcher
|
||||
|
||||
- **[数据表字段:Markdown(Vditor)]** 修复 markdown(Vditor) 字段没有正确显数据(缓存) ([#5176](https://github.com/nocobase/nocobase/pull/5176)) by @katherinehhh
|
||||
|
||||
## [v1.3.14-beta](https://github.com/nocobase/nocobase/compare/v1.3.13-beta...v1.3.14-beta) - 2024-09-03
|
||||
|
||||
### 🎉 新特性
|
||||
|
||||
- **[client]** 支持在筛选表单中配置对多关系目标表中的字段。 ([#5178](https://github.com/nocobase/nocobase/pull/5178)) by @zhangzhonghe
|
||||
|
||||
### 🚀 优化
|
||||
|
||||
- **[操作:自定义请求]** 去掉添加数据表单自定义请求按钮的联动规则 ([#5179](https://github.com/nocobase/nocobase/pull/5179)) by @katherinehhh
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[数据表字段:公式]** 公式字段使用日期字段时页面报错 ([#5168](https://github.com/nocobase/nocobase/pull/5168)) by @katherinehhh
|
||||
|
||||
## [v1.3.13-beta](https://github.com/nocobase/nocobase/compare/v1.3.12-beta...v1.3.13-beta) - 2024-09-03
|
||||
|
||||
### 🐛 修复
|
||||
|
||||
- **[操作:导出记录]** 修复导出关系数据不正确的问题 ([#5170](https://github.com/nocobase/nocobase/pull/5170)) by @chareice
|
||||
|
@ -6,7 +6,7 @@ ARG BEFORE_PACK_NOCOBASE="ls -l"
|
||||
ARG PLUGINS_DIRS
|
||||
|
||||
ENV PLUGINS_DIRS=${PLUGINS_DIRS}
|
||||
|
||||
ENV COMMIT_HASH=${COMMIT_HASH}
|
||||
|
||||
RUN npx npm-cli-adduser --username test --password test -e test@nocobase.com -r $VERDACCIO_URL
|
||||
|
||||
@ -40,17 +40,18 @@ RUN cd /app \
|
||||
&& rm -rf nocobase.tar.gz \
|
||||
&& tar -zcf ./nocobase.tar.gz -C /app/my-nocobase-app .
|
||||
|
||||
RUN echo "${COMMIT_HASH}" > /tmp/commit_hash.txt
|
||||
|
||||
|
||||
FROM node:20.13-bullseye-slim
|
||||
RUN apt-get update && apt-get install -y nginx
|
||||
RUN apt-get update && apt-get install -y nginx libaio1
|
||||
RUN rm -rf /etc/nginx/sites-enabled/default
|
||||
COPY ./docker/nocobase/nocobase.conf /etc/nginx/sites-enabled/nocobase.conf
|
||||
COPY --from=builder /app/nocobase.tar.gz /app/nocobase.tar.gz
|
||||
COPY --from=builder /tmp/commit_hash.txt /app/commit_hash.txt
|
||||
|
||||
WORKDIR /app/nocobase
|
||||
|
||||
RUN mkdir -p /app/nocobase/storage/uploads/ && echo "$COMMIT_HASH" >> /app/nocobase/storage/uploads/COMMIT_HASH
|
||||
|
||||
COPY ./docker/nocobase/docker-entrypoint.sh /app/
|
||||
|
||||
CMD ["/app/docker-entrypoint.sh"]
|
||||
|
65
Dockerfile.pro
Normal file
@ -0,0 +1,65 @@
|
||||
FROM node:20.13-bullseye as builder
|
||||
ARG VERDACCIO_URL=http://host.docker.internal:10104/
|
||||
ARG COMMIT_HASH
|
||||
ARG APPEND_PRESET_LOCAL_PLUGINS
|
||||
ARG BEFORE_PACK_NOCOBASE="ls -l"
|
||||
ARG PLUGINS_DIRS
|
||||
|
||||
ENV PLUGINS_DIRS=${PLUGINS_DIRS}
|
||||
|
||||
|
||||
RUN npx npm-cli-adduser --username test --password test -e test@nocobase.com -r $VERDACCIO_URL
|
||||
|
||||
RUN apt-get update && apt-get install -y jq
|
||||
WORKDIR /tmp
|
||||
COPY . /tmp
|
||||
RUN yarn install && yarn build --no-dts
|
||||
|
||||
RUN cd /tmp && \
|
||||
NEWVERSION="$(cat lerna.json | jq '.version' | tr -d '"').$(date +'%Y%m%d%H%M%S')" \
|
||||
&& git checkout -b release-$(date +'%Y%m%d%H%M%S') \
|
||||
&& yarn lerna version ${NEWVERSION} -y --no-git-tag-version
|
||||
RUN git config user.email "test@mail.com" \
|
||||
&& git config user.name "test" && git add . \
|
||||
&& git commit -m "chore(versions): test publish packages"
|
||||
RUN yarn release:force --registry $VERDACCIO_URL
|
||||
|
||||
RUN yarn config set registry $VERDACCIO_URL
|
||||
WORKDIR /app
|
||||
RUN cd /app \
|
||||
&& yarn config set network-timeout 600000 -g \
|
||||
&& yarn create nocobase-app my-nocobase-app -a -e APP_ENV=production -e APPEND_PRESET_LOCAL_PLUGINS=$APPEND_PRESET_LOCAL_PLUGINS \
|
||||
&& cd /app/my-nocobase-app \
|
||||
&& yarn install --production
|
||||
|
||||
WORKDIR /app/my-nocobase-app
|
||||
RUN $BEFORE_PACK_NOCOBASE
|
||||
|
||||
RUN cd /app \
|
||||
&& rm -rf my-nocobase-app/packages/app/client/src/.umi \
|
||||
&& rm -rf nocobase.tar.gz \
|
||||
&& tar -zcf ./nocobase.tar.gz -C /app/my-nocobase-app .
|
||||
|
||||
FROM node:20.13-bullseye-slim
|
||||
RUN apt-get update && apt-get install -y nginx
|
||||
RUN rm -rf /etc/nginx/sites-enabled/default
|
||||
|
||||
COPY ./docker/nocobase/nocobase.conf /etc/nginx/sites-enabled/nocobase.conf
|
||||
COPY --from=builder /app/nocobase.tar.gz /app/nocobase.tar.gz
|
||||
|
||||
WORKDIR /app/nocobase
|
||||
|
||||
RUN mkdir -p /app/nocobase/storage/uploads/ && echo "$COMMIT_HASH" >> /app/nocobase/storage/uploads/COMMIT_HASH
|
||||
|
||||
# install postgresql-client and mysql-client
|
||||
RUN apt update && apt install -y wget postgresql-common gnupg \
|
||||
&& /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y \
|
||||
&& apt install -y postgresql-client-16 \
|
||||
&& wget https://downloads.mysql.com/archives/get/p/23/file/mysql-community-client-core_8.1.0-1debian11_amd64.deb \
|
||||
&& dpkg -x mysql-community-client-core_8.1.0-1debian11_amd64.deb /tmp/mysql-client \
|
||||
&& cp /tmp/mysql-client/usr/bin/mysqldump /usr/bin/ \
|
||||
&& cp /tmp/mysql-client/usr/bin/mysql /usr/bin/
|
||||
|
||||
COPY ./docker/nocobase/docker-entrypoint.sh /app/
|
||||
|
||||
CMD ["/app/docker-entrypoint.sh"]
|
20
LICENSE.txt
@ -1,4 +1,4 @@
|
||||
Updated Date: August 15, 2024
|
||||
Updated Date: September 17, 2024
|
||||
|
||||
NocoBase License Agreement
|
||||
|
||||
@ -92,21 +92,23 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr
|
||||
|
||||
7.1 It is not allowed to remove or change all intellectual property statements about NocoBase in the code.
|
||||
|
||||
7.2 It is not allowed to sell, transfer, lease, share, or give away the Commercial License.
|
||||
7.2 It is not allowed to sell, transfer, lease, share, or distribute the Commercial License.
|
||||
|
||||
7.3 It is not allowed to provide any form of no-code, zero-code, low-code platform SaaS products to the public using the original or modified Software.
|
||||
7.3 It is not allowed to sell, transfer, lease, share, or distribute any form of no-code, zero-code, low-code platform, or developer tools developed based on Software.
|
||||
|
||||
7.4 It is not allowed for Users with a standard license to sell Upper Layer Application to clients without a commercial license.
|
||||
7.4 It is not allowed to provide any form of no-code, zero-code, low-code platform SaaS products to the public using the original or modified Software.
|
||||
|
||||
7.5 It is not allowed to use reverse engineering, decompilation, and other means to try to discover the source code of Commercial Plugins that have not obtained source code license.
|
||||
7.5 It is not allowed for Users with a standard license to sell Upper Layer Application to clients without a commercial license.
|
||||
|
||||
7.6 It is not allowed to disclose the source code of Commercial Plugins to any third party.
|
||||
7.6 It is not allowed to use reverse engineering, decompilation, and other means to try to discover the source code of Commercial Plugins that have not obtained source code license.
|
||||
|
||||
7.7 It is not allowed to publicly sell plugins developed for Software outside of the Marketplace.
|
||||
7.7 It is not allowed to disclose the source code of Commercial Plugins to any third party.
|
||||
|
||||
7.8 If there is a violation of the above obligations or the terms of this Agreement, the rights owned by the User will be immediately terminated, the paid fees will not be refunded, and the Company reserves the right to pursue the User's legal responsibility.
|
||||
7.8 It is not allowed to publicly sell plugins developed for Software outside of the Marketplace.
|
||||
|
||||
7.9 If there are other agreements in the contract for the above obligations, the contract agreement shall prevail.
|
||||
7.9 If there is a violation of the above obligations or the terms of this Agreement, the rights owned by the User will be immediately terminated, the paid fees will not be refunded, and the Company reserves the right to pursue the User's legal responsibility.
|
||||
|
||||
7.10 If there are other agreements in the contract for the above obligations, the contract agreement shall prevail.
|
||||
|
||||
=============================================================
|
||||
8. Legal Jurisdiction, Interpretation, and Dispute Resolution
|
||||
|
@ -8,18 +8,20 @@ https://github.com/user-attachments/assets/b11cbb68-76bc-4e8b-a2aa-2a1feed0ab77
|
||||
<a href="https://www.producthunt.com/posts/nocobase?embed=true&utm_source=badge-top-post-topic-badge&utm_medium=badge&utm_souce=badge-nocobase" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-topic-badge.svg?post_id=456520&theme=light&period=weekly&topic_id=267" alt="NocoBase - Scalability-first, open-source no-code platform | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
## 最近の重要なリリース
|
||||
- [v1.0.1-alpha.1:ブロックの高さ設定をサポート - 2024/06/07](https://docs-cn.nocobase.com/welcome/changelog/20240607)
|
||||
- [v1.0.0-alpha.15:新しいプラグインの追加、「設定操作」インターフェースの改善 - 2024/05/19](https://docs-cn.nocobase.com/welcome/changelog/20240519)
|
||||
- [v1.0:新しいマイルストーン - 2024/04/28](https://docs-cn.nocobase.com/welcome/release/v1001-changelog)
|
||||
- [v0.21:ブロックのパフォーマンスの最適化 - 2024/03/29](https://docs-cn.nocobase.com/welcome/release/v0210-changelog)
|
||||
- [v0.20:複数のデータソースをサポート - 2024/03/03](https://docs-cn.nocobase.com/welcome/release/v0200-changelog)
|
||||
- [v0.19:アプリケーションフローの最適化 - 2024/01/08](https://blog-cn.nocobase.com/posts/release-v019/)
|
||||
- [v0.18:完全なテストシステムの確立 - 2023/12/21](https://blog-cn.nocobase.com/posts/release-v018/)
|
||||
- [v0.17:新しいSchemaInitializerおよびSchemaSettings - 2023/12/11](https://blog-cn.nocobase.com/posts/release-v017/)
|
||||
- [v0.16:新しいキャッシュモジュール - 2023/11/20](https://blog-cn.nocobase.com/posts/release-v016/)
|
||||
- [v0.15:新しいプラグイン設定センター - 2023/11/13](https://blog-cn.nocobase.com/posts/release-v015/)
|
||||
- [v0.14:新しいプラグインマネージャー、インターフェースを通じたプラグインの追加をサポート - 2023/09/11](https://blog-cn.nocobase.com/posts/release-v014/)
|
||||
- [v0.13: 新しいアプリケーションステートフロー - 2023/08/24](https://blog-cn.nocobase.com/posts/release-v013/)
|
||||
|
||||
- [v1.3:REST API データソース、モバイル版 V2 などの新機能 - 2024/08/29](https://www.nocobase.com/en/blog/nocobase-1-3)
|
||||
- [v1.0.1-alpha.1:ブロックの高さ設定をサポート - 2024/06/07](https://www.nocobase.com/en/blog/release-v101-alpha1)
|
||||
- [v1.0.0-alpha.15:新しいプラグインの追加、「設定操作」インターフェースの改善 - 2024/05/19](https://www.nocobase.com/en/blog/release-v100-alpha15)
|
||||
- [v1.0:新しいマイルストーン - 2024/04/28](https://www.nocobase.com/en/blog/release-v10)
|
||||
- [v0.21:ブロックのパフォーマンスの最適化 - 2024/03/29](https://www.nocobase.com/en/blog/release-v021)
|
||||
- [v0.20:複数のデータソースをサポート - 2024/03/03](https://www.nocobase.com/en/blog/release-v020)
|
||||
- [v0.19:アプリケーションフローの最適化 - 2024/01/08](https://www.nocobase.com/en/blog/release-v019)
|
||||
- [v0.18:完全なテストシステムの確立 - 2023/12/21](https://www.nocobase.com/en/blog/release-v018)
|
||||
- [v0.17:新しいSchemaInitializerおよびSchemaSettings - 2023/12/11](https://www.nocobase.com/en/blog/release-v017)
|
||||
- [v0.16:新しいキャッシュモジュール - 2023/11/20](https://www.nocobase.com/en/blog/release-v016)
|
||||
- [v0.15:新しいプラグイン設定センター - 2023/11/13](https://www.nocobase.com/en/blog/release-v015)
|
||||
- [v0.14:新しいプラグインマネージャー、インターフェースを通じたプラグインの追加をサポート - 2023/09/11](https://www.nocobase.com/en/blog/release-v014)
|
||||
- [v0.13: 新しいアプリケーションステートフロー - 2023/08/24](https://www.nocobase.com/en/blog/release-v013)
|
||||
|
||||
## NocoBaseはなに?
|
||||
|
||||
|
25
README.md
@ -10,18 +10,19 @@ https://github.com/nocobase/nocobase/assets/1267426/1d6a3979-d1eb-4e50-b726-2f90
|
||||
|
||||
## Recent major updates
|
||||
|
||||
- [v1.0.1-alpha.1: Blocks support height settings - 2024/06/07](https://docs.nocobase.com/welcome/changelog/20240607)
|
||||
- [v1.0.0-alpha.15: New Plugins and Improved “Configure actions” Interaction - 2024/05/19](https://docs.nocobase.com/welcome/changelog/20240519)
|
||||
- [v1.0: Significant Milestone - 2024/04/28](https://docs.nocobase.com/welcome/release/v1001-changelog)
|
||||
- [v0.21: Block performance optimization - 2024/03/29](https://docs.nocobase.com/welcome/release/v0210-changelog)
|
||||
- [v0.20: Support for multiple data sources - 2024/03/03](https://docs.nocobase.com/welcome/release/v0200-changelog)
|
||||
- [v0.19: Application process optimization - 2024/01/08](https://docs.nocobase.com/welcome/release/v0190-changelog)
|
||||
- [v0.18: Establish a sound testing system - 2023/12/21](https://docs.nocobase.com/welcome/release/v0180-changelog)
|
||||
- [v0.17: New SchemaInitializer and SchemaSettings - 2023/12/11](https://docs.nocobase.com/welcome/release/v0170-changelog)
|
||||
- [v0.16: New cache manager - 2023/11/20](https://docs.nocobase.com/welcome/release/v0160-changelog)
|
||||
- [v0.15: New plugin settings manager - 2023/11/13](https://docs.nocobase.com/welcome/release/v0150-changelog)
|
||||
- [v0.14: New plugin manager, supports adding plugins through UI - 2023/09/11](https://docs.nocobase.com/welcome/release/v0140-changelog)
|
||||
- [v0.13: New application status flow - 2023/08/24](https://docs.nocobase.com/welcome/release/v0130-changelog)
|
||||
- [v1.3: REST API data source, mobile v2, and more features - 2024/08/29](https://www.nocobase.com/en/blog/nocobase-1-3)
|
||||
- [v1.0.1-alpha.1: Blocks support height settings - 2024/06/07](https://www.nocobase.com/en/blog/release-v101-alpha1)
|
||||
- [v1.0.0-alpha.15: New Plugins and Improved “Configure actions” Interaction - 2024/05/19](https://www.nocobase.com/en/blog/release-v100-alpha15)
|
||||
- [v1.0: Significant Milestone - 2024/04/28](https://www.nocobase.com/en/blog/release-v10)
|
||||
- [v0.21: Block performance optimization - 2024/03/29](https://www.nocobase.com/en/blog/release-v021)
|
||||
- [v0.20: Support for multiple data sources - 2024/03/03](https://www.nocobase.com/en/blog/release-v020)
|
||||
- [v0.19: Application process optimization - 2024/01/08](https://www.nocobase.com/en/blog/release-v019)
|
||||
- [v0.18: Establish a sound testing system - 2023/12/21](https://www.nocobase.com/en/blog/release-v018)
|
||||
- [v0.17: New SchemaInitializer and SchemaSettings - 2023/12/11](https://www.nocobase.com/en/blog/release-v017)
|
||||
- [v0.16: New cache manager - 2023/11/20](https://www.nocobase.com/en/blog/release-v016)
|
||||
- [v0.15: New plugin settings manager - 2023/11/13](https://www.nocobase.com/en/blog/release-v015)
|
||||
- [v0.14: New plugin manager, supports adding plugins through UI - 2023/09/11](https://www.nocobase.com/en/blog/release-v014)
|
||||
- [v0.13: New application status flow - 2023/08/24](https://www.nocobase.com/en/blog/release-v013)
|
||||
|
||||
## What is NocoBase
|
||||
|
||||
|
@ -11,18 +11,19 @@ https://github.com/nocobase/nocobase/assets/1267426/29623e45-9a48-4598-bb9e-9dd1
|
||||
我们正在招聘远程 **全栈开发工程师** 、 **测试工程师** 、 **技术培训与文档专家**。 欢迎对 NocoBase 有强烈兴趣的伙伴加入。[查看详情](https://www.nocobase.com/cn/recruitment)
|
||||
|
||||
## 最近重要更新
|
||||
- [v1.0.1-alpha.1:区块支持高度设置 - 2024/06/07](https://docs-cn.nocobase.com/welcome/changelog/20240607)
|
||||
- [v1.0.0-alpha.15:新增插件、改进「配置操作」交互 - 2024/05/19](https://docs-cn.nocobase.com/welcome/changelog/20240519)
|
||||
- [v1.0:新的里程碑 - 2024/04/28](https://docs-cn.nocobase.com/welcome/release/v1001-changelog)
|
||||
- [v0.21:优化区块性能 - 2024/03/29](https://docs-cn.nocobase.com/welcome/release/v0210-changelog)
|
||||
- [v0.20:支持多数据源 - 2024/03/03](https://docs-cn.nocobase.com/welcome/release/v0200-changelog)
|
||||
- [v0.19:应用流程优化 - 2024/01/08](https://blog-cn.nocobase.com/posts/release-v019/)
|
||||
- [v0.18:建立健全的测试体系 - 2023/12/21](https://blog-cn.nocobase.com/posts/release-v018/)
|
||||
- [v0.17:全新的 SchemaInitializer 和 SchemaSettings - 2023/12/11](https://blog-cn.nocobase.com/posts/release-v017/)
|
||||
- [v0.16:全新的缓存模块 - 2023/11/20](https://blog-cn.nocobase.com/posts/release-v016/)
|
||||
- [v0.15:全新的插件设置中心 - 2023/11/13](https://blog-cn.nocobase.com/posts/release-v015/)
|
||||
- [v0.14:全新的插件管理器,支持通过界面添加插件 - 2023/09/11](https://blog-cn.nocobase.com/posts/release-v014/)
|
||||
- [v0.13: 全新的应用状态流转 - 2023/08/24](https://blog-cn.nocobase.com/posts/release-v013/)
|
||||
- [v1.3:REST API 数据源、移动端 V2 和更多功能 - 2024/08/29](https://www.nocobase.com/cn/blog/nocobase-1-3)
|
||||
- [v1.0.1-alpha.1:区块支持高度设置 - 2024/06/07](https://www.nocobase.com/cn/blog/release-v101-alpha1)
|
||||
- [v1.0.0-alpha.15:新增插件、改进「配置操作」交互 - 2024/05/19](https://www.nocobase.com/cn/blog/release-v100-alpha15)
|
||||
- [v1.0:新的里程碑 - 2024/04/28](https://www.nocobase.com/cn/blog/release-v10)
|
||||
- [v0.21:优化区块性能 - 2024/03/29](https://www.nocobase.com/cn/blog/release-v021)
|
||||
- [v0.20:支持多数据源 - 2024/03/03](https://www.nocobase.com/cn/blog/release-v020)
|
||||
- [v0.19:应用流程优化 - 2024/01/08](https://www.nocobase.com/cn/blog/release-v019)
|
||||
- [v0.18:建立健全的测试体系 - 2023/12/21](https://www.nocobase.com/cn/blog/release-v018)
|
||||
- [v0.17:全新的 SchemaInitializer 和 SchemaSettings - 2023/12/11](https://www.nocobase.com/cn/blog/release-v017)
|
||||
- [v0.16:全新的缓存模块 - 2023/11/20](https://www.nocobase.com/cn/blog/release-v016)
|
||||
- [v0.15:全新的插件设置中心 - 2023/11/13](https://www.nocobase.com/cn/blog/release-v015)
|
||||
- [v0.14:全新的插件管理器,支持通过界面添加插件 - 2023/09/11](https://www.nocobase.com/cn/blog/release-v014)
|
||||
- [v0.13: 全新的应用状态流转 - 2023/08/24](https://www.nocobase.com/cn/blog/release-v013)
|
||||
|
||||
## NocoBase 是什么
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "COMMIT_HASH: $(cat /app/commit_hash.txt)"
|
||||
|
||||
if [ ! -d "/app/nocobase" ]; then
|
||||
mkdir nocobase
|
||||
fi
|
||||
@ -18,6 +20,14 @@ ln -s /app/nocobase/storage/nocobase.conf /etc/nginx/sites-enabled/nocobase.conf
|
||||
nginx
|
||||
echo 'nginx started';
|
||||
|
||||
# run scripts in storage/scripts
|
||||
if [ -d "/app/nocobase/storage/scripts" ]; then
|
||||
for f in /app/nocobase/storage/scripts/*.sh; do
|
||||
echo "Running $f"
|
||||
sh "$f"
|
||||
done
|
||||
fi
|
||||
|
||||
cd /app/nocobase && yarn start --quickstart
|
||||
|
||||
# Run command with node if the first argument contains a "-" or is not a system command. The last
|
||||
|
@ -3,9 +3,14 @@
|
||||
FILES=$(find packages/pro-plugins/@nocobase -name .npmignore)
|
||||
|
||||
if [ "$1" == "ignore-src" ]; then
|
||||
CONTENT="/node_modules"
|
||||
CONTENT="/node_modules
|
||||
/docker
|
||||
/docs
|
||||
"
|
||||
else
|
||||
CONTENT="/node_modules
|
||||
/docker
|
||||
/docs
|
||||
/src"
|
||||
fi
|
||||
|
||||
|
@ -146,4 +146,52 @@ describe('list action', () => {
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body.count).toEqual(0);
|
||||
});
|
||||
|
||||
it('should list with simple paginate', async () => {
|
||||
const Item = app.collection({
|
||||
name: 'items',
|
||||
simplePaginate: true,
|
||||
fields: [{ type: 'string', name: 'name' }],
|
||||
});
|
||||
|
||||
await app.db.sync();
|
||||
|
||||
await Item.repository.create({
|
||||
values: [
|
||||
{
|
||||
name: 'item1',
|
||||
},
|
||||
{
|
||||
name: 'item2',
|
||||
},
|
||||
{
|
||||
name: 'item3',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await app
|
||||
.agent()
|
||||
.resource('items')
|
||||
.list({
|
||||
fields: ['id'],
|
||||
pageSize: 1,
|
||||
page: 2,
|
||||
});
|
||||
|
||||
const body = response.body;
|
||||
expect(body.hasNext).toBeTruthy();
|
||||
|
||||
const lastPageResponse = await app
|
||||
.agent()
|
||||
.resource('items')
|
||||
.list({
|
||||
fields: ['id'],
|
||||
pageSize: 1,
|
||||
page: 3,
|
||||
});
|
||||
|
||||
const lastPageBody = lastPageResponse.body;
|
||||
expect(lastPageBody.hasNext).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,6 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { assign, isValidFilter } from '@nocobase/utils';
|
||||
import { Context } from '..';
|
||||
import { getRepositoryFromParams, pageArgsToLimitArgs } from '../utils';
|
||||
import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../constants';
|
||||
@ -49,9 +48,13 @@ async function listWithPagination(ctx: Context) {
|
||||
});
|
||||
|
||||
if (simplePaginate) {
|
||||
options.limit = options.limit + 1;
|
||||
|
||||
const rows = await repository.find(options);
|
||||
|
||||
ctx.body = {
|
||||
rows,
|
||||
rows: rows.slice(0, pageSize),
|
||||
hasNext: rows.length > pageSize,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize),
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getUmiConfig, IndexGenerator } from '@nocobase/devtools/umiConfig';
|
||||
import { generatePlugins, getUmiConfig } from '@nocobase/devtools/umiConfig';
|
||||
import path from 'path';
|
||||
import { defineConfig } from 'umi';
|
||||
|
||||
@ -8,17 +8,11 @@ process.env.MFSU_AD = 'none';
|
||||
process.env.DID_YOU_KNOW = 'none';
|
||||
|
||||
const pluginPrefix = (process.env.PLUGIN_PACKAGE_PREFIX || '').split(',').filter((item) => !item.includes('preset')); // 因为现在 preset 是直接引入的,所以不能忽略,如果以后 preset 也是动态插件的形式引入,那么这里可以去掉
|
||||
|
||||
const pluginDirs = (process.env.PLUGIN_PATH || 'packages/plugins/,packages/samples/,packages/pro-plugins/')
|
||||
.split(',').map(item => path.join(process.cwd(), item));
|
||||
|
||||
const outputPluginPath = path.join(__dirname, 'src', '.plugins');
|
||||
const indexGenerator = new IndexGenerator(outputPluginPath, pluginDirs);
|
||||
indexGenerator.generate();
|
||||
|
||||
const isDevCmd = !!process.env.IS_DEV_CMD;
|
||||
const appPublicPath = isDevCmd ? '/' : '{{env.APP_PUBLIC_PATH}}';
|
||||
|
||||
generatePlugins();
|
||||
|
||||
export default defineConfig({
|
||||
title: 'Loading...',
|
||||
devtool: process.env.NODE_ENV === 'development' ? 'source-map' : false,
|
||||
|
BIN
packages/core/app/client/public/file-placeholder/ai-200-200.png
Normal file
After Width: | Height: | Size: 979 B |
After Width: | Height: | Size: 841 B |
After Width: | Height: | Size: 1017 B |
BIN
packages/core/app/client/public/file-placeholder/eps-200-200.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
packages/core/app/client/public/file-placeholder/gif-200-200.png
Normal file
After Width: | Height: | Size: 958 B |
After Width: | Height: | Size: 1.0 KiB |
BIN
packages/core/app/client/public/file-placeholder/pdf-200-200.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
packages/core/app/client/public/file-placeholder/png-200-200.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 981 B |
BIN
packages/core/app/client/public/file-placeholder/psd-200-200.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
packages/core/app/client/public/file-placeholder/svg-200-200.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 813 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 1.2 KiB |
BIN
packages/core/app/client/public/file-placeholder/zip-200-200.png
Normal file
After Width: | Height: | Size: 715 B |
@ -70,7 +70,7 @@ describe('middleware', () => {
|
||||
hasFn.mockImplementation(() => true);
|
||||
const res = await agent.resource('auth').check();
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.text).toContain('token is not available');
|
||||
expect(res.text).toContain('Token is invalid');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -109,7 +109,10 @@ export class AuthManager {
|
||||
return async (ctx: Context & { auth: Auth }, next: Next) => {
|
||||
const token = ctx.getBearerToken();
|
||||
if (token && (await ctx.app.authManager.jwt.blacklist?.has(token))) {
|
||||
return ctx.throw(401, ctx.t('token is not available'));
|
||||
return ctx.throw(401, {
|
||||
code: 'TOKEN_INVALID',
|
||||
message: ctx.t('Token is invalid'),
|
||||
});
|
||||
}
|
||||
|
||||
const name = ctx.get(this.options.authKey) || this.options.default;
|
||||
|
@ -69,14 +69,14 @@ export class BaseAuth extends Auth {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const { userId, roleName } = await this.jwt.decode(token);
|
||||
const { userId, roleName, iat, temp } = await this.jwt.decode(token);
|
||||
|
||||
if (roleName) {
|
||||
this.ctx.headers['x-role'] = roleName;
|
||||
}
|
||||
|
||||
const cache = this.ctx.cache as Cache;
|
||||
return await cache.wrap(this.getCacheKey(userId), () =>
|
||||
const user = await cache.wrap(this.getCacheKey(userId), () =>
|
||||
this.userRepository.findOne({
|
||||
filter: {
|
||||
id: userId,
|
||||
@ -84,6 +84,10 @@ export class BaseAuth extends Auth {
|
||||
raw: true,
|
||||
}),
|
||||
);
|
||||
if (temp && user.passwordChangeTz && iat * 1000 < user.passwordChangeTz) {
|
||||
throw new Error('Token is invalid');
|
||||
}
|
||||
return user;
|
||||
} catch (err) {
|
||||
this.ctx.logger.error(err, { method: 'check' });
|
||||
return null;
|
||||
@ -106,6 +110,7 @@ export class BaseAuth extends Auth {
|
||||
}
|
||||
const token = this.jwt.sign({
|
||||
userId: user.id,
|
||||
temp: true,
|
||||
});
|
||||
return {
|
||||
user,
|
||||
@ -119,7 +124,7 @@ export class BaseAuth extends Auth {
|
||||
return;
|
||||
}
|
||||
const { userId } = await this.jwt.decode(token);
|
||||
await this.ctx.app.emitAsync('beforeSignOut', { userId });
|
||||
await this.ctx.app.emitAsync('cache:del:roles', { userId });
|
||||
await this.ctx.cache.del(this.getCacheKey(userId));
|
||||
return await this.jwt.block(token);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "7.25.2",
|
||||
"@babel/plugin-transform-modules-amd": "7.24.7",
|
||||
"@babel/preset-env": "7.25.3",
|
||||
"@babel/preset-env": "7.25.4",
|
||||
"@hapi/topo": "^6.0.0",
|
||||
"@lerna/project": "4.0.0",
|
||||
"@types/gulp": "^4.0.13",
|
||||
|
@ -22,7 +22,7 @@
|
||||
"portfinder": "^1.0.28",
|
||||
"serve": "^13.0.2",
|
||||
"tree-kill": "^1.2.2",
|
||||
"tsx": "^4.6.2"
|
||||
"tsx": "^4.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nocobase/devtools": "1.4.0-alpha"
|
||||
|
@ -9,8 +9,12 @@
|
||||
|
||||
const chalk = require('chalk');
|
||||
const { Command } = require('commander');
|
||||
const { runAppCommand, runInstall, run, postCheck, nodeCheck, promptForTs } = require('../util');
|
||||
const { generatePlugins, run, postCheck, nodeCheck, promptForTs } = require('../util');
|
||||
const { getPortPromise } = require('portfinder');
|
||||
const chokidar = require('chokidar');
|
||||
const { uid } = require('@formily/shared');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
*
|
||||
@ -27,6 +31,25 @@ module.exports = (cli) => {
|
||||
.option('--inspect [port]')
|
||||
.allowUnknownOption()
|
||||
.action(async (opts) => {
|
||||
const watcher = chokidar.watch('./storage/plugins/**/*', {
|
||||
cwd: process.cwd(),
|
||||
ignored: /(^|[\/\\])\../, // 忽略隐藏文件
|
||||
persistent: true,
|
||||
depth: 1, // 只监听第一层目录
|
||||
});
|
||||
|
||||
watcher
|
||||
.on('addDir', async (pathname) => {
|
||||
generatePlugins();
|
||||
const file = path.resolve(process.cwd(), 'storage/app.watch.ts');
|
||||
await fs.promises.writeFile(file, `export const watchId = '${uid()}';`, 'utf-8');
|
||||
})
|
||||
.on('unlinkDir', async (pathname) => {
|
||||
generatePlugins();
|
||||
const file = path.resolve(process.cwd(), 'storage/app.watch.ts');
|
||||
await fs.promises.writeFile(file, `export const watchId = '${uid()}';`, 'utf-8');
|
||||
});
|
||||
|
||||
promptForTs();
|
||||
const { SERVER_TSCONFIG_PATH } = process.env;
|
||||
process.env.IS_DEV_CMD = true;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
const { Command } = require('commander');
|
||||
const { run, isDev, isProd, promptForTs } = require('../util');
|
||||
const { run, isDev, isProd, promptForTs, downloadPro } = require('../util');
|
||||
|
||||
/**
|
||||
*
|
||||
@ -20,10 +20,14 @@ module.exports = (cli) => {
|
||||
.allowUnknownOption()
|
||||
.option('-h, --help')
|
||||
.option('--ts-node-dev')
|
||||
.action((options) => {
|
||||
.action(async (options) => {
|
||||
const cmd = process.argv.slice(2)?.[0];
|
||||
if (cmd === 'install') {
|
||||
await downloadPro();
|
||||
}
|
||||
if (isDev()) {
|
||||
promptForTs();
|
||||
run('tsx', [
|
||||
await run('tsx', [
|
||||
'--tsconfig',
|
||||
SERVER_TSCONFIG_PATH,
|
||||
'-r',
|
||||
@ -32,7 +36,7 @@ module.exports = (cli) => {
|
||||
...process.argv.slice(2),
|
||||
]);
|
||||
} else if (isProd()) {
|
||||
run('node', [`${APP_PACKAGE_ROOT}/lib/index.js`, ...process.argv.slice(2)]);
|
||||
await run('node', [`${APP_PACKAGE_ROOT}/lib/index.js`, ...process.argv.slice(2)]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -31,6 +31,7 @@ module.exports = (cli) => {
|
||||
require('./umi')(cli);
|
||||
require('./upgrade')(cli);
|
||||
require('./postinstall')(cli);
|
||||
require('./pkg')(cli);
|
||||
if (isPackageValid('@umijs/utils')) {
|
||||
require('./create-plugin')(cli);
|
||||
}
|
||||
|
218
packages/core/cli/src/commands/pkg.js
Normal file
@ -0,0 +1,218 @@
|
||||
/**
|
||||
* 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 { Command } = require('commander');
|
||||
const axios = require('axios');
|
||||
const fs = require('fs-extra');
|
||||
const zlib = require('zlib');
|
||||
const tar = require('tar');
|
||||
const path = require('path');
|
||||
const { createStoragePluginsSymlink } = require('@nocobase/utils/plugin-symlink');
|
||||
const chalk = require('chalk');
|
||||
|
||||
class Package {
|
||||
data;
|
||||
constructor(packageName, packageManager) {
|
||||
this.packageName = packageName;
|
||||
this.packageManager = packageManager;
|
||||
this.outputDir = path.resolve(process.cwd(), `storage/plugins/${this.packageName}`);
|
||||
}
|
||||
|
||||
get token() {
|
||||
return this.packageManager.getToken();
|
||||
}
|
||||
|
||||
url(path) {
|
||||
return this.packageManager.url(path);
|
||||
}
|
||||
|
||||
async mkdir() {
|
||||
if (await fs.exists(this.outputDir)) {
|
||||
await fs.rm(this.outputDir, { recursive: true, force: true });
|
||||
}
|
||||
await fs.mkdirp(this.outputDir);
|
||||
}
|
||||
|
||||
async getInfo() {
|
||||
try {
|
||||
const res = await axios.get(this.url(this.packageName), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
},
|
||||
responseType: 'json',
|
||||
});
|
||||
this.data = res.data;
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
getTarball(version = 'latest') {
|
||||
if (this.data.versions[version]) {
|
||||
return [version, this.data.versions[version].dist.tarball];
|
||||
}
|
||||
|
||||
if (version.includes('beta')) {
|
||||
version = version.split('beta')[0] + 'beta';
|
||||
} else if (version.includes('alpha')) {
|
||||
const prefix = (version = version.split('alpha')[0]);
|
||||
version = Object.keys(this.data.versions)
|
||||
.filter((ver) => ver.startsWith(`${prefix}alpha`))
|
||||
.sort()
|
||||
.pop();
|
||||
}
|
||||
|
||||
if (version === 'latest') {
|
||||
version = this.data['dist-tags']['latest'];
|
||||
} else if (version === 'next') {
|
||||
version = this.data['dist-tags']['next'];
|
||||
}
|
||||
|
||||
if (!this.data.versions[version]) {
|
||||
console.log(chalk.redBright(`Download failed: ${this.packageName}@${version} package does not exist`));
|
||||
}
|
||||
|
||||
return [version, this.data.versions[version].dist.tarball];
|
||||
}
|
||||
|
||||
async isDevPackage() {
|
||||
let file = path.resolve(process.cwd(), 'packages/plugins', this.packageName, 'package.json');
|
||||
if (await fs.exists(file)) {
|
||||
return true;
|
||||
}
|
||||
file = path.resolve(process.cwd(), 'packages/pro-plugins', this.packageName, 'package.json');
|
||||
if (await fs.exists(file)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async download(options = {}) {
|
||||
if (await this.isDevPackage()) {
|
||||
console.log(chalk.yellowBright(`Skipped: ${this.packageName} is dev package`));
|
||||
return;
|
||||
}
|
||||
await this.getInfo();
|
||||
if (!this.data) {
|
||||
console.log(chalk.redBright(`Download failed: ${this.packageName} package does not exist`));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const [version, url] = this.getTarball(options.version);
|
||||
const response = await axios({
|
||||
url,
|
||||
responseType: 'stream',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
},
|
||||
});
|
||||
await this.mkdir();
|
||||
await new Promise((resolve, reject) => {
|
||||
response.data
|
||||
.pipe(zlib.createGunzip()) // 解压 gzip
|
||||
.pipe(tar.extract({ cwd: this.outputDir, strip: 1 })) // 解压 tar
|
||||
.on('finish', resolve)
|
||||
.on('error', reject);
|
||||
});
|
||||
console.log(chalk.greenBright(`Download success: ${this.packageName}@${version}`));
|
||||
} catch (error) {
|
||||
console.log(chalk.redBright(`Download failed: ${this.packageName}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PackageManager {
|
||||
token;
|
||||
baseURL;
|
||||
|
||||
constructor({ baseURL }) {
|
||||
this.baseURL = baseURL;
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
getBaseURL() {
|
||||
return this.baseURL;
|
||||
}
|
||||
|
||||
url(path) {
|
||||
return this.baseURL + path;
|
||||
}
|
||||
|
||||
async login(credentials) {
|
||||
try {
|
||||
const res1 = await axios.post(`${this.baseURL}-/verdaccio/sec/login`, credentials, {
|
||||
responseType: 'json',
|
||||
});
|
||||
this.token = res1.data.token;
|
||||
} catch (error) {
|
||||
console.error(chalk.redBright(`Login failed: ${this.baseURL}`));
|
||||
}
|
||||
}
|
||||
|
||||
getPackage(packageName) {
|
||||
return new Package(packageName, this);
|
||||
}
|
||||
|
||||
async getProPackages() {
|
||||
const res = await axios.get(this.url('pro-packages'), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
},
|
||||
responseType: 'json',
|
||||
});
|
||||
return res.data.data;
|
||||
}
|
||||
|
||||
async getPackages() {
|
||||
const pkgs = await this.getProPackages();
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
async download(options = {}) {
|
||||
const { version } = options;
|
||||
if (!this.token) {
|
||||
return;
|
||||
}
|
||||
const pkgs = await this.getPackages();
|
||||
for (const pkg of pkgs) {
|
||||
await this.getPackage(pkg).download({ version });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Command} cli
|
||||
*/
|
||||
module.exports = (cli) => {
|
||||
const pkg = cli.command('pkg');
|
||||
pkg
|
||||
.command('download-pro')
|
||||
.option('-V, --version [version]')
|
||||
.action(async () => {
|
||||
const { NOCOBASE_PKG_URL, NOCOBASE_PKG_USERNAME, NOCOBASE_PKG_PASSWORD } = process.env;
|
||||
if (!(NOCOBASE_PKG_URL && NOCOBASE_PKG_USERNAME && NOCOBASE_PKG_PASSWORD)) {
|
||||
return;
|
||||
}
|
||||
const credentials = { username: NOCOBASE_PKG_USERNAME, password: NOCOBASE_PKG_PASSWORD };
|
||||
const pm = new PackageManager({ baseURL: NOCOBASE_PKG_URL });
|
||||
await pm.login(credentials);
|
||||
const file = path.resolve(__dirname, '../../package.json');
|
||||
const json = await fs.readJson(file);
|
||||
await pm.download({ version: json.version });
|
||||
await createStoragePluginsSymlink();
|
||||
});
|
||||
pkg.command('export-all').action(async () => {
|
||||
console.log('Todo...');
|
||||
});
|
||||
};
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
const { Command } = require('commander');
|
||||
const { run, isDev, isPackageValid, generatePlaywrightPath } = require('../util');
|
||||
const { run, isDev, isPackageValid, generatePlaywrightPath, generatePlugins } = require('../util');
|
||||
const { dirname, resolve } = require('path');
|
||||
const { existsSync, mkdirSync, readFileSync, appendFileSync } = require('fs');
|
||||
const { readFile, writeFile } = require('fs').promises;
|
||||
@ -41,7 +41,7 @@ module.exports = (cli) => {
|
||||
.option('--skip-umi')
|
||||
.action(async (options) => {
|
||||
writeToExclude();
|
||||
|
||||
generatePlugins();
|
||||
generatePlaywrightPath(true);
|
||||
await createStoragePluginsSymlink();
|
||||
if (!isDev()) {
|
||||
|
@ -8,10 +8,11 @@
|
||||
*/
|
||||
|
||||
const { Command } = require('commander');
|
||||
const { isDev, run, postCheck, runInstall, promptForTs } = require('../util');
|
||||
const { isDev, run, postCheck, downloadPro, promptForTs } = require('../util');
|
||||
const { existsSync, rmSync } = require('fs');
|
||||
const { resolve } = require('path');
|
||||
const chalk = require('chalk');
|
||||
const chokidar = require('chokidar');
|
||||
|
||||
function deleteSockFiles() {
|
||||
const { SOCKET_PATH, PM2_HOME } = process.env;
|
||||
@ -38,6 +39,23 @@ module.exports = (cli) => {
|
||||
.option('--quickstart')
|
||||
.allowUnknownOption()
|
||||
.action(async (opts) => {
|
||||
if (opts.quickstart) {
|
||||
await downloadPro();
|
||||
}
|
||||
|
||||
const watcher = chokidar.watch('./storage/plugins/**/*', {
|
||||
cwd: process.cwd(),
|
||||
ignoreInitial: true,
|
||||
ignored: /(^|[\/\\])\../, // 忽略隐藏文件
|
||||
persistent: true,
|
||||
depth: 1, // 只监听第一层目录
|
||||
});
|
||||
|
||||
watcher.on('addDir', async (pathname) => {
|
||||
console.log('pathname', pathname);
|
||||
await run('yarn', ['nocobase', 'pm2-restart']);
|
||||
});
|
||||
|
||||
if (opts.port) {
|
||||
process.env.APP_PORT = opts.port;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
const chalk = require('chalk');
|
||||
const { Command } = require('commander');
|
||||
const { resolve } = require('path');
|
||||
const { run, promptForTs, runAppCommand, hasCorePackages, updateJsonFile, hasTsNode } = require('../util');
|
||||
const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode } = require('../util');
|
||||
const { existsSync, rmSync } = require('fs');
|
||||
|
||||
/**
|
||||
@ -29,15 +29,18 @@ module.exports = (cli) => {
|
||||
if (hasTsNode()) promptForTs();
|
||||
if (hasCorePackages()) {
|
||||
// await run('yarn', ['install']);
|
||||
await downloadPro();
|
||||
await runAppCommand('upgrade');
|
||||
return;
|
||||
}
|
||||
if (options.skipCodeUpdate) {
|
||||
await downloadPro();
|
||||
await runAppCommand('upgrade');
|
||||
return;
|
||||
}
|
||||
// await runAppCommand('upgrade');
|
||||
if (!hasTsNode()) {
|
||||
await downloadPro();
|
||||
await runAppCommand('upgrade');
|
||||
return;
|
||||
}
|
||||
@ -54,8 +57,9 @@ module.exports = (cli) => {
|
||||
stdio: 'pipe',
|
||||
});
|
||||
if (pkg.version === stdout) {
|
||||
await downloadPro();
|
||||
await runAppCommand('upgrade');
|
||||
rmAppDir();
|
||||
await rmAppDir();
|
||||
return;
|
||||
}
|
||||
const currentY = 1 * pkg.version.split('.')[1];
|
||||
@ -66,7 +70,8 @@ module.exports = (cli) => {
|
||||
await run('yarn', ['add', '@nocobase/cli', '@nocobase/devtools', '-W']);
|
||||
}
|
||||
await run('yarn', ['install']);
|
||||
await downloadPro();
|
||||
await runAppCommand('upgrade');
|
||||
rmAppDir();
|
||||
await rmAppDir();
|
||||
});
|
||||
};
|
||||
|
@ -72,7 +72,7 @@ class PluginGenerator extends Generator {
|
||||
});
|
||||
this.log('');
|
||||
genTsConfigPaths();
|
||||
execa.sync('yarn', ['postinstall', '--skip-umi'], { shell: true, stdio: 'inherit' });
|
||||
execa.sync('yarn', ['postinstall'], { shell: true, stdio: 'inherit' });
|
||||
this.log(`The plugin folder is in ${chalk.green(`packages/plugins/${name}`)}`);
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +163,10 @@ exports.promptForTs = () => {
|
||||
console.log(chalk.green('WAIT: ') + 'TypeScript compiling...');
|
||||
};
|
||||
|
||||
exports.downloadPro = async () => {
|
||||
await exports.run('yarn', ['nocobase', 'pkg', 'download-pro']);
|
||||
};
|
||||
|
||||
exports.updateJsonFile = async (target, fn) => {
|
||||
const content = await readFile(target, 'utf-8');
|
||||
const json = JSON.parse(content);
|
||||
@ -416,3 +420,13 @@ exports.initEnv = function initEnv() {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
exports.generatePlugins = function () {
|
||||
try {
|
||||
require.resolve('@nocobase/devtools/umiConfig');
|
||||
const { generatePlugins } = require('@nocobase/devtools/umiConfig');
|
||||
generatePlugins();
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
"@ant-design/icons": "^5.1.4",
|
||||
"@ant-design/pro-layout": "^7.16.11",
|
||||
"@antv/g2plot": "^2.4.18",
|
||||
"@budibase/handlebars-helpers": "^0.13.2",
|
||||
"@ctrl/tinycolor": "^3.6.0",
|
||||
"@dnd-kit/core": "^5.0.1",
|
||||
"@dnd-kit/modifiers": "^6.0.0",
|
||||
@ -30,7 +31,7 @@
|
||||
"@nocobase/sdk": "1.4.0-alpha",
|
||||
"@nocobase/utils": "1.4.0-alpha",
|
||||
"ahooks": "^3.7.2",
|
||||
"antd": "^5.12.8",
|
||||
"antd": "5.12.8",
|
||||
"antd-style": "3.4.5",
|
||||
"axios": "^0.26.1",
|
||||
"bignumber.js": "^9.1.2",
|
||||
@ -48,6 +49,7 @@
|
||||
"markdown-it-highlightjs": "3.3.1",
|
||||
"mathjs": "^10.6.0",
|
||||
"mermaid": "9.4.3",
|
||||
"mime": "^4.0.4",
|
||||
"mime-match": "^1.0.2",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
"react-drag-listview": "^0.1.9",
|
||||
@ -57,7 +59,7 @@
|
||||
"react-i18next": "^11.15.1",
|
||||
"react-iframe": "~1.8.5",
|
||||
"react-image-lightbox": "^5.1.4",
|
||||
"react-intersection-observer": "9.8.1",
|
||||
"react-intersection-observer": "9.13.0",
|
||||
"react-js-cron": "^3.1.0",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.11.2",
|
||||
|
@ -20,6 +20,7 @@ import { useResourceActionContext } from '../collection-manager/ResourceActionPr
|
||||
import { useDataSourceKey } from '../data-source/data-source/DataSourceProvider';
|
||||
import { useRecord } from '../record-provider';
|
||||
import { SchemaComponentOptions, useDesignable } from '../schema-component';
|
||||
import { CollectionNotAllowViewPlaceholder } from '../data-source';
|
||||
|
||||
import { useApp } from '../application';
|
||||
|
||||
@ -102,6 +103,13 @@ export const useACLContext = () => {
|
||||
export const ACLActionParamsContext = createContext<any>({});
|
||||
ACLActionParamsContext.displayName = 'ACLActionParamsContext';
|
||||
|
||||
export const ACLCustomContext = createContext<any>({});
|
||||
ACLCustomContext.displayName = 'ACLCustomContext';
|
||||
|
||||
const useACLCustomContext = () => {
|
||||
return useContext(ACLCustomContext);
|
||||
};
|
||||
|
||||
export const useACLRolesCheck = () => {
|
||||
const ctx = useContext(ACLContext);
|
||||
const dataSourceName = useDataSourceKey();
|
||||
@ -204,11 +212,23 @@ export function useACLRoleContext() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get whether the current user has permission to configure UI
|
||||
* @returns {allowConfigUI: boolean}
|
||||
*/
|
||||
export function useUIConfigurationPermissions(): { allowConfigUI: boolean } {
|
||||
const { allowAll, snippets } = useACLRoleContext();
|
||||
return {
|
||||
allowConfigUI: allowAll || snippets.includes('ui.*'),
|
||||
};
|
||||
}
|
||||
|
||||
export const ACLCollectionProvider = (props) => {
|
||||
const { allowAll, parseAction } = useACLRoleContext();
|
||||
const { allowAll: customAllowAll } = useACLCustomContext();
|
||||
const app = useApp();
|
||||
const schema = useFieldSchema();
|
||||
if (allowAll || app.disableAcl) {
|
||||
if (allowAll || app.disableAcl || customAllowAll) {
|
||||
return props.children;
|
||||
}
|
||||
let actionPath = schema?.['x-acl-action'] || props.actionPath;
|
||||
@ -222,7 +242,7 @@ export const ACLCollectionProvider = (props) => {
|
||||
}
|
||||
const params = parseAction(actionPath, { schema });
|
||||
if (!params) {
|
||||
return null;
|
||||
return <CollectionNotAllowViewPlaceholder />;
|
||||
}
|
||||
const [_, actionName] = actionPath.split(':');
|
||||
params.actionName = actionName;
|
||||
|
@ -54,6 +54,7 @@ export const StrategyActions = connect((props) => {
|
||||
<Table
|
||||
size={'small'}
|
||||
pagination={false}
|
||||
rowKey={'name'}
|
||||
columns={[
|
||||
{
|
||||
dataIndex: 'displayName',
|
||||
|
@ -92,17 +92,31 @@ export class APIClient extends APIClientSDK {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
const errs = error?.response?.data?.errors || [{ message: 'Server error' }];
|
||||
const errs = this.toErrMessages(error);
|
||||
// Hard code here temporarily
|
||||
// TODO(yangqia): improve error code and message
|
||||
if (errs.find((error: { code?: string }) => error.code === 'ROLE_NOT_FOUND_ERR')) {
|
||||
this.auth.setRole(null);
|
||||
}
|
||||
if (errs.find((error: { code?: string }) => error.code === 'TOKEN_INVALID')) {
|
||||
this.auth.setToken(null);
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
toErrMessages(error) {
|
||||
if (typeof error?.response?.data === 'string') {
|
||||
return [{ message: error?.response?.data }];
|
||||
}
|
||||
return (
|
||||
error?.response?.data?.errors ||
|
||||
error?.response?.data?.messages ||
|
||||
error?.response?.error || [{ message: error.message || 'Server error' }]
|
||||
);
|
||||
}
|
||||
|
||||
useNotificationMiddleware() {
|
||||
this.axios.interceptors.response.use(
|
||||
(response) => {
|
||||
@ -119,9 +133,11 @@ export class APIClient extends APIClientSDK {
|
||||
}
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
async (error) => {
|
||||
if (this.silence) {
|
||||
throw error;
|
||||
console.error(error);
|
||||
return;
|
||||
// throw error;
|
||||
}
|
||||
const redirectTo = error?.response?.data?.redirectTo;
|
||||
if (redirectTo) {
|
||||
@ -143,7 +159,7 @@ export class APIClient extends APIClientSDK {
|
||||
} else if (this.app.error) {
|
||||
this.app.error = null;
|
||||
}
|
||||
let errs = error?.response?.data?.errors || error?.response?.data?.messages || [{ message: 'Server error' }];
|
||||
let errs = this.toErrMessages(error);
|
||||
errs = errs.filter((error) => {
|
||||
const lastTime = errorCache.get(error.message);
|
||||
if (lastTime && new Date().getTime() - lastTime < 500) {
|
||||
|
@ -34,17 +34,17 @@ import { compose, normalizeContainer } from './utils';
|
||||
import { defineGlobalDeps } from './utils/globalDeps';
|
||||
import { getRequireJs } from './utils/requirejs';
|
||||
|
||||
import { CollectionFieldInterfaceComponentOption } from '../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { CollectionField } from '../data-source/collection-field/CollectionField';
|
||||
import { DataSourceApplicationProvider } from '../data-source/components/DataSourceApplicationProvider';
|
||||
import { DataBlockProvider } from '../data-source/data-block/DataBlockProvider';
|
||||
import { DataSourceManager, type DataSourceManagerOptions } from '../data-source/data-source/DataSourceManager';
|
||||
import { CollectionFieldInterfaceComponentOption } from '../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
|
||||
import type { CollectionFieldInterfaceFactory } from '../data-source';
|
||||
import { OpenModeProvider } from '../modules/popup/OpenModeProvider';
|
||||
import { AppSchemaComponentProvider } from './AppSchemaComponentProvider';
|
||||
import type { Plugin } from './Plugin';
|
||||
import type { RequireJS } from './utils/requirejs';
|
||||
import type { CollectionFieldInterfaceFactory } from '../data-source';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -282,10 +282,21 @@ export class Application {
|
||||
});
|
||||
}
|
||||
loadFailed = true;
|
||||
const others = error?.response?.data?.error || error?.response?.data?.errors?.[0] || { message: error?.message };
|
||||
const toError = (error) => {
|
||||
if (typeof error?.response?.data === 'string') {
|
||||
return { message: error?.response?.data };
|
||||
}
|
||||
if (error?.response?.data?.error) {
|
||||
return error?.response?.data?.error;
|
||||
}
|
||||
if (error?.response?.data?.errors?.[0]) {
|
||||
return error?.response?.data?.errors?.[0];
|
||||
}
|
||||
return { message: error?.message };
|
||||
};
|
||||
this.error = {
|
||||
code: 'LOAD_ERROR',
|
||||
...others,
|
||||
...toError(error),
|
||||
};
|
||||
console.error(error, this.error);
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ describe('remotePlugins', () => {
|
||||
expect(requirejs.requirejs.config).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it('If there is devDynamicImport and devDynamicImport returns partial, remote API will be requested', async () => {
|
||||
it.skip('If there is devDynamicImport and devDynamicImport returns partial, remote API will be requested', async () => {
|
||||
const remoteFn = vi.fn();
|
||||
const mockPluginsModules = (pluginData, resolve) => {
|
||||
remoteFn();
|
||||
|
@ -48,7 +48,11 @@ export const SchemaInitializerMenu: FC<MenuProps> = (props) => {
|
||||
const { items, ...others } = props;
|
||||
const { token } = theme.useToken();
|
||||
const itemsWithPopupClass = useMemo(
|
||||
() => items.map((item) => ({ ...item, popupClassName: `${hashId} ${componentCls}-menu-sub` })),
|
||||
() =>
|
||||
items.map((item) => ({
|
||||
...item,
|
||||
popupClassName: `${hashId} ${componentCls}-menu-sub`,
|
||||
})),
|
||||
[componentCls, hashId, items],
|
||||
);
|
||||
// selectedKeys 为了不让有选中效果
|
||||
@ -62,9 +66,23 @@ export const SchemaInitializerMenu: FC<MenuProps> = (props) => {
|
||||
border-inline-end: 0 !important;
|
||||
.ant-menu-sub {
|
||||
max-height: 50vh !important;
|
||||
padding: ${token.paddingXXS}px !important;
|
||||
}
|
||||
.ant-menu-item {
|
||||
margin-block: 0;
|
||||
margin-inline: ${token.marginXXS}px !important;
|
||||
margin-block: 0 !important;
|
||||
width: auto !important;
|
||||
padding: 0 ${token.paddingSM}px 0 ${token.padding}px !important;
|
||||
}
|
||||
.ant-menu-item-group-title {
|
||||
padding: 0 ${token.padding}px;
|
||||
margin-inline: 0;
|
||||
line-height: 32px;
|
||||
}
|
||||
.ant-menu-submenu-title {
|
||||
margin: 0 ${token.marginXXS}px !important;
|
||||
padding-left: ${token.padding}px !important;
|
||||
width: auto !important;
|
||||
}
|
||||
.ant-menu-root {
|
||||
margin: 0 -${token.margin}px;
|
||||
|
@ -51,11 +51,6 @@ export const useSchemaInitializerStyles = genStyleHook('nb-schema-initializer',
|
||||
},
|
||||
},
|
||||
},
|
||||
[`${componentCls}-menu-sub`]: {
|
||||
ul: {
|
||||
maxHeight: '50vh !important',
|
||||
},
|
||||
},
|
||||
[`${componentCls}-item-content`]: {
|
||||
marginLeft: token.marginXS,
|
||||
},
|
||||
|
@ -12,10 +12,10 @@ import React, { FC, useMemo } from 'react';
|
||||
import { useApp } from '../../hooks';
|
||||
import { SchemaInitializerItems } from '../components';
|
||||
import { SchemaInitializerButton } from '../components/SchemaInitializerButton';
|
||||
import { withInitializer } from '../withInitializer';
|
||||
import { SchemaInitializerOptions } from '../types';
|
||||
import { SchemaInitializer } from '../SchemaInitializer';
|
||||
|
||||
import { SchemaInitializerOptions } from '../types';
|
||||
import { withInitializer } from '../withInitializer';
|
||||
import { useOpenModeContext } from '../../../modules/popup/OpenModeProvider';
|
||||
const InitializerComponent: FC<SchemaInitializerOptions<any, any>> = React.memo((options) => {
|
||||
const Component: any = options.Component || SchemaInitializerButton;
|
||||
|
||||
@ -38,6 +38,18 @@ export function useSchemaInitializerRender<P1 = ButtonProps, P2 = {}>(
|
||||
options?: Omit<SchemaInitializerOptions<P1, P2>, 'name'>,
|
||||
) {
|
||||
const app = useApp();
|
||||
const { isMobile } = useOpenModeContext() || {};
|
||||
|
||||
// compatible with mobile
|
||||
// TODO: delete this code
|
||||
if (
|
||||
name === 'popup:common:addBlock' &&
|
||||
app.schemaInitializerManager.has('mobile:popup:common:addBlock') &&
|
||||
isMobile
|
||||
) {
|
||||
name = 'mobile:popup:common:addBlock';
|
||||
}
|
||||
|
||||
const initializer = useMemo(
|
||||
() => (typeof name === 'object' ? name : app.schemaInitializerManager.get<P1, P2>(name)),
|
||||
[app.schemaInitializerManager, name],
|
||||
|
@ -7,10 +7,10 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import type { DevDynamicImport } from '../Application';
|
||||
import type { Plugin } from '../Plugin';
|
||||
import type { PluginData } from '../PluginManager';
|
||||
import type { RequireJS } from './requirejs';
|
||||
import type { DevDynamicImport } from '../Application';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -131,7 +131,10 @@ export async function getPlugins(options: GetPluginsOption): Promise<Array<[stri
|
||||
return res;
|
||||
}
|
||||
|
||||
const remotePluginList = await getRemotePlugins(requirejs, remotePlugins);
|
||||
res.push(...remotePluginList);
|
||||
if (res.length === 0) {
|
||||
const remotePluginList = await getRemotePlugins(requirejs, remotePlugins);
|
||||
res.push(...remotePluginList);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { Field, GeneralField } from '@formily/core';
|
||||
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import { Col, Row } from 'antd';
|
||||
import merge from 'deepmerge';
|
||||
import { isArray } from 'lodash';
|
||||
import template from 'lodash/template';
|
||||
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -307,7 +308,15 @@ export const useFilterByTk = () => {
|
||||
const association = getCollectionField(assoc);
|
||||
return recordData?.[association.targetKey || 'id'];
|
||||
}
|
||||
return recordData?.[collection.filterTargetKey || 'id'];
|
||||
if (isArray(collection.filterTargetKey)) {
|
||||
const filterByTk = {};
|
||||
for (const key of collection.filterTargetKey) {
|
||||
filterByTk[key] = recordData?.[key];
|
||||
}
|
||||
return filterByTk;
|
||||
} else {
|
||||
return recordData?.[collection.filterTargetKey || 'id'];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -141,7 +141,8 @@ export const useDetailsBlockProps = () => {
|
||||
ctx.form
|
||||
.reset()
|
||||
.then(() => {
|
||||
ctx.form.setValues(data || {});
|
||||
ctx.form.setInitialValues(data || {});
|
||||
// Using `ctx.form.setValues(data || {});` here may cause an internal infinite loop in Formily
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ const InternalFormBlockProvider = (props) => {
|
||||
updateAssociationValues,
|
||||
]);
|
||||
|
||||
if (service.loading && Object.keys(form?.initialValues)?.length === 0 && action) {
|
||||
if (service.loading && Object.keys(form?.initialValues || {})?.length === 0 && action) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
@ -151,9 +151,10 @@ export const useFormBlockContext = () => {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const useFormBlockProps = (shouldClearInitialValues = false) => {
|
||||
export const useFormBlockProps = () => {
|
||||
const ctx = useFormBlockContext();
|
||||
const treeParentRecord = useTreeParentRecord();
|
||||
|
||||
useEffect(() => {
|
||||
if (treeParentRecord) {
|
||||
ctx.form?.query('parent').take((field) => {
|
||||
@ -164,19 +165,14 @@ export const useFormBlockProps = (shouldClearInitialValues = false) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!ctx?.service?.loading) {
|
||||
const form: Form = ctx.form;
|
||||
if (form) {
|
||||
// form 字段中可能一开始就存在一些默认值(比如设置了字段默认值的模板区块)。在编辑表单中,
|
||||
// 这些默认值是不需要的,需要清除掉,不然会导致一些问题。比如:https://github.com/nocobase/nocobase/issues/4868
|
||||
if (shouldClearInitialValues === true) {
|
||||
form.initialValues = {};
|
||||
form.reset();
|
||||
}
|
||||
form.setInitialValues(ctx.service?.data?.data);
|
||||
}
|
||||
const form: Form = ctx.form;
|
||||
|
||||
if (!form || ctx.service?.loading) {
|
||||
return;
|
||||
}
|
||||
}, [ctx?.service?.loading]);
|
||||
|
||||
form.setInitialValues(ctx.service?.data?.data);
|
||||
}, [ctx.form, ctx.service?.data?.data, ctx.service?.loading]);
|
||||
return {
|
||||
form: ctx.form,
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ import { FormContext, useField, useFieldSchema } from '@formily/react';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useCollectionManager_deprecated } from '../collection-manager';
|
||||
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
||||
import { useTableBlockParams } from '../modules/blocks/data-blocks/table';
|
||||
import { useTableBlockParams } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps';
|
||||
import { FixedBlockWrapper, SchemaComponentOptions } from '../schema-component';
|
||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||
import { useBlockHeightProps } from './hooks';
|
||||
|
@ -183,9 +183,10 @@ export const useTableFieldProps = () => {
|
||||
rowKey: (record: any) => {
|
||||
return field.value?.indexOf?.(record);
|
||||
},
|
||||
onRowSelectionChange(selectedRowKeys) {
|
||||
onRowSelectionChange(selectedRowKeys, selectedRowData) {
|
||||
ctx.field.data = ctx?.field?.data || {};
|
||||
ctx.field.data.selectedRowKeys = selectedRowKeys;
|
||||
ctx.field.data.selectedRowData = selectedRowData;
|
||||
},
|
||||
onChange({ current, pageSize }) {
|
||||
ctx.service.run({ page: current, pageSize });
|
||||
|
@ -319,9 +319,10 @@ export const useTableSelectorProps = () => {
|
||||
dragSort: false,
|
||||
rowKey: ctx.rowKey || 'id',
|
||||
pagination: fieldSchema?.['x-component-props']?.pagination === false ? false : field.componentProps.pagination,
|
||||
onRowSelectionChange(selectedRowKeys, selectedRows) {
|
||||
onRowSelectionChange(selectedRowKeys, selectedRowData) {
|
||||
ctx.field.data = ctx?.field?.data || {};
|
||||
ctx.field.data.selectedRowKeys = selectedRowKeys;
|
||||
ctx.field.data.selectedRowData = selectedRowData;
|
||||
},
|
||||
async onRowDragEnd({ from, to }) {
|
||||
await ctx.resource.move({
|
||||
|
@ -24,7 +24,7 @@ describe('parseVariablesAndChangeParamsToQueryString', () => {
|
||||
{ name: 'param3', value: 'value3' },
|
||||
];
|
||||
const variables: any = {
|
||||
parseVariable: vi.fn().mockResolvedValue('parsedValue'),
|
||||
parseVariable: vi.fn().mockResolvedValue({ value: 'parsedValue' }),
|
||||
};
|
||||
const localVariables: any = [
|
||||
{ name: '$var1', ctx: { value: 'localValue1' } },
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
useCollectionRecord,
|
||||
useDataSourceHeaders,
|
||||
useFormActiveFields,
|
||||
useParsedFilter,
|
||||
useRouterBasename,
|
||||
useTableBlockContext,
|
||||
} from '../..';
|
||||
@ -41,7 +42,7 @@ import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/Tree
|
||||
import { useRecord } from '../../record-provider';
|
||||
import { removeNullCondition, useActionContext, useCompile } from '../../schema-component';
|
||||
import { isSubMode } from '../../schema-component/antd/association-field/util';
|
||||
import { replaceVariables } from '../../schema-component/antd/form-v2/utils';
|
||||
import { replaceVariables } from '../../schema-settings/LinkageRules/bindLinkageRulesToFiled';
|
||||
import { useCurrentUserContext } from '../../user';
|
||||
import { useLocalVariables, useVariables } from '../../variables';
|
||||
import { VariableOption, VariablesContextType } from '../../variables/types';
|
||||
@ -163,9 +164,9 @@ export function useCollectValuesToSubmit() {
|
||||
}
|
||||
|
||||
if (isVariable(value)) {
|
||||
const result = await variables?.parseVariable(value, localVariables);
|
||||
if (result) {
|
||||
assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField });
|
||||
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
|
||||
if (parsedValue) {
|
||||
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
|
||||
}
|
||||
} else if (value != null && value !== '') {
|
||||
assignedValues[key] = value;
|
||||
@ -223,7 +224,7 @@ export const useCreateActionProps = () => {
|
||||
return {
|
||||
async onClick() {
|
||||
const { onSuccess, skipValidator, triggerWorkflows } = actionSchema?.['x-action-settings'] ?? {};
|
||||
|
||||
const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {};
|
||||
if (!skipValidator) {
|
||||
await form.submit();
|
||||
}
|
||||
@ -241,39 +242,49 @@ export const useCreateActionProps = () => {
|
||||
: undefined,
|
||||
updateAssociationValues,
|
||||
});
|
||||
setVisible?.(false);
|
||||
if (actionAfterSuccess === 'previous' || (!actionAfterSuccess && redirecting !== true)) {
|
||||
setVisible?.(false);
|
||||
}
|
||||
setSubmitted?.(true);
|
||||
setFormValueChanged?.(false);
|
||||
actionField.data.loading = false;
|
||||
actionField.data.data = data;
|
||||
// __parent?.service?.refresh?.();
|
||||
if (!onSuccess?.successMessage) {
|
||||
if (!successMessage) {
|
||||
message.success(t('Saved successfully'));
|
||||
await resetFormCorrectly(form);
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
if (manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
title: compile(successMessage),
|
||||
onOk: async () => {
|
||||
await resetFormCorrectly(form);
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
message.success(compile(successMessage));
|
||||
await resetFormCorrectly(form);
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,7 +322,7 @@ export const useAssociationCreateActionProps = () => {
|
||||
triggerWorkflows,
|
||||
} = actionSchema?.['x-action-settings'] ?? {};
|
||||
const addChild = fieldSchema?.['x-component-props']?.addChild;
|
||||
|
||||
const { successMessage } = onSuccess || {};
|
||||
const assignedValues = {};
|
||||
const waitList = Object.keys(originalAssignedValues).map(async (key) => {
|
||||
const value = originalAssignedValues[key];
|
||||
@ -324,9 +335,9 @@ export const useAssociationCreateActionProps = () => {
|
||||
}
|
||||
|
||||
if (isVariable(value)) {
|
||||
const result = await variables?.parseVariable(value, localVariables);
|
||||
if (result) {
|
||||
assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField });
|
||||
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
|
||||
if (parsedValue) {
|
||||
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
|
||||
}
|
||||
} else if (value != null && value !== '') {
|
||||
assignedValues[key] = value;
|
||||
@ -371,10 +382,10 @@ export const useAssociationCreateActionProps = () => {
|
||||
__parent?.service?.refresh?.();
|
||||
setVisible?.(false);
|
||||
setSubmitted?.(true);
|
||||
if (!onSuccess?.successMessage) {
|
||||
if (!successMessage) {
|
||||
return;
|
||||
}
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
message.success(compile(successMessage));
|
||||
} catch (error) {
|
||||
actionField.data.data = null;
|
||||
actionField.data.loading = false;
|
||||
@ -560,6 +571,7 @@ export const useCustomizeUpdateActionProps = () => {
|
||||
const variables = useVariables();
|
||||
const localVariables = useLocalVariables({ currentForm: form });
|
||||
const { name, getField } = useCollection_deprecated();
|
||||
const { setVisible } = useActionContext();
|
||||
|
||||
return {
|
||||
async onClick(e?, callBack?) {
|
||||
@ -569,7 +581,7 @@ export const useCustomizeUpdateActionProps = () => {
|
||||
skipValidator,
|
||||
triggerWorkflows,
|
||||
} = actionSchema?.['x-action-settings'] ?? {};
|
||||
|
||||
const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {};
|
||||
const assignedValues = {};
|
||||
const waitList = Object.keys(originalAssignedValues).map(async (key) => {
|
||||
const value = originalAssignedValues[key];
|
||||
@ -582,9 +594,9 @@ export const useCustomizeUpdateActionProps = () => {
|
||||
}
|
||||
|
||||
if (isVariable(value)) {
|
||||
const result = await variables?.parseVariable(value, localVariables);
|
||||
if (result) {
|
||||
assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField });
|
||||
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
|
||||
if (parsedValue) {
|
||||
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
|
||||
}
|
||||
} else if (value != null && value !== '') {
|
||||
assignedValues[key] = value;
|
||||
@ -603,6 +615,9 @@ export const useCustomizeUpdateActionProps = () => {
|
||||
? triggerWorkflows.map((row) => [row.workflowKey, row.context].filter(Boolean).join('!')).join(',')
|
||||
: undefined,
|
||||
});
|
||||
if (actionAfterSuccess === 'previous' || (!actionAfterSuccess && redirecting !== true)) {
|
||||
setVisible?.(false);
|
||||
}
|
||||
// service?.refresh?.();
|
||||
if (callBack) {
|
||||
callBack?.();
|
||||
@ -610,29 +625,29 @@ export const useCustomizeUpdateActionProps = () => {
|
||||
if (!(resource instanceof TableFieldResource)) {
|
||||
__parent?.service?.refresh?.();
|
||||
}
|
||||
if (!onSuccess?.successMessage) {
|
||||
if (!successMessage) {
|
||||
return;
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
if (manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
title: compile(successMessage),
|
||||
onOk: async () => {
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
message.success(compile(successMessage));
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,6 +672,7 @@ export const useCustomizeBulkUpdateActionProps = () => {
|
||||
const record = useRecord();
|
||||
const { name, getField } = useCollection_deprecated();
|
||||
const localVariables = useLocalVariables();
|
||||
const { setVisible } = useActionContext();
|
||||
|
||||
return {
|
||||
async onClick() {
|
||||
@ -665,6 +681,7 @@ export const useCustomizeBulkUpdateActionProps = () => {
|
||||
onSuccess,
|
||||
updateMode,
|
||||
} = actionSchema?.['x-action-settings'] ?? {};
|
||||
const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {};
|
||||
actionField.data = field.data || {};
|
||||
actionField.data.loading = true;
|
||||
|
||||
@ -680,16 +697,18 @@ export const useCustomizeBulkUpdateActionProps = () => {
|
||||
}
|
||||
|
||||
if (isVariable(value)) {
|
||||
const result = await variables?.parseVariable(value, localVariables);
|
||||
if (result) {
|
||||
assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField });
|
||||
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
|
||||
if (parsedValue) {
|
||||
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
|
||||
}
|
||||
} else if (value != null && value !== '') {
|
||||
assignedValues[key] = value;
|
||||
}
|
||||
});
|
||||
await Promise.all(waitList);
|
||||
|
||||
if (actionAfterSuccess === 'previous' || (!actionAfterSuccess && redirecting !== true)) {
|
||||
setVisible?.(false);
|
||||
}
|
||||
modal.confirm({
|
||||
title: t('Bulk update'),
|
||||
content: updateMode === 'selected' ? t('Update selected data?') : t('Update all data?'),
|
||||
@ -722,29 +741,29 @@ export const useCustomizeBulkUpdateActionProps = () => {
|
||||
if (!(resource instanceof TableFieldResource)) {
|
||||
__parent?.service?.refresh?.();
|
||||
}
|
||||
if (!onSuccess?.successMessage) {
|
||||
if (!successMessage) {
|
||||
return;
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
if (manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
title: compile(successMessage),
|
||||
onOk: async () => {
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
message.success(compile(successMessage));
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -770,6 +789,7 @@ export const useCustomizeRequestActionProps = () => {
|
||||
const currentUserContext = useCurrentUserContext();
|
||||
const currentUser = currentUserContext?.data?.data;
|
||||
const actionField = useField();
|
||||
const { t } = useTranslation();
|
||||
const { setVisible } = useActionContext();
|
||||
const { modal } = App.useApp();
|
||||
const { getActiveFieldsName } = useFormActiveFields() || {};
|
||||
@ -777,6 +797,7 @@ export const useCustomizeRequestActionProps = () => {
|
||||
return {
|
||||
async onClick() {
|
||||
const { skipValidator, onSuccess, requestSettings } = actionSchema?.['x-action-settings'] ?? {};
|
||||
const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {};
|
||||
const xAction = actionSchema?.['x-action'];
|
||||
if (!requestSettings['url']) {
|
||||
return;
|
||||
@ -821,26 +842,36 @@ export const useCustomizeRequestActionProps = () => {
|
||||
}
|
||||
service?.refresh?.();
|
||||
if (xAction === 'customize:form:request') {
|
||||
setVisible?.(false);
|
||||
if (actionAfterSuccess === 'previous' || (!actionAfterSuccess && redirecting !== true)) {
|
||||
setVisible?.(false);
|
||||
}
|
||||
}
|
||||
if (!onSuccess?.successMessage) {
|
||||
return;
|
||||
if (!successMessage) {
|
||||
message.success(t('Saved successfully'));
|
||||
await resetFormCorrectly(form);
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
if (manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
title: compile(successMessage),
|
||||
onOk: async () => {
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
message.success(compile(successMessage));
|
||||
}
|
||||
} finally {
|
||||
actionField.data.loading = false;
|
||||
@ -853,7 +884,7 @@ export const useUpdateActionProps = () => {
|
||||
const form = useForm();
|
||||
const filterByTk = useFilterByTk();
|
||||
const { field, resource, __parent } = useBlockRequestContext();
|
||||
const { setVisible, setSubmitted, setFormValueChanged } = useActionContext();
|
||||
const { setVisible, setFormValueChanged } = useActionContext();
|
||||
const actionSchema = useFieldSchema();
|
||||
const navigate = useNavigateNoUpdate();
|
||||
const { fields, getField, name } = useCollection_deprecated();
|
||||
@ -867,7 +898,7 @@ export const useUpdateActionProps = () => {
|
||||
const { getActiveFieldsName } = useFormActiveFields() || {};
|
||||
|
||||
return {
|
||||
async onClick() {
|
||||
async onClick(e?, callBack?) {
|
||||
const {
|
||||
assignedValues: originalAssignedValues = {},
|
||||
onSuccess,
|
||||
@ -875,7 +906,7 @@ export const useUpdateActionProps = () => {
|
||||
skipValidator,
|
||||
triggerWorkflows,
|
||||
} = actionSchema?.['x-action-settings'] ?? {};
|
||||
|
||||
const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {};
|
||||
const assignedValues = {};
|
||||
const waitList = Object.keys(originalAssignedValues).map(async (key) => {
|
||||
const value = originalAssignedValues[key];
|
||||
@ -888,9 +919,9 @@ export const useUpdateActionProps = () => {
|
||||
}
|
||||
|
||||
if (isVariable(value)) {
|
||||
const result = await variables?.parseVariable(value, localVariables);
|
||||
if (result) {
|
||||
assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField });
|
||||
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
|
||||
if (parsedValue) {
|
||||
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
|
||||
}
|
||||
} else if (value != null && value !== '') {
|
||||
assignedValues[key] = value;
|
||||
@ -930,33 +961,42 @@ export const useUpdateActionProps = () => {
|
||||
});
|
||||
actionField.data.loading = false;
|
||||
// __parent?.service?.refresh?.();
|
||||
setVisible?.(false);
|
||||
setSubmitted?.(true);
|
||||
if (callBack) {
|
||||
callBack?.();
|
||||
}
|
||||
if (actionAfterSuccess === 'previous' || (!actionAfterSuccess && redirecting !== true)) {
|
||||
setVisible?.(false);
|
||||
}
|
||||
setFormValueChanged?.(false);
|
||||
if (!onSuccess?.successMessage) {
|
||||
if (!successMessage) {
|
||||
return;
|
||||
}
|
||||
if (onSuccess?.manualClose) {
|
||||
if (manualClose) {
|
||||
modal.success({
|
||||
title: compile(onSuccess?.successMessage),
|
||||
title: compile(successMessage),
|
||||
onOk: async () => {
|
||||
await form.reset();
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
if (((redirecting && !actionAfterSuccess) || actionAfterSuccess === 'redirect') && redirectTo) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.success(compile(onSuccess?.successMessage));
|
||||
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
|
||||
if (isURL(onSuccess.redirectTo)) {
|
||||
window.location.href = onSuccess.redirectTo;
|
||||
message.success(compile(successMessage));
|
||||
if (
|
||||
((redirecting && !actionAfterSuccess) ||
|
||||
actionAfterSuccess === 'redirect' ||
|
||||
actionAfterSuccess === 'redirect') &&
|
||||
redirectTo
|
||||
) {
|
||||
if (isURL(redirectTo)) {
|
||||
window.location.href = redirectTo;
|
||||
} else {
|
||||
navigate(onSuccess.redirectTo);
|
||||
navigate(redirectTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1077,13 +1117,25 @@ export const useBulkDestroyActionProps = () => {
|
||||
const { field } = useBlockRequestContext();
|
||||
const { resource, service } = useBlockRequestContext();
|
||||
const { setSubmitted } = useActionContext();
|
||||
const collection = useCollection_deprecated();
|
||||
const { filterTargetKey } = collection;
|
||||
return {
|
||||
async onClick(e?, callBack?) {
|
||||
let filterByTk = field.data?.selectedRowKeys;
|
||||
if (Array.isArray(filterTargetKey)) {
|
||||
filterByTk = field.data.selectedRowData.map((v) => {
|
||||
const obj = {};
|
||||
filterTargetKey.map((j) => {
|
||||
obj[j] = v[j];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
if (!field?.data?.selectedRowKeys?.length) {
|
||||
return;
|
||||
}
|
||||
await resource.destroy({
|
||||
filterByTk: field.data?.selectedRowKeys,
|
||||
filterByTk,
|
||||
});
|
||||
field.data.selectedRowKeys = [];
|
||||
const currentPage = service.params[0]?.page;
|
||||
@ -1095,7 +1147,7 @@ export const useBulkDestroyActionProps = () => {
|
||||
callBack?.();
|
||||
}
|
||||
setSubmitted?.(true);
|
||||
// service?.refresh?.();
|
||||
service?.refresh?.();
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -1245,6 +1297,7 @@ export const useAssociationFilterBlockProps = () => {
|
||||
const { props: blockProps } = useBlockRequestContext();
|
||||
const headers = useDataSourceHeaders(blockProps?.dataSource);
|
||||
const cm = useCollectionManager_deprecated();
|
||||
const { filter, parseVariableLoading } = useParsedFilter({ filterOption: field.componentProps?.params?.filter });
|
||||
|
||||
let list, handleSearchInput, params, run, data, valueKey, labelKey, filterKey;
|
||||
|
||||
@ -1264,6 +1317,7 @@ export const useAssociationFilterBlockProps = () => {
|
||||
pageSize: 200,
|
||||
page: 1,
|
||||
...field.componentProps?.params,
|
||||
filter,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -1275,12 +1329,64 @@ export const useAssociationFilterBlockProps = () => {
|
||||
|
||||
useEffect(() => {
|
||||
// 由于选项字段不需要触发当前请求,所以请求单独在关系字段的时候触发
|
||||
if (!isOptionalField(collectionField)) {
|
||||
if (!isOptionalField(collectionField) && parseVariableLoading === false) {
|
||||
run();
|
||||
}
|
||||
|
||||
// do not format the dependencies
|
||||
}, [collectionField, labelKey, run, valueKey, field.componentProps?.params, field.componentProps?.params?.sort]);
|
||||
}, [
|
||||
collectionField,
|
||||
labelKey,
|
||||
run,
|
||||
valueKey,
|
||||
field.componentProps?.params,
|
||||
field.componentProps?.params?.sort,
|
||||
parseVariableLoading,
|
||||
]);
|
||||
|
||||
const onSelected = useCallback(
|
||||
(value) => {
|
||||
const { targets, uid } = findFilterTargets(fieldSchema);
|
||||
|
||||
getDataBlocks().forEach((block) => {
|
||||
const target = targets.find((target) => target.uid === block.uid);
|
||||
if (!target) return;
|
||||
|
||||
const key = `${uid}${fieldSchema.name}`;
|
||||
const param = block.service.params?.[0] || {};
|
||||
|
||||
if (!block.service.params?.[1]?.filters) {
|
||||
_.set(block.service.params, '[1].filters', {});
|
||||
}
|
||||
|
||||
// 保留原有的 filter
|
||||
const storedFilter = block.service.params[1].filters;
|
||||
|
||||
if (value.length) {
|
||||
storedFilter[key] = {
|
||||
[filterKey]: value,
|
||||
};
|
||||
} else {
|
||||
if (block.dataLoadingMode === 'manual') {
|
||||
return block.clearData();
|
||||
}
|
||||
delete storedFilter[key];
|
||||
}
|
||||
|
||||
const mergedFilter = mergeFilter([...Object.values(storedFilter), block.defaultFilter]);
|
||||
|
||||
return block.doFilter(
|
||||
{
|
||||
...param,
|
||||
page: 1,
|
||||
filter: mergedFilter,
|
||||
},
|
||||
{ filters: storedFilter },
|
||||
);
|
||||
});
|
||||
},
|
||||
[fieldSchema, filterKey, getDataBlocks],
|
||||
);
|
||||
|
||||
if (!collectionField) {
|
||||
return {};
|
||||
@ -1323,41 +1429,6 @@ export const useAssociationFilterBlockProps = () => {
|
||||
};
|
||||
}
|
||||
|
||||
const onSelected = (value) => {
|
||||
const { targets, uid } = findFilterTargets(fieldSchema);
|
||||
|
||||
getDataBlocks().forEach((block) => {
|
||||
const target = targets.find((target) => target.uid === block.uid);
|
||||
if (!target) return;
|
||||
|
||||
const key = `${uid}${fieldSchema.name}`;
|
||||
const param = block.service.params?.[0] || {};
|
||||
// 保留原有的 filter
|
||||
const storedFilter = block.service.params?.[1]?.filters || {};
|
||||
if (value.length) {
|
||||
storedFilter[key] = {
|
||||
[filterKey]: value,
|
||||
};
|
||||
} else {
|
||||
if (block.dataLoadingMode === 'manual') {
|
||||
return block.clearData();
|
||||
}
|
||||
delete storedFilter[key];
|
||||
}
|
||||
|
||||
const mergedFilter = mergeFilter([...Object.values(storedFilter), block.defaultFilter]);
|
||||
|
||||
return block.doFilter(
|
||||
{
|
||||
...param,
|
||||
page: 1,
|
||||
filter: mergedFilter,
|
||||
},
|
||||
{ filters: storedFilter },
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
/** 渲染 Collapse 的列表数据 */
|
||||
list,
|
||||
@ -1438,7 +1509,8 @@ export const useAssociationNames = (dataSource?: string) => {
|
||||
['hasOne', 'hasMany', 'belongsTo', 'belongsToMany', 'belongsToArray'].includes(collectionField.type);
|
||||
|
||||
// 根据联动规则中条件的字段获取一些 appends
|
||||
if (s['x-linkage-rules']) {
|
||||
// 需要排除掉子表格和子表单中的联动规则
|
||||
if (s['x-linkage-rules'] && !isSubMode(s)) {
|
||||
const collectAppends = (obj) => {
|
||||
const type = Object.keys(obj)[0] || '$and';
|
||||
const list = obj[type];
|
||||
@ -1661,8 +1733,8 @@ export async function parseVariablesAndChangeParamsToQueryString({
|
||||
searchParams.map(async ({ name, value }) => {
|
||||
if (typeof value === 'string') {
|
||||
if (isVariable(value)) {
|
||||
const result = await variables.parseVariable(value, localVariables);
|
||||
return { name, value: result };
|
||||
const { value: parsedValue } = (await variables.parseVariable(value, localVariables)) || {};
|
||||
return { name, value: parsedValue };
|
||||
}
|
||||
const result = await replaceVariableValue(value, variables, localVariables);
|
||||
return { name, value: result };
|
||||
|
@ -16,64 +16,23 @@ import { cloneDeep } from 'lodash';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRequest } from '../../api-client';
|
||||
import { CollectionFieldInterface } from '../../data-source';
|
||||
import { RecordProvider, useRecord } from '../../record-provider';
|
||||
import { ActionContextProvider, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
|
||||
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
||||
import { useCancelAction } from '../action-hooks';
|
||||
import { useCollectionManager_deprecated } from '../hooks';
|
||||
import useDialect from '../hooks/useDialect';
|
||||
import { IField } from '../interfaces/types';
|
||||
import * as components from './components';
|
||||
import { useFieldInterfaceOptions } from './interfaces';
|
||||
|
||||
const getSchema = (schema: IField, record: any, compile) => {
|
||||
const getSchema = (schema: CollectionFieldInterface, record: any, compile) => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
|
||||
const properties = cloneDeep(schema.properties) as any;
|
||||
const properties = schema.getConfigureFormProperties();
|
||||
|
||||
if (schema.hasDefaultValue === true) {
|
||||
properties['defaultValue'] = cloneDeep(schema?.default?.uiSchema);
|
||||
properties.defaultValue.required = false;
|
||||
properties['defaultValue']['title'] = compile('{{ t("Default value") }}');
|
||||
properties['defaultValue']['x-decorator'] = 'FormItem';
|
||||
properties['defaultValue']['x-reactions'] = [
|
||||
{
|
||||
dependencies: [
|
||||
'uiSchema.x-component-props.gmt',
|
||||
'uiSchema.x-component-props.showTime',
|
||||
'uiSchema.x-component-props.dateFormat',
|
||||
'uiSchema.x-component-props.timeFormat',
|
||||
],
|
||||
fulfill: {
|
||||
state: {
|
||||
componentProps: {
|
||||
gmt: '{{$deps[0]}}',
|
||||
showTime: '{{$deps[1]}}',
|
||||
dateFormat: '{{$deps[2]}}',
|
||||
timeFormat: '{{$deps[3]}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
dependencies: ['primaryKey', 'unique', 'autoIncrement'],
|
||||
when: '{{$deps[0]||$deps[1]||$deps[2]}}',
|
||||
fulfill: {
|
||||
state: {
|
||||
hidden: true,
|
||||
value: null,
|
||||
},
|
||||
},
|
||||
otherwise: {
|
||||
state: {
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
const initialValue: any = {
|
||||
name: `f_${uid()}`,
|
||||
...cloneDeep(schema.default),
|
||||
|
@ -64,6 +64,9 @@ const getSchema = (schema: IField, record: any, compile, getContainer): ISchema
|
||||
description: `{{t( "If a collection lacks a primary key, you must configure a unique record key to locate row records within a block, failure to configure this will prevent the creation of data blocks for the collection.")}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
multiple: true,
|
||||
},
|
||||
'x-reactions': ['{{useAsyncDataSource(loadFilterTargetKeys)}}'],
|
||||
},
|
||||
footer: {
|
||||
|
@ -16,6 +16,7 @@ import set from 'lodash/set';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAPIClient, useRequest } from '../../api-client';
|
||||
import { CollectionFieldInterface } from '../../data-source';
|
||||
import { useCollectionParentRecordData } from '../../data-source/collection-record/CollectionRecordProvider';
|
||||
import { RecordProvider, useRecord } from '../../record-provider';
|
||||
import { ActionContextProvider, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
|
||||
@ -23,60 +24,23 @@ import { useResourceActionContext, useResourceContext } from '../ResourceActionP
|
||||
import { useCancelAction } from '../action-hooks';
|
||||
import { useCollectionManager_deprecated } from '../hooks';
|
||||
import useDialect from '../hooks/useDialect';
|
||||
import { IField } from '../interfaces/types';
|
||||
import * as components from './components';
|
||||
|
||||
const getSchema = (schema: IField, record: any, compile, getContainer): ISchema => {
|
||||
const getSchema = (
|
||||
schema: CollectionFieldInterface,
|
||||
defaultValues: any,
|
||||
record: any,
|
||||
compile,
|
||||
getContainer,
|
||||
): ISchema => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
const properties = cloneDeep(schema.properties) as any;
|
||||
const properties = schema.getConfigureFormProperties();
|
||||
if (properties?.name) {
|
||||
properties.name['x-disabled'] = true;
|
||||
}
|
||||
|
||||
if (schema.hasDefaultValue === true) {
|
||||
properties['defaultValue'] = cloneDeep(schema.default.uiSchema) || {};
|
||||
properties.defaultValue.required = false;
|
||||
properties['defaultValue']['title'] = compile('{{ t("Default value") }}');
|
||||
properties['defaultValue']['x-decorator'] = 'FormItem';
|
||||
properties['defaultValue']['x-reactions'] = [
|
||||
{
|
||||
dependencies: [
|
||||
'uiSchema.x-component-props.gmt',
|
||||
'uiSchema.x-component-props.showTime',
|
||||
'uiSchema.x-component-props.dateFormat',
|
||||
'uiSchema.x-component-props.timeFormat',
|
||||
],
|
||||
fulfill: {
|
||||
state: {
|
||||
componentProps: {
|
||||
gmt: '{{$deps[0]}}',
|
||||
showTime: '{{$deps[1]}}',
|
||||
dateFormat: '{{$deps[2]}}',
|
||||
timeFormat: '{{$deps[3]}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
dependencies: ['primaryKey', 'unique', 'autoIncrement'],
|
||||
when: '{{$deps[0]||$deps[1]||$deps[2]}}',
|
||||
fulfill: {
|
||||
state: {
|
||||
hidden: true,
|
||||
value: undefined,
|
||||
},
|
||||
},
|
||||
otherwise: {
|
||||
state: {
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@ -92,7 +56,7 @@ const getSchema = (schema: IField, record: any, compile, getContainer): ISchema
|
||||
return useRequest(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
data: cloneDeep(omit(schema.default, ['uiSchema.rawTitle'])),
|
||||
data: cloneDeep(omit(defaultValues, ['uiSchema.rawTitle'])),
|
||||
}),
|
||||
options,
|
||||
);
|
||||
@ -230,15 +194,7 @@ export const EditFieldAction = (props) => {
|
||||
set(defaultValues.reverseField, 'name', `f_${uid()}`);
|
||||
set(defaultValues.reverseField, 'uiSchema.title', record.__parent?.title);
|
||||
}
|
||||
const schema = getSchema(
|
||||
{
|
||||
...interfaceConf,
|
||||
default: defaultValues,
|
||||
},
|
||||
record,
|
||||
compile,
|
||||
getContainer,
|
||||
);
|
||||
const schema = getSchema(interfaceConf, defaultValues, record, compile, getContainer);
|
||||
setSchema(schema);
|
||||
setVisible(true);
|
||||
}}
|
||||
|
@ -136,6 +136,6 @@ export const useResourceContext = () => {
|
||||
resource,
|
||||
collection,
|
||||
association,
|
||||
targetKey: association?.targetKey || collection?.targetKey || 'id',
|
||||
targetKey: association?.targetKey || collection?.filterTargetKey || collection?.targetKey || 'id',
|
||||
};
|
||||
};
|
||||
|
@ -51,6 +51,8 @@ import {
|
||||
UUIDFieldInterface,
|
||||
NanoidFieldInterface,
|
||||
UnixTimestampFieldInterface,
|
||||
DateFieldInterface,
|
||||
DatetimeNoTzFieldInterface,
|
||||
} from './interfaces';
|
||||
import {
|
||||
GeneralCollectionTemplate,
|
||||
@ -171,6 +173,8 @@ export class CollectionPlugin extends Plugin {
|
||||
UUIDFieldInterface,
|
||||
NanoidFieldInterface,
|
||||
UnixTimestampFieldInterface,
|
||||
DateFieldInterface,
|
||||
DatetimeNoTzFieldInterface,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export class CreatedAtFieldInterface extends CollectionFieldInterface {
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
};
|
||||
availableTypes = ['date'];
|
||||
availableTypes = [];
|
||||
properties = {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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 { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { dateTimeProps, defaultProps, operators } from './properties';
|
||||
|
||||
export class DateFieldInterface extends CollectionFieldInterface {
|
||||
name = 'date';
|
||||
type = 'object';
|
||||
group = 'datetime';
|
||||
order = 3;
|
||||
title = '{{t("DateOnly")}}';
|
||||
sortable = true;
|
||||
default = {
|
||||
type: 'dateOnly',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {
|
||||
dateOnly: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
availableTypes = ['dateOnly'];
|
||||
hasDefaultValue = true;
|
||||
properties = {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
'uiSchema.x-component-props.showTime': {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-visible': false,
|
||||
},
|
||||
};
|
||||
filterable = {
|
||||
operators: operators.datetime,
|
||||
};
|
||||
titleUsable = true;
|
||||
}
|
@ -15,23 +15,39 @@ export class DatetimeFieldInterface extends CollectionFieldInterface {
|
||||
type = 'object';
|
||||
group = 'datetime';
|
||||
order = 1;
|
||||
title = '{{t("Datetime")}}';
|
||||
title = '{{t("Datetime (with time zone)")}}';
|
||||
sortable = true;
|
||||
default = {
|
||||
type: 'date',
|
||||
defaultToCurrentTime: false,
|
||||
onUpdateToCurrentTime: false,
|
||||
timezone: true,
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {
|
||||
showTime: false,
|
||||
utc: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
availableTypes = ['date', 'dateOnly', 'string'];
|
||||
availableTypes = ['date', 'string', 'datetime', 'datetimeTz'];
|
||||
hasDefaultValue = true;
|
||||
properties = {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
defaultToCurrentTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Default value to current time")}}',
|
||||
},
|
||||
onUpdateToCurrentTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Automatically update timestamp on update")}}',
|
||||
},
|
||||
'uiSchema.x-component-props.gmt': {
|
||||
type: 'boolean',
|
||||
title: '{{t("GMT")}}',
|
||||
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { dateTimeProps, defaultProps, operators } from './properties';
|
||||
|
||||
export class DatetimeNoTzFieldInterface extends CollectionFieldInterface {
|
||||
name = 'datetimeNoTz';
|
||||
type = 'object';
|
||||
group = 'datetime';
|
||||
order = 2;
|
||||
title = '{{t("Datetime (without time zone)")}}';
|
||||
sortable = true;
|
||||
default = {
|
||||
type: 'datetimeNoTz',
|
||||
defaultToCurrentTime: false,
|
||||
onUpdateToCurrentTime: false,
|
||||
timezone: false,
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {
|
||||
showTime: false,
|
||||
utc: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
availableTypes = ['string', 'datetimeNoTz'];
|
||||
hasDefaultValue = true;
|
||||
properties = {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
defaultToCurrentTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Default value to current server time")}}',
|
||||
},
|
||||
onUpdateToCurrentTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Automatically update timestamp to the current server time on update")}}',
|
||||
},
|
||||
'uiSchema.x-component-props.gmt': {
|
||||
type: 'boolean',
|
||||
title: '{{t("GMT")}}',
|
||||
'x-hidden': true,
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Use the same time zone (GMT) for all users")}}',
|
||||
'x-decorator': 'FormItem',
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
filterable = {
|
||||
operators: operators.datetime,
|
||||
};
|
||||
titleUsable = true;
|
||||
}
|
@ -45,3 +45,5 @@ export * from './url';
|
||||
export * from './uuid';
|
||||
export * from './nanoid';
|
||||
export * from './unixTimestamp';
|
||||
export * from './dateOnly';
|
||||
export * from './datetimeNoTz';
|
||||
|
@ -10,7 +10,7 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { getUniqueKeyFromCollection } from './o2m';
|
||||
import { getUniqueKeyFromCollection } from './utils';
|
||||
import { defaultProps, relationshipType, reverseFieldProperties } from './properties';
|
||||
|
||||
export class M2MFieldInterface extends CollectionFieldInterface {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import { ISchema } from '@formily/react';
|
||||
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { getUniqueKeyFromCollection } from './o2m';
|
||||
import { getUniqueKeyFromCollection } from './utils';
|
||||
import { constraintsProps, relationshipType, reverseFieldProperties } from './properties';
|
||||
|
||||
export class M2OFieldInterface extends CollectionFieldInterface {
|
||||
|
@ -8,10 +8,9 @@
|
||||
*/
|
||||
|
||||
import { ISchema } from '@formily/react';
|
||||
import { Collection } from '../../data-source';
|
||||
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { constraintsProps, relationshipType, reverseFieldProperties } from './properties';
|
||||
|
||||
import { getUniqueKeyFromCollection } from './utils';
|
||||
export class O2MFieldInterface extends CollectionFieldInterface {
|
||||
name = 'o2m';
|
||||
type = 'object';
|
||||
@ -215,7 +214,3 @@ export class O2MFieldInterface extends CollectionFieldInterface {
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function getUniqueKeyFromCollection(collection: Collection) {
|
||||
return collection?.filterTargetKey || collection?.getPrimaryKey() || 'id';
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import { ISchema } from '@formily/react';
|
||||
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { getUniqueKeyFromCollection } from './o2m';
|
||||
import { getUniqueKeyFromCollection } from './utils';
|
||||
import { constraintsProps, relationshipType, reverseFieldProperties } from './properties';
|
||||
|
||||
export class O2OFieldInterface extends CollectionFieldInterface {
|
||||
|
@ -10,6 +10,8 @@
|
||||
import { Field } from '@formily/core';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { css } from '@emotion/css';
|
||||
import { DateFormatCom } from '../../../schema-component/antd/expiresRadio';
|
||||
export * as operators from './operators';
|
||||
|
||||
export const type: ISchema = {
|
||||
@ -225,26 +227,88 @@ export const reverseFieldProperties: Record<string, ISchema> = {
|
||||
};
|
||||
|
||||
export const dateTimeProps: { [key: string]: ISchema } = {
|
||||
'uiSchema.x-component-props.picker': {
|
||||
type: 'string',
|
||||
title: '{{t("Picker")}}',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Radio.Group',
|
||||
default: 'date',
|
||||
enum: [
|
||||
{
|
||||
label: '{{t("Date")}}',
|
||||
value: 'date',
|
||||
},
|
||||
// {
|
||||
// label: '{{t("Week")}}',
|
||||
// value: 'week',
|
||||
// },
|
||||
{
|
||||
label: '{{t("Month")}}',
|
||||
value: 'month',
|
||||
},
|
||||
{
|
||||
label: '{{t("Quarter")}}',
|
||||
value: 'quarter',
|
||||
},
|
||||
{
|
||||
label: '{{t("Year")}}',
|
||||
value: 'year',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
'uiSchema.x-component-props.dateFormat': {
|
||||
type: 'string',
|
||||
title: '{{t("Date format")}}',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ExpiresRadio',
|
||||
'x-decorator-props': {},
|
||||
'x-component-props': {
|
||||
className: css`
|
||||
.ant-radio-wrapper {
|
||||
display: flex;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
`,
|
||||
defaultValue: 'dddd',
|
||||
formats: ['MMMM Do YYYY', 'YYYY-MM-DD', 'MM/DD/YY', 'YYYY/MM/DD', 'DD/MM/YYYY'],
|
||||
},
|
||||
default: 'YYYY-MM-DD',
|
||||
enum: [
|
||||
{
|
||||
label: '{{t("Year/Month/Day")}}',
|
||||
value: 'YYYY/MM/DD',
|
||||
label: DateFormatCom({ format: 'MMMM Do YYYY' }),
|
||||
value: 'MMMM Do YYYY',
|
||||
},
|
||||
{
|
||||
label: '{{t("Year-Month-Day")}}',
|
||||
label: DateFormatCom({ format: 'YYYY-MM-DD' }),
|
||||
value: 'YYYY-MM-DD',
|
||||
},
|
||||
{
|
||||
label: '{{t("Day/Month/Year")}}',
|
||||
label: DateFormatCom({ format: 'MM/DD/YY' }),
|
||||
value: 'MM/DD/YY',
|
||||
},
|
||||
{
|
||||
label: DateFormatCom({ format: 'YYYY/MM/DD' }),
|
||||
value: 'YYYY/MM/DD',
|
||||
},
|
||||
{
|
||||
label: DateFormatCom({ format: 'DD/MM/YYYY' }),
|
||||
value: 'DD/MM/YYYY',
|
||||
},
|
||||
{
|
||||
label: 'custom',
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
'x-reactions': {
|
||||
dependencies: ['uiSchema.x-component-props.picker'],
|
||||
fulfill: {
|
||||
state: {
|
||||
value: `{{ getPickerFormat($deps[0])}}`,
|
||||
componentProps: { picker: `{{$deps[0]}}` },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'uiSchema.x-component-props.showTime': {
|
||||
type: 'boolean',
|
||||
@ -253,10 +317,26 @@ export const dateTimeProps: { [key: string]: ISchema } = {
|
||||
'x-content': '{{t("Show time")}}',
|
||||
'x-reactions': [
|
||||
`{{(field) => {
|
||||
field.query('..[].timeFormat').take(f => {
|
||||
f.display = field.value ? 'visible' : 'none';
|
||||
});
|
||||
}}}`,
|
||||
field.query('..[].timeFormat').take(f => {
|
||||
f.display = field.value ? 'visible' : 'none';
|
||||
f.value='HH:mm:ss'
|
||||
});
|
||||
}}}`,
|
||||
{
|
||||
dependencies: ['uiSchema.x-component-props.picker'],
|
||||
when: '{{$deps[0]==="date"}}',
|
||||
fulfill: {
|
||||
state: {
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
otherwise: {
|
||||
state: {
|
||||
hidden: true,
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
'uiSchema.x-component-props.timeFormat': {
|
||||
@ -275,6 +355,14 @@ export const dateTimeProps: { [key: string]: ISchema } = {
|
||||
value: 'HH:mm:ss',
|
||||
},
|
||||
],
|
||||
'x-reactions': {
|
||||
dependencies: ['uiSchema.x-component-props.showTime'],
|
||||
fulfill: {
|
||||
state: {
|
||||
hidden: `{{ !$deps[0] }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -60,13 +60,48 @@ export const object = [
|
||||
];
|
||||
|
||||
export const datetime = [
|
||||
{ label: "{{ t('is') }}", value: '$dateOn', selected: true },
|
||||
{ label: "{{ t('is not') }}", value: '$dateNotOn' },
|
||||
{ label: "{{ t('is before') }}", value: '$dateBefore' },
|
||||
{ label: "{{ t('is after') }}", value: '$dateAfter' },
|
||||
{ label: "{{ t('is on or after') }}", value: '$dateNotBefore' },
|
||||
{ label: "{{ t('is on or before') }}", value: '$dateNotAfter' },
|
||||
{ label: "{{ t('is between') }}", value: '$dateBetween', schema: { 'x-component': 'DatePicker.RangePicker' } },
|
||||
{
|
||||
label: "{{ t('is') }}",
|
||||
value: '$dateOn',
|
||||
selected: true,
|
||||
schema: { 'x-component': 'DatePicker.FilterWithPicker' },
|
||||
onlyFilterAction: true, //schema 仅在Filter.Action生效,筛选表单中不生效
|
||||
},
|
||||
{
|
||||
label: "{{ t('is not') }}",
|
||||
value: '$dateNotOn',
|
||||
schema: { 'x-component': 'DatePicker.FilterWithPicker' },
|
||||
onlyFilterAction: true,
|
||||
},
|
||||
{
|
||||
label: "{{ t('is before') }}",
|
||||
value: '$dateBefore',
|
||||
schema: { 'x-component': 'DatePicker.FilterWithPicker' },
|
||||
onlyFilterAction: true,
|
||||
},
|
||||
{
|
||||
label: "{{ t('is after') }}",
|
||||
value: '$dateAfter',
|
||||
schema: { 'x-component': 'DatePicker.FilterWithPicker' },
|
||||
onlyFilterAction: true,
|
||||
},
|
||||
{
|
||||
label: "{{ t('is on or after') }}",
|
||||
value: '$dateNotBefore',
|
||||
schema: { 'x-component': 'DatePicker.FilterWithPicker' },
|
||||
onlyFilterAction: true,
|
||||
},
|
||||
{
|
||||
label: "{{ t('is on or before') }}",
|
||||
value: '$dateNotAfter',
|
||||
schema: { 'x-component': 'DatePicker.FilterWithPicker' },
|
||||
onlyFilterAction: true,
|
||||
},
|
||||
{
|
||||
label: "{{ t('is between') }}",
|
||||
value: '$dateBetween',
|
||||
schema: { 'x-component': 'DatePicker.RangePicker' },
|
||||
},
|
||||
{ label: "{{ t('is empty') }}", value: '$empty', noValue: true },
|
||||
{ label: "{{ t('is not empty') }}", value: '$notEmpty', noValue: true },
|
||||
];
|
||||
@ -157,18 +192,18 @@ export const collection = [
|
||||
label: '{{t("is")}}',
|
||||
value: '$eq',
|
||||
selected: true,
|
||||
schema: { 'x-component': 'CollectionSelect' },
|
||||
schema: { 'x-component': 'DataSourceCollectionCascader' },
|
||||
},
|
||||
{
|
||||
label: '{{t("is not")}}',
|
||||
value: '$ne',
|
||||
schema: { 'x-component': 'CollectionSelect' },
|
||||
schema: { 'x-component': 'DataSourceCollectionCascader' },
|
||||
},
|
||||
{
|
||||
label: '{{t("is any of")}}',
|
||||
value: '$in',
|
||||
schema: {
|
||||
'x-component': 'CollectionSelect',
|
||||
'x-component': 'DataSourceCollectionCascader',
|
||||
'x-component-props': { mode: 'tags' },
|
||||
},
|
||||
},
|
||||
@ -176,7 +211,7 @@ export const collection = [
|
||||
label: '{{t("is none of")}}',
|
||||
value: '$notIn',
|
||||
schema: {
|
||||
'x-component': 'CollectionSelect',
|
||||
'x-component': 'DataSourceCollectionCascader',
|
||||
'x-component-props': { mode: 'tags' },
|
||||
},
|
||||
},
|
||||
|
@ -14,7 +14,7 @@ export class TimeFieldInterface extends CollectionFieldInterface {
|
||||
name = 'time';
|
||||
type = 'object';
|
||||
group = 'datetime';
|
||||
order = 2;
|
||||
order = 4;
|
||||
title = '{{t("Time")}}';
|
||||
sortable = true;
|
||||
default = {
|
||||
|
@ -8,31 +8,34 @@
|
||||
*/
|
||||
|
||||
import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
|
||||
import { dateTimeProps, defaultProps, operators } from './properties';
|
||||
|
||||
import { defaultProps, operators, dateTimeProps } from './properties';
|
||||
export class UnixTimestampFieldInterface extends CollectionFieldInterface {
|
||||
name = 'unixTimestamp';
|
||||
type = 'object';
|
||||
group = 'datetime';
|
||||
order = 1;
|
||||
order = 4;
|
||||
title = '{{t("Unix Timestamp")}}';
|
||||
sortable = true;
|
||||
default = {
|
||||
type: 'bigInt',
|
||||
type: 'unixTimestamp',
|
||||
accuracy: 'second',
|
||||
timezone: true,
|
||||
defaultToCurrentTime: false,
|
||||
onUpdateToCurrentTime: false,
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
'x-component': 'UnixTimestamp',
|
||||
'x-component-props': {
|
||||
accuracy: 'second',
|
||||
showTime: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
availableTypes = ['integer', 'bigInt'];
|
||||
hasDefaultValue = true;
|
||||
availableTypes = ['unixTimestamp'];
|
||||
hasDefaultValue = false;
|
||||
properties = {
|
||||
...defaultProps,
|
||||
'uiSchema.x-component-props.accuracy': {
|
||||
...dateTimeProps,
|
||||
accuracy: {
|
||||
type: 'string',
|
||||
title: '{{t("Accuracy")}}',
|
||||
'x-component': 'Radio.Group',
|
||||
@ -43,9 +46,21 @@ export class UnixTimestampFieldInterface extends CollectionFieldInterface {
|
||||
{ value: 'second', label: '{{t("Second")}}' },
|
||||
],
|
||||
},
|
||||
defaultToCurrentTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Default value to current time")}}',
|
||||
},
|
||||
onUpdateToCurrentTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Automatically update timestamp on update")}}',
|
||||
},
|
||||
};
|
||||
filterable = {
|
||||
operators: operators.number,
|
||||
operators: operators.datetime,
|
||||
};
|
||||
titleUsable = true;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ export class UpdatedAtFieldInterface extends CollectionFieldInterface {
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
};
|
||||
availableTypes = ['date'];
|
||||
availableTypes = [];
|
||||
properties = {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 { Collection } from '../../../data-source';
|
||||
|
||||
export function getUniqueKeyFromCollection(collection: Collection) {
|
||||
if (collection?.filterTargetKey) {
|
||||
if (Array.isArray(collection.filterTargetKey)) {
|
||||
return collection?.filterTargetKey?.[0];
|
||||
}
|
||||
return collection?.filterTargetKey;
|
||||
}
|
||||
return collection?.getPrimaryKey() || 'id';
|
||||
}
|
@ -26,7 +26,7 @@ export class UUIDFieldInterface extends CollectionFieldInterface {
|
||||
'x-validator': 'uuid',
|
||||
},
|
||||
};
|
||||
availableTypes = ['string', 'uid', 'uuid'];
|
||||
availableTypes = ['string', 'uuid'];
|
||||
properties = {
|
||||
'uiSchema.title': {
|
||||
type: 'string',
|
||||
|
@ -90,6 +90,9 @@ export class SqlCollectionTemplate extends CollectionTemplate {
|
||||
description: `{{t( "If a collection lacks a primary key, you must configure a unique record key to locate row records within a block, failure to configure this will prevent the creation of data blocks for the collection.")}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
multiple: true,
|
||||
},
|
||||
'x-reactions': ['{{useAsyncDataSource(loadFilterTargetKeys)}}'],
|
||||
},
|
||||
};
|
||||
|
@ -153,6 +153,9 @@ export class ViewCollectionTemplate extends CollectionTemplate {
|
||||
description: `{{t( "If a collection lacks a primary key, you must configure a unique record key to locate row records within a block, failure to configure this will prevent the creation of data blocks for the collection.")}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
multiple: true,
|
||||
},
|
||||
'x-reactions': ['{{useAsyncDataSource(loadFilterTargetKeys)}}'],
|
||||
},
|
||||
...getConfigurableProperties('category', 'description'),
|
||||
|