diff --git a/packages/core/client/src/block-provider/DetailsBlockProvider.tsx b/packages/core/client/src/block-provider/DetailsBlockProvider.tsx index 42450701b0..908bcd9e65 100644 --- a/packages/core/client/src/block-provider/DetailsBlockProvider.tsx +++ b/packages/core/client/src/block-provider/DetailsBlockProvider.tsx @@ -141,6 +141,7 @@ export const useDetailsBlockProps = () => { ctx.form .reset() .then(() => { + ctx.form.setInitialValues(data || {}); ctx.form.setValues(data || {}); }) .catch(console.error); diff --git a/packages/core/client/src/modules/popup/PopupContextProvider.tsx b/packages/core/client/src/modules/popup/PopupContextProvider.tsx index 4aead69568..488aa5abdc 100644 --- a/packages/core/client/src/modules/popup/PopupContextProvider.tsx +++ b/packages/core/client/src/modules/popup/PopupContextProvider.tsx @@ -17,7 +17,10 @@ import { PopupVisibleProvider, PopupVisibleProviderContext } from '../../schema- * @param props * @returns */ -export const PopupContextProvider: React.FC = (props) => { +export const PopupContextProvider: React.FC<{ + visible?: boolean; + setVisible?: (visible: boolean) => void; +}> = (props) => { const [visible, setVisible] = useState(false); const { visible: visibleWithURL, setVisible: setVisibleWithURL } = useContext(PopupVisibleProviderContext) || { visible: false, @@ -26,10 +29,11 @@ export const PopupContextProvider: React.FC = (props) => { const fieldSchema = useFieldSchema(); const _setVisible = useCallback( (value: boolean): void => { + props.setVisible?.(value); setVisible?.(value); setVisibleWithURL?.(value); }, - [setVisibleWithURL], + [props, setVisibleWithURL], ); const openMode = fieldSchema['x-component-props']?.['openMode'] || 'drawer'; const openSize = fieldSchema['x-component-props']?.['openSize']; @@ -37,7 +41,7 @@ export const PopupContextProvider: React.FC = (props) => { return ( void; } -interface PopupProps { +export interface PopupProps { params: PopupParams; context: PopupContext; /** diff --git a/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx b/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx index 36b8502f39..ea021fc252 100644 --- a/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx +++ b/packages/core/client/src/schema-component/antd/page/PopupSettingsProvider.tsx @@ -7,13 +7,31 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useCallback } from 'react'; +import React, { FC, useCallback, useMemo } from 'react'; + +const PopupSettingsContext = React.createContext({ + enableURL: true, +}); + +export const PopupSettingsProvider: FC<{ + /** + * @default true + */ + enableURL?: boolean; +}> = (props) => { + const { enableURL = true } = props; + const value = useMemo(() => ({ enableURL }), [enableURL]); + + return {props.children}; +}; /** * Hook for accessing the popup settings. * @returns The popup settings. */ export const usePopupSettings = () => { + const { enableURL } = React.useContext(PopupSettingsContext); + const isPopupVisibleControlledByURL = useCallback(() => { const pathname = window.location.pathname; const hash = window.location.hash; @@ -21,8 +39,8 @@ export const usePopupSettings = () => { const isNewMobileMode = pathname?.includes('/m/'); const isPCMode = pathname?.includes('/admin/'); - return (isPCMode || isNewMobileMode) && !isOldMobileMode; - }, []); + return (isPCMode || isNewMobileMode) && !isOldMobileMode && enableURL; + }, [enableURL]); return { /** 弹窗窗口的显隐是否由 URL 控制 */ diff --git a/packages/core/client/src/schema-component/antd/page/index.ts b/packages/core/client/src/schema-component/antd/page/index.ts index db761fc2f3..477ed6ef08 100644 --- a/packages/core/client/src/schema-component/antd/page/index.ts +++ b/packages/core/client/src/schema-component/antd/page/index.ts @@ -15,3 +15,4 @@ export * from './Page.Settings'; export { PagePopups } from './PagePopups'; export { storePopupContext } from './pagePopupUtils'; export * from './PageTab.Settings'; +export { PopupSettingsProvider } from './PopupSettingsProvider'; diff --git a/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx b/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx index 6e73c22aa1..3a7242de7c 100644 --- a/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx +++ b/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx @@ -127,7 +127,16 @@ export const getPopupPathFromParams = (params: PopupParams) => { * Note: use this hook in a plugin is not recommended * @returns */ -export const usePopupUtils = () => { +export const usePopupUtils = ( + options: { + /** + * when the popup does not support opening via URL, you can control the display status of the popup through this method + * @param visible + * @returns + */ + setVisible?: (visible: boolean) => void; + } = {}, +) => { const navigate = useNavigateNoUpdate(); const location = useLocationNoUpdate(); const fieldSchema = useFieldSchema(); @@ -141,14 +150,16 @@ export const usePopupUtils = () => { const { params: popupParams } = useCurrentPopupContext(); const service = useDataBlockRequest(); const { isPopupVisibleControlledByURL } = usePopupSettings(); - const { setVisible: setVisibleFromAction } = useContext(ActionContext); + const { setVisible: _setVisibleFromAction } = useContext(ActionContext); const { updatePopupContext } = usePopupContextInActionOrAssociationField(); + const currentPopupContext = useCurrentPopupContext(); const getSourceId = useCallback( (_parentRecordData?: Record) => (_parentRecordData || parentRecord?.data)?.[cm.getSourceKeyByAssociation(association)], [parentRecord, association], ); - const currentPopupUidWithoutOpened = fieldSchema?.['x-uid']; + + const setVisibleFromAction = options.setVisible || _setVisibleFromAction; const getNewPathname = useCallback( ({ @@ -199,6 +210,7 @@ export const usePopupUtils = () => { parentRecordData, collectionNameUsedInURL, popupUidUsedInURL, + customActionSchema, }: { recordData?: Record; parentRecordData?: Record; @@ -206,11 +218,13 @@ export const usePopupUtils = () => { collectionNameUsedInURL?: string; /** if this value exists, it will be saved in the URL */ popupUidUsedInURL?: string; + customActionSchema?: ISchema; } = {}) => { if (!isPopupVisibleControlledByURL()) { return setVisibleFromAction?.(true); } + const currentPopupUidWithoutOpened = customActionSchema?.['x-uid'] || fieldSchema?.['x-uid']; const sourceId = getSourceId(parentRecordData); recordData = recordData || record?.data; @@ -227,7 +241,7 @@ export const usePopupUtils = () => { } storePopupContext(currentPopupUidWithoutOpened, { - schema: fieldSchema, + schema: customActionSchema || fieldSchema, record: new CollectionRecord({ isNew: false, data: recordData }), parentRecord: parentRecordData ? new CollectionRecord({ isNew: false, data: parentRecordData }) : parentRecord, service, @@ -237,7 +251,7 @@ export const usePopupUtils = () => { sourceId, }); - updatePopupContext(getPopupContext()); + updatePopupContext(getPopupContext(), customActionSchema); navigate(withSearchParams(`${url}${pathname}`)); }, @@ -256,7 +270,6 @@ export const usePopupUtils = () => { isPopupVisibleControlledByURL, getSourceId, getPopupContext, - currentPopupUidWithoutOpened, ], ); @@ -317,6 +330,7 @@ export const usePopupUtils = () => { closePopup, savePopupSchemaToSchema, getPopupSchemaFromSchema, + context: currentPopupContext, /** * @deprecated * TODO: remove this diff --git a/packages/core/client/src/schema-component/antd/page/usePopupContextInActionOrAssociationField.ts b/packages/core/client/src/schema-component/antd/page/usePopupContextInActionOrAssociationField.ts index 200e6acdfb..a854eab15a 100644 --- a/packages/core/client/src/schema-component/antd/page/usePopupContextInActionOrAssociationField.ts +++ b/packages/core/client/src/schema-component/antd/page/usePopupContextInActionOrAssociationField.ts @@ -29,18 +29,19 @@ export const usePopupContextInActionOrAssociationField = () => { const { dn } = useDesignable(); const updatePopupContext = useCallback( - (context: PopupContext) => { + (context: PopupContext, customSchema?: ISchema) => { + customSchema = customSchema || fieldSchema; context = _.omitBy(context, _.isNil) as PopupContext; - if (_.isEqual(context, getPopupContextFromActionOrAssociationFieldSchema(fieldSchema))) { + if (_.isEqual(context, getPopupContextFromActionOrAssociationFieldSchema(customSchema))) { return; } - fieldSchema[CONTEXT_SCHEMA_KEY] = context; + customSchema[CONTEXT_SCHEMA_KEY] = context; return dn.emit('patch', { schema: { - 'x-uid': fieldSchema['x-uid'], + 'x-uid': customSchema['x-uid'], [CONTEXT_SCHEMA_KEY]: context, }, }); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditActionDecorator.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditActionDecorator.tsx new file mode 100644 index 0000000000..354fa1cfb2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditActionDecorator.tsx @@ -0,0 +1,19 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { ACLActionProvider, PopupSettingsProvider } from '@nocobase/client'; +import React, { FC } from 'react'; + +export const BulkEditActionDecorator: FC = (props) => { + return ( + + {props.children} + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx index c1f52a22bb..83d2149023 100644 --- a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx @@ -9,6 +9,7 @@ import { Plugin, useActionAvailable } from '@nocobase/client'; import { bulkEditActionSettings, deprecatedBulkEditActionSettings } from './BulkEditAction.Settings'; +import { BulkEditActionDecorator } from './BulkEditActionDecorator'; import { BulkEditActionInitializer } from './BulkEditActionInitializer'; import { BulkEditBlockInitializers_deprecated, @@ -25,7 +26,7 @@ import { BulkEditField } from './component/BulkEditField'; import { useCustomizeBulkEditActionProps } from './utils'; export class PluginActionBulkEditClient extends Plugin { async load() { - this.app.addComponents({ BulkEditField }); + this.app.addComponents({ BulkEditField, BulkEditActionDecorator }); this.app.addScopes({ useCustomizeBulkEditActionProps }); this.app.schemaSettingsManager.add(deprecatedBulkEditActionSettings); this.app.schemaSettingsManager.add(bulkEditActionSettings); @@ -45,7 +46,7 @@ export class PluginActionBulkEditClient extends Plugin { Component: BulkEditActionInitializer, schema: { 'x-align': 'right', - 'x-decorator': 'ACLActionProvider', + 'x-decorator': 'BulkEditActionDecorator', 'x-action': 'customize:bulkEdit', 'x-toolbar': 'ActionSchemaToolbar', 'x-settings': 'actionSettings:bulkEdit', diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx index 361ac3f4e3..95907e04f7 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx @@ -13,6 +13,7 @@ import { ActionContextProvider, CollectionProvider_deprecated, FormBlockContext, + PopupSettingsProvider, RecordProvider, fetchTemplateData, useACLActionParamsContext, @@ -203,7 +204,9 @@ export const DuplicateAction = observer( {/* 这里的 record 就是弹窗中创建表单的 sourceRecord */} - + + + diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionDecorator.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionDecorator.tsx new file mode 100644 index 0000000000..dfe9a85390 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionDecorator.tsx @@ -0,0 +1,15 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { ACLActionProvider } from '@nocobase/client'; +import React, { FC } from 'react'; + +export const DuplicateActionDecorator: FC = (props) => { + return {props.children}; +}; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx index fd18e8a8af..a145dc72dd 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx @@ -19,7 +19,7 @@ export const DuplicateActionInitializer = (props) => { 'x-acl-action': 'create', title: '{{ t("Duplicate") }}', 'x-component': 'Action.Link', - 'x-decorator': 'ACLActionProvider', + 'x-decorator': 'DuplicateActionDecorator', 'x-component-props': { openMode: defaultOpenMode, component: 'DuplicateAction', diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts index 882b100e64..af78cfde3e 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts @@ -10,6 +10,7 @@ import { Plugin, useActionAvailable } from '@nocobase/client'; import { DuplicateAction } from './DuplicateAction'; import { deprecatedDuplicateActionSettings, duplicateActionSettings } from './DuplicateAction.Settings'; +import { DuplicateActionDecorator } from './DuplicateActionDecorator'; import { DuplicateActionInitializer } from './DuplicateActionInitializer'; import { DuplicatePluginProvider } from './DuplicatePluginProvider'; @@ -19,6 +20,7 @@ export class PluginActionDuplicateClient extends Plugin { this.app.addComponents({ DuplicateActionInitializer, DuplicateAction, + DuplicateActionDecorator, }); this.app.schemaSettingsManager.add(deprecatedDuplicateActionSettings); this.app.schemaSettingsManager.add(duplicateActionSettings); @@ -31,7 +33,7 @@ export class PluginActionDuplicateClient extends Plugin { 'x-action': 'duplicate', 'x-toolbar': 'ActionSchemaToolbar', 'x-settings': 'actionSettings:duplicate', - 'x-decorator': 'ACLActionProvider', + 'x-decorator': 'DuplicateActionDecorator', 'x-component-props': { type: 'primary', }, diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx index 7f8a13407c..1e485890c2 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx @@ -10,12 +10,12 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { RecursionField, Schema, observer, useFieldSchema } from '@formily/react'; import { - ActionContextProvider, + PopupContextProvider, RecordProvider, - VariablePopupRecordProvider, getLabelFormatValue, useCollection, useCollectionParentRecordData, + usePopupUtils, useProps, withDynamicSchemaProps, } from '@nocobase/client'; @@ -23,10 +23,11 @@ import { parseExpression } from 'cron-parser'; import type { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; import get from 'lodash/get'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { Calendar as BigCalendar, View, dayjsLocalizer } from 'react-big-calendar'; import * as dates from 'react-big-calendar/lib/utils/dates'; import { i18nt, useTranslation } from '../../locale'; +import { CalendarRecordViewer, findEventSchema } from './CalendarRecordViewer'; import Header from './components/Header'; import { CalendarToolbarContext } from './context'; import GlobalStyle from './global.style'; @@ -160,54 +161,24 @@ const useEvents = (dataSource: any, fieldNames: any, date: Date, view: (typeof W }, [dataSource, fieldNames.start, fieldNames.end, fieldNames.id, fieldNames.title, date, view, t]); }; -const CalendarRecordViewer = (props) => { - const { visible, setVisible, record } = props; - const { t } = useTranslation(); - const collection = useCollection(); - const parentRecordData = useCollectionParentRecordData(); - const fieldSchema = useFieldSchema(); - const eventSchema: Schema = useMemo( - () => - fieldSchema.reduceProperties((buf, current) => { - if (current['x-component'].endsWith('.Event')) { - return current; - } - return buf; - }, null), - [], - ); - - const close = useCallback(() => { - setVisible(false); - }, []); - - return ( - eventSchema && ( - - - - - - - - - - ) - ); -}; - export const Calendar: any = withDynamicSchemaProps( observer( (props: any) => { + const [visible, setVisible] = useState(false); + const { openPopup } = usePopupUtils({ + setVisible, + }); + // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema const { dataSource, fieldNames, showLunar } = useProps(props); const height = useCalenderHeight(); const [date, setDate] = useState(new Date()); const [view, setView] = useState('month'); const events = useEvents(dataSource, fieldNames, date, view); - const [visible, setVisible] = useState(false); const [record, setRecord] = useState({}); const { wrapSSR, hashId, componentCls: containerClassName } = useStyle(); + const parentRecordData = useCollectionParentRecordData(); + const fieldSchema = useFieldSchema(); const components = useMemo(() => { return { @@ -247,50 +218,57 @@ export const Calendar: any = withDynamicSchemaProps( }; return wrapSSR(
- - - { - console.log('onSelectSlot', slotInfo); - }} - onDoubleClickEvent={() => { - console.log('onDoubleClickEvent'); - }} - onSelectEvent={(event) => { - const record = dataSource?.find((item) => item[fieldNames.id] === event.id); - if (!record) { - return; - } - record.__event = { ...event, start: formatDate(dayjs(event.start)), end: formatDate(dayjs(event.end)) }; - - setRecord(record); - setVisible(true); - }} - formats={{ - monthHeaderFormat: 'YYYY-M', - agendaDateFormat: 'M-DD', - dayHeaderFormat: 'YYYY-M-DD', - dayRangeHeaderFormat: ({ start, end }, culture, local) => { - if (dates.eq(start, end, 'month')) { - return local.format(start, 'YYYY-M', culture); + + + + + + { + console.log('onSelectSlot', slotInfo); + }} + onDoubleClickEvent={() => { + console.log('onDoubleClickEvent'); + }} + onSelectEvent={(event) => { + const record = dataSource?.find((item) => item[fieldNames.id] === event.id); + if (!record) { + return; } - return `${local.format(start, 'YYYY-M', culture)} - ${local.format(end, 'YYYY-M', culture)}`; - }, - }} - components={components} - localizer={localizer} - /> + record.__event = { ...event, start: formatDate(dayjs(event.start)), end: formatDate(dayjs(event.end)) }; + + setRecord(record); + openPopup({ + recordData: record, + customActionSchema: findEventSchema(fieldSchema), + }); + }} + formats={{ + monthHeaderFormat: 'YYYY-M', + agendaDateFormat: 'M-DD', + dayHeaderFormat: 'YYYY-M-DD', + dayRangeHeaderFormat: ({ start, end }, culture, local) => { + if (dates.eq(start, end, 'month')) { + return local.format(start, 'YYYY-M', culture); + } + return `${local.format(start, 'YYYY-M', culture)} - ${local.format(end, 'YYYY-M', culture)}`; + }, + }} + components={components} + localizer={localizer} + /> +
, ); }, diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/CalendarRecordViewer.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/CalendarRecordViewer.tsx new file mode 100644 index 0000000000..ebace0bdbd --- /dev/null +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/CalendarRecordViewer.tsx @@ -0,0 +1,35 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { RecursionField, Schema, useFieldSchema } from '@formily/react'; +import React, { FC, useMemo } from 'react'; + +export const CalendarRecordViewer: FC = (props) => { + const fieldSchema = useFieldSchema(); + const eventSchema: Schema = useMemo(() => findEventSchema(fieldSchema), [fieldSchema]); + + if (!eventSchema) { + return null; + } + + return ; +}; + +export function findEventSchema(schema: Schema) { + if (schema['x-component'].endsWith('.Event')) { + return schema; + } + + return schema.reduceProperties((buf, current) => { + if (current['x-component'].endsWith('.Event')) { + return current; + } + return buf; + }, null); +} diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Event.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Event.tsx index 5e08c41e85..e9fbc44955 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Event.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Event.tsx @@ -8,11 +8,35 @@ */ import { observer } from '@formily/react'; -import React from 'react'; +import { + PopupContextProvider, + useActionContext, + useCollection, + useCollectionRecordData, + VariablePopupRecordProvider, +} from '@nocobase/client'; +import React, { useCallback } from 'react'; +import { DeleteEventContext } from './Calendar'; export const Event = observer( (props) => { - return <>{props.children}; + const { visible, setVisible } = useActionContext(); + const recordData = useCollectionRecordData(); + const collection = useCollection(); + + const close = useCallback(() => { + setVisible(false); + }, [setVisible]); + + return ( + + + + {props.children} + + + + ); }, { displayName: 'Event' }, ); diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/Event.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/Event.tsx index 5e08c41e85..c41d86b4cd 100644 --- a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/Event.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/Event.tsx @@ -8,11 +8,28 @@ */ import { observer } from '@formily/react'; +import { + PopupContextProvider, + useActionContext, + useCollection, + useCollectionRecordData, + VariablePopupRecordProvider, +} from '@nocobase/client'; import React from 'react'; export const Event = observer( (props) => { - return <>{props.children}; + const { visible, setVisible } = useActionContext(); + const recordData = useCollectionRecordData(); + const collection = useCollection(); + + return ( + + + {props.children} + + + ); }, { displayName: 'Event' }, ); diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/GanttRecordViewer.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/GanttRecordViewer.tsx new file mode 100644 index 0000000000..65b33a519d --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/GanttRecordViewer.tsx @@ -0,0 +1,23 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { RecursionField, useFieldSchema } from '@formily/react'; +import { Schema } from '@nocobase/utils'; +import React, { FC } from 'react'; + +export const GanttRecordViewer: FC = (props) => { + const fieldSchema = useFieldSchema(); + const eventSchema: Schema = fieldSchema.properties.detail; + + if (!eventSchema) { + return null; + } + + return ; +}; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx index a656a6d081..3e495a47e6 100644 --- a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx @@ -8,17 +8,16 @@ */ import { css, cx } from '@emotion/css'; -import { RecursionField, Schema, useFieldSchema } from '@formily/react'; +import { RecursionField, useFieldSchema } from '@formily/react'; import { - ActionContextProvider, + PopupContextProvider, RecordProvider, - VariablePopupRecordProvider, useAPIClient, useBlockRequestContext, - useCollection, useCollectionParentRecordData, useCurrentAppInfo, useDesignable, + usePopupUtils, useProps, useTableBlockContext, useToken, @@ -26,7 +25,7 @@ import { } from '@nocobase/client'; import { Spin, message } from 'antd'; import { debounce } from 'lodash'; -import React, { SyntheticEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { SyntheticEvent, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useGanttBlockContext } from '../../GanttBlockProvider'; import { convertToBarTasks } from '../../helpers/bar-helper'; @@ -41,6 +40,7 @@ import { GridProps } from '../grid/grid'; import { HorizontalScroll } from '../other/horizontal-scroll'; import { StandardTooltipContent, Tooltip } from '../other/tooltip'; import { VerticalScroll } from '../other/vertical-scroll'; +import { GanttRecordViewer } from './GanttRecordViewer'; import useStyles from './style'; import { TaskGantt } from './task-gantt'; import { TaskGanttContentProps } from './task-gantt-content'; @@ -49,34 +49,7 @@ const getColumnWidth = (dataSetLength: any, clientWidth: any) => { const columnWidth = clientWidth / dataSetLength > 50 ? Math.floor(clientWidth / dataSetLength) + 20 : 50; return columnWidth; }; -export const DeleteEventContext = React.createContext({ - close: () => {}, -}); -const GanttRecordViewer = (props) => { - const { visible, setVisible, record } = props; - const { t } = useTranslation(); - const collection = useCollection(); - const parentRecordData = useCollectionParentRecordData(); - const fieldSchema = useFieldSchema(); - const eventSchema: Schema = fieldSchema.properties.detail; - const close = useCallback(() => { - setVisible(false); - }, []); - return ( - eventSchema && ( - - - - - - - - - - ) - ); -}; const debounceHandleTaskChange = debounce(async (task: Task, resource, fieldNames, service, t) => { await resource.update({ filterByTk: task.id, @@ -160,7 +133,11 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => { return { viewMode, dates: seedDates(startDate, endDate, viewMode) }; }); const [visible, setVisible] = useState(false); + const { openPopup } = usePopupUtils({ + setVisible, + }); const [record, setRecord] = useState({}); + const parentRecordData = useCollectionParentRecordData(); const [currentViewDate, setCurrentViewDate] = useState(undefined); const [taskListWidth, setTaskListWidth] = useState(0); const [svgContainerWidth, setSvgContainerWidth] = useState(0); @@ -473,7 +450,10 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => { return; } setRecord(recordData); - setVisible(true); + openPopup({ + recordData, + customActionSchema: fieldSchema.properties.detail, + }); }; const gridProps: GridProps = { columnWidth, @@ -536,55 +516,59 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => { `)} ref={ganttRef} > - - - -
- - {ganttEvent.changedTask && ( - + + + + + +
+ + {ganttEvent.changedTask && ( + + )} + - )} - - - - -
+ + + +
+ ); }); diff --git a/packages/plugins/@nocobase/plugin-map/src/client/block/MapBlock.tsx b/packages/plugins/@nocobase/plugin-map/src/client/block/MapBlock.tsx index 1915fa44a4..52157ee421 100644 --- a/packages/plugins/@nocobase/plugin-map/src/client/block/MapBlock.tsx +++ b/packages/plugins/@nocobase/plugin-map/src/client/block/MapBlock.tsx @@ -8,15 +8,29 @@ */ import { + PopupContextProvider, useCollection_deprecated, useCollectionManager_deprecated, + usePopupUtils, useProps, withDynamicSchemaProps, } from '@nocobase/client'; import React, { useMemo } from 'react'; import { MapBlockComponent } from '../components'; +import { MapBlockDrawer } from '../components/MapBlockDrawer'; export const MapBlock = withDynamicSchemaProps((props) => { + const { context } = usePopupUtils(); + + // only render the popup + if (context.currentLevel) { + return ( + + + + ); + } + // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema const { fieldNames } = useProps(props); diff --git a/packages/plugins/@nocobase/plugin-map/src/client/components/AMap/Block.tsx b/packages/plugins/@nocobase/plugin-map/src/client/components/AMap/Block.tsx index 50e5d8ed70..7238c7e88b 100644 --- a/packages/plugins/@nocobase/plugin-map/src/client/components/AMap/Block.tsx +++ b/packages/plugins/@nocobase/plugin-map/src/client/components/AMap/Block.tsx @@ -8,11 +8,8 @@ */ import { CheckOutlined, EnvironmentOutlined, ExpandOutlined } from '@ant-design/icons'; -import { RecursionField, useFieldSchema } from '@formily/react'; import { - ActionContextProvider, RecordProvider, - VariablePopupRecordProvider, css, getLabelFormatValue, useCollection, @@ -21,14 +18,16 @@ import { useCollection_deprecated, useCompile, useFilterAPI, + usePopupUtils, useProps, } from '@nocobase/client'; import { useMemoizedFn } from 'ahooks'; import { Button, Space } from 'antd'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { defaultImage, selectedImage } from '../../constants'; import { useMapTranslation } from '../../locale'; import { getSource } from '../../utils'; +import { MapBlockDrawer } from '../MapBlockDrawer'; import { AMapComponent, AMapForwardedRefProps } from './Map'; export const AMapBlock = (props) => { @@ -50,6 +49,9 @@ export const AMapBlock = (props) => { const selectingModeRef = useRef(selectingMode); selectingModeRef.current = selectingMode; const { fields } = useCollection(); + const parentRecordData = useCollectionParentRecordData(); + const { openPopup } = usePopupUtils(); + const labelUiSchema = fields.find((v) => v.name === fieldNames?.marker)?.uiSchema; const setOverlayOptions = (overlay: AMap.Polygon | AMap.Marker, state?: boolean) => { const extData = overlay.getExtData(); @@ -199,6 +201,9 @@ export const AMapBlock = (props) => { if (data) { setRecord(data); + openPopup({ + recordData: data, + }); } }; o.on('click', onClick); @@ -244,7 +249,17 @@ export const AMapBlock = (props) => { }); events.forEach((e) => e()); }; - }, [dataSource, isMapInitialization, fieldNames, name, primaryKey, collectionField.type, isConnected, lineSort]); + }, [ + dataSource, + isMapInitialization, + fieldNames, + name, + primaryKey, + collectionField.type, + isConnected, + lineSort, + openPopup, + ]); useEffect(() => { setTimeout(() => { @@ -307,7 +322,9 @@ export const AMapBlock = (props) => { ) : null} - + + + { ); }; -const MapBlockDrawer = (props) => { - const { setVisible, record } = props; - const collection = useCollection(); - const parentRecordData = useCollectionParentRecordData(); - const fieldSchema = useFieldSchema(); - const schema = useMemo( - () => - fieldSchema.reduceProperties((buf, current) => { - if (current.name === 'drawer') { - return current; - } - return buf; - }, null), - [fieldSchema], - ); - - return ( - schema && ( - - - - - - - - ) - ); -}; - function clearSelected(marker: AMap.Marker | AMap.Polygon | AMap.Polyline | AMap.Circle) { if ((marker as AMap.Marker).dom) { (marker as AMap.Marker).dom.style.filter = 'none'; diff --git a/packages/plugins/@nocobase/plugin-map/src/client/components/GoogleMaps/Block.tsx b/packages/plugins/@nocobase/plugin-map/src/client/components/GoogleMaps/Block.tsx index 716768183f..41d6e66971 100644 --- a/packages/plugins/@nocobase/plugin-map/src/client/components/GoogleMaps/Block.tsx +++ b/packages/plugins/@nocobase/plugin-map/src/client/components/GoogleMaps/Block.tsx @@ -8,11 +8,8 @@ */ import { CheckOutlined, EnvironmentOutlined, ExpandOutlined } from '@ant-design/icons'; -import { RecursionField, Schema, useFieldSchema } from '@formily/react'; import { - ActionContextProvider, RecordProvider, - VariablePopupRecordProvider, css, getLabelFormatValue, useCollection, @@ -21,14 +18,16 @@ import { useCollection_deprecated, useCompile, useFilterAPI, + usePopupUtils, useProps, } from '@nocobase/client'; import { useMemoizedFn } from 'ahooks'; import { Button, Space } from 'antd'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { defaultImage, selectedImage } from '../../constants'; import { useMapTranslation } from '../../locale'; import { getSource } from '../../utils'; +import { MapBlockDrawer } from '../MapBlockDrawer'; import { GoogleMapForwardedRefProps, GoogleMapsComponent, OverlayOptions } from './Map'; import { getIcon } from './utils'; @@ -69,8 +68,10 @@ export const GoogleMapsBlock = (props) => { const overlaysRef = useRef([]); selectingModeRef.current = selectingMode; const { fields } = useCollection(); + const parentRecordData = useCollectionParentRecordData(); const labelUiSchema = fields.find((v) => v.name === fieldNames?.marker)?.uiSchema; const { getCollectionJoinField } = useCollectionManager_deprecated(); + const { openPopup } = usePopupUtils(); const setOverlayOptions = (overlay: google.maps.MVCObject, state?: boolean) => { const selected = typeof state !== 'undefined' ? !state : overlay.get(OVERLAY_SELECtED); @@ -234,6 +235,9 @@ export const GoogleMapsBlock = (props) => { if (data) { setRecord(data); + openPopup({ + recordData: data, + }); } }; o.addListener('click', onClick); @@ -291,7 +295,7 @@ export const GoogleMapsBlock = (props) => { }); events.forEach((e) => e()); }; - }, [dataSource, isMapInitialization, markerName, collectionField.type, isConnected]); + }, [dataSource, isMapInitialization, markerName, collectionField.type, isConnected, openPopup]); useEffect(() => { setTimeout(() => { @@ -354,7 +358,9 @@ export const GoogleMapsBlock = (props) => { ) : null} - + + + )} { ); }; -const MapBlockDrawer = (props) => { - const { setVisible, record } = props; - const collection = useCollection(); - const parentRecordData = useCollectionParentRecordData(); - const fieldSchema = useFieldSchema(); - const schema: Schema = useMemo( - () => - fieldSchema.reduceProperties((buf, current) => { - if (current.name === 'drawer') { - return current; - } - return buf; - }, null), - [fieldSchema], - ); - - return ( - schema && ( - - - - - - - - ) - ); -}; - function clearSelected(target: google.maps.Polygon) { if (target instanceof google.maps.Marker) { return target.setIcon(getIcon(defaultImage)); diff --git a/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlock.tsx b/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlock.tsx index 2fcc0d6fd2..2bdbd650ac 100644 --- a/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlock.tsx +++ b/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlock.tsx @@ -7,6 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { PopupContextProvider } from '@nocobase/client'; import React, { useMemo } from 'react'; import { useMapTranslation } from '../locale'; import { AMapBlock } from './AMap'; @@ -29,5 +30,9 @@ export const MapBlockComponent: React.FC = (props) => { return
{t(`The ${mapType} cannot found`)}
; } - return ; + return ( + + + + ); }; diff --git a/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx b/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx new file mode 100644 index 0000000000..eae0a4c608 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx @@ -0,0 +1,38 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { RecursionField, useFieldSchema } from '@formily/react'; +import { useCollection, useCollectionRecordData, VariablePopupRecordProvider } from '@nocobase/client'; +import React, { FC, useMemo } from 'react'; + +export const MapBlockDrawer: FC = (props) => { + const recordData = useCollectionRecordData(); + const collection = useCollection(); + const fieldSchema = useFieldSchema(); + const schema = useMemo( + () => + fieldSchema.reduceProperties((buf, current) => { + if (current.name === 'drawer') { + return current; + } + return buf; + }, null), + [fieldSchema], + ); + + if (!schema) { + return null; + } + + return ( + + + + ); +};