600 lines
28 KiB
TypeScript
600 lines
28 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Row, Col, Card, Button, Tag, Table, Typography, Space, Statistic, message, Divider, Tooltip, Avatar, Empty
|
|
} from 'antd';
|
|
import {
|
|
ReloadOutlined, InfoCircleOutlined, ThunderboltOutlined,
|
|
LineChartOutlined, AppstoreOutlined, BellOutlined,
|
|
CheckCircleOutlined, CalendarOutlined, ArrowUpOutlined,
|
|
ArrowDownOutlined, RiseOutlined, FallOutlined, ScanOutlined,
|
|
DatabaseOutlined, CloudOutlined, RestOutlined, AimOutlined,
|
|
SelectOutlined, FireOutlined, WarningOutlined, ExclamationCircleOutlined
|
|
} from '@ant-design/icons';
|
|
import { Line, Area } from '@ant-design/charts';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { UniversalLineChart } from "../components/recharts"
|
|
|
|
const { Title, Text } = Typography;
|
|
|
|
|
|
const degradationData = [
|
|
{
|
|
date: '2024-01',
|
|
average: 0,
|
|
best: 0,
|
|
worst: -1.0,
|
|
},
|
|
{
|
|
date: '2024-04',
|
|
average: -1.2,
|
|
best: -0.5,
|
|
worst: -2.5,
|
|
},
|
|
{
|
|
date: '2024-07',
|
|
average: -2.5,
|
|
best: -1.0,
|
|
worst: -4.0,
|
|
},
|
|
{
|
|
date: '2024-10',
|
|
average: -3.8,
|
|
best: -1.5,
|
|
worst: -6.5,
|
|
},
|
|
{
|
|
date: '2025-01',
|
|
average: -4.5,
|
|
best: -2.0,
|
|
worst: -8.0,
|
|
},
|
|
{
|
|
date: '2025-05',
|
|
average: -5.32,
|
|
best: -2.5,
|
|
worst: -10.2,
|
|
},
|
|
];
|
|
|
|
const generationPerformanceData = [
|
|
{
|
|
time: '00:00',
|
|
actual: 0,
|
|
predict: 0,
|
|
theory: 0,
|
|
},
|
|
{
|
|
time: '03:00',
|
|
actual: 0,
|
|
predict: 0,
|
|
theory: 0,
|
|
},
|
|
{
|
|
time: '06:00',
|
|
actual: 100,
|
|
predict: 150,
|
|
theory: 200,
|
|
},
|
|
{
|
|
time: '09:00',
|
|
actual: 800,
|
|
predict: 950,
|
|
theory: 1100,
|
|
},
|
|
{
|
|
time: '12:00',
|
|
actual: 1400,
|
|
predict: 1650,
|
|
theory: 1800,
|
|
},
|
|
{
|
|
time: '15:00',
|
|
actual: 1200,
|
|
predict: 1350,
|
|
theory: 1500,
|
|
},
|
|
{
|
|
time: '18:00',
|
|
actual: 400,
|
|
predict: 550,
|
|
theory: 650,
|
|
},
|
|
{
|
|
time: '21:00',
|
|
actual: 0,
|
|
predict: 0,
|
|
theory: 0,
|
|
},
|
|
{
|
|
time: '24:00',
|
|
actual: 0,
|
|
predict: 0,
|
|
theory: 0,
|
|
},
|
|
];
|
|
|
|
|
|
export default function AIDiagnosisPage() {
|
|
const { t } = useTranslation();
|
|
const cardStyle = {
|
|
borderRadius: 12,
|
|
border: '1px solid #f0f0f0',
|
|
boxShadow: '0 4px 12px rgba(0,0,0,0.05)',
|
|
overflow: 'hidden'
|
|
};
|
|
|
|
const aiRecommendations = [
|
|
{ key: '1', measure: t('aiAnalysis.handleHotspotComponents'), priority: t('workOrder.high'), expectedGain: '+2.15%', confidence: '92%' },
|
|
{ key: '2', measure: t('aiAnalysis.checkAbnormalStrings'), priority: t('workOrder.high'), expectedGain: '+1.85%', confidence: '90%' },
|
|
{ key: '3', measure: t('aiAnalysis.cleanShadedAreas'), priority: t('workOrder.medium'), expectedGain: '+0.45%', confidence: '78%' },
|
|
{ key: '4', measure: t('aiAnalysis.trackDegradedComponents'), priority: t('workOrder.medium'), expectedGain: '+0.35%', confidence: '86%' },
|
|
{ key: '5', measure: t('aiAnalysis.monitorInverterEfficiency'), priority: t('workOrder.low'), expectedGain: '+0.10%', confidence: '93%' },
|
|
];
|
|
|
|
const thermalImagingData = [
|
|
{ name: t('aiAnalysis.arrayLabel', { name: 'A-05' }), temp: '71.5℃', id: 'A05', status: 'serious' },
|
|
{ name: t('aiAnalysis.arrayLabel', { name: 'A-12' }), temp: '68.3℃', id: 'A12', status: 'warning' },
|
|
{ name: t('aiAnalysis.arrayLabel', { name: 'B-03' }), temp: '64.8℃', id: 'B03', status: 'warning' },
|
|
{ name: t('aiAnalysis.arrayLabel', { name: 'C-07' }), temp: '67.2℃', id: 'C07', status: 'warning' },
|
|
];
|
|
|
|
const renderStatusTag = (status: string) => {
|
|
let color = '';
|
|
if (status === t('home.abnormal')) color = '#ff4d4f';
|
|
else if (status === t('aiAnalysis.minor')) color = '#1677ff';
|
|
else if (status === t('roles.normal')) color = '#52c41a';
|
|
return <Tag color={color} style={{ margin: 0, fontWeight: 600 }}>{status}</Tag>;
|
|
};
|
|
|
|
// 影响程度标签渲染
|
|
const renderImpactTag = (impact: string) => {
|
|
let color = '';
|
|
if (impact === t('workOrder.high')) color = '#ff4d4f';
|
|
else if (impact === t('workOrder.medium')) color = '#fa8c16';
|
|
else if (impact === t('workOrder.low')) color = '#52c41a';
|
|
return <Tag color={color} style={{ margin: 0, fontWeight: 600 }}>{impact}</Tag>;
|
|
};
|
|
|
|
// 优先级标签渲染(建议措施用)
|
|
const renderPriorityTag = (priority: string) => {
|
|
let color = '';
|
|
if (priority === t('workOrder.high')) color = '#ff4d4f';
|
|
else if (priority === t('workOrder.medium')) color = '#fa8c16';
|
|
else if (priority === t('workOrder.low')) color = '#52c41a';
|
|
return <Tag color={color} style={{ margin: 0, fontWeight: 600 }}>{priority}</Tag>;
|
|
};
|
|
|
|
return (
|
|
<div style={{ background: '#f5f7fa', minHeight: '100vh', padding: '8px 0px', boxSizing: 'border-box', overflowX: 'hidden' }}>
|
|
|
|
<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;
|
|
}
|
|
.thermal-card:hover {
|
|
transform: translateY(-2px);
|
|
transition: all 0.3s;
|
|
}
|
|
.grid-item:hover {
|
|
filter: brightness(0.9);
|
|
cursor: pointer;
|
|
}
|
|
`}
|
|
</style>
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingBottom: "16px" }}>
|
|
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(18px, 1.5vw, 24px)', color: '#1D2129' }}>{t('aiAnalysis.fixed2Ai2')}</Typography.Title>
|
|
<Space>
|
|
<Button icon={<ReloadOutlined />}>{t('aiAnalysis.fixed2Refresh')}</Button>
|
|
<Button type="primary">{t('aiAnalysis.fixed2Export')}</Button>
|
|
</Space>
|
|
</div>
|
|
|
|
{/* 第一排:顶部统计卡片 */}
|
|
<Row gutter={[8, 8]} style={{ marginBottom: 8 }}>
|
|
{[
|
|
{ title: t('aiAnalysis.prValue'), value: '82.6%', trend: t('devices.vsYesterday', { value: '-1.3%' }), icon: <RiseOutlined />, color: '#00B42A', bg: '#E8FFEA', trendColor: '#F53F3F', direction: 'down' },
|
|
{ title: t('aiAnalysis.fixed2GenerationDeviation'), value: '-4.35%', trend: t('devices.vsYesterday', { value: '-1.02%' }), icon: <FallOutlined />, color: '#F53F3F', bg: '#FFF1F0', trendColor: '#F53F3F', direction: 'down' },
|
|
{ title: t('aiAnalysis.fixed2AbnormalStringFixed2QuantityFixed2'), value: '8', trend: t('devices.vsYesterday', { value: '+2 ↑' }), icon: <AppstoreOutlined />, color: '#1D2129', bg: '#F5F7FA', trendColor: '#F53F3F', direction: 'up' },
|
|
{ title: t('aiAnalysis.fixed2HotspotSuspectFixed2QuantityFixed2'), value: '36', trend: t('devices.vsYesterday', { value: '+5 ↑' }), icon: <BellOutlined />, color: '#F53F3F', bg: '#FFF1F0', trendColor: '#F53F3F', direction: 'up' },
|
|
{ title: t('aiAnalysis.s25'), value: '86.3', unit: t('aiAnalysis.scoreUnit'), trend: t('devices.vsYesterday', { value: '-1.7' }), icon: <CheckCircleOutlined />, color: '#00B42A', bg: '#E8FFEA', trendColor: '#F53F3F', direction: 'down' },
|
|
{ title: t('aiAnalysis.fixed2PredictedGeneration'), value: '4,821', unit: t('home.kWh'), trend: t('devices.vsYesterday', { value: '+2.6% ↑' }), icon: <ThunderboltOutlined />, color: '#165DFF', bg: '#E8F3FF', trendColor: '#00B42A', direction: 'up' },
|
|
].map((item, index) => (
|
|
<Col key={index} xs={24} sm={12} md={8} xl={4}>
|
|
<Card
|
|
variant="borderless"
|
|
styles={{ body: { padding: '20px 16px' } }}
|
|
style={{ ...cardStyle, height: '100%' }}
|
|
>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
|
<div style={{ flex: 1 }}>
|
|
<div style={{ fontSize: 'clamp(12px, 0.8vw, 13px)', color: '#86909C', marginBottom: 8, whiteSpace: 'nowrap' }}>{item.title}</div>
|
|
<div style={{ fontSize: 'clamp(20px, 1.4vw, 24px)', fontWeight: 800, color: item.color, display: 'flex', alignItems: 'baseline', gap: 4 }}>
|
|
{item.value}
|
|
{item.unit && <span style={{ fontSize: 13, fontWeight: 400, color: '#86909C' }}>{item.unit}</span>}
|
|
</div>
|
|
<div style={{ fontSize: 11, color: '#86909C', marginTop: 10, display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
<span style={{ color: item.trendColor }}>{item.trend}</span>
|
|
{item.direction === 'up' ? <ArrowUpOutlined style={{ color: item.trendColor }} /> : <ArrowDownOutlined style={{ color: item.trendColor }} />}
|
|
</div>
|
|
</div>
|
|
<div style={{
|
|
width: 44, height: 44, background: item.bg, borderRadius: '12px',
|
|
display: 'flex', justifyContent: 'center', alignItems: 'center', flexShrink: 0,
|
|
boxShadow: `0 4px 10px ${item.bg}80`
|
|
}}>
|
|
{React.cloneElement(item.icon as React.ReactElement, { style: { fontSize: 22, color: item.color === '#1D2129' ? '#4E5969' : item.color } })}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
))}
|
|
</Row>
|
|
|
|
{/* 第二排:热成像分析 和 组串异常定位 */}
|
|
<Row gutter={[12, 12]} style={{ marginBottom: 8 }}>
|
|
<Col xs={24} lg={10}>
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700, color: '#1D2129' }}>{t('aiAnalysis.fixed2UavInspection')}<InfoCircleOutlined style={{ marginLeft: 6, color: '#86909C', fontSize: 13 }} /></span>}
|
|
extra={<Text type="secondary" style={{ fontSize: 12 }}>{t('aiAnalysis.fixed2Inspection202505200930')}<CalendarOutlined style={{ marginLeft: 4 }} /></Text>}
|
|
variant="borderless"
|
|
styles={{ body: { padding: '16px 20px' } }}
|
|
style={{ ...cardStyle, height: '100%' }}
|
|
>
|
|
<Row gutter={[12, 12]}>
|
|
{thermalImagingData.map((item, idx) => (
|
|
<Col span={6} key={idx}>
|
|
<div className="thermal-card" style={{ background: '#000', borderRadius: 8, height: 'clamp(90px, 9vw, 120px)', position: 'relative', overflow: 'hidden', border: '1px solid #333', cursor: 'pointer' }}>
|
|
<div style={{ width: '100%', height: '100%', background: 'linear-gradient(135deg, #1a0000 0%, #7a0000 30%, #ff4d4f 60%, #ffec3d 100%)', opacity: 0.9 }} />
|
|
{/* 模拟热斑点 */}
|
|
<div style={{ position: 'absolute', top: '25%', left: '35%', width: 14, height: 14, border: '2px solid #fff', borderRadius: '50%', boxShadow: '0 0 12px #ff4d4f', animation: 'pulse 2s infinite' }} />
|
|
<div style={{ position: 'absolute', bottom: 6, right: 6, background: 'rgba(0,0,0,0.7)', color: '#fff', padding: '2px 6px', borderRadius: 4, fontSize: 10, fontWeight: 600, letterSpacing: 0.5 }}>
|
|
{item.temp}
|
|
</div>
|
|
<div style={{ position: 'absolute', top: 6, left: 6 }}>
|
|
<Tag color={item.status === 'serious' ? 'error' : 'warning'} style={{ margin: 0, fontSize: 9, padding: '0 4px', lineHeight: '14px' }}>
|
|
{item.status === 'serious' ? t('aiAnalysis.highDanger') : t('aiAnalysis.attention')}
|
|
</Tag>
|
|
</div>
|
|
</div>
|
|
<div style={{ textAlign: 'center', marginTop: 10, fontSize: 12, fontWeight: 500, color: '#4E5969' }}>{item.name}</div>
|
|
</Col>
|
|
))}
|
|
</Row>
|
|
<div style={{ background: '#f8fafc', padding: '12px 16px', borderRadius: 8, marginTop: 20, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 }}>
|
|
<div style={{ display: 'flex', gap: 16, fontSize: 12 }}>
|
|
<span style={{ display: 'flex', alignItems: 'center', color: '#1D2129' }}><AimOutlined style={{ color: '#F53F3F', marginRight: 6 }} />{t('aiAnalysis.fixed2HotspotSuspect')}</span>
|
|
<span style={{ display: 'flex', alignItems: 'center', color: '#1D2129' }}><div style={{ width: 14, height: 10, border: '1.5px dashed #F53F3F', borderRadius: 2, marginRight: 6 }}></div>{t('aiAnalysis.areaFixed2')}</span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
<span style={{ fontSize: 12, color: '#86909C' }}>{t('aiAnalysis.fixed2Temperature2585')}</span>
|
|
<div style={{ width: 120, height: 8, background: 'linear-gradient(to right, #000080, #0000ff, #00ff00, #ffff00, #ff0000)', borderRadius: 4 }} />
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
<Col xs={24} lg={14}>
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700, color: '#1D2129' }}>{t('aiAnalysis.status')}<InfoCircleOutlined style={{ marginLeft: 6, color: '#86909C', fontSize: 13 }} /></span>}
|
|
extra={<Space split={<Divider type="vertical" />} style={{ fontSize: 12, color: '#86909C' }}>
|
|
<span>{t('aiAnalysis.s9')}<b style={{ color: '#1D2129' }}>160</b></span>
|
|
<span>{t('aiAnalysis.fixed2Abnormal')}<b style={{ color: '#F53F3F' }}>8</b></span>
|
|
</Space>}
|
|
variant="borderless"
|
|
styles={{ body: { padding: '16px 20px' } }}
|
|
style={{ ...cardStyle, height: '100%' }}
|
|
>
|
|
<Row gutter={[20, 20]}>
|
|
<Col xs={24} md={18}>
|
|
<div style={{ overflowX: 'auto', paddingBottom: 8 }} className="custom-scrollbar">
|
|
<div style={{ minWidth: 500 }}>
|
|
<div style={{ display: 'grid', gridTemplateColumns: '40px repeat(20, 1fr)', gap: '4px' }}>
|
|
<div></div>
|
|
{Array.from({ length: 20 }).map((_, i) => (
|
|
<div key={i} style={{ fontSize: 11, color: '#86909C', textAlign: 'center', fontWeight: 500 }}>{String(i + 1).padStart(2, '0')}</div>
|
|
))}
|
|
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'].map((row) => (
|
|
<React.Fragment key={row}>
|
|
<div style={{ fontSize: 11, color: '#86909C', display: 'flex', alignItems: 'center', fontWeight: 600 }}>{row}{t('aiAnalysis.rowUnit')}</div>
|
|
{Array.from({ length: 20 }).map((_, col) => {
|
|
let color = '#E8F3FF';
|
|
let icon = null;
|
|
let statusKey = 'normal';
|
|
|
|
if (row === 'B' && col === 3) { color = '#F53F3F'; icon = <div style={{ width: 6, height: 6, background: '#fff', borderRadius: '50%' }} />; statusKey = 'seriousHotspot'; }
|
|
if (row === 'B' && col === 17) { color = '#F53F3F'; icon = <div style={{ width: 6, height: 6, background: '#fff', borderRadius: '50%' }} />; statusKey = 'seriousBreak'; }
|
|
if (row === 'C' && col === 15) { color = '#FF7D00'; icon = <div style={{ width: 6, height: 6, background: '#fff', borderRadius: '50%' }} />; statusKey = 'abnormalPowerDeviation'; }
|
|
if (row === 'E' && col === 4) { color = '#FF7D00'; icon = <div style={{ width: 6, height: 6, background: '#fff', borderRadius: '50%' }} />; statusKey = 'abnormalShading'; }
|
|
if (row === 'F' && col === 11) { color = '#F7BA1E'; icon = <div style={{ width: 6, height: 6, background: '#fff', borderRadius: '50%' }} />; statusKey = 'minor'; }
|
|
if (row === 'G' && col === 1) { color = '#F7BA1E'; icon = <div style={{ width: 6, height: 6, background: '#fff', borderRadius: '50%' }} />; statusKey = 'minor'; }
|
|
if (row === 'H' && col === 10) { color = '#86909C'; statusKey = 'offline'; }
|
|
|
|
const tooltip = t('aiAnalysis.stringCellTooltip', { row, col: col + 1, status: t(`aiAnalysis.stringStatus.${statusKey}`) });
|
|
|
|
return (
|
|
<Tooltip title={tooltip} key={`${row}-${col}`}>
|
|
<div className="grid-item" style={{ width: '100%', height: 18, background: color, borderRadius: 3, display: 'flex', justifyContent: 'center', alignItems: 'center', transition: 'all 0.2s' }}>
|
|
{icon}
|
|
</div>
|
|
</Tooltip>
|
|
);
|
|
})}
|
|
</React.Fragment>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: 16, marginTop: 20, fontSize: 11, flexWrap: 'wrap', background: '#f8fafc', padding: '8px 12px', borderRadius: 6 }}>
|
|
{[
|
|
{ color: '#E8F3FF', label: t('aiAnalysis.normalFixed2') },
|
|
{ color: '#F7BA1E', label: t('aiAnalysis.abnormalFixed22') },
|
|
{ color: '#FF7D00', label: t('aiAnalysis.abnormalFixed2') },
|
|
{ color: '#F53F3F', label: t('aiAnalysis.fixed2CriticalFixed2ErrorFixed2') },
|
|
{ color: '#86909C', label: t('aiAnalysis.fixed2DeviceFixed2OfflineFixed2') },
|
|
].map(item => (
|
|
<span key={item.label} style={{ display: 'flex', alignItems: 'center', color: '#4E5969' }}>
|
|
<span style={{ width: 10, height: 10, background: item.color, marginRight: 6, borderRadius: 2 }}></span>
|
|
{item.label}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</Col>
|
|
<Col xs={24} md={6}>
|
|
<div style={{ background: '#f8fafc', padding: 16, borderRadius: 8, height: '100%' }}>
|
|
<div style={{ fontSize: 13, fontWeight: 700, color: '#1D2129', marginBottom: 16, display: 'flex', alignItems: 'center' }}>
|
|
<WarningOutlined style={{ marginRight: 8, color: '#F53F3F' }} />{t('aiAnalysis.fixed2AbnormalFixed2Type')}</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
|
{[
|
|
{ label: t('aiAnalysis.fixed2CriticalFixed2ErrorFixed2'), value: 2, color: '#F53F3F', percent: '25%' },
|
|
{ label: t('aiAnalysis.abnormalFixed2'), value: 4, color: '#FF7D00', percent: '50%' },
|
|
{ label: t('aiAnalysis.abnormalFixed22'), value: 2, color: '#F7BA1E', percent: '25%' },
|
|
{ label: t('aiAnalysis.fixed2Offline'), value: 0, color: '#86909C', percent: '0%' },
|
|
].map(item => (
|
|
<div key={item.label}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
|
|
<span style={{ fontSize: 12, color: '#4E5969' }}>{item.label}</span>
|
|
<span style={{ fontWeight: 700, color: item.color, fontSize: 13 }}>{item.value}</span>
|
|
</div>
|
|
<div style={{ width: '100%', height: 4, background: '#E5E6EB', borderRadius: 2 }}>
|
|
<div style={{ width: item.percent, height: '100%', background: item.color, borderRadius: 2 }}></div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<Divider style={{ margin: '16px 0' }} />
|
|
<Button block type="dashed" size="small" style={{ fontSize: 12 }}>{t('aiAnalysis.fixed2View')}</Button>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
|
|
{/* 第三排:三个主要分析区域 */}
|
|
<Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
|
|
<Col xs={24} lg={7}>
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700, color: '#1D2129' }}>{t('aiAnalysis.s13')}<InfoCircleOutlined style={{ marginLeft: 6, color: '#86909C', fontSize: 13 }} /></span>}
|
|
variant="borderless"
|
|
styles={{ body: { padding: '16px 20px' } }}
|
|
style={{ ...cardStyle, height: '100%' }}
|
|
>
|
|
<div style={{ fontSize: 12, color: '#86909C', marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
|
|
<span>{t('aiAnalysis.degradationTrend')}</span>
|
|
<span style={{ color: '#F53F3F', fontWeight: 600 }}>{t('aiAnalysis.fixed2Current532')}</span>
|
|
</div>
|
|
<UniversalLineChart
|
|
data={degradationData}
|
|
xKey="date"
|
|
height={240}
|
|
smooth
|
|
dot
|
|
showGrid
|
|
showLegend
|
|
series={[
|
|
{
|
|
key: 'average',
|
|
name: t('aiAnalysis.averageDegradationRate'),
|
|
color: '#165DFF',
|
|
strokeWidth: 3,
|
|
},
|
|
{
|
|
key: 'best',
|
|
name: t('aiAnalysis.bestComponent'),
|
|
color: '#00B42A',
|
|
strokeWidth: 2,
|
|
},
|
|
{
|
|
key: 'worst',
|
|
name: t('aiAnalysis.worstComponent'),
|
|
color: '#F53F3F',
|
|
strokeWidth: 2,
|
|
},
|
|
]}
|
|
/> <div style={{ marginTop: 16, padding: '12px', background: '#FFF7E8', borderRadius: 8, border: '1px solid #FFE4BA' }}>
|
|
<div style={{ fontSize: 12, color: '#86909C', display: 'flex', alignItems: 'center' }}>
|
|
<ExclamationCircleOutlined style={{ color: '#FF7D00', marginRight: 6 }} />
|
|
<span style={{ color: '#1D2129', fontWeight: 600 }}>{t('aiAnalysis.s15')}</span>
|
|
<span style={{ marginLeft: 4 }}>{t('aiAnalysis.12')}</span>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
|
|
<Col xs={24} lg={10}>
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700, color: '#1D2129' }}>{t('aiAnalysis.generation')}<InfoCircleOutlined style={{ marginLeft: 6, color: '#86909C', fontSize: 13 }} /></span>}
|
|
variant="borderless"
|
|
styles={{ body: { padding: '16px 20px' } }}
|
|
style={{ ...cardStyle, height: '100%' }}
|
|
>
|
|
<div style={{ display: 'flex', gap: 20 }}>
|
|
<div style={{ flex: 1 }}>
|
|
<div style={{ fontSize: 12, color: '#86909C', marginBottom: 16 }}>{t('aiAnalysis.generationComparison')}</div>
|
|
<UniversalLineChart
|
|
data={generationPerformanceData}
|
|
xKey="time"
|
|
height={240}
|
|
smooth
|
|
dot={false}
|
|
showGrid
|
|
showLegend
|
|
series={[
|
|
{
|
|
key: 'actual',
|
|
name: t('aiAnalysis.actualGeneration'),
|
|
color: '#165DFF',
|
|
strokeWidth: 3,
|
|
},
|
|
{
|
|
key: 'predict',
|
|
name: t('aiAnalysis.predictedGeneration'),
|
|
color: '#00B42A',
|
|
strokeWidth: 2,
|
|
},
|
|
{
|
|
key: 'theory',
|
|
name: t('aiAnalysis.theoreticalGeneration'),
|
|
color: '#86909C',
|
|
strokeWidth: 2,
|
|
},
|
|
]}
|
|
/> </div>
|
|
<div style={{ width: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 16, flexShrink: 0, paddingLeft: 16, borderLeft: '1px solid #f0f0f0' }}>
|
|
{[
|
|
{ label: t('aiAnalysis.actualFixed2GenerationFixed2'), value: '4,752', unit: t('home.kWh'), color: '#165DFF' },
|
|
{ label: t('aiAnalysis.predictedGenerationFixed2'), value: '4,960', unit: t('home.kWh'), color: '#00B42A' },
|
|
{ label: t('aiAnalysis.fixed2GenerationDeviation'), value: '-4.35%', unit: '', color: '#F53F3F' },
|
|
{ label: t('aiAnalysis.pr'), value: '82.6%', unit: '', color: '#1D2129' },
|
|
].map(item => (
|
|
<div key={item.label}>
|
|
<div style={{ color: '#86909C', marginBottom: 4, fontSize: 11 }}>{item.label}</div>
|
|
<div style={{ fontWeight: 800, color: item.color, fontSize: 15 }}>{item.value} <span style={{ fontSize: 10, fontWeight: 400, color: '#86909C' }}>{item.unit}</span></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
|
|
<Col xs={24} lg={7}>
|
|
<Card
|
|
variant="borderless"
|
|
styles={{ body: { padding: 0 } }}
|
|
style={{ ...cardStyle, height: '100%' }}
|
|
>
|
|
{/* AI诊断总结 */}
|
|
<div style={{ padding: '12px 14px' }}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
|
|
<span style={{ fontSize: 14, fontWeight: 700, color: '#1D2129' }}>{t('aiAnalysis.fixed2Ai2')}</span>
|
|
<span style={{ fontSize: 12, color: '#86909C' }}>{t('aiAnalysis.fixed2UpdatedAt202505200945')}</span>
|
|
</div>
|
|
<Table
|
|
rowKey="key"
|
|
columns={[
|
|
{ title: t('aiAnalysis.s36'), dataIndex: 'diagnosis', width: 78 },
|
|
{ title: t('roles.status'), dataIndex: 'status', render: renderStatusTag, },
|
|
{ title: t('aiAnalysis.confidence'), dataIndex: 'confidence', },
|
|
{ title: t('aiAnalysis.s37'), dataIndex: 'impact', render: renderImpactTag, width: 52 },
|
|
{ title: t('common.detail'), dataIndex: 'detail', ellipsis: true },
|
|
]}
|
|
dataSource={[
|
|
{ key: '1', diagnosis: t('aiAnalysis.hotspotDetection'), status: t('home.abnormal'), confidence: '92%', impact: t('workOrder.high'), detail: t('aiAnalysis.hotspotDetail') },
|
|
{ key: '2', diagnosis: t('aiAnalysis.stringMismatch'), status: t('home.abnormal'), confidence: '90%', impact: t('workOrder.high'), detail: t('aiAnalysis.stringMismatchDetail', { count: 8 }) },
|
|
{ key: '3', diagnosis: t('aiAnalysis.shadingRisk'), status: t('aiAnalysis.minor'), confidence: '78%', impact: t('workOrder.medium'), detail: t('aiAnalysis.shadingDetail') },
|
|
{ key: '4', diagnosis: t('aiAnalysis.componentDegradation'), status: t('home.abnormal'), confidence: '86%', impact: t('workOrder.medium'), detail: t('aiAnalysis.degradationDetail', { value: '-5.32%' }) },
|
|
{ key: '5', diagnosis: t('aiAnalysis.inverterAbnormal'), status: t('roles.normal'), confidence: '93%', impact: t('workOrder.low'), detail: t('aiAnalysis.inverterNormalDetail') },
|
|
]}
|
|
pagination={false}
|
|
size="small"
|
|
style={{ fontSize: 12 }}
|
|
/>
|
|
</div>
|
|
|
|
<Divider style={{ margin: 0 }} />
|
|
|
|
<div style={{ padding: '12px 14px' }}>
|
|
<div style={{ fontSize: 14, fontWeight: 700, color: '#1D2129', marginBottom: 10 }}>{t('aiAnalysis.fixed2Ai')}</div>
|
|
<Table
|
|
rowKey="key"
|
|
columns={[
|
|
{ title: t('aiAnalysis.s39'), dataIndex: 'measure', ellipsis: true },
|
|
{ title: t('workOrder.priority'), dataIndex: 'priority', render: renderPriorityTag, width: 58 },
|
|
{ title: t('aiAnalysis.s40'), dataIndex: 'gain', render: g => <span style={{ color: '#00B42A', fontWeight: 600 }}>{g}</span>, width: 68 },
|
|
{ title: t('aiAnalysis.confidence'), dataIndex: 'confidence', width: 60 },
|
|
{
|
|
title: t('roles.actions'),
|
|
key: 'action',
|
|
width: 76,
|
|
render: (_, record) => (
|
|
<Button
|
|
type="primary"
|
|
size="small"
|
|
style={{ fontSize: 11, borderRadius: 4, height: 22, padding: '0 6px' }}
|
|
>
|
|
{record.isView ? t('common.detail') : t('workOrder.dispatch')}
|
|
</Button>
|
|
)
|
|
},
|
|
]}
|
|
dataSource={[
|
|
{ key: '1', measure: t('aiAnalysis.handleHotspotComponents'), priority: t('workOrder.high'), gain: '+2.15%', confidence: '92%', isView: false },
|
|
{ key: '2', measure: t('aiAnalysis.checkAbnormalStrings'), priority: t('workOrder.high'), gain: '+1.85%', confidence: '90%', isView: false },
|
|
{ key: '3', measure: t('aiAnalysis.cleanShadedAreas'), priority: t('workOrder.medium'), gain: '+0.45%', confidence: '78%', isView: false },
|
|
{ key: '4', measure: t('aiAnalysis.trackDegradedComponents'), priority: t('workOrder.medium'), gain: '+0.35%', confidence: '86%', isView: false },
|
|
{ key: '5', measure: t('aiAnalysis.monitorInverterEfficiency'), priority: t('workOrder.low'), gain: '+0.10%', confidence: '93%', isView: true },
|
|
]}
|
|
pagination={false}
|
|
size="small"
|
|
style={{ fontSize: 12 }}
|
|
/>
|
|
</div>
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
|
|
{/* 底部:数据来源 */}
|
|
<Card
|
|
title={<span style={{ fontSize: 15, fontWeight: 700, color: '#1D2129' }}>{t('aiAnalysis.sourceFixed2')}</span>}
|
|
variant="borderless"
|
|
styles={{ body: { padding: '16px 20px' } }}
|
|
style={cardStyle}
|
|
>
|
|
<Row gutter={[16, 16]}>
|
|
{[
|
|
{ title: t('aiAnalysis.fixed2Uav'), time: '2025-05-20 09:30', icon: <ScanOutlined />, status: t('aiAnalysis.syncing') },
|
|
{ title: t('aiAnalysis.scada'), time: '2025-05-20 09:45:12', icon: <DatabaseOutlined />, status: t('roles.normal') },
|
|
{ title: t('aiAnalysis.s43'), time: '2025-05-20 09:45:00', icon: <CloudOutlined />, status: t('roles.normal') },
|
|
{ title: t('aiAnalysis.oM'), time: t('aiAnalysis.lastUpdated', { value: '2025-05-18' }), icon: <RestOutlined />, status: t('roles.normal') },
|
|
].map((source, idx) => (
|
|
<Col xs={24} sm={12} md={6} key={idx}>
|
|
<div style={{ background: '#f8fafc', padding: '16px', borderRadius: 12, display: 'flex', alignItems: 'center', gap: 16, border: '1px solid #f0f0f0', transition: 'all 0.3s' }} className="thermal-card">
|
|
<div style={{ width: 48, height: 48, background: '#fff', borderRadius: 12, display: 'flex', justifyContent: 'center', alignItems: 'center', fontSize: 24, color: '#165DFF', boxShadow: '0 4px 10px rgba(0,0,0,0.05)' }}>
|
|
{source.icon}
|
|
</div>
|
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
<div style={{ fontSize: 14, fontWeight: 700, color: '#1D2129' }}>{source.title}</div>
|
|
<div style={{ fontSize: 12, color: '#86909C', marginTop: 4, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t('aiAnalysis.dataPoint', { time: source.time })}</div>
|
|
<div style={{ fontSize: 11, color: source.status === t('roles.normal') ? '#00B42A' : '#165DFF', marginTop: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
<div style={{ width: 6, height: 6, background: source.status === t('roles.normal') ? '#00B42A' : '#165DFF', borderRadius: '50%' }}></div>
|
|
{t('aiAnalysis.statusLabel', { status: source.status })}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
))}
|
|
</Row>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|