diff --git a/docker/task-manager-server.jar b/docker/task-manager-server.jar index abed5d9..164a5f6 100644 Binary files a/docker/task-manager-server.jar and b/docker/task-manager-server.jar differ diff --git a/package-lock.json b/package-lock.json index 648a1f6..49158bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "react": "^18", "react-big-calendar": "^1.12.2", "react-dom": "^18", + "sass": "^1.77.3", "tailwindcss": "3.3.3" }, "devDependencies": { @@ -4092,6 +4093,11 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6465,6 +6471,22 @@ "node": ">=10" } }, + "node_modules/sass": { + "version": "1.77.3", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.77.3.tgz", + "integrity": "sha512-WJHo+jmFp0dwRuymPmIovuxHaBntcCyja5hCB0yYY9wWrViEp4kF5Cdai98P72v6FzroPuABqu+ddLMbQWmwzA==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz", diff --git a/package.json b/package.json index 3b21c52..14aa4e2 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react": "^18", "react-big-calendar": "^1.12.2", "react-dom": "^18", + "sass": "^1.77.3", "tailwindcss": "3.3.3" }, "devDependencies": { diff --git a/src/lib/task/calendar/data.tsx b/src/lib/task/calendar/data.tsx new file mode 100644 index 0000000..afc84ab --- /dev/null +++ b/src/lib/task/calendar/data.tsx @@ -0,0 +1,6 @@ +import {Event} from "react-big-calendar"; +export interface TaskEvent extends Event { + id?: any; + state?:any; + priority?:any; +} diff --git a/src/lib/task/project/data.tsx b/src/lib/task/project/data.tsx index 94ac056..4be7e0d 100644 --- a/src/lib/task/project/data.tsx +++ b/src/lib/task/project/data.tsx @@ -1,7 +1,6 @@ import {unstable_noStore as noStore} from 'next/cache'; import axios, {AxiosResponse} from "axios"; import {DataType, DictType, ResponseVO, ResultPage} from "@/lib/definitions"; -import {message} from "antd"; export async function getTaskTreeResult(requestParam:string): Promise>> { noStore(); try { diff --git a/src/ui/task/OperationButton.tsx b/src/ui/task/OperationButton.tsx index 46b982e..f44c80e 100644 --- a/src/ui/task/OperationButton.tsx +++ b/src/ui/task/OperationButton.tsx @@ -90,12 +90,12 @@ class OperationButton extends React.Component { commonUpdate({ - updateColoumList:[{ + updateColumnList:[{ name:'state', code:'state', value:'7' }], - conditionColoumList:[{ + conditionColumnList:[{ name:'id', code:'id', operateType:'=', diff --git a/src/ui/task/calendar/CalShow.tsx b/src/ui/task/calendar/CalShow.tsx index 0de1a94..7331b04 100644 --- a/src/ui/task/calendar/CalShow.tsx +++ b/src/ui/task/calendar/CalShow.tsx @@ -1,41 +1,53 @@ 'use client' -import React, {useCallback, useContext, useEffect, useMemo} from "react"; +import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from "react"; import {Calendar, dayjsLocalizer, Event, SlotInfo, View} from 'react-big-calendar' // https://day.js.org/docs/zh-CN/get-set/get-set import dayjs, {Dayjs} from 'dayjs' import 'react-big-calendar/lib/css/react-big-calendar.css' -import {getTaskTreeResult, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data"; +import 'react-big-calendar/lib/sass/styles.scss' +import 'react-big-calendar/lib/addons/dragAndDrop/styles.scss' +import '@/ui/task/calendar/index.modules.css' +import {commonUpdate, getTaskTreeResult, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data"; import {useSearchParams} from "next/dist/client/components/navigation"; import {DetailModelForm} from "@/ui/task/project/DetailModelForm"; import {SearchObject} from "@/lib/definitions"; import LocalContext from "@/ui/LocalContent"; +import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop"; +import {TaskEvent} from "@/lib/task/calendar/data"; +import {number} from "prop-types"; /** * https://github.com/jquense/react-big-calendar?tab=readme-ov-file * @constructor */ const localizer = dayjsLocalizer(dayjs) +const DragAndDropCalendar = withDragAndDrop(Calendar) const CalShow: React.FC = () => { dayjs.locale('zh-cn') - const [view, setView] = React.useState('month'); - const [date, setDate] = React.useState(new Date()); + const [view, setView] = useState('month'); + const [date, setDate] = useState(new Date()); + const clickRef = useRef(null) // 展示在页面的任务,默认获取当前月的信息。 - const [events, setEvents] = React.useState([]); - const [open, setOpen] = React.useState(false); - const [description, setDescription] = React.useState(''); - const [operationId, setOperationId] = React.useState(-1); - const [itemId,setItemId] = React.useState(-1); - const [expectedStartTime,setExpectedStartTime] = React.useState(); - const [expectedEndTime,setExpectedEndTime] = React.useState(); - let state:string=useContext(LocalContext).taskState + const [events, setEvents] = useState([]); + const [open, setOpen] = useState(false); + const [description, setDescription] = useState(''); + const [operationId, setOperationId] = useState(-1); + const [itemId, setItemId] = useState(-1); + const [expectedStartTime, setExpectedStartTime] = useState(); + const [expectedEndTime, setExpectedEndTime] = useState(); + + let state: string = useContext(LocalContext).taskState const handleViewChange = (newView: View) => { setView(newView); }; var pid = useSearchParams().get('pid'); + function clearClickTimeout(){ + clickRef && typeof clickRef.current=== 'number' &&!isNaN(clickRef.current) && isFinite(clickRef.current)&&window.clearTimeout(clickRef.current) + } const handleNavigate = (newDate: Date) => { console.log('handleNavigate', newDate) setDate(newDate); - const searchList:SearchObject[] = [] + const searchList: SearchObject[] = [] if (pid != null) { searchList.push({name: "pid", value: pid, operateType: "="}, { name: 'TREE', @@ -65,6 +77,14 @@ const CalShow: React.FC = () => { searchListE.push({name: 'expectedStartTime', value: dayjs(date).endOf('week'), operateType: "<="}) } loadData(searchListE); + /** + * What Is This? + * This is to prevent a memory leak, in the off chance that you + * teardown your interface prior to the timed method being called. + */ + return () => { + clearClickTimeout() + } }, [useContext(LocalContext)]); const message = { week: '周', @@ -76,8 +96,8 @@ const CalShow: React.FC = () => { today: '当下', agenda: '日程' } - const loadData = (searchList:SearchObject[])=>{ - if (state.length > 0){ + const loadData = (searchList: SearchObject[]) => { + if (state.length > 0) { searchList.push({name: 'state', value: state, operateType: "IN"}) } searchList.push({name: 'expectedEndTime', value: dayjs(date).endOf('month'), operateType: "NOT NULL"}) @@ -88,20 +108,25 @@ const CalShow: React.FC = () => { }) getTaskTreeResult(request).then(responseD => { if (responseD.status.success) { - setEvents(responseD.data.content.map(taskState => { + let result:TaskEvent[] =responseD.data.content.map(taskState => { return { start: dayjs(taskState.expectedStartTime).toDate(), end: dayjs(taskState.expectedEndTime).toDate(), title: taskState.name, - resource:taskState.id + resource: taskState.id, + id:taskState.id, + state:taskState.state, + priority:taskState.priority } - })) + }); + console.log('responseD.data.content:',result) + setEvents(result) } }) } - const reloadData = ()=>{ + const reloadData = () => { setOpen(false) - handleNavigate(expectedStartTime?expectedStartTime.toDate():date) + handleNavigate(expectedStartTime ? expectedStartTime.toDate() : date) } const handleSelectSlot = useCallback( ({start, end}: SlotInfo) => { @@ -116,12 +141,15 @@ const CalShow: React.FC = () => { const handleSelectEvent = useCallback( (event: Event, e: React.SyntheticEvent) => { - // window.alert(event.title); - console.log(event) - setOperationId(OPERATION_BUTTON_TYPE.DETAIL) - setDescription("任务详情") - setItemId(event.resource) - setOpen(true); + clearClickTimeout() + clickRef.current = window.setTimeout(()=> { + // window.alert(event.title); + console.log(event) + setOperationId(OPERATION_BUTTON_TYPE.DETAIL) + setDescription("任务详情") + setItemId(event.resource) + setOpen(true); + },250) }, [] ) @@ -132,24 +160,123 @@ const CalShow: React.FC = () => { }), [] ) + + const doubleClick = (event: TaskEvent, e: React.SyntheticEvent) => { + clearClickTimeout() + clickRef.current = window.setTimeout(()=>{ + // 数据落库 + commonUpdate({ + updateColumnList: [{ + name: '任务状态', + code: 'state', + value: 7 + }], + conditionColumnList: [{ + name: 'id', + code: 'id', + operateType: '=', + value: event.resource + }] + }) + setEvents((prev: TaskEvent[]) => { + const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource); + const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource); + if (existing !== undefined) { + return [...filtered, {...existing, state:7}]; + } + if (filtered !== undefined) { + return [...filtered]; + } + return []; + }) + },250) + } + + const moveEvent = useCallback( + ({event, start, end, isAllDay: droppedOnAllDaySlot = false}: EventInteractionArgs) => { + const {allDay} = event + if (!allDay && droppedOnAllDaySlot) { + event.allDay = true + } + // 数据落库 + commonUpdate({ + updateColumnList: [{ + name: '期望开始时间', + code: 'expectedStartTime', + value: start + }, { + name: '期望结束时间', + code: 'expectedEndTime', + value: end + }], + conditionColumnList: [{ + name: 'id', + code: 'id', + operateType: '=', + value: event.resource + }] + }) + setEvents((prev: TaskEvent[]) => { + const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource); + const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource); + if (start instanceof Date && end instanceof Date && existing !== undefined) { + return [...filtered, {...existing, start, end, allDay}]; + } + if (filtered !== undefined) { + return [...filtered]; + } + return []; + }) + }, + [setEvents] + ) + + const eventPropGetter = useCallback( + (event:TaskEvent) => ({ + ...(event.state===7 + ? { className: 'completeTask' } + : event.priority===3?{ className: 'importantUrgentTask' }: + event.priority===2?{ className: 'importantNotUrgentTask' }: + event.priority===1?{ className: 'notImportantUrgentTask' }: + { className: 'notImportantNotUrgentTask' }), + }), + [setEvents] + ) return
- - } +
} diff --git a/src/ui/task/calendar/index.modules.css b/src/ui/task/calendar/index.modules.css new file mode 100644 index 0000000..b4a4847 --- /dev/null +++ b/src/ui/task/calendar/index.modules.css @@ -0,0 +1,20 @@ +.completeTask{ + background-color: deepskyblue !important; + color: black !important; +} +.importantUrgentTask{ + background-color: red !important; + color: black !important; +} +.notImportantUrgentTask{ + background-color: gray !important; + color: black !important; +} +.importantNotUrgentTask{ + background-color: yellow !important; + color: black !important; +} +.notImportantNotUrgentTask{ + background-color: green !important; + color: black !important; +} diff --git a/src/ui/task/four/TreeTable.tsx b/src/ui/task/four/TreeTable.tsx index c8ed907..4c2cca4 100644 --- a/src/ui/task/four/TreeTable.tsx +++ b/src/ui/task/four/TreeTable.tsx @@ -1,5 +1,5 @@ 'use client' -import React, {useContext, useEffect, useState} from 'react'; +import React, {useContext} from 'react'; import {ConfigProvider, Table} from 'antd'; import type {TableColumnsType, TableProps} from 'antd'; import {taskPriorityList, taskStateList} from "@/lib/task/project/data";