import React, { useState, useEffect, useRef, useMemo } from 'react'; import { Row, Col, Card, Button, Space, Table, Tag, Typography, Progress, Badge, Tabs, Input, Switch, Divider, Tooltip, Timeline, Select, message, DatePicker, Modal, InputNumber, TimePicker, Popconfirm, App, Radio } from 'antd'; import { PlusOutlined, ImportOutlined, NodeIndexOutlined, SaveOutlined, PlayCircleOutlined, EyeOutlined, DeleteOutlined, CloseOutlined, ReloadOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; import { Car, Navigation, Wind, Thermometer, Droplets, Satellite, LayoutGrid, Clock, CheckCircle2, AlertCircle, Zap, Waves, Calendar, User, TrendingUp, Maximize2, Play, Square, X, MapPin, RotateCw } from 'lucide-react'; import { getRobotRouteList, getSingleRouteList, saveDeviceTask, getRobotTaskList, deleteDeviceTask, getSiteDevice, excuetRoute, cancelRoute, pauseRoute, recoverRoute, getDeviceTastPool, getSiteDeviceTaskHistory, getRouteByTask, deleteRoute, getTaskPath, } from '../../api/device'; import dayjs from 'dayjs'; import DraggableBox from '../Draggable'; import utils, { getLocationQuality } from '../../lib/utils'; import CesiumMap from '../Map'; import { fix2, getVal } from '../../lib/utils'; import { useSelector } from 'react-redux'; import { RootState } from '@/src/store'; import envConfig from '../../../env'; import DeviceController from './DeviceController'; import Draggable, { DraggableData, DraggableEvent } from 'react-draggable'; import { MowerAllView } from './mowerAllView.jsx'; import { useWebRTCPlayer } from '../useWebRTCStream.js'; import { useTranslation } from 'react-i18next'; const { Text, Title } = Typography; const { RangePicker } = DatePicker; export default function RobotTaskPage() { const { t } = useTranslation(); const { userInfo } = useSelector((state: RootState) => state.user); const { stationId, currentStation } = useSelector((state: RootState) => state.station); const [excuteStatus, setExcuteStatus] = useState("stop"); //"stop" "running" "pause" // 机器人实时位置 const [robotRealtimePosition, setRobotRealtimePosition] = useState(null); const [execute, setExecute] = useState(false); // 是否执行任务 const [taskList, setTaskList] = useState([]); const [arrivedPoint, setArrivedPoint] = useState(null);//完成点 const { modal } = App.useApp(); const [polyPoints, setPolyPoints] = useState([]); // 设备数据 const [devices, setDevices] = useState([]); const [deleteLoading, setDeleteLoading] = useState(false); const [focusLocation, setFocusLocation] = useState(null); const [points, setPoints] = useState([]); const [routeMode, setRouteMode] = useState(''); const [clearNavLine, setClearNavLine] = useState(0); const [clearPlanLine, setClearPlanLine] = useState(0); const [routeSearch, setRouteSearch] = useState(''); const [routeList, setRouteList] = useState([]); // 执行任务弹窗 const [executeModalVisible, setExecuteModalVisible] = useState(false); const [selectedRouteForExecute, setSelectedRouteForExecute] = useState(null); const [selectedRobotForExecute, setSelectedRobotForExecute] = useState(null); const [runningPoolTask, setRunningPoolTask] = useState(null);//正在执行的任务 // 当前执行任务的机器人 const [executingRobot, setExecutingRobot] = useState(null); const [showVideo, setShowVideo] = useState(false); // 控制视频组件显示 // 任务池和历史任务数据 const [taskPoolList, setTaskPoolList] = useState([]); const [taskHistoryList, setTaskHistoryList] = useState([]); // 当前激活的Tab const [activeTab, setActiveTab] = useState('pool'); // 'task' for 任务列表, 'pool' for 任务池, 'history' for 历史任务 // 任务池和历史任务筛选表单 const [poolFilterForm, setPoolFilterForm] = useState({ taskStatus: null, robot: null, planId: null, }); const { video: robotVideo, error, ready } = useWebRTCPlayer( executingRobot?.serialNumber ? executingRobot?.serialNumber : '', `webrtc://1.95.137.212/live/livestream/${executingRobot?.serialNumber}`, ); const [historyFilterForm, setHistoryFilterForm] = useState({ taskStatus: null, robot: null, dateRange: null, }); // 取消任务模式 normal普通取消 force强制取消 const [cancelMode, setCancelMode] = useState<'normal' | 'force'>('normal'); const fontSmall = 'clamp(10px, 0.7vw, 11px)'; const fontNormal = 'clamp(12px, 0.75vw, 13px)'; const fontTitle = 'clamp(14px, 0.85vw, 16px)'; const fontLarge = 'clamp(18px, 1.1vw, 24px)'; const cardStyle = { borderRadius: 12, border: 'none', boxShadow: '0 2px 12px rgba(0,0,0,0.04)' }; // 任务列表 独立筛选状态 // 统一用 form 管理筛选条件 const [filterForm, setFilterForm] = useState({ dateRange: null, // 时间范围 taskStatus: null, // 任务状态 robot: null // 机器人设备 }); const [messageApi, contextHolder] = message.useMessage(); const controllerRef = useRef(null); const isFirstConnect = useRef(true); const fixStatusMap: Record = { 'no_fix': t('devices.noFix'), 'single': t('devices.single'), 'dgnss': t('devices.dgnss'), 'ins': t('devices.ins'), 'rtk_fixed': t('devices.rtkFixed'), 'rtk_float': t('devices.rtkFloat'), 'single_no_heading': t('devices.singleNoHeading'), 'dgnss_no_heading': t('devices.dgnssNoHeading'), 'rtk_fixed_no_heading': t('devices.rtkFixedNoHeading'), 'rtk_float_no_heading': t('devices.rtkFloatNoHeading'), } const handleFinish = (data) => { setExecute(false); setShowVideo(false); // 任务完成关闭视频 utils.message.success(t('devices.fixed2TaskSuspended2Fixed2CompletedFixed2')); setClearNavLine(clearNavLine + 1); setSelectedRobotForExecute(null); setSelectedRouteForExecute(null); fetchTaskList(); fetchTaskHistoryList(); fetchTaskPoolList(); } const handleFinishEdPoint = (point) => { setArrivedPoint(point); }; // 根据任务ID获取路径并显示在地图上 const loadTaskPath = async (taskId) => { //return; if (!taskId) return; try { const res = await getRouteByTask({ taskId }); if (res?.code === 200 && res?.data?.jsonData?.path) { let pathPoints = []; try { const data = res.data; pathPoints = data.jsonData.path || []; setRouteMode(data?.mode || 'coverage'); // 设置路线模式 } catch (err) { console.error('解析任务路径数据失败:', err); } setPolyPoints(pathPoints); } } catch (err) { console.error('获取任务路径失败:', err); } }; const handleDeleteRoute = async (route) => { console.log('删除航线:', route); // 防重复点击拦截 if (deleteLoading) return; modal.confirm({ title: t('systemSetting.fixed2ConfirmFixed2DeleteFixed2'), content: `确定要删除航线「${route.workName}」吗?删除后数据无法恢复!`, icon: , okText: t('devices.fixed2OkFixed2DeleteFixed2'), cancelText: t('common.cancel'), okButtonProps: { danger: true, loading: deleteLoading }, onOk: async () => { setDeleteLoading(true); try { const res = await deleteRoute(route.workName); if (res.code === 200) { message.success(t('devices.deletedSuccessfullyFixed2')); fetchRouteList(); } else { utils.message.error(res.msg || '删除失败,请稍后重试'); } } catch (err) { console.error('删除航线接口报错:', err); utils.message.error(t('devices.fixed2DeleteFailedAbnormalFixed2')); } finally { setDeleteLoading(false); } }, }); }; // 任务池项点击事件 const handlePoolTaskClick = (record) => { setRunningPoolTask(record); loadTaskPath(record.id); // 查找对应的设备 const targetDevice = devices?.find(d => d.serialNumber === record.deviceId); if (!targetDevice) { console.warn('未找到任务对应的设备'); return; } // 判断是否是新设备(避免重复连接) const isSameDevice = executingRobot?.serialNumber === targetDevice.serialNumber; if (isSameDevice) { console.log('✅ 任务使用相同设备,无需重新连接'); return; } // 切换设备连接 console.log('🔄 点击任务切换设备连接', targetDevice); setExecute(true); setExecutingRobot(targetDevice); setClearNavLine(v => v + 1); // 清空地图轨迹 if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(targetDevice); } else { controllerRef.current?.switchDevice(targetDevice); } setShowVideo(true); }; useEffect(() => { const controller = new DeviceController(userInfo, messageApi); controller.onLocationInfoUpdate = setRobotRealtimePosition; controller.onIsFinished = handleFinish; controller.handleArrivedPoint = handleFinishEdPoint; // 处理路线点回调:taskId必须和当前选中任务一致 controller.onRoutePost = (msg) => { console.log('收到 route_post 消息:', msg); const { taskId, data } = msg; // 1. 先在任务池中查找该taskId对应的任务 const matchedTask = taskPoolList.find(task => String(task.id) === String(taskId)); // 2. 两种情况处理: // a) 当前没有选中任务,且匹配到了任务池中的任务 → 自动选中该任务 // b) 当前已选中任务,且选中任务的id === 传来的taskId → 更新数据 const isSameAsSelected = runningPoolTask && String(runningPoolTask.id) === String(taskId); const shouldSelectNewTask = !runningPoolTask && matchedTask; if (isSameAsSelected || shouldSelectNewTask) { console.log('taskId匹配成功,处理路线数据', { taskId, runningPoolTaskId: runningPoolTask?.id, matchedTask }); // 如果是匹配到新任务就设置选中状态 if (shouldSelectNewTask) { setRunningPoolTask(matchedTask); } // 先直接用推送的data设置点(实时性) if (data) { setPoints(data); } } else { console.log('taskId不匹配,忽略本次推送', { receivedTaskId: taskId, currentSelectedTaskId: runningPoolTask?.id, matchedTaskFound: !!matchedTask }); } }; controller.init(); controllerRef.current = controller; return () => { if (controllerRef.current) { controllerRef.current.destroy?.(); } controllerRef.current = null; }; }, []); // 监听任务列表和设备列表变化,自动连接正在执行中的机器人(仅在没有选择任务时) useEffect(() => { if (!taskPoolList || taskPoolList.length == 0) { setClearNavLine(v => v + 1); // 清空地图轨迹 setClearPlanLine(v => v + 1); setRunningPoolTask(null); return; } if (!devices) return; // 如果已经有选择的任务(runningPoolTask),则不自动连接,让用户手动选择 if (runningPoolTask) return; // 查找正在执行中的任务 const runningTask = taskPoolList.find(item => ['EXECUTING', 'PAUSE'].includes(item.taskStaus)); if (!runningTask) return; //fetchRouteDetail(runningTask?.id); // 找到对应的设备 const targetDevice = devices.find(d => d.serialNumber === runningTask.deviceId); if (!targetDevice) return; // 判断是否是新设备(避免重复连接) const isSameDevice = executingRobot?.serialNumber === targetDevice.serialNumber; if (isSameDevice) return; // 切换设备 console.log('🔄 检测到正在执行任务的新设备,开始连接', targetDevice); setRunningPoolTask(runningTask); // 设置当前任务 setExecute(true); setExecutingRobot(targetDevice); setClearNavLine(v => v + 1); // 清空地图轨迹 if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(targetDevice); } else { controllerRef.current?.switchDevice(targetDevice); } setShowVideo(true); }, [devices, taskPoolList, executingRobot, runningPoolTask]); const realtimePosition = useMemo(() => { const data = { lon: robotRealtimePosition?.longitude || 0, lat: robotRealtimePosition?.latitude || 0, heading: robotRealtimePosition?.yaw || 0, } return data; }, [robotRealtimePosition]) //useEffect(() => { // if (robotRealtimePosition && execute) { // let warningMsg = ''; // if (robotRealtimePosition?.controlMode == "1") { // warningMsg = `请切换到远程模式 `; // } else if (robotRealtimePosition?.qual != "4") { // warningMsg = '定位未固定'; // } else if (robotRealtimePosition?.headingStatus != "1") { // warningMsg = '请先初始化航向角'; // } // if (warningMsg) { // utils.message.destroy(); // utils.message.warning(warningMsg); // } // } //}, [robotRealtimePosition, execute]) // 获取数据 const fetchAllData = async () => { if (!stationId) return; try { const deviceRes = await getSiteDevice({ siteId: stationId, pageSize: 9999, pageNum: 1, orgId: currentStation?.orgId }); const devData = deviceRes?.code === 200 ? (deviceRes.rows || []) : []; setDevices(devData); } catch (err) { console.error('数据请求失败', err); } }; useEffect(() => { fetchAllData(); }, [userInfo, stationId]); // 新建任务弹窗状态 const [isTaskModalOpen, setIsTaskModalOpen] = useState(false); const [selectedPathWork, setSelectedPathWork] = useState(null); // 获取任务列表数据 const fetchTaskList = async () => { try { const params = { siteId: String(stationId), pageNum: 1, pageSize: 99999999 }; // 时间范围 if (filterForm.dateRange && filterForm.dateRange.length === 2) { params.startTime = filterForm.dateRange[0].format('YYYY-MM-DD'); params.endTime = filterForm.dateRange[1].format('YYYY-MM-DD'); } // 设备 if (filterForm.robot?.serialNumber) { params.deviceId = filterForm.robot.serialNumber; } // 状态 if (filterForm.taskStatus !== null && filterForm.taskStatus !== undefined) { params.taskStaus = filterForm.taskStatus; } const res = await getRobotTaskList(params); if (res.code == 200) { setTaskList(res.data); } } catch (err) { message.error(t('devices.taskListRefreshFail')); } }; useEffect(() => { //console.log('获取任务列表', selectedPathWork?.id); if (!selectedRobotForExecute?.id || !execute) { setPoints([]); return; } }, [selectedRobotForExecute?.id]); // 监听任务池列表变化,自动加载第一个任务的路径 useEffect(() => { if (taskPoolList && taskPoolList.length > 0) { setRunningPoolTask(taskPoolList[0]); } }, [taskPoolList]); const getRouteOut = async () => { try { const res = await getSingleRouteList({ id: selectedPathWork?.id }); // coverage(区域覆盖)绘制为多边形,其余为折线/单点 const mode = res?.data?.mode; setRouteMode(mode); if (res?.code === 200 && res?.data?.jsonData) { let pathPoints = []; try { // 尝试解析 JSON pathPoints = res?.data?.jsonData?.path || []; } catch (err) { console.error('解析路线数据失败:', err); } if (pathPoints.length === 0) { utils.message.warning(t('devices.route2')); } setPolyPoints(pathPoints); } else { setPolyPoints([]); setRouteMode(''); } } catch (err) { console.error('获取路线详情失败:', err); setPolyPoints([]); setRouteMode(''); } } useEffect(() => { getRouteOut(); }, [selectedPathWork?.id]) // 调用接口获取路线详情 const fetchRouteDetail = async (id) => { console.log(id, "获取正在执行任务的id") try { const params = { taskId: id, } const res = await getTaskPath(params) //const res = await getSingleRouteList({ id: selectedPathWork.id }); if (res?.code === 200 && res?.data) { let pathPoints = []; try { // 尝试解析 JSON pathPoints = res.data || []; } catch (err) { console.error('解析路线数据失败:', err); } if (pathPoints.length === 0) { utils.message.warning(t('devices.route2')); } setPoints(pathPoints); } else { setPoints([]); } } catch (err) { console.error('获取路线详情失败:', err); setPoints([]); } }; useEffect(() => { if (!runningPoolTask?.id) return; loadTaskPath(runningPoolTask.id); fetchRouteDetail(runningPoolTask?.id); // 只有当任务是执行状态,并且没有连接的设备时,才自动连接 if ((runningPoolTask.taskStaus == 'EXECUTING' || runningPoolTask.taskStaus == 'PAUSE') && !executingRobot) { const targetDevice = devices?.find(d => d.serialNumber === runningPoolTask.deviceId); if (targetDevice) { setExecute(true); setExecutingRobot(targetDevice); setClearNavLine(v => v + 1); // 清空地图轨迹 if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(targetDevice); } else { controllerRef.current?.switchDevice(targetDevice); } setShowVideo(true); } } }, [runningPoolTask, devices, executingRobot]) // 获取路线列表 const fetchRouteList = async () => { if (!stationId) return; getRobotRouteList({ siteId: stationId }).then(res => { if (res.code == 200 && res.data) { setRouteList(res.data); } else { setRouteList([]); } }).catch(() => setRouteList([])); }; const handleDelete = (id) => { deleteDeviceTask({ id }).then(res => { if (res.code == 200) { utils.message.success(t('common.deleteSuccess')); } else { utils.message.error(res.msg); } }).catch(() => message.error(t('common.deleteFail'))).finally(() => { fetchTaskList(); fetchTaskPoolList(); fetchTaskHistoryList(); }); } // 清空所有筛选项并刷新任务列表 // 清空所有筛选项并刷新 —— 现在超级简单! const resetFiltersAndRefresh = () => { setFilterForm({ dateRange: null, taskStatus: null, robot: null }); }; const handleTaskexcuted = async (route) => { try { console.log(route); setSelectedRouteForExecute(route); setExecuteModalVisible(true); setSelectedRobotForExecute(null); } catch (err) { console.error('检查路线失败:', err); utils.message.error(t('devices.checkRouteFail')); } } const handleExecuteConfirm = async () => { if (!selectedRobotForExecute) { message.error(t('devices.selectExecuteDevice')); return; } try { const params = { deviceId: selectedRobotForExecute.serialNumber, routeId: selectedRouteForExecute.id, siteId: stationId, ...(currentStation?.orgId ? { orgId: currentStation.orgId } : {}), }; const res = await excuetRoute(params); if (res.code === 200) { setExecute(true); utils.message.success(t('devices.taskExecSuccess')); setExecuteModalVisible(false); setExecutingRobot(selectedRobotForExecute); fetchTaskList(); fetchTaskPoolList(); fetchTaskHistoryList(); if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(selectedRobotForExecute); } else { controllerRef.current?.switchDevice(selectedRobotForExecute); } setShowVideo(true); // 连接成功后自动打开视频 } else { setExecute(false); utils.message.error(res.msg || '任务执行失败'); } } catch (err) { setExecute(false); console.error('执行任务失败:', err); utils.message.error(t('devices.taskExecuteException')); } } const handleTaskCreated = () => { resetFiltersAndRefresh(); }; // 获取任务池数据 const fetchTaskPoolList = async () => { if (!stationId) return; try { const params = { userId: userInfo?.userId, siteId: stationId, orgId: currentStation?.orgId, pageNum: 1, pageSize: 99999999 }; if (poolFilterForm.taskStatus) { params.taskStaus = poolFilterForm.taskStatus; } if (poolFilterForm.robot?.serialNumber) { params.deviceId = poolFilterForm.robot.serialNumber; } if (poolFilterForm.planId) { params.planId = poolFilterForm.planId; } const res = await getDeviceTastPool(params); if (res.code === 200) { const list = res.rows || []; list.sort((a, b) => { const aEmpty = !a.planId; const bEmpty = !b.planId; return aEmpty ? -1 : bEmpty ? 1 : 0; }); setTaskPoolList(list); } } catch (err) { console.error('获取任务池失败:', err); } }; // 获取历史任务数据 const fetchTaskHistoryList = async () => { if (!stationId) return; try { const params = { siteId: stationId, orgId: currentStation?.orgId, pageNum: 1, pageSize: 99999999 }; if (historyFilterForm.taskStatus) { params.taskStaus = historyFilterForm.taskStatus; } if (historyFilterForm.robot?.serialNumber) { params.deviceId = historyFilterForm.robot.serialNumber; } if (historyFilterForm.dateRange && historyFilterForm.dateRange.length === 2) { params.startTime = historyFilterForm.dateRange[0].format('YYYY-MM-DD HH:mm:ss'); params.endTime = historyFilterForm.dateRange[1].format('YYYY-MM-DD HH:mm:ss'); } const res = await getSiteDeviceTaskHistory(params); if (res.code === 200) { setTaskHistoryList(res.rows || []); } } catch (err) { console.error('获取历史任务失败:', err); } }; // 取消任务 const handleCancelTask = async (task, mode) => { try { const params = { taskId: task.id, //deviceId: task.deviceId, siteId: stationId, orgId: task.orgId, forceCancel: mode === 'force' ? true : false, }; const res = await cancelRoute(params); if (res.code === 200 && res.data == true) { utils.message.success(t('devices.fixed2TaskSuspended2Fixed2CancelledFixed2')); fetchTaskPoolList(); fetchTaskHistoryList(); setExecute(false); //if (controllerRef.current) { // controllerRef.current.destroy?.(); //} setSelectedRobotForExecute(null); setSelectedRouteForExecute(null); } else { utils.message.error(t('devices.taskCancelFail')); } } catch (err) { console.error('取消任务失败:', err); utils.message.error(t('devices.fixed2CancelFixed2TaskSuspended2Fixed2AbnormalFixed2')); } }; // 暂停任务 const handlePauseTask = async (task) => { try { const params = { taskId: task.id, orgId: task.orgId, //deviceId: task.deviceId, siteId: stationId, }; const res = await pauseRoute(params); if (res.code === 200) { utils.message.success(t('devices.taskPaused')); fetchTaskPoolList(); fetchTaskHistoryList(); } else { utils.message.error(res.msg || '暂停失败'); } } catch (err) { console.error('暂停任务失败:', err); utils.message.error(t('devices.fixed2SuspendTaskFixed2AbnormalFixed2')); } }; // 恢复任务 const handleRecoverTask = async (task) => { try { const params = { taskId: task.id, orgId: task.orgId, siteId: stationId, }; const res = await recoverRoute(params); if (res.code === 200) { utils.message.success(t('devices.taskResumed')); fetchTaskPoolList(); fetchTaskHistoryList(); } else { utils.message.error(res.msg || '恢复失败'); } } catch (err) { console.error('恢复任务失败:', err); utils.message.error(t('devices.fixed2ResumeTaskFixed2AbnormalFixed2')); } }; useEffect(() => { if (stationId) { fetchRouteList(); } }, [stationId]); // 监听筛选条件变化 useEffect(() => { if (stationId) { fetchTaskList(); } }, [filterForm, stationId]); // 监听任务池筛选条件变化 useEffect(() => { if (stationId) { fetchTaskPoolList(); } }, [poolFilterForm, stationId]); // 监听历史任务筛选条件变化 useEffect(() => { if (stationId) { fetchTaskHistoryList(); } }, [historyFilterForm, stationId]); const taskColumns = [ { title: t('devices.taskName'), dataIndex: 'planName', width: 160, ellipsis: true, fixed: "left" }, { title: t('devices.device'), dataIndex: 'deviceAlias', width: 320, render: (_, record) => { return record.deviceAlias ? record.deviceAlias : record.deviceId; } }, { title: t('devices.creator'), dataIndex: 'createBy', width: 100, render: (text) => text }, { title: t('common.createTime'), dataIndex: 'createTime', width: 170, render: t => t ? dayjs(t).format('YYYY-MM-DD HH:mm:ss') : '-' }, // 执行计划 + 周期时间 { title: t('devices.executionPlan'), width: 280, render: (_, record) => { const rule = record.deviceTaskPlanRules || {}; const { type, startDate, dayTime, days, dayOfMonth } = rule; // 周映射 const weekMap = { SUNDAY: t('devices.sun'), MONDAY: t('devices.mon'), TUESDAY: t('devices.tue'), WEDNESDAY: t('devices.wed'), THURSDAY: t('devices.thu'), FRIDAY: t('devices.fri'), SATURDAY: t('devices.sat') }; switch (type) { case 'DAY': return t('devices.planByDay', { startDate: startDate || '', dayTime: dayTime || '' }); case 'WEEK': const weekText = (days || []).map(d => weekMap[d]).join('、'); return t('devices.planByWeek', { weekText, dayTime: dayTime || '' }); case 'MONTH': const monthText = (dayOfMonth || []).map(d => t('devices.daySuffix', { day: d })).join('、'); return t('devices.planByMonth', { monthText, dayTime: dayTime || '' }); default: return t('devices.unknownPlan'); } } }, { title: t('devices.executionCycle'), width: 320, render: (_, record) => { return `${record.startTime || ''} - ${record.finishTime || ''}`; } }, { title: t('roles.status'), dataIndex: 'taskStaus', width: 100, render: (status, record) => { console.log('status', status, record); const statusMap = { 'NEW': { color: 'default', text: t('devices.statusNew') }, 'EXECUTING': { color: 'processing', text: t('workOrder.inProgress') }, 'PAUSE': { color: 'warning', text: t('devices.statusPaused') }, 'FINISH': { color: 'success', text: t('devices.statusFinished') }, 'FAILED': { color: 'error', text: t('devices.statusFailed') }, "CANCELING": { color: 'warning', text: t('devices.statusCanceling') }, }; const item = statusMap[status] || { color: 'default', text: record.taskStausTranslate || t('constants.locationUnknown') }; return {record.taskStausTranslate}; } }, { title: t('roles.actions'), width: 300, fixed: 'right', render: (_, record) => ( handleDelete(record.id)} okText={t('common.ok')} cancelText={t('common.cancel')} > ) } ]; // 任务池表格列 const taskPoolColumns = [ { title: t('devices.device'), dataIndex: 'deviceId', width: 320, }, { title: t('roles.status'), dataIndex: 'taskStaus', width: 100, render: (status, record) => { const statusMap = { 'NEW': { color: 'default', text: t('devices.statusNew') }, 'EXECUTING': { color: 'processing', text: t('workOrder.inProgress') }, 'PAUSE': { color: 'warning', text: t('devices.statusPaused') }, 'FINISH': { color: 'success', text: t('devices.statusFinished') }, 'FAILED': { color: 'error', text: t('devices.statusFailed') }, "CANCELING": { color: 'warning', text: t('devices.statusCanceling') }, }; const item = statusMap[status] || { color: 'default', text: t('constants.locationUnknown') }; return {item.text}; } }, { title: t('common.createTime'), dataIndex: 'createTime', width: 170, }, { title: t('roles.actions'), width: 100, // 加宽一点 fixed: 'right', render: (_, record) => ( <> {/*{record.taskStaus === 'NEW' && (*/} setCancelMode(e.target.value)} style={{ marginTop: 8 }} > {t('devices.normalCancel')} {t('devices.forceCancel')} } onConfirm={() => handleCancelTask(record, cancelMode)} okText={t('common.ok')} cancelText={t('common.cancel')} > {/*//)}*/} {/*{record.taskStaus === 'EXECUTING' && (*/} handlePauseTask(record)} okText={t('common.ok')} cancelText={t('common.cancel')} > {/*)}/*/} {record.taskStaus === 'PAUSE' && ( handleRecoverTask(record)} okText={t('common.ok')} cancelText={t('common.cancel')} > )} ) }, ]; // 历史任务表格列 const taskHistoryColumns = [ { title: t('devices.device'), dataIndex: 'deviceId', width: 320, }, { title: t('roles.status'), dataIndex: 'taskStausTranslate', width: 100, render: (text, record) => { const status = record.taskStaus; // 原始状态值 let color = 'default'; if (status === 'NEW') color = 'default'; else if (status === 'EXECUTING') color = 'processing'; else if (status === 'PAUSE') color = 'warning'; else if (status === 'FINISH') color = 'success'; else if (status === 'FAILED') color = 'error'; return {text}; }, }, { title: t('devices.startTime'), dataIndex: 'startTime', width: 170, }, { title: t('devices.endTime'), dataIndex: 'finishTime', width: 170, }, { title: t('common.createTime'), dataIndex: 'createTime', width: 170, } ]; const handleRowClick = (record) => { if (record.longitude && record.latitude) { setFocusLocation({ lon: record.longitude, lat: record.latitude, type: 'robot', name: record.deviceName }); } }; const groupBoardData = useMemo(() => { const waitList = taskList.filter(item => item.taskStaus === 'NEW'); const runList = taskList.filter(item => ['EXECUTING', 'PAUSE'].includes(item.taskStaus)); const finishList = taskList.filter(item => ['FINISH', 'FAILED'].includes(item.taskStaus)); return { waitList, runList, finishList }; }, [taskList]); const historyData = useMemo(() => { const successfulTaskList = taskHistoryList.filter(item => ['FINISH', 'FAILED'].includes(item.taskStaus)); return { successfulTaskList }; }, [taskHistoryList]); const { waitList, runList, finishList } = groupBoardData; const { successfulTaskList } = historyData; const topCards = [ { title: t('devices.statTodayPlan'), value: taskList.length, unit: t('devices.unitPlan'), desc: t('devices.statInProgress'), icon: , color: '#165DFF' }, { title: t('devices.statWorkingRobots'), value: taskPoolList?.filter(item => ['EXECUTING', 'PAUSE'].includes(item.taskStaus))?.length, unit: t('devices.unitRobot'), desc: t('devices.statWorking'), icon: , color: '#165DFF' }, { title: t('devices.statTotalDistance'), value: '12.5', unit: 'km', trend: t('devices.vsYesterday', { value: '+1.2' }), trendColor: '#00B42A', icon: , color: '#722ED1' }, { title: t('devices.statCompletionRate'), value: successfulTaskList.length ? Math.round((successfulTaskList.length / taskHistoryList.length) * 100) : 0, unit: '%', trend: t('devices.vsYesterday', { value: '+5%' }), trendColor: '#00B42A', icon: , color: '#00B42A' }, { title: t('devices.statAlerts'), value: '0', unit: t('devices.unitTimes'), trend: t('devices.vsYesterday', { value: '-1' }), trendColor: '#00B42A', icon: , color: '#F53F3F' }, { title: t('devices.statOnlineRate'), value: `${devices?.length ? Math.round((devices.filter(d => d.onlineStatus == 1).length / devices.length) * 100) : 0}`, unit: '%', desc: t('devices.good'), icon: , color: '#165DFF' }]; return (
{t('devices.robotTask')}
{topCards.map((item, index) => (
{React.cloneElement(item.icon as React.ReactElement, { size: 18 })}
{item.title}
{item.value} {item.unit}
{item.trend || item.desc}
))}
{t('devices.robotRouteMonitor')}
} size="small" extra={ {/* Space开启自动换行,小屏按钮自动折行 */} {/* */} {/*