import React, { useState, useEffect } from 'react'; import { Table, Button, Modal, Form, Input, Select, DatePicker, message, Card, Space, Tag, Dropdown, Menu, Tabs, Timeline, Descriptions } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, EyeOutlined, MoreOutlined, UserOutlined, ToolOutlined, CheckCircleOutlined, SyncOutlined, ClockCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'; import axios from 'axios'; import dayjs from 'dayjs'; const { Option } = Select; const { RangePicker } = DatePicker; const { TextArea } = Input; const { TabPane } = Tabs; function TicketManagement() { const [tickets, setTickets] = useState([]); const [devices, setDevices] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [modalVisible, setModalVisible] = useState(false); const [detailModalVisible, setDetailModalVisible] = useState(false); const [processingModalVisible, setProcessingModalVisible] = useState(false); const [editingTicket, setEditingTicket] = useState(null); const [selectedTicket, setSelectedTicket] = useState(null); const [operationRecords, setOperationRecords] = useState([]); const [form] = Form.useForm(); const [processForm] = Form.useForm(); const [searchForm] = Form.useForm(); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, pageSizeOptions: ['10', '20', '30', '50'], showSizeChanger: true, showTotal: (total) => `共 ${total} 条记录` }); const [searchFilters, setSearchFilters] = useState({}); const fetchTickets = async (page = 1, pageSize = 10, filters = {}) => { try { setLoading(true); const params = { page, pageSize, ...searchFilters, ...filters }; const response = await axios.get('/api/tickets', { params }); const { tickets: ticketList, total } = response.data; setTickets(ticketList); setPagination(prev => ({ ...prev, current: page, pageSize, total })); } catch (error) { message.error('获取工单列表失败'); console.error('获取工单列表失败:', error); } finally { setLoading(false); } }; const fetchDevices = async () => { try { const response = await axios.get('/api/devices', { params: { pageSize: 1000 } }); setDevices(response.data.devices || []); } catch (error) { console.error('获取设备列表失败:', error); } }; const fetchCategories = async () => { try { const response = await axios.get('/api/ticket-categories'); setCategories(response.data || []); } catch (error) { console.error('获取分类列表失败:', error); } }; const fetchTicketDetail = async (ticketId) => { try { const [ticketRes, operationsRes] = await Promise.all([ axios.get(`/api/tickets/${ticketId}`), axios.get(`/api/tickets/${ticketId}/operations`) ]); setSelectedTicket(ticketRes.data); setOperationRecords(operationsRes.data || []); setDetailModalVisible(true); } catch (error) { message.error('获取工单详情失败'); console.error('获取工单详情失败:', error); } }; useEffect(() => { fetchTickets(); fetchDevices(); fetchCategories(); }, []); const showModal = (ticket = null) => { setEditingTicket(ticket); if (ticket) { const ticketData = { ...ticket }; if (ticketData.expectedCompletionDate) { ticketData.expectedCompletionDate = dayjs(ticketData.expectedCompletionDate); } if (ticketData.completionDate) { ticketData.completionDate = dayjs(ticketData.completionDate); } form.setFieldsValue(ticketData); } else { form.resetFields(); } setModalVisible(true); }; const handleCancel = () => { setModalVisible(false); setEditingTicket(null); }; const handleSubmit = async (values) => { try { const ticketData = { ...values, expectedCompletionDate: values.expectedCompletionDate ? values.expectedCompletionDate.format('YYYY-MM-DD HH:mm:ss') : null, completionDate: values.completionDate ? values.completionDate.format('YYYY-MM-DD HH:mm:ss') : null }; if (editingTicket) { await axios.put(`/api/tickets/${editingTicket.ticketId}`, ticketData); message.success('工单更新成功'); } else { const user = JSON.parse(localStorage.getItem('user') || '{}'); ticketData.reporterId = user.userId || localStorage.getItem('userId') || 'USER001'; ticketData.reporterName = user.username || '系统用户'; await axios.post('/api/tickets', ticketData); message.success('工单创建成功'); } setModalVisible(false); fetchTickets(); setEditingTicket(null); } catch (error) { message.error(editingTicket ? '工单更新失败' : '工单创建失败'); console.error(error); } }; const handleDelete = async (ticketId) => { Modal.confirm({ title: '确认删除', content: '确定要删除这个工单吗?', okText: '删除', okType: 'danger', cancelText: '取消', onOk: async () => { try { await axios.delete(`/api/tickets/${ticketId}`); message.success('工单删除成功'); fetchTickets(); } catch (error) { message.error('工单删除失败'); console.error(error); } } }); }; const handleProcess = (ticket) => { setSelectedTicket(ticket); processForm.resetFields(); setProcessingModalVisible(true); }; const handleProcessSubmit = async (values) => { try { await axios.put(`/api/tickets/${selectedTicket.ticketId}/process`, { ...values, operatorId: localStorage.getItem('userId'), operatorName: JSON.parse(localStorage.getItem('user') || '{}').username }); message.success('工单处理完成'); setProcessingModalVisible(false); fetchTickets(); } catch (error) { message.error('处理失败'); console.error(error); } }; const handleStatusChange = async (ticketId, newStatus) => { try { await axios.put(`/api/tickets/${ticketId}/status`, { status: newStatus, operatorId: localStorage.getItem('userId'), operatorName: JSON.parse(localStorage.getItem('user') || '{}').username }); message.success('状态更新成功'); fetchTickets(); } catch (error) { message.error('状态更新失败'); console.error(error); } }; const handleSearch = (values) => { setSearchFilters(values); fetchTickets(1, pagination.pageSize, values); }; const handleReset = () => { searchForm.resetFields(); setSearchFilters({}); fetchTickets(1, pagination.pageSize, {}); }; const handleTableChange = (paginationInfo) => { setPagination(paginationInfo); fetchTickets(paginationInfo.current, paginationInfo.pageSize, searchFilters); }; const getStatusColor = (status) => { const colors = { pending: 'orange', in_progress: 'processing', completed: 'green', closed: 'default' }; return colors[status] || 'default'; }; const getStatusText = (status) => { const texts = { pending: '待处理', 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 columns = [ { title: '工单编号', dataIndex: 'ticketId', key: 'ticketId', width: 150, render: (text, record) => ( ) }, { title: '标题', dataIndex: 'title', key: 'title', width: 200, ellipsis: true }, { title: '设备信息', key: 'deviceInfo', width: 180, render: (_, record) => (
{record.deviceName}
{record.serialNumber}
) }, { title: '故障分类', dataIndex: 'faultCategory', key: 'faultCategory', width: 120, render: (text) => text ? {text} : '-' }, { title: '优先级', dataIndex: 'priority', key: 'priority', width: 80, render: (priority) => ( {getPriorityText(priority)} ) }, { title: '状态', dataIndex: 'status', key: 'status', width: 100, render: (status) => ( {getStatusText(status)} ) }, { title: '报告人', dataIndex: ['reporter', 'username'], key: 'reporter', width: 100, render: (text) => text || '-' }, { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 160, render: (text) => text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-' }, { title: '期望完成时间', dataIndex: 'expectedCompletionDate', key: 'expectedCompletionDate', width: 160, render: (text) => text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-' }, { title: '操作', key: 'action', width: 150, fixed: 'right', render: (_, record) => ( {record.status === 'pending' ? ( ) : null} } onClick={() => showModal(record)}> 编辑 {(record.status === 'pending' || record.status === 'in_progress') && ( } onClick={() => handleStatusChange(record.ticketId, 'completed')} > 完成工单 )} {record.status === 'completed' && ( } onClick={() => handleStatusChange(record.ticketId, 'closed')} > 关闭工单 )} } danger onClick={() => handleDelete(record.ticketId)} > 删除 } > }>