assistant-todo/src/ui/task/calendar/CalShow.tsx

386 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import React, {Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState} from "react";
import {Calendar, dateFnsLocalizer, dayjsLocalizer, Event, SlotInfo, View} from 'react-big-calendar'
import dayjs, {Dayjs} from 'dayjs'
import 'react-big-calendar/lib/css/react-big-calendar.css'
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, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
import {useSearchParams} from "next/dist/client/components/navigation";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
import {Request, 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 {editExpectAPI, getTaskAndScheduleRecordAPI} from "@/lib/task/calendar/service";
import TaskNameAndIcon from "@/components/TaskNameAndIcon";
import {TaskWebSelectVO} from "@/lib/task/project/definitions";
import {message} from "antd";
import ClickRecord from "@/components/ClickRecord";
import {editClickRecordRangeAPI} from "@/components/service/ScheduleTask";
/**
* 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] = useState<View>('week');
const [date, setDate] = useState<Date>(new Date());
const clickRef = useRef<number | undefined | null>(null)
// 展示在页面的任务,默认获取当前月的信息。
const [events, setEvents] = useState<TaskEvent[]>([]);
const [open, setOpen] = useState(false);
const [openClickRecord, setOpenClickRecord] = useState(false);
const [description, setDescription] = useState('');
const [operationId, setOperationId] = useState(-1);
const [itemId, setItemId] = useState('-1');
const [recordId,setRecordId] = useState();
const [taskName, setTaskName] = useState("");
const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>();
const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>();
const [range, setRange] = useState<{ start: Date; end: Date }>({
start: dayjs(date).startOf('week').toDate(),
end: dayjs(date).endOf('week').toDate()
});
const {taskState:state,taskTypeList} = useContext(LocalContext);
const [searchObject,setSearchObject] =
useState<Request<TaskWebSelectVO>>({
pageSize: 9999,
pageNumber: 1,
data:{
treeList:true,
treeFilter:true,
treeOrList:false,
expectedStartTime:range.start,
expectedEndTime:range.end
}
})
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[] = []
if (pid != null) {
searchList.push({name: "pid", value: pid, operateType: "="},
{name: 'ALL-CHILD', value: "true", operateType: "ALL-CHILD"},
{name: 'TREE-FILTER', value: "true", operateType: "TREE-FILTER"},
);
searchObject.data.pid=pid
setSearchObject({...searchObject})
}
loadData(searchList);
};
useEffect(() => {
console.log("CalShow:useEffect:range", range)
// const searchListE = []
if (pid != null) {
// searchListE.push(
// {name: "pid", value: pid, operateType: "="},
// {name: 'ALL-CHILD', value: "true", operateType: "ALL-CHILD"},
// {name: 'TREE-FILTER', value: "true", operateType: "TREE-FILTER"},
// );
searchObject.data.pid=pid
setSearchObject({...searchObject})
}
// searchListE.push({name: 'expectedStartTime', value: range.start, operateType: ">="})
// searchListE.push({name: 'expectedStartTime', value: range.end, operateType: "<="})
// loadData(searchListE);
loadData()
/**
* 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), range]);
const calMessages = {
week: '周',
work_week: '工作周',
day: '天',
month: '月',
previous: '前',
next: '后',
today: '当下',
agenda: '日程'
}
const loadData = (searchList?: SearchObject[]) => {
if (state.length > 0) {
// searchList.push({name: 'state', value: state, operateType: "IN"})
searchObject.data.state=state
}
if (taskTypeList.length>0){
searchObject.data.taskTypeList=taskTypeList
}
searchObject.data.expectedStartTime=range.start
searchObject.data.expectedEndTime=range.end
setSearchObject({...searchObject})
getTaskAndScheduleRecordAPI(searchObject).then(responseD => {
if (responseD.status.success) {
let result: TaskEvent[] = responseD.data.content.map<TaskEvent>(taskState => {
return {
start: dayjs(taskState.expectedStartTime).toDate(),
end: dayjs(taskState.expectedEndTime).toDate(),
title: <Fragment><TaskNameAndIcon task={taskState}/><div>{taskState.description}</div></Fragment>,
// style: {
// backgroundColor: 'green',
// color: 'white',
// },
name:taskState.name,
resource: taskState.id,
id: taskState.id,
state: taskState.state,
priority: taskState.priority,
taskType:taskState.taskType
}
});
console.log('responseD.data.content:', result)
setEvents([...result])
}
})
// searchList.push({name: 'expectedEndTime', value: dayjs(date).endOf('month'), operateType: "NOT NULL"})
// let request = JSON.stringify({
// pageSize: 9999,
// pageNumber: 1,
// data: searchList,
// startTime: range.start,
// // startTime:dayjs(range.start).format('YYYY-MM-DD HH:mm:ss'),
// endTime: range.end,
// // endTime:dayjs(range.end).format('YYYY-MM-DD HH:mm:ss'),
// startColumn: "expected_start_time",
// endColumn: "expected_end_time"
// })
// getTaskTreeResult(request).then(responseD => {
// if (responseD.status.success) {
// let result: TaskEvent[] = responseD.data.content.map<TaskEvent>(taskState => {
// return {
// start: dayjs(taskState.expectedStartTime).toDate(),
// end: dayjs(taskState.expectedEndTime).toDate(),
// title: <TaskNameAndIcon task={taskState}/>,
// resource: taskState.id,
// id: taskState.id,
// state: taskState.state,
// priority: taskState.priority
// }
// });
// console.log('responseD.data.content:', result)
// setEvents([...result])
// }
// })
}
const reloadData = () => {
setOpen(false)
handleNavigate(expectedStartTime ? expectedStartTime.toDate() : date)
}
const handleSelectSlot = useCallback(
({start, end}: SlotInfo) => {
setExpectedEndTime(dayjs(end))
setExpectedStartTime(dayjs(start))
setOperationId(OPERATION_BUTTON_TYPE.ADD)
setDescription("添加任务")
setOpen(true);
},
[setEvents]
)
const handleSelectEvent = useCallback(
(event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout()
clickRef.current = window.setTimeout(() => {
if(event.taskType=='4'){
setTaskName(event.name||"")
setRecordId(event.id)
setOpenClickRecord(true);
}else {
setOperationId(OPERATION_BUTTON_TYPE.DETAIL)
setDescription("任务详情")
setItemId(event.resource)
setOpen(true);
}
}, 250)
},
[]
)
const {defaultDate, scrollToTime} = useMemo(
() => ({
defaultDate: new Date(2015, 3, 12),
scrollToTime: new Date(1970, 1, 1, 6),
}),
[]
)
const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout()
clickRef.current = window.setTimeout(() => {
if (event.taskType=='4'||event.taskType=='5'){
message.error("计划双击完成,非计划双击无效果。")
return;
}
// 数据落库
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);
let result: TaskEvent[] = [];
if (existing !== undefined && filtered !== undefined) {
result = [...filtered, {...existing, state: 7}];
}
let strings = state.split(",");
console.log('result', result, strings)
return result.filter((ev: TaskEvent) => !ev.state || strings.indexOf(ev.state.toString()) >= 0);
})
}, 250)
}
const moveEvent = useCallback(
async ({event, start, end, isAllDay: droppedOnAllDaySlot = false}: EventInteractionArgs<TaskEvent>) => {
console.log("onEventResize || onEventDrop :", start, end, event)
const {allDay} = event
if (!allDay && droppedOnAllDaySlot) {
event.allDay = true
}
if (event.taskType=='4'){
editClickRecordRangeAPI({
id:event.id,
startDate:dayjs(start!).toDate(),
recordDate:dayjs(end!).toDate(),
});
}else {
editExpectAPI({
expectedStartTime: start,
expectedEndTime: end,
id: 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]
)
const rangeChange = (rangeLet: Date[] | { start: Date; end: Date }, current?: View | undefined) => {
console.log("rangeChange:", rangeLet, (current ? current : view))
// view 为天的时候类型为数组index:0为当天
if ((current ? current : view) === "day" && Array.isArray(rangeLet)) {
if (range.start.valueOf() > rangeLet[0].valueOf()) {
setRange({...range, start: rangeLet[0]})
} else if (range.end.valueOf() < rangeLet[0].valueOf()) {
setRange({...range, end: rangeLet[0]})
}
}
// 为周的时候类型为数组,周一到周日七天
if ((current ? current : view) === "week" && Array.isArray(rangeLet)) {
if (range.start.valueOf() > rangeLet[0].valueOf()) {
setRange({...range, start: rangeLet[0]})
}
if (range.end.valueOf() < rangeLet[6].valueOf()) {
setRange({...range, end: rangeLet[6]})
}
}
// 为周的时候类型为对象
if ((current ? current : view) === "month" && rangeLet && !Array.isArray(rangeLet)) {
if (range.start.valueOf() > rangeLet.start.valueOf()) {
setRange({...range, start: rangeLet.start})
}
if (range.end.valueOf() < rangeLet.end.valueOf()) {
setRange({...range, end: rangeLet.end})
}
}
}
return <div className="App" style={{height: '90vh'}}>
{open && <DetailModelForm operationId={operationId}
description={description}
open={open}
haveButton={false}
itemId={itemId}
reloadData={reloadData} expectedStartTime={expectedStartTime}
closeOpen={() => setOpen(false)}
expectedEndTime={expectedEndTime}/>}
{openClickRecord &&
<ClickRecord recordId={recordId} taskName={taskName}
openClickRecord={openClickRecord} setOpenClickRecord={setOpenClickRecord}
reloadData={loadData}/>}
<DragAndDropCalendar
// 本地设置
localizer={localizer}
messages={calMessages}
// 修改style
eventPropGetter={eventPropGetter}
events={events}
// 界面
view={view}
// 界面改变
onView={handleViewChange}
onRangeChange={rangeChange}
// 时间
date={date}
// 条目信息改变
onNavigate={handleNavigate}
// 点击
selectable
scrollToTime={scrollToTime}
// 双击
onDoubleClickEvent={doubleClick}
// 点击任务
onSelectEvent={handleSelectEvent}
// 点击空白处
onSelectSlot={handleSelectSlot}
// 改变时间长短
resizable
onEventResize={moveEvent}
onEventDrop={moveEvent}
/>
</div>
}
export default CalShow;