feat:日历滑动

This commit is contained in:
1708-huayu 2025-01-24 18:56:31 +08:00
parent c2eb04b4b0
commit 55cfe79dab
5 changed files with 234 additions and 111 deletions

View File

@ -5,7 +5,7 @@ import {
Button, Button,
Dialog, Dialog,
TextArea, TextArea,
Space, Tag, Radio Space, Tag, Radio, Checkbox
} from 'antd-mobile' } from 'antd-mobile'
import ParentTask from "../ParentTask"; import ParentTask from "../ParentTask";
import "./index.css" import "./index.css"
@ -13,25 +13,34 @@ import {addTask, getPTask, getTaskById, updateTask} from "../../utils";
import {useLocation, useNavigate, useOutletContext, useSearchParams} from "react-router-dom"; import {useLocation, useNavigate, useOutletContext, useSearchParams} from "react-router-dom";
import dayjs from "dayjs"; import dayjs from "dayjs";
import DataPickItemPopup from "../DataPickItemPopup"; import DataPickItemPopup from "../DataPickItemPopup";
import {getDictionary} from "../../utils/dictUtil";
const DetailForm= () => { const DetailForm = () => {
// 进入此页面的操作:添加,修改,详情(按钮为添加任务日志) // 进入此页面的操作:添加,修改,详情(按钮为添加任务日志)
const location = useLocation(); const location = useLocation();
let [params] = useSearchParams(); let [params] = useSearchParams();
// 设置标题栏 // 设置标题栏
const {setTitle,setRightDesc} = useOutletContext(); const {setTitle, setRightDesc} = useOutletContext();
const [currentPath, setCurrentPath] = React.useState(""); const [currentPath, setCurrentPath] = React.useState("");
const [updateFiledDisabled, setUpdateFiledDisabled] = React.useState(true); const [updateFiledDisabled, setUpdateFiledDisabled] = React.useState(true);
const [pName, setPName] = React.useState(); const [pName, setPName] = React.useState();
const [pidArray, setPidArray] = React.useState([]); const [pidArray, setPidArray] = React.useState([]);
const [stateList, setStateList] = React.useState([]);
const [priorityList, setPriorityList] = React.useState([]);
// 路由 // 路由
const navigate = useNavigate(); const navigate = useNavigate();
// 获取form引用 // 获取form引用
const [form] = Form.useForm(); const [form] = Form.useForm();
const addEditPName=(name)=>{ const addEditPName = (name) => {
setPName(name) setPName(name)
} }
useEffect(() => { useEffect(() => {
getDictionary("2").then(stateDictionary => {
setStateList(Array.from(stateDictionary.values()));
})
getDictionary("1").then(priorityDictionary => {
setPriorityList(Array.from(priorityDictionary.values()));
})
window.scrollTo(0, 0); window.scrollTo(0, 0);
if (location.pathname.endsWith("addTask")) { if (location.pathname.endsWith("addTask")) {
setTitle("添加任务"); setTitle("添加任务");
@ -50,7 +59,8 @@ const DetailForm= () => {
initData(params.get('id')); initData(params.get('id'));
setUpdateFiledDisabled(true); setUpdateFiledDisabled(true);
setRightDesc(<div> setRightDesc(<div>
<Button type="button" color="danger" onClick={() => {Dialog.show({ <Button type="button" color="danger" onClick={() => {
Dialog.show({
content: `进入任务编辑`, content: `进入任务编辑`,
closeOnAction: true, closeOnAction: true,
actions: [ actions: [
@ -68,7 +78,8 @@ const DetailForm= () => {
} }
], ],
], ],
})}}>编辑</Button> })
}}>编辑</Button>
</div>) </div>)
} else { } else {
// todo 异常处理 // todo 异常处理
@ -85,7 +96,7 @@ const DetailForm= () => {
console.log({res, parentMessageVOList}); console.log({res, parentMessageVOList});
setPName(parentMessageVOList[parentMessageVOList.length - 1].name); setPName(parentMessageVOList[parentMessageVOList.length - 1].name);
setPidArray(parentMessageVOList.map(parent => parent.id)) setPidArray(parentMessageVOList.map(parent => parent.id))
form.setFieldValue("pidArray",parentMessageVOList.map(parent => parent.id)) form.setFieldValue("pidArray", parentMessageVOList.map(parent => parent.id))
}) })
} }
@ -218,23 +229,41 @@ const DetailForm= () => {
showCount showCount
/> />
</Form.Item> </Form.Item>
<Form.Item name='state' initialValue='8' label='任务状态' rules={[{required: true, message: '任务状态不能为空'}]} disabled={updateFiledDisabled}> <Form.Item name='state' initialValue='8' label='任务状态'
rules={[{required: true, message: '任务状态不能为空'}]} disabled={updateFiledDisabled}>
<Radio.Group> <Radio.Group>
<Space direction='vertical'> <Space direction='vertical'>
<Radio value='8'><Tag color='primary'>未开始</Tag></Radio> {/*<Radio value='8'><Tag color='primary'>未开始</Tag></Radio>*/}
<Radio value='9'><Tag color='warning'>进行中</Tag></Radio> {/*<Radio value='9'><Tag color='warning'>进行中</Tag></Radio>*/}
<Radio value='7'><Tag color='success'>已完成</Tag></Radio> {/*<Radio value='7'><Tag color='success'>已完成</Tag></Radio>*/}
<Radio value='10'><Tag color='danger'>已逾期</Tag></Radio> {/*<Radio value='10'><Tag color='danger'>已逾期</Tag></Radio>*/}
{
stateList.map(stateDict =>
<Radio key={stateDict.itemCode} value={stateDict.itemCode}>
<Tag key={stateDict.itemCode}
color={stateDict.jsonValue?.color}>{stateDict.itemName}</Tag>
</Radio>
)
}
</Space> </Space>
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
<Form.Item name='priority' label='任务优先级' initialValue='2' rules={[{required: true, message: '任务优先级不能为空'}]} disabled={updateFiledDisabled}> <Form.Item name='priority' label='任务优先级' initialValue='2'
rules={[{required: true, message: '任务优先级不能为空'}]} disabled={updateFiledDisabled}>
<Radio.Group> <Radio.Group>
<Space direction='vertical'> <Space direction='vertical'>
<Radio value='3'><Tag color='danger'>紧急重要</Tag></Radio> {/*<Radio value='3'><Tag color='danger'>紧急重要</Tag></Radio>*/}
<Radio value='2'><Tag color='warning'>不紧急重要</Tag></Radio> {/*<Radio value='2'><Tag color='warning'>不紧急重要</Tag></Radio>*/}
<Radio value='1'><Tag>紧急不重要</Tag></Radio> {/*<Radio value='1'><Tag>紧急不重要</Tag></Radio>*/}
<Radio value='0'><Tag color='success'>不紧急不重要</Tag></Radio> {/*<Radio value='0'><Tag color='success'>不紧急不重要</Tag></Radio>*/}
{
priorityList.map(stateDict =>
<Radio key={stateDict.itemCode} value={stateDict.itemCode}>
<Tag key={stateDict.itemCode}
color={stateDict.jsonValue?.color}>{stateDict.itemName}</Tag>
</Radio>
)
}
</Space> </Space>
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
@ -244,7 +273,8 @@ const DetailForm= () => {
labelName={"预计结束时间"}/> labelName={"预计结束时间"}/>
<DataPickItemPopup disabled={updateFiledDisabled} fieldName={"actualStartTime"} <DataPickItemPopup disabled={updateFiledDisabled} fieldName={"actualStartTime"}
labelName={"实际开始时间"}/> labelName={"实际开始时间"}/>
<DataPickItemPopup disabled={updateFiledDisabled} fieldName={"actualEndTime"} labelName={"实际结束时间"}/> <DataPickItemPopup disabled={updateFiledDisabled} fieldName={"actualEndTime"}
labelName={"实际结束时间"}/>
</Form> </Form>
</> </>
) )

View File

@ -0,0 +1,3 @@
.adm-cascader-header-title{
flex: 0.5;
}

View File

@ -1,18 +1,27 @@
import {Cascader, Input, Toast} from "antd-mobile"; import {Cascader, Input, SearchBar, Toast} from "antd-mobile";
import React, {useEffect, useMemo, useState} from "react"; import React, {useEffect, useMemo, useState} from "react";
import { import {
Form, Form,
} from 'antd-mobile' } from 'antd-mobile'
import {getTaskByPid} from "../../utils"; import {getTaskByPid} from "../../utils";
import "./index.css"
import {CloseCircleFill} from "antd-mobile-icons";
const ParentTask = (props) => { const ParentTask = (props) => {
const [valueToOptions, setValueToOptions] = useState([]) const [valueToOptions, setValueToOptions] = useState([])
const {form, disabled, pName, pidArray, addEditPName} = props; const {form, disabled, pName, pidArray, addEditPName} = props;
const [parentValue, setParentValue] = useState(pidArray ?? []) const [parentValue, setParentValue] = useState(pidArray ?? [])
const [visible, setVisible] = useState(false) const [visible, setVisible] = useState(false)
const [searchValue, setSearchValue] = useState("");
const [currentLab, setCurrentLab] = useState(0);
const [selectValue, setSelectValue] = useState([]);
// 当前标签
// let currentLab=0;
// let selectValue=[];
const options = useMemo(() => { const options = useMemo(() => {
console.log("useMemo")
function generate(v) { function generate(v) {
const options = valueToOptions[v] const options = valueToOptions[v]
if (options === null) { if (options === null) {
@ -21,6 +30,27 @@ const ParentTask = (props) => {
if (options === undefined) { if (options === undefined) {
return Cascader.optionSkeleton return Cascader.optionSkeleton
} }
// 如果有搜索有值,需要过滤,
// 根据currentLab查看需要过滤第几层当前层可能没有值就会停留在上一层
if (searchValue && searchValue.length > 0) {
if (selectValue.length <= currentLab + 1) {
// 可能展示最新的,或没有停留在当前页面,正常是没有选择的时候搜索
console.log({searchValue}, {currentLab}, {selectValue})
if (currentLab === 0 && v === '0') {
return options.filter(option => option.label.includes(searchValue)).map(option => ({
...option,
children: generate(option.value),
}))
} else if (v === selectValue[currentLab - 1]) {
return options.filter(option => option.label.includes(searchValue)).map(option => ({
...option,
children: generate(option.value),
}))
}
} else {
// 用户切换回之前的标签,暂不处理
}
}
return options.map(option => ({ return options.map(option => ({
...option, ...option,
children: generate(option.value), children: generate(option.value),
@ -28,7 +58,7 @@ const ParentTask = (props) => {
} }
return generate('0') ?? [] return generate('0') ?? []
}, [valueToOptions]) }, [valueToOptions, searchValue])
async function fetchOptionsForValue(v, level) { async function fetchOptionsForValue(v, level) {
if (v in valueToOptions) return if (v in valueToOptions) return
@ -72,13 +102,38 @@ const ParentTask = (props) => {
return <Form.Item return <Form.Item
name='pidArray' name='pidArray'
label='主线任务' label='主线任务'
value={parentValue}
disabled={disabled}
arrow={
(form.getFieldValue('pidArray') && form.getFieldValue('pidArray').length > 0) ? (
<CloseCircleFill
style={{
color: 'var(--adm-color-light)',
fontSize: 14,
}}
onClick={e => {
form.setFieldsValue({pidArray: []})
setParentValue([])
// 阻止冒泡
e.stopPropagation()
// 阻止所有后续事件处理程序
// e.nativeEvent.stopImmediatePropagation();
}}
/>) : true
}
onClick={() => { onClick={() => {
setVisible(true) setVisible(true)
}} }}
value={parentValue}
disabled={disabled}
> >
<Cascader <Cascader
title={<SearchBar placeholder='搜索当前层相关标题' onChange={
// 获取当前选卡,过滤当前选项
(value) => {
console.log("搜索" + value)
setSearchValue(value);
}
}/>}
options={options} options={options}
visible={visible} visible={visible}
defaultValue={pidArray} defaultValue={pidArray}
@ -91,11 +146,13 @@ const ParentTask = (props) => {
setParentValue(val[val.length - 1]) setParentValue(val[val.length - 1])
form.setFieldValue('pidArray', val) form.setFieldValue('pidArray', val)
}} }}
// onSelect={(val, extend) => { onTabsChange={index => {
// console.log('onSelect', val, extend.items) console.log(index);
// }} setCurrentLab(index)
}}
onSelect={value => { onSelect={value => {
console.log("value", value) console.log("value", value, currentLab)
setSelectValue(value)
value.forEach((v, index) => { value.forEach((v, index) => {
fetchOptionsForValue(v, index + 1) fetchOptionsForValue(v, index + 1)
}) })
@ -103,10 +160,11 @@ const ParentTask = (props) => {
> >
{items => { {items => {
if (items.every(item => item === null)) { if (items.every(item => item === null)) {
return pName ? (<span>{pName}</span>) : disabled ? return (pName && form.getFieldValue('pidArray') && form.getFieldValue('pidArray').length > 0) ? (
<span>{pName}</span>) : disabled ?
(<span>主线任务选填</span>) : (<span>主线任务选填</span>) :
(<span style={{color: "#cccccc"}}>主线任务选填</span>) (<span style={{color: "#cccccc"}}>主线任务选填</span>)
} else { } else if (items) {
if (addEditPName) { if (addEditPName) {
addEditPName(items[items.length - 1].label) addEditPName(items[items.length - 1].label)
} }

View File

@ -26,7 +26,7 @@ export function DetailLogTask() {
const [currentTask, setCurrentTask] = useState({}); const [currentTask, setCurrentTask] = useState({});
const [actions,setActions] = useState([ const [actions,setActions] = useState([
{ text: '复制', key: 'copy' }, { text: '复制', key: 'copy' },
{ text: '有效', key: 'edit' }, { text: '有效', key: 'need' },
{ text: '创建任务', key: 'addTask'}, { text: '创建任务', key: 'addTask'},
{ {
text: '删除', text: '删除',
@ -48,7 +48,7 @@ export function DetailLogTask() {
map.get(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))?.push(taskLog); map.get(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))?.push(taskLog);
return map; return map;
}, new Map()); }, new Map());
}, [taskLogList]) }, [taskLogList,currentShow])
const handleSend = () => { const handleSend = () => {
addTaskLog({ addTaskLog({
"description": sendValue, "description": sendValue,
@ -110,7 +110,7 @@ export function DetailLogTask() {
{taskLogMapMemory.get(key).map(taskLog => { {taskLogMapMemory.get(key).map(taskLog => {
return <div key={taskLog.id} style={{ display: 'flex', width: "80%", padding: "2px 20px"}}> return <div key={taskLog.id} style={{ display: 'flex', width: "80%", padding: "2px 20px"}}>
<span className="detail-line" onClick={()=>{ <span className="detail-line" onClick={()=>{
actions[1]={ text: '失效', key: 'edit' } actions[1]={ text: '失效', key: 'noneed' }
// setActions([...actions]) // setActions([...actions])
setCurrentTask(taskLog) setCurrentTask(taskLog)
setActionSheetVisible(true) setActionSheetVisible(true)

View File

@ -1,4 +1,4 @@
import {Calendar, Cascader, Tag} from "antd-mobile"; import {Calendar, Cascader, SwipeAction, Tag} from "antd-mobile";
import dayjs from "dayjs"; import dayjs from "dayjs";
import {TaskCount} from "../TaskCount"; import {TaskCount} from "../TaskCount";
import React, {Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState} from "react"; import React, {Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState} from "react";
@ -12,6 +12,7 @@ import './index.css'
const ToDoCal = (props) => { const ToDoCal = (props) => {
const today = new Date() const today = new Date()
const calRef = useRef(null); const calRef = useRef(null);
const refSwip = useRef(null);
const [currentDay, setCurrentDay] = React.useState(new Date()) const [currentDay, setCurrentDay] = React.useState(new Date())
const [currentMonth, setCurrentMonth] = React.useState(dayjs().set("date", 1).format('YYYY-MM-DD')); const [currentMonth, setCurrentMonth] = React.useState(dayjs().set("date", 1).format('YYYY-MM-DD'));
// Map keyvalue // Map keyvalue
@ -57,6 +58,36 @@ const ToDoCal = (props) => {
return ( return (
<Fragment> <Fragment>
<SwipeAction
ref={refSwip}
rightActions={[
{
key: 'next',
text: '自送进入下一个月',
color: 'success',
},
]}
leftActions={[
{
key: 'last',
text: '自送进入上一个月',
color: 'success',
},
]}
onActionsReveal={(sideType) => {
console.log(sideType,sideType === 'left',sideType === 'right',currentMonth)
if (sideType === 'left') {
const newMonth = dayjs(currentMonth).subtract(1, 'months')
calRef.current.jumpTo({year: newMonth.year(), month: newMonth.month()+1})
console.log(newMonth.year(),newMonth.month()+1)
} else if (sideType === 'right') {
const newMonth = dayjs(currentMonth).add(1, 'months')
calRef.current.jumpTo({year: newMonth.year(), month: newMonth.month()+1})
console.log(newMonth.year(),newMonth.month()+1)
}
refSwip.current?.close()
}}
>
<Calendar <Calendar
ref={calRef} ref={calRef}
selectionMode='single' selectionMode='single'
@ -121,6 +152,7 @@ const ToDoCal = (props) => {
}} }}
/> />
</SwipeAction>
<TaskCount currentDay={currentDay} taskCount={listTaskMap?.get(currentMonth)} today={today} <TaskCount currentDay={currentDay} taskCount={listTaskMap?.get(currentMonth)} today={today}
currentMonth={currentMonth} currentMonth={currentMonth}
backToToday={backToToday}/> backToToday={backToToday}/>