feat:日历滑动

This commit is contained in:
1708-huayu 2025-01-26 15:18:18 +08:00
parent 55cfe79dab
commit 69a1bd0de3
9 changed files with 302 additions and 81 deletions

33
package-lock.json generated
View File

@ -19,6 +19,7 @@
"node-gyp": "^9.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-infinite-scroll-component": "^6.1.0",
"react-router-dom": "^6.6.2",
"react-scripts": "5.0.1",
"react-virtualized": "^9.22.3",
@ -14160,6 +14161,17 @@
"resolved": "https://registry.npmmirror.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/react-infinite-scroll-component": {
"version": "6.1.0",
"resolved": "https://registry.npmmirror.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz",
"integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==",
"dependencies": {
"throttle-debounce": "^2.1.0"
},
"peerDependencies": {
"react": ">=16.0.0"
}
},
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
@ -15757,6 +15769,14 @@
"resolved": "https://registry.npmmirror.com/throat/-/throat-6.0.2.tgz",
"integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ=="
},
"node_modules/throttle-debounce": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz",
"integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/thunky": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz",
@ -27773,6 +27793,14 @@
"resolved": "https://registry.npmmirror.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"react-infinite-scroll-component": {
"version": "6.1.0",
"resolved": "https://registry.npmmirror.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz",
"integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==",
"requires": {
"throttle-debounce": "^2.1.0"
}
},
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
@ -29019,6 +29047,11 @@
"resolved": "https://registry.npmmirror.com/throat/-/throat-6.0.2.tgz",
"integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ=="
},
"throttle-debounce": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz",
"integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ=="
},
"thunky": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz",

View File

@ -16,6 +16,7 @@
"node-gyp": "^9.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-infinite-scroll-component": "^6.1.0",
"react-router-dom": "^6.6.2",
"react-scripts": "5.0.1",
"react-virtualized": "^9.22.3",

View File

