diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/EmbedLayout.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/EmbedLayout.tsx
new file mode 100644
index 000000000..3b0502d72
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/EmbedLayout.tsx
@@ -0,0 +1,30 @@
+import { css } from '@nocobase/client';
+import { Layout } from 'antd';
+import React from 'react';
+import { Outlet } from 'react-router-dom';
+
+export const EmbedLayout = () => (
+
+ div {
+ position: relative;
+ }
+ .ant-layout-footer {
+ position: absolute;
+ bottom: 0;
+ text-align: center;
+ width: 100%;
+ z-index: 0;
+ padding: 0px 50px;
+ }
+ `}
+ >
+
+
+
+);
diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/EmbedPage.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/EmbedPage.tsx
new file mode 100644
index 000000000..38ab74da9
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/EmbedPage.tsx
@@ -0,0 +1,33 @@
+import { AdminProvider } from '@nocobase/client';
+import React, { useEffect } from 'react';
+import { useLocation, useMatch, useNavigate } from 'react-router-dom';
+import { NotAuthorityResult } from './NotAuthorityResult';
+import { EmbedLayout } from './EmbedLayout';
+import { useEmbedToken } from './useEmbedToken';
+
+export const EmbedPage = () => {
+ const embedToken = useEmbedToken();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const match = useMatch('/embed/:name');
+ // FIXME
+ useEffect(
+ () => () => {
+ setTimeout(() => {
+ match &&
+ setTimeout(() => {
+ window.location.pathname !== location.pathname && navigate('/embed/not-authorized');
+ });
+ });
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [],
+ );
+ return !embedToken || location.pathname === '/embed/not-authorized' ? (
+
+ ) : (
+
+
+
+ );
+};
diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/EmbedSchemaComponent.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/EmbedSchemaComponent.tsx
new file mode 100644
index 000000000..3ba815cf0
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/EmbedSchemaComponent.tsx
@@ -0,0 +1,8 @@
+import { RemoteSchemaComponent } from '@nocobase/client';
+import React from 'react';
+import { useParams } from 'react-router-dom';
+
+export function EmbedSchemaComponent() {
+ const params = useParams();
+ return ;
+}
diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/NotAuthorityResult.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/NotAuthorityResult.tsx
new file mode 100644
index 000000000..d9af4f2a8
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/NotAuthorityResult.tsx
@@ -0,0 +1,6 @@
+import { Result } from 'antd';
+import React from 'react';
+
+export function NotAuthorityResult() {
+ return ;
+}
diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/index.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/index.tsx
new file mode 100644
index 000000000..32eade2f9
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/index.tsx
@@ -0,0 +1,18 @@
+import { Plugin } from '@nocobase/client';
+import { usePageSettingsProps } from './usePageSettingsProps';
+import { EmbedSchemaComponent } from './EmbedSchemaComponent';
+import { EmbedPage } from './EmbedPage';
+
+const keyEmbed = 'embed';
+const pathEmbed = `/${keyEmbed}`;
+export class EmbedPlugin extends Plugin {
+ async load() {
+ this.router.add(keyEmbed, { path: pathEmbed, Component: EmbedPage });
+ this.router.add(`${keyEmbed}.page`, { path: `${pathEmbed}/:name`, Component: EmbedSchemaComponent });
+ this.router.add(`${keyEmbed}.notAuthorized`, { path: `${pathEmbed}/not-authorized`, element: null });
+ this.schemaSettingsManager.addItem('PageSettings', keyEmbed, {
+ type: 'item',
+ useComponentProps: usePageSettingsProps,
+ });
+ }
+}
diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/useEmbedToken.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/useEmbedToken.tsx
new file mode 100644
index 000000000..aedcb6751
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/useEmbedToken.tsx
@@ -0,0 +1,8 @@
+import { useAPIClient } from '@nocobase/client';
+
+export function useEmbedToken() {
+ const url = new URL(window.location.href);
+ const api = useAPIClient();
+ const token = url.searchParams.get('token');
+ return token && api.auth.setToken(token), api.auth.getToken();
+}
diff --git a/packages/plugins/@hera/plugin-core/src/client/embed/usePageSettingsProps.tsx b/packages/plugins/@hera/plugin-core/src/client/embed/usePageSettingsProps.tsx
new file mode 100644
index 000000000..90baa06f5
--- /dev/null
+++ b/packages/plugins/@hera/plugin-core/src/client/embed/usePageSettingsProps.tsx
@@ -0,0 +1,30 @@
+import { useFieldSchema } from '@nocobase/schema';
+import { App } from 'antd';
+import { useMatch } from 'react-router-dom';
+import { useTranslation } from '../locale';
+
+export function usePageSettingsProps() {
+ const isAdmin = useMatch('/admin/:name');
+ const isEmbed = useMatch('/embed/:name');
+ const currentRoute = isAdmin || isEmbed;
+ const fieldSchema = useFieldSchema();
+ const { message, notification } = App.useApp();
+ const { t } = useTranslation();
+ return {
+ title: t('Copy embedded link'),
+ onClick: () => {
+ const link = window.location.href
+ .replace('/admin', '/embed')
+ .replace(currentRoute.params.name, fieldSchema['x-uid'])
+ .replace(window.location.search || '', '');
+ navigator.clipboard
+ .writeText(link)
+ .then(() => {
+ message.success(t('Copy successful'));
+ })
+ .catch((error) => {
+ notification.error({ message: t('Copy Failed'), description: error.message, placement: 'topRight' });
+ });
+ },
+ };
+}
diff --git a/packages/plugins/@hera/plugin-core/src/client/index.tsx b/packages/plugins/@hera/plugin-core/src/client/index.tsx
index c7c91b2fb..646a60c1e 100644
--- a/packages/plugins/@hera/plugin-core/src/client/index.tsx
+++ b/packages/plugins/@hera/plugin-core/src/client/index.tsx
@@ -74,6 +74,7 @@ import {
useCustomPresets1,
} from './schema-settings/SchemaSettingsDatePresets';
import { SchemaSettingsSubmitDataType } from './schema-settings/SchemaSettingsSubmitDataType';
+import { EmbedPlugin } from './embed';
export { usePDFViewerRef } from './schema-initializer';
export * from './components/custom-components/custom-components';
@@ -82,6 +83,7 @@ export class PluginCoreClient extends Plugin {
async afterAdd() {
await this.app.pm.add(GroupBlockPlugin);
+ await this.app.pm.add(EmbedPlugin);
}
async registerSettings() {