Compare commits

...

36 Commits

Author SHA1 Message Date
Junyi
3387366f1e
fix(plugin-workflow): except json field for load performance (#7138) 2025-07-01 10:59:30 +08:00
Junyi
88cb9884f6
refactor(database): add pool options from env (#7133)
* refactor(database): add pool options from env

* Update packages/core/database/src/helpers.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix(database): only inject env configured

* fix(server): simplify database options

* revert(server): revert create database api

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-01 10:40:15 +08:00
Zeke Zhang
22cd39552f
fix: update collection name handling in useCurrentFormVariable (#7125) 2025-07-01 10:01:50 +08:00
Katherine
026baab241
fix: filtering error on DateOnly or Datetime (without time zone) using Exact day variable (#7113)
* fix: filtering error on DateOnly or Datetime (without time zone) fields using Exact day variable

* fix: bug

* fix: bug

* fix: bug
2025-06-30 22:41:20 +11:00
ajie
d1ff280d9f
fix: setting field displayName in connected view does not take effect (#7130) 2025-06-30 17:34:17 +08:00
Junyi
88591454ec
fix(plugin-workflow): fix cycling import (#7134) 2025-06-30 15:25:57 +08:00
Junyi
3c555e9fc0
refactor(plugin-workflow): add log for node testing (#7129) 2025-06-30 11:45:39 +08:00
Junyi
6afcbffdec
fix(client): fault tolerance for settings based on x-acl-action (#7128) 2025-06-29 09:34:21 +08:00
Katherine
8d91e1fded
fix: association field default value overrides existing data in sub-table (#7120)
* fix: association field default value overrides existing data in sub-table

* fix: bug

* fix: bug
2025-06-27 14:15:56 +08:00
Katherine
940002a876
refactor: add filter support to multi-app management (#7124) 2025-06-27 14:15:34 +08:00
nocobase[bot]
a4eb026ea4 docs: update changelogs 2025-06-26 14:00:06 +00:00
nocobase[bot]
74f3fd8132 chore(versions): 😊 publish v1.7.18 2025-06-26 13:49:54 +00:00
Junyi
4dcf934b15
fix(plugin-workflow): fix params of loading record in tasks (#7123)
* fix(plugin-workflow): fix tasks type load params

* test(plugin-workflow): skip workflow-approval e2e cases
2025-06-26 21:26:05 +08:00
ajie
17195c788d
fix: ensure blocks are displayed under pages when setting role menu p… (#7112) 2025-06-26 19:42:19 +08:00
Zeke Zhang
d538cd7db7
fix(MobileDatePicker): reorder imports and update date range limits (#7117) 2025-06-26 09:56:51 +08:00
Junyi
a9bd6f7844
fix(plugin-workflow): optimize mobile style (#7040)
* fix(plugin-workflow): optimize mobile style

* fix(plugin-workflow): fix mobile style

* feat(plugin-workflow): add tasks to mobile menu initializer

* refactor(plugin-workflow): adjust tasks center api

* fix(ActionDrawer): apply zIndex style to ActionDrawer component

* fix(plugin-workflow-manual): fix manual style under mobile

* fix(plugin-workflow): fix styles

* fix(plugin-workflow): fix mobile layout check

* fix(plugin-workflow): adjust pagination footer style

* fix(plugin-workflow): fix icon only props

* fix(plugin-workflow-manual): fix todo item title

* revert(plugin-field-sort): revert mistaken commit

---------

Co-authored-by: Zeke Zhang <958414905@qq.com>
2025-06-24 22:18:02 +08:00
nocobase[bot]
e527e4d6ba docs: update changelogs 2025-06-23 16:11:04 +00:00
nocobase[bot]
4606bdd7fd chore(versions): 😊 publish v1.7.17 2025-06-23 16:00:59 +00:00
Zeke Zhang
dcfed325de
fix: add style prop to mobile ActionDrawer and FilterAction components (#7110) 2025-06-23 21:58:45 +08:00
Katherine
a0181b58b1
fix: incorrect range limitation on date fields with time (#7107) 2025-06-23 10:38:57 +08:00
Zeke Zhang
bd900e9a92
fix(useLocalVariables): add defaultValue to URL search params context (#7104) 2025-06-20 17:23:46 +08:00
Katherine
4959e6a78e
fix: date field issue in quick create form of calendar block (#7106) 2025-06-20 16:55:14 +08:00
nocobase[bot]
e808da6f52 docs: update changelogs 2025-06-19 16:40:45 +00:00
nocobase[bot]
784bedbc08 chore(versions): 😊 publish v1.7.16 2025-06-19 16:31:25 +00:00
Junyi
2c8934bd9a
fix(plugin-workflow): fix stats cascade deleted (#7103) 2025-06-20 00:07:54 +08:00
Junyi
aa1070ad0f
fix(plugin-workflow): fix node can not be created when stats is bigint (#7099) 2025-06-19 16:40:12 +08:00
ajie
0cbcd03390
fix: resolve login failure after batch import of username and password (#7076) 2025-06-19 09:26:16 +08:00
nocobase[bot]
aa0699c4a8 docs: update changelogs 2025-06-18 15:09:40 +00:00
nocobase[bot]
d318138700 chore(versions): 😊 publish v1.7.15 2025-06-18 14:59:38 +00:00
Junyi
3623b33f5f
fix(cli): fix typo (#7096) 2025-06-18 21:54:36 +08:00
chenos
46553df6ec chore: update ci 2025-06-18 20:59:18 +08:00
Zeke Zhang
94e40c5c7b
fix(ReadPretty): apply conditional flex styling based on ellipsis prop (#7094) 2025-06-18 20:34:07 +08:00
Zeke Zhang
544f561b26
fix(assigned-field): prevent deep cloning of React elements in scope (#7086)
* fix(assigned-field): prevent deep cloning of React elements in scope

* fix: simplify variable value assignment in action props
2025-06-18 19:30:27 +08:00
Junyi
cca6b0faaf
fix(plugin-workflow): fix executed check on bigint (#7091)
* fix(plugin-workflow): fix executed check on bigint

* fix(plugin-workflow): fix test case

* fix(plugin-workflow): fix types

* fix(plugin-workflow): fix test case

* fix(plugin-workflow): fix types

* fix: types
2025-06-18 18:52:11 +08:00
Junyi
d70eb68651
fix(plugin-file-manager): fix collection definition (#7093) 2025-06-18 18:34:24 +08:00
Junyi
a67302a468
Revert "fix(client): fix assigned field variable scope (#7012)" (#7092)
This reverts commit 75ff573357f5567b5d238474bd43586bee007a55.
2025-06-18 12:33:14 +08:00
170 changed files with 1797 additions and 825 deletions

View File

@ -48,6 +48,14 @@ DB_PASSWORD=nocobase
# DB_LOGGING=on
# DB_UNDERSCORED=false
# @see https://sequelize.org/api/v6/class/src/sequelize.js~sequelize#instance-constructor-constructor
# DB_POOL_MAX=5
# DB_POOL_MIN=0
# DB_POOL_IDLE=10000
# DB_POOL_ACQUIRE=60000
# DB_POOL_EVICT=1000
# DB_POOL_MAX_USES=0
# sqlite only
# DB_STORAGE=storage/db/nocobase.sqlite

View File

@ -36,7 +36,7 @@ jobs:
uses: docker/build-push-action@v3
with:
context: ./docker/nocobase
file: ./docker/nocobase/Dockerfile-cn
file: ./docker/nocobase/Dockerfile-full
build-args: |
CNA_VERSION=${{ inputs.tag_name }}
platforms: linux/amd64,linux/arm64

View File

@ -274,7 +274,7 @@ jobs:
- run: npx playwright install chromium --with-deps
- name: Test with postgres
run: yarn e2e p-test --match 'packages/**/{plugin-workflow,plugin-workflow-*}/**/__e2e__/**/*.test.ts' --ignore 'packages/**/plugin-workflow-approval/**/__e2e__/**/*.test.ts'
run: yarn e2e p-test --match 'packages/**/{plugin-workflow,plugin-workflow-*}/**/__e2e__/**/*.test.ts' --ignore 'packages/**/plugin-workflow-approval/**/__e2e__/**/*.test.ts' --ignore 'packages/**/plugin-workflow-manual/**/__e2e__/**/*.test.ts'
env:
__E2E__: true
APP_ENV: production
@ -297,131 +297,131 @@ jobs:
timeout-minutes: 60
plugin-workflow-approval:
name: plugin-workflow-approval
needs: build
runs-on: ubuntu-latest
container: node:20
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres:11
# Provide the password for postgres
env:
POSTGRES_USER: nocobase
POSTGRES_PASSWORD: password
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
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-workflow-approval
skip-token-revoke: true
- uses: actions/checkout@v4
- name: Checkout pro-plugins
continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
uses: actions/checkout@v4
with:
repository: nocobase/pro-plugins
ref: main
path: packages/pro-plugins
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Checkout plugin-workflow-approval
continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
uses: actions/checkout@v4
with:
repository: nocobase/plugin-workflow-approval
ref: main
path: packages/pro-plugins/@nocobase/plugin-workflow-approval
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- run: |
cd packages/pro-plugins &&
if git show-ref --quiet refs/remotes/origin/${{ github.head_ref || github.ref_name }}; then
git checkout ${{ github.head_ref || github.ref_name }}
else
if git show-ref --quiet refs/remotes/origin/${{ github.event.pull_request.base.ref }}; then
git checkout ${{ github.event.pull_request.base.ref }}
else
git checkout main
fi
fi
cd ../../
cd packages/pro-plugins/@nocobase/plugin-workflow-approval &&
if git show-ref --quiet refs/remotes/origin/${{ github.head_ref || github.ref_name }}; then
git checkout ${{ github.head_ref || github.ref_name }}
else
if git show-ref --quiet refs/remotes/origin/${{ github.event.pull_request.base.ref }}; then
git checkout ${{ github.event.pull_request.base.ref }}
else
git checkout main
fi
fi
cd ../../../../
continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
- name: Git logs
run: |
cd packages/pro-plugins/@nocobase/plugin-workflow-approval && git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -n 10
continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
- name: Set variables
continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
run: |
APPEND_PRESET_LOCAL_PLUGINS=$(find ./packages/pro-plugins/@nocobase -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sed 's/^plugin-//' | tr '\n' ',' | sed 's/,$//')
echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT
id: vars
# plugin-workflow-approval:
# name: plugin-workflow-approval
# needs: build
# runs-on: ubuntu-latest
# container: node:20
# services:
# # Label used to access the service container
# postgres:
# # Docker Hub image
# image: postgres:11
# # Provide the password for postgres
# env:
# POSTGRES_USER: nocobase
# POSTGRES_PASSWORD: password
# # Set health checks to wait until postgres has started
# options: >-
# --health-cmd pg_isready
# --health-interval 10s
# --health-timeout 5s
# --health-retries 5
# 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-workflow-approval
# skip-token-revoke: true
# - uses: actions/checkout@v4
# - name: Checkout pro-plugins
# continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
# uses: actions/checkout@v4
# with:
# repository: nocobase/pro-plugins
# ref: main
# path: packages/pro-plugins
# fetch-depth: 0
# token: ${{ steps.app-token.outputs.token }}
# - name: Checkout plugin-workflow-approval
# continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
# uses: actions/checkout@v4
# with:
# repository: nocobase/plugin-workflow-approval
# ref: main
# path: packages/pro-plugins/@nocobase/plugin-workflow-approval
# fetch-depth: 0
# token: ${{ steps.app-token.outputs.token }}
# - run: |
# cd packages/pro-plugins &&
# if git show-ref --quiet refs/remotes/origin/${{ github.head_ref || github.ref_name }}; then
# git checkout ${{ github.head_ref || github.ref_name }}
# else
# if git show-ref --quiet refs/remotes/origin/${{ github.event.pull_request.base.ref }}; then
# git checkout ${{ github.event.pull_request.base.ref }}
# else
# git checkout main
# fi
# fi
# cd ../../
# cd packages/pro-plugins/@nocobase/plugin-workflow-approval &&
# if git show-ref --quiet refs/remotes/origin/${{ github.head_ref || github.ref_name }}; then
# git checkout ${{ github.head_ref || github.ref_name }}
# else
# if git show-ref --quiet refs/remotes/origin/${{ github.event.pull_request.base.ref }}; then
# git checkout ${{ github.event.pull_request.base.ref }}
# else
# git checkout main
# fi
# fi
# cd ../../../../
# continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
# - name: Git logs
# run: |
# cd packages/pro-plugins/@nocobase/plugin-workflow-approval && git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -n 10
# continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
# - name: Set variables
# continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true
# run: |
# APPEND_PRESET_LOCAL_PLUGINS=$(find ./packages/pro-plugins/@nocobase -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sed 's/^plugin-//' | tr '\n' ',' | sed 's/,$//')
# echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT
# id: vars
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
# - name: Get yarn cache directory path
# id: yarn-cache-dir-path
# run: echo "::set-output name=dir::$(yarn cache dir)"
# - uses: actions/cache@v4
# id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
# with:
# path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
# key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
# restore-keys: |
# ${{ runner.os }}-yarn-
- run: yarn
# - run: yarn
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-artifact
path: packages
# - name: Download build artifact
# uses: actions/download-artifact@v4
# with:
# name: build-artifact
# path: packages
- run: npx playwright install chromium --with-deps
- name: Test with postgres
run: yarn e2e p-test --match 'packages/**/plugin-workflow-approval/**/__e2e__/**/*.test.ts'
env:
__E2E__: true
APP_ENV: production
LOGGER_LEVEL: error
DB_DIALECT: postgres
DB_HOST: postgres
DB_PORT: 5432
DB_USER: nocobase
DB_PASSWORD: password
DB_DATABASE: nocobase
APPEND_PRESET_LOCAL_PLUGINS: ${{ steps.vars.outputs.var2 }}
ENCRYPTION_FIELD_KEY: 1%&glK;<UA}aIxJVc53-4G(rTi0vg@J]
# - run: npx playwright install chromium --with-deps
# - name: Test with postgres
# run: yarn e2e p-test --match 'packages/**/plugin-workflow-approval/**/__e2e__/**/*.test.ts'
# env:
# __E2E__: true
# APP_ENV: production
# LOGGER_LEVEL: error
# DB_DIALECT: postgres
# DB_HOST: postgres
# DB_PORT: 5432
# DB_USER: nocobase
# DB_PASSWORD: password
# DB_DATABASE: nocobase
# APPEND_PRESET_LOCAL_PLUGINS: ${{ steps.vars.outputs.var2 }}
# ENCRYPTION_FIELD_KEY: 1%&glK;<UA}aIxJVc53-4G(rTi0vg@J]
- name: Upload e2e-report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: e2e-report-${{ github.job }} # 为了防止在多个任务中存在冲突
path: ./storage/playwright/tests-report-blob/blob-*/*
# - name: Upload e2e-report
# if: ${{ !cancelled() }}
# uses: actions/upload-artifact@v4
# with:
# name: e2e-report-${{ github.job }} # 为了防止在多个任务中存在冲突
# path: ./storage/playwright/tests-report-blob/blob-*/*
timeout-minutes: 180
# timeout-minutes: 180
plugin-data-source-main:
name: plugin-data-source-main

View File

@ -216,7 +216,7 @@ jobs:
uses: docker/build-push-action@v3
with:
context: ./docker/nocobase
file: ./docker/nocobase/Dockerfile-cn
file: ./docker/nocobase/Dockerfile-full
build-args: |
CNA_VERSION=${{ steps.get-info.outputs.defaultTag }}
platforms: linux/amd64,linux/arm64

View File

@ -5,6 +5,70 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.7.18](https://github.com/nocobase/nocobase/compare/v1.7.17...v1.7.18) - 2025-06-26
### 🚀 Improvements
- **[Workflow]** Optimize mobile style ([#7040](https://github.com/nocobase/nocobase/pull/7040)) by @mytharcher
- **[Public forms]** Optimize the performance of date components in public forms ([#7117](https://github.com/nocobase/nocobase/pull/7117)) by @zhangzhonghe
### 🐛 Bug Fixes
- **[Workflow]** Fix params of loading record in tasks ([#7123](https://github.com/nocobase/nocobase/pull/7123)) by @mytharcher
- **[WEB client]** Fix issue where blocks under pages were not displayed after setting role menu permissions ([#7112](https://github.com/nocobase/nocobase/pull/7112)) by @aaaaaajie
- **[Workflow: Approval]**
- Fix applicant variable name in trigger by @mytharcher
- Fix mobile styles by @mytharcher
- Fix error thrown when approval related collection deleted by @mytharcher
## [v1.7.17](https://github.com/nocobase/nocobase/compare/v1.7.16...v1.7.17) - 2025-06-23
### 🐛 Bug Fixes
- **[client]**
- incorrect range limitation on date fields with time ([#7107](https://github.com/nocobase/nocobase/pull/7107)) by @katherinehhh
- When URL query parameter variables are empty, the data scope conditions are not removed ([#7104](https://github.com/nocobase/nocobase/pull/7104)) by @zhangzhonghe
- **[Mobile]** Fix mobile popup z-index issue ([#7110](https://github.com/nocobase/nocobase/pull/7110)) by @zhangzhonghe
- **[Calendar]** date field issue in quick create form of calendar block ([#7106](https://github.com/nocobase/nocobase/pull/7106)) by @katherinehhh
## [v1.7.16](https://github.com/nocobase/nocobase/compare/v1.7.15...v1.7.16) - 2025-06-19
### 🐛 Bug Fixes
- **[Workflow]**
- Fix incorrectly executed checking on big integer number ([#7099](https://github.com/nocobase/nocobase/pull/7099)) by @mytharcher
- Fix stats cascade deleted by non-current workflow version ([#7103](https://github.com/nocobase/nocobase/pull/7103)) by @mytharcher
- **[Action: Import records]** Resolve login failure issue after batch import of usernames and passwords ([#7076](https://github.com/nocobase/nocobase/pull/7076)) by @aaaaaajie
- **[Workflow: Approval]** Only participants can view (get) detail of approval by @mytharcher
## [v1.7.15](https://github.com/nocobase/nocobase/compare/v1.7.14...v1.7.15) - 2025-06-18
### 🐛 Bug Fixes
- **[client]**
- Use independent variable scope for each field ([#7012](https://github.com/nocobase/nocobase/pull/7012)) by @mytharcher
- Assign field values: Unable to clear data for relation fields ([#7086](https://github.com/nocobase/nocobase/pull/7086)) by @zhangzhonghe
- Table column text alignment function is not working ([#7094](https://github.com/nocobase/nocobase/pull/7094)) by @zhangzhonghe
- **[Workflow]** Fix incorrectly executed checking on big integer number ([#7091](https://github.com/nocobase/nocobase/pull/7091)) by @mytharcher
- **[File manager]** Fix attachments field can not be updated in approval process ([#7093](https://github.com/nocobase/nocobase/pull/7093)) by @mytharcher
- **[Workflow: Approval]** Use comparison instead of implicit logic to avoid type issues by @mytharcher
## [v1.7.14](https://github.com/nocobase/nocobase/compare/v1.7.13...v1.7.14) - 2025-06-17
### 🚀 Improvements

View File

@ -5,6 +5,70 @@
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。
## [v1.7.18](https://github.com/nocobase/nocobase/compare/v1.7.17...v1.7.18) - 2025-06-26
### 🚀 优化
- **[工作流]** 优化移动端样式 ([#7040](https://github.com/nocobase/nocobase/pull/7040)) by @mytharcher
- **[公开表单]** 优化公开表单中日期组件的性能 ([#7117](https://github.com/nocobase/nocobase/pull/7117)) by @zhangzhonghe
### 🐛 修复
- **[工作流]** 修复待办中心加载记录的参数 ([#7123](https://github.com/nocobase/nocobase/pull/7123)) by @mytharcher
- **[WEB 客户端]** 修复设置角色菜单权限后页面下区块不显示的问题 ([#7112](https://github.com/nocobase/nocobase/pull/7112)) by @aaaaaajie
- **[工作流:审批]**
- 修复审批触发器中申请人变量名的问题 by @mytharcher
- 修复移动端样式 by @mytharcher
- 修复审批关联表被删除后的报错 by @mytharcher
## [v1.7.17](https://github.com/nocobase/nocobase/compare/v1.7.16...v1.7.17) - 2025-06-23
### 🐛 修复
- **[client]**
- 修复日期字段在含时间格式下的范围约束错误 ([#7107](https://github.com/nocobase/nocobase/pull/7107)) by @katherinehhh
- URL 查询参数变量为空时,数据范围的条件没有被移除 ([#7104](https://github.com/nocobase/nocobase/pull/7104)) by @zhangzhonghe
- **[移动端]** 修复移动端弹窗的层级问题 ([#7110](https://github.com/nocobase/nocobase/pull/7110)) by @zhangzhonghe
- **[日历]** 修复日历区块快速创建事项时,表单日期字段异常问题 ([#7106](https://github.com/nocobase/nocobase/pull/7106)) by @katherinehhh
## [v1.7.16](https://github.com/nocobase/nocobase/compare/v1.7.15...v1.7.16) - 2025-06-19
### 🐛 修复
- **[工作流]**
- 修复已执行数在大整型数时检查错误的问题 ([#7099](https://github.com/nocobase/nocobase/pull/7099)) by @mytharcher
- 修复统计数据被不是主版本的工作流级联删除的问题 ([#7103](https://github.com/nocobase/nocobase/pull/7103)) by @mytharcher
- **[操作:导入记录]** 修复批量导入用户名和密码后无法登录的问题 ([#7076](https://github.com/nocobase/nocobase/pull/7076)) by @aaaaaajie
- **[工作流:审批]** 限制只有参与者可以查看审批详情 by @mytharcher
## [v1.7.15](https://github.com/nocobase/nocobase/compare/v1.7.14...v1.7.15) - 2025-06-18
### 🐛 修复
- **[client]**
- 对每个字段使用独立的变量范围 ([#7012](https://github.com/nocobase/nocobase/pull/7012)) by @mytharcher
- 字段赋值:关系字段无法被清空数据 ([#7086](https://github.com/nocobase/nocobase/pull/7086)) by @zhangzhonghe
- 表格列的文本对齐功能无效 ([#7094](https://github.com/nocobase/nocobase/pull/7094)) by @zhangzhonghe
- **[工作流]** 修复已执行数在大整型数时检查错误的问题 ([#7091](https://github.com/nocobase/nocobase/pull/7091)) by @mytharcher
- **[文件管理器]** 修复审批处理中附件字段无法被更新的问题 ([#7093](https://github.com/nocobase/nocobase/pull/7093)) by @mytharcher
- **[工作流:审批]** 使用比较代替隐式逻辑以避免类型问题 by @mytharcher
## [v1.7.14](https://github.com/nocobase/nocobase/compare/v1.7.13...v1.7.14) - 2025-06-17
### 🚀 优化

View File

@ -1,19 +0,0 @@
#!/bin/sh
set -e
nginx
echo 'nginx started';
cd /app/nocobase && yarn nocobase db:auth --retry=30
cd /app/nocobase && yarn nocobase install -s
cd /app/nocobase && yarn nocobase upgrade -S
cd /app/nocobase && yarn start
# Run command with node if the first argument contains a "-" or is not a system command. The last
# part inside the "{}" is a workaround for the following bug in ash/dash:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ] || { [ -f "${1}" ] && ! [ -x "${1}" ]; }; then
set -- node "$@"
fi
exec "$@"

View File

@ -1,43 +0,0 @@
log_format apm '"$time_local" client=$remote_addr '
'method=$request_method request="$request" '
'request_length=$request_length '
'status=$status bytes_sent=$bytes_sent '
'body_bytes_sent=$body_bytes_sent '
'referer=$http_referer '
'user_agent="$http_user_agent" '
'upstream_addr=$upstream_addr '
'upstream_status=$upstream_status '
'request_time=$request_time '
'upstream_response_time=$upstream_response_time '
'upstream_connect_time=$upstream_connect_time '
'upstream_header_time=$upstream_header_time';
server {
listen 80;
server_name _;
root /app/nocobase/packages/app/client/dist;
index index.html;
client_max_body_size 0;
access_log /var/log/nginx/nocobase.log apm;
location /storage/uploads/ {
alias /app/nocobase/storage/uploads/;
autoindex off;
}
location / {
root /app/nocobase/packages/app/client/dist;
try_files $uri $uri/ /index.html;
}
location ^~ /api/ {
proxy_pass http://127.0.0.1:13000/api/;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

View File

@ -1,5 +1,5 @@
{
"version": "1.7.14",
"version": "1.7.18",
"npmClient": "yarn",
"useWorkspaces": true,
"npmClientArgs": ["--ignore-engines"],

View File

@ -1,13 +1,13 @@
{
"name": "@nocobase/acl",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/resourcer": "1.7.14",
"@nocobase/utils": "1.7.14",
"@nocobase/resourcer": "1.7.18",
"@nocobase/utils": "1.7.18",
"minimatch": "^5.1.1"
},
"repository": {

View File

@ -1,14 +1,14 @@
{
"name": "@nocobase/actions",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/cache": "1.7.14",
"@nocobase/database": "1.7.14",
"@nocobase/resourcer": "1.7.14"
"@nocobase/cache": "1.7.18",
"@nocobase/database": "1.7.18",
"@nocobase/resourcer": "1.7.18"
},
"repository": {
"type": "git",

View File

@ -1,17 +1,17 @@
{
"name": "@nocobase/app",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/database": "1.7.14",
"@nocobase/preset-nocobase": "1.7.14",
"@nocobase/server": "1.7.14"
"@nocobase/database": "1.7.18",
"@nocobase/preset-nocobase": "1.7.18",
"@nocobase/server": "1.7.18"
},
"devDependencies": {
"@nocobase/client": "1.7.14"
"@nocobase/client": "1.7.18"
},
"repository": {
"type": "git",

View File

@ -1,16 +1,16 @@
{
"name": "@nocobase/auth",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/actions": "1.7.14",
"@nocobase/cache": "1.7.14",
"@nocobase/database": "1.7.14",
"@nocobase/resourcer": "1.7.14",
"@nocobase/utils": "1.7.14",
"@nocobase/actions": "1.7.18",
"@nocobase/cache": "1.7.18",
"@nocobase/database": "1.7.18",
"@nocobase/resourcer": "1.7.18",
"@nocobase/utils": "1.7.18",
"@types/jsonwebtoken": "^9.0.9",
"jsonwebtoken": "^9.0.2"
},

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/build",
"version": "1.7.14",
"version": "1.7.18",
"description": "Library build tool based on rollup.",
"main": "lib/index.js",
"types": "./lib/index.d.ts",

View File

@ -1,12 +1,12 @@
{
"name": "@nocobase/cache",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/lock-manager": "1.7.14",
"@nocobase/lock-manager": "1.7.18",
"bloom-filters": "^3.0.1",
"cache-manager": "^5.2.4",
"cache-manager-redis-yet": "^4.1.2"

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/cli",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./src/index.js",
@ -8,7 +8,7 @@
"nocobase": "./bin/index.js"
},
"dependencies": {
"@nocobase/app": "1.7.14",
"@nocobase/app": "1.7.18",
"@types/fs-extra": "^11.0.1",
"@umijs/utils": "3.5.20",
"chalk": "^4.1.1",
@ -26,7 +26,7 @@
"tsx": "^4.19.0"
},
"devDependencies": {
"@nocobase/devtools": "1.7.14"
"@nocobase/devtools": "1.7.18"
},
"repository": {
"type": "git",

View File

@ -114,7 +114,7 @@ exports.postCheck = async (opts) => {
const port = opts.port || process.env.APP_PORT;
const result = await exports.isPortReachable(port);
if (result) {
console.error(chalk.red(`post already in use ${port}`));
console.error(chalk.red(`Port ${port} already in use`));
process.exit(1);
}
};

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/client",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "lib/index.js",
"module": "es/index.mjs",
@ -26,9 +26,9 @@
"@formily/reactive-react": "^2.2.27",
"@formily/shared": "^2.2.27",
"@formily/validator": "^2.2.27",
"@nocobase/evaluators": "1.7.14",
"@nocobase/sdk": "1.7.14",
"@nocobase/utils": "1.7.14",
"@nocobase/evaluators": "1.7.18",
"@nocobase/sdk": "1.7.18",
"@nocobase/utils": "1.7.18",
"ahooks": "^3.7.2",
"antd": "5.24.2",
"antd-style": "3.7.1",

View File

@ -197,9 +197,7 @@ export function useCollectValuesToSubmit() {
if (isVariable(value)) {
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
if (parsedValue !== null && parsedValue !== undefined) {
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
}
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
} else if (value !== '') {
assignedValues[key] = value;
}
@ -385,9 +383,7 @@ export const useAssociationCreateActionProps = () => {
if (isVariable(value)) {
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
if (parsedValue) {
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
}
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
} else if (value !== '') {
assignedValues[key] = value;
}
@ -658,9 +654,7 @@ export const useCustomizeUpdateActionProps = () => {
if (isVariable(value)) {
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
if (parsedValue) {
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
}
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
} else if (value !== '') {
assignedValues[key] = value;
}
@ -771,9 +765,7 @@ export const useCustomizeBulkUpdateActionProps = () => {
if (isVariable(value)) {
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
if (parsedValue) {
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
}
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
} else if (value !== '') {
assignedValues[key] = value;
}
@ -999,9 +991,7 @@ export const useUpdateActionProps = () => {
if (isVariable(value)) {
const { value: parsedValue } = (await variables?.parseVariable(value, localVariables)) || {};
if (parsedValue) {
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
}
assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField });
} else if (value !== '') {
assignedValues[key] = value;
}

View File

@ -24,6 +24,7 @@ export class DateFieldInterface extends CollectionFieldInterface {
'x-component': 'DatePicker',
'x-component-props': {
dateOnly: true,
showTime: false,
},
},
};

View File

@ -9,7 +9,7 @@
import { useField, useForm } from '@formily/react';
import { Cascader, Input, Select, Spin, Table, Tag } from 'antd';
import { last, omit } from 'lodash';
import _, { last, omit } from 'lodash';
import React, { useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ResourceActionContext, useCompile } from '../../../';
@ -120,9 +120,10 @@ const PreviewCom = (props) => {
}, [databaseView]);
const handleFieldChange = (record, index) => {
dataSource.splice(index, 1, record);
setDataSource(dataSource);
field.value = dataSource.map((v) => {
const newDataSource = _.cloneDeep(dataSource);
newDataSource[index] = record;
setDataSource(newDataSource);
field.value = newDataSource.map((v) => {
const source = typeof v.source === 'string' ? v.source : v.source?.filter?.(Boolean)?.join('.');
return {
...v,
@ -198,8 +199,7 @@ const PreviewCom = (props) => {
style={{ width: '100%' }}
popupMatchSelectWidth={false}
onChange={(value) => {
const interfaceConfig = getInterface(value);
handleFieldChange({ ...item, interface: value, uiSchema: interfaceConfig?.default?.uiSchema }, index);
handleFieldChange({ ...item, interface: value }, index);
}}
>
{data.map((group) => (

View File

@ -66,7 +66,7 @@ export const createFormBlockSettings = new SchemaSettings({
useVisible() {
const { action } = useFormBlockContext();
const schema = useFieldSchema();
return !action && schema?.['x-acl-action'].includes('create');
return !action && schema?.['x-acl-action']?.includes('create');
},
useComponentProps() {
const { name } = useCollection_deprecated();

View File

@ -19,6 +19,7 @@ import { useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive'
import { useSchemaComponentContext } from '../../hooks';
import { AssociationFieldContext } from './context';
import { FormItem, useSchemaOptionsContext } from '../../../schema-component';
import { useCollectionRecord } from '../../../data-source';
export const AssociationFieldProvider = observer(
(props) => {
@ -28,6 +29,7 @@ export const AssociationFieldProvider = observer(
const api = useAPIClient();
const option = useSchemaOptionsContext();
const rootRef = useRef<HTMLDivElement>(null);
const record = useCollectionRecord();
// 这里有点奇怪,在 Table 切换显示的组件时,这个组件并不会触发重新渲染,所以增加这个 Hooks 让其重新渲染
useSchemaComponentContext();
@ -71,7 +73,9 @@ export const AssociationFieldProvider = observer(
if (_.isUndefined(ids) || _.isNil(ids) || _.isNaN(ids)) {
return Promise.reject(null);
}
if (record && !record.isNew) {
return Promise.reject(null);
}
return api.request({
resource: collectionField.target,
action: Array.isArray(ids) ? 'list' : 'get',

View File

@ -58,90 +58,98 @@ export const DatePicker = (props: any) => {
}, [props._maxDate, props._minDate, localVariables, parseVariable]);
const limitDate = async () => {
//dayjs()如果传入undefined可能会被解析成当前时间
let minDateTimePromise = props._minDate ? Promise.resolve(dayjs(props._minDate)) : Promise.resolve(null);
// 直接将 UTC 时间字符串转换成 dayjs 对象(注意不转成本地时间)
let minDateTimePromise = props._minDate
? Promise.resolve(dayjs(props._minDate)) // 保持 UTC 时间
: Promise.resolve(null);
let maxDateTimePromise = props._maxDate ? Promise.resolve(dayjs(props._maxDate)) : Promise.resolve(null);
if (isVariable(props._maxDate)) {
maxDateTimePromise = parseVariable(props._maxDate, localVariables).then((result) => {
console.log(dayjs(Array.isArray(result.value) ? last(result.value) : result.value));
return dayjs(Array.isArray(result.value) ? last(result.value) : result.value);
});
}
if (isVariable(props._minDate)) {
minDateTimePromise = parseVariable(props._minDate, localVariables).then((result) => {
return dayjs(Array.isArray(result.value) ? first(result.value) : result.value);
});
minDateTimePromise = parseVariable(props._minDate, localVariables).then((result) =>
dayjs(Array.isArray(result.value) ? first(result.value) : result.value),
);
}
const [minDateTime, maxDateTime] = await Promise.all([minDateTimePromise, maxDateTimePromise]);
const fullTimeArr = Array.from({ length: 60 }, (_, i) => i);
// 根据最小日期和最大日期限定日期时间
// disabledDate 只禁用日期,不要管时间部分
const disabledDate = (current) => {
if (!dayjs.isDayjs(current)) return false;
const currentDate = dayjs(current);
const min = minDateTime ? dayjs(minDateTime) : null;
const max = maxDateTime ? dayjs(maxDateTime).endOf('day') : null; // 设为 23:59:59
// 把 current 转成本地时间(不加 .utc(),因为 current 就是本地时间)
// 然后和 maxDateTime同为本地时间做比较
const min = minDateTime ? minDateTime.startOf('day') : null;
const max = maxDateTime ? maxDateTime.endOf('day') : null;
return (min && currentDate.isBefore(min, 'minute')) || (max && currentDate.isAfter(max, 'minute'));
return (min && current.isBefore(min, 'day')) || (max && current.isAfter(max, 'day'));
};
// 禁用时分秒
const disabledTime = (current) => {
if (!current || (!minDateTime && !maxDateTime)) {
return { disabledHours: () => [], disabledMinutes: () => [], disabledSeconds: () => [] };
}
const currentDate = dayjs(current);
const isCurrentMinDay = currentDate.isSame(minDateTime, 'day');
const isCurrentMaxDay = currentDate.isSame(maxDateTime, 'day');
// current 是本地时间,转成 UTC 时间
const currentUtc = current.utc();
// 判断是不是 minDate 和 maxDate 的同一天UTC
const isCurrentMinDay = minDateTime && currentUtc.isSame(minDateTime, 'day');
const isCurrentMaxDay = maxDateTime && currentUtc.isSame(maxDateTime, 'day');
const disabledHours = () => {
const hours = [];
if (isCurrentMinDay) {
hours.push(...Array.from({ length: minDateTime.hour() }, (_, i) => i));
for (let h = 0; h < minDateTime.hour(); h++) {
hours.push(h);
}
}
if (isCurrentMaxDay) {
hours.push(...Array.from({ length: 24 - maxDateTime.hour() }, (_, i) => i + maxDateTime.hour() + 1));
for (let h = maxDateTime.hour() + 1; h < 24; h++) {
hours.push(h);
}
}
return hours;
};
const getDisabledMinutes = (selectedHour: number) => {
const disabledMinutes = (selectedHour) => {
if (isCurrentMinDay && selectedHour === minDateTime.hour()) {
return fullTimeArr.filter((i) => i < minDateTime.minute());
return fullTimeArr.filter((m) => m < minDateTime.minute());
}
if (isCurrentMaxDay && selectedHour === maxDateTime.hour()) {
return fullTimeArr.filter((i) => i > maxDateTime.minute());
return fullTimeArr.filter((m) => m > maxDateTime.minute());
}
return [];
};
const getDisabledSeconds = (selectedHour: number, selectedMinute: number) => {
const disabledSeconds = (selectedHour, selectedMinute) => {
if (isCurrentMinDay && selectedHour === minDateTime.hour() && selectedMinute === minDateTime.minute()) {
return fullTimeArr.filter((i) => i < minDateTime.second());
return fullTimeArr.filter((s) => s < minDateTime.second());
}
if (isCurrentMaxDay && selectedHour === maxDateTime.hour() && selectedMinute === maxDateTime.minute()) {
return fullTimeArr.filter((i) => i > maxDateTime.second());
return fullTimeArr.filter((s) => s > maxDateTime.second());
}
return [];
};
return {
disabledHours,
disabledMinutes: getDisabledMinutes,
disabledSeconds: getDisabledSeconds,
disabledMinutes,
disabledSeconds,
};
};
setDisabledDate(() => {
return disabledDate;
});
setDisabledTime(() => {
return disabledTime;
});
setDisabledDate(() => disabledDate);
setDisabledTime(() => disabledTime);
};
const newProps = {
utc,
...props,

View File

@ -10,6 +10,7 @@
import { getDefaultFormat, str2moment, toGmt, toLocal, getPickerFormat } from '@nocobase/utils/client';
import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import { dayjsable, formatDayjsValue } from '@formily/antd-v5/esm/__builtins__';
const toStringByPicker = (value, picker = 'date', timezone: 'gmt' | 'local') => {
if (!dayjs.isDayjs(value)) return value;
@ -89,7 +90,7 @@ export const handleDateChangeOnForm = (value, dateOnly, utc, picker, showTime, g
return value;
}
if (dateOnly) {
return dayjs(value).startOf(picker).format('YYYY-MM-DD');
return formatDayjsValue(value, 'YYYY-MM-DD');
}
if (utc) {
if (gmt) {
@ -114,6 +115,7 @@ export const mapDatePicker = function () {
const { dateOnly, showTime, picker = 'date', utc, gmt, underFilter } = props;
const format = getDefaultFormat(props);
const onChange = props.onChange;
return {
...props,
inputReadOnly: isMobileMedia,

View File

@ -56,12 +56,13 @@ ReadPretty.Input = (props: InputReadPrettyProps) => {
return props.value && typeof props.value === 'object' ? JSON.stringify(props.value) : compile(props.value);
}, [props.value]);
const flexStyle = props.ellipsis ? { display: 'flex', alignItems: 'center' } : {};
return (
<div
className={cls(prefixCls, props.className)}
style={{
display: 'flex',
alignItems: 'center',
...flexStyle,
overflowWrap: 'break-word',
whiteSpace: 'normal',
...props.style,

View File

@ -48,11 +48,11 @@ const useStyles = genStyleHook('nb-list', (token) => {
width: '100%',
flexDirection: 'column',
'&:not(:first-child)': {
paddingTop: token.paddingContentVertical,
marginTop: token.paddingContentVertical,
},
'&:not(:last-child)': {
paddingBottom: token.paddingContentVertical,
marginBottom: token.paddingContentVertical,
borderBottom: `1px solid ${token.colorBorderSecondary}`,
},
},

View File

@ -244,7 +244,7 @@ function FinallyButton({
inheritsCollections,
linkageFromForm,
allowAddToCurrent,
props,
props: { onlyIcon, ...props },
componentType,
menu,
onClick,
@ -362,7 +362,7 @@ function FinallyButton({
...buttonStyle,
}}
>
{props.onlyIcon ? props?.children?.[1] : props?.children}
{onlyIcon ? props?.children?.[1] : props?.children}
</Button>
);
}

View File

@ -10,8 +10,9 @@
import { Field } from '@formily/core';
import { useField, useFieldSchema } from '@formily/react';
import { merge } from '@formily/shared';
import _, { cloneDeep } from 'lodash';
import React, { useCallback, useEffect, useMemo } from 'react';
import _, { cloneDeepWith } from 'lodash';
import React, { isValidElement, useCallback, useEffect, useMemo } from 'react';
import { useBlockContext } from '../../../block-provider';
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
import {
useCollectionField_deprecated,
@ -20,14 +21,13 @@ import {
useCollection_deprecated,
} from '../../../collection-manager';
import { CollectionFieldProvider } from '../../../data-source';
import { FlagProvider } from '../../../flag-provider';
import { useRecord } from '../../../record-provider';
import { useCompile, useComponent } from '../../../schema-component';
import { VariableInput, getShouldChange } from '../../../schema-settings/VariableInput/VariableInput';
import { Option } from '../../../schema-settings/VariableInput/type';
import { formatVariableScop } from '../../../schema-settings/VariableInput/utils/formatVariableScop';
import { useLocalVariables, useVariables } from '../../../variables';
import { useBlockContext } from '../../../block-provider';
import { FlagProvider } from '../../../flag-provider';
interface AssignedFieldProps {
value: any;
onChange: (value: any) => void;
@ -126,7 +126,14 @@ export const AssignedFieldInner = (props: AssignedFieldProps) => {
currentForm.children = formatVariableScop(currentFormFields);
}
return cloneDeep(scope);
return cloneDeepWith(scope, (value) => {
// 不对 `ReactElement` 进行深拷贝,因为会报错
if (isValidElement(value)) {
return value;
}
// 对于其他类型的对象,继续正常的深拷贝
return undefined;
});
},
[currentFormFields, name],
);

View File

@ -64,7 +64,7 @@ export const SchemaSettingsDefaultValue = function DefaultValueConfigure(props:
const localVariables = useLocalVariables();
const collection = useCollection_deprecated();
const record = useRecord();
const { form } = useFormBlockContext();
const { form, type } = useFormBlockContext();
const { getFields } = useCollectionFilterOptionsV2(collection);
const { isInSubForm, isInSubTable } = useFlag() || {};
@ -219,7 +219,6 @@ export const SchemaSettingsDefaultValue = function DefaultValueConfigure(props:
targetField,
variables,
]);
const handleSubmit: (values: any) => void = useCallback(
(v) => {
const schema: ISchema = {
@ -227,7 +226,7 @@ export const SchemaSettingsDefaultValue = function DefaultValueConfigure(props:
};
fieldSchema.default = v.default ?? null;
if (!isVariable(v.default)) {
field.setInitialValue?.(v.default);
(record.__isNewRecord__ || type === 'create') && field.setInitialValue?.(v.default);
}
schema.default = v.default ?? null;
dn.emit('patch', {

View File

@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
import { useBlockContext } from '../../../block-provider';
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
import { useDataBlockRequestData, useDataSource } from '../../../data-source';
import { useCollection, useDataSource } from '../../../data-source';
import { useFlag } from '../../../flag-provider/hooks/useFlag';
import { useBaseVariable } from './useBaseVariable';
@ -100,6 +100,7 @@ export const useCurrentFormVariable = ({
const { currentFormCtx, shouldDisplayCurrentForm } = useCurrentFormContext({ form: _form });
const { t } = useTranslation();
const { collectionName } = useFormBlockContext();
const collection = useCollection();
const dataSource = useDataSource();
const currentFormSettings = useBaseVariable({
collectionField,
@ -108,7 +109,7 @@ export const useCurrentFormVariable = ({
maxDepth: 4,
name: '$nForm',
title: t('Current form'),
collectionName: collectionName,
collectionName: collectionName || collection?.name,
noDisabled,
dataSource: dataSource?.key,
returnFields: (fields, option) => {

View File

@ -53,7 +53,11 @@ const useLocalVariables = (props?: Props) => {
dataSource: parentPopupDataSource,
defaultValue: defaultValueOfParentPopupRecord,
} = useParentPopupVariableContext();
const { urlSearchParamsCtx, shouldDisplay: shouldDisplayURLSearchParams } = useURLSearchParamsVariable();
const {
urlSearchParamsCtx,
shouldDisplay: shouldDisplayURLSearchParams,
defaultValue: defaultValueOfURLSearchParams,
} = useURLSearchParamsVariable();
const { datetimeCtx } = useDatetimeVariableContext();
const { currentFormCtx } = useCurrentFormContext({ form: props?.currentForm });
const { name: currentCollectionName } = useCollection_deprecated();
@ -151,6 +155,7 @@ const useLocalVariables = (props?: Props) => {
shouldDisplayURLSearchParams && {
name: '$nURLSearchParams',
ctx: urlSearchParamsCtx,
defaultValue: defaultValueOfURLSearchParams,
},
] as VariableOption[]
).filter(Boolean);

View File

@ -1,6 +1,6 @@
{
"name": "create-nocobase-app",
"version": "1.7.14",
"version": "1.7.18",
"main": "src/index.js",
"license": "AGPL-3.0",
"dependencies": {

View File

@ -1,16 +1,16 @@
{
"name": "@nocobase/data-source-manager",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/actions": "1.7.14",
"@nocobase/cache": "1.7.14",
"@nocobase/database": "1.7.14",
"@nocobase/resourcer": "1.7.14",
"@nocobase/utils": "1.7.14",
"@nocobase/actions": "1.7.18",
"@nocobase/cache": "1.7.18",
"@nocobase/database": "1.7.18",
"@nocobase/resourcer": "1.7.18",
"@nocobase/utils": "1.7.18",
"@types/jsonwebtoken": "^8.5.8",
"jsonwebtoken": "^9.0.2"
},

View File

@ -1,13 +1,13 @@
{
"name": "@nocobase/database",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"license": "AGPL-3.0",
"dependencies": {
"@nocobase/logger": "1.7.14",
"@nocobase/utils": "1.7.14",
"@nocobase/logger": "1.7.18",
"@nocobase/utils": "1.7.18",
"async-mutex": "^0.3.2",
"chalk": "^4.1.1",
"cron-parser": "4.4.0",

View File

@ -40,4 +40,25 @@ describe('password field', () => {
user = await User.model.findOne();
expect(await pwd.verify('654321', user.password)).toBeTruthy();
});
it('should be encrypted when adding password fields in batches', async () => {
const User = db.collection({
name: 'users',
fields: [
{ type: 'string', name: 'name' },
{ type: 'password', name: 'password' },
],
});
await db.sync();
const instances = await User.model.bulkCreate([
{
password: '123456',
name: 'zhangsan',
},
]);
const pwd = User.getField<PasswordField>('password');
expect(await pwd.verify('123456', instances[0].password)).toBeTruthy();
const user = await User.model.findOne({ where: { name: 'zhangsan' } });
expect(await pwd.verify('123456', user.password)).toBeTruthy();
});
});

View File

@ -0,0 +1,40 @@
/**
* 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 { parseDatabaseOptionsFromEnv } from '@nocobase/database';
describe('database helpers', () => {
describe('parseDatabaseOptionsFromEnv()', () => {
it('undefined pool options', async () => {
const options1 = await parseDatabaseOptionsFromEnv();
expect(options1).toMatchObject({
pool: {},
});
});
it('custom pool options', async () => {
process.env.DB_POOL_MAX = '10';
process.env.DB_POOL_MIN = '1';
process.env.DB_POOL_IDLE = '5000';
process.env.DB_POOL_ACQUIRE = '30000';
process.env.DB_POOL_EVICT = '2000';
process.env.DB_POOL_MAX_USES = '0'; // Set to 0 to test default behavior
const options2 = await parseDatabaseOptionsFromEnv();
expect(options2.pool).toMatchObject({
max: 10,
min: 1,
idle: 5000,
acquire: 30000,
evict: 2000,
maxUses: Number.POSITIVE_INFINITY, // Default value
});
});
});
});

View File

@ -56,16 +56,19 @@ export class PasswordField extends Field {
init() {
const { name } = this.options;
this.listener = async (model: Model) => {
if (!model.changed(name as any)) {
return;
}
const value = model.get(name) as string;
if (value) {
const hash = await this.hash(value);
model.set(name, hash);
} else {
model.set(name, model.previous(name));
this.listener = async (instances: Model[]) => {
instances = Array.isArray(instances) ? instances : [instances];
for (const instance of instances) {
if (!instance.changed(name as any)) {
continue;
}
const value = instance.get(name) as string;
if (value) {
const hash = await this.hash(value);
instance.set(name, hash);
} else {
instance.set(name, instance.previous(name));
}
}
};
}
@ -73,12 +76,14 @@ export class PasswordField extends Field {
bind() {
super.bind();
this.on('beforeCreate', this.listener);
this.on('beforeBulkCreate', this.listener);
this.on('beforeUpdate', this.listener);
}
unbind() {
super.unbind();
this.off('beforeCreate', this.listener);
this.off('beforeBulkCreate', this.listener);
this.off('beforeUpdate', this.listener);
}
}

View File

@ -15,6 +15,7 @@ import { MysqlDialect } from './dialects/mysql-dialect';
import { SqliteDialect } from './dialects/sqlite-dialect';
import { MariadbDialect } from './dialects/mariadb-dialect';
import { PostgresDialect } from './dialects/postgres-dialect';
import { PoolOptions } from 'sequelize';
function getEnvValue(key, defaultValue?) {
return process.env[key] || defaultValue;
@ -73,6 +74,29 @@ function extractSSLOptionsFromEnv() {
});
}
function getPoolOptions(): PoolOptions {
const options: PoolOptions = {};
if (process.env.DB_POOL_MAX) {
options.max = Number.parseInt(process.env.DB_POOL_MAX, 10);
}
if (process.env.DB_POOL_MIN) {
options.min = Number.parseInt(process.env.DB_POOL_MIN, 10);
}
if (process.env.DB_POOL_IDLE) {
options.idle = Number.parseInt(process.env.DB_POOL_IDLE, 10);
}
if (process.env.DB_POOL_ACQUIRE) {
options.acquire = Number.parseInt(process.env.DB_POOL_ACQUIRE, 10);
}
if (process.env.DB_POOL_EVICT) {
options.evict = Number.parseInt(process.env.DB_POOL_EVICT, 10);
}
if (process.env.DB_POOL_MAX_USES) {
options.maxUses = Number.parseInt(process.env.DB_POOL_MAX_USES, 10) || Number.POSITIVE_INFINITY;
}
return options;
}
export async function parseDatabaseOptionsFromEnv(): Promise<IDatabaseOptions> {
const databaseOptions: IDatabaseOptions = {
logging: process.env.DB_LOGGING == 'on' ? customLogger : false,
@ -87,6 +111,7 @@ export async function parseDatabaseOptionsFromEnv(): Promise<IDatabaseOptions> {
tablePrefix: process.env.DB_TABLE_PREFIX,
schema: process.env.DB_SCHEMA,
underscored: process.env.DB_UNDERSCORED === 'true',
pool: getPoolOptions(),
};
const sslOptions = await extractSSLOptionsFromEnv();

View File

@ -33,7 +33,7 @@ const toDate = (date, options: any = {}) => {
}
if (field.constructor.name === 'DateOnlyField') {
val = moment(val).format('YYYY-MM-DD HH:mm:ss');
val = moment.utc(val).format('YYYY-MM-DD HH:mm:ss');
}
const eventObj = {
@ -69,7 +69,6 @@ export default {
const r = parseDate(value, {
timezone: parseDateTimezone(ctx),
});
if (typeof r === 'string') {
return {
[Op.eq]: toDate(r, { ctx }),
@ -77,6 +76,9 @@ export default {
}
if (Array.isArray(r)) {
console.log(11111111, {
[Op.and]: [{ [Op.gte]: toDate(r[0], { ctx }) }, { [Op.lt]: toDate(r[1], { ctx }) }],
});
return {
[Op.and]: [{ [Op.gte]: toDate(r[0], { ctx }) }, { [Op.lt]: toDate(r[1], { ctx }) }],
};

View File

@ -1,13 +1,13 @@
{
"name": "@nocobase/devtools",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"license": "AGPL-3.0",
"main": "./src/index.js",
"dependencies": {
"@nocobase/build": "1.7.14",
"@nocobase/client": "1.7.14",
"@nocobase/test": "1.7.14",
"@nocobase/build": "1.7.18",
"@nocobase/client": "1.7.18",
"@nocobase/test": "1.7.18",
"@types/koa": "^2.15.0",
"@types/koa-bodyparser": "^4.3.4",
"@types/lodash": "^4.14.177",

View File

@ -1,13 +1,13 @@
{
"name": "@nocobase/evaluators",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"license": "AGPL-3.0",
"dependencies": {
"@formulajs/formulajs": "4.4.9",
"@nocobase/utils": "1.7.14",
"@nocobase/utils": "1.7.18",
"mathjs": "^10.6.0"
},
"repository": {

View File

@ -1,10 +1,10 @@
{
"name": "@nocobase/lock-manager",
"version": "1.7.14",
"version": "1.7.18",
"main": "lib/index.js",
"license": "AGPL-3.0",
"devDependencies": {
"@nocobase/utils": "1.7.14",
"@nocobase/utils": "1.7.18",
"async-mutex": "^0.5.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/logger",
"version": "1.7.14",
"version": "1.7.18",
"description": "nocobase logging library",
"license": "AGPL-3.0",
"main": "./lib/index.js",

View File

@ -1,12 +1,12 @@
{
"name": "@nocobase/resourcer",
"version": "1.7.14",
"version": "1.7.18",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"license": "AGPL-3.0",
"dependencies": {
"@nocobase/utils": "1.7.14",
"@nocobase/utils": "1.7.18",
"deepmerge": "^4.2.2",
"koa-compose": "^4.1.0",
"lodash": "^4.17.21",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/sdk",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/server",
"version": "1.7.14",
"version": "1.7.18",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"license": "AGPL-3.0",
@ -10,19 +10,19 @@
"@koa/cors": "^5.0.0",
"@koa/multer": "^3.1.0",
"@koa/router": "^13.1.0",
"@nocobase/acl": "1.7.14",
"@nocobase/actions": "1.7.14",
"@nocobase/auth": "1.7.14",
"@nocobase/cache": "1.7.14",
"@nocobase/data-source-manager": "1.7.14",
"@nocobase/database": "1.7.14",
"@nocobase/evaluators": "1.7.14",
"@nocobase/lock-manager": "1.7.14",
"@nocobase/logger": "1.7.14",
"@nocobase/resourcer": "1.7.14",
"@nocobase/sdk": "1.7.14",
"@nocobase/telemetry": "1.7.14",
"@nocobase/utils": "1.7.14",
"@nocobase/acl": "1.7.18",
"@nocobase/actions": "1.7.18",
"@nocobase/auth": "1.7.18",
"@nocobase/cache": "1.7.18",
"@nocobase/data-source-manager": "1.7.18",
"@nocobase/database": "1.7.18",
"@nocobase/evaluators": "1.7.18",
"@nocobase/lock-manager": "1.7.18",
"@nocobase/logger": "1.7.18",
"@nocobase/resourcer": "1.7.18",
"@nocobase/sdk": "1.7.18",
"@nocobase/telemetry": "1.7.18",
"@nocobase/utils": "1.7.18",
"@types/decompress": "4.2.7",
"@types/ini": "^1.3.31",
"@types/koa-send": "^4.1.3",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/telemetry",
"version": "1.7.14",
"version": "1.7.18",
"description": "nocobase telemetry library",
"license": "AGPL-3.0",
"main": "./lib/index.js",
@ -11,7 +11,7 @@
"directory": "packages/telemetry"
},
"dependencies": {
"@nocobase/utils": "1.7.14",
"@nocobase/utils": "1.7.18",
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/instrumentation": "^0.46.0",
"@opentelemetry/resources": "^1.19.0",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/test",
"version": "1.7.14",
"version": "1.7.18",
"main": "lib/index.js",
"module": "./src/index.ts",
"types": "./lib/index.d.ts",
@ -51,7 +51,7 @@
},
"dependencies": {
"@faker-js/faker": "8.1.0",
"@nocobase/server": "1.7.14",
"@nocobase/server": "1.7.18",
"@playwright/test": "^1.45.3",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/utils",
"version": "1.7.14",
"version": "1.7.18",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"license": "AGPL-3.0",

View File

@ -15,6 +15,7 @@ export interface Str2momentOptions {
picker?: 'year' | 'month' | 'week' | 'quarter';
utcOffset?: number;
utc?: boolean;
dateOnly?: boolean;
}
export type Str2momentValue = string | string[] | dayjs.Dayjs | dayjs.Dayjs[];
@ -83,10 +84,14 @@ const toMoment = (val: any, options?: Str2momentOptions) => {
return;
}
const offset = options.utcOffset;
const { gmt, picker, utc = true } = options;
const { gmt, picker, utc = true, dateOnly } = options;
if (dayjs(val).isValid()) {
if (dateOnly) {
return dayjs.utc(val, 'YYYY-MM-DD');
}
if (!utc) {
return dayjs(val);
return dayjs.utc(val);
}
if (dayjs.isDayjs(val)) {

View File

@ -188,9 +188,11 @@ export const parseFilter = async (filter: any, opts: ParseFilterOptions = {}) =>
const field = getField?.(path);
if (field?.constructor.name === 'DateOnlyField' || field?.constructor.name === 'DatetimeNoTzField') {
if (value.type) {
return getDayRangeByParams({ ...value, timezone: field?.timezone || timezone });
}
return value;
}
return dateValueWrapper(value, field?.timezone || timezone);
}
return value;

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "权限控制",
"description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.",
"description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/acl",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-action-bulk-edit",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-bulk-edit",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-edit",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-action-bulk-update",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-bulk-update",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-bulk-update",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-action-custom-request",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-custom-request",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-custom-request",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-action-duplicate",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-duplicate",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-duplicate",

View File

@ -84,8 +84,8 @@ export const actionDesignerCss = css`
`;
export const DuplicateAction = observer(
(props: any) => {
const { children, onlyIcon, icon, title, ...others } = props;
({ onlyIcon, ...props }: any) => {
const { children, icon, title, ...others } = props;
const { message } = App.useApp();
const field = useField();
const fieldSchema = useFieldSchema();

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "操作:导出记录",
"description": "Export filtered records to excel, you can configure which fields to export.",
"description.zh-CN": "导出筛选后的记录到 Excel 中,可以配置导出哪些字段。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-export",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "操作:导入记录",
"description": "Import records using excel templates. You can configure which fields to import and templates will be generated automatically.",
"description.zh-CN": "使用 Excel 模板导入数据,可以配置导入哪些字段,自动生成模板。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-import",

View File

@ -13,6 +13,7 @@ import { XlsxImporter } from '../services/xlsx-importer';
import XLSX from 'xlsx';
import * as process from 'node:process';
import moment from 'moment';
import { PasswordField } from '@nocobase/database';
describe('xlsx importer', () => {
let app: MockServer;
@ -2322,4 +2323,57 @@ describe('xlsx importer', () => {
expect(importer.run()).rejects.toThrow();
});
it('should import password field, insert data is encrypt', async () => {
const User = app.db.collection({
name: 'users',
fields: [
{ type: 'string', name: 'name' },
{ type: 'password', name: 'password' },
],
});
await app.db.sync();
const templateCreator = new TemplateCreator({
collection: User,
columns: [
{
dataIndex: ['name'],
defaultTitle: '姓名',
},
{
dataIndex: ['password'],
defaultTitle: '密码',
},
],
});
const template = (await templateCreator.run({ returnXLSXWorkbook: true })) as XLSX.WorkBook;
const worksheet = template.Sheets[template.SheetNames[0]];
XLSX.utils.sheet_add_aoa(worksheet, [['zhangsan', '123456']], {
origin: 'A2',
});
const importer = new XlsxImporter({
collectionManager: app.mainDataSource.collectionManager,
collection: User,
columns: [
{
dataIndex: ['name'],
defaultTitle: '姓名',
},
{
dataIndex: ['password'],
defaultTitle: '密码',
},
],
workbook: template,
});
await importer.run();
const pwd = User.getField<PasswordField>('password');
const user = await User.model.findOne({ where: { name: 'zhangsan' } });
expect(await pwd.verify('123456', user.password)).toBeTruthy();
});
});

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-action-print",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/action-print",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-print",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "AI 集成",
"description": "Support integration with AI services, providing AI-related workflow nodes to enhance business processing capabilities.",
"description.zh-CN": "支持接入 AI 服务,提供 AI 相关的工作流节点,增强业务处理能力。",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"peerDependencies": {
"@nocobase/client": "1.x",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-api-doc",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "API documentation",
"displayName.zh-CN": "API 文档",
"description": "An OpenAPI documentation generator for NocoBase HTTP API.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "认证API 密钥",
"description": "Allows users to use API key to access application's HTTP API",
"description.zh-CN": "允许用户使用 API 密钥访问应用的 HTTP API",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/api-keys",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "异步任务管理器",
"description": "Manage and monitor asynchronous tasks such as data import/export. Support task progress tracking and notification.",
"description.zh-CN": "管理和监控数据导入导出等异步任务。支持任务进度跟踪和通知。",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"peerDependencies": {
"@nocobase/client": "1.x",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-audit-logs",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Audit logs (deprecated)",
"displayName.zh-CN": "审计日志(废弃)",
"description": "This plugin is deprecated. There will be a new audit log plugin in the future.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "认证:短信",
"description": "SMS authentication.",
"description.zh-CN": "通过短信验证码认证身份。",
"version": "1.7.14",
"version": "1.7.18",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/auth-sms",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth-sms",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-auth",
"version": "1.7.14",
"version": "1.7.18",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/auth",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/auth",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "应用的备份与还原(废弃)",
"description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.",
"description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/backup-restore",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "区块iframe",
"description": "Create an iframe block on the page to embed and display external web pages or content.",
"description.zh-CN": "在页面上创建和管理iframe用于嵌入和展示外部网页或内容。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/block-iframe",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "区块:模板",
"description": "Create and manage block templates for reuse on pages.",
"description.zh-CN": "创建和管理区块模板,用于在页面中重复使用。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/block-template",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-block-workbench",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Block: Action panel",
"displayName.zh-CN": "区块:操作面板",
"description": "Centrally manages and displays various actions, allowing users to efficiently perform tasks. It supports extensibility, with current action types including pop-ups, links, scanning, and custom requests.",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-calendar",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Calendar",
"displayName.zh-CN": "日历",
"description": "Provides callendar collection template and block for managing date data, typically for date/time related information such as events, appointments, tasks, and so on.",

View File

@ -47,8 +47,9 @@ import useStyle from './style';
import type { ToolbarProps } from './types';
import { formatDate } from './utils';
import updateLocale from 'dayjs/plugin/updateLocale';
dayjs.extend(updateLocale);
import { dateFnsLocalizer } from 'react-big-calendar';
import { format, parse, startOfWeek, getDay } from 'date-fns';
import enUS from 'date-fns/locale/en-US';
interface Event {
id: string;
@ -268,12 +269,16 @@ export const Calendar: any = withDynamicSchemaProps(
);
const localizer = useMemo(() => {
dayjs.updateLocale('en', {
weekStart: props.weekStart ?? '1',
return dateFnsLocalizer({
format,
parse,
startOfWeek: (date) => {
return startOfWeek(date, { locale: { options: { weekStartsOn: props.weekStart } } });
},
getDay,
locales: { 'en-US': enUS },
});
return reactBigCalendar.dayjsLocalizer(dayjs);
}, [reactBigCalendar]);
}, [props.weekStart]);
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { dataSource, fieldNames, showLunar, getFontColor, getBackgroundColor, enableQuickCreateEvent } =
useProps(props);
@ -455,14 +460,14 @@ export const Calendar: any = withDynamicSchemaProps(
});
}}
formats={{
monthHeaderFormat: 'YYYY-M',
agendaDateFormat: 'M-DD',
dayHeaderFormat: 'YYYY-M-DD',
monthHeaderFormat: 'yyyy-M',
agendaDateFormat: 'M-dd',
dayHeaderFormat: 'yyyy-M-dd',
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
if (eq(start, end, 'month')) {
return local.format(start, 'YYYY-M', culture);
return local.format(start, 'yyyy-M', culture);
}
return `${local.format(start, 'YYYY-M', culture)} - ${local.format(end, 'YYYY-M', culture)}`;
return `${local.format(start, 'yyyy-M', culture)} - ${local.format(end, 'yyyy-M', culture)}`;
},
}}
components={components}

View File

@ -277,11 +277,11 @@ export const calendarBlockSettings = new SchemaSettings({
const { name } = useCollection();
const app = useApp();
const plugin = app.pm.get('calendar') as any;
const { dateTimeFields } = plugin;
const { dateTimeFieldInterfaces } = plugin;
return {
title: t('Start date field'),
value: fieldNames.start,
options: getCollectionFieldsOptions(name, null, dateTimeFields, {
options: getCollectionFieldsOptions(name, null, dateTimeFieldInterfaces, {
association: ['o2o', 'obo', 'oho', 'm2o'],
}),
onChange: (start) => {
@ -315,11 +315,11 @@ export const calendarBlockSettings = new SchemaSettings({
const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {};
const app = useApp();
const plugin = app.pm.get('calendar') as any;
const { dateTimeFields } = plugin;
const { dateTimeFieldInterfaces } = plugin;
return {
title: t('End date field'),
value: fieldNames.end,
options: getCollectionFieldsOptions(name, null, dateTimeFields, {
options: getCollectionFieldsOptions(name, null, dateTimeFieldInterfaces, {
association: ['o2o', 'obo', 'oho', 'm2o'],
}),
onChange: (end) => {

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "图表(废弃)",
"description": "The plugin has been deprecated, please use the data visualization plugin instead.",
"description.zh-CN": "已废弃插件,请使用数据可视化插件代替。",
"version": "1.7.14",
"version": "1.7.18",
"main": "./dist/server/index.js",
"license": "AGPL-3.0",
"devDependencies": {

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "WEB 客户端",
"description": "Provides a client interface for the NocoBase server",
"description.zh-CN": "为 NocoBase 服务端提供客户端界面",
"version": "1.7.14",
"version": "1.7.18",
"main": "./dist/server/index.js",
"license": "AGPL-3.0",
"devDependencies": {

View File

@ -7,7 +7,7 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Model, Transaction } from '@nocobase/database';
import { Model, MultipleRelationRepository, Transaction } from '@nocobase/database';
import PluginLocalizationServer from '@nocobase/plugin-localization';
import { Plugin } from '@nocobase/server';
import { tval } from '@nocobase/utils';
@ -34,7 +34,7 @@ async function getLang(ctx) {
}
export class PluginClientServer extends Plugin {
async beforeLoad() {}
async beforeLoad() { }
async install() {
const uiSchemas = this.db.getRepository<any>('uiSchemas');
@ -216,7 +216,7 @@ export class PluginClientServer extends Plugin {
const tabIds = tabs.map((x) => x.get('id'));
const where = { desktopRouteId: tabIds, roleName };
if (action === 'create') {
const exists = await repository.find({ where });
const exists = await repository.find({ where, transaction });
const modelsByRouteId = _.keyBy(exists, (x) => x.get('desktopRouteId'));
const createModels = tabs
.map((x) => !modelsByRouteId[x.get('id')] && { desktopRouteId: x.get('id'), roleName })
@ -282,6 +282,27 @@ export class PluginClientServer extends Plugin {
await next();
});
this.app.resourceManager.registerActionHandler('roles.desktopRoutes:set', async (ctx, next) => {
let { values } = ctx.action.params;
if (values.length) {
const instances = await this.app.db.getRepository('desktopRoutes').find({
filter: {
$or: [
{ id: { $in: values } },
{ parentId: { $in: values } }
]
}
});
values = instances.map((instance) => instance.get('id'));
};
const { resourceName, sourceId } = ctx.action;
const repository = this.app.db.getRepository<MultipleRelationRepository>(resourceName, sourceId)
await repository['set'](values);
ctx.status = 200;
await next();
});
}
registerLocalizationSource() {

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表: SQL",
"description": "Provides SQL collection template",
"description.zh-CN": "提供 SQL 数据表模板",
"version": "1.7.14",
"version": "1.7.18",
"homepage": "https://docs-cn.nocobase.com/handbook/collection-sql",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/collection-sql",
"main": "dist/server/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-collection-tree",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Collection: Tree",
"displayName.zh-CN": "数据表:树",
"description": "Provides tree collection template",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据源:主数据库",
"description": "NocoBase main database, supports relational databases such as PostgreSQL, MySQL, MariaDB and so on.",
"description.zh-CN": "NocoBase 主数据库,支持 PostgreSQL、MySQL、MariaDB 等关系型数据库。",
"version": "1.7.14",
"version": "1.7.18",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/data-source-main",
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/data-source-main",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-data-source-manager",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"displayName": "Data source manager",
"displayName.zh-CN": "数据源管理",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-data-visualization",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Data visualization",
"displayName.zh-CN": "数据可视化",
"description": "Provides data visualization feature, including chart block and chart filter block, support line charts, area charts, bar charts and more than a dozen kinds of charts, you can also extend more chart types.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "部门",
"description": "Organize users by departments, set hierarchical relationships, link roles to control permissions, and use departments as variables in workflows and expressions.",
"description.zh-CN": "以部门来组织用户,设定上下级关系,绑定角色控制权限,并支持作为变量用于工作流和表达式。",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"peerDependencies": {
"@nocobase/actions": "1.x",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-disable-pm-add",
"version": "1.7.14",
"version": "1.7.18",
"main": "./dist/server/index.js",
"peerDependencies": {
"@nocobase/client": "1.x",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-environment-variables",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"peerDependencies": {
"@nocobase/client": "1.x",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "错误处理器",
"description": "Handling application errors and exceptions.",
"description.zh-CN": "处理应用程序中的错误和异常。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-field-attachment-url",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"displayName": "Collection field: Attachment(URL)",
"displayName.zh-CN": "数据表字段附件URL",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-field-china-region",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Collection field: administrative divisions of China",
"displayName.zh-CN": "数据表字段:中国行政区划",
"description": "Provides data and field type for administrative divisions of China.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表字段:公式",
"description": "Configure and store the results of calculations between multiple field values in the same record, supporting both Math.js and Excel formula functions.",
"description.zh-CN": "可以配置并存储同一条记录的多字段值之间的计算结果,支持 Math.js 和 Excel formula functions 两种引擎",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/field-formula",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表字段:多对多 (数组)",
"description": "Allows to create many to many relationships between two models by storing an array of unique keys of the target model.",
"description.zh-CN": "支持通过在数组中存储目标表唯一键的方式建立多对多关系。",
"version": "1.7.14",
"version": "1.7.18",
"main": "dist/server/index.js",
"peerDependencies": {
"@nocobase/client": "1.x",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表字段Markdown(Vditor)",
"description": "Used to store Markdown and render it using Vditor editor, supports common Markdown syntax such as list, code, quote, etc., and supports uploading images, recordings, etc.It also allows for instant rendering, where what you see is what you get.",
"description.zh-CN": "用于存储 Markdown并使用 Vditor 编辑器渲染,支持常见 Markdown 语法,如列表,代码,引用等,并支持上传图片,录音等。同时可以做到即时渲染,所见即所得。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/field-markdown-vditor",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "数据表字段:自动编码",
"description": "Automatically generate codes based on configured rules, supporting combinations of dates, numbers, and text.",
"description.zh-CN": "根据配置的规则自动生成编码,支持日期、数字、文本的组合。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/field-sequence",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-field-sort",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"displayName": "Collection field: Sort",
"displayName.zh-CN": "数据表字段:排序",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-file-manager",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "File manager",
"displayName.zh-CN": "文件管理器",
"description": "Provides files storage services with files collection template and attachment field.",

View File

@ -18,6 +18,7 @@ export default {
createdBy: true,
updatedBy: true,
template: 'file',
filterTargetKey: 'id',
fields: [
{
comment: '用户文件名(不含扩展名)',

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-gantt",
"version": "1.7.14",
"version": "1.7.18",
"displayName": "Block: Gantt",
"displayName.zh-CN": "区块:甘特图",
"description": "Provides Gantt block.",

View File

@ -4,7 +4,7 @@
"displayName.zh-CN": "可视化数据表管理",
"description": "An ER diagram-like tool. Currently only the Master database is supported.",
"description.zh-CN": "类似 ER 图的工具,目前只支持主数据库。",
"version": "1.7.14",
"version": "1.7.18",
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"homepage": "https://docs.nocobase.com/handbook/graph-collection-manager",

Some files were not shown because too many files have changed in this diff Show More