diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/Event.tsx b/packages/core/client/src/schema-component/antd/gantt/components/gantt/Event.tsx new file mode 100644 index 0000000000..73a9f26f6d --- /dev/null +++ b/packages/core/client/src/schema-component/antd/gantt/components/gantt/Event.tsx @@ -0,0 +1,6 @@ +import { observer } from '@formily/react'; +import React from 'react'; + +export const Event = observer((props) => { + return <>{props.children}; +}); diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx b/packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx index 4053e42017..a3a8707360 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx +++ b/packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx @@ -1,6 +1,7 @@ -import React, { useState, SyntheticEvent, useRef, useEffect, useMemo } from 'react'; +import React, { useState, SyntheticEvent, useRef, useEffect, useMemo, useCallback } from 'react'; import { useFieldSchema, Schema, RecursionField } from '@formily/react'; import { cx } from '@emotion/css'; +import { createForm } from '@formily/core'; import { Task } from '../../types/public-types'; import { GridProps } from '../grid/grid'; import { ganttDateRange, seedDates } from '../../helpers/date-helper'; @@ -16,15 +17,39 @@ import { DateSetup } from '../../types/date-setup'; import { HorizontalScroll } from '../other/horizontal-scroll'; import { removeHiddenTasks, sortTasks } from '../../helpers/other-helper'; import { wrapper } from './style'; +import { ActionContext } from '../../../action'; import { useDesignable } from '../../../../../schema-component'; -import { useGanttBlockContext, useBlockRequestContext } from '../../../../../block-provider'; - +import { useBlockRequestContext } from '../../../../../block-provider'; +import { RecordProvider } from '../../../../../record-provider'; 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 form = useMemo(() => createForm(), [record]); + const fieldSchema = useFieldSchema(); + const eventSchema: Schema = fieldSchema.properties.detail; + const close = useCallback(() => { + setVisible(false); + }, []); + return ( + eventSchema && ( + + + + + + + + ) + ); +}; export const Gantt: any = (props: any) => { const { designable } = useDesignable(); const { @@ -63,9 +88,7 @@ export const Gantt: any = (props: any) => { useProps, } = props; const { onExpanderClick, tasks } = useProps(); - // const ctx = useGanttBlockContext(); - const { resource ,service} = useBlockRequestContext(); - // console.log(ctx,useBlockRequestContext()) + const { resource, service } = useBlockRequestContext(); const fieldSchema = useFieldSchema(); const { fieldNames } = useProps(props); const viewMode = fieldNames.range || 'day'; @@ -76,6 +99,8 @@ export const Gantt: any = (props: any) => { const [startDate, endDate] = ganttDateRange(tasks, viewMode, preStepsCount); return { viewMode, dates: seedDates(startDate, endDate, viewMode) }; }); + const [visible, setVisible] = useState(false); + const [record, setRecord] = useState({}); const [currentViewDate, setCurrentViewDate] = useState(undefined); const [taskListWidth, setTaskListWidth] = useState(0); const [svgContainerWidth, setSvgContainerWidth] = useState(0); @@ -359,7 +384,7 @@ export const Gantt: any = (props: any) => { [fieldNames.progress]: task.progress / 100, }, }); - await service?.refresh() + await service?.refresh(); }; const handleTaskChange = async (task: Task) => { await resource.update({ @@ -370,7 +395,15 @@ export const Gantt: any = (props: any) => { [fieldNames.end]: task.end, }, }); - await service?.refresh() + await service?.refresh(); + }; + const handleBarClick = (data) => { + const recordData = service.data?.data?.find((item) => item.id === +data.id); + if (!recordData) { + return; + } + setRecord(recordData); + setVisible(true); }; const gridProps: GridProps = { columnWidth, @@ -412,13 +445,13 @@ export const Gantt: any = (props: any) => { onDateChange: handleTaskChange, onProgressChange: fieldNames.progress && handleProgressChange, onDoubleClick, - onClick, + onClick: handleBarClick, onDelete, }; - return (
+
diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt-content.tsx b/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt-content.tsx index 0b07d03029..4e5b56e59d 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt-content.tsx +++ b/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt-content.tsx @@ -1,16 +1,14 @@ -import React, { useEffect, useState } from "react"; -import { EventOption } from "../../types/public-types"; -import { BarTask } from "../../types/bar-task"; -import { Arrow } from "../other/arrow"; -import { handleTaskBySVGMouseEvent } from "../../helpers/bar-helper"; -import { isKeyboardEvent } from "../../helpers/other-helper"; -import { TaskItem } from "../task-item/task-item"; -import { - BarMoveAction, - GanttContentMoveAction, - GanttEvent, -} from "../../types/gantt-task-actions"; +import React, { useEffect, useState } from 'react'; +import { EventOption } from '../../types/public-types'; +import { BarTask } from '../../types/bar-task'; +import { Arrow } from '../other/arrow'; +import { handleTaskBySVGMouseEvent } from '../../helpers/bar-helper'; +import { isKeyboardEvent } from '../../helpers/other-helper'; +import { TaskItem } from '../task-item/task-item'; +import { BarMoveAction, GanttContentMoveAction, GanttEvent } from '../../types/gantt-task-actions'; +let lastAction = null; +let lastStart = null; export type TaskGanttContentProps = { tasks: BarTask[]; dates: Date[]; @@ -78,9 +76,7 @@ export const TaskGanttContent: React.FC = ({ event.preventDefault(); point.x = event.clientX; - const cursor = point.matrixTransform( - svg?.current.getScreenCTM()?.inverse() - ); + const cursor = point.matrixTransform(svg?.current.getScreenCTM()?.inverse()); const { isChanged, changedTask } = handleTaskBySVGMouseEvent( cursor.x, @@ -89,7 +85,7 @@ export const TaskGanttContent: React.FC = ({ xStep, timeStep, initEventX1Delta, - rtl + rtl, ); if (isChanged) { setGanttEvent({ action: ganttEvent.action, changedTask }); @@ -98,14 +94,11 @@ export const TaskGanttContent: React.FC = ({ const handleMouseUp = async (event: MouseEvent) => { const { action, originalSelectedTask, changedTask } = ganttEvent; - if (!changedTask || !point || !svg?.current || !originalSelectedTask) - return; + if (!changedTask || !point || !svg?.current || !originalSelectedTask) return; event.preventDefault(); point.x = event.clientX; - const cursor = point.matrixTransform( - svg?.current.getScreenCTM()?.inverse() - ); + const cursor = point.matrixTransform(svg?.current.getScreenCTM()?.inverse()); const { changedTask: newChangedTask } = handleTaskBySVGMouseEvent( cursor.x, action as BarMoveAction, @@ -113,7 +106,7 @@ export const TaskGanttContent: React.FC = ({ xStep, timeStep, initEventX1Delta, - rtl + rtl, ); const isNotLikeOriginal = @@ -122,23 +115,16 @@ export const TaskGanttContent: React.FC = ({ originalSelectedTask.progress !== newChangedTask.progress; // remove listeners - svg.current.removeEventListener("mousemove", handleMouseMove); - svg.current.removeEventListener("mouseup", handleMouseUp); - setGanttEvent({ action: "" }); + svg.current.removeEventListener('mousemove', handleMouseMove); + svg.current.removeEventListener('mouseup', handleMouseUp); + setGanttEvent({ action: '' }); setIsMoving(false); // custom operation start - let operationSuccess:any = true; - if ( - (action === "move" || action === "end" || action === "start") && - onDateChange && - isNotLikeOriginal - ) { + let operationSuccess: any = true; + if ((action === 'move' || action === 'end' || action === 'start') && onDateChange && isNotLikeOriginal) { try { - const result = await onDateChange( - newChangedTask, - newChangedTask.barChildren - ); + const result = await onDateChange(newChangedTask, newChangedTask.barChildren); if (result !== undefined) { operationSuccess = result; } @@ -147,10 +133,7 @@ export const TaskGanttContent: React.FC = ({ } } else if (onProgressChange && isNotLikeOriginal) { try { - const result = await onProgressChange( - newChangedTask, - newChangedTask.barChildren - ); + const result = await onProgressChange(newChangedTask, newChangedTask.barChildren); if (result !== undefined) { operationSuccess = result; } @@ -167,14 +150,14 @@ export const TaskGanttContent: React.FC = ({ if ( !isMoving && - (ganttEvent.action === "move" || - ganttEvent.action === "end" || - ganttEvent.action === "start" || - ganttEvent.action === "progress") && + (ganttEvent.action === 'move' || + ganttEvent.action === 'end' || + ganttEvent.action === 'start' || + ganttEvent.action === 'progress') && svg?.current ) { - svg.current.addEventListener("mousemove", handleMouseMove); - svg.current.addEventListener("mouseup", handleMouseUp); + svg.current.addEventListener('mousemove', handleMouseMove); + svg.current.addEventListener('mouseup', handleMouseUp); setIsMoving(true); } }, [ @@ -198,16 +181,16 @@ export const TaskGanttContent: React.FC = ({ const handleBarEventStart = async ( action: GanttContentMoveAction, task: BarTask, - event?: React.MouseEvent | React.KeyboardEvent + event?: React.MouseEvent | React.KeyboardEvent, ) => { if (!event) { - if (action === "select") { + if (action === 'select') { setSelectedTask(task.id); } } // Keyboard events else if (isKeyboardEvent(event)) { - if (action === "delete") { + if (action === 'delete') { if (onDelete) { try { const result = await onDelete(task); @@ -215,13 +198,13 @@ export const TaskGanttContent: React.FC = ({ setGanttEvent({ action, changedTask: task }); } } catch (error) { - console.error("Error on Delete. " + error); + console.error('Error on Delete. ' + error); } } } } // Mouse Events - else if (action === "mouseenter") { + else if (action === 'mouseenter') { if (!ganttEvent.action) { setGanttEvent({ action, @@ -229,22 +212,20 @@ export const TaskGanttContent: React.FC = ({ originalSelectedTask: task, }); } - } else if (action === "mouseleave") { - if (ganttEvent.action === "mouseenter") { - setGanttEvent({ action: "" }); + } else if (action === 'mouseleave') { + if (ganttEvent.action === 'mouseenter') { + setGanttEvent({ action: '' }); } - } else if (action === "dblclick") { + } else if (action === 'dblclick') { !!onDoubleClick && onDoubleClick(task); - } else if (action === "click") { + } else if (action === 'click') { !!onClick && onClick(task); } // Change task event start - else if (action === "move") { + else if (action === 'move') { if (!svg?.current || !point) return; point.x = event.clientX; - const cursor = point.matrixTransform( - svg.current.getScreenCTM()?.inverse() - ); + const cursor = point.matrixTransform(svg.current.getScreenCTM()?.inverse()); setInitEventX1Delta(cursor.x - task.x1); setGanttEvent({ action, @@ -260,11 +241,27 @@ export const TaskGanttContent: React.FC = ({ } }; + const handleBarEvent = (action, task, event) => { + if (['click'].includes(action)) { + if (!['start', 'end', 'progress'].includes(lastAction) && (!lastStart || lastStart === task.start)) { + handleBarEventStart(action, task, event); + } + lastAction = null; + lastStart = null; + } else if (['move', 'select'].includes(action)) { + lastStart = task.start; + handleBarEventStart(action, task, event); + } else { + lastStart = task.start; + lastAction = action; + handleBarEventStart(action, task, event); + } + }; return ( - {tasks.map(task => { - return task.barChildren.map(child => { + {tasks.map((task) => { + return task.barChildren.map((child) => { return ( = ({ })} - {tasks.map(task => { + {tasks.map((task) => { return ( = ({ isProgressChangeable={!!onProgressChange && !task.isDisabled} isDateChangeable={!!onDateChange && !task.isDisabled} isDelete={!task.isDisabled} - onEventStart={handleBarEventStart} + onEventStart={handleBarEvent} key={task.id} isSelected={!!selectedTask && task.id === selectedTask.id} rtl={rtl} diff --git a/packages/core/client/src/schema-component/antd/gantt/index.ts b/packages/core/client/src/schema-component/antd/gantt/index.ts index 9b63d55846..01b30a830c 100644 --- a/packages/core/client/src/schema-component/antd/gantt/index.ts +++ b/packages/core/client/src/schema-component/antd/gantt/index.ts @@ -1,13 +1,14 @@ import { ActionBar } from '../action'; import { Gantt } from './components/gantt/gantt'; import { GanttDesigner } from './Gantt.Designer'; - import { ViewMode } from './types/public-types'; +import {Event} from './components/gantt/Event'; Gantt.ActionBar = ActionBar; Gantt.ViewMode = ViewMode; Gantt.Designer = GanttDesigner; +Gantt.Event = Event; // const GanttV2 = Gantt; diff --git a/packages/core/client/src/schema-initializer/utils.ts b/packages/core/client/src/schema-initializer/utils.ts index 8e080abda5..c59b8d2653 100644 --- a/packages/core/client/src/schema-initializer/utils.ts +++ b/packages/core/client/src/schema-initializer/utils.ts @@ -1100,6 +1100,45 @@ export const createGanttBlockSchema = (options) => { }, }, }, + detail: { + type: 'void', + 'x-component': 'Gantt.Event', + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + title: '{{ t("View record") }}', + properties: { + tabs: { + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + properties: {}, + }, + }, + }, + }, + }, + }, + }, + }, + }, }, }, },