mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
feat(Link): add 'Open in new window' option (#4898)
* refactor: rename 'useURLAndParamsSchema' to 'useURLAndHTMLSchema' and improve it * feat: add 'Open in new window' option * test: add e2e test
This commit is contained in:
parent
a39df74a49
commit
7d660f5a0f
@ -7,7 +7,13 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { appendQueryStringToUrl, parseVariablesAndChangeParamsToQueryString, reduceValueSize } from '../../hooks/index';
|
||||
import {
|
||||
appendQueryStringToUrl,
|
||||
completeURL,
|
||||
navigateWithinSelf,
|
||||
parseVariablesAndChangeParamsToQueryString,
|
||||
reduceValueSize,
|
||||
} from '../../hooks/index';
|
||||
|
||||
describe('parseVariablesAndChangeParamsToQueryString', () => {
|
||||
it('should parse variables and change params to query string', async () => {
|
||||
@ -150,3 +156,82 @@ describe('appendQueryStringToUrl', () => {
|
||||
expect(result).toBe('https://example.com?existingParam=value¶m1=value1¶m2=value2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('completeURL', () => {
|
||||
it('should complete a relative URL with the origin', () => {
|
||||
const origin = 'https://example.com';
|
||||
const url = '/path/to/resource';
|
||||
|
||||
const result = completeURL(url, origin);
|
||||
|
||||
expect(result).toBe('https://example.com/path/to/resource');
|
||||
});
|
||||
|
||||
it('should return the URL as is if it is already complete', () => {
|
||||
const origin = 'https://example.com';
|
||||
const url = 'https://example.com/path/to/resource';
|
||||
|
||||
const result = completeURL(url, origin);
|
||||
|
||||
expect(result).toBe('https://example.com/path/to/resource');
|
||||
});
|
||||
|
||||
it('should return the URL as is if it is already complete with a different origin', () => {
|
||||
const origin = 'https://example.com';
|
||||
const url = 'https://another.com/path/to/resource';
|
||||
|
||||
const result = completeURL(url, origin);
|
||||
|
||||
expect(result).toBe('https://another.com/path/to/resource');
|
||||
});
|
||||
|
||||
it('should return an empty string if the URL is falsy', () => {
|
||||
const origin = 'https://example.com';
|
||||
const url = '';
|
||||
|
||||
const result = completeURL(url, origin);
|
||||
|
||||
expect(result).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigateWithinSelf', () => {
|
||||
it('should navigate within the same window if the link is a relative URL', () => {
|
||||
const origin = 'https://example.com';
|
||||
const link = '/path/to/resource';
|
||||
const navigate = vi.fn();
|
||||
|
||||
navigateWithinSelf(link, navigate, origin);
|
||||
|
||||
expect(navigate).toHaveBeenCalledWith(link);
|
||||
});
|
||||
|
||||
it('should navigate within the same window if the link starts with the origin', () => {
|
||||
const origin = 'https://example.com';
|
||||
const link = 'https://example.com/path/to/resource';
|
||||
const navigate = vi.fn();
|
||||
|
||||
navigateWithinSelf(link, navigate, origin);
|
||||
|
||||
expect(navigate).toHaveBeenCalledWith('/path/to/resource');
|
||||
});
|
||||
|
||||
it('should open the link in the same window if it is an external URL', () => {
|
||||
const origin = 'https://example.com';
|
||||
const link = 'https://other.com/path/to/resource';
|
||||
window.open = vi.fn();
|
||||
|
||||
navigateWithinSelf(link, () => {}, origin);
|
||||
|
||||
expect(window.open).toHaveBeenCalledWith(link, '_self');
|
||||
});
|
||||
|
||||
it('should log an error if the link is not a string', () => {
|
||||
const link = null;
|
||||
console.error = vi.fn();
|
||||
|
||||
navigateWithinSelf(link, () => {});
|
||||
|
||||
expect(console.error).toHaveBeenCalledWith('link should be a string');
|
||||
});
|
||||
});
|
||||
|
@ -19,7 +19,7 @@ import omit from 'lodash/omit';
|
||||
import qs from 'qs';
|
||||
import { ChangeEvent, useCallback, useContext, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { NavigateFunction, useNavigate } from 'react-router-dom';
|
||||
import { useReactToPrint } from 'react-to-print';
|
||||
import {
|
||||
AssociationFilter,
|
||||
@ -1560,6 +1560,7 @@ export function useLinkActionProps() {
|
||||
const { t } = useTranslation();
|
||||
const url = fieldSchema?.['x-component-props']?.['url'];
|
||||
const searchParams = fieldSchema?.['x-component-props']?.['params'] || [];
|
||||
const openInNewWindow = fieldSchema?.['x-component-props']?.['openInNewWindow'];
|
||||
const { parseURLAndParams } = useParseURLAndParams();
|
||||
|
||||
return {
|
||||
@ -1572,11 +1573,13 @@ export function useLinkActionProps() {
|
||||
const link = await parseURLAndParams(url, searchParams);
|
||||
|
||||
if (link) {
|
||||
if (isURL(link)) {
|
||||
window.open(link, '_blank');
|
||||
if (openInNewWindow) {
|
||||
window.open(completeURL(link), '_blank');
|
||||
} else {
|
||||
navigate(link);
|
||||
navigateWithinSelf(link, navigate);
|
||||
}
|
||||
} else {
|
||||
console.error('link should be a string');
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -1675,3 +1678,30 @@ export function reduceValueSize(value: any) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// 补全 URL
|
||||
export function completeURL(url: string, origin = window.location.origin) {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
if (isURL(url)) {
|
||||
return url;
|
||||
}
|
||||
return url.startsWith('/') ? `${origin}${url}` : `${origin}/${url}`;
|
||||
}
|
||||
|
||||
export function navigateWithinSelf(link: string, navigate: NavigateFunction, origin = window.location.origin) {
|
||||
if (!_.isString(link)) {
|
||||
return console.error('link should be a string');
|
||||
}
|
||||
|
||||
if (isURL(link)) {
|
||||
if (link.startsWith(origin)) {
|
||||
navigate(link.replace(origin, ''));
|
||||
} else {
|
||||
window.open(link, '_self');
|
||||
}
|
||||
} else {
|
||||
navigate(link.startsWith('/') ? link : `/${link}`);
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export * from './variables';
|
||||
export { withDynamicSchemaProps } from './hoc/withDynamicSchemaProps';
|
||||
|
||||
export { SchemaSettingsActionLinkItem } from './modules/actions/link/customizeLinkActionSettings';
|
||||
export { useURLAndParamsSchema } from './modules/actions/link/useURLAndParamsSchema';
|
||||
export { useURLAndHTMLSchema } from './modules/actions/link/useURLAndHTMLSchema';
|
||||
export * from './modules/blocks/BlockSchemaToolbar';
|
||||
export * from './modules/blocks/data-blocks/form';
|
||||
export * from './modules/blocks/data-blocks/table';
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { expect, test } from '@nocobase/test/e2e';
|
||||
import { oneEmptyTableWithUsers } from './templates';
|
||||
import { oneEmptyTableWithUsers, openInNewWidow } from './templates';
|
||||
|
||||
test.describe('Link', () => {
|
||||
test('basic', async ({ page, mockPage, mockRecords }) => {
|
||||
@ -84,4 +84,34 @@ test.describe('Link', () => {
|
||||
await expect(page.getByRole('button', { name: 'nocobase', exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: users[1].username, exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test('open in new window', async ({ page, mockPage, mockRecords }) => {
|
||||
await mockPage(openInNewWidow).goto();
|
||||
const otherPage = mockPage();
|
||||
const otherPageUrl = await otherPage.getUrl();
|
||||
|
||||
// 默认情况下,点击链接按钮会在当前窗口打开
|
||||
await page.getByLabel('action-Action.Link-Link-').hover();
|
||||
await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:link-users').hover();
|
||||
await page.getByRole('menuitem', { name: 'Edit link' }).click();
|
||||
await page.getByLabel('block-item-users-table-URL').getByLabel('textbox').fill(otherPageUrl);
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await page.getByLabel('action-Action.Link-Link-').click();
|
||||
expect(page.url().endsWith(otherPageUrl)).toBe(true);
|
||||
|
||||
// 开启 “Open in new window” 选项后,点击链接按钮会在新窗口打开
|
||||
await page.goBack();
|
||||
await page.getByLabel('action-Action.Link-Link-').hover();
|
||||
await page.getByLabel('designer-schema-settings-Action.Link-actionSettings:link-users').hover();
|
||||
await page.getByRole('menuitem', { name: 'Edit link' }).click();
|
||||
await page.getByLabel('Open in new window').check();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await page.getByLabel('action-Action.Link-Link-').click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const newPage = page.context().pages()[1];
|
||||
expect(newPage.url().endsWith(otherPageUrl)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -222,3 +222,173 @@ export const oneEmptyTableWithUsers = {
|
||||
'x-index': 1,
|
||||
},
|
||||
};
|
||||
export const openInNewWidow = {
|
||||
pageSchema: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
'x-app-version': '1.2.21-alpha',
|
||||
properties: {
|
||||
g3i4gd8j68w: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'page:addBlock',
|
||||
'x-app-version': '1.2.21-alpha',
|
||||
properties: {
|
||||
k0tx69u4ge5: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-app-version': '1.2.21-alpha',
|
||||
properties: {
|
||||
hck9q8g9nfw: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-app-version': '1.2.21-alpha',
|
||||
properties: {
|
||||
jq1vb4hd0vk: {
|
||||
_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.21-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.21-alpha',
|
||||
'x-uid': 'n7hoqcgj7nn',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
'068dk7k35nk': {
|
||||
_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.21-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.21-alpha',
|
||||
properties: {
|
||||
mo7685lm8vz: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
'x-app-version': '1.2.21-alpha',
|
||||
properties: {
|
||||
t4s3iem6ael: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Link") }}',
|
||||
'x-action': 'customize:link',
|
||||
'x-toolbar': 'ActionSchemaToolbar',
|
||||
'x-settings': 'actionSettings:link',
|
||||
'x-component': 'Action.Link',
|
||||
'x-use-component-props': 'useLinkActionProps',
|
||||
'x-designer-props': {
|
||||
linkageAction: true,
|
||||
},
|
||||
'x-uid': 'pvrerrk3t7w',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'cih8h4b2193',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '50yfcnetw5a',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '4q78sr1a4so',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'k38ru1y7s6s',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'a0aiamh41on',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'gn2c5n843iv',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'gvbup8mmfql',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'af02n5ipxrk',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
},
|
||||
};
|
||||
|
@ -17,15 +17,19 @@ import { SchemaSettings } from '../../../application/schema-settings/SchemaSetti
|
||||
import { useCollection_deprecated } from '../../../collection-manager';
|
||||
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
||||
import { SchemaSettingsLinkageRules, SchemaSettingsModalItem } from '../../../schema-settings';
|
||||
import { useURLAndParamsSchema } from './useURLAndParamsSchema';
|
||||
import { useURLAndHTMLSchema } from './useURLAndHTMLSchema';
|
||||
|
||||
export function SchemaSettingsActionLinkItem() {
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
const { urlSchema, paramsSchema } = useURLAndParamsSchema();
|
||||
const initialValues = { url: field.componentProps.url, params: field.componentProps.params || [{}] };
|
||||
const { urlSchema, paramsSchema, openInNewWindowSchema } = useURLAndHTMLSchema();
|
||||
const initialValues = {
|
||||
url: field.componentProps.url,
|
||||
params: field.componentProps.params || [{}],
|
||||
openInNewWindow: field.componentProps.openInNewWindow,
|
||||
};
|
||||
|
||||
return (
|
||||
<SchemaSettingsModalItem
|
||||
@ -40,16 +44,21 @@ export function SchemaSettingsActionLinkItem() {
|
||||
required: true,
|
||||
},
|
||||
params: paramsSchema,
|
||||
openInNewWindow: openInNewWindowSchema,
|
||||
},
|
||||
}}
|
||||
onSubmit={({ url, params }) => {
|
||||
onSubmit={({ url, params, openInNewWindow }) => {
|
||||
const componentProps = fieldSchema['x-component-props'] || {};
|
||||
componentProps.url = url;
|
||||
fieldSchema['x-component-props'] = componentProps;
|
||||
field.componentProps.url = url;
|
||||
componentProps.params = params;
|
||||
componentProps.openInNewWindow = openInNewWindow;
|
||||
|
||||
fieldSchema['x-component-props'] = componentProps;
|
||||
|
||||
field.componentProps.url = url;
|
||||
field.componentProps.params = params;
|
||||
field.componentProps.openInNewWindow = openInNewWindow;
|
||||
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
|
@ -32,8 +32,7 @@ const getVariableComponentWithScope = (Com) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useURLAndParamsSchema = () => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
export const useURLAndHTMLSchema = () => {
|
||||
const { t } = useTranslation();
|
||||
const Com = useMemo(() => getVariableComponentWithScope(Variable.TextArea), []);
|
||||
|
||||
@ -53,7 +52,40 @@ export const useURLAndParamsSchema = () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
}, [t, fieldSchema]);
|
||||
}, [t, Com]);
|
||||
|
||||
const modeSchema = useMemo(() => {
|
||||
return {
|
||||
title: '{{t("Mode")}}',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
default: 'url',
|
||||
enum: [
|
||||
{ value: 'url', label: t('URL') },
|
||||
{ value: 'html', label: t('HTML') },
|
||||
],
|
||||
};
|
||||
}, [t]);
|
||||
|
||||
const htmlSchema = useMemo(() => {
|
||||
return {
|
||||
title: t('html'),
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': getVariableComponentWithScope(Variable.RawTextArea),
|
||||
'x-component-props': {
|
||||
rows: 10,
|
||||
},
|
||||
'x-reactions': {
|
||||
dependencies: ['mode'],
|
||||
fulfill: {
|
||||
state: {
|
||||
hidden: '{{$deps[0] === "url"}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}, [t]);
|
||||
|
||||
const paramsSchema = useMemo(() => {
|
||||
return {
|
||||
@ -123,7 +155,24 @@ export const useURLAndParamsSchema = () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
}, [fieldSchema]);
|
||||
}, [Com]);
|
||||
|
||||
return { urlSchema, paramsSchema };
|
||||
const openInNewWindowSchema = useMemo(() => {
|
||||
return {
|
||||
type: 'boolean',
|
||||
'x-content': t('Open in new window'),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-reactions': {
|
||||
dependencies: ['mode'],
|
||||
fulfill: {
|
||||
state: {
|
||||
hidden: '{{$deps[0] === "html"}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}, [t]);
|
||||
|
||||
return { urlSchema, paramsSchema, openInNewWindowSchema, modeSchema, htmlSchema };
|
||||
};
|
@ -16,7 +16,7 @@ import { SchemaInitializerItem, useSchemaInitializer } from '../../application';
|
||||
import { useGlobalTheme } from '../../global-theme';
|
||||
import { FormDialog, SchemaComponent, SchemaComponentOptions } from '../../schema-component';
|
||||
import { useStyles } from '../../schema-component/antd/menu/MenuItemInitializers';
|
||||
import { useURLAndParamsSchema } from '../actions/link/useURLAndParamsSchema';
|
||||
import { useURLAndHTMLSchema } from '../actions/link/useURLAndHTMLSchema';
|
||||
|
||||
export const LinkMenuItem = () => {
|
||||
const { insert } = useSchemaInitializer();
|
||||
@ -24,7 +24,7 @@ export const LinkMenuItem = () => {
|
||||
const options = useContext(SchemaOptionsContext);
|
||||
const { theme } = useGlobalTheme();
|
||||
const { styles } = useStyles();
|
||||
const { urlSchema, paramsSchema } = useURLAndParamsSchema();
|
||||
const { urlSchema, paramsSchema } = useURLAndHTMLSchema();
|
||||
|
||||
const handleClick = useCallback(async () => {
|
||||
const values = await FormDialog(
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
SchemaSettingsSubMenu,
|
||||
useAPIClient,
|
||||
useDesignable,
|
||||
useURLAndParamsSchema,
|
||||
useURLAndHTMLSchema,
|
||||
} from '../../../';
|
||||
|
||||
const toItems = (properties = {}) => {
|
||||
@ -61,7 +61,7 @@ const InsertMenuItems = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const { dn } = useDesignable();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { urlSchema, paramsSchema } = useURLAndParamsSchema();
|
||||
const { urlSchema, paramsSchema } = useURLAndHTMLSchema();
|
||||
const isSubMenu = fieldSchema['x-component'] === 'Menu.SubMenu';
|
||||
if (!isSubMenu && insertPosition === 'beforeEnd') {
|
||||
return null;
|
||||
@ -218,7 +218,7 @@ export const MenuDesigner = () => {
|
||||
const { t } = useTranslation();
|
||||
const menuSchema = findMenuSchema(fieldSchema);
|
||||
const compile = useCompile();
|
||||
const { urlSchema, paramsSchema } = useURLAndParamsSchema();
|
||||
const { urlSchema, paramsSchema } = useURLAndHTMLSchema();
|
||||
const onSelect = compile(menuSchema?.['x-component-props']?.['onSelect']);
|
||||
const items = toItems(menuSchema?.properties);
|
||||
const effects = (form) => {
|
||||
|
@ -7,8 +7,13 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 是否是完整的 URL(带协议的)
|
||||
* @param string
|
||||
* @returns
|
||||
*/
|
||||
export function isURL(string) {
|
||||
let url;
|
||||
let url: URL;
|
||||
|
||||
try {
|
||||
url = new URL(string);
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
useDesignable,
|
||||
useFormBlockContext,
|
||||
useRecord,
|
||||
useURLAndParamsSchema,
|
||||
useURLAndHTMLSchema,
|
||||
useVariableOptions,
|
||||
} from '@nocobase/client';
|
||||
import React from 'react';
|
||||
@ -65,7 +65,7 @@ const commonOptions: any = {
|
||||
return data?.data;
|
||||
}
|
||||
};
|
||||
const { urlSchema, paramsSchema } = useURLAndParamsSchema();
|
||||
const { urlSchema, paramsSchema } = useURLAndHTMLSchema();
|
||||
const submitHandler = async ({ mode, url, html, height, params }) => {
|
||||
const componentProps = fieldSchema['x-component-props'] || {};
|
||||
componentProps['mode'] = mode;
|
||||
|
Loading…
x
Reference in New Issue
Block a user