diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b9a3c671..3f2ee38c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,297 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14 + +### 🎉 New Features + +- **[Departments]** Make Department, Attachment URL, and Workflow response message plugins free ([#6663](https://github.com/nocobase/nocobase/pull/6663)) by @chenos + +### 🐛 Bug Fixes + +- **[client]** + - The filter form should not display the "Unsaved changes" prompt ([#6657](https://github.com/nocobase/nocobase/pull/6657)) by @zhangzhonghe + + - "allow multiple" option not working for relation field ([#6661](https://github.com/nocobase/nocobase/pull/6661)) by @katherinehhh + + - In the filter form, when the filter button is clicked, if there are fields that have not passed validation, the filtering is still triggered ([#6659](https://github.com/nocobase/nocobase/pull/6659)) by @zhangzhonghe + + - Switching to the group menu should not jump to a page that has already been hidden in menu ([#6654](https://github.com/nocobase/nocobase/pull/6654)) by @zhangzhonghe + +- **[File storage: S3(Pro)]** + - Organize language by @jiannx + + - Individual baseurl and public settings, improve S3 pro storage config UX by @jiannx + +- **[Migration manager]** the skip auto backup option becomes invalid if environment variable popup appears during migration by @gchust + +## [v1.6.19](https://github.com/nocobase/nocobase/compare/v1.6.18...v1.6.19) - 2025-04-14 + +### 🐛 Bug Fixes + +- **[client]** + - Fix the issue of preview images being obscured ([#6651](https://github.com/nocobase/nocobase/pull/6651)) by @zhangzhonghe + + - In the form block, the default value of the field configuration will first be displayed as the original variable string and then disappear ([#6649](https://github.com/nocobase/nocobase/pull/6649)) by @zhangzhonghe + +## [v1.6.18](https://github.com/nocobase/nocobase/compare/v1.6.17...v1.6.18) - 2025-04-11 + +### 🚀 Improvements + +- **[client]** + - Add default type fallback API for `Variable.Input` ([#6644](https://github.com/nocobase/nocobase/pull/6644)) by @mytharcher + + - Optimize prompts for unconfigured pages ([#6641](https://github.com/nocobase/nocobase/pull/6641)) by @zhangzhonghe + +- **[Workflow: Delay node]** Support to use variable for duration ([#6621](https://github.com/nocobase/nocobase/pull/6621)) by @mytharcher + +- **[Workflow: Custom action event]** Add refresh settings for trigger workflow button by @mytharcher + +### 🐛 Bug Fixes + +- **[client]** + - subtable description overlapping with add new button ([#6646](https://github.com/nocobase/nocobase/pull/6646)) by @katherinehhh + + - dashed underline caused by horizontal form layout in modal ([#6639](https://github.com/nocobase/nocobase/pull/6639)) by @katherinehhh + +- **[File storage: S3(Pro)]** Fix missing await for next call. by @jiannx + +- **[Email manager]** Fix missing await for next call. by @jiannx + +## [v1.6.17](https://github.com/nocobase/nocobase/compare/v1.6.16...v1.6.17) - 2025-04-09 + +### 🚀 Improvements + +- **[utils]** Add duration extension for dayjs ([#6630](https://github.com/nocobase/nocobase/pull/6630)) by @mytharcher + +- **[client]** + - Support to search field in Filter component ([#6627](https://github.com/nocobase/nocobase/pull/6627)) by @mytharcher + + - Add `trim` API for `Input` and `Variable.TextArea` ([#6624](https://github.com/nocobase/nocobase/pull/6624)) by @mytharcher + +- **[Error handler]** Support custom title in AppError component. ([#6409](https://github.com/nocobase/nocobase/pull/6409)) by @sheldon66 + +- **[IP restriction]** Update IP restriction message content. by @sheldon66 + +- **[File storage: S3(Pro)]** Support global variables in storage configuration by @mytharcher + +### 🐛 Bug Fixes + +- **[client]** + - rule with 'any' condition does not take effect when condition list is empty ([#6628](https://github.com/nocobase/nocobase/pull/6628)) by @katherinehhh + + - data issue with Gantt block in tree collection ([#6617](https://github.com/nocobase/nocobase/pull/6617)) by @katherinehhh + + - The relationship fields in the filter form report an error after the page is refreshed because x-data-source is not carried ([#6619](https://github.com/nocobase/nocobase/pull/6619)) by @zhangzhonghe + + - variable parse failure when URL parameters contain Chinese characters ([#6618](https://github.com/nocobase/nocobase/pull/6618)) by @katherinehhh + +- **[Users]** Issue with parsing the user profile form schema ([#6635](https://github.com/nocobase/nocobase/pull/6635)) by @2013xile + +- **[Mobile]** single-select field with 'contains' filter on mobile does not support multiple selection ([#6629](https://github.com/nocobase/nocobase/pull/6629)) by @katherinehhh + +- **[Action: Export records]** missing filter params when exporting data after changing pagination ([#6633](https://github.com/nocobase/nocobase/pull/6633)) by @katherinehhh + +- **[Email manager]** fix email management permission cannot view email list by @jiannx + +- **[File storage: S3(Pro)]** Throw error to user when upload logo to S3 Pro storage (set to default) by @mytharcher + +- **[Workflow: Approval]** Fix `updatedAt` changed after migration by @mytharcher + +- **[Migration manager]** migration log creation time is displayed incorrectly in some environments by @gchust + +## [v1.6.16](https://github.com/nocobase/nocobase/compare/v1.6.15...v1.6.16) - 2025-04-03 + +### 🐛 Bug Fixes + +- **[client]** + - x-disabled property not taking effect on form fields ([#6610](https://github.com/nocobase/nocobase/pull/6610)) by @katherinehhh + + - field label display issue to prevent truncation by colon ([#6599](https://github.com/nocobase/nocobase/pull/6599)) by @katherinehhh + +- **[database]** When deleting one-to-many records, both `filter` and `filterByTk` are passed and `filter` includes an association field, the `filterByTk` is ignored ([#6606](https://github.com/nocobase/nocobase/pull/6606)) by @2013xile + +## [v1.6.15](https://github.com/nocobase/nocobase/compare/v1.6.14...v1.6.15) - 2025-04-01 + +### 🚀 Improvements + +- **[database]** + - Add trim option for text field ([#6603](https://github.com/nocobase/nocobase/pull/6603)) by @mytharcher + + - Add trim option for string field ([#6565](https://github.com/nocobase/nocobase/pull/6565)) by @mytharcher + +- **[File manager]** Add trim option for text fields of storages collection ([#6604](https://github.com/nocobase/nocobase/pull/6604)) by @mytharcher + +- **[Workflow]** Improve code ([#6589](https://github.com/nocobase/nocobase/pull/6589)) by @mytharcher + +- **[Workflow: Approval]** Support to use block template for approval process form by @mytharcher + +### 🐛 Bug Fixes + +- **[database]** Avoid "datetimeNoTz" field changes when value not changed in updating record ([#6588](https://github.com/nocobase/nocobase/pull/6588)) by @mytharcher + +- **[client]** + - association field (select) displaying N/A when exposing related collection fields ([#6582](https://github.com/nocobase/nocobase/pull/6582)) by @katherinehhh + + - Fix `disabled` property not works when `SchemaInitializerItem` has `items` ([#6597](https://github.com/nocobase/nocobase/pull/6597)) by @mytharcher + + - cascade issue: 'The value of xxx cannot be in array format' when deleting and re-selecting ([#6585](https://github.com/nocobase/nocobase/pull/6585)) by @katherinehhh + +- **[Collection field: Many to many (array)]** Issue of filtering by fields in an association collection with a many to many (array) field ([#6596](https://github.com/nocobase/nocobase/pull/6596)) by @2013xile + +- **[Public forms]** View permissions include list and get ([#6607](https://github.com/nocobase/nocobase/pull/6607)) by @chenos + +- **[Authentication]** token assignment in `AuthProvider` ([#6593](https://github.com/nocobase/nocobase/pull/6593)) by @2013xile + +- **[Workflow]** Fix sync option display incorrectly ([#6595](https://github.com/nocobase/nocobase/pull/6595)) by @mytharcher + +- **[Block: Map]** map management validation should not pass with space input ([#6575](https://github.com/nocobase/nocobase/pull/6575)) by @katherinehhh + +- **[Workflow: Approval]** + - Fix client variables to use in approval form by @mytharcher + + - Fix branch mode when `endOnReject` configured as `true` by @mytharcher + +## [v1.6.14](https://github.com/nocobase/nocobase/compare/v1.6.13...v1.6.14) - 2025-03-29 + +### 🐛 Bug Fixes + +- **[Calendar]** missing data on boundary dates in weekly calendar view ([#6587](https://github.com/nocobase/nocobase/pull/6587)) by @katherinehhh + +- **[Auth: OIDC]** Incorrect redirection occurs when the callback path is the string 'null' by @2013xile + +- **[Workflow: Approval]** Fix approval node configuration is incorrect after schema changed by @mytharcher + +## [v1.6.13](https://github.com/nocobase/nocobase/compare/v1.6.12...v1.6.13) - 2025-03-28 + +### 🚀 Improvements + +- **[Async task manager]** optimize import/export buttons in Pro ([#6531](https://github.com/nocobase/nocobase/pull/6531)) by @chenos + +- **[Action: Export records Pro]** optimize import/export buttons in Pro by @katherinehhh + +- **[Migration manager]** allow skip automatic backup and restore for migration by @gchust + +### 🐛 Bug Fixes + +- **[client]** linkage conflict between same-named association fields in different sub-tables within the same form ([#6577](https://github.com/nocobase/nocobase/pull/6577)) by @katherinehhh + +- **[Action: Batch edit]** Click the batch edit button, configure the pop-up window, and then open it again, the pop-up window is blank ([#6578](https://github.com/nocobase/nocobase/pull/6578)) by @zhangzhonghe + +## [v1.6.12](https://github.com/nocobase/nocobase/compare/v1.6.11...v1.6.12) - 2025-03-27 + +### 🐛 Bug Fixes + +- **[Block: Multi-step form]** + - the submit button has the same color in its default and highlighted by @jiannx + + - fixed the bug that form reset is invalid when the field is associated with other field by @jiannx + +- **[Workflow: Approval]** Fix approval form values to submit by @mytharcher + +## [v1.6.11](https://github.com/nocobase/nocobase/compare/v1.6.10...v1.6.11) - 2025-03-27 + +### 🚀 Improvements + +- **[client]** + - Optimize 502 error message ([#6547](https://github.com/nocobase/nocobase/pull/6547)) by @chenos + + - Only support plain text file to preview ([#6563](https://github.com/nocobase/nocobase/pull/6563)) by @mytharcher + +- **[Collection field: Sequence]** support setting sequence as the title field for calendar block ([#6562](https://github.com/nocobase/nocobase/pull/6562)) by @katherinehhh + +- **[Workflow: Approval]** Support to skip validator in settings by @mytharcher + +### 🐛 Bug Fixes + +- **[client]** + - issue with date field display in data scope filtering ([#6564](https://github.com/nocobase/nocobase/pull/6564)) by @katherinehhh + + - The 'Ellipsis overflow content' option requires a page refresh for the toggle state to take effect ([#6520](https://github.com/nocobase/nocobase/pull/6520)) by @zhangzhonghe + + - Unable to open another modal within a modal ([#6535](https://github.com/nocobase/nocobase/pull/6535)) by @zhangzhonghe + +- **[API documentation]** API document page cannot scroll ([#6566](https://github.com/nocobase/nocobase/pull/6566)) by @zhangzhonghe + +- **[Workflow]** Make sure workflow key is generated before save ([#6567](https://github.com/nocobase/nocobase/pull/6567)) by @mytharcher + +- **[Workflow: Post-action event]** Multiple records in bulk action should trigger multiple times ([#6559](https://github.com/nocobase/nocobase/pull/6559)) by @mytharcher + +- **[Authentication]** Localization issue for fields of sign up page ([#6556](https://github.com/nocobase/nocobase/pull/6556)) by @2013xile + +- **[Public forms]** issue with public form page title displaying 'Loading...' ([#6569](https://github.com/nocobase/nocobase/pull/6569)) by @katherinehhh + +## [v1.6.10](https://github.com/nocobase/nocobase/compare/v1.6.9...v1.6.10) - 2025-03-25 + +### 🐛 Bug Fixes + +- **[client]** + - Unable to use 'Current User' variable when adding a link page ([#6536](https://github.com/nocobase/nocobase/pull/6536)) by @zhangzhonghe + + - field assignment with null value is ineffective ([#6549](https://github.com/nocobase/nocobase/pull/6549)) by @katherinehhh + + - `yarn doc` command error ([#6540](https://github.com/nocobase/nocobase/pull/6540)) by @gchust + + - Remove the 'Allow multiple selection' option from dropdown single-select fields in filter forms ([#6515](https://github.com/nocobase/nocobase/pull/6515)) by @zhangzhonghe + + - Relational field's data range linkage is not effective ([#6530](https://github.com/nocobase/nocobase/pull/6530)) by @zhangzhonghe + +- **[Collection: Tree]** Migration issue for plugin-collection-tree ([#6537](https://github.com/nocobase/nocobase/pull/6537)) by @2013xile + +- **[Action: Custom request]** Unable to download UTF-8 encoded files ([#6541](https://github.com/nocobase/nocobase/pull/6541)) by @2013xile + +## [v1.6.9](https://github.com/nocobase/nocobase/compare/v1.6.8...v1.6.9) - 2025-03-23 + +### 🐛 Bug Fixes + +- **[client]** action button transparency causing setting display issue on hover ([#6529](https://github.com/nocobase/nocobase/pull/6529)) by @katherinehhh + +## [v1.6.8](https://github.com/nocobase/nocobase/compare/v1.6.7...v1.6.8) - 2025-03-22 + +### 🐛 Bug Fixes + +- **[server]** The upgrade command may cause workflow errors ([#6524](https://github.com/nocobase/nocobase/pull/6524)) by @gchust + +- **[client]** the height of the subtable in the form is set along with the form height ([#6518](https://github.com/nocobase/nocobase/pull/6518)) by @katherinehhh + +- **[Authentication]** + - X-Authenticator missing ([#6526](https://github.com/nocobase/nocobase/pull/6526)) by @chenos + + - Trim authenticator options ([#6527](https://github.com/nocobase/nocobase/pull/6527)) by @2013xile + +- **[Block: Map]** map block key management issue causing request failures due to invisible characters ([#6521](https://github.com/nocobase/nocobase/pull/6521)) by @katherinehhh + +- **[Backup manager]** Restoration may cause workflow execution errors by @gchust + +- **[WeCom]** Resolve environment variables and secrets when retrieving notification configuration. by @2013xile + +## [v1.6.7](https://github.com/nocobase/nocobase/compare/v1.6.6...v1.6.7) - 2025-03-20 + +### 🚀 Improvements + +- **[Workflow: mailer node]** Add secure field config description. ([#6510](https://github.com/nocobase/nocobase/pull/6510)) by @sheldon66 + +- **[Notification: Email]** Add secure field config description. ([#6501](https://github.com/nocobase/nocobase/pull/6501)) by @sheldon66 + +- **[Calendar]** Calendar plugin with optional settings to enable or disable quick event creation ([#6391](https://github.com/nocobase/nocobase/pull/6391)) by @Cyx649312038 + +### 🐛 Bug Fixes + +- **[client]** time field submission error in Chinese locale (invalid input syntax for type time) ([#6511](https://github.com/nocobase/nocobase/pull/6511)) by @katherinehhh + +- **[File manager]** Unable to access files stored in COS ([#6512](https://github.com/nocobase/nocobase/pull/6512)) by @chenos + +- **[Block: Map]** secret key fields not triggering validation in map management ([#6509](https://github.com/nocobase/nocobase/pull/6509)) by @katherinehhh + +- **[WEB client]** The path in the route management table is different from the actual path ([#6483](https://github.com/nocobase/nocobase/pull/6483)) by @zhangzhonghe + +- **[Action: Export records Pro]** Unable to export attachments by @chenos + +- **[Workflow: Approval]** + - Fix null user caused crash by @mytharcher + + - Fix error thrown when add query node result by @mytharcher + ## [v1.6.6](https://github.com/nocobase/nocobase/compare/v1.6.5...v1.6.6) - 2025-03-18 ### 🎉 New Features diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 5861cd0756..7bb43bdfb4 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -5,6 +5,297 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。 +## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14 + +### 🎉 新特性 + +- **[部门]** 商业插件部门、附件 URL、工作流响应消息改为免费提供 ([#6663](https://github.com/nocobase/nocobase/pull/6663)) by @chenos + +### 🐛 修复 + +- **[client]** + - 筛选表单不应该显示“未保存修改”提示 ([#6657](https://github.com/nocobase/nocobase/pull/6657)) by @zhangzhonghe + + - 筛选表单中关系字段的“允许多选”设置项不生效 ([#6661](https://github.com/nocobase/nocobase/pull/6661)) by @katherinehhh + + - 筛选表单中,当点击筛选按钮时,如果有字段未校验通过,依然会触发筛选的问题 ([#6659](https://github.com/nocobase/nocobase/pull/6659)) by @zhangzhonghe + + - 切换到分组菜单时,不应该跳转到已经在菜单中被隐藏的页面 ([#6654](https://github.com/nocobase/nocobase/pull/6654)) by @zhangzhonghe + +- **[文件存储:S3 (Pro)]** + - 整理语言文案 by @jiannx + + - baseurl 和 public 设置不再互相关联,改进 S3 pro 存储的配置交互体验 by @jiannx + +- **[迁移管理]** 迁移时若弹出环境变量弹窗,跳过自动备份选项会失效 by @gchust + +## [v1.6.19](https://github.com/nocobase/nocobase/compare/v1.6.18...v1.6.19) - 2025-04-14 + +### 🐛 修复 + +- **[client]** + - 修复预览图片被遮挡的问题 ([#6651](https://github.com/nocobase/nocobase/pull/6651)) by @zhangzhonghe + + - 表单区块中,字段配置的默认值会先显示为原始变量字符串然后再消失 ([#6649](https://github.com/nocobase/nocobase/pull/6649)) by @zhangzhonghe + +## [v1.6.18](https://github.com/nocobase/nocobase/compare/v1.6.17...v1.6.18) - 2025-04-11 + +### 🚀 优化 + +- **[client]** + - 为 `Variable.Input` 组件增加默认退避类型的 API ([#6644](https://github.com/nocobase/nocobase/pull/6644)) by @mytharcher + + - 优化未配置页面时的提示 ([#6641](https://github.com/nocobase/nocobase/pull/6641)) by @zhangzhonghe + +- **[工作流:延时节点]** 支持延迟时间使用变量 ([#6621](https://github.com/nocobase/nocobase/pull/6621)) by @mytharcher + +- **[工作流:自定义操作事件]** 为触发工作流按钮增加刷新配置项 by @mytharcher + +### 🐛 修复 + +- **[client]** + - 子表格中描述信息与操作按钮遮挡 ([#6646](https://github.com/nocobase/nocobase/pull/6646)) by @katherinehhh + + - 弹窗表单在 horizontal 布局下初始宽度计算错误,导致出现提示和 下划虚线 ([#6639](https://github.com/nocobase/nocobase/pull/6639)) by @katherinehhh + +- **[文件存储:S3 (Pro)]** 修复next调用缺少await by @jiannx + +- **[邮件管理]** 修复next调用缺少await by @jiannx + +## [v1.6.17](https://github.com/nocobase/nocobase/compare/v1.6.16...v1.6.17) - 2025-04-09 + +### 🚀 优化 + +- **[utils]** 为 dayjs 包增加时长扩展 ([#6630](https://github.com/nocobase/nocobase/pull/6630)) by @mytharcher + +- **[client]** + - 支持筛选组件中对字段进行搜索 ([#6627](https://github.com/nocobase/nocobase/pull/6627)) by @mytharcher + + - 为 `Input` 和 `Variable.TextArea` 组件增加 `trim` API ([#6624](https://github.com/nocobase/nocobase/pull/6624)) by @mytharcher + +- **[错误处理器]** 在 AppError 组件中支持自定义标题。 ([#6409](https://github.com/nocobase/nocobase/pull/6409)) by @sheldon66 + +- **[IP 限制]** 更新 IP 限制消息内容。 by @sheldon66 + +- **[文件存储:S3 (Pro)]** 支持存储引擎的配置中使用全局变量 by @mytharcher + +### 🐛 修复 + +- **[client]** + - 联动规则条件设置为任意且无条件内容时属性设置不生效 ([#6628](https://github.com/nocobase/nocobase/pull/6628)) by @katherinehhh + + - 树表使用甘特图区块时数据显示异常 ([#6617](https://github.com/nocobase/nocobase/pull/6617)) by @katherinehhh + + - 筛选表单中的关系字段在刷新页面后,由于没有携带 x-data-source 而报错 ([#6619](https://github.com/nocobase/nocobase/pull/6619)) by @zhangzhonghe + + - 链接中中文参数变量值解析失败 ([#6618](https://github.com/nocobase/nocobase/pull/6618)) by @katherinehhh + +- **[用户]** 用户个人资料表单 schema 的解析问题 ([#6635](https://github.com/nocobase/nocobase/pull/6635)) by @2013xile + +- **[移动端]** 下拉单选字段在移动端设置筛选符为包含时组件未支持多选 ([#6629](https://github.com/nocobase/nocobase/pull/6629)) by @katherinehhh + +- **[操作:导出记录]** 筛选数据后切换分页再导出时筛选参数丢失 ([#6633](https://github.com/nocobase/nocobase/pull/6633)) by @katherinehhh + +- **[邮件管理]** 邮件管理权限无法查看邮件列表 by @jiannx + +- **[文件存储:S3 (Pro)]** 当用户上传 logo 失败时提示错误(设置为默认存储的 S3 Pro) by @mytharcher + +- **[工作流:审批]** 修复更新时间在迁移后变化 by @mytharcher + +- **[迁移管理]** 部分服务器环境下迁移日志创建日期显示不正确 by @gchust + +## [v1.6.16](https://github.com/nocobase/nocobase/compare/v1.6.15...v1.6.16) - 2025-04-03 + +### 🐛 修复 + +- **[client]** + - 表单字段设置不可编辑不起作用 ([#6610](https://github.com/nocobase/nocobase/pull/6610)) by @katherinehhh + + - 表单字段标题因冒号导致的截断问题 ([#6599](https://github.com/nocobase/nocobase/pull/6599)) by @katherinehhh + +- **[database]** 删除一对多记录时,同时传递 `filter` 和 `filterByTk` 参数,`filter` 包含关系字段时,`filterByTk` 参数失效 ([#6606](https://github.com/nocobase/nocobase/pull/6606)) by @2013xile + +## [v1.6.15](https://github.com/nocobase/nocobase/compare/v1.6.14...v1.6.15) - 2025-04-01 + +### 🚀 优化 + +- **[database]** + - 为多行文本类型字段增加去除首尾空白字符的选项 ([#6603](https://github.com/nocobase/nocobase/pull/6603)) by @mytharcher + + - 为单行文本增加自动去除首尾空白字符的选项 ([#6565](https://github.com/nocobase/nocobase/pull/6565)) by @mytharcher + +- **[文件管理器]** 为存储引擎表的文本字段增加去除首尾空白字符的选项 ([#6604](https://github.com/nocobase/nocobase/pull/6604)) by @mytharcher + +- **[工作流]** 优化代码 ([#6589](https://github.com/nocobase/nocobase/pull/6589)) by @mytharcher + +- **[工作流:审批]** 支持审批表单使用区块模板 by @mytharcher + +### 🐛 修复 + +- **[database]** 避免“日期时间(无时区)”字段在值未变动的更新时触发值改变 ([#6588](https://github.com/nocobase/nocobase/pull/6588)) by @mytharcher + +- **[client]** + - 关系字段(select)放出关系表字段时默认显示 N/A ([#6582](https://github.com/nocobase/nocobase/pull/6582)) by @katherinehhh + + - 修复 `SchemaInitializerItem` 配置了 `items` 时 `disabled` 属性无效的问题 ([#6597](https://github.com/nocobase/nocobase/pull/6597)) by @mytharcher + + - 级联组件删除后重新选择时出现 'The value of xxx cannot be in array format' ([#6585](https://github.com/nocobase/nocobase/pull/6585)) by @katherinehhh + +- **[数据表字段:多对多 (数组)]** 主表筛选带有多对多(数组)字段的关联表中的字段报错的问题 ([#6596](https://github.com/nocobase/nocobase/pull/6596)) by @2013xile + +- **[公开表单]** 查看权限包括 list 和 get ([#6607](https://github.com/nocobase/nocobase/pull/6607)) by @chenos + +- **[用户认证]** `AuthProvider` 中的 token 赋值 ([#6593](https://github.com/nocobase/nocobase/pull/6593)) by @2013xile + +- **[工作流]** 修复同步选项展示问题 ([#6595](https://github.com/nocobase/nocobase/pull/6595)) by @mytharcher + +- **[区块:地图]** 地图管理必填校验不应通过空格输入 ([#6575](https://github.com/nocobase/nocobase/pull/6575)) by @katherinehhh + +- **[工作流:审批]** + - 修复审批表单中的前端变量 by @mytharcher + + - 修复分支模式下配置拒绝则结束时的流程问题 by @mytharcher + +## [v1.6.14](https://github.com/nocobase/nocobase/compare/v1.6.13...v1.6.14) - 2025-03-29 + +### 🐛 修复 + +- **[日历]** 日历区块以周为视图时,边界日期不显示数据 ([#6587](https://github.com/nocobase/nocobase/pull/6587)) by @katherinehhh + +- **[认证:OIDC]** 回调路径是字符串'null'时导致跳转不正确 by @2013xile + +- **[工作流:审批]** 修复审批节点界面配置变更后数据未同步的问题 by @mytharcher + +## [v1.6.13](https://github.com/nocobase/nocobase/compare/v1.6.12...v1.6.13) - 2025-03-28 + +### 🚀 优化 + +- **[异步任务管理器]** 优化 Pro 导入导出按钮异步逻辑 ([#6531](https://github.com/nocobase/nocobase/pull/6531)) by @chenos + +- **[操作:导出记录 Pro]** 优化 Pro 导入导出按钮 by @katherinehhh + +- **[迁移管理]** 允许执行迁移时跳过自动备份还原 by @gchust + +### 🐛 修复 + +- **[client]** 同一表单中不同关系字段的同名关系字段的联动互相影响 ([#6577](https://github.com/nocobase/nocobase/pull/6577)) by @katherinehhh + +- **[操作:批量编辑]** 点击批量编辑按钮,配置完弹窗再打开,弹窗是空白的 ([#6578](https://github.com/nocobase/nocobase/pull/6578)) by @zhangzhonghe + +## [v1.6.12](https://github.com/nocobase/nocobase/compare/v1.6.11...v1.6.12) - 2025-03-27 + +### 🐛 修复 + +- **[区块:分步表单]** + - 提交按钮默认和高亮情况下颜色一样 by @jiannx + + - 修复当字段与其他表单字段存在关联时,表单重置无效 by @jiannx + +- **[工作流:审批]** 修复审批表单提交值的问题 by @mytharcher + +## [v1.6.11](https://github.com/nocobase/nocobase/compare/v1.6.10...v1.6.11) - 2025-03-27 + +### 🚀 优化 + +- **[client]** + - 优化 502 错误提示 ([#6547](https://github.com/nocobase/nocobase/pull/6547)) by @chenos + + - 仅支持纯文本文件预览 ([#6563](https://github.com/nocobase/nocobase/pull/6563)) by @mytharcher + +- **[数据表字段:自动编码]** 支持使用 sequence 作为日历区块的标题字段 ([#6562](https://github.com/nocobase/nocobase/pull/6562)) by @katherinehhh + +- **[工作流:审批]** 支持审批处理按钮跳过表单验证的设置 by @mytharcher + +### 🐛 修复 + +- **[client]** + - 数据范围中筛选日期字段显示异常 ([#6564](https://github.com/nocobase/nocobase/pull/6564)) by @katherinehhh + + - 选项“省略超出长度的内容”需要刷新页面,开关的状态才生效 ([#6520](https://github.com/nocobase/nocobase/pull/6520)) by @zhangzhonghe + + - 在弹窗中无法再次打开弹窗 ([#6535](https://github.com/nocobase/nocobase/pull/6535)) by @zhangzhonghe + +- **[API 文档]** API 文档页面不能滚动 ([#6566](https://github.com/nocobase/nocobase/pull/6566)) by @zhangzhonghe + +- **[工作流]** 确保创建工作流之前 key 已生成 ([#6567](https://github.com/nocobase/nocobase/pull/6567)) by @mytharcher + +- **[工作流:操作后事件]** 多行记录的批量操作需要触发多次 ([#6559](https://github.com/nocobase/nocobase/pull/6559)) by @mytharcher + +- **[用户认证]** 注册页面字段的本地化问题 ([#6556](https://github.com/nocobase/nocobase/pull/6556)) by @2013xile + +- **[公开表单]** 公开表单页面标题不应该显示 Loading... ([#6569](https://github.com/nocobase/nocobase/pull/6569)) by @katherinehhh + +## [v1.6.10](https://github.com/nocobase/nocobase/compare/v1.6.9...v1.6.10) - 2025-03-25 + +### 🐛 修复 + +- **[client]** + - 添加链接页面时,无法使用“当前用户”变量 ([#6536](https://github.com/nocobase/nocobase/pull/6536)) by @zhangzhonghe + + - 字段赋值对字段进行“空值”赋值无效 ([#6549](https://github.com/nocobase/nocobase/pull/6549)) by @katherinehhh + + - `yarn doc` 命令报错 ([#6540](https://github.com/nocobase/nocobase/pull/6540)) by @gchust + + - 筛选表单中,移除下拉单选字段的“允许多选”选项 ([#6515](https://github.com/nocobase/nocobase/pull/6515)) by @zhangzhonghe + + - 关系字段的数据范围联动不生效 ([#6530](https://github.com/nocobase/nocobase/pull/6530)) by @zhangzhonghe + +- **[数据表:树]** 树表插件的迁移脚本问题 ([#6537](https://github.com/nocobase/nocobase/pull/6537)) by @2013xile + +- **[操作:自定义请求]** 无法下载utf8编码的文件 ([#6541](https://github.com/nocobase/nocobase/pull/6541)) by @2013xile + +## [v1.6.9](https://github.com/nocobase/nocobase/compare/v1.6.8...v1.6.9) - 2025-03-23 + +### 🐛 修复 + +- **[client]** 操作按钮透明状态导致 hover 时按钮 setting 显示异常 ([#6529](https://github.com/nocobase/nocobase/pull/6529)) by @katherinehhh + +## [v1.6.8](https://github.com/nocobase/nocobase/compare/v1.6.7...v1.6.8) - 2025-03-22 + +### 🐛 修复 + +- **[server]** Upgrade 命令可能造成工作流报错 ([#6524](https://github.com/nocobase/nocobase/pull/6524)) by @gchust + +- **[client]** 表单中的子表格高度会随主表单高度一同设置 ([#6518](https://github.com/nocobase/nocobase/pull/6518)) by @katherinehhh + +- **[用户认证]** + - X-Authenticator 缺失 ([#6526](https://github.com/nocobase/nocobase/pull/6526)) by @chenos + + - 移除认证器配置项前后的空格、换行符 ([#6527](https://github.com/nocobase/nocobase/pull/6527)) by @2013xile + +- **[区块:地图]** 地图区块 密钥管理中不可见字符导致的密钥请求失败的问题 ([#6521](https://github.com/nocobase/nocobase/pull/6521)) by @katherinehhh + +- **[备份管理器]** 还原过程中可能引起工作流执行报错 by @gchust + +- **[企业微信]** 获取通知配置时需要解析环境变量和密钥 by @2013xile + +## [v1.6.7](https://github.com/nocobase/nocobase/compare/v1.6.6...v1.6.7) - 2025-03-20 + +### 🚀 优化 + +- **[工作流:邮件发送节点]** 增加安全字段配置描述。 ([#6510](https://github.com/nocobase/nocobase/pull/6510)) by @sheldon66 + +- **[通知:电子邮件]** 增加安全字段配置描述。 ([#6501](https://github.com/nocobase/nocobase/pull/6501)) by @sheldon66 + +- **[日历]** 日历插件添加开启或关闭快速创建事件可选设置 ([#6391](https://github.com/nocobase/nocobase/pull/6391)) by @Cyx649312038 + +### 🐛 修复 + +- **[client]** 时间字段在中文语言下提交时报错 invalid input syntax for type time ([#6511](https://github.com/nocobase/nocobase/pull/6511)) by @katherinehhh + +- **[文件管理器]** COS 存储的文件无法访问 ([#6512](https://github.com/nocobase/nocobase/pull/6512)) by @chenos + +- **[区块:地图]** 地图管理中密钥必填校验失败 ([#6509](https://github.com/nocobase/nocobase/pull/6509)) by @katherinehhh + +- **[WEB 客户端]** 路由管理表格中的路径与实际路径不一样 ([#6483](https://github.com/nocobase/nocobase/pull/6483)) by @zhangzhonghe + +- **[操作:导出记录 Pro]** 无法导出附件 by @chenos + +- **[工作流:审批]** + - 修复空用户造成页面崩溃 by @mytharcher + + - 修复审批人界面配置添加查询节点时的页面崩溃 by @mytharcher + ## [v1.6.6](https://github.com/nocobase/nocobase/compare/v1.6.5...v1.6.6) - 2025-03-18 ### 🎉 新特性 diff --git a/LICENSE.txt b/LICENSE.txt index b3c87c1e83..babf2bf053 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Updated Date: February 20, 2025 +Updated Date: April 1, 2025 NocoBase License Agreement @@ -88,7 +88,7 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 6.6 Can sell plugins developed for Software in the Marketplace. -6.7 The User with an Enterprise Edition License can sell Upper Layer Application to their clients. +6.7 The User with a Professional or Enterprise Edition License can sell Upper Layer Application to their clients. 6.8 Not restricted by the AGPL-3.0 agreement. @@ -106,9 +106,9 @@ Except for Third-Party Open Source Software, the Company owns all copyrights, tr 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 for the User withot an Enterprise Edition license to sell Upper Layer Application to clients without a Commercial license. +7.5 It is not allowed for the User withot a Professional or Enterprise Edition license to sell Upper Layer Application to clients without a Commercial license. -7.6 It is not allowed for the User with an Enterprise Edition license to sell Upper Layer Application to clients without a Commercial license with access to further development and configuration. +7.6 It is not allowed for the User with a Professional or Enterprise Edition license to sell Upper Layer Application to clients without a Commercial license with access to further development and configuration. 7.7 It is not allowed to publicly sell plugins developed for Software outside of the Marketplace. diff --git a/README.ja-JP.md b/README.ja-JP.md index 2fff33bddd..49e5c1be42 100644 --- a/README.ja-JP.md +++ b/README.ja-JP.md @@ -2,14 +2,10 @@ https://github.com/user-attachments/assets/cf08bfe5-e6e6-453c-8b96-350a6a8bed17 -## ご協力ありがとうございます! +

