feat:四象限拖拽

This commit is contained in:
1708-huayu 2025-07-10 19:03:22 +08:00
parent 5da031aebc
commit 4b32486a5f
9 changed files with 123 additions and 20 deletions

View File

@ -7,26 +7,35 @@ import {DragDropContext, DropResult} from 'react-beautiful-dnd';
import {DroppableTable} from "@/ui/task/drag/DroppableTable"; import {DroppableTable} from "@/ui/task/drag/DroppableTable";
import {DataType, Request} from "@/lib/definitions"; import {DataType, Request} from "@/lib/definitions";
import {TaskSelectVO} from "@/lib/task/drag/data"; import {TaskSelectVO} from "@/lib/task/drag/data";
import {selectTaskAPI} from "@/lib/task/drag/service"; import {moveItem, selectTaskAPI} from "@/lib/task/drag/service";
import LocalContext from "@/ui/LocalContent"; import LocalContext from "@/ui/LocalContent";
import {useSearchParams} from "next/dist/client/components/navigation"; import {useSearchParams} from "next/dist/client/components/navigation";
export default function Layout({children}: { children: React.ReactNode }) { export default function Layout({children}: { children: React.ReactNode }) {
const [allTaskList, setAllTaskList] = useState<DataType[]>([]); const [allTaskList, setAllTaskList] = useState<DataType[]>([]);
// 3
const [importUrgent, setImportUrgent] = useState<DataType[]>([]);
// 2
const [notImportUrgent, setNotImportUrgent] = useState<DataType[]>([]);
// 1
const [importNotUrgent, setImportNotUrgent] = useState<DataType[]>([]);
// 0
const [notImportNotUrgent, setNotImportNotUrgent] = useState<DataType[]>([]);
const data = useContext(LocalContext); const data = useContext(LocalContext);
console.log('data',data); console.log('data',data);
let pid = useSearchParams().get('pid'); let pid = useSearchParams().get('pid');
useEffect(() => { useEffect(() => {
addData() selectData()
}, [data]) }, [data])
function addData() { function selectData() {
const requestParam: Request<TaskSelectVO> = { const requestParam: Request<TaskSelectVO> = {
pageSize: 1000, pageSize: 1000,
pageNumber: 1, pageNumber: 1,
data: {state: data.taskState,pid:pid} data: {state: data.taskState,pid:pid,treeList:true,treeOrList:false,treeFilter:true},
sortList:[{property:"sort_no",direction:"ASC"}]
} }
if (data.expectedStartTime.length>0){ if (data.expectedStartTime.length>0){
const parse = JSON.parse(data.expectedStartTime); const parse = JSON.parse(data.expectedStartTime);
@ -35,34 +44,88 @@ export default function Layout({children}: { children: React.ReactNode }) {
} }
selectTaskAPI(requestParam).then(res=>{ selectTaskAPI(requestParam).then(res=>{
setAllTaskList(res.data.content) setAllTaskList(res.data.content)
setImportUrgent(res.data.content.filter(task => task.priority == '3'))
setNotImportUrgent(res.data.content.filter(task => task.priority == '2'))
setImportNotUrgent(res.data.content.filter(task => task.priority == '1'))
setNotImportNotUrgent(res.data.content.filter(task => task.priority == '0'))
}) })
} }
const filterByIndex = (list:DataType[],filterIndex:number)=>{
return list.filter((item,index)=>index!=filterIndex)
}
const addItemByIndex = (list:DataType[],addIndex:number,item:DataType)=>{
console.log("list.splice(addIndex,0,item)",list,addIndex,item)
list.splice(addIndex,0,item)
return list;
}
// 处理拖拽结束事件 // 处理拖拽结束事件
const onDragEnd = (result: DropResult) => { const onDragEnd = async (result: DropResult) => {
console.log('拖拽结束') console.log('拖拽结束',{result})
const {source, destination} = result; const {source, destination} = result;
console.log('Source Droppable ID:', source.droppableId);
console.log('Destination Droppable ID:', destination?.droppableId);
if (!destination || !['table1', 'table2'].includes(destination.droppableId)) return;
// 如果拖拽到无效区域,直接返回 // 如果拖拽到无效区域,直接返回
if (!destination) return; if (!destination) return;
// 如果拖拽到同一个表格的同一个位置,直接返回 // 如果拖拽到同一个表格的同一个位置,直接返回
if (source.droppableId === destination.droppableId && source.index === destination.index) { if (destination==source) return;
return;
let sourceList =allTaskList.filter(task => task.priority == source.droppableId)
let currentId = sourceList[source.index].id;
let destinationList=allTaskList.filter(task => task.priority == destination.droppableId);
let preId;
let nextId;
if(destination.droppableId==source.droppableId){
// 从后往前
if (source.index>destination.index){
if (destination.index!=0){
preId=destinationList[destination.index-1].id
}
nextId=destinationList[destination.index].id
}else {
preId=destinationList[destination.index].id
if (destination.index!=destinationList.length-1){
nextId=destinationList[destination.index+1].id
}
}
}else {
if (destination.index!=0){
preId=destinationList[destination.index-1].id
}
if (destination.index!=destinationList.length){
nextId=destinationList[destination.index].id
}
// 移除
if (source.droppableId=="3"){setImportUrgent(filterByIndex(importUrgent,source.index))}
if (source.droppableId=="2"){setNotImportUrgent(filterByIndex(notImportUrgent,source.index))}
if (source.droppableId=="1"){setImportNotUrgent(filterByIndex(importNotUrgent,source.index))}
if (source.droppableId=="0"){setNotImportNotUrgent(filterByIndex(notImportNotUrgent,source.index))}
// 加入
if (destination.droppableId=="3"){setImportUrgent(addItemByIndex(importUrgent,destination.index,sourceList[source.index]))}
if (destination.droppableId=="2"){setNotImportUrgent(addItemByIndex(notImportUrgent,destination.index,sourceList[source.index]))}
if (destination.droppableId=="1"){setImportNotUrgent(addItemByIndex(importNotUrgent,destination.index,sourceList[source.index]))}
if (destination.droppableId=="0"){setNotImportNotUrgent(addItemByIndex(notImportNotUrgent,destination.index,sourceList[source.index]))}
} }
try{
await moveItem({priority:destination.droppableId,currentId,preId,nextId})
selectData()
}catch (e){
console.log(e)
}
}; };
return (<div style={{display: 'flex', flexWrap: 'wrap', boxSizing: 'border-box', width: '100vw'}}> return (<div style={{display: 'flex', flexWrap: 'wrap', boxSizing: 'border-box', width: '100vw'}}>
<DragDropContext onDragEnd={onDragEnd}> <DragDropContext onDragEnd={onDragEnd}>
{/* 紧急重要 */} {/* 紧急重要 */}
<DroppableTable tableCode='3' taskList={allTaskList.filter(task => task.priority == '3')}/> <DroppableTable refreshDate={selectData} tableCode='3' taskList={importUrgent}/>
{/* 不紧急重要 */} {/* 不紧急重要 */}
<DroppableTable tableCode='2' taskList={allTaskList.filter(task => task.priority == '2')}/> <DroppableTable refreshDate={selectData} tableCode='2' taskList={notImportUrgent}/>
{/* 紧急不重要 */} {/* 紧急不重要 */}
<DroppableTable tableCode="1" taskList={allTaskList.filter(task => task.priority == '1')}/> <DroppableTable refreshDate={selectData} tableCode="1" taskList={importNotUrgent}/>
{/* 不紧急不重要 */} {/* 不紧急不重要 */}
<DroppableTable tableCode='0' taskList={allTaskList.filter(task => task.priority == '0')}/> <DroppableTable refreshDate={selectData} tableCode='0' taskList={notImportNotUrgent}/>
</DragDropContext></div> </DragDropContext></div>
); );
} }

View File

@ -6,7 +6,7 @@ import dayjs from "dayjs";
import {ConfigProvider} from "antd"; import {ConfigProvider} from "antd";
export default function Layout({children}: { children: React.ReactNode }) { export default function Layout({children}: { children: React.ReactNode }) {
const [taskState, setTaskState] = React.useState<string>('8,9') const [taskState, setTaskState] = React.useState<string>('8,9,10')
let expectStartTimeList = []; let expectStartTimeList = [];
expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().subtract(7, 'day'), 'operateType': ">="}); expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().subtract(7, 'day'), 'operateType': ">="});
expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().add(7, 'day'), 'operateType': "<"}) expectStartTimeList.push({'name': "expectedStartTime", 'value': dayjs().add(7, 'day'), 'operateType': "<"})

