@@ -1,141 +1,120 @@
import React , { useState , useEffect } from 'react' ;
import {
Row , Col , Card , Button , Space , Table , Tag , Typography , Progress , Badge ,
Tabs , Input , Switch , Divider , Tooltip , Timeline
Tabs , Input , Switch , Divider , Tooltip , Timeline , Select , message
} from 'antd' ;
import {
PlusOutlined , ImportOutlined , NodeIndexOutlined , SafetyCertificateOutlined ,
SaveOutlined , FullscreenOutlined , EnvironmentOutlined , RobotOutlined ,
PlusOutlined , ImportOutlined , NodeIndexOutlined ,
SaveOutlined , FullscreenOutlined , EnvironmentOutlined ,
BranchesOutlined , CloudOutlined , ThunderboltOutlined , CheckCircleOutlined ,
InfoCircleOutlined , ClockCircleOutlined , RiseOutlined , FallOutlined ,
SettingOutlined , WarningOutlined , ArrowRightOutlined , CloudSyncOutlined ,
InfoCircleOutlined , ClockCircleOutlined ,
SettingOutlined , WarningOutlined , ArrowRightOutlined ,
SearchOutlined , MoreOutlined , AimOutlined
} from '@ant-design/icons' ;
import {
Plane ,
Navigation ,
Wind ,
Thermometer ,
Droplets ,
Satellite ,
LayoutGrid ,
Clock ,
CheckCircle2 ,
AlertCircle ,
Zap ,
Waves ,
Calendar ,
User ,
TrendingUp ,
GripVertical ,
Maximize2 ,
Target
Plane , Navigation , Wind , Thermometer , Droplets , Satellite ,
LayoutGrid , Clock , CheckCircle2 , AlertCircle , Zap , Waves ,
Calendar , User , TrendingUp , Maximize2
} from 'lucide-react' ;
import { getsinglePlan } from '@/src/api/api ' ;
import { getsinglePlan } from '@/api/device ' ;
import dayjs from 'dayjs' ;
const { Text , Title } = Typography ;
export default function WayLinePage() {
// 响应式字体大小
export default function WayLinePage( { planedevices } ) {
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)' } ;
const [ selectedDrone , setSelectedDrone ] = useState ( null ) ;
const [ taskList , setTaskList ] = useState ( [ ] ) ; // 真实任务列表
// 顶部统计卡片数据
// 顶部统计
const topCards = [
{ title : '今日航线计划' , value : '18' , unit : '条' , trend : '较昨日 +2' , trendColor : '#00B42A' , icon : < LayoutGrid size = { 22 } / > , color : '#165DFF' } ,
{ title : '执行中任务' , value : '6' , unit : '个' , desc : '进行中' , icon : < Plane size = { 22 } / > , color : '#165DFF' } ,
{ title : '待审批任务' , value : '4' , unit : '个' , trend : '较昨日 -1' , trendColor : '#F53F3F' , icon : < Calendar size = { 22 } / > , color : '#FF7D00' } ,
{ title : '任务完成率' , value : '92' , unit : '%' , trend : '较昨日 +6%' , trendColor : '#00B42A' , icon : < CheckCircle2 size = { 22 } / > , color : '#00B42A' } ,
{ title : 'AI 优化收益' , value : '+7.8' , unit : '%' , trend : '较昨日 +1.2%' , trendColor : '#00B42A' , icon : < TrendingUp size = { 22 } / > , color : '#722ED1' } ,
{ title : '覆盖装备' , value : '4 类 / 9 台' , unit : '' , desc : '全部在线' , icon : < Satellite size = { 22 } / > , color : '#165DFF' } ,
] ;
// 获取真实任务
const getTaskList = async ( drone ) = > {
if ( ! drone || ! drone . device_sn ) return ;
try {
const res = await getsinglePlan ( drone ) ;
if ( res ? . code === 0 ) {
setTaskList ( res . data ? . list || [ ] ) ;
}
} catch ( err ) {
message . error ( '获取任务失败' ) ;
}
} ;
// 默认选中第一个无人机
useEffect ( ( ) = > {
if ( planedevices ? . length ) {
setSelectedDrone ( planedevices [ 0 ] ) ;
}
} , [ planedevices ] ) ;
// 切换无人机时刷新任务
useEffect ( ( ) = > {
getTaskList ( selectedDrone ) ;
} , [ selectedDrone ] ) ;
// 任务状态颜色
const getStatusTag = ( status ) = > {
const map = {
success : { color : 'green' , text : '成功' } ,
running : { color : 'blue' , text : '执行中' } ,
failed : { color : 'red' , text : '失败' } ,
} ;
const item = map [ status ] || { color : 'default' , text : status } ;
return < Tag color = { item . color } > { item . text } < / Tag > ;
} ;
// 任务表格列
const taskColumns = [
{ title : '任务名称' , dataIndex : 'name' , width : 260 , ellipsis : true , fixed : "left" } ,
{ title : '任务UUID' , dataIndex : 'uuid' , width : 260 , ellipsis : true } ,
{ title : '设备SN' , dataIndex : 'sn' , width : 140 } ,
{ title : '状态' , dataIndex : 'status' , width : 80 , render : getStatusTag } ,
{
title : '今日航线计划 ' ,
value : '18 ',
unit : '条' ,
t rend: '较昨日 +2' ,
trendColor : '#00B42A' ,
icon : < LayoutGrid size = { 22 } / > ,
color : '#165DFF' ,
bg : 'linear-gradient(135deg, #E8F3FF 0%, #BAE0FF 100%)'
title : '开始时间 ' ,
dataIndex : 'begin_at ',
width : 170 ,
render : t = > t ? dayjs ( t ) . format ( 'YYYY-MM-DD HH:mm:ss' ) : '-'
} ,
{
title : '执行中任务 ' ,
value : '6 ',
unit : '个' ,
desc : '进行中' ,
icon : < Plane size = { 22 } / > ,
color : '#165DFF' ,
bg : 'linear-gradient(135deg, #E8F3FF 0%, #BAE0FF 100%)'
} ,
{
title : '待审批任务' ,
value : '4' ,
unit : '个' ,
trend : '较昨日 -1' ,
trendColor : '#F53F3F' ,
icon : < Calendar size = { 22 } / > ,
color : '#FF7D00' ,
bg : 'linear-gradient(135deg, #FFF7E6 0%, #FFE4BA 100%)'
} ,
{
title : '任务完成率' ,
value : '92' ,
unit : '%' ,
trend : '较昨日 +6%' ,
trendColor : '#00B42A' ,
icon : < CheckCircle2 size = { 22 } / > ,
color : '#00B42A' ,
bg : 'linear-gradient(135deg, #E8FFEA 0%, #AFF0B5 100%)'
} ,
{
title : 'AI 优化收益' ,
value : '+7.8' ,
unit : '%' ,
trend : '较昨日 +1.2%' ,
trendColor : '#00B42A' ,
icon : < TrendingUp size = { 22 } / > ,
color : '#722ED1' ,
bg : 'linear-gradient(135deg, #F9F0FF 0%, #EFDBFF 100%)'
} ,
{
title : '覆盖装备' ,
value : '4 类 / 9 台' ,
unit : '' ,
desc : '全部在线' ,
icon : < Satellite size = { 22 } / > ,
color : '#165DFF' ,
bg : 'linear-gradient(135deg, #E8F3FF 0%, #BAE0FF 100%)'
title : '结束时间 ' ,
dataIndex : 'completed_at ',
width : 170 ,
render : t = > t ? dayjs ( t ) . format ( 'YYYY-MM-DD HH:mm:ss' ) : '-'
} ,
] ;
//useEffect(()=>{
// getsinglePlan().then(res=>{
// })
//})
return (
< div className = "normal-spacing" style = { { background : '#f5f7fa' , minHeight : '100vh' , padding : '16px' } } >
< Row gutter = { [ 16 , 16 ] } style = { { marginBottom : 16 } } >
{ topCards . map ( ( item , index ) = > (
< Col xs = { 12 } sm = { 8 } md = { 4 } key = { index } >
< Card bordered = { false } bodyStyle = { { padding : '12px 14px' } } style = { cardStyle } >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } >
< div style = { {
width : 36 , height : 36 ,
background : item.bg ,
borderRadius : 10 ,
display : 'flex' , justifyContent : 'center' , alignItems : 'center' ,
color : item.color
} } >
{ React . cloneElement ( item . icon as React . ReactElement , { size : 18 } ) }
< div style = { { width : 36 , height : 36 , background : item.bg , borderRadius : 10 , display : 'flex' , justifyContent : 'center' , alignItems : 'center' , color : item.color } } >
{ React . cloneElement ( item . icon , { size : 18 } ) }
< / div >
< div style = { { flex : 1 } } >
< div style = { { fontSize : 10 , color : '#86909C' , marginBottom : 0 } } > { item . title } < / div >
< div style = { { fontSize : 10 , color : '#86909C' } } > { item . title } < / div >
< div style = { { display : 'flex' , alignItems : 'baseline' , gap : 4 } } >
< span style = { { fontSize : 'clamp(16px, 1vw, 20px)' , fontWeight : 700 , color : '#1D2129' } } > { item . value } < / span >
< span style = { { fontSize : 9 , color : '#4E5969' } } > { item . unit } < / span >
< / div >
< div style = { { fontSize : 9 , color : item.trendColor || '#86909C' , fontWeight : 500 , lineHeight : 1 } } >
< div style = { { fontSize : 9 , color : item.trendColor || '#86909C' , fontWeight : 500 } } >
{ item . trend || item . desc }
< / div >
< / div >
@@ -145,143 +124,92 @@ export default function WayLinePage() {
) ) }
< / Row >
{ /* 中间主要内容区 */ }
< Row gutter = { 10 } style = { { marginBottom : 10 } } >
{ /* 左侧:航线编辑与路径规划 */ }
< Col xs = { 24 } xl = { 14 } style = { { display : "flex" , flexDirection : "column" } } >
{ /* 地图卡片 */ }
< Card
title = { < span style = { { fontSize : fontNormal , fontWeight : 700 , color : '#1D2129' } } > 航 线 编 辑 与 路 径 规 划 < / span > }
title = { < span style = { { fontSize : fontNormal , fontWeight : 700 } } > 航 线 编 辑 与 路 径 规 划 < / span > }
size = "small"
extra = {
< Space size = { 4 } >
< Button size = "small" icon = { < PlusOutlined / > } style = { { borderRadius : 4 , fontSize : 11 } } > 新 增 < / Button >
< Button size = "small" icon = { < ImportOutlined / > } style = { { borderRadius : 4 , fontSize : 11 } } > 导 入 < / Button >
< Button size = "small" icon = { < NodeIndexOutlined / > } style = { { borderRadius : 4 , fontSize : 11 } } > 优 化 < / Button >
< Button size = "small" icon = { < SaveOutlined / > } style = { { borderRadius : 4 , fontSize : 11 } } > 保 存 < / Button >
< Button size = "small" icon = { < Maximize2 size = { 12 } / > } style = { { borderRadius : 4 } } / >
< Button size = "small" icon = { < PlusOutlined / > } style = { { borderRadius : 4 } } > 新 增 < / Button >
< Button size = "small" icon = { < ImportOutlined / > } style = { { borderRadius : 4 } } > 导 入 < / Button >
< Button size = "small" icon = { < NodeIndexOutlined / > } style = { { borderRadius : 4 } } > 优 化 < / Button >
< Button size = "small" icon = { < SaveOutlined / > } style = { { borderRadius : 4 } } > 保 存 < / Button >
< Button size = "small" icon = { < Maximize2 size = { 12 } / > } / >
< / Space >
}
bordered = { false }
bodyStyle = { { padding : 0 , position : 'relative' , overflow : 'hidden' } }
bodyStyle = { { padding : 0 , position : 'relative' } }
style = { { . . . cardStyle , marginBottom : 10 } }
>
{ /* 地图区域 */ }
< div style = { { width : '100%' , height : 420 , background : '#e8f3ff url(https://picsum.photos/seed/solar_map_v2/1600/1000) center/cover' , position : 'relative' } } >
{ /* SVG 航线与区域覆盖 */ }
< svg style = { { position : 'absolute' , top : 0 , left : 0 , width : '100%' , height : '100%' , pointerEvents : 'none' } } >
{ /* 航线 1 (蓝色) */ }
< path d = "M100,120 L200,80 L350,150 L500,100 L650,180" fill = "none" stroke = "#165DFF" strokeWidth = "3" strokeDasharray = "8,4" / >
{ /* 航线 2 (橙色) */ }
< path d = "M220,300 Q350,220 480,350 T700,280" fill = "none" stroke = "#FF7D00" strokeWidth = "3" / >
{ /* 航线 3 (绿色) */ }
< path d = "M400,80 L550,140 L700,100" fill = "none" stroke = "#00B42A" strokeWidth = "3" strokeDasharray = "4,2" / >
{ /* 航点标记 */ }
< circle cx = "100" cy = "120" r = "6" fill = "#165DFF" stroke = "#fff" strokeWidth = "2" / >
< circle cx = "200" cy = "80" r = "6" fill = "#165DFF" stroke = "#fff" strokeWidth = "2" / >
< circle cx = "350" cy = "150" r = "6" fill = "#165DFF" stroke = "#fff" strokeWidth = "2" / >
{ /* 区域覆盖 */ }
< rect x = "550" y = "50" width = "120" height = "80" fill = "rgba(245,63,63,0.2)" stroke = "#F53F3F" strokeWidth = "1" strokeDasharray = "4,2" / >
< rect x = "300" y = "280" width = "150" height = "100" fill = "rgba(22,93,255,0.1)" stroke = "#165DFF" strokeWidth = "1" strokeDasharray = "4,4" / >
< path d = "M100,350 L250,350 L250,450 L100,450 Z" fill = "rgba(255,125,0,0.15)" stroke = "#FF7D00" strokeWidth = "1" / >
< / svg >
{ /* 左侧图例 - 拟真样式 */ }
< div style = { { position : 'absolute' , top : 16 , left : 16 , background : 'rgba(255,255,255,0.92)' , backdropFilter : 'blur(8px)' , padding : '14px 16px' , borderRadius : 10 , width : 150 , boxShadow : '0 4px 12px rgba(0,0,0,0.1)' } } >
< div style = { { fontSize : fontSmall , display : 'flex' , flexDirection : 'column' , gap : 10 } } >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#165DFF' , borderRadius : 1 } } > < / span > 无 人 机 航 线 < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#FF7D00' , borderRadius : 1 } } > < / span > 巡 检 机 器 人 路线 < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#00B42A' , borderRadius : 1 } } > < / span > 除 草 路 线 < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#13C2C2' , borderRadius : 1 } } > < / span > 清 洗 航 线 < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#165DFF' } } > < / span > 无 人 机 航 线 < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#FF7D00' } } > < / span > 巡 检 路 线 < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 8 } } > < span style = { { width : 14 , height : 2 , background : '#00B42A' } } > < / span > 除 草 路 线 < / div >
< Divider style = { { margin : '6px 0' } } / >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } >
< div style = { { width : 18 , height : 18 , background : '#165DFF' , borderRadius : '50%' , display : 'flex' , justifyContent : 'center' , alignItems : 'center' , color : '#fff' , fontSize : 10 , fontWeight : 700 } } > H < / div >
< span > 无 人 机 机 场 < / span >
< / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < Zap size = { 14 } color = "#00B42A" fill = "#00B42A" / > < span > 充 电 点 < / span > < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < Waves size = { 14 } color = "#165DFF" / > < span > 补 水 点 < / span > < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < div style = { { width : 18 , height : 18 , background : '#165DFF' , borderRadius : '50%' , display : 'flex' , justifyContent : 'center' , alignItems : 'center' , color : '#fff' , fontSize : 10 } } > H < / div > < span > 无 人 机 机 场 < / span > < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < Zap size = { 14 } color = "#00B42A" / > < span > 充 电 点 < / span > < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < AlertCircle size = { 14 } color = "#F53F3F" / > < span > 禁 飞 区 < / span > < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < div style = { { width : 14 , height : 14 , border : '1px dashed #F53F3F' , background : 'rgba(245,63,63,0.1)' } } / > < span > 高 优 先 巡 检 区 < / span > < / div >
< div style = { { display : 'flex' , alignItems : 'center' , gap : 10 } } > < div style = { { width : 14 , height : 14 , background : 'rgba(255,125,0,0.2)' , border : '1px solid #FF7D00' } } / > < span > 障 碍 物 区 域 < / span > < / div >
< / div >
< / div >
{ /* 地图交互层 */ }
< div style = { { position : 'absolute' , top : '22%' , left : '16%' , color : '#fff' , textAlign : 'center' } } >
< div style = { { background : '#165DFF' , borderRadius : '50%' , width : 28 , height : 28 , display : 'flex' , justifyContent : 'center' , alignItems : 'center' , fontSize : 12 , fontWeight : 700 , boxShadow : '0 0 10px rgba(22,93,255,0.6)' , border : '2px solid #fff' } } > H < / div >
< div style = { { fontSize : 10 , background : 'rgba(29,33,41,0.8)' , padding : '2px 8px' , borderRadius : 4 , marginTop : 4 } } > 无 人 机 机 场 A01 < / div >
< / div >
< div style = { { position : 'absolute' , top : '42%' , left : '46%' , textAlign : 'center' } } >
< div style = { { border : '2px dashed #F53F3F' , background : 'rgba(245,63,63,0.2)' , width : 140 , height : 90 , borderRadius : 8 , display : 'flex' , justifyContent : 'center' , alignItems : 'center' } } >
< span style = { { fontSize : 11 , fontWeight : 700 , color : '#fff' , textShadow : '0 1px 2px rgba(0,0,0,0.8)' } } > 高 优 先 级 巡 检 区 B - 12 < / span >
< / div >
< / div >
{ /* 右下角地图缩放控制 */ }
< div style = { { position : 'absolute' , bottom : 20 , right : 20 , display : 'flex' , flexDirection : 'column' , gap : 8 } } >
< div style = { { background : '#fff' , borderRadius : 8 , padding : 8 , display : 'flex' , flexDirection : 'column' , gap : 12 , boxShadow : '0 2px 8px rgba(0,0,0,0.1)' } } >
< AimOutlined style = { { fontSize : 18 , color : '#4E5969' , cursor : 'pointer' } } / >
< Divider style = { { margin : 0 } } / >
< PlusOutlined style = { { fontSize : 18 , color : '#4E5969' , cursor : 'pointer' } } / >
< Divider style = { { margin : 0 } } / >
< div style = { { width : 18 , height : 2 , background : '#C9CDD4' } } > < / div >
< / div >
< / div >
< / div >
< / Card >
{ /* 航点列表 */ }
{ /* 真实任务列表表格 */ }
< Card
title = { < span style = { { fontSize : fontNormal , fontWeight : 700 } } > 航 线 列 表 < Text type = "secondary" style = { { fontSize : 10 , fontWeight : 400 , marginLeft : 8 } } > 航 线 : FL - 03 < / Text > < / span > }
title = {
< div className = "flex justify-between w-full items-center" >
< span style = { { fontSize : 14 , fontWeight : 700 } } >
设 备 任 务 列 表
< / span >
< Select
style = { { width : 180 } }
size = "small"
placeholder = "选择无人机"
showSearch
allowClear
options = { planedevices ? . map ( item = > ( {
value : item.device_sn ,
label : item.drone_callsign || item . device_sn
} ) ) }
value = { selectedDrone ? . device_sn }
onChange = { ( val ) = > {
const find = planedevices . find ( i = > i . device_sn === val ) ;
setSelectedDrone ( find || null ) ;
} }
/ >
< / div >
}
size = "small"
extra = { < PlusOutlined style = { { cursor : 'pointer' , color : '#165DFF' , fontSize : 12 } } / > }
extra = { < PlusOutlined style = { { cursor : 'pointer' , color : '#165DFF' , fontSize : 12 , marginLeft : 10 } } / > }
bordered = { false }
bodyStyle = { { padding : 0 , height : "100%" } }
style = { {
. . . cardStyle ,
flex : 1 ,
display : "flex" ,
flexDirection : "column" ,
height : "100%" ,
} }
bodyStyle = { { padding : 0 } }
style = { { . . . cardStyle , flex : 1 } }
>
< div style = { {
flex : 1 ,
height : "100%" ,
position : "relative" ,
} } >
< Table
size = "small"
pagination = { false }
scroll = { {
y : "auto" ,
} }
dataSource = { [
{ key : '1' , point : 'WP-01' , coord : '30.32921, 120.12345' , action : '起飞' , height : 35 , speed : 6.0 , stay : 0 , remark : '从机场起飞' } ,
{ key : '2' , point : 'WP-02' , coord : '区块 B-01' , action : '拍摄' , height : 35 , speed : 6.0 , stay : 2 , remark : '组件可见光' } ,
{ key : '3' , point : 'WP-03' , coord : '区块 B-05' , action : '热成像' , height : 35 , speed : 6.0 , stay : 3 , remark : '热斑检测' } ,
{ key : '4' , point : 'WP-04' , coord : '区块 B-09' , action : '悬停' , height : 35 , speed : 0.0 , stay : 10 , remark : '重点检查' } ,
{ key : '5' , point : 'WP-05' , coord : '区块 B-12' , action : '热成像' , height : 35 , speed : 6.0 , stay : 3 , remark : '高优先区' } ,
{ key : '6' , point : 'WP-06' , coord : '区块 B-16' , action : '拍摄' , height : 35 , speed : 6.0 , stay : 2 , remark : '组件可见光' } ,
] }
columns = { [
{ title : '航点' , dataIndex : 'point' , width : 80 , render : ( t ) = > < Text strong > { t } < / Text > } ,
{ title : '坐标/区域' , dataIndex : 'coord' , width : 120 , ellipsis : true } ,
{ title : '动作' , dataIndex : 'action' , width : 80 , render : ( a ) = > < Tag color = { a === '起飞' ? 'blue' : a === '悬停' ? 'orange' : 'cyan' } bordered = { false } style = { { margin : 0 } } > { a } < / Tag > } ,
{ title : '高度(m)' , dataIndex : 'height' , width : 70 } ,
{ title : '速度(m/s)' , dataIndex : 'speed' , width : 90 } ,
{ title : '停留(s)' , dataIndex : 'stay' , width : 70 } ,
{ title : '备注' , dataIndex : 'remark' , ellipsis : true } ,
] }
style = { { height : "100%" } }
/ >
< / div >
< Table
size = "small"
rowKey = "uuid"
columns = { taskColumns }
dataSource = { taskList }
pagination = { false }
scroll = { { y : 260 } }
style = { { height : '100%' } }
/ >
< / Card >
< / Col >
{ /* 右侧:任务编排、参数与天气 */ }
< Col xs = { 24 } xl = { 10 } >
< Card
title = { < span style = { { fontSize : fontTitle , fontWeight : 700 } } > 任 务 编 排 与 AI 建 议 < / span > }