refactor: fix warning of antd 4.x (#1998)
* refactor: fix warning by codemod * refactor: fix warning of Dropdown * perf: use memo * refactor: resolve SchemaInitializer * refactor: fix lint * refactor: remove SettingsForm * refactor: resolve SchemaInitializer * refactor: fix lint * refactor: move useMenuItem to root dir * chore: fix conflicts * refactor: resolve SchemaSetting * refactor: fix lint * test: fix failed * chore: upgrade Vite * fix: fix style * refactor: fix lint * refactor: extract component * refactor: resovle Menu * refactor: resolve Tabs * refactor(getPopupContainer): should return the unique div * refactor(Drawer): change style to rootStyle and className to rootClassName * chore: update yarn.lock * fix: fix T-432 * fix: fix T-338 * fix: fix T-490 * fix: collection fields * fix: fix style * fix: fix T-500 * fix: fix SettingMenu error (close T-516) * fix: fix tanslation of Map (T-506) * style: fix style (T-508) * fix: fix schemaSetting switch of mobile (T-517) * fix: fix T-518 * fix: fix T-524 * fix: fix T-507 * perf: optimize SchemaInitializer.Button * perf: optimize SchemaSettings * fix: fix serch of SchemaInitializer (T-547) * chore: change delay * fix: fix button style (T-548) * fix: fix scroll bar * fix: update yarn.lock * fix: fix build error * fix: should update sideMenu when change it * fix: fix build error * chore: mouseEnterDelay * fix: fix group menu can not selected
This commit is contained in:
parent
5fc5428d03
commit
6eed9ac2bb
@ -63,7 +63,7 @@
|
|||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"pretty-format": "^24.0.0",
|
"pretty-format": "^24.0.0",
|
||||||
"pretty-quick": "^3.1.0",
|
"pretty-quick": "^3.1.0",
|
||||||
"vite": "^4.3.8",
|
"vite": "^4.3.9",
|
||||||
"vitest": "^0.32.0"
|
"vitest": "^0.32.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"typings": "es/index.d.ts",
|
"typings": "es/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antv/g2plot": "^2.4.18",
|
"@antv/g2plot": "^2.4.18",
|
||||||
|
"@ant-design/pro-layout": "^7.14.3",
|
||||||
"@dnd-kit/core": "^5.0.1",
|
"@dnd-kit/core": "^5.0.1",
|
||||||
"@dnd-kit/sortable": "^6.0.0",
|
"@dnd-kit/sortable": "^6.0.0",
|
||||||
"@emotion/css": "^11.7.1",
|
"@emotion/css": "^11.7.1",
|
||||||
|
@ -7,6 +7,8 @@ import { Link, NavLink } from 'react-router-dom';
|
|||||||
import { ACLProvider } from '../acl';
|
import { ACLProvider } from '../acl';
|
||||||
import { AntdConfigProvider } from '../antd-config-provider';
|
import { AntdConfigProvider } from '../antd-config-provider';
|
||||||
import { APIClient, APIClientProvider } from '../api-client';
|
import { APIClient, APIClientProvider } from '../api-client';
|
||||||
|
import { SigninPage, SignupPage } from '../auth';
|
||||||
|
import { SigninPageExtensionProvider } from '../auth/SigninPageExtension';
|
||||||
import { BlockSchemaComponentProvider } from '../block-provider';
|
import { BlockSchemaComponentProvider } from '../block-provider';
|
||||||
import { RemoteDocumentTitleProvider } from '../document-title';
|
import { RemoteDocumentTitleProvider } from '../document-title';
|
||||||
import { i18n } from '../i18n';
|
import { i18n } from '../i18n';
|
||||||
@ -30,8 +32,6 @@ import { ErrorFallback } from '../schema-component/antd/error-fallback';
|
|||||||
import { SchemaInitializerProvider } from '../schema-initializer';
|
import { SchemaInitializerProvider } from '../schema-initializer';
|
||||||
import { BlockTemplateDetails, BlockTemplatePage } from '../schema-templates';
|
import { BlockTemplateDetails, BlockTemplatePage } from '../schema-templates';
|
||||||
import { SystemSettingsProvider } from '../system-settings';
|
import { SystemSettingsProvider } from '../system-settings';
|
||||||
import { SigninPage, SignupPage } from '../auth';
|
|
||||||
import { SigninPageExtensionProvider } from '../auth/SigninPageExtension';
|
|
||||||
import { compose } from './compose';
|
import { compose } from './compose';
|
||||||
|
|
||||||
export interface ApplicationOptions {
|
export interface ApplicationOptions {
|
||||||
@ -52,9 +52,7 @@ export type PluginCallback = () => Promise<any>;
|
|||||||
const App = React.memo((props: any) => {
|
const App = React.memo((props: any) => {
|
||||||
const C = compose(...props.providers)(() => {
|
const C = compose(...props.providers)(() => {
|
||||||
const routes = useRoutes();
|
const routes = useRoutes();
|
||||||
return (
|
return <RouteSwitch routes={routes} />;
|
||||||
<RouteSwitch routes={routes} />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
return <C />;
|
return <C />;
|
||||||
});
|
});
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { useForm } from '@formily/react';
|
||||||
import { Space, Tabs } from 'antd';
|
import { Space, Tabs } from 'antd';
|
||||||
import React, {
|
import React, {
|
||||||
|
FunctionComponent,
|
||||||
|
FunctionComponentElement,
|
||||||
|
createContext,
|
||||||
|
createElement,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
createContext,
|
|
||||||
FunctionComponent,
|
|
||||||
createElement,
|
|
||||||
useState,
|
useState,
|
||||||
FunctionComponentElement,
|
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { css } from '@emotion/css';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { useAPIClient, useCurrentDocumentTitle, useRequest, useViewport } from '..';
|
import { useAPIClient, useCurrentDocumentTitle, useRequest, useViewport } from '..';
|
||||||
import { useSigninPageExtension } from './SigninPageExtension';
|
import { useSigninPageExtension } from './SigninPageExtension';
|
||||||
import { useForm } from '@formily/react';
|
|
||||||
|
|
||||||
const SigninPageContext = createContext<{
|
const SigninPageContext = createContext<{
|
||||||
[authType: string]: {
|
[authType: string]: {
|
||||||
@ -132,13 +132,7 @@ export const SigninPage = () => {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{tabs.length > 1 ? (
|
{tabs.length > 1 ? (
|
||||||
<Tabs>
|
<Tabs items={tabs.map((tab) => ({ label: tab.tabTitle, key: tab.name, children: tab.component }))} />
|
||||||
{tabs.map((tab) => (
|
|
||||||
<Tabs.TabPane tab={tab.tabTitle} key={tab.name}>
|
|
||||||
{tab.component}
|
|
||||||
</Tabs.TabPane>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
) : tabs.length ? (
|
) : tabs.length ? (
|
||||||
<div>{tabs[0].component}</div>
|
<div>{tabs[0].component}</div>
|
||||||
) : (
|
) : (
|
||||||
|
@ -2,9 +2,9 @@ import { DownOutlined, PlusOutlined } from '@ant-design/icons';
|
|||||||
import { ArrayTable } from '@formily/antd';
|
import { ArrayTable } from '@formily/antd';
|
||||||
import { ISchema, useField, useForm } from '@formily/react';
|
import { ISchema, useField, useForm } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Button, Dropdown, Menu } from 'antd';
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRequest } from '../../api-client';
|
import { useRequest } from '../../api-client';
|
||||||
import { RecordProvider, useRecord } from '../../record-provider';
|
import { RecordProvider, useRecord } from '../../record-provider';
|
||||||
@ -246,41 +246,41 @@ export const AddCollectionAction = (props) => {
|
|||||||
const [schema, setSchema] = useState({});
|
const [schema, setSchema] = useState({});
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const collectionTemplates = templateOptions();
|
const collectionTemplates = useMemo(templateOptions, []);
|
||||||
const items = [];
|
const items = useMemo(() => {
|
||||||
collectionTemplates.forEach((item) => {
|
const result = [];
|
||||||
if (item.divider) {
|
collectionTemplates.forEach((item) => {
|
||||||
items.push({
|
if (item.divider) {
|
||||||
type: 'divider',
|
result.push({
|
||||||
});
|
type: 'divider',
|
||||||
}
|
});
|
||||||
items.push({ label: compile(item.title), key: item.name });
|
}
|
||||||
});
|
result.push({ label: compile(item.title), key: item.name });
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}, [collectionTemplates]);
|
||||||
const {
|
const {
|
||||||
state: { category },
|
state: { category },
|
||||||
} = useResourceActionContext();
|
} = useResourceActionContext();
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
|
return {
|
||||||
|
style: {
|
||||||
|
maxHeight: '60vh',
|
||||||
|
overflow: 'auto',
|
||||||
|
},
|
||||||
|
onClick: (info) => {
|
||||||
|
const schema = getSchema(getTemplate(info.key), category, compile);
|
||||||
|
setSchema(schema);
|
||||||
|
setVisible(true);
|
||||||
|
},
|
||||||
|
items,
|
||||||
|
};
|
||||||
|
}, [category, items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordProvider record={record}>
|
<RecordProvider record={record}>
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<Dropdown
|
<Dropdown getPopupContainer={getContainer} trigger={trigger} align={align} menu={menu}>
|
||||||
getPopupContainer={getContainer}
|
|
||||||
trigger={trigger}
|
|
||||||
align={align}
|
|
||||||
overlay={
|
|
||||||
<Menu
|
|
||||||
style={{
|
|
||||||
maxHeight: '60vh',
|
|
||||||
overflow: 'auto',
|
|
||||||
}}
|
|
||||||
onClick={(info) => {
|
|
||||||
const schema = getSchema(getTemplate(info.key), category, compile);
|
|
||||||
setSchema(schema);
|
|
||||||
setVisible(true);
|
|
||||||
}}
|
|
||||||
items={items}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{children || (
|
{children || (
|
||||||
<Button icon={<PlusOutlined />} type={'primary'}>
|
<Button icon={<PlusOutlined />} type={'primary'}>
|
||||||
{t('Create collection')} <DownOutlined />
|
{t('Create collection')} <DownOutlined />
|
||||||
|
@ -2,9 +2,9 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { ArrayTable } from '@formily/antd';
|
import { ArrayTable } from '@formily/antd';
|
||||||
import { useField, useForm } from '@formily/react';
|
import { useField, useForm } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Button, Dropdown, Menu } from 'antd';
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRequest } from '../../api-client';
|
import { useRequest } from '../../api-client';
|
||||||
import { RecordProvider, useRecord } from '../../record-provider';
|
import { RecordProvider, useRecord } from '../../record-provider';
|
||||||
@ -12,7 +12,6 @@ import { ActionContextProvider, SchemaComponent, useActionContext, useCompile }
|
|||||||
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
||||||
import { useCancelAction } from '../action-hooks';
|
import { useCancelAction } from '../action-hooks';
|
||||||
import { useCollectionManager } from '../hooks';
|
import { useCollectionManager } from '../hooks';
|
||||||
import { useOptions } from '../hooks/useOptions';
|
|
||||||
import { IField } from '../interfaces/types';
|
import { IField } from '../interfaces/types';
|
||||||
import * as components from './components';
|
import * as components from './components';
|
||||||
import { getOptions } from './interfaces';
|
import { getOptions } from './interfaces';
|
||||||
@ -175,8 +174,7 @@ export const AddFieldAction = (props) => {
|
|||||||
const [schema, setSchema] = useState({});
|
const [schema, setSchema] = useState({});
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const options = useOptions();
|
const getFieldOptions = useCallback(() => {
|
||||||
const getFieldOptions = () => {
|
|
||||||
const { availableFieldInterfaces } = getTemplate(record.template) || {};
|
const { availableFieldInterfaces } = getTemplate(record.template) || {};
|
||||||
const { exclude, include } = availableFieldInterfaces || {};
|
const { exclude, include } = availableFieldInterfaces || {};
|
||||||
const optionArr = [];
|
const optionArr = [];
|
||||||
@ -218,52 +216,57 @@ export const AddFieldAction = (props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return optionArr;
|
return optionArr;
|
||||||
};
|
}, [getTemplate, record]);
|
||||||
|
const items = useMemo<MenuProps['items']>(() => {
|
||||||
|
return getFieldOptions().map((option) => {
|
||||||
|
if (option.children.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'group',
|
||||||
|
label: compile(option.label),
|
||||||
|
title: compile(option.label),
|
||||||
|
key: option.label,
|
||||||
|
children: option.children
|
||||||
|
.filter((child) => !['o2o', 'subTable', 'linkTo'].includes(child.name))
|
||||||
|
.map((child) => {
|
||||||
|
return {
|
||||||
|
label: compile(child.title),
|
||||||
|
title: compile(child.title),
|
||||||
|
key: child.name,
|
||||||
|
dataTargetScope: child.targetScope,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [getFieldOptions]);
|
||||||
|
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
|
return {
|
||||||
|
style: {
|
||||||
|
maxHeight: '60vh',
|
||||||
|
overflow: 'auto',
|
||||||
|
},
|
||||||
|
onClick: (e) => {
|
||||||
|
//@ts-ignore
|
||||||
|
const targetScope = e.item.props['data-targetScope'];
|
||||||
|
targetScope && setTargetScope(targetScope);
|
||||||
|
const schema = getSchema(getInterface(e.key), record, compile);
|
||||||
|
if (schema) {
|
||||||
|
setSchema(schema);
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items,
|
||||||
|
};
|
||||||
|
}, [getInterface, items, record]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
record.template !== 'view' && (
|
record.template !== 'view' && (
|
||||||
<RecordProvider record={record}>
|
<RecordProvider record={record}>
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<Dropdown
|
<Dropdown getPopupContainer={getContainer} trigger={trigger} align={align} menu={menu}>
|
||||||
getPopupContainer={getContainer}
|
|
||||||
trigger={trigger}
|
|
||||||
align={align}
|
|
||||||
overlay={
|
|
||||||
<Menu
|
|
||||||
style={{
|
|
||||||
maxHeight: '60vh',
|
|
||||||
overflow: 'auto',
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
//@ts-ignore
|
|
||||||
const targetScope = e.item.props['data-targetScope'];
|
|
||||||
targetScope && setTargetScope(targetScope);
|
|
||||||
const schema = getSchema(getInterface(e.key), record, compile);
|
|
||||||
if (schema) {
|
|
||||||
setSchema(schema);
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getFieldOptions().map((option) => {
|
|
||||||
return (
|
|
||||||
option.children.length > 0 && (
|
|
||||||
<Menu.ItemGroup key={option.label} title={compile(option.label)}>
|
|
||||||
{option.children
|
|
||||||
.filter((child) => !['o2o', 'subTable', 'linkTo'].includes(child.name))
|
|
||||||
.map((child) => {
|
|
||||||
return (
|
|
||||||
<Menu.Item key={child.name} data-targetScope={child.targetScope}>
|
|
||||||
{compile(child.title)}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Menu.ItemGroup>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{children || (
|
{children || (
|
||||||
<Button icon={<PlusOutlined />} type={'primary'}>
|
<Button icon={<PlusOutlined />} type={'primary'}>
|
||||||
{t('Add field')}
|
{t('Add field')}
|
||||||
|
@ -2,9 +2,9 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { ArrayTable } from '@formily/antd';
|
import { ArrayTable } from '@formily/antd';
|
||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Button, Dropdown, Menu } from 'antd';
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRequest } from '../../api-client';
|
import { useRequest } from '../../api-client';
|
||||||
import { RecordProvider } from '../../record-provider';
|
import { RecordProvider } from '../../record-provider';
|
||||||
@ -101,34 +101,36 @@ export const AddSubFieldAction = () => {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const options = useOptions();
|
const options = useOptions();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const items = options.map((option) => {
|
const items = useMemo(() => {
|
||||||
const children = option.children.map((child) => {
|
return options.map((option) => {
|
||||||
return { label: compile(child.title), key: child.name };
|
const children = option.children.map((child) => {
|
||||||
|
return { label: compile(child.title), key: child.name };
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
label: compile(option.label),
|
||||||
|
key: option.key,
|
||||||
|
children,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
}, [options]);
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
return {
|
return {
|
||||||
label: compile(option.label),
|
style: {
|
||||||
key: option.key,
|
maxHeight: '60vh',
|
||||||
children,
|
overflow: 'auto',
|
||||||
|
},
|
||||||
|
onClick: (info) => {
|
||||||
|
const schema = getSchema(getInterface(info.key));
|
||||||
|
setSchema(schema);
|
||||||
|
setVisible(true);
|
||||||
|
},
|
||||||
|
items,
|
||||||
};
|
};
|
||||||
});
|
}, [items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<Dropdown
|
<Dropdown menu={menu}>
|
||||||
overlay={
|
|
||||||
<Menu
|
|
||||||
style={{
|
|
||||||
maxHeight: '60vh',
|
|
||||||
overflow: 'auto',
|
|
||||||
}}
|
|
||||||
onClick={(info) => {
|
|
||||||
const schema = getSchema(getInterface(info.key));
|
|
||||||
setSchema(schema);
|
|
||||||
setVisible(true);
|
|
||||||
}}
|
|
||||||
items={items}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button icon={<PlusOutlined />} type={'primary'}>
|
<Button icon={<PlusOutlined />} type={'primary'}>
|
||||||
{t('Add field')}
|
{t('Add field')}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -11,7 +11,8 @@ import {
|
|||||||
} from '@dnd-kit/core';
|
} from '@dnd-kit/core';
|
||||||
import { RecursionField, observer } from '@formily/react';
|
import { RecursionField, observer } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Badge, Card, Dropdown, Menu, Modal, Tabs } from 'antd';
|
import { Badge, Card, Dropdown, Modal, Tabs } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { useAPIClient } from '../../api-client';
|
import { useAPIClient } from '../../api-client';
|
||||||
import { SchemaComponent, SchemaComponentOptions, useCompile } from '../../schema-component';
|
import { SchemaComponent, SchemaComponentOptions, useCompile } from '../../schema-component';
|
||||||
@ -181,28 +182,37 @@ export const ConfigurationTabs = () => {
|
|||||||
value: item.id,
|
value: item.id,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
const menu = (item) => (
|
|
||||||
<Menu>
|
const menu = _.memoize((item) => {
|
||||||
<Menu.Item key={'edit'}>
|
return {
|
||||||
<SchemaComponent
|
items: [
|
||||||
schema={{
|
{
|
||||||
type: 'void',
|
key: 'edit',
|
||||||
properties: {
|
label: (
|
||||||
[uid()]: {
|
<SchemaComponent
|
||||||
'x-component': 'EditCategory',
|
schema={{
|
||||||
'x-component-props': {
|
type: 'void',
|
||||||
item: item,
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
'x-component': 'EditCategory',
|
||||||
|
'x-component-props': {
|
||||||
|
item: item,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}}
|
||||||
},
|
/>
|
||||||
}}
|
),
|
||||||
/>
|
},
|
||||||
</Menu.Item>
|
{
|
||||||
<Menu.Item key="delete" onClick={() => remove(item.id)}>
|
key: 'delete',
|
||||||
{compile("{{t('Delete category')}}")}
|
label: compile("{{t('Delete category')}}"),
|
||||||
</Menu.Item>
|
onClick: () => remove(item.id),
|
||||||
</Menu>
|
},
|
||||||
);
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndProvider>
|
<DndProvider>
|
||||||
<Tabs
|
<Tabs
|
||||||
@ -228,27 +238,24 @@ export const ConfigurationTabs = () => {
|
|||||||
type="editable-card"
|
type="editable-card"
|
||||||
destroyInactiveTabPane={true}
|
destroyInactiveTabPane={true}
|
||||||
tabBarStyle={{ marginBottom: '0px' }}
|
tabBarStyle={{ marginBottom: '0px' }}
|
||||||
>
|
items={tabsItems.map((item) => {
|
||||||
{tabsItems.map((item) => {
|
return {
|
||||||
return (
|
label:
|
||||||
<Tabs.TabPane
|
item.id !== 'all' ? (
|
||||||
tab={
|
<div data-no-dnd="true">
|
||||||
item.id !== 'all' ? (
|
<TabTitle item={item} />
|
||||||
<div data-no-dnd="true">
|
</div>
|
||||||
<TabTitle item={item} />
|
) : (
|
||||||
</div>
|
compile(item.name)
|
||||||
) : (
|
),
|
||||||
compile(item.name)
|
key: item.id,
|
||||||
)
|
closable: item.closable,
|
||||||
}
|
closeIcon: (
|
||||||
key={item.id}
|
<Dropdown menu={menu(item)}>
|
||||||
closable={item.closable}
|
<MenuOutlined />
|
||||||
closeIcon={
|
</Dropdown>
|
||||||
<Dropdown overlay={menu(item)}>
|
),
|
||||||
<MenuOutlined />
|
children: (
|
||||||
</Dropdown>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Card bordered={false}>
|
<Card bordered={false}>
|
||||||
<SchemaComponentOptions
|
<SchemaComponentOptions
|
||||||
components={{ CollectionFields }}
|
components={{ CollectionFields }}
|
||||||
@ -258,10 +265,10 @@ export const ConfigurationTabs = () => {
|
|||||||
<RecursionField name={key} schema={item.schema} onlyRenderProperties />
|
<RecursionField name={key} schema={item.schema} onlyRenderProperties />
|
||||||
</SchemaComponentOptions>
|
</SchemaComponentOptions>
|
||||||
</Card>
|
</Card>
|
||||||
</Tabs.TabPane>
|
),
|
||||||
);
|
};
|
||||||
})}
|
})}
|
||||||
</Tabs>
|
/>
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -82,7 +82,7 @@ const FormItemInitializer = (props) => {
|
|||||||
collection.fields.push(options);
|
collection.fields.push(options);
|
||||||
form.setValuesIn(name, uid());
|
form.setValuesIn(name, uid());
|
||||||
|
|
||||||
const { values } = await FormDrawer('Add field', () => {
|
await FormDrawer('Add field', () => {
|
||||||
return (
|
return (
|
||||||
<CollectionManagerContext.Provider value={cm}>
|
<CollectionManagerContext.Provider value={cm}>
|
||||||
<AntdSchemaComponentProvider>
|
<AntdSchemaComponentProvider>
|
||||||
|
@ -1,45 +1,48 @@
|
|||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import { useCollectionManager } from './useCollectionManager';
|
import { useCollectionManager } from './useCollectionManager';
|
||||||
|
|
||||||
export const useOptions = () => {
|
export const useOptions = () => {
|
||||||
const { interfaces } = useCollectionManager();
|
const { interfaces } = useCollectionManager();
|
||||||
|
|
||||||
const fields = {};
|
return useMemo(() => {
|
||||||
|
const fields = {};
|
||||||
|
|
||||||
Object.keys(interfaces).forEach((type) => {
|
Object.keys(interfaces).forEach((type) => {
|
||||||
const schema = interfaces[type];
|
const schema = interfaces[type];
|
||||||
registerField(schema.group || 'others', type, { order: 0, ...schema });
|
registerField(schema.group || 'others', type, { order: 0, ...schema });
|
||||||
});
|
});
|
||||||
|
|
||||||
function registerField(group: string, type: string, schema) {
|
function registerField(group: string, type: string, schema) {
|
||||||
fields[group] = fields[group] || {};
|
fields[group] = fields[group] || {};
|
||||||
set(fields, [group, type], schema);
|
set(fields, [group, type], schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupLabels = {
|
const groupLabels = {
|
||||||
basic: '{{t("Basic")}}',
|
basic: '{{t("Basic")}}',
|
||||||
choices: '{{t("Choices")}}',
|
choices: '{{t("Choices")}}',
|
||||||
media: '{{t("Media")}}',
|
media: '{{t("Media")}}',
|
||||||
datetime: '{{t("Date & Time")}}',
|
datetime: '{{t("Date & Time")}}',
|
||||||
relation: '{{t("Relation")}}',
|
relation: '{{t("Relation")}}',
|
||||||
advanced: '{{t("Advanced type")}}',
|
advanced: '{{t("Advanced type")}}',
|
||||||
systemInfo: '{{t("System info")}}',
|
systemInfo: '{{t("System info")}}',
|
||||||
others: '{{t("Others")}}',
|
others: '{{t("Others")}}',
|
||||||
};
|
};
|
||||||
|
|
||||||
return Object.keys(groupLabels).map((groupName) => ({
|
return Object.keys(groupLabels).map((groupName) => ({
|
||||||
label: groupLabels[groupName],
|
label: groupLabels[groupName],
|
||||||
key: groupName,
|
key: groupName,
|
||||||
children: Object.keys(fields[groupName] || {})
|
children: Object.keys(fields[groupName] || {})
|
||||||
.map((type) => {
|
.map((type) => {
|
||||||
const field = fields[groupName][type];
|
const field = fields[groupName][type];
|
||||||
return {
|
return {
|
||||||
value: type,
|
value: type,
|
||||||
label: field.title,
|
label: field.title,
|
||||||
name: type,
|
name: type,
|
||||||
...fields[groupName][type],
|
...fields[groupName][type],
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => a.order - b.order),
|
.sort((a, b) => a.order - b.order),
|
||||||
}));
|
}));
|
||||||
|
}, [interfaces]);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Field, onFormSubmitValidateStart } from '@formily/core';
|
import { Field, onFormSubmitValidateStart } from '@formily/core';
|
||||||
import { useField, useFormEffects } from '@formily/react';
|
import { useField, useFormEffects } from '@formily/react';
|
||||||
import { Dropdown, Menu } from 'antd';
|
import { Dropdown, MenuProps } from 'antd';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function pasteHtml(html, selectPastedContent = false) {
|
function pasteHtml(html, selectPastedContent = false) {
|
||||||
@ -71,19 +71,27 @@ export const Expression = (props) => {
|
|||||||
const inputRef = useRef<any>();
|
const inputRef = useRef<any>();
|
||||||
const [changed, setChanged] = useState(false);
|
const [changed, setChanged] = useState(false);
|
||||||
|
|
||||||
const onChange = (value) => {
|
const onChange = useCallback(
|
||||||
setChanged(true);
|
(value) => {
|
||||||
props.onChange(value);
|
setChanged(true);
|
||||||
};
|
props.onChange(value);
|
||||||
|
},
|
||||||
|
[props.onChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { numColumns, scope } = useMemo(() => {
|
||||||
|
const numColumns = new Map<string, string>();
|
||||||
|
const scope = {};
|
||||||
|
fields
|
||||||
|
.filter((field) => supports.includes(field.interface))
|
||||||
|
.forEach((field) => {
|
||||||
|
numColumns.set(field.name, field.uiSchema.title);
|
||||||
|
scope[field.name] = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { numColumns, scope };
|
||||||
|
}, [fields, supports]);
|
||||||
|
|
||||||
const numColumns = new Map<string, string>();
|
|
||||||
const scope = {};
|
|
||||||
fields
|
|
||||||
.filter((field) => supports.includes(field.interface))
|
|
||||||
.forEach((field) => {
|
|
||||||
numColumns.set(field.name, field.uiSchema.title);
|
|
||||||
scope[field.name] = 1;
|
|
||||||
});
|
|
||||||
const keys = Array.from(numColumns.keys());
|
const keys = Array.from(numColumns.keys());
|
||||||
const [html, setHtml] = useState(() => {
|
const [html, setHtml] = useState(() => {
|
||||||
const scope = {};
|
const scope = {};
|
||||||
@ -95,6 +103,7 @@ export const Expression = (props) => {
|
|||||||
}
|
}
|
||||||
return renderExp(value || '', scope);
|
return renderExp(value || '', scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
return;
|
return;
|
||||||
@ -109,34 +118,44 @@ export const Expression = (props) => {
|
|||||||
const val = renderExp(value || '', scope);
|
const val = renderExp(value || '', scope);
|
||||||
setHtml(val);
|
setHtml(val);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
const menu = (
|
|
||||||
<Menu>
|
const menuItems = useMemo<MenuProps['items']>(() => {
|
||||||
{keys.length > 0 ? (
|
if (keys.length > 0) {
|
||||||
keys.map((key) => (
|
return keys.map((key) => ({
|
||||||
<Menu.Item disabled key={key}>
|
key,
|
||||||
<button
|
disabled: true,
|
||||||
onClick={async (args) => {
|
label: (
|
||||||
(inputRef.current as any).focus();
|
<button
|
||||||
const val = numColumns.get(key);
|
onClick={async (args) => {
|
||||||
pasteHtml(
|
(inputRef.current as any).focus();
|
||||||
` <span class="ant-tag" style="margin: 0 3px;" contentEditable="false" data-key="${key}">${val}</span> `,
|
const val = numColumns.get(key);
|
||||||
);
|
pasteHtml(
|
||||||
const text = getValue(inputRef.current);
|
` <span class="ant-tag" style="margin: 0 3px;" contentEditable="false" data-key="${key}">${val}</span> `,
|
||||||
onChange(text);
|
);
|
||||||
console.log('onChange', text);
|
const text = getValue(inputRef.current);
|
||||||
}}
|
onChange(text);
|
||||||
>
|
}}
|
||||||
{numColumns.get(key)}
|
>
|
||||||
</button>
|
{numColumns.get(key)}
|
||||||
</Menu.Item>
|
</button>
|
||||||
))
|
),
|
||||||
) : (
|
}));
|
||||||
<Menu.Item disabled key={0}>
|
} else {
|
||||||
{t('No available fields')}
|
return [
|
||||||
</Menu.Item>
|
{
|
||||||
)}
|
key: 0,
|
||||||
</Menu>
|
disabled: true,
|
||||||
);
|
label: t('No available fields'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}, [keys, numColumns, onChange]);
|
||||||
|
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
|
return {
|
||||||
|
items: menuItems,
|
||||||
|
};
|
||||||
|
}, [menuItems]);
|
||||||
|
|
||||||
useFormEffects(() => {
|
useFormEffects(() => {
|
||||||
onFormSubmitValidateStart(() => {
|
onFormSubmitValidateStart(() => {
|
||||||
@ -157,7 +176,7 @@ export const Expression = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
overlay={menu}
|
menu={menu}
|
||||||
overlayClassName={css`
|
overlayClassName={css`
|
||||||
.ant-dropdown-menu-item {
|
.ant-dropdown-menu-item {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
102
packages/core/client/src/hooks/useMenuItem.tsx
Normal file
102
packages/core/client/src/hooks/useMenuItem.tsx
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { MenuProps } from 'antd';
|
||||||
|
import React, { ReactNode, createContext, useCallback, useContext, useRef } from 'react';
|
||||||
|
|
||||||
|
type Item = MenuProps['items'][0] & {
|
||||||
|
/** 在清空数组时,如果该字段为 true 则保留该选项 */
|
||||||
|
notdelete?: boolean;
|
||||||
|
/** 用于给列表排序 */
|
||||||
|
order?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GetMenuItemContext = createContext<{ collectMenuItem?(item: Item): void; onChange?: () => void }>(null);
|
||||||
|
export const GetMenuItemsContext = createContext<{ pushMenuItem?(item: Item): void }>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于为 SchemaInitializer.Item 组件提供一些方法,比如收集菜单项数据
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useCollectMenuItem = () => {
|
||||||
|
return useContext(GetMenuItemContext) || {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCollectMenuItems = () => {
|
||||||
|
return useContext(GetMenuItemsContext) || {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于在 antd 从 4.x 升级到 5.x 中,用于把 SchemaInitializer.Item 组件这种写法转换成 Menu 组件的 items 写法
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useMenuItem = () => {
|
||||||
|
const list = useRef<any[]>([]);
|
||||||
|
const renderItems = useRef<() => JSX.Element>(null);
|
||||||
|
const shouldRerender = useRef(false);
|
||||||
|
|
||||||
|
const Component = useCallback(() => {
|
||||||
|
if (!shouldRerender.current) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
shouldRerender.current = false;
|
||||||
|
|
||||||
|
if (renderItems.current) {
|
||||||
|
return renderItems.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{list.current.map((Com, index) => (
|
||||||
|
<Com key={index} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getMenuItems = useCallback((Com: () => ReactNode): Item[] => {
|
||||||
|
const items: Item[] = [];
|
||||||
|
|
||||||
|
const pushMenuItem = (item: Item) => {
|
||||||
|
items.push(item);
|
||||||
|
items.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
shouldRerender.current = true;
|
||||||
|
renderItems.current = () => {
|
||||||
|
const notDeleteItems = items.filter((item) => item.notdelete);
|
||||||
|
items.length = 0;
|
||||||
|
items.push(...notDeleteItems);
|
||||||
|
return (
|
||||||
|
<GetMenuItemsContext.Provider
|
||||||
|
value={{
|
||||||
|
pushMenuItem,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Com()}
|
||||||
|
</GetMenuItemsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getMenuItem = useCallback((Com: () => JSX.Element): Item => {
|
||||||
|
const item = {} as Item;
|
||||||
|
|
||||||
|
const collectMenuItem = (menuItem: Item) => {
|
||||||
|
Object.assign(item, menuItem);
|
||||||
|
};
|
||||||
|
|
||||||
|
shouldRerender.current = true;
|
||||||
|
list.current.push(() => {
|
||||||
|
return <GetMenuItemContext.Provider value={{ collectMenuItem }}>{Com()}</GetMenuItemContext.Provider>;
|
||||||
|
});
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 防止 list 有重复元素
|
||||||
|
const clean = useCallback(() => {
|
||||||
|
list.current = [];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { Component, getMenuItems, getMenuItem, clean };
|
||||||
|
};
|
@ -14,6 +14,7 @@ export * from './collection-manager';
|
|||||||
export * from './document-title';
|
export * from './document-title';
|
||||||
export * from './filter-provider';
|
export * from './filter-provider';
|
||||||
export * from './formula';
|
export * from './formula';
|
||||||
|
export * from './hooks';
|
||||||
export * from './i18n';
|
export * from './i18n';
|
||||||
export * from './icon';
|
export * from './icon';
|
||||||
export * from './plugin-manager';
|
export * from './plugin-manager';
|
||||||
@ -23,10 +24,9 @@ export * from './record-provider';
|
|||||||
export * from './route-switch';
|
export * from './route-switch';
|
||||||
export * from './schema-component';
|
export * from './schema-component';
|
||||||
export * from './schema-initializer';
|
export * from './schema-initializer';
|
||||||
|
export * from './schema-items';
|
||||||
export * from './schema-settings';
|
export * from './schema-settings';
|
||||||
export * from './schema-templates';
|
export * from './schema-templates';
|
||||||
export * from './schema-items';
|
|
||||||
export * from './settings-form';
|
|
||||||
export * from './system-settings';
|
export * from './system-settings';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
export * from './hooks';
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import React, { useEffect, useMemo, useState, useCallback, MouseEventHandler } from 'react';
|
import { DeleteOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
import { useAPIClient, useRequest } from '../api-client';
|
import { css } from '@emotion/css';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Card,
|
Card,
|
||||||
message,
|
|
||||||
Modal,
|
Modal,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Spin,
|
Spin,
|
||||||
@ -13,14 +12,15 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { css } from '@emotion/css';
|
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DeleteOutlined, SettingOutlined } from '@ant-design/icons';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useParseMarkdown } from '../schema-component/antd/markdown/util';
|
|
||||||
import type { IPluginData } from '.';
|
import type { IPluginData } from '.';
|
||||||
|
import { useAPIClient, useRequest } from '../api-client';
|
||||||
|
import { useParseMarkdown } from '../schema-component/antd/markdown/util';
|
||||||
|
|
||||||
interface PluginDocumentProps {
|
interface PluginDocumentProps {
|
||||||
path: string;
|
path: string;
|
||||||
@ -163,7 +163,7 @@ function PluginDetail(props: IPluginDetail) {
|
|||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
{plugin?.description && <div className={'plugin-desc'}>{plugin?.description}</div>}
|
{plugin?.description && <div className={'plugin-desc'}>{plugin?.description}</div>}
|
||||||
<Tabs items={items}></Tabs>
|
<Tabs items={items} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { ApiOutlined, SettingOutlined } from '@ant-design/icons';
|
import { ApiOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
import { Button, Dropdown, Menu, Tooltip } from 'antd';
|
import { Button, Dropdown, MenuProps, Tooltip } from 'antd';
|
||||||
import React, { useContext, useState } from 'react';
|
import _ from 'lodash';
|
||||||
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useACLRoleContext } from '../acl/ACLProvider';
|
import { useACLRoleContext } from '../acl/ACLProvider';
|
||||||
import { ActionContextProvider, useCompile } from '../schema-component';
|
import { ActionContextProvider, useCompile } from '../schema-component';
|
||||||
import { getPluginsTabs, SettingsCenterContext } from './index';
|
import { SettingsCenterContext, getPluginsTabs } from './index';
|
||||||
|
|
||||||
export const PluginManagerLink = () => {
|
export const PluginManagerLink = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -23,7 +24,7 @@ export const PluginManagerLink = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBookmarkTabs = (data) => {
|
const getBookmarkTabs = _.memoize((data) => {
|
||||||
const bookmarkTabs = [];
|
const bookmarkTabs = [];
|
||||||
data.forEach((plugin) => {
|
data.forEach((plugin) => {
|
||||||
const tabs = plugin.tabs;
|
const tabs = plugin.tabs;
|
||||||
@ -32,7 +33,7 @@ const getBookmarkTabs = (data) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
return bookmarkTabs;
|
return bookmarkTabs;
|
||||||
};
|
});
|
||||||
export const SettingsCenterDropdown = () => {
|
export const SettingsCenterDropdown = () => {
|
||||||
const { snippets = [] } = useACLRoleContext();
|
const { snippets = [] } = useACLRoleContext();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
@ -42,27 +43,28 @@ export const SettingsCenterDropdown = () => {
|
|||||||
const itemData = useContext(SettingsCenterContext);
|
const itemData = useContext(SettingsCenterContext);
|
||||||
const pluginsTabs = getPluginsTabs(itemData, snippets);
|
const pluginsTabs = getPluginsTabs(itemData, snippets);
|
||||||
const bookmarkTabs = getBookmarkTabs(pluginsTabs);
|
const bookmarkTabs = getBookmarkTabs(pluginsTabs);
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
|
return {
|
||||||
|
items: [
|
||||||
|
...bookmarkTabs.map((tab) => ({
|
||||||
|
key: `/admin/settings/${tab.path}`,
|
||||||
|
label: compile(tab.title),
|
||||||
|
})),
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: '/admin/settings',
|
||||||
|
label: t('All plugin settings'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onClick({ key }) {
|
||||||
|
navigate(key);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, [bookmarkTabs]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<Dropdown
|
<Dropdown placement="bottom" menu={menu}>
|
||||||
placement="bottom"
|
|
||||||
menu={{
|
|
||||||
items: [
|
|
||||||
...bookmarkTabs.map((tab) => ({
|
|
||||||
key: `/admin/settings/${tab.path}`,
|
|
||||||
label: compile(tab.title),
|
|
||||||
})),
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
key: '/admin/settings',
|
|
||||||
label: t('All plugin settings'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onClick({ key }) {
|
|
||||||
navigate(key);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
icon={<SettingOutlined />}
|
icon={<SettingOutlined />}
|
||||||
// title={t('All plugin settings')}
|
// title={t('All plugin settings')}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
import { PageHeader } from '@ant-design/pro-layout';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Layout, Menu, PageHeader, Result, Spin, Tabs } from 'antd';
|
import { Layout, Menu, Result, Spin, Tabs } from 'antd';
|
||||||
import { sortBy } from 'lodash';
|
import _, { sortBy } from 'lodash';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Navigate, useNavigate, useParams } from 'react-router-dom';
|
import { Navigate, useNavigate, useParams } from 'react-router-dom';
|
||||||
@ -126,6 +127,10 @@ const PluginList = (props) => {
|
|||||||
return snippets.includes('pm') ? (
|
return snippets.includes('pm') ? (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'white',
|
||||||
|
paddingBottom: 0,
|
||||||
|
}}
|
||||||
ghost={false}
|
ghost={false}
|
||||||
title={t('Plugin manager')}
|
title={t('Plugin manager')}
|
||||||
footer={
|
footer={
|
||||||
@ -134,11 +139,21 @@ const PluginList = (props) => {
|
|||||||
onChange={(activeKey) => {
|
onChange={(activeKey) => {
|
||||||
navigate(`/admin/pm/list/${activeKey}`);
|
navigate(`/admin/pm/list/${activeKey}`);
|
||||||
}}
|
}}
|
||||||
>
|
items={[
|
||||||
<Tabs.TabPane tab={t('Local')} key={'local'} />
|
{
|
||||||
<Tabs.TabPane tab={t('Built-in')} key={'built-in'} />
|
key: 'local',
|
||||||
<Tabs.TabPane tab={t('Marketplace')} key={'marketplace'} />
|
label: t('Local'),
|
||||||
</Tabs>
|
},
|
||||||
|
{
|
||||||
|
key: 'built-in',
|
||||||
|
label: t('Built-in'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'marketplace',
|
||||||
|
label: t('Marketplace'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div className={'m24'} style={{ margin: 24, display: 'flex', flexFlow: 'row wrap' }}>
|
<div className={'m24'} style={{ margin: 24, display: 'flex', flexFlow: 'row wrap' }}>
|
||||||
@ -202,7 +217,7 @@ const settings = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPluginsTabs = (items, snippets) => {
|
export const getPluginsTabs = _.memoize((items, snippets) => {
|
||||||
const pluginsTabs = Object.keys(items).map((plugin) => {
|
const pluginsTabs = Object.keys(items).map((plugin) => {
|
||||||
const tabsObj = items[plugin].tabs;
|
const tabsObj = items[plugin].tabs;
|
||||||
const tabs = sortBy(
|
const tabs = sortBy(
|
||||||
@ -223,7 +238,7 @@ export const getPluginsTabs = (items, snippets) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
return sortBy(pluginsTabs, (o) => !o.isAllow);
|
return sortBy(pluginsTabs, (o) => !o.isAllow);
|
||||||
};
|
});
|
||||||
|
|
||||||
const SettingsCenter = (props) => {
|
const SettingsCenter = (props) => {
|
||||||
const { snippets = [] } = useACLRoleContext();
|
const { snippets = [] } = useACLRoleContext();
|
||||||
@ -296,6 +311,7 @@ const SettingsCenter = (props) => {
|
|||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
{aclPluginTabCheck && (
|
{aclPluginTabCheck && (
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
style={{ backgroundColor: 'white', paddingBottom: 0 }}
|
||||||
ghost={false}
|
ghost={false}
|
||||||
title={compile(items[pluginName]?.title)}
|
title={compile(items[pluginName]?.title)}
|
||||||
footer={
|
footer={
|
||||||
@ -304,11 +320,16 @@ const SettingsCenter = (props) => {
|
|||||||
onChange={(activeKey) => {
|
onChange={(activeKey) => {
|
||||||
navigate(`/admin/settings/${pluginName}/${activeKey}`);
|
navigate(`/admin/settings/${pluginName}/${activeKey}`);
|
||||||
}}
|
}}
|
||||||
>
|
items={plugin.tabs?.map((tab) => {
|
||||||
{plugin.tabs?.map((tab) => {
|
if (!tab.isAllow) {
|
||||||
return tab.isAllow && <Tabs.TabPane tab={compile(tab?.title)} key={tab.key} />;
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: compile(tab?.title),
|
||||||
|
key: tab.key,
|
||||||
|
};
|
||||||
})}
|
})}
|
||||||
</Tabs>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { connect, ISchema, mapProps, useField, useFieldSchema } from '@formily/react';
|
import { connect, ISchema, mapProps, useField, useFieldSchema } from '@formily/react';
|
||||||
import { isValid, uid } from '@formily/shared';
|
import { isValid, uid } from '@formily/shared';
|
||||||
import { Tree as AntdTree, Menu } from 'antd';
|
import { Tree as AntdTree } from 'antd';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -46,7 +46,11 @@ const MenuGroup = (props) => {
|
|||||||
) {
|
) {
|
||||||
return <>{props.children}</>;
|
return <>{props.children}</>;
|
||||||
}
|
}
|
||||||
return <Menu.ItemGroup title={`${t('Customize')} > ${actionTitles[actionType]}`}>{props.children}</Menu.ItemGroup>;
|
return (
|
||||||
|
<SchemaSettings.ItemGroup title={`${t('Customize')} > ${actionTitles[actionType]}`}>
|
||||||
|
{props.children}
|
||||||
|
</SchemaSettings.ItemGroup>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ActionDesigner = (props) => {
|
export const ActionDesigner = (props) => {
|
||||||
@ -54,7 +58,7 @@ export const ActionDesigner = (props) => {
|
|||||||
const field = useField();
|
const field = useField();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { name } = useCollection();
|
const { name } = useCollection();
|
||||||
const { getChildrenCollections, getCollection, getCollectionField } = useCollectionManager();
|
const { getChildrenCollections } = useCollectionManager();
|
||||||
const { dn } = useDesignable();
|
const { dn } = useDesignable();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isAction = useLinkageAction();
|
const isAction = useLinkageAction();
|
||||||
|
@ -36,7 +36,7 @@ export const ActionDrawer: ComposedActionDrawer = observer(
|
|||||||
{...others}
|
{...others}
|
||||||
{...drawerProps}
|
{...drawerProps}
|
||||||
{...modalProps}
|
{...modalProps}
|
||||||
style={{
|
rootStyle={{
|
||||||
...drawerProps?.style,
|
...drawerProps?.style,
|
||||||
...others?.style,
|
...others?.style,
|
||||||
}}
|
}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { cx } from '@emotion/css';
|
import { cx } from '@emotion/css';
|
||||||
import { observer, RecursionField, useFieldSchema } from '@formily/react';
|
import { RecursionField, observer, useFieldSchema } from '@formily/react';
|
||||||
import { Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
import React, { CSSProperties, useContext } from 'react';
|
import React, { CSSProperties, useContext } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
@ -46,7 +46,7 @@ export const DeleteEvent = observer(
|
|||||||
return createPortal(
|
return createPortal(
|
||||||
<Modal
|
<Modal
|
||||||
title={cron ? t('Delete events') : null}
|
title={cron ? t('Delete events') : null}
|
||||||
visible={visible}
|
open={visible}
|
||||||
onCancel={() => setVisible(false)}
|
onCancel={() => setVisible(false)}
|
||||||
onOk={() => onOk()}
|
onOk={() => onOk()}
|
||||||
confirmLoading={loading}
|
confirmLoading={loading}
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import { useFieldSchema, useField, ISchema } from '@formily/react';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { ArrayItems } from '@formily/antd';
|
import { ArrayItems } from '@formily/antd';
|
||||||
|
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Slider } from 'antd';
|
import { Slider } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCollection, useCollectionFilterOptions, useSortFields } from '../../../collection-manager';
|
import { useCollection, useCollectionFilterOptions, useSortFields } from '../../../collection-manager';
|
||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||||
import { useSchemaTemplate } from '../../../schema-templates';
|
import { useSchemaTemplate } from '../../../schema-templates';
|
||||||
|
import { SchemaComponentOptions } from '../../core';
|
||||||
import { useDesignable } from '../../hooks';
|
import { useDesignable } from '../../hooks';
|
||||||
import { removeNullCondition } from '../filter';
|
import { removeNullCondition } from '../filter';
|
||||||
import { FilterDynamicComponent } from '../table-v2/FilterDynamicComponent';
|
import { FilterDynamicComponent } from '../table-v2/FilterDynamicComponent';
|
||||||
import { SchemaComponentOptions } from '../../core';
|
|
||||||
import { defaultColumnCount, gridSizes, pageSizeOptions, screenSizeMaps, screenSizeTitleMaps } from './options';
|
import { defaultColumnCount, gridSizes, pageSizeOptions, screenSizeMaps, screenSizeTitleMaps } from './options';
|
||||||
Slider;
|
|
||||||
|
|
||||||
const columnCountMarks = [1, 2, 3, 4, 6, 8, 12, 24].reduce((obj, cur) => {
|
const columnCountMarks = [1, 2, 3, 4, 6, 8, 12, 24].reduce((obj, cur) => {
|
||||||
obj[cur] = cur;
|
obj[cur] = cur;
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import {
|
import {
|
||||||
|
FieldContext,
|
||||||
observer,
|
observer,
|
||||||
RecursionField,
|
RecursionField,
|
||||||
Schema,
|
SchemaContext,
|
||||||
SchemaExpressionScopeContext,
|
SchemaExpressionScopeContext,
|
||||||
useField,
|
useField,
|
||||||
useFieldSchema,
|
useFieldSchema,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { Menu as AntdMenu } from 'antd';
|
import { error } from '@nocobase/utils/client';
|
||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
import { Menu as AntdMenu, MenuProps } from 'antd';
|
||||||
|
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { createDesignable, DndContext, SortableItem, useDesignable, useDesigner } from '../..';
|
import { createDesignable, DndContext, SortableItem, useDesignable, useDesigner } from '../..';
|
||||||
import { Icon, useAPIClient, useSchemaInitializer } from '../../../';
|
import { Icon, useAPIClient, useSchemaInitializer } from '../../../';
|
||||||
|
import { useCollectMenuItems, useMenuItem } from '../../../hooks/useMenuItem';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
import { MenuDesigner } from './Menu.Designer';
|
import { MenuDesigner } from './Menu.Designer';
|
||||||
import { findKeysByUid, findMenuItem } from './util';
|
import { findKeysByUid, findMenuItem } from './util';
|
||||||
@ -115,7 +118,7 @@ const designerCss = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const antdMenuClass = css`
|
const headerMenuClass = css`
|
||||||
.ant-menu-item:hover {
|
.ant-menu-item:hover {
|
||||||
> .ant-menu-title-content > div {
|
> .ant-menu-title-content > div {
|
||||||
.general-schema-designer {
|
.general-schema-designer {
|
||||||
@ -125,6 +128,45 @@ const antdMenuClass = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const sideMenuClass = css`
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
.ant-menu-item {
|
||||||
|
> .ant-menu-title-content {
|
||||||
|
margin-left: -24px;
|
||||||
|
margin-right: -16px;
|
||||||
|
padding: 0 16px 0 24px;
|
||||||
|
> div {
|
||||||
|
> .general-schema-designer {
|
||||||
|
right: 6px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-menu-submenu-title {
|
||||||
|
.ant-menu-title-content {
|
||||||
|
margin-left: -24px;
|
||||||
|
margin-right: -34px;
|
||||||
|
padding: 0 34px 0 24px;
|
||||||
|
> div {
|
||||||
|
> .general-schema-designer {
|
||||||
|
right: 6px !important;
|
||||||
|
}
|
||||||
|
> span.anticon {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const menuItemClass = css`
|
||||||
|
:active {
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
type ComposedMenu = React.FC<any> & {
|
type ComposedMenu = React.FC<any> & {
|
||||||
Item?: React.FC<any>;
|
Item?: React.FC<any>;
|
||||||
URL?: React.FC<any>;
|
URL?: React.FC<any>;
|
||||||
@ -132,6 +174,158 @@ type ComposedMenu = React.FC<any> & {
|
|||||||
Designer?: React.FC<any>;
|
Designer?: React.FC<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const HeaderMenu = ({
|
||||||
|
others,
|
||||||
|
schema,
|
||||||
|
mode,
|
||||||
|
onSelect,
|
||||||
|
setLoading,
|
||||||
|
setDefaultSelectedKeys,
|
||||||
|
defaultSelectedKeys,
|
||||||
|
defaultOpenKeys,
|
||||||
|
selectedKeys,
|
||||||
|
designable,
|
||||||
|
render,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
|
const items = useMemo(() => {
|
||||||
|
const designerBtn = {
|
||||||
|
key: 'x-designer-button',
|
||||||
|
disabled: true,
|
||||||
|
style: { padding: '0 8px', order: 9999 },
|
||||||
|
label: render({ style: { background: 'none' } }),
|
||||||
|
notdelete: true,
|
||||||
|
};
|
||||||
|
const result = getMenuItems(() => {
|
||||||
|
return children;
|
||||||
|
});
|
||||||
|
if (designable) {
|
||||||
|
result.push(designerBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [children, designable]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Component />
|
||||||
|
<AntdMenu
|
||||||
|
{...others}
|
||||||
|
className={headerMenuClass}
|
||||||
|
onSelect={(info: any) => {
|
||||||
|
const s = schema.properties[info.key];
|
||||||
|
if (mode === 'mix') {
|
||||||
|
if (s['x-component'] !== 'Menu.SubMenu') {
|
||||||
|
onSelect && onSelect(info);
|
||||||
|
} else {
|
||||||
|
const menuItemSchema = findMenuItem(s);
|
||||||
|
if (!menuItemSchema) {
|
||||||
|
return onSelect && onSelect(info);
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
setLoading(true);
|
||||||
|
const keys = findKeysByUid(schema, menuItemSchema['x-uid']);
|
||||||
|
setDefaultSelectedKeys(keys);
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, 100);
|
||||||
|
onSelect &&
|
||||||
|
onSelect({
|
||||||
|
key: menuItemSchema.name,
|
||||||
|
item: {
|
||||||
|
props: {
|
||||||
|
schema: menuItemSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onSelect && onSelect(info);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
mode={mode === 'mix' ? 'horizontal' : mode}
|
||||||
|
defaultOpenKeys={defaultOpenKeys}
|
||||||
|
defaultSelectedKeys={defaultSelectedKeys}
|
||||||
|
selectedKeys={selectedKeys}
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SideMenu = ({
|
||||||
|
loading,
|
||||||
|
mode,
|
||||||
|
sideMenuSchema,
|
||||||
|
sideMenuRef,
|
||||||
|
defaultOpenKeys,
|
||||||
|
defaultSelectedKeys,
|
||||||
|
onSelect,
|
||||||
|
render,
|
||||||
|
t,
|
||||||
|
api,
|
||||||
|
refresh,
|
||||||
|
designable,
|
||||||
|
}) => {
|
||||||
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
const result = getMenuItems(() => {
|
||||||
|
return <RecursionField schema={sideMenuSchema} onlyRenderProperties />;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (designable) {
|
||||||
|
result.push({
|
||||||
|
key: 'x-designer-button',
|
||||||
|
disabled: true,
|
||||||
|
label: render({
|
||||||
|
insert: (s) => {
|
||||||
|
const dn = createDesignable({
|
||||||
|
t,
|
||||||
|
api,
|
||||||
|
refresh,
|
||||||
|
current: sideMenuSchema,
|
||||||
|
});
|
||||||
|
dn.loadAPIClientEvents();
|
||||||
|
dn.insertAdjacent('beforeEnd', s);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
order: 1,
|
||||||
|
notdelete: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [render, sideMenuSchema, designable, loading]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
mode === 'mix' &&
|
||||||
|
sideMenuSchema?.['x-component'] === 'Menu.SubMenu' &&
|
||||||
|
sideMenuRef?.current?.firstChild &&
|
||||||
|
createPortal(
|
||||||
|
<MenuModeContext.Provider value={'inline'}>
|
||||||
|
<Component />
|
||||||
|
<AntdMenu
|
||||||
|
mode={'inline'}
|
||||||
|
defaultOpenKeys={defaultOpenKeys}
|
||||||
|
defaultSelectedKeys={defaultSelectedKeys}
|
||||||
|
onSelect={(info) => {
|
||||||
|
onSelect && onSelect(info);
|
||||||
|
}}
|
||||||
|
className={sideMenuClass}
|
||||||
|
items={items as MenuProps['items']}
|
||||||
|
/>
|
||||||
|
</MenuModeContext.Provider>,
|
||||||
|
sideMenuRef.current.firstChild,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const MenuModeContext = createContext(null);
|
const MenuModeContext = createContext(null);
|
||||||
|
|
||||||
MenuModeContext.displayName = 'MenuModeContext';
|
MenuModeContext.displayName = 'MenuModeContext';
|
||||||
@ -159,6 +353,7 @@ export const Menu: ComposedMenu = observer(
|
|||||||
sideMenuRefScopeKey,
|
sideMenuRefScopeKey,
|
||||||
defaultSelectedKeys: dSelectedKeys,
|
defaultSelectedKeys: dSelectedKeys,
|
||||||
defaultOpenKeys: dOpenKeys,
|
defaultOpenKeys: dOpenKeys,
|
||||||
|
children,
|
||||||
...others
|
...others
|
||||||
} = useProps(props);
|
} = useProps(props);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -185,8 +380,17 @@ export const Menu: ComposedMenu = observer(
|
|||||||
}
|
}
|
||||||
return dOpenKeys;
|
return dOpenKeys;
|
||||||
});
|
});
|
||||||
const [sideMenuSchema, setSideMenuSchema] = useState<Schema>(() => {
|
|
||||||
const key = defaultSelectedKeys?.[0] || null;
|
const sideMenuSchema = useMemo(() => {
|
||||||
|
let key;
|
||||||
|
|
||||||
|
if (selectedUid) {
|
||||||
|
const keys = findKeysByUid(schema, selectedUid);
|
||||||
|
key = keys?.[0] || null;
|
||||||
|
} else {
|
||||||
|
key = defaultSelectedKeys?.[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode === 'mix' && key) {
|
if (mode === 'mix' && key) {
|
||||||
const s = schema.properties?.[key];
|
const s = schema.properties?.[key];
|
||||||
if (s['x-component'] === 'Menu.SubMenu') {
|
if (s['x-component'] === 'Menu.SubMenu') {
|
||||||
@ -194,7 +398,8 @@ export const Menu: ComposedMenu = observer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
}, [defaultSelectedKeys, mode, schema, selectedUid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedUid) {
|
if (!selectedUid) {
|
||||||
setSelectedKeys(undefined);
|
setSelectedKeys(undefined);
|
||||||
@ -206,17 +411,6 @@ export const Menu: ComposedMenu = observer(
|
|||||||
if (['inline', 'mix'].includes(mode)) {
|
if (['inline', 'mix'].includes(mode)) {
|
||||||
setDefaultOpenKeys(dOpenKeys || keys);
|
setDefaultOpenKeys(dOpenKeys || keys);
|
||||||
}
|
}
|
||||||
const key = keys?.[0] || null;
|
|
||||||
if (mode === 'mix') {
|
|
||||||
if (key) {
|
|
||||||
const s = schema.properties?.[key];
|
|
||||||
if (s['x-component'] === 'Menu.SubMenu') {
|
|
||||||
setSideMenuSchema(s);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setSideMenuSchema(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [selectedUid]);
|
}, [selectedUid]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (['inline', 'mix'].includes(mode)) {
|
if (['inline', 'mix'].includes(mode)) {
|
||||||
@ -228,118 +422,35 @@ export const Menu: ComposedMenu = observer(
|
|||||||
<DndContext>
|
<DndContext>
|
||||||
<MenuItemDesignerContext.Provider value={Designer}>
|
<MenuItemDesignerContext.Provider value={Designer}>
|
||||||
<MenuModeContext.Provider value={mode}>
|
<MenuModeContext.Provider value={mode}>
|
||||||
<AntdMenu
|
<HeaderMenu
|
||||||
{...others}
|
others={others}
|
||||||
className={antdMenuClass}
|
schema={schema}
|
||||||
onSelect={(info: any) => {
|
mode={mode}
|
||||||
const s = schema.properties[info.key];
|
onSelect={onSelect}
|
||||||
if (mode === 'mix') {
|
setLoading={setLoading}
|
||||||
setSideMenuSchema(s);
|
setDefaultSelectedKeys={setDefaultSelectedKeys}
|
||||||
if (s['x-component'] !== 'Menu.SubMenu') {
|
defaultSelectedKeys={defaultSelectedKeys}
|
||||||
onSelect && onSelect(info);
|
defaultOpenKeys={defaultOpenKeys}
|
||||||
} else {
|
selectedKeys={selectedKeys}
|
||||||
const menuItemSchema = findMenuItem(s);
|
designable={designable}
|
||||||
if (!menuItemSchema) {
|
render={render}
|
||||||
return;
|
>
|
||||||
}
|
{children}
|
||||||
// TODO
|
</HeaderMenu>
|
||||||
setLoading(true);
|
<SideMenu
|
||||||
const keys = findKeysByUid(schema, menuItemSchema['x-uid']);
|
loading={loading}
|
||||||
setDefaultSelectedKeys(keys);
|
mode={mode}
|
||||||
setTimeout(() => {
|
sideMenuSchema={sideMenuSchema}
|
||||||
setLoading(false);
|
sideMenuRef={sideMenuRef}
|
||||||
}, 100);
|
|
||||||
onSelect &&
|
|
||||||
onSelect({
|
|
||||||
key: menuItemSchema.name,
|
|
||||||
item: {
|
|
||||||
props: {
|
|
||||||
schema: menuItemSchema,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onSelect && onSelect(info);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
mode={mode === 'mix' ? 'horizontal' : mode}
|
|
||||||
defaultOpenKeys={defaultOpenKeys}
|
defaultOpenKeys={defaultOpenKeys}
|
||||||
defaultSelectedKeys={defaultSelectedKeys}
|
defaultSelectedKeys={defaultSelectedKeys}
|
||||||
selectedKeys={selectedKeys}
|
onSelect={onSelect}
|
||||||
>
|
render={render}
|
||||||
{designable && (
|
t={t}
|
||||||
<AntdMenu.Item disabled key="x-designer-button" style={{ padding: '0 8px', order: 9999 }}>
|
api={api}
|
||||||
{render({ style: { background: 'none' } })}
|
refresh={refresh}
|
||||||
</AntdMenu.Item>
|
designable={designable}
|
||||||
)}
|
/>
|
||||||
{props.children}
|
|
||||||
</AntdMenu>
|
|
||||||
{loading
|
|
||||||
? null
|
|
||||||
: mode === 'mix' &&
|
|
||||||
sideMenuSchema?.['x-component'] === 'Menu.SubMenu' &&
|
|
||||||
sideMenuRef?.current?.firstChild &&
|
|
||||||
createPortal(
|
|
||||||
<MenuModeContext.Provider value={'inline'}>
|
|
||||||
<AntdMenu
|
|
||||||
mode={'inline'}
|
|
||||||
defaultOpenKeys={defaultOpenKeys}
|
|
||||||
defaultSelectedKeys={defaultSelectedKeys}
|
|
||||||
onSelect={(info) => {
|
|
||||||
onSelect && onSelect(info);
|
|
||||||
}}
|
|
||||||
className={css`
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
.ant-menu-item {
|
|
||||||
> .ant-menu-title-content {
|
|
||||||
margin-left: -24px;
|
|
||||||
margin-right: -16px;
|
|
||||||
padding: 0 16px 0 24px;
|
|
||||||
> div {
|
|
||||||
> .general-schema-designer {
|
|
||||||
right: 6px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-menu-submenu-title {
|
|
||||||
.ant-menu-title-content {
|
|
||||||
margin-left: -24px;
|
|
||||||
margin-right: -34px;
|
|
||||||
padding: 0 34px 0 24px;
|
|
||||||
> div {
|
|
||||||
> .general-schema-designer {
|
|
||||||
right: 6px !important;
|
|
||||||
}
|
|
||||||
> span.anticon {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
|
||||||
{render({
|
|
||||||
style: { margin: 8 },
|
|
||||||
insert: (s) => {
|
|
||||||
const dn = createDesignable({
|
|
||||||
t,
|
|
||||||
api,
|
|
||||||
refresh,
|
|
||||||
current: sideMenuSchema,
|
|
||||||
});
|
|
||||||
dn.loadAPIClientEvents();
|
|
||||||
dn.insertAdjacent('beforeEnd', s);
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</AntdMenu>
|
|
||||||
</MenuModeContext.Provider>,
|
|
||||||
sideMenuRef.current.firstChild,
|
|
||||||
)}
|
|
||||||
</MenuModeContext.Provider>
|
</MenuModeContext.Provider>
|
||||||
</MenuItemDesignerContext.Provider>
|
</MenuItemDesignerContext.Provider>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
@ -350,116 +461,149 @@ export const Menu: ComposedMenu = observer(
|
|||||||
|
|
||||||
Menu.Item = observer(
|
Menu.Item = observer(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { icon, ...others } = props;
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const { icon, children, ...others } = props;
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const Designer = useContext(MenuItemDesignerContext);
|
const Designer = useContext(MenuItemDesignerContext);
|
||||||
return (
|
const item = useMemo(() => {
|
||||||
<AntdMenu.Item
|
return {
|
||||||
{...others}
|
...others,
|
||||||
className={css`
|
className: menuItemClass,
|
||||||
:active {
|
key: schema.name,
|
||||||
background: inherit;
|
eventKey: schema.name,
|
||||||
}
|
schema,
|
||||||
`}
|
label: (
|
||||||
key={schema.name}
|
<SchemaContext.Provider value={schema}>
|
||||||
eventKey={schema.name}
|
<FieldContext.Provider value={field}>
|
||||||
schema={schema}
|
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||||
>
|
<Icon type={icon} />
|
||||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
<span
|
||||||
<Icon type={icon} />
|
style={{
|
||||||
<span
|
overflow: 'hidden',
|
||||||
className={css`
|
textOverflow: 'ellipsis',
|
||||||
overflow: hidden;
|
display: 'inline-block',
|
||||||
text-overflow: ellipsis;
|
width: '100%',
|
||||||
display: inline-block;
|
verticalAlign: 'middle',
|
||||||
width: 100%;
|
}}
|
||||||
vertical-align: middle;
|
>
|
||||||
`}
|
{field.title}
|
||||||
>
|
</span>
|
||||||
{field.title}
|
{Designer && <Designer />}
|
||||||
</span>
|
</SortableItem>
|
||||||
{Designer && <Designer />}
|
</FieldContext.Provider>
|
||||||
</SortableItem>
|
</SchemaContext.Provider>
|
||||||
</AntdMenu.Item>
|
),
|
||||||
);
|
};
|
||||||
|
}, [field.title, icon, schema]);
|
||||||
|
|
||||||
|
if (!pushMenuItem) {
|
||||||
|
error('Menu.Item must be wrapped by GetMenuItemsContext.Provider');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushMenuItem(item);
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
{ displayName: 'Menu.Item' },
|
{ displayName: 'Menu.Item' },
|
||||||
);
|
);
|
||||||
|
|
||||||
Menu.URL = observer(
|
Menu.URL = observer(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { icon, ...others } = props;
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const { icon, children, ...others } = props;
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const Designer = useContext(MenuItemDesignerContext);
|
const Designer = useContext(MenuItemDesignerContext);
|
||||||
return (
|
|
||||||
<AntdMenu.Item
|
if (!pushMenuItem) {
|
||||||
{...others}
|
error('Menu.URL must be wrapped by GetMenuItemsContext.Provider');
|
||||||
className={css`
|
return null;
|
||||||
:active {
|
}
|
||||||
background: inherit;
|
|
||||||
}
|
const item = useMemo(() => {
|
||||||
`}
|
return {
|
||||||
key={schema.name}
|
...others,
|
||||||
eventKey={schema.name}
|
className: menuItemClass,
|
||||||
schema={schema}
|
key: schema.name,
|
||||||
onClick={() => {
|
eventKey: schema.name,
|
||||||
|
schema,
|
||||||
|
onClick: () => {
|
||||||
window.open(props.href, '_blank');
|
window.open(props.href, '_blank');
|
||||||
}}
|
},
|
||||||
>
|
label: (
|
||||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
<SchemaContext.Provider value={schema}>
|
||||||
<Icon type={icon} />
|
<FieldContext.Provider value={field}>
|
||||||
<span
|
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||||
className={css`
|
<Icon type={icon} />
|
||||||
overflow: hidden;
|
<span
|
||||||
text-overflow: ellipsis;
|
style={{
|
||||||
display: inline-block;
|
overflow: 'hidden',
|
||||||
width: 100%;
|
textOverflow: 'ellipsis',
|
||||||
vertical-align: middle;
|
display: 'inline-block',
|
||||||
`}
|
width: '100%',
|
||||||
>
|
verticalAlign: 'middle',
|
||||||
{field.title}
|
}}
|
||||||
</span>
|
>
|
||||||
{Designer && <Designer />}
|
{field.title}
|
||||||
</SortableItem>
|
</span>
|
||||||
</AntdMenu.Item>
|
{Designer && <Designer />}
|
||||||
);
|
</SortableItem>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</SchemaContext.Provider>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [field.title, icon, props.href, schema]);
|
||||||
|
|
||||||
|
pushMenuItem(item);
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
{ displayName: 'MenuURL' },
|
{ displayName: 'MenuURL' },
|
||||||
);
|
);
|
||||||
|
|
||||||
Menu.SubMenu = observer(
|
Menu.SubMenu = observer(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { icon, ...others } = props;
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const { icon, children, ...others } = props;
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const mode = useContext(MenuModeContext);
|
const mode = useContext(MenuModeContext);
|
||||||
const Designer = useContext(MenuItemDesignerContext);
|
const Designer = useContext(MenuItemDesignerContext);
|
||||||
|
const submenu = useMemo(() => {
|
||||||
|
return {
|
||||||
|
...others,
|
||||||
|
className: menuItemClass,
|
||||||
|
key: schema.name,
|
||||||
|
eventKey: schema.name,
|
||||||
|
label: (
|
||||||
|
<SchemaContext.Provider value={schema}>
|
||||||
|
<FieldContext.Provider value={field}>
|
||||||
|
<SortableItem className={subMenuDesignerCss} removeParentsIfNoChildren={false}>
|
||||||
|
<Icon type={icon} />
|
||||||
|
{field.title}
|
||||||
|
{Designer && <Designer />}
|
||||||
|
</SortableItem>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</SchemaContext.Provider>
|
||||||
|
),
|
||||||
|
children: getMenuItems(() => {
|
||||||
|
return <RecursionField schema={schema} onlyRenderProperties />;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}, [field.title, icon, schema, children]);
|
||||||
|
|
||||||
|
if (!pushMenuItem) {
|
||||||
|
error('Menu.SubMenu must be wrapped by GetMenuItemsContext.Provider');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode === 'mix') {
|
if (mode === 'mix') {
|
||||||
return <Menu.Item {...props} />;
|
return <Menu.Item {...props} />;
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<AntdMenu.SubMenu
|
pushMenuItem(submenu);
|
||||||
{...others}
|
return <Component />;
|
||||||
className={css`
|
|
||||||
:active {
|
|
||||||
background: inherit;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
key={schema.name}
|
|
||||||
eventKey={schema.name}
|
|
||||||
title={
|
|
||||||
<SortableItem className={subMenuDesignerCss} removeParentsIfNoChildren={false}>
|
|
||||||
<Icon type={icon} />
|
|
||||||
{field.title}
|
|
||||||
{Designer && <Designer />}
|
|
||||||
</SortableItem>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<RecursionField schema={schema} onlyRenderProperties />
|
|
||||||
</AntdMenu.SubMenu>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
{ displayName: 'Menu.SubMenu' },
|
{ displayName: 'Menu.SubMenu' },
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { FormDialog, FormLayout } from '@formily/antd';
|
import { FormDialog, FormLayout } from '@formily/antd';
|
||||||
import { Schema, SchemaOptionsContext, useFieldSchema } from '@formily/react';
|
import { Schema, SchemaOptionsContext, useFieldSchema } from '@formily/react';
|
||||||
import { Button, PageHeader as AntdPageHeader, Spin, Tabs } from 'antd';
|
import { Button, Spin, Tabs } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { ErrorBoundary } from 'react-error-boundary';
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
@ -119,6 +120,25 @@ const pageWithFixedBlockCss = classNames([
|
|||||||
`,
|
`,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const pageHeaderCss = css`
|
||||||
|
background-color: white;
|
||||||
|
&.ant-page-header-has-footer {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
.ant-page-header-heading-left {
|
||||||
|
/* margin: 0; */
|
||||||
|
}
|
||||||
|
.ant-page-header-footer {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const height0 = css`
|
||||||
|
font-size: 0;
|
||||||
|
height: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
export const Page = (props) => {
|
export const Page = (props) => {
|
||||||
const { children, ...others } = props;
|
const { children, ...others } = props;
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
@ -156,6 +176,8 @@ export const Page = (props) => {
|
|||||||
const handleErrors = (error) => {
|
const handleErrors = (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pageHeaderTitle = hidePageTitle ? undefined : fieldSchema.title || compile(title);
|
||||||
return (
|
return (
|
||||||
<FilterBlockProvider>
|
<FilterBlockProvider>
|
||||||
<div className={pageDesignerCss}>
|
<div className={pageDesignerCss}>
|
||||||
@ -167,19 +189,10 @@ export const Page = (props) => {
|
|||||||
>
|
>
|
||||||
{!disablePageHeader && (
|
{!disablePageHeader && (
|
||||||
<AntdPageHeader
|
<AntdPageHeader
|
||||||
className={css`
|
className={classNames(pageHeaderCss, pageHeaderTitle ? '' : height0)}
|
||||||
&.has-footer {
|
|
||||||
padding-top: 12px;
|
|
||||||
.ant-page-header-heading-left {
|
|
||||||
/* margin: 0; */
|
|
||||||
}
|
|
||||||
.ant-page-header-footer {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
ghost={false}
|
ghost={false}
|
||||||
title={hidePageTitle ? undefined : fieldSchema.title || compile(title)}
|
// 如果标题为空的时候会导致 PageHeader 不渲染,所以这里设置一个空白字符,然后再设置高度为 0
|
||||||
|
title={pageHeaderTitle || ' '}
|
||||||
{...others}
|
{...others}
|
||||||
footer={
|
footer={
|
||||||
enablePageTabs && (
|
enablePageTabs && (
|
||||||
@ -247,26 +260,23 @@ export const Page = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
items={fieldSchema.mapProperties((schema) => {
|
||||||
{fieldSchema.mapProperties((schema) => {
|
return {
|
||||||
return (
|
label: (
|
||||||
<Tabs.TabPane
|
<SortableItem
|
||||||
tab={
|
id={schema.name as string}
|
||||||
<SortableItem
|
schema={schema}
|
||||||
id={schema.name as string}
|
className={classNames('nb-action-link', designerCss, props.className)}
|
||||||
schema={schema}
|
>
|
||||||
className={classNames('nb-action-link', designerCss, props.className)}
|
{schema['x-icon'] && <Icon style={{ marginRight: 8 }} type={schema['x-icon']} />}
|
||||||
>
|
<span>{schema.title || t('Unnamed')}</span>
|
||||||
{schema['x-icon'] && <Icon style={{ marginRight: 8 }} type={schema['x-icon']} />}
|
<PageTabDesigner schema={schema} />
|
||||||
<span>{schema.title || t('Unnamed')}</span>
|
</SortableItem>
|
||||||
<PageTabDesigner schema={schema} />
|
),
|
||||||
</SortableItem>
|
key: schema.name as string,
|
||||||
}
|
};
|
||||||
key={schema.name}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</Tabs>
|
/>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -193,344 +193,347 @@ const useValidator = (validator: (value: any) => string) => {
|
|||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Table: any = observer((props: any) => {
|
export const Table: any = observer(
|
||||||
const { pagination: pagination1, useProps, onChange, ...others1 } = props;
|
(props: any) => {
|
||||||
const { pagination: pagination2, onClickRow, ...others2 } = useProps?.() || {};
|
const { pagination: pagination1, useProps, onChange, ...others1 } = props;
|
||||||
const {
|
const { pagination: pagination2, onClickRow, ...others2 } = useProps?.() || {};
|
||||||
dragSort = false,
|
const {
|
||||||
showIndex = true,
|
dragSort = false,
|
||||||
onRowSelectionChange,
|
showIndex = true,
|
||||||
onChange: onTableChange,
|
onRowSelectionChange,
|
||||||
rowSelection,
|
onChange: onTableChange,
|
||||||
rowKey,
|
rowSelection,
|
||||||
required,
|
rowKey,
|
||||||
onExpand,
|
required,
|
||||||
...others
|
onExpand,
|
||||||
} = { ...others1, ...others2 } as any;
|
...others
|
||||||
const field = useArrayField(others);
|
} = { ...others1, ...others2 } as any;
|
||||||
const columns = useTableColumns(others);
|
const field = useArrayField(others);
|
||||||
const schema = useFieldSchema();
|
const columns = useTableColumns(others);
|
||||||
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
const schema = useFieldSchema();
|
||||||
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
||||||
const { expandFlag } = ctx;
|
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
||||||
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => {}));
|
const { expandFlag } = ctx;
|
||||||
const paginationProps = usePaginationProps(pagination1, pagination2);
|
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => {}));
|
||||||
// const requiredValidator = field.required || required;
|
const paginationProps = usePaginationProps(pagination1, pagination2);
|
||||||
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
// const requiredValidator = field.required || required;
|
||||||
const [expandedKeys, setExpandesKeys] = useState([]);
|
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
||||||
const [allIncludesChildren, setAllIncludesChildren] = useState([]);
|
const [expandedKeys, setExpandesKeys] = useState([]);
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>(field?.data?.selectedRowKeys || []);
|
const [allIncludesChildren, setAllIncludesChildren] = useState([]);
|
||||||
const [selectedRow, setSelectedRow] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>(field?.data?.selectedRowKeys || []);
|
||||||
const dataSource = field?.value?.slice?.()?.filter?.(Boolean) || [];
|
const [selectedRow, setSelectedRow] = useState([]);
|
||||||
const isRowSelect = rowSelection?.type !== 'none';
|
const dataSource = field?.value?.slice?.()?.filter?.(Boolean) || [];
|
||||||
|
const isRowSelect = rowSelection?.type !== 'none';
|
||||||
|
|
||||||
let onRow = null,
|
let onRow = null,
|
||||||
highlightRow = '';
|
highlightRow = '';
|
||||||
|
|
||||||
if (onClickRow) {
|
if (onClickRow) {
|
||||||
onRow = (record) => {
|
onRow = (record) => {
|
||||||
return {
|
return {
|
||||||
onClick: () => onClickRow(record, setSelectedRow, selectedRow),
|
onClick: () => onClickRow(record, setSelectedRow, selectedRow),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
highlightRow = css`
|
||||||
highlightRow = css`
|
& > td {
|
||||||
& > td {
|
background-color: #caedff !important;
|
||||||
background-color: #caedff !important;
|
}
|
||||||
}
|
&:hover > td {
|
||||||
&:hover > td {
|
background-color: #caedff !important;
|
||||||
background-color: #caedff !important;
|
}
|
||||||
}
|
`;
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// field.setValidator((value) => {
|
|
||||||
// if (requiredValidator) {
|
|
||||||
// return Array.isArray(value) && value.length > 0 ? null : 'The field value is required';
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
// });
|
|
||||||
// }, [requiredValidator]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (treeTable !== false) {
|
|
||||||
const keys = getIdsWithChildren(field.value?.slice?.());
|
|
||||||
setAllIncludesChildren(keys);
|
|
||||||
}
|
}
|
||||||
}, [field.value]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (expandFlag) {
|
|
||||||
setExpandesKeys(allIncludesChildren);
|
|
||||||
} else {
|
|
||||||
setExpandesKeys([]);
|
|
||||||
}
|
|
||||||
}, [expandFlag, allIncludesChildren]);
|
|
||||||
|
|
||||||
const components = useMemo(() => {
|
// useEffect(() => {
|
||||||
return {
|
// field.setValidator((value) => {
|
||||||
header: {
|
// if (requiredValidator) {
|
||||||
wrapper: (props) => {
|
// return Array.isArray(value) && value.length > 0 ? null : 'The field value is required';
|
||||||
return (
|
// }
|
||||||
<DndContext>
|
// return;
|
||||||
<thead {...props} />
|
// });
|
||||||
</DndContext>
|
// }, [requiredValidator]);
|
||||||
);
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (treeTable !== false) {
|
||||||
|
const keys = getIdsWithChildren(field.value?.slice?.());
|
||||||
|
setAllIncludesChildren(keys);
|
||||||
|
}
|
||||||
|
}, [field.value]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (expandFlag) {
|
||||||
|
setExpandesKeys(allIncludesChildren);
|
||||||
|
} else {
|
||||||
|
setExpandesKeys([]);
|
||||||
|
}
|
||||||
|
}, [expandFlag, allIncludesChildren]);
|
||||||
|
|
||||||
|
const components = useMemo(() => {
|
||||||
|
return {
|
||||||
|
header: {
|
||||||
|
wrapper: (props) => {
|
||||||
|
return (
|
||||||
|
<DndContext>
|
||||||
|
<thead {...props} />
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: (props) => {
|
||||||
|
return (
|
||||||
|
<th
|
||||||
|
{...props}
|
||||||
|
className={cls(
|
||||||
|
props.className,
|
||||||
|
css`
|
||||||
|
max-width: 300px;
|
||||||
|
white-space: nowrap;
|
||||||
|
&:hover .general-schema-designer {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cell: (props) => {
|
body: {
|
||||||
return (
|
wrapper: (props) => {
|
||||||
<th
|
return (
|
||||||
|
<DndContext
|
||||||
|
onDragEnd={(e) => {
|
||||||
|
if (!e.active || !e.over) {
|
||||||
|
console.warn('move cancel');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromIndex = e.active?.data.current?.sortable?.index;
|
||||||
|
const toIndex = e.over?.data.current?.sortable?.index;
|
||||||
|
const from = field.value[fromIndex];
|
||||||
|
const to = field.value[toIndex];
|
||||||
|
field.move(fromIndex, toIndex);
|
||||||
|
onRowDragEnd({ fromIndex, toIndex, from, to });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<tbody {...props} />
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
row: (props) => {
|
||||||
|
return <SortableRow {...props}></SortableRow>;
|
||||||
|
},
|
||||||
|
cell: (props) => (
|
||||||
|
<td
|
||||||
{...props}
|
{...props}
|
||||||
className={cls(
|
className={classNames(
|
||||||
props.className,
|
props.className,
|
||||||
css`
|
css`
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
&:hover .general-schema-designer {
|
.nb-read-pretty-input-number {
|
||||||
display: block;
|
text-align: right;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
),
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
body: {
|
}, [field, onRowDragEnd, dragSort]);
|
||||||
wrapper: (props) => {
|
|
||||||
return (
|
|
||||||
<DndContext
|
|
||||||
onDragEnd={(e) => {
|
|
||||||
if (!e.active || !e.over) {
|
|
||||||
console.warn('move cancel');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fromIndex = e.active?.data.current?.sortable?.index;
|
const defaultRowKey = (record: any) => {
|
||||||
const toIndex = e.over?.data.current?.sortable?.index;
|
return field.value?.indexOf?.(record);
|
||||||
const from = field.value[fromIndex];
|
|
||||||
const to = field.value[toIndex];
|
|
||||||
field.move(fromIndex, toIndex);
|
|
||||||
onRowDragEnd({ fromIndex, toIndex, from, to });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<tbody {...props} />
|
|
||||||
</DndContext>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
row: (props) => {
|
|
||||||
return <SortableRow {...props}></SortableRow>;
|
|
||||||
},
|
|
||||||
cell: (props) => (
|
|
||||||
<td
|
|
||||||
{...props}
|
|
||||||
className={classNames(
|
|
||||||
props.className,
|
|
||||||
css`
|
|
||||||
max-width: 300px;
|
|
||||||
white-space: nowrap;
|
|
||||||
.nb-read-pretty-input-number {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}, [field, onRowDragEnd, dragSort]);
|
|
||||||
|
|
||||||
const defaultRowKey = (record: any) => {
|
const getRowKey = (record: any) => {
|
||||||
return field.value?.indexOf?.(record);
|
if (typeof rowKey === 'string') {
|
||||||
};
|
return record[rowKey]?.toString();
|
||||||
|
} else {
|
||||||
|
return (rowKey ?? defaultRowKey)(record)?.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getRowKey = (record: any) => {
|
const restProps = {
|
||||||
if (typeof rowKey === 'string') {
|
rowSelection: rowSelection
|
||||||
return record[rowKey]?.toString();
|
? {
|
||||||
} else {
|
type: 'checkbox',
|
||||||
return (rowKey ?? defaultRowKey)(record)?.toString();
|
selectedRowKeys: selectedRowKeys,
|
||||||
}
|
onChange(selectedRowKeys: any[], selectedRows: any[]) {
|
||||||
};
|
field.data = field.data || {};
|
||||||
|
field.data.selectedRowKeys = selectedRowKeys;
|
||||||
const restProps = {
|
setSelectedRowKeys(selectedRowKeys);
|
||||||
rowSelection: rowSelection
|
onRowSelectionChange?.(selectedRowKeys, selectedRows);
|
||||||
? {
|
},
|
||||||
type: 'checkbox',
|
renderCell: (checked, record, index, originNode) => {
|
||||||
selectedRowKeys: selectedRowKeys,
|
if (!dragSort && !showIndex) {
|
||||||
onChange(selectedRowKeys: any[], selectedRows: any[]) {
|
return originNode;
|
||||||
field.data = field.data || {};
|
}
|
||||||
field.data.selectedRowKeys = selectedRowKeys;
|
const current = props?.pagination?.current;
|
||||||
setSelectedRowKeys(selectedRowKeys);
|
const pageSize = props?.pagination?.pageSize || 20;
|
||||||
onRowSelectionChange?.(selectedRowKeys, selectedRows);
|
if (current) {
|
||||||
},
|
index = index + (current - 1) * pageSize + 1;
|
||||||
renderCell: (checked, record, index, originNode) => {
|
} else {
|
||||||
if (!dragSort && !showIndex) {
|
index = index + 1;
|
||||||
return originNode;
|
}
|
||||||
}
|
if (record.__index) {
|
||||||
const current = props?.pagination?.current;
|
index = extractIndex(record.__index);
|
||||||
const pageSize = props?.pagination?.pageSize || 20;
|
}
|
||||||
if (current) {
|
return (
|
||||||
index = index + (current - 1) * pageSize + 1;
|
|
||||||
} else {
|
|
||||||
index = index + 1;
|
|
||||||
}
|
|
||||||
if (record.__index) {
|
|
||||||
index = extractIndex(record.__index);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
checked ? 'checked' : null,
|
|
||||||
css`
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
float: left;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
padding-right: 8px;
|
|
||||||
.nb-table-index {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
&:not(.checked) {
|
|
||||||
.nb-table-index {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
[css`
|
|
||||||
&:hover {
|
|
||||||
.nb-table-index {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.nb-origin-node {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`]: isRowSelect,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
float: left;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
|
padding-right: 8px;
|
||||||
|
.nb-table-index {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
&:not(.checked) {
|
||||||
|
.nb-table-index {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
|
{
|
||||||
|
[css`
|
||||||
|
&:hover {
|
||||||
|
.nb-table-index {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.nb-origin-node {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`]: isRowSelect,
|
||||||
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{dragSort && <SortHandle id={getRowKey(record)} />}
|
|
||||||
{showIndex && <TableIndex index={index} />}
|
|
||||||
</div>
|
|
||||||
{isRowSelect && (
|
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'nb-origin-node',
|
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: absolute;
|
position: relative;
|
||||||
right: 50%;
|
display: flex;
|
||||||
transform: translateX(50%);
|
align-items: center;
|
||||||
&:not(.checked) {
|
justify-content: space-evenly;
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{originNode}
|
{dragSort && <SortHandle id={getRowKey(record)} />}
|
||||||
|
{showIndex && <TableIndex index={index} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
{isRowSelect && (
|
||||||
</div>
|
<div
|
||||||
);
|
className={classNames(
|
||||||
},
|
'nb-origin-node',
|
||||||
...rowSelection,
|
checked ? 'checked' : null,
|
||||||
}
|
css`
|
||||||
: undefined,
|
position: absolute;
|
||||||
};
|
right: 50%;
|
||||||
const SortableWrapper = useCallback<React.FC>(
|
transform: translateX(50%);
|
||||||
({ children }) => {
|
&:not(.checked) {
|
||||||
return dragSort
|
display: none;
|
||||||
? React.createElement(SortableContext, {
|
}
|
||||||
items: field.value?.map?.(getRowKey) || [],
|
`,
|
||||||
children: children,
|
)}
|
||||||
})
|
>
|
||||||
: React.createElement(React.Fragment, {
|
{originNode}
|
||||||
children,
|
</div>
|
||||||
});
|
)}
|
||||||
},
|
</div>
|
||||||
[field, dragSort],
|
);
|
||||||
);
|
},
|
||||||
const fieldSchema = useFieldSchema();
|
...rowSelection,
|
||||||
const fixedBlock = fieldSchema?.parent?.['x-decorator-props']?.fixedBlock;
|
}
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
const SortableWrapper = useCallback<React.FC>(
|
||||||
|
({ children }) => {
|
||||||
|
return dragSort
|
||||||
|
? React.createElement(SortableContext, {
|
||||||
|
items: field.value?.map?.(getRowKey) || [],
|
||||||
|
children,
|
||||||
|
})
|
||||||
|
: React.createElement(React.Fragment, {
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[field, dragSort],
|
||||||
|
);
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const fixedBlock = fieldSchema?.parent?.['x-decorator-props']?.fixedBlock;
|
||||||
|
|
||||||
const { height: tableHeight, tableSizeRefCallback } = useTableSize();
|
const { height: tableHeight, tableSizeRefCallback } = useTableSize();
|
||||||
const scroll = useMemo(() => {
|
const scroll = useMemo(() => {
|
||||||
return fixedBlock
|
return fixedBlock
|
||||||
? {
|
? {
|
||||||
x: 'max-content',
|
x: 'max-content',
|
||||||
y: tableHeight,
|
y: tableHeight,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
x: 'max-content',
|
x: 'max-content',
|
||||||
};
|
};
|
||||||
}, [fixedBlock, tableHeight]);
|
}, [fixedBlock, tableHeight]);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css`
|
className={css`
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
.ant-table-wrapper {
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.ant-spin-nested-loading {
|
overflow: hidden;
|
||||||
|
.ant-table-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.ant-spin-container {
|
.ant-spin-nested-loading {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
.ant-spin-container {
|
||||||
flex-direction: column;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.ant-table {
|
||||||
.ant-table {
|
overflow-x: auto;
|
||||||
overflow-x: auto;
|
overflow-y: hidden;
|
||||||
overflow-y: hidden;
|
}
|
||||||
}
|
`}
|
||||||
`}
|
>
|
||||||
>
|
<SortableWrapper>
|
||||||
<SortableWrapper>
|
<AntdTable
|
||||||
<AntdTable
|
ref={tableSizeRefCallback}
|
||||||
ref={tableSizeRefCallback}
|
rowKey={rowKey ?? defaultRowKey}
|
||||||
rowKey={rowKey ?? defaultRowKey}
|
dataSource={dataSource}
|
||||||
dataSource={dataSource}
|
{...others}
|
||||||
{...others}
|
{...restProps}
|
||||||
{...restProps}
|
pagination={paginationProps}
|
||||||
pagination={paginationProps}
|
components={components}
|
||||||
components={components}
|
onChange={(pagination, filters, sorter, extra) => {
|
||||||
onChange={(pagination, filters, sorter, extra) => {
|
onTableChange?.(pagination, filters, sorter, extra);
|
||||||
onTableChange?.(pagination, filters, sorter, extra);
|
}}
|
||||||
}}
|
onRow={onRow}
|
||||||
onRow={onRow}
|
rowClassName={(record) => (selectedRow.includes(record[rowKey]) ? highlightRow : '')}
|
||||||
rowClassName={(record) => (selectedRow.includes(record[rowKey]) ? highlightRow : '')}
|
tableLayout={'auto'}
|
||||||
tableLayout={'auto'}
|
scroll={scroll}
|
||||||
scroll={scroll}
|
columns={columns}
|
||||||
columns={columns}
|
expandable={{
|
||||||
expandable={{
|
onExpand: (flag, record) => {
|
||||||
onExpand: (flag, record) => {
|
const newKeys = flag ? [...expandedKeys, record.id] : expandedKeys.filter((i) => record.id !== i);
|
||||||
const newKeys = flag ? [...expandedKeys, record.id] : expandedKeys.filter((i) => record.id !== i);
|
setExpandesKeys(newKeys);
|
||||||
setExpandesKeys(newKeys);
|
onExpand?.(flag, record);
|
||||||
onExpand?.(flag, record);
|
},
|
||||||
},
|
expandedRowKeys: expandedKeys,
|
||||||
expandedRowKeys: expandedKeys,
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</SortableWrapper>
|
||||||
</SortableWrapper>
|
{field.errors.length > 0 && (
|
||||||
{field.errors.length > 0 && (
|
<div className="ant-formily-item-error-help ant-formily-item-help ant-formily-item-help-enter ant-formily-item-help-enter-active">
|
||||||
<div className="ant-formily-item-error-help ant-formily-item-help ant-formily-item-help-enter ant-formily-item-help-enter-active">
|
{field.errors.map((error) => {
|
||||||
{field.errors.map((error) => {
|
return error.messages.map((message) => <div>{message}</div>);
|
||||||
return error.messages.map((message) => <div>{message}</div>);
|
})}
|
||||||
})}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
},
|
||||||
});
|
{ displayName: 'Table' },
|
||||||
|
);
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||||
import { TabPaneProps, Tabs as AntdTabs, TabsProps } from 'antd';
|
import { Tabs as AntdTabs, TabPaneProps, TabsProps } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Icon } from '../../../icon';
|
import { Icon } from '../../../icon';
|
||||||
import { useSchemaInitializer } from '../../../schema-initializer';
|
import { useSchemaInitializer } from '../../../schema-initializer';
|
||||||
import { DndContext, SortableItem } from '../../common';
|
import { DndContext, SortableItem } from '../../common';
|
||||||
|
import { useDesignable } from '../../hooks';
|
||||||
import { useDesigner } from '../../hooks/useDesigner';
|
import { useDesigner } from '../../hooks/useDesigner';
|
||||||
import { useTabsContext } from './context';
|
import { useTabsContext } from './context';
|
||||||
import { TabsDesigner } from './Tabs.Designer';
|
import { TabsDesigner } from './Tabs.Designer';
|
||||||
import { useDesignable } from '../../hooks';
|
|
||||||
|
|
||||||
export const Tabs: any = observer(
|
export const Tabs: any = observer(
|
||||||
(props: TabsProps) => {
|
(props: TabsProps) => {
|
||||||
@ -19,20 +19,33 @@ export const Tabs: any = observer(
|
|||||||
const contextProps = useTabsContext();
|
const contextProps = useTabsContext();
|
||||||
const { PaneRoot = React.Fragment as React.FC<any> } = contextProps;
|
const { PaneRoot = React.Fragment as React.FC<any> } = contextProps;
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
const result = fieldSchema.mapProperties((schema, key: string) => {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
label: <RecursionField name={key} schema={schema} onlyRenderSelf />,
|
||||||
|
children: (
|
||||||
|
<PaneRoot active={key === contextProps.activeKey}>
|
||||||
|
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
||||||
|
</PaneRoot>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (designable) {
|
||||||
|
result.push({
|
||||||
|
key: 'designer',
|
||||||
|
label: render(),
|
||||||
|
children: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [fieldSchema.mapProperties((s, key) => key).join()]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext>
|
<DndContext>
|
||||||
<AntdTabs {...contextProps} style={props.style}>
|
<AntdTabs {...contextProps} style={props.style} items={items} />
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
|
||||||
return (
|
|
||||||
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
|
|
||||||
<PaneRoot active={key === contextProps.activeKey}>
|
|
||||||
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
|
||||||
</PaneRoot>
|
|
||||||
</AntdTabs.TabPane>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{designable && <AntdTabs.TabPane tab={render()} />}
|
|
||||||
</AntdTabs>
|
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@ type Composed = React.FC<UploadProps> & {
|
|||||||
|
|
||||||
export const ReadPretty: Composed = () => null;
|
export const ReadPretty: Composed = () => null;
|
||||||
|
|
||||||
ReadPretty.File = (props: UploadProps) => {
|
ReadPretty.File = function File(props: UploadProps) {
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const value = isString(field.value) ? record : field.value;
|
const value = isString(field.value) ? record : field.value;
|
||||||
@ -44,7 +44,7 @@ ReadPretty.File = (props: UploadProps) => {
|
|||||||
// }
|
// }
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className={'ant-upload-list-picture-card-container'}>
|
<div key={file.name} className={'ant-upload-list-picture-card-container'}>
|
||||||
<div className="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card">
|
<div className="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card">
|
||||||
<div className={'ant-upload-list-item-info'}>
|
<div className={'ant-upload-list-item-info'}>
|
||||||
<span className="ant-upload-span">
|
<span className="ant-upload-span">
|
||||||
@ -114,6 +114,7 @@ ReadPretty.File = (props: UploadProps) => {
|
|||||||
imageTitle={images[photoIndex]?.title}
|
imageTitle={images[photoIndex]?.title}
|
||||||
toolbarButtons={[
|
toolbarButtons={[
|
||||||
<button
|
<button
|
||||||
|
key={'download'}
|
||||||
style={{ fontSize: 22, background: 'none', lineHeight: 1 }}
|
style={{ fontSize: 22, background: 'none', lineHeight: 1 }}
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Zoom in"
|
aria-label="Zoom in"
|
||||||
@ -135,10 +136,10 @@ ReadPretty.File = (props: UploadProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ReadPretty.Upload = (props) => {
|
ReadPretty.Upload = function Upload(props) {
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
return (field.value || []).map((item) => (
|
return (field.value || []).map((item) => (
|
||||||
<div>
|
<div key={item.name}>
|
||||||
{item.url ? (
|
{item.url ? (
|
||||||
<a target={'_blank'} href={item.url} rel="noreferrer">
|
<a target={'_blank'} href={item.url} rel="noreferrer">
|
||||||
{item.name}
|
{item.name}
|
||||||
|
@ -65,7 +65,7 @@ export function VariableSelect(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
dropdownClassName={css`
|
popupClassName={css`
|
||||||
.ant-cascader-menu {
|
.ant-cascader-menu {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { ISchema, observer, useForm } from '@formily/react';
|
import { ISchema, observer, useForm } from '@formily/react';
|
||||||
import { Button, Dropdown, Menu, Switch } from 'antd';
|
import { error, isString } from '@nocobase/utils/client';
|
||||||
|
import { Button, Dropdown, MenuProps, Switch } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||||
|
import { useCollectMenuItem, useMenuItem } from '../hooks/useMenuItem';
|
||||||
import { Icon } from '../icon';
|
import { Icon } from '../icon';
|
||||||
import { SchemaComponent, useActionContext } from '../schema-component';
|
import { SchemaComponent, useActionContext } from '../schema-component';
|
||||||
import { useCompile, useDesignable } from '../schema-component/hooks';
|
import { useCompile, useDesignable } from '../schema-component/hooks';
|
||||||
@ -14,10 +16,22 @@ import {
|
|||||||
SchemaInitializerItemProps,
|
SchemaInitializerItemProps,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
|
const overlayClassName = css`
|
||||||
|
.ant-dropdown-menu-item-group-list {
|
||||||
|
max-height: 40vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const defaultWrap = (s: ISchema) => s;
|
const defaultWrap = (s: ISchema) => s;
|
||||||
|
|
||||||
export const SchemaInitializerItemContext = createContext(null);
|
export const SchemaInitializerItemContext = createContext(null);
|
||||||
export const SchemaInitializerButtonContext = createContext<any>({});
|
export const SchemaInitializerButtonContext = createContext<{
|
||||||
|
visible?: boolean;
|
||||||
|
setVisible?: (v: boolean) => void;
|
||||||
|
searchValue?: string;
|
||||||
|
setSearchValue?: (v: string) => void;
|
||||||
|
}>({});
|
||||||
|
|
||||||
export const SchemaInitializer = () => null;
|
export const SchemaInitializer = () => null;
|
||||||
|
|
||||||
@ -41,27 +55,72 @@ SchemaInitializer.Button = observer(
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { insertAdjacent, findComponent, designable } = useDesignable();
|
const { insertAdjacent, findComponent, designable } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { Component: CollectionComponent, getMenuItem, clean } = useMenuItem();
|
||||||
|
const [shouldRender, setShouldRender] = useState(false);
|
||||||
|
const [searchValue, setSearchValue] = useState('');
|
||||||
|
|
||||||
|
if (!designable && props.designable !== true) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonDom = (
|
||||||
|
<div
|
||||||
|
style={{ display: 'inline-block' }}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
setShouldRender(true);
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{component ? (
|
||||||
|
component
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
type={'dashed'}
|
||||||
|
style={{
|
||||||
|
borderColor: '#f18b62',
|
||||||
|
color: '#f18b62',
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
{...others}
|
||||||
|
icon={typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
|
||||||
|
>
|
||||||
|
{compile(props.children || props.title)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
if (!shouldRender || !items.length) {
|
||||||
|
return buttonDom;
|
||||||
|
}
|
||||||
|
|
||||||
const insertSchema = (schema) => {
|
const insertSchema = (schema) => {
|
||||||
if (props.insert) {
|
if (insert) {
|
||||||
props.insert(wrap(schema));
|
insert(wrap(schema));
|
||||||
} else {
|
} else {
|
||||||
insertAdjacent(insertPosition, wrap(schema), { onSuccess });
|
insertAdjacent(insertPosition, wrap(schema), { onSuccess });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderItems = (items: any) => {
|
const renderItems = (items: any) => {
|
||||||
return items
|
return items
|
||||||
.filter((v) => {
|
.filter((v: any) => {
|
||||||
return v && (v?.visible ? v.visible() : true);
|
return v && (v?.visible ? v.visible() : true);
|
||||||
})
|
})
|
||||||
?.map((item, indexA) => {
|
?.map((item: any, indexA: number) => {
|
||||||
if (item.type === 'divider') {
|
if (item.type === 'divider') {
|
||||||
return <Menu.Divider key={item.key || `item-${indexA}`} />;
|
return { type: 'divider', key: item.key || `item-${indexA}` };
|
||||||
}
|
}
|
||||||
if (item.type === 'item' && item.component) {
|
if (item.type === 'item' && item.component) {
|
||||||
const Component = findComponent(item.component);
|
const Component = findComponent(item.component);
|
||||||
item.key = `${item.key || item.title}-${indexA}`;
|
if (!Component) {
|
||||||
return (
|
error(`SchemaInitializer: component "${item.component}" not found`);
|
||||||
Component && (
|
return null;
|
||||||
|
}
|
||||||
|
if (!item.key) {
|
||||||
|
item.key = `${item.title}-${indexA}`;
|
||||||
|
}
|
||||||
|
return getMenuItem(() => {
|
||||||
|
return (
|
||||||
<SchemaInitializerItemContext.Provider
|
<SchemaInitializerItemContext.Provider
|
||||||
key={item.key}
|
key={item.key}
|
||||||
value={{
|
value={{
|
||||||
@ -80,75 +139,61 @@ SchemaInitializer.Button = observer(
|
|||||||
insert={insertSchema}
|
insert={insertSchema}
|
||||||
/>
|
/>
|
||||||
</SchemaInitializerItemContext.Provider>
|
</SchemaInitializerItemContext.Provider>
|
||||||
)
|
);
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
if (item.type === 'itemGroup') {
|
if (item.type === 'itemGroup') {
|
||||||
|
const label = compile(item.title);
|
||||||
return (
|
return (
|
||||||
!!item.children?.length && (
|
!!item.children?.length && {
|
||||||
<Menu.ItemGroup key={item.key || `item-group-${indexA}`} title={compile(item.title)}>
|
type: 'group',
|
||||||
{renderItems(item.children)}
|
key: item.key || `item-group-${indexA}`,
|
||||||
</Menu.ItemGroup>
|
label,
|
||||||
)
|
title: label,
|
||||||
|
children: renderItems(item.children),
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (item.type === 'subMenu') {
|
if (item.type === 'subMenu') {
|
||||||
|
const label = compile(item.title);
|
||||||
return (
|
return (
|
||||||
!!item.children?.length && (
|
!!item.children?.length && {
|
||||||
<Menu.SubMenu
|
key: item.key || `item-group-${indexA}`,
|
||||||
key={item.key || `item-group-${indexA}`}
|
label,
|
||||||
title={compile(item.title)}
|
title: label,
|
||||||
popupClassName={menuItemGroupCss}
|
popupClassName: menuItemGroupCss,
|
||||||
>
|
children: renderItems(item.children),
|
||||||
{renderItems(item.children)}
|
}
|
||||||
</Menu.SubMenu>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonDom = (
|
clean();
|
||||||
<Button
|
const menuItems = renderItems(items);
|
||||||
type={'dashed'}
|
|
||||||
style={{
|
|
||||||
borderColor: '#f18b62',
|
|
||||||
color: '#f18b62',
|
|
||||||
...style,
|
|
||||||
}}
|
|
||||||
{...others}
|
|
||||||
icon={typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
|
|
||||||
>
|
|
||||||
{compile(props.children || props.title)}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
if (!items.length) {
|
|
||||||
return buttonDom;
|
|
||||||
}
|
|
||||||
const menu = <Menu style={{ maxHeight: '60vh', overflowY: 'auto' }}>{renderItems(items)}</Menu>;
|
|
||||||
if (!designable && props.designable !== true) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<SchemaInitializerButtonContext.Provider value={{ visible, setVisible }}>
|
<SchemaInitializerButtonContext.Provider value={{ visible, setVisible, searchValue, setSearchValue }}>
|
||||||
|
<CollectionComponent />
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className={classNames('nb-schema-initializer-button')}
|
className={classNames('nb-schema-initializer-button')}
|
||||||
openClassName={`nb-schema-initializer-button-open`}
|
openClassName={`nb-schema-initializer-button-open`}
|
||||||
overlayClassName={classNames(
|
overlayClassName={classNames('nb-schema-initializer-button-overlay', overlayClassName)}
|
||||||
'nb-schema-initializer-button-overlay',
|
|
||||||
css`
|
|
||||||
.ant-dropdown-menu-item-group-list {
|
|
||||||
max-height: 40vh;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
)}
|
|
||||||
open={visible}
|
open={visible}
|
||||||
onOpenChange={(visible) => {
|
onOpenChange={() => {
|
||||||
setVisible(visible);
|
// 如果不清空输入框的值,那么下次打开的时候会出现上次输入的值
|
||||||
|
setSearchValue('');
|
||||||
|
setShouldRender(false);
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
menu={{
|
||||||
|
style: {
|
||||||
|
maxHeight: '60vh',
|
||||||
|
overflowY: 'auto',
|
||||||
|
},
|
||||||
|
items: menuItems,
|
||||||
}}
|
}}
|
||||||
{...dropdown}
|
{...dropdown}
|
||||||
overlay={menu}
|
|
||||||
>
|
>
|
||||||
{component ? component : buttonDom}
|
{component ? component : buttonDom}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@ -158,10 +203,17 @@ SchemaInitializer.Button = observer(
|
|||||||
{ displayName: 'SchemaInitializer.Button' },
|
{ displayName: 'SchemaInitializer.Button' },
|
||||||
);
|
);
|
||||||
|
|
||||||
SchemaInitializer.Item = (props: SchemaInitializerItemProps) => {
|
SchemaInitializer.Item = function Item(props: SchemaInitializerItemProps) {
|
||||||
const { index, info } = useContext(SchemaInitializerItemContext);
|
const { info } = useContext(SchemaInitializerItemContext);
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { eventKey, items = [], children = info?.title, icon, onClick, ...others } = props;
|
const { items = [], children = info?.title, icon, onClick } = props;
|
||||||
|
const { collectMenuItem } = useCollectMenuItem();
|
||||||
|
|
||||||
|
if (!collectMenuItem) {
|
||||||
|
error('SchemaInitializer.Item: collectMenuItem is undefined, please check the context');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (items?.length > 0) {
|
if (items?.length > 0) {
|
||||||
const renderMenuItem = (items: SchemaInitializerItemOptions[]) => {
|
const renderMenuItem = (items: SchemaInitializerItemOptions[]) => {
|
||||||
if (!items?.length) {
|
if (!items?.length) {
|
||||||
@ -169,77 +221,70 @@ SchemaInitializer.Item = (props: SchemaInitializerItemProps) => {
|
|||||||
}
|
}
|
||||||
return items.map((item, indexA) => {
|
return items.map((item, indexA) => {
|
||||||
if (item.type === 'divider') {
|
if (item.type === 'divider') {
|
||||||
return <Menu.Divider key={`divider-${indexA}`} />;
|
return { type: 'divider', key: `divider-${indexA}` };
|
||||||
}
|
}
|
||||||
if (item.type === 'itemGroup') {
|
if (item.type === 'itemGroup') {
|
||||||
return (
|
const label = compile(item.title);
|
||||||
<Menu.ItemGroup
|
return {
|
||||||
// @ts-ignore
|
type: 'group',
|
||||||
eventKey={item.key || `item-group-${indexA}`}
|
key: item.key || `item-group-${indexA}`,
|
||||||
key={item.key || `item-group-${indexA}`}
|
label,
|
||||||
title={compile(item.title)}
|
title: label,
|
||||||
className={menuItemGroupCss}
|
className: menuItemGroupCss,
|
||||||
>
|
children: renderMenuItem(item.children),
|
||||||
{renderMenuItem(item.children)}
|
} as MenuProps['items'][0];
|
||||||
</Menu.ItemGroup>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (item.type === 'subMenu') {
|
if (item.type === 'subMenu') {
|
||||||
return (
|
const label = compile(item.title);
|
||||||
<Menu.SubMenu
|
return {
|
||||||
// @ts-ignore
|
key: item.key || `sub-menu-${indexA}`,
|
||||||
eventKey={item.key || `sub-menu-${indexA}`}
|
label,
|
||||||
key={item.key || `sub-menu-${indexA}`}
|
title: label,
|
||||||
title={compile(item.title)}
|
children: renderMenuItem(item.children),
|
||||||
>
|
};
|
||||||
{renderMenuItem(item.children)}
|
|
||||||
</Menu.SubMenu>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return (
|
const label = compile(item.title);
|
||||||
<Menu.Item
|
return {
|
||||||
eventKey={item.key}
|
key: item.key || `${info.key}-${item.title}-${indexA}`,
|
||||||
key={item.key}
|
label,
|
||||||
onClick={(info) => {
|
title: label,
|
||||||
item?.clearKeywords?.();
|
onClick: (info) => {
|
||||||
if (item.onClick) {
|
item?.clearKeywords?.();
|
||||||
item.onClick({ ...info, item });
|
if (item.onClick) {
|
||||||
} else {
|
item.onClick({ ...info, item });
|
||||||
onClick({ ...info, item });
|
} else {
|
||||||
}
|
onClick({ ...info, item });
|
||||||
}}
|
}
|
||||||
>
|
},
|
||||||
{compile(item.title)}
|
};
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
|
||||||
<Menu.SubMenu
|
const item = {
|
||||||
// @ts-ignore
|
key: info.key,
|
||||||
eventKey={eventKey ? `${eventKey}-${index}` : info.key}
|
label: isString(children) ? compile(children) : children,
|
||||||
key={info.key}
|
icon: typeof icon === 'string' ? <Icon type={icon as string} /> : icon,
|
||||||
title={compile(children)}
|
children: renderMenuItem(items),
|
||||||
icon={typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
|
};
|
||||||
>
|
|
||||||
{renderMenuItem(items)}
|
collectMenuItem(item);
|
||||||
</Menu.SubMenu>
|
return null;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<Menu.Item
|
const label = isString(children) ? compile(children) : children;
|
||||||
// {...others}
|
const item = {
|
||||||
key={info.key}
|
key: info.key,
|
||||||
eventKey={eventKey ? `${eventKey}-${index}` : info.key}
|
label,
|
||||||
icon={typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
|
title: label,
|
||||||
onClick={(opts) => {
|
icon: typeof icon === 'string' ? <Icon type={icon as string} /> : icon,
|
||||||
info?.clearKeywords?.();
|
onClick: (opts) => {
|
||||||
onClick({ ...opts, item: info });
|
info?.clearKeywords?.();
|
||||||
}}
|
onClick({ ...opts, item: info });
|
||||||
>
|
},
|
||||||
{compile(children)}
|
};
|
||||||
</Menu.Item>
|
|
||||||
);
|
collectMenuItem(item);
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaInitializer.itemWrap = (component?: SchemaInitializerItemComponent) => {
|
SchemaInitializer.itemWrap = (component?: SchemaInitializerItemComponent) => {
|
||||||
@ -253,11 +298,13 @@ interface SchemaInitializerActionModalProps {
|
|||||||
onSubmit?: (values: any) => void;
|
onSubmit?: (values: any) => void;
|
||||||
buttonText?: any;
|
buttonText?: any;
|
||||||
}
|
}
|
||||||
SchemaInitializer.ActionModal = (props: SchemaInitializerActionModalProps) => {
|
SchemaInitializer.ActionModal = function ActionModal(props: SchemaInitializerActionModalProps) {
|
||||||
const { title, schema, buttonText, onCancel, onSubmit } = props;
|
const { title, schema, buttonText, onCancel, onSubmit } = props;
|
||||||
|
|
||||||
const useCancelAction = useCallback(() => {
|
const useCancelAction = useCallback(() => {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const ctx = useActionContext();
|
const ctx = useActionContext();
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
@ -269,7 +316,9 @@ SchemaInitializer.ActionModal = (props: SchemaInitializerActionModalProps) => {
|
|||||||
}, [onCancel]);
|
}, [onCancel]);
|
||||||
|
|
||||||
const useSubmitAction = useCallback(() => {
|
const useSubmitAction = useCallback(() => {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const ctx = useActionContext();
|
const ctx = useActionContext();
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
import { Divider, Input } from 'antd';
|
import { Divider, Input } from 'antd';
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCollectionManager } from '../collection-manager';
|
|
||||||
|
|
||||||
export const SelectCollection = ({ value, onChange, setSelected }) => {
|
export const SelectCollection = ({ value: outValue, onChange }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { collections } = useCollectionManager();
|
const [value, setValue] = useState<string>(outValue);
|
||||||
|
|
||||||
|
// 之所以要增加个内部的 value 是为了防止用户输入过快时造成卡顿的问题
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(outValue);
|
||||||
|
}, [outValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: 210 }}>
|
<div style={{ width: 210 }}>
|
||||||
<Input
|
<Input
|
||||||
|
autoFocus
|
||||||
allowClear
|
allowClear
|
||||||
style={{ padding: '0 4px 6px' }}
|
style={{ padding: '0 4px 6px' }}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
placeholder={t('Search and select collection')}
|
placeholder={t('Search and select collection')}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const names = collections
|
|
||||||
.filter((collection) => {
|
|
||||||
if (!collection.title) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return collection.title.toUpperCase().includes(e.target.value.toUpperCase());
|
|
||||||
})
|
|
||||||
.map((item) => item.name);
|
|
||||||
setSelected(names);
|
|
||||||
onChange(e.target.value);
|
onChange(e.target.value);
|
||||||
|
setValue(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Divider style={{ margin: 0 }} />
|
<Divider style={{ margin: 0 }} />
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { MenuOutlined } from '@ant-design/icons';
|
import { MenuOutlined } from '@ant-design/icons';
|
||||||
import { ISchema, useFieldSchema } from '@formily/react';
|
import { ISchema, useFieldSchema } from '@formily/react';
|
||||||
|
import _ from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SchemaInitializer, SchemaSettings } from '../..';
|
import { SchemaInitializer, SchemaSettings } from '../..';
|
||||||
import { useAPIClient } from '../../api-client';
|
import { useAPIClient } from '../../api-client';
|
||||||
import { useCollection } from '../../collection-manager';
|
import { useCollection } from '../../collection-manager';
|
||||||
import { createDesignable, useDesignable } from '../../schema-component';
|
import { createDesignable, useDesignable } from '../../schema-component';
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
export const Resizable = (props) => {
|
export const Resizable = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -32,7 +32,7 @@ export const TableActionInitializers = {
|
|||||||
skipScopeCheck: true,
|
skipScopeCheck: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
visible: () => {
|
visible: function useVisible() {
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
return collection.template !== 'view' && collection.template !== 'file';
|
return collection.template !== 'view' && collection.template !== 'file';
|
||||||
},
|
},
|
||||||
@ -45,7 +45,7 @@ export const TableActionInitializers = {
|
|||||||
'x-align': 'right',
|
'x-align': 'right',
|
||||||
'x-decorator': 'ACLActionProvider',
|
'x-decorator': 'ACLActionProvider',
|
||||||
},
|
},
|
||||||
visible: () => {
|
visible: function useVisible() {
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
return (collection as any).template !== 'view';
|
return (collection as any).template !== 'view';
|
||||||
},
|
},
|
||||||
@ -65,7 +65,7 @@ export const TableActionInitializers = {
|
|||||||
schema: {
|
schema: {
|
||||||
'x-align': 'right',
|
'x-align': 'right',
|
||||||
},
|
},
|
||||||
visible: () => {
|
visible: function useVisible() {
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
||||||
@ -76,7 +76,7 @@ export const TableActionInitializers = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
visible: () => {
|
visible: function useVisible() {
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
return (collection as any).template !== 'view';
|
return (collection as any).template !== 'view';
|
||||||
},
|
},
|
||||||
@ -157,7 +157,7 @@ export const TableActionInitializers = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
visible: () => {
|
visible: function useVisible() {
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
return (collection as any).template !== 'view';
|
return (collection as any).template !== 'view';
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { DownOutlined, PlusOutlined } from '@ant-design/icons';
|
import { DownOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
import { RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Button, Dropdown, Menu } from 'antd';
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useDesignable } from '../../';
|
import { useDesignable } from '../../';
|
||||||
import { useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider';
|
import { useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider';
|
||||||
import { CollectionProvider, useCollection, useCollectionManager } from '../../collection-manager';
|
import { CollectionProvider, useCollection, useCollectionManager } from '../../collection-manager';
|
||||||
@ -131,44 +131,44 @@ export const CreateAction = observer(
|
|||||||
const componentType = field.componentProps.type || 'primary';
|
const componentType = field.componentProps.type || 'primary';
|
||||||
const { getChildrenCollections } = useCollectionManager();
|
const { getChildrenCollections } = useCollectionManager();
|
||||||
const totalChildCollections = getChildrenCollections(collection.name);
|
const totalChildCollections = getChildrenCollections(collection.name);
|
||||||
const inheritsCollections = enableChildren
|
const inheritsCollections = useMemo(() => {
|
||||||
.map((k) => {
|
return enableChildren
|
||||||
if (!k) {
|
.map((k) => {
|
||||||
return;
|
if (!k) {
|
||||||
}
|
return;
|
||||||
const childCollection = totalChildCollections.find((j) => j.name === k.collection);
|
}
|
||||||
if (!childCollection) {
|
const childCollection = totalChildCollections.find((j) => j.name === k.collection);
|
||||||
return;
|
if (!childCollection) {
|
||||||
}
|
return;
|
||||||
return {
|
}
|
||||||
...childCollection,
|
return {
|
||||||
title: k.title || childCollection.title,
|
...childCollection,
|
||||||
};
|
title: k.title || childCollection.title,
|
||||||
})
|
};
|
||||||
.filter((v) => {
|
})
|
||||||
return v && actionAclCheck(`${v.name}:create`);
|
.filter((v) => {
|
||||||
});
|
return v && actionAclCheck(`${v.name}:create`);
|
||||||
|
});
|
||||||
|
}, [enableChildren, totalChildCollections]);
|
||||||
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
|
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
|
||||||
const values = useRecord();
|
const values = useRecord();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
const icon = props.icon || <PlusOutlined />;
|
const icon = props.icon || <PlusOutlined />;
|
||||||
const menu = (
|
const menuItems = useMemo<MenuProps['items']>(() => {
|
||||||
<Menu>
|
return inheritsCollections.map((option) => ({
|
||||||
{inheritsCollections.map((option) => {
|
key: option.name,
|
||||||
return (
|
label: compile(option.title),
|
||||||
<Menu.Item
|
onClick: () => onClick?.(option.name),
|
||||||
key={option.name}
|
}));
|
||||||
onClick={(info) => {
|
}, [inheritsCollections, onClick]);
|
||||||
onClick?.(option.name);
|
|
||||||
}}
|
const menu = useMemo<MenuProps>(() => {
|
||||||
>
|
return {
|
||||||
{compile(option.title)}
|
items: menuItems,
|
||||||
</Menu.Item>
|
};
|
||||||
);
|
}, [menuItems]);
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
field.linkageProperty = {};
|
field.linkageProperty = {};
|
||||||
linkageRules
|
linkageRules
|
||||||
@ -190,7 +190,7 @@ export const CreateAction = observer(
|
|||||||
leftButton,
|
leftButton,
|
||||||
React.cloneElement(rightButton as React.ReactElement<any, string>, { loading: false }),
|
React.cloneElement(rightButton as React.ReactElement<any, string>, { loading: false }),
|
||||||
]}
|
]}
|
||||||
overlay={menu}
|
menu={menu}
|
||||||
onClick={(info) => {
|
onClick={(info) => {
|
||||||
onClick?.(collection.name);
|
onClick?.(collection.name);
|
||||||
}}
|
}}
|
||||||
@ -199,7 +199,7 @@ export const CreateAction = observer(
|
|||||||
{props.children}
|
{props.children}
|
||||||
</Dropdown.Button>
|
</Dropdown.Button>
|
||||||
) : (
|
) : (
|
||||||
<Dropdown overlay={menu}>
|
<Dropdown menu={menu}>
|
||||||
{
|
{
|
||||||
<Button icon={icon} type={componentType}>
|
<Button icon={icon} type={componentType}>
|
||||||
{props.children} <DownOutlined />
|
{props.children} <DownOutlined />
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React, { useContext } from 'react';
|
|
||||||
import { FormDialog, FormLayout } from '@formily/antd';
|
|
||||||
import { FormOutlined } from '@ant-design/icons';
|
import { FormOutlined } from '@ant-design/icons';
|
||||||
|
import { FormDialog, FormLayout } from '@formily/antd';
|
||||||
import { SchemaOptionsContext } from '@formily/react';
|
import { SchemaOptionsContext } from '@formily/react';
|
||||||
|
import React, { useContext } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { useCollection, useCollectionManager } from '../../collection-manager';
|
import { useCollectionManager } from '../../collection-manager';
|
||||||
import { SchemaComponent, SchemaComponentOptions, useCompile } from '../../schema-component';
|
import { SchemaComponent, SchemaComponentOptions } from '../../schema-component';
|
||||||
import { createCalendarBlockSchema } from '../utils';
|
import { createCalendarBlockSchema } from '../utils';
|
||||||
import { DataBlockInitializer } from './DataBlockInitializer';
|
import { DataBlockInitializer } from './DataBlockInitializer';
|
||||||
import { CascaderProps } from 'antd';
|
|
||||||
|
|
||||||
export const CalendarBlockInitializer = (props) => {
|
export const CalendarBlockInitializer = (props) => {
|
||||||
const { insert } = props;
|
const { insert } = props;
|
||||||
|
@ -3,8 +3,6 @@ import React from 'react';
|
|||||||
|
|
||||||
import { SchemaInitializer } from '..';
|
import { SchemaInitializer } from '..';
|
||||||
import { useCurrentSchema } from '../utils';
|
import { useCurrentSchema } from '../utils';
|
||||||
import { useBlockRequestContext } from '../../block-provider';
|
|
||||||
import { useCollection } from '../../collection-manager';
|
|
||||||
|
|
||||||
export const InitializerWithSwitch = (props) => {
|
export const InitializerWithSwitch = (props) => {
|
||||||
const { type, schema, item, insert, remove: passInRemove } = props;
|
const { type, schema, item, insert, remove: passInRemove } = props;
|
||||||
@ -14,6 +12,7 @@ export const InitializerWithSwitch = (props) => {
|
|||||||
item.find,
|
item.find,
|
||||||
passInRemove ?? item.remove,
|
passInRemove ?? item.remove,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaInitializer.SwitchItem
|
<SchemaInitializer.SwitchItem
|
||||||
checked={exists}
|
checked={exists}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { ISchema, Schema, useFieldSchema, useForm } from '@formily/react';
|
import { ISchema, Schema, useFieldSchema, useForm } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import React, { useContext, useMemo, useState } from 'react';
|
import { error } from '@nocobase/utils/client';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { BlockRequestContext, SchemaInitializerItemOptions } from '../';
|
import { BlockRequestContext, SchemaInitializerButtonContext, SchemaInitializerItemOptions } from '../';
|
||||||
import { FieldOptions, useCollection, useCollectionManager } from '../collection-manager';
|
import { FieldOptions, useCollection, useCollectionManager } from '../collection-manager';
|
||||||
import { isAssocField } from '../filter-provider/utils';
|
import { isAssocField } from '../filter-provider/utils';
|
||||||
import { useActionContext, useDesignable } from '../schema-component';
|
import { useActionContext, useDesignable } from '../schema-component';
|
||||||
@ -807,24 +809,28 @@ export const useCollectionDataSourceItems = (componentName) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { collections, getCollectionFields } = useCollectionManager();
|
const { collections, getCollectionFields } = useCollectionManager();
|
||||||
const { getTemplatesByCollection } = useSchemaTemplateManager();
|
const { getTemplatesByCollection } = useSchemaTemplateManager();
|
||||||
const [selected, setSelected] = useState([]);
|
const { searchValue, setSearchValue } = useContext(SchemaInitializerButtonContext);
|
||||||
const [value, onChange] = useState(null);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
const onChange = useCallback(_.debounce(setSearchValue, 300), [setSearchValue]);
|
||||||
|
|
||||||
|
if (!setSearchValue) {
|
||||||
|
error('useCollectionDataSourceItems: please use in SchemaInitializerButtonContext and provide setSearchValue');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const clearKeywords = () => {
|
const clearKeywords = () => {
|
||||||
setSelected([]);
|
setSearchValue('');
|
||||||
onChange(null);
|
|
||||||
};
|
};
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: 'tableBlock',
|
key: 'tableBlock',
|
||||||
type: 'itemGroup',
|
type: 'itemGroup',
|
||||||
title: React.createElement(SelectCollection, {
|
title: React.createElement(SelectCollection, {
|
||||||
value,
|
value: searchValue,
|
||||||
onChange,
|
onChange,
|
||||||
setSelected,
|
|
||||||
}),
|
}),
|
||||||
children: collections
|
children: collections
|
||||||
?.filter((item) => {
|
?.filter((item) => {
|
||||||
const b = !value || selected.includes(item.name);
|
|
||||||
if (item.inherit) {
|
if (item.inherit) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -836,7 +842,12 @@ export const useCollectionDataSourceItems = (componentName) => {
|
|||||||
} else if (item.template === 'file' && ['Kanban', 'FormItem', 'Calendar'].includes(componentName)) {
|
} else if (item.template === 'file' && ['Kanban', 'FormItem', 'Calendar'].includes(componentName)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return b && !(item?.isThrough && item?.autoCreate);
|
if (!item.title) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
item.title.toUpperCase().includes(searchValue.toUpperCase()) && !(item?.isThrough && item?.autoCreate)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
?.map((item, index) => {
|
?.map((item, index) => {
|
||||||
|
@ -11,8 +11,8 @@ import {
|
|||||||
CascaderProps,
|
CascaderProps,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Empty,
|
Empty,
|
||||||
Menu,
|
|
||||||
MenuItemProps,
|
MenuItemProps,
|
||||||
|
MenuProps,
|
||||||
Modal,
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
@ -46,6 +46,7 @@ import {
|
|||||||
} from '..';
|
} from '..';
|
||||||
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
|
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
|
||||||
import { FilterBlockType, isSameCollection, useSupportedBlocks } from '../filter-provider/utils';
|
import { FilterBlockType, isSameCollection, useSupportedBlocks } from '../filter-provider/utils';
|
||||||
|
import { useCollectMenuItem, useCollectMenuItems, useMenuItem } from '../hooks/useMenuItem';
|
||||||
import { getTargetKey } from '../schema-component/antd/association-filter/utilts';
|
import { getTargetKey } from '../schema-component/antd/association-filter/utilts';
|
||||||
import { useSchemaTemplateManager } from '../schema-templates';
|
import { useSchemaTemplateManager } from '../schema-templates';
|
||||||
import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
|
import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
|
||||||
@ -53,7 +54,6 @@ import { FormDataTemplates } from './DataTemplates';
|
|||||||
import { EnableChildCollections } from './EnableChildCollections';
|
import { EnableChildCollections } from './EnableChildCollections';
|
||||||
import { FormLinkageRules } from './LinkageRules';
|
import { FormLinkageRules } from './LinkageRules';
|
||||||
import { useLinkageCollectionFieldOptions } from './LinkageRules/action-hooks';
|
import { useLinkageCollectionFieldOptions } from './LinkageRules/action-hooks';
|
||||||
import { MenuDividerProps } from 'antd/lib/menu';
|
|
||||||
|
|
||||||
interface SchemaSettingsProps {
|
interface SchemaSettingsProps {
|
||||||
title?: any;
|
title?: any;
|
||||||
@ -117,37 +117,60 @@ export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (pr
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const overlayClassName = classNames(
|
||||||
|
'nb-schema-initializer-button-overlay',
|
||||||
|
css`
|
||||||
|
.ant-dropdown-menu-item-group-list {
|
||||||
|
max-height: 40vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNested = (props) => {
|
export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNested = (props) => {
|
||||||
const { title, dn, ...others } = props;
|
const { title, dn, ...others } = props;
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const DropdownMenu = (
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
<Dropdown
|
const [shouldRender, setShouldRender] = useState(false);
|
||||||
open={visible}
|
|
||||||
onOpenChange={(visible) => {
|
if (!shouldRender) {
|
||||||
setVisible(visible);
|
return (
|
||||||
}}
|
<div
|
||||||
overlay={<Menu>{props.children as any}</Menu>}
|
onMouseEnter={() => {
|
||||||
overlayClassName={classNames(
|
setShouldRender(true);
|
||||||
'nb-schema-initializer-button-overlay',
|
setVisible(true);
|
||||||
css`
|
}}
|
||||||
.ant-dropdown-menu-item-group-list {
|
>
|
||||||
max-height: 40vh;
|
{typeof title === 'string' ? <span>{title}</span> : title}
|
||||||
overflow: auto;
|
</div>
|
||||||
}
|
);
|
||||||
`,
|
}
|
||||||
)}
|
|
||||||
>
|
const dropdownMenu = () => (
|
||||||
{typeof title === 'string' ? <span>{title}</span> : title}
|
<>
|
||||||
</Dropdown>
|
<Component />
|
||||||
|
<Dropdown
|
||||||
|
open={visible}
|
||||||
|
onOpenChange={() => {
|
||||||
|
setShouldRender(false);
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
menu={{ items: getMenuItems(() => props.children) }}
|
||||||
|
overlayClassName={overlayClassName}
|
||||||
|
>
|
||||||
|
{typeof title === 'string' ? <span>{title}</span> : title}
|
||||||
|
</Dropdown>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (dn) {
|
if (dn) {
|
||||||
return (
|
return (
|
||||||
<SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={dn} {...others}>
|
<SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={dn} {...others}>
|
||||||
{DropdownMenu}
|
{dropdownMenu()}
|
||||||
</SchemaSettingsProvider>
|
</SchemaSettingsProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return DropdownMenu;
|
return dropdownMenu();
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaSettings.Template = function Template(props) {
|
SchemaSettings.Template = function Template(props) {
|
||||||
@ -388,35 +411,70 @@ SchemaSettings.FormItemTemplate = function FormItemTemplate(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SchemaSettings.Item = function Item(props) {
|
SchemaSettings.Item = function Item(props) {
|
||||||
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const { collectMenuItem } = useCollectMenuItem();
|
||||||
const { eventKey } = props;
|
const { eventKey } = props;
|
||||||
const key = useMemo(() => uid(), []);
|
const key = useMemo(() => uid(), []);
|
||||||
return (
|
const item = {
|
||||||
<Menu.Item
|
..._.omit(props, ['children']),
|
||||||
key={key}
|
key,
|
||||||
eventKey={(eventKey as any) || key}
|
eventKey: (eventKey as any) || key,
|
||||||
{...props}
|
onClick: (info) => {
|
||||||
onClick={(info) => {
|
info.domEvent.preventDefault();
|
||||||
info.domEvent.preventDefault();
|
info.domEvent.stopPropagation();
|
||||||
info.domEvent.stopPropagation();
|
props?.onClick?.(info);
|
||||||
props?.onClick?.(info);
|
},
|
||||||
}}
|
style: { minWidth: 120 },
|
||||||
style={{ minWidth: 120 }}
|
label: props.children || props.title,
|
||||||
>
|
title: props.title,
|
||||||
{props.children || props.title}
|
} as MenuProps['items'][0];
|
||||||
</Menu.Item>
|
|
||||||
);
|
pushMenuItem?.(item);
|
||||||
|
collectMenuItem?.(item);
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaSettings.ItemGroup = (props) => {
|
SchemaSettings.ItemGroup = function ItemGroup(props) {
|
||||||
return <Menu.ItemGroup {...props} />;
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const key = useMemo(() => uid(), []);
|
||||||
|
const item = {
|
||||||
|
key,
|
||||||
|
type: 'group',
|
||||||
|
title: props.title,
|
||||||
|
label: props.title,
|
||||||
|
children: getMenuItems(() => props.children),
|
||||||
|
} as MenuProps['items'][0];
|
||||||
|
|
||||||
|
pushMenuItem(item);
|
||||||
|
return <Component />;
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaSettings.SubMenu = (props) => {
|
SchemaSettings.SubMenu = function SubMenu(props) {
|
||||||
return <Menu.SubMenu {...props} />;
|
const { Component, getMenuItems } = useMenuItem();
|
||||||
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const key = useMemo(() => uid(), []);
|
||||||
|
const item = {
|
||||||
|
key,
|
||||||
|
label: props.title,
|
||||||
|
title: props.title,
|
||||||
|
children: getMenuItems(() => props.children),
|
||||||
|
} as MenuProps['items'][0];
|
||||||
|
|
||||||
|
pushMenuItem(item);
|
||||||
|
return <Component />;
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaSettings.Divider = (props: MenuDividerProps) => {
|
SchemaSettings.Divider = function Divider() {
|
||||||
return <Menu.Divider {...props} />;
|
const { pushMenuItem } = useCollectMenuItems();
|
||||||
|
const key = useMemo(() => uid(), []);
|
||||||
|
const item = {
|
||||||
|
key,
|
||||||
|
type: 'divider',
|
||||||
|
} as MenuProps['items'][0];
|
||||||
|
|
||||||
|
pushMenuItem(item);
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaSettings.Remove = function Remove(props: any) {
|
SchemaSettings.Remove = function Remove(props: any) {
|
||||||
@ -470,6 +528,7 @@ SchemaSettings.ConnectDataBlocks = function ConnectDataBlocks(props: {
|
|||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
const { inProvider } = useFilterBlock();
|
const { inProvider } = useFilterBlock();
|
||||||
const dataBlocks = useSupportedBlocks(type);
|
const dataBlocks = useSupportedBlocks(type);
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let { targets = [], uid } = findFilterTargets(fieldSchema);
|
let { targets = [], uid } = findFilterTargets(fieldSchema);
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
|
||||||
@ -578,11 +637,13 @@ SchemaSettings.ConnectDataBlocks = function ConnectDataBlocks(props: {
|
|||||||
{Content.length ? (
|
{Content.length ? (
|
||||||
Content
|
Content
|
||||||
) : (
|
) : (
|
||||||
<Empty
|
<SchemaSettings.Item>
|
||||||
style={{ width: 160, padding: '0 1em' }}
|
<Empty
|
||||||
description={emptyDescription}
|
style={{ width: 160, padding: '0 1em' }}
|
||||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
description={emptyDescription}
|
||||||
/>
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||||
|
/>
|
||||||
|
</SchemaSettings.Item>
|
||||||
)}
|
)}
|
||||||
</SchemaSettings.SubMenu>
|
</SchemaSettings.SubMenu>
|
||||||
);
|
);
|
||||||
@ -755,7 +816,7 @@ SchemaSettings.ActionModalItem = React.memo((props: any) => {
|
|||||||
title={compile(title)}
|
title={compile(title)}
|
||||||
{...others}
|
{...others}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
visible={visible}
|
open={visible}
|
||||||
onCancel={cancelHandler}
|
onCancel={cancelHandler}
|
||||||
footer={
|
footer={
|
||||||
<Space>
|
<Space>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { PageHeader as AntdPageHeader, Input, Spin } from 'antd';
|
import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout';
|
||||||
|
import { Input, Spin } from 'antd';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { useAPIClient, useRequest, useSchemaTemplateManager } from '..';
|
import { useAPIClient, useRequest, useSchemaTemplateManager } from '..';
|
||||||
@ -75,6 +76,7 @@ export const BlockTemplateDetails = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AntdPageHeader
|
<AntdPageHeader
|
||||||
|
style={{ backgroundColor: 'white' }}
|
||||||
onBack={() => {
|
onBack={() => {
|
||||||
navigate('/admin/plugins/block-templates');
|
navigate('/admin/plugins/block-templates');
|
||||||
}}
|
}}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { PageHeader as AntdPageHeader } from 'antd';
|
import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CollectionManagerProvider } from '../collection-manager';
|
import { CollectionManagerProvider } from '../collection-manager';
|
||||||
@ -10,7 +10,7 @@ export const BlockTemplatePage = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AntdPageHeader ghost={false} title={t('Block templates')} />
|
<AntdPageHeader style={{ backgroundColor: 'white' }} ghost={false} title={t('Block templates')} />
|
||||||
<div style={{ margin: 'var(--nb-spacing)' }}>
|
<div style={{ margin: 'var(--nb-spacing)' }}>
|
||||||
<CollectionManagerProvider collections={[uiSchemaTemplatesCollection]}>
|
<CollectionManagerProvider collections={[uiSchemaTemplatesCollection]}>
|
||||||
<SchemaComponent schema={uiSchemaTemplatesSchema} />
|
<SchemaComponent schema={uiSchemaTemplatesSchema} />
|
||||||
|
@ -1,278 +0,0 @@
|
|||||||
import { FormButtonGroup, FormDialog, FormDrawer, FormItem, FormLayout, Reset, Submit } from '@formily/antd';
|
|
||||||
import { createForm, Field, ObjectField, onFormValuesChange } from '@formily/core';
|
|
||||||
import {
|
|
||||||
FieldContext,
|
|
||||||
FormContext,
|
|
||||||
observer,
|
|
||||||
RecursionField,
|
|
||||||
Schema,
|
|
||||||
SchemaOptionsContext,
|
|
||||||
useField,
|
|
||||||
useFieldSchema,
|
|
||||||
useForm,
|
|
||||||
} from '@formily/react';
|
|
||||||
import { Dropdown, Menu, Modal, Select, Switch } from 'antd';
|
|
||||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
|
||||||
import { SchemaComponentOptions, useAttach, useDesignable } from '..';
|
|
||||||
|
|
||||||
export interface SettingsFormContextProps {
|
|
||||||
field?: Field;
|
|
||||||
fieldSchema?: Schema;
|
|
||||||
dropdownVisible?: boolean;
|
|
||||||
setDropdownVisible?: (v: boolean) => void;
|
|
||||||
dn?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SettingsFormContext = createContext<SettingsFormContextProps>(null);
|
|
||||||
|
|
||||||
export const useSettingsFormContext = () => {
|
|
||||||
return useContext(SettingsFormContext);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SettingsForm: any = observer(
|
|
||||||
(props: any) => {
|
|
||||||
const dn = useDesignable();
|
|
||||||
const field = useField<Field>();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const [dropdownVisible, setDropdownVisible] = useState(false);
|
|
||||||
const settingsFormSchema = useMemo(() => new Schema(props.schema), []);
|
|
||||||
const form = useMemo(
|
|
||||||
() =>
|
|
||||||
createForm({
|
|
||||||
initialValues: fieldSchema.toJSON(),
|
|
||||||
effects(form) {
|
|
||||||
onFormValuesChange((form) => {
|
|
||||||
dn.patch(form.values);
|
|
||||||
console.log('form.values', form.values);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const f = useAttach(form.createVoidField({ ...field.props, basePath: '' }));
|
|
||||||
return (
|
|
||||||
<SettingsFormContext.Provider value={{ dn, field, fieldSchema, dropdownVisible, setDropdownVisible }}>
|
|
||||||
<SchemaComponentOptions components={{ SettingsForm }}>
|
|
||||||
<FieldContext.Provider value={null}>
|
|
||||||
<FormContext.Provider value={form}>
|
|
||||||
<FieldContext.Provider value={f}>
|
|
||||||
<Dropdown
|
|
||||||
open={dropdownVisible}
|
|
||||||
onOpenChange={(visible) => setDropdownVisible(visible)}
|
|
||||||
overlayStyle={{ width: 200 }}
|
|
||||||
overlay={
|
|
||||||
<Menu>
|
|
||||||
{settingsFormSchema.mapProperties((s, key) => {
|
|
||||||
return <RecursionField name={key} schema={s} />;
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<a>配置</a>
|
|
||||||
</Dropdown>
|
|
||||||
</FieldContext.Provider>
|
|
||||||
</FormContext.Provider>
|
|
||||||
</FieldContext.Provider>
|
|
||||||
</SchemaComponentOptions>
|
|
||||||
</SettingsFormContext.Provider>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'SettingsForm' },
|
|
||||||
);
|
|
||||||
|
|
||||||
SettingsForm.Divider = () => {
|
|
||||||
return <Menu.Divider />;
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsForm.Remove = (props) => {
|
|
||||||
const field = useField();
|
|
||||||
const { dn, setDropdownVisible } = useSettingsFormContext();
|
|
||||||
return (
|
|
||||||
<Menu.Item
|
|
||||||
onClick={() => {
|
|
||||||
setDropdownVisible(false);
|
|
||||||
Modal.confirm({
|
|
||||||
title: 'Are you sure delete this task?',
|
|
||||||
content: 'Some descriptions',
|
|
||||||
okText: 'Yes',
|
|
||||||
okType: 'danger',
|
|
||||||
cancelText: 'No',
|
|
||||||
...props.confirm,
|
|
||||||
onOk() {
|
|
||||||
dn.remove();
|
|
||||||
console.log('OK');
|
|
||||||
},
|
|
||||||
onCancel() {
|
|
||||||
console.log('Cancel');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{field.title}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsForm.Switch = observer(
|
|
||||||
() => {
|
|
||||||
const field = useField<Field>();
|
|
||||||
return (
|
|
||||||
<Menu.Item
|
|
||||||
onClick={() => {
|
|
||||||
field.value = !field.value;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
||||||
{field.title} <Switch checked={!!field.value} />
|
|
||||||
</div>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'SettingsForm' },
|
|
||||||
);
|
|
||||||
|
|
||||||
SettingsForm.Select = observer(
|
|
||||||
(props) => {
|
|
||||||
const field = useField<Field>();
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
return (
|
|
||||||
<Menu.Item onClick={() => !open && setOpen(true)}>
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
||||||
{field.title}
|
|
||||||
<Select
|
|
||||||
open={open}
|
|
||||||
onDropdownVisibleChange={(open) => setOpen(open)}
|
|
||||||
onSelect={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
onChange={(value) => {
|
|
||||||
field.value = value;
|
|
||||||
}}
|
|
||||||
value={field.value}
|
|
||||||
options={field.dataSource}
|
|
||||||
style={{ width: '60%' }}
|
|
||||||
size={'small'}
|
|
||||||
bordered={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'SettingsForm' },
|
|
||||||
);
|
|
||||||
|
|
||||||
SettingsForm.Modal = () => {
|
|
||||||
const form = useForm();
|
|
||||||
const field = useField<Field>();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const options = useContext(SchemaOptionsContext);
|
|
||||||
const { setDropdownVisible } = useSettingsFormContext();
|
|
||||||
return (
|
|
||||||
<Menu.Item
|
|
||||||
style={{ width: 200 }}
|
|
||||||
onClick={async () => {
|
|
||||||
setDropdownVisible(false);
|
|
||||||
const values = await FormDialog('Title', () => {
|
|
||||||
return (
|
|
||||||
<SchemaComponentOptions scope={options.scope} components={{ ...options.components, FormItem }}>
|
|
||||||
<FormLayout layout={'vertical'}>
|
|
||||||
<RecursionField schema={fieldSchema} onlyRenderProperties />
|
|
||||||
</FormLayout>
|
|
||||||
</SchemaComponentOptions>
|
|
||||||
);
|
|
||||||
}).open({
|
|
||||||
initialValues: fieldSchema.type !== 'void' ? field.value : form.values,
|
|
||||||
});
|
|
||||||
if (fieldSchema.type !== 'void') {
|
|
||||||
form.setValues(
|
|
||||||
{
|
|
||||||
[fieldSchema.name]: values,
|
|
||||||
},
|
|
||||||
'deepMerge',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValues(values);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{field.title}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsForm.Drawer = () => {
|
|
||||||
const form = useForm();
|
|
||||||
const field = useField<ObjectField>();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const options = useContext(SchemaOptionsContext);
|
|
||||||
const { setDropdownVisible } = useSettingsFormContext();
|
|
||||||
return (
|
|
||||||
<Menu.Item
|
|
||||||
style={{ width: 200 }}
|
|
||||||
onClick={async () => {
|
|
||||||
setDropdownVisible(false);
|
|
||||||
const values = await FormDrawer('Popup form', () => {
|
|
||||||
return (
|
|
||||||
<SchemaComponentOptions scope={options.scope} components={{ ...options.components, FormItem }}>
|
|
||||||
<FormLayout layout={'vertical'}>
|
|
||||||
<RecursionField schema={fieldSchema} onlyRenderProperties />
|
|
||||||
<FormDrawer.Footer>
|
|
||||||
<FormButtonGroup align="right">
|
|
||||||
<Reset>Reset</Reset>
|
|
||||||
<Submit
|
|
||||||
onSubmit={() => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, 1000);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Submit>
|
|
||||||
</FormButtonGroup>
|
|
||||||
</FormDrawer.Footer>
|
|
||||||
</FormLayout>
|
|
||||||
</SchemaComponentOptions>
|
|
||||||
);
|
|
||||||
}).open({
|
|
||||||
initialValues: fieldSchema.type !== 'void' ? field.value : form.values,
|
|
||||||
});
|
|
||||||
if (fieldSchema.type !== 'void') {
|
|
||||||
form.setValues(
|
|
||||||
{
|
|
||||||
[fieldSchema.name]: values,
|
|
||||||
},
|
|
||||||
'deepMerge',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setValues(values);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{field.title}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsForm.SubMenu = () => {
|
|
||||||
const field = useField();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
return (
|
|
||||||
<Menu.SubMenu title={field.title}>
|
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
|
||||||
return <RecursionField name={key} schema={schema} />;
|
|
||||||
})}
|
|
||||||
</Menu.SubMenu>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsForm.ItemGroup = () => {
|
|
||||||
const field = useField();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
return (
|
|
||||||
<Menu.ItemGroup title={field.title}>
|
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
|
||||||
return <RecursionField name={key} schema={schema} />;
|
|
||||||
})}
|
|
||||||
</Menu.ItemGroup>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,136 +0,0 @@
|
|||||||
import { ISchema, observer, useFieldSchema } from '@formily/react';
|
|
||||||
import { AntdSchemaComponentProvider, SchemaComponent, SchemaComponentProvider, SettingsForm } from '@nocobase/client';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
'x-component-props.switch': {
|
|
||||||
title: 'Switch',
|
|
||||||
'x-component': 'SettingsForm.Switch',
|
|
||||||
},
|
|
||||||
'x-component-props.select': {
|
|
||||||
title: 'Select',
|
|
||||||
'x-component': 'SettingsForm.Select',
|
|
||||||
enum: [
|
|
||||||
{ label: 'Option1', value: 'option1' },
|
|
||||||
{ label: 'Option2', value: 'option2' },
|
|
||||||
{ label: 'Option3', value: 'option3' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
modal: {
|
|
||||||
type: 'void',
|
|
||||||
title: 'Open Modal',
|
|
||||||
'x-component': 'SettingsForm.Modal',
|
|
||||||
'x-component-props': {},
|
|
||||||
properties: {
|
|
||||||
'x-component-props.title': {
|
|
||||||
title: '标题',
|
|
||||||
'x-component': 'Input',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
drawer: {
|
|
||||||
type: 'void',
|
|
||||||
title: 'Open Drawer',
|
|
||||||
'x-component': 'SettingsForm.Drawer',
|
|
||||||
properties: {
|
|
||||||
'x-component-props.title': {
|
|
||||||
title: '标题',
|
|
||||||
'x-component': 'Input',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
group: {
|
|
||||||
type: 'void',
|
|
||||||
title: 'ItemGroup',
|
|
||||||
'x-component': 'SettingsForm.ItemGroup',
|
|
||||||
properties: {
|
|
||||||
'x-component-props': {
|
|
||||||
type: 'object',
|
|
||||||
title: 'Open Modal',
|
|
||||||
'x-component': 'SettingsForm.Modal',
|
|
||||||
properties: {
|
|
||||||
title: {
|
|
||||||
title: '标题',
|
|
||||||
'x-component': 'Input',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
submenu: {
|
|
||||||
type: 'void',
|
|
||||||
title: 'SubMenu',
|
|
||||||
'x-component': 'SettingsForm.SubMenu',
|
|
||||||
properties: {
|
|
||||||
'x-component-props': {
|
|
||||||
type: 'object',
|
|
||||||
title: 'Open Modal',
|
|
||||||
'x-component': 'SettingsForm.Modal',
|
|
||||||
properties: {
|
|
||||||
title: {
|
|
||||||
title: '标题',
|
|
||||||
'x-component': 'Input',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
'x-component': 'SettingsForm.Divider',
|
|
||||||
},
|
|
||||||
remove: {
|
|
||||||
title: 'Delete',
|
|
||||||
'x-component': 'SettingsForm.Remove',
|
|
||||||
'x-component-props': {
|
|
||||||
confirm: {
|
|
||||||
title: 'Are you sure delete this task?',
|
|
||||||
content: 'Some descriptions',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Hello = observer(
|
|
||||||
(props: any) => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<pre>{JSON.stringify(props, null, 2)}</pre>
|
|
||||||
<pre>{JSON.stringify(fieldSchema.toJSON(), null, 2)}</pre>
|
|
||||||
<SettingsForm schema={schema} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'Hello' },
|
|
||||||
);
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
return (
|
|
||||||
<SchemaComponentProvider components={{ Hello }}>
|
|
||||||
<AntdSchemaComponentProvider>
|
|
||||||
<SchemaComponent
|
|
||||||
schema={{
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
hello: {
|
|
||||||
'x-component': 'Hello',
|
|
||||||
'x-component-props': {
|
|
||||||
title: 'abc',
|
|
||||||
switch: true,
|
|
||||||
select: 'option1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</AntdSchemaComponentProvider>
|
|
||||||
</SchemaComponentProvider>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
group:
|
|
||||||
title: Client
|
|
||||||
order: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# SettingsForm
|
|
||||||
|
|
||||||
<code src="./demos/demo1.tsx"></code>
|
|
@ -1 +0,0 @@
|
|||||||
export * from './SettingsForm';
|
|
@ -1,11 +1,10 @@
|
|||||||
import { ISchema, useForm } from '@formily/react';
|
import { ISchema, useForm } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Menu } from 'antd';
|
import { MenuProps } from 'antd';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ActionContextProvider, SchemaComponent, useActionContext } from '../';
|
import { ActionContextProvider, DropdownVisibleContext, SchemaComponent, useActionContext } from '../';
|
||||||
import { useAPIClient } from '../api-client';
|
import { useAPIClient } from '../api-client';
|
||||||
import { DropdownVisibleContext } from './CurrentUser';
|
|
||||||
|
|
||||||
const useCloseAction = () => {
|
const useCloseAction = () => {
|
||||||
const { setVisible } = useActionContext();
|
const { setVisible } = useActionContext();
|
||||||
@ -114,23 +113,29 @@ const schema: ISchema = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ChangePassword = () => {
|
export const useChangePassword = () => {
|
||||||
|
const ctx = useContext(DropdownVisibleContext);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const ctx = useContext(DropdownVisibleContext);
|
|
||||||
return (
|
return useMemo<MenuProps['items'][0]>(() => {
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
return {
|
||||||
<Menu.Item
|
key: 'password',
|
||||||
key="password"
|
eventKey: 'ChangePassword',
|
||||||
eventKey={'ChangePassword'}
|
onClick: () => {
|
||||||
onClick={() => {
|
setVisible(true);
|
||||||
ctx?.setVisible?.(false);
|
ctx?.setVisible(false);
|
||||||
setVisible(true);
|
},
|
||||||
}}
|
label: (
|
||||||
>
|
<>
|
||||||
{t('Change password')}
|
{t('Change password')}
|
||||||
</Menu.Item>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<SchemaComponent scope={{ useCloseAction, useSaveCurrentUserValues }} schema={schema} />
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
</ActionContextProvider>
|
<SchemaComponent scope={{ useCloseAction, useSaveCurrentUserValues }} schema={schema} />
|
||||||
);
|
</div>
|
||||||
|
</ActionContextProvider>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [visible]);
|
||||||
};
|
};
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Dropdown, Menu, Modal } from 'antd';
|
import { error } from '@nocobase/utils/client';
|
||||||
import React, { createContext, useState } from 'react';
|
import { Dropdown, Menu, MenuProps, Modal } from 'antd';
|
||||||
|
import React, { createContext, useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useACLRoleContext, useAPIClient, useCurrentUserContext } from '..';
|
import { useACLRoleContext, useAPIClient, useCurrentUserContext } from '..';
|
||||||
import { useCurrentAppInfo } from '../appInfo/CurrentAppInfoProvider';
|
import { useCurrentAppInfo } from '../appInfo/CurrentAppInfoProvider';
|
||||||
import { ChangePassword } from './ChangePassword';
|
import { useChangePassword } from './ChangePassword';
|
||||||
import { EditProfile } from './EditProfile';
|
import { useEditProfile } from './EditProfile';
|
||||||
import { LanguageSettings } from './LanguageSettings';
|
import { useLanguageSettings } from './LanguageSettings';
|
||||||
import { SwitchRole } from './SwitchRole';
|
import { useSwitchRole } from './SwitchRole';
|
||||||
import { ThemeSettings } from './ThemeSettings';
|
import { useThemeSettings } from './ThemeSettings';
|
||||||
const ApplicationVersion = () => {
|
const useApplicationVersion = () => {
|
||||||
const data = useCurrentAppInfo();
|
const data = useCurrentAppInfo();
|
||||||
return (
|
return useMemo(() => {
|
||||||
<Menu.Item key="version" disabled>
|
return {
|
||||||
Version {data?.data?.version}
|
key: 'version',
|
||||||
</Menu.Item>
|
disabled: true,
|
||||||
);
|
label: `Version ${data?.data?.version}`,
|
||||||
|
};
|
||||||
|
}, [data?.data?.version]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,7 +35,7 @@ export const SettingsMenu: React.FC<{
|
|||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const silenceApi = useAPIClient();
|
const silenceApi = useAPIClient();
|
||||||
const check = async () => {
|
const check = useCallback(async () => {
|
||||||
return await new Promise((resolve) => {
|
return await new Promise((resolve) => {
|
||||||
const heartbeat = setInterval(() => {
|
const heartbeat = setInterval(() => {
|
||||||
silenceApi
|
silenceApi
|
||||||
@ -47,75 +50,100 @@ export const SettingsMenu: React.FC<{
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
// ignore
|
error(err);
|
||||||
});
|
});
|
||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
};
|
}, [silenceApi]);
|
||||||
return (
|
const divider = useMemo<MenuProps['items'][0]>(() => {
|
||||||
<Menu>
|
return {
|
||||||
<ApplicationVersion />
|
type: 'divider',
|
||||||
<Menu.Divider />
|
};
|
||||||
<EditProfile />
|
}, []);
|
||||||
<ChangePassword />
|
const appVersion = useApplicationVersion();
|
||||||
<Menu.Divider />
|
const editProfile = useEditProfile();
|
||||||
<SwitchRole />
|
const changePassword = useChangePassword();
|
||||||
<LanguageSettings />
|
const switchRole = useSwitchRole();
|
||||||
<ThemeSettings />
|
const languageSettings = useLanguageSettings();
|
||||||
<Menu.Divider />
|
const themeSettings = useThemeSettings();
|
||||||
{appAllowed && (
|
const controlApp = useMemo<MenuProps['items']>(() => {
|
||||||
<>
|
if (!appAllowed) {
|
||||||
<Menu.Item
|
return [];
|
||||||
key="cache"
|
}
|
||||||
onClick={async () => {
|
|
||||||
await api.resource('app').clearCache();
|
return [
|
||||||
|
{
|
||||||
|
key: 'cache',
|
||||||
|
label: t('Clear cache'),
|
||||||
|
onClick: async () => {
|
||||||
|
await api.resource('app').clearCache();
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'reboot',
|
||||||
|
label: t('Reboot application'),
|
||||||
|
onClick: async () => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('Reboot application'),
|
||||||
|
content: t('The will interrupt service, it may take a few seconds to restart. Are you sure to continue?'),
|
||||||
|
okText: t('Reboot'),
|
||||||
|
okButtonProps: {
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
onOk: async () => {
|
||||||
|
await api.resource('app').reboot();
|
||||||
|
await check();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}}
|
},
|
||||||
>
|
});
|
||||||
{t('Clear cache')}
|
},
|
||||||
</Menu.Item>
|
},
|
||||||
<Menu.Item
|
divider,
|
||||||
key="reboot"
|
];
|
||||||
onClick={async () => {
|
}, [appAllowed, check]);
|
||||||
Modal.confirm({
|
const items = useMemo<MenuProps['items']>(() => {
|
||||||
title: t('Reboot application'),
|
return [
|
||||||
content: t(
|
appVersion,
|
||||||
'The will interrupt service, it may take a few seconds to restart. Are you sure to continue?',
|
divider,
|
||||||
),
|
editProfile,
|
||||||
okText: t('Reboot'),
|
changePassword,
|
||||||
okButtonProps: {
|
divider,
|
||||||
danger: true,
|
switchRole,
|
||||||
},
|
languageSettings,
|
||||||
onOk: async () => {
|
themeSettings,
|
||||||
await api.resource('app').reboot();
|
divider,
|
||||||
await check();
|
...controlApp,
|
||||||
window.location.reload();
|
{
|
||||||
},
|
key: 'signout',
|
||||||
});
|
label: t('Sign out'),
|
||||||
}}
|
onClick: async () => {
|
||||||
>
|
|
||||||
{t('Reboot application')}
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Menu.Item
|
|
||||||
key="signout"
|
|
||||||
onClick={async () => {
|
|
||||||
await api.auth.signOut();
|
await api.auth.signOut();
|
||||||
navigate(`/signin?redirect=${encodeURIComponent(redirectUrl)}`);
|
navigate(`/signin?redirect=${encodeURIComponent(redirectUrl)}`);
|
||||||
}}
|
},
|
||||||
>
|
},
|
||||||
{t('Sign out')}
|
];
|
||||||
</Menu.Item>
|
}, [
|
||||||
</Menu>
|
appVersion,
|
||||||
);
|
changePassword,
|
||||||
|
controlApp,
|
||||||
|
divider,
|
||||||
|
editProfile,
|
||||||
|
history,
|
||||||
|
languageSettings,
|
||||||
|
switchRole,
|
||||||
|
themeSettings,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return <Menu items={items} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DropdownVisibleContext = createContext(null);
|
export const DropdownVisibleContext = createContext(null);
|
||||||
export const CurrentUser = () => {
|
export const CurrentUser = () => {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { data } = useCurrentUserContext();
|
const { data } = useCurrentUserContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'inline-flex', verticalAlign: 'top' }}>
|
<div style={{ display: 'inline-flex', verticalAlign: 'top' }}>
|
||||||
<DropdownVisibleContext.Provider value={{ visible, setVisible }}>
|
<DropdownVisibleContext.Provider value={{ visible, setVisible }}>
|
||||||
@ -124,7 +152,9 @@ export const CurrentUser = () => {
|
|||||||
onOpenChange={(visible) => {
|
onOpenChange={(visible) => {
|
||||||
setVisible(visible);
|
setVisible(visible);
|
||||||
}}
|
}}
|
||||||
overlay={<SettingsMenu />}
|
dropdownRender={() => {
|
||||||
|
return <SettingsMenu />;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={css`
|
className={css`
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ISchema, useForm } from '@formily/react';
|
import { ISchema, useForm } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Menu } from 'antd';
|
import { MenuProps } from 'antd';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
ActionContextProvider,
|
ActionContextProvider,
|
||||||
@ -114,23 +114,32 @@ const schema: ISchema = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EditProfile = () => {
|
export const useEditProfile = () => {
|
||||||
|
const ctx = useContext(DropdownVisibleContext);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const ctx = useContext(DropdownVisibleContext);
|
|
||||||
return (
|
return useMemo<MenuProps['items'][0]>(() => {
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
return {
|
||||||
<Menu.Item
|
key: 'profile',
|
||||||
key="profile"
|
eventKey: 'EditProfile',
|
||||||
eventKey={'EditProfile'}
|
onClick: () => {
|
||||||
onClick={() => {
|
setVisible(true);
|
||||||
setVisible(true);
|
ctx?.setVisible(false);
|
||||||
ctx?.setVisible(false);
|
},
|
||||||
}}
|
label: (
|
||||||
>
|
<>
|
||||||
{t('Edit profile')}
|
{t('Edit profile')}
|
||||||
</Menu.Item>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<SchemaComponent scope={{ useCurrentUserValues, useCloseAction, useSaveCurrentUserValues }} schema={schema} />
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
</ActionContextProvider>
|
<SchemaComponent
|
||||||
);
|
scope={{ useCurrentUserValues, useCloseAction, useSaveCurrentUserValues }}
|
||||||
|
schema={schema}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ActionContextProvider>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [visible]);
|
||||||
};
|
};
|
||||||
|
@ -1,65 +1,66 @@
|
|||||||
import { css } from '@emotion/css';
|
import { MenuProps, Select } from 'antd';
|
||||||
import { Menu, Select } from 'antd';
|
import React, { useMemo, useState } from 'react';
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAPIClient, useCurrentUserContext, useSystemSettings } from '..';
|
import { useAPIClient, useSystemSettings } from '..';
|
||||||
import locale from '../locale';
|
import locale from '../locale';
|
||||||
|
|
||||||
export const LanguageSettings = () => {
|
export const useLanguageSettings = () => {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const ctx = useCurrentUserContext();
|
|
||||||
const { data } = useSystemSettings();
|
const { data } = useSystemSettings();
|
||||||
const enabledLanguages: string[] = data?.data?.enabledLanguages || [];
|
const enabledLanguages: string[] = data?.data?.enabledLanguages || [];
|
||||||
|
const result = useMemo<MenuProps['items'][0]>(() => {
|
||||||
|
return {
|
||||||
|
key: 'language',
|
||||||
|
eventKey: 'LanguageSettings',
|
||||||
|
onClick: () => {
|
||||||
|
setOpen(true);
|
||||||
|
},
|
||||||
|
label: (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Language')}{' '}
|
||||||
|
<Select
|
||||||
|
style={{ minWidth: 100 }}
|
||||||
|
bordered={false}
|
||||||
|
open={open}
|
||||||
|
onDropdownVisibleChange={(open) => {
|
||||||
|
setOpen(open);
|
||||||
|
}}
|
||||||
|
options={Object.keys(locale)
|
||||||
|
.filter((lang) => enabledLanguages.includes(lang))
|
||||||
|
.map((lang) => {
|
||||||
|
return {
|
||||||
|
label: locale[lang].label,
|
||||||
|
value: lang,
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
value={i18n.language}
|
||||||
|
onChange={async (lang) => {
|
||||||
|
await api.resource('users').updateProfile({
|
||||||
|
values: {
|
||||||
|
appLang: lang,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
api.auth.setLocale(lang);
|
||||||
|
await i18n.changeLanguage(lang);
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [enabledLanguages, i18n, open]);
|
||||||
|
|
||||||
if (enabledLanguages.length < 2) {
|
if (enabledLanguages.length < 2) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// console.log('data', data?.data?.enabledLanguages);
|
|
||||||
return (
|
return result;
|
||||||
<Menu.Item
|
|
||||||
key="language"
|
|
||||||
eventKey={'LanguageSettings'}
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{t('Language')}{' '}
|
|
||||||
<Select
|
|
||||||
style={{ minWidth: 100 }}
|
|
||||||
bordered={false}
|
|
||||||
open={open}
|
|
||||||
onDropdownVisibleChange={(open) => {
|
|
||||||
setOpen(open);
|
|
||||||
}}
|
|
||||||
options={Object.keys(locale)
|
|
||||||
.filter((lang) => enabledLanguages.includes(lang))
|
|
||||||
.map((lang) => {
|
|
||||||
return {
|
|
||||||
label: locale[lang].label,
|
|
||||||
value: lang,
|
|
||||||
};
|
|
||||||
})}
|
|
||||||
value={i18n.language}
|
|
||||||
onChange={async (lang) => {
|
|
||||||
await api.resource('users').updateProfile({
|
|
||||||
values: {
|
|
||||||
appLang: lang,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
api.auth.setLocale(lang);
|
|
||||||
await i18n.changeLanguage(lang);
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
import { ISchema, useForm } from '@formily/react';
|
import { ISchema, useForm } from '@formily/react';
|
||||||
import { Space, Tabs } from 'antd';
|
import { Space, Tabs } from 'antd';
|
||||||
import React, { useCallback, useContext } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { css } from '@emotion/css';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { SchemaComponent, useAPIClient, useCurrentDocumentTitle, useSystemSettings } from '..';
|
import { SchemaComponent, useAPIClient, useCurrentDocumentTitle, useSystemSettings } from '..';
|
||||||
@ -142,21 +142,30 @@ export const SigninPage = (props: SigninPageProps) => {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{smsAuthEnabled ? (
|
{smsAuthEnabled ? (
|
||||||
<Tabs defaultActiveKey="password">
|
<Tabs
|
||||||
<Tabs.TabPane tab={t('Sign in via account')} key="password">
|
defaultActiveKey="password"
|
||||||
<SchemaComponent scope={{ usePasswordSignIn }} schema={schema || passwordForm} />
|
items={[
|
||||||
</Tabs.TabPane>
|
{
|
||||||
<Tabs.TabPane tab={t('Sign in via phone')} key="phone">
|
label: t('Sign in via account'),
|
||||||
<SchemaComponent
|
key: 'password',
|
||||||
schema={phoneForm}
|
children: <SchemaComponent scope={{ usePasswordSignIn }} schema={schema || passwordForm} />,
|
||||||
scope={{ usePhoneSignIn, ...scope }}
|
},
|
||||||
components={{
|
{
|
||||||
VerificationCode,
|
label: t('Sign in via phone'),
|
||||||
...components,
|
key: 'phone',
|
||||||
}}
|
children: (
|
||||||
/>
|
<SchemaComponent
|
||||||
</Tabs.TabPane>
|
schema={phoneForm}
|
||||||
</Tabs>
|
scope={{ usePhoneSignIn, ...scope }}
|
||||||
|
components={{
|
||||||
|
VerificationCode,
|
||||||
|
...components,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
components={{ ...components }}
|
components={{ ...components }}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { css } from '@emotion/css';
|
import { MenuProps, Select } from 'antd';
|
||||||
import { Menu, Select } from 'antd';
|
import React, { useMemo } from 'react';
|
||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useACLRoleContext } from '../acl';
|
import { useACLRoleContext } from '../acl';
|
||||||
import { useAPIClient } from '../api-client';
|
import { useAPIClient } from '../api-client';
|
||||||
@ -26,40 +25,47 @@ const useCurrentRoles = () => {
|
|||||||
return compile(options);
|
return compile(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SwitchRole = () => {
|
export const useSwitchRole = () => {
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const roles = useCurrentRoles();
|
const roles = useCurrentRoles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const result = useMemo<MenuProps['items'][0]>(() => {
|
||||||
|
return {
|
||||||
|
key: 'role',
|
||||||
|
eventKey: 'SwitchRole',
|
||||||
|
label: (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Switch role')}{' '}
|
||||||
|
<Select
|
||||||
|
style={{ minWidth: 100 }}
|
||||||
|
bordered={false}
|
||||||
|
fieldNames={{
|
||||||
|
label: 'title',
|
||||||
|
value: 'name',
|
||||||
|
}}
|
||||||
|
options={roles}
|
||||||
|
value={api.auth.role}
|
||||||
|
onChange={async (roleName) => {
|
||||||
|
api.auth.setRole(roleName);
|
||||||
|
await api.resource('users').setDefaultRole({ values: { roleName } });
|
||||||
|
location.reload();
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [api, history, roles]);
|
||||||
|
|
||||||
if (roles.length <= 1) {
|
if (roles.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<Menu.Item key="role" eventKey={'SwitchRole'}>
|
return result;
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{t('Switch role')}{' '}
|
|
||||||
<Select
|
|
||||||
style={{ minWidth: 100 }}
|
|
||||||
bordered={false}
|
|
||||||
fieldNames={{
|
|
||||||
label: 'title',
|
|
||||||
value: 'name',
|
|
||||||
}}
|
|
||||||
options={roles}
|
|
||||||
value={api.auth.role}
|
|
||||||
onChange={async (roleName) => {
|
|
||||||
api.auth.setRole(roleName);
|
|
||||||
await api.resource('users').setDefaultRole({ values: { roleName } });
|
|
||||||
location.reload();
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -1,47 +1,51 @@
|
|||||||
import { css } from '@emotion/css';
|
import { MenuProps, Select } from 'antd';
|
||||||
import { Menu, Select } from 'antd';
|
import React, { useMemo } from 'react';
|
||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAPIClient } from '../api-client';
|
import { useAPIClient } from '../api-client';
|
||||||
import { useCurrentUserContext } from './CurrentUserProvider';
|
import { useCurrentUserContext } from './CurrentUserProvider';
|
||||||
|
|
||||||
export const ThemeSettings = () => {
|
export const useThemeSettings = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const ctx = useCurrentUserContext();
|
const ctx = useCurrentUserContext();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
return (
|
|
||||||
<Menu.Item key="theme" eventKey={'theme'}>
|
return useMemo<MenuProps['items'][0]>(() => {
|
||||||
<div
|
return {
|
||||||
className={css`
|
key: 'theme',
|
||||||
display: flex;
|
eventKey: 'theme',
|
||||||
align-items: center;
|
label: (
|
||||||
justify-content: space-between;
|
<div
|
||||||
`}
|
style={{
|
||||||
>
|
display: 'flex',
|
||||||
{t('Theme')}{' '}
|
alignItems: 'center',
|
||||||
<Select
|
justifyContent: 'space-between',
|
||||||
style={{ minWidth: 100 }}
|
|
||||||
bordered={false}
|
|
||||||
defaultValue={localStorage.getItem('NOCOBASE_THEME')}
|
|
||||||
options={[
|
|
||||||
{ label: t('Default theme'), value: 'default' },
|
|
||||||
{ label: t('Compact theme'), value: 'compact' },
|
|
||||||
]}
|
|
||||||
onChange={async (value) => {
|
|
||||||
await api.resource('users').update({
|
|
||||||
filterByTk: ctx.data.data.id,
|
|
||||||
values: {
|
|
||||||
systemSettings: {
|
|
||||||
...ctx.data.data.systemSettings,
|
|
||||||
theme: value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
localStorage.setItem('NOCOBASE_THEME', value);
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</div>
|
{t('Theme')}{' '}
|
||||||
</Menu.Item>
|
<Select
|
||||||
);
|
style={{ minWidth: 100 }}
|
||||||
|
bordered={false}
|
||||||
|
defaultValue={localStorage.getItem('NOCOBASE_THEME')}
|
||||||
|
options={[
|
||||||
|
{ label: t('Default theme'), value: 'default' },
|
||||||
|
{ label: t('Compact theme'), value: 'compact' },
|
||||||
|
]}
|
||||||
|
onChange={async (value) => {
|
||||||
|
await api.resource('users').update({
|
||||||
|
filterByTk: ctx.data.data.id,
|
||||||
|
values: {
|
||||||
|
systemSettings: {
|
||||||
|
...ctx.data.data.systemSettings,
|
||||||
|
theme: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
localStorage.setItem('NOCOBASE_THEME', value);
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [ctx.data.data.id, ctx.data.data.systemSettings]);
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,8 @@ import {
|
|||||||
SchemaInitializerButtonContext,
|
SchemaInitializerButtonContext,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import { error } from '@nocobase/utils/client';
|
||||||
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
import { useChartQueryMetadataContext } from './ChartQueryMetadataProvider';
|
import { useChartQueryMetadataContext } from './ChartQueryMetadataProvider';
|
||||||
import { lang } from './locale';
|
import { lang } from './locale';
|
||||||
import { getQueryTypeSchema } from './settings/queryTypes';
|
import { getQueryTypeSchema } from './settings/queryTypes';
|
||||||
@ -21,107 +22,121 @@ export interface ChartQueryMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ChartQueryBlockInitializer = (props) => {
|
export const ChartQueryBlockInitializer = (props) => {
|
||||||
const defaultItems: any = [
|
|
||||||
{
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: lang('Select query data'),
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const { templateWrap, onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props;
|
const { templateWrap, onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props;
|
||||||
const { setVisible } = useContext(SchemaInitializerButtonContext);
|
const { setVisible } = useContext(SchemaInitializerButtonContext);
|
||||||
const [items, setItems] = useState(defaultItems);
|
|
||||||
const apiClient = useAPIClient();
|
const apiClient = useAPIClient();
|
||||||
const ctx = useChartQueryMetadataContext();
|
const ctx = useChartQueryMetadataContext();
|
||||||
const options = useContext(SchemaOptionsContext);
|
const options = useContext(SchemaOptionsContext);
|
||||||
const onAddQuery = (info) => {
|
const onAddQuery = useCallback(
|
||||||
FormDialog(
|
(info) => {
|
||||||
{
|
FormDialog(
|
||||||
sql: lang('Add SQL query'),
|
{
|
||||||
json: lang('Add JSON query'),
|
sql: lang('Add SQL query'),
|
||||||
}[info.key],
|
json: lang('Add JSON query'),
|
||||||
() => {
|
}[info.key],
|
||||||
return (
|
() => {
|
||||||
<div>
|
return (
|
||||||
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
<div>
|
||||||
<FormLayout layout={'vertical'}>
|
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
||||||
<SchemaComponent
|
<FormLayout layout={'vertical'}>
|
||||||
schema={{
|
<SchemaComponent
|
||||||
type: 'object',
|
schema={{
|
||||||
properties: {
|
type: 'object',
|
||||||
title: {
|
properties: {
|
||||||
title: lang('Title'),
|
title: {
|
||||||
required: true,
|
title: lang('Title'),
|
||||||
'x-component': 'Input',
|
required: true,
|
||||||
'x-decorator': 'FormItem',
|
'x-component': 'Input',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
options: getQueryTypeSchema(info.key),
|
||||||
},
|
},
|
||||||
options: getQueryTypeSchema(info.key),
|
}}
|
||||||
},
|
/>
|
||||||
}}
|
</FormLayout>
|
||||||
/>
|
</SchemaComponentOptions>
|
||||||
</FormLayout>
|
</div>
|
||||||
</SchemaComponentOptions>
|
);
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.open({
|
|
||||||
initialValues: {
|
|
||||||
type: info.key,
|
|
||||||
},
|
},
|
||||||
})
|
)
|
||||||
.then(async (values) => {
|
.open({
|
||||||
try {
|
initialValues: {
|
||||||
const { data } = await apiClient.resource('chartsQueries')?.create?.({ values });
|
type: info.key,
|
||||||
const items = (await ctx.refresh()) as any;
|
},
|
||||||
const item = items.find((item) => item.id === data?.data?.id);
|
})
|
||||||
onCreateBlockSchema({ item });
|
.then(async (values) => {
|
||||||
setVisible(false);
|
try {
|
||||||
} catch (error) {}
|
if (apiClient.resource('chartsQueries')?.create) {
|
||||||
})
|
const { data } = await apiClient.resource('chartsQueries').create({ values });
|
||||||
.catch(() => {});
|
const items = (await ctx.refresh()) as any;
|
||||||
};
|
const item = items.find((item) => item.id === data?.data?.id);
|
||||||
useEffect(() => {
|
onCreateBlockSchema({ item });
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
} catch (err) {
|
||||||
|
error(err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
error(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[apiClient, ctx, onCreateBlockSchema, options.components, options.scope, setVisible],
|
||||||
|
);
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
const defaultItems: any = [
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: lang('Select query data'),
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
const chartQueryMetadata = ctx.data;
|
const chartQueryMetadata = ctx.data;
|
||||||
if (chartQueryMetadata && Array.isArray(chartQueryMetadata)) {
|
if (chartQueryMetadata && Array.isArray(chartQueryMetadata)) {
|
||||||
setItems(
|
const item1 =
|
||||||
[
|
chartQueryMetadata.length > 0
|
||||||
chartQueryMetadata.length > 0
|
? {
|
||||||
? {
|
type: 'itemGroup',
|
||||||
type: 'itemGroup',
|
title: '{{t("Select chart query", {ns: "charts"})}}',
|
||||||
title: '{{t("Select chart query", {ns: "charts"})}}',
|
children: chartQueryMetadata,
|
||||||
children: chartQueryMetadata,
|
}
|
||||||
}
|
: null;
|
||||||
: null,
|
const item2 =
|
||||||
chartQueryMetadata.length > 0
|
chartQueryMetadata.length > 0
|
||||||
? {
|
? {
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
}
|
}
|
||||||
: null,
|
: null;
|
||||||
,
|
|
||||||
{
|
return [
|
||||||
type: 'subMenu',
|
item1,
|
||||||
title: lang('Add chart query'),
|
item2,
|
||||||
// component: AddChartQuery,
|
{
|
||||||
children: [
|
type: 'subMenu',
|
||||||
{
|
title: lang('Add chart query'),
|
||||||
key: 'sql',
|
// component: AddChartQuery,
|
||||||
type: 'item',
|
children: [
|
||||||
title: 'SQL',
|
{
|
||||||
onClick: onAddQuery,
|
key: 'sql',
|
||||||
},
|
type: 'item',
|
||||||
{
|
title: 'SQL',
|
||||||
key: 'json',
|
onClick: onAddQuery,
|
||||||
type: 'item',
|
},
|
||||||
title: 'JSON',
|
{
|
||||||
onClick: onAddQuery,
|
key: 'json',
|
||||||
},
|
type: 'item',
|
||||||
],
|
title: 'JSON',
|
||||||
},
|
onClick: onAddQuery,
|
||||||
].filter(Boolean),
|
},
|
||||||
);
|
],
|
||||||
|
},
|
||||||
|
].filter(Boolean);
|
||||||
}
|
}
|
||||||
}, []);
|
|
||||||
|
return defaultItems;
|
||||||
|
}, [ctx.data, onAddQuery]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaInitializer.Item
|
<SchemaInitializer.Item
|
||||||
icon={<TableOutlined />}
|
icon={<TableOutlined />}
|
||||||
|
@ -10,11 +10,11 @@ import {
|
|||||||
useResourceActionContext,
|
useResourceActionContext,
|
||||||
useResourceContext,
|
useResourceContext,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Button, Dropdown, Menu } from 'antd';
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useChartQueryMetadataContext } from '../ChartQueryMetadataProvider';
|
import { useChartQueryMetadataContext } from '../ChartQueryMetadataProvider';
|
||||||
import { getQueryTypeSchema } from './queryTypes';
|
|
||||||
import { lang } from '../locale';
|
import { lang } from '../locale';
|
||||||
|
import { getQueryTypeSchema } from './queryTypes';
|
||||||
|
|
||||||
const useCreateAction = () => {
|
const useCreateAction = () => {
|
||||||
const { setVisible } = useActionContext();
|
const { setVisible } = useActionContext();
|
||||||
@ -112,25 +112,40 @@ export const AddNewQuery = () => {
|
|||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [schema, setSchema] = useState({});
|
const [schema, setSchema] = useState({});
|
||||||
const form = useMemo(() => createForm(), []);
|
const form = useMemo(() => createForm(), []);
|
||||||
const menu = (
|
|
||||||
<Menu
|
const menu = useMemo<MenuProps>(() => {
|
||||||
onClick={(info) => {
|
return {
|
||||||
|
onClick: (info) => {
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
form.setValues({ type: info.key });
|
form.setValues({ type: info.key });
|
||||||
setSchema(getSchema({ type: info.key }, { form, isNewRecord: true }));
|
setSchema(getSchema({ type: info.key }, { form, isNewRecord: true }));
|
||||||
}}
|
},
|
||||||
>
|
items: [
|
||||||
<Menu.Item key={'json'}>JSON</Menu.Item>
|
{
|
||||||
<Menu.Item key={'sql'}>SQL</Menu.Item>
|
key: 'json',
|
||||||
<Menu.Item disabled key={'api'}>
|
label: 'JSON',
|
||||||
API
|
},
|
||||||
</Menu.Item>
|
{
|
||||||
<Menu.Item disabled>Collection</Menu.Item>
|
key: 'sql',
|
||||||
</Menu>
|
label: 'SQL',
|
||||||
);
|
},
|
||||||
|
{
|
||||||
|
key: 'api',
|
||||||
|
label: 'API',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'collection',
|
||||||
|
label: 'Collection',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}, [form]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionContextProvider value={{ visible, setVisible }}>
|
<ActionContextProvider value={{ visible, setVisible }}>
|
||||||
<Dropdown overlay={menu}>
|
<Dropdown menu={menu}>
|
||||||
<Button icon={<PlusOutlined />} type={'primary'}>
|
<Button icon={<PlusOutlined />} type={'primary'}>
|
||||||
{lang('Add query')} <DownOutlined />
|
{lang('Add query')} <DownOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -12,19 +12,19 @@ import { css, cx } from '@emotion/css';
|
|||||||
import { SchemaOptionsContext } from '@formily/react';
|
import { SchemaOptionsContext } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
APIClientProvider,
|
APIClientProvider,
|
||||||
collection,
|
CollectionCategroriesContext,
|
||||||
|
CollectionCategroriesProvider,
|
||||||
CollectionManagerContext,
|
CollectionManagerContext,
|
||||||
CollectionManagerProvider,
|
CollectionManagerProvider,
|
||||||
CurrentAppInfoContext,
|
CurrentAppInfoContext,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaComponentOptions,
|
SchemaComponentOptions,
|
||||||
Select,
|
Select,
|
||||||
|
collection,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
useCollectionManager,
|
useCollectionManager,
|
||||||
useCompile,
|
useCompile,
|
||||||
useCurrentAppInfo,
|
useCurrentAppInfo,
|
||||||
CollectionCategroriesProvider,
|
|
||||||
CollectionCategroriesContext,
|
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { useFullscreen } from 'ahooks';
|
import { useFullscreen } from 'ahooks';
|
||||||
import { Button, Input, Layout, Menu, Popover, Switch, Tooltip } from 'antd';
|
import { Button, Input, Layout, Menu, Popover, Switch, Tooltip } from 'antd';
|
||||||
@ -42,6 +42,7 @@ import {
|
|||||||
getDiffEdge,
|
getDiffEdge,
|
||||||
getDiffNode,
|
getDiffNode,
|
||||||
getInheritCollections,
|
getInheritCollections,
|
||||||
|
getPopupContainer,
|
||||||
useGCMTranslation,
|
useGCMTranslation,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
@ -232,10 +233,6 @@ function getEdges(edges) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPopupContainer = () => {
|
|
||||||
return document.getElementById('graph_container');
|
|
||||||
};
|
|
||||||
|
|
||||||
const CollapsedContext = createContext<any>({});
|
const CollapsedContext = createContext<any>({});
|
||||||
const formatNodeData = () => {
|
const formatNodeData = () => {
|
||||||
const layoutNodes = [];
|
const layoutNodes = [];
|
||||||
@ -1021,14 +1018,7 @@ export const GraphDrawPage = React.memo(() => {
|
|||||||
<div className={cx(collectionListClass)}>
|
<div className={cx(collectionListClass)}>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
components={{
|
components={{
|
||||||
Select: (props) => (
|
Select: (props) => <Select {...props} getPopupContainer={getPopupContainer} />,
|
||||||
<Select
|
|
||||||
{...props}
|
|
||||||
getPopupContainer={() => {
|
|
||||||
return document.getElementById('graph_container');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
AddCollectionAction,
|
AddCollectionAction,
|
||||||
}}
|
}}
|
||||||
schema={{
|
schema={{
|
||||||
@ -1100,7 +1090,7 @@ export const GraphDrawPage = React.memo(() => {
|
|||||||
},
|
},
|
||||||
collectionList: {
|
collectionList: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': () => {
|
'x-component': function Com() {
|
||||||
const { handleSearchCollection, collectionList } = useContext(CollapsedContext);
|
const { handleSearchCollection, collectionList } = useContext(CollapsedContext);
|
||||||
const [selectedKeys, setSelectKey] = useState([]);
|
const [selectedKeys, setSelectKey] = useState([]);
|
||||||
const content = (
|
const content = (
|
||||||
@ -1121,13 +1111,13 @@ export const GraphDrawPage = React.memo(() => {
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
style={{ maxHeight: '70vh', overflowY: 'auto', border: 'none' }}
|
style={{ maxHeight: '70vh', overflowY: 'auto', border: 'none' }}
|
||||||
>
|
items={[
|
||||||
<Menu.Divider />
|
{ type: 'divider' },
|
||||||
{collectionList.map((v) => {
|
...collectionList.map((v) => {
|
||||||
return (
|
return {
|
||||||
<Menu.Item
|
key: v.name,
|
||||||
key={v.name}
|
label: compile(v.title),
|
||||||
onClick={(e: any) => {
|
onClick: (e: any) => {
|
||||||
if (e.key !== selectedKeys[0]) {
|
if (e.key !== selectedKeys[0]) {
|
||||||
setSelectKey([e.key]);
|
setSelectKey([e.key]);
|
||||||
handleFiterCollections(e.key);
|
handleFiterCollections(e.key);
|
||||||
@ -1136,13 +1126,11 @@ export const GraphDrawPage = React.memo(() => {
|
|||||||
handleFiterCollections(false);
|
handleFiterCollections(false);
|
||||||
setSelectKey([]);
|
setSelectKey([]);
|
||||||
}
|
}
|
||||||
}}
|
},
|
||||||
>
|
};
|
||||||
<span>{compile(v.title)}</span>
|
}),
|
||||||
</Menu.Item>
|
]}
|
||||||
);
|
/>
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@ -1226,24 +1214,22 @@ export const GraphDrawPage = React.memo(() => {
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
style={{ maxHeight: '70vh', overflowY: 'auto', border: 'none' }}
|
style={{ maxHeight: '70vh', overflowY: 'auto', border: 'none' }}
|
||||||
>
|
items={[
|
||||||
<Menu.Divider />
|
{ type: 'divider' },
|
||||||
{menuItems.map((v) => {
|
...menuItems.map((v) => {
|
||||||
return (
|
return {
|
||||||
<Menu.Item
|
key: v.key,
|
||||||
key={v.key}
|
label: t(v.label),
|
||||||
onClick={(e: any) => {
|
onClick: (e: any) => {
|
||||||
targetGraph.connectionType = v.key;
|
targetGraph.connectionType = v.key;
|
||||||
const { filterConfig } = targetGraph;
|
const { filterConfig } = targetGraph;
|
||||||
filterConfig && handleFiterCollections(filterConfig.key);
|
filterConfig && handleFiterCollections(filterConfig.key);
|
||||||
handleSetRelationshipType(v.key);
|
handleSetRelationshipType(v.key);
|
||||||
}}
|
},
|
||||||
>
|
};
|
||||||
<span>{t(v.label)}</span>
|
}),
|
||||||
</Menu.Item>
|
]}
|
||||||
);
|
/>
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@ -1303,25 +1289,23 @@ export const GraphDrawPage = React.memo(() => {
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
style={{ maxHeight: '70vh', overflowY: 'auto', border: 'none' }}
|
style={{ maxHeight: '70vh', overflowY: 'auto', border: 'none' }}
|
||||||
>
|
items={[
|
||||||
<Menu.Divider />
|
{ type: 'divider' },
|
||||||
{menuItems.map((v) => {
|
...menuItems.map((v) => {
|
||||||
return (
|
return {
|
||||||
<Menu.Item
|
key: v.key,
|
||||||
key={v.key}
|
label: t(v.label),
|
||||||
onClick={(e: any) => {
|
onClick: (e: any) => {
|
||||||
targetGraph.direction = v.key;
|
targetGraph.direction = v.key;
|
||||||
const { filterConfig } = targetGraph;
|
const { filterConfig } = targetGraph;
|
||||||
if (filterConfig) {
|
if (filterConfig) {
|
||||||
handleFiterCollections(filterConfig.key);
|
handleFiterCollections(filterConfig.key);
|
||||||
}
|
}
|
||||||
}}
|
},
|
||||||
>
|
};
|
||||||
<span>{t(v.label)}</span>
|
}),
|
||||||
</Menu.Item>
|
]}
|
||||||
);
|
/>
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { AddCollection } from '@nocobase/client';
|
import { AddCollection } from '@nocobase/client';
|
||||||
import React from 'react';
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
import { useCancelAction } from '../action-hooks';
|
import { useCancelAction } from '../action-hooks';
|
||||||
|
import { getPopupContainer } from '../utils';
|
||||||
|
|
||||||
export const AddCollectionAction = ({ item: record }) => {
|
export const AddCollectionAction = ({ item: record }) => {
|
||||||
return (
|
return (
|
||||||
@ -17,7 +18,7 @@ export const AddCollectionAction = ({ item: record }) => {
|
|||||||
scope={{
|
scope={{
|
||||||
useCancelAction,
|
useCancelAction,
|
||||||
}}
|
}}
|
||||||
getContainer={() => document.getElementById('graph_container')}
|
getContainer={getPopupContainer}
|
||||||
>
|
>
|
||||||
<Button type="primary">
|
<Button type="primary">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
|
@ -2,6 +2,7 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { AddFieldAction as AddCollectionFieldAction } from '@nocobase/client';
|
import { AddFieldAction as AddCollectionFieldAction } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCancelAction, useCreateAction } from '../action-hooks';
|
import { useCancelAction, useCreateAction } from '../action-hooks';
|
||||||
|
import { getPopupContainer } from '../utils';
|
||||||
|
|
||||||
const useCreateCollectionField = (record) => {
|
const useCreateCollectionField = (record) => {
|
||||||
const title = record.collectionName;
|
const title = record.collectionName;
|
||||||
@ -27,7 +28,7 @@ export const AddFieldAction = ({ item: record }) => {
|
|||||||
useCancelAction,
|
useCancelAction,
|
||||||
useCreateCollectionField: () => useCreateCollectionField(record),
|
useCreateCollectionField: () => useCreateCollectionField(record),
|
||||||
}}
|
}}
|
||||||
getContainer={() => document.getElementById('graph_container')}
|
getContainer={getPopupContainer}
|
||||||
>
|
>
|
||||||
<PlusOutlined className="btn-add" id="graph_btn_add_field" />
|
<PlusOutlined className="btn-add" id="graph_btn_add_field" />
|
||||||
</AddCollectionFieldAction>
|
</AddCollectionFieldAction>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { EditOutlined } from '@ant-design/icons';
|
import { EditOutlined } from '@ant-design/icons';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import { EditCollection } from '@nocobase/client';
|
import { EditCollection } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { css } from '@emotion/css';
|
|
||||||
import { useCancelAction, useUpdateCollectionActionAndRefreshCM } from '../action-hooks';
|
import { useCancelAction, useUpdateCollectionActionAndRefreshCM } from '../action-hooks';
|
||||||
|
import { getPopupContainer } from '../utils';
|
||||||
|
|
||||||
export const EditCollectionAction = ({ item: record }) => {
|
export const EditCollectionAction = ({ item: record }) => {
|
||||||
return (
|
return (
|
||||||
@ -13,7 +14,7 @@ export const EditCollectionAction = ({ item: record }) => {
|
|||||||
useUpdateCollectionActionAndRefreshCM,
|
useUpdateCollectionActionAndRefreshCM,
|
||||||
createOnly: false,
|
createOnly: false,
|
||||||
}}
|
}}
|
||||||
getContainer={() => document.getElementById('graph_container')}
|
getContainer={getPopupContainer}
|
||||||
>
|
>
|
||||||
<EditOutlined
|
<EditOutlined
|
||||||
className={css`
|
className={css`
|
||||||
|
@ -2,6 +2,7 @@ import { EditOutlined } from '@ant-design/icons';
|
|||||||
import { EditFieldAction as EditCollectionFieldAction } from '@nocobase/client';
|
import { EditFieldAction as EditCollectionFieldAction } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCancelAction, useUpdateFieldAction } from '../action-hooks';
|
import { useCancelAction, useUpdateFieldAction } from '../action-hooks';
|
||||||
|
import { getPopupContainer } from '../utils';
|
||||||
|
|
||||||
const useUpdateCollectionField = (record) => {
|
const useUpdateCollectionField = (record) => {
|
||||||
const collectionName = record.collectionName;
|
const collectionName = record.collectionName;
|
||||||
@ -21,7 +22,7 @@ export const EditFieldAction = ({ item: record }) => {
|
|||||||
useCancelAction,
|
useCancelAction,
|
||||||
useUpdateCollectionField: () => useUpdateCollectionField(record),
|
useUpdateCollectionField: () => useUpdateCollectionField(record),
|
||||||
}}
|
}}
|
||||||
getContainer={() => document.getElementById('graph_container')}
|
getContainer={getPopupContainer}
|
||||||
>
|
>
|
||||||
<EditOutlined className="btn-edit" />
|
<EditOutlined className="btn-edit" />
|
||||||
</EditCollectionFieldAction>
|
</EditCollectionFieldAction>
|
||||||
|
@ -5,7 +5,7 @@ import { uid } from '@formily/shared';
|
|||||||
import {
|
import {
|
||||||
Action,
|
Action,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
collection,
|
CollectionCategroriesContext,
|
||||||
CollectionField,
|
CollectionField,
|
||||||
CollectionProvider,
|
CollectionProvider,
|
||||||
Form,
|
Form,
|
||||||
@ -19,15 +19,15 @@ import {
|
|||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaComponentProvider,
|
SchemaComponentProvider,
|
||||||
Select,
|
Select,
|
||||||
|
collection,
|
||||||
useCollectionManager,
|
useCollectionManager,
|
||||||
useCompile,
|
useCompile,
|
||||||
useCurrentAppInfo,
|
useCurrentAppInfo,
|
||||||
useRecord,
|
useRecord,
|
||||||
CollectionCategroriesContext,
|
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Badge, Dropdown, Popover, Tag } from 'antd';
|
import { Badge, Dropdown, Popover, Tag } from 'antd';
|
||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
import React, { useRef, useState, useContext } from 'react';
|
import React, { useContext, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
useAsyncDataSource,
|
useAsyncDataSource,
|
||||||
useCancelAction,
|
useCancelAction,
|
||||||
@ -37,7 +37,7 @@ import {
|
|||||||
useValuesFromRecord,
|
useValuesFromRecord,
|
||||||
} from '../action-hooks';
|
} from '../action-hooks';
|
||||||
import { collectiionPopoverClass, entityContainer, headClass, tableBtnClass, tableNameClass } from '../style';
|
import { collectiionPopoverClass, entityContainer, headClass, tableBtnClass, tableNameClass } from '../style';
|
||||||
import { useGCMTranslation } from '../utils';
|
import { getPopupContainer, useGCMTranslation } from '../utils';
|
||||||
import { AddFieldAction } from './AddFieldAction';
|
import { AddFieldAction } from './AddFieldAction';
|
||||||
import { CollectionNodeProvder } from './CollectionNodeProvder';
|
import { CollectionNodeProvder } from './CollectionNodeProvder';
|
||||||
import { EditCollectionAction } from './EditCollectionAction';
|
import { EditCollectionAction } from './EditCollectionAction';
|
||||||
@ -161,9 +161,7 @@ const Entity: React.FC<{
|
|||||||
|
|
||||||
confirm: {
|
confirm: {
|
||||||
title: "{{t('Delete record')}}",
|
title: "{{t('Delete record')}}",
|
||||||
getContainer: () => {
|
getContainer: getPopupContainer,
|
||||||
return document.getElementById('graph_container');
|
|
||||||
},
|
|
||||||
collectionConten: "{{t('Are you sure you want to delete it?')}}",
|
collectionConten: "{{t('Are you sure you want to delete it?')}}",
|
||||||
},
|
},
|
||||||
useAction: () => useDestroyActionAndRefreshCM({ name, id }),
|
useAction: () => useDestroyActionAndRefreshCM({ name, id }),
|
||||||
@ -233,6 +231,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
|
|||||||
return 'orange';
|
return 'orange';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const OperationButton = ({ property }) => {
|
const OperationButton = ({ property }) => {
|
||||||
const isInheritField = !(property.collectionName !== name);
|
const isInheritField = !(property.collectionName !== name);
|
||||||
return (
|
return (
|
||||||
@ -244,14 +243,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
|
|||||||
Input,
|
Input,
|
||||||
Form,
|
Form,
|
||||||
ResourceActionProvider,
|
ResourceActionProvider,
|
||||||
Select: (props) => (
|
Select: (props) => <Select {...props} getPopupContainer={getPopupContainer} />,
|
||||||
<Select
|
|
||||||
{...props}
|
|
||||||
getPopupContainer={() => {
|
|
||||||
return document.getElementById('graph_container');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Radio,
|
Radio,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
@ -334,9 +326,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
|
|||||||
`,
|
`,
|
||||||
confirm: {
|
confirm: {
|
||||||
title: "{{t('Delete record')}}",
|
title: "{{t('Delete record')}}",
|
||||||
getContainer: () => {
|
getContainer: getPopupContainer,
|
||||||
return document.getElementById('graph_container');
|
|
||||||
},
|
|
||||||
collectionConten: "{{t('Are you sure you want to delete it?')}}",
|
collectionConten: "{{t('Are you sure you want to delete it?')}}",
|
||||||
},
|
},
|
||||||
useAction: () =>
|
useAction: () =>
|
||||||
@ -409,9 +399,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
|
|||||||
property.uiSchema && (
|
property.uiSchema && (
|
||||||
<Popover
|
<Popover
|
||||||
content={CollectionConten(property)}
|
content={CollectionConten(property)}
|
||||||
getPopupContainer={() => {
|
getPopupContainer={getPopupContainer}
|
||||||
return document.getElementById('graph_container');
|
|
||||||
}}
|
|
||||||
mouseLeaveDelay={0}
|
mouseLeaveDelay={0}
|
||||||
zIndex={100}
|
zIndex={100}
|
||||||
title={
|
title={
|
||||||
@ -452,9 +440,7 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
|
|||||||
property.uiSchema && (
|
property.uiSchema && (
|
||||||
<Popover
|
<Popover
|
||||||
content={CollectionConten(property)}
|
content={CollectionConten(property)}
|
||||||
getPopupContainer={() => {
|
getPopupContainer={getPopupContainer}
|
||||||
return document.getElementById('graph_container');
|
|
||||||
}}
|
|
||||||
mouseLeaveDelay={0}
|
mouseLeaveDelay={0}
|
||||||
zIndex={100}
|
zIndex={100}
|
||||||
title={
|
title={
|
||||||
|
@ -2,6 +2,7 @@ import { CopyOutlined } from '@ant-design/icons';
|
|||||||
import { OverridingFieldAction as OverridingCollectionFieldAction } from '@nocobase/client';
|
import { OverridingFieldAction as OverridingCollectionFieldAction } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCancelAction, useCreateAction } from '../action-hooks';
|
import { useCancelAction, useCreateAction } from '../action-hooks';
|
||||||
|
import { getPopupContainer } from '../utils';
|
||||||
|
|
||||||
const useOverridingCollectionField = (record) => {
|
const useOverridingCollectionField = (record) => {
|
||||||
const collectionName = record.targetCollection;
|
const collectionName = record.targetCollection;
|
||||||
@ -21,7 +22,7 @@ export const OverrideFieldAction = ({ item: record }) => {
|
|||||||
useCancelAction,
|
useCancelAction,
|
||||||
useOverridingCollectionField: () => useOverridingCollectionField(record),
|
useOverridingCollectionField: () => useOverridingCollectionField(record),
|
||||||
}}
|
}}
|
||||||
getContainer={() => document.getElementById('graph_container')}
|
getContainer={getPopupContainer}
|
||||||
>
|
>
|
||||||
<CopyOutlined className="btn-override" />
|
<CopyOutlined className="btn-override" />
|
||||||
</OverridingCollectionFieldAction>
|
</OverridingCollectionFieldAction>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { EyeOutlined } from '@ant-design/icons';
|
import { EyeOutlined } from '@ant-design/icons';
|
||||||
import { ViewFieldAction as ViewCollectionFieldAction } from '@nocobase/client';
|
import { ViewFieldAction as ViewCollectionFieldAction } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { getPopupContainer } from '../utils';
|
||||||
|
|
||||||
export const ViewFieldAction = ({ item: record }) => {
|
export const ViewFieldAction = ({ item: record }) => {
|
||||||
return (
|
return (
|
||||||
<ViewCollectionFieldAction item={{ ...record }} getContainer={() => document.getElementById('graph_container')}>
|
<ViewCollectionFieldAction item={{ ...record }} getContainer={getPopupContainer}>
|
||||||
<EyeOutlined className="btn-view" />
|
<EyeOutlined className="btn-view" />
|
||||||
</ViewCollectionFieldAction>
|
</ViewCollectionFieldAction>
|
||||||
);
|
);
|
||||||
|
@ -532,3 +532,15 @@ export const getDiffEdge = (newEdges, oldEdges) => {
|
|||||||
}
|
}
|
||||||
return edges;
|
return edges;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let graphContainer;
|
||||||
|
/**
|
||||||
|
* 所有的 getPopupContainer 都需要保证返回的是唯一的 div。React 18 concurrent 下会反复调用该方法
|
||||||
|
* 参考:https://ant.design/docs/react/migration-v5-cn#%E5%8D%87%E7%BA%A7%E5%87%86%E5%A4%87
|
||||||
|
*/
|
||||||
|
export const getPopupContainer = () => {
|
||||||
|
if (graphContainer) {
|
||||||
|
return graphContainer;
|
||||||
|
}
|
||||||
|
return (graphContainer = document.getElementById('graph_container'));
|
||||||
|
};
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { FormOutlined } from '@ant-design/icons';
|
import { FormOutlined } from '@ant-design/icons';
|
||||||
import { SchemaInitializer } from '@nocobase/client';
|
import { SchemaInitializer } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
export const IframeBlockInitializer = (props) => {
|
export const IframeBlockInitializer = (props) => {
|
||||||
const { insert } = props;
|
const { insert } = props;
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
return (
|
||||||
<SchemaInitializer.Item
|
<SchemaInitializer.Item
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -18,7 +18,7 @@ export const ImportInitializerProvider = (props: any) => {
|
|||||||
skipScopeCheck: true,
|
skipScopeCheck: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
visible: () => {
|
visible: function useVisible() {
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
return collection.template !== 'view' && collection.template !== 'file';
|
return collection.template !== 'view' && collection.template !== 'file';
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useAPIClient, useCompile } from '@nocobase/client';
|
import { useAPIClient, useCompile } from '@nocobase/client';
|
||||||
import { useBoolean } from 'ahooks';
|
import { useBoolean } from 'ahooks';
|
||||||
import { Button, Card, Form, Input, message, Tabs } from 'antd';
|
import { Button, Card, Form, Input, Tabs, message } from 'antd';
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { MapTypes } from '../constants';
|
import { MapTypes } from '../constants';
|
||||||
import { MapConfigurationResourceKey, getSSKey, useMapConfiguration } from '../hooks';
|
import { MapConfigurationResourceKey, getSSKey, useMapConfiguration } from '../hooks';
|
||||||
@ -76,15 +76,16 @@ const Configuration = () => {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
return (
|
return (
|
||||||
<Card bordered>
|
<Card bordered>
|
||||||
<Tabs type="card">
|
<Tabs
|
||||||
{tabList.map((tab) => {
|
type="card"
|
||||||
return (
|
items={tabList.map((tab) => {
|
||||||
<Tabs.TabPane key={tab.value} tab={compile(tab.label)}>
|
return {
|
||||||
<tab.component type={tab.value} />
|
key: tab.value,
|
||||||
</Tabs.TabPane>
|
label: compile(tab.label),
|
||||||
);
|
children: <tab.component type={tab.value} />,
|
||||||
|
};
|
||||||
})}
|
})}
|
||||||
</Tabs>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { SchemaSettings, useDesignable } from '@nocobase/client';
|
import { MenuOutlined } from '@ant-design/icons';
|
||||||
import React from 'react';
|
|
||||||
import { generateNTemplate, useTranslation } from '../../../../locale';
|
|
||||||
import { Schema, useField, useFieldSchema } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { SchemaSettings, useDesignable } from '@nocobase/client';
|
||||||
import { findSchema } from '../../helpers';
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { MenuOutlined } from '@ant-design/icons';
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { generateNTemplate, useTranslation } from '../../../../locale';
|
||||||
import { PageSchema } from '../../common';
|
import { PageSchema } from '../../common';
|
||||||
|
import { findSchema } from '../../helpers';
|
||||||
|
|
||||||
export const ContainerDesigner = () => {
|
export const ContainerDesigner = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -32,6 +32,7 @@ export const ContainerDesigner = () => {
|
|||||||
style={{
|
style={{
|
||||||
borderColor: 'rgb(241, 139, 98)',
|
borderColor: 'rgb(241, 139, 98)',
|
||||||
color: 'rgb(241, 139, 98)',
|
color: 'rgb(241, 139, 98)',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
icon={<MenuOutlined />}
|
icon={<MenuOutlined />}
|
||||||
type="dashed"
|
type="dashed"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { GeneralSchemaDesigner, SchemaSettings, useDesignable } from '@nocobase/client';
|
import { MenuOutlined } from '@ant-design/icons';
|
||||||
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { SchemaSettings, useDesignable } from '@nocobase/client';
|
||||||
|
import { Button } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { generateNTemplate, useTranslation } from '../../../../locale';
|
import { generateNTemplate, useTranslation } from '../../../../locale';
|
||||||
import { useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { findGridSchema } from '../../helpers';
|
import { findGridSchema } from '../../helpers';
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import { MenuOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
export const PageDesigner = (props) => {
|
export const PageDesigner = (props) => {
|
||||||
const { showBack } = props;
|
const { showBack } = props;
|
||||||
@ -28,6 +28,7 @@ export const PageDesigner = (props) => {
|
|||||||
style={{
|
style={{
|
||||||
borderColor: 'rgb(241, 139, 98)',
|
borderColor: 'rgb(241, 139, 98)',
|
||||||
color: 'rgb(241, 139, 98)',
|
color: 'rgb(241, 139, 98)',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
icon={<MenuOutlined />}
|
icon={<MenuOutlined />}
|
||||||
type="dashed"
|
type="dashed"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
|
||||||
import { SettingsMenu, SortableItem, useDesigner } from '@nocobase/client';
|
|
||||||
import { SettingsDesigner } from './Settings.Designer';
|
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
|
import { SettingsMenu, SortableItem, useDesigner } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { SettingsDesigner } from './Settings.Designer';
|
||||||
export const InternalSettings = () => {
|
export const InternalSettings = () => {
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
return (
|
return (
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
SettingsCenterProvider,
|
SettingsCenterProvider,
|
||||||
useRequest,
|
useRequest,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Button, Dropdown, Menu } from 'antd';
|
import { Button, Dropdown } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { AppManager } from './AppManager';
|
import { AppManager } from './AppManager';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React from 'react';
|
|
||||||
import { cx } from '@emotion/css';
|
|
||||||
import { Dropdown, Menu, Button } from 'antd';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { cx } from '@emotion/css';
|
||||||
import { useAPIClient, useCompile } from '@nocobase/client';
|
import { useAPIClient, useCompile } from '@nocobase/client';
|
||||||
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { useFlowContext } from './FlowContext';
|
import { useFlowContext } from './FlowContext';
|
||||||
|
import { NAMESPACE } from './locale';
|
||||||
import { Instruction, instructions } from './nodes';
|
import { Instruction, instructions } from './nodes';
|
||||||
import { addButtonClass } from './style';
|
import { addButtonClass } from './style';
|
||||||
import { NAMESPACE } from './locale';
|
|
||||||
|
|
||||||
interface AddButtonProps {
|
interface AddButtonProps {
|
||||||
upstream;
|
upstream;
|
||||||
@ -17,72 +17,82 @@ export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { workflow, refresh } = useFlowContext() ?? {};
|
const { workflow, refresh } = useFlowContext() ?? {};
|
||||||
|
const instructionList = Array.from(instructions.getValues()) as Instruction[];
|
||||||
|
const groups = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{ key: 'control', label: `{{t("Control", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
{ key: 'collection', label: `{{t("Collection operations", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
{ key: 'manual', label: `{{t("Manual", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
{ key: 'extended', label: `{{t("Extended types", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
]
|
||||||
|
.filter((group) => instructionList.filter((item) => item.group === group.key).length)
|
||||||
|
.map((group) => {
|
||||||
|
const groupInstructions = instructionList.filter((item) => item.group === group.key);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
type: 'group',
|
||||||
|
children: groupInstructions.map((item) => ({
|
||||||
|
key: item.type,
|
||||||
|
label: item.title,
|
||||||
|
type: item.options ? 'subMenu' : null,
|
||||||
|
children: item.options
|
||||||
|
? item.options.map((option) => ({
|
||||||
|
key: option.key,
|
||||||
|
label: option.label,
|
||||||
|
}))
|
||||||
|
: null,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [instructionList]);
|
||||||
|
const resource = useMemo(() => {
|
||||||
|
if (!workflow) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return api.resource('workflows.nodes', workflow.id);
|
||||||
|
}, [workflow?.id]);
|
||||||
|
const onCreate = useCallback(
|
||||||
|
async ({ keyPath }) => {
|
||||||
|
const type = keyPath.pop();
|
||||||
|
const config = {};
|
||||||
|
const [optionKey] = keyPath;
|
||||||
|
const instruction = instructions.get(type);
|
||||||
|
if (optionKey) {
|
||||||
|
const { value } = instruction.options?.find((item) => item.key === optionKey) ?? {};
|
||||||
|
Object.assign(config, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
|
await resource.create({
|
||||||
|
values: {
|
||||||
|
type,
|
||||||
|
upstreamId: upstream?.id ?? null,
|
||||||
|
branchIndex,
|
||||||
|
title: compile(instruction.title),
|
||||||
|
config,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[branchIndex, resource?.create, upstream?.id],
|
||||||
|
);
|
||||||
|
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
|
return {
|
||||||
|
onClick: (ev) => onCreate(ev),
|
||||||
|
items: compile(groups),
|
||||||
|
};
|
||||||
|
}, [groups, onCreate]);
|
||||||
|
|
||||||
if (!workflow) {
|
if (!workflow) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const resource = api.resource('workflows.nodes', workflow.id);
|
|
||||||
|
|
||||||
async function onCreate({ keyPath }) {
|
|
||||||
const type = keyPath.pop();
|
|
||||||
const config = {};
|
|
||||||
const [optionKey] = keyPath;
|
|
||||||
const instruction = instructions.get(type);
|
|
||||||
if (optionKey) {
|
|
||||||
const { value } = instruction.options?.find((item) => item.key === optionKey) ?? {};
|
|
||||||
Object.assign(config, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: { data: node },
|
|
||||||
} = await resource.create!({
|
|
||||||
values: {
|
|
||||||
type,
|
|
||||||
upstreamId: upstream?.id ?? null,
|
|
||||||
branchIndex,
|
|
||||||
title: compile(instruction.title),
|
|
||||||
config,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
const instructionList = Array.from(instructions.getValues()) as Instruction[];
|
|
||||||
|
|
||||||
const groups = [
|
|
||||||
{ key: 'control', label: `{{t("Control", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
{ key: 'collection', label: `{{t("Collection operations", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
{ key: 'manual', label: `{{t("Manual", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
{ key: 'extended', label: `{{t("Extended types", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
]
|
|
||||||
.filter((group) => instructionList.filter((item) => item.group === group.key).length)
|
|
||||||
.map((group) => {
|
|
||||||
const groupInstructions = instructionList.filter((item) => item.group === group.key);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...group,
|
|
||||||
type: 'group',
|
|
||||||
children: groupInstructions.map((item) => ({
|
|
||||||
key: item.type,
|
|
||||||
label: item.title,
|
|
||||||
type: item.options ? 'subMenu' : null,
|
|
||||||
children: item.options
|
|
||||||
? item.options.map((option) => ({
|
|
||||||
key: option.key,
|
|
||||||
label: option.label,
|
|
||||||
}))
|
|
||||||
: null,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(addButtonClass)}>
|
<div className={cx(addButtonClass)}>
|
||||||
<Dropdown
|
<Dropdown trigger={['click']} menu={menu} disabled={workflow.executed}>
|
||||||
trigger={['click']}
|
|
||||||
overlay={<Menu onClick={(ev) => onCreate(ev)} items={compile(groups)} />}
|
|
||||||
disabled={workflow.executed}
|
|
||||||
>
|
|
||||||
<Button shape="circle" icon={<PlusOutlined />} />
|
<Button shape="circle" icon={<PlusOutlined />} />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaComponentOptions,
|
SchemaComponentOptions,
|
||||||
SettingsCenterProvider,
|
SettingsCenterProvider,
|
||||||
useCollectionDataSource
|
useCollectionDataSource,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Card } from 'antd';
|
import { Card } from 'antd';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useCallback } from 'react';
|
import { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import { observer, useForm, useField } from '@formily/react';
|
|
||||||
import { Input, Button, Dropdown, Menu, Form } from 'antd';
|
|
||||||
import { PlusOutlined, CloseCircleOutlined } from '@ant-design/icons';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
|
import { observer, useField, useForm } from '@formily/react';
|
||||||
|
import { Button, Dropdown, Form, Input, MenuProps } from 'antd';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CollectionField,
|
CollectionField,
|
||||||
@ -53,8 +53,23 @@ const CollectionFieldSet = observer(
|
|||||||
const collectionFields = useCollectionUIFields(collectionName);
|
const collectionFields = useCollectionUIFields(collectionName);
|
||||||
const fields = filter ? collectionFields.filter(filter.bind(config)) : collectionFields;
|
const fields = filter ? collectionFields.filter(filter.bind(config)) : collectionFields;
|
||||||
|
|
||||||
const unassignedFields = fields.filter((field) => !value || !(field.name in value));
|
const unassignedFields = useMemo(() => fields.filter((field) => !value || !(field.name in value)), [fields, value]);
|
||||||
const mergedDisabled = disabled || form.disabled;
|
const mergedDisabled = disabled || form.disabled;
|
||||||
|
const menu = useMemo<MenuProps>(() => {
|
||||||
|
return {
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
onChange({ ...value, [key]: null });
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
maxHeight: 300,
|
||||||
|
overflowY: 'auto',
|
||||||
|
},
|
||||||
|
items: unassignedFields.map((field) => ({
|
||||||
|
key: field.name,
|
||||||
|
label: compile(field.uiSchema?.title ?? field.name),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}, [onChange, unassignedFields, value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset
|
<fieldset
|
||||||
@ -127,21 +142,7 @@ const CollectionFieldSet = observer(
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{unassignedFields.length ? (
|
{unassignedFields.length ? (
|
||||||
<Dropdown
|
<Dropdown menu={menu}>
|
||||||
overlay={
|
|
||||||
<Menu
|
|
||||||
items={unassignedFields.map((field) => ({
|
|
||||||
key: field.name,
|
|
||||||
label: compile(field.uiSchema?.title ?? field.name),
|
|
||||||
}))}
|
|
||||||
onClick={({ key }) => onChange({ ...value, [key]: null })}
|
|
||||||
className={css`
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button icon={<PlusOutlined />}>{t('Add field')}</Button>
|
<Button icon={<PlusOutlined />}>{t('Add field')}</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
) : null}
|
) : null}
|
||||||
|
151
yarn.lock
151
yarn.lock
@ -163,10 +163,10 @@
|
|||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
rc-util "^5.9.4"
|
rc-util "^5.9.4"
|
||||||
|
|
||||||
"@ant-design/icons@^5.1.0":
|
"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.1.0":
|
||||||
version "5.1.2"
|
version "5.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.1.2.tgz#6dc30c5caff0670f11b6391f0839ed41847c72e6"
|
resolved "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.1.4.tgz#614e29e26d092c2c1c1a2acbc0d84434d8d1474e"
|
||||||
integrity sha512-sSorFv+4e5nkSq9vD7UHG2IQYUmR8jCO49+z4vn3MUeiM1G1qxCqh7JiR5pexVY0hZywHes/uxiNflAQZCWZ8Q==
|
integrity sha512-YHKL7Jx3bM12OxvtiYDon04BsBT/6LGitYEqar3GljzWaAyMOAD8i/uF1Rsi5Us/YNdWWXBGSvZV2OZWMpJlcA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ant-design/colors" "^7.0.0"
|
"@ant-design/colors" "^7.0.0"
|
||||||
"@ant-design/icons-svg" "^4.2.1"
|
"@ant-design/icons-svg" "^4.2.1"
|
||||||
@ -185,6 +185,52 @@
|
|||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
rc-util "^5.31.1"
|
rc-util "^5.31.1"
|
||||||
|
|
||||||
|
"@ant-design/pro-layout@^7.14.3":
|
||||||
|
version "7.14.3"
|
||||||
|
resolved "https://registry.npmmirror.com/@ant-design/pro-layout/-/pro-layout-7.14.3.tgz#01063883a334060c41b72ec084c3e83200673952"
|
||||||
|
integrity sha512-FcKj23f/laod2hfSSYfBbkjb9r74lZLgan8dylI+tngQnZKQc1o1a1f9If7CggkrhEdgDyIkIyFl9Ii/tfDa+A==
|
||||||
|
dependencies:
|
||||||
|
"@ant-design/icons" "^5.0.0"
|
||||||
|
"@ant-design/pro-provider" "^2.10.2"
|
||||||
|
"@ant-design/pro-utils" "^2.11.3"
|
||||||
|
"@babel/runtime" "^7.18.0"
|
||||||
|
"@umijs/route-utils" "^4.0.0"
|
||||||
|
"@umijs/use-params" "^1.0.9"
|
||||||
|
classnames "^2.3.2"
|
||||||
|
lodash.merge "^4.6.2"
|
||||||
|
omit.js "^2.0.2"
|
||||||
|
path-to-regexp "2.4.0"
|
||||||
|
rc-resize-observer "^1.1.0"
|
||||||
|
rc-util "^5.0.6"
|
||||||
|
swr "^2.0.0"
|
||||||
|
use-json-comparison "^1.0.3"
|
||||||
|
use-media-antd-query "^1.1.0"
|
||||||
|
warning "^4.0.3"
|
||||||
|
|
||||||
|
"@ant-design/pro-provider@^2.10.2":
|
||||||
|
version "2.10.2"
|
||||||
|
resolved "https://registry.npmmirror.com/@ant-design/pro-provider/-/pro-provider-2.10.2.tgz#3338afa88f35fe06da8f3573e7cf31c323cad456"
|
||||||
|
integrity sha512-I0wfnP/fJUgGrl/9P5haMtITg8oHe2NOm83QKFVjqQWGYnA2V4O5rVqD+vdWveuaJ6rtCW8mEoeelfHF6nOnxw==
|
||||||
|
dependencies:
|
||||||
|
"@ant-design/cssinjs" "^1.9.1"
|
||||||
|
"@babel/runtime" "^7.18.0"
|
||||||
|
"@ctrl/tinycolor" "^3.4.0"
|
||||||
|
rc-util "^5.0.1"
|
||||||
|
swr "^2.0.0"
|
||||||
|
|
||||||
|
"@ant-design/pro-utils@^2.11.3":
|
||||||
|
version "2.11.3"
|
||||||
|
resolved "https://registry.npmmirror.com/@ant-design/pro-utils/-/pro-utils-2.11.3.tgz#847da90763f4157bc5e273bf0aa018af13ec28ae"
|
||||||
|
integrity sha512-Sd2oHzhQlSJ0svaD3Ih3v2VeXPnYWwJ05ul+ek8i9GWAGnFWD0yDTawGj7pDZwCQwJYaLpAzPHhfNvq3Led0/w==
|
||||||
|
dependencies:
|
||||||
|
"@ant-design/icons" "^5.0.0"
|
||||||
|
"@ant-design/pro-provider" "^2.10.2"
|
||||||
|
"@babel/runtime" "^7.18.0"
|
||||||
|
classnames "^2.3.2"
|
||||||
|
dayjs "^1.11.4"
|
||||||
|
rc-util "^5.0.6"
|
||||||
|
swr "^2.0.0"
|
||||||
|
|
||||||
"@ant-design/react-slick@~0.29.1":
|
"@ant-design/react-slick@~0.29.1":
|
||||||
version "0.29.2"
|
version "0.29.2"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-0.29.2.tgz#53e6a7920ea3562eebb304c15a7fc2d7e619d29c"
|
resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-0.29.2.tgz#53e6a7920ea3562eebb304c15a7fc2d7e619d29c"
|
||||||
@ -3394,7 +3440,7 @@
|
|||||||
|
|
||||||
"@emotion/hash@^0.8.0":
|
"@emotion/hash@^0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
resolved "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||||
|
|
||||||
"@emotion/hash@^0.9.1":
|
"@emotion/hash@^0.9.1":
|
||||||
@ -3442,16 +3488,15 @@
|
|||||||
"@emotion/sheet@^1.2.2":
|
"@emotion/sheet@^1.2.2":
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
|
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
|
||||||
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
|
|
||||||
|
|
||||||
"@emotion/stylis@^0.8.4":
|
"@emotion/stylis@^0.8.4":
|
||||||
version "0.8.5"
|
version "0.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
|
resolved "https://registry.npmmirror.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
|
||||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
||||||
|
|
||||||
"@emotion/unitless@^0.7.4", "@emotion/unitless@^0.7.5":
|
"@emotion/unitless@^0.7.4", "@emotion/unitless@^0.7.5":
|
||||||
version "0.7.5"
|
version "0.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
resolved "https://registry.npmmirror.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
||||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||||
|
|
||||||
"@emotion/unitless@^0.8.1":
|
"@emotion/unitless@^0.8.1":
|
||||||
@ -7376,6 +7421,11 @@
|
|||||||
react-helmet-async "1.3.0"
|
react-helmet-async "1.3.0"
|
||||||
react-router-dom "6.3.0"
|
react-router-dom "6.3.0"
|
||||||
|
|
||||||
|
"@umijs/route-utils@^4.0.0":
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@umijs/route-utils/-/route-utils-4.0.1.tgz#156df5b3f2328059722d3ee7dd8f65e18c3cde8b"
|
||||||
|
integrity sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==
|
||||||
|
|
||||||
"@umijs/server@4.0.69":
|
"@umijs/server@4.0.69":
|
||||||
version "4.0.69"
|
version "4.0.69"
|
||||||
resolved "https://registry.yarnpkg.com/@umijs/server/-/server-4.0.69.tgz#6819612ee294ae432a37b7c673ea39e39a6aa7ef"
|
resolved "https://registry.yarnpkg.com/@umijs/server/-/server-4.0.69.tgz#6819612ee294ae432a37b7c673ea39e39a6aa7ef"
|
||||||
@ -7406,6 +7456,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@umijs/ui/-/ui-3.0.1.tgz#64ae7ef36bf9374823f7361a7a844876d96c9e06"
|
resolved "https://registry.yarnpkg.com/@umijs/ui/-/ui-3.0.1.tgz#64ae7ef36bf9374823f7361a7a844876d96c9e06"
|
||||||
integrity sha512-zcz37AJH0xt/6XVVbyO/hmsK9Hq4vH23HZ4KYVi5A8rbM9KeJkJigTS7ELOdArawZhVNGe+h3a5Oixs4a2QsWw==
|
integrity sha512-zcz37AJH0xt/6XVVbyO/hmsK9Hq4vH23HZ4KYVi5A8rbM9KeJkJigTS7ELOdArawZhVNGe+h3a5Oixs4a2QsWw==
|
||||||
|
|
||||||
|
"@umijs/use-params@^1.0.9":
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.npmmirror.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52"
|
||||||
|
integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w==
|
||||||
|
|
||||||
"@umijs/utils@3.5.39", "@umijs/utils@^3.5.20":
|
"@umijs/utils@3.5.39", "@umijs/utils@^3.5.20":
|
||||||
version "3.5.39"
|
version "3.5.39"
|
||||||
resolved "https://registry.yarnpkg.com/@umijs/utils/-/utils-3.5.39.tgz#86a57e262bfbdbc356cdcacaf415b11b1a450e12"
|
resolved "https://registry.yarnpkg.com/@umijs/utils/-/utils-3.5.39.tgz#86a57e262bfbdbc356cdcacaf415b11b1a450e12"
|
||||||
@ -11113,13 +11168,15 @@ dateformat@^3.0.0:
|
|||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||||
|
|
||||||
dayjs@1.x, dayjs@^1.11.1, dayjs@^1.11.7, dayjs@^1.9.1, dayjs@~1.11.5:
|
dayjs@1.x, dayjs@^1.11.1, dayjs@^1.11.4, dayjs@^1.11.7, dayjs@^1.9.1, dayjs@~1.11.5:
|
||||||
version "1.11.7"
|
version "1.11.8"
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea"
|
||||||
|
integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==
|
||||||
|
|
||||||
dayjs@~1.8.24:
|
dayjs@~1.8.24:
|
||||||
version "1.8.36"
|
version "1.8.36"
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.36.tgz#be36e248467afabf8f5a86bae0de0cdceecced50"
|
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.8.36.tgz#be36e248467afabf8f5a86bae0de0cdceecced50"
|
||||||
|
integrity sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==
|
||||||
|
|
||||||
debounce-fn@^4.0.0:
|
debounce-fn@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@ -19239,6 +19296,11 @@ omit-deep@0.3.0:
|
|||||||
is-plain-object "^2.0.1"
|
is-plain-object "^2.0.1"
|
||||||
unset-value "^0.1.1"
|
unset-value "^0.1.1"
|
||||||
|
|
||||||
|
omit.js@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.npmmirror.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f"
|
||||||
|
integrity sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==
|
||||||
|
|
||||||
on-exit-leak-free@^0.2.0:
|
on-exit-leak-free@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209"
|
resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209"
|
||||||
@ -19874,6 +19936,11 @@ path-to-regexp@2.2.1:
|
|||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
|
||||||
|
|
||||||
|
path-to-regexp@2.4.0:
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704"
|
||||||
|
integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==
|
||||||
|
|
||||||
path-to-regexp@^6.1.0:
|
path-to-regexp@^6.1.0:
|
||||||
version "6.2.1"
|
version "6.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
|
||||||
@ -21883,17 +21950,17 @@ rc-upload@~4.3.0:
|
|||||||
classnames "^2.2.5"
|
classnames "^2.2.5"
|
||||||
rc-util "^5.2.0"
|
rc-util "^5.2.0"
|
||||||
|
|
||||||
rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.4.0, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4:
|
rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.31.1, rc-util@^5.4.0, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4:
|
||||||
version "5.29.3"
|
version "5.33.0"
|
||||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.29.3.tgz#dc02b7b2103468e9fdf14e0daa58584f47898e37"
|
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.33.0.tgz#8e673700e467b24c722014f2fe2f2f77aa6c2c07"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
react-is "^16.12.0"
|
react-is "^16.12.0"
|
||||||
|
|
||||||
rc-util@^5.21.5, rc-util@^5.25.2, rc-util@^5.27.1, rc-util@^5.28.0, rc-util@^5.31.1, rc-util@^5.33.0:
|
rc-util@^5.21.5, rc-util@^5.27.1, rc-util@^5.28.0, rc-util@^5.33.0:
|
||||||
version "5.33.0"
|
version "5.33.1"
|
||||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.33.0.tgz#8e673700e467b24c722014f2fe2f2f77aa6c2c07"
|
resolved "https://registry.npmmirror.com/rc-util/-/rc-util-5.33.1.tgz#96e5814400e04b819bace502b6ca40a67f3be37f"
|
||||||
integrity sha512-mq2NkEAnHklq4fgU/JqjiE0PS8+8u33gEWw2bDUNDPck3OroPpSgw/8oEyuFrvPgaZEmt9BgQdh59JfQt2cU+w==
|
integrity sha512-oMs2OIV/2lUCF8nllevzLccneyxAzdSOaHSs5y91qOLdqaLbIMsuL49C6/DhF/WKMqiAKEKGdVk2F1sB5HQe9A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
react-is "^16.12.0"
|
react-is "^16.12.0"
|
||||||
@ -21906,16 +21973,7 @@ rc-util@^5.30.0:
|
|||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
react-is "^16.12.0"
|
react-is "^16.12.0"
|
||||||
|
|
||||||
rc-virtual-list@^3.2.0:
|
rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.8, rc-virtual-list@^3.5.2:
|
||||||
version "3.4.13"
|
|
||||||
resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.13.tgz#20acc934b263abcf7b7c161f50ef82281b2f7e8d"
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.20.0"
|
|
||||||
classnames "^2.2.6"
|
|
||||||
rc-resize-observer "^1.0.0"
|
|
||||||
rc-util "^5.15.0"
|
|
||||||
|
|
||||||
rc-virtual-list@^3.4.8, rc-virtual-list@^3.5.2:
|
|
||||||
version "3.5.2"
|
version "3.5.2"
|
||||||
resolved "https://registry.npmmirror.com/rc-virtual-list/-/rc-virtual-list-3.5.2.tgz#5e1028869bae900eacbae6788d4eca7210736006"
|
resolved "https://registry.npmmirror.com/rc-virtual-list/-/rc-virtual-list-3.5.2.tgz#5e1028869bae900eacbae6788d4eca7210736006"
|
||||||
integrity sha512-sE2G9hTPjVmatQni8OP2Kx33+Oth6DMKm67OblBBmgMBJDJQOOFpSGH7KZ6Pm85rrI2IGxDRXZCr0QhYOH2pfQ==
|
integrity sha512-sE2G9hTPjVmatQni8OP2Kx33+Oth6DMKm67OblBBmgMBJDJQOOFpSGH7KZ6Pm85rrI2IGxDRXZCr0QhYOH2pfQ==
|
||||||
@ -24256,25 +24314,21 @@ stylehacks@^4.0.0:
|
|||||||
|
|
||||||
stylelint-config-recommended@^7.0.0:
|
stylelint-config-recommended@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz#7497372ae83ab7a6fffc18d7d7b424c6480ae15e"
|
resolved "https://registry.npmmirror.com/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz#7497372ae83ab7a6fffc18d7d7b424c6480ae15e"
|
||||||
integrity sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==
|
integrity sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==
|
||||||
|
|
||||||
stylelint-config-standard@25.0.0:
|
stylelint-config-standard@25.0.0:
|
||||||
version "25.0.0"
|
version "25.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz#2c916984e6655d40d6e8748b19baa8603b680bff"
|
resolved "https://registry.npmmirror.com/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz#2c916984e6655d40d6e8748b19baa8603b680bff"
|
||||||
integrity sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==
|
integrity sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==
|
||||||
dependencies:
|
dependencies:
|
||||||
stylelint-config-recommended "^7.0.0"
|
stylelint-config-recommended "^7.0.0"
|
||||||
|
|
||||||
stylis@4.2.0, stylis@^4.0.13:
|
stylis@4.2.0, stylis@^4.0.13, stylis@^4.1.2:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
|
resolved "https://registry.npmmirror.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
|
||||||
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
|
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
|
||||||
|
|
||||||
stylis@^4.1.2:
|
|
||||||
version "4.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7"
|
|
||||||
|
|
||||||
superagent@^8.0.5:
|
superagent@^8.0.5:
|
||||||
version "8.0.9"
|
version "8.0.9"
|
||||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.0.9.tgz#2c6fda6fadb40516515f93e9098c0eb1602e0535"
|
resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.0.9.tgz#2c6fda6fadb40516515f93e9098c0eb1602e0535"
|
||||||
@ -24409,6 +24463,13 @@ svgson@^4.1.0:
|
|||||||
omit-deep "0.3.0"
|
omit-deep "0.3.0"
|
||||||
xml-reader "2.4.3"
|
xml-reader "2.4.3"
|
||||||
|
|
||||||
|
swr@^2.0.0:
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.npmmirror.com/swr/-/swr-2.1.5.tgz#688effa719c03f6d35c66decbb0f8e79c7190399"
|
||||||
|
integrity sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==
|
||||||
|
dependencies:
|
||||||
|
use-sync-external-store "^1.2.0"
|
||||||
|
|
||||||
symbol-tree@^3.2.2, symbol-tree@^3.2.4:
|
symbol-tree@^3.2.2, symbol-tree@^3.2.4:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
@ -25641,6 +25702,16 @@ use-isomorphic-layout-effect@^1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
|
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
|
||||||
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
|
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
|
||||||
|
|
||||||
|
use-json-comparison@^1.0.3:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.npmmirror.com/use-json-comparison/-/use-json-comparison-1.0.6.tgz#a012bbc258ce745db1f56745dc653f575226cb21"
|
||||||
|
integrity sha512-xPadt5yMRbEmVfOSGFSMqjjICrq7nLbfSH3rYIXsrtcuFX7PmbYDN/ku8ObBn3v8o/yZelO1OxUS5+5TI3+fUw==
|
||||||
|
|
||||||
|
use-media-antd-query@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/use-media-antd-query/-/use-media-antd-query-1.1.0.tgz#f083ad7e292c1c0261b6bbfaac0edc3e0920d85d"
|
||||||
|
integrity sha512-B6kKZwNV4R+l4Rl11sWO7HqOay9alzs1Vp1b4YJqjz33YxbltBCZtt/yxXxkXN9rc1S7OeEL/GbwC30Wmqhw6Q==
|
||||||
|
|
||||||
use-memo-one@^1.1.1:
|
use-memo-one@^1.1.1:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
||||||
@ -25889,7 +25960,7 @@ vite-node@0.32.0:
|
|||||||
|
|
||||||
vite@4.3.1:
|
vite@4.3.1:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.1.tgz#9badb1377f995632cdcf05f32103414db6fbb95a"
|
resolved "https://registry.npmmirror.com/vite/-/vite-4.3.1.tgz#9badb1377f995632cdcf05f32103414db6fbb95a"
|
||||||
integrity sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==
|
integrity sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.17.5"
|
esbuild "^0.17.5"
|
||||||
@ -25898,9 +25969,9 @@ vite@4.3.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
"vite@^3.0.0 || ^4.0.0", vite@^4.3.8:
|
"vite@^3.0.0 || ^4.0.0", vite@^4.3.9:
|
||||||
version "4.3.9"
|
version "4.3.9"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d"
|
resolved "https://registry.npmmirror.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d"
|
||||||
integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==
|
integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.17.5"
|
esbuild "^0.17.5"
|
||||||
|
Loading…
Reference in New Issue
Block a user