nocobase%2Fnocobase | Trendshift - NocoBase - Scalability-first, open-source no-code platform | Product Hunt - -## リリースノート - -リリースノートは[ブログ](https://www.nocobase.com/ja/blog/timeline)で随時更新され、週ごとにまとめて公開しています。 +

## NocoBaseはなに? @@ -28,6 +24,16 @@ https://docs-cn.nocobase.com/ コミュニティ: https://forum.nocobase.com/ +チュートリアル: +https://www.nocobase.com/ja/tutorials + +顧客のストーリー: +https://www.nocobase.com/ja/blog/tags/customer-stories + +## リリースノート + +リリースノートは[ブログ](https://www.nocobase.com/ja/blog/timeline)で随時更新され、週ごとにまとめて公開しています。 + ## 他の製品との違い ### 1. データモデル駆動 diff --git a/README.md b/README.md index 1314bec051..a2cfdb2bc4 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,14 @@ English | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md) https://github.com/user-attachments/assets/a50c100a-4561-4e06-b2d2-d48098659ec0 -## We'd love your support! - +

nocobase%2Fnocobase | Trendshift - NocoBase - Scalability-first, open-source no-code platform | Product Hunt - -## Release Notes - -Our [blog](https://www.nocobase.com/en/blog/timeline) is regularly updated with release notes and provides a weekly summary. +

