import React, { useState, useEffect } from 'react'; import { Button, Table, Space, Modal, Form, Input, Select, message, List, Avatar, Progress, Tag, Timeline, DatePicker, Switch, Dropdown, Menu } from 'antd'; import type { MenuProps } from 'antd'; import { CloseOutlined } from '@ant-design/icons'; import dayjs from 'dayjs'; import { WorkOrderAPI } from '../api/work_orders'; import { DeviceInstanceAPI } from '../api/device_instance'; import { Uploader } from '../components/components_uploader'; import { WorkOrderPriority, WorkOrderStatus } from '@/share/monitorTypes'; import type { WorkOrder, WorkOrderSettings, DeadlineInfo } from '@/share/monitorTypes'; const { Column } = Table; const { Option } = Select; const { TextArea } = Input; export function WorkOrdersPage() { const [workOrders, setWorkOrders] = useState([]); const [settings, setSettings] = useState(); const [loading, setLoading] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [currentOrder, setCurrentOrder] = useState>(); const [categories, setCategories] = useState([]); const [devices, setDevices] = useState([]); const [attachments, setAttachments] = useState([]); const [comments, setComments] = useState([]); const [commentContent, setCommentContent] = useState(''); const [form] = Form.useForm(); const [historyVisible, setHistoryVisible] = useState(false); const [statusHistory, setStatusHistory] = useState([]); const [deadlineInfo, setDeadlineInfo] = useState(); const [autoDispatchVisible, setAutoDispatchVisible] = useState(false); const [autoDispatchForm] = Form.useForm(); const [detailModalVisible, setDetailModalVisible] = useState(false); const [currentDetail, setCurrentDetail] = useState(''); useEffect(() => { // 开发环境下生成模拟数据 if (process.env.NODE_ENV === 'development') { const mockOrders = [ { id: 'mock-1', title: '设备网络故障', order_no: `WO-${dayjs().format('YYYYMMDD')}-1001`, device_name: '网络交换机1', problem_desc: '设备无法连接网络', priority: WorkOrderPriority.URGENT, creator_id: 'system', creator_name: '系统管理员', deadline: dayjs().add(1, 'day').toISOString(), created_at: dayjs().toISOString(), updated_at: dayjs().toISOString(), problem_type: '网络', status: WorkOrderStatus.PENDING, feedback: '', attachments: [] }, { id: 'mock-2', title: '服务器硬件故障', order_no: `WO-${dayjs().format('YYYYMMDD')}-1002`, device_name: '服务器A', problem_desc: '硬盘故障需要更换', priority: WorkOrderPriority.IMPORTANT, creator_id: 'system', creator_name: '系统管理员', deadline: dayjs().add(2, 'day').toISOString(), created_at: dayjs().toISOString(), updated_at: dayjs().toISOString(), problem_type: '硬件', status: WorkOrderStatus.PROCESSING, feedback: '已订购新硬盘', attachments: [] }, { id: 'mock-3', title: '软件系统升级', order_no: `WO-${dayjs().format('YYYYMMDD')}-1003`, device_name: '办公电脑', problem_desc: '需要升级到最新版本', priority: WorkOrderPriority.NORMAL, creator_id: 'system', creator_name: '系统管理员', deadline: dayjs().add(3, 'day').toISOString(), created_at: dayjs().toISOString(), updated_at: dayjs().toISOString(), problem_type: '软件', status: WorkOrderStatus.CLOSED, feedback: '已完成升级', attachments: [] }, { id: 'mock-4', title: '打印机维护', order_no: `WO-${dayjs().format('YYYYMMDD')}-1004`, device_name: '办公室打印机', problem_desc: '定期维护保养', priority: WorkOrderPriority.NORMAL, creator_id: 'system', creator_name: '系统管理员', deadline: dayjs().add(4, 'day').toISOString(), created_at: dayjs().toISOString(), updated_at: dayjs().toISOString(), problem_type: '其他', status: WorkOrderStatus.PENDING, feedback: '', attachments: [] }, { id: 'mock-5', title: '数据库优化', order_no: `WO-${dayjs().format('YYYYMMDD')}-1005`, device_name: '数据库服务器', problem_desc: '查询性能优化', priority: WorkOrderPriority.IMPORTANT, creator_id: 'system', creator_name: '系统管理员', deadline: dayjs().add(5, 'day').toISOString(), created_at: dayjs().toISOString(), updated_at: dayjs().toISOString(), problem_type: '软件', status: WorkOrderStatus.PROCESSING, feedback: '正在优化索引', attachments: [] } ]; setWorkOrders(mockOrders); } else { fetchData(); } fetchSettings(); fetchCategories(); fetchDevices(); }, []); const [searchParams, setSearchParams] = useState({ status: undefined, problemType: undefined, keyword: undefined, startDate: undefined, endDate: undefined }); const fetchData = async () => { setLoading(true); try { const result = await WorkOrderAPI.getList(searchParams); setWorkOrders(result.data); } catch (error) { message.error('获取工单列表失败'); } finally { setLoading(false); } }; const handleSearch = (values: any) => { setSearchParams({ status: values.status, problemType: values.problemType, keyword: values.keyword, startDate: values.dateRange?.[0]?.toISOString(), endDate: values.dateRange?.[1]?.toISOString() }); }; const handleReset = () => { setSearchParams({ status: undefined, problemType: undefined, keyword: undefined, startDate: undefined, endDate: undefined }); }; const fetchSettings = async () => { try { const result = await WorkOrderAPI.getSettings(); setSettings(result.data); } catch (error) { message.error('获取工单设置失败'); } }; const fetchCategories = async () => { try { const result = await WorkOrderAPI.getCategories(); setCategories(result.data); } catch (error) { message.error('获取分类列表失败'); } }; const fetchDevices = async () => { try { const result = await DeviceInstanceAPI.getDeviceInstances(); setDevices(result.data); } catch (error) { message.error('获取设备列表失败'); } }; const fetchComments = async (id: string) => { try { const result = await WorkOrderAPI.getComments(id); setComments(result.data); } catch (error) { message.error('获取评论失败'); } }; const fetchStatusHistory = async (id: string) => { try { const result = await WorkOrderAPI.getStatusHistory(id); setStatusHistory(result.data); } catch (error) { message.error('获取状态历史失败'); } }; const checkDeadline = async (order: WorkOrder) => { if (!order.deadline) return; try { const result = await WorkOrderAPI.getDeadline(order.id); const { remaining_hours, is_overdue } = result.data; let color = 'green'; let text = '进行中'; let progress = 100; if (is_overdue) { color = 'red'; text = '已超时'; progress = 0; } else if (remaining_hours < 24) { color = 'orange'; text = `即将到期 (剩余${remaining_hours}小时)`; progress = Math.max(10, Math.min(90, remaining_hours * 4)); } setDeadlineInfo({ color, text, progress: Number(progress), remainingTime: `${remaining_hours}小时`, isOverdue: remaining_hours < 0, }); } catch (error) { message.error('获取时限信息失败'); } }; const handleCreate = () => { setCurrentOrder({}); setModalVisible(true); }; const handleEdit = async (record: WorkOrder) => { setCurrentOrder(record); form.setFieldsValue(record); setModalVisible(true); checkDeadline(record); if (record.id) { await fetchComments(record.id); } }; const handleSubmit = async () => { try { const values = await form.validateFields(); if (currentOrder?.id) { await WorkOrderAPI.update(currentOrder.id, values); message.success('更新工单成功'); } else { await WorkOrderAPI.create(values); message.success('创建工单成功'); } setModalVisible(false); fetchData(); } catch (error) { message.error('操作失败'); } }; const handleStatusChange = async (id: string, status: string) => { Modal.confirm({ title: '确认状态变更', content: (
), onOk: async (close) => { try { const values = await form.validateFields(); await WorkOrderAPI.changeStatus( id, status, 'current_user', // TODO: 替换为实际用户 values.comment ); message.success('状态更新成功'); fetchData(); close(); } catch (error) { message.error('状态更新失败'); } } }); }; const renderStatusActions = (record: WorkOrder) => { const statusOptions = settings?.statusOptions || []; const currentStatusIndex = statusOptions.indexOf(record.status); const nextStatus = statusOptions[currentStatusIndex + 1]; const prevStatus = statusOptions[currentStatusIndex - 1]; return ( {prevStatus && ( )} {nextStatus && ( )} {!nextStatus && !prevStatus && ( 无可用操作 )} ); }; const handleShowHistory = async (id: string) => { await fetchStatusHistory(id); setHistoryVisible(true); }; const handleAssign = async (id: string, assignee: string) => { try { await WorkOrderAPI.assign(id, assignee); message.success('分配成功'); fetchData(); } catch (error) { message.error('分配失败'); } }; const handleUploadSuccess = (fileUrl: string, fileInfo: any) => { if (currentOrder?.id) { setAttachments(prev => [...prev, { id: fileInfo.id, url: fileUrl, name: fileInfo.original_filename }]); message.success('附件上传成功'); } }; const handleAddComment = async () => { if (!currentOrder?.id) { message.error('请先保存工单'); return; } if (!commentContent.trim()) { message.error('评论内容不能为空'); return; } if (commentContent.length > 500) { message.error('评论内容不能超过500字'); return; } // 简单敏感词过滤 const bannedWords = ['敏感词1', '敏感词2', '敏感词3']; if (bannedWords.some(word => commentContent.includes(word))) { message.error('评论包含不允许的内容'); return; } try { await WorkOrderAPI.addComment(currentOrder.id, commentContent); setCommentContent(''); await fetchComments(currentOrder.id); message.success('评论添加成功'); } catch (error) { message.error('评论添加失败'); } }; const handleAccept = async (id: string) => { try { await WorkOrderAPI.changeStatus(id, '处理中', 'current_user', '工单已受理'); message.success('工单受理成功'); fetchData(); } catch (error) { message.error('受理失败'); } }; const handleReassign = async (id: string) => { Modal.confirm({ title: '改派工单', content: ( ), onOk: async (close) => { try { await WorkOrderAPI.assign(id, 'new_assignee'); // TODO: 替换为实际选择的值 message.success('工单改派成功'); fetchData(); close(); } catch (error) { message.error('改派失败'); } } }); }; const handleClose = async (id: string) => { Modal.confirm({ title: '关闭工单', content: (