feat:右键,虚拟列表导致拖拽被阻挡,使用enum
This commit is contained in:
parent
135bf4ff41
commit
55e5468d47
|
@ -2,27 +2,40 @@
|
|||
* A layout is UI that is shared between multiple routes. On navigation, layouts preserve state, remain interactive, and do not re-render.
|
||||
*/
|
||||
'use client'
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {DragDropContext, DropResult} from 'react-beautiful-dnd';
|
||||
import {DroppableTable} from "@/ui/task/drag/DroppableTable";
|
||||
import {DataType, Request} from "@/lib/definitions";
|
||||
import {TaskSelectVO} from "@/lib/task/drag/data";
|
||||
import {selectTaskAPI} from "@/lib/task/drag/service";
|
||||
import LocalContext from "@/ui/LocalContent";
|
||||
import {useSearchParams} from "next/dist/client/components/navigation";
|
||||
|
||||
export default function Layout({children}: { children: React.ReactNode }) {
|
||||
const [allTaskList, setAllTaskList] = useState<DataType[]>([]);
|
||||
|
||||
const data = useContext(LocalContext);
|
||||
console.log('data',data);
|
||||
let pid = useSearchParams().get('pid');
|
||||
|
||||
useEffect(() => {
|
||||
addData()
|
||||
}, [])
|
||||
}, [data])
|
||||
|
||||
async function addData() {
|
||||
function addData() {
|
||||
const requestParam: Request<TaskSelectVO> = {
|
||||
pageSize: 1000,
|
||||
pageNumber: 1,
|
||||
data: {state: '9'}
|
||||
data: {state: data.taskState}
|
||||
}
|
||||
const res = await selectTaskAPI(requestParam)
|
||||
setAllTaskList(res.data.content)
|
||||
if (data.expectedStartTime.length>0){
|
||||
const parse = JSON.parse(data.expectedStartTime);
|
||||
requestParam.data.expectedStartTimeStart=parse[0].value;
|
||||
requestParam.data.expectedStartTimeEnd=parse[1].value;
|
||||
}
|
||||
selectTaskAPI(requestParam).then(res=>{
|
||||
setAllTaskList(res.data.content)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理拖拽结束事件
|
||||
|
@ -51,6 +64,5 @@ export default function Layout({children}: { children: React.ReactNode }) {
|
|||
{/* 不紧急不重要 */}
|
||||
<DroppableTable tableCode='0' taskList={allTaskList.filter(task => task.priority == '0')}/>
|
||||
</DragDropContext></div>
|
||||
)
|
||||
;
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,4 +3,6 @@ export type TaskSelectVO = {
|
|||
state?: string;
|
||||
priority?: string;
|
||||
allOverdueTasks?:boolean;
|
||||
expectedStartTimeStart?:string;
|
||||
expectedStartTimeEnd?:string;
|
||||
}
|
|
@ -9,7 +9,7 @@ export async function selectTaskAPI(requestParam: Request<TaskSelectVO>):
|
|||
noStore();
|
||||
// 使用 Axios 发送 PUT 请求获取数据
|
||||
const response: AxiosResponse<ResponseVO<ResultPage<DataType>>> = await httpReq.post(
|
||||
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/task/select', requestParam);
|
||||
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/V2/task/select', requestParam);
|
||||
// 从响应中提取数据并返回
|
||||
return response.data;
|
||||
}
|
|
@ -116,7 +116,7 @@ export const taskPriorityList: DictType[] = [
|
|||
code: '1',
|
||||
name: '不重要紧急',
|
||||
order: 2,
|
||||
color: '#fafafa'
|
||||
color: '#1677ff'
|
||||
}, {
|
||||
id: 0,
|
||||
code: '0',
|
||||
|
@ -216,5 +216,6 @@ export enum OPERATION_BUTTON_TYPE {
|
|||
SHOW_FOUR,
|
||||
SHOW_CALENDAR,
|
||||
ADD,
|
||||
UPDATE_PRIORITY
|
||||
UPDATE_PRIORITY,
|
||||
INIT
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
import React, {Fragment, useState} from 'react';
|
||||
import {Form, MenuProps, message, Popconfirm} from 'antd';
|
||||
import { Dropdown, theme } from 'antd';
|
||||
import {commonUpdate, deleteTask, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
|
||||
import {QuestionCircleOutlined} from "@ant-design/icons";
|
||||
import Link from "next/link";
|
||||
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
|
||||
interface OperationButtonProps {
|
||||
itemId: string,
|
||||
priority?:string,
|
||||
pid: string,
|
||||
pPid: string,
|
||||
operationId?: OPERATION_BUTTON_TYPE,
|
||||
refreshDate?: () => void,
|
||||
children: React.ReactNode
|
||||
}
|
||||
const RightOption: React.FC<OperationButtonProps> = (props) => {
|
||||
const [operateState, setOperateState] = useState({
|
||||
operationId:OPERATION_BUTTON_TYPE.INIT,openModal:false});
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.DETAIL,
|
||||
label: <a onClick={(e) => {
|
||||
setOperateState({openModal: true, operationId: OPERATION_BUTTON_TYPE.DETAIL})
|
||||
}}>任务详情</a>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.ADD_CHILD,
|
||||
label: <a onClick={(e) => {
|
||||
setOperateState({openModal: true, operationId: OPERATION_BUTTON_TYPE.ADD_CHILD})
|
||||
}}>添加支线任务</a>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.UPDATE,
|
||||
label: <a onClick={(e) => {
|
||||
setOperateState({openModal: true, operationId: OPERATION_BUTTON_TYPE.UPDATE})
|
||||
}}>修改任务</a>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.DELETE,
|
||||
label: <Popconfirm
|
||||
title="删除任务"
|
||||
description="确认要删除任务?"
|
||||
icon={<QuestionCircleOutlined style={{color: 'red'}}/>}
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
onConfirm={() => {
|
||||
deleteTask(props.itemId).then((response => {
|
||||
console.log('response', response)
|
||||
if (response.status.success) {
|
||||
message.success("删除任务成功:" + response.data)
|
||||
props.refreshDate?.()
|
||||
}
|
||||
}));
|
||||
}}
|
||||
><a>删除任务</a></Popconfirm>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.COMPLETE,
|
||||
label: <Popconfirm
|
||||
title="完成任务"
|
||||
description="确认要完成任务?"
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
onConfirm={() => {
|
||||
commonUpdate({
|
||||
updateColumnList:[{
|
||||
name:'state',
|
||||
code:'state',
|
||||
value:'7'
|
||||
}],
|
||||
conditionColumnList:[{
|
||||
name:'id',
|
||||
code:'id',
|
||||
operateType:'=',
|
||||
value:props.itemId
|
||||
}]
|
||||
}).then((response => {
|
||||
console.log('response', response)
|
||||
if (response.status.success) {
|
||||
message.success("完成任务成功")
|
||||
props.refreshDate?.()
|
||||
}
|
||||
}));
|
||||
}}
|
||||
><a>完成任务</a></Popconfirm>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.SHOW_TREE,
|
||||
label: <Link href={"/task/project?pid=" + props.itemId}>树任务显示子任务</Link>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.SHOW_FOUR,
|
||||
label: <Link href={"/task/drag?pid=" + props.itemId}>四象限显示子任务</Link>,
|
||||
},
|
||||
{
|
||||
key: OPERATION_BUTTON_TYPE.SHOW_CALENDAR,
|
||||
label: <Link href={"/task/calendar?pid=" + props.itemId}>日历显示子任务</Link>,
|
||||
}
|
||||
];
|
||||
// 获取系统样式
|
||||
const {
|
||||
token: { colorBgLayout, colorTextTertiary },
|
||||
} = theme.useToken();
|
||||
|
||||
const handleCancel = () => {
|
||||
setOperateState({...operateState, openModal: false})
|
||||
if (operateState.operationId !== OPERATION_BUTTON_TYPE.DETAIL) {
|
||||
props.refreshDate?.()
|
||||
}
|
||||
}
|
||||
const onClick: MenuProps['onClick'] = ({key}) => {
|
||||
console.log(key)
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Dropdown menu={{ items }} trigger={['contextMenu']}>
|
||||
{props.children}
|
||||
</Dropdown>
|
||||
{operateState.openModal&&<DetailModelForm
|
||||
haveButton={false}
|
||||
itemId={operateState.operationId === OPERATION_BUTTON_TYPE.UPDATE||operateState.operationId === OPERATION_BUTTON_TYPE.DETAIL?props.itemId:undefined}
|
||||
pPid={props.pPid}
|
||||
pid={operateState.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ?props.itemId:undefined}
|
||||
operationId={operateState.operationId}
|
||||
description={operateState.operationId === OPERATION_BUTTON_TYPE.DETAIL ? '任务详情' :
|
||||
operateState.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ? '添加支线任务' :
|
||||
operateState.operationId === OPERATION_BUTTON_TYPE.UPDATE ? '修改任务' : '未知操作'}
|
||||
open={operateState.openModal}
|
||||
reloadData={handleCancel}/>}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default RightOption;
|
|
@ -7,9 +7,8 @@ import {DataType} from "@/lib/definitions";
|
|||
import './index.modules.css'
|
||||
import dayjs from "dayjs";
|
||||
import {getTaskState, taskPriorityList} from "@/lib/task/project/data";
|
||||
import {List as VirtualizedList, AutoSizer} from "react-virtualized";
|
||||
import 'react-virtualized/styles.css';
|
||||
import OperationButton from "@/ui/task/OperationButton";
|
||||
import RightOption from "@/ui/task/RightOption";
|
||||
|
||||
interface DroppableTableProps {
|
||||
tableCode: string,
|
||||
|
@ -35,13 +34,14 @@ export const DroppableTable = React.memo((props: DroppableTableProps) => {
|
|||
const getItemStyle = React.useCallback((isDragging: boolean, draggableStyle: any): CSSProperties => ({
|
||||
userSelect: "none",
|
||||
background: isDragging ? "lightgreen" : "white",
|
||||
position: 'relative', // 确保 zIndex 生效
|
||||
zIndex: isDragging ? 2147483647 : 'auto', // 使用 'auto' 代替 0 可能更好
|
||||
position: 'relative',
|
||||
zIndex: isDragging ? 2147483647 : 'auto',
|
||||
...draggableStyle,
|
||||
borderBottom: '1px solid #f0f0f0',
|
||||
boxSizing: 'border-box',
|
||||
// verticalAlign: 'middle',
|
||||
// textAlign: 'center',
|
||||
verticalAlign: 'middle',
|
||||
textAlign: 'center',
|
||||
borderColor:taskPriorityList.find((item) => item.code === props.tableCode)?.color
|
||||
}), []);
|
||||
|
||||
const getListStyle = React.useCallback((isDraggingOver: boolean) => ({
|
||||
|
@ -50,55 +50,10 @@ export const DroppableTable = React.memo((props: DroppableTableProps) => {
|
|||
width: '50vw'
|
||||
}), []);
|
||||
|
||||
const rowRenderer = React.useCallback(({index, key, style}: {
|
||||
index: number;
|
||||
key: string;
|
||||
style: React.CSSProperties
|
||||
}) => {
|
||||
const record = props.taskList[index];
|
||||
if (!record) return null;
|
||||
|
||||
return (
|
||||
<Draggable key={record.id} draggableId={record.id} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={getItemStyle(snapshot.isDragging, {...provided.draggableProps.style, ...style})}
|
||||
className="virtualized-row displayFlexRow"
|
||||
>
|
||||
<div style={{width: '15%'}} className='displayFlexRow'>
|
||||
<Tooltip placement="topLeft" title={record.name} className='displayFlexRow'>
|
||||
<div className='displayFlexRow'>{record.name}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{width: '40%' , boxSizing: 'border-box', minWidth: 0}} className='displayFlexRow'>
|
||||
<Tooltip placement="topLeft" title={record.description}>
|
||||
<div className='displayFlexRow'>{record.description}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{
|
||||
width: '10%',
|
||||
}} className='displayFlexRow'>{getTaskState(record.state) ? getTaskState(record.state).name : ""}</div>
|
||||
<div style={{width: '25%'}} className='displayFlexColumn'>
|
||||
<div>起: {record.expectedStartTime ? dayjs(record.expectedStartTime).format("YYYY-MM-DD HH:mm") : ""}</div>
|
||||
<div>止: {record.expectedStartTime ? dayjs(record.expectedStartTime).format("YYYY-MM-DD HH:mm") : ""}</div>
|
||||
</div>
|
||||
<div style={{width: '10%'}} className='displayFlexRow'>
|
||||
<OperationButton itemId={record.id} priority={record.priority} pid={record.pid}
|
||||
pPid={record.pPid}/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
}, [props.taskList, getItemStyle]);
|
||||
|
||||
const headerStyle = React.useMemo(() => ({
|
||||
backgroundColor: taskPriorityList.find((item) => item.code === props.tableCode)?.color,
|
||||
height: '55px',
|
||||
borderBottom: '1px solid #f0f0f0',
|
||||
// borderBottom: '1px solid #f0f0f0',
|
||||
fontWeight: 'bold'
|
||||
}), [props.tableCode]);
|
||||
|
||||
|
@ -109,32 +64,54 @@ export const DroppableTable = React.memo((props: DroppableTableProps) => {
|
|||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
style={getListStyle(snapshot.isDraggingOver)}
|
||||
className="droppable-table"
|
||||
className="droppable-table scrollHidden"
|
||||
>
|
||||
<ConfigProvider>
|
||||
{/* 表头 */}
|
||||
<div style={headerStyle} className='displayFlexRow'>
|
||||
<div style={{width: '15%'}} className='displayFlexRow'>{stateName}</div>
|
||||
<div style={{width: '40%'}} className='displayFlexRow'>任务描述</div>
|
||||
<div style={{width: '20%'}} className='displayFlexRow'>{stateName}</div>
|
||||
<div style={{width: '45%'}} className='displayFlexRow'>任务描述</div>
|
||||
<div style={{width: '15%'}} className='displayFlexRow'>任务状态</div>
|
||||
<div style={{width: '20%'}} className='displayFlexRow'>期望时间</div>
|
||||
<div style={{width: '10%'}} className='displayFlexRow'>操作</div>
|
||||
</div>
|
||||
{/* 虚拟列表主体 */}
|
||||
<div style={{height: 'calc(50vh - 76px)'}}>
|
||||
<AutoSizer>
|
||||
{({height, width}) => (
|
||||
<VirtualizedList
|
||||
className={"scrollHidden"}
|
||||
height={height}
|
||||
width={width}
|
||||
rowCount={props.taskList.length}
|
||||
rowHeight={80} // 与你的行高匹配
|
||||
rowRenderer={rowRenderer}
|
||||
overscanRowCount={8} // 预渲染的行数
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
<div style={{height: 'calc(50vh - 76px)',overflow: 'auto'}}>
|
||||
{props.taskList.map((record, index) => {
|
||||
return <Draggable key={record.id} draggableId={record.id} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<RightOption itemId={record.id} pid={record.pid} pPid={record.pPid} children={<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={getItemStyle(snapshot.isDragging, {...provided.draggableProps.style})}
|
||||
className="virtualized-row displayFlexRow"
|
||||
>
|
||||
<div style={{width: '20%'}} className='displayFlexRow'>
|
||||
<Tooltip placement="topLeft" title={record.name}
|
||||
className='displayFlexRow'>
|
||||
<div className='displayFlexRow'>{record.name}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{width: '45%', boxSizing: 'border-box', minWidth: 0}}
|
||||
className='displayFlexRow'>
|
||||
<Tooltip placement="topLeft" title={record.description}>
|
||||
<div className='displayFlexRow'>{record.description}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{
|
||||
width: '10%',
|
||||
}}
|
||||
className='displayFlexRow'>{getTaskState(record.state) ? getTaskState(record.state).name : ""}</div>
|
||||
<div style={{width: '25%'}} className='displayFlexColumn'>
|
||||
<div>起: {record.expectedStartTime ? dayjs(record.expectedStartTime).format("YYYY-MM-DD HH:mm") : ""}</div>
|
||||
<div>止: {record.expectedEndTime ? dayjs(record.expectedEndTime).format("YYYY-MM-DD HH:mm") : ""}</div>
|
||||
</div>
|
||||
</div>}>
|
||||
|
||||
</RightOption>
|
||||
)}
|
||||
</Draggable>
|
||||
})}
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
{provided.placeholder}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
box-sizing: border-box;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.virtualized-row {
|
||||
|
|
|
@ -25,7 +25,7 @@ export type DetailModelFormProps={
|
|||
// 祖宗任务id
|
||||
pPid?:string,
|
||||
// 操作id
|
||||
operationId: number,
|
||||
operationId: OPERATION_BUTTON_TYPE,
|
||||
// 标题描述
|
||||
description:string,
|
||||
// 是否打开界面,用于非按钮操作
|
||||
|
|
Loading…
Reference in New Issue