View File

@ -5,6 +5,7 @@ export type Request<T>={
data:T, data:T,
pageSize:number, pageSize:number,
pageNumber:number, pageNumber:number,
sortList?:{direction:string,property:string}[]
} }
type Status={ type Status={
success:boolean; success:boolean;

View File

@ -2,6 +2,9 @@ export type TaskSelectVO = {
pid?:string|undefined|null; pid?:string|undefined|null;
name?: string; name?: string;
state?: string; state?: string;
treeList?:boolean;
treeOrList?:boolean;
treeFilter?:boolean;
priority?: string; priority?: string;
allOverdueTasks?:boolean; allOverdueTasks?:boolean;
expectedStartTimeStart?:string; expectedStartTimeStart?:string;

View File

@ -13,3 +13,13 @@ export async function selectTaskAPI(requestParam: Request<TaskSelectVO>):
// 从响应中提取数据并返回 // 从响应中提取数据并返回
return response.data; return response.data;
} }
export async function moveItem(requestParam:{}):
Promise<ResponseVO<boolean|undefined>> {
noStore();
// 使用 Axios 发送 PUT 请求获取数据
const response: AxiosResponse<ResponseVO<boolean|undefined>> = await httpReq.post(
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/task/moveItem', requestParam);
// 从响应中提取数据并返回
return response.data;
}

View File

@ -124,7 +124,7 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
{ {
/*日历需要状态*/ /*日历需要状态*/
!usePathname().startsWith("/task/project") && <Fragment> !usePathname().startsWith("/task/project") && <Fragment>
<Button></Button> <Button type={"primary"} onClick={()=>{refreshData()}}></Button>
</Fragment> </Fragment>
} }
</Space> </Space>

View File

@ -12,7 +12,8 @@ import RightOption from "@/ui/task/RightOption";
interface DroppableTableProps { interface DroppableTableProps {
tableCode: string, tableCode: string,
taskList: DataType[] taskList: DataType[],
refreshDate?: () => void,
} }
export const DroppableTable = React.memo((props: DroppableTableProps) => { export const DroppableTable = React.memo((props: DroppableTableProps) => {
@ -79,7 +80,9 @@ export const DroppableTable = React.memo((props: DroppableTableProps) => {
{props.taskList.map((record, index) => { {props.taskList.map((record, index) => {
return <Draggable key={record.id} draggableId={record.id} index={index}> return <Draggable key={record.id} draggableId={record.id} index={index}>
{(provided, snapshot) => ( {(provided, snapshot) => (
<RightOption itemId={record.id} itemName={record.name} pid={record.pid} pPid={record.pPid} children={<div <RightOption refreshDate={props.refreshDate} itemId={record.id}
itemName={record.name} pid={record.pid}
pPid={record.pPid} children={<div
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
{...provided.dragHandleProps} {...provided.dragHandleProps}

View File

@ -222,6 +222,11 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
onChange={(value:string, option)=>{ onChange={(value:string, option)=>{
setTaskType(value) setTaskType(value)
}} }}
rules={[
{required:true,
message:"请输入计划类型"
}
]}
/> />
<ProFormText <ProFormText
required={true} required={true}
@ -254,6 +259,14 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
tooltip="最长为 10 位" tooltip="最长为 10 位"
placeholder="请输入任务名称" placeholder="请输入任务名称"
disabled ={editFormDisable} disabled ={editFormDisable}
rules={[
{required:true,
message:"请输入计划名称"
},{
max:10,
message:"名称长度不易超过10个字"
}
]}
/> />
</ProForm.Group> </ProForm.Group>
<ProFormTextArea <ProFormTextArea
@ -278,6 +291,11 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
label="任务优先级" label="任务优先级"
initialValue='3' initialValue='3'
disabled ={editFormDisable} disabled ={editFormDisable}
rules={[
{required:true,
message:"请选择计划优先级"
}
]}
/> />
<ProFormSelect <ProFormSelect
width="sm" width="sm"
@ -291,6 +309,11 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
label="任务状态" label="任务状态"
initialValue='8' initialValue='8'
disabled ={editFormDisable} disabled ={editFormDisable}
rules={[
{required:true,
message:"请选择计划状态"
}
]}
/> />
</ProForm.Group> </ProForm.Group>

View File

@ -8,7 +8,7 @@ export const httpReq = axios.create({
validateStatus: function (status) { validateStatus: function (status) {
return status >= 200 && status < 300; // default return status >= 200 && status < 300; // default
}, },
timeout: 2000, timeout: 10000,
timeoutErrorMessage: "网络连接超时", timeoutErrorMessage: "网络连接超时",
withCredentials: false, withCredentials: false,
}) })