597 lines
28 KiB
TypeScript
597 lines
28 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Row, Col, Card, Button, Table, Tag, Typography, Space, Statistic, DatePicker,
|
|
Divider, Progress, Badge, Select, Tooltip, Avatar
|
|
} from 'antd';
|
|
import {
|
|
ReloadOutlined, PlusOutlined, ArrowUpOutlined, ArrowDownOutlined,
|
|
CheckCircleOutlined, CloudOutlined, SunOutlined, MoneyCollectOutlined,
|
|
CalendarOutlined, DropOutlined, DownloadOutlined, InfoCircleOutlined,
|
|
EnvironmentOutlined, RocketOutlined, UserOutlined, WindOutlined,
|
|
PercentageOutlined, GlobalOutlined, AimOutlined, FilterOutlined,
|
|
FullscreenOutlined, ZoomInOutlined, ZoomOutOutlined
|
|
} from '@ant-design/icons';
|
|
import { Line, Pie, Column, DualAxes } from '@ant-design/charts';
|
|
import {
|
|
Droplets,
|
|
Zap,
|
|
TrendingUp,
|
|
Calendar,
|
|
DollarSign,
|
|
LayoutGrid,
|
|
ChevronRight,
|
|
Bot,
|
|
User,
|
|
CloudRain,
|
|
Thermometer,
|
|
Wind,
|
|
Info,
|
|
ShieldCheck,
|
|
Leaf
|
|
} from 'lucide-react';
|
|
import { UniversalBarChart, UniversalLineChart, UniversalPieChart } from '../components/recharts';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
const { Title, Text } = Typography;
|
|
const { RangePicker } = DatePicker;
|
|
const { Option } = Select;
|
|
|
|
// 模拟数据
|
|
const pollutionLineData = [
|
|
{ date: '05-18', pollution: 78, threshold: 75 },
|
|
{ date: '05-19', pollution: 72, threshold: 75 },
|
|
{ date: '05-20', pollution: 65, threshold: 75 },
|
|
{ date: '05-21', pollution: 75, threshold: 75 },
|
|
{ date: '05-22', pollution: 68, threshold: 75 },
|
|
{ date: '05-23', pollution: 60, threshold: 75 },
|
|
{ date: '05-24', pollution: 68.5, threshold: 75 },
|
|
];
|
|
|
|
|
|
|
|
const compareChartData = [
|
|
{ date: 'D-7', before: 800, after: 1200 },
|
|
{ date: 'D-6', before: 850, after: 1250 },
|
|
{ date: 'D-5', before: 900, after: 1300 },
|
|
{ date: 'D-4', before: 880, after: 1280 },
|
|
{ date: 'D-3', before: 920, after: 1320 },
|
|
{ date: 'D-2', before: 950, after: 1350 },
|
|
{ date: 'D-1', before: 980, after: 1380 },
|
|
];
|
|
|
|
|
|
export default function CleanOptimizationPage() {
|
|
const { t } = useTranslation();
|
|
const cardStyle = {
|
|
borderRadius: 12,
|
|
border: '1px solid #f0f0f0',
|
|
boxShadow: '0 4px 12px rgba(0,0,0,0.05)',
|
|
overflow: 'hidden'
|
|
};
|
|
|
|
const smartCleanPlanData = [
|
|
{ id: t('clean.plan', { name: '01' }), priority: t('workOrder.high'), area: t('clean.block', { block: '15, 01' }), boost: '4,250 kWh', weather: '88%' },
|
|
{ id: t('clean.plan', { name: '02' }), priority: t('workOrder.high'), area: t('clean.block', { block: '02, 11' }), boost: '3,180 kWh', weather: '85%' },
|
|
{ id: t('clean.plan', { name: '03' }), priority: t('workOrder.medium'), area: t('clean.block', { block: '05, 12' }), boost: '2,210 kWh', weather: '80%' },
|
|
{ id: t('clean.plan', { name: '04' }), priority: t('workOrder.medium'), area: t('clean.block', { block: '07, 03' }), boost: '1,620 kWh', weather: '78%' },
|
|
{ id: t('clean.plan', { name: '05' }), priority: t('workOrder.low'), area: t('clean.block', { block: '17, 13' }), boost: '1,210 kWh', weather: '82%' },
|
|
];
|
|
|
|
const workOrderPreviewData = [
|
|
{ area: t('clean.block', { block: '15' }), priority: t('workOrder.high'), duration: '4.0 h', handler: t('clean.engineer', { name: 'Li' }), planTime: '2025-05-26 - 05-27', status: t('clean.pendingCreate') },
|
|
{ area: t('clean.block', { block: '01' }), priority: t('workOrder.high'), duration: '3.5 h', handler: t('clean.engineer', { name: 'Li' }), planTime: '2025-05-26 - 05-27', status: t('clean.pendingCreate') },
|
|
{ area: t('clean.block', { block: '02' }), priority: t('workOrder.high'), duration: '2.8 h', handler: t('clean.engineer', { name: 'Wang' }), planTime: '2025-05-28 - 05-29', status: t('clean.pendingCreate') },
|
|
{ area: t('clean.block', { block: '11' }), priority: t('workOrder.high'), duration: '2.6 h', handler: t('clean.engineer', { name: 'Wang' }), planTime: '2025-05-28 - 05-29', status: t('clean.pendingCreate') },
|
|
{ area: t('clean.block', { block: '05' }), priority: t('workOrder.medium'), duration: '2.2 h', handler: t('clean.engineer', { name: 'Zhao' }), planTime: '2025-05-29 - 05-30', status: t('clean.pendingCreate') },
|
|
];
|
|
|
|
const dualAxesData = [
|
|
[
|
|
{ date: 'D-7', value: 800, type: t('clean.generationBefore') },
|
|
{ date: 'D-7', value: 1200, type: t('clean.generationAfter') },
|
|
{ date: 'D-6', value: 850, type: t('clean.generationBefore') },
|
|
{ date: 'D-6', value: 1250, type: t('clean.generationAfter') },
|
|
{ date: 'D-5', value: 900, type: t('clean.generationBefore') },
|
|
{ date: 'D-5', value: 1300, type: t('clean.generationAfter') },
|
|
{ date: 'D-4', value: 880, type: t('clean.generationBefore') },
|
|
{ date: 'D-4', value: 1280, type: t('clean.generationAfter') },
|
|
{ date: 'D-3', value: 920, type: t('clean.generationBefore') },
|
|
{ date: 'D-3', value: 1320, type: t('clean.generationAfter') },
|
|
{ date: 'D-2', value: 950, type: t('clean.generationBefore') },
|
|
{ date: 'D-2', value: 1350, type: t('clean.generationAfter') },
|
|
{ date: 'D-1', value: 980, type: t('clean.generationBefore') },
|
|
{ date: 'D-1', value: 1380, type: t('clean.generationAfter') },
|
|
],
|
|
[
|
|
{ date: 'D-7', rate: 35 },
|
|
{ date: 'D-6', rate: 32 },
|
|
{ date: 'D-5', rate: 30 },
|
|
{ date: 'D-4', rate: 28 },
|
|
{ date: 'D-3', rate: 31 },
|
|
{ date: 'D-2', rate: 29 },
|
|
{ date: 'D-1', rate: 33 },
|
|
],
|
|
|
|
];
|
|
|
|
const pieChartData = [
|
|
{ name: t('clean.dustAccumulation'), value: 6120, color: '#165DFF' },
|
|
{ name: t('clean.birdDroppings'), value: 1480, color: '#F53F3F' },
|
|
{ name: t('clean.sandDeposition'), value: 1120, color: '#F7BA1E' },
|
|
{ name: t('constants.others'), value: 600, color: '#86909C' },
|
|
];
|
|
|
|
const renderPriorityTag = (priority: string) => {
|
|
const config: Record<string, any> = {
|
|
[t('workOrder.high')]: { color: '#ff4d4f', bg: '#fff1f0' },
|
|
[t('workOrder.medium')]: { color: '#faad14', bg: '#fff7e6' },
|
|
[t('workOrder.low')]: { color: '#52c41a', bg: '#f6ffed' },
|
|
};
|
|
return <Tag color={config[priority]?.bg} style={{ color: config[priority]?.color, border: `1px solid ${config[priority]?.color}`, margin: 0, borderRadius: 4 }}>{priority}</Tag>;
|
|
};
|
|
|
|
return (
|
|
<div style={{ background: '#f5f7fa', minHeight: '100vh', padding: '12px 0px', boxSizing: 'border-box' }}>
|
|
<style>
|
|
{`
|
|
.compact-table .ant-table-thead > tr > th {
|
|
background: #f8fafc;
|
|
padding: 8px 12px !important;
|
|
font-size: 12px;
|
|
color: #4E5969;
|
|
}
|
|
.compact-table .ant-table-tbody > tr > td {
|
|
padding: 8px 12px !important;
|
|
font-size: 12px;
|
|
}
|
|
.map-block {
|
|
transition: all 0.3s;
|
|
cursor: pointer;
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
|
}
|
|
.map-block:hover {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 8px 16px rgba(0,0,0,0.3);
|
|
z-index: 10;
|
|
}
|
|
`}
|
|
</style>
|
|
|
|
|
|
<Row
|
|
gutter={[8, 8]}
|
|
style={{
|
|
marginBottom: 16,
|
|
flexWrap: 'wrap',
|
|
}}
|
|
>
|
|
{[
|
|
{
|
|
title: t('clean.s7'),
|
|
value: '68.5',
|
|
unit: '',
|
|
tag: t('clean.levelMedium'),
|
|
tagColor: '#52c41a',
|
|
trend: t('devices.vsYesterday', { value: '▼ 5.3' }),
|
|
trendColor: '#ff4d4f',
|
|
icon: <SunOutlined />,
|
|
color: '#faad14',
|
|
bg: '#fff7e6'
|
|
},
|
|
{
|
|
title: t('clean.fixed2RecommendedCleanBlock'),
|
|
value: '8 / 20',
|
|
unit: '',
|
|
trend: t('clean.share', { value: '40%' }),
|
|
icon: <LayoutGrid />,
|
|
color: '#1890ff',
|
|
bg: '#e6f7ff'
|
|
},
|
|
{
|
|
title: t('clean.generation'),
|
|
value: '12,850',
|
|
unit: t('home.kWh'),
|
|
trend: t('clean.shareOfTodayGeneration', { value: '14.2%' }),
|
|
icon: <TrendingUp />,
|
|
color: '#1890ff',
|
|
bg: '#f0f5ff'
|
|
},
|
|
{
|
|
title: t('clean.fixed2EstimatedRevenue'),
|
|
value: '¥ 7,286',
|
|
unit: '',
|
|
trend: t('clean.calcByElectricityPrice', { value: `0.568 ${t('clean.kwhFixed2')}` }),
|
|
icon: <DollarSign />,
|
|
color: '#faad14',
|
|
bg: '#fff7e6'
|
|
},
|
|
{
|
|
title: t('clean.cleaningFixed2TimeFixed2'),
|
|
value: '2025-05-12',
|
|
unit: '',
|
|
trend: t('clean.daysAgo', { days: '12' }),
|
|
icon: <Calendar />,
|
|
color: '#1890ff',
|
|
bg: '#f0f5ff'
|
|
},
|
|
{
|
|
title: t('clean.fixed2CleaningCost'),
|
|
value: '¥ 3,200',
|
|
unit: '',
|
|
trend: t('clean.estimatedTotalCostPerClean'),
|
|
icon: <Droplets />,
|
|
color: '#1890ff',
|
|
bg: '#e6f7ff'
|
|
},
|
|
].map((item, index) => (
|
|
<Col key={index} style={{ minWidth: '168px', flex: '1 0 auto', maxWidth: "250px" }}>
|
|
<Card
|
|
variant="borderless"
|
|
styles={{ body: { padding: '16px' } }}
|
|
style={{ borderRadius: 8, height: 120 }}
|
|
>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', height: '100%' }}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
|
<div style={{ fontSize: 13, color: '#86909C', marginBottom: 4 }}>{item.title}</div>
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
|
|
<div style={{ fontSize: 24, fontWeight: 700, color: item.color }}>
|
|
{item.value}
|
|
</div>
|
|
{item.unit && (
|
|
<span style={{ fontSize: 14, color: '#4E5969' }}>{item.unit}</span>
|
|
)}
|
|
{item.tag && (
|
|
<Tag
|
|
color={item.tagColor}
|
|
style={{
|
|
borderRadius: 10,
|
|
margin: 0,
|
|
padding: '0 8px',
|
|
fontSize: 12
|
|
}}
|
|
>
|
|
{item.tag}
|
|
</Tag>
|
|
)}
|
|
</div>
|
|
|
|
<div style={{ fontSize: 12, color: item.trendColor || '#86909C' }}>
|
|
{item.trend}
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: '50%',
|
|
background: item.bg,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
flexShrink: 0
|
|
}}>
|
|
{React.cloneElement(item.icon, { style: { fontSize: 20, color: item.color } })}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
))}
|
|
</Row>
|
|
{/* 标题与操作栏 */}
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
<Space size={8}>
|
|
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(18px, 1.5vw, 24px)', }}>{t('clean.fixed2CleaningOptimization')}</Typography.Title>
|
|
<InfoCircleOutlined style={{ color: '#86909C' }} />
|
|
</Space>
|
|
<Space>
|
|
<Text type="secondary" style={{ fontSize: 12 }}><ReloadOutlined />{t('clean.fixed2UpdatedAt202505240930')}</Text>
|
|
<Button icon={<ReloadOutlined />}>{t('aiAnalysis.fixed2Refresh')}</Button>
|
|
<Select defaultValue="7d" style={{ width: 110 }}>
|
|
<Option value="7d">{t('clean.7')}</Option>
|
|
<Option value="30d">{t('clean.30')}</Option>
|
|
</Select>
|
|
<Button type="primary" icon={<DownloadOutlined />}>{t('aiAnalysis.fixed2Export')}</Button>
|
|
</Space>
|
|
</div>
|
|
|
|
<Row gutter={[8, 8]} style={{ display: 'flex', alignItems: 'stretch' }}>
|
|
{/* 左侧:分析图表 */}
|
|
<Col xs={24} xl={6} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.s6')}</span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<div style={{ fontSize: 12, color: '#86909C', marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
|
|
<span>{t('clean.s7')}</span>
|
|
<Space size={12}>
|
|
<span style={{ display: 'flex', alignItems: 'center' }}><div style={{ width: 8, height: 8, background: '#165DFF', marginRight: 4 }}></div>{t('clean.s7')}</span>
|
|
<span style={{ display: 'flex', alignItems: 'center' }}><div style={{ width: 8, height: 2, borderBottom: '1px dashed #F53F3F', marginRight: 4 }}></div>{t('alerts.threshold')}</span>
|
|
</Space>
|
|
</div>
|
|
<UniversalLineChart
|
|
data={pollutionLineData}
|
|
xKey="date"
|
|
height={180}
|
|
dot
|
|
series={[
|
|
{
|
|
key: 'pollution',
|
|
name: t('clean.s7'),
|
|
color: '#1890ff',
|
|
strokeWidth: 3,
|
|
},
|
|
{
|
|
key: 'threshold',
|
|
name: t('alerts.threshold'),
|
|
color: '#ff4d4f',
|
|
dashed: true,
|
|
strokeWidth: 2,
|
|
},
|
|
]}
|
|
/>
|
|
</Card>
|
|
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.fixed2Generation')}<Text type="secondary" style={{ fontWeight: 400, fontSize: 12 }}>{t('clean.near7Days')}</Text></span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<div style={{ position: 'relative', height: "100%" }}>
|
|
<UniversalPieChart
|
|
data={pieChartData}
|
|
height={220}
|
|
innerRadius={50}
|
|
outerRadius={80}
|
|
showLabel={false}
|
|
/>
|
|
<div style={{ position: 'absolute', left: '30%', top: '50%', transform: 'translate(-50%, -50%)', textAlign: 'center' }}>
|
|
<div style={{ fontSize: 10, color: '#86909C' }}>{t('clean.batteryFixed2')}</div>
|
|
<div style={{ fontSize: 16, fontWeight: 800, color: '#1D2129' }}>9,320</div>
|
|
<div style={{ fontSize: 10, color: '#86909C' }}>{t('home.kWh')}</div>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.fixed2Cleaning2')}<Text type="secondary" style={{ fontWeight: 400, fontSize: 12 }}>{t('clean.nearLast')}</Text></span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<UniversalBarChart
|
|
data={compareChartData}
|
|
xKey="date"
|
|
height={220}
|
|
barSize={18}
|
|
series={[
|
|
{
|
|
key: 'before',
|
|
name: `${t('clean.generationBefore')} (kWh)`,
|
|
color: '#1890ff',
|
|
},
|
|
{
|
|
key: 'after',
|
|
name: `${t('clean.generationAfter')} (kWh)`,
|
|
color: '#52c41a',
|
|
},
|
|
]}
|
|
/>
|
|
</Card>
|
|
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.fixed2CleaningFixed2')}</span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<Row gutter={[16, 16]}>
|
|
<Col span={12}>
|
|
<div style={{ fontSize: 11, color: '#86909C', marginBottom: 4 }}>{t('clean.generation')}</div>
|
|
<div style={{ fontSize: 15, fontWeight: 800 }}>12,850 <span style={{ fontSize: 11, fontWeight: 400 }}>{t('home.kWh')}</span></div>
|
|
</Col>
|
|
<Col span={12}>
|
|
<div style={{ fontSize: 11, color: '#86909C', marginBottom: 4 }}>{t('clean.electricityPrice')}</div>
|
|
<div style={{ fontSize: 15, fontWeight: 800 }}>0.568 <span style={{ fontSize: 11, fontWeight: 400 }}>{t('clean.kwhFixed2')}</span></div>
|
|
</Col>
|
|
<Col span={12}>
|
|
<div style={{ fontSize: 11, color: '#86909C', marginBottom: 4 }}>{t('clean.netRevenueFixed2')}</div>
|
|
<div style={{ fontSize: 15, fontWeight: 800, color: '#00B42A' }}>7,286 <span style={{ fontSize: 11, fontWeight: 400 }}>{t('clean.s16')}</span></div>
|
|
</Col>
|
|
<Col span={12}>
|
|
<div style={{ fontSize: 11, color: '#86909C', marginBottom: 4 }}>{t('clean.roi')}</div>
|
|
<div style={{ fontSize: 20, fontWeight: 800, color: '#00B42A' }}>227%</div>
|
|
</Col>
|
|
</Row>
|
|
<Divider style={{ margin: '16px 0' }} />
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: '#f8fafc', padding: '10px 16px', borderRadius: 8 }}>
|
|
<span style={{ fontSize: 12, color: '#86909C' }}>{t('clean.s17')}</span>
|
|
<span style={{ fontSize: 18, fontWeight: 800 }}>0.44 <Text style={{ fontSize: 12 }}>{t('devices.day')}</Text></span>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
|
|
{/* 中间:地图分布与工单预览 */}
|
|
<Col xs={24} xl={11} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700 }}>{t('clean.recommendedCleanBlock')}</span>}
|
|
variant="borderless"
|
|
style={{ ...cardStyle, }}
|
|
styles={{ body: { padding: 0, position: 'relative', overflow: 'hidden', minHeight: 500 } }}
|
|
>
|
|
<div style={{ background: '#f8fafc', padding: '10px 20px', display: 'flex', justifyContent: 'space-between', borderBottom: '1px solid #f0f0f0', flexWrap: 'wrap', gap: 8 }}>
|
|
<Space size={16} wrap style={{ fontSize: 11 }}>
|
|
<span style={{ color: '#86909C' }}>{t('clean.s20')}</span>
|
|
<Badge color="#F53F3F" text={t('clean.priorityClean')} />
|
|
<Badge color="#FF7D00" text={t('clean.higherRange')} />
|
|
<Badge color="#F7BA1E" text={t('clean.mediumRange')} />
|
|
<Badge color="#D1FF6B" text={t('clean.lowerRange')} />
|
|
<Badge color="#00B42A" text={t('clean.cleanRange')} />
|
|
</Space>
|
|
</div>
|
|
|
|
{/* 地图背景图与浮动标签 */}
|
|
<div style={{ position: 'relative', width: '100%', height: '100%', background: '#e5e6eb' }}>
|
|
<img src="https://images.unsplash.com/photo-1509391366360-2e959784a276?ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&q=80" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
|
|
|
{/* 模拟区块叠加层 */}
|
|
<div style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', background: 'rgba(0,0,0,0.1)' }} />
|
|
|
|
{/* 具体的污染区块标签 */}
|
|
{[
|
|
{ id: '01', score: '82.3', top: '25%', left: '30%', color: '#F53F3F' },
|
|
{ id: '02', score: '76.8', top: '20%', left: '45%', color: '#F53F3F' },
|
|
{ id: '03', score: '63.2', top: '22%', left: '60%', color: '#FF7D00' },
|
|
{ id: '08', score: '19.7', top: '28%', left: '75%', color: '#00B42A' },
|
|
{ id: '04', score: '33.6', top: '45%', left: '20%', color: '#F7BA1E' },
|
|
{ id: '05', score: '68.7', top: '42%', left: '38%', color: '#F53F3F' },
|
|
{ id: '15', score: '82.9', top: '48%', left: '55%', color: '#F53F3F' },
|
|
{ id: '19', score: '44.4', top: '55%', left: '65%', color: '#F7BA1E' },
|
|
{ id: '11', score: '44.8', top: '65%', left: '42%', color: '#F7BA1E' },
|
|
{ id: '13', score: '28.6', top: '75%', left: '30%', color: '#F7BA1E' },
|
|
{ id: '19', score: '24.5', top: '78%', left: '50%', color: '#D1FF6B' },
|
|
{ id: '20', score: '17.8', top: '85%', left: '40%', color: '#00B42A' },
|
|
].map(block => (
|
|
<div key={block.id} className="map-block" style={{ position: 'absolute', top: block.top, left: block.left, background: block.color, color: '#fff', padding: '4px 10px', borderRadius: 6, border: '1.5px solid #fff', textAlign: 'center', minWidth: 60 }}>
|
|
<div style={{ fontSize: 9, opacity: 0.9 }}>{t('clean.block', { block: block.id })}</div>
|
|
<div style={{ fontSize: 13, fontWeight: 800 }}>{block.score}</div>
|
|
</div>
|
|
))}
|
|
|
|
{/* 右侧悬浮控制按钮 */}
|
|
<div style={{ position: 'absolute', top: 20, right: 20, display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
<Button icon={<AimOutlined />} style={{ borderRadius: 8, width: 40, height: 40, background: 'rgba(255,255,255,0.9)', border: 'none' }} />
|
|
<Button icon={<FilterOutlined />} style={{ borderRadius: 8, width: 40, height: 40, background: 'rgba(255,255,255,0.9)', border: 'none' }} />
|
|
<Button icon={<ZoomInOutlined />} style={{ borderRadius: 8, width: 40, height: 40, background: 'rgba(255,255,255,0.9)', border: 'none' }} />
|
|
<Button icon={<ZoomOutOutlined />} style={{ borderRadius: 8, width: 40, height: 40, background: 'rgba(255,255,255,0.9)', border: 'none' }} />
|
|
<Button icon={<ReloadOutlined />} style={{ borderRadius: 8, width: 40, height: 40, background: 'rgba(255,255,255,0.9)', border: 'none' }} />
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700 }}>{t('clean.fixed2Cleaning2')}</span>}
|
|
extra={<Button type="link" size="small">{t('devices.viewMore')}<ChevronRight size={14} /></Button>}
|
|
variant="borderless"
|
|
style={cardStyle}
|
|
styles={{ body: { padding: 0 } }}
|
|
>
|
|
<Table
|
|
rowKey="area"
|
|
columns={[
|
|
{ title: t('clean.s45'), dataIndex: 'area', key: 'area' },
|
|
{ title: t('workOrder.priority'), dataIndex: 'priority', key: 'priority', render: renderPriorityTag },
|
|
{ title: t('clean.s46'), dataIndex: 'duration', key: 'duration' },
|
|
{ title: t('systemSetting.leader'), dataIndex: 'handler', key: 'handler' },
|
|
{ title: t('clean.timeFixed2'), dataIndex: 'planTime', key: 'planTime' },
|
|
{ title: t('roles.status'), dataIndex: 'status', key: 'status', render: (s) => <Tag color="blue" variant="outline" style={{ borderRadius: 4 }}>{s}</Tag> },
|
|
{ title: t('roles.actions'), key: 'action', render: () => <Button type="primary" size="small" style={{ borderRadius: 4 }}>{t('clean.s23')}</Button> },
|
|
]}
|
|
dataSource={workOrderPreviewData}
|
|
pagination={false}
|
|
size="small"
|
|
className="compact-table"
|
|
/>
|
|
</Card>
|
|
</Col>
|
|
|
|
{/* 右侧:决策建议与参考 */}
|
|
<Col xs={24} xl={7} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.cleaning2')}</span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 0 } }}>
|
|
<div style={{ background: '#f8fafc', padding: '8px 16px', borderBottom: '1px solid #f0f0f0' }}>
|
|
<Text type="secondary" style={{ fontSize: 11 }}>{t('clean.algorithmModel')}</Text>
|
|
</div>
|
|
<Table
|
|
rowKey="id"
|
|
columns={[
|
|
{ title: t('clean.s48'), dataIndex: 'id', key: 'id', width: 85 },
|
|
{ title: t('workOrder.priority'), dataIndex: 'priority', key: 'priority', render: (p) => <span style={{ color: p === t('workOrder.high') ? '#F53F3F' : '#F7BA1E', fontWeight: 600 }}>{p}</span>, width: 65 },
|
|
{ title: t('clean.s49'), dataIndex: 'area', key: 'area' },
|
|
{ title: t('clean.s50'), dataIndex: 'boost', key: 'boost', render: (b) => <span style={{ color: '#00B42A', fontWeight: 600 }}>{b}</span> },
|
|
{ title: t('clean.fixed2WeatherAdvice2Fixed2SuitableExec2'), dataIndex: 'weather', key: 'weather', render: (w) => <Space size={4}>{w} <SunOutlined style={{ color: '#F7BA1E' }} /></Space> },
|
|
]}
|
|
dataSource={smartCleanPlanData}
|
|
pagination={false}
|
|
size="small"
|
|
className="compact-table"
|
|
/>
|
|
</Card>
|
|
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.fixed2Cleaning2')}</span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<Row gutter={12}>
|
|
<Col span={12}>
|
|
<div style={{ background: '#F9F0FF', borderRadius: 12, padding: 16, border: '1px solid #D3ADF7', height: '100%' }}>
|
|
<Space align="center" style={{ marginBottom: 12 }}>
|
|
<div style={{ width: 32, height: 32, background: '#722ED1', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Bot size={18} color="#fff" /></div>
|
|
<Text strong style={{ fontSize: 13, color: '#391085' }}>{t('clean.robot')}</Text>
|
|
</Space>
|
|
<div style={{ fontSize: 11, color: '#531DAB', marginBottom: 12, lineHeight: 1.5 }}>{t('clean.cleaning')}</div>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11 }}>
|
|
<span style={{ color: '#8c8c8c' }}>{t('clean.s28')}</span>
|
|
<Text strong>{t('clean.18')}</Text>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
<Col span={12}>
|
|
<div style={{ background: '#E6F7FF', borderRadius: 12, padding: 16, border: '1px solid #91D5FF', height: '100%' }}>
|
|
<Space align="center" style={{ marginBottom: 12 }}>
|
|
<div style={{ width: 32, height: 32, background: '#1890FF', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><User size={18} color="#fff" /></div>
|
|
<Text strong style={{ fontSize: 13, color: '#003A8C' }}>{t('clean.fixed2Manual')}</Text>
|
|
</Space>
|
|
<div style={{ fontSize: 11, color: '#0050B3', marginBottom: 12, lineHeight: 1.5 }}>{t('clean.handleCriticalArea')}</div>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11 }}>
|
|
<span style={{ color: '#8c8c8c' }}>{t('clean.s28')}</span>
|
|
<Text strong>{t('clean.32')}</Text>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</Card>
|
|
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.fixed2WeatherAdvice2')}<Text type="secondary" style={{ fontSize: 12, fontWeight: 400 }}>({t('clean.plan', { name: '01' })})</Text></span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<Row gutter={12}>
|
|
<Col span={8}>
|
|
<div style={{ background: '#f8fafc', padding: '10px 4px', borderRadius: 8, textAlign: 'center', border: '1px solid #f0f0f0' }}>
|
|
<div style={{ fontSize: 11, color: '#86909C' }}>{`05-26 (${t('common.mondayShort')})`}</div>
|
|
<SunOutlined style={{ fontSize: 24, color: '#F7BA1E', margin: '8px 0' }} />
|
|
<div style={{ fontSize: 12, fontWeight: 700 }}>{t('clean.fixed2Cloudy1828')}</div>
|
|
</div>
|
|
</Col>
|
|
<Col span={8}>
|
|
<div style={{ background: '#f8fafc', padding: '10px 4px', borderRadius: 8, textAlign: 'center', border: '1px solid #f0f0f0' }}>
|
|
<div style={{ fontSize: 11, color: '#86909C' }}>{`05-27 (${t('common.tuesdayShort')})`}</div>
|
|
<SunOutlined style={{ fontSize: 24, color: '#F7BA1E', margin: '8px 0' }} />
|
|
<div style={{ fontSize: 12, fontWeight: 700 }}>{t('clean.1930')}</div>
|
|
</div>
|
|
</Col>
|
|
<Col span={8}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, justifyContent: 'center', height: '100%' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
<Wind size={14} style={{ color: '#86909C' }} /><span style={{ fontSize: 11 }}>{t('clean.s36')}<b style={{ color: '#00B42A' }}>{`2.1m/s (${t('clean.suitable')})`}</b></span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
<CloudRain size={14} style={{ color: '#86909C' }} /><span style={{ fontSize: 11 }}>{t('clean.s37')}<b style={{ color: '#86909C' }}>{`10% (${t('clean.low')})`}</b></span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
<Droplets size={14} style={{ color: '#165DFF' }} /><span style={{ fontSize: 11 }}>{t('clean.s38')}<b style={{ color: '#165DFF' }}>{`${t('clean.sufficient')} (50t)`}</b></span>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</Card>
|
|
|
|
<Card title={<span style={{ fontSize: 14, fontWeight: 700 }}>{t('clean.fixed2Cleaning')}</span>} variant="borderless" style={cardStyle} styles={{ body: { padding: 16 } }}>
|
|
<Row gutter={[16, 16]}>
|
|
{[
|
|
{ icon: <Droplets size={18} color="#165DFF" />, bg: '#E8F3FF', label: t('clean.s52'), value: t('clean.tonUnit', { value: '18' }), sub: t('clean.waterSaveWithRobot', { value: '30%' }), subColor: '#00B42A' },
|
|
{ icon: <Calendar size={18} color="#165DFF" />, bg: '#E8F3FF', label: t('clean.s53'), value: t('clean.hourUnit', { value: '12' }), sub: t('clean.parallelJobs', { count: '2' }), subColor: '#86909C' },
|
|
{ icon: <Leaf size={18} color="#00B42A" />, bg: '#E8FFEA', label: t('clean.s54'), value: t('clean.tonUnit', { value: '6.85' }), sub: t('clean.co2Reduction'), subColor: '#86909C' },
|
|
{ icon: <ShieldCheck size={18} color="#F7BA1E" />, bg: '#FFF7E8', label: t('clean.s55'), value: t('aiAnalysis.lowRisk'), valueColor: '#00B42A', sub: t('clean.safeControllable'), subColor: '#86909C' },
|
|
].map((item, idx) => (
|
|
<Col span={12} key={idx}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
<div style={{ width: 40, height: 40, background: item.bg, borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{item.icon}</div>
|
|
<div>
|
|
<div style={{ fontSize: 11, color: '#86909C' }}>{item.label}</div>
|
|
<div style={{ fontSize: 14, fontWeight: 800, color: item.valueColor || '#1D2129' }}>{item.value}</div>
|
|
<div style={{ fontSize: 9, color: item.subColor }}>{item.sub}</div>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
))}
|
|
</Row>
|
|
<div style={{ marginTop: 20, padding: 12, background: '#f8fafc', borderRadius: 8, display: 'flex', gap: 8, alignItems: 'flex-start' }}>
|
|
<InfoCircleOutlined style={{ fontSize: 12, color: '#86909C', marginTop: 2 }} />
|
|
<Text type="secondary" style={{ fontSize: 10, lineHeight: 1.5 }}>{t('clean.generationActualFixed2ExecuteWeatherAdvice2Ok')}</Text>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
);
|
|
}
|
|
|