## What is NocoBase -NocoBase is a scalability-first, open-source no-code development platform. +NocoBase is an extensibility-first, open-source no-code development platform. Instead of investing years of time and millions of dollars in research and development, deploy NocoBase in a few minutes and you'll have a private, controllable, and extremely scalable no-code development platform! Homepage: @@ -29,6 +24,17 @@ https://docs.nocobase.com/ Forum: https://forum.nocobase.com/ +Tutorials: +https://www.nocobase.com/en/tutorials + +Use Cases: +https://www.nocobase.com/en/blog/tags/customer-stories + + +## Release Notes + +Our [blog](https://www.nocobase.com/en/blog/timeline) is regularly updated with release notes and provides a weekly summary. + ## Distinctive features ### 1. Data model-driven diff --git a/README.zh-CN.md b/README.zh-CN.md index 66e9c281d7..728695efc7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -2,13 +2,10 @@ https://github.com/nocobase/nocobase/assets/1267426/29623e45-9a48-4598-bb9e-9dd173ade553 -## 感谢支持 +

nocobase%2Fnocobase | Trendshift - NocoBase - Scalability-first, open-source no-code platform | Product Hunt - -## 发布日志 -我们的[博客](https://www.nocobase.com/cn/blog/timeline)会及时更新发布日志,并每周进行汇总。 +

