This commit is contained in:
mmc
2026-05-11 12:40:31 +08:00
parent 0bd1fc42d3
commit 020023c8f5
3 changed files with 888 additions and 778 deletions

View File

@@ -59,6 +59,10 @@
.ant-select-selector{
border-radius:8px !important;
}
.ant-btn{
border-radius:8px !important;
}
@keyframes sunRotate {

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,47 @@
import React, { useState } from 'react';
import {
Layout, Card, Row, Col, Button, Table, Tag, Select, Input, Typography, Space,
Statistic, message, Drawer, Descriptions, Timeline, Divider, Form, Modal
Statistic, Modal, message, Empty, Drawer, Descriptions, Timeline, Divider, Form, Tabs, Progress, Badge, DatePicker, Pagination
} from 'antd';
import {
PlusOutlined, SearchOutlined, EditOutlined, EyeOutlined,
ClockCircleOutlined, CheckCircleOutlined, PlayCircleOutlined,
CloseCircleOutlined, ThunderboltOutlined, SettingOutlined,
SendOutlined, PauseOutlined, ArrowRightOutlined, FileDoneOutlined,
RobotOutlined, CloudOutlined, RiseOutlined,
BranchesOutlined
CheckOutlined, ClockCircleOutlined, ControlOutlined,
BarChartOutlined, RiseOutlined, ArrowUpOutlined,
SearchOutlined, PlusOutlined, EditOutlined, DeleteOutlined,
EyeOutlined, CommentOutlined, PauseOutlined, FormOutlined,
ReloadOutlined, FilterOutlined, MoreOutlined,
CheckCircleFilled, PlayCircleFilled, InfoCircleFilled,
RobotOutlined, ThunderboltOutlined, AlertOutlined
} from '@ant-design/icons';
import { Pie, Line, Bar } from '@ant-design/charts';
import dayjs from 'dayjs';
import { Pie, Line } from '@ant-design/charts';
import {
ClipboardList,
Clock,
Play,
CheckCircle,
Timer,
Cpu,
Calendar as CalendarIcon,
ChevronRight,
History,
ExternalLink
} from 'lucide-react';
const { Option } = Select;
const { Search } = Input;
const { Text, Title } = Typography;
const { RangePicker } = DatePicker;
// 模拟数据(完全匹配参考图)
const workOrderList = [
// 模拟数据
const workOrderData = [
{
id: 'WO20250524001',
title: '逆变器 INV-05 告警排查',
source: 'AI诊断',
type: '检修',
priority: 'high',
status: 'pending',
handler: '李工',
deadline: '2025-05-24 14:30',
status: 'pending',
description: ''
},
{
id: 'WO20250524002',
@@ -37,10 +49,9 @@ const workOrderList = [
source: '无人机巡检',
type: '巡检',
priority: 'high',
status: 'processing',
handler: '王工',
deadline: '2025-05-24 15:00',
status: 'processing',
description: '对AI识别热斑区域进行复检与红外复核,确认是否需要更换组件。'
},
{
id: 'WO20250524003',
@@ -48,10 +59,9 @@ const workOrderList = [
source: '清洗优化',
type: '清洗',
priority: 'medium',
status: 'dispatched',
handler: '赵工',
deadline: '2025-05-24 16:00',
status: 'dispatched',
description: ''
},
{
id: 'WO20250524004',
@@ -59,10 +69,9 @@ const workOrderList = [
source: '视频监控',
type: '维修',
priority: 'medium',
status: 'processing',
handler: '陈工',
deadline: '2025-05-24 17:30',
status: 'processing',
description: ''
},
{
id: 'WO20250524005',
@@ -70,10 +79,9 @@ const workOrderList = [
source: '装备控制',
type: '运维',
priority: 'low',
status: 'pending',
handler: '刘工',
deadline: '2025-05-24 18:00',
status: 'pending',
description: ''
},
{
id: 'WO20250524006',
@@ -81,78 +89,46 @@ const workOrderList = [
source: '人工创建',
type: '巡检',
priority: 'medium',
status: 'completed',
handler: '张运维',
deadline: '2025-05-24 21:00',
status: 'completed',
description: ''
}
];
// 工单状态配置
const statusConfig = {
pending: { label: '待处理', color: '#1890ff', icon: <ClockCircleOutlined /> },
dispatched: { label: '已派发', color: '#52c41a', icon: <SendOutlined /> },
processing: { label: '执行中', color: '#2f54eb', icon: <PlayCircleOutlined /> },
suspended: { label: '挂起', color: '#faad14', icon: <PauseOutlined /> },
completed: { label: '已完成', color: '#52c41a', icon: <CheckCircleOutlined /> },
closed: { label: '已关闭', color: '#8c8c8c', icon: <CloseCircleOutlined /> }
pending: { label: '待处理', color: '#52c41a', textColor: '#1890ff', bg: '#e6f7ff', border: '#91d5ff' },
dispatched: { label: '已派发', color: '#52c41a', textColor: '#52c41a', bg: '#f6ffed', border: '#b7eb8f' },
processing: { label: '执行中', color: '#1890ff', textColor: '#1890ff', bg: '#e6f7ff', border: '#91d5ff' },
completed: { label: '已完成', color: '#52c41a', textColor: '#52c41a', bg: '#f6ffed', border: '#b7eb8f' },
suspended: { label: '已挂起', color: '#faad14', textColor: '#faad14', bg: '#fff7e6', border: '#ffe7ba' }
};
// 优先级配置
const priorityConfig = {
low: { label: '低', color: '#52c41a' },
medium: { label: '中', color: '#faad14' },
high: { label: '高', color: '#ff4d4f' },
urgent: { label: '紧急', color: '#f5222d' }
low: { label: '低', color: '#52c41a', bg: '#f6ffed' },
medium: { label: '中', color: '#faad14', bg: '#fff7e6' },
high: { label: '高', color: '#ff4d4f', bg: '#fff1f0' }
};
// 统计卡片数据
const overviewCards = [
{ title: '今日新增工单', value: 18, suffix: '单', desc: '较昨日 +3', color: '#1890ff', icon: <FileDoneOutlined /> },
{ title: '待处理工单', value: 12, suffix: '单', desc: '高优先级 5', color: '#ff4d4f', icon: <ClockCircleOutlined /> },
{ title: '执行中工单', value: 9, suffix: '单', desc: '按时率 92%', color: '#52c41a', icon: <PlayCircleOutlined /> },
{ title: '已完成工单', value: 126, suffix: '单', desc: '本月累计', color: '#52c41a', icon: <CheckCircleOutlined /> },
{ title: '平均处理时长', value: 4.3, suffix: '小时', desc: '较上周 -12% ↓', color: '#1890ff', icon: <ClockCircleOutlined /> },
{ title: 'AI建议生成', value: 7, suffix: '单', desc: '今日自动生成', color: '#722ed1', icon: <RobotOutlined /> }
];
const WorkOrderPage = () => {
const [activeTab, setActiveTab] = useState('1');
const [selectedRowKey, setSelectedRowKey] = useState('WO20250524002');
// 任务排班数据
const scheduleData = [
{ handler: '李工', tasks: [{ title: 'INV-05 告警排查', time: '08:30 - 12:00', type: 'alarm' }] },
{ handler: '王工', tasks: [{ title: 'B-12 热斑复检', time: '13:30 - 15:00', type: 'inspect' }] },
{ handler: '赵工', tasks: [{ title: '组件清洗 C-05', time: '09:00 - 12:00', type: 'clean' }, { title: '清洗效果复检', time: '15:00 - 17:00', type: 'clean' }] },
{ handler: '陈工', tasks: [{ title: '周界摄像头修复', time: '10:00 - 12:30', type: 'repair' }, { title: '设备巡检', time: '15:30 - 17:30', type: 'inspect' }] }
];
// 统计数据
const stats = [
{ title: '今日新增工单', value: 18, subValue: '+3', subText: '较昨日', icon: <ClipboardList size={24} color="#1890ff" />, bg: '#e6f7ff' },
{ title: '待处理工单', value: 12, subValue: '5', subText: '高优先级', icon: <Clock size={24} color="#ff4d4f" />, bg: '#fff1f0' },
{ title: '执行中工单', value: 9, subValue: '92%', subText: '按时率', icon: <Play size={24} color="#52c41a" />, bg: '#f6ffed' },
{ title: '已完成工单', value: 126, subValue: '', subText: '本月累计', icon: <CheckCircle size={24} color="#52c41a" />, bg: '#f6ffed' },
{ title: '平均处理时长', value: 4.3, unit: '小时', subValue: '-12%', subText: '较上周', icon: <Timer size={24} color="#1890ff" />, bg: '#e6f7ff' },
{ title: 'AI建议生成', value: 7, unit: '单', subValue: '', subText: '今日自动生成', icon: <Cpu size={24} color="#722ed1" />, bg: '#f9f0ff' },
];
// 告警趋势数据
const alarmTrendData = [
{ date: '05-18', count: 5 },
{ date: '05-19', count: 10 },
{ date: '05-20', count: 5 },
{ date: '05-21', count: 5 },
{ date: '05-22', count: 12 },
{ date: '05-23', count: 10 },
{ date: '05-24', count: 12 }
];
// 告警列表数据
const alarmList = [
{ name: 'INV-05 逆变器告警', time: '05-24 10:24' },
{ name: 'B-12 区块热斑告警', time: '05-24 09:56' },
{ name: 'B-12 组件温度异常', time: '05-24 09:32' }
];
export default function WorkOrderPage() {
const [selectedOrder, setSelectedOrder] = useState(workOrderList[1]); // 默认选中执行中的工单
const [drawerVisible, setDrawerVisible] = useState(false);
const [editModalVisible, setEditModalVisible] = useState(false);
const [editingOrder, setEditingOrder] = useState(null);
const [editForm] = Form.useForm();
// 表格列定义
// 表格列
const columns = [
{ title: '工单编号', dataIndex: 'id', key: 'id', width: 140 },
{ title: '工单标题', dataIndex: 'title', key: 'title' },
{ title: '工单编号', dataIndex: 'id', key: 'id', width: 130 },
{ title: '工单标题', dataIndex: 'title', key: 'title', width: 180, ellipsis: true },
{ title: '来源', dataIndex: 'source', key: 'source', width: 100 },
{ title: '类型', dataIndex: 'type', key: 'type', width: 80 },
{
@@ -160,62 +136,89 @@ export default function WorkOrderPage() {
dataIndex: 'priority',
key: 'priority',
width: 80,
render: (priority) => (
<Tag color={priorityConfig[priority]?.color} style={{ borderRadius: 4 }}>
{priorityConfig[priority]?.label}
render: (p) => (
<Tag color={priorityConfig[p].bg} style={{ color: priorityConfig[p].color, border: `1px solid ${priorityConfig[p].color}`, borderRadius: 4 }}>
{priorityConfig[p].label}
</Tag>
)
},
{ title: '负责人', dataIndex: 'handler', key: 'handler', width: 80 },
{ title: '截止时间', dataIndex: 'deadline', key: 'deadline', width: 160 },
{ title: '截止时间', dataIndex: 'deadline', key: 'deadline', width: 150 },
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (status) => (
<Tag color={statusConfig[status]?.color} style={{ borderRadius: 4 }}>
{statusConfig[status]?.label}
</Tag>
width: 90,
render: (s) => (
<span style={{ color: statusConfig[s].textColor }}>{statusConfig[s].label}</span>
)
},
{
title: '操作',
key: 'action',
width: 80,
render: (_, record) => (
<Button type="link" size="small" onClick={() => { setSelectedOrder(record); setDrawerVisible(true); }}>
查看
</Button>
)
}
width: 60,
render: () => <Button type="link" size="small">查看</Button>
},
];
// 工单状态分布饼图数据
// 图表数据
const pieData = [
{ type: '待处理', value: 12, color: '#1890ff' },
{ type: '执行中', value: 9, color: '#2f54eb' },
{ type: '已完成', value: 126, color: '#52c41a' },
{ type: '已挂起', value: 4, color: '#faad14' }
{ type: '待处理', value: 12 },
{ type: '执行中', value: 9 },
{ type: '已完成', value: 126 },
{ type: '已挂起', value: 4 },
];
const alarmTrendData = [
{ date: '05-18', value: 5 },
{ date: '05-19', value: 10 },
{ date: '05-20', value: 7 },
{ date: '05-21', value: 12 },
{ date: '05-22', value: 8 },
{ date: '05-23', value: 15 },
{ date: '05-24', value: 11 },
];
return (
<div style={{ padding: 20, background: '#f5f7fa', minHeight: '100vh' }}>
{/* 顶部统计卡片 */}
<Row gutter={16} style={{ marginBottom: 20 }}>
{overviewCards.map((card, idx) => (
<Col span={4} key={idx}>
<Card bordered={false} style={{ borderRadius: 8, height: 120 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ padding: '16px 24px', background: '#f0f2f5', minHeight: '100vh' }}>
{/* 顶部标题和页签 */}
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}>
<Title level={4} style={{ margin: '0 32px 0 0' }}>工单任务</Title>
<Tabs
activeKey={activeTab}
onChange={setActiveTab}
items={[
{ key: '1', label: '工单总览' },
{ key: '2', label: '待处理工单' },
{ key: '3', label: '执行中' },
{ key: '4', label: '已完成' },
{ key: '5', label: '工单模板' },
]}
tabBarStyle={{ marginBottom: 0 }}
/>
</div>
{/* 统计卡片 */}
<Row gutter={16} style={{ marginBottom: 16 }}>
{stats.map((item, index) => (
<Col span={4} key={index}>
<Card bordered={false} bodyStyle={{ padding: '16px' }} style={{ borderRadius: 8 }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<div style={{ fontSize: 14, color: '#666' }}>{card.title}</div>
<div style={{ fontSize: 28, fontWeight: 700, color: card.color, margin: '8px 0' }}>
{card.value} <span style={{ fontSize: 14, fontWeight: 400 }}>{card.suffix}</span>
<Text type="secondary" style={{ fontSize: 13 }}>{item.title}</Text>
<div style={{ margin: '4px 0' }}>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>{item.value}</Text>
{item.unit && <Text style={{ fontSize: 14, marginLeft: 4 }}>{item.unit}</Text>}
</div>
<div style={{ fontSize: 12 }}>
<Text type="secondary">{item.subText}</Text>
<Text style={{ marginLeft: 4, color: item.subValue.startsWith('+') ? '#52c41a' : item.subValue.startsWith('-') ? '#ff4d4f' : '#1890ff', fontWeight: 'bold' }}>
{item.subValue}
</Text>
</div>
<div style={{ fontSize: 12, color: '#999' }}>{card.desc}</div>
</div>
<div style={{ width: 48, height: 48, borderRadius: '50%', background: `${card.color}15`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: card.color, fontSize: 20 }}>
{card.icon}
<div style={{ width: 48, height: 48, borderRadius: '50%', background: item.bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{item.icon}
</div>
</div>
</Card>
@@ -224,109 +227,151 @@ export default function WorkOrderPage() {
</Row>
{/* 筛选栏 */}
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 20, gap: 16, flexWrap: 'wrap' }}>
<Select placeholder="工单类型" style={{ width: 120 }} defaultValue="all">
<Option value="all">全部</Option>
<Option value="inspect">巡检</Option>
<Option value="repair">维修</Option>
<Option value="clean">清洗</Option>
</Select>
<Select placeholder="优先级" style={{ width: 120 }} defaultValue="all">
<Option value="all">全部</Option>
<Option value="high">高</Option>
<Option value="medium">中</Option>
<Option value="low">低</Option>
</Select>
<Select placeholder="负责人" style={{ width: 120 }} defaultValue="all">
<Option value="all">全部</Option>
<Option value="李工">李工</Option>
<Option value="王工">王工</Option>
<Option value="赵工">赵工</Option>
</Select>
<Space>
<span>时间范围</span>
<Input style={{ width: 180 }} value="2025-05-24 ~ 2025-05-24" readOnly />
</Space>
<Select placeholder="状态" style={{ width: 120 }} defaultValue="all">
<Option value="all">全部</Option>
<Option value="pending">待处理</Option>
<Option value="processing">执行中</Option>
<Option value="completed">已完成</Option>
</Select>
<Input placeholder="请输入工单编号/标题" style={{ width: 240 }} prefix={<SearchOutlined />} />
<Space>
<Button>重置</Button>
<Button type="primary">查询</Button>
</Space>
</div>
<Card bordered={false} bodyStyle={{ padding: '12px 16px' }} style={{ borderRadius: 8, marginBottom: 16 }}>
<Row gutter={16} align="middle">
<Col span={3}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ marginRight: 8, whiteSpace: 'nowrap' }}>工单类型</Text>
<Select defaultValue="all" style={{ width: '100%' }}>
<Option value="all">全部</Option>
</Select>
</div>
</Col>
<Col span={3}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ marginRight: 8, whiteSpace: 'nowrap' }}>优先级</Text>
<Select defaultValue="all" style={{ width: '100%' }}>
<Option value="all">全部</Option>
</Select>
</div>
</Col>
<Col span={3}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ marginRight: 8, whiteSpace: 'nowrap' }}>负责人</Text>
<Select defaultValue="all" style={{ width: '100%' }}>
<Option value="all">全部</Option>
</Select>
</div>
</Col>
<Col span={5}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ marginRight: 8, whiteSpace: 'nowrap' }}>时间范围</Text>
<RangePicker style={{ width: '100%' }} />
</div>
</Col>
<Col span={3}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ marginRight: 8, whiteSpace: 'nowrap' }}>状态</Text>
<Select defaultValue="all" style={{ width: '100%' }}>
<Option value="all">全部</Option>
</Select>
</div>
</Col>
<Col span={4}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ marginRight: 8, whiteSpace: 'nowrap' }}>关键词</Text>
<Input placeholder="请输入工单编号/标题" suffix={<SearchOutlined />} />
</div>
</Col>
<Col span={3} style={{ textAlign: 'right' }}>
<Space>
<Button>重置</Button>
<Button type="primary">查询</Button>
</Space>
</Col>
</Row>
</Card>
<Row gutter={16}>
{/* 左侧工单列表 */}
<Col span={14}>
<Card bordered={false} style={{ borderRadius: 8, marginBottom: 20 }} title="工单列表(共 147 条)">
{/* 左侧:列表 + 底部图表 */}
<Col span={15}>
<Card
bordered={false}
title={<div style={{ display: 'flex', alignItems: 'center' }}><Text strong style={{ fontSize: 16 }}>工单列表</Text><Text type="secondary" style={{ fontSize: 12, marginLeft: 8 }}>(共 147 条)</Text></div>}
bodyStyle={{ padding: 0 }}
style={{ borderRadius: 8, marginBottom: 16 }}
>
<Table
rowKey="id"
dataSource={workOrderData}
columns={columns}
dataSource={workOrderList}
pagination={{ pageSize: 15, showSizeChanger: true, showTotal: (total) => `共 ${total} 条` }}
pagination={false}
size="middle"
rowKey="id"
scroll={{ x: 'max-content' }}
rowClassName={(record) => record.id === selectedOrder?.id ? 'selected-row' : ''}
onRow={(record) => ({ onClick: () => setSelectedOrder(record) })}
rowSelection={{
type: 'radio',
selectedRowKeys: [selectedRowKey],
onChange: (keys) => setSelectedRowKey(keys[0] as string)
}}
/>
<style>{`
.selected-row { background-color: #e6f7ff !important; }
`}</style>
<div style={{ padding: '16px', textAlign: 'right' }}>
<Pagination size="small" total={147} showSizeChanger showQuickJumper />
</div>
</Card>
{/* 工单状态分布 + 任务排班 */}
<Row gutter={16}>
<Col span={8}>
<Card bordered={false} style={{ borderRadius: 8 }} title="工单状态分布">
<Pie
data={pieData}
angleField="value"
colorField="type"
color={['#1890ff', '#2f54eb', '#52c41a', '#faad14']}
radius={0.8}
innerRadius={0.6}
height={180}
label={{
formatter: (datum) => `${datum.value} (${((datum.value / 147) * 100).toFixed(2)}%)`
}}
interactions={[{ type: 'pie-legend-active' }]}
/>
<div style={{ textAlign: 'center', marginTop: -120 }}>
<div style={{ fontSize: 16, fontWeight: 600 }}>总数</div>
<div style={{ fontSize: 24, fontWeight: 700 }}>147</div>
<Col span={10}>
<Card bordered={false} title="工单状态分布" style={{ borderRadius: 8, height: 360 }}>
<div style={{ height: 280, position: 'relative' }}>
<Pie
data={pieData}
angleField="value"
colorField="type"
radius={1}
innerRadius={0.6}
label={false}
legend={{
position: 'right',
layout: 'vertical',
itemValueLabel: {
formatter: (text, item) => {
const total = pieData.reduce((a, b) => a + b.value, 0);
const percent = ((item.value / total) * 100).toFixed(2);
return `${item.value} (${percent}%)`;
}
}
}}
height={240}
/>
<div style={{ position: 'absolute', top: '42%', left: '30%', textAlign: 'center', transform: 'translate(-50%, -50%)' }}>
<div style={{ fontSize: 14, color: '#8c8c8c' }}>总数</div>
<div style={{ fontSize: 24, fontWeight: 'bold' }}>147</div>
</div>
</div>
</Card>
</Col>
<Col span={16}>
<Card bordered={false} style={{ borderRadius: 8 }} title={<div style={{ display: 'flex', justifyContent: 'space-between' }}><span>任务排班 / 日历视图</span><Button type="link">更多 </Button></div>}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}>
<Button type="text" icon={<ArrowRightOutlined style={{ transform: 'rotate(180deg)' }} />} />
<span style={{ margin: '0 16px' }}>2025-05-24 (今天)</span>
<Button type="text" icon={<ArrowRightOutlined />} />
<Col span={14}>
<Card
bordered={false}
title="任务排班 / 日历视图"
extra={<Button type="link" size="small">更多 <ChevronRight size={14} /></Button>}
style={{ borderRadius: 8, height: 360 }}
>
<div style={{ marginBottom: 12 }}>
<Space>
<Button size="small" icon={<ChevronRight style={{ transform: 'rotate(180deg)' }} size={14} />} />
<Text strong>2025-05-24 (今天)</Text>
<Button size="small" icon={<ChevronRight size={14} />} />
</Space>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
{scheduleData.map((item, idx) => (
<div key={idx} style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: 60, textAlign: 'right' }}>{item.handler}</div>
<div style={{ flex: 1, display: 'flex', gap: 16, height: 40, alignItems: 'center', position: 'relative' }}>
<div style={{ position: 'absolute', left: 0, right: 0, height: 1, background: '#e8e8e8', zIndex: 0 }} />
{item.tasks.map((task, i) => (
<div key={i} style={{
background: task.type === 'alarm' ? '#e6ffed' : task.type === 'inspect' ? '#e6f7ff' : task.type === 'clean' ? '#f9f0ff' : '#fff7e6',
border: '1px solid #d9d9d9',
borderRadius: 4,
padding: '4px 8px',
fontSize: 12,
zIndex: 1
}}>
<div>{task.title}</div>
<div style={{ color: '#666' }}>{task.time}</div>
</div>
))}
<div style={{ border: '1px solid #f0f0f0', borderRadius: 4, overflow: 'hidden' }}>
<div style={{ display: 'flex', background: '#fafafa', borderBottom: '1px solid #f0f0f0' }}>
<div style={{ width: 60, padding: '8px', borderRight: '1px solid #f0f0f0' }}></div>
{['08:00', '10:00', '12:00', '14:00', '16:00', '18:00'].map(t => (
<div key={t} style={{ flex: 1, padding: '8px', textAlign: 'center', fontSize: 12 }}>{t}</div>
))}
</div>
{['李工', '王工', '赵工', '陈工'].map((name, idx) => (
<div key={name} style={{ display: 'flex', borderBottom: idx === 3 ? 'none' : '1px solid #f0f0f0', height: 50, position: 'relative' }}>
<div style={{ width: 60, padding: '8px', borderRight: '1px solid #f0f0f0', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12 }}>{name}</div>
<div style={{ flex: 1, position: 'relative' }}>
{idx === 0 && <div style={{ position: 'absolute', left: '10%', top: 5, width: '40%', height: 40, background: '#f6ffed', border: '1px solid #b7eb8f', borderRadius: 4, padding: '2px 8px', fontSize: 10 }}>INV-05 告警排查<br />08:30 - 12:00</div>}
{idx === 1 && <div style={{ position: 'absolute', left: '55%', top: 5, width: '30%', height: 40, background: '#e6f7ff', border: '1px solid #91d5ff', borderRadius: 4, padding: '2px 8px', fontSize: 10 }}>B-12 热斑复检<br />13:30 - 15:00</div>}
{idx === 1 && <div style={{ position: 'absolute', left: '86%', top: 5, width: '12%', height: 40, background: '#f0f5ff', border: '1px solid #adc6ff', borderRadius: 4, borderStyle: 'dashed', padding: '2px 4px', fontSize: 10 }}>无人机巡检<br />16:00-18:00</div>}
{idx === 2 && <div style={{ position: 'absolute', left: '20%', top: 5, width: '35%', height: 40, background: '#f9f0ff', border: '1px solid #d3adf7', borderRadius: 4, padding: '2px 8px', fontSize: 10 }}>组件清洗 C-05<br />09:00 - 12:00</div>}
{idx === 2 && <div style={{ position: 'absolute', left: '70%', top: 5, width: '25%', height: 40, background: '#fff7e6', border: '1px solid #ffe7ba', borderRadius: 4, padding: '2px 8px', fontSize: 10 }}>清洗效果复检<br />15:00 - 17:00</div>}
{idx === 3 && <div style={{ position: 'absolute', left: '30%', top: 5, width: '30%', height: 40, background: '#fff2e8', border: '1px solid #ffbb96', borderRadius: 4, padding: '2px 8px', fontSize: 10 }}>周界摄像头修复<br />10:00 - 12:30</div>}
{idx === 3 && <div style={{ position: 'absolute', left: '75%', top: 5, width: '20%', height: 40, background: '#fffbe6', border: '1px solid #ffe58f', borderRadius: 4, padding: '2px 8px', fontSize: 10 }}>设备巡检<br />15:30 - 17:30</div>}
</div>
</div>
))}
@@ -336,177 +381,145 @@ export default function WorkOrderPage() {
</Row>
</Col>
{/* 右侧工单详情面板 */}
<Col span={10}>
<Card bordered={false} style={{ borderRadius: 8, marginBottom: 16 }} title={
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Title level={5} style={{ margin: 0 }}>{selectedOrder?.title}</Title>
<Tag color="red" size="small">高优先级</Tag>
</div>
<Text type="secondary">工单编号: {selectedOrder?.id}</Text>
</div>
}>
<Row gutter={16} style={{ marginBottom: 16 }}>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>来源:</span><span>{selectedOrder?.source}</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>工单类型:</span><span>{selectedOrder?.type}</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>负责人:</span><span>{selectedOrder?.handler}</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>协同人员:</span><span>无人机A01 / 张运维</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>计划时间:</span><span>2025-05-24 13:30 ~ 15:00</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>关联区域:</span><span>B-12</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>关联设备:</span><span>UAV-A01</span></div></Col>
<Col span={12}><div style={{ display: 'flex', justifyContent: 'space-between' }}><span>当前状态:</span><Tag color="blue">执行中</Tag></div></Col>
</Row>
<div style={{ marginBottom: 16 }}>
<div style={{ fontSize: 14, fontWeight: 500, marginBottom: 8 }}>任务描述</div>
<div style={{ fontSize: 13, color: '#666', background: '#fafafa', padding: 12, borderRadius: 4 }}>
{selectedOrder?.description}
</div>
{/* 右侧:详情 + AI + 告警 */}
<Col span={9}>
<Card
bordered={false}
style={{ borderRadius: 8, marginBottom: 16 }}
bodyStyle={{ padding: '16px' }}
>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<Text strong style={{ fontSize: 16 }}>工单详情</Text>
<Text type="secondary" style={{ fontSize: 12 }}>工单编号: WO20250524002</Text>
</div>
{/* 流程步骤 */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24, padding: '0 20px' }}>
{['已创建', '已派发', '执行中', '已完成'].map((step, idx) => (
<div key={idx} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<div style={{
width: 24, height: 24, borderRadius: '50%', background: idx < 3 ? '#1890ff' : '#d9d9d9',
display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 12
}}>
{idx + 1}
</div>
<div style={{ fontSize: 12, marginTop: 4 }}>{step}</div>
<div style={{ marginBottom: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}>
<Title level={4} style={{ margin: '0 12px 0 0' }}>区块 B-12 热斑复检</Title>
<Tag color="#fff1f0" style={{ color: '#ff4d4f', border: '1px solid #ff4d4f' }}>高优先级</Tag>
</div>
<Row gutter={[0, 12]}>
<Col span={12}><Text type="secondary">来源:</Text><Text>无人机巡检</Text></Col>
<Col span={12}><Text type="secondary">工单类型:</Text><Text>巡检</Text></Col>
<Col span={12}><Text type="secondary">负责人:</Text><Text>王工</Text></Col>
<Col span={12}><Text type="secondary">协同人员:</Text><Text>无人机 A01 / 张运维</Text></Col>
<Col span={24}><Text type="secondary">计划时间:</Text><Text>2025-05-24 13:30 - 15:00</Text></Col>
<Col span={12}><Text type="secondary">关联设备:</Text><Text style={{ color: '#1890ff' }}>UAV-A01</Text></Col>
<Col span={12}><Text type="secondary">关联区域:</Text><Text>B-12</Text></Col>
<Col span={24}><Text type="secondary">当前状态:</Text><Tag color="blue" style={{ borderRadius: 10 }}>执行中</Tag></Col>
<Col span={24}>
<Text type="secondary">任务描述:</Text>
<Text>对 AI 识别热斑区域进行复检与红外复核,确认是否需要更换组件。</Text>
</Col>
</Row>
</div>
<div style={{ marginBottom: 24 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', padding: '0 10px', marginBottom: 8 }}>
<div style={{ textAlign: 'center' }}>
<CheckCircleFilled style={{ color: '#52c41a', fontSize: 20 }} />
<div style={{ fontSize: 12, marginTop: 4 }}>已创建</div>
</div>
))}
<div style={{ position: 'absolute', width: 'calc(100% - 120px)', height: 2, background: '#e8e8e8', left: 60, top: 100, zIndex: 0 }} />
<div style={{ flex: 1, height: 2, background: '#52c41a', marginTop: 10 }}></div>
<div style={{ textAlign: 'center' }}>
<CheckCircleFilled style={{ color: '#52c41a', fontSize: 20 }} />
<div style={{ fontSize: 12, marginTop: 4 }}>已派发</div>
</div>
<div style={{ flex: 1, height: 2, background: '#1890ff', marginTop: 10 }}></div>
<div style={{ textAlign: 'center' }}>
<div style={{ width: 20, height: 20, borderRadius: '50%', background: '#1890ff', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12 }}>3</div>
<div style={{ fontSize: 12, marginTop: 4 }}>执行中</div>
</div>
<div style={{ flex: 1, height: 2, background: '#f0f0f0', marginTop: 10 }}></div>
<div style={{ textAlign: 'center' }}>
<div style={{ width: 20, height: 20, borderRadius: '50%', border: '1px solid #d9d9d9', color: '#d9d9d9', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12 }}>4</div>
<div style={{ fontSize: 12, marginTop: 4, color: '#d9d9d9' }}>已完成</div>
</div>
</div>
</div>
{/* 操作按钮 */}
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
<Button type="primary">派发文单</Button>
<Button>转派</Button>
<Button>挂起</Button>
<Button type="primary" style={{ background: '#52c41a', borderColor: '#52c41a' }}>完成工单</Button>
</div>
<Row gutter={8}>
<Col span={6}><Button type="primary" block>派发工单</Button></Col>
<Col span={6}><Button icon={<ReloadOutlined />} block>转派</Button></Col>
<Col span={6}><Button icon={<PauseOutlined />} danger ghost block>挂起</Button></Col>
<Col span={6}><Button icon={<CheckOutlined />} style={{ background: '#52c41a', color: '#fff', border: 'none' }} block>完成工单</Button></Col>
</Row>
</Card>
{/* AI建议与关联信息 */}
<Card bordered={false} style={{ borderRadius: 8 }} title="AI建议与关联信息">
<div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
<div style={{ flex: 1, background: '#f0f5ff', padding: 12, borderRadius: 8 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
<div style={{ width: 32, height: 32, borderRadius: 4, background: '#e6f7ff', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#1890ff' }}>
<CloudOutlined />
</div>
<div style={{ fontSize: 13, fontWeight: 500 }}>建议携带红外热像设备</div>
<Card
bordered={false}
title={<div style={{ display: 'flex', alignItems: 'center' }}><Text strong>AI 建议与关联信息</Text></div>}
style={{ borderRadius: 8, marginBottom: 16 }}
bodyStyle={{ padding: '12px' }}
>
<Row gutter={8}>
<Col span={8}>
<div style={{ background: '#f9f0ff', padding: '12px 8px', borderRadius: 8, textAlign: 'center' }}>
<RobotOutlined style={{ fontSize: 20, color: '#722ed1', marginBottom: 8 }} />
<div style={{ fontSize: 12 }}>建议携带</div>
<div style={{ fontSize: 12, fontWeight: 'bold' }}>红外热像设备</div>
</div>
</div>
<div style={{ flex: 1, background: '#f6ffed', padding: 12, borderRadius: 8 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
<div style={{ width: 32, height: 32, borderRadius: 4, background: '#e6ffed', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#52c41a' }}>
<BranchesOutlined />
</div>
<div style={{ fontSize: 13, fontWeight: 500 }}>优先复检组件 B-12-03 / B-12-05</div>
</Col>
<Col span={8}>
<div style={{ background: '#f6ffed', padding: '12px 8px', borderRadius: 8, textAlign: 'center' }}>
<ThunderboltOutlined style={{ fontSize: 20, color: '#52c41a', marginBottom: 8 }} />
<div style={{ fontSize: 12 }}>优先复检组件</div>
<div style={{ fontSize: 10, fontWeight: 'bold' }}>B-12-03 / B-12-05</div>
</div>
</div>
<div style={{ flex: 1, background: '#fff7e6', padding: 12, borderRadius: 8 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
<div style={{ width: 32, height: 32, borderRadius: 4, background: '#fff7e6', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fa8c16' }}>
<ThunderboltOutlined />
</div>
<div style={{ fontSize: 13, fontWeight: 500 }}>预计影响发电 2.1%</div>
</Col>
<Col span={8}>
<div style={{ background: '#fff7e6', padding: '12px 8px', borderRadius: 8, textAlign: 'center' }}>
<RiseOutlined style={{ fontSize: 20, color: '#faad14', marginBottom: 8 }} />
<div style={{ fontSize: 12 }}>预计影响发电</div>
<div style={{ fontSize: 12, fontWeight: 'bold' }}>2.1%</div>
</div>
</div>
</div>
</Col>
</Row>
</Card>
<Row gutter={16}>
<Col span={12}>
<div style={{ fontSize: 14, fontWeight: 500, marginBottom: 8 }}>关联告警趋势(近7天)</div>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false} title="关联告警趋势 (近 7 天)" style={{ borderRadius: 8 }}>
<Line
data={alarmTrendData}
xField="date"
yField="count"
color="#1890ff"
point={{ size: 4 }}
height={120}
legend={false}
yField="value"
smooth
point={{ size: 4, shape: 'circle' }}
height={120}
padding="auto"
xAxis={{ label: { style: { fontSize: 10 } } }}
yAxis={{ label: { style: { fontSize: 10 } } }}
/>
</Col>
<Col span={12}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
<span style={{ fontSize: 14, fontWeight: 500 }}>关联告警列表(近3条)</span>
<Button type="link" size="small">查看全部 </Button>
</Card>
</Col>
<Col span={12}>
<Card bordered={false} title="关联告警列表 (近 3 条)" extra={<Button type="link" size="small" style={{ fontSize: 12 }}>查看全部 <ChevronRight size={12} /></Button>} style={{ borderRadius: 8 }}>
<div style={{ fontSize: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}>
<Badge status="error" style={{ marginRight: 8 }} />
<Text ellipsis style={{ flex: 1 }}>INV-05 逆变器告警</Text>
<Text type="secondary" style={{ fontSize: 10 }}>05-24 10:24</Text>
</div>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}>
<Badge status="warning" style={{ marginRight: 8 }} />
<Text ellipsis style={{ flex: 1 }}>B-12 区块热斑告警</Text>
<Text type="secondary" style={{ fontSize: 10 }}>05-24 09:56</Text>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Badge status="warning" style={{ marginRight: 8 }} />
<Text ellipsis style={{ flex: 1 }}>B-12 组件温度异常</Text>
<Text type="secondary" style={{ fontSize: 10 }}>05-24 09:32</Text>
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{alarmList.map((alarm, idx) => (
<div key={idx} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '4px 8px', background: '#fafafa', borderRadius: 4 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ width: 8, height: 8, borderRadius: '50%', background: idx === 0 ? '#ff4d4f' : idx === 1 ? '#fa8c16' : '#faad14' }} />
<span style={{ fontSize: 13 }}>{alarm.name}</span>
</div>
<span style={{ fontSize: 12, color: '#999' }}>{alarm.time}</span>
</div>
))}
</div>
</Col>
</Row>
</Card>
</Card>
</Col>
</Row>
</Col>
</Row>
{/* 工单详情抽屉 */}
<Drawer
title="工单详情"
placement="right"
width={600}
onClose={() => setDrawerVisible(false)}
open={drawerVisible}
destroyOnClose
>
{selectedOrder && (
<div>
<Descriptions column={2} bordered size="small" title="基础信息">
<Descriptions.Item label="工单编号">{selectedOrder.id}</Descriptions.Item>
<Descriptions.Item label="状态">{statusConfig[selectedOrder.status]?.label}</Descriptions.Item>
<Descriptions.Item label="标题">{selectedOrder.title}</Descriptions.Item>
<Descriptions.Item label="优先级">
<Tag color={priorityConfig[selectedOrder.priority]?.color}>{priorityConfig[selectedOrder.priority]?.label}</Tag>
</Descriptions.Item>
<Descriptions.Item label="来源">{selectedOrder.source}</Descriptions.Item>
<Descriptions.Item label="类型">{selectedOrder.type}</Descriptions.Item>
<Descriptions.Item label="负责人">{selectedOrder.handler || '—'}</Descriptions.Item>
<Descriptions.Item label="截止时间">{selectedOrder.deadline}</Descriptions.Item>
<Descriptions.Item label="描述" span={2}>{selectedOrder.description || '—'}</Descriptions.Item>
</Descriptions>
</div>
)}
</Drawer>
{/* 编辑工单弹窗 */}
<Modal
title="编辑工单"
open={editModalVisible}
onCancel={() => setEditModalVisible(false)}
onOk={() => { editForm.validateFields().then(values => { message.success('工单保存成功'); setEditModalVisible(false); }); }}
okText="保存"
cancelText="取消"
width={600}
destroyOnClose
>
<Form form={editForm} layout="vertical" initialValues={{ priority: 'low', source: '人工创建', type: '巡检' }}>
<Form.Item label="标题" name="title" rules={[{ required: true, message: '请输入标题' }]}><Input /></Form.Item>
<Row gutter={16}>
<Col span={12}><Form.Item label="来源" name="source"><Select><Option value="人工创建">人工创建</Option><Option value="告警">告警</Option><Option value="AI诊断">AI诊断</Option></Select></Form.Item></Col>
<Col span={12}><Form.Item label="类型" name="type"><Select><Option value="巡检">巡检</Option><Option value="故障排查">故障排查</Option><Option value="维护">维护</Option><Option value="清洗">清洗</Option></Select></Form.Item></Col>
</Row>
<Row gutter={16}>
<Col span={12}><Form.Item label="优先级" name="priority"><Select><Option value="low">低</Option><Option value="medium">中</Option><Option value="high">高</Option></Select></Form.Item></Col>
<Col span={12}><Form.Item label="负责人" name="handler"><Input /></Form.Item></Col>
</Row>
<Form.Item label="描述" name="description"><Input.TextArea rows={4} /></Form.Item>
</Form>
</Modal>
</div>
);
}
};
export default WorkOrderPage;