feat:树任务跳转
This commit is contained in:
parent
7ddeb50e3f
commit
779a28a364
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"antd-mobile": "^5.27.0",
|
||||
"antd-mobile": "^5.28.0",
|
||||
"antd-mobile-icons": "^0.3.0",
|
||||
"axios": "^1.2.2",
|
||||
"dayjs": "^1.11.13",
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import {Button, Divider, TextArea} from "antd-mobile";
|
||||
import {ActionSheet, Button, Dialog, Divider, Tabs, TextArea, Toast} from "antd-mobile";
|
||||
import {Fragment, useEffect, useMemo, useRef, useState} from "react";
|
||||
import "./index.css"
|
||||
import {useOutletContext, useSearchParams} from "react-router-dom";
|
||||
import {addTaskLog, listTaskLog} from "../../api/detailLogTaskApi";
|
||||
import dayjs from "dayjs";
|
||||
import {copyTextToClipboard} from "../../utils/copyToClipBoard";
|
||||
|
||||
export function DetailLogTask() {
|
||||
// 设置标题栏
|
||||
|
@ -13,11 +14,32 @@ export function DetailLogTask() {
|
|||
setRightDesc(<Fragment/>);
|
||||
}, [])
|
||||
let [params] = useSearchParams();
|
||||
|
||||
// 发送展示日记
|
||||
const [currentShow, setCurrentShow] = useState('need');
|
||||
const textAreaRef = useRef(null);
|
||||
const [taskLogMap, setTaskLogMap] = useState([]);
|
||||
const [taskLogList, setTaskLogList] = useState([]);
|
||||
const [sendValue, setSendValue] = useState([]);
|
||||
|
||||
// 点击操作面板
|
||||
const [actionSheetVisible, setActionSheetVisible] = useState(false)
|
||||
const [currentTask, setCurrentTask] = useState({});
|
||||
const [actions,setActions] = useState([
|
||||
{ text: '复制', key: 'copy' },
|
||||
{ text: '有效', key: 'edit' },
|
||||
{ text: '创建任务', key: 'addTask'},
|
||||
{
|
||||
text: '删除',
|
||||
key: 'delete',
|
||||
onClick: async () => {
|
||||
const result = await Dialog.confirm({ content: '确定要删除吗?' })
|
||||
if (result) {
|
||||
Toast.show('执行了删除操作')
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const taskLogMapMemory = useMemo(() => {
|
||||
return taskLogList.reduce((map, taskLog) => {
|
||||
if (!map.has(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))) {
|
||||
|
@ -42,7 +64,14 @@ export function DetailLogTask() {
|
|||
textAreaRef.current.focus();
|
||||
// 获取之前的日志信息,根据日期分组排序,遍历map
|
||||
listTaskLog(`{
|
||||
"sortList":[{"direction":"DESC","property":"createdDate"}]
|
||||
"sortList":[{"direction":"DESC","property":"createdDate"}],
|
||||
"data": {
|
||||
"andList":[{
|
||||
"name":"taskId",
|
||||
"operateType":"=",
|
||||
"value":"${params.get('id')}"
|
||||
}]
|
||||
}
|
||||
}`).then(res => {
|
||||
console.log({res})
|
||||
if (res.content.length > 0) {
|
||||
|
@ -57,9 +86,17 @@ export function DetailLogTask() {
|
|||
}
|
||||
})
|
||||
}, [])
|
||||
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>
|
||||
|
@ -72,7 +109,13 @@ export function DetailLogTask() {
|
|||
</div>}
|
||||
{taskLogMapMemory.get(key).map(taskLog => {
|
||||
return <div key={taskLog.id} style={{ display: 'flex', width: "80%", padding: "2px 20px"}}>
|
||||
<span className="detail-line">{taskLog.description}</span>
|
||||
<span className="detail-line" onClick={()=>{
|
||||
actions[1]={ text: '失效', key: 'edit' }
|
||||
// setActions([...actions])
|
||||
setCurrentTask(taskLog)
|
||||
setActionSheetVisible(true)
|
||||
}}>
|
||||
{taskLog.description}</span>
|
||||
</div>
|
||||
})}</Fragment>)
|
||||
})
|
||||
|
@ -96,6 +139,19 @@ export function DetailLogTask() {
|
|||
onClick={handleSend}
|
||||
>发送
|
||||
</Button>
|
||||
<ActionSheet
|
||||
extra='请选择你要进行的操作'
|
||||
cancelText='取消'
|
||||
visible={actionSheetVisible}
|
||||
actions={actions}
|
||||
onClose={() => setActionSheetVisible(false)}
|
||||
onAction={action => {
|
||||
if (action.key === 'copy') {
|
||||
copyTextToClipboard(currentTask.description)
|
||||
}
|
||||
setActionSheetVisible(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -3,40 +3,19 @@ import {getTaskCount} from "../../utils";
|
|||
import dayjs from "dayjs";
|
||||
import {DATE_FORMAT, dayStartUtcFormat, nextDayStartUtcFormat} from "../../utils/timeFormatUtil";
|
||||
import {getDictionary} from "../../utils/dictUtil";
|
||||
import {Tag} from "antd-mobile";
|
||||
import {Divider, Tag} from "antd-mobile";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {MyRootContext, UPDATE_SEARCH} from "../../components/MyRootContext";
|
||||
|
||||
|
||||
const TaskCount = (props) => {
|
||||
let currentDay = props.currentDay;
|
||||
const {currentDay, taskCount, today, backToToday} = props;
|
||||
const navigate = useNavigate();
|
||||
const [taskCount, setTaskCount] = React.useState([]);
|
||||
const [stateMap, setStateMap] = React.useState(new Map);
|
||||
const [priorityMap, setPriorityMap] = React.useState(new Map);
|
||||
const {dispatch } = useContext(MyRootContext);
|
||||
useEffect(() => {
|
||||
console.log("useEffect");
|
||||
if (currentDay) {
|
||||
getTaskCount(dayStartUtcFormat(currentDay),
|
||||
nextDayStartUtcFormat(currentDay))
|
||||
.then(taskCount => {
|
||||
setTaskCount(taskCount)
|
||||
})
|
||||
getDictionary("2").then(state => {
|
||||
setStateMap(state)
|
||||
})
|
||||
getDictionary("1").then(priority => {
|
||||
console.log(priority)
|
||||
setPriorityMap(priority)
|
||||
})
|
||||
}else {
|
||||
setTaskCount([])
|
||||
}
|
||||
const {dispatch} = useContext(MyRootContext);
|
||||
|
||||
}, [currentDay])
|
||||
|
||||
const todoDayDetail = ()=>{
|
||||
const todoDayDetail = () => {
|
||||
let andSearchModel = {}
|
||||
let orSearchModel = {andSearchModel}
|
||||
if (currentDay) {
|
||||
|
@ -82,49 +61,56 @@ const TaskCount = (props) => {
|
|||
}
|
||||
}
|
||||
console.log({orSearchModel})
|
||||
dispatch({type:UPDATE_SEARCH,search:{
|
||||
dispatch({
|
||||
type: UPDATE_SEARCH, search: {
|
||||
"pageSize": 12,
|
||||
"pageNumber": 1,
|
||||
"data": {
|
||||
orSearchModel
|
||||
}
|
||||
}})
|
||||
}
|
||||
})
|
||||
navigate("/home/listTask")
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log("useEffect");
|
||||
getDictionary("2").then(state => {
|
||||
setStateMap(state)
|
||||
})
|
||||
getDictionary("1").then(priority => {
|
||||
console.log(priority)
|
||||
setPriorityMap(priority)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div style={{margin:"20px"}}>
|
||||
<div style={{margin: "20px"}}>
|
||||
<h2>TODO日{currentDay && dayjs(currentDay).format(DATE_FORMAT)}代办:
|
||||
{currentDay&&<a onClick={todoDayDetail}>详情</a>}</h2>
|
||||
<h3>任务状态</h3>
|
||||
{
|
||||
// taskCount.map(task => {
|
||||
// // if (dayjs(task.todoDay).isSame(dayjs(currentDay))){
|
||||
// // console.log(dict);
|
||||
// // return <span key={task.todoDay}>{task.todoDay}</span>
|
||||
// return Array.from(stateMap.entries()).map(([item,value]) => {
|
||||
// console.log("key",item,"value",value,task.state)
|
||||
// return <Tag color={value.josnValue?.color}>value.itemName</Tag> + task.state[item]
|
||||
// })
|
||||
//
|
||||
// // return task.priority.map((key,value)=>getDictionary(2).get(key)+value)
|
||||
// // }
|
||||
// })
|
||||
taskCount[0] && Object.keys(taskCount[0].state).map(ob => {
|
||||
return <div style={{marginBottom:"20px"}} key={ob}><Tag color={
|
||||
stateMap.get(ob).jsonValue?stateMap.get(ob).jsonValue.color:"default"
|
||||
}>{stateMap.get(ob).itemName}</Tag> 共
|
||||
{taskCount[0].state[ob]} 项代办;</div>;
|
||||
})
|
||||
}
|
||||
<h3>优先级</h3>
|
||||
{
|
||||
taskCount[0] && Object.keys(taskCount[0].priority).map(ob => {
|
||||
console.log("stateMap.get(ob).jsonValue?.color", priorityMap.get(ob))
|
||||
return <div style={{marginBottom:"20px"}} key={ob}><Tag
|
||||
color={priorityMap.get(ob).jsonValue.color}>{priorityMap.get(ob).itemName}</Tag> 共
|
||||
{taskCount[0].priority[ob]} 项代办;</div>;
|
||||
})
|
||||
{currentDay && <a onClick={todoDayDetail}>详情</a>}
|
||||
{!dayjs(currentDay).isSame(today, 'date') &&
|
||||
<Fragment><Divider direction='vertical'/><a onClick={() => backToToday()}>回到今天</a></Fragment>}
|
||||
</h2>
|
||||
{taskCount.filter(taskC => dayjs(taskC.todoDay).isSame(dayjs(currentDay), 'date'))?.map(taskC => {
|
||||
return <Fragment>
|
||||
<h3>任务状态</h3>
|
||||
{
|
||||
Object.keys(taskC.state).map(ob => {
|
||||
return <div style={{marginBottom: "20px"}} key={ob}><Tag color={
|
||||
stateMap.get(ob).jsonValue ? stateMap.get(ob).jsonValue.color : "default"
|
||||
}>{stateMap.get(ob).itemName}</Tag> 共
|
||||
{taskC.state[ob]} 项代办;</div>;
|
||||
})}
|
||||
<h3>优先级</h3>
|
||||
{
|
||||
Object.keys(taskC.priority).map(ob => {
|
||||
console.log("stateMap.get(ob).jsonValue?.color", priorityMap.get(ob))
|
||||
return <div style={{marginBottom: "20px"}} key={ob}><Tag
|
||||
color={priorityMap.get(ob).jsonValue.color}>{priorityMap.get(ob).itemName}</Tag> 共
|
||||
{taskC.priority[ob]} 项代办;</div>;
|
||||
})
|
||||
}
|
||||
</Fragment>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,26 +1,82 @@
|
|||
import {Calendar} from "antd-mobile";
|
||||
import {Calendar, Tag} from "antd-mobile";
|
||||
import dayjs from "dayjs";
|
||||
import {TaskCount} from "../TaskCount";
|
||||
import React,{Fragment} from "react";
|
||||
import React, {Fragment, useEffect, useLayoutEffect, useRef} from "react";
|
||||
|
||||
import {
|
||||
dateStartUtcFormat,
|
||||
nextDateStartUtcFormat,
|
||||
} from "../../utils/timeFormatUtil";
|
||||
import {getTaskCount} from "../../utils";
|
||||
import {FrownFill, SmileFill} from "antd-mobile-icons";
|
||||
import {NEW, OVERDUE, UNDER_WAY} from "../../utils/commonConstant";
|
||||
const ToDoCal = (props) => {
|
||||
const calRef = useRef(null);
|
||||
const [currentDay, setCurrentDay] = React.useState(new Date())
|
||||
const today = dayjs()
|
||||
const today = new Date()
|
||||
const [allDay,setAllDay] = React.useState([])
|
||||
const [taskCount,setTaskCount] = React.useState([])
|
||||
function listTaskCount(){
|
||||
if (allDay.length === 0){
|
||||
return
|
||||
}
|
||||
getTaskCount(dateStartUtcFormat(allDay[0]),
|
||||
nextDateStartUtcFormat(allDay[allDay.length-1]))
|
||||
.then(res => {
|
||||
setTaskCount(res)
|
||||
})
|
||||
}
|
||||
useLayoutEffect(()=>{
|
||||
console.log("useLayoutEffect",allDay)
|
||||
|
||||
listTaskCount();
|
||||
},[])
|
||||
function backToToday(){
|
||||
calRef.current.jumpToToday();
|
||||
setCurrentDay(today)
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<Calendar
|
||||
ref={calRef}
|
||||
selectionMode='single'
|
||||
renderLabel={date => {
|
||||
if (dayjs(date).isSame(today, 'day')) return '今天'
|
||||
if (date.getDay() === 0 || date.getDay() === 6) {
|
||||
return '周末'
|
||||
}
|
||||
allDay.push(date)
|
||||
|
||||
// 没有任务不显示
|
||||
return taskCount.filter(taskC => dayjs(taskC.todoDay).isSame(dayjs(date), 'date'))?.map(taskC => {
|
||||
// 如果有逾期的 红色
|
||||
// Object.keys() 返回一个包含对象自身所有可枚举属性的数组
|
||||
// Object.entries() 返回一个包含对象所有可枚举属性的键值对数组。
|
||||
if (Object.keys(taskC.state).length>0){
|
||||
console.log("taskC.state",taskC.state)
|
||||
if (taskC.state[OVERDUE]){
|
||||
return <FrownFill color='var(--adm-color-danger)'/>;
|
||||
}
|
||||
// 如果有未完成的任务 warn
|
||||
if (taskC.state[NEW]||taskC.state[UNDER_WAY]){
|
||||
return <SmileFill color='var(--adm-color-warning)'/>;
|
||||
}
|
||||
// 任务全部完成 绿色
|
||||
return <SmileFill color='var(--adm-color-success)'/>;
|
||||
}
|
||||
})
|
||||
|
||||
// if (dayjs(date).isSame(today, 'day')) return '今天'
|
||||
// if (date.getDay() === 0 || date.getDay() === 6) {
|
||||
// return '周末'
|
||||
// }
|
||||
}}
|
||||
// renderDate={date=>{
|
||||
// return <span style={{"color":"red"}}>{dayjs(date).date()}</span>
|
||||
// }}
|
||||
defaultValue={currentDay}
|
||||
onChange={val => {
|
||||
setCurrentDay(val)
|
||||
}}
|
||||
|
||||
/>
|
||||
<TaskCount currentDay={currentDay}/>
|
||||
<TaskCount currentDay={currentDay} taskCount={taskCount} today={today} backToToday={backToToday}/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,79 +1,24 @@
|
|||
import {Fragment, useEffect, useMemo, useState} from "react";
|
||||
import {Card, Cascader, CascaderView} from "antd-mobile";
|
||||
import {getTaskByPid} from "../../utils";
|
||||
import dayjs from "dayjs";
|
||||
import {DATE_TIME_FORMAT} from "../../utils/timeFormatUtil";
|
||||
import {Fragment} from "react";
|
||||
import {Card, CascaderView, Dialog, Toast} from "antd-mobile";
|
||||
|
||||
import {useChildList} from "../../hooks/useChildList";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export default () => {
|
||||
// const [valueToOptions, setValueToOptions] = useState([])
|
||||
// const [currentTask, setCurrentTask] = useState({
|
||||
// name:"想做的事情",
|
||||
// description:"想做的事情描述",
|
||||
// expectedStartTime:dayjs().format(DATE_TIME_FORMAT),
|
||||
// expectedEndTime:"",
|
||||
// state:"",
|
||||
// priority:"",
|
||||
// })
|
||||
// const options = useMemo(() => {
|
||||
// function generate(v) {
|
||||
// const options = valueToOptions[v]
|
||||
// if (options === null || options === undefined) {
|
||||
// return undefined
|
||||
// }
|
||||
// // if (options === undefined) {
|
||||
// // return Cascader.optionSkeleton
|
||||
// // }
|
||||
// return options.map(option => ({
|
||||
// ...option,
|
||||
// children: generate(option.value),
|
||||
// }))
|
||||
// }
|
||||
//
|
||||
// return generate('0') ?? []
|
||||
// }, [valueToOptions])
|
||||
//
|
||||
// async function fetchOptionsForValue(v, level) {
|
||||
// if (v in valueToOptions) return
|
||||
// // if (level >= 3) {
|
||||
// // setValueToOptions(prev => ({
|
||||
// // ...prev,
|
||||
// // [v]: null,
|
||||
// // }))
|
||||
// // return
|
||||
// // }
|
||||
// const data = await getTaskByPid(v)
|
||||
// console.log("await getTaskByPid(v)", data.content)
|
||||
// const options =
|
||||
// data.content.length === 0
|
||||
// ? null
|
||||
// : data.content.map(task => ({
|
||||
// value: task.id,
|
||||
// label: task.name,
|
||||
// }))
|
||||
// console.log("await getTaskByPid(v) options", options)
|
||||
// if (options){
|
||||
// setValueToOptions(prev => ({
|
||||
// ...prev,
|
||||
// [v]: options,
|
||||
// }))
|
||||
// }else {
|
||||
// setValueToOptions(prev => ({
|
||||
// ...prev,
|
||||
// [v]: undefined,
|
||||
// }))
|
||||
// }
|
||||
// }
|
||||
|
||||
// useEffect(() => {
|
||||
// fetchOptionsForValue('0', 0)
|
||||
// }, [])
|
||||
|
||||
const {task, options, changeTaskId,} = useChildList();
|
||||
const navigate = useNavigate();
|
||||
return <Fragment>
|
||||
<Card title={task?.name} style={{height: "30vh", borderRadius: "10px"}}>
|
||||
<Card title={task?.name} style={{height: "30vh", borderRadius: "10px"}}
|
||||
onClick={async ()=>{
|
||||
if (task&&task.id) {
|
||||
const result = await Dialog.confirm({ content: '确定要进入详情页吗?' })
|
||||
if (result) {
|
||||
navigate(`/detail/selectTask?id=${task.id}`)
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<p>{task?.description}</p>
|
||||
|
||||
<p>预计开始时间:{task?.expectedStartTime}</p>
|
||||
<p>预计结束时间:{task?.expectedEndTime}</p>
|
||||
</Card>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
const OVERDUE= '10';
|
||||
const COMPLETE = '7';
|
||||
const NEW = '8';
|
||||
const UNDER_WAY = '9';
|
||||
export { OVERDUE, COMPLETE, NEW, UNDER_WAY };
|
|
@ -0,0 +1,39 @@
|
|||
export function copyTextToClipboard(text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
// 尝试使用现代剪贴板 API
|
||||
navigator.clipboard.writeText(text)
|
||||
.then(() => resolve('文本已成功复制到剪贴板'))
|
||||
.catch((err) => reject(`无法复制文本:${err.message}`));
|
||||
} else {
|
||||
// 回退到旧方法
|
||||
fallbackCopyTextToClipboard(text)
|
||||
.then(message => resolve(message))
|
||||
.catch(err => reject(err));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fallbackCopyTextToClipboard(text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
|
||||
// 避免 CSS 样式影响滚动条
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-9999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
const successful = document.execCommand('copy');
|
||||
const msg = successful ? '文本已成功复制到剪贴板' : '无法复制文本';
|
||||
resolve(msg);
|
||||
} catch (err) {
|
||||
reject(`无法复制文本:${err.message}`);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
});
|
||||
}
|
|
@ -21,6 +21,22 @@ function dayStartUtcFormat(dayjsString) {
|
|||
return dayJsObj.set('h', 0).set('m', 0).set('s', 0).set('ms', 0).utc().format()
|
||||
}
|
||||
|
||||
function dateStartUtcFormat(dateObject) {
|
||||
if (!dateObject) {
|
||||
return
|
||||
}
|
||||
let dayJsObj= dayjs(dateObject);
|
||||
return dayJsObj.set('h', 0).set('m', 0).set('s', 0).set('ms', 0).utc().format()
|
||||
}
|
||||
|
||||
function nextDateStartUtcFormat(dateObject) {
|
||||
if (!dateObject) {
|
||||
return
|
||||
}
|
||||
let dayJsObj= dayjs(dateObject);
|
||||
return dayJsObj.add(1, "d").set('h', 0).set('m', 0).set('s', 0).set('ms', 0).utc().format()
|
||||
}
|
||||
|
||||
function nextDayStartUtcFormat(dayjsString) {
|
||||
if (!dayjsString) {
|
||||
return
|
||||
|
@ -34,4 +50,7 @@ function nextDayStartUtcFormat(dayjsString) {
|
|||
return dayJsObj.add(1, "d").set('h', 0).set('m', 0).set('s', 0).set('ms', 0).utc().format()
|
||||
}
|
||||
|
||||
export {DATE_TIME_FORMAT, DATE_FORMAT,DATE_TIME_FORMAT_SIMPLE,dayStartUtcFormat,nextDayStartUtcFormat}
|
||||
export {DATE_TIME_FORMAT, DATE_FORMAT,DATE_TIME_FORMAT_SIMPLE,
|
||||
dayStartUtcFormat,nextDayStartUtcFormat,
|
||||
dateStartUtcFormat,nextDateStartUtcFormat,
|
||||
}
|
Loading…
Reference in New Issue