## NocoBase 是什么 @@ -27,6 +24,15 @@ https://docs-cn.nocobase.com/ 社区: https://forum.nocobase.com/ +教程: +https://www.nocobase.com/cn/tutorials + +用户故事: +https://www.nocobase.com/cn/blog/tags/customer-stories + +## 发布日志 +我们的[博客](https://www.nocobase.com/cn/blog/timeline)会及时更新发布日志,并每周进行汇总。 + ## 与众不同之处 ### 1. 数据模型驱动 diff --git a/lerna.json b/lerna.json index 9f7e584b89..1c020ae68f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.6.6", + "version": "1.6.20", "npmClient": "yarn", "useWorkspaces": true, "npmClientArgs": ["--ignore-engines"], diff --git a/packages/core/acl/package.json b/packages/core/acl/package.json index 14318a4306..fd51012a13 100644 --- a/packages/core/acl/package.json +++ b/packages/core/acl/package.json @@ -1,13 +1,13 @@ { "name": "@nocobase/acl", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/resourcer": "1.6.6", - "@nocobase/utils": "1.6.6", + "@nocobase/resourcer": "1.6.20", + "@nocobase/utils": "1.6.20", "minimatch": "^5.1.1" }, "repository": { diff --git a/packages/core/actions/package.json b/packages/core/actions/package.json index 76f69b1c68..bea716ae5b 100644 --- a/packages/core/actions/package.json +++ b/packages/core/actions/package.json @@ -1,14 +1,14 @@ { "name": "@nocobase/actions", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/cache": "1.6.6", - "@nocobase/database": "1.6.6", - "@nocobase/resourcer": "1.6.6" + "@nocobase/cache": "1.6.20", + "@nocobase/database": "1.6.20", + "@nocobase/resourcer": "1.6.20" }, "repository": { "type": "git", diff --git a/packages/core/app/package.json b/packages/core/app/package.json index 6be8b6ecf7..bc58bcd6d0 100644 --- a/packages/core/app/package.json +++ b/packages/core/app/package.json @@ -1,17 +1,17 @@ { "name": "@nocobase/app", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/database": "1.6.6", - "@nocobase/preset-nocobase": "1.6.6", - "@nocobase/server": "1.6.6" + "@nocobase/database": "1.6.20", + "@nocobase/preset-nocobase": "1.6.20", + "@nocobase/server": "1.6.20" }, "devDependencies": { - "@nocobase/client": "1.6.6" + "@nocobase/client": "1.6.20" }, "repository": { "type": "git", diff --git a/packages/core/app/src/__tests__/commands.test.ts b/packages/core/app/src/__tests__/commands.test.ts index f300d4cbda..946df74d9c 100644 --- a/packages/core/app/src/__tests__/commands.test.ts +++ b/packages/core/app/src/__tests__/commands.test.ts @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { mockDatabase } from '@nocobase/database'; +import { createMockDatabase, mockDatabase } from '@nocobase/database'; import { uid } from '@nocobase/utils'; import axios from 'axios'; import execa from 'execa'; @@ -64,7 +64,7 @@ const createDatabase = async () => { if (process.env.DB_DIALECT === 'sqlite') { return 'nocobase'; } - const db = mockDatabase(); + const db = await createMockDatabase(); const name = `d_${uid()}`; await db.sequelize.query(`CREATE DATABASE ${name}`); await db.close(); diff --git a/packages/core/auth/package.json b/packages/core/auth/package.json index 8ecfb5bec7..41029ecf8f 100644 --- a/packages/core/auth/package.json +++ b/packages/core/auth/package.json @@ -1,16 +1,16 @@ { "name": "@nocobase/auth", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", "types": "./lib/index.d.ts", "dependencies": { - "@nocobase/actions": "1.6.6", - "@nocobase/cache": "1.6.6", - "@nocobase/database": "1.6.6", - "@nocobase/resourcer": "1.6.6", - "@nocobase/utils": "1.6.6", + "@nocobase/actions": "1.6.20", + "@nocobase/cache": "1.6.20", + "@nocobase/database": "1.6.20", + "@nocobase/resourcer": "1.6.20", + "@nocobase/utils": "1.6.20", "@types/jsonwebtoken": "^8.5.8", "jsonwebtoken": "^8.5.1" }, diff --git a/packages/core/build/package.json b/packages/core/build/package.json index cc0bbe18a1..42a41bce84 100644 --- a/packages/core/build/package.json +++ b/packages/core/build/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/build", - "version": "1.6.6", + "version": "1.6.20", "description": "Library build tool based on rollup.", "main": "lib/index.js", "types": "./lib/index.d.ts", @@ -17,7 +17,7 @@ "@lerna/project": "4.0.0", "@rsbuild/plugin-babel": "^1.0.3", "@rsdoctor/rspack-plugin": "^0.4.8", - "@rspack/core": "1.1.1", + "@rspack/core": "1.3.2", "@svgr/webpack": "^8.1.0", "@types/gulp": "^4.0.13", "@types/lerna__package": "5.1.0", diff --git a/packages/core/build/src/buildPlugin.ts b/packages/core/build/src/buildPlugin.ts index f851f37ed8..9b7f228072 100644 --- a/packages/core/build/src/buildPlugin.ts +++ b/packages/core/build/src/buildPlugin.ts @@ -347,6 +347,7 @@ export async function buildPluginClient(cwd: string, userConfig: UserConfig, sou umdNamedDefine: true, }, }, + amd: {}, resolve: { tsConfig: path.join(process.cwd(), 'tsconfig.json'), extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.less', '.css'], diff --git a/packages/core/cache/package.json b/packages/core/cache/package.json index b3158707d4..9a62423999 100644 --- a/packages/core/cache/package.json +++ b/packages/core/cache/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cache", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./lib/index.js", diff --git a/packages/core/cli/package.json b/packages/core/cli/package.json index b0d32d7757..d4158c9467 100644 --- a/packages/core/cli/package.json +++ b/packages/core/cli/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/cli", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./src/index.js", @@ -8,7 +8,7 @@ "nocobase": "./bin/index.js" }, "dependencies": { - "@nocobase/app": "1.6.6", + "@nocobase/app": "1.6.20", "@types/fs-extra": "^11.0.1", "@umijs/utils": "3.5.20", "chalk": "^4.1.1", @@ -25,7 +25,7 @@ "tsx": "^4.19.0" }, "devDependencies": { - "@nocobase/devtools": "1.6.6" + "@nocobase/devtools": "1.6.20" }, "repository": { "type": "git", diff --git a/packages/core/cli/src/util.js b/packages/core/cli/src/util.js index f48dbd4c1d..1c760cd4ba 100644 --- a/packages/core/cli/src/util.js +++ b/packages/core/cli/src/util.js @@ -460,6 +460,16 @@ exports.initEnv = function initEnv() { process.env.SOCKET_PATH = generateGatewayPath(); fs.mkdirpSync(dirname(process.env.SOCKET_PATH), { force: true, recursive: true }); fs.mkdirpSync(process.env.PM2_HOME, { force: true, recursive: true }); + const pkgs = [ + '@nocobase/plugin-multi-app-manager', + '@nocobase/plugin-departments', + '@nocobase/plugin-field-attachment-url', + '@nocobase/plugin-workflow-response-message', + ]; + for (const pkg of pkgs) { + const pkgDir = resolve(process.cwd(), 'storage/plugins', pkg); + fs.existsSync(pkgDir) && fs.rmdirSync(pkgDir, { recursive: true, force: true }); + } }; exports.generatePlugins = function () { diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 79535b243b..38e0b87fe3 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/client", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "lib/index.js", "module": "es/index.mjs", @@ -27,9 +27,9 @@ "@formily/reactive-react": "^2.2.27", "@formily/shared": "^2.2.27", "@formily/validator": "^2.2.27", - "@nocobase/evaluators": "1.6.6", - "@nocobase/sdk": "1.6.6", - "@nocobase/utils": "1.6.6", + "@nocobase/evaluators": "1.6.20", + "@nocobase/sdk": "1.6.20", + "@nocobase/utils": "1.6.20", "ahooks": "^3.7.2", "antd": "5.12.8", "antd-style": "3.7.1", diff --git a/packages/core/client/src/acl/ACLProvider.tsx b/packages/core/client/src/acl/ACLProvider.tsx index 6dfa112361..09eaf964ad 100644 --- a/packages/core/client/src/acl/ACLProvider.tsx +++ b/packages/core/client/src/acl/ACLProvider.tsx @@ -74,6 +74,7 @@ export const ACLRolesCheckProvider = (props) => { url: 'roles:check', }, { + manual: !api.auth.token, onSuccess(data) { if (!data?.data?.snippets.includes('ui.*')) { setDesignable(false); diff --git a/packages/core/client/src/api-client/APIClient.ts b/packages/core/client/src/api-client/APIClient.ts index 1cbbcdacdd..f85c766e10 100644 --- a/packages/core/client/src/api-client/APIClient.ts +++ b/packages/core/client/src/api-client/APIClient.ts @@ -139,7 +139,19 @@ export class APIClient extends APIClientSDK { if (typeof error?.response?.data === 'string') { const tempElement = document.createElement('div'); tempElement.innerHTML = error?.response?.data; - return [{ message: tempElement.textContent || tempElement.innerText }]; + let message = tempElement.textContent || tempElement.innerText; + if (message.includes('Error occurred while trying')) { + message = 'The application may be starting up. Please try again later.'; + return [{ code: 'APP_WARNING', message }]; + } + if (message.includes('502 Bad Gateway')) { + message = 'The application may be starting up. Please try again later.'; + return [{ code: 'APP_WARNING', message }]; + } + return [{ message }]; + } + if (error?.response?.data?.error) { + return [error?.response?.data?.error]; } return ( error?.response?.data?.errors || diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index c441f1ae92..efe709fa21 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -350,23 +350,9 @@ export class Application { setTimeout(() => resolve(null), 1000); }); } - const toError = (error) => { - if (typeof error?.response?.data === 'string') { - const tempElement = document.createElement('div'); - tempElement.innerHTML = error?.response?.data; - return { message: tempElement.textContent || tempElement.innerText }; - } - 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', - ...toError(error), + ...this.apiClient.toErrMessages(error)?.[0], }; console.error(error, this.error); } diff --git a/packages/core/client/src/application/components/defaultComponents.tsx b/packages/core/client/src/application/components/defaultComponents.tsx index aeb2c475b9..31801e97ed 100644 --- a/packages/core/client/src/application/components/defaultComponents.tsx +++ b/packages/core/client/src/application/components/defaultComponents.tsx @@ -11,10 +11,11 @@ import React, { FC } from 'react'; import { MainComponent } from './MainComponent'; const Loading: FC = () =>
Loading...
; -const AppError: FC<{ error: Error }> = ({ error }) => { +const AppError: FC<{ error: Error & { title?: string } }> = ({ error }) => { + const title = error?.title || 'App Error'; return (
-
App Error
+
{title}
{error?.message} {process.env.__TEST__ && error?.stack}
diff --git a/packages/core/client/src/application/schema-initializer/components/SchemaInitializerItem.tsx b/packages/core/client/src/application/schema-initializer/components/SchemaInitializerItem.tsx index 436fc54f37..dfdedad636 100644 --- a/packages/core/client/src/application/schema-initializer/components/SchemaInitializerItem.tsx +++ b/packages/core/client/src/application/schema-initializer/components/SchemaInitializerItem.tsx @@ -63,7 +63,7 @@ export const SchemaInitializerItem = memo( className: className, label: children || compile(title), onClick: (info) => { - if (info.key !== name) return; + if (disabled || info.key !== name) return; if (closeInitializerMenuWhenClick) { setVisible?.(false); } @@ -73,10 +73,10 @@ export const SchemaInitializerItem = memo( children: childrenItems, }, ]; - }, [name, style, className, children, title, onClick, icon, childrenItems]); + }, [name, disabled, style, className, children, title, onClick, icon, childrenItems]); if (items && items.length > 0) { - return ; + return ; } return (
{ const filedSchema = useFieldSchema(); @@ -35,7 +35,7 @@ export const FilterFormBlockProvider = withDynamicSchemaProps((props) => { }} > false}> - + diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index e304a5ddaf..1b82601e71 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -167,7 +167,7 @@ export function useCollectValuesToSubmit() { if (parsedValue !== null && parsedValue !== undefined) { assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField }); } - } else if (value != null && value !== '') { + } else if (value !== '') { assignedValues[key] = value; } }); @@ -338,7 +338,7 @@ export const useAssociationCreateActionProps = () => { if (parsedValue) { assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField }); } - } else if (value != null && value !== '') { + } else if (value !== '') { assignedValues[key] = value; } }); @@ -522,9 +522,11 @@ export const useFilterBlockActionProps = () => { const { doFilter } = useDoFilter(); const actionField = useField(); actionField.data = actionField.data || {}; + const form = useForm(); return { async onClick() { + await form.submit(); actionField.data.loading = true; await doFilter(); actionField.data.loading = false; @@ -605,7 +607,7 @@ export const useCustomizeUpdateActionProps = () => { if (parsedValue) { assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField }); } - } else if (value != null && value !== '') { + } else if (value !== '') { assignedValues[key] = value; } }); @@ -708,7 +710,7 @@ export const useCustomizeBulkUpdateActionProps = () => { if (parsedValue) { assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField }); } - } else if (value != null && value !== '') { + } else if (value !== '') { assignedValues[key] = value; } }); @@ -930,7 +932,7 @@ export const useUpdateActionProps = () => { if (parsedValue) { assignedValues[key] = transformVariableValue(parsedValue, { targetCollectionField: collectionField }); } - } else if (value != null && value !== '') { + } else if (value !== '') { assignedValues[key] = value; } }); diff --git a/packages/core/client/src/collection-manager/interfaces/input.ts b/packages/core/client/src/collection-manager/interfaces/input.ts index ac5897adb7..40d131e5be 100644 --- a/packages/core/client/src/collection-manager/interfaces/input.ts +++ b/packages/core/client/src/collection-manager/interfaces/input.ts @@ -62,6 +62,12 @@ export class InputFieldInterface extends CollectionFieldInterface { hasDefaultValue = true; properties = { ...defaultProps, + trim: { + type: 'boolean', + 'x-content': '{{t("Automatically remove heading and tailing spaces")}}', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + }, layout: { type: 'void', title: '{{t("Index")}}', diff --git a/packages/core/client/src/collection-manager/interfaces/properties/operators.ts b/packages/core/client/src/collection-manager/interfaces/properties/operators.ts index ce0b6d411f..80b4e31527 100644 --- a/packages/core/client/src/collection-manager/interfaces/properties/operators.ts +++ b/packages/core/client/src/collection-manager/interfaces/properties/operators.ts @@ -129,12 +129,12 @@ export const enumType = [ label: '{{t("is")}}', value: '$eq', selected: true, - schema: { 'x-component': 'Select' }, + schema: { 'x-component': 'Select', 'x-component-props': { mode: null } }, }, { label: '{{t("is not")}}', value: '$ne', - schema: { 'x-component': 'Select' }, + schema: { 'x-component': 'Select', 'x-component-props': { mode: null } }, }, { label: '{{t("is any of")}}', diff --git a/packages/core/client/src/collection-manager/interfaces/textarea.ts b/packages/core/client/src/collection-manager/interfaces/textarea.ts index 2ccebbdeb6..19f48bc4c1 100644 --- a/packages/core/client/src/collection-manager/interfaces/textarea.ts +++ b/packages/core/client/src/collection-manager/interfaces/textarea.ts @@ -31,6 +31,12 @@ export class TextareaFieldInterface extends CollectionFieldInterface { titleUsable = true; properties = { ...defaultProps, + trim: { + type: 'boolean', + 'x-content': '{{t("Automatically remove heading and tailing spaces")}}', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + }, }; schemaInitialize(schema: ISchema, { block }) { if (['Table', 'Kanban'].includes(block)) { diff --git a/packages/core/client/src/common/SelectWithTitle.tsx b/packages/core/client/src/common/SelectWithTitle.tsx index 4bd43648a3..b3e0be59a8 100644 --- a/packages/core/client/src/common/SelectWithTitle.tsx +++ b/packages/core/client/src/common/SelectWithTitle.tsx @@ -18,7 +18,14 @@ export interface SelectWithTitleProps { onChange?: (...args: any[]) => void; } -export function SelectWithTitle({ title, defaultValue, onChange, options, fieldNames }: SelectWithTitleProps) { +export function SelectWithTitle({ + title, + defaultValue, + onChange, + options, + fieldNames, + ...others +}: SelectWithTitleProps) { const [open, setOpen] = useState(false); const timerRef = useRef(null); return ( @@ -36,6 +43,7 @@ export function SelectWithTitle({ title, defaultValue, onChange, options, fieldN > {title} { + if (!value) { + field.setValue([]); + return; + } + field.setValue( + value.map(({ label, value }: { label: string; value: string }) => ({ + id: value, + nickname: label, + })), + ); + }} + mode="multiple" + value={value} + labelInValue={true} + onDropdownVisibleChange={(open) => setVisible(open)} + /> + + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTable.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTable.tsx new file mode 100644 index 0000000000..e86ca90309 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTable.tsx @@ -0,0 +1,261 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import { + CollectionContext, + CollectionProvider_deprecated, + ResourceActionContext, + SchemaComponent, + mergeFilter, + removeNullCondition, + useFilterFieldOptions, + useFilterFieldProps, + useResourceActionContext, +} from '@nocobase/client'; +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { useDepartmentManager } from '../hooks'; +import { Table, TablePaginationConfig } from 'antd'; +import { departmentCollection } from '../collections/departments'; +import { useDepartmentTranslation } from '../locale'; +import { useField } from '@formily/react'; +import { Field } from '@formily/core'; +import { uid } from '@formily/shared'; +import { getDepartmentTitle } from '../utils'; + +const ExpandMetaContext = createContext({}); + +export const useFilterActionProps = () => { + const { setHasFilter, setExpandedKeys } = useContext(ExpandMetaContext); + const { t } = useDepartmentTranslation(); + const collection = useContext(CollectionContext); + const options = useFilterFieldOptions(collection.fields); + const service = useResourceActionContext(); + const { run, defaultRequest } = service; + const field = useField(); + const { params } = defaultRequest || {}; + + return { + options, + onSubmit: async (values: any) => { + // filter parameter for the block + const defaultFilter = params.filter; + // filter parameter for the filter action + const filter = removeNullCondition(values?.filter); + run({ + ...params, + page: 1, + pageSize: 10, + filter: mergeFilter([filter, defaultFilter]), + }); + const items = filter?.$and || filter?.$or; + if (items?.length) { + field.title = t('{{count}} filter items', { count: items?.length || 0 }); + setHasFilter(true); + } else { + field.title = t('Filter'); + setHasFilter(false); + } + }, + onReset() { + run({ + ...(params || {}), + filter: { + ...(params?.filter || {}), + parentId: null, + }, + page: 1, + pageSize: 10, + }); + field.title = t('Filter'); + setHasFilter(false); + setExpandedKeys([]); + }, + }; +}; + +const useDefaultDisabled = () => { + return { + disabled: () => false, + }; +}; + +const InternalDepartmentTable: React.FC<{ + useDisabled?: () => { + disabled: (record: any) => boolean; + }; +}> = ({ useDisabled = useDefaultDisabled }) => { + const { t } = useDepartmentTranslation(); + const ctx = useResourceActionContext(); + console.log(ctx); + const { run, data, loading, defaultRequest } = ctx; + const { resource, resourceOf, params } = defaultRequest || {}; + const { treeData, initData, loadData } = useDepartmentManager({ + resource, + resourceOf, + params, + }); + const field = useField(); + const { disabled } = useDisabled(); + const { hasFilter, expandedKeys, setExpandedKeys } = useContext(ExpandMetaContext); + + useEffect(() => { + if (hasFilter) { + return; + } + initData(data?.data); + }, [data, initData, loading, hasFilter]); + + const pagination: TablePaginationConfig = {}; + if (params?.pageSize) { + pagination.defaultPageSize = params.pageSize; + } + if (!pagination.total && data?.meta) { + const { count, page, pageSize } = data.meta; + pagination.total = count; + pagination.current = page; + pagination.pageSize = pageSize; + } + + return ( + (hasFilter ? getDepartmentTitle(record) : text), + }, + ]} + rowSelection={{ + selectedRowKeys: (field?.value || []).map((dept: any) => dept.id), + onChange: (keys, depts) => field?.setValue?.(depts), + getCheckboxProps: (record: any) => ({ + disabled: disabled(record), + }), + }} + pagination={{ + showSizeChanger: true, + ...pagination, + onChange(page, pageSize) { + run({ + ...(ctx?.params?.[0] || {}), + page, + pageSize, + }); + }, + }} + dataSource={hasFilter ? data?.data || [] : treeData} + expandable={{ + onExpand: (expanded, record) => { + loadData({ + key: record.id, + children: record.children, + }); + }, + expandedRowKeys: expandedKeys, + onExpandedRowsChange: (keys) => setExpandedKeys(keys), + }} + /> + ); +}; + +const RequestProvider: React.FC<{ + useDataSource: any; +}> = (props) => { + const [expandedKeys, setExpandedKeys] = useState([]); + const [hasFilter, setHasFilter] = useState(false); + const { useDataSource } = props; + const service = useDataSource({ + manual: true, + }); + useEffect(() => { + service.run({ + filter: { + parentId: null, + }, + pageSize: 10, + }); + }, []); + return ( + + + + {props.children} + + + + ); +}; + +export const DepartmentTable: React.FC<{ + useDataSource: any; + useDisabled?: (record: any) => boolean; +}> = ({ useDataSource, useDisabled }) => { + return ( + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTree.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTree.tsx new file mode 100644 index 0000000000..03ac4080b6 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTree.tsx @@ -0,0 +1,181 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import React, { useContext, useEffect } from 'react'; +import { Tree, Dropdown, App, Empty } from 'antd'; +import { MoreOutlined } from '@ant-design/icons'; +import { useAPIClient, useResourceActionContext } from '@nocobase/client'; +import { useDepartmentTranslation } from '../locale'; +import { editDepartmentSchema, newSubDepartmentSchema } from './schemas/departments'; +import { ResourcesContext } from '../ResourcesProvider'; +import { DepartmentTreeContext } from './Department'; +import { css } from '@emotion/css'; + +type DepartmentTreeProps = { + node: { + id: number; + title: string; + parent?: any; + }; + setVisible: (visible: boolean) => void; + setDrawer: (schema: any) => void; +}; + +export const DepartmentTree: React.FC & { + Item: React.FC; +} = () => { + const { data, loading } = useResourceActionContext(); + const { department, setDepartment, setUser } = useContext(ResourcesContext); + const { treeData, nodeMap, loadData, loadedKeys, setLoadedKeys, initData, expandedKeys, setExpandedKeys } = + useContext(DepartmentTreeContext); + const handleSelect = (keys: number[]) => { + if (!keys.length) { + return; + } + const node = nodeMap[keys[0]]; + setDepartment(node); + setUser(null); + }; + + const handleExpand = (keys: number[]) => { + setExpandedKeys(keys); + }; + + const handleLoad = (keys: number[]) => { + setLoadedKeys(keys); + }; + + useEffect(() => { + initData(data?.data); + }, [data, initData, loading]); + + useEffect(() => { + if (!department) { + return; + } + const getIds = (node: any) => { + if (node.parent) { + return [node.parent.id, ...getIds(node.parent)]; + } + return []; + }; + const newKeys = getIds(department); + setExpandedKeys((keys) => Array.from(new Set([...keys, ...newKeys]))); + }, [department, setExpandedKeys]); + + return ( +
+ {treeData?.length ? ( + + ) : ( + + )} +
+ ); +}; + +DepartmentTree.Item = function DepartmentTreeItem({ node, setVisible, setDrawer }: DepartmentTreeProps) { + const { t } = useDepartmentTranslation(); + const { refreshAsync } = useResourceActionContext(); + const { setLoadedKeys, expandedKeys, setExpandedKeys } = useContext(DepartmentTreeContext); + const { modal, message } = App.useApp(); + const api = useAPIClient(); + const deleteDepartment = () => { + modal.confirm({ + title: t('Delete'), + content: t('Are you sure you want to delete it?'), + onOk: async () => { + await api.resource('departments').destroy({ filterByTk: node.id }); + message.success(t('Deleted successfully')); + setExpandedKeys((keys) => keys.filter((k) => k !== node.id)); + const expanded = [...expandedKeys]; + setLoadedKeys([]); + setExpandedKeys([]); + await refreshAsync(); + setExpandedKeys(expanded); + }, + }); + }; + const openDrawer = (schema: any) => { + setDrawer({ schema, node }); + setVisible(true); + }; + const handleClick = ({ key, domEvent }) => { + domEvent.stopPropagation(); + switch (key) { + case 'new-sub': + openDrawer(newSubDepartmentSchema); + break; + case 'edit': + openDrawer(editDepartmentSchema); + break; + case 'delete': + deleteDepartment(); + } + }; + return ( +
+
{node.title}
+ +
+ +
+
+
+ ); +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTreeSelect.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTreeSelect.tsx new file mode 100644 index 0000000000..b66b511548 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/DepartmentTreeSelect.tsx @@ -0,0 +1,136 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import React, { useCallback, useContext, useEffect } from 'react'; +import { TreeSelect } from 'antd'; +import { useField } from '@formily/react'; +import { Field } from '@formily/core'; +import { useRecord } from '@nocobase/client'; +import { ResourcesContext } from '../ResourcesProvider'; +import { useDepartmentManager } from '../hooks/departments-manager'; + +export const DepartmentTreeSelect: React.FC<{ + originData: any; + treeData: any[]; + [key: string]: any; +}> = (props) => { + const field = useField(); + const [value, setValue] = React.useState({ label: null, value: null }); + const { treeData, initData, getByKeyword, loadData, loadedKeys, setLoadedKeys, originData } = props; + + const handleSearch = async (keyword: string) => { + if (!keyword) { + initData(originData); + return; + } + await getByKeyword(keyword); + }; + + const getTitle = useCallback((record: any) => { + const title = record.title; + const parent = record.parent; + if (parent) { + return getTitle(parent) + ' / ' + title; + } + return title; + }, []); + + useEffect(() => { + initData(originData); + }, [originData, initData]); + + useEffect(() => { + if (!field.value) { + setValue({ label: null, value: null }); + return; + } + setValue({ + label: getTitle(field.value) || field.value.label, + value: field.value.id, + }); + }, [field.value, getTitle]); + + return ( + { + field.setValue(node); + }} + onChange={(value: any) => { + if (!value) { + field.setValue(null); + } + }} + treeData={treeData} + treeLoadedKeys={loadedKeys} + onTreeLoad={(keys: any[]) => setLoadedKeys(keys)} + loadData={(node: any) => loadData({ key: node.id, children: node.children })} + fieldNames={{ + value: 'id', + }} + showSearch + allowClear + treeNodeFilterProp="title" + onSearch={handleSearch} + labelInValue={true} + /> + ); +}; + +export const DepartmentSelect: React.FC = () => { + const departmentManager = useDepartmentManager(); + const { departmentsResource } = useContext(ResourcesContext); + const { + service: { data }, + } = departmentsResource || {}; + return ; +}; + +export const SuperiorDepartmentSelect: React.FC = () => { + const departmentManager = useDepartmentManager(); + const { setTreeData, getChildrenIds } = departmentManager; + const record = useRecord() as any; + const { departmentsResource } = useContext(ResourcesContext); + const { + service: { data }, + } = departmentsResource || {}; + + useEffect(() => { + if (!record.id) { + return; + } + const childrenIds = getChildrenIds(record.id); + childrenIds.push(record.id); + setTreeData((treeData) => { + const setDisabled = (treeData: any[]) => { + return treeData.map((node) => { + if (childrenIds.includes(node.id)) { + node.disabled = true; + } + if (node.children) { + node.children = setDisabled(node.children); + } + return node; + }); + }; + return setDisabled(treeData); + }); + }, [setTreeData, record.id, getChildrenIds]); + + return ; +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/IsOwnerField.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/IsOwnerField.tsx new file mode 100644 index 0000000000..e685d2d89a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/IsOwnerField.tsx @@ -0,0 +1,30 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import React, { useContext } from 'react'; +import { useDepartmentTranslation } from '../locale'; +import { Checkbox, useRecord } from '@nocobase/client'; +import { ResourcesContext } from '../ResourcesProvider'; + +export const IsOwnerField: React.FC = () => { + const { department } = useContext(ResourcesContext); + const record = useRecord() as any; + const dept = (record.departments || []).find((dept: any) => dept?.id === department?.id); + + return ; +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/Member.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/Member.tsx new file mode 100644 index 0000000000..a23aa2bde8 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/Member.tsx @@ -0,0 +1,235 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import React, { useContext, useRef, useEffect, useMemo } from 'react'; +import { useDepartmentTranslation } from '../locale'; +import { + CollectionContext, + ResourceActionProvider, + SchemaComponent, + useAPIClient, + useActionContext, + useFilterFieldOptions, + useFilterFieldProps, + useRecord, + useResourceActionContext, + useTableBlockContext, +} from '@nocobase/client'; +import { membersActionSchema, addMembersSchema, rowRemoveActionSchema, getMembersSchema } from './schemas/users'; +import { App } from 'antd'; +import { DepartmentField } from './DepartmentField'; +import { IsOwnerField } from './IsOwnerField'; +import { UserDepartmentsField } from './UserDepartmentsField'; +import { ResourcesContext } from '../ResourcesProvider'; +import { useTableBlockProps } from '../hooks/useTableBlockProps'; + +const AddMembersListProvider: React.FC = (props) => { + const { department } = useContext(ResourcesContext); + return ( + + {props.children} + + ); +}; + +const useAddMembersFilterActionProps = () => { + const collection = useContext(CollectionContext); + const options = useFilterFieldOptions(collection.fields); + const service = useResourceActionContext(); + return useFilterFieldProps({ + options, + params: service.state?.params?.[0] || service.params, + service, + }); +}; + +export const AddMembers: React.FC = () => { + const { department } = useContext(ResourcesContext); + // This resource is the list of members of the current department. + const { + service: { refresh }, + } = useTableBlockContext(); + const selectedKeys = useRef([]); + const api = useAPIClient(); + + const useAddMembersActionProps = () => { + const { department } = useContext(ResourcesContext); + const { setVisible } = useActionContext(); + return { + async onClick() { + const selected = selectedKeys.current; + if (!selected?.length) { + return; + } + await api.resource('departments.members', department.id).add({ + values: selected, + }); + selectedKeys.current = []; + refresh(); + setVisible?.(false); + }, + }; + }; + + const handleSelect = (keys: any[]) => { + selectedKeys.current = keys; + }; + + return ( + + ); +}; + +const useBulkRemoveMembersAction = () => { + const { t } = useDepartmentTranslation(); + const { message } = App.useApp(); + const api = useAPIClient(); + const { + service: { refresh }, + field, + } = useTableBlockContext(); + const { department } = useContext(ResourcesContext); + return { + async run() { + const selected = field?.data?.selectedRowKeys; + if (!selected?.length) { + message.warning(t('Please select members')); + return; + } + await api.resource('departments.members', department.id).remove({ + values: selected, + }); + field.data.selectedRowKeys = []; + refresh(); + }, + }; +}; + +const useRemoveMemberAction = () => { + const api = useAPIClient(); + const { department } = useContext(ResourcesContext); + const { id } = useRecord() as any; + const { + service: { refresh }, + } = useTableBlockContext(); + return { + async run() { + await api.resource('departments.members', department.id).remove({ + values: [id], + }); + refresh(); + }, + }; +}; + +const useShowTotal = () => { + const { + service: { data }, + } = useTableBlockContext(); + const { t } = useDepartmentTranslation(); + return t('Total {{count}} members', { count: data?.meta?.count }); +}; + +const useRefreshActionProps = () => { + const { service } = useTableBlockContext(); + return { + async onClick() { + service?.refresh?.(); + }, + }; +}; + +const RowRemoveAction = () => { + const { department } = useContext(ResourcesContext); + return department ? : null; +}; + +const MemberActions = () => { + const { department } = useContext(ResourcesContext); + return department ? : null; +}; + +const useMemberFilterActionProps = () => { + const collection = useContext(CollectionContext); + const options = useFilterFieldOptions(collection.fields); + const { service } = useTableBlockContext(); + return useFilterFieldProps({ + options, + params: service.state?.params?.[0] || service.params, + service, + }); +}; + +export const Member: React.FC = () => { + const { t } = useDepartmentTranslation(); + const { department, user } = useContext(ResourcesContext); + const { + service: { data, setState }, + } = useTableBlockContext(); + + useEffect(() => { + setState?.({ selectedRowKeys: [] }); + }, [data, setState]); + + const schema = useMemo(() => getMembersSchema(department, user), [department, user]); + + return ( + <> + {!user ?

{t(department?.title || 'All users')}

:

{t('Search results')}

} + + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/NewDepartment.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/NewDepartment.tsx new file mode 100644 index 0000000000..08f4e78a9f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/NewDepartment.tsx @@ -0,0 +1,97 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import { SchemaComponent } from '@nocobase/client'; +import React from 'react'; +import { useDepartmentTranslation } from '../locale'; + +export const NewDepartment: React.FC = () => { + const { t } = useDepartmentTranslation(); + return ( + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-departments/src/client/departments/UserDepartmentsField.tsx b/packages/plugins/@nocobase/plugin-departments/src/client/departments/UserDepartmentsField.tsx new file mode 100644 index 0000000000..e72869046a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-departments/src/client/departments/UserDepartmentsField.tsx @@ -0,0 +1,273 @@ +/** + * 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. + */ + +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This program is offered under a commercial license. + * For more information, see + */ + +import { + ActionContextProvider, + SchemaComponent, + useAPIClient, + useRecord, + useRequest, + useResourceActionContext, + useTableBlockContext, +} from '@nocobase/client'; +import React, { useState } from 'react'; +import { Tag, Button, Dropdown, App } from 'antd'; +import { PlusOutlined, MoreOutlined } from '@ant-design/icons'; +import { Field } from '@formily/core'; +import { useField, useForm } from '@formily/react'; +import { userDepartmentsSchema } from './schemas/users'; +import { getDepartmentTitle } from '../utils'; +import { useDepartmentTranslation } from '../locale'; +import { DepartmentTable } from './DepartmentTable'; + +const useDataSource = (options?: any) => { + const defaultRequest = { + resource: 'departments', + action: 'list', + params: { + appends: ['parent(recursively=true)'], + // filter: { + // parentId: null, + // }, + sort: ['createdAt'], + }, + }; + const service = useRequest(defaultRequest, options); + return { + ...service, + defaultRequest, + }; +}; + +export const UserDepartmentsField: React.FC = () => { + const { modal, message } = App.useApp(); + const { t } = useDepartmentTranslation(); + const [visible, setVisible] = useState(false); + const user = useRecord() as any; + const field = useField(); + const { + service: { refresh }, + } = useTableBlockContext(); + + const formatData = (data: any[]) => { + if (!data?.length) { + return []; + } + + return data.map((department) => ({ + ...department, + isMain: department.departmentsUsers?.isMain, + isOwner: department.departmentsUsers?.isOwner, + title: getDepartmentTitle(department), + })); + }; + + const api = useAPIClient(); + useRequest( + () => + api + .resource(`users.departments`, user.id) + .list({ + appends: ['parent(recursively=true)'], + paginate: false, + }) + .then((res) => { + const data = formatData(res?.data?.data); + field.setValue(data); + }), + { + ready: user.id, + }, + ); + + const useAddDepartments = () => { + const api = useAPIClient(); + const drawerForm = useForm(); + const { departments } = drawerForm.values || {}; + return { + async run() { + await api.resource('users.departments', user.id).add({ + values: departments.map((dept: any) => dept.id), + }); + drawerForm.reset(); + field.setValue([ + ...field.value, + ...departments.map((dept: any, index: number) => ({ + ...dept, + isMain: index === 0 && field.value.length === 0, + title: getDepartmentTitle(dept), + })), + ]); + setVisible(false); + refresh(); + }, + }; + }; + + const removeDepartment = (dept: any) => { + modal.confirm({ + title: t('Remove department'), + content: t('Are you sure you want to remove it?'), + onOk: async () => { + await api.resource('users.departments', user.id).remove({ values: [dept.id] }); + message.success(t('Deleted successfully')); + field.setValue( + field.value + .filter((d: any) => d.id !== dept.id) + .map((d: any, index: number) => ({ + ...d, + isMain: (dept.isMain && index === 0) || d.isMain, + })), + ); + refresh(); + }, + }); + }; + + const setMainDepartment = async (dept: any) => { + await api.resource('users').setMainDepartment({ + values: { + userId: user.id, + departmentId: dept.id, + }, + }); + message.success(t('Set successfully')); + field.setValue( + field.value.map((d: any) => ({ + ...d, + isMain: d.id === dept.id, + })), + ); + refresh(); + }; + + const setOwner = async (dept: any) => { + await api.resource('departments').setOwner({ + values: { + userId: user.id, + departmentId: dept.id, + }, + }); + message.success(t('Set successfully')); + field.setValue( + field.value.map((d: any) => ({ + ...d, + isOwner: d.id === dept.id ? true : d.isOwner, + })), + ); + refresh(); + }; + + const removeOwner = async (dept: any) => { + await api.resource('departments').removeOwner({ + values: { + userId: user.id, + departmentId: dept.id, + }, + }); + message.success(t('Set successfully')); + field.setValue( + field.value.map((d: any) => ({ + ...d, + isOwner: d.id === dept.id ? false : d.isOwner, + })), + ); + refresh(); + }; + + const handleClick = (key: string, dept: any) => { + switch (key) { + case 'setMain': + setMainDepartment(dept); + break; + case 'setOwner': + setOwner(dept); + break; + case 'removeOwner': + removeOwner(dept); + break; + case 'remove': + removeDepartment(dept); + } + }; + + const useDisabled = () => ({ + disabled: (record: any) => { + return field.value.some((dept: any) => dept.id === record.id); + }, + }); + + return ( + + <> + {(field?.value || []).map((dept) => ( + + {dept.title} + {dept.isMain ? ( + + {t('Main')} + + ) : ( + '' + )} + {/* {dept.isOwner ? ( */} + {/* */} + {/* {t('Owner')} */} + {/* */} + {/* ) : ( */} + {/* '' */} + {/* )} */} + handleClick(key, dept), + }} + > +
+ +
+
+
+ ))} + diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/initializer.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/initializer.tsx index 8026ff27ab..af89b5eb2b 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/initializer.tsx +++ b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/initializer.tsx @@ -15,9 +15,6 @@ export const mobileAddBlockInitializer = new SchemaInitializer({ name: 'mobile:addBlock', icon: 'PlusOutlined', wrap: gridRowColWrap, - style: { - margin: 20, - }, items: [ { name: 'dataBlocks', diff --git a/packages/plugins/@nocobase/plugin-mock-collections/package.json b/packages/plugins/@nocobase/plugin-mock-collections/package.json index edc114e691..8004cb3e8e 100644 --- a/packages/plugins/@nocobase/plugin-mock-collections/package.json +++ b/packages/plugins/@nocobase/plugin-mock-collections/package.json @@ -2,7 +2,7 @@ "name": "@nocobase/plugin-mock-collections", "displayName": "mock-collections", "description": "mock-collections", - "version": "1.6.6", + "version": "1.6.20", "main": "./dist/server/index.js", "license": "AGPL-3.0", "peerDependencies": { diff --git a/packages/plugins/@nocobase/plugin-multi-app-manager/package.json b/packages/plugins/@nocobase/plugin-multi-app-manager/package.json index 83a4b81292..eef94c1d18 100644 --- a/packages/plugins/@nocobase/plugin-multi-app-manager/package.json +++ b/packages/plugins/@nocobase/plugin-multi-app-manager/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "多应用管理器", "description": "Dynamically create multiple apps without separate deployments.", "description.zh-CN": "无需单独部署即可动态创建多个应用。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/multi-app-manager", diff --git a/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json b/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json index 1065babfb9..668c50a0bd 100644 --- a/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json +++ b/packages/plugins/@nocobase/plugin-multi-app-share-collection/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "多应用数据表共享", "description": "", "description.zh-CN": "", - "version": "1.6.6", + "version": "1.6.20", "main": "./dist/server/index.js", "devDependencies": { "@formily/react": "2.x", diff --git a/packages/plugins/@nocobase/plugin-notification-email/package.json b/packages/plugins/@nocobase/plugin-notification-email/package.json index 3449dd0296..680243a49c 100644 --- a/packages/plugins/@nocobase/plugin-notification-email/package.json +++ b/packages/plugins/@nocobase/plugin-notification-email/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-notification-email", - "version": "1.6.6", + "version": "1.6.20", "displayName": "Notification: Email", "displayName.zh-CN": "通知:电子邮件", "description": "Used for sending email notifications with built-in SMTP transport.", diff --git a/packages/plugins/@nocobase/plugin-notification-email/src/client/ConfigForm.tsx b/packages/plugins/@nocobase/plugin-notification-email/src/client/ConfigForm.tsx index f1c0a8587f..c4c6c0470c 100644 --- a/packages/plugins/@nocobase/plugin-notification-email/src/client/ConfigForm.tsx +++ b/packages/plugins/@nocobase/plugin-notification-email/src/client/ConfigForm.tsx @@ -42,7 +42,7 @@ export const ChannelConfigForm = () => { type: 'void', 'x-component': 'Grid.Col', 'x-component-props': { - width: 50, + width: 45, }, properties: { host: { @@ -62,7 +62,7 @@ export const ChannelConfigForm = () => { type: 'void', 'x-component': 'Grid.Col', 'x-component-props': { - width: 25, + width: 20, }, properties: { port: { @@ -92,7 +92,7 @@ export const ChannelConfigForm = () => { type: 'void', 'x-component': 'Grid.Col', 'x-component-props': { - width: 25, + width: 35, }, properties: { secure: { @@ -100,6 +100,8 @@ export const ChannelConfigForm = () => { title: '{{t("Secure")}}', 'x-decorator': 'FormItem', 'x-component': 'TextAreaWithGlobalScope', + description: + '{{t("In most cases, if using port 465, set it to true; otherwise, set it to false.")}}', 'x-component-props': { boolean: true, useTypedConstant: [['boolean', { style: { width: '100%' } }]], diff --git a/packages/plugins/@nocobase/plugin-notification-email/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-notification-email/src/locale/zh-CN.json index 6a74e27196..e29654a9eb 100644 --- a/packages/plugins/@nocobase/plugin-notification-email/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-notification-email/src/locale/zh-CN.json @@ -18,5 +18,6 @@ "SMTP server host": "SMTP 服务器主机", "Content type": "内容格式", "Plain text": "纯文本", - "Transport": "传输方式" + "Transport": "传输方式", + "In most cases, if using port 465, set it to true; otherwise, set it to false.": "通常情况下,如果使用端口 465 ,请设置为 true ;否则,请设置为 false 。" } diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/package.json b/packages/plugins/@nocobase/plugin-notification-in-app-message/package.json index 0461847ab9..1d17c649e2 100644 --- a/packages/plugins/@nocobase/plugin-notification-in-app-message/package.json +++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-notification-in-app-message", - "version": "1.6.6", + "version": "1.6.20", "displayName": "Notification: In-app message", "displayName.zh-CN": "通知:站内信", "description": "It supports users in receiving real-time message notifications within the NocoBase application.", diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/MessageList.tsx b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/MessageList.tsx index c114023d2b..874c2d51e7 100644 --- a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/MessageList.tsx +++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/MessageList.tsx @@ -7,28 +7,36 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React, { useState, useCallback } from 'react'; -import { observer } from '@formily/reactive-react'; import { Schema } from '@formily/react'; -import { Card, Descriptions, Button, Spin, Tag, ConfigProvider, Typography, Tooltip, theme } from 'antd'; +import { observer } from '@formily/reactive-react'; import { dayjs } from '@nocobase/utils/client'; +import { Button, Card, ConfigProvider, Descriptions, Spin, Tag, Tooltip, Typography, theme } from 'antd'; +import React, { useCallback, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useLocalTranslation } from '../../locale'; +import { useApp } from '@nocobase/client'; import { - selectedChannelNameObs, channelMapObs, fetchMessages, + inboxVisible, isFecthingMessageObs, + selectedChannelNameObs, selectedMessageListObs, showMsgLoadingMoreObs, updateMessage, - inboxVisible, } from '../observables'; -import { useApp } from '@nocobase/client'; + +function removeStringIfStartsWith(text: string, prefix: string): string { + if (text.startsWith(prefix)) { + return text.slice(prefix.length); + } + return text; +} const MessageList = observer(() => { const app = useApp(); + const basename = app.router.basename.replace(/\/+$/, ''); const { t } = useLocalTranslation(); const navigate = useNavigate(); const { token } = theme.useToken(); @@ -51,7 +59,7 @@ const MessageList = observer(() => { if (message.options?.url) { inboxVisible.value = false; const url = message.options.url; - if (url.startsWith('/')) navigate(url); + if (url.startsWith('/')) navigate(removeStringIfStartsWith(url, basename)); else { window.location.href = url; } diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/locale/it-IT.json b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/locale/it-IT.json index 8f0b8d40d1..c062312806 100644 --- a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/locale/it-IT.json +++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/locale/it-IT.json @@ -6,7 +6,7 @@ "No more": "Non c'è altro", "Loading failed,": "Caricamento fallito,", "please reload": "ricarica", - "Detail": "Dettaglio", + "Detail": "Dettagli", "Content": "Contenuto", "Datetime": "Data e ora", "Status": "Stato", @@ -14,11 +14,11 @@ "Read": "Letto", "Unread": "Non letto", "In-app message": "Messaggio in-app", - "Receivers": "Destinatari", - "Channel name": "Nome canale", + "Receivers": "Destinatari", "Message group name": "Nome gruppo messaggi", "Message title": "Titolo messaggio", "Message content": "Contenuto messaggio", + "detail URL": "dettagli URL", "Details page for desktop": "Pagina dettagli per desktop", "Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "Supporta due tipi di link: link interni e link esterni. Se si utilizza un link interno, il link inizia con \"/\", ad esempio, \"/admin\". Se si utilizza un link esterno, il link inizia con \"http\", ad esempio, \"https://example.com\".", "Mark as read": "Segna come letto", diff --git a/packages/plugins/@nocobase/plugin-notification-manager/package.json b/packages/plugins/@nocobase/plugin-notification-manager/package.json index a650ed14a5..32397f1558 100644 --- a/packages/plugins/@nocobase/plugin-notification-manager/package.json +++ b/packages/plugins/@nocobase/plugin-notification-manager/package.json @@ -4,7 +4,7 @@ "description": "Provides a unified management service that includes channel configuration, logging, and other features, supporting the configuration of various notification channels, including in-app message and email.", "displayName.zh-CN": "通知管理", "description.zh-CN": "提供统一的管理服务,涵盖渠道配置、日志记录等功能,支持多种通知渠道的配置,包括站内信和电子邮件等。", - "version": "1.6.6", + "version": "1.6.20", "homepage": "https://docs.nocobase.com/handbook/notification-manager", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/notification-manager", "main": "dist/server/index.js", diff --git a/packages/plugins/@nocobase/plugin-notification-manager/src/locale/it-IT.json b/packages/plugins/@nocobase/plugin-notification-manager/src/locale/it-IT.json index 326573c179..8810be949d 100644 --- a/packages/plugins/@nocobase/plugin-notification-manager/src/locale/it-IT.json +++ b/packages/plugins/@nocobase/plugin-notification-manager/src/locale/it-IT.json @@ -18,9 +18,9 @@ "Channel display name": "Nome visualizzato canale", "Configure": "Configura", "Add new": "Aggiungi nuovo", - "Trigger From": "Attivato da", + "Trigger from": "Attivato da", "Status": "Stato", - "Created At": "Creato il", + "Created at": "Creato il", "Delete record": "Elimina record", "Are you sure you want to delete it?": "Sei sicuro di volerlo eliminare?", "Deleted successfully!": "Eliminato con successo!", @@ -32,7 +32,7 @@ "Select user": "Seleziona utente", "No channel enabled yet": "Nessun canale ancora abilitato", "Success": "Successo", - "Fail": "Fallito", + "Failure": "Fallimento", "Reason": "Motivo", "Failed reason": "Motivo fallimento", "Log detail": "Dettaglio registro", diff --git a/packages/plugins/@nocobase/plugin-notifications/package.json b/packages/plugins/@nocobase/plugin-notifications/package.json index 95b955841b..462c7ddd51 100644 --- a/packages/plugins/@nocobase/plugin-notifications/package.json +++ b/packages/plugins/@nocobase/plugin-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-notifications", - "version": "1.6.6", + "version": "1.6.20", "description": "", "license": "AGPL-3.0", "main": "./dist/server/index.js", diff --git a/packages/plugins/@nocobase/plugin-public-forms/package.json b/packages/plugins/@nocobase/plugin-public-forms/package.json index bec95738a5..51f6b318a1 100644 --- a/packages/plugins/@nocobase/plugin-public-forms/package.json +++ b/packages/plugins/@nocobase/plugin-public-forms/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-public-forms", - "version": "1.6.6", + "version": "1.6.20", "main": "dist/server/index.js", "displayName": "Public forms", "displayName.zh-CN": "公开表单", diff --git a/packages/plugins/@nocobase/plugin-public-forms/src/client/components/AdminPublicFormPage.tsx b/packages/plugins/@nocobase/plugin-public-forms/src/client/components/AdminPublicFormPage.tsx index e0ef14cb01..b62b6e8b29 100644 --- a/packages/plugins/@nocobase/plugin-public-forms/src/client/components/AdminPublicFormPage.tsx +++ b/packages/plugins/@nocobase/plugin-public-forms/src/client/components/AdminPublicFormPage.tsx @@ -24,6 +24,7 @@ import { TextAreaWithGlobalScope, ApplicationContext, useGlobalVariable, + useCompile, } from '@nocobase/client'; import { Breadcrumb, @@ -71,6 +72,7 @@ export function AdminPublicFormPage() { const { t } = usePublicFormTranslation(); const { theme } = useGlobalTheme(); const apiClient = useAPIClient(); + const compile = useCompile(); const { token } = AntdTheme.useToken(); const app = useApp(); const environmentCtx = useGlobalVariable('$env'); @@ -145,12 +147,13 @@ export function AdminPublicFormPage() { }} > {t('Public forms', { ns: NAMESPACE })}, }, { - title: title, + title: compile(title), }, ]} /> diff --git a/packages/plugins/@nocobase/plugin-public-forms/src/client/components/PublicFormPage.tsx b/packages/plugins/@nocobase/plugin-public-forms/src/client/components/PublicFormPage.tsx index b3ad843f22..4973888367 100644 --- a/packages/plugins/@nocobase/plugin-public-forms/src/client/components/PublicFormPage.tsx +++ b/packages/plugins/@nocobase/plugin-public-forms/src/client/components/PublicFormPage.tsx @@ -28,6 +28,7 @@ import { useApp, useRequest, VariablesProvider, + useCompile, } from '@nocobase/client'; import { Input, Modal, Spin } from 'antd'; import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; @@ -35,7 +36,6 @@ import { isDesktop } from 'react-device-detect'; import { useParams } from 'react-router'; import { usePublicSubmitActionProps } from '../hooks'; import { UnEnabledFormPlaceholder, UnFoundFormPlaceholder } from './UnEnabledFormPlaceholder'; - import { Button as MobileButton, Dialog as MobileDialog } from 'antd-mobile'; import { MobileDateTimePicker } from './components/MobileDatePicker'; import { MobilePicker } from './components/MobilePicker'; @@ -83,6 +83,14 @@ function PublicAPIClientProvider({ children }) { return {children}; } +function useTitle(data) { + const compile = useCompile(); + useEffect(() => { + if (!data) return; + document.title = compile(data?.data?.title); + }, [data]); +} + export const PublicFormMessageContext = createContext({}); export const PageBackgroundColor = '#f5f5f5'; @@ -174,6 +182,7 @@ function InternalPublicForm() { ); const [pwd, setPwd] = useState(''); const ctx = useContext(SchemaComponentContext); + useTitle(data); // 设置的移动端 meta useEffect(() => { if (!isDesktop) { diff --git a/packages/plugins/@nocobase/plugin-public-forms/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-public-forms/src/server/plugin.ts index 6b04a5bac1..7a8c4ae98b 100644 --- a/packages/plugins/@nocobase/plugin-public-forms/src/server/plugin.ts +++ b/packages/plugins/@nocobase/plugin-public-forms/src/server/plugin.ts @@ -73,6 +73,7 @@ export class PluginPublicFormsServer extends Plugin { const keys = instance.collection.split(':'); const collectionName = keys.pop(); const dataSourceKey = keys.pop() || 'main'; + const title = instance.get('title'); const schema = await uiSchema.getJsonSchema(filterByTk); const { getAssociationAppends } = parseAssociationNames(dataSourceKey, collectionName, this.app, schema); const { appends } = getAssociationAppends(); @@ -94,6 +95,7 @@ export class PluginPublicFormsServer extends Plugin { }, ), schema, + title, }; } @@ -169,7 +171,7 @@ export class PluginPublicFormsServer extends Plugin { skip: true, }; } else if ( - (actionName === 'list' && ctx.PublicForm['targetCollections'].includes(resourceName)) || + (['list', 'get'].includes(actionName) && ctx.PublicForm['targetCollections'].includes(resourceName)) || (collection.options.template === 'file' && actionName === 'create') || (resourceName === 'storages' && actionName === 'getBasicInfo') || (resourceName === 'map-configuration' && actionName === 'get') diff --git a/packages/plugins/@nocobase/plugin-sample-hello/package.json b/packages/plugins/@nocobase/plugin-sample-hello/package.json index da131e7edf..7f06acda1c 100644 --- a/packages/plugins/@nocobase/plugin-sample-hello/package.json +++ b/packages/plugins/@nocobase/plugin-sample-hello/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-sample-hello", - "version": "1.6.6", + "version": "1.6.20", "main": "./dist/server/index.js", "displayName": "Hello", "displayName.zh-CN": "Hello", diff --git a/packages/plugins/@nocobase/plugin-snapshot-field/package.json b/packages/plugins/@nocobase/plugin-snapshot-field/package.json index f99807627f..735336ef3e 100644 --- a/packages/plugins/@nocobase/plugin-snapshot-field/package.json +++ b/packages/plugins/@nocobase/plugin-snapshot-field/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "数据表字段:关系快照", "description": "When adding a new record, create a snapshot for its relational record and save in the new record. The snapshot will not be updated when the relational record is updated.", "description.zh-CN": "在添加数据时,为它的关系数据创建快照,并保存在当前的数据中。关系数据更新时,快照不会更新。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/field-snapshot", diff --git a/packages/plugins/@nocobase/plugin-snapshot-field/src/locale/it-IT.json b/packages/plugins/@nocobase/plugin-snapshot-field/src/locale/it-IT.json index d8046c85b6..49e39c1495 100644 --- a/packages/plugins/@nocobase/plugin-snapshot-field/src/locale/it-IT.json +++ b/packages/plugins/@nocobase/plugin-snapshot-field/src/locale/it-IT.json @@ -2,7 +2,7 @@ "Detail": "Dettaglio", "Snapshot": "Snapshot", "Add block": "Aggiungi blocco", - "When adding a new record, create a snapshot for its relational record and save in the current record. The snapshot is not updated when the record is subsequently updated.": "Quando si aggiunge un nuovo record, crea uno snapshot del suo record relazionale e salvalo nel record corrente. Lo snapshot non viene aggiornato quando il record viene successivamente aggiornato.", + "When adding a new record, create a snapshot for its relational record and save in the new record. The snapshot will not be updated when the relational record is updated.": "Quando si aggiunge un nuovo record, crea uno snapshot del suo record relazionale e salvalo nel nuovo record. Quando il record relazionale viene aggiornato lo snapshot non verrà aggiornato .", "View record": "Visualizza record", "Allow linking to multiple records": "Consenti collegamento a più record", "The association field to snapshot": "Campo di associazione per lo snapshot", diff --git a/packages/plugins/@nocobase/plugin-system-settings/package.json b/packages/plugins/@nocobase/plugin-system-settings/package.json index 7f04122728..7d4a96653c 100644 --- a/packages/plugins/@nocobase/plugin-system-settings/package.json +++ b/packages/plugins/@nocobase/plugin-system-settings/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "系统设置", "description": "Used to adjust the system title, logo, language, etc.", "description.zh-CN": "用于调整系统的标题、LOGO、语言等。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/system-settings", diff --git a/packages/plugins/@nocobase/plugin-theme-editor/package.json b/packages/plugins/@nocobase/plugin-theme-editor/package.json index a49e502d25..e7cbc6924b 100644 --- a/packages/plugins/@nocobase/plugin-theme-editor/package.json +++ b/packages/plugins/@nocobase/plugin-theme-editor/package.json @@ -1,6 +1,6 @@ { "name": "@nocobase/plugin-theme-editor", - "version": "1.6.6", + "version": "1.6.20", "main": "dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/theme-editor", "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/theme-editor", diff --git a/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json b/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json index 30e77b4e05..2a0c0e1f14 100644 --- a/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json +++ b/packages/plugins/@nocobase/plugin-ui-schema-storage/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "UI schema 存储服务", "description": "Provides centralized UI schema storage service.", "description.zh-CN": "提供中心化的 UI schema 存储服务。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/ui-schema-storage", diff --git a/packages/plugins/@nocobase/plugin-user-data-sync/package.json b/packages/plugins/@nocobase/plugin-user-data-sync/package.json index bb047cdd72..4af7bfd597 100644 --- a/packages/plugins/@nocobase/plugin-user-data-sync/package.json +++ b/packages/plugins/@nocobase/plugin-user-data-sync/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "用户数据同步", "description": "Reigster and manage extensible user data synchronization sources, with HTTP API provided by default. Support for synchronizing data to resources such as users and departments.", "description.zh-CN": "注册和管理可扩展的用户数据同步来源,默认提供 HTTP API。支持向用户和部门等资源同步数据。", - "version": "1.6.6", + "version": "1.6.20", "main": "dist/server/index.js", "peerDependencies": { "@nocobase/client": "1.x", diff --git a/packages/plugins/@nocobase/plugin-users/package.json b/packages/plugins/@nocobase/plugin-users/package.json index 5c5cb5de73..38c3bc12db 100644 --- a/packages/plugins/@nocobase/plugin-users/package.json +++ b/packages/plugins/@nocobase/plugin-users/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "用户", "description": "Provides basic user model, as well as created by and updated by fields.", "description.zh-CN": "提供了基础的用户模型,以及创建人和最后更新人字段。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/users", diff --git a/packages/plugins/@nocobase/plugin-users/src/server/actions/users.ts b/packages/plugins/@nocobase/plugin-users/src/server/actions/users.ts index ae2ab602bc..a002aaa929 100644 --- a/packages/plugins/@nocobase/plugin-users/src/server/actions/users.ts +++ b/packages/plugins/@nocobase/plugin-users/src/server/actions/users.ts @@ -12,16 +12,39 @@ import { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage'; import _ from 'lodash'; import { namespace } from '..'; import { ValidationError, ValidationErrorItem } from 'sequelize'; -import PluginSystemSettingsServer from '@nocobase/plugin-system-settings'; + +function findNonVoidTypeObjects(obj: any, path = '', results = []): { name: string; props: any }[] { + for (const key in obj) { + const value = obj[key]; + + const currentPath = path ? `${path}.${key}` : key; + + if (value && typeof value === 'object') { + if (value.type && value.type !== 'void') { + results.push({ + name: key, + props: value, + }); + } + + if (value.properties) { + findNonVoidTypeObjects(value.properties, `${currentPath}.properties`, results); + } else { + findNonVoidTypeObjects(value, currentPath, results); + } + } + } + + return results; +} function parseProfileFormSchema(schema: any) { const properties = _.get(schema, 'properties.form.properties.edit.properties.grid.properties') || {}; const fields = []; const requiredFields = []; - Object.values(properties).forEach((row: any) => { - const col = Object.values(row.properties)[0] as any; - const [name, props] = Object.entries(col.properties)[0]; - if (props['x-read-pretty'] || props['x-disable']) { + const configs = findNonVoidTypeObjects(properties); + Object.values(configs).forEach(({ name, props }) => { + if (props['x-read-pretty'] || props['x-disabled']) { return; } if (props['required']) { diff --git a/packages/plugins/@nocobase/plugin-verification/package.json b/packages/plugins/@nocobase/plugin-verification/package.json index 93f4f5d5aa..7dbd553d80 100644 --- a/packages/plugins/@nocobase/plugin-verification/package.json +++ b/packages/plugins/@nocobase/plugin-verification/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "验证码", "description": "verification setting.", "description.zh-CN": "验证码配置。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/verification", diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json b/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json index 0db458aef4..d9633d514a 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:操作后事件", "description": "Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or \"submit to workflow\". Suitable for data processing, sending notifications, etc., after actions are completed.", "description.zh-CN": "通过操作按钮或 API 发起请求并在执行完成后触发,比如新增、更新、删除数据或者“提交至工作流”之后。适用于在操作完成后进行数据处理、发送通知等。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/plugins/workflow-action-trigger", diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/locale/it-IT.json b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/locale/it-IT.json index fccf898cf0..e3e482f32b 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/locale/it-IT.json +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/locale/it-IT.json @@ -1,9 +1,17 @@ { - "Form event": "Evento modulo", - "Event triggers when submitted a workflow bound form action.": "L'evento si attiva quando viene inviata un'azione di un modulo associato a un workflow.", - "Form data model": "Modello dati modulo", - "Use a collection to match form data.": "Utilizza una raccolta per abbinare i dati del modulo.", + "Post-action event": "Evento post-azione", + "Triggered after the completion of a request initiated through an action button or API, such as after adding or updating data. Suitable for data processing, sending notifications, etc., after actions are completed.": + "Attivato dopo il completamento con esito positivo di una richiesta avviata tramite un pulsante di azione o un'API, ad esempio dopo l'aggiunta o l'aggiornamento di dati. Adatto per l'elaborazione dei dati, l'invio di notifiche, ecc. dopo il completamento delle azioni.", + "Collection": "Raccolta", + "The collection to which the triggered data belongs.": "La raccolta a cui appartengono i dati attivati.", + "Trigger mode": "Modalità di attivazione", + "Local mode, triggered after the completion of actions bound to this workflow": "Modalità locale, attivata dopo il completamento delle azioni associate a questo workflow", + "Global mode, triggered after the completion of the following actions": "Modalità globale, attivata dopo il completamento delle seguenti azioni", + "Select actions": "Seleziona azioni", + "Create record action": "Azione di creazione record", + "Update record action": "Azione di aggiornamento record", "Associations to use": "Associazioni da utilizzare", - "User submitted form": "Modulo inviato dall'utente", - "Role of user submitted form": "Ruolo del modulo inviato dall'utente" + "Trigger data": "Dati attivazione", + "User acted": "Utente che ha agito", + "Role of user acted": "Ruolo dell'utente che ha agito" } diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/ActionTrigger.ts b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/ActionTrigger.ts index df577cf496..3c970d84f5 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/ActionTrigger.ts +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/ActionTrigger.ts @@ -174,7 +174,7 @@ export default class extends Trigger { }); } } - event.push({ data: toJSON(payload), ...userInfo }); + (workflow.sync ? syncGroup : asyncGroup).push([workflow, { data: toJSON(payload), ...userInfo }]); } } else { const { filterTargetKey, repository } = (context.app).dataSourceManager.dataSources @@ -188,9 +188,8 @@ export default class extends Trigger { appends, }); } - event.push({ data, ...userInfo }); + (workflow.sync ? syncGroup : asyncGroup).push([workflow, { data, ...userInfo }]); } - (workflow.sync ? syncGroup : asyncGroup).push(event); } for (const event of syncGroup) { diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/__tests__/trigger.test.ts b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/__tests__/trigger.test.ts index 8654bf9b4c..d29d71facf 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/__tests__/trigger.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/server/__tests__/trigger.test.ts @@ -260,6 +260,40 @@ describe('workflow > action-trigger', () => { expect(e2.context.data).toHaveProperty('title', 't2'); expect(e2.context.data).toHaveProperty('createdBy'); }); + + it('bulk update', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'action', + config: { + collection: 'posts', + }, + }); + + const p1 = await PostRepo.create({ + values: { title: 't1' }, + }); + const p2 = await PostRepo.create({ + values: { title: 't2' }, + }); + + const res1 = await userAgents[0].resource('posts').update({ + filter: { + id: [p1.id, p2.id], + }, + values: { title: 't3' }, + triggerWorkflows: `${workflow.key}`, + }); + + await sleep(500); + + const e1s = await workflow.getExecutions(); + expect(e1s.length).toBe(2); + expect(e1s[0].status).toBe(EXECUTION_STATUS.RESOLVED); + expect(e1s[0].context.data).toHaveProperty('title', 't3'); + expect(e1s[1].status).toBe(EXECUTION_STATUS.RESOLVED); + expect(e1s[1].context.data).toHaveProperty('title', 't3'); + }); }); describe.skip('destroy', () => { diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json index 4929bef5a6..df8b17e6db 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:聚合查询节点", "description": "Used to aggregate data against the database in workflow, such as: statistics, sum, average, etc.", "description.zh-CN": "可用于在工作流中对数据库进行聚合查询,如:统计数量、求和、平均值等。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-aggregate", diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts index 492bfd181e..f3fbfecae2 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts @@ -351,7 +351,7 @@ test.describe('filter', () => { aggregateNodeCollectionData.reduce((total, currentValue) => { return currentValue.staffnum > 3 ? total + currentValue.staffnum : total; }, 0) / aggregateNodeCollectionDataCount; - expect(aggregateNodeJobResult).toBe(round(aggregateNodeCollectionDataAvg, 14)); + expect(aggregateNodeJobResult).toBe(round(aggregateNodeCollectionDataAvg, 2)); // 4、后置处理:删除工作流 await apiDeleteWorkflow(workflowId); }); diff --git a/packages/plugins/@nocobase/plugin-workflow-delay/package.json b/packages/plugins/@nocobase/plugin-workflow-delay/package.json index fe2ef5754c..c0bc7c4308 100644 --- a/packages/plugins/@nocobase/plugin-workflow-delay/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-delay/package.json @@ -4,7 +4,7 @@ "displayName.zh-CN": "工作流:延时节点", "description": "Could be used in workflow parallel branch for waiting other branches.", "description.zh-CN": "可用于工作流并行分支中等待其他分支执行完成。", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./dist/server/index.js", "homepage": "https://docs.nocobase.com/handbook/workflow-delay", diff --git a/packages/plugins/@nocobase/plugin-workflow-delay/src/client/DelayInstruction.tsx b/packages/plugins/@nocobase/plugin-workflow-delay/src/client/DelayInstruction.tsx index 81935fa347..245f1d6270 100644 --- a/packages/plugins/@nocobase/plugin-workflow-delay/src/client/DelayInstruction.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-delay/src/client/DelayInstruction.tsx @@ -8,11 +8,10 @@ */ import React from 'react'; -import { InputNumber, Select } from 'antd'; +import { Space } from 'antd'; import { HourglassOutlined } from '@ant-design/icons'; -import { css, useCompile } from '@nocobase/client'; -import { Instruction, JOB_STATUS } from '@nocobase/plugin-workflow/client'; +import { Instruction, JOB_STATUS, WorkflowVariableInput } from '@nocobase/plugin-workflow/client'; import { NAMESPACE } from '../locale'; @@ -24,47 +23,6 @@ const UnitOptions = [ { value: 604800_000, label: `{{t('Weeks', { ns: "workflow" })}}` }, ]; -function getNumberOption(v) { - return UnitOptions.slice() - .reverse() - .find((item) => !(v % item.value)); -} - -function Duration({ value = 60000, onChange }) { - const compile = useCompile(); - const option = getNumberOption(value); - const quantity = Math.round(value / option.value); - - return ( -
- onChange(Math.round(v * option.value))} - className="auto-width" - /> - + - {typeof typeValue === 'number' ? : null} + {typeof typeValue === 'number' ? ( + + ) : null} {typeValue === 'cron' ? ( onChange(`0 ${v}`)} clearButton={false} locale={window['cronLocale']} + disabled={disabled} /> ) : null}
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/locale/it-IT.json b/packages/plugins/@nocobase/plugin-workflow/src/locale/it-IT.json index b5f141b641..c714b7c4b2 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/locale/it-IT.json +++ b/packages/plugins/@nocobase/plugin-workflow/src/locale/it-IT.json @@ -53,13 +53,8 @@ "By custom date": "Per data personalizzata", "Advanced": "Avanzato", "End": "Fine", - "Node result": "Risultato nodo", - "Constant": "Costante", - "Null": "Null", - "Boolean": "Booleano", - "String": "Stringa", - "Operator": "Operatore", - "Arithmetic calculation": "Calcolo aritmetico", + "Node result": "Risultato nodo", + "Operator": "Operatore", "String operation": "Operazione stringa", "Executed at": "Eseguito alle", "Queueing": "In coda", @@ -73,28 +68,138 @@ "Collection operations": "Operazioni raccolta", "Extended types": "Tipi estesi", "Node type": "Tipo nodo", - "Calculation": "Calcolo", - "Configure calculation": "Configura calcolo", + "Calculation": "Calcolo", "Calculation result": "Risultato calcolo", "True": "Vero", "False": "Falso", "concat": "concatena", "Condition": "Condizione", "Mode": "Modalità", - "Continue when \"Yes\"": "Continua quando \"Yes\"", - "Branch into \"Yes\" and \"No\"": "Dirama in \"Yes\" e \"No\"", - "Conditions": "Condizioni", + "Continue when \"Yes\"": "Continua quando \"Sì\"", + "Branch into \"Yes\" and \"No\"": "Dirama in \"Sì\" e \"No\"", "Create record": "Crea record", "Update record": "Aggiorna record", "Query record": "Interroga record", "Multiple records": "Record multipli", "Please select collection first": "Seleziona prima la raccolta", - "Only update records matching conditions": "Aggiorna solo i record che corrispondono alle condizioni", - "Fields that are not assigned a value will be set to the default value, and those that do not have a default value are set to null.": "I campi a cui non è assegnato un valore verranno impostati sul valore predefinito, e quelli che non hanno un valore predefinito verranno impostati su null.", - "Trigger in executed workflow cannot be modified": "Il trigger nel workflow eseguito non può essere modificato", - "Node in executed workflow cannot be modified": "Il nodo nel workflow eseguito non può essere modificato", + "Only update records matching conditions": "Aggiorna solo i record che corrispondono alle condizioni", "Can not delete": "Impossibile eliminare", "The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting.": "Il risultato di questo nodo è stato referenziato da altri nodi ({{nodes}}), rimuovi l'utilizzo prima di eliminare.", - "Maximum number of loop calls": "Numero massimo di chiamate ciclo", - "If the number of loop calls is too large, there will be performance issues.": "Se il numero di chiamate ciclo è troppo grande, ci saranno problemi di prestazioni." + "Clear all executions": "Cancella tutte le esecuzioni", + "Clear executions will not reset executed count, and started executions will not be deleted, are you sure you want to delete them all?": "La cancellazione delle esecuzioni non reimposta il conteggio delle esecuzioni e le esecuzioni avviate non verranno eliminate. Sei sicuro di volerle eliminare tutte?", + "Sync": "Sincronizza", + "Sync enabled status of all workflows from database": "Sincronizza lo stato abilitato di tutti i workflow dal database", + "Duplicate to new workflow": "Duplica in un nuovo workflow", + "Delete a main version will cause all other revisions to be deleted too.": "L'eliminazione di una versione principale comporterà l'eliminazione anche di tutte le altre revisioni.", + "Execute manually": "Esegui manualmente", + "The trigger is not configured correctly, please check the trigger configuration.": "Il trigger non è configurato correttamente, controlla la configurazione del trigger.", + "This type of trigger has not been supported to be executed manually.": "Questo tipo di trigger non è supportato per l'esecuzione manuale.", + "Trigger variables need to be filled for executing.": "Le variabili del trigger devono essere compilate per l'esecuzione.", + "A new version will be created automatically after execution if current version is not executed.": "Se la versione corrente non è stata eseguita, verrà creata automaticamente una nuova versione dopo l'esecuzione.", + "This will perform all the actions configured in the workflow. Are you sure you want to continue?": "Verranno eseguite tutte le azioni configurate nel workflow. Sei sicuro di voler continuare?", + "Automatically create a new version after execution": "Crea automaticamente una nuova versione dopo l'esecuzione", + "Workflow executed, the result status is <1>{{statusText}}<2>View the execution": "Workflow eseguito, lo stato del risultato è <1>{{statusText}}<2>Visualizza l'esecuzione", + "Use transaction": "Usa transazione", + "Data operation nodes in workflow will run in a same transaction until any interruption. Any failure will cause data rollback, and will also rollback the history of the execution.": "I nodi di operazione dati nel workflow verranno eseguiti nella stessa transazione fino a qualsiasi interruzione. Qualsiasi errore causerà il rollback dei dati e ripristinerà anche la cronologia dell'esecuzione.", + "Auto delete history when execution is on end status": "Elimina automaticamente la cronologia quando l'esecuzione è in stato finale", + "Maximum number of cycling triggers": "Numero massimo di trigger ciclici", + "The triggers of same workflow by some node (create, update and sub-flow etc.) more than this number will be ignored. Large number may cause performance issues. Please use with caution.": "I trigger dello stesso workflow da un nodo (crea, aggiorna e sub-flow ecc.) superiori a questo numero verranno ignorati. Un numero elevato può causare problemi di prestazioni. Si prega di usare con cautela.", + "Unknown trigger": "Trigger sconosciuto", + "Workflow with unknown type will cause error. Please delete it or check plugin which provide this type.": "Un workflow con tipo sconosciuto causerà un errore. Si prega di eliminarlo o controllare il plugin che fornisce questo tipo.", + "Execute mode": "Modalità di esecuzione", + "Execute workflow asynchronously or synchronously based on trigger type, and could not be changed after created.": "Esegui il workflow in modo asincrono o sincrono in base al tipo di trigger e non può essere modificato dopo la creazione.", + "Asynchronously": "Asincrono", + "Synchronously": "Sincrono", + "Will be executed in the background as a queued task.": "Verrà eseguito in background come attività in coda.", + "For user actions that require immediate feedback. Can not use asynchronous nodes in such mode, and it is not recommended to perform time-consuming operations under synchronous mode.": "Per le azioni dell'utente che richiedono un feedback immediato. Non è possibile utilizzare nodi asincroni in questa modalità e non è consigliabile eseguire operazioni che richiedono tempo in modalità sincrona.", + "Go back": "Torna indietro", + "Bind workflows": "Collega workflow", + "Support pre-action event (local mode), post-action event (local mode), and approval event here.": "Qui sono supportati evento pre-azione (modalità locale), evento post-azione (modalità locale) e evento di approvazione.", + "Workflow will be triggered directly once the button clicked, without data saving. Only supports to be bound with \"Custom action event\".": "Il workflow verrà attivato direttamente una volta cliccato il pulsante, senza salvataggio dei dati. Supporta solo il collegamento con \"evento azione personalizzata\".", + "\"Submit to workflow\" to \"Post-action event\" is deprecated, please use \"Custom action event\" instead.": "\"Invia a workflow\" a \"evento post-azione\" è obsoleto, si prega di utilizzare invece \"evento azione personalizzata\".", + "Workflow will be triggered before deleting succeeded (only supports pre-action event in local mode).": "Il workflow verrà attivato prima dell'eliminazione riuscita (supporta solo l'evento pre-azione in modalità locale).", + "Submit to workflow": "Invia a workflow", + "Add workflow": "Aggiungi workflow", + "Select workflow": "Seleziona workflow", + "Trigger data context": "Contesto dati trigger", + "Full form data": "Dati completi del modulo", + "Select context": "Seleziona contesto", + "Triggered when data changes in the collection, such as after adding, updating, or deleting a record. Unlike \"Post-action event\", Collection event listens for data changes rather than HTTP requests. Unless you understand the exact meaning, it is recommended to use \"Post-action event\".": "Attivato quando i dati cambiano nella raccolta, ad esempio dopo l'aggiunta, l'aggiornamento o l'eliminazione di un record. A differenza dell' \"evento post-azione\", l'evento raccolta ascolta le modifiche dei dati anziché le richieste HTTP. A meno che tu non capisca il significato esatto, si consiglia di utilizzare \"Evento post-azione\".", + "Preload associations": "Precarica associazioni", + "Please select the associated fields that need to be accessed in subsequent nodes. With more than two levels of to-many associations may cause performance issue, please use with caution.": "Seleziona i campi associati a cui è necessario accedere nei nodi successivi. Con più di due livelli di associazioni molti-a-molti possono verificarsi problemi di prestazioni, si prega di usare con cautela.", + "Choose a record or primary key of a record in the collection to trigger.": "Scegli un record o la chiave primaria di un record nella raccolta per attivare.", + "Triggered according to preset time conditions. Suitable for one-time or periodic tasks, such as sending notifications and cleaning data on a schedule.": "Attivato secondo condizioni di tempo preimpostate. Adatto per attività una tantum o periodiche, come l'invio di notifiche e la pulizia dei dati in base a una pianificazione.", + "Execute on": "Esegui su", + "Current time": "Ora corrente", + "Variable key of node": "Chiave variabile del nodo", + "Scope variables": "Variabili di ambito", + "Calculate an expression based on a calculation engine and obtain a value as the result. Variables in the upstream nodes can be used in the expression.": "Calcola un'espressione basata su un motore di calcolo e ottieni un valore come risultato. Le variabili nei nodi upstream possono essere utilizzate nell'espressione.", + "System variables": "Variabili di sistema", + "System time": "Ora di sistema", + "Date variables": "Variabili data", + "Date range": "Intervallo date", + "Resolved": "Risolto", + "Error": "Errore", + "Aborted": "Interrotto", + "Rejected": "Rifiutato", + "Retry needed": "Richiesto nuovo tentativo", + "Completed": "Completato", + "All": "Tutto", + "View result": "Visualizza risultato", + "Triggered but still waiting in queue to execute.": "Attivato ma ancora in attesa in coda per l'esecuzione.", + "Started and executing, maybe waiting for an async callback (manual, delay etc.).": "Avviato ed in esecuzione, forse in attesa di un callback asincrono (manuale, ritardo ecc.).", + "Successfully finished.": "Terminato con successo.", + "Failed to satisfy node configurations.": "Fallito nel soddisfare le configurazioni del nodo.", + "Some node meets error.": "Qualche nodo presenta un errore.", + "Running of some node was aborted by program flow.": "L'esecuzione di qualche nodo è stata interrotta dal flusso del programma.", + "Manually canceled whole execution when waiting.": "L'intera esecuzione è stata cancellata manualmente durante l'attesa.", + "Rejected from a manual node.": "Rifiutato da un nodo manuale.", + "General failed but should do another try.": "Fallimento generico, ma dovrebbe essere fatto un altro tentativo.", + "Cancel the execution": "Annulla l'esecuzione", + "Are you sure you want to cancel the execution?": "Sei sicuro di voler annullare l'esecuzione?", + "Operations": "Operazioni", + "Manual": "Manuale", + "Unknown node": "Nodo sconosciuto", + "Node with unknown type will cause error. Please delete it or check plugin which provide this type.": "Un nodo con tipo sconosciuto causerà un errore. Si prega di eliminarlo o controllare il plugin che fornisce questo tipo.", + "Calculation engine": "Motore di calcolo", + "Basic": "Base", + "Calculation expression": "Espressione di calcolo", + "Expression syntax error": "Errore di sintassi dell'espressione", + "Syntax references: ": "Riferimenti di sintassi:", + "Based on boolean result of the calculation to determine whether to \"continue\" or \"exit\" the process, or continue on different branches of \"yes\" and \"no\".": "Basato sul risultato booleano del calcolo per determinare se \"continuare\" o \"uscire\" dal processo, o continuare su rami diversi di \"sì\" e \"no\".", + "Condition expression": "Espressione di condizione", + "Inside of \"Yes\" branch": "All'interno del ramo \"Sì\"", + "Inside of \"No\" branch": "All'interno del ramo \"No\"", + "Add new record to a collection. You can use variables from upstream nodes to assign values to fields.": "Aggiungi un nuovo record a una raccolta. Puoi usare variabili dai nodi upstream per assegnare valori ai campi.", + "Update records of a collection. You can use variables from upstream nodes as query conditions and field values.": "Aggiorna i record di una raccolta. Puoi usare variabili dai nodi upstream come condizioni di query e valori campo.", + "Update mode": "Modalità di aggiornamento", + "Update in a batch": "Aggiornamento in batch", + "Update one by one": "Aggiornamento uno per uno", + "Update all eligible data at one time, which has better performance when the amount of data is large. But association fields are not supported (unless foreign key in current collection), and the updated data will not trigger other workflows.": "Aggiorna tutti i dati idonei in una volta, il che ha prestazioni migliori quando la quantità di dati è grande. Ma i campi di associazione non sono supportati (a meno che la chiave esterna non sia nella raccolta corrente), e i dati aggiornati non attiveranno altri workflow.", + "The updated data can trigger other workflows, and the audit log will also be recorded. But it is usually only applicable to several or dozens of pieces of data, otherwise there will be performance problems.": "I dati aggiornati possono attivare altri workflow, e il registro eventi sarà anche registrato. Ma di solito è applicabile solo a diverse o decine di dati, altrimenti ci saranno problemi di prestazioni.", + "Query records from a collection. You can use variables from upstream nodes as query conditions.": "Interroga i record da una raccolta. Puoi usare variabili dai nodi upstream come condizioni di query.", + "Allow multiple records as result": "Consenti più record come risultato", + "If checked, when there are multiple records in the query result, an array will be returned as the result, which can be operated on one by one using a loop node. Otherwise, only one record will be returned.": "Se selezionato, quando ci sono più record nel risultato della query, sarà restituito come risultato un array, che può essere operato uno per uno usando un nodo ciclo. Altrimenti, sarà restituito solo un record.", + "Result type": "Tipo risultato", + "Single record": "Record singolo", + "The result will be an object of the first matching record only, or null if no matched record.": "Il risultato sarà un oggetto solo del primo record corrispondente, oppure null se non vi è alcun record corrispondente.", + "The result will be an array containing matched records, or an empty one if no matching records. This can be used to be processed in a loop node.": "Il risultato sarà un array contenente record corrispondenti, o uno vuoto se non ci sono record corrispondenti. Questo può essere usato per essere elaborato in un nodo ciclo.", + "Exit when query result is null": "Esci quando il risultato della query è null", + "Please add at least one condition": "Si prega di aggiungere almeno una condizione", + "Unassigned fields will be set to default values, and those without default values will be set to null.": "I campi non assegnati saranno impostati ai valori predefiniti, e quelli senza valori predefiniti saranno impostati a null.", + "Delete record": "Elimina record", + "Delete records of a collection. Could use variables in workflow context as filter. All records match the filter will be deleted.": "Elimina i record di una raccolta. Potrebbe usare variabili nel contesto del workflow come filtro. Tutti i record che corrispondono al filtro saranno eliminati.", + "Executed workflow cannot be modified. Could be copied to a new version to modify.": "Il workflow eseguito non può essere modificato. Potrebbe essere copiato in una nuova versione per essere modificato.", + "End process": "Termina processo", + "End the process immediately, with set status.": "Termina il processo immediatamente, con stato impostato.", + "End status": "Stato finale", + "Test run": "Test run", + "Test run will do the actual data manipulating or API calling, please use with caution.": "Il test run farà l'effettiva manipolazione dei dati o la chiamata API, si prega di usare con cautela.", + "No variable": "Nessuna variabile", + "Add node": "Aggiungi nodo", + "Move all downstream nodes to": "Sposta tutti i nodi downstream a", + "After end of branches": "Dopo la fine dei rami", + "Inside of branch": "All'interno del ramo", + "Workflow todos": "Da fare", + "New version enabled": "Nuova versione abilitata" } diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts index a9abb48bee..998ddc42a9 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/Plugin.ts @@ -15,7 +15,7 @@ import LRUCache from 'lru-cache'; import { Op } from '@nocobase/database'; import { Plugin } from '@nocobase/server'; -import { Registry } from '@nocobase/utils'; +import { Registry, uid } from '@nocobase/utils'; import { SequelizeCollectionManager } from '@nocobase/data-source-manager'; import { Logger, LoggerOptions } from '@nocobase/logger'; @@ -75,6 +75,10 @@ export default class PluginWorkflowServer extends Plugin { private onBeforeSave = async (instance: WorkflowModel, { transaction }) => { const Model = instance.constructor; + if (!instance.key) { + instance.set('key', uid()); + } + if (instance.enabled) { instance.set('current', true); } @@ -362,11 +366,16 @@ export default class PluginWorkflowServer extends Plugin { const prev = workflow.previous(); if (prev.config) { trigger.off({ ...workflow.get(), ...prev }); + this.getLogger(workflow.id).info(`toggle OFF workflow ${workflow.id} based on configuration before updated`); } trigger.on(workflow); + this.getLogger(workflow.id).info(`toggle ON workflow ${workflow.id}`); + this.enabledCache.set(workflow.id, workflow); } else { trigger.off(workflow); + this.getLogger(workflow.id).info(`toggle OFF workflow ${workflow.id}`); + this.enabledCache.delete(workflow.id); } if (!silent) { diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/triggers/collection.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/triggers/collection.test.ts index f67b7a84e4..cff13e5d64 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/triggers/collection.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/triggers/collection.test.ts @@ -359,6 +359,50 @@ describe('workflow > triggers > collection', () => { const executions = await workflow.getExecutions(); expect(executions.length).toBe(1); }); + + it('datetime field not changed', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + sync: true, + type: 'collection', + config: { + mode: 2, + collection: 'posts', + changed: ['createdAt'], + }, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + await PostRepo.update({ filterByTk: post.id, values: { ...post.get(), title: 't2' } }); + + const executions = await workflow.getExecutions(); + expect(executions.length).toBe(0); + }); + + it('datetimeNoTz field not changed', async () => { + db.getCollection('posts').addField('dateOnly', { + type: 'datetimeNoTz', + }); + + await db.sync(); + + const workflow = await WorkflowModel.create({ + enabled: true, + sync: true, + type: 'collection', + config: { + mode: 2, + collection: 'posts', + changed: ['dateOnly'], + }, + }); + + const post = await PostRepo.create({ values: { title: 't1', dateOnly: '2020-01-01 00:00:00' } }); + await PostRepo.update({ filterByTk: post.id, values: { ...post.get(), title: 't2' } }); + + const executions = await workflow.getExecutions(); + expect(executions.length).toBe(0); + }); }); describe('config.condition', () => { diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.ts index c720e13eca..5a62986a72 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.ts @@ -104,50 +104,54 @@ export default class DateFieldScheduleTrigger { // caching workflows in range, default to 5min cacheCycle = 300_000; + onAfterStart = () => { + if (this.timer) { + return; + } + + this.timer = setInterval(() => this.reload(), this.cacheCycle); + + this.reload(); + }; + + onBeforeStop = () => { + if (this.timer) { + clearInterval(this.timer); + } + + for (const [key, timer] of this.cache.entries()) { + clearTimeout(timer); + this.cache.delete(key); + } + }; + constructor(public workflow: Plugin) { - workflow.app.on('afterStart', async () => { - if (this.timer) { - return; - } - - this.timer = setInterval(() => this.reload(), this.cacheCycle); - - this.reload(); - }); - - workflow.app.on('beforeStop', () => { - if (this.timer) { - clearInterval(this.timer); - } - - for (const [key, timer] of this.cache.entries()) { - clearTimeout(timer); - this.cache.delete(key); - } - }); + workflow.app.on('afterStart', this.onAfterStart); + workflow.app.on('beforeStop', this.onBeforeStop); } - async reload() { + reload() { + for (const [key, timer] of this.cache.entries()) { + clearTimeout(timer); + this.cache.delete(key); + } + const workflows = Array.from(this.workflow.enabledCache.values()).filter( (item) => item.type === 'schedule' && item.config.mode === SCHEDULE_MODE.DATE_FIELD, ); - // NOTE: clear cached jobs in last cycle - this.cache = new Map(); - - this.inspect(workflows); + workflows.forEach((workflow) => { + this.inspect(workflow); + }); } - inspect(workflows: WorkflowModel[]) { + async inspect(workflow: WorkflowModel) { const now = new Date(); - - workflows.forEach(async (workflow) => { - const records = await this.loadRecordsToSchedule(workflow, now); - this.workflow.getLogger(workflow.id).info(`[Schedule on date field] ${records.length} records to schedule`); - records.forEach((record) => { - const nextTime = this.getRecordNextTime(workflow, record); - this.schedule(workflow, record, nextTime, Boolean(nextTime)); - }); + const records = await this.loadRecordsToSchedule(workflow, now); + this.workflow.getLogger(workflow.id).info(`[Schedule on date field] ${records.length} records to schedule`); + records.forEach((record) => { + const nextTime = this.getRecordNextTime(workflow, record); + this.schedule(workflow, record, nextTime, Boolean(nextTime)); }); } @@ -233,8 +237,6 @@ export default class DateFieldScheduleTrigger { [Op.gte]: new Date(endTimestamp), }, }); - } else { - this.workflow.getLogger(id).warn(`[Schedule on date field] "endsOn.field" is not configured`); } } } @@ -367,7 +369,7 @@ export default class DateFieldScheduleTrigger { } on(workflow: WorkflowModel) { - this.inspect([workflow]); + this.inspect(workflow); const { collection } = workflow.config; const [dataSourceName, collectionName] = parseCollectionName(collection); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/StaticScheduleTrigger.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/StaticScheduleTrigger.ts index 9552ceae22..988c4f545b 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/StaticScheduleTrigger.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/triggers/ScheduleTrigger/StaticScheduleTrigger.ts @@ -18,36 +18,39 @@ const MAX_SAFE_INTERVAL = 2147483647; export default class StaticScheduleTrigger { private timers: Map = new Map(); - constructor(public workflow: Plugin) { - workflow.app.on('afterStart', async () => { - const workflows = Array.from(this.workflow.enabledCache.values()).filter( - (item) => item.type === 'schedule' && item.config.mode === SCHEDULE_MODE.STATIC, - ); - - this.inspect(workflows); - }); - - workflow.app.on('beforeStop', () => { - for (const timer of this.timers.values()) { - clearInterval(timer); - } - }); - } - - inspect(workflows: WorkflowModel[]) { - const now = new Date(); + onAfterStart = () => { + const workflows = Array.from(this.workflow.enabledCache.values()).filter( + (item) => item.type === 'schedule' && item.config.mode === SCHEDULE_MODE.STATIC, + ); workflows.forEach((workflow) => { - const nextTime = this.getNextTime(workflow, now); - if (nextTime) { - this.workflow - .getLogger(workflow.id) - .info(`caching scheduled workflow will run at: ${new Date(nextTime).toISOString()}`); - } else { - this.workflow.getLogger(workflow.id).info('workflow will not be scheduled'); - } - this.schedule(workflow, nextTime, nextTime >= now.getTime()); + this.inspect(workflow); }); + }; + + onBeforeStop = () => { + for (const timer of this.timers.values()) { + clearInterval(timer); + } + }; + + constructor(public workflow: Plugin) { + workflow.app.on('afterStart', this.onAfterStart); + workflow.app.on('beforeStop', this.onBeforeStop); + } + + inspect(workflow: WorkflowModel) { + const now = new Date(); + + const nextTime = this.getNextTime(workflow, now); + if (nextTime) { + this.workflow + .getLogger(workflow.id) + .info(`caching scheduled workflow will run at: ${new Date(nextTime).toISOString()}`); + } else { + this.workflow.getLogger(workflow.id).info('workflow will not be scheduled'); + } + this.schedule(workflow, nextTime, nextTime >= now.getTime()); } getNextTime({ config, allExecuted }: WorkflowModel, currentDate: Date, nextSecond = false) { @@ -130,7 +133,7 @@ export default class StaticScheduleTrigger { } on(workflow) { - this.inspect([workflow]); + this.inspect(workflow); } off(workflow) { diff --git a/packages/presets/nocobase/package.json b/packages/presets/nocobase/package.json index c1ad557e29..3c1968cc3c 100644 --- a/packages/presets/nocobase/package.json +++ b/packages/presets/nocobase/package.json @@ -1,80 +1,84 @@ { "name": "@nocobase/preset-nocobase", - "version": "1.6.6", + "version": "1.6.20", "license": "AGPL-3.0", "main": "./lib/server/index.js", "dependencies": { "@formily/json-schema": "2.x", - "@nocobase/plugin-acl": "1.6.6", - "@nocobase/plugin-action-bulk-edit": "1.6.6", - "@nocobase/plugin-action-bulk-update": "1.6.6", - "@nocobase/plugin-action-custom-request": "1.6.6", - "@nocobase/plugin-action-duplicate": "1.6.6", - "@nocobase/plugin-action-export": "1.6.6", - "@nocobase/plugin-action-import": "1.6.6", - "@nocobase/plugin-action-print": "1.6.6", - "@nocobase/plugin-ai": "1.6.6", - "@nocobase/plugin-api-doc": "1.6.6", - "@nocobase/plugin-api-keys": "1.6.6", - "@nocobase/plugin-async-task-manager": "1.6.6", - "@nocobase/plugin-audit-logs": "1.6.6", - "@nocobase/plugin-auth": "1.6.6", - "@nocobase/plugin-auth-sms": "1.6.6", - "@nocobase/plugin-backup-restore": "1.6.6", - "@nocobase/plugin-block-iframe": "1.6.6", - "@nocobase/plugin-block-workbench": "1.6.6", - "@nocobase/plugin-calendar": "1.6.6", - "@nocobase/plugin-charts": "1.6.6", - "@nocobase/plugin-client": "1.6.6", - "@nocobase/plugin-collection-sql": "1.6.6", - "@nocobase/plugin-collection-tree": "1.6.6", - "@nocobase/plugin-data-source-main": "1.6.6", - "@nocobase/plugin-data-source-manager": "1.6.6", - "@nocobase/plugin-data-visualization": "1.6.6", - "@nocobase/plugin-environment-variables": "1.6.6", - "@nocobase/plugin-error-handler": "1.6.6", - "@nocobase/plugin-field-china-region": "1.6.6", - "@nocobase/plugin-field-formula": "1.6.6", - "@nocobase/plugin-field-m2m-array": "1.6.6", - "@nocobase/plugin-field-markdown-vditor": "1.6.6", - "@nocobase/plugin-field-sequence": "1.6.6", - "@nocobase/plugin-field-sort": "1.6.6", - "@nocobase/plugin-file-manager": "1.6.6", - "@nocobase/plugin-gantt": "1.6.6", - "@nocobase/plugin-graph-collection-manager": "1.6.6", - "@nocobase/plugin-kanban": "1.6.6", - "@nocobase/plugin-localization": "1.6.6", - "@nocobase/plugin-logger": "1.6.6", - "@nocobase/plugin-map": "1.6.6", - "@nocobase/plugin-mobile": "1.6.6", - "@nocobase/plugin-mobile-client": "1.6.6", - "@nocobase/plugin-mock-collections": "1.6.6", - "@nocobase/plugin-multi-app-manager": "1.6.6", - "@nocobase/plugin-multi-app-share-collection": "1.6.6", - "@nocobase/plugin-notification-email": "1.6.6", - "@nocobase/plugin-notification-in-app-message": "1.6.6", - "@nocobase/plugin-notification-manager": "1.6.6", - "@nocobase/plugin-public-forms": "1.6.6", - "@nocobase/plugin-snapshot-field": "1.6.6", - "@nocobase/plugin-system-settings": "1.6.6", - "@nocobase/plugin-theme-editor": "1.6.6", - "@nocobase/plugin-ui-schema-storage": "1.6.6", - "@nocobase/plugin-user-data-sync": "1.6.6", - "@nocobase/plugin-users": "1.6.6", - "@nocobase/plugin-verification": "1.6.6", - "@nocobase/plugin-workflow": "1.6.6", - "@nocobase/plugin-workflow-action-trigger": "1.6.6", - "@nocobase/plugin-workflow-aggregate": "1.6.6", - "@nocobase/plugin-workflow-delay": "1.6.6", - "@nocobase/plugin-workflow-dynamic-calculation": "1.6.6", - "@nocobase/plugin-workflow-loop": "1.6.6", - "@nocobase/plugin-workflow-mailer": "1.6.6", - "@nocobase/plugin-workflow-manual": "1.6.6", - "@nocobase/plugin-workflow-notification": "1.6.6", - "@nocobase/plugin-workflow-parallel": "1.6.6", - "@nocobase/plugin-workflow-request": "1.6.6", - "@nocobase/plugin-workflow-sql": "1.6.6", - "@nocobase/server": "1.6.6", + "@nocobase/plugin-acl": "1.6.20", + "@nocobase/plugin-action-bulk-edit": "1.6.20", + "@nocobase/plugin-action-bulk-update": "1.6.20", + "@nocobase/plugin-action-custom-request": "1.6.20", + "@nocobase/plugin-action-duplicate": "1.6.20", + "@nocobase/plugin-action-export": "1.6.20", + "@nocobase/plugin-action-import": "1.6.20", + "@nocobase/plugin-action-print": "1.6.20", + "@nocobase/plugin-ai": "1.6.20", + "@nocobase/plugin-api-doc": "1.6.20", + "@nocobase/plugin-api-keys": "1.6.20", + "@nocobase/plugin-async-task-manager": "1.6.20", + "@nocobase/plugin-audit-logs": "1.6.20", + "@nocobase/plugin-auth": "1.6.20", + "@nocobase/plugin-auth-sms": "1.6.20", + "@nocobase/plugin-backup-restore": "1.6.20", + "@nocobase/plugin-block-iframe": "1.6.20", + "@nocobase/plugin-block-workbench": "1.6.20", + "@nocobase/plugin-calendar": "1.6.20", + "@nocobase/plugin-charts": "1.6.20", + "@nocobase/plugin-client": "1.6.20", + "@nocobase/plugin-collection-sql": "1.6.20", + "@nocobase/plugin-collection-tree": "1.6.20", + "@nocobase/plugin-data-source-main": "1.6.20", + "@nocobase/plugin-data-source-manager": "1.6.20", + "@nocobase/plugin-data-visualization": "1.6.20", + "@nocobase/plugin-departments": "1.6.20", + "@nocobase/plugin-environment-variables": "1.6.20", + "@nocobase/plugin-error-handler": "1.6.20", + "@nocobase/plugin-field-attachment-url": "1.6.20", + "@nocobase/plugin-field-china-region": "1.6.20", + "@nocobase/plugin-field-formula": "1.6.20", + "@nocobase/plugin-field-m2m-array": "1.6.20", + "@nocobase/plugin-field-markdown-vditor": "1.6.20", + "@nocobase/plugin-field-sequence": "1.6.20", + "@nocobase/plugin-field-sort": "1.6.20", + "@nocobase/plugin-file-manager": "1.6.20", + "@nocobase/plugin-gantt": "1.6.20", + "@nocobase/plugin-graph-collection-manager": "1.6.20", + "@nocobase/plugin-kanban": "1.6.20", + "@nocobase/plugin-locale-tester": "1.6.20", + "@nocobase/plugin-localization": "1.6.20", + "@nocobase/plugin-logger": "1.6.20", + "@nocobase/plugin-map": "1.6.20", + "@nocobase/plugin-mobile": "1.6.20", + "@nocobase/plugin-mobile-client": "1.6.20", + "@nocobase/plugin-mock-collections": "1.6.20", + "@nocobase/plugin-multi-app-manager": "1.6.20", + "@nocobase/plugin-multi-app-share-collection": "1.6.20", + "@nocobase/plugin-notification-email": "1.6.20", + "@nocobase/plugin-notification-in-app-message": "1.6.20", + "@nocobase/plugin-notification-manager": "1.6.20", + "@nocobase/plugin-public-forms": "1.6.20", + "@nocobase/plugin-snapshot-field": "1.6.20", + "@nocobase/plugin-system-settings": "1.6.20", + "@nocobase/plugin-theme-editor": "1.6.20", + "@nocobase/plugin-ui-schema-storage": "1.6.20", + "@nocobase/plugin-user-data-sync": "1.6.20", + "@nocobase/plugin-users": "1.6.20", + "@nocobase/plugin-verification": "1.6.20", + "@nocobase/plugin-workflow": "1.6.20", + "@nocobase/plugin-workflow-action-trigger": "1.6.20", + "@nocobase/plugin-workflow-aggregate": "1.6.20", + "@nocobase/plugin-workflow-delay": "1.6.20", + "@nocobase/plugin-workflow-dynamic-calculation": "1.6.20", + "@nocobase/plugin-workflow-loop": "1.6.20", + "@nocobase/plugin-workflow-mailer": "1.6.20", + "@nocobase/plugin-workflow-manual": "1.6.20", + "@nocobase/plugin-workflow-notification": "1.6.20", + "@nocobase/plugin-workflow-parallel": "1.6.20", + "@nocobase/plugin-workflow-request": "1.6.20", + "@nocobase/plugin-workflow-response-message": "1.6.20", + "@nocobase/plugin-workflow-sql": "1.6.20", + "@nocobase/server": "1.6.20", "cronstrue": "^2.11.0", "fs-extra": "^11.1.1" },