@ -8,3 +8,18 @@ export const addTaskLog = (taskLog) => {
export const listTaskLog = (request) => {
return requestUtil.get('/todo-server/V2/search/task_message_diary?search=' + encodeURI(request));
}
export const updateTaskLogEnableById = (enableFlag, id) => {
let request = {
"updateColumnList": [{"name": "enableFlag", "value": enableFlag, "operateType": "="}],
"conditionColumnList": [{"name": "id", "value": id, "operateType": "="}]
}
return requestUtil.put('/todo-server/search/task_message_diary', request);
}
export const deleteTaskLogById = (id) => {
let request = {
"updateColumnList": [{"name": "deleteFlag", "value": "1", "operateType": "="}],
"conditionColumnList": [{"name": "id", "value": id, "operateType": "="}]
}
return requestUtil.put('/todo-server/search/task_message_diary', request);
}

View File

@ -23,7 +23,7 @@ const DetailForm = () => {
const {setTitle, setRightDesc} = useOutletContext();
const [currentPath, setCurrentPath] = React.useState("");
const [updateFiledDisabled, setUpdateFiledDisabled] = React.useState(true);
const [pName, setPName] = React.useState();
const [pName, setPName] = React.useState("");
const [pidArray, setPidArray] = React.useState([]);
const [stateList, setStateList] = React.useState([]);
const [priorityList, setPriorityList] = React.useState([]);
@ -47,6 +47,18 @@ const DetailForm = () => {
setCurrentPath("addTask");
setUpdateFiledDisabled(false);
setRightDesc(<Fragment/>)
// 日志创建任务会传来pidArray和pName
if (params.get('pidArray') && params.get('pName') && params.get('description')) {
setPName(params.get('pName'));
setPidArray(params.get('pidArray').split(','))
form.setFieldValue("pidArray", params.get('pidArray').split(','))
form.setFieldValue("description", params.get('description'))
if (params.get('description').length>5){
form.setFieldValue("name", params.get('description').substring(0,5))
}else {
form.setFieldValue("description", params.get('description'))
}
}
} else if (location.pathname.endsWith("updateTask")) {
setTitle("修改任务");
setCurrentPath("updateTask");

View File

@ -1,11 +1,21 @@
.log-task-detail{
display: flex;
.log-task-detail {
height: calc(100vh - 45px);
/* 确保子元素可以相对于此容器定位 */
position: relative;
display: flex;
flex-direction: column;
}
.log-send{
.log-detail {
/*position: absolute;*/
overflow-y: auto;
width: 100%;
display: flex;
max-height: calc(100vh - 140px);
flex-direction: column-reverse;
}
.log-send {
position: absolute;
bottom: 0;
width: 100%;
@ -15,12 +25,13 @@
border-radius: 12px;
}
.log-send .send-button{
.log-send .send-button {
border-radius: 12px;
width: 80px;
height: 40px;
}
.detail-line{
.detail-line {
border-radius: 6px;
border: solid;
width: auto;

View File

@ -1,10 +1,12 @@
import {ActionSheet, Button, Dialog, Divider, Tabs, TextArea, Toast} from "antd-mobile";
import {Fragment, useEffect, useMemo, useRef, useState} from "react";
import React, {Fragment, useEffect, useMemo, useRef, useState} from "react";
import "./index.css"
import {useOutletContext, useSearchParams} from "react-router-dom";
import {addTaskLog, listTaskLog} from "../../api/detailLogTaskApi";
import {useNavigate, useOutletContext, useSearchParams} from "react-router-dom";
import {addTaskLog, deleteTaskLogById, listTaskLog, updateTaskLogEnableById} from "../../api/detailLogTaskApi";
import dayjs from "dayjs";
import {copyTextToClipboard} from "../../utils/copyToClipBoard";
import {getPTask} from "../../utils";
import InfiniteScroll from "react-infinite-scroll-component";
export function DetailLogTask() {
// 设置标题栏
@ -14,114 +16,150 @@ export function DetailLogTask() {
setRightDesc(<Fragment/>);
}, [])
let [params] = useSearchParams();
// 路由
const navigate = useNavigate();
// 发送展示日记
const [currentShow, setCurrentShow] = useState('need');
const textAreaRef = useRef(null);
const [taskLogList, setTaskLogList] = useState([]);
const [sendValue, setSendValue] = useState('');
const [hasMore, setHasMore] = useState(true);
const [pageTime, setPageTime] = useState(new Date());
// 记录上一次的滚动高度
const previousScrollHeight = useRef(0);
// 点击操作面板
const [actionSheetVisible, setActionSheetVisible] = useState(false)
const [currentTask, setCurrentTask] = useState({});
const [actions,setActions] = useState([
{ text: '复制', key: 'copy' },
{ text: '有效', key: 'need' },
{ text: '创建任务', key: 'addTask'},
{
text: '删除',
key: 'delete',
onClick: async () => {
const result = await Dialog.confirm({ content: '确定要删除吗?' })
if (result) {
Toast.show('执行了删除操作')
}
},
},
const [actions, setActions] = useState([
{text: '复制', key: 'copy'},
{text: '有效', key: 'need'},
{text: '创建任务', key: 'addTask'},
{text: '删除', key: 'delete'},
]);
const taskLogMapMemory = useMemo(() => {
return taskLogList.reduce((map, taskLog) => {
if (!map.has(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))) {
map.set(dayjs(taskLog.createdDate).format("YYYY-MM-DD"), []);
}
map.get(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))?.push(taskLog);
return map;
}, new Map());
}, [taskLogList,currentShow])
return taskLogList
.filter(taskLog => {
if (currentShow === 'all') {
return true
} else if (currentShow === 'need' && taskLog.enableFlag === '1') {
return true
} else return currentShow === 'noNeed' && taskLog.enableFlag === '0';
})
.reduce((map, taskLog) => {
if (!map.has(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))) {
map.set(dayjs(taskLog.createdDate).format("YYYY-MM-DD"), []);
}
map.get(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))?.push(taskLog);
return map;
}, new Map());
}, [taskLogList, currentShow])
const handleSend = () => {
addTaskLog({
"description": sendValue,
"taskId": params.get('id'),
"enableFlag": '1'
}).then(res => {
// 加入当前集合中
setTaskLogList([...taskLogList,res]);
setTaskLogList([...taskLogList, res]);
})
setSendValue('')
textAreaRef.current.focus();
};
useEffect(() => {
textAreaRef.current.focus();
async function fetchMessages() {
// 获取之前的日志信息根据日期分组排序遍历map
listTaskLog(`{
"sortList":[{"direction":"DESC","property":"createdDate"}],
const res = await listTaskLog(JSON.stringify({
"pageSize": 20,
"sortList": [{"direction": "DESC", "property": "createdDate"}],
"data": {
"andList":[{
"name":"taskId",
"operateType":"=",
"value":"${params.get('id')}"
"andList": [{
"name": "taskId",
"operateType": "=",
"value": params.get('id')
}, {
"name": "createdDate",
"operateType": "<",
"value": pageTime
}]
}
}`).then(res => {
console.log({res})
if (res.content.length > 0) {
setTaskLogList(res.content.reverse());
// setTaskLogMap(res.content.reduce((map, taskLog) => {
// if (!map.has(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))) {
// map.set(dayjs(taskLog.createdDate).format("YYYY-MM-DD"), []);
// }
// map.get(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))?.push(taskLog);
// return map;
// }, new Map()));
}
})
}))
console.log({res})
if (res.content.length > 0) {
setPageTime(res.content[res.content.length - 1].createdDate)
setTaskLogList([...taskLogList, ...res.content]);
} else if (res.page.totalElements === 0) {
setHasMore(false);
}
}
useEffect(() => {
setPageTime(new Date())
textAreaRef.current.focus();
fetchMessages()
}, [])
function NoNeed(){
function NoNeed() {
return <s>失效</s>
}
return <Fragment>
<div className="log-task-detail">
<div className="log-detail">
<Tabs defaultActiveKey='need' onChange={(key)=>setCurrentShow(key)}>
<Tabs.Tab title='全部' key='all' />
<Tabs.Tab title='有效' key='need' />
<Tabs.Tab key='noneed' title=<NoNeed/> />
</Tabs>
{
Array.from(taskLogMapMemory.keys()).map(key => {
return (<Fragment>
{dayjs().isSame(dayjs(key), "date") ?
<div key={key} style={{width: "100%", padding: "0 40px"}}>
<Divider>今日</Divider>
</div> :
<div key={key} style={{width: "100%", padding: "0 20px"}}>
<Divider>{key}</Divider>
</div>}
{taskLogMapMemory.get(key).map(taskLog => {
return <div key={taskLog.id} style={{ display: 'flex', width: "80%", padding: "2px 20px"}}>
<span className="detail-line" onClick={()=>{
actions[1]={ text: '失效', key: 'noneed' }
<Tabs defaultActiveKey='need' onChange={(key) => setCurrentShow(key)}>
<Tabs.Tab title='全部' key='all'/>
<Tabs.Tab title='有效' key='need'/>
<Tabs.Tab key='noNeed' title=<NoNeed/> />
</Tabs>
<div id="scrollableDiv" className="log-detail"
>
<InfiniteScroll
dataLength={taskLogList.length}
next={fetchMessages}
hasMore={hasMore}
inverse={true} // 设置为 true 来反转列表的方向,使新消息在底部
loader={<h4>加载中...</h4>}
style={{display: 'flex', flexDirection: 'column-reverse'}}
scrollableTarget="scrollableDiv"
endMessage={
<Divider>没有更久远的信息了</Divider>
}
>
{
Array.from(taskLogMapMemory.keys()).map(key => {
return (<Fragment>
{taskLogMapMemory.get(key).map(taskLog => {
return <div key={taskLog.id}
style={{display: 'flex', width: "80%", padding: "2px 20px"}}>
<span className="detail-line" onClick={() => {
if (taskLog.enableFlag === '0') {
actions[1] = {text: '有效', key: 'need'}
} else {
actions[1] = {text: '失效', key: 'noNeed'}
}
// setActions([...actions])
setCurrentTask(taskLog)
setActionSheetVisible(true)
}}>
{taskLog.description}</span>
</div>
})}</Fragment>)
})
}
</div>
})}
{dayjs().isSame(dayjs(key), "date") ?
<div key={key} style={{width: "100%", padding: "0 40px"}}>
<Divider>今日</Divider>
</div> :
<div key={key} style={{width: "100%", padding: "0 20px"}}>
<Divider>{key}</Divider>
</div>}
</Fragment>)
})
}
</InfiniteScroll>
</div>
<div className="log-send">
<TextArea
ref={(node) => {
@ -133,6 +171,13 @@ export function DetailLogTask() {
rows={1}
autoSize={{minRows: 1, maxRows: 5}}
maxLength={100}
onKeyDown={event => {
if (event.key === 'Enter') {
handleSend();
// 阻止换行符插入
event.preventDefault();
}
}}
/>
<Button
className="send-button"
@ -148,6 +193,48 @@ export function DetailLogTask() {
onAction={action => {
if (action.key === 'copy') {
copyTextToClipboard(currentTask.description)
} else if (action.key === 'delete') {
Dialog.confirm({
content: '确定要删除吗?', onConfirm: () => {
deleteTaskLogById(currentTask.id).then(result => {
setTaskLogList(taskLogList.filter(log => log.id !== currentTask.id))
})
}
})
} else if (action.key === 'noNeed') {
updateTaskLogEnableById('0', currentTask.id).then(result => {
setTaskLogList(taskLogList.map(log => {
if (log.id === currentTask.id) {
log.enableFlag = '0'
}
return log;
})
)
});
} else if (action.key === 'need') {
updateTaskLogEnableById('1', currentTask.id).then(result => {
setTaskLogList(taskLogList.map(log => {
if (log.id === currentTask.id) {
log.enableFlag = '1'
}
return log;
})
)
});
} else if (action.key === 'addTask') {
// 跳转到新增和新建下一条差不多设置pidArray
// 获取父任务信息
Dialog.confirm({
content: '确定要创建任务吗?', onConfirm: () => {
getPTask(params.get('id')).then(res => {
let parentMessageVOList = res[0].parentMessageVOList;
console.log({res, parentMessageVOList});
// 进入新增页面
navigate(`/detail/addTask?
pName=${parentMessageVOList[parentMessageVOList.length - 1].name}&pidArray=${parentMessageVOList.map(parent => parent.id).join(',')}&description=${currentTask.description}`)
})
}
})
}
setActionSheetVisible(false)
}}

View File

@ -232,7 +232,7 @@ const DetailSearchContext = () => {
<CalendarPicker
visible={visible}
selectionMode='single'
defaultDate={getFieldValue('todoDay')}
defaultValue={getFieldValue('todoDay')??new Date()}
onClose={() => {
setVisible(false)
}}

View File

@ -140,9 +140,12 @@ const ToDoList = () => {
text: '删除',
color: 'danger',
onClick: async () => {
await Dialog.confirm({
const result = await Dialog.confirm({
content: '确定要删除吗?',
})
if (result){
await deleteTaskById(item.id)
}
// 关闭当前激活的 SwipeAction
closeActiveSwipeAction();
localRefresh()

View File

@ -0,0 +1,59 @@
.chat-window {
display: flex;
flex-direction: column;
height: 80vh;
width: 300px;
margin: auto;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, .1);
/*overflow:auto*/
}
.messages-container {
flex: 1;
padding: 10px;
overflow-y: auto;
}
.message {
margin: 5px 0;
padding: 8px;
border-radius: 4px;
max-width: 60%;
}
.message.user {
background-color: #d1e7dd;
align-self: flex-end;
}
.message.other {
background-color: #f8d7da;
align-self: flex-start;
}
.input-area {
display: flex;
border-top: 1px solid #ccc;
}
.input-area input {
flex: 1;
padding: 10px;
font-size: 16px;
border: none;
outline: none;
}
.input-area button {
padding: 10px;
border: none;
background-color: #007bff;
color: white;
cursor: pointer;
}
.input-area button:hover {
background-color: #0056b3;
}