From ec5e4b03365e2afd152eddf9c3dbfe5233ec1778 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Fri, 5 Jul 2024 20:15:11 +0800 Subject: [PATCH] feat: open subpages within the main page (#4797) * feat: open subpages within the main page * fix: fix known bugs and fix tests * refactor: optimize popups style * fix(style): avoid flickering * chore: add comment * fix: optimize nested popups * refactor: optimize path after closing popup * fix: fix draging * chore: optimize routing stack * feat: add back button for sub page * test: add e2e test * fix: enable returning from URL-opened pop-ups and subpages * fix: enable subpages to navigate via main page menu * refactor: optimize code * fix: fix closePopup method * fix: ensure block data refreshes after submitting from pop-up * fix: add 404 info when popup is deleted and add e2e test * fix: fix embed page * chore: add translation * fix(duplicate): fix e2e test * fix: fix filterByTK * chore(CI): add job for workflow-approval * chore(CI): fix syntax * chore(CI): add 'plugin-workflow-approval' in needs --- .github/workflows/e2e.yml | 83 +- .../src/block-provider/BlockProvider.tsx | 6 - packages/core/client/src/locale/en_US.json | 3 +- packages/core/client/src/locale/es_ES.json | 3 +- packages/core/client/src/locale/fr_FR.json | 3 +- packages/core/client/src/locale/ja_JP.json | 3 +- packages/core/client/src/locale/ko_KR.json | 3 +- packages/core/client/src/locale/pt_BR.json | 3 +- packages/core/client/src/locale/ru_RU.json | 3 +- packages/core/client/src/locale/tr_TR.json | 3 +- packages/core/client/src/locale/uk_UA.json | 3 +- packages/core/client/src/locale/zh-CN.json | 3 +- packages/core/client/src/locale/zh-TW.json | 3 +- .../__e2e__/action.schemaSetting.test.ts | 17 + .../form-create/schemaSettings.test.ts | 20 +- .../table/__e2e__/schemaSettings.test.ts | 9 +- .../popup/__e2e__/deletedPopups.test.ts | 31 + .../src/modules/popup/__e2e__/router.test.ts | 68 + .../src/modules/popup/__e2e__/subPage.test.ts | 79 + .../modules/popup/__e2e__/templatesOfBug.ts | 2306 +++++++++++++++++ .../src/nocobase-buildin-plugin/index.tsx | 5 - .../route-switch/antd/admin-layout/index.tsx | 62 +- .../antd/action/Action.Drawer.style.ts | 2 +- .../antd/action/Action.Drawer.tsx | 16 +- .../antd/action/Action.Modal.tsx | 17 +- .../Action.Page.style.ts} | 10 +- .../antd/action/Action.Page.tsx | 94 +- .../schema-component/antd/action/Action.tsx | 8 +- .../antd/action/__tests__/action.test.tsx | 40 - .../schema-component/antd/action/context.tsx | 46 +- .../src/schema-component/antd/action/hooks.ts | 28 +- .../antd/action/hooks/usePopupSlotDOM.ts | 24 + .../src/schema-component/antd/menu/Menu.tsx | 6 +- .../antd/page/BackButtonUsedInSubPage.tsx | 51 + .../schema-component/antd/page/Page.style.ts | 2 + .../src/schema-component/antd/page/Page.tsx | 2 +- .../schema-component/antd/page/PagePopups.tsx | 222 +- .../antd/page/PopupSettingsProvider.tsx | 37 +- .../schema-component/antd/page/SubPages.tsx | 288 -- .../antd/page/__tests__/SubPages.test.tsx | 122 - .../page/__tests__/pagePopupUtils.test.ts | 4 +- .../src/schema-component/antd/page/index.ts | 1 - .../antd/page/pagePopupUtils.tsx | 84 +- ...ePopupContextInActionOrAssociationField.ts | 19 - .../src/schema-component/antd/tabs/Tabs.tsx | 9 +- .../components/CreateRecordAction.tsx | 6 - .../src/schema-items/OpenModeSchemaItems.tsx | 6 +- .../src/client/router/Application.tsx | 70 +- 48 files changed, 3140 insertions(+), 793 deletions(-) create mode 100644 packages/core/client/src/modules/popup/__e2e__/deletedPopups.test.ts create mode 100644 packages/core/client/src/modules/popup/__e2e__/router.test.ts create mode 100644 packages/core/client/src/modules/popup/__e2e__/subPage.test.ts rename packages/core/client/src/schema-component/antd/{page/SubPages.style.ts => action/Action.Page.style.ts} (70%) create mode 100644 packages/core/client/src/schema-component/antd/action/hooks/usePopupSlotDOM.ts create mode 100644 packages/core/client/src/schema-component/antd/page/BackButtonUsedInSubPage.tsx delete mode 100644 packages/core/client/src/schema-component/antd/page/SubPages.tsx delete mode 100644 packages/core/client/src/schema-component/antd/page/__tests__/SubPages.test.tsx diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 4b4d7c315..1c86f9351 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -199,7 +199,87 @@ jobs: - run: npx playwright install chromium --with-deps - name: Test with postgres - run: yarn e2e p-test --match 'packages/**/{plugin-workflow,plugin-workflow-*}/**/__e2e__/**/*.test.ts' + run: yarn e2e p-test --match 'packages/**/{plugin-workflow,plugin-workflow-*}/**/__e2e__/**/*.test.ts' --ignore 'packages/**/plugin-workflow-approval/**/__e2e__/**/*.test.ts' + env: + __E2E__: true + APP_ENV: production + LOGGER_LEVEL: error + DB_DIALECT: postgres + DB_HOST: postgres + DB_PORT: 5432 + DB_USER: nocobase + DB_PASSWORD: password + DB_DATABASE: nocobase + APPEND_PRESET_LOCAL_PLUGINS: ${{ steps.vars.outputs.var2 }} + + - name: Upload e2e-report + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: e2e-report-${{ github.job }} # 为了防止在多个任务中存在冲突 + path: ./storage/playwright/tests-report-blob/blob-*/* + + timeout-minutes: 60 + + plugin-workflow-approval: + name: plugin-workflow-approval + needs: build + runs-on: ubuntu-latest + container: node:18 + services: + # Label used to access the service container + postgres: + # Docker Hub image + image: postgres:11 + # Provide the password for postgres + env: + POSTGRES_USER: nocobase + POSTGRES_PASSWORD: password + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v4 + - name: Checkout pro-plugins + continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true + uses: actions/checkout@v4 + with: + repository: nocobase/pro-plugins + ref: main + path: packages/pro-plugins + ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }} + - name: Set variables + continue-on-error: true # 外部开发者提交 PR 的时候因为没有权限这里会报错,为了能够继续执行后续步骤,所以这里设置为 continue-on-error: true + run: | + APPEND_PRESET_LOCAL_PLUGINS=$(find ./packages/pro-plugins/@nocobase -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sed 's/^plugin-//' | tr '\n' ',' | sed 's/,$//') + echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT + id: vars + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v4 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - run: yarn + + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: build-artifact + path: packages + + - run: npx playwright install chromium --with-deps + - name: Test with postgres + run: yarn e2e p-test --match 'packages/**/plugin-workflow-approval/**/__e2e__/**/*.test.ts' env: __E2E__: true APP_ENV: production @@ -307,6 +387,7 @@ jobs: needs: - core-and-plugins - plugin-workflow + - plugin-workflow-approval - plugin-data-source-main if: ${{ !cancelled() && github.event.pull_request.number }} steps: diff --git a/packages/core/client/src/block-provider/BlockProvider.tsx b/packages/core/client/src/block-provider/BlockProvider.tsx index 6575a2063..51bfcd958 100644 --- a/packages/core/client/src/block-provider/BlockProvider.tsx +++ b/packages/core/client/src/block-provider/BlockProvider.tsx @@ -36,7 +36,6 @@ import { import { DataBlockCollector } from '../filter-provider/FilterProvider'; import { useSourceId } from '../modules/blocks/useSourceId'; import { RecordProvider, useRecordIndex } from '../record-provider'; -import { usePagePopup } from '../schema-component/antd/page/pagePopupUtils'; import { useAssociationNames } from './hooks'; import { useDataBlockParentRecord } from './hooks/useDataBlockParentRecord'; @@ -300,11 +299,6 @@ export const useFilterByTk = () => { const { getCollectionField } = useCollectionManager_deprecated(); const assoc = useBlockAssociationContext(); const withoutTableFieldResource = useContext(WithoutTableFieldResource); - const { popupParams } = usePagePopup(); - - if (popupParams?.filterbytk) { - return popupParams.filterbytk; - } if (!withoutTableFieldResource) { if (resource instanceof TableFieldResource || __parent?.block === 'TableField') { diff --git a/packages/core/client/src/locale/en_US.json b/packages/core/client/src/locale/en_US.json index 1807bdd95..15d3b9732 100644 --- a/packages/core/client/src/locale/en_US.json +++ b/packages/core/client/src/locale/en_US.json @@ -833,5 +833,6 @@ "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.", "URL search params": "URL search params", "Expand All": "Expand All", - "Search": "Search" + "Search": "Search", + "Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist." } diff --git a/packages/core/client/src/locale/es_ES.json b/packages/core/client/src/locale/es_ES.json index 9e70cb364..84f5288d0 100644 --- a/packages/core/client/src/locale/es_ES.json +++ b/packages/core/client/src/locale/es_ES.json @@ -762,5 +762,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "La variable ha sido obsoleta; \"Formulario actual\" puede ser utilizada como sustituto", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "El valor de esta variable se deriva de la cadena de consulta de la URL de la página. Esta variable sólo puede utilizarse normalmente cuando la página tiene una cadena de consulta.", "URL search params": "Parámetros de búsqueda de URL", - "Expand All": "Expandir todo" + "Expand All": "Expandir todo", + "Sorry, the page you visited does not exist.": "Lo siento, la página que visitaste no existe." } diff --git a/packages/core/client/src/locale/fr_FR.json b/packages/core/client/src/locale/fr_FR.json index 781913ac5..83055423c 100644 --- a/packages/core/client/src/locale/fr_FR.json +++ b/packages/core/client/src/locale/fr_FR.json @@ -782,5 +782,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "La variable a été obsolète ; \"Formulaire actuel\" peut être utilisé comme substitut", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "La valeur de cette variable est dérivée de la chaîne de requête de l'URL de la page. Cette variable ne peut être utilisée normalement que lorsque la page a une chaîne de requête.", "URL search params": "Paramètres de recherche d'URL", - "Expand All": "Tout déplier" + "Expand All": "Tout déplier", + "Sorry, the page you visited does not exist.": "Désolé, la page que vous avez visitée n'existe pas." } diff --git a/packages/core/client/src/locale/ja_JP.json b/packages/core/client/src/locale/ja_JP.json index 27afe7191..337b5a2e6 100644 --- a/packages/core/client/src/locale/ja_JP.json +++ b/packages/core/client/src/locale/ja_JP.json @@ -701,5 +701,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "この変数は非推奨です。代わりに「現在のフォーム」を使用してください", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "この変数の値はページURLのクエリ文字列から取得されます。この変数は、ページにクエリ文字列がある場合にのみ正常に使用できます。", "URL search params": "URL検索パラメータ", - "Expand All": "すべて展開" + "Expand All": "すべて展開", + "Sorry, the page you visited does not exist.": "申し訳ありませんが、お探しのページは存在しません。" } diff --git a/packages/core/client/src/locale/ko_KR.json b/packages/core/client/src/locale/ko_KR.json index 0d1d431a4..ec0349115 100644 --- a/packages/core/client/src/locale/ko_KR.json +++ b/packages/core/client/src/locale/ko_KR.json @@ -873,5 +873,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "변수가 폐기되었습니다. \"현재 폼\"을 대체로 사용할 수 있습니다", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "이 변수의 값은 페이지 URL의 쿼리 문자열에서 파생됩니다. 이 변수는 페이지에 쿼리 문자열이 있는 경우에만 정상적으로 사용할 수 있습니다.", "URL search params": "URL 검색 매개변수", -"Expand All": "모두 펼치기" +"Expand All": "모두 펼치기", +"Sorry, the page you visited does not exist.": "죄송합니다. 방문한 페이지가 존재하지 않습니다." } diff --git a/packages/core/client/src/locale/pt_BR.json b/packages/core/client/src/locale/pt_BR.json index 7e28e499c..d54edc224 100644 --- a/packages/core/client/src/locale/pt_BR.json +++ b/packages/core/client/src/locale/pt_BR.json @@ -739,5 +739,6 @@ "URL search params": "Parâmetros de pesquisa de URL", "Expand All": "Expandir tudo", "Parent popup record": "Registro pop-up pai", - "Current popup record": "Registro pop-up atual" + "Current popup record": "Registro pop-up atual", + "Sorry, the page you visited does not exist.": "Desculpe, a página que você visitou não existe." } diff --git a/packages/core/client/src/locale/ru_RU.json b/packages/core/client/src/locale/ru_RU.json index 7ee11eed9..f6976c151 100644 --- a/packages/core/client/src/locale/ru_RU.json +++ b/packages/core/client/src/locale/ru_RU.json @@ -576,5 +576,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "Переменная устарела; \"Текущая форма\" может быть использована в качестве замены", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "Значение этой переменной происходит из строки запроса URL страницы. Эта переменная может использоваться только в том случае, если у страницы есть строка запроса.", "URL search params": "Параметры поиска URL", - "Expand All": "Развернуть все" + "Expand All": "Развернуть все", + "Sorry, the page you visited does not exist.": "Извините, посещенной вами страницы не существует." } diff --git a/packages/core/client/src/locale/tr_TR.json b/packages/core/client/src/locale/tr_TR.json index 9131834f1..5da0ab375 100644 --- a/packages/core/client/src/locale/tr_TR.json +++ b/packages/core/client/src/locale/tr_TR.json @@ -574,5 +574,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "Değişken kullanımdan kaldırıldı; \"Geçerli form\" yerine kullanılabilir", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "Bu değişkenin değeri sayfa URL'sinin sorgu dizgisinden türetilir. Bu değişken, sayfanın bir sorgu dizgisi olduğunda yalnızca normal olarak kullanılabilir.", "URL search params": "URL arama parametreleri", - "Expand All": "Tümünü genişlet" + "Expand All": "Tümünü genişlet", + "Sorry, the page you visited does not exist.": "Üzgünüz, ziyaret ettiğiniz sayfa mevcut değil." } diff --git a/packages/core/client/src/locale/uk_UA.json b/packages/core/client/src/locale/uk_UA.json index ae32622ad..052e586ce 100644 --- a/packages/core/client/src/locale/uk_UA.json +++ b/packages/core/client/src/locale/uk_UA.json @@ -782,5 +782,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "Змінна була застарілою; \"Поточна форма\" може бути використана як заміна", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "Значення цієї змінної походить з рядка запиту URL-адреси сторінки. Цю змінну можна використовувати нормально лише тоді, коли у сторінки є рядок запиту.", "URL search params": "Параметри пошуку URL", - "Expand All": "Розгорнути все" + "Expand All": "Розгорнути все", + "Sorry, the page you visited does not exist.": "Вибачте, сторінка, яку ви відвідали, не існує." } diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index efa153cc0..a158aff1f 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -963,5 +963,6 @@ "Add parameter": "添加参数", "URL search params": "URL 查询参数", "Expand All": "展开全部", - "Search": "搜索" + "Search": "搜索", + "Sorry, the page you visited does not exist.": "抱歉,你访问的页面不存在。" } diff --git a/packages/core/client/src/locale/zh-TW.json b/packages/core/client/src/locale/zh-TW.json index e51eca510..b899edba0 100644 --- a/packages/core/client/src/locale/zh-TW.json +++ b/packages/core/client/src/locale/zh-TW.json @@ -871,5 +871,6 @@ "This variable has been deprecated and can be replaced with \"Current form\"": "該變數已被棄用,可以使用“當前表單”作為替代", "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "該變數的值來自頁面 URL 的查詢字符串,只有當頁面有查詢字符串時,該變數才能正常使用。", "URL search params": "URL 查詢參數", - "Expand All": "展開全部" + "Expand All": "展開全部", + "Sorry, the page you visited does not exist.": "抱歉,你訪問的頁面不存在。" } diff --git a/packages/core/client/src/modules/actions/__e2e__/action.schemaSetting.test.ts b/packages/core/client/src/modules/actions/__e2e__/action.schemaSetting.test.ts index d4c8bee00..965888999 100644 --- a/packages/core/client/src/modules/actions/__e2e__/action.schemaSetting.test.ts +++ b/packages/core/client/src/modules/actions/__e2e__/action.schemaSetting.test.ts @@ -64,5 +64,22 @@ test.describe('action settings', () => { // close the first popup await page.getByLabel('drawer-Action.Container-users-Edit record-mask').click(); await expect(page.getByLabel('block-item-CardItem-users-').getByRole('button', { name: 'abc123' })).toBeVisible(); + + // 重复上面的步骤,中间加上刷新页面的操作 ----------------------------------------------------------------------------------- + await page.getByLabel('action-Action.Link-Edit-update-users-table-1').click(); + await page.getByTestId('drawer-Action.Container-users-Edit record').getByLabel('action-Action.Link-Edit-').click(); + + // 刷新页面后依然正常 + await page.reload(); + + await page.getByLabel('block-item-CollectionField-').getByRole('textbox').fill('abc456'); + await page.getByLabel('action-Action-Submit-submit-').click(); + + // the first popup + await expect(page.getByRole('button', { name: 'abc456' })).toBeVisible(); + + // close the first popup + await page.locator('.ant-drawer-mask').click(); + await expect(page.getByLabel('block-item-CardItem-users-').getByRole('button', { name: 'abc456' })).toBeVisible(); }); }); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts index 949d3997a..18af36036 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts @@ -526,8 +526,8 @@ test.describe('set default value', () => { await page.getByLabel('action-Action.Link-View').click(); // 在第一级弹窗中,不应该包含 Parent popup record 变量 - await page.getByLabel('block-item-CardItem-users-').hover(); - await page.getByLabel('designer-schema-settings-CardItem-blockSettings:table-users').hover(); + await page.getByText('UsersAdd newConfigure').hover(); + await page.getByRole('button', { name: 'designer-schema-settings-' }).hover(); await page.getByRole('menuitem', { name: 'Set the data scope' }).click(); await page.getByText('Add condition', { exact: true }).click(); await page.getByLabel('variable-button').click(); @@ -664,8 +664,8 @@ test.describe('set default value', () => { await page.getByLabel('action-Action.Link-View').click(); // 在第一级弹窗中,不应该包含 Parent popup record 变量 - await page.getByLabel('block-item-CardItem-users-').hover(); - await page.getByLabel('designer-schema-settings-CardItem-blockSettings:table-users').hover(); + await page.getByText('UsersAdd newConfigure').hover(); + await page.getByRole('button', { name: 'designer-schema-settings-' }).hover(); await page.getByRole('menuitem', { name: 'Set the data scope' }).click(); await page.getByText('Add condition', { exact: true }).click(); await page.getByLabel('variable-button').click(); @@ -675,8 +675,8 @@ test.describe('set default value', () => { // 关闭数据范围设置弹窗 await page.getByRole('button', { name: 'Close', exact: true }).click(); - await page.getByLabel('action-Action.Link-View in popup').click(); - await page.getByLabel('schema-initializer-Grid-popup').hover(); + await page.getByLabel('action-Action.Link-View in').click(); + await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover(); await page.getByRole('menuitem', { name: 'form Form (Add new) right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); @@ -728,18 +728,18 @@ test.describe('set default value', () => { // 3. Table 数据选择器中使用 `Parent popup record` // 创建 Table 区块 - await page.getByLabel('schema-initializer-Grid-popup').hover(); + await page.getByLabel('schema-initializer-Grid-popup').first().hover(); await page.getByRole('menuitem', { name: 'table Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); // 显示 Nickname 字段 - await page.getByLabel('schema-initializer-TableV2-').hover(); + await page.getByLabel('schema-initializer-TableV2-').nth(1).hover(); await page.getByRole('menuitem', { name: 'Nickname' }).click(); await page.mouse.move(300, 0); // 设置数据范围(使用 `Parent popup record` 变量) - await page.getByLabel('block-item-CardItem-users-table').hover(); - await page.getByLabel('designer-schema-settings-CardItem-blockSettings:table-users').hover(); + await page.getByLabel('block-item-CardItem-users-table').nth(1).hover(); + await page.getByRole('button', { name: 'designer-schema-settings-' }).hover(); await page.getByRole('menuitem', { name: 'Set the data scope' }).click(); await page.getByText('Add condition', { exact: true }).click(); await page.getByTestId('select-filter-field').click(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts index ab9349fa1..b735dfb54 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts @@ -101,7 +101,6 @@ test.describe('actions schema settings', () => { // 点击按钮后会跳转到一个页面 await page.getByLabel('action-Action-Add new-create-').click(); - expect(page.url()).toContain('/subpages/'); // 配置出一个表单 await page.getByLabel('schema-initializer-Grid-popup').hover(); @@ -112,17 +111,16 @@ test.describe('actions schema settings', () => { await page.getByRole('menuitem', { name: 'Single select' }).click(); await page.mouse.move(300, 0); - await page.getByLabel('schema-initializer-ActionBar-').hover(); + await page.getByLabel('schema-initializer-ActionBar-createForm:configureActions-general').hover(); await page.getByRole('menuitem', { name: 'Submit' }).click(); // 创建一条数据后返回,列表中应该有这条数据 await page.getByTestId('select-single').click(); await page.getByRole('option', { name: 'option3' }).click(); + // 提交后会自动返回 await page.getByLabel('action-Action-Submit-submit-').click(); - await page.goBack(); - await page.getByLabel('schema-initializer-TableV2-').hover(); await page.getByRole('menuitem', { name: 'Single select' }).click(); await page.mouse.move(300, 0); @@ -538,7 +536,6 @@ test.describe('actions schema settings', () => { // 跳转到子页面后,其内容应该和弹窗中的内容一致 await page.getByLabel('action-Action.Link-View').click(); - expect(page.url()).toContain('/subpages'); // 详情区块 await expect( @@ -732,7 +729,7 @@ test.describe('actions schema settings', () => { // 使用变量 `Current popup record` 和 `Parent popup record` 设置默认值 await expect( page - .getByLabel('block-item-CardItem-users-form') + .getByText("UsersUse 'Current popup") .getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname') .getByRole('textbox'), ).toHaveValue('admin'); diff --git a/packages/core/client/src/modules/popup/__e2e__/deletedPopups.test.ts b/packages/core/client/src/modules/popup/__e2e__/deletedPopups.test.ts new file mode 100644 index 000000000..42d8cf484 --- /dev/null +++ b/packages/core/client/src/modules/popup/__e2e__/deletedPopups.test.ts @@ -0,0 +1,31 @@ +/** + * 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 { expect, test } from '@nocobase/test/e2e'; + +test.describe('deleted popups', () => { + test('should display error info when deleted popups', async ({ page, mockPage }) => { + const nocoPage = await mockPage().waitForInit(); + const url = await nocoPage.getUrl(); + + await page.goto( + url + + '/popups/vygn5ile3xz/filterbytk/1/popups/n24hos465bj/filterbytk/admin/sourceid/1/popups/s32h1ed5g9i/filterbytk/admin/sourceid/1', + ); + + await expect(page.getByText('Sorry, the page you visited does not exist.')).toHaveCount(3); + + // close the popups + await page.getByLabel('drawer-Action.Container-Error message-mask').click(); + await page.getByLabel('drawer-Action.Container-Error message-mask').click(); + await page.getByLabel('drawer-Action.Container-Error message-mask').click(); + + await expect(page.getByText('Sorry, the page you visited does not exist.')).toHaveCount(0); + }); +}); diff --git a/packages/core/client/src/modules/popup/__e2e__/router.test.ts b/packages/core/client/src/modules/popup/__e2e__/router.test.ts new file mode 100644 index 000000000..690b235b8 --- /dev/null +++ b/packages/core/client/src/modules/popup/__e2e__/router.test.ts @@ -0,0 +1,68 @@ +/** + * 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 { expect, test } from '@nocobase/test/e2e'; +import { shouldBackAfterClickBackButton } from './templatesOfBug'; + +test.describe('popup router', () => { + test('should work opened by URL', async ({ page, mockPage }) => { + const nocoPage = await mockPage({ + keepUid: true, + ...shouldBackAfterClickBackButton, + }).waitForInit(); + const url = await nocoPage.getUrl(); + + // 直接跳转到子页面,然后点击返回按钮,查看是否能返回到上一级页面 + await page.goto( + url + + '/popups/56tsj7l3k35/filterbytk/1/popups/bd3nizznkdw/filterbytk/member/sourceid/1/popups/1ct9qd9jlbm/filterbytk/member/sourceid/1', + ); + + // close the sub page + await page.getByLabel('back-button').click(); + + // open the sub page again then close it + await page.getByLabel('action-Action-Edit-update-roles-details-member').click(); + await page.getByLabel('back-button').click(); + + // close the drawer + await page.getByLabel('drawer-Action.Container-roles-View record-mask').click(); + await page.locator('.ant-drawer-mask').click(); + + // expect to be back to the first page + await page.getByText('Users单层子页面Configure').hover(); + await expect( + page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }), + ).toBeVisible(); + + // the same steps again by manual click ------------------------------------------------------------- + // first open the sub page + await page.getByLabel('action-Action.Link-View-view-').nth(2).click(); + await page.getByLabel('action-Action.Link-View-view-roles-table-member').click(); + await page.getByLabel('action-Action-Edit-update-').click(); + + // the same steps with above + // close the sub page + await page.getByLabel('back-button').click(); + + // open the sub page again then close it + await page.getByLabel('action-Action-Edit-update-roles-details-member').click(); + await page.getByLabel('back-button').click(); + + // close the drawer + await page.getByLabel('drawer-Action.Container-roles-View record-mask').click(); + await page.locator('.ant-drawer-mask').click(); + + // expect to be back to the first page + await page.getByText('Users单层子页面Configure').hover(); + await expect( + page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }), + ).toBeVisible(); + }); +}); diff --git a/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts b/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts new file mode 100644 index 000000000..2ecf6fde4 --- /dev/null +++ b/packages/core/client/src/modules/popup/__e2e__/subPage.test.ts @@ -0,0 +1,79 @@ +/** + * 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 { expect, test } from '@nocobase/test/e2e'; +import { shouldBackAfterClickBackButton } from './templatesOfBug'; + +test.describe('sub page', () => { + test('should back after click back button', async ({ page, mockPage }) => { + await mockPage(shouldBackAfterClickBackButton).goto(); + + // 单层子页面 ------------------------------------------------------------------------ + await page.getByLabel('action-Action.Link-View-view-').first().click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:单层子页面中的内容。'), + ).toBeVisible(); + + // 切换 tab 之后点击返回按钮 + await page.getByText('tab2').click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:单层子页面中的内容,tab2。'), + ).toBeVisible(); + await page.getByLabel('back-button').click(); + + // 从弹窗中打开子页面 ---------------------------------------------------------------------- + await page.getByLabel('action-Action.Link-View-view-').nth(1).click(); + await page.getByLabel('action-Action.Link-View-view-roles-table-admin').click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:从弹窗中打开的子页面。'), + ).toBeVisible(); + + // 切换 tab 之后点击返回按钮 + await page.getByText('tab2').click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:从弹窗中打开的子页面,tab2。'), + ).toBeVisible(); + await page.getByLabel('back-button').click(); + await page.goBack(); + + // 从嵌套弹窗中打开子页面 -------------------------------------------------------------------- + await page.getByLabel('action-Action.Link-View-view-').nth(2).click(); + await page.getByLabel('action-Action.Link-View-view-roles-table-admin').click(); + await page.getByLabel('action-Action-Edit-update-').click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:从嵌套弹窗中打开的子页面。'), + ).toBeVisible(); + await page.getByLabel('back-button').click(); + await page.getByLabel('drawer-Action.Container-roles-View record-mask').click(); + await page.getByLabel('drawer-Action.Container-users-View record-mask').click(); + + // 嵌套的子页面 ---------------------------------------------------------------------------- + await page.getByLabel('action-Action.Link-View-view-').nth(3).click(); + await page.getByLabel('action-Action.Link-View-view-roles-table-member').click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:嵌套的子页面,第二层级。'), + ).toBeVisible(); + + // 切换 tab 之后点击返回按钮 + await page.getByText('tab2').click(); + await expect( + page.getByLabel('block-item-Markdown.Void-').getByText('Markdown:嵌套的子页面,第二层级,tab2。'), + ).toBeVisible(); + await page.getByLabel('back-button').nth(1).click(); + await page.getByLabel('back-button').click(); + + expect(page.url()).not.toContain('/popups/'); + + // 确认是否回到了主页面 + await page.getByText('Users单层子页面Configure').hover(); + await expect( + page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }), + ).toBeVisible(); + }); +}); diff --git a/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts b/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts index db67702e9..df02d6b90 100644 --- a/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts +++ b/packages/core/client/src/modules/popup/__e2e__/templatesOfBug.ts @@ -2389,3 +2389,2309 @@ export const tableWithInheritWithoutAssociation = { 'x-index': 1, }, }; +export const shouldBackAfterClickBackButton = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + '97tan6tabn7': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + wbt3g94yem8: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + kcv4ztopi4o: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + qu4gkg1uo94: { + 'x-uid': '9f57o2qs72a', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + 'x-component-props': { + title: '单层子页面', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': 'u32kya63iku', + 'x-async': false, + 'x-index': 1, + }, + '9v7x23shm9a': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + '57e8dtkzg1i': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + amr6reuai6k: { + 'x-uid': '3j7cousnr32', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + properties: { + nc214fzespi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + '19t93b8tim0': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + gp1oe83i3ao: { + 'x-uid': '77yrnmca79i', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-settings': 'blockSettings:markdown', + 'x-decorator': 'CardItem', + 'x-decorator-props': { + name: 'markdown', + }, + 'x-component': 'Markdown.Void', + 'x-editable': false, + 'x-component-props': { + content: 'Markdown:单层子页面中的内容。', + }, + 'x-app-version': '1.2.11-alpha', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4rm3gbaxrut', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'unggn8pmrub', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5ojmkdqmsc9', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9q58tm27c1w', + 'x-async': false, + 'x-index': 1, + }, + jvlulxc7loq: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'tab2', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': '1.2.11-alpha', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + 'x-app-version': '1.2.11-alpha', + properties: { + '2gmgch1t5ci': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + lsyrygdbkq6: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + ub3zswl0vf6: { + 'x-uid': '077klvtotd2', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-settings': 'blockSettings:markdown', + 'x-decorator': 'CardItem', + 'x-decorator-props': { + name: 'markdown', + }, + 'x-component': 'Markdown.Void', + 'x-editable': false, + 'x-component-props': { + content: 'Markdown:单层子页面中的内容,tab2。', + }, + 'x-app-version': '1.2.11-alpha', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lsqk8vzygfd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'favqgqca7fc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'i0gqg16trgq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3pat0jg4cu8', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '6e6mgze8p96', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'b8ipp00xlvq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wkn4sh95whn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dpgt61383u6', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3ze07ij98dz', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '23art3luykd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'j19q8edcc0c', + 'x-async': false, + 'x-index': 1, + }, + q2y197mfviz: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + p7p7mrq6ns3: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + w6emv7nd3rg: { + 'x-uid': '1r2mzjhoybh', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + 'x-component-props': { + title: '从弹窗中打开子页面', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': '4ovn5e2e5ar', + 'x-async': false, + 'x-index': 1, + }, + '277u5j5eoit': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + '3t5io89no75': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + '8mxx5icnwgi': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + properties: { + t0zpnpco6gw: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + '50qw14vli5j': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + vlk2m57k8ij: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users.roles:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + association: 'users.roles', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'name', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': 'ic59hmjdpml', + 'x-async': false, + 'x-index': 1, + }, + u1mh5zpad2r: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + x1fstixrwq5: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + qz1x67f887t: { + 'x-uid': 'erooyv3qc22', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + }, + 'x-action-context': { + dataSource: 'main', + association: 'users.roles', + parentPopupRecord: { + collection: 'users', + filterByTk: 1, + }, + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + properties: { + '7ztzwrpq8qz': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.2.11-alpha', + properties: { + nw14g9cyne8: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.2.11-alpha', + properties: { + wrn8vg1jmwm: { + 'x-uid': + 'q6ofkksvfia', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Markdown:从弹窗中打开的子页面。', + }, + 'x-app-version': + '1.2.11-alpha', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '6m2whcotbwx', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '4dncnhexual', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wkfge43voz4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'sdma5uvvock', + 'x-async': false, + 'x-index': 1, + }, + t381gswc4u8: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'tab2', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': + '1.2.11-alpha', + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + 'x-app-version': + '1.2.11-alpha', + properties: { + ugeyrpa1ub8: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.2.11-alpha', + properties: { + '3m1o1ludxia': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.2.11-alpha', + properties: { + ftl0a45psk3: { + 'x-uid': + 'ks0w60u7gzv', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Markdown:从弹窗中打开的子页面,tab2。', + }, + 'x-app-version': + '1.2.11-alpha', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'xdis626c2g4', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '91oc53uvjps', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ht9wqas1i7z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fl51npqziof', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '8b7moh7ojdl', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'm3746x2hq72', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'epmmdtclegd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 't7x3572wm7v', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'r63yrogrn07', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'm5hmhkrauqn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wg5yy0o87l7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bide3fs0cwz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7f4ii238ehy', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'uz92jjv8uuw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9wz538jotyx', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'e229c8d149g', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'muydx3a8mm7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xw4q1p6ayv1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'mctdour2oga', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'iy6psp8vv7e', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'uxamrt8zems', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'u0tyssykpqo', + 'x-async': false, + 'x-index': 2, + }, + v15ya8a1m4l: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + '851olhhzgy6': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + y2zy3qyqrjn: { + 'x-uid': 'bpggay3uc8j', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + 'x-component-props': { + title: '从嵌套弹窗中打开子页面', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': 'w59rjulfkl2', + 'x-async': false, + 'x-index': 1, + }, + zsdnt9vawwi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + bnehc6k1r1b: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + qw6y1vjw3os: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + properties: { + vi3594cpy8q: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + sutzvi8f3ik: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + ecx6vh89tc5: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users.roles:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + association: 'users.roles', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'name', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': 'l0btiid3qdg', + 'x-async': false, + 'x-index': 1, + }, + vw1aen1ekoa: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + j5spqe7zhut: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + up4lxze9sld: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-action-context': { + dataSource: 'main', + association: 'users.roles', + parentPopupRecord: { + collection: 'users', + filterByTk: 1, + }, + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + properties: { + xpc333w57zk: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.2.11-alpha', + properties: { + tj9si7puaph: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.2.11-alpha', + properties: { + i3ttwm7rz98: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-acl-action': + 'users.roles:get', + 'x-decorator': + 'DetailsBlockProvider', + 'x-use-decorator-props': + 'useDetailsDecoratorProps', + 'x-decorator-props': + { + dataSource: + 'main', + association: + 'users.roles', + readPretty: + true, + action: + 'get', + }, + 'x-toolbar': + 'BlockSchemaToolbar', + 'x-settings': + 'blockSettings:details', + 'x-component': + 'CardItem', + 'x-is-current': + true, + 'x-app-version': + '1.2.11-alpha', + properties: + { + i20wik3jvnr: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Details', + 'x-read-pretty': + true, + 'x-use-component-props': + 'useDetailsProps', + 'x-app-version': + '1.2.11-alpha', + properties: + { + gf414um0rqy: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-initializer': + 'details:configureActions', + 'x-component': + 'ActionBar', + 'x-component-props': + { + style: + { + marginBottom: 24, + }, + }, + 'x-app-version': + '1.2.11-alpha', + properties: + { + j8vo5nfc4nm: + { + 'x-uid': + '1ct9qd9jlbm', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + title: + '{{ t("Edit") }}', + 'x-action': + 'update', + 'x-toolbar': + 'ActionSchemaToolbar', + 'x-settings': + 'actionSettings:edit', + 'x-component': + 'Action', + 'x-component-props': + { + openMode: + 'page', + icon: 'EditOutlined', + type: 'primary', + }, + 'x-action-context': + { + dataSource: + 'main', + association: + 'users.roles', + parentPopupRecord: + { + collection: + 'roles', + filterByTk: + 'admin', + }, + }, + 'x-decorator': + 'ACLActionProvider', + 'x-app-version': + '1.2.11-alpha', + properties: + { + drawer: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + title: + '{{ t("Edit record") }}', + 'x-component': + 'Action.Container', + 'x-component-props': + { + className: + 'nb-action-popup', + }, + 'x-app-version': + '1.2.11-alpha', + properties: + { + tabs: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Tabs', + 'x-component-props': + {}, + 'x-initializer': + 'popup:addTab', + 'x-app-version': + '1.2.11-alpha', + properties: + { + tab1: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + title: + '{{t("Edit")}}', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': + {}, + 'x-app-version': + '1.2.11-alpha', + properties: + { + grid: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid', + 'x-initializer': + 'popup:common:addBlock', + 'x-app-version': + '1.2.11-alpha', + properties: + { + zkslw04ebta: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.2.11-alpha', + properties: + { + opndt1e9gld: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.2.11-alpha', + properties: + { + hmc0k54aff7: + { + 'x-uid': + '4pb507f2ogv', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Markdown:从嵌套弹窗中打开的子页面。', + }, + 'x-app-version': + '1.2.11-alpha', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '9pyvixeo4p8', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'y3s06g3mmnn', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'so5nyswl2tn', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '0a6z4stdtvv', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '7l9zhdzmvrl', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '2x6fwtfvkks', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '6olkx9unnri', + 'x-async': + false, + 'x-index': 1, + }, + grid: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid', + 'x-initializer': + 'details:configureFields', + 'x-app-version': + '1.2.11-alpha', + 'x-uid': + 'gu80n1txnro', + 'x-async': + false, + 'x-index': 2, + }, + }, + 'x-uid': + '1by5v8iv34w', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'cztt3y1bgor', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'cwjnmpashsw', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'xd3jxvuh0uu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'yuh9ln1beb7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'skn2rg6i69w', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'd86n6uwh3bp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'v1qjjrpax1d', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bd3nizznkdw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 's4vogdgtvee', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ftldvta11u5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5sd13kmn5by', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'kl6jgkap0hm', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3ee607ult3r', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'woxi5swq3eg', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1q2x6q2ppbp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9wo00ltfxrx', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vrmltv7ft3h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'cpxmv3sg94v', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '56tsj7l3k35', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'm6aspnitoun', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1i77bx9kgzq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '0cwavsh3a2p', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'jo81qbmu5vz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '2x8h80y9qar', + 'x-async': false, + 'x-index': 3, + }, + yp0g3u675ag: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + x2w02rf3w75: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + jt9x9694vkt: { + 'x-uid': '2i5820tc8a7', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + 'x-component-props': { + title: '嵌套的子页面', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': 'bmoyp6ssad8', + 'x-async': false, + 'x-index': 1, + }, + tnyim2afz64: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + c7tvdhrf7bc: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + yaxmxps0sco: { + 'x-uid': '0odbntoklq4', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'users', + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + properties: { + ayd0gasuunq: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.2.11-alpha', + properties: { + '1qtokxl5tqi': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.2.11-alpha', + properties: { + '7a9fjrtmdig': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users.roles:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + association: 'users.roles', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'name', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.2.11-alpha', + 'x-uid': 'sow53nemamo', + 'x-async': false, + 'x-index': 1, + }, + '4svbyier3po': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.2.11-alpha', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + '4ee6xcmbza3': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.2.11-alpha', + properties: { + '3qj941c0o77': { + 'x-uid': '7bwrckgb2qz', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-action': 'view', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:view', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + }, + 'x-action-context': { + dataSource: 'main', + association: 'users.roles', + parentPopupRecord: { + collection: 'users', + filterByTk: 1, + }, + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + properties: { + '75bgz4587a8': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.2.11-alpha', + properties: { + z390g7i8lvq: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.2.11-alpha', + properties: { + ca9pyg76qga: { + 'x-uid': + 'id81yz2cpip', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Markdown:嵌套的子页面,第二层级。', + }, + 'x-app-version': + '1.2.11-alpha', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'jjooza0r1bc', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '1rn0yytm14k', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'n1fnwuisud4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ytffyqb66bz', + 'x-async': false, + 'x-index': 1, + }, + zlrkv0rum6m: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: 'tab2', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + 'x-app-version': + '1.2.11-alpha', + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + 'x-app-version': + '1.2.11-alpha', + properties: { + '9lof63mhvmr': { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.2.11-alpha', + properties: { + ynn98kqolac: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.2.11-alpha', + properties: { + ym6o0oia28x: { + 'x-uid': + '4vz5669r4s7', + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-settings': + 'blockSettings:markdown', + 'x-decorator': + 'CardItem', + 'x-decorator-props': + { + name: 'markdown', + }, + 'x-component': + 'Markdown.Void', + 'x-editable': + false, + 'x-component-props': + { + content: + 'Markdown:嵌套的子页面,第二层级,tab2。', + }, + 'x-app-version': + '1.2.11-alpha', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'b64fox1t5dq', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'zkqdci9nwo5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p6k33ab0jkk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xiwqovztp2l', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'n25bezli26e', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p76ypirrotc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'qjcsvvoqgio', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'o991l8oye7a', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'j6ucd06ivu2', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '3b8k5fi5j2k', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'k0if0x4hzl1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '72zx0xrvyh4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7yyn3mkcn2z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fht4tt8jvvv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ik49cgssro5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8z3b7fdh3ye', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fnggqaqjyi4', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'htsvweltqpq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'j8oje6djfsf', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '94hcvhpfi9y', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'r863ebmphg7', + 'x-async': false, + 'x-index': 4, + }, + }, + 'x-uid': '6udxejou23t', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'euvn3lv0jkz', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/nocobase-buildin-plugin/index.tsx b/packages/core/client/src/nocobase-buildin-plugin/index.tsx index e975cd987..e846be160 100644 --- a/packages/core/client/src/nocobase-buildin-plugin/index.tsx +++ b/packages/core/client/src/nocobase-buildin-plugin/index.tsx @@ -27,7 +27,6 @@ import { AdminLayoutPlugin, RouteSchemaComponent } from '../route-switch'; import { AntdSchemaComponentPlugin, PageTabs, SchemaComponentPlugin } from '../schema-component'; import { ErrorFallback } from '../schema-component/antd/error-fallback'; import { PagePopups } from '../schema-component/antd/page/PagePopups'; -import { SubPage } from '../schema-component/antd/page/SubPages'; import { AssociationFilterPlugin, SchemaInitializerPlugin } from '../schema-initializer'; import { SchemaSettingsPlugin } from '../schema-settings'; import { BlockTemplateDetails, BlockTemplatePage } from '../schema-templates'; @@ -316,10 +315,6 @@ export class NocoBaseBuildInPlugin extends Plugin { path: '/admin/:name/tabs/:tabUid/popups/*', Component: PagePopups, }); - this.router.add('admin.subPage', { - path: '/admin/subpages/*', - Component: SubPage, - }); } addComponents() { diff --git a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx index 203cc3e7d..2b5c89c29 100644 --- a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx +++ b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx @@ -92,7 +92,7 @@ const MenuEditor = (props) => { const ctx = useACLRoleContext(); const [current, setCurrent] = useState(null); - const onSelect = useCallback(({ item }) => { + const onSelect = useCallback(({ item }: { item; key; keyPath; domEvent }) => { const schema = item.props.schema; setTitle(schema.title); setCurrent(schema); @@ -323,6 +323,33 @@ export const AdminDynamicPage = () => { return ; }; +const layoutContentClass = css` + display: flex; + flex-direction: column; + position: relative; + overflow-y: hidden; + height: 100vh; + > div { + position: relative; + } + .ant-layout-footer { + position: absolute; + bottom: 0; + text-align: center; + width: 100%; + z-index: 0; + padding: 0px 50px; + } +`; + +const layoutContentHeaderClass = css` + flex-shrink: 0; + height: var(--nb-header-height); + line-height: var(--nb-header-height); + background: transparent; + pointer-events: none; +`; + export const InternalAdminLayout = () => { const result = useSystemSettings(); const { token } = useToken(); @@ -447,36 +474,9 @@ export const InternalAdminLayout = () => { - div { - position: relative; - } - .ant-layout-footer { - position: absolute; - bottom: 0; - text-align: center; - width: 100%; - z-index: 0; - padding: 0px 50px; - } - `} - > -
+ {/* Use the "nb-subpages-slot-without-header-and-side" class name to locate the position of the subpages */} + +
{/* {service.contentLoading ? render() : } */}
diff --git a/packages/core/client/src/schema-component/antd/action/Action.Drawer.style.ts b/packages/core/client/src/schema-component/antd/action/Action.Drawer.style.ts index 037b8b933..1974d0dd9 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Drawer.style.ts +++ b/packages/core/client/src/schema-component/antd/action/Action.Drawer.style.ts @@ -64,5 +64,5 @@ export const useStyles = genStyleHook('nb-action-drawer', (token) => { // margin: `-${token.paddingPopupVertical}px -${token.paddingPopupHorizontal}px`, // }, }, - }; + } as any; }); diff --git a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx index c8131ed50..3a560d2f1 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx @@ -10,9 +10,10 @@ import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { Drawer } from 'antd'; import classNames from 'classnames'; -import React from 'react'; +import React, { useMemo } from 'react'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { ErrorFallback } from '../error-fallback'; +import { useCurrentPopupContext } from '../page/PagePopups'; import { useStyles } from './Action.Drawer.style'; import { useActionContext } from './hooks'; import { useSetAriaLabelForDrawer } from './hooks/useSetAriaLabelForDrawer'; @@ -45,6 +46,14 @@ export const InternalActionDrawer: React.FC = observer( } return buf; }); + const { hidden } = useCurrentPopupContext(); + const rootStyle: React.CSSProperties = useMemo(() => { + return { + ...drawerProps?.style, + ...others?.style, + display: hidden ? 'none' : 'block', + }; + }, [hidden, drawerProps?.style, others?.style]); if (process.env.__E2E__) { useSetAriaLabelForDrawer(visible); @@ -56,10 +65,7 @@ export const InternalActionDrawer: React.FC = observer( title={field.title} {...others} {...drawerProps} - rootStyle={{ - ...drawerProps?.style, - ...others?.style, - }} + rootStyle={rootStyle} destroyOnClose open={visible} onClose={() => setVisible(false, true)} diff --git a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx index 1800471a2..6e798d576 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx @@ -11,10 +11,11 @@ import { css } from '@emotion/css'; import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { Modal, ModalProps } from 'antd'; import classNames from 'classnames'; -import React from 'react'; +import React, { useMemo } from 'react'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { useToken } from '../../../style'; import { ErrorFallback } from '../error-fallback'; +import { useCurrentPopupContext } from '../page/PagePopups'; import { useActionContext } from './hooks'; import { useSetAriaLabelForModal } from './hooks/useSetAriaLabelForModal'; import { ActionDrawerProps, ComposedActionDrawer, OpenSize } from './types'; @@ -33,6 +34,7 @@ const openSizeWidthMap = new Map([ ['middle', '60%'], ['large', '80%'], ]); + export const InternalActionModal: React.FC> = observer( (props) => { const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props; @@ -47,6 +49,18 @@ export const InternalActionModal: React.FC> = obse } return buf; }); + const { hidden } = useCurrentPopupContext(); + const styles: any = useMemo(() => { + return { + mask: { + display: hidden ? 'none' : 'block', + }, + content: { + display: hidden ? 'none' : 'block', + }, + }; + }, [hidden]); + const showFooter = !!footerSchema; if (process.env.__E2E__) { useSetAriaLabelForModal(visible); @@ -58,6 +72,7 @@ export const InternalActionModal: React.FC> = obse title={field.title} {...(others as ModalProps)} {...modalProps} + styles={styles} style={{ ...modalProps?.style, ...others?.style, diff --git a/packages/core/client/src/schema-component/antd/page/SubPages.style.ts b/packages/core/client/src/schema-component/antd/action/Action.Page.style.ts similarity index 70% rename from packages/core/client/src/schema-component/antd/page/SubPages.style.ts rename to packages/core/client/src/schema-component/antd/action/Action.Page.style.ts index f531ac62c..ff3b007be 100644 --- a/packages/core/client/src/schema-component/antd/page/SubPages.style.ts +++ b/packages/core/client/src/schema-component/antd/action/Action.Page.style.ts @@ -9,9 +9,17 @@ import { createStyles } from 'antd-style'; -export const useSubPagesStyle = createStyles(({ css, token }: any) => { +export const useActionPageStyle = createStyles(({ css, token }: any) => { return { container: css` + position: absolute !important; + top: var(--nb-header-height); + left: 0; + right: 0; + bottom: 0; + background-color: ${token.colorBgLayout}; + overflow: auto; + .ant-tabs-nav { background: ${token.colorBgContainer}; padding: 0 ${token.paddingPageVertical}px; diff --git a/packages/core/client/src/schema-component/antd/action/Action.Page.tsx b/packages/core/client/src/schema-component/antd/action/Action.Page.tsx index 9b78dbfd2..4565e6058 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Page.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Page.tsx @@ -7,81 +7,49 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { css } from '@emotion/css'; -import { observer, RecursionField, SchemaExpressionScopeContext, useField, useFieldSchema } from '@formily/react'; -import React, { useContext } from 'react'; +import { RecursionField, observer, useFieldSchema } from '@formily/react'; +import React, { useMemo } from 'react'; import { createPortal } from 'react-dom'; import { useActionContext } from '.'; +import { useCurrentPopupContext } from '../page/PagePopups'; +import { useActionPageStyle } from './Action.Page.style'; +import { usePopupOrSubpagesContainerDOM } from './hooks/usePopupSlotDOM'; import { ComposedActionDrawer } from './types'; -const useScope = (key: string) => { - const scope = useContext(SchemaExpressionScopeContext); - return scope[key]; -}; - export const ActionPage: ComposedActionDrawer = observer( - (props: any) => { - const { footerNodeName = 'Action.Page.Footer', ...others } = props; - const { containerRefKey, visible, setVisible } = useActionContext(); - const containerRef = useScope(containerRefKey); - const schema = useFieldSchema(); - const field = useField(); - const footerSchema = schema.reduceProperties((buf, s) => { - if (s['x-component'] === footerNodeName) { - return s; - } - return buf; - }); - return ( - <> - {containerRef?.current && - visible && - createPortal( -
- { - return s['x-component'] !== footerNodeName; - }} - /> - {footerSchema && ( -
- { - return s['x-component'] === footerNodeName; - }} - /> -
- )} -
, - containerRef?.current, - )} - + () => { + const filedSchema = useFieldSchema(); + const ctx = useActionContext(); + const { getContainerDOM } = usePopupOrSubpagesContainerDOM(); + const { styles } = useActionPageStyle(); + const { currentLevel } = useCurrentPopupContext(); + + const style = useMemo(() => { + return { + // 20 is the z-index value of the main page + zIndex: 20 + currentLevel, + }; + }, [currentLevel]); + + if (!ctx.visible) { + return null; + } + + const actionPageNode = ( +
+ +
); + + return createPortal(actionPageNode, getContainerDOM()); }, { displayName: 'ActionPage' }, ); ActionPage.Footer = observer( () => { - const field = useField(); - const schema = useFieldSchema(); - return ; + // TODO: Implement in the future if needed + return null; }, { displayName: 'ActionPage.Footer' }, ); diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 2b6b19b08..309b9dd4c 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -31,7 +31,6 @@ import { useProps } from '../../hooks/useProps'; import { PopupVisibleProvider } from '../page/PagePopups'; import { usePagePopup } from '../page/pagePopupUtils'; import { usePopupSettings } from '../page/PopupSettingsProvider'; -import { useNavigateTOSubPage } from '../page/SubPages'; import ActionContainer from './Action.Container'; import { ActionDesigner } from './Action.Designer'; import { ActionDrawer } from './Action.Drawer'; @@ -306,7 +305,6 @@ function RenderButton({ modal, }) { const { t } = useTranslation(); - const { navigateToSubPage } = useNavigateTOSubPage(); const { isPopupVisibleControlledByURL } = usePopupSettings(); const { openPopup } = usePagePopup(); @@ -320,20 +318,18 @@ function RenderButton({ if (!disabled && aclCtx) { const onOk = () => { - if (openMode === 'page') { - return navigateToSubPage(); - } if (onClick) { onClick(e, () => { if (refreshDataBlockRequest !== false) { service?.refresh?.(); } }); - } else if (isBulkEditAction(fieldSchema) || !isPopupVisibleControlledByURL) { + } else if (isBulkEditAction(fieldSchema) || !isPopupVisibleControlledByURL()) { setVisible(true); run?.(); } else { if ( + // Currently, only buttons of these types can control the visibility of popups through URLs. ['view', 'update', 'create', 'customize:popup'].includes(fieldSchema['x-action']) && fieldSchema['x-uid'] ) { diff --git a/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx b/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx index 937a92a93..9fb0d4f6e 100644 --- a/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx +++ b/packages/core/client/src/schema-component/antd/action/__tests__/action.test.tsx @@ -11,7 +11,6 @@ import { fireEvent, render, screen, userEvent, waitFor } from '@nocobase/test/cl import React from 'react'; import App1 from '../demos/demo1'; import App2 from '../demos/demo2'; -import App3 from '../demos/demo3'; import App4 from '../demos/demo4'; describe('Action', () => { @@ -55,45 +54,6 @@ describe('Action', () => { expect(document.querySelector('.ant-drawer')).not.toBeInTheDocument(); }); }); - - it('openMode', async () => { - const { getByText } = render(); - - expect(document.querySelector('.ant-drawer')).not.toBeInTheDocument(); - expect(document.querySelector('.ant-modal')).not.toBeInTheDocument(); - expect(document.querySelector('.nb-action-page')).not.toBeInTheDocument(); - - // drawer - await waitFor(async () => { - await userEvent.click(getByText('Drawer')); - await userEvent.click(getByText('Open')); - expect(document.querySelector('.ant-drawer')).toBeInTheDocument(); - expect(document.querySelector('.ant-modal')).not.toBeInTheDocument(); - expect(document.querySelector('.nb-action-page')).not.toBeInTheDocument(); - }); - - // modal - await waitFor(async () => { - await userEvent.click(getByText('Close')); - await userEvent.click(getByText('Modal')); - await userEvent.click(getByText('Open')); - expect(document.querySelector('.ant-drawer')).not.toBeInTheDocument(); - expect(document.querySelector('.ant-modal')).toBeInTheDocument(); - expect(document.querySelector('.nb-action-page')).not.toBeInTheDocument(); - }); - await waitFor(async () => { - // page - await userEvent.click(getByText('Page')); - await userEvent.click(getByText('Open')); - expect(document.querySelector('.ant-drawer')).not.toBeInTheDocument(); - expect(document.querySelector('.ant-modal')).not.toBeInTheDocument(); - expect(document.querySelector('.nb-action-page')).toBeInTheDocument(); - }); - await userEvent.click(getByText('Close')); - - // TODO: 点击关闭按钮时应该消失 - // expect(document.querySelector('.nb-action-page')).not.toBeInTheDocument(); - }); }); describe('Action.Drawer without Action', () => { diff --git a/packages/core/client/src/schema-component/antd/action/context.tsx b/packages/core/client/src/schema-component/antd/action/context.tsx index 895701f86..e7eecf9c6 100644 --- a/packages/core/client/src/schema-component/antd/action/context.tsx +++ b/packages/core/client/src/schema-component/antd/action/context.tsx @@ -7,8 +7,11 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React, { createContext, useEffect, useRef, useState } from 'react'; +import { useFieldSchema } from '@formily/react'; +import React, { createContext, useEffect, useState } from 'react'; import { useDataBlockRequest } from '../../../data-source'; +import { useCurrentPopupContext } from '../page/PagePopups'; +import { getBlockService, storeBlockService } from '../page/pagePopupUtils'; import { ActionContextProps } from './types'; export const ActionContext = createContext({}); @@ -17,24 +20,19 @@ ActionContext.displayName = 'ActionContext'; export const ActionContextProvider: React.FC = (props) => { const [submitted, setSubmitted] = useState(false); //是否有提交记录 const { visible } = { ...props, ...props.value } || {}; - const isFirstRender = useRef(true); // 使用ref跟踪是否为首次渲染 - const service = useDataBlockRequest(); const { setSubmitted: setParentSubmitted } = { ...props, ...props.value }; + const service = useBlockServiceInActionButton(); + useEffect(() => { - if (visible !== undefined) { - if (isFirstRender.current) { - isFirstRender.current = false; - } else { - if (visible === false && submitted && service) { - service.refresh(); - setParentSubmitted?.(true); //传递给上一层 - } - } + if (visible === false && submitted && service) { + service.refresh(); + setParentSubmitted?.(true); //传递给上一层 } + return () => { setSubmitted(false); }; - }, [visible]); + }, [visible, service]); return ( @@ -42,3 +40,25 @@ export const ActionContextProvider: React.FC ); }; + +const useBlockServiceInActionButton = () => { + const { params } = useCurrentPopupContext(); + const popupUidWithoutOpened = useFieldSchema()?.['x-uid']; + const service = useDataBlockRequest(); + const currentPopupUid = params?.popupuid; + + // By using caching, we solve the problem of not being able to obtain the correct service when closing a popup through a URL + useEffect(() => { + // This case refers to when the current button is rendered on a page or in a popup + if (popupUidWithoutOpened && currentPopupUid !== popupUidWithoutOpened) { + storeBlockService(popupUidWithoutOpened, { service }); + } + }, [popupUidWithoutOpened, service, currentPopupUid]); + + // This case refers to when the current button is closed as a popup (the button's uid is the same as the popup's uid) + if (currentPopupUid === popupUidWithoutOpened) { + return getBlockService(currentPopupUid)?.service || service; + } + + return service; +}; diff --git a/packages/core/client/src/schema-component/antd/action/hooks.ts b/packages/core/client/src/schema-component/antd/action/hooks.ts index 4992f3a33..e337b38f2 100644 --- a/packages/core/client/src/schema-component/antd/action/hooks.ts +++ b/packages/core/client/src/schema-component/antd/action/hooks.ts @@ -22,23 +22,21 @@ export const useActionContext = () => { return { ...ctx, setVisible(visible: boolean, confirm = false) { - if (ctx?.openMode !== 'page') { - if (!visible) { - if (confirm && ctx.formValueChanged) { - modal.confirm({ - title: t('Unsaved changes'), - content: t("Are you sure you don't want to save?"), - async onOk() { - ctx.setFormValueChanged(false); - ctx.setVisible?.(false); - }, - }); - } else { - ctx?.setVisible?.(false); - } + if (!visible) { + if (confirm && ctx.formValueChanged) { + modal.confirm({ + title: t('Unsaved changes'), + content: t("Are you sure you don't want to save?"), + async onOk() { + ctx.setFormValueChanged(false); + ctx.setVisible?.(false); + }, + }); } else { - ctx?.setVisible?.(visible); + ctx?.setVisible?.(false); } + } else { + ctx?.setVisible?.(visible); } }, }; diff --git a/packages/core/client/src/schema-component/antd/action/hooks/usePopupSlotDOM.ts b/packages/core/client/src/schema-component/antd/action/hooks/usePopupSlotDOM.ts new file mode 100644 index 000000000..f5b0cac22 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/hooks/usePopupSlotDOM.ts @@ -0,0 +1,24 @@ +/** + * 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 { useCallback, useMemo } from 'react'; + +/** + * Used to get the DOM container for rendering popups or subpages. + * @returns + */ +export const usePopupOrSubpagesContainerDOM = () => { + const containerDOM: HTMLElement = useMemo( + () => document.querySelector('.nb-subpages-slot-without-header-and-side'), + [], + ); + const getContainerDOM = useCallback(() => containerDOM, [containerDOM]); + + return { getContainerDOM }; +}; diff --git a/packages/core/client/src/schema-component/antd/menu/Menu.tsx b/packages/core/client/src/schema-component/antd/menu/Menu.tsx index 29b1f08bc..45bb6f243 100644 --- a/packages/core/client/src/schema-component/antd/menu/Menu.tsx +++ b/packages/core/client/src/schema-component/antd/menu/Menu.tsx @@ -230,7 +230,7 @@ const HeaderMenu = ({ }, [children, designable]); const handleSelect = useCallback( - (info: any) => { + (info: { item; key; keyPath; domEvent }) => { const s = schema.properties?.[info.key]; if (!s) { @@ -274,7 +274,7 @@ const HeaderMenu = ({ { + const { params } = useCurrentPopupContext(); + const { closePopup } = usePagePopup(); + const { token } = useToken(); + // tab item gutter, this is fixed value in antd + const horizontalItemGutter = 32; + + const resetStyle = useMemo(() => { + return { + width: 'auto', + height: 'auto', + lineHeight: 1, + padding: token.paddingXS, + marginRight: horizontalItemGutter - token.paddingXS, + }; + }, [token.paddingXS]); + + const handleClick = useCallback(() => { + closePopup(params.popupuid); + }, [params.popupuid]); + + return ( +