Compare commits

...

180 Commits

Author SHA1 Message Date
Zeke Zhang
a458fd6d7c fix: refactor parameter extraction in listWithPagination function 2025-04-30 16:27:40 +08:00
Zeke Zhang
c34a923279 Merge branch 'next' into feat/plugin-bulk-filter 2025-04-30 15:29:37 +08:00
Katherine
1814835ec5
fix: build error (#6812) 2025-04-30 10:36:08 +08:00
katherinehhh
d4259214fc Merge branch 'main' into next 2025-04-30 09:54:17 +08:00
gchust
b164d9a2fa
fix: incorrect variable selection when first click to load data (#6802)
* fix: incorrect variable selection when first click to load data

* Revert "fix: incorrect variable selection when first click to load data"

This reverts commit 0caa9a84b15c51cf87e03819c51b79d8be3e99e9.

* fix: e2e tests
2025-04-30 09:42:35 +08:00
Katherine
fd8606e69e
fix: duplicate validation error messages for rule violations in form field (#6805) 2025-04-30 09:36:05 +08:00
Katherine
fe005e091a
fix: ssue with configuring linkage rules in subform (popup) within subtable (#6803) 2025-04-30 09:35:26 +08:00
xilesun
dfc5ec098c fix(verification): migration version number 2025-04-30 09:34:05 +08:00
nocobase[bot]
b98a9a97e8 Merge branch 'main' into next 2025-04-29 23:42:41 +00:00
Zeke Zhang
db88e8301f
fix: exclude 'attachmentURL' from associated form item filter (#6809) 2025-04-30 07:42:18 +08:00
nocobase[bot]
39e27f94a7 Merge branch 'main' into next 2025-04-29 12:07:16 +00:00
nocobase[bot]
5c2bd3c496 docs: update changelogs 2025-04-29 12:06:39 +00:00
nocobase[bot]
4a0ce6fdda Merge branch 'main' into next 2025-04-29 11:58:24 +00:00
nocobase[bot]
09c53403eb chore(versions): 😊 publish v1.6.25 2025-04-29 11:57:38 +00:00
nocobase[bot]
0965749f8c Merge branch 'main' into next 2025-04-29 07:26:50 +00:00
YANG QIA
6cb13f73e6
fix(auth): disallow changing of authenticator uid (#6808) 2025-04-29 15:26:23 +08:00
katherinehhh
08bd9fa8a8 Merge branch 'main' into next 2025-04-29 14:35:36 +08:00
Katherine
ab1cf294d6
fix: duplicate linkage rules in the operation panel's link action (#6806) 2025-04-29 14:22:37 +08:00
Katherine
4f06ebcdd2
style: optimize subtable add button style and align paginator on the same row (#6790) 2025-04-29 11:07:17 +08:00
Katherine
bd077d5cc2
fix: field linkage rules not working for subform (popup) in subtable (#6800) 2025-04-28 22:33:38 +08:00
nocobase[bot]
973038a2e9 Merge branch 'main' into next 2025-04-28 13:17:32 +00:00
Junyi
823d67ac13
fix(database): fix trim option for string and text field (#6797) 2025-04-28 21:17:05 +08:00
Katherine
9c789b88c4
fix: custom request response set to variable not displaying correctly (#6793) 2025-04-28 21:01:35 +08:00
nocobase[bot]
090e012384 Merge branch 'main' into next 2025-04-28 11:48:43 +00:00
Junyi
3b1b4cfef5
fix(plugin-file-manager): add oss timeout option default to 10min (#6795) 2025-04-28 19:48:18 +08:00
nocobase[bot]
cfbd0d6fbb Merge branch 'main' into next 2025-04-28 11:27:38 +00:00
Katherine
a28e1d2c39
fix: association field not submitting data when displaying field from related collection (#6798) 2025-04-28 19:27:16 +08:00
nocobase[bot]
aaef833be5 Merge branch 'main' into next 2025-04-28 11:21:28 +00:00
Katherine
c1f8b4d2f7
Revert "fix: association field not submitting data when displaying field fro…" (#6796)
This reverts commit d4bf52a047243f5274e23dbf3e57e49d396dba00.
2025-04-28 19:21:06 +08:00
nocobase[bot]
630409b4cb Merge branch 'main' into next 2025-04-28 10:05:52 +00:00
Katherine
d4bf52a047
fix: association field not submitting data when displaying field from related collection (#6789)
* fix: o2o association field not submitting data when display association field are used

* fix: bug

* fix: bug
2025-04-28 18:04:36 +08:00
Katherine
f964d25bfa
fix: field linkage rules in filter form block missing 'current form' variable (#6794) 2025-04-28 17:48:12 +08:00
nocobase[bot]
19225ab829 chore(versions): 😊 publish v1.7.0-beta.26 2025-04-28 07:07:10 +00:00
Zeke Zhang
20c07d9947 Merge branch 'main' into next 2025-04-28 14:44:41 +08:00
Zeke Zhang
4547b7dd96
fix(Collapse): issue with data scope being cleared (#6782) 2025-04-28 14:25:15 +08:00
Katherine
93839acdb7
fix: alignment issue of the add child record button in the tree table (#6791) 2025-04-28 14:16:13 +08:00
Zeke Zhang
6808620a3d
fix: refreshing the page after switching operators causes an error (#6781)
* fix: clear default value when switching operators to prevent filtering API errors

* fix: update EditOperator to set default value to null and assign componentProps to x-component-props

* fix: set field value and initialValue to null in EditOperator to ensure proper state management
2025-04-28 14:12:28 +08:00
Zeke Zhang
4f1e3df663
chore(Filter): improve user experience (#6779) 2025-04-28 14:11:46 +08:00
nocobase[bot]
ca182a9cb5 Merge branch 'main' into next 2025-04-28 06:08:18 +00:00
Zeke Zhang
281b813f0c
refactor: remove rowKey from table block schema creation (#6792)
* refactor: remove rowKey from table block schema creation

* chore: fix unit test
2025-04-28 14:07:49 +08:00
Zeke Zhang
fa887c4ad4 Merge branch 'main' into next 2025-04-28 12:34:49 +08:00
nocobase[bot]
4f33515136 chore(versions): 😊 publish v1.7.0-beta.25 2025-04-28 04:04:59 +00:00
Katherine
14e6ccca01
refactor: show button title with tooltip on action icon hover (#6761)
* refactor: show button title with tooltip on action icon hover

* fix: test

* fix: style improve

* fix: filter action should onlyicon

* fix: test

* fix: test

* style: link action style improve
2025-04-27 19:51:10 +08:00
Sheldon Guo
c2521a04c1
feat(file-manager): add getFileStream api (#6741) 2025-04-27 19:50:24 +08:00
Jiann
1b4b71bba4
fix: add github publish ci for license kit (#6786) 2025-04-27 16:58:55 +08:00
Zeke Zhang
17eaee373c
refactor: unify 404 page components (#6778) 2025-04-27 12:38:53 +08:00
Zeke Zhang
462cc31998
fix: filter out tabs from route items in toItems function (#6777)
* fix: filter out tabs from route items in toItems function

* chore: fix e2e

* chore: fix e2e
2025-04-27 12:32:51 +08:00
nocobase[bot]
33141d100e chore(versions): 😊 publish v1.7.0-beta.24 2025-04-25 13:16:59 +00:00
Katherine
163ec58136
feat: support linkage rules in blocks (#6636)
* feat: support linkage rules setting for association block action

* chore: linkage rule

* fix: bug

* fix: bug

* refactor: linkage rule

* refactor: code imporve

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* refactor: code imporve

* fix: test

* fix: test

* fix: test

* test: e2e test

* fix: bug

* fix: bug

* fix: test

* fix: e2e test

* fix: bug

* feat: block support linkage rule

* feat: block support linkage rule

* feat: form block support linkage rule

* refactor: code improve

* refactor: detail block support linkage rule

* chore: support legacy leftVar in linkage rules

* fix: bug

* refactor: gantt support linkage rule

* refactor: map block support linkage rule

* fix: bug

* feat: markdown support linkage rule

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: create submit linkage rule

* fix: action panel

* refactor: code improve

* fix: bug

* refactor: action panpel support linkage rule

* fix: test

* fix: bug

* refactor: code improve

* fix: test

* fix: code improve

* fix: e2e test

* refactor: chart block

* refactor: support markdown under form block

* fix: bug

* refactor: refresh & expend action support linkage rule

* refactor: code improve

* fix: test

* fix: bug

* fix: bug

* fix: e2e test

* fix: e2e test

* fix: e2e test

* fix: test

* fix: test

* fix: merge bug

* fix: test

* fix: test bug

* fix: merge bug

* fix: bug

* fix: build error

* fix: bug

* fix: bug

* fix: bug

* fix: e2e test

* fix: e2e test

* fix: e2e test
2025-04-25 17:26:39 +08:00
nocobase[bot]
20abfedc90 Merge branch 'main' into next 2025-04-25 07:43:57 +00:00
Zeke Zhang
1f974d241f test: fix failed tests 2025-04-25 15:43:13 +08:00
Katherine
9ae303844d
fix: url query parameter operator issue (#6770)
* fix: url query parameter operator issue

* fix: test
2025-04-25 15:09:32 +08:00
Katherine
cf57be8fb4
fix: action panel icon missing when icon-only is enabled (#6773) 2025-04-25 15:05:26 +08:00
YANG QIA
757765228f
feat(data-vi): add extended API for field interface configuration (#6742)
* feat(data-vi): add extend api

* feat(data-vi): add extend API for field interface configuration
2025-04-25 10:51:46 +08:00
gchust
b923d89fa0
feat: keep reference templates feature (#6743)
* feat: be able to create reference template

* chore: update template title

* fix: template menu position inconsitant

* chore: add reference templates into block template management view

* fix: menu not correct

* fix: incorrect label

* chore: remove useless component

* fix: build error

* fix: incorrect translate

* fix: incorrect  translate tab [skip ci]

* chore: remove edit icon[skip ci]

* fix: e2e tests failing
2025-04-24 23:05:12 +08:00
nocobase[bot]
5b0b208515 Merge branch 'main' into next 2025-04-24 12:29:38 +00:00
chenos
b0cee75bf1
chore: downgrade mariadb to 2.5.6 due to compatibility issues (#6762) 2025-04-24 20:29:13 +08:00
nocobase[bot]
b1eda23558 Merge branch 'main' into next 2025-04-24 12:10:10 +00:00
Katherine
e2e4c69941
refactor: unavailableActions to sql collection and view collection (#6765)
* refactor: unavailableActions to sql collection and view collection

* fix: unavailableActions
2025-04-24 20:09:42 +08:00
nocobase[bot]
5cf066a865 chore(versions): 😊 publish v1.7.0-beta.23 2025-04-24 11:45:11 +00:00
nocobase[bot]
dee148c18e Merge branch 'main' into next 2025-04-24 10:29:42 +00:00
nocobase[bot]
24376fd3dd docs: update changelogs 2025-04-24 10:29:18 +00:00
nocobase[bot]
02e130df5a Merge branch 'main' into next 2025-04-24 10:21:08 +00:00
nocobase[bot]
bfe77cd81d chore(versions): 😊 publish v1.6.24 2025-04-24 10:20:37 +00:00
Katherine
a9fb7ea56e
feat(custom-request): support variable in success message of custom request action (#6694)
* feat: support variable  in success message of custom request action

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: bug
2025-04-24 17:59:01 +08:00
nocobase[bot]
d1d8f4bbe7 Merge branch 'main' into next 2025-04-24 09:33:26 +00:00
YANG QIA
bd06d13749
fix(m2m-array): incorrect data retrieved for m2m array fields from association in forms (#6744)
* fix(m2m-array): incorrect data retrieved for m2m array fields from association in forms

* fix(getAction): update pagination parameter for hasMany and belongsToMany actions

---------

Co-authored-by: Zeke Zhang <958414905@qq.com>
2025-04-24 17:33:00 +08:00
nocobase[bot]
b739367029 Merge branch 'main' into next 2025-04-24 09:09:34 +00:00
Katherine
b8bfc17a59
refactor: adjust view collection action permission control (#6764) 2025-04-24 17:09:11 +08:00
nocobase[bot]
717b5cc3c6 Merge branch 'main' into next 2025-04-24 08:32:27 +00:00
Katherine
04e17444fa
fix: only export action in view collection is support when writableView is false (#6763) 2025-04-24 16:32:06 +08:00
nocobase[bot]
6b3d0e1f6b Merge branch 'main' into next 2025-04-24 08:16:49 +00:00
Junyi
717177b471
fix(client): adjust upload message (#6757) 2025-04-24 16:16:22 +08:00
Katherine
2ef89513ff
refactor: remove icon from row buttons in plugin (#6760) 2025-04-24 15:09:01 +08:00
nocobase[bot]
c6935a8923 Merge branch 'main' into next 2025-04-24 01:54:43 +00:00
Katherine
0758490913
fix: unexpected data creation when displaying association field under sub-form/sub-table in create form action (#6727)
* fix: unexpected data creation when displaying relational field subtable in create form

* fix: bug

* fix: bug

* fix: bug
2025-04-24 09:54:22 +08:00
nocobase[bot]
d7c4921d1d chore(versions): 😊 publish v1.7.0-beta.22 2025-04-24 00:00:43 +00:00
nocobase[bot]
eaf7da5528 Merge branch 'main' into next 2025-04-23 15:18:13 +00:00
nocobase[bot]
04296bbac8 docs: update changelogs 2025-04-23 15:17:46 +00:00
nocobase[bot]
53f7584eb0 Merge branch 'main' into next 2025-04-23 15:06:56 +00:00
nocobase[bot]
c8e5a1c7e7 chore(versions): 😊 publish v1.6.23 2025-04-23 15:06:25 +00:00
Katherine
a76ae40968
fix: display issue with linkage rules in multi-level association data (#6755) 2025-04-23 22:06:16 +08:00
nocobase[bot]
aecca20215 Merge branch 'main' into next 2025-04-23 13:26:43 +00:00
chenos
452619c511
refactor: optimize internal logic of the nocobase upgrade command (#6754)
* chore: update-deps

* fix: upgrade
2025-04-23 21:26:07 +08:00
nocobase[bot]
7fb0805888 chore(versions): 😊 publish v1.7.0-beta.21 2025-04-23 10:21:44 +00:00
nocobase[bot]
88926ca75e Merge branch 'main' into next 2025-04-23 08:50:02 +00:00
Katherine
285918c1a8
style(gantt): gantt chart block overlapping months in calendar header for month view (#6753)
* style: gantt block calendar header style improve

* style: gantt block calendar header style improve
2025-04-23 16:49:41 +08:00
nocobase[bot]
946d045df8 Merge branch 'main' into next 2025-04-23 08:24:12 +00:00
ajie
ced8af89ef
fix: import and export invalid when set field permissions (#6677)
* fix:  import and export invalid when set field permissions
2025-04-23 16:23:49 +08:00
nocobase[bot]
97c33f6eed Merge branch 'main' into next 2025-04-23 08:19:57 +00:00
Katherine
3d73ea0acb
fix: tree table 'Add Child' button linkage rule without 'current record' variable (#6752) 2025-04-23 16:19:36 +08:00
nocobase[bot]
2622074094 Merge branch 'main' into next 2025-04-23 06:01:45 +00:00
Katherine
63ebe1d357
refactor: formula syntax references link (#6751) 2025-04-23 14:01:20 +08:00
nocobase[bot]
ba89bcd4b4 Merge branch 'main' into next 2025-04-23 03:40:15 +00:00
Katherine
7340a7d187
fix: missing filter for already associated data when adding association data (#6750)
* fix: missing filter for already associated data when adding association collection table

* fix: bug
2025-04-23 11:39:54 +08:00
Zeke Zhang
9eb5dbc853
fix: issue with data block refresh not working (#6748) 2025-04-23 08:30:55 +08:00
nocobase[bot]
5d4d901429 Merge branch 'main' into next 2025-04-22 23:02:25 +00:00
chenos
174693e219
chore: auto-update package.json on upgrade (#6747)
* fix: update package.json

* fix: deps
2025-04-23 07:01:57 +08:00
nocobase[bot]
b24770291c Merge branch 'main' into next 2025-04-22 09:54:13 +00:00
nocobase[bot]
a494dacdaf docs: update changelogs 2025-04-22 09:53:48 +00:00
nocobase[bot]
5aae89eaef Merge branch 'main' into next 2025-04-22 09:44:24 +00:00
nocobase[bot]
5fd8649fc3 chore(versions): 😊 publish v1.6.22 2025-04-22 09:44:09 +00:00
gchust
d31aa4a91c
feat: add convert template block to page block action (#6662)
* feat: add convert template block to page block action

* feat: convert template block to normal block

* fix: remove remaining template properties

* fix: list template not loading data

* fix: user menu render error

* fix: save as template and convert to block should hide in workflow config page

* fix: incorrect translation
2025-04-22 13:25:29 +08:00
nocobase[bot]
5eb337bd7a Merge branch 'main' into next 2025-04-22 05:11:21 +00:00
chenos
62aff88e5e
fix: appVersion incorrectly generated by create-migration (#6740) 2025-04-22 13:10:59 +08:00
Zeke Zhang
f42a4306a8
fix: update min and max date limits in MobileDateTimePicker (#6735) 2025-04-22 11:38:40 +08:00
nocobase[bot]
ad0b48a26c Merge branch 'main' into next 2025-04-22 03:37:11 +00:00
Zeke Zhang
e5055ef4f6
fix(styles): add relative positioning to span elements in mobile navigation bar action (#6734) 2025-04-22 11:36:48 +08:00
nocobase[bot]
75aede8ef5 Merge branch 'main' into next 2025-04-22 03:36:16 +00:00
Zeke Zhang
2faed8de79
fix(SchemaSettingsActionModalItem): fix zIndex (#6733) 2025-04-22 11:35:54 +08:00
nocobase[bot]
0f83157d60 Merge branch 'main' into next 2025-04-22 03:34:58 +00:00
chenos
8dbe798fca
chore: move pm2 to devDependencies (#6737) 2025-04-22 11:34:29 +08:00
Katherine
3049f35490
fix: issue with time field as condition in linkage rules (#6738) 2025-04-22 11:13:29 +08:00
nocobase[bot]
b979e8b71a Merge branch 'main' into next 2025-04-22 01:54:09 +00:00
chenos
bebb875694 chore: launchMode 2025-04-22 09:53:34 +08:00
Katherine
ffdd03b4bc
fix: date time field condition not working in linkage rules (#6728)
* fix: datetime field in linakge rule condiction

* fix: bug

* fix: bug
2025-04-21 20:52:24 +08:00
chenos
72c5fdd14b Merge branch 'main' into next
# Conflicts:
#	packages/core/auth/package.json
#	packages/core/create-nocobase-app/templates/app/package.json.tpl
#	packages/core/data-source-manager/package.json
#	packages/core/server/package.json
2025-04-21 19:48:52 +08:00
chenos
120aa3420c
chore(deps): upgrade dependencies (#6708)
* chore: upgrade jsonwebtoken to version 9.0.2

* chore: update

* fix: deps

* fix: semver

* chore: dicer

* chore: upgrade multer

* fix: dicer

* fix: remove sqlite

* chore: check db dialect

* chore: remove sqlite3

* fix: yarn add sqlite3 --no-save -W

* fix: test

* fix: test

* fix: ci

* fix: test

* fix: deps
2025-04-21 18:52:26 +08:00
gchust
c1b24c5c53
fix: block template menus not shown in mysql env (#6726) 2025-04-21 16:21:08 +08:00
Katherine
374c569dc0
fix: incorrect value display for "Enable index column" (#6724) 2025-04-21 09:47:15 +08:00
nocobase[bot]
31a0189e2e Merge branch 'main' into next 2025-04-21 01:31:49 +00:00
Junyi
73956a834d
fix(build): tar cli (#6722) 2025-04-21 09:31:27 +08:00
nocobase[bot]
813be720de Merge branch 'main' into next 2025-04-21 01:25:02 +00:00
chenos
31da3c78e9
feat: allow setting sidebar width in theme config (#6720) 2025-04-21 09:19:31 +08:00
Katherine
569ae3c110
fix: missing action option constraints when reopening linkage rules (#6723)
* fix: missing action option constraints when reopening linkage rules

* fix: bug
2025-04-21 09:19:05 +08:00
nocobase[bot]
d0b172db59 Merge branch 'main' into next 2025-04-20 09:34:02 +00:00
Junyi
b04c26144e
fix(plugin-workflow): fix schedule in subflow (#6721) 2025-04-20 17:33:36 +08:00
nocobase[bot]
4dbde49b89 Merge branch 'main' into next 2025-04-20 07:35:40 +00:00
Zeke Zhang
386e76661d
fix(linkage-rules): hidden required fields should not affect form submission (#6709)
* test: add e2e test

* fix(linkage-rules): hidden required fields should not affect form submission

* chore: fix e2e
2025-04-20 15:35:19 +08:00
nocobase[bot]
0fa82f0404 Merge branch 'main' into next 2025-04-20 07:24:32 +00:00
Junyi
ec0e1787d4
refactor(plugin-file-manager): expose utils api (#6705) 2025-04-20 15:24:11 +08:00
nocobase[bot]
6b055f4900 Merge branch 'main' into next 2025-04-20 06:13:38 +00:00
Junyi
817ba15a91
refactor(plugin-workflow): adjust date type of variable (#6717) 2025-04-20 14:13:12 +08:00
YANG QIA
97f4a6ccff
fix(data-vi): enum field options are empty in the filter block (#6706)
* fix(data-vi): enum field options are empty in the filter block

* fix: specific props
2025-04-18 19:20:46 +08:00
Katherine
5931e2914e
fix: after success link (#6707) 2025-04-18 18:00:33 +08:00
katherinehhh
8e4ccd1916 Merge branch 'main' into next 2025-04-18 17:44:36 +08:00
Katherine
aa6a46924a
fix(date-picker): picker switching issue in date field of filter button (#6695)
* fix:  picker switching issue in date field of filter button

* fix: bug

* fix: bug
2025-04-18 17:29:52 +08:00
Katherine
d550a628d8
fix(linkage-rule): variable conversion in sub-table/sub-form linkage rule conditions (#6702)
* fix: variable conversion in sub-table/sub-form linkage rule conditions

* fix: bug

* fix: style in sub-table

* fix: bug
2025-04-18 17:29:07 +08:00
nocobase[bot]
e5b73758bf Merge branch 'main' into next 2025-04-18 06:42:53 +00:00
chenos
deb85d30bd chore: build internal image 2025-04-18 14:42:19 +08:00
nocobase[bot]
69163443c8 Merge branch 'main' into next 2025-04-18 06:00:01 +00:00
Katherine
8ac84e6e2c
fix: export button shown without export permission (#6689) 2025-04-18 13:59:39 +08:00
nocobase[bot]
b76eda2136 Merge branch 'main' into next 2025-04-18 04:23:47 +00:00
Sheldon Guo
f5818066ca
fix(in-app-message): disconnect SSE on mobile when message component unmounts or tab is switched to avoid excessive persistent connections. 2025-04-18 12:23:25 +08:00
chenos
88dd0c84e7
fix: ensure custom request data must be JSON (#6701) 2025-04-18 11:15:06 +08:00
nocobase[bot]
e1e2f7a83c Merge branch 'main' into next 2025-04-18 01:24:33 +00:00
Zeke Zhang
3067b3a4b1
fix(tree-block): the issue where the tree block filter condition was empty (#6634)
* fix: enhance useFilterAPI to support sourceKey in filter functions

* fix: update useFilterAPI to use key variable for dynamic field assignment

* feat: add treeFind function for depth-first traversal of tree structures

* feat: enhance getSupportFieldsByForeignKey to include target collection matching

* feat: add getInheritChain method to InheritanceCollectionMixin and corresponding tests
2025-04-18 09:24:08 +08:00
nocobase[bot]
402c3f4b3a chore(versions): 😊 publish v1.7.0-beta.20 2025-04-18 00:52:00 +00:00
Junyi
42cbe1e1dd
fix(plugin-workflow-manual): acl (#6693)
* fix(plugin-workflow-manual): fix acl for tasks

* test(plugin-workflow-manual): add test case

* fix(plugin-workflow-manual): fix test case
2025-04-17 22:42:13 +08:00
Junyi
e9560bb42c
fix(client): fix error thrown when no block context (#6696) 2025-04-17 20:26:49 +08:00
nocobase[bot]
b04e877d31 chore(versions): 😊 publish v1.7.0-beta.19 2025-04-17 08:13:12 +00:00
chenos
0fc8f5d0cf Merge branch 'main' into next
# Conflicts:
#	yarn.lock
2025-04-17 15:11:07 +08:00
chenos
8abdb8eaac chore: upgrade tar to version 7.4.3 2025-04-17 15:09:10 +08:00
nocobase[bot]
af82078998 Merge branch 'main' into next 2025-04-17 07:01:14 +00:00
nocobase[bot]
3413bffcbb docs: update changelogs 2025-04-17 07:00:50 +00:00
nocobase[bot]
2c6547e95c Merge branch 'main' into next 2025-04-17 06:52:14 +00:00
nocobase[bot]
7f1d642848 chore(versions): 😊 publish v1.6.21 2025-04-17 06:51:36 +00:00
nocobase[bot]
66ecba9d96 Merge branch 'main' into next 2025-04-17 06:30:55 +00:00
Junyi
9add517d75
refactor(client): add delay API for scenarios without delay (#6681) 2025-04-17 14:30:32 +08:00
Katherine
f5fb2844da
feat(custom-request): support selected table records in custom request (#6647)
* feat: support selected table records in custom request

* fix: test

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: merge bug

* fix: e2e test

* fix: e2e test
2025-04-17 12:35:03 +08:00
nocobase[bot]
1c343cad66 Merge branch 'main' into next 2025-04-17 04:19:18 +00:00
Junyi
18447848ca
fix(client): add fault tolerant when no x-component in schema (#6691) 2025-04-17 12:18:51 +08:00
nocobase[bot]
00629d118e Merge branch 'main' into next 2025-04-17 04:14:06 +00:00
ajie
454ab34921
fix: ci build (#6687) 2025-04-17 12:13:41 +08:00
nocobase[bot]
ff772fc5cf Merge branch 'main' into next 2025-04-17 03:44:59 +00:00
Katherine
0961c4399d
fix: association field detection to be based on type (#6692) 2025-04-17 11:44:38 +08:00
Katherine
38f8cf9d56
fix: linkage rules compatibility with legacy data (#6686)
* fix: linkage rules compatibility with legacy data

* fix: bug
2025-04-17 10:32:41 +08:00
nocobase[bot]
78fefe197b Merge branch 'main' into next 2025-04-17 01:06:30 +00:00
xilesun
83be9b1013 chore: update ci 2025-04-17 09:05:38 +08:00
nocobase[bot]
1652b50edf Merge branch 'main' into next 2025-04-17 01:04:09 +00:00
YANG QIA
824ad7f29e
fix: bug (#6684) 2025-04-17 09:03:33 +08:00
mytharcher
10a6fbba41 fix(plugin-workflow-manual): fix merge error 2025-04-16 21:47:18 +08:00
nocobase[bot]
ccbb2d0783 Merge branch 'main' into next 2025-04-16 13:20:20 +00:00
xilesun
18791bfe2d fix(departments): test error 2025-04-16 21:18:34 +08:00
mytharcher
42a9d5a6cb Merge branch 'main' into next 2025-04-16 21:06:12 +08:00
gchust
181c24c2a3
fix: layload errors if component not exist (#6683) 2025-04-16 20:40:32 +08:00
Junyi
8d8229f8ca
fix(client): fix upload component locale (#6682) 2025-04-16 18:40:05 +08:00
ajie
5eda5ba21d
fix: fixed an error importing xlsx time field (#6672) 2025-04-16 17:14:23 +08:00
Junyi
baec449c03
fix(client): fix input component (#6679)
* fix(client): fix input component

* fix(client): remove useless definition
2025-04-16 12:33:46 +08:00
Junyi
63a842aab3
fix(plugin-workflow-manual): fix task status (#6676) 2025-04-16 09:48:48 +08:00
353 changed files with 5621 additions and 1841 deletions

View File

@ -0,0 +1,119 @@
name: Build Image (Internal)
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
ref_name:
description: 'Branch or tag name to release'
jobs:
get-plugins:
uses: nocobase/nocobase/.github/workflows/get-plugins.yml@main
secrets: inherit
push-docker:
runs-on: ubuntu-latest
needs: get-plugins
services:
verdaccio:
image: verdaccio/verdaccio:5
ports:
- 4873:4873
steps:
- name: Set Node.js 20
uses: actions/setup-node@v3
with:
node-version: 20
- name: Get info
id: get-info
shell: bash
run: |
if [[ "${{ inputs.ref_name || github.ref_name }}" =~ "beta" ]]; then
echo "defaultTag=$(echo 'beta')" >> $GITHUB_OUTPUT
echo "proRepos=$(echo '${{ needs.get-plugins.outputs.beta-plugins }}')" >> $GITHUB_OUTPUT
elif [[ "${{ inputs.ref_name || github.ref_name }}" =~ "alpha" ]]; then
echo "defaultTag=$(echo 'alpha')" >> $GITHUB_OUTPUT
echo "proRepos=$(echo '${{ needs.get-plugins.outputs.alpha-plugins }}')" >> $GITHUB_OUTPUT
else
# rc
echo "defaultTag=$(echo 'latest')" >> $GITHUB_OUTPUT
echo "proRepos=$(echo '${{ needs.get-plugins.outputs.rc-plugins }}')" >> $GITHUB_OUTPUT
fi
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: nocobase,pro-plugins,${{ join(fromJSON(steps.get-info.outputs.proRepos), ',') }},${{ join(fromJSON(needs.get-plugins.outputs.custom-plugins), ',') }}
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ inputs.ref_name || github.ref_name }}
- name: yarn install
run: |
yarn install
- name: Checkout pro-plugins
uses: actions/checkout@v3
with:
repository: nocobase/pro-plugins
path: packages/pro-plugins
ref: ${{ inputs.ref_name || github.ref_name }}
token: ${{ steps.app-token.outputs.token }}
- name: Clone pro repos
shell: bash
run: |
for repo in ${{ join(fromJSON(steps.get-info.outputs.proRepos), ' ') }} ${{ join(fromJSON(needs.get-plugins.outputs.custom-plugins), ' ') }}
do
git clone -b ${{ inputs.ref_name || github.ref_name }} https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/nocobase/$repo.git packages/pro-plugins/@nocobase/$repo
done
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
nocobase/nocobase
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Login to Aliyun Container Registry
uses: docker/login-action@v2
with:
registry: ${{ secrets.ALI_DOCKER_REGISTRY }}
username: ${{ secrets.ALI_DOCKER_USERNAME }}
password: ${{ secrets.ALI_DOCKER_PASSWORD }}
- name: Set variables
run: |
target_directory="./packages/pro-plugins/@nocobase"
subdirectories=$(find "$target_directory" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | tr '\n' ' ')
trimmed_variable=$(echo "$subdirectories" | xargs)
packageNames="@nocobase/${trimmed_variable// / @nocobase/}"
pluginNames="${trimmed_variable//plugin-/}"
BEFORE_PACK_NOCOBASE="yarn add @nocobase/plugin-notifications @nocobase/plugin-disable-pm-add $packageNames -W --production"
APPEND_PRESET_LOCAL_PLUGINS="notifications,disable-pm-add,${pluginNames// /,}"
echo "var1=$BEFORE_PACK_NOCOBASE" >> $GITHUB_OUTPUT
echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
file: Dockerfile
build-args: |
VERDACCIO_URL=http://localhost:4873/
COMMIT_HASH=${GITHUB_SHA}
PLUGINS_DIRS=pro-plugins
BEFORE_PACK_NOCOBASE=${{ steps.vars.outputs.var1 }}
APPEND_PRESET_LOCAL_PLUGINS=${{ steps.vars.outputs.var2 }}
push: true
tags: ${{ secrets.ALI_DOCKER_PUBLIC_REGISTRY }}/nocobase/nocobase:${{ steps.get-info.outputs.defaultTag }},${{ secrets.ALI_DOCKER_PUBLIC_REGISTRY }}/${{ steps.meta.outputs.tags }}

View File

@ -0,0 +1,619 @@
name: Manual npm publish license-kit
env:
DEBUG: napi:*
APP_NAME: license-kit
MACOSX_DEPLOYMENT_TARGET: '10.13'
permissions:
contents: write
id-token: write
on:
workflow_dispatch:
# 'on':
# push:
# branches:
# - main
# tags-ignore:
# - '**'
# paths-ignore:
# - '**/*.md'
# - LICENSE
# - '**/*.gitignore'
# - .editorconfig
# - docs/**
# pull_request: null
jobs:
build:
strategy:
fail-fast: false
matrix:
settings:
- host: macos-latest
target: x86_64-apple-darwin
build: yarn build --target x86_64-apple-darwin
- host: windows-latest
build: yarn build --target x86_64-pc-windows-msvc
target: x86_64-pc-windows-msvc
- host: windows-latest
build: yarn build --target i686-pc-windows-msvc
target: i686-pc-windows-msvc
- host: ubuntu-latest
target: x86_64-unknown-linux-gnu
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
build: yarn build --target x86_64-unknown-linux-gnu
- host: ubuntu-latest
target: x86_64-unknown-linux-musl
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
build: yarn build --target x86_64-unknown-linux-musl
- host: macos-latest
target: aarch64-apple-darwin
build: yarn build --target aarch64-apple-darwin
- host: ubuntu-latest
target: aarch64-unknown-linux-gnu
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
build: yarn build --target aarch64-unknown-linux-gnu
- host: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
setup: |
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf -y
build: yarn build --target armv7-unknown-linux-gnueabihf
- host: ubuntu-latest
target: armv7-unknown-linux-musleabihf
build: yarn build --target armv7-unknown-linux-musleabihf
- host: ubuntu-latest
target: aarch64-linux-android
build: yarn build --target aarch64-linux-android
- host: ubuntu-latest
target: armv7-linux-androideabi
build: yarn build --target armv7-linux-androideabi
- host: ubuntu-latest
target: aarch64-unknown-linux-musl
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
build: |-
set -e &&
rustup target add aarch64-unknown-linux-musl &&
yarn build --target aarch64-unknown-linux-musl
- host: windows-latest
target: aarch64-pc-windows-msvc
build: yarn build --target aarch64-pc-windows-msvc
- host: ubuntu-latest
target: riscv64gc-unknown-linux-gnu
setup: |
sudo apt-get update
sudo apt-get install gcc-riscv64-linux-gnu -y
build: yarn build --target riscv64gc-unknown-linux-gnu
name: stable - ${{ matrix.settings.target }} - node@20
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Setup node
uses: actions/setup-node@v4
if: ${{ !matrix.settings.docker }}
with:
node-version: 20
cache: yarn
- name: Install
uses: dtolnay/rust-toolchain@stable
if: ${{ !matrix.settings.docker }}
with:
toolchain: stable
targets: ${{ matrix.settings.target }}
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
.cargo-cache
target/
key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }}
- uses: goto-bus-stop/setup-zig@v2
if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' || matrix.settings.target == 'armv7-unknown-linux-musleabihf' }}
with:
version: 0.13.0
- name: Setup toolchain
run: ${{ matrix.settings.setup }}
if: ${{ matrix.settings.setup }}
shell: bash
- name: Setup node x86
if: matrix.settings.target == 'i686-pc-windows-msvc'
run: yarn config set supportedArchitectures.cpu "ia32"
shell: bash
- name: Install dependencies
run: yarn install
- name: Setup node x86
uses: actions/setup-node@v4
if: matrix.settings.target == 'i686-pc-windows-msvc'
with:
node-version: 20
cache: yarn
architecture: x86
- name: Install OpenSSL dev
if: ${{ matrix.settings.host == 'ubuntu-latest' }}
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libssl-dev
- name: Build in docker
uses: addnab/docker-run-action@v3
if: ${{ matrix.settings.docker }}
with:
image: ${{ matrix.settings.docker }}
options: '--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build'
run: ${{ matrix.settings.build }}
- name: Build
run: ${{ matrix.settings.build }}
if: ${{ !matrix.settings.docker }}
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.settings.target }}
path: ${{ env.APP_NAME }}.*.node
if-no-files-found: error
build-freebsd:
runs-on: ubuntu-latest
name: Build FreeBSD
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Build
id: build
uses: cross-platform-actions/action@v0.27.0
env:
DEBUG: napi:*
RUSTUP_IO_THREADS: 1
with:
operating_system: freebsd
version: '14.2'
memory: 8G
cpu_count: 3
environment_variables: 'DEBUG RUSTUP_IO_THREADS'
shell: bash
run: |
sudo pkg install -y -f curl node libnghttp2 npm openssl
sudo npm install -g yarn --ignore-scripts
curl https://sh.rustup.rs -sSf --output rustup.sh
sh rustup.sh -y --profile minimal --default-toolchain beta
source "$HOME/.cargo/env"
echo "~~~~ rustc --version ~~~~"
rustc --version
echo "~~~~ node -v ~~~~"
node -v
echo "~~~~ yarn --version ~~~~"
yarn --version
pwd
ls -lah
whoami
env
freebsd-version
yarn install
yarn build
rm -rf node_modules
rm -rf target
rm -rf .yarn/cache
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: bindings-freebsd
path: ${{ env.APP_NAME }}.*.node
if-no-files-found: error
test-macOS-windows-binding:
name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }}
needs:
- build
strategy:
fail-fast: false
matrix:
settings:
- host: macos-latest
target: x86_64-apple-darwin
- host: windows-latest
target: x86_64-pc-windows-msvc
node:
- '18'
- '20'
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: yarn
architecture: x64
- name: Install dependencies
run: yarn install
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: bindings-${{ matrix.settings.target }}
path: .
- name: List packages
run: ls -R .
shell: bash
- name: Test bindings
run: yarn test
test-linux-x64-gnu-binding:
name: Test bindings on Linux-x64-gnu - node@${{ matrix.node }}
needs:
- build
strategy:
fail-fast: false
matrix:
node:
- '18'
- '20'
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: yarn
- name: Install dependencies
run: yarn install
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: bindings-x86_64-unknown-linux-gnu
path: .
- name: List packages
run: ls -R .
shell: bash
- name: Test bindings
run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-slim yarn test
test-linux-x64-musl-binding:
name: Test bindings on x86_64-unknown-linux-musl - node@${{ matrix.node }}
needs:
- build
strategy:
fail-fast: false
matrix:
node:
- '18'
- '20'
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: yarn
- name: Install dependencies
run: |
yarn config set supportedArchitectures.libc "musl"
yarn install
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: bindings-x86_64-unknown-linux-musl
path: .
- name: List packages
run: ls -R .
shell: bash
- name: Test bindings
run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-alpine yarn test
test-linux-aarch64-gnu-binding:
name: Test bindings on aarch64-unknown-linux-gnu - node@${{ matrix.node }}
needs:
- build
strategy:
fail-fast: false
matrix:
node:
- '18'
- '20'
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: bindings-aarch64-unknown-linux-gnu
path: .
- name: List packages
run: ls -R .
shell: bash
- name: Install dependencies
run: |
yarn config set supportedArchitectures.cpu "arm64"
yarn config set supportedArchitectures.libc "glibc"
yarn install
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- name: Setup and run tests
uses: addnab/docker-run-action@v3
with:
image: node:${{ matrix.node }}-slim
options: '--platform linux/arm64 -v ${{ github.workspace }}:/build -w /build'
run: |
set -e
yarn test
ls -la
test-linux-aarch64-musl-binding:
name: Test bindings on aarch64-unknown-linux-musl - node@${{ matrix.node }}
needs:
- build
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: bindings-aarch64-unknown-linux-musl
path: .
- name: List packages
run: ls -R .
shell: bash
- name: Install dependencies
run: |
yarn config set supportedArchitectures.cpu "arm64"
yarn config set supportedArchitectures.libc "musl"
yarn install
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- name: Setup and run tests
uses: addnab/docker-run-action@v3
with:
image: node:lts-alpine
options: '--platform linux/arm64 -v ${{ github.workspace }}:/build -w /build'
run: |
set -e
yarn test
test-linux-arm-gnueabihf-binding:
name: Test bindings on armv7-unknown-linux-gnueabihf - node@${{ matrix.node }}
needs:
- build
strategy:
fail-fast: false
matrix:
node:
- '18'
- '20'
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: bindings-armv7-unknown-linux-gnueabihf
path: .
- name: List packages
run: ls -R .
shell: bash
- name: Install dependencies
run: |
yarn config set supportedArchitectures.cpu "arm"
yarn install
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- name: Setup and run tests
uses: addnab/docker-run-action@v3
with:
image: node:${{ matrix.node }}-bullseye-slim
options: '--platform linux/arm/v7 -v ${{ github.workspace }}:/build -w /build'
run: |
set -e
yarn test
ls -la
universal-macOS:
name: Build universal macOS binary
needs:
- build
runs-on: macos-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
- name: Install dependencies
run: yarn install
- name: Download macOS x64 artifact
uses: actions/download-artifact@v4
with:
name: bindings-x86_64-apple-darwin
path: artifacts
- name: Download macOS arm64 artifact
uses: actions/download-artifact@v4
with:
name: bindings-aarch64-apple-darwin
path: artifacts
- name: Combine binaries
run: yarn universal
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: bindings-universal-apple-darwin
path: ${{ env.APP_NAME }}.*.node
if-no-files-found: error
publish:
name: Publish
runs-on: ubuntu-latest
needs:
- build-freebsd
- test-macOS-windows-binding
- test-linux-x64-gnu-binding
- test-linux-x64-musl-binding
- test-linux-aarch64-gnu-binding
- test-linux-aarch64-musl-binding
- test-linux-arm-gnueabihf-binding
- universal-macOS
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: license-kit
skip-token-revoke: true
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/license-kit
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
ref: main
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
- name: Install dependencies
run: yarn install
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Move artifacts
run: yarn artifacts
- name: List packages
run: ls -R ./npm
shell: bash
- name: Publish
run: |
npm config set provenance true
if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$";
then
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
npm publish --access public
elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+";
then
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
npm publish --tag next --access public
else
echo "Not a release, skipping publish"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -5,38 +5,40 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
on: on:
push: workflow_dispatch:
branches:
- main # push:
- next # branches:
- develop # - main
paths: # - next
- 'package.json' # - develop
- '**/yarn.lock' # paths:
- 'packages/core/acl/**' # - 'package.json'
- 'packages/core/auth/**' # - '**/yarn.lock'
- 'packages/core/actions/**' # - 'packages/core/acl/**'
- 'packages/core/database/**' # - 'packages/core/auth/**'
- 'packages/core/resourcer/**' # - 'packages/core/actions/**'
- 'packages/core/data-source-manager/**' # - 'packages/core/database/**'
- 'packages/core/server/**' # - 'packages/core/resourcer/**'
- 'packages/core/utils/**' # - 'packages/core/data-source-manager/**'
- 'packages/plugins/**/src/server/**' # - 'packages/core/server/**'
- '.github/workflows/nocobase-test-backend.yml' # - 'packages/core/utils/**'
pull_request: # - 'packages/plugins/**/src/server/**'
paths: # - '.github/workflows/nocobase-test-backend.yml'
- 'package.json' # pull_request:
- '**/yarn.lock' # paths:
- 'packages/core/acl/**' # - 'package.json'
- 'packages/core/auth/**' # - '**/yarn.lock'
- 'packages/core/actions/**' # - 'packages/core/acl/**'
- 'packages/core/database/**' # - 'packages/core/auth/**'
- 'packages/core/resourcer/**' # - 'packages/core/actions/**'
- 'packages/core/data-source-manager/**' # - 'packages/core/database/**'
- 'packages/core/server/**' # - 'packages/core/resourcer/**'
- 'packages/core/utils/**' # - 'packages/core/data-source-manager/**'
- 'packages/plugins/**/src/server/**' # - 'packages/core/server/**'
- '.github/workflows/nocobase-test-backend.yml' # - 'packages/core/utils/**'
# - 'packages/plugins/**/src/server/**'
# - '.github/workflows/nocobase-test-backend.yml'
jobs: jobs:
sqlite-test: sqlite-test:
@ -59,7 +61,9 @@ jobs:
node-version: ${{ matrix.node_version }} node-version: ${{ matrix.node_version }}
cache: 'yarn' cache: 'yarn'
- name: Install project dependencies - name: Install project dependencies
run: yarn install run: |
yarn install
yarn add sqlite3 --no-save -W
- name: Test with Sqlite - name: Test with Sqlite
run: yarn test --server --single-thread=false run: yarn test --server --single-thread=false
env: env:

View File

@ -63,7 +63,9 @@ jobs:
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-
- name: Install project dependencies - name: Install project dependencies
run: yarn --prefer-offline run: |
yarn --prefer-offline
yarn add sqlite3 --no-save -W
- name: Test with Sqlite - name: Test with Sqlite
run: yarn test --server --single-thread=false run: yarn test --server --single-thread=false

View File

@ -5,6 +5,173 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.6.25](https://github.com/nocobase/nocobase/compare/v1.6.24...v1.6.25) - 2025-04-29
### 🎉 New Features
- **[undefined]** add publish ci for license kit ([#6786](https://github.com/nocobase/nocobase/pull/6786)) by @jiannx
- **[Data visualization: ECharts]** Add "Y-Axis inverse" setting for bar charts by @2013xile
### 🚀 Improvements
- **[utils]** Increase the height of the filter button field list and sort/categorize the fields ([#6779](https://github.com/nocobase/nocobase/pull/6779)) by @zhangzhonghe
- **[client]** optimize subtable add button style and align paginator on the same row ([#6790](https://github.com/nocobase/nocobase/pull/6790)) by @katherinehhh
- **[File manager]** Add OSS timeout option default to 10min ([#6795](https://github.com/nocobase/nocobase/pull/6795)) by @mytharcher
- **[Password policy]** Change default password expiration to never expire by @2013xile
- **[WeCom]** Prioritize corporate email over personal email when updating the user's email by @2013xile
### 🐛 Bug Fixes
- **[client]**
- In the collapse block, clicking the clear button in the relationship field search box should not delete the data range ([#6782](https://github.com/nocobase/nocobase/pull/6782)) by @zhangzhonghe
- association field not submitting data when displaying field from related collection ([#6798](https://github.com/nocobase/nocobase/pull/6798)) by @katherinehhh
- Prohibit moving menus before or after page tabs ([#6777](https://github.com/nocobase/nocobase/pull/6777)) by @zhangzhonghe
- Table block displays duplicate data when filtering ([#6792](https://github.com/nocobase/nocobase/pull/6792)) by @zhangzhonghe
- In the filter form, switching the field operator and then refreshing the page causes an error ([#6781](https://github.com/nocobase/nocobase/pull/6781)) by @zhangzhonghe
- **[database]**
- Avoid error thrown when data type is not string ([#6797](https://github.com/nocobase/nocobase/pull/6797)) by @mytharcher
- add unavailableActions to sql collection and view collection ([#6765](https://github.com/nocobase/nocobase/pull/6765)) by @katherinehhh
- **[create-nocobase-app]** Temporarily fix mariadb issue by downgrading to 2.5.6 ([#6762](https://github.com/nocobase/nocobase/pull/6762)) by @chenos
- **[Authentication]** Disallow changing authenticator name ([#6808](https://github.com/nocobase/nocobase/pull/6808)) by @2013xile
- **[Template print]** Fix: Correct permission validation logic to prevent unauthorized actions. by @sheldon66
- **[File storage: S3(Pro)]** access url expiration invalid by @jiannx
- **[Block: Tree]** After connecting through a foreign key, clicking to trigger filtering results in empty filter conditions by @zhangzhonghe
## [v1.6.24](https://github.com/nocobase/nocobase/compare/v1.6.23...v1.6.24) - 2025-04-24
### 🚀 Improvements
- **[client]** Adjust upload message ([#6757](https://github.com/nocobase/nocobase/pull/6757)) by @mytharcher
### 🐛 Bug Fixes
- **[client]**
- only export action in view collection is support when writableView is false ([#6763](https://github.com/nocobase/nocobase/pull/6763)) by @katherinehhh
- unexpected association data creation when displaying association field under sub-form/sub-table in create form ([#6727](https://github.com/nocobase/nocobase/pull/6727)) by @katherinehhh
- Incorrect data retrieved for many-to-many array fields from related tables in forms ([#6744](https://github.com/nocobase/nocobase/pull/6744)) by @2013xile
## [v1.6.23](https://github.com/nocobase/nocobase/compare/v1.6.22...v1.6.23) - 2025-04-23
### 🚀 Improvements
- **[cli]** Optimize internal logic of the `nocobase upgrade` command ([#6754](https://github.com/nocobase/nocobase/pull/6754)) by @chenos
- **[Template print]** Replaced datasource action control with client role-based access control. by @sheldon66
### 🐛 Bug Fixes
- **[cli]** Auto-update package.json on upgrade ([#6747](https://github.com/nocobase/nocobase/pull/6747)) by @chenos
- **[client]**
- missing filter for already associated data when adding association data ([#6750](https://github.com/nocobase/nocobase/pull/6750)) by @katherinehhh
- tree table 'Add Child' button linkage rule missing 'current record' ([#6752](https://github.com/nocobase/nocobase/pull/6752)) by @katherinehhh
- **[Action: Import records]** Fix the import and export exceptions that occur when setting field permissions. ([#6677](https://github.com/nocobase/nocobase/pull/6677)) by @aaaaaajie
- **[Block: Gantt]** gantt chart block overlapping months in calendar header for month view ([#6753](https://github.com/nocobase/nocobase/pull/6753)) by @katherinehhh
- **[Action: Export records Pro]**
- pro export button losing filter parameters after sorting table column by @katherinehhh
- Fix the import and export exceptions that occur when setting field permissions. by @aaaaaajie
- **[File storage: S3(Pro)]** Fix response data of uploaded file by @mytharcher
- **[Workflow: Approval]** Fix preload association fields for records by @mytharcher
## [v1.6.22](https://github.com/nocobase/nocobase/compare/v1.6.21...v1.6.22) - 2025-04-22
### 🚀 Improvements
- **[create-nocobase-app]** Upgrade dependencies and remove SQLite support ([#6708](https://github.com/nocobase/nocobase/pull/6708)) by @chenos
- **[File manager]** Expose utils API ([#6705](https://github.com/nocobase/nocobase/pull/6705)) by @mytharcher
- **[Workflow]** Add date types to variable types set ([#6717](https://github.com/nocobase/nocobase/pull/6717)) by @mytharcher
### 🐛 Bug Fixes
- **[client]**
- The problem of mobile top navigation bar icons being difficult to delete ([#6734](https://github.com/nocobase/nocobase/pull/6734)) by @zhangzhonghe
- After connecting through a foreign key, clicking to trigger filtering results in empty filter conditions ([#6634](https://github.com/nocobase/nocobase/pull/6634)) by @zhangzhonghe
- picker switching issue in date field of filter button ([#6695](https://github.com/nocobase/nocobase/pull/6695)) by @katherinehhh
- The issue of the collapse button in the left menu being obscured by the workflow pop-up window ([#6733](https://github.com/nocobase/nocobase/pull/6733)) by @zhangzhonghe
- missing action option constraints when reopening linkage rules ([#6723](https://github.com/nocobase/nocobase/pull/6723)) by @katherinehhh
- export button shown without export permission ([#6689](https://github.com/nocobase/nocobase/pull/6689)) by @katherinehhh
- Required fields hidden by linkage rules should not affect form submission ([#6709](https://github.com/nocobase/nocobase/pull/6709)) by @zhangzhonghe
- **[server]** appVersion incorrectly generated by create-migration ([#6740](https://github.com/nocobase/nocobase/pull/6740)) by @chenos
- **[build]** Fix error thrown in tar command ([#6722](https://github.com/nocobase/nocobase/pull/6722)) by @mytharcher
- **[Workflow]** Fix error thrown when execute schedule event in subflow ([#6721](https://github.com/nocobase/nocobase/pull/6721)) by @mytharcher
- **[Workflow: Custom action event]** Support to execute in multiple records mode by @mytharcher
- **[File storage: S3(Pro)]** Add multer make logic for server-side upload by @mytharcher
## [v1.6.21](https://github.com/nocobase/nocobase/compare/v1.6.20...v1.6.21) - 2025-04-17
### 🚀 Improvements
- **[client]** Add delay API for scenarios which open without delay ([#6681](https://github.com/nocobase/nocobase/pull/6681)) by @mytharcher
- **[create-nocobase-app]** Upgrade some dependencies to latest versions ([#6673](https://github.com/nocobase/nocobase/pull/6673)) by @chenos
### 🐛 Bug Fixes
- **[client]**
- Fix error thrown when mouse hover on referenced template block in approval node configuration ([#6691](https://github.com/nocobase/nocobase/pull/6691)) by @mytharcher
- custom association field not displaying field component settings ([#6692](https://github.com/nocobase/nocobase/pull/6692)) by @katherinehhh
- Fix locale for upload component ([#6682](https://github.com/nocobase/nocobase/pull/6682)) by @mytharcher
- lazy load missing ui component will cause render error ([#6683](https://github.com/nocobase/nocobase/pull/6683)) by @gchust
- Add native Password component to HoC Input ([#6679](https://github.com/nocobase/nocobase/pull/6679)) by @mytharcher
- inherited fields shown in current collection field assignment list ([#6666](https://github.com/nocobase/nocobase/pull/6666)) by @katherinehhh
- **[database]** Fixed ci build error ([#6687](https://github.com/nocobase/nocobase/pull/6687)) by @aaaaaajie
- **[build]** build output is incorrect when plugin depends on some AMD libraries ([#6665](https://github.com/nocobase/nocobase/pull/6665)) by @gchust
- **[Action: Import records]** fixed an error importing xlsx time field ([#6672](https://github.com/nocobase/nocobase/pull/6672)) by @aaaaaajie
- **[Workflow: Manual node]** Fix manual task status constant ([#6676](https://github.com/nocobase/nocobase/pull/6676)) by @mytharcher
- **[Block: iframe]** vertical scrollbar appears when iframe block is set to full height ([#6675](https://github.com/nocobase/nocobase/pull/6675)) by @katherinehhh
- **[Workflow: Custom action event]** Fix test cases by @mytharcher
- **[Backup manager]** timeout error occurs when trying to restore an unecrypted backup with a password by @gchust
## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14 ## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14
### 🎉 New Features ### 🎉 New Features

View File

@ -5,6 +5,173 @@
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。 并且本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。
## [v1.6.25](https://github.com/nocobase/nocobase/compare/v1.6.24...v1.6.25) - 2025-04-29
### 🎉 新特性
- **[undefined]** 添加 license kit 发包ci ([#6786](https://github.com/nocobase/nocobase/pull/6786)) by @jiannx
- **[数据可视化EChrats]** 条形图支持“y轴反向”设置 by @2013xile
### 🚀 优化
- **[utils]** 增加筛选按钮字段列表的高度,和对字段进行排序分类 ([#6779](https://github.com/nocobase/nocobase/pull/6779)) by @zhangzhonghe
- **[client]** 优化子表格添加按钮样式,并将分页器与按钮排列在同一行 ([#6790](https://github.com/nocobase/nocobase/pull/6790)) by @katherinehhh
- **[文件管理器]** 增加 OSS 存储引擎的超时时间配置项,且默认为 10 分钟 ([#6795](https://github.com/nocobase/nocobase/pull/6795)) by @mytharcher
- **[密码策略]** 默认密码过期时间修改为不过期 by @2013xile
- **[企业微信]** 更新用户邮箱时优先使用企业邮箱而不是个人邮箱 by @2013xile
### 🐛 修复
- **[client]**
- 折叠面板区块中,当点击关系字段搜索框的清空按钮后,不应该删除数据范围 ([#6782](https://github.com/nocobase/nocobase/pull/6782)) by @zhangzhonghe
- 关系字段,在显示关系表下的字段数据时不提交数据 ([#6798](https://github.com/nocobase/nocobase/pull/6798)) by @katherinehhh
- 禁止将菜单移动到页面 tab 的前面和后面 ([#6777](https://github.com/nocobase/nocobase/pull/6777)) by @zhangzhonghe
- 表格区块在筛选时重复显示数据 ([#6792](https://github.com/nocobase/nocobase/pull/6792)) by @zhangzhonghe
- 筛选表单中,切换字段操作符后,刷新页面会报错 ([#6781](https://github.com/nocobase/nocobase/pull/6781)) by @zhangzhonghe
- **[database]**
- 避免文本类型输入数据不是字符串时的报错 ([#6797](https://github.com/nocobase/nocobase/pull/6797)) by @mytharcher
- 补充sql collection和view collection 的unavailableActions ([#6765](https://github.com/nocobase/nocobase/pull/6765)) by @katherinehhh
- **[create-nocobase-app]** 回退 mariadb 版本至 2.5.6,解决兼容性问题 ([#6762](https://github.com/nocobase/nocobase/pull/6762)) by @chenos
- **[用户认证]** 不允许修改认证器标识 ([#6808](https://github.com/nocobase/nocobase/pull/6808)) by @2013xile
- **[模板打印]** 修复:修正权限校验逻辑,防止未授权操作。 by @sheldon66
- **[文件存储S3 (Pro)]** 访问地址有效期无效 by @jiannx
- **[区块:树]** 通过外键连接后,点击触发筛选,筛选条件为空 by @zhangzhonghe
## [v1.6.24](https://github.com/nocobase/nocobase/compare/v1.6.23...v1.6.24) - 2025-04-24
### 🚀 优化
- **[client]** 调整上传文件的提示信息 ([#6757](https://github.com/nocobase/nocobase/pull/6757)) by @mytharcher
### 🐛 修复
- **[client]**
- 视图表,无编辑权限时允许显示导出按钮 ([#6763](https://github.com/nocobase/nocobase/pull/6763)) by @katherinehhh
- 新增表单中显示关系字段子表格/子表单时关系数据也被新增 ([#6727](https://github.com/nocobase/nocobase/pull/6727)) by @katherinehhh
- 在表单中获取关联表中的多对多数组字段数据不正确 ([#6744](https://github.com/nocobase/nocobase/pull/6744)) by @2013xile
## [v1.6.23](https://github.com/nocobase/nocobase/compare/v1.6.22...v1.6.23) - 2025-04-23
### 🚀 优化
- **[cli]** 优化 `nocobase upgrade` 命令的内部实现逻辑 ([#6754](https://github.com/nocobase/nocobase/pull/6754)) by @chenos
- **[模板打印]** 用客户端角色访问控制替换了数据源操作权限控制。 by @sheldon66
### 🐛 修复
- **[cli]** 升级时自动更新项目的 package.json ([#6747](https://github.com/nocobase/nocobase/pull/6747)) by @chenos
- **[client]**
- 添加关联表格时未过滤已关联的数据 ([#6750](https://github.com/nocobase/nocobase/pull/6750)) by @katherinehhh
- 树表格中添加子记录按钮的联动规则缺失「当前记录」变量 ([#6752](https://github.com/nocobase/nocobase/pull/6752)) by @katherinehhh
- **[操作:导入记录]** 修复设置字段权限时出现的导入导出异常。 ([#6677](https://github.com/nocobase/nocobase/pull/6677)) by @aaaaaajie
- **[区块:甘特图]** 甘特图区块设置月份视图时,日历头部月份重叠 ([#6753](https://github.com/nocobase/nocobase/pull/6753)) by @katherinehhh
- **[操作:导出记录 Pro]**
- pro导出按钮在点击表格排序后丢失过滤参数 by @katherinehhh
- 修复设置字段权限时出现的导入导出异常。 by @aaaaaajie
- **[文件存储S3 (Pro)]** 修复已上传文件的响应数据 by @mytharcher
- **[工作流:审批]** 修复预加载审批记录数据的关系字段 by @mytharcher
## [v1.6.22](https://github.com/nocobase/nocobase/compare/v1.6.21...v1.6.22) - 2025-04-22
### 🚀 优化
- **[create-nocobase-app]** 更新依赖,移除 SQLite 支持 ([#6708](https://github.com/nocobase/nocobase/pull/6708)) by @chenos
- **[文件管理器]** 暴露公共包 API ([#6705](https://github.com/nocobase/nocobase/pull/6705)) by @mytharcher
- **[工作流]** 为变量的类型集合增加日期相关类型 ([#6717](https://github.com/nocobase/nocobase/pull/6717)) by @mytharcher
### 🐛 修复
- **[client]**
- 移动端顶部的导航栏图标很难被删除的问题 ([#6734](https://github.com/nocobase/nocobase/pull/6734)) by @zhangzhonghe
- 通过外键连接后,点击触发筛选,筛选条件为空 ([#6634](https://github.com/nocobase/nocobase/pull/6634)) by @zhangzhonghe
- 筛选按钮中日期字段切换picker 异常 ([#6695](https://github.com/nocobase/nocobase/pull/6695)) by @katherinehhh
- 左侧菜单的收起按钮会被绑定工作流弹窗遮挡的问题 ([#6733](https://github.com/nocobase/nocobase/pull/6733)) by @zhangzhonghe
- 重新打开联动规则时缺少操作选项约束 ([#6723](https://github.com/nocobase/nocobase/pull/6723)) by @katherinehhh
- 未设置导出权限时仍显示导出按钮 ([#6689](https://github.com/nocobase/nocobase/pull/6689)) by @katherinehhh
- 被联动规则隐藏的必填字段,不应该影响表单的提交 ([#6709](https://github.com/nocobase/nocobase/pull/6709)) by @zhangzhonghe
- **[server]** create-migration 命令生成的 appVersion 不准确 ([#6740](https://github.com/nocobase/nocobase/pull/6740)) by @chenos
- **[build]** 修复 tar 命令报错的问题 ([#6722](https://github.com/nocobase/nocobase/pull/6722)) by @mytharcher
- **[工作流]** 修复子流程执行定时任务报错的问题 ([#6721](https://github.com/nocobase/nocobase/pull/6721)) by @mytharcher
- **[工作流:自定义操作事件]** 支持多行记录模式的手动执行 by @mytharcher
- **[文件存储S3 (Pro)]** 增加 multer 逻辑用于服务端上传 by @mytharcher
## [v1.6.21](https://github.com/nocobase/nocobase/compare/v1.6.20...v1.6.21) - 2025-04-17
### 🚀 优化
- **[client]** 为弹窗组件增加 delay API ([#6681](https://github.com/nocobase/nocobase/pull/6681)) by @mytharcher
- **[create-nocobase-app]** 升级部分依赖的版本 ([#6673](https://github.com/nocobase/nocobase/pull/6673)) by @chenos
### 🐛 修复
- **[client]**
- 修复审批节点配置中引用模板区块的添加按钮报错问题 ([#6691](https://github.com/nocobase/nocobase/pull/6691)) by @mytharcher
- 自定义的关系字段没有显示关系字段组件 ([#6692](https://github.com/nocobase/nocobase/pull/6692)) by @katherinehhh
- 修复上传组件语言问题 ([#6682](https://github.com/nocobase/nocobase/pull/6682)) by @mytharcher
- 懒加载组件不存在时界面报错 ([#6683](https://github.com/nocobase/nocobase/pull/6683)) by @gchust
- 补全原生的 Password 组件到封装过的输入组件 ([#6679](https://github.com/nocobase/nocobase/pull/6679)) by @mytharcher
- 字段赋值本表字段列表中显示了继承表字段,应只显示本表字段 ([#6666](https://github.com/nocobase/nocobase/pull/6666)) by @katherinehhh
- **[database]** 修复 CI 编译错误 ([#6687](https://github.com/nocobase/nocobase/pull/6687)) by @aaaaaajie
- **[build]** 插件依赖 AMD 库时构建产物不正确 ([#6665](https://github.com/nocobase/nocobase/pull/6665)) by @gchust
- **[操作:导入记录]** 修复导入包含时间字段的 xlsx 错误 ([#6672](https://github.com/nocobase/nocobase/pull/6672)) by @aaaaaajie
- **[工作流:人工处理节点]** 修复人工节点任务状态常量 ([#6676](https://github.com/nocobase/nocobase/pull/6676)) by @mytharcher
- **[区块iframe]** iframe 区块设置全高时页面出现滚动条 ([#6675](https://github.com/nocobase/nocobase/pull/6675)) by @katherinehhh
- **[工作流:自定义操作事件]** 修复测试用例 by @mytharcher
- **[备份管理器]** 还原时若备份未设置密码,但用户输入了密码,还原会出现超时报错 by @gchust
## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14 ## [v1.6.20](https://github.com/nocobase/nocobase/compare/v1.6.19...v1.6.20) - 2025-04-14
### 🎉 新特性 ### 🎉 新特性

View File

@ -8,7 +8,11 @@ RUN cd /app \
&& yarn config set network-timeout 600000 -g \ && yarn config set network-timeout 600000 -g \
&& npx -y create-nocobase-app@${CNA_VERSION} my-nocobase-app --skip-dev-dependencies -a -e APP_ENV=production \ && npx -y create-nocobase-app@${CNA_VERSION} my-nocobase-app --skip-dev-dependencies -a -e APP_ENV=production \
&& cd /app/my-nocobase-app \ && cd /app/my-nocobase-app \
&& yarn install --production && yarn install --production \
&& rm -rf yarn.lock \
&& find node_modules -type f -name "yarn.lock" -delete \
&& find node_modules -type f -name "bower.json" -delete \
&& find node_modules -type f -name "composer.json" -delete
RUN cd /app \ RUN cd /app \
&& rm -rf nocobase.tar.gz \ && rm -rf nocobase.tar.gz \

View File

@ -1,5 +1,5 @@
{ {
"version": "1.7.0-beta.18", "version": "1.7.0-beta.26",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"npmClientArgs": ["--ignore-engines"], "npmClientArgs": ["--ignore-engines"],

View File

@ -83,6 +83,7 @@
"ghooks": "^2.0.4", "ghooks": "^2.0.4",
"lint-staged": "^13.2.3", "lint-staged": "^13.2.3",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"pm2": "^6.0.5",
"pretty-format": "^24.0.0", "pretty-format": "^24.0.0",
"pretty-quick": "^3.1.0", "pretty-quick": "^3.1.0",
"react": "^18.0.0", "react": "^18.0.0",

View File

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

View File

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

View File

@ -30,7 +30,9 @@ function findArgs(ctx: Context) {
} }
async function listWithPagination(ctx: Context) { async function listWithPagination(ctx: Context) {
const { page = DEFAULT_PAGE, pageSize = DEFAULT_PER_PAGE } = ctx.action.params; const params = ctx.action.params;
const { values = {}, others } = params;
const { page = DEFAULT_PAGE, pageSize = DEFAULT_PER_PAGE } = { ...others, ...values };
const repository = getRepositoryFromParams(ctx); const repository = getRepositoryFromParams(ctx);

View File

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

View File

@ -1,18 +1,18 @@
{ {
"name": "@nocobase/auth", "name": "@nocobase/auth",
"version": "1.7.0-beta.18", "version": "1.7.0-beta.26",
"description": "", "description": "",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/actions": "1.7.0-beta.18", "@nocobase/actions": "1.7.0-beta.26",
"@nocobase/cache": "1.7.0-beta.18", "@nocobase/cache": "1.7.0-beta.26",
"@nocobase/database": "1.7.0-beta.18", "@nocobase/database": "1.7.0-beta.26",
"@nocobase/resourcer": "1.7.0-beta.18", "@nocobase/resourcer": "1.7.0-beta.26",
"@nocobase/utils": "1.7.0-beta.18", "@nocobase/utils": "1.7.0-beta.26",
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^9.0.9",
"jsonwebtoken": "^8.5.1" "jsonwebtoken": "^9.0.2"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/build", "name": "@nocobase/build",
"version": "1.7.0-beta.18", "version": "1.7.0-beta.26",
"description": "Library build tool based on rollup.", "description": "Library build tool based on rollup.",
"main": "lib/index.js", "main": "lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
@ -39,7 +39,7 @@
"postcss-preset-env": "^9.1.2", "postcss-preset-env": "^9.1.2",
"react-imported-component": "^6.5.4", "react-imported-component": "^6.5.4",
"style-loader": "^3.3.3", "style-loader": "^3.3.3",
"tar": "^6.2.0", "tar": "^7.4.3",
"tsup": "8.2.4", "tsup": "8.2.4",
"typescript": "5.1.3", "typescript": "5.1.3",
"update-notifier": "3.0.0", "update-notifier": "3.0.0",

View File

@ -8,7 +8,7 @@
*/ */
import path from 'path'; import path from 'path';
import tar from 'tar'; import { create } from 'tar';
import fg from 'fast-glob'; import fg from 'fast-glob';
import fs from 'fs-extra'; import fs from 'fs-extra';
@ -38,5 +38,5 @@ export function tarPlugin(cwd: string, log: PkgLog) {
fs.mkdirpSync(path.dirname(tarball)); fs.mkdirpSync(path.dirname(tarball));
fs.rmSync(tarball, { force: true }); fs.rmSync(tarball, { force: true });
return tar.c({ gzip: true, file: tarball, cwd }, tarFiles); return create({ gzip: true, file: tarball, cwd }, tarFiles);
} }

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/cli", "name": "@nocobase/cli",
"version": "1.7.0-beta.18", "version": "1.7.0-beta.26",
"description": "", "description": "",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./src/index.js", "main": "./src/index.js",
@ -8,11 +8,12 @@
"nocobase": "./bin/index.js" "nocobase": "./bin/index.js"
}, },
"dependencies": { "dependencies": {
"@nocobase/app": "1.7.0-beta.18", "@nocobase/app": "1.7.0-beta.26",
"@types/fs-extra": "^11.0.1", "@types/fs-extra": "^11.0.1",
"@umijs/utils": "3.5.20", "@umijs/utils": "3.5.20",
"chalk": "^4.1.1", "chalk": "^4.1.1",
"commander": "^9.2.0", "commander": "^9.2.0",
"deepmerge": "^4.3.1",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"execa": "^5.1.1", "execa": "^5.1.1",
"fast-glob": "^3.3.1", "fast-glob": "^3.3.1",
@ -20,11 +21,12 @@
"p-all": "3.0.0", "p-all": "3.0.0",
"pm2": "^6.0.5", "pm2": "^6.0.5",
"portfinder": "^1.0.28", "portfinder": "^1.0.28",
"tar": "^7.4.3",
"tree-kill": "^1.2.2", "tree-kill": "^1.2.2",
"tsx": "^4.19.0" "tsx": "^4.19.0"
}, },
"devDependencies": { "devDependencies": {
"@nocobase/devtools": "1.7.0-beta.18" "@nocobase/devtools": "1.7.0-beta.26"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -8,7 +8,7 @@
*/ */
const _ = require('lodash'); const _ = require('lodash');
const { Command } = require('commander'); const { Command } = require('commander');
const { generatePlugins, run, postCheck, nodeCheck, promptForTs, isPortReachable } = require('../util'); const { generatePlugins, run, postCheck, nodeCheck, promptForTs, isPortReachable, checkDBDialect } = require('../util');
const { getPortPromise } = require('portfinder'); const { getPortPromise } = require('portfinder');
const chokidar = require('chokidar'); const chokidar = require('chokidar');
const { uid } = require('@formily/shared'); const { uid } = require('@formily/shared');
@ -36,6 +36,7 @@ module.exports = (cli) => {
.option('-i, --inspect [port]') .option('-i, --inspect [port]')
.allowUnknownOption() .allowUnknownOption()
.action(async (opts) => { .action(async (opts) => {
checkDBDialect();
let subprocess; let subprocess;
const runDevClient = () => { const runDevClient = () => {
console.log('starting client', 1 * clientPort); console.log('starting client', 1 * clientPort);

View File

@ -8,7 +8,7 @@
*/ */
const { Command } = require('commander'); const { Command } = require('commander');
const { run, isPortReachable } = require('../util'); const { run, isPortReachable, checkDBDialect } = require('../util');
const { execSync } = require('node:child_process'); const { execSync } = require('node:child_process');
const axios = require('axios'); const axios = require('axios');
const { pTest } = require('./p-test'); const { pTest } = require('./p-test');
@ -165,6 +165,7 @@ const filterArgv = () => {
*/ */
module.exports = (cli) => { module.exports = (cli) => {
const e2e = cli.command('e2e').hook('preAction', () => { const e2e = cli.command('e2e').hook('preAction', () => {
checkDBDialect();
if (process.env.APP_BASE_URL) { if (process.env.APP_BASE_URL) {
process.env.APP_BASE_URL = process.env.APP_BASE_URL.replace('localhost', '127.0.0.1'); process.env.APP_BASE_URL = process.env.APP_BASE_URL.replace('localhost', '127.0.0.1');
console.log('APP_BASE_URL:', process.env.APP_BASE_URL); console.log('APP_BASE_URL:', process.env.APP_BASE_URL);

View File

@ -8,7 +8,7 @@
*/ */
const { Command } = require('commander'); const { Command } = require('commander');
const { run, isDev, isProd, promptForTs, downloadPro } = require('../util'); const { run, isDev, isProd, promptForTs, downloadPro, checkDBDialect } = require('../util');
/** /**
* *
@ -21,6 +21,7 @@ module.exports = (cli) => {
.option('-h, --help') .option('-h, --help')
.option('--ts-node-dev') .option('--ts-node-dev')
.action(async (options) => { .action(async (options) => {
checkDBDialect();
const cmd = process.argv.slice(2)?.[0]; const cmd = process.argv.slice(2)?.[0];
if (cmd === 'install') { if (cmd === 'install') {
await downloadPro(); await downloadPro();

View File

@ -30,6 +30,7 @@ module.exports = (cli) => {
require('./test')(cli); require('./test')(cli);
require('./test-coverage')(cli); require('./test-coverage')(cli);
require('./umi')(cli); require('./umi')(cli);
require('./update-deps')(cli);
require('./upgrade')(cli); require('./upgrade')(cli);
require('./postinstall')(cli); require('./postinstall')(cli);
require('./pkg')(cli); require('./pkg')(cli);

View File

@ -8,7 +8,7 @@
*/ */
const _ = require('lodash'); const _ = require('lodash');
const { Command } = require('commander'); const { Command } = require('commander');
const { run, postCheck, downloadPro, promptForTs } = require('../util'); const { run, postCheck, downloadPro, promptForTs, checkDBDialect } = require('../util');
const { existsSync, rmSync } = require('fs'); const { existsSync, rmSync } = require('fs');
const { resolve, isAbsolute } = require('path'); const { resolve, isAbsolute } = require('path');
const chalk = require('chalk'); const chalk = require('chalk');
@ -48,8 +48,10 @@ module.exports = (cli) => {
.option('-i, --instances [instances]') .option('-i, --instances [instances]')
.option('--db-sync') .option('--db-sync')
.option('--quickstart') .option('--quickstart')
.option('--launch-mode [launchMode]')
.allowUnknownOption() .allowUnknownOption()
.action(async (opts) => { .action(async (opts) => {
checkDBDialect();
if (opts.quickstart) { if (opts.quickstart) {
await downloadPro(); await downloadPro();
} }
@ -118,17 +120,27 @@ module.exports = (cli) => {
]); ]);
process.exit(); process.exit();
} else { } else {
run( const launchMode = opts.launchMode || process.env.APP_LAUNCH_MODE || 'pm2';
'pm2-runtime', if (launchMode === 'pm2') {
[ run(
'start', 'pm2-runtime',
...instancesArgs, [
`${APP_PACKAGE_ROOT}/lib/index.js`, 'start',
NODE_ARGS ? `--node-args="${NODE_ARGS}"` : undefined, ...instancesArgs,
'--', `${APP_PACKAGE_ROOT}/lib/index.js`,
...process.argv.slice(2), NODE_ARGS ? `--node-args="${NODE_ARGS}"` : undefined,
].filter(Boolean), '--',
); ...process.argv.slice(2),
].filter(Boolean),
);
} else {
run(
'node',
[`${APP_PACKAGE_ROOT}/lib/index.js`, ...(NODE_ARGS || '').split(' '), ...process.argv.slice(2)].filter(
Boolean,
),
);
}
} }
}); });
}; };

View File

@ -7,7 +7,7 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
const { run } = require('../util'); const { run, checkDBDialect } = require('../util');
const fg = require('fast-glob'); const fg = require('fast-glob');
const coreClientPackages = ['packages/core/client', 'packages/core/sdk']; const coreClientPackages = ['packages/core/client', 'packages/core/sdk'];
@ -30,6 +30,7 @@ const getPackagesDir = (isClient) => {
module.exports = (cli) => { module.exports = (cli) => {
cli.command('test-coverage:server').action(async () => { cli.command('test-coverage:server').action(async () => {
checkDBDialect();
const packageRoots = getPackagesDir(false); const packageRoots = getPackagesDir(false);
for (const dir of packageRoots) { for (const dir of packageRoots) {
try { try {
@ -41,6 +42,7 @@ module.exports = (cli) => {
}); });
cli.command('test-coverage:client').action(async () => { cli.command('test-coverage:client').action(async () => {
checkDBDialect();
const packageRoots = getPackagesDir(true); const packageRoots = getPackagesDir(true);
for (const dir of packageRoots) { for (const dir of packageRoots) {
try { try {

View File

@ -8,7 +8,7 @@
*/ */
const { Command } = require('commander'); const { Command } = require('commander');
const { run } = require('../util'); const { run, checkDBDialect } = require('../util');
const path = require('path'); const path = require('path');
/** /**
@ -29,6 +29,7 @@ function addTestCommand(name, cli) {
.arguments('[paths...]') .arguments('[paths...]')
.allowUnknownOption() .allowUnknownOption()
.action(async (paths, opts) => { .action(async (paths, opts) => {
checkDBDialect();
if (name === 'test:server') { if (name === 'test:server') {
process.env.TEST_ENV = 'server-side'; process.env.TEST_ENV = 'server-side';
} else if (name === 'test:client') { } else if (name === 'test:client') {

View File

@ -0,0 +1,71 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
const chalk = require('chalk');
const { Command } = require('commander');
const { resolve } = require('path');
const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode, checkDBDialect } = require('../util');
const { existsSync, rmSync } = require('fs');
const { readJSON, writeJSON } = require('fs-extra');
const deepmerge = require('deepmerge');
const rmAppDir = () => {
// If ts-node is not installed, do not do the following
const appDevDir = resolve(process.cwd(), './storage/.app-dev');
if (existsSync(appDevDir)) {
rmSync(appDevDir, { recursive: true, force: true });
}
};
/**
*
* @param {Command} cli
*/
module.exports = (cli) => {
cli
.command('update-deps')
.option('--force')
.allowUnknownOption()
.action(async (options) => {
if (hasCorePackages() || !hasTsNode()) {
await downloadPro();
return;
}
const pkg = require('../../package.json');
let distTag = 'latest';
if (pkg.version.includes('alpha')) {
distTag = 'alpha';
} else if (pkg.version.includes('beta')) {
distTag = 'beta';
}
const { stdout } = await run('npm', ['info', `@nocobase/cli@${distTag}`, 'version'], {
stdio: 'pipe',
});
if (!options.force && pkg.version === stdout) {
await downloadPro();
rmAppDir();
return;
}
const descPath = resolve(process.cwd(), 'package.json');
const descJson = await readJSON(descPath, 'utf8');
const sourcePath = resolve(__dirname, '../../templates/create-app-package.json');
const sourceJson = await readJSON(sourcePath, 'utf8');
if (descJson['dependencies']?.['@nocobase/cli']) {
descJson['dependencies']['@nocobase/cli'] = stdout;
}
if (descJson['devDependencies']?.['@nocobase/devtools']) {
descJson['devDependencies']['@nocobase/devtools'] = stdout;
}
const json = deepmerge(descJson, sourceJson);
await writeJSON(descPath, json, { spaces: 2, encoding: 'utf8' });
await run('yarn', ['install']);
await downloadPro();
rmAppDir();
});
};

View File

@ -10,15 +10,25 @@
const chalk = require('chalk'); const chalk = require('chalk');
const { Command } = require('commander'); const { Command } = require('commander');
const { resolve } = require('path'); const { resolve } = require('path');
const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode } = require('../util'); const { run, promptForTs, runAppCommand, hasCorePackages, downloadPro, hasTsNode, checkDBDialect } = require('../util');
const { existsSync, rmSync } = require('fs'); const { existsSync, rmSync } = require('fs');
const { readJSON, writeJSON } = require('fs-extra');
const deepmerge = require('deepmerge');
async function updatePackage() {
const sourcePath = resolve(__dirname, '../../templates/create-app-package.json');
const descPath = resolve(process.cwd(), 'package.json');
const sourceJson = await readJSON(sourcePath, 'utf8');
const descJson = await readJSON(descPath, 'utf8');
const json = deepmerge(descJson, sourceJson);
await writeJSON(descPath, json, { spaces: 2, encoding: 'utf8' });
}
/** /**
* *
* @param {Command} cli * @param {Command} cli
*/ */
module.exports = (cli) => { module.exports = (cli) => {
const { APP_PACKAGE_ROOT } = process.env;
cli cli
.command('upgrade') .command('upgrade')
.allowUnknownOption() .allowUnknownOption()
@ -26,52 +36,12 @@ module.exports = (cli) => {
.option('--next') .option('--next')
.option('-S|--skip-code-update') .option('-S|--skip-code-update')
.action(async (options) => { .action(async (options) => {
if (hasTsNode()) promptForTs(); checkDBDialect();
if (hasCorePackages()) {
// await run('yarn', ['install']);
await downloadPro();
await runAppCommand('upgrade');
return;
}
if (options.skipCodeUpdate) { if (options.skipCodeUpdate) {
await downloadPro();
await runAppCommand('upgrade'); await runAppCommand('upgrade');
return; } else {
await run('nocobase', ['update-deps']);
await run('nocobase', ['upgrade', '--skip-code-update']);
} }
// await runAppCommand('upgrade');
if (!hasTsNode()) {
await downloadPro();
await runAppCommand('upgrade');
return;
}
const rmAppDir = () => {
// If ts-node is not installed, do not do the following
const appDevDir = resolve(process.cwd(), './storage/.app-dev');
if (existsSync(appDevDir)) {
rmSync(appDevDir, { recursive: true, force: true });
}
};
const pkg = require('../../package.json');
let distTag = 'latest';
if (pkg.version.includes('alpha')) {
distTag = 'alpha';
} else if (pkg.version.includes('beta')) {
distTag = 'beta';
}
// get latest version
const { stdout } = await run('npm', ['info', `@nocobase/cli@${distTag}`, 'version'], {
stdio: 'pipe',
});
if (pkg.version === stdout) {
await downloadPro();
await runAppCommand('upgrade');
await rmAppDir();
return;
}
await run('yarn', ['add', `@nocobase/cli@${distTag}`, `@nocobase/devtools@${distTag}`, '-W']);
await run('yarn', ['install']);
await downloadPro();
await runAppCommand('upgrade');
await rmAppDir();
}); });
}; };

View File

@ -360,7 +360,7 @@ exports.initEnv = function initEnv() {
API_BASE_PATH: '/api/', API_BASE_PATH: '/api/',
API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_', API_CLIENT_STORAGE_PREFIX: 'NOCOBASE_',
API_CLIENT_STORAGE_TYPE: 'localStorage', API_CLIENT_STORAGE_TYPE: 'localStorage',
DB_DIALECT: 'sqlite', // DB_DIALECT: 'sqlite',
DB_STORAGE: 'storage/db/nocobase.sqlite', DB_STORAGE: 'storage/db/nocobase.sqlite',
// DB_TIMEZONE: '+00:00', // DB_TIMEZONE: '+00:00',
DB_UNDERSCORED: parseEnv('DB_UNDERSCORED'), DB_UNDERSCORED: parseEnv('DB_UNDERSCORED'),
@ -472,6 +472,12 @@ exports.initEnv = function initEnv() {
} }
}; };
exports.checkDBDialect = function () {
if (!process.env.DB_DIALECT) {
throw new Error('DB_DIALECT is required.');
}
};
exports.generatePlugins = function () { exports.generatePlugins = function () {
try { try {
require.resolve('@nocobase/devtools/umiConfig'); require.resolve('@nocobase/devtools/umiConfig');

View File

@ -0,0 +1,39 @@
{
"private": true,
"workspaces": ["packages/*/*", "packages/*/*/*"],
"engines": {
"node": ">=18"
},
"scripts": {
"nocobase": "nocobase",
"pm": "nocobase pm",
"pm2": "nocobase pm2",
"dev": "nocobase dev",
"start": "nocobase start",
"clean": "nocobase clean",
"build": "nocobase build",
"test": "nocobase test",
"e2e": "nocobase e2e",
"tar": "nocobase tar",
"postinstall": "nocobase postinstall",
"lint": "eslint ."
},
"resolutions": {
"cytoscape": "3.28.0",
"@types/react": "18.3.18",
"@types/react-dom": "^18.0.0",
"react-router-dom": "6.28.1",
"react-router": "6.28.1",
"async": "^3.2.6",
"antd": "5.12.8",
"rollup": "4.24.0",
"semver": "^7.7.1"
},
"dependencies": {
"pm2": "^6.0.5",
"mysql2": "^3.14.0",
"mariadb": "^2.5.6",
"pg": "^8.14.1",
"pg-hstore": "^2.3.4"
}
}

View File

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

View File

@ -314,15 +314,17 @@ export const ACLActionProvider = (props) => {
const schema = useFieldSchema(); const schema = useFieldSchema();
const currentUid = schema['x-uid']; const currentUid = schema['x-uid'];
let actionPath = schema['x-acl-action']; let actionPath = schema['x-acl-action'];
const editablePath = ['create', 'update', 'destroy', 'importXlsx']; // 只兼容这些数据表资源按钮
const resourceActionPath = ['create', 'update', 'destroy', 'importXlsx', 'export'];
// 视图表无编辑权限时不支持的操作
const writableViewCollectionAction = ['create', 'update', 'destroy', 'importXlsx', 'bulkDestroy', 'bulkUpdate'];
if (!actionPath && resource && schema['x-action'] && editablePath.includes(schema['x-action'])) { if (!actionPath && resource && schema['x-action'] && resourceActionPath.includes(schema['x-action'])) {
actionPath = `${resource}:${schema['x-action']}`; actionPath = `${resource}:${schema['x-action']}`;
} }
if (actionPath && !actionPath?.includes(':')) { if (actionPath && !actionPath?.includes(':')) {
actionPath = `${resource}:${actionPath}`; actionPath = `${resource}:${actionPath}`;
} }
const params = useMemo( const params = useMemo(
() => actionPath && parseAction(actionPath, { schema, recordPkValue }), () => actionPath && parseAction(actionPath, { schema, recordPkValue }),
[parseAction, actionPath, schema, recordPkValue], [parseAction, actionPath, schema, recordPkValue],
@ -339,16 +341,18 @@ export const ACLActionProvider = (props) => {
if (!params) { if (!params) {
return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>; return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>;
} }
//视图表无编辑权限时不显示 //视图表无编辑权限时不支持 writableViewCollectionAction 的按钮
if (editablePath.includes(actionPath) || editablePath.includes(actionPath?.split(':')[1])) { if (
writableViewCollectionAction.includes(actionPath) ||
writableViewCollectionAction.includes(actionPath?.split(':')[1])
) {
if ((collection && collection.template !== 'view') || collection?.writableView) { if ((collection && collection.template !== 'view') || collection?.writableView) {
return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>; return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>;
} }
return null; return <ACLActionParamsContext.Provider value={false}>{props.children}</ACLActionParamsContext.Provider>;
} }
return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>; return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>;
}; };
export const useACLFieldWhitelist = () => { export const useACLFieldWhitelist = () => {
const params = useContext(ACLActionParamsContext); const params = useContext(ACLActionParamsContext);
const whitelist = useMemo(() => { const whitelist = useMemo(() => {

View File

@ -236,7 +236,6 @@ export const scopesSchema: ISchema = {
'x-component': 'Action.Link', 'x-component': 'Action.Link',
'x-component-props': { 'x-component-props': {
openMode: 'drawer', openMode: 'drawer',
icon: 'EditOutlined',
}, },
properties: { properties: {
drawer: { drawer: {

View File

@ -167,7 +167,7 @@ export function getOperators() {
const dateA = parseDate(a); const dateA = parseDate(a);
const dateB = parseDate(b); const dateB = parseDate(b);
if (!dateA || !dateB) { if (!dateA || !dateB) {
throw new Error('Invalid date format'); return false;
} }
return dateA < dateB; return dateA < dateB;
}, },
@ -651,10 +651,11 @@ function parseYear(dateStr) {
} }
function parseDate(targetDateStr) { function parseDate(targetDateStr) {
let dateStr = Array.isArray(targetDateStr) ? targetDateStr[1] : targetDateStr; let dateStr = Array.isArray(targetDateStr) ? targetDateStr[1] ?? targetDateStr[0] : targetDateStr;
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(dateStr)) { if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(dateStr)) {
// ISO 8601 格式YYYY-MM-DDTHH:mm:ss.sssZ return new Date(dateStr);
return new Date(dateStr); // 直接解析为 Date 对象 } else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateStr)) {
return new Date(dateStr.replace(' ', 'T'));
} else if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { } else if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
// YYYY-MM-DD 格式 // YYYY-MM-DD 格式
return parseFullDate(dateStr); return parseFullDate(dateStr);
@ -668,5 +669,6 @@ function parseDate(targetDateStr) {
// YYYY 格式 // YYYY 格式
return parseYear(dateStr); return parseYear(dateStr);
} }
return null; // Invalid format
return null;
} }

View File

@ -34,20 +34,6 @@ export interface SchemaSettingsChildrenProps {
children: SchemaSettingsItemType[]; children: SchemaSettingsItemType[];
} }
const typeComponentMap = {
item: SchemaSettingsItem,
itemGroup: SchemaSettingsItemGroup,
subMenu: SchemaSettingsSubMenu,
divider: SchemaSettingsDivider,
remove: SchemaSettingsRemove,
select: SchemaSettingsSelectItem,
cascader: SchemaSettingsCascaderItem,
switch: SchemaSettingsSwitchItem,
popup: SchemaSettingsPopupItem,
actionModal: SchemaSettingsActionModalItem,
modal: SchemaSettingsModalItem,
};
const SchemaSettingsChildErrorFallback: FC< const SchemaSettingsChildErrorFallback: FC<
FallbackProps & { FallbackProps & {
title: string; title: string;
@ -113,6 +99,19 @@ export const SchemaSettingsChild: FC<SchemaSettingsItemType> = (props) => {
hideIfNoChildren, hideIfNoChildren,
componentProps, componentProps,
} = props as any; } = props as any;
const typeComponentMap = {
item: SchemaSettingsItem,
itemGroup: SchemaSettingsItemGroup,
subMenu: SchemaSettingsSubMenu,
divider: SchemaSettingsDivider,
remove: SchemaSettingsRemove,
select: SchemaSettingsSelectItem,
cascader: SchemaSettingsCascaderItem,
switch: SchemaSettingsSwitchItem,
popup: SchemaSettingsPopupItem,
actionModal: SchemaSettingsActionModalItem,
modal: SchemaSettingsModalItem,
};
const useChildrenRes = useChildren(); const useChildrenRes = useChildren();
const useComponentPropsRes = useComponentProps(); const useComponentPropsRes = useComponentProps();
const findComponent = useFindComponent(); const findComponent = useFindComponent();

View File

@ -22,8 +22,15 @@ import { useCreateFormBlockProps } from '../modules/blocks/data-blocks/form/hook
import { useEditFormBlockDecoratorProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps'; import { useEditFormBlockDecoratorProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps';
import { useEditFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockProps'; import { useEditFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockProps';
import { useDataFormItemProps } from '../modules/blocks/data-blocks/form/hooks/useDataFormItemProps'; import { useDataFormItemProps } from '../modules/blocks/data-blocks/form/hooks/useDataFormItemProps';
import { useGridCardBlockDecoratorProps } from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps'; import {
import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps'; useGridCardBlockDecoratorProps,
useGridCardBlockItemProps,
useGridCardBlockProps,
} from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps';
import {
useListBlockDecoratorProps,
useListBlockProps,
} from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps';
import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps'; import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps';
import { TableColumnSchemaToolbar } from '../modules/blocks/data-blocks/table/TableColumnSchemaToolbar'; import { TableColumnSchemaToolbar } from '../modules/blocks/data-blocks/table/TableColumnSchemaToolbar';
import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps'; import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps';
@ -80,11 +87,14 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
useTableSelectorProps, useTableSelectorProps,
useTableBlockDecoratorProps, useTableBlockDecoratorProps,
useListBlockDecoratorProps, useListBlockDecoratorProps,
useListBlockProps,
useTableSelectorDecoratorProps, useTableSelectorDecoratorProps,
useCollapseBlockDecoratorProps, useCollapseBlockDecoratorProps,
useFilterFormBlockProps, useFilterFormBlockProps,
useFilterFormBlockDecoratorProps, useFilterFormBlockDecoratorProps,
useGridCardBlockDecoratorProps, useGridCardBlockDecoratorProps,
useGridCardBlockItemProps,
useGridCardBlockProps,
useFormItemProps, useFormItemProps,
useDataFormItemProps, useDataFormItemProps,
}} }}
@ -141,11 +151,14 @@ export class BlockSchemaComponentPlugin extends Plugin {
useTableSelectorProps, useTableSelectorProps,
useTableBlockDecoratorProps, useTableBlockDecoratorProps,
useListBlockDecoratorProps, useListBlockDecoratorProps,
useListBlockProps,
useTableSelectorDecoratorProps, useTableSelectorDecoratorProps,
useCollapseBlockDecoratorProps, useCollapseBlockDecoratorProps,
useFilterFormBlockProps, useFilterFormBlockProps,
useFilterFormBlockDecoratorProps, useFilterFormBlockDecoratorProps,
useGridCardBlockDecoratorProps, useGridCardBlockDecoratorProps,
useGridCardBlockProps,
useGridCardBlockItemProps,
useFormItemProps, useFormItemProps,
useDataFormItemProps, useDataFormItemProps,
}); });

View File

@ -50,6 +50,7 @@ const InternalFormBlockProvider = (props) => {
const form = useMemo( const form = useMemo(
() => () =>
createForm({ createForm({
validateFirst: true,
readPretty, readPretty,
}), }),
[readPretty], [readPretty],

View File

@ -97,6 +97,35 @@ const filterValue = (value) => {
return obj; return obj;
}; };
function getFilteredFormValues(form) {
const values = _.cloneDeep(form.values);
const allFields = [];
form.query('*').forEach((field) => {
if (field) {
allFields.push(field);
}
});
const readonlyPaths = _.uniq(
allFields
.filter((field) => field?.componentProps?.readOnlySubmit)
.map((field) => {
const segments = field.path?.segments || [];
if (segments.length <= 1) {
return segments.join('.');
}
return segments.slice(0, -1).join('.');
}),
);
readonlyPaths.forEach((path, index) => {
if (index !== 0 || path.includes('.')) {
// 清空值,但跳过第一层
_.unset(values, path);
}
});
return values;
}
export function getFormValues({ export function getFormValues({
filterByTk, filterByTk,
field, field,
@ -124,7 +153,7 @@ export function getFormValues({
} }
} }
return form.values; return getFilteredFormValues(form);
} }
export function useCollectValuesToSubmit() { export function useCollectValuesToSubmit() {
@ -203,8 +232,8 @@ export function useCollectValuesToSubmit() {
]); ]);
} }
function interpolateVariables(str: string, scope: Record<string, any>): string { export function interpolateVariables(str: string, scope: Record<string, any>): string {
return str.replace(/\{\{\s*([a-zA-Z0-9_$-.]+?)\s*\}\}/g, (_, key) => { return str.replace(/\{\{\s*([a-zA-Z0-9_$.-]+?)\s*\}\}/g, (_, key) => {
return scope[key] !== undefined ? String(scope[key]) : ''; return scope[key] !== undefined ? String(scope[key]) : '';
}); });
} }
@ -1484,6 +1513,7 @@ export const useAssociationFilterBlockProps = () => {
run, run,
valueKey, valueKey,
labelKey, labelKey,
dataScopeFilter: filter,
}; };
}; };
async function doReset({ async function doReset({

View File

@ -7,9 +7,9 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import { filter, unionBy, uniq } from 'lodash';
import type { CollectionFieldOptions, GetCollectionFieldPredicate } from '../../data-source'; import type { CollectionFieldOptions, GetCollectionFieldPredicate } from '../../data-source';
import { Collection } from '../../data-source/collection/Collection'; import { Collection } from '../../data-source/collection/Collection';
import _, { filter, unionBy, uniq } from 'lodash';
export class InheritanceCollectionMixin extends Collection { export class InheritanceCollectionMixin extends Collection {
protected parentCollectionsName: string[]; protected parentCollectionsName: string[];
@ -22,6 +22,7 @@ export class InheritanceCollectionMixin extends Collection {
protected parentCollectionFields: Record<string, CollectionFieldOptions[]> = {}; protected parentCollectionFields: Record<string, CollectionFieldOptions[]> = {};
protected allCollectionsInheritChain: string[]; protected allCollectionsInheritChain: string[];
protected inheritCollectionsChain: string[]; protected inheritCollectionsChain: string[];
protected inheritChain: string[];
protected foreignKeyFields: CollectionFieldOptions[]; protected foreignKeyFields: CollectionFieldOptions[];
getParentCollectionsName() { getParentCollectionsName() {
@ -233,6 +234,43 @@ export class InheritanceCollectionMixin extends Collection {
return this.inheritCollectionsChain; return this.inheritCollectionsChain;
} }
/**
*
* - 使
*/
getInheritChain() {
if (this.inheritChain) {
return this.inheritChain.slice();
}
const ancestorChain = this.getInheritCollectionsChain();
const descendantNames = this.getChildrenCollectionsName();
// 构建最终的链,首先包含祖先链(包括自身)
const inheritChain = [...ancestorChain];
// 再添加直接后代及其后代,但不包括兄弟表
const addDescendants = (names: string[]) => {
for (const name of names) {
if (!inheritChain.includes(name)) {
inheritChain.push(name);
const childCollection = this.collectionManager.getCollection<InheritanceCollectionMixin>(name);
if (childCollection) {
// 递归添加每个后代的后代
const childrenNames = childCollection.getChildrenCollectionsName();
addDescendants(childrenNames);
}
}
}
};
// 从当前集合的直接后代开始添加
addDescendants(descendantNames);
this.inheritChain = inheritChain;
return this.inheritChain;
}
getAllFields(predicate?: GetCollectionFieldPredicate) { getAllFields(predicate?: GetCollectionFieldPredicate) {
if (this.allFields) { if (this.allFields) {
return this.allFields.slice(); return this.allFields.slice();

View File

@ -0,0 +1,189 @@
import { Application } from '@nocobase/client';
import { CollectionManager } from '../../../data-source/collection/CollectionManager';
import { InheritanceCollectionMixin } from '../InheritanceCollectionMixin';
describe('InheritanceCollectionMixin', () => {
let app: Application;
let collectionManager: CollectionManager;
beforeEach(() => {
app = new Application({
dataSourceManager: {
collectionMixins: [InheritanceCollectionMixin],
},
});
collectionManager = app.getCollectionManager();
});
describe('getInheritChain', () => {
it('should return itself when there are no ancestors or descendants', () => {
const options = {
name: 'test',
fields: [{ name: 'field1', interface: 'input' }],
};
collectionManager.addCollections([options]);
const collection = collectionManager.getCollection<InheritanceCollectionMixin>('test');
const inheritChain = collection.getInheritChain();
expect(inheritChain).toEqual(['test']);
});
it('should return a chain including all ancestor tables', () => {
// 创建三代数据表结构grandparent -> parent -> child
const grandparentOptions = {
name: 'grandparent',
fields: [{ name: 'field1', interface: 'input' }],
};
const parentOptions = {
name: 'parent',
inherits: ['grandparent'],
fields: [{ name: 'field2', interface: 'input' }],
};
const childOptions = {
name: 'child',
inherits: ['parent'],
fields: [{ name: 'field3', interface: 'input' }],
};
// 先将所有集合添加到 collectionManager
collectionManager.addCollections([grandparentOptions, parentOptions, childOptions]);
// 获取最终的集合实例以调用方法
const child = collectionManager.getCollection<InheritanceCollectionMixin>('child');
// 测试 child 的继承链包含所有祖先表
const inheritChain = child.getInheritChain();
expect(inheritChain).toContain('child');
expect(inheritChain).toContain('parent');
expect(inheritChain).toContain('grandparent');
expect(inheritChain.length).toBe(3);
});
it('should include all descendant tables, but not sibling tables', () => {
// 创建具有兄弟和后代关系的数据表结构
// parent (祖先表)
// |-- child1 (子表)
// | |-- grandChild1 (孙表1)
// | |-- grandChild2 (孙表2)
// |-- child2 (兄弟表)
// |-- grandChild3 (兄弟的子表,不应该包括在测试集合的继承链中)
const collections = [
{
name: 'parent',
fields: [{ name: 'parentField', interface: 'input' }],
},
{
name: 'child1',
inherits: ['parent'],
fields: [{ name: 'child1Field', interface: 'input' }],
},
{
name: 'child2',
inherits: ['parent'],
fields: [{ name: 'child2Field', interface: 'input' }],
},
{
name: 'grandChild1',
inherits: ['child1'],
fields: [{ name: 'grandChild1Field', interface: 'input' }],
},
{
name: 'grandChild2',
inherits: ['child1'],
fields: [{ name: 'grandChild2Field', interface: 'input' }],
},
{
name: 'grandChild3',
inherits: ['child2'],
fields: [{ name: 'grandChild3Field', interface: 'input' }],
},
];
// 一次性添加所有集合
collectionManager.addCollections(collections);
// 获取要测试的集合实例
const child1 = collectionManager.getCollection<InheritanceCollectionMixin>('child1');
// 测试 child1 的继承链
const child1InheritChain = child1.getInheritChain();
// 应该包含自身、父表和子表
expect(child1InheritChain).toContain('child1');
expect(child1InheritChain).toContain('parent');
expect(child1InheritChain).toContain('grandChild1');
expect(child1InheritChain).toContain('grandChild2');
// 不应该包含兄弟表及其子表
expect(child1InheritChain).not.toContain('child2');
expect(child1InheritChain).not.toContain('grandChild3');
// 检查总数量是否正确 (parent, child1, grandChild1, grandChild2)
expect(child1InheritChain.length).toBe(4);
});
it('should properly handle multiple inheritance', () => {
// 创建多重继承的数据表结构
// parent1 parent2
// \ /
// \ /
// child
// |
// grandChild
const collections = [
{
name: 'parent1',
fields: [{ name: 'parent1Field', interface: 'input' }],
},
{
name: 'parent2',
fields: [{ name: 'parent2Field', interface: 'input' }],
},
{
name: 'child',
inherits: ['parent1', 'parent2'],
fields: [{ name: 'childField', interface: 'input' }],
},
{
name: 'grandChild',
inherits: ['child'],
fields: [{ name: 'grandChildField', interface: 'input' }],
},
];
// 一次性添加所有集合
collectionManager.addCollections(collections);
// 获取要测试的集合实例
const child = collectionManager.getCollection<InheritanceCollectionMixin>('child');
const grandChild = collectionManager.getCollection<InheritanceCollectionMixin>('grandChild');
// 测试 child 的继承链
const childInheritChain = child.getInheritChain();
// 应该包含自身、两个父表和子表
expect(childInheritChain).toContain('child');
expect(childInheritChain).toContain('parent1');
expect(childInheritChain).toContain('parent2');
expect(childInheritChain).toContain('grandChild');
// 检查总数量是否正确 (child, parent1, parent2, grandChild)
expect(childInheritChain.length).toBe(4);
// 测试 grandChild 的继承链
const grandChildInheritChain = grandChild.getInheritChain();
// 应该包含自身及所有祖先表
expect(grandChildInheritChain).toContain('grandChild');
expect(grandChildInheritChain).toContain('child');
expect(grandChildInheritChain).toContain('parent1');
expect(grandChildInheritChain).toContain('parent2');
// 检查总数量是否正确 (grandChild, child, parent1, parent2)
expect(grandChildInheritChain.length).toBe(4);
});
});
});

View File

@ -0,0 +1,30 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Button, Result } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
export const AppNotFound = () => {
const navigate = useNavigate();
const { t } = useTranslation();
return (
<Result
status="404"
title="404"
subTitle={t('Sorry, the page you visited does not exist.')}
extra={
<Button onClick={() => navigate('/', { replace: true })} type="primary">
Back Home
</Button>
}
/>
);
};

View File

@ -7,5 +7,6 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
export * from './AppNotFound';
export * from './SelectWithTitle'; export * from './SelectWithTitle';
export * from './useFieldComponentName'; export * from './useFieldComponentName';

View File

@ -23,6 +23,7 @@ import {
import { CollectionRecord } from '../collection-record'; import { CollectionRecord } from '../collection-record';
import { BlockRequestProvider } from './DataBlockRequestProvider'; import { BlockRequestProvider } from './DataBlockRequestProvider';
import { DataBlockResourceProvider } from './DataBlockResourceProvider'; import { DataBlockResourceProvider } from './DataBlockResourceProvider';
import { BlockLinkageRuleProvider } from '../../modules/blocks/BlockLinkageRuleProvider';
export interface AllDataBlockProps { export interface AllDataBlockProps {
collection: string | CollectionOptions; collection: string | CollectionOptions;
@ -189,13 +190,15 @@ export const DataBlockProvider: FC<Partial<AllDataBlockProps>> = withDynamicSche
<CollectionManagerProvider dataSource={dataSource}> <CollectionManagerProvider dataSource={dataSource}>
<AssociationOrCollectionProvider collection={collection} association={association}> <AssociationOrCollectionProvider collection={collection} association={association}>
<ACLCollectionProvider> <ACLCollectionProvider>
<DataBlockResourceProvider> <BlockLinkageRuleProvider>
<BlockRequestProvider> <DataBlockResourceProvider>
<DataBlockCollector params={props.params}> <BlockRequestProvider>
<RerenderDataBlockProvider>{children}</RerenderDataBlockProvider> <DataBlockCollector params={props.params}>
</DataBlockCollector> <RerenderDataBlockProvider>{children}</RerenderDataBlockProvider>
</BlockRequestProvider> </DataBlockCollector>
</DataBlockResourceProvider> </BlockRequestProvider>
</DataBlockResourceProvider>
</BlockLinkageRuleProvider>
</ACLCollectionProvider> </ACLCollectionProvider>
</AssociationOrCollectionProvider> </AssociationOrCollectionProvider>
</CollectionManagerProvider> </CollectionManagerProvider>

View File

@ -29,7 +29,7 @@ export function useDataSourceManager() {
} }
/** /**
* collection collection * collection collection
* @returns * @returns
*/ */
export function useAllCollectionsInheritChainGetter() { export function useAllCollectionsInheritChainGetter() {
@ -39,7 +39,7 @@ export function useAllCollectionsInheritChainGetter() {
return dm return dm
?.getDataSource(customDataSource) ?.getDataSource(customDataSource)
?.collectionManager?.getCollection<InheritanceCollectionMixin>(collectionName) ?.collectionManager?.getCollection<InheritanceCollectionMixin>(collectionName)
?.getAllCollectionsInheritChain(); ?.getInheritChain();
}, },
[dm], [dm],
); );

View File

@ -67,6 +67,108 @@ describe('getSupportFieldsByAssociation', () => {
}); });
describe('getSupportFieldsByForeignKey', () => { describe('getSupportFieldsByForeignKey', () => {
it('should return foreign key fields matching both name and target collection', () => {
const filterBlockCollection = {
fields: [
{ id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' },
{ id: 2, name: 'field2', type: 'hasMany', foreignKey: 'fk2', target: 'collection2' },
{ id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'collection3' },
],
};
const block = {
foreignKeyFields: [
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 2, name: 'fk2', collectionName: 'collection2' },
{ id: 3, name: 'fk3', collectionName: 'collection3' },
],
};
const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any);
expect(result).toEqual([
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 2, name: 'fk2', collectionName: 'collection2' },
{ id: 3, name: 'fk3', collectionName: 'collection3' },
]);
});
it("should not return foreign key fields when target collection doesn't match", () => {
const filterBlockCollection = {
fields: [
{ id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' },
{ id: 2, name: 'field2', type: 'hasMany', foreignKey: 'fk2', target: 'collectionX' }, // target不匹配
{ id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'collection3' },
],
};
const block = {
foreignKeyFields: [
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 2, name: 'fk2', collectionName: 'collection2' }, // 与field2的target不匹配
{ id: 3, name: 'fk3', collectionName: 'collection3' },
],
};
const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any);
expect(result).toEqual([
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 3, name: 'fk3', collectionName: 'collection3' },
]);
});
it('should filter out belongsTo type fields', () => {
const filterBlockCollection = {
fields: [
{ id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' },
{ id: 2, name: 'field2', type: 'belongsTo', foreignKey: 'fk2', target: 'collection2' }, // belongsTo类型
{ id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'collection3' },
],
};
const block = {
foreignKeyFields: [
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 2, name: 'fk2', collectionName: 'collection2' },
{ id: 3, name: 'fk3', collectionName: 'collection3' },
],
};
const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any);
expect(result).toEqual([
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 3, name: 'fk3', collectionName: 'collection3' },
]);
});
it('should handle when both name and target collection match', () => {
const filterBlockCollection = {
fields: [
{ id: 1, name: 'field1', type: 'hasMany', foreignKey: 'fk1', target: 'collection1' },
{ id: 2, name: 'field2', type: 'hasOne', foreignKey: 'fk2', target: 'collection2' },
{ id: 3, name: 'field3', type: 'hasMany', foreignKey: 'fk3', target: 'wrongCollection' }, // 目标表不匹配
],
};
const block = {
foreignKeyFields: [
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 2, name: 'fk2', collectionName: 'collection2' },
{ id: 3, name: 'fk3', collectionName: 'collection3' }, // 与field3的target不匹配
],
};
const result = getSupportFieldsByForeignKey(filterBlockCollection as any, block as any);
expect(result).toEqual([
{ id: 1, name: 'fk1', collectionName: 'collection1' },
{ id: 2, name: 'fk2', collectionName: 'collection2' },
]);
});
// 保留原有的通用测试用例
it("should return all foreign key fields matching the filter block collection's foreign key properties", () => { it("should return all foreign key fields matching the filter block collection's foreign key properties", () => {
const filterBlockCollection = { const filterBlockCollection = {
fields: [ fields: [

View File

@ -49,9 +49,13 @@ export const getSupportFieldsByAssociation = (inheritCollectionsChain: string[],
export const getSupportFieldsByForeignKey = (filterBlockCollection: Collection, block: DataBlock) => { export const getSupportFieldsByForeignKey = (filterBlockCollection: Collection, block: DataBlock) => {
return block.foreignKeyFields?.filter((foreignKeyField) => { return block.foreignKeyFields?.filter((foreignKeyField) => {
return filterBlockCollection.fields.some( return filterBlockCollection.fields.some((field) => {
(field) => field.type !== 'belongsTo' && field.foreignKey === foreignKeyField.name, return (
); field.type !== 'belongsTo' &&
field.foreignKey === foreignKeyField.name && // 1. 外键字段的 name 要一致
field.target === foreignKeyField.collectionName // 2. 关系字段的目标表要和外键的数据表一致
);
});
}); });
}; };
@ -193,19 +197,21 @@ export const useFilterAPI = () => {
const doFilter = useCallback( const doFilter = useCallback(
( (
value: any | ((target: FilterTarget['targets'][0], block: DataBlock) => any), value: any | ((target: FilterTarget['targets'][0], block: DataBlock, sourceKey?: string) => any),
field: string | ((target: FilterTarget['targets'][0], block: DataBlock) => string) = 'id', field: string | ((target: FilterTarget['targets'][0], block: DataBlock) => string) = 'id',
operator: string | ((target: FilterTarget['targets'][0]) => string) = '$eq', operator: string | ((target: FilterTarget['targets'][0]) => string) = '$eq',
) => { ) => {
const currentBlock = dataBlocks.find((block) => block.uid === fieldSchema.parent['x-uid']);
dataBlocks.forEach((block) => { dataBlocks.forEach((block) => {
let key = field as string;
const target = targets.find((target) => target.uid === block.uid); const target = targets.find((target) => target.uid === block.uid);
if (!target) return; if (!target) return;
if (_.isFunction(value)) { if (_.isFunction(value)) {
value = value(target, block); value = value(target, block, getSourceKey(currentBlock, target.field));
} }
if (_.isFunction(field)) { if (_.isFunction(field)) {
field = field(target, block); key = field(target, block);
} }
if (_.isFunction(operator)) { if (_.isFunction(operator)) {
operator = operator(target); operator = operator(target);
@ -219,7 +225,7 @@ export const useFilterAPI = () => {
storedFilter[uid] = { storedFilter[uid] = {
$and: [ $and: [
{ {
[field]: { [key]: {
[operator]: value, [operator]: value,
}, },
}, },
@ -248,7 +254,7 @@ export const useFilterAPI = () => {
); );
}); });
}, },
[dataBlocks, targets, uid], [dataBlocks, targets, uid, fieldSchema],
); );
return { return {
@ -268,3 +274,8 @@ export const isInFilterFormBlock = (fieldSchema: Schema) => {
} }
return false; return false;
}; };
function getSourceKey(currentBlock: DataBlock, field: string) {
const associationField = currentBlock?.associatedFields?.find((item) => item.foreignKey === field);
return associationField?.sourceKey || field?.split?.('.')?.[1];
}

View File

@ -49,6 +49,8 @@ export interface CustomToken extends AliasToken {
marginBlock: number; marginBlock: number;
/** 区块的圆角 */ /** 区块的圆角 */
borderRadiusBlock: number; borderRadiusBlock: number;
siderWidth: number;
} }
export interface ThemeConfig extends _ThemeConfig { export interface ThemeConfig extends _ThemeConfig {

View File

@ -500,7 +500,8 @@
"Turn pages": "Seiten umblättern", "Turn pages": "Seiten umblättern",
"Others": "Andere", "Others": "Andere",
"Other records": "Andere Datensätze", "Other records": "Andere Datensätze",
"Save as template": "Als Vorlage speichern", "Save as reference template": "Als Referenzvorlage speichern",
"Save as inherited template": "Als vererbte Vorlage speichern",
"Save as block template": "Als Blockvorlage speichern", "Save as block template": "Als Blockvorlage speichern",
"Block templates": "Blockvorlagen", "Block templates": "Blockvorlagen",
"Block template": "Blockvorlage", "Block template": "Blockvorlage",
@ -589,6 +590,7 @@
"Blank block": "Leerer Block", "Blank block": "Leerer Block",
"Duplicate template": "Vorlage duplizieren", "Duplicate template": "Vorlage duplizieren",
"Reference template": "Referenzvorlage", "Reference template": "Referenzvorlage",
"Inherited template": "Vererbte Vorlage",
"Create calendar block": "Kalenderblock erstellen", "Create calendar block": "Kalenderblock erstellen",
"Create kanban block": "Kanban-Block erstellen", "Create kanban block": "Kanban-Block erstellen",
"Grouping field": "Gruppierungsfeld", "Grouping field": "Gruppierungsfeld",

View File

@ -505,7 +505,7 @@
"Save as block template": "Save as block template", "Save as block template": "Save as block template",
"Block templates": "Block templates", "Block templates": "Block templates",
"Block template": "Block template", "Block template": "Block template",
"Convert reference to duplicate": "Convert reference to duplicate", "Convert template to duplicate": "Convert template to duplicate",
"Template name": "Template name", "Template name": "Template name",
"Block type": "Block type", "Block type": "Block type",
"No blocks to connect": "No blocks to connect", "No blocks to connect": "No blocks to connect",
@ -590,6 +590,7 @@
"Blank block": "Blank block", "Blank block": "Blank block",
"Duplicate template": "Duplicate template", "Duplicate template": "Duplicate template",
"Reference template": "Reference template", "Reference template": "Reference template",
"Inherited template": "Inherited template",
"Create calendar block": "Create calendar block", "Create calendar block": "Create calendar block",
"Create kanban block": "Create kanban block", "Create kanban block": "Create kanban block",
"Grouping field": "Grouping field", "Grouping field": "Grouping field",
@ -889,7 +890,6 @@
"No pages yet, please configure first": "No pages yet, please configure first", "No pages yet, please configure first": "No pages yet, please configure first",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode",
"Deprecated": "Deprecated", "Deprecated": "Deprecated",
"The following old template features have been deprecated and will be removed in next version.": "The following old template features have been deprecated and will be removed in next version.",
"Full permissions": "Full permissions", "Full permissions": "Full permissions",
"Refresh data blocks": "Refresh data blocks", "Refresh data blocks": "Refresh data blocks",
"Select data blocks to refresh": "Select data blocks to refresh", "Select data blocks to refresh": "Select data blocks to refresh",

View File

@ -470,7 +470,8 @@
"Turn pages": "Pasar páginas", "Turn pages": "Pasar páginas",
"Others": "Otros", "Others": "Otros",
"Other records": "Otros registros", "Other records": "Otros registros",
"Save as template": "Guardar como plantilla", "Save as reference template": "Guardar como plantilla de referencia",
"Save as inherited template": "Guardar como plantilla heredada",
"Save as block template": "Guardar como plantilla de bloque", "Save as block template": "Guardar como plantilla de bloque",
"Block templates": "Bloquear plantillas", "Block templates": "Bloquear plantillas",
"Block template": "Plantilla de bloque", "Block template": "Plantilla de bloque",
@ -560,6 +561,7 @@
"Blank block": "Bloque en blanco", "Blank block": "Bloque en blanco",
"Duplicate template": "Duplicar plantilla", "Duplicate template": "Duplicar plantilla",
"Reference template": "Plantilla de referencia", "Reference template": "Plantilla de referencia",
"Inherited template": "Plantilla heredada",
"Create calendar block": "Crear bloque de calendario", "Create calendar block": "Crear bloque de calendario",
"Create kanban block": "Crear bloque kanban", "Create kanban block": "Crear bloque kanban",
"Grouping field": "Campo de agrupación", "Grouping field": "Campo de agrupación",
@ -806,7 +808,6 @@
"No pages yet, please configure first": "Aún no hay páginas, por favor configura primero", "No pages yet, please configure first": "Aún no hay páginas, por favor configura primero",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Haga clic en el icono \"Editor de UI\" en la esquina superior derecha para entrar en el modo de Editor de UI.", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Haga clic en el icono \"Editor de UI\" en la esquina superior derecha para entrar en el modo de Editor de UI.",
"Deprecated": "Obsoleto", "Deprecated": "Obsoleto",
"The following old template features have been deprecated and will be removed in next version.": "Las siguientes características de plantilla antigua han quedado obsoletas y se eliminarán en la próxima versión.",
"Full permissions": "Todos los derechos", "Full permissions": "Todos los derechos",
"Refresh data blocks": "Actualizar bloques de datos", "Refresh data blocks": "Actualizar bloques de datos",
"Select data blocks to refresh": "Actualizar bloques de datos", "Select data blocks to refresh": "Actualizar bloques de datos",

View File

@ -485,7 +485,8 @@
"Turn pages": "Tourner les pages", "Turn pages": "Tourner les pages",
"Others": "Autres", "Others": "Autres",
"Other records": "Autres enregistrements", "Other records": "Autres enregistrements",
"Save as template": "Enregistrer en tant que modèle", "Save as reference template": "Enregistrer en tant que modèle de référence",
"Save as inherited template": "Enregistrer en tant que modèle hérité",
"Save as block template": "Enregistrer en tant que modèle de bloc", "Save as block template": "Enregistrer en tant que modèle de bloc",
"Block templates": "Modèles de bloc", "Block templates": "Modèles de bloc",
"Block template": "Modèle de bloc", "Block template": "Modèle de bloc",
@ -573,6 +574,7 @@
"Blank block": "Bloc vierge", "Blank block": "Bloc vierge",
"Duplicate template": "Dupliquer le modèle", "Duplicate template": "Dupliquer le modèle",
"Reference template": "Référencer le modèle", "Reference template": "Référencer le modèle",
"Inherited template": "Modèle hérité",
"Create calendar block": "Créer un bloc de calendrier", "Create calendar block": "Créer un bloc de calendrier",
"Create kanban block": "Créer un bloc kanban", "Create kanban block": "Créer un bloc kanban",
"Grouping field": "Champ de regroupement", "Grouping field": "Champ de regroupement",
@ -826,7 +828,6 @@
"No pages yet, please configure first": "Pas encore de pages, veuillez configurer d'abord", "No pages yet, please configure first": "Pas encore de pages, veuillez configurer d'abord",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Cliquez sur l'icône \"Éditeur d'interface utilisateur\" dans le coin supérieur droit pour entrer en mode Éditeur d'interface utilisateur", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Cliquez sur l'icône \"Éditeur d'interface utilisateur\" dans le coin supérieur droit pour entrer en mode Éditeur d'interface utilisateur",
"Deprecated": "Déprécié", "Deprecated": "Déprécié",
"The following old template features have been deprecated and will be removed in next version.": "Les fonctionnalités des anciens modèles ont été dépréciées et seront supprimées dans la prochaine version.",
"Full permissions": "Tous les droits", "Full permissions": "Tous les droits",
"Refresh data blocks": "Actualiser les blocs de données", "Refresh data blocks": "Actualiser les blocs de données",
"Select data blocks to refresh": "Actualiser les blocs de données", "Select data blocks to refresh": "Actualiser les blocs de données",

View File

@ -494,7 +494,8 @@
"Turn pages": "Volta pagine", "Turn pages": "Volta pagine",
"Others": "Altri", "Others": "Altri",
"Other records": "Altri record", "Other records": "Altri record",
"Save as template": "Salva come modello", "Save as reference template": "Salva come modello di riferimento",
"Save as inherited template": "Salva come modello ereditato",
"Save as block template": "Salva come modello blocco", "Save as block template": "Salva come modello blocco",
"Block templates": "Modelli blocco", "Block templates": "Modelli blocco",
"Block template": "Modello blocco", "Block template": "Modello blocco",
@ -580,6 +581,7 @@
"Blank block": "Blocco vuoto", "Blank block": "Blocco vuoto",
"Duplicate template": "Modello duplicato", "Duplicate template": "Modello duplicato",
"Reference template": "Modello di riferimento", "Reference template": "Modello di riferimento",
"Inherited template": "Modello ereditato",
"Create calendar block": "Crea blocco calendario", "Create calendar block": "Crea blocco calendario",
"Create kanban block": "Crea blocco kanban", "Create kanban block": "Crea blocco kanban",
"Grouping field": "Campo di raggruppamento", "Grouping field": "Campo di raggruppamento",

View File

@ -397,7 +397,8 @@
"Turn pages": "ページをめくる", "Turn pages": "ページをめくる",
"Others": "その他", "Others": "その他",
"Other records": "他のレコード", "Other records": "他のレコード",
"Save as template": "テンプレートとして保存", "Save as reference template": "参照テンプレートとして保存",
"Save as inherited template": "継承テンプレートとして保存",
"Save as block template": "ブロックテンプレートとして保存", "Save as block template": "ブロックテンプレートとして保存",
"Block templates": "ブロックテンプレート", "Block templates": "ブロックテンプレート",
"Block template": "ブロックテンプレート", "Block template": "ブロックテンプレート",
@ -472,6 +473,7 @@
"Blank block": "空のブロック", "Blank block": "空のブロック",
"Duplicate template": "テンプレートをコピー", "Duplicate template": "テンプレートをコピー",
"Reference template": "テンプレートを参照", "Reference template": "テンプレートを参照",
"Inherited template": "継承テンプレート",
"Create calendar block": "カレンダーブロックの作成", "Create calendar block": "カレンダーブロックの作成",
"Create kanban block": "かんばんブロックの作成", "Create kanban block": "かんばんブロックの作成",
"Grouping field": "グループフィールド", "Grouping field": "グループフィールド",
@ -1044,7 +1046,6 @@
"No pages yet, please configure first": "まだページがありません。最初に設定してください", "No pages yet, please configure first": "まだページがありません。最初に設定してください",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "ユーザーインターフェースエディターモードに入るには、右上隅の「UIエディタ」アイコンをクリックしてください", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "ユーザーインターフェースエディターモードに入るには、右上隅の「UIエディタ」アイコンをクリックしてください",
"Deprecated": "非推奨", "Deprecated": "非推奨",
"The following old template features have been deprecated and will be removed in next version.": "次の古いテンプレート機能は非推奨になり、次のバージョンで削除されます。",
"Full permissions": "すべての権限", "Full permissions": "すべての権限",
"Refresh data blocks": "データブロックを更新", "Refresh data blocks": "データブロックを更新",
"Select data blocks to refresh": "データブロックを選択して更新", "Select data blocks to refresh": "データブロックを選択して更新",

View File

@ -517,7 +517,8 @@
"Turn pages": "페이지 넘김", "Turn pages": "페이지 넘김",
"Others": "기타", "Others": "기타",
"Other records": "기타 레코드", "Other records": "기타 레코드",
"Save as template": "템플릿으로 저장", "Save as reference template": "참조 템플릿으로 저장",
"Save as inherited template": "상속 템플릿으로 저장",
"Save as block template": "블록 템플릿으로 저장", "Save as block template": "블록 템플릿으로 저장",
"Block templates": "블록 템플릿", "Block templates": "블록 템플릿",
"Block template": "블록 템플릿", "Block template": "블록 템플릿",
@ -601,6 +602,7 @@
"Blank block": "빈 블록", "Blank block": "빈 블록",
"Duplicate template": "템플릿 복제", "Duplicate template": "템플릿 복제",
"Reference template": "참조 템플릿", "Reference template": "참조 템플릿",
"Inherited template": "상속 템플릿",
"Create calendar block": "캘린더 블록 생성", "Create calendar block": "캘린더 블록 생성",
"Create kanban block": "칸반 블록 생성", "Create kanban block": "칸반 블록 생성",
"Grouping field": "그루핑 필드", "Grouping field": "그루핑 필드",
@ -917,7 +919,6 @@
"No pages yet, please configure first": "아직 페이지가 없습니다. 먼저 설정하십시오", "No pages yet, please configure first": "아직 페이지가 없습니다. 먼저 설정하십시오",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "사용자 인터페이스 편집기 모드에 들어가려면 오른쪽 상단의 \"UI 편집기\" 아이콘을 클릭하십시오", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "사용자 인터페이스 편집기 모드에 들어가려면 오른쪽 상단의 \"UI 편집기\" 아이콘을 클릭하십시오",
"Deprecated": "사용 중단됨", "Deprecated": "사용 중단됨",
"The following old template features have been deprecated and will be removed in next version.": "다음 오래된 템플릿 기능은 사용 중단되었으며 다음 버전에서 제거될 것입니다.",
"Full permissions": "모든 권한", "Full permissions": "모든 권한",
"Refresh data blocks": "데이터 블록 새로 고침", "Refresh data blocks": "데이터 블록 새로 고침",
"Select data blocks to refresh": "데이터 블록을 선택하여 새로 고침", "Select data blocks to refresh": "데이터 블록을 선택하여 새로 고침",

View File

@ -504,7 +504,8 @@
"Turn pages": "Pagina's omslaan", "Turn pages": "Pagina's omslaan",
"Others": "Overigen", "Others": "Overigen",
"Other records": "Andere records", "Other records": "Andere records",
"Save as template": "Opslaan als sjabloon", "Save as reference template": "Opslaan als referentiesjabloon",
"Save as inherited template": "Opslaan als overerfde sjabloon",
"Save as block template": "Opslaan als bloksjabloon", "Save as block template": "Opslaan als bloksjabloon",
"Block templates": "Bloksjablonen", "Block templates": "Bloksjablonen",
"Block template": "Bloksjabloon", "Block template": "Bloksjabloon",
@ -593,6 +594,7 @@
"Blank block": "Leeg blok", "Blank block": "Leeg blok",
"Duplicate template": "Sjabloon dupliceren", "Duplicate template": "Sjabloon dupliceren",
"Reference template": "Sjabloon refereren", "Reference template": "Sjabloon refereren",
"Inherited template": "Overerfde sjabloon",
"Create calendar block": "Kalenderblok maken", "Create calendar block": "Kalenderblok maken",
"Create kanban block": "Kanbanblok maken", "Create kanban block": "Kanbanblok maken",
"Grouping field": "Groepeer veld", "Grouping field": "Groepeer veld",

View File

@ -433,7 +433,8 @@
"Turn pages": "Virar páginas", "Turn pages": "Virar páginas",
"Others": "Outros", "Others": "Outros",
"Other records": "Outros registros", "Other records": "Outros registros",
"Save as template": "Salvar como modelo", "Save as reference template": "Salvar como modelo de referência",
"Save as inherited template": "Salvar como modelo herdado",
"Save as block template": "Salvar como modelo de bloco", "Save as block template": "Salvar como modelo de bloco",
"Block templates": "Modelos de bloco", "Block templates": "Modelos de bloco",
"Block template": "Modelo de bloco", "Block template": "Modelo de bloco",
@ -525,6 +526,7 @@
"Blank block": "Bloco em branco", "Blank block": "Bloco em branco",
"Duplicate template": "Duplicar modelo", "Duplicate template": "Duplicar modelo",
"Reference template": "Modelo de referência", "Reference template": "Modelo de referência",
"Inherited template": "Modelo herdado",
"Create calendar block": "Criar bloco de calendário", "Create calendar block": "Criar bloco de calendário",
"Create kanban block": "Criar bloco Kanban", "Create kanban block": "Criar bloco Kanban",
"Grouping field": "Campo de agrupamento", "Grouping field": "Campo de agrupamento",
@ -781,7 +783,6 @@
"Are you sure you want to hide this tab?": "Tem certeza de que deseja ocultar esta guia?", "Are you sure you want to hide this tab?": "Tem certeza de que deseja ocultar esta guia?",
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Depois de ocultar, esta guia não aparecerá mais na barra de guias. Para mostrá-la novamente, você precisa ir à página de gerenciamento de rotas para configurá-la.", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Depois de ocultar, esta guia não aparecerá mais na barra de guias. Para mostrá-la novamente, você precisa ir à página de gerenciamento de rotas para configurá-la.",
"Deprecated": "Descontinuado", "Deprecated": "Descontinuado",
"The following old template features have been deprecated and will be removed in next version.": "As seguintes funcionalidades de modelo antigo foram descontinuadas e serão removidas na próxima versão.",
"Full permissions": "Todas as permissões", "Full permissions": "Todas as permissões",
"No pages yet, please configure first": "Ainda não há páginas, por favor configure primeiro", "No pages yet, please configure first": "Ainda não há páginas, por favor configure primeiro",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Cliquez sur l'icône \"Éditeur d'interface utilisateur\" dans le coin supérieur droit pour entrer en mode Éditeur d'interface utilisateur", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Cliquez sur l'icône \"Éditeur d'interface utilisateur\" dans le coin supérieur droit pour entrer en mode Éditeur d'interface utilisateur",

View File

@ -337,7 +337,8 @@
"Turn pages": "Перелистывать страницы", "Turn pages": "Перелистывать страницы",
"Others": "Другие", "Others": "Другие",
"Other records": "Другие записи", "Other records": "Другие записи",
"Save as template": "Сохранить как шаблон", "Save as reference template": "Сохранить как шаблон ссылки",
"Save as inherited template": "Сохранить как шаблон наследования",
"Save as block template": "Сохранить как шаблон Блока", "Save as block template": "Сохранить как шаблон Блока",
"Block templates": "Шаблоны блоков", "Block templates": "Шаблоны блоков",
"Convert reference to duplicate": "Преобразовать ссылку в дубликат", "Convert reference to duplicate": "Преобразовать ссылку в дубликат",
@ -411,6 +412,7 @@
"Blank block": "Пустой блок", "Blank block": "Пустой блок",
"Duplicate template": "Дублировать шаблон", "Duplicate template": "Дублировать шаблон",
"Reference template": "Справочный шаблон", "Reference template": "Справочный шаблон",
"Inherited template": "Наследуемый шаблон",
"Create calendar block": "Создать блок календаря", "Create calendar block": "Создать блок календаря",
"Create kanban block": "Создать блок Канбан", "Create kanban block": "Создать блок Канбан",
"Grouping field": "Поле группировки", "Grouping field": "Поле группировки",
@ -610,7 +612,6 @@
"Are you sure you want to hide this tab?": "Вы уверены, что хотите скрыть эту вкладку?", "Are you sure you want to hide this tab?": "Вы уверены, что хотите скрыть эту вкладку?",
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "После скрытия этой вкладки она больше не будет отображаться во вкладке. Чтобы снова отобразить ее, вам нужно будет перейти на страницу управления маршрутами, чтобы установить ее.", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "После скрытия этой вкладки она больше не будет отображаться во вкладке. Чтобы снова отобразить ее, вам нужно будет перейти на страницу управления маршрутами, чтобы установить ее.",
"Deprecated": "Устаревший", "Deprecated": "Устаревший",
"The following old template features have been deprecated and will be removed in next version.": "Следующие старые функции шаблонов устарели и будут удалены в следующей версии.",
"Full permissions": "Полные права", "Full permissions": "Полные права",
"No pages yet, please configure first": "Пока нет страниц, пожалуйста, настройте сначала", "No pages yet, please configure first": "Пока нет страниц, пожалуйста, настройте сначала",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Нажмите на значок \"Редактор пользовательского интерфейса\" в правом верхнем углу, чтобы войти в режим редактора пользовательского интерфейса", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Нажмите на значок \"Редактор пользовательского интерфейса\" в правом верхнем углу, чтобы войти в режим редактора пользовательского интерфейса",

View File

@ -336,7 +336,8 @@
"Turn pages": "Sayfaları çevir", "Turn pages": "Sayfaları çevir",
"Others": "Diğerleri", "Others": "Diğerleri",
"Other records": "Diğer kayıtlar", "Other records": "Diğer kayıtlar",
"Save as template": "Şablon olarak kaydet", "Save as reference template": "Referans şablonu olarak kaydet",
"Save as inherited template": "Kalıtım şablonu olarak kaydet",
"Save as block template": "Blok şablonu olarak kaydet", "Save as block template": "Blok şablonu olarak kaydet",
"Block templates": "Blok şablonları", "Block templates": "Blok şablonları",
"Block template": "Blok şablonu", "Block template": "Blok şablonu",
@ -411,6 +412,7 @@
"Blank block": "Boş blok", "Blank block": "Boş blok",
"Duplicate template": "Şablonun kopyasını oluştur", "Duplicate template": "Şablonun kopyasını oluştur",
"Reference template": "Referans şablon", "Reference template": "Referans şablon",
"Inherited template": "Kalıtım şablonu",
"Create calendar block": "Takvim bloğu oluştur", "Create calendar block": "Takvim bloğu oluştur",
"Create kanban block": "Kanban bloğu oluştur", "Create kanban block": "Kanban bloğu oluştur",
"Grouping field": "Alan gruplandırma", "Grouping field": "Alan gruplandırma",
@ -608,7 +610,6 @@
"Are you sure you want to hide this tab?": "Bu sekmeyi gizlemek istediğinizden emin misiniz?", "Are you sure you want to hide this tab?": "Bu sekmeyi gizlemek istediğinizden emin misiniz?",
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Gizlendikten sonra, bu sekme artık sekme çubuğunda görünmeyecek. Onu tekrar göstermek için, rotayı yönetim sayfasına gidip ayarlamanız gerekiyor.", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Gizlendikten sonra, bu sekme artık sekme çubuğunda görünmeyecek. Onu tekrar göstermek için, rotayı yönetim sayfasına gidip ayarlamanız gerekiyor.",
"Deprecated": "Kullanımdan kaldırıldı", "Deprecated": "Kullanımdan kaldırıldı",
"The following old template features have been deprecated and will be removed in next version.": "Aşağıdaki eski şablon özellikleri kullanımdan kaldırıldı ve gelecek sürümlerde kaldırılacaktır.",
"Full permissions": "Tüm izinler", "Full permissions": "Tüm izinler",
"No pages yet, please configure first": "Henüz sayfa yok, lütfen önce yapılandırın", "No pages yet, please configure first": "Henüz sayfa yok, lütfen önce yapılandırın",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Kullanıcı arayüzü düzenleyici moduna girmek için sağ üst köşedeki \"Kullanıcı Arayüzü Düzenleyici\" simgesine tıklayın", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Kullanıcı arayüzü düzenleyici moduna girmek için sağ üst köşedeki \"Kullanıcı Arayüzü Düzenleyici\" simgesine tıklayın",

View File

@ -487,7 +487,8 @@
"Turn pages": "Переключати сторінки", "Turn pages": "Переключати сторінки",
"Others": "Інші", "Others": "Інші",
"Other records": "Інші записи", "Other records": "Інші записи",
"Save as template": "Зберегти як шаблон", "Save as reference template": "Зберегти як шаблон посилання",
"Save as inherited template": "Зберегти як шаблон нащадка",
"Save as block template": "Зберегти як шаблон блока", "Save as block template": "Зберегти як шаблон блока",
"Block templates": "Шаблони блоків", "Block templates": "Шаблони блоків",
"Block template": "Шаблон блока", "Block template": "Шаблон блока",
@ -577,6 +578,7 @@
"Blank block": "Порожній блок", "Blank block": "Порожній блок",
"Duplicate template": "Дублювати шаблон", "Duplicate template": "Дублювати шаблон",
"Reference template": "Посилання на шаблон", "Reference template": "Посилання на шаблон",
"Inherited template": "Нащадковий шаблон",
"Create calendar block": "Створити блок календаря", "Create calendar block": "Створити блок календаря",
"Create kanban block": "Створити блок канбану", "Create kanban block": "Створити блок канбану",
"Grouping field": "Поле для групування", "Grouping field": "Поле для групування",
@ -824,7 +826,6 @@
"Are you sure you want to hide this tab?": "Ви впевнені, що хочете приховати цю вкладку?", "Are you sure you want to hide this tab?": "Ви впевнені, що хочете приховати цю вкладку?",
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Після приховування цієї вкладки вона більше не з'явиться в панелі вкладок. Щоб знову показати її, вам потрібно перейти на сторінку керування маршрутами, щоб налаштувати її.", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "Після приховування цієї вкладки вона більше не з'явиться в панелі вкладок. Щоб знову показати її, вам потрібно перейти на сторінку керування маршрутами, щоб налаштувати її.",
"Deprecated": "Застаріло", "Deprecated": "Застаріло",
"The following old template features have been deprecated and will be removed in next version.": "Наступні старі функції шаблонів були застарілі і будуть видалені в наступній версії.",
"Full permissions": "Повні права", "Full permissions": "Повні права",
"No pages yet, please configure first": "Ще немає сторінок, будь ласка, спочатку налаштуйте", "No pages yet, please configure first": "Ще немає сторінок, будь ласка, спочатку налаштуйте",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Натисніть на значок \"Редактор користувацького інтерфейсу\" в правому верхньому куті, щоб увійти в режим редактора користувацького інтерфейсу.", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "Натисніть на значок \"Редактор користувацького інтерфейсу\" в правому верхньому куті, щоб увійти в режим редактора користувацького інтерфейсу.",

View File

@ -527,7 +527,8 @@
"Turn pages": "翻页", "Turn pages": "翻页",
"Others": "其他", "Others": "其他",
"Other records": "其他记录", "Other records": "其他记录",
"Save as template": "保存为模板", "Save as reference template": "保存为引用模板",
"Save as inherited template": "保存为继承模板",
"Save as block template": "保存为区块模板", "Save as block template": "保存为区块模板",
"Block templates": "区块模板", "Block templates": "区块模板",
"Block template": "区块模板", "Block template": "区块模板",
@ -611,6 +612,7 @@
"Blank block": "空区块", "Blank block": "空区块",
"Duplicate template": "复制模板", "Duplicate template": "复制模板",
"Reference template": "引用模板", "Reference template": "引用模板",
"Inherited template": "继承模板",
"Create calendar block": "创建日历区块", "Create calendar block": "创建日历区块",
"Create kanban block": "创建看板区块", "Create kanban block": "创建看板区块",
"Grouping field": "分组字段", "Grouping field": "分组字段",
@ -821,7 +823,8 @@
"File size should not exceed {{size}}.": "文件大小不能超过 {{size}}", "File size should not exceed {{size}}.": "文件大小不能超过 {{size}}",
"File size exceeds the limit": "文件大小超过限制", "File size exceeds the limit": "文件大小超过限制",
"File type is not allowed": "文件类型不允许", "File type is not allowed": "文件类型不允许",
"Incomplete uploading files need to be resolved": "未完成上传的文件需要处理", "Uploading": "上传中",
"Some files are not uploaded correctly, please check.": "部分文件未上传成功,请检查。",
"Default title for each record": "用作数据的默认标题", "Default title for each record": "用作数据的默认标题",
"If collection inherits, choose inherited collections as templates": "当前表有继承关系时,可选择继承链路上的表作为模板来源", "If collection inherits, choose inherited collections as templates": "当前表有继承关系时,可选择继承链路上的表作为模板来源",
"Select an existing piece of data as the initialization data for the form": "选择一条已有的数据作为表单的初始化数据", "Select an existing piece of data as the initialization data for the form": "选择一条已有的数据作为表单的初始化数据",
@ -1087,7 +1090,6 @@
"Are you sure you want to hide this tab?": "你确定要隐藏该标签页吗?", "Are you sure you want to hide this tab?": "你确定要隐藏该标签页吗?",
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "隐藏后,该标签将不再显示在标签栏中。要想再次显示它,你需要到路由管理页面进行设置。", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "隐藏后,该标签将不再显示在标签栏中。要想再次显示它,你需要到路由管理页面进行设置。",
"Deprecated": "已弃用", "Deprecated": "已弃用",
"The following old template features have been deprecated and will be removed in next version.": "以下旧的模板功能已弃用,将在下个版本移除。",
"Full permissions": "全部权限", "Full permissions": "全部权限",
"Enable index column": "启用序号列", "Enable index column": "启用序号列",
"Date scope": "日期范围", "Date scope": "日期范围",
@ -1102,5 +1104,7 @@
"Colon":"冒号", "Colon":"冒号",
"No pages yet, please configure first": "暂无页面,请先配置", "No pages yet, please configure first": "暂无页面,请先配置",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "点击右上角的“界面配置”图标,进入界面配置模式", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "点击右上角的“界面配置”图标,进入界面配置模式",
"After successful submission, the selected data blocks will be automatically refreshed.": "提交成功后,会自动刷新这里选中的数据区块。" "After successful submission, the selected data blocks will be automatically refreshed.": "提交成功后,会自动刷新这里选中的数据区块。",
"Block Linkage rules":"区块联动规则",
"Field Linkage rules":"字段联动规则"
} }

View File

@ -518,7 +518,8 @@
"Turn pages": "翻頁", "Turn pages": "翻頁",
"Others": "其他", "Others": "其他",
"Other records": "其他記錄", "Other records": "其他記錄",
"Save as template": "儲存為模板", "Save as reference template": "儲存為引用模板",
"Save as inherited template": "儲存為繼承模板",
"Save as block template": "儲存為區塊模板", "Save as block template": "儲存為區塊模板",
"Block templates": "區塊模板", "Block templates": "區塊模板",
"Block template": "區塊模板", "Block template": "區塊模板",
@ -602,6 +603,7 @@
"Blank block": "空區塊", "Blank block": "空區塊",
"Duplicate template": "複製模板", "Duplicate template": "複製模板",
"Reference template": "引用模板", "Reference template": "引用模板",
"Inherited template": "繼承模板",
"Create calendar block": "建立日曆區塊", "Create calendar block": "建立日曆區塊",
"Create kanban block": "建立看板區塊", "Create kanban block": "建立看板區塊",
"Grouping field": "群組欄位", "Grouping field": "群組欄位",
@ -915,7 +917,6 @@
"Are you sure you want to hide this tab?": "你確定要隱藏這個標籤嗎?", "Are you sure you want to hide this tab?": "你確定要隱藏這個標籤嗎?",
"After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "隱藏後,這個標籤將不再出現在標籤欄中。要再次顯示它,你需要到路由管理頁面進行設置。", "After hiding, this tab will no longer appear in the tab bar. To show it again, you need to go to the route management page to set it.": "隱藏後,這個標籤將不再出現在標籤欄中。要再次顯示它,你需要到路由管理頁面進行設置。",
"Deprecated": "已棄用", "Deprecated": "已棄用",
"The following old template features have been deprecated and will be removed in next version.": "以下舊的模板功能已棄用,將在下個版本移除。",
"Full permissions": "完全權限", "Full permissions": "完全權限",
"No pages yet, please configure first": "尚未配置頁面,請先配置", "No pages yet, please configure first": "尚未配置頁面,請先配置",
"Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "點擊右上角的 \"介面設定\" 圖示進入介面設定模式", "Click the \"UI Editor\" icon in the upper right corner to enter the UI Editor mode": "點擊右上角的 \"介面設定\" 圖示進入介面設定模式",

View File

@ -33,8 +33,9 @@ test.describe('Link', () => {
).toHaveCount(1); ).toHaveCount(1);
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-actionSettings:link-users' }).hover(); await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-actionSettings:link-users' }).hover();
await page.getByRole('menuitem', { name: 'Edit link' }).click(); await page.getByRole('menuitem', { name: 'Edit link' }).click();
await page.getByLabel('block-item-users-URL').getByLabel('textbox').click();
await page await page
.getByLabel('block-item-users-table-URL') .getByLabel('block-item-users-URL')
.getByLabel('textbox') .getByLabel('textbox')
.fill(await nocoPage.getUrl()); .fill(await nocoPage.getUrl());
await page.getByPlaceholder('Name').fill('id'); await page.getByPlaceholder('Name').fill('id');
@ -102,7 +103,7 @@ test.describe('Link', () => {
await page.getByLabel('action-Action.Link-Link-').hover(); await page.getByLabel('action-Action.Link-Link-').hover();
await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:link-users').hover(); await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:link-users').hover();
await page.getByRole('menuitem', { name: 'Edit link' }).click(); await page.getByRole('menuitem', { name: 'Edit link' }).click();
await page.getByLabel('block-item-users-table-URL').getByLabel('textbox').fill(otherPageUrl); await page.getByLabel('block-item-users-URL').getByLabel('textbox').fill(otherPageUrl);
await page.getByRole('button', { name: 'OK', exact: true }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.getByLabel('action-Action.Link-Link-').click(); await page.getByLabel('action-Action.Link-Link-').click();

View File

@ -69,6 +69,21 @@ export const addNewActionSettings = new SchemaSettings({
return isChildCollectionAction; return isChildCollectionAction;
}, },
}, },
{
name: 'linkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { association } = useDataBlockProps() || {};
const { name } = useCollection_deprecated();
const { getCollectionField } = useCollectionManager_deprecated();
const associationField = getCollectionField(association);
const { linkageRulesProps } = useSchemaToolbar();
return {
...linkageRulesProps,
collectionName: associationField?.collectionName || name,
};
},
},
{ {
name: 'delete', name: 'delete',
sort: 100, sort: 100,

View File

@ -7,7 +7,7 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import React, { useState, useContext } from 'react'; import React, { useState, useContext, useEffect } from 'react';
import { RecordPickerProvider, RecordPickerContext } from '../../../schema-component/antd/record-picker'; import { RecordPickerProvider, RecordPickerContext } from '../../../schema-component/antd/record-picker';
import { import {
SchemaComponentOptions, SchemaComponentOptions,
@ -41,9 +41,16 @@ const useTableSelectorProps = () => {
export const AssociateActionProvider = (props) => { export const AssociateActionProvider = (props) => {
const [selectedRows, setSelectedRows] = useState([]); const [selectedRows, setSelectedRows] = useState([]);
const collection = useCollection(); const collection = useCollection();
const { resource, service, block, __parent } = useBlockRequestContext(); const { resource, block, __parent } = useBlockRequestContext();
const actionCtx = useActionContext(); const actionCtx = useActionContext();
const { isMobile } = useOpenModeContext() || {}; const { isMobile } = useOpenModeContext() || {};
const [associationData, setAssociationData] = useState([]);
useEffect(() => {
resource?.list?.().then((res) => {
setAssociationData(res.data?.data || []);
});
}, [resource]);
const pickerProps = { const pickerProps = {
size: 'small', size: 'small',
onChange: props?.onChange, onChange: props?.onChange,
@ -73,8 +80,8 @@ export const AssociateActionProvider = (props) => {
}; };
const getFilter = () => { const getFilter = () => {
const targetKey = collection?.filterTargetKey || 'id'; const targetKey = collection?.filterTargetKey || 'id';
if (service.data?.data) { if (associationData) {
const list = service.data?.data.map((option) => option[targetKey]).filter(Boolean); const list = associationData.map((option) => option[targetKey]).filter(Boolean);
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {}; const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
return filter; return filter;
} }

View File

@ -51,6 +51,16 @@ export const bulkDeleteActionSettings = new SchemaSettings({
}; };
}, },
}, },
{
name: 'linkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { linkageRulesProps } = useSchemaToolbar();
return {
...linkageRulesProps,
};
},
},
{ {
name: 'remove', name: 'remove',
sort: 100, sort: 100,

View File

@ -53,14 +53,33 @@ export const filterActionSettings = new SchemaSettings({
default: fieldSchema?.['x-component-props']?.icon, default: fieldSchema?.['x-component-props']?.icon,
'x-component-props': {}, 'x-component-props': {},
}, },
onlyIcon: {
'x-decorator': 'FormItem',
'x-component': 'Checkbox',
title: t('Icon only'),
default: fieldSchema?.['x-component-props']?.onlyIcon,
'x-component-props': {},
'x-reactions': [
{
dependencies: ['icon'],
fulfill: {
state: {
hidden: '{{!$deps[0]}}',
},
},
},
],
},
}, },
} as ISchema, } as ISchema,
onSubmit: ({ title, icon }) => { onSubmit: ({ title, icon, onlyIcon }) => {
fieldSchema.title = title; fieldSchema.title = title;
field.title = title; field.title = title;
field.componentProps.icon = icon; field.componentProps.icon = icon;
field.componentProps.onlyIcon = onlyIcon;
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props'].icon = icon; fieldSchema['x-component-props'].icon = icon;
fieldSchema['x-component-props'].onlyIcon = onlyIcon;
dn.emit('patch', { dn.emit('patch', {
schema: { schema: {
['x-uid']: fieldSchema['x-uid'], ['x-uid']: fieldSchema['x-uid'],

View File

@ -15,6 +15,7 @@ import { useDesignable } from '../../../';
import { useSchemaToolbar } from '../../../application'; import { useSchemaToolbar } from '../../../application';
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer'; import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
import { useCollectionManager_deprecated } from '../../../collection-manager';
import { import {
SchemaSettingsLinkageRules, SchemaSettingsLinkageRules,
SchemaSettingsModalItem, SchemaSettingsModalItem,
@ -96,6 +97,9 @@ export const customizeLinkActionSettings = new SchemaSettings({
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const { linkageRulesProps } = useSchemaToolbar(); const { linkageRulesProps } = useSchemaToolbar();
const { association } = useDataBlockProps() || {};
const { getCollectionField } = useCollectionManager_deprecated();
const associationField = getCollectionField(association);
return { return {
...linkageRulesProps, ...linkageRulesProps,
}; };

View File

@ -0,0 +1,100 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React, { useMemo, useEffect, useState } from 'react';
import { useFieldSchema, useForm } from '@formily/react';
import { last, isEqual } from 'lodash';
import { uid } from '@formily/shared';
import { reaction } from '@formily/reactive';
import { useLocalVariables, useVariables } from '../../variables';
import { useReactiveLinkageEffect } from './utils';
import { useDesignable } from '../../';
import { forEachLinkageRule } from '../../schema-settings/LinkageRules/forEachLinkageRule';
import {
getVariableValuesInCondition,
getVariableValuesInExpression,
} from '../../schema-settings/LinkageRules/bindLinkageRulesToFiled';
const getLinkageRules = (fieldSchema) => {
if (!fieldSchema) {
return [];
}
let linkageRules = fieldSchema?.['x-block-linkage-rules'] || [];
fieldSchema.mapProperties((schema) => {
if (schema['x-block-linkage-rules']) {
linkageRules = schema['x-block-linkage-rules'];
}
});
return linkageRules?.filter((k) => !k.disabled);
};
export const BlockLinkageRuleProvider = (props) => {
const schema = useFieldSchema();
const variables = useVariables();
const localVariables = useLocalVariables();
const { designable } = useDesignable();
const form = useForm();
const linkageRules = useMemo(() => getLinkageRules(schema), [schema]);
const [triggerLinkageUpdate, setTriggerLinkageUpdate] = useState(null);
const displayResult = useReactiveLinkageEffect(linkageRules, variables, localVariables, triggerLinkageUpdate);
const shouldCalculateFormLinkage = schema?.['x-decorator'] === 'FormItem' && !form.readPretty && linkageRules.length;
useEffect(() => {
if (shouldCalculateFormLinkage) {
const id = uid();
const disposes = [];
// 延迟执行,防止一开始获取到的 form.values 值是旧的
setTimeout(() => {
form.addEffects(id, () => {
forEachLinkageRule(linkageRules, (action, rule) => {
return reaction(
() => {
// 获取条件中的变量值
const variableValuesInCondition = getVariableValuesInCondition({ linkageRules, localVariables });
// 获取 value 表达式中的变量值
const variableValuesInExpression = getVariableValuesInExpression({ action, localVariables });
const result = [variableValuesInCondition, variableValuesInExpression]
.map((item) => JSON.stringify(item))
.join(',');
return result;
},
() => {
setTriggerLinkageUpdate(uid());
},
{ fireImmediately: true, equals: isEqual },
);
});
});
});
// 清理副作用
return () => {
form.removeEffects(id);
disposes.forEach((dispose) => {
dispose();
});
};
}
}, [linkageRules, shouldCalculateFormLinkage]);
if (!linkageRules.length) {
return props.children;
}
if (displayResult === null) return null;
if (last(displayResult) === 'hidden') {
if (designable) {
return <div style={{ opacity: 0.3 }}>{props.children}</div>;
} else {
return null;
}
}
return props.children;
};

View File

@ -45,7 +45,7 @@ test.describe('multi data details block schema settings', () => {
// 禁用规则,联动规则失效 // 禁用规则,联动规则失效
await page.getByLabel('block-item-CardItem-users-').hover(); await page.getByLabel('block-item-CardItem-users-').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:detailsWithPagination-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:detailsWithPagination-users').hover();
await page.getByText('Linkage rules').click(); await page.getByText('Field Linkage rules').click();
await page.getByRole('switch', { name: 'On Off' }).click(); await page.getByRole('switch', { name: 'On Off' }).click();
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK' }).click();
await page.reload(); await page.reload();

View File

@ -14,7 +14,7 @@ import { SchemaSettings } from '../../../../application/schema-settings/SchemaSe
import { SchemaSettingsItemType } from '../../../../application/schema-settings/types'; import { SchemaSettingsItemType } from '../../../../application/schema-settings/types';
import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider'; import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider';
import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider';
import { useCollection_deprecated, useSortFields } from '../../../../collection-manager'; import { useSortFields } from '../../../../collection-manager';
import { removeNullCondition, useDesignable } from '../../../../schema-component'; import { removeNullCondition, useDesignable } from '../../../../schema-component';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings'; import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem';
@ -24,6 +24,8 @@ import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettin
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { setDataLoadingModeSettingsItem } from './setDataLoadingModeSettingsItem'; import { setDataLoadingModeSettingsItem } from './setDataLoadingModeSettingsItem';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
import { useCollection } from '../../../../data-source';
const commonItems: SchemaSettingsItemType[] = [ const commonItems: SchemaSettingsItemType[] = [
{ {
@ -35,13 +37,28 @@ const commonItems: SchemaSettingsItemType[] = [
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{ {
name: 'linkageRules', name: 'fieldLinkageRules',
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection();
const { t } = useTranslation();
return { return {
collectionName: name, collectionName: name,
readPretty: true, readPretty: true,
title: t('Field Linkage rules'),
};
},
},
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
}; };
}, },
}, },
@ -49,7 +66,7 @@ const commonItems: SchemaSettingsItemType[] = [
name: 'dataScope', name: 'dataScope',
Component: SchemaSettingsDataScope, Component: SchemaSettingsDataScope,
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { form } = useFormBlockContext(); const { form } = useFormBlockContext();
const field = useField(); const field = useField();
@ -83,7 +100,7 @@ const commonItems: SchemaSettingsItemType[] = [
name: 'sortingRules', name: 'sortingRules',
type: 'modal', type: 'modal',
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection();
const { t } = useTranslation(); const { t } = useTranslation();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const field = useField(); const field = useField();
@ -201,7 +218,7 @@ const commonItems: SchemaSettingsItemType[] = [
name: 'template', name: 'template',
Component: SchemaSettingsTemplate, Component: SchemaSettingsTemplate,
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { componentNamePrefix } = useBlockTemplateContext(); const { componentNamePrefix } = useBlockTemplateContext();
const defaultResource = const defaultResource =

View File

@ -8,14 +8,17 @@
*/ */
import { useFieldSchema } from '@formily/react'; import { useFieldSchema } from '@formily/react';
import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { SchemaSettingsItemType } from '../../../../application/schema-settings/types'; import { SchemaSettingsItemType } from '../../../../application/schema-settings/types';
import { useCollection } from '../../../../data-source/collection/CollectionProvider'; import { useCollection } from '../../../../data-source/collection/CollectionProvider';
import { useCollection_deprecated } from '../../../../collection-manager';
import { SchemaSettingsFormItemTemplate, SchemaSettingsLinkageRules } from '../../../../schema-settings'; import { SchemaSettingsFormItemTemplate, SchemaSettingsLinkageRules } from '../../../../schema-settings';
import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem';
import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem';
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
const commonItems: SchemaSettingsItemType[] = [ const commonItems: SchemaSettingsItemType[] = [
{ {
@ -27,13 +30,28 @@ const commonItems: SchemaSettingsItemType[] = [
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{ {
name: 'linkageRules', name: 'fieldLinkageRules',
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const { name } = useCollection(); const { name } = useCollection();
const { t } = useTranslation();
return { return {
collectionName: name, collectionName: name,
readPretty: true, readPretty: true,
title: t('Field Linkage rules'),
};
},
},
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection_deprecated();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
}; };
}, },
}, },

View File

@ -310,7 +310,7 @@ test.describe('set default value', () => {
// 设置联动规则 // 设置联动规则
await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); await page.getByRole('menuitem', { name: 'Field linkage rules' }).click();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click(); await page.getByText('Add property').click();
@ -438,7 +438,7 @@ test.describe('set default value', () => {
// 设置联动规则 // 设置联动规则
await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); await page.getByRole('menuitem', { name: 'Field linkage rules' }).click();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click(); await page.getByText('Add property').click();
@ -563,7 +563,7 @@ test.describe('set default value', () => {
// 设置联动规则 // 设置联动规则
await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); await page.getByRole('menuitem', { name: 'Field linkage rules' }).click();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click(); await page.getByText('Add property').click();
@ -701,7 +701,7 @@ test.describe('set default value', () => {
// 设置联动规则 // 设置联动规则
await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); await page.getByRole('menuitem', { name: 'Field linkage rules' }).click();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click(); await page.getByText('Add property').click();

View File

@ -18,7 +18,7 @@ test.describe('deprecated variables', () => {
await page.getByLabel('action-Action.Link-Edit').click(); await page.getByLabel('action-Action.Link-Edit').click();
await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); await page.getByRole('menuitem', { name: 'Field linkage rules' }).click();
await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible(); await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible();
// 2. 但是变量列表中是禁用状态 // 2. 但是变量列表中是禁用状态
@ -57,7 +57,7 @@ test.describe('deprecated variables', () => {
// 4. 再次打开弹窗,变量列表中的弃用变量不再显示 // 4. 再次打开弹窗,变量列表中的弃用变量不再显示
await page.getByLabel('block-item-CardItem-users-form').hover(); await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); await page.getByRole('menuitem', { name: 'Field linkage rules' }).click();
await page.locator('button').filter({ hasText: /^x$/ }).last().click(); await page.locator('button').filter({ hasText: /^x$/ }).last().click();
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden(); await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
// 使下拉菜单消失 // 使下拉菜单消失

View File

@ -8,6 +8,7 @@
*/ */
import { useFieldSchema } from '@formily/react'; import { useFieldSchema } from '@formily/react';
import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
@ -21,6 +22,7 @@ import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/Schem
import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem';
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
export const createFormBlockSettings = new SchemaSettings({ export const createFormBlockSettings = new SchemaSettings({
name: 'blockSettings:createForm', name: 'blockSettings:createForm',
@ -34,12 +36,27 @@ export const createFormBlockSettings = new SchemaSettings({
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{ {
name: 'linkageRules', name: 'fieldLinkageRules',
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection_deprecated();
const { t } = useTranslation();
return { return {
collectionName: name, collectionName: name,
title: t('Field Linkage rules'),
};
},
},
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection_deprecated();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
}; };
}, },
}, },

View File

@ -8,6 +8,7 @@
*/ */
import { useFieldSchema } from '@formily/react'; import { useFieldSchema } from '@formily/react';
import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider'; import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
@ -21,6 +22,7 @@ import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/Schem
import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem';
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
export const editFormBlockSettings = new SchemaSettings({ export const editFormBlockSettings = new SchemaSettings({
name: 'blockSettings:editForm', name: 'blockSettings:editForm',
@ -34,12 +36,27 @@ export const editFormBlockSettings = new SchemaSettings({
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{ {
name: 'linkageRules', name: 'fieldLinkageRules',
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection_deprecated();
const { t } = useTranslation();
return { return {
collectionName: name, collectionName: name,
title: t('Field Linkage rules'),
};
},
},
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection_deprecated();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
}; };
}, },
}, },

View File

@ -28,7 +28,7 @@ test.describe('where grid card block can be added', () => {
await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible(); await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible();
}); });
test('popup', async ({ page, mockPage }) => { test.skip('popup', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableWithUsers).goto(); await mockPage(oneEmptyTableWithUsers).goto();
// 1. 打开弹窗,通过 Associated records 创建一个列表区块 // 1. 打开弹窗,通过 Associated records 创建一个列表区块

View File

@ -24,6 +24,8 @@ import { useBlockTemplateContext } from '../../../../schema-templates/BlockTempl
import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem'; import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem';
import { SetTheCountOfColumnsDisplayedInARow } from './SetTheCountOfColumnsDisplayedInARow'; import { SetTheCountOfColumnsDisplayedInARow } from './SetTheCountOfColumnsDisplayedInARow';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
export const gridCardBlockSettings = new SchemaSettings({ export const gridCardBlockSettings = new SchemaSettings({
name: 'blockSettings:gridCard', name: 'blockSettings:gridCard',
@ -32,6 +34,19 @@ export const gridCardBlockSettings = new SchemaSettings({
name: 'setTheBlockHeight', name: 'setTheBlockHeight',
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection_deprecated();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
};
},
},
{ {
name: 'SetTheCountOfColumnsDisplayedInARow', name: 'SetTheCountOfColumnsDisplayedInARow',
Component: SetTheCountOfColumnsDisplayedInARow, Component: SetTheCountOfColumnsDisplayedInARow,

View File

@ -27,3 +27,11 @@ export function useGridCardBlockDecoratorProps(props) {
parseVariableLoading, parseVariableLoading,
}; };
} }
export function useGridCardBlockItemProps() {
return {};
}
export function useGridCardBlockProps() {
return {};
}

View File

@ -22,3 +22,7 @@ export function useListBlockDecoratorProps(props) {
parentRecord, parentRecord,
}; };
} }
export function useListBlockProps() {
return {};
}

View File

@ -22,6 +22,8 @@ import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettin
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem'; import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
export const listBlockSettings = new SchemaSettings({ export const listBlockSettings = new SchemaSettings({
name: 'blockSettings:list', name: 'blockSettings:list',
@ -34,6 +36,19 @@ export const listBlockSettings = new SchemaSettings({
name: 'setTheBlockHeight', name: 'setTheBlockHeight',
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection_deprecated();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
};
},
},
{ {
name: 'SetTheDataScope', name: 'SetTheDataScope',
Component: SchemaSettingsDataScope, Component: SchemaSettingsDataScope,

View File

@ -61,14 +61,11 @@ export const TableBlockInitializer = ({
export const useCreateTableBlock = () => { export const useCreateTableBlock = () => {
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createTableBlock = ({ item }) => { const createTableBlock = ({ item }) => {
const collection = getCollection(item.name, item.dataSource);
const schema = createTableBlockUISchema({ const schema = createTableBlockUISchema({
collectionName: item.name, collectionName: item.name,
dataSource: item.dataSource, dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
}); });
insert(schema); insert(schema);
}; };

View File

@ -17,14 +17,14 @@ test('action linkage by row data', async ({ page, mockPage }) => {
.getByLabel('action-Action.Link-Edit-update-roles-table-admin') .getByLabel('action-Action.Link-Edit-update-roles-table-admin')
.locator('.nb-action-title'); .locator('.nb-action-title');
const adminEditActionStyle = await adminEditAction.evaluate((element) => { const adminEditActionStyle = await adminEditAction.evaluate((element) => {
const computedStyle = window.getComputedStyle(element); const computedStyle = window.getComputedStyle(element.querySelector('.nb-action-title'));
return { return {
opacity: computedStyle.opacity, opacity: computedStyle.opacity,
}; };
}); });
const rootEditAction = page.getByLabel('action-Action.Link-Edit-update-roles-table-root').locator('.nb-action-title'); const rootEditAction = page.getByLabel('action-Action.Link-Edit-update-roles-table-root').locator('.nb-action-title');
const rootEditActionStyle = await rootEditAction.evaluate((element) => { const rootEditActionStyle = await rootEditAction.evaluate((element) => {
const computedStyle = window.getComputedStyle(element); const computedStyle = window.getComputedStyle(element.querySelector('.nb-action-title'));
return { return {
opacity: computedStyle.opacity, opacity: computedStyle.opacity,
// 添加其他你需要的样式属性 // 添加其他你需要的样式属性

View File

@ -309,6 +309,7 @@ test.describe('configure actions column', () => {
await page.getByText('Actions', { exact: true }).hover({ force: true }); await page.getByText('Actions', { exact: true }).hover({ force: true });
await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover(); await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click(); await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(500, 0);
// await page.getByText('Actions', { exact: true }).hover({ force: true }); // await page.getByText('Actions', { exact: true }).hover({ force: true });
// await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover(); // await page.getByLabel('designer-schema-initializer-TableV2.Column-TableV2.ActionColumnDesigner-').hover();

View File

@ -39,7 +39,7 @@ test.describe('where table block can be added', () => {
await page.getByLabel('schema-initializer-Grid-popup').hover(); await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Table right' }).hover();
await page.getByRole('menuitem', { name: 'childAssociationField' }).waitFor({ state: 'detached' }); await page.getByRole('menuitem', { name: 'childAssociationField' }).waitFor({ state: 'detached' });
await page.getByRole('menuitem', { name: 'Associated records' }).last().hover(); await page.getByRole('menuitem', { name: 'Associated records right' }).last().hover();
await page.getByRole('menuitem', { name: 'childAssociationField' }).click(); await page.getByRole('menuitem', { name: 'childAssociationField' }).click();
await page await page
.getByTestId('drawer-Action.Container-childCollection-View record') .getByTestId('drawer-Action.Container-childCollection-View record')
@ -50,8 +50,9 @@ test.describe('where table block can be added', () => {
// 添加父表关系区块 // 添加父表关系区块
await page.getByRole('menuitem', { name: 'Table right' }).waitFor({ state: 'detached' }); await page.getByRole('menuitem', { name: 'Table right' }).waitFor({ state: 'detached' });
await page.getByLabel('schema-initializer-Grid-popup').hover(); await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'Associated records right' }).waitFor({ state: 'detached' });
await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Table right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records' }).last().hover(); await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
await page.getByRole('menuitem', { name: 'parentAssociationField' }).click(); await page.getByRole('menuitem', { name: 'parentAssociationField' }).click();
await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover(); await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover();
await page.getByRole('menuitem', { name: 'parentTargetText' }).click(); await page.getByRole('menuitem', { name: 'parentTargetText' }).click();
@ -72,6 +73,7 @@ test.describe('where table block can be added', () => {
// 通过 Other records 创建一个表格区块 // 通过 Other records 创建一个表格区块
await page.getByLabel('schema-initializer-Grid-popup').hover(); await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'Other records right' }).waitFor({ state: 'detached' });
await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Table right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click(); await page.getByRole('menuitem', { name: 'Users' }).click();

View File

@ -17,7 +17,7 @@ vi.mock('@formily/shared', () => {
describe('createTableBLockSchemaV2', () => { describe('createTableBLockSchemaV2', () => {
it('should create a default table block schema with minimum options', () => { it('should create a default table block schema with minimum options', () => {
const options = { dataSource: 'abc', collectionName: 'users', association: 'users.roles', rowKey: 'rowKey' }; const options = { dataSource: 'abc', collectionName: 'users', association: 'users.roles' };
const schema = createTableBlockUISchema(options); const schema = createTableBlockUISchema(options);
expect(schema).toMatchInlineSnapshot(` expect(schema).toMatchInlineSnapshot(`
@ -85,7 +85,6 @@ describe('createTableBLockSchemaV2', () => {
"params": { "params": {
"pageSize": 20, "pageSize": 20,
}, },
"rowKey": "rowKey",
"showIndex": true, "showIndex": true,
}, },
"x-filter-targets": [], "x-filter-targets": [],

View File

@ -13,10 +13,9 @@ import { uid } from '@formily/shared';
export const createTableBlockUISchema = (options: { export const createTableBlockUISchema = (options: {
dataSource: string; dataSource: string;
collectionName?: string; collectionName?: string;
rowKey?: string;
association?: string; association?: string;
}): ISchema => { }): ISchema => {
const { collectionName, dataSource, rowKey, association } = options; const { collectionName, dataSource, association } = options;
if (!dataSource) { if (!dataSource) {
throw new Error('dataSource is required'); throw new Error('dataSource is required');
@ -35,7 +34,6 @@ export const createTableBlockUISchema = (options: {
params: { params: {
pageSize: 20, pageSize: 20,
}, },
rowKey,
showIndex: true, showIndex: true,
dragSort: false, dragSort: false,
}, },

View File

@ -11,15 +11,19 @@ import { useFieldSchema } from '@formily/react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter'; import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter';
import { useParentRecordCommon } from '../../../useParentRecordCommon'; import { useParentRecordCommon } from '../../../useParentRecordCommon';
import { useDataSourceManager } from '../../../../../data-source';
export const useTableBlockDecoratorProps = (props) => { export const useTableBlockDecoratorProps = (props) => {
const { params, parseVariableLoading } = useTableBlockParams(props); const { params, parseVariableLoading } = useTableBlockParams(props);
const parentRecord = useParentRecordCommon(props.association); const parentRecord = useParentRecordCommon(props.association);
const dm = useDataSourceManager();
const collection = dm.getDataSource(props.dataSource)?.collectionManager.getCollection(props.collection);
return { return {
params, params,
parentRecord, parentRecord,
parseVariableLoading, parseVariableLoading,
rowKey: collection?.filterTargetKey || 'id',
}; };
}; };

View File

@ -26,6 +26,8 @@ import { setTheDataScopeSchemaSettingsItem } from '../../../../schema-settings/s
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem'; import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem';
import { SchemaSettingsItemType } from '../../../../application'; import { SchemaSettingsItemType } from '../../../../application';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
const enabledIndexColumn: SchemaSettingsItemType = { const enabledIndexColumn: SchemaSettingsItemType = {
name: 'enableIndexColumn', name: 'enableIndexColumn',
@ -37,7 +39,7 @@ const enabledIndexColumn: SchemaSettingsItemType = {
const { dn } = useDesignable(); const { dn } = useDesignable();
return { return {
title: t('Enable index column'), title: t('Enable index column'),
checked: field.decoratorProps.enableSelectColumn !== false, checked: field.decoratorProps.enableIndexÏColumn !== false,
onChange: async (enableIndexÏColumn) => { onChange: async (enableIndexÏColumn) => {
field.decoratorProps = field.decoratorProps || {}; field.decoratorProps = field.decoratorProps || {};
field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn; field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn;
@ -64,6 +66,19 @@ export const tableBlockSettings = new SchemaSettings({
name: 'setTheBlockHeight', name: 'setTheBlockHeight',
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{
name: 'linkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection_deprecated();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
};
},
},
{ {
name: 'treeTable', name: 'treeTable',
type: 'switch', type: 'switch',
@ -138,7 +153,6 @@ export const tableBlockSettings = new SchemaSettings({
const { resource } = field.decoratorProps; const { resource } = field.decoratorProps;
const collectionField = resource && getCollectionField(resource); const collectionField = resource && getCollectionField(resource);
const api = useAPIClient(); const api = useAPIClient();
return { return {
title: t('Enable drag and drop sorting'), title: t('Enable drag and drop sorting'),
checked: field.decoratorProps.dragSort, checked: field.decoratorProps.dragSort,

View File

@ -12,11 +12,14 @@ import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { FilterBlockType } from '../../../../filter-provider'; import { FilterBlockType } from '../../../../filter-provider';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem';
import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem'; import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem';
import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks'; import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks';
import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettingsTemplate'; import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettingsTemplate';
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
import { useCollection } from '../../../../data-source/collection/CollectionProvider';
export const filterCollapseBlockSettings = new SchemaSettings({ export const filterCollapseBlockSettings = new SchemaSettings({
name: 'blockSettings:filterCollapse', name: 'blockSettings:filterCollapse',
@ -29,6 +32,19 @@ export const filterCollapseBlockSettings = new SchemaSettings({
name: 'setTheBlockHeight', name: 'setTheBlockHeight',
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
};
},
},
{ {
name: 'ConvertReferenceToDuplicate', name: 'ConvertReferenceToDuplicate',
Component: SchemaSettingsTemplate, Component: SchemaSettingsTemplate,

View File

@ -19,6 +19,7 @@ import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/Schema
import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks'; import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks';
import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider'; import { useBlockTemplateContext } from '../../../../schema-templates/BlockTemplateProvider';
import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem'; import { SchemaSettingsLayoutItem } from '../../../../schema-settings/SchemaSettingsLayoutItem';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
export const filterFormBlockSettings = new SchemaSettings({ export const filterFormBlockSettings = new SchemaSettings({
name: 'blockSettings:filterForm', name: 'blockSettings:filterForm',
@ -48,12 +49,27 @@ export const filterFormBlockSettings = new SchemaSettings({
}, },
}, },
{ {
name: 'linkageRules', name: 'fieldLinkageRules',
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const { name } = useCollection_deprecated(); const { name } = useCollection_deprecated();
const { t } = useTranslation();
return { return {
collectionName: name, collectionName: name,
title: t('Field Linkage rules'),
};
},
},
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { name } = useCollection();
const { t } = useTranslation();
return {
collectionName: name,
title: t('Block Linkage rules'),
category: LinkageRuleCategory.block,
}; };
}, },
}, },

View File

@ -7,11 +7,14 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import { useField } from '@formily/react'; import { useField, useFieldSchema } from '@formily/react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem'; import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem';
import { SchemaSettingsRenderEngine } from '../../../../schema-settings/SchemaSettingsRenderEngine'; import { SchemaSettingsRenderEngine } from '../../../../schema-settings/SchemaSettingsRenderEngine';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
import { LinkageRuleCategory } from '../../../../schema-settings/LinkageRules/type';
export const markdownBlockSettings = new SchemaSettings({ export const markdownBlockSettings = new SchemaSettings({
name: 'blockSettings:markdown', name: 'blockSettings:markdown',
items: [ items: [
@ -21,7 +24,6 @@ export const markdownBlockSettings = new SchemaSettings({
useComponentProps() { useComponentProps() {
const field = useField(); const field = useField();
const { t } = useTranslation(); const { t } = useTranslation();
return { return {
title: t('Edit markdown'), title: t('Edit markdown'),
onClick: () => { onClick: () => {
@ -34,6 +36,27 @@ export const markdownBlockSettings = new SchemaSettings({
name: 'setTheBlockHeight', name: 'setTheBlockHeight',
Component: SchemaSettingsBlockHeightItem, Component: SchemaSettingsBlockHeightItem,
}, },
{
name: 'blockLinkageRules',
Component: SchemaSettingsLinkageRules,
useComponentProps() {
const { t } = useTranslation();
const fieldSchema = useFieldSchema();
const underForm = fieldSchema['x-decorator'] === 'FormItem';
return {
title: underForm ? t('Linkage rules') : t('Block Linkage rules'),
category: LinkageRuleCategory.block,
returnScope: (options) => {
return options.filter((v) => {
if (!underForm) {
return !['$nForm', '$nRecord'].includes(v.value);
}
return true;
});
},
};
},
},
{ {
name: 'setBlockTemplate', name: 'setBlockTemplate',
Component: SchemaSettingsRenderEngine, Component: SchemaSettingsRenderEngine,

View File

@ -0,0 +1,91 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useEffect, useState } from 'react';
import { VariableOption, VariablesContextType } from '../../variables/types';
import { conditionAnalyses } from '../../schema-component/common/utils/uitls';
import { useApp } from '../../application';
import { useCollectionRecord } from '../../data-source';
enum ActionType {
Visible = 'visible',
Hidden = 'hidden',
}
const linkageAction = async (
{
operator,
condition,
variables,
localVariables,
conditionType,
displayResult,
}: {
operator;
condition;
variables: VariablesContextType;
localVariables: VariableOption[];
conditionType: 'advanced';
displayResult: any[];
},
jsonLogic: any,
) => {
switch (operator) {
case ActionType.Visible:
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) {
displayResult.push(ActionType.Visible);
}
return displayResult;
case ActionType.Hidden:
if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables, conditionType }, jsonLogic)) {
displayResult.push(ActionType.Hidden);
}
return displayResult;
default:
return null;
}
};
export const useReactiveLinkageEffect = (
linkageRules: any[],
variables: VariablesContextType,
localVariables: VariableOption[],
triggerLinkageUpdate,
) => {
const app = useApp();
const jsonLogic = app.jsonLogic;
const [displayResult, setDisplayResult] = useState<string[] | null>(null);
const record = useCollectionRecord();
useEffect(() => {
const runLinkages = async () => {
const result: string[] = [];
for (const rule of linkageRules.filter((r) => !r.disabled)) {
for (const action of rule.actions || []) {
await linkageAction(
{
operator: action.operator,
condition: rule.condition,
variables,
localVariables,
conditionType: rule.conditionType,
displayResult: result,
},
jsonLogic,
);
}
}
setDisplayResult(result);
};
runLinkages();
}, [linkageRules, triggerLinkageUpdate, record]);
return displayResult;
};

View File

@ -28,6 +28,7 @@ import { isSubMode } from '../../../../schema-component/antd/association-field/u
import { useIsAssociationField } from '../../../../schema-component/antd/form-item'; import { useIsAssociationField } from '../../../../schema-component/antd/form-item';
import { FormLinkageRules } from '../../../../schema-settings/LinkageRules'; import { FormLinkageRules } from '../../../../schema-settings/LinkageRules';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings/SchemaSettings'; import { SchemaSettingsLinkageRules } from '../../../../schema-settings/SchemaSettings';
import { useColumnSchema } from '../../../../schema-component';
import { SchemaSettingsItemType } from '../../../../application'; import { SchemaSettingsItemType } from '../../../../application';
const enabledIndexColumn: SchemaSettingsItemType = { const enabledIndexColumn: SchemaSettingsItemType = {
@ -338,11 +339,12 @@ export const linkageRules = {
Component: SchemaSettingsLinkageRules, Component: SchemaSettingsLinkageRules,
useComponentProps() { useComponentProps() {
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const schema = useFieldSchema();
const { fieldSchema: columnSchema } = useColumnSchema();
const fieldSchema = columnSchema || schema;
const cm = useCollectionManager(); const cm = useCollectionManager();
const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']); const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
const { rerenderDataBlock } = useRerenderDataBlock(); const { rerenderDataBlock } = useRerenderDataBlock();
return { return {
collectionName: collectionField?.target, collectionName: collectionField?.target,
Component: LinkageRulesComponent, Component: LinkageRulesComponent,

View File

@ -57,6 +57,7 @@ test.describe('add blocks to the popup', () => {
await page.getByRole('menuitem', { name: 'Details right' }).hover(); await page.getByRole('menuitem', { name: 'Details right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records' }).last().hover(); await page.getByRole('menuitem', { name: 'Associated records' }).last().hover();
await page.getByRole('menuitem', { name: 'Roles' }).click(); await page.getByRole('menuitem', { name: 'Roles' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover(); await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover();
await page.getByRole('menuitem', { name: 'Role UID' }).click(); await page.getByRole('menuitem', { name: 'Role UID' }).click();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);

View File

@ -71,7 +71,7 @@ test.describe('sub page', () => {
expect(page.url()).not.toContain('/popups/'); expect(page.url()).not.toContain('/popups/');
// 确认是否回到了主页面 // 确认是否回到了主页面
await page.getByText('Users单层子页面Configure').hover(); // await page.getByText('Users单层子页面Configure').hover();
await expect( await expect(
page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }), page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }),
).toBeVisible(); ).toBeVisible();

View File

@ -14,12 +14,13 @@ import { getSubAppName } from '@nocobase/sdk';
import { tval } from '@nocobase/utils/client'; import { tval } from '@nocobase/utils/client';
import { Button, Modal, Result, Spin } from 'antd'; import { Button, Modal, Result, Spin } from 'antd';
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Navigate, useNavigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import { ACLPlugin } from '../acl'; import { ACLPlugin } from '../acl';
import { Application } from '../application'; import { Application } from '../application';
import { Plugin } from '../application/Plugin'; import { Plugin } from '../application/Plugin';
import { BlockSchemaComponentPlugin } from '../block-provider'; import { BlockSchemaComponentPlugin } from '../block-provider';
import { CollectionPlugin } from '../collection-manager'; import { CollectionPlugin } from '../collection-manager';
import { AppNotFound } from '../common/AppNotFound';
import { RemoteDocumentTitlePlugin } from '../document-title'; import { RemoteDocumentTitlePlugin } from '../document-title';
import { PinnedListPlugin } from '../plugin-manager'; import { PinnedListPlugin } from '../plugin-manager';
import { PMPlugin } from '../pm'; import { PMPlugin } from '../pm';
@ -260,22 +261,6 @@ const AppMaintainingDialog: FC<{ app: Application; error: Error }> = observer(
{ displayName: 'AppMaintainingDialog' }, { displayName: 'AppMaintainingDialog' },
); );
export const AppNotFound = () => {
const navigate = useNavigate();
return (
<Result
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
extra={
<Button onClick={() => navigate('/', { replace: true })} type="primary">
Back Home
</Button>
}
/>
);
};
export class NocoBaseBuildInPlugin extends Plugin { export class NocoBaseBuildInPlugin extends Plugin {
async afterAdd() { async afterAdd() {
this.app.addComponents({ this.app.addComponents({

View File

@ -10,7 +10,7 @@
export * from './PluginManagerLink'; export * from './PluginManagerLink';
import { PageHeader } from '@ant-design/pro-layout'; import { PageHeader } from '@ant-design/pro-layout';
import { useDebounce } from 'ahooks'; import { useDebounce } from 'ahooks';
import { Button, Col, Divider, Input, List, Modal, Result, Row, Space, Spin, Table, Tabs, TableProps } from 'antd'; import { Button, Col, Divider, Input, List, Modal, Row, Space, Spin, Table, TableProps, Tabs } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -19,6 +19,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useACLRoleContext } from '../acl/ACLProvider'; import { useACLRoleContext } from '../acl/ACLProvider';
import { useAPIClient, useRequest } from '../api-client'; import { useAPIClient, useRequest } from '../api-client';
import { AppNotFound } from '../common/AppNotFound';
import { useDocumentTitle } from '../document-title'; import { useDocumentTitle } from '../document-title';
import { useToken } from '../style'; import { useToken } from '../style';
import { PluginCard } from './PluginCard'; import { PluginCard } from './PluginCard';
@ -146,7 +147,7 @@ function BulkEnableButton({ plugins = [] }) {
width: 300, width: 300,
ellipsis: true, ellipsis: true,
}, },
] as TableProps['columns'] ] as TableProps<any>['columns']
} }
dataSource={items} dataSource={items}
/> />
@ -409,6 +410,6 @@ export const PluginManager = () => {
</div> </div>
</div> </div>
) : ( ) : (
<Result status="404" title="404" subTitle="Sorry, the page you visited does not exist." /> <AppNotFound />
); );
}; };

View File

@ -9,11 +9,12 @@
import { PageHeader } from '@ant-design/pro-layout'; import { PageHeader } from '@ant-design/pro-layout';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { Layout, Menu, Result } from 'antd'; import { Layout, Menu } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { createContext, useCallback, useEffect, useMemo } from 'react'; import React, { createContext, useCallback, useEffect, useMemo } from 'react';
import { Navigate, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'; import { Navigate, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
import { ADMIN_SETTINGS_PATH, PluginSettingsPageType, useApp } from '../application'; import { ADMIN_SETTINGS_PATH, PluginSettingsPageType, useApp } from '../application';
import { AppNotFound } from '../common/AppNotFound';
import { useDocumentTitle } from '../document-title'; import { useDocumentTitle } from '../document-title';
import { useCompile } from '../schema-component'; import { useCompile } from '../schema-component';
import { useStyles } from './style'; import { useStyles } from './style';
@ -223,13 +224,7 @@ export const AdminSettingsLayout = () => {
} }
/> />
)} )}
<div className={styles.pageContent}> <div className={styles.pageContent}>{currentSetting ? <Outlet /> : <AppNotFound />}</div>
{currentSetting ? (
<Outlet />
) : (
<Result status="404" title="404" subTitle="Sorry, the page you visited does not exist." />
)}
</div>
</Layout.Content> </Layout.Content>
</Layout> </Layout>
</div> </div>

View File

@ -18,7 +18,6 @@ import { useTranslation } from 'react-i18next';
import { Link, Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom'; import { Link, Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
import { import {
ACLRolesCheckProvider, ACLRolesCheckProvider,
AppNotFound,
CurrentAppInfoProvider, CurrentAppInfoProvider,
DndContext, DndContext,
Icon, Icon,
@ -46,6 +45,7 @@ import {
useLocationNoUpdate, useLocationNoUpdate,
} from '../../../application/CustomRouterContextProvider'; } from '../../../application/CustomRouterContextProvider';
import { Plugin } from '../../../application/Plugin'; import { Plugin } from '../../../application/Plugin';
import { AppNotFound } from '../../../common/AppNotFound';
import { withTooltipComponent } from '../../../hoc/withTooltipComponent'; import { withTooltipComponent } from '../../../hoc/withTooltipComponent';
import { menuItemInitializer } from '../../../modules/menu/menuItemInitializer'; import { menuItemInitializer } from '../../../modules/menu/menuItemInitializer';
import { useMenuTranslation } from '../../../schema-component/antd/menu/locale'; import { useMenuTranslation } from '../../../schema-component/antd/menu/locale';
@ -503,6 +503,8 @@ const subMenuItemRender = (item, dom) => {
}; };
const CollapsedButton: FC<{ collapsed: boolean }> = (props) => { const CollapsedButton: FC<{ collapsed: boolean }> = (props) => {
const { token } = useToken();
return ( return (
<RouteContext.Consumer> <RouteContext.Consumer>
{(context) => {(context) =>
@ -515,7 +517,7 @@ const CollapsedButton: FC<{ collapsed: boolean }> = (props) => {
// Fix the issue where the collapse/expand button is covered by subpages // Fix the issue where the collapse/expand button is covered by subpages
.ant-pro-sider-collapsed-button { .ant-pro-sider-collapsed-button {
top: 64px; top: 64px;
left: ${props.collapsed ? 52 : 188}px; left: ${props.collapsed ? 52 : (token.siderWidth || 200) - 12}px;
z-index: 200; z-index: 200;
transition: left 0.2s; transition: left 0.2s;
} }
@ -671,7 +673,7 @@ export const InternalAdminLayout = () => {
<DndContext onDragEnd={onDragEnd}> <DndContext onDragEnd={onDragEnd}>
<ProLayout <ProLayout
contentStyle={contentStyle} contentStyle={contentStyle}
siderWidth={200} siderWidth={token.siderWidth || 200}
className={resetStyle} className={resetStyle}
location={location} location={location}
route={route} route={route}

View File

@ -47,6 +47,11 @@ const components = { TreeSelect };
const toItems = (routes: NocoBaseDesktopRoute[], { t, compile }) => { const toItems = (routes: NocoBaseDesktopRoute[], { t, compile }) => {
const items = []; const items = [];
for (const route of routes) { for (const route of routes) {
// filter out the tabs
if (route.type === NocoBaseDesktopRouteType.tabs) {
continue;
}
const item = { const item = {
label: isVariable(route.title) ? compile(route.title) : t(route.title), label: isVariable(route.title) ? compile(route.title) : t(route.title),
value: `${route.id}||${route.type}`, value: `${route.id}||${route.type}`,

View File

@ -10,32 +10,16 @@
import { observer } from '@formily/react'; import { observer } from '@formily/react';
import classnames from 'classnames'; import classnames from 'classnames';
import React from 'react'; import React from 'react';
import { Space, Tooltip } from 'antd';
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
import Action from './Action'; import Action from './Action';
import { ComposedAction } from './types'; import { ComposedAction } from './types';
import { Icon } from '../../../icon';
const WrapperComponent = React.forwardRef(
({ component: Component = 'a', icon, onlyIcon, children, ...restProps }: any, ref) => {
return (
<Component ref={ref} {...restProps}>
<Tooltip title={restProps.title}>
<span style={{ marginRight: 3 }}>{icon && typeof icon === 'string' ? <Icon type={icon} /> : icon}</span>
</Tooltip>
{onlyIcon ? children[1] : children}
</Component>
);
},
);
WrapperComponent.displayName = 'WrapperComponentLink';
export const ActionLink: ComposedAction = withDynamicSchemaProps( export const ActionLink: ComposedAction = withDynamicSchemaProps(
observer((props: any) => { observer((props: any) => {
return ( return (
<Action <Action
{...props} {...props}
component={props.component || WrapperComponent} component={props.component || 'a'}
className={classnames('nb-action-link', props.className)} className={classnames('nb-action-link', props.className)}
isLink isLink
/> />

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