feat:localStorage处理,日历背景颜色处理

This commit is contained in:
1708-huayu 2025-08-12 18:24:55 +08:00
parent 03626f9f58
commit ccea84285c
10 changed files with 161 additions and 55 deletions

View File

@ -6,36 +6,52 @@ import {
ProFormText,
ProFormTextArea
} from "@ant-design/pro-components";
import {TaskScheduleRecordForm, TaskScheduleRecordVO} from "@/components/type/TaskSchedule.d";
import {clickRecordAPI, getRecordById} from "@/components/service/ScheduleTask";
import {TaskScheduleRecordForm} from "@/components/type/TaskSchedule.d";
import {
clickRecordAPI,
deleteClickRecordAPI,
editClickRecordAPI,
getRecordById
} from "@/components/service/ScheduleTask";
import dayjs, {UnitTypeShort} from "dayjs";
import {onceConsumerRead} from "@/utils/codeToReadName";
import {betweenTime} from "@/utils/timeFormatUtil";
interface ClickRecordProps {
openClickRecord?:boolean;
recordId?:string;
openClickRecord?: boolean;
recordId?: string;
taskId?: string;
taskName: string;
onceConsume?: string;
setOpenClickRecord?:(boolean: boolean) => void;
setOpenClickRecord?: (boolean: boolean) => void;
reloadData?:()=>void
}
const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOpenClickRecord,taskId, taskName, onceConsume}) => {
const ClickRecord: React.FC<ClickRecordProps> = ({
recordId,
openClickRecord,
setOpenClickRecord,
taskId,
taskName,
onceConsume,
reloadData
}) => {
const [form] = Form.useForm<TaskScheduleRecordForm>();
const [editAble, setEditAble] = useState<boolean>(true);
useEffect(() => {
console.log("ClickRecord:useEffect:",openClickRecord)
if (recordId){
console.log("ClickRecord:useEffect:", openClickRecord)
if (recordId) {
setEditAble(false)
getRecordById(recordId).then(res=>{
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]})
getRecordById(recordId).then(res => {
if (res.data.status.success) {
form.setFieldsValue({
...res.data.data,
recordTimeRange: [res.data.data.startDate ? dayjs(res.data.data.startDate) : undefined,
res.data.data.startDate ? dayjs(res.data.data.recordDate) : undefined]
})
}
})
}else {
} else {
let data = {
'recordTimeRange': [onceConsume ? dayjs().subtract(Number(onceConsume.split(",")[0]), onceConsume.split(",")[1] as UnitTypeShort) : dayjs(), dayjs()],
'timeDifference': onceConsumerRead(onceConsume),
@ -48,7 +64,7 @@ const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOp
<ModalForm<TaskScheduleRecordForm>
title={`${taskName}打卡`}
layout="horizontal"
trigger={(recordId?undefined:
trigger={(recordId ? undefined :
<Button type="primary">
</Button>)}
@ -62,14 +78,24 @@ const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOp
submitter={{
render: (prop, defaultDoms) => {
const result = [];
if (recordId&&!editAble){
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(<Button color="danger" key="delete"
onClick={() => {
deleteClickRecordAPI(recordId).then(res => {
if (res.data.status.success) {
setOpenClickRecord?.(false)
reloadData?.()
}
})
}}>
</Button>)
} else {
result.push(defaultDoms)
}
return result;
@ -80,7 +106,8 @@ const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOp
autoFocusFirstInput
readonly={!editAble}
onFinish={async (values) => {
if(taskId){
console.log({values})
if (taskId) {
values.taskId = taskId
}
if (values.recordTimeRange[0]) {
@ -89,7 +116,13 @@ const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOp
if (values.recordTimeRange[1]) {
values.recordDate = new Date(values.recordTimeRange[1])
}
await clickRecordAPI(values);
if (values.id) {
await editClickRecordAPI(values);
} else {
await clickRecordAPI(values);
}
setOpenClickRecord?.(false)
reloadData?.()
return true
}}
>
@ -118,6 +151,16 @@ const ClickRecord: React.FC<ClickRecordProps> = ({recordId,openClickRecord,setOp
label="耗时"
readonly={true}
/>
<ProFormText
width="xs"
name="id"
hidden={true}
/>
<ProFormText
width="xs"
hidden={true}
name="taskId"
/>
<ProFormTextArea name="remarks" label="备注"/>
</ModalForm>)
}

View File

@ -1,5 +1,5 @@
import React, {useState, useEffect, Fragment} from 'react';
import dayjs, {Dayjs} from 'dayjs';
import dayjs, {Dayjs, UnitTypeShort} from 'dayjs';
import {Button, Input, Modal, Segmented} from 'antd';
import style from "@/components/SettingCton.module.css"
import {generateNextTimeAPI} from "@/components/service/ScheduleTask";
@ -7,7 +7,9 @@ import {generateNextTimeAPI} from "@/components/service/ScheduleTask";
type PresetType = 'everyMinute' | 'everyHour' | 'daily' | 'weekly' | 'monthly';
interface CronGeneratorProps {
setCronFunction: (data: boolean) => void;
onceConsumer:string[]
setCronFunction: (data: string) => void;
setExpectedTimeRange:(expect:(Dayjs|undefined)[]) => void;
cron?: string;
canSetting:boolean;
}
@ -148,7 +150,9 @@ function cronToChinese(cronExpression: string) {
return description;
}
const CronGenerator: React.FC<CronGeneratorProps> = ({setCronFunction, cron,canSetting}) => {
const CronGenerator: React.FC<CronGeneratorProps> = ({setCronFunction,setExpectedTimeRange,
onceConsumer,
cron,canSetting}) => {
const [showModal, setShowModal] = useState(false);
const [current, setCurrent] = useState<number>(0);
const [cronSeconds, setCronSeconds] = useState<string>(cron ? cron.split(' ')[0] : '*');
@ -183,16 +187,14 @@ const CronGenerator: React.FC<CronGeneratorProps> = ({setCronFunction, cron,canS
try {
generateNextTimeAPI('0 ' + fullCronExpression).then(res => {
setNextOccurrences(res.data.data.map((next: string) => dayjs(next)));
setCronFunction(false);
setExpectedTimeRange([dayjs(res.data.data[0]),
onceConsumer&&onceConsumer.length==2 ? dayjs(res.data.data[0]).add(Number(onceConsumer[0]), onceConsumer[1] as UnitTypeShort):undefined])
setCronFunction(fullCronExpression);
});
console.log('Cron confirmed:', fullCronExpression);
setCronFunction(false);
setShowModal(false)
} catch (error) {
console.error('Cron expression is invalid', error);
}
} else {
setCronFunction(false);
}
};
@ -268,7 +270,7 @@ const CronGenerator: React.FC<CronGeneratorProps> = ({setCronFunction, cron,canS
return (
<Fragment>
<text style={{"paddingRight":"5px"}}>{canReadCron}</text>
<text style={{"paddingRight":"5px"}}>{cron&&canReadCron}</text>
<Button type="primary" onClick={() => setShowModal(!showModal)} disabled={!canSetting}>
</Button>

View File

@ -11,11 +11,21 @@ export const clickRecordAPI = (data: TaskScheduleRecordVO) => {
return httpReq.post(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/click`, data)
}
export const editClickRecordAPI = (data: TaskScheduleRecordVO) => {
return httpReq.put(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/record/edit`, data)
}
export const deleteClickRecordAPI = (recordId: string):Promise<AxiosResponse<ResponseVO<string>>> => {
return httpReq.delete(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule?id=${recordId}`)
}
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>>> =>{
export const editClickRecordRangeAPI = (data:TaskScheduleRangeVO):Promise<AxiosResponse<ResponseVO<TaskScheduleRecordVO>>> =>{
return httpReq.post(process.env.NEXT_PUBLIC_TODO_REQUEST_URL +
`/task/schedule/updateRange`,data)
}

View File

@ -8,4 +8,13 @@ export type LoginObject = {
export type CaptchaLoginSuccess = {
username: string;
token: string;
}
export type AskLoginResult = {
username:string;
nickname:string;
token:string;
setting:string;
autoLogin:boolean;
expireAt:string;
}

View File

@ -1,10 +1,13 @@
import {httpReq} from "@/utils/axiosReq";
import {AskLoginResult} from "@/lib/login/definitions";
import {Axios, AxiosResponse} from "axios";
import {ResponseVO} from "@/lib/definitions";
export const generateQrcodeAPI = (data:{}) => {
return httpReq.post(process.env.NEXT_PUBLIC_SECURITY_REQUEST_URL + "/V2/wx/login/generate/qrcode",
data)
}
export const askLoginAPI = (data:{})=>{
export const askLoginAPI = (data:{}):Promise<AxiosResponse<ResponseVO<AskLoginResult[]>>> =>{
return httpReq.post(process.env.NEXT_PUBLIC_SECURITY_REQUEST_URL + "/V2/wx/login/pc/ask/login",
data)
}

View File

@ -78,6 +78,9 @@ export default function XcxLoginPage() {
}
// localStorage.removeItem("platform-security")
localStorage.setItem('platform-security', res.data.data[0].token)
localStorage.setItem('user-message', JSON.stringify(res.data.data[0],(key, value) => {
return key === 'password' ? undefined : value;
}))
// 删除名为 'platform-security' 的Cookie
// Cookies.remove('platform-security');
// 设置一个有效期为7天的Cookie

View File

@ -31,6 +31,7 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
const [pathParam, setPathParam] = useState<string | undefined>(searchParams.toString());
const [pathName, setPathName] = useState(pathname);
console.log('usePathname()', pathname);
console.log('useSearchParams()', searchParams.toString(), searchParams.get('pName'), searchParams.get('pid'));
const data = useContext(LocalContext);
@ -41,7 +42,7 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
expectStartTimeParseResult[0] && expectStartTimeParseResult[0].value ? dayjs(expectStartTimeParseResult[0].value.toString()) : undefined,
expectStartTimeParseResult[1] && expectStartTimeParseResult[1].value ? dayjs(expectStartTimeParseResult[1].value.toString()) : undefined
];
const [nickName,setNickName]=useState("微信用户")
const typeList: CheckboxOptionType<string>[] = [
{label: '计划', value: '0,1,2,3'},
{label: '打卡', value: '4'},
@ -58,9 +59,18 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
setPathParam(searchParams.toString());
}, [searchParams]); // searchParams 变化时触发
useEffect(() => {
let userMessage = localStorage.getItem('user-message');
if (userMessage){
let parse = JSON.parse(userMessage);
setNickName(parse.nickname)
}
}, []);
const onClick: MenuProps['onClick'] = ({key}) => {
if (key == "1") {
localStorage.removeItem('platform-security')
replace(`/login`)
} else if (key == "2") {
replace(pathName)
setPathParam(undefined)
@ -191,9 +201,12 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
}
</Space>
<Dropdown menu={{items: pathParam ? itemsPid : items, onClick}}>
<svg style={{height: "32px", width: "32px", alignItems: "center"}} className="icon" aria-hidden="true">
<use xlinkHref="#icon-user__easyico"></use>
</svg>
<div style={{display:"flex", height: "32px", alignItems: "center"}}>
<svg style={{ height: "32px",width: "32px",marginRight:"0.5rem" }} className="icon" aria-hidden="true">
<use xlinkHref="#icon-user__easyico"></use>
</svg>
<span>{nickName}</span>
</div>
</Dropdown>
</div>
}

View File

@ -6,7 +6,7 @@ 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, getTaskTreeResult, getTaskTreeResultAPI, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
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";
@ -14,12 +14,11 @@ 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 TaskNamePrefixIcon 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";
import {editClickRecordRangeAPI} from "@/components/service/ScheduleTask";
/**
* https://github.com/jquense/react-big-calendar?tab=readme-ov-file
@ -138,7 +137,11 @@ const CalShow: React.FC = () => {
return {
start: dayjs(taskState.expectedStartTime).toDate(),
end: dayjs(taskState.expectedEndTime).toDate(),
title: <TaskNameAndIcon task={taskState}/>,
title: <Fragment><TaskNameAndIcon task={taskState}/><div>{taskState.description}</div></Fragment>,
// style: {
// backgroundColor: 'green',
// color: 'white',
// },
name:taskState.name,
resource: taskState.id,
id: taskState.id,
@ -252,7 +255,7 @@ const CalShow: React.FC = () => {
}
let strings = state.split(",");
console.log('result', result, strings)
return result.filter((ev: TaskEvent) => strings.indexOf(ev.state.toString()) >= 0);
return result.filter((ev: TaskEvent) => !ev.state || strings.indexOf(ev.state.toString()) >= 0);
})
}, 250)
}
@ -265,7 +268,7 @@ const CalShow: React.FC = () => {
event.allDay = true
}
if (event.taskType=='4'){
editClickRecordAPI({
editClickRecordRangeAPI({
id:event.id,
startDate:dayjs(start!).toDate(),
recordDate:dayjs(end!).toDate(),
@ -294,11 +297,11 @@ const CalShow: React.FC = () => {
const eventPropGetter = useCallback(
(event: TaskEvent) => ({
...(event.state === 7
...(event.state === '7'
? {className: 'completeTask'}
: event.priority === 3 ? {className: 'importantUrgentTask'} :
event.priority === 2 ? {className: 'importantNotUrgentTask'} :
event.priority === 1 ? {className: 'notImportantUrgentTask'} :
: event.priority === '3' ? {className: 'importantUrgentTask'} :
event.priority === '2' ? {className: 'importantNotUrgentTask'} :
event.priority === '1' ? {className: 'notImportantUrgentTask'} :
{className: 'notImportantNotUrgentTask'}),
}),
[setEvents]
@ -343,7 +346,9 @@ const CalShow: React.FC = () => {
closeOpen={() => setOpen(false)}
expectedEndTime={expectedEndTime}/>}
{openClickRecord &&
<ClickRecord recordId={recordId} taskName={taskName} openClickRecord={openClickRecord} setOpenClickRecord={setOpenClickRecord}/>}
<ClickRecord recordId={recordId} taskName={taskName}
openClickRecord={openClickRecord} setOpenClickRecord={setOpenClickRecord}
reloadData={loadData}/>}
<DragAndDropCalendar
// 本地设置
localizer={localizer}

View File

@ -329,6 +329,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
}
if (taskType == '3') {
result.push(<ClickRecord taskId={props.itemId!} taskName={requestTask!.name}
reloadData={props.reloadData}
onceConsume={requestTask!.onceConsume}/>)
}
return result;
@ -557,7 +558,8 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
placeholder="请输入任务描述"
disabled={editFormDisable}
/>
<TaskRemindComponent remindTypeList={remindTypeList} setRemindTypeList={(a:string[])=>setRemindTypeList(a)} readonly={editFormDisable} />
{taskType=='0'&&
<TaskRemindComponent remindTypeList={remindTypeList} setRemindTypeList={(a:string[])=>setRemindTypeList(a)} readonly={editFormDisable} />}
<ProForm.Group>
<ProFormSelect
request={async () =>
@ -596,7 +598,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
</ProForm.Group>
<ProForm.Group>
<ProFormDateTimeRangePicker
{taskType!='3'&& <ProFormDateTimeRangePicker
initialValue={[dayjs(), undefined]}
name="expectedTimeRange"
label="期望时间"
@ -609,8 +611,22 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
placeholder={['开始时间', '结束时间']}
disabled={editFormDisable}
/>
<ProFormDateTimeRangePicker
/>}
{taskType=='3'&& <ProFormDateTimeRangePicker
initialValue={[dayjs(), undefined]}
name="expectedTimeRange"
label="下次触发时间"
fieldProps={{
showTime: {
format: 'HH:mm',
},
format: "YYYY-MM-DD HH:mm", allowEmpty: [true, true], needConfirm: true
}}
placeholder={['开始时间', '结束时间']}
disabled={editFormDisable}
/>}
{taskType!='3'&&<ProFormDateTimeRangePicker
name="actualTimeRange"
label="实际时间"
fieldProps={{
@ -621,7 +637,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
}}
placeholder={['开始时间', '结束时间']}
disabled={editFormDisable}
/>
/>}
</ProForm.Group>
{taskType == "3" &&
<Fragment>
@ -665,8 +681,11 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
{/*</Space>*/}
</div>}
<SettingCron canSetting={!editFormDisable} setCronFunction={() => {
}}/></Fragment>}
<SettingCron canSetting={!editFormDisable} cron={form.getFieldValue("cron")}
setCronFunction={(cron:string) => {form.setFieldValue("cron",cron)}}
onceConsumer={onceConsumeChange}
setExpectedTimeRange={(expect:(Dayjs|undefined)[])=>{form.setFieldValue("expectedTimeRange",expect)}}
/></Fragment>}
</ModalForm>
</Fragment>

View File

@ -1,6 +1,5 @@
import axios, {Canceler, CancelToken, CancelTokenSource} from "axios";
import axios, {CancelTokenSource} from "axios";
import {message} from "antd";
import {usePathname, useSearchParams} from "next/navigation";
export const httpReq = axios.create({