import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Card, Row, Col, Statistic, Table, DatePicker, Select, Space, Tag, message } from 'antd'; import { BarChartOutlined, PieChartOutlined, RiseOutlined, FallOutlined, ClockCircleOutlined, CheckCircleOutlined, ExclamationCircleOutlined, } from '@ant-design/icons'; import axios from 'axios'; import dayjs from 'dayjs'; const { RangePicker } = DatePicker; const { Option } = Select; const getStatusColor = status => { const colors = { pending: 'orange', assigned: 'blue', in_progress: 'processing', completed: 'green', closed: 'default', }; return colors[status] || 'default'; }; const getStatusText = status => { const texts = { pending: '待处理', assigned: '已分配', in_progress: '处理中', completed: '已完成', closed: '已关闭', }; return texts[status] || status; }; const getPriorityColor = priority => { const colors = { low: 'green', medium: 'orange', high: 'red', urgent: 'magenta', }; return colors[priority] || 'default'; }; const getPriorityText = priority => { const texts = { low: '低', medium: '中', high: '高', urgent: '紧急', }; return texts[priority] || priority; }; function TicketStatistics() { const [loading, setLoading] = useState(true); const [dateRange, setDateRange] = useState([dayjs().subtract(30, 'days'), dayjs()]); const [statistics, setStatistics] = useState({ total: 0, pending: 0, inProgress: 0, completed: 0, closed: 0, avgProcessingTime: 0, byCategory: [], byPriority: [], byStatus: [], byDevice: [], trend: [], }); const fetchStatistics = useCallback(async () => { try { setLoading(true); const params = { startDate: dateRange[0].format('YYYY-MM-DD'), endDate: dateRange[1].format('YYYY-MM-DD'), }; const response = await axios.get('/api/tickets/statistics', { params }); setStatistics(response.data); } catch (error) { message.error('获取统计数据失败'); console.error('获取统计数据失败:', error); } finally { setLoading(false); } }, [dateRange]); useEffect(() => { fetchStatistics(); }, [fetchStatistics]); const handleDateChange = useCallback(dates => { if (dates) { setDateRange(dates); } }, []); const getStatusColor = status => { const colors = { pending: 'orange', assigned: 'blue', in_progress: 'processing', completed: 'green', closed: 'default', }; return colors[status] || 'default'; }; const getStatusText = status => { const texts = { pending: '待处理', assigned: '已分配', in_progress: '处理中', completed: '已完成', closed: '已关闭', }; return texts[status] || status; }; const getPriorityColor = priority => { const colors = { low: 'green', medium: 'orange', high: 'red', urgent: 'magenta', }; return colors[priority] || 'default'; }; const getPriorityText = priority => { const texts = { low: '低', medium: '中', high: '高', urgent: '紧急', }; return texts[priority] || priority; }; const statusColumns = useMemo( () => [ { title: '状态', dataIndex: 'status', key: 'status', width: 120, render: status => {getStatusText(status)}, }, { title: '工单数量', dataIndex: 'count', key: 'count', width: 120, render: count => , }, { title: '占比', dataIndex: 'percentage', key: 'percentage', width: 120, render: pct => ( 30 ? '#ff4d4f' : '#52c41a' }}> {pct !== undefined && pct !== null ? `${pct.toFixed(1)}%` : '-'} ), }, ], [] ); const categoryColumns = useMemo( () => [ { title: '故障分类', dataIndex: 'category', key: 'category', width: 150, }, { title: '工单数量', dataIndex: 'count', key: 'count', width: 120, render: count => , }, { title: '占比', dataIndex: 'percentage', key: 'percentage', width: 100, render: pct => `${pct !== undefined && pct !== null ? pct.toFixed(1) : 0}%`, }, { title: '已完成', dataIndex: 'completed', key: 'completed', width: 100, render: count => {count}, }, { title: '平均处理时间(小时)', dataIndex: 'avgTime', key: 'avgTime', width: 150, render: time => (time !== undefined && time !== null ? time.toFixed(1) : '-'), }, ], [] ); const priorityColumns = useMemo( () => [ { title: '优先级', dataIndex: 'priority', key: 'priority', width: 100, render: priority => ( {getPriorityText(priority)} ), }, { title: '工单数量', dataIndex: 'count', key: 'count', width: 120, render: count => , }, { title: '已完成', dataIndex: 'completed', key: 'completed', width: 100, render: count => {count}, }, { title: '平均处理时间(小时)', dataIndex: 'avgTime', key: 'avgTime', width: 150, render: time => (time !== undefined && time !== null ? time.toFixed(1) : '-'), }, ], [] ); const deviceColumns = useMemo( () => [ { title: '设备名称', dataIndex: 'deviceName', key: 'deviceName', width: 180, }, { title: '故障次数', dataIndex: 'count', key: 'count', width: 100, render: count => {count}, }, { title: '最后故障时间', dataIndex: 'lastFaultTime', key: 'lastFaultTime', width: 160, render: text => (text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'), }, { title: '设备类型', dataIndex: 'deviceType', key: 'deviceType', width: 100, }, ], [] ); const simpleBarData = [ { name: '待处理', value: statistics.pending }, { name: '处理中', value: statistics.inProgress }, { name: '已完成', value: statistics.completed }, { name: '已关闭', value: statistics.closed }, ]; return (
} > } valueStyle={{ color: '#1890ff' }} /> } valueStyle={{ color: '#fa8c16' }} /> } valueStyle={{ color: '#52c41a' }} /> } valueStyle={{ color: '#722ed1' }} precision={1} /> } valueStyle={{ color: '#eb2f96' }} /> } valueStyle={{ color: '#8c8c8c' }} /> 0 ? ((statistics.completed / statistics.total) * 100).toFixed(1) : 0 } suffix="%" prefix={} valueStyle={{ color: '#ff4d4f' }} /> 0 ? ((statistics.inProgress / statistics.total) * 100).toFixed(1) : 0 } suffix="%" prefix={} valueStyle={{ color: '#52c41a' }} />
(text ? dayjs(text).format('YYYY-MM-DD') : '-'), }, { title: '新建工单', dataIndex: 'created', key: 'created', width: 100, render: count => {count}, }, { title: '已完成', dataIndex: 'completed', key: 'completed', width: 100, render: count => {count}, }, { title: '关闭工单', dataIndex: 'closed', key: 'closed', width: 100, render: count => {count}, }, { title: '当日处理中', dataIndex: 'inProgress', key: 'inProgress', width: 120, render: count => {count}, }, { title: '新增待处理', dataIndex: 'pending', key: 'pending', width: 120, render: count => {count}, }, ]} dataSource={statistics.trend} rowKey="date" pagination={{ pageSize: 10 }} size="small" /> ); } export default React.memo(TicketStatistics);