feat:打卡日历显示

This commit is contained in:
1708-huayu 2025-08-11 18:30:45 +08:00
parent c6444b2662
commit 03626f9f58
17 changed files with 271 additions and 111 deletions

View File

@ -11,6 +11,7 @@ export default function Layout({children}: { children: React.ReactNode }) {
expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().subtract(7, 'day'), 'operateType': ">="}); expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().subtract(7, 'day'), 'operateType': ">="});
expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().add(7, 'day'), 'operateType': "<"}) expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().add(7, 'day'), 'operateType': "<"})
const [expectedStartTime, setExpectedStartTime] = React.useState<string>(JSON.stringify(expectStartTimeList)) const [expectedStartTime, setExpectedStartTime] = React.useState<string>(JSON.stringify(expectStartTimeList))
const [taskTypeList,setTaskTypeList] = React.useState<string[]>(["0,1,2,3","4"])
const [refreshDataFlag, setRefreshDataFlag] = React.useState<boolean>(true) const [refreshDataFlag, setRefreshDataFlag] = React.useState<boolean>(true)
function refreshData() { function refreshData() {
@ -38,9 +39,11 @@ export default function Layout({children}: { children: React.ReactNode }) {
<LocalContext.Provider value={{ <LocalContext.Provider value={{
'taskState': taskState, 'taskState': taskState,
'expectedStartTime': expectedStartTime, 'expectedStartTime': expectedStartTime,
'refreshData': refreshDataFlag 'refreshData': refreshDataFlag,
'taskTypeList':taskTypeList
}}> }}>
<TitleOperation setTaskState={setTaskState} setExpectedStartTime={setExpectedStartTime} <TitleOperation setTaskState={setTaskState} setExpectedStartTime={setExpectedStartTime}
setTaskTypeList={setTaskTypeList}
refreshData={refreshData}/> refreshData={refreshData}/>
<div style={{height:"calc(100vh - 42px)",overflow:"auto"}}> <div style={{height:"calc(100vh - 42px)",overflow:"auto"}}>
{children} {children}

View File

@ -1,5 +1,5 @@
import {Button, Form} from "antd"; import {Button, Form} from "antd";
import React, {useEffect} from "react"; import React, {useEffect, useState} from "react";
import { import {
ModalForm, ModalForm,
ProFormDateTimeRangePicker, ProFormDateTimeRangePicker,
@ -7,43 +7,82 @@ import {
ProFormTextArea ProFormTextArea
} from "@ant-design/pro-components"; } from "@ant-design/pro-components";
import {TaskScheduleRecordForm, TaskScheduleRecordVO} from "@/components/type/TaskSchedule.d"; import {TaskScheduleRecordForm, TaskScheduleRecordVO} from "@/components/type/TaskSchedule.d";
import {clickRecordAPI} from "@/components/service/ScheduleTask"; import {clickRecordAPI, getRecordById} from "@/components/service/ScheduleTask";
import dayjs, {UnitTypeShort} from "dayjs"; import dayjs, {UnitTypeShort} from "dayjs";
import {onceConsumerRead} from "@/utils/codeToReadName"; import {onceConsumerRead} from "@/utils/codeToReadName";
import {betweenTime} from "@/utils/timeFormatUtil"; import {betweenTime} from "@/utils/timeFormatUtil";
interface ClickRecordProps { interface ClickRecordProps {
taskId: string; openClickRecord?:boolean;
recordId?:string;
taskId?: string;
taskName: string; taskName: string;
onceConsume: string | undefined; onceConsume?: string;
setOpenClickRecord?:(boolean: boolean) => void;
} }
const ClickRecord: React.FC<ClickRecordProps> = ({taskId, taskName, onceConsume}) => { const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOpenClickRecord,taskId, taskName, onceConsume}) => {
const [form] = Form.useForm<TaskScheduleRecordForm>(); const [form] = Form.useForm<TaskScheduleRecordForm>();
const [editAble, setEditAble] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
let data = { console.log("ClickRecord:useEffect:",openClickRecord)
'recordTimeRange': [onceConsume ? dayjs().subtract(Number(onceConsume.split(",")[0]), onceConsume.split(",")[1] as UnitTypeShort) : dayjs(), dayjs()], if (recordId){
'timeDifference': onceConsumerRead(onceConsume), setEditAble(false)
}; getRecordById(recordId).then(res=>{
form.setFieldsValue(data) if (res.data.status.success){
}, []); form.setFieldsValue({...res.data.data,
recordTimeRange:[res.data.data.startDate?dayjs(res.data.data.startDate).toDate():undefined,
res.data.data.startDate?dayjs(res.data.data.recordDate).toDate():undefined]})
}
})
}else {
let data = {
'recordTimeRange': [onceConsume ? dayjs().subtract(Number(onceConsume.split(",")[0]), onceConsume.split(",")[1] as UnitTypeShort) : dayjs(), dayjs()],
'timeDifference': onceConsumerRead(onceConsume),
};
form.setFieldsValue(data)
}
}, [recordId]);
return ( return (
<ModalForm<TaskScheduleRecordForm> <ModalForm<TaskScheduleRecordForm>
title={`${taskName}打卡`} title={`${taskName}打卡`}
layout="horizontal" layout="horizontal"
trigger={ trigger={(recordId?undefined:
<Button type="primary"> <Button type="primary">
</Button>} </Button>)}
modalProps={{
destroyOnClose: true,
maskClosable: false,
onCancel: () => {
setOpenClickRecord?.(false)
},
}}
submitter={{
render: (prop, defaultDoms) => {
const result = [];
if (recordId&&!editAble){
result.push(<Button color="danger" key="edit"
onClick={() => setEditAble(true)}>
</Button>)
result.push(<Button type="primary" key="close"
onClick={() => setOpenClickRecord?.(false)}>
</Button>)
}else{
result.push(defaultDoms)
}
return result;
}
}}
open={openClickRecord}
form={form} form={form}
autoFocusFirstInput autoFocusFirstInput
modalProps={{ readonly={!editAble}
// destroyOnClose: true,
onCancel: () => console.log('run'),
}}
onFinish={async (values) => { onFinish={async (values) => {
values.taskId = taskId if(taskId){
values.taskId = taskId
}
if (values.recordTimeRange[0]) { if (values.recordTimeRange[0]) {
values.startDate = new Date(values.recordTimeRange[0]) values.startDate = new Date(values.recordTimeRange[0])
} }

View File

@ -18,7 +18,7 @@ const TaskNameAndIcon = (props: {task:TaskMessage}) => {
<use xlinkHref="#icon-dingshi"></use> <use xlinkHref="#icon-dingshi"></use>
</svg> </svg>
} }
<text>{props.task.name}</text> <span>{props.task.name}</span>
</Fragment>) </Fragment>)
} }
export default TaskNameAndIcon; export default TaskNameAndIcon;

View File

@ -1,7 +1,3 @@
.localDiv{ .localDiv{
margin-bottom: 24px; margin-bottom: 24px;
}
.icon{
font-size: 1rem;
font-color: orange;
} }

View File

@ -1,13 +1,21 @@
import {AxiosResponse} from "axios"; import {AxiosResponse} from "axios";
import {ResponseVO} from "@/lib/definitions"; import {ResponseVO} from "@/lib/definitions";
import {httpReq} from "@/utils/axiosReq"; import {httpReq} from "@/utils/axiosReq";
import {TaskScheduleRecordVO} from "@/components/type/TaskSchedule.d"; import {TaskScheduleRangeVO, TaskScheduleRecordForm, TaskScheduleRecordVO} from "@/components/type/TaskSchedule.d";
export const generateNextTimeAPI = (cron: string): Promise<AxiosResponse<ResponseVO<string[]>>> => { export const generateNextTimeAPI = (cron: string): Promise<AxiosResponse<ResponseVO<string[]>>> => {
return httpReq.get(process.env.NEXT_PUBLIC_TODO_REQUEST_URL + return httpReq.get(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/nextTime?cron=${cron}`) `/task/schedule/nextTime?cron=${cron}`)
} }
export const clickRecordAPI = (data:TaskScheduleRecordVO)=>{ export const clickRecordAPI = (data: TaskScheduleRecordVO) => {
return httpReq.post(process.env.NEXT_PUBLIC_TODO_REQUEST_URL + return httpReq.post(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/click`,data) `/task/schedule/click`, data)
}
export const getRecordById = (recordId: string): Promise<AxiosResponse<ResponseVO<TaskScheduleRecordForm>>> => {
return httpReq.get(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/id?id=${recordId}`)
}
export const editClickRecordAPI = (data:TaskScheduleRangeVO):Promise<AxiosResponse<ResponseVO<TaskScheduleRecordVO>>> =>{
return httpReq.post(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/updateRange`,data)
} }

View File

@ -1,11 +1,15 @@
export interface TaskScheduleRecordVO{ export interface TaskScheduleRangeVO {
id:string, id: string,
timeDifference:string, startDate: Date,
remarks:string, recordDate: Date,
startDate:Date,
recordDate:Date,
taskId:string,
} }
export interface TaskScheduleRecordForm extends TaskScheduleRecordVO{
recordTimeRange:Date[] export interface TaskScheduleRecordVO extends TaskScheduleRangeVO{
timeDifference: string,
remarks: string,
taskId: string,
}
export interface TaskScheduleRecordForm extends TaskScheduleRecordVO {
recordTimeRange: (Date | undefined)[]
} }

View File

@ -1,6 +1,8 @@
import {Event} from "react-big-calendar"; import {Event} from "react-big-calendar";
export interface TaskEvent extends Event { export interface TaskEvent extends Event {
id?: any; id?: any;
name?:string;
state?:any; state?:any;
taskType?:string;
priority?:any; priority?:any;
} }

View File

@ -1,5 +1,8 @@
import {httpReq} from "@/utils/axiosReq"; import {httpReq} from "@/utils/axiosReq";
import {unstable_noStore as noStore} from "next/dist/server/web/spec-extension/unstable-no-store"; import {unstable_noStore as noStore} from "next/dist/server/web/spec-extension/unstable-no-store";
import {DataType, Request, ResponseVO, ResultPage} from "@/lib/definitions";
import {TaskWebSelectVO} from "@/lib/task/project/definitions";
import {AxiosResponse} from "axios";
export const editExpectAPI = (requestParam: {}) =>{ export const editExpectAPI = (requestParam: {}) =>{
noStore(); noStore();
@ -7,3 +10,13 @@ export const editExpectAPI = (requestParam: {}) =>{
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/task/editExpect', requestParam) process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/task/editExpect', requestParam)
} }
export async function getTaskAndScheduleRecordAPI(requestParam: Request<TaskWebSelectVO>): Promise<ResponseVO<ResultPage<DataType>>> {
noStore();
// 使用 Axios 发送 POST 请求获取数据
const response: AxiosResponse<ResponseVO<ResultPage<DataType>>> = await httpReq.post(
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/V2/task/my/schedule/record',requestParam);
// 从响应中提取数据并返回
console.log("response.data", response.data)
return response.data;
}

View File

@ -7,7 +7,7 @@ import {TaskSelectVO} from "@/lib/task/drag/data";
export async function selectTaskAPI(requestParam: Request<TaskSelectVO>): export async function selectTaskAPI(requestParam: Request<TaskSelectVO>):
Promise<ResponseVO<ResultPage<DataType>>> { Promise<ResponseVO<ResultPage<DataType>>> {
noStore(); noStore();
// 使用 Axios 发送 PUT 请求获取数据 // 使用 Axios 发送 POST 请求获取数据
const response: AxiosResponse<ResponseVO<ResultPage<DataType>>> = await httpReq.post( const response: AxiosResponse<ResponseVO<ResultPage<DataType>>> = await httpReq.post(
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/V2/task/select', requestParam); process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/V2/task/select', requestParam);
// 从响应中提取数据并返回 // 从响应中提取数据并返回

View File

@ -5,9 +5,13 @@ export type TaskWebSelectVO ={
description?:string, description?:string,
treeList:boolean, treeList:boolean,
treeFilter?:boolean, treeFilter?:boolean,
treeOrList?:boolean,
pid?:string, pid?:string,
state?:string[], state?:string[]|string,
taskTypeList?:string[],
priority?:string[], priority?:string[],
expectedStartTimeStart?:Dayjs, expectedStartTimeStart?:Dayjs,
expectedStartTimeEnd?:Dayjs expectedStartTimeEnd?:Dayjs,
expectedStartTime?:Dayjs|Date,
expectedEndTime?:Dayjs|Date,
} }

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
const LocalContext = React.createContext({'taskState':'','expectedStartTime':'','refreshData':true}); const LocalContext = React.createContext({'taskState':'','expectedStartTime':'','refreshData':true,
'taskTypeList':[""]});
export default LocalContext; export default LocalContext;

View File

@ -29,7 +29,7 @@ html {
vertical-align: -0.15em; vertical-align: -0.15em;
fill: currentColor; fill: currentColor;
overflow: hidden; overflow: hidden;
padding-right: 0.5em; /*padding-right: 0.5em;*/
} }
.displayFlexRow{ .displayFlexRow{
display: flex; display: flex;

View File

@ -13,12 +13,14 @@ import Dropdown from "antd/es/dropdown/dropdown";
interface TitleOperationProps { interface TitleOperationProps {
setTaskState: (value: string) => void; setTaskState: (value: string) => void;
setTaskTypeList: (value: string[]) => void;
setExpectedStartTime: (value: string) => void; setExpectedStartTime: (value: string) => void;
refreshData: () => void; refreshData: () => void;
} }
export const TitleOperation: React.FC<TitleOperationProps> = ({ export const TitleOperation: React.FC<TitleOperationProps> = ({
setTaskState, setTaskState,
setTaskTypeList,
setExpectedStartTime, setExpectedStartTime,
refreshData refreshData
}: TitleOperationProps) => { }: TitleOperationProps) => {
@ -30,7 +32,7 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
const [pathName, setPathName] = useState(pathname); const [pathName, setPathName] = useState(pathname);
console.log('usePathname()', pathname); console.log('usePathname()', pathname);
console.log('useSearchParams()',searchParams.toString(),searchParams.get('pName'), searchParams.get('pid')); console.log('useSearchParams()', searchParams.toString(), searchParams.get('pName'), searchParams.get('pid'));
const data = useContext(LocalContext); const data = useContext(LocalContext);
const {RangePicker} = DatePicker; const {RangePicker} = DatePicker;
const expectStartTimeParseResult: RequestDateType[] = data.expectedStartTime.length > 0 ? JSON.parse(data.expectedStartTime) : [undefined, undefined] const expectStartTimeParseResult: RequestDateType[] = data.expectedStartTime.length > 0 ? JSON.parse(data.expectedStartTime) : [undefined, undefined]
@ -41,26 +43,28 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
]; ];
const typeList: CheckboxOptionType<string>[] = [ const typeList: CheckboxOptionType<string>[] = [
{ label: '计划', value: '0,1,2,3', }, {label: '计划', value: '0,1,2,3'},
{ label: '打卡', value: '4', }, {label: '打卡', value: '4'},
{ label: '事件', value: '5', }, // { label: '事件', value: '5', },
]; ];
// ✅ 监听 searchParams 变化 // ✅ 监听 searchParams 变化
useEffect(() => { useEffect(() => {
const pName = searchParams.get("pName"); const pName = searchParams.get("pName");
if(pName&&pName!=document.title){ if (pName && pName != document.title) {
document.title = pName; document.title = pName;
} }
setPathName(pathname)
setPathParam(searchParams.toString()); setPathParam(searchParams.toString());
}, [searchParams]); // searchParams 变化时触发 }, [searchParams]); // searchParams 变化时触发
const onClick: MenuProps['onClick'] = ({ key }) => { const onClick: MenuProps['onClick'] = ({key}) => {
if (key == "1"){ if (key == "1") {
}else if (key == "2"){ } else if (key == "2") {
replace(pathName) replace(pathName)
setPathParam(undefined) setPathParam(undefined)
refreshData()
} }
}; };
@ -88,28 +92,35 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
{ {
!usePathname().startsWith("/task/project") && !usePathname().startsWith("/task/project") &&
<Button type="primary" onClick={() => { <Button type="primary" onClick={() => {
replace(`/task/project${pathParam?('?'+pathParam):""}`); replace(`/task/project${pathParam ? ('?' + pathParam) : ""}`);
// setCurrentPath("/task/project") // setCurrentPath("/task/project")
}}></Button> }}></Button>
} }
{ {
!usePathname().startsWith("/task/drag") && !usePathname().startsWith("/task/drag") &&
<Button type="primary" onClick={() => { <Button type="primary" onClick={() => {
replace(`/task/drag${pathParam?('?'+pathParam):""}`); replace(`/task/drag${pathParam ? ('?' + pathParam) : ""}`);
// setCurrentPath("/task/four"); // setCurrentPath("/task/four");
}}></Button> }}></Button>
} }
{ {
!usePathname().startsWith("/task/calendar") && !usePathname().startsWith("/task/calendar") &&
<Button type="primary" onClick={() => { <Button type="primary" onClick={() => {
replace(`/task/calendar${pathParam?('?'+pathParam):""}`); replace(`/task/calendar${pathParam ? ('?' + pathParam) : ""}`);
// setCurrentPath("/task/project") // setCurrentPath("/task/project")
}}></Button> }}></Button>
} }
{ {
usePathname().startsWith("/task/calendar") && usePathname().startsWith("/task/calendar") && (<Fragment>
( <div style={{color: 'red'}}>*</div>
<Checkbox.Group options={typeList} defaultValue={['0,1,2,3','4']} /> <Checkbox.Group options={typeList} value={data.taskTypeList} onChange={(checkedValue) => {
if (checkedValue.length == 0) {
message.error("至少选择一个展示项")
} else {
setTaskTypeList(checkedValue)
}
}}/>
</Fragment>
) )
} }
{ {
@ -173,14 +184,16 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
{ {
/*日历需要状态*/ /*日历需要状态*/
!usePathname().startsWith("/task/project") && <Fragment> !usePathname().startsWith("/task/project") && <Fragment>
<Button type={"primary"} onClick={()=>{refreshData()}}></Button> <Button type={"primary"} onClick={() => {
refreshData()
}}></Button>
</Fragment> </Fragment>
} }
</Space> </Space>
<Dropdown menu={{ items:pathParam?itemsPid:items, onClick }}> <Dropdown menu={{items: pathParam ? itemsPid : items, onClick}}>
<svg style={{height: "32px", width: "32px", alignItems: "center"}} className="icon" aria-hidden="true"> <svg style={{height: "32px", width: "32px", alignItems: "center"}} className="icon" aria-hidden="true">
<use xlinkHref="#icon-user__easyico"></use> <use xlinkHref="#icon-user__easyico"></use>
</svg> </svg>
</Dropdown> </Dropdown>
</div> </div>
} }

View File

@ -6,16 +6,20 @@ import 'react-big-calendar/lib/css/react-big-calendar.css'
import 'react-big-calendar/lib/sass/styles.scss' import 'react-big-calendar/lib/sass/styles.scss'
import 'react-big-calendar/lib/addons/dragAndDrop/styles.scss' import 'react-big-calendar/lib/addons/dragAndDrop/styles.scss'
import '@/ui/task/calendar/index.modules.css' import '@/ui/task/calendar/index.modules.css'
import {commonUpdate, getTaskTreeResult, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data"; import {commonUpdate, getTaskTreeResult, getTaskTreeResultAPI, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
import {useSearchParams} from "next/dist/client/components/navigation"; import {useSearchParams} from "next/dist/client/components/navigation";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm"; import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
import {SearchObject} from "@/lib/definitions"; import {Request, SearchObject} from "@/lib/definitions";
import LocalContext from "@/ui/LocalContent"; import LocalContext from "@/ui/LocalContent";
import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop"; import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop";
import {TaskEvent} from "@/lib/task/calendar/data"; import {TaskEvent} from "@/lib/task/calendar/data";
import {editExpectAPI} from "@/lib/task/calendar/service"; import {editExpectAPI, getTaskAndScheduleRecordAPI} from "@/lib/task/calendar/service";
import TaskNamePrefixIcon from "@/components/TaskNameAndIcon"; import TaskNamePrefixIcon from "@/components/TaskNameAndIcon";
import TaskNameAndIcon from "@/components/TaskNameAndIcon"; import TaskNameAndIcon from "@/components/TaskNameAndIcon";
import {TaskWebSelectVO} from "@/lib/task/project/definitions";
import {message} from "antd";
import ClickRecord from "@/components/ClickRecord";
import {editClickRecordAPI} from "@/components/service/ScheduleTask";
/** /**
* https://github.com/jquense/react-big-calendar?tab=readme-ov-file * https://github.com/jquense/react-big-calendar?tab=readme-ov-file
@ -31,16 +35,32 @@ const CalShow: React.FC = () => {
// 展示在页面的任务,默认获取当前月的信息。 // 展示在页面的任务,默认获取当前月的信息。
const [events, setEvents] = useState<TaskEvent[]>([]); const [events, setEvents] = useState<TaskEvent[]>([]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [openClickRecord, setOpenClickRecord] = useState(false);
const [description, setDescription] = useState(''); const [description, setDescription] = useState('');
const [operationId, setOperationId] = useState(-1); const [operationId, setOperationId] = useState(-1);
const [itemId, setItemId] = useState('-1'); const [itemId, setItemId] = useState('-1');
const [recordId,setRecordId] = useState();
const [taskName, setTaskName] = useState("");
const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>(); const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>();
const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>(); const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>();
const [range, setRange] = useState<{ start: Date; end: Date }>({ const [range, setRange] = useState<{ start: Date; end: Date }>({
start: dayjs(date).startOf('week').toDate(), start: dayjs(date).startOf('week').toDate(),
end: dayjs(date).endOf('week').toDate() end: dayjs(date).endOf('week').toDate()
}); });
const state = useContext(LocalContext).taskState; 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) => { const handleViewChange = (newView: View) => {
setView(newView); setView(newView);
@ -60,23 +80,28 @@ const CalShow: React.FC = () => {
{name: 'ALL-CHILD', value: "true", operateType: "ALL-CHILD"}, {name: 'ALL-CHILD', value: "true", operateType: "ALL-CHILD"},
{name: 'TREE-FILTER', value: "true", operateType: "TREE-FILTER"}, {name: 'TREE-FILTER', value: "true", operateType: "TREE-FILTER"},
); );
searchObject.data.pid=pid
setSearchObject({...searchObject})
} }
loadData(searchList); loadData(searchList);
}; };
useEffect(() => { useEffect(() => {
console.log("CalShow:useEffect:range", range) console.log("CalShow:useEffect:range", range)
const searchListE = [] // const searchListE = []
if (pid != null) { if (pid != null) {
searchListE.push( // searchListE.push(
{name: "pid", value: pid, operateType: "="}, // {name: "pid", value: pid, operateType: "="},
{name: 'ALL-CHILD', value: "true", operateType: "ALL-CHILD"}, // {name: 'ALL-CHILD', value: "true", operateType: "ALL-CHILD"},
{name: 'TREE-FILTER', value: "true", operateType: "TREE-FILTER"}, // {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.start, operateType: ">="})
// searchListE.push({name: 'expectedStartTime', value: range.end, operateType: "<="}) // searchListE.push({name: 'expectedStartTime', value: range.end, operateType: "<="})
loadData(searchListE); // loadData(searchListE);
loadData()
/** /**
* What Is This? * What Is This?
* This is to prevent a memory leak, in the off chance that you * This is to prevent a memory leak, in the off chance that you
@ -86,7 +111,7 @@ const CalShow: React.FC = () => {
clearClickTimeout() clearClickTimeout()
} }
}, [useContext(LocalContext), range]); }, [useContext(LocalContext), range]);
const message = { const calMessages = {
week: '周', week: '周',
work_week: '工作周', work_week: '工作周',
day: '天', day: '天',
@ -96,39 +121,65 @@ const CalShow: React.FC = () => {
today: '当下', today: '当下',
agenda: '日程' agenda: '日程'
} }
const loadData = (searchList: SearchObject[]) => { const loadData = (searchList?: SearchObject[]) => {
if (state.length > 0) { if (state.length > 0) {
searchList.push({name: 'state', value: state, operateType: "IN"}) // searchList.push({name: 'state', value: state, operateType: "IN"})
searchObject.data.state=state
} }
// searchList.push({name: 'expectedEndTime', value: dayjs(date).endOf('month'), operateType: "NOT NULL"}) if (taskTypeList.length>0){
let request = JSON.stringify({ searchObject.data.taskTypeList=taskTypeList
pageSize: 9999, }
pageNumber: 1, searchObject.data.expectedStartTime=range.start
data: searchList, searchObject.data.expectedEndTime=range.end
startTime: range.start, setSearchObject({...searchObject})
// startTime:dayjs(range.start).format('YYYY-MM-DD HH:mm:ss'), getTaskAndScheduleRecordAPI(searchObject).then(responseD => {
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) { if (responseD.status.success) {
let result: TaskEvent[] = responseD.data.content.map<TaskEvent>(taskState => { let result: TaskEvent[] = responseD.data.content.map<TaskEvent>(taskState => {
return { return {
start: dayjs(taskState.expectedStartTime).toDate(), start: dayjs(taskState.expectedStartTime).toDate(),
end: dayjs(taskState.expectedEndTime).toDate(), end: dayjs(taskState.expectedEndTime).toDate(),
title: <TaskNameAndIcon task={taskState}/>, title: <TaskNameAndIcon task={taskState}/>,
name:taskState.name,
resource: taskState.id, resource: taskState.id,
id: taskState.id, id: taskState.id,
state: taskState.state, state: taskState.state,
priority: taskState.priority priority: taskState.priority,
taskType:taskState.taskType
} }
}); });
console.log('responseD.data.content:', result) console.log('responseD.data.content:', result)
setEvents([...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 = () => { const reloadData = () => {
setOpen(false) setOpen(false)
@ -146,15 +197,19 @@ const CalShow: React.FC = () => {
) )
const handleSelectEvent = useCallback( const handleSelectEvent = useCallback(
(event: Event, e: React.SyntheticEvent<HTMLElement>) => { (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout() clearClickTimeout()
clickRef.current = window.setTimeout(() => { clickRef.current = window.setTimeout(() => {
// window.alert(event.title); if(event.taskType=='4'){
console.log("event") setTaskName(event.name||"")
setOperationId(OPERATION_BUTTON_TYPE.DETAIL) setRecordId(event.id)
setDescription("任务详情") setOpenClickRecord(true);
setItemId(event.resource) }else {
setOpen(true); setOperationId(OPERATION_BUTTON_TYPE.DETAIL)
setDescription("任务详情")
setItemId(event.resource)
setOpen(true);
}
}, 250) }, 250)
}, },
[] []
@ -170,6 +225,10 @@ const CalShow: React.FC = () => {
const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => { const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout() clearClickTimeout()
clickRef.current = window.setTimeout(() => { clickRef.current = window.setTimeout(() => {
if (event.taskType=='4'||event.taskType=='5'){
message.error("计划双击完成,非计划双击无效果。")
return;
}
// 数据落库 // 数据落库
commonUpdate({ commonUpdate({
updateColumnList: [{ updateColumnList: [{
@ -205,11 +264,19 @@ const CalShow: React.FC = () => {
if (!allDay && droppedOnAllDaySlot) { if (!allDay && droppedOnAllDaySlot) {
event.allDay = true event.allDay = true
} }
editExpectAPI({ if (event.taskType=='4'){
expectedStartTime: start, editClickRecordAPI({
expectedEndTime: end, id:event.id,
id: event.resource startDate:dayjs(start!).toDate(),
}) recordDate:dayjs(end!).toDate(),
});
}else {
editExpectAPI({
expectedStartTime: start,
expectedEndTime: end,
id: event.resource
})
}
setEvents((prev: TaskEvent[]) => { setEvents((prev: TaskEvent[]) => {
const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource); const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource);
const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource); const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource);
@ -275,10 +342,12 @@ const CalShow: React.FC = () => {
reloadData={reloadData} expectedStartTime={expectedStartTime} reloadData={reloadData} expectedStartTime={expectedStartTime}
closeOpen={() => setOpen(false)} closeOpen={() => setOpen(false)}
expectedEndTime={expectedEndTime}/>} expectedEndTime={expectedEndTime}/>}
{openClickRecord &&
<ClickRecord recordId={recordId} taskName={taskName} openClickRecord={openClickRecord} setOpenClickRecord={setOpenClickRecord}/>}
<DragAndDropCalendar <DragAndDropCalendar
// 本地设置 // 本地设置
localizer={localizer} localizer={localizer}
messages={message} messages={calMessages}
// 修改style // 修改style
eventPropGetter={eventPropGetter} eventPropGetter={eventPropGetter}
events={events} events={events}

View File

@ -9,6 +9,7 @@ import dayjs from "dayjs";
import {getTaskState, taskPriorityList} from "@/lib/task/project/data"; import {getTaskState, taskPriorityList} from "@/lib/task/project/data";
import 'react-virtualized/styles.css'; import 'react-virtualized/styles.css';
import RightOption from "@/ui/task/RightOption"; import RightOption from "@/ui/task/RightOption";
import TaskNameAndIcon from "@/components/TaskNameAndIcon";
interface DroppableTableProps { interface DroppableTableProps {
@ -95,7 +96,7 @@ export const DroppableTable = React.memo((props: DroppableTableProps) => {
<div style={{width: '20%',paddingLeft:"1rem"}} className='displayFlexRow'> <div style={{width: '20%',paddingLeft:"1rem"}} className='displayFlexRow'>
<Tooltip placement="topLeft" title={record.name} <Tooltip placement="topLeft" title={record.name}
className='displayFlexRow'> className='displayFlexRow'>
<div className='displayFlexRow'>{record.name}</div> <div className='displayFlexRow'> <TaskNameAndIcon task={record}/></div>
</Tooltip> </Tooltip>
</div> </div>
<div style={{width: '45%', boxSizing: 'border-box', minWidth: 0}} <div style={{width: '45%', boxSizing: 'border-box', minWidth: 0}}

View File

@ -123,7 +123,7 @@ export const DetailForm: React.FC<DetailFormProps> = (props) => {
if (props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD) { if (props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD) {
addTask(request).then(response => { addTask(request).then(response => {
console.log('response', response) console.log('response', response)
if (response.status.success) { if (response.data.status.success) {
message.success("添加任务成功:" + response.data) message.success("添加任务成功:" + response.data)
props.handleCancel() props.handleCancel()
} }
@ -140,11 +140,11 @@ export const DetailForm: React.FC<DetailFormProps> = (props) => {
} }
updateTask(request).then(response => { updateTask(request).then(response => {
console.log('response', response) console.log('response', response)
if (response.status.success) { if (response.data.status.success) {
message.success("修改任务成功:" + response.data) message.success("修改任务成功:" + response.data)
props.handleCancel() props.handleCancel()
}else { }else {
message.error(response.status.message) message.error(response.data.status.message)
} }
} }
) )

View File

@ -126,18 +126,25 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
const cascaderOnChange: CascaderProps<CascaderOption>['onChange'] = const cascaderOnChange: CascaderProps<CascaderOption>['onChange'] =
(value: (string | number)[], selectedOptions: CascaderOption[]) => { (value: (string | number)[], selectedOptions: CascaderOption[]) => {
setOnceConsumeChange(value.map(toString)) if (value.length>0){
console.log({value})
setOnceConsumeChange(value.map(valueMap=>valueMap.toString()))
}else {
setOnceConsumeChange([])
}
}; };
const changeValueToLabel = ()=>{ const changeValueToLabel = ()=>{
const onceConsumeShow = ["1", "小時"];
if (onceConsumeChange&&onceConsumeChange.length==2){ if (onceConsumeChange&&onceConsumeChange.length==2){
if(onceConsumeChange[1]=='m'){ if(onceConsumeChange[1]=='m'){
onceConsumeChange[1]='分钟' onceConsumeShow[1]='分钟'
}else if(onceConsumeChange[1]=='h'){ }else if(onceConsumeChange[1]=='h'){
onceConsumeChange[1]='小时' onceConsumeShow[1]='小时'
}else if(onceConsumeChange[1]=='d'){ }else if(onceConsumeChange[1]=='d'){
onceConsumeChange[1]='天' onceConsumeShow[1]='天'
} }
return onceConsumeChange; return onceConsumeShow;
}else { }else {
return [] return []
} }
@ -384,6 +391,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
if (priority) { if (priority) {
values.priority = priority.code values.priority = priority.code
} }
console.log('update:values:',{values})
// todo 修改 // todo 修改
if (props.operationId === OPERATION_BUTTON_TYPE.UPDATE || (props.operationId === OPERATION_BUTTON_TYPE.DETAIL && !editFormDisable)) { if (props.operationId === OPERATION_BUTTON_TYPE.UPDATE || (props.operationId === OPERATION_BUTTON_TYPE.DETAIL && !editFormDisable)) {
await updateTask(values).then(response => { await updateTask(values).then(response => {
@ -626,7 +634,6 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
<Cascader options={options} loadData={cascaderLoadData} <Cascader options={options} loadData={cascaderLoadData}
onChange={cascaderOnChange} onChange={cascaderOnChange}
defaultValue={changeValueToLabel()} defaultValue={changeValueToLabel()}
onClick={()=>setOnceConsumeChange([])}
clearIcon={true} clearIcon={true}
// value={remindType.split(",")} // value={remindType.split(",")}
/> />