diff --git a/package-lock.json b/package-lock.json index 19e8ce1..1d40120 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "axios": "^1.6.8", "dayjs": "^1.11.13", "js-cookie": "^3.0.5", + "lunar-calendar": "^0.1.4", "next": "14.2.29", "postcss": "8.4.31", "react": "^18", @@ -4840,6 +4841,14 @@ "node": "14 || >=16.14" } }, + "node_modules/lunar-calendar": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/lunar-calendar/-/lunar-calendar-0.1.4.tgz", + "integrity": "sha512-5r87vbg5yg56z/jkf3A+Ur+ZggUTiJw1VATT9P7RELQgWcTNhfJ+OLkNYroSna6r65bMqyaAgapo9vRN40L75A==", + "engines": { + "node": "*" + } + }, "node_modules/luxon": { "version": "3.4.4", "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz", @@ -11500,6 +11509,11 @@ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" }, + "lunar-calendar": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/lunar-calendar/-/lunar-calendar-0.1.4.tgz", + "integrity": "sha512-5r87vbg5yg56z/jkf3A+Ur+ZggUTiJw1VATT9P7RELQgWcTNhfJ+OLkNYroSna6r65bMqyaAgapo9vRN40L75A==" + }, "luxon": { "version": "3.4.4", "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz", diff --git a/package.json b/package.json index 72783ee..f02e1e2 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "axios": "^1.6.8", "dayjs": "^1.11.13", "js-cookie": "^3.0.5", + "lunar-calendar": "^0.1.4", "next": "14.2.29", "postcss": "8.4.31", "react": "^18", diff --git a/src/app/task/drag/layout.tsx b/src/app/task/drag/layout.tsx index 49f8a36..14dcd0a 100644 --- a/src/app/task/drag/layout.tsx +++ b/src/app/task/drag/layout.tsx @@ -30,7 +30,7 @@ export default function Layout({children}: { children: React.ReactNode }) { const [spinning,setSpinning]=useState(false); useEffect(() => { selectData() - }, [data]) + }, [data,pid]) function selectData() { setSpinning(true) diff --git a/src/components/ClickRecord.tsx b/src/components/ClickRecord.tsx index 1ce9581..c6303e0 100644 --- a/src/components/ClickRecord.tsx +++ b/src/components/ClickRecord.tsx @@ -1,5 +1,5 @@ import {Button, Form, message, Popconfirm} from "antd"; -import React, {useEffect, useState} from "react"; +import React, {useEffect, useRef, useState} from "react"; import { ModalForm, ProFormDateTimeRangePicker, @@ -17,7 +17,6 @@ import dayjs, {UnitTypeShort} from "dayjs"; import {onceConsumerRead} from "@/utils/codeToReadName"; import {betweenTime} from "@/utils/timeFormatUtil"; import {QuestionCircleOutlined} from "@ant-design/icons"; -import {deleteTask} from "@/lib/task/project/data"; interface ClickRecordProps { openClickRecord?: boolean; @@ -67,6 +66,7 @@ const ClickRecord: React.FC = ({ form.setFieldsValue(data) } }, [recordId]); + return ( @@ -139,8 +139,11 @@ const ClickRecord: React.FC = ({ } else { await clickRecordAPI(values); } + console.log("before reloadData") + reloadData?.(); + console.log("after reloadData") + // 然后在onFinish中使用 setOpenClickRecord?.(false) - reloadData?.() return true }catch (e){ console.error(e) diff --git a/src/components/TaskNameAndIcon.tsx b/src/components/TaskNameAndIcon.tsx index 30846c6..06f9977 100644 --- a/src/components/TaskNameAndIcon.tsx +++ b/src/components/TaskNameAndIcon.tsx @@ -1,9 +1,15 @@ import {TaskMessage} from "@/lib/definitions"; import React, {Fragment} from "react"; -import {SmileOutlined} from "@ant-design/icons"; +import {CopyOutlined, SmileOutlined} from "@ant-design/icons"; +import {copyToClipboard} from "@/lib/copyToClipboard"; -const TaskNameAndIcon = (props: {task:TaskMessage}) => { +const TaskNameAndIcon = (props: { task: TaskMessage }) => { return ( + { + e.preventDefault(); // 阻止默认行为(如果有) + e.stopPropagation(); // 阻止事件冒泡 + copyToClipboard(props.task.name) + }}/> {props.task.fId && } - {props.task.taskType == '4' && + {props.task.taskType == '4' && } {props.task.name} ) diff --git a/src/lib/type/lunar.d.ts b/src/lib/type/lunar.d.ts new file mode 100644 index 0000000..e99b647 --- /dev/null +++ b/src/lib/type/lunar.d.ts @@ -0,0 +1,23 @@ +declare module 'lunar-calendar' { + export interface LunarDate { + lunarYear: number; + lunarMonth: number; + lunarDay: number; + zodiac: string; + ganZhiYear: string; + ganZhiMonth: string; + ganZhiDay: string; + lunarMonthName: string; + lunarDayName: string; + solarTerm: string; + // 其他农历字段... + } + + export function solarToLunar( + year: number, + month: number, + day: number + ): LunarDate; + + // 其他可能的方法... +} \ No newline at end of file diff --git a/src/ui/task/TitleOperation.tsx b/src/ui/task/TitleOperation.tsx index 7e4929c..69e620e 100644 --- a/src/ui/task/TitleOperation.tsx +++ b/src/ui/task/TitleOperation.tsx @@ -74,7 +74,7 @@ export const TitleOperation: React.FC = ({ } else if (key == "2") { replace(pathName) setPathParam(undefined) - refreshData() + // refreshData() } }; diff --git a/src/ui/task/calendar/CalShow.tsx b/src/ui/task/calendar/CalShow.tsx index 5b37978..d2ef124 100644 --- a/src/ui/task/calendar/CalShow.tsx +++ b/src/ui/task/calendar/CalShow.tsx @@ -1,6 +1,15 @@ '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 { + Calendar, + Culture, + dateFnsLocalizer, + DateLocalizer, + dayjsLocalizer, + Event, + SlotInfo, + View +} from 'react-big-calendar' import dayjs, {Dayjs} from 'dayjs' import 'dayjs/locale/zh-cn'; import 'react-big-calendar/lib/css/react-big-calendar.css' @@ -20,8 +29,9 @@ import {TaskWebSelectVO} from "@/lib/task/project/definitions"; import {message, Modal, Spin} from "antd"; import ClickRecord from "@/components/ClickRecord"; import {editClickRecordRangeAPI} from "@/components/service/ScheduleTask"; -import {ExclamationCircleFilled, LoadingOutlined} from "@ant-design/icons"; - +import {CopyOutlined, ExclamationCircleFilled, LoadingOutlined} from "@ant-design/icons"; +import {copyToClipboard} from "@/lib/copyToClipboard"; +import {solarToLunar} from "lunar-calendar"; const {confirm} = Modal; /** * https://github.com/jquense/react-big-calendar?tab=readme-ov-file @@ -64,7 +74,16 @@ const CalShow: React.FC = () => { expectedEndTime: range.end } }) - + const dateFormat = (date: Date, culture?: Culture, localizer?: DateLocalizer) => { + const solarDate = new Date(date); + const lunarDate = solarToLunar( + solarDate.getFullYear(), + solarDate.getMonth() + 1, + solarDate.getDate() + ); + console.log({lunarDate}) + return `${solarDate.getDate()}/农历:${lunarDate.lunarMonthName}-${lunarDate.lunarDayName}`; + }; const handleViewChange = (newView: View) => { setView(newView); }; @@ -75,19 +94,23 @@ const CalShow: React.FC = () => { clickRef && typeof clickRef.current === 'number' && !isNaN(clickRef.current) && isFinite(clickRef.current) && window.clearTimeout(clickRef.current) } + /** + * 会触发范围变化 + * @param newDate + */ 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); + // 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(); }; useEffect(() => { @@ -102,6 +125,10 @@ const CalShow: React.FC = () => { // ); searchObject.data.pid = pid setSearchObject({...searchObject}) + } else { + const {pid, ...search} = searchObject.data + searchObject.data = search + setSearchObject({...searchObject}) } // searchListE.push({name: 'expectedStartTime', value: range.start, operateType: ">="}) // searchListE.push({name: 'expectedStartTime', value: range.end, operateType: "<="}) @@ -115,7 +142,7 @@ const CalShow: React.FC = () => { return () => { clearClickTimeout() } - }, [state, taskTypeList, refreshData, range]); + }, [state, taskTypeList, refreshData, range, pid]); const calMessages = { week: '周', work_week: '工作周', @@ -135,6 +162,7 @@ const CalShow: React.FC = () => { noEventsInRange: '暂无计划', } const loadData = (searchList?: SearchObject[]) => { + console.log("loadData") setSpinning(true) searchObject.data.state = state searchObject.data.taskTypeList = taskTypeList @@ -148,7 +176,12 @@ const CalShow: React.FC = () => { start: dayjs(taskState.expectedStartTime).toDate(), end: dayjs(taskState.expectedEndTime).toDate(), title: -
{taskState.description}
+
+ {taskState.description && { + e.preventDefault(); // 阻止默认行为(如果有) + e.stopPropagation(); // 阻止事件冒泡 + copyToClipboard(taskState.description) + }}/>} {taskState.description}
, // style: { // backgroundColor: 'green', @@ -198,9 +231,13 @@ const CalShow: React.FC = () => { // } // }) } + /** + * from表单使用,打卡记录新建使用 + */ const reloadData = () => { setOpen(false) - handleNavigate(date) + loadData(); + // handleNavigate(date) } const handleSelectSlot = useCallback( ({start, end}: SlotInfo) => { @@ -246,7 +283,7 @@ const CalShow: React.FC = () => { message.error("计划双击完成,非计划双击无效果。") return; } - if (event.state=='7'){ + if (event.state == '7') { message.error("任务已经完成") return; } @@ -276,7 +313,7 @@ const CalShow: React.FC = () => { if (existing !== undefined && filtered !== undefined) { result = [...filtered, {...existing, state: "7"}]; } - let strings = state==""?[]:state.split(","); + let strings = state == "" ? [] : state.split(","); console.log('result', result, strings) return result.filter((ev: TaskEvent) => !ev.state || strings.length == 0 || strings.indexOf(ev.state.toString()) >= 0); }) @@ -340,36 +377,31 @@ const CalShow: React.FC = () => { 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]}) - range.start=rangeLet[0]; - } else if (range.end.valueOf() < rangeLet[0].valueOf()) { - // setRange({...range, end: rangeLet[0]}) - range.end=rangeLet[0] - } + // if (range.start.valueOf() > rangeLet[0].valueOf()) { + range.start = rangeLet[0]; + // } + // if (range.end.valueOf() < rangeLet[0].valueOf()) { + range.end = dayjs(rangeLet[0]).add(1,'d').toDate() + // } } // 为周的时候类型为数组,周一到周日七天 if ((current ? current : view) === "week" && Array.isArray(rangeLet)) { - if (range.start.valueOf() > rangeLet[0].valueOf()) { - // setRange({...range, start: rangeLet[0]}) - range.start=rangeLet[0]; - } - if (range.end.valueOf() < rangeLet[6].valueOf()) { - // setRange({...range, end: rangeLet[6]}) - range.end=rangeLet[6] - } + // if (range.start.valueOf() > rangeLet[0].valueOf()) { + range.start = rangeLet[0]; + // } + // if (range.end.valueOf() < rangeLet[6].valueOf()) { + range.end = dayjs(rangeLet[6]).add(1,'d').toDate() + // } } // 为月的时候类型为对象 if ((current ? current : view) === "month" && rangeLet && !Array.isArray(rangeLet)) { - if (range.start.valueOf() > rangeLet.start.valueOf()) { - // setRange({...range, start: rangeLet.start}) - range.start = rangeLet.start - } - if (range.end.valueOf() < rangeLet.end.valueOf()) { - // setRange({...range, end: rangeLet.end}) - range.end = rangeLet.end + // if (range.start.valueOf() > rangeLet.start.valueOf()) { + range.start = rangeLet.start + // } + // if (range.end.valueOf() < rangeLet.end.valueOf()) { + range.end = dayjs(rangeLet.end).add(1,'d').toDate() - } + // } } setRange({...range}) } @@ -388,9 +420,24 @@ const CalShow: React.FC = () => { reloadData={loadData}/>} } fullscreen/> { + const solarDate = new Date(date); + const lunarDate = solarToLunar( + solarDate.getFullYear(), + solarDate.getMonth() + 1, + 1 + ); + return `${solarDate.getFullYear()}年${solarDate.getMonth() + 1}月 (农历${lunarDate.lunarMonthName})`; + }, + dayHeaderFormat: dateFormat + }} // 修改style eventPropGetter={eventPropGetter} events={events} diff --git a/src/ui/task/drag/DroppableTable.tsx b/src/ui/task/drag/DroppableTable.tsx index 3e580e6..ed21953 100644 --- a/src/ui/task/drag/DroppableTable.tsx +++ b/src/ui/task/drag/DroppableTable.tsx @@ -10,6 +10,8 @@ import {getTaskState, taskPriorityList, taskStateList} from "@/lib/task/project/ import 'react-virtualized/styles.css'; import RightOption from "@/ui/task/RightOption"; import TaskNameAndIcon from "@/components/TaskNameAndIcon"; +import {copyToClipboard} from "@/lib/copyToClipboard"; +import {CopyOutlined} from "@ant-design/icons"; interface DroppableTableProps { @@ -99,9 +101,13 @@ export const DroppableTable = React.memo((props: DroppableTableProps) => {
-
- +
+ + {record.description && { + e.preventDefault(); + copyToClipboard(record.description) + }}/>}
{record.description}
diff --git a/src/ui/task/project/DetailModelForm.tsx b/src/ui/task/project/DetailModelForm.tsx index 62faaaa..5c89558 100644 --- a/src/ui/task/project/DetailModelForm.tsx +++ b/src/ui/task/project/DetailModelForm.tsx @@ -481,8 +481,6 @@ export const DetailModelForm: React.FC = (props) => { } ); } else { - - await addTask(values).then(response => { console.log('response', response) if (response.data.status.success) { diff --git a/src/ui/task/project/TreeTablePro.tsx b/src/ui/task/project/TreeTablePro.tsx index a76840f..bb6e786 100644 --- a/src/ui/task/project/TreeTablePro.tsx +++ b/src/ui/task/project/TreeTablePro.tsx @@ -1,6 +1,6 @@ 'use client' import { - CheckSquareFilled, + CheckSquareFilled, CopyOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import type {ActionType, FormInstance, ProColumns, ProFormInstance} from '@ant-design/pro-components'; @@ -21,6 +21,7 @@ import '@/ui/task/project/TreeTablePro.modules.css' import {useSearchParams} from "next/navigation"; import {TaskWebSelectVO} from "@/lib/task/project/definitions"; import TaskNameAndIcon from "@/components/TaskNameAndIcon"; +import {copyToClipboard} from "@/lib/copyToClipboard"; const TreeTablePro: React.FC = (props: { joinId?: string }) => { // 刷新表格 @@ -64,9 +65,15 @@ const TreeTablePro: React.FC = (props: { joinId?: string }) => { title: '任务描述', dataIndex: 'description', render: (_, record) => { - return -
{record.description}
-
+ return + + +
{record.description && { + e.preventDefault(); + copyToClipboard(record.description) + }}/>}{record.description}
+
+
} }, { @@ -246,16 +253,16 @@ const TreeTablePro: React.FC = (props: { joinId?: string }) => { name: params.name, description: params.description, } - // 列表展示 + // 列表展示 if (!switchChecked) { - search.treeOrList=false; - search.treeList=true; - search.treeFilter=true; - }else { + search.treeOrList = false; + search.treeList = true; + search.treeFilter = true; + } else { // 树展示 - search.treeOrList=true; - search.treeList=true; - search.treeFilter=filterChecked; + search.treeOrList = true; + search.treeList = true; + search.treeFilter = filterChecked; } if (params.state) { search.state = params.state.join(','); diff --git a/src/utils/axiosReq.ts b/src/utils/axiosReq.ts index 686a646..71c62f0 100644 --- a/src/utils/axiosReq.ts +++ b/src/utils/axiosReq.ts @@ -1,4 +1,4 @@ -import axios, {CancelTokenSource} from "axios"; +import axios, {AxiosInterceptorOptions, CancelTokenSource} from "axios"; import {message} from "antd"; import {refreshTokenAPI} from "@/lib/login/service"; @@ -19,11 +19,14 @@ const cancelTokenSource: CancelTokenSource = axios.CancelToken.source(); httpReq.defaults.headers.common['Accept'] = 'application/json'; // 请求前处理 httpReq.interceptors.request.use((config) => { + console.log("config.url", config.url) // 从本地存储中获取 token const token = localStorage.getItem('platform-security'); if (token) { config.headers.Authorization = `Bearer ${token}`; - reduceToken(token) + if (config.url && config.url.indexOf("refreshToken") == -1) { + reduceToken(token); + } } config.headers.set("source-client", "web") return config; @@ -32,6 +35,9 @@ httpReq.interceptors.request.use((config) => { console.info("interceptors错误提示.request" + error) return Promise.reject(error); }) +httpReq.interceptors.response.use((onFulfilled) => { + return onFulfilled; +}) function reduceToken(token: string) { if (!token || token.split('.').length !== 3) { @@ -62,10 +68,10 @@ function reduceToken(token: string) { console.log("updateToken:decodedToken:", decodedToken); const endTime = decodedToken.exp; console.log('距离过期' + (((endTime as number) - (Date.now() / 1000)) / 60 / 60 + '小时')) - if (((endTime as number) - (Date.now() / 1000)) / 60 / 60 / 24 < 3) { + if (((endTime as number) - (Date.now() / 1000)) / 60 / 60 < 3) { // 小于3小时更新toke3小时或12小时不动过期、内不动 - refreshTokenAPI().then(res=>{ - if (res.data.status.success){ + refreshTokenAPI().then(res => { + if (res.data.status.success) { localStorage.setItem('platform-security', res.data.data) } })