2024-05-28 06:54:58 -04:00
|
|
|
|
'use client'
|
2024-05-30 02:32:17 -04:00
|
|
|
|
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from "react";
|
2024-05-29 04:44:23 -04:00
|
|
|
|
import {Calendar, dayjsLocalizer, Event, SlotInfo, View} from 'react-big-calendar'
|
2024-05-28 06:54:58 -04:00
|
|
|
|
// https://day.js.org/docs/zh-CN/get-set/get-set
|
2024-05-29 04:44:23 -04:00
|
|
|
|
import dayjs, {Dayjs} from 'dayjs'
|
2024-05-28 06:54:58 -04:00
|
|
|
|
import 'react-big-calendar/lib/css/react-big-calendar.css'
|
2024-05-30 02:32:17 -04:00
|
|
|
|
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";
|
2024-05-28 06:54:58 -04:00
|
|
|
|
import {useSearchParams} from "next/dist/client/components/navigation";
|
2024-05-29 04:44:23 -04:00
|
|
|
|
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
|
|
|
|
|
import {SearchObject} from "@/lib/definitions";
|
|
|
|
|
import LocalContext from "@/ui/LocalContent";
|
2024-05-30 02:32:17 -04:00
|
|
|
|
import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop";
|
|
|
|
|
import {TaskEvent} from "@/lib/task/calendar/data";
|
|
|
|
|
import {number} from "prop-types";
|
2024-05-30 05:55:55 -04:00
|
|
|
|
import {DATE_TIME} from "@/lib/constants";
|
2024-05-28 06:54:58 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* https://github.com/jquense/react-big-calendar?tab=readme-ov-file
|
|
|
|
|
* @constructor
|
|
|
|
|
*/
|
|
|
|
|
const localizer = dayjsLocalizer(dayjs)
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const DragAndDropCalendar = withDragAndDrop(Calendar)
|
2024-05-28 06:54:58 -04:00
|
|
|
|
const CalShow: React.FC = () => {
|
|
|
|
|
dayjs.locale('zh-cn')
|
2024-05-31 02:00:16 -04:00
|
|
|
|
const [view, setView] = useState<View>('week');
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const [date, setDate] = useState<Date>(new Date());
|
|
|
|
|
const clickRef = useRef<number|undefined|null>(null)
|
2024-05-28 06:54:58 -04:00
|
|
|
|
// 展示在页面的任务,默认获取当前月的信息。
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const [events, setEvents] = useState<TaskEvent[]>([]);
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const [description, setDescription] = useState('');
|
|
|
|
|
const [operationId, setOperationId] = useState(-1);
|
|
|
|
|
const [itemId, setItemId] = useState(-1);
|
|
|
|
|
const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>();
|
|
|
|
|
const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>();
|
2024-05-31 02:00:16 -04:00
|
|
|
|
const [range, setRange] = useState<{start: Date; end: Date}>({
|
|
|
|
|
start: dayjs(date).startOf('week').toDate(),
|
|
|
|
|
end: dayjs(date).endOf('week').toDate()
|
|
|
|
|
});
|
2024-05-30 02:32:17 -04:00
|
|
|
|
|
|
|
|
|
let state: string = useContext(LocalContext).taskState
|
2024-05-29 04:44:23 -04:00
|
|
|
|
const handleViewChange = (newView: View) => {
|
2024-05-28 06:54:58 -04:00
|
|
|
|
setView(newView);
|
|
|
|
|
};
|
|
|
|
|
var pid = useSearchParams().get('pid');
|
2024-05-30 02:32:17 -04:00
|
|
|
|
function clearClickTimeout(){
|
|
|
|
|
clickRef && typeof clickRef.current=== 'number' &&!isNaN(clickRef.current) && isFinite(clickRef.current)&&window.clearTimeout(clickRef.current)
|
|
|
|
|
}
|
2024-05-29 04:44:23 -04:00
|
|
|
|
const handleNavigate = (newDate: Date) => {
|
|
|
|
|
console.log('handleNavigate', newDate)
|
|
|
|
|
setDate(newDate);
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const searchList: SearchObject[] = []
|
2024-05-29 04:44:23 -04:00
|
|
|
|
if (pid != null) {
|
|
|
|
|
searchList.push({name: "pid", value: pid, operateType: "="}, {
|
|
|
|
|
name: 'TREE',
|
|
|
|
|
value: "false",
|
|
|
|
|
operateType: "TREE"
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-05-31 02:00:16 -04:00
|
|
|
|
searchList.push({name: "expectedStartTime", value: range.start, operateType: ">="})
|
|
|
|
|
searchList.push({name: 'expectedStartTime', value: range.end, operateType: "<="})
|
2024-05-29 04:44:23 -04:00
|
|
|
|
loadData(searchList);
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-28 06:54:58 -04:00
|
|
|
|
useEffect(() => {
|
2024-05-31 02:00:16 -04:00
|
|
|
|
console.log("CalShow:useEffect:range",range)
|
2024-05-29 04:44:23 -04:00
|
|
|
|
const searchListE = []
|
|
|
|
|
if (pid != null) {
|
|
|
|
|
searchListE.push({name: "pid", value: pid, operateType: "="}, {
|
|
|
|
|
name: 'TREE',
|
|
|
|
|
value: "false",
|
|
|
|
|
operateType: "TREE"
|
|
|
|
|
});
|
2024-05-28 06:54:58 -04:00
|
|
|
|
}
|
2024-05-31 02:00:16 -04:00
|
|
|
|
searchListE.push({name: 'expectedStartTime', value: range.start, operateType: ">="})
|
|
|
|
|
searchListE.push({name: 'expectedStartTime', value: range.end, operateType: "<="})
|
2024-05-29 04:44:23 -04:00
|
|
|
|
loadData(searchListE);
|
2024-05-30 02:32:17 -04:00
|
|
|
|
/**
|
|
|
|
|
* 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()
|
|
|
|
|
}
|
2024-06-02 23:19:40 -04:00
|
|
|
|
}, [useContext(LocalContext),range]);
|
2024-05-29 04:44:23 -04:00
|
|
|
|
const message = {
|
|
|
|
|
week: '周',
|
|
|
|
|
work_week: '工作周',
|
|
|
|
|
day: '天',
|
|
|
|
|
month: '月',
|
|
|
|
|
previous: '前',
|
|
|
|
|
next: '后',
|
|
|
|
|
today: '当下',
|
|
|
|
|
agenda: '日程'
|
|
|
|
|
}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const loadData = (searchList: SearchObject[]) => {
|
|
|
|
|
if (state.length > 0) {
|
2024-05-29 04:44:23 -04:00
|
|
|
|
searchList.push({name: 'state', value: state, operateType: "IN"})
|
2024-05-28 06:54:58 -04:00
|
|
|
|
}
|
2024-05-29 04:44:23 -04:00
|
|
|
|
searchList.push({name: 'expectedEndTime', value: dayjs(date).endOf('month'), operateType: "NOT NULL"})
|
2024-05-28 06:54:58 -04:00
|
|
|
|
let request = JSON.stringify({
|
2024-05-29 04:44:23 -04:00
|
|
|
|
pageSize: 9999,
|
|
|
|
|
pageNumber: 1,
|
2024-05-28 06:54:58 -04:00
|
|
|
|
data: searchList
|
|
|
|
|
})
|
2024-05-29 04:44:23 -04:00
|
|
|
|
getTaskTreeResult(request).then(responseD => {
|
|
|
|
|
if (responseD.status.success) {
|
2024-05-30 02:32:17 -04:00
|
|
|
|
let result:TaskEvent[] =responseD.data.content.map<TaskEvent>(taskState => {
|
2024-05-28 06:54:58 -04:00
|
|
|
|
return {
|
2024-05-29 04:44:23 -04:00
|
|
|
|
start: dayjs(taskState.expectedStartTime).toDate(),
|
|
|
|
|
end: dayjs(taskState.expectedEndTime).toDate(),
|
|
|
|
|
title: taskState.name,
|
2024-05-30 02:32:17 -04:00
|
|
|
|
resource: taskState.id,
|
|
|
|
|
id:taskState.id,
|
|
|
|
|
state:taskState.state,
|
|
|
|
|
priority:taskState.priority
|
2024-05-28 06:54:58 -04:00
|
|
|
|
}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
});
|
|
|
|
|
console.log('responseD.data.content:',result)
|
2024-05-30 05:55:55 -04:00
|
|
|
|
setEvents([...result])
|
2024-05-28 06:54:58 -04:00
|
|
|
|
}
|
|
|
|
|
})
|
2024-05-29 04:44:23 -04:00
|
|
|
|
}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const reloadData = () => {
|
2024-05-29 04:44:23 -04:00
|
|
|
|
setOpen(false)
|
2024-05-30 02:32:17 -04:00
|
|
|
|
handleNavigate(expectedStartTime ? expectedStartTime.toDate() : date)
|
2024-05-29 04:44:23 -04:00
|
|
|
|
}
|
|
|
|
|
const handleSelectSlot = useCallback(
|
|
|
|
|
({start, end}: SlotInfo) => {
|
|
|
|
|
setExpectedEndTime(dayjs(end))
|
|
|
|
|
setExpectedStartTime(dayjs(start))
|
|
|
|
|
setOperationId(OPERATION_BUTTON_TYPE.ADD)
|
|
|
|
|
setDescription("添加任务")
|
|
|
|
|
setOpen(true);
|
|
|
|
|
},
|
|
|
|
|
[setEvents]
|
|
|
|
|
)
|
2024-05-28 06:54:58 -04:00
|
|
|
|
|
2024-05-29 04:44:23 -04:00
|
|
|
|
const handleSelectEvent = useCallback(
|
|
|
|
|
(event: Event, e: React.SyntheticEvent<HTMLElement>) => {
|
2024-05-30 02:32:17 -04:00
|
|
|
|
clearClickTimeout()
|
|
|
|
|
clickRef.current = window.setTimeout(()=> {
|
|
|
|
|
// window.alert(event.title);
|
|
|
|
|
console.log(event)
|
|
|
|
|
setOperationId(OPERATION_BUTTON_TYPE.DETAIL)
|
|
|
|
|
setDescription("任务详情")
|
|
|
|
|
setItemId(event.resource)
|
|
|
|
|
setOpen(true);
|
|
|
|
|
},250)
|
2024-05-29 04:44:23 -04:00
|
|
|
|
},
|
|
|
|
|
[]
|
|
|
|
|
)
|
|
|
|
|
const {defaultDate, scrollToTime} = useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
defaultDate: new Date(2015, 3, 12),
|
|
|
|
|
scrollToTime: new Date(1970, 1, 1, 6),
|
|
|
|
|
}),
|
|
|
|
|
[]
|
|
|
|
|
)
|
2024-05-30 02:32:17 -04:00
|
|
|
|
|
|
|
|
|
const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
|
|
|
|
|
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);
|
2024-05-30 05:55:55 -04:00
|
|
|
|
let result: TaskEvent[] = [];
|
|
|
|
|
if (existing !== undefined&&filtered !== undefined) {
|
|
|
|
|
result= [...filtered, {...existing, state:7}];
|
2024-05-30 02:32:17 -04:00
|
|
|
|
}
|
2024-05-30 05:55:55 -04:00
|
|
|
|
console.log('result',result)
|
|
|
|
|
return result;
|
2024-05-30 02:32:17 -04:00
|
|
|
|
})
|
|
|
|
|
},250)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const moveEvent = useCallback(
|
|
|
|
|
({event, start, end, isAllDay: droppedOnAllDaySlot = false}: EventInteractionArgs<TaskEvent>) => {
|
2024-05-30 05:55:55 -04:00
|
|
|
|
console.log("onEventResize || onEventDrop :",start,end)
|
2024-05-30 02:32:17 -04:00
|
|
|
|
const {allDay} = event
|
|
|
|
|
if (!allDay && droppedOnAllDaySlot) {
|
|
|
|
|
event.allDay = true
|
|
|
|
|
}
|
|
|
|
|
// 数据落库
|
|
|
|
|
commonUpdate({
|
|
|
|
|
updateColumnList: [{
|
2024-05-30 05:55:55 -04:00
|
|
|
|
name: 'expectedStartTime',
|
|
|
|
|
code: '',
|
|
|
|
|
value: start.toLocaleString()
|
2024-05-30 02:32:17 -04:00
|
|
|
|
}, {
|
2024-05-30 05:55:55 -04:00
|
|
|
|
name: 'expectedEndTime',
|
|
|
|
|
code: '',
|
|
|
|
|
value: end.toLocaleString()
|
2024-05-30 02:32:17 -04:00
|
|
|
|
}],
|
|
|
|
|
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]
|
|
|
|
|
)
|
2024-05-31 02:36:21 -04:00
|
|
|
|
const rangeChange = (rangeLet: Date[]|{ start: Date; end: Date },current?:View|undefined)=>{
|
|
|
|
|
console.log("rangeChange:",rangeLet,(current?current:view))
|
2024-05-31 02:00:16 -04:00
|
|
|
|
// view 为天的时候类型为数组,index:0为当天
|
2024-05-31 02:36:21 -04:00
|
|
|
|
if ((current?current:view)==="day"&&Array.isArray(rangeLet)) {
|
2024-05-31 02:00:16 -04:00
|
|
|
|
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]})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 为周的时候类型为数组,周一到周日七天
|
2024-05-31 02:36:21 -04:00
|
|
|
|
if ((current?current:view)==="week"&&Array.isArray(rangeLet)){
|
2024-05-31 02:00:16 -04:00
|
|
|
|
if (range.start.valueOf()>rangeLet[0].valueOf()){
|
|
|
|
|
setRange({...range,start:rangeLet[0]})
|
|
|
|
|
}
|
|
|
|
|
if (range.end.valueOf()<rangeLet[6].valueOf()){
|
|
|
|
|
setRange({...range,end:rangeLet[6]})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 为周的时候类型为对象
|
2024-05-31 02:36:21 -04:00
|
|
|
|
if ((current?current:view)==="month"&& rangeLet && !Array.isArray(rangeLet)){
|
2024-05-31 02:00:16 -04:00
|
|
|
|
if (range.start.valueOf()>rangeLet.start.valueOf()){
|
|
|
|
|
setRange({...range,start:rangeLet.start})
|
|
|
|
|
}
|
|
|
|
|
if (range.end.valueOf()<rangeLet.end.valueOf()){
|
|
|
|
|
setRange({...range,end:rangeLet.end})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2024-05-29 04:44:23 -04:00
|
|
|
|
return <div className="App" style={{height: '90vh'}}>
|
2024-05-30 02:32:17 -04:00
|
|
|
|
{open&&<DetailModelForm operationId={operationId} description={description} open={open} haveButton={false}
|
2024-05-30 22:56:18 -04:00
|
|
|
|
itemId={itemId} pid={pid?Number(pid):0}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
reloadData={reloadData} expectedStartTime={expectedStartTime}
|
|
|
|
|
expectedEndTime={expectedEndTime}/>}
|
|
|
|
|
<DragAndDropCalendar
|
|
|
|
|
// 本地设置
|
2024-05-28 06:54:58 -04:00
|
|
|
|
localizer={localizer}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
messages={message}
|
|
|
|
|
// 修改style
|
|
|
|
|
eventPropGetter={eventPropGetter}
|
2024-05-28 06:54:58 -04:00
|
|
|
|
events={events}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
// 界面
|
2024-05-28 06:54:58 -04:00
|
|
|
|
view={view}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
// 界面改变
|
2024-05-28 06:54:58 -04:00
|
|
|
|
onView={handleViewChange}
|
2024-05-31 02:00:16 -04:00
|
|
|
|
onRangeChange={rangeChange}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
// 时间
|
2024-05-28 06:54:58 -04:00
|
|
|
|
date={date}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
// 条目信息改变
|
2024-05-28 06:54:58 -04:00
|
|
|
|
onNavigate={handleNavigate}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
|
|
|
|
|
// 点击
|
|
|
|
|
selectable
|
|
|
|
|
scrollToTime={scrollToTime}
|
|
|
|
|
// 双击
|
|
|
|
|
onDoubleClickEvent={doubleClick}
|
2024-05-29 04:44:23 -04:00
|
|
|
|
// 点击任务
|
|
|
|
|
onSelectEvent={handleSelectEvent}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
// 点击空白处
|
2024-05-29 04:44:23 -04:00
|
|
|
|
onSelectSlot={handleSelectSlot}
|
2024-05-30 02:32:17 -04:00
|
|
|
|
// 改变时间长短
|
|
|
|
|
resizable
|
|
|
|
|
onEventResize={moveEvent}
|
|
|
|
|
onEventDrop={moveEvent}
|
2024-05-28 06:54:58 -04:00
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
export default CalShow;
|