210 lines
9.1 KiB
TypeScript
210 lines
9.1 KiB
TypeScript
import { useTheme } from '@/contexts/ThemeContext';
|
|
import { actionColumnItem, ActionListItem, TaskListItem, taskStatusList } from '@/pages/TaskManagement';
|
|
import { Bot, ChevronRight, Download, Edit, Eye, FileText, Layers, MoreHorizontal, Play, Search, User, Users, Workflow } from 'lucide-react';
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
import { PageType } from '../Sidebar';
|
|
import { format } from 'date-fns';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Col, Empty, Loading, Pagination, Progress, Row } from 'tdesign-react';
|
|
import { requestCrudService } from '@/service/request-axios-service';
|
|
import { ActionColumn } from './ActionColumn';
|
|
|
|
interface ActionAnalyzesProps {
|
|
TaskType?: string;
|
|
title: string;
|
|
category: string;
|
|
subCategory: string;
|
|
onNavigate: (page: PageType) => void;
|
|
}
|
|
|
|
const service = requestCrudService;
|
|
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
|
|
export const ActionAnalyzes: React.FC<ActionAnalyzesProps> = ({ title, category, subCategory, onNavigate }) => {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === 'dark';
|
|
// 主题颜色变量
|
|
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
|
|
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-100';
|
|
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
|
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
|
|
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-white';
|
|
const navigate = useNavigate();
|
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
|
const [taskList, setTaskList] = useState<TaskListItem[]>([]); // 任务列表
|
|
const [taskId, setTaskId] = useState<number>(0); // 任务
|
|
const [actionColumnList, setActionColumnList] = useState<actionColumnItem[]>([]);
|
|
const [taskLoading, setTaskLoading] = useState<boolean>(false);
|
|
const [actionLoading, setActionLoading] = useState<boolean>(false);
|
|
// 当前页码,默认为第 1 页
|
|
const [pageNum, setPageNum] = useState<number>(1);
|
|
// 每页显示数量,默认为 10 条
|
|
const [pageSize, setPageSize] = useState<number>(10); // 每页显示 10 条
|
|
const [total, setTotal] = useState<number>(0); // 查询任务总数
|
|
const state = useRef({
|
|
pageIndex: 1,
|
|
pageSize: 10
|
|
})
|
|
// 回车处理
|
|
const handleKeyDown = async (e) => {
|
|
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
|
|
e.preventDefault();
|
|
} else if (e.keyCode === 13) {
|
|
e.preventDefault();
|
|
await taskListSearch()
|
|
} else if (e.key === "/") {
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 页码变化时的回调函数
|
|
* 当用户切换页码时触发,更新页码并重新获取数据
|
|
* @param value - 新的页码值
|
|
*/
|
|
const pageIndexChange = async (value: number) => {
|
|
// 更新 ref 中存储的页码值
|
|
// 更新页码状态,触发组件重新渲染
|
|
setPageNum(value);
|
|
state.current.pageIndex = value;
|
|
await taskListSearch();
|
|
}
|
|
|
|
// 任务查询
|
|
const taskListSearch = async () => {
|
|
const param = {
|
|
keyword: '',
|
|
pageNum: state.current.pageIndex,
|
|
pageSize: state.current.pageSize
|
|
}
|
|
setTaskLoading(true);
|
|
const res = await service.post(`${zncmUrl}/action/cop-action-tasks`, param);
|
|
setTaskLoading(false);
|
|
console.log('任务搜索', res);
|
|
const result = res.data;
|
|
result.forEach((item: TaskListItem) => {
|
|
item.progress = item.endCount / item.sumAll || 0; // 获取已完成和未完成的占比
|
|
item.taskStatusData = taskStatusList[item.taskStatus] // 根据任务状态码获取相关信息
|
|
})
|
|
setTaskList(result || []); // 设置任务列表页
|
|
const totalRes = await service.post(`${zncmUrl}/action/cop-action-tasks-count`, param); // 查询任务总数
|
|
console.log('totalRes', totalRes)
|
|
setTotal(totalRes.data || 0); // 设置总数
|
|
if (result.length > 0) {
|
|
setTaskId(result[0].id);
|
|
await actionListSearch(result[0].id);
|
|
}
|
|
}
|
|
|
|
// 行动查询
|
|
const actionListSearch = async (taskId: number) => {
|
|
const param = {
|
|
taskId: taskId,
|
|
}
|
|
setActionLoading(true);
|
|
const res = await service.get(`${zncmUrl}/action/cop-task-actions`, param);
|
|
console.log('行动查询', res);
|
|
setActionLoading(false);
|
|
const result = res.data;
|
|
setActionColumnList([
|
|
{ columnName: '待分析', icon: '待CEH.svg', actionList: result.collect || [] },
|
|
{ columnName: 'CEH中', icon: '进行中.svg', actionList: result.collect1 || [] },
|
|
{ columnName: 'CEH完成', icon: '完成.svg', actionList: result.collect2 || [] },
|
|
{ columnName: '已终止', icon: '已终止.svg', actionList: result.endDate || [] },
|
|
]);
|
|
}
|
|
|
|
const taskDataChange = async (taskId: number) => {
|
|
setTaskId(taskId);
|
|
await actionListSearch(taskId);
|
|
}
|
|
|
|
useEffect(() => {
|
|
taskListSearch();
|
|
}, [])
|
|
return (
|
|
<div className="h-full flex flex-col">
|
|
<Row className="h-full flex flex-col">
|
|
<Col span={2} className="h-full">
|
|
<div className={`h-[calc(100vh-100px)] p-[10px] ${isDark ? 'bg-[rgba(3,38,80,0.6)]' : 'bg-white'} shadow-[2px_0px_6px_rgba(0,0,0,0.12)]`} >
|
|
{/* <div className="flex justify-between">
|
|
<span>筛选</span>
|
|
<span className="flex items-center">
|
|
<span>下发时间排序</span>
|
|
</span>
|
|
</div> */}
|
|
<div className="relative flex-1 max-w-md">
|
|
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${textMuted}`} />
|
|
<input
|
|
type="text"
|
|
placeholder="搜索任务名称"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
onKeyDown={(e) => handleKeyDown(e)}
|
|
className={`w-full pl-10 pr-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-red-500/20 transition-colors ${inputBg} ${cardBorder} ${textPrimary} placeholder:${textMuted}`}
|
|
/>
|
|
</div>
|
|
{/* 任务列表 */}
|
|
<Loading loading={taskLoading}>
|
|
<div className="h-[calc(100vh-200px)] overflow-y-auto pr-[10px]">
|
|
{
|
|
taskList.map((item: TaskListItem, index: number) => (
|
|
<div key={index} className={`${isDark ? 'bg-[#0062d91a] border-[#55a6fe1a] shadow-[0_3px_4px_#8b8b8b0f] dark-theme' : 'bg-white border-[#f5f5f5] shadow-[2px_0px_6px_rgba(0,0,0,0.08)] task-item-active'} p-[10px] mt-[10px] mb-[10px] rounded-[4px] border ${taskId === item.id && 'task-active'} cursor-pointer`} onClick={() => taskDataChange(item.id)}>
|
|
<div className="text-[var(--text-color)] text-[17px] mb-[5px] font-[500]">{item.taskName}</div>
|
|
<div className="text-[#7f7f7f] mb-[5px]">{format(new Date(item.taskStartDate), 'yyyy-MM-dd HH:mm:ss')}</div>
|
|
<div className="flex items-center mb-[8px]">
|
|
<img src={`/icon/${item?.taskStatusData?.icon}`} alt="" />
|
|
<span className="ml-[5px] leading-[16px]">{item?.taskStatusData?.label}</span>
|
|
</div>
|
|
<div className="pb-[10px] pt-[10px]">
|
|
<div className="border-b border-[#e8e8e8]"></div>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<div className="h-[24px] item-active pl-[10px] pr-[10px] task-tab-color leading-[24px] rounded-[50px] bg-[var(--bg-color)]">
|
|
完成{item.endCount}/{item.sumAll}
|
|
</div>
|
|
<div className="pl-[20px] flex-1">
|
|
<Progress percentage={item.progress} label={false} color={item?.taskStatusData?.color} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))
|
|
}
|
|
</div>
|
|
</Loading>
|
|
{/* 分页 */}
|
|
<div>
|
|
<Pagination
|
|
total={total}
|
|
size="small"
|
|
theme="simple"
|
|
current={pageNum}
|
|
pageSize={pageSize}
|
|
showPageSize={false}
|
|
onCurrentChange={(page) => pageIndexChange(Number(page))}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
|
|
<Col span={10} className="h-full">
|
|
<div className="h-full flex flex-col p-[0_10px]">
|
|
<Loading loading={actionLoading}>
|
|
<div className="flex grid grid-cols-4 gap-[10px] pl-[10px]">
|
|
{
|
|
actionColumnList.map((item: actionColumnItem, index: number) => (
|
|
<ActionColumn key={index} icon={item.icon} columnIndex={index} columnName={item.columnName} actionCount={item.actionList.length} actionList={item.actionList} title={title} category={category} subCategory={subCategory} />
|
|
))
|
|
}
|
|
{
|
|
actionColumnList.length === 0 && <Empty/>
|
|
}
|
|
</div>
|
|
</Loading>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
);
|
|
}
|