import React, { useState, useEffect, useCallback } from 'react'; import { Table, Button, Modal, Form, Input, Select, DatePicker, message, Card, Space, Tag, Row, Col, Statistic, Popconfirm, Tooltip, Empty, Progress, Tabs, } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, PlayCircleOutlined, CheckCircleOutlined, SearchOutlined, ReloadOutlined, InboxOutlined, InfoCircleOutlined, FileSearchOutlined, ClockCircleOutlined, CheckSquareOutlined, SyncOutlined, } from '@ant-design/icons'; import api from '../api'; import dayjs from 'dayjs'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { designTokens } from '../config/theme'; import CloseButton from '../components/CloseButton'; const { RangePicker } = DatePicker; const { TabPane } = Tabs; const InventoryManagement = () => { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const planIdParam = searchParams.get('planId'); const [activeTab, setActiveTab] = useState(planIdParam ? 'execution' : 'list'); const [plans, setPlans] = useState([]); const [loading, setLoading] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [editingPlan, setEditingPlan] = useState(null); const [rooms, setRooms] = useState([]); const [racks, setRacks] = useState([]); const [filteredRacks, setFilteredRacks] = useState([]); const [selectedRooms, setSelectedRooms] = useState([]); const [stats, setStats] = useState({ totalPlans: 0, completedPlans: 0, inProgressPlans: 0, }); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); const [searchParamsObj, setSearchParamsObj] = useState({}); const [form] = Form.useForm(); const fetchPlans = useCallback(async () => { setLoading(true); try { const params = { page: pagination.current, pageSize: pagination.pageSize, ...searchParamsObj, }; const res = await api.get('/inventory/plans', { params }); setPlans(res.data.plans || []); setPagination(prev => ({ ...prev, total: res.data.total || 0, })); } catch (error) { message.error('获取盘点计划失败'); } finally { setLoading(false); } }, [pagination.current, pagination.pageSize, searchParamsObj]); const fetchStats = async () => { try { const res = await api.get('/inventory/stats/dashboard'); setStats({ totalPlans: res.data.totalPlans || 0, completedPlans: res.data.completedPlans || 0, inProgressPlans: res.data.inProgressPlans || 0, }); } catch (error) { console.error('获取统计失败', error); } }; const fetchRooms = async () => { try { const res = await api.get('/rooms', { params: { pageSize: 1000 } }); setRooms(Array.isArray(res.data) ? res.data : res.data.rooms || []); } catch (error) { console.error('获取机房失败', error); } }; const fetchRacks = async () => { try { const res = await api.get('/racks', { params: { pageSize: 1000 } }); const allRacks = Array.isArray(res.data) ? res.data : res.data.racks || []; setRacks(allRacks); setFilteredRacks(allRacks); } catch (error) { console.error('获取机柜失败', error); } }; useEffect(() => { fetchPlans(); fetchStats(); fetchRooms(); fetchRacks(); }, [fetchPlans]); useEffect(() => { if (planIdParam) { setActiveTab('execution'); } }, [planIdParam]); const handleAdd = () => { setEditingPlan(null); form.resetFields(); setSelectedRooms([]); setFilteredRacks(racks); setModalVisible(true); }; const handleEdit = record => { setEditingPlan(record); const targetRooms = record.targetRooms || []; setSelectedRooms(targetRooms); if (targetRooms.length > 0) { const filtered = racks.filter( rack => targetRooms.includes(rack.roomId) || (rack.Room && targetRooms.includes(rack.Room.roomId)) ); setFilteredRacks(filtered); } else { setFilteredRacks(racks); } form.setFieldsValue({ name: record.name, type: record.type, description: record.description, scheduledDate: record.scheduledDate ? dayjs(record.scheduledDate) : null, targetRooms: targetRooms, targetRacks: record.targetRacks || [], }); setModalVisible(true); }; const handleDelete = async planId => { Modal.confirm({ title: '确认删除', content: '确定要删除这个盘点计划吗?此操作不可恢复!', okText: '删除', okType: 'danger', cancelText: '取消', onOk: async () => { try { await api.delete(`/inventory/plans/${planId}`); message.success('删除成功'); fetchPlans(); fetchStats(); } catch (error) { message.error('删除失败'); } }, }); }; const handleSubmit = async values => { try { const data = { ...values, scheduledDate: values.scheduledDate?.toISOString(), targetRooms: values.targetRooms || [], targetRacks: values.targetRacks || [], }; if (editingPlan) { await api.put(`/inventory/plans/${editingPlan.planId}`, data); message.success('更新成功'); } else { await api.post('/inventory/plans', data); message.success('创建成功'); } setModalVisible(false); fetchPlans(); fetchStats(); } catch (error) { message.error(error.response?.data?.error || '操作失败'); } }; const handleStart = async plan => { try { await api.post(`/inventory/plans/${plan.planId}/start`); message.success('盘点任务已启动'); fetchPlans(); fetchStats(); } catch (error) { message.error(error.response?.data?.error || '启动失败'); } }; const handleComplete = async plan => { try { await api.post(`/inventory/plans/${plan.planId}/complete`); message.success('盘点已完成'); fetchPlans(); fetchStats(); } catch (error) { message.error(error.response?.data?.error || '完成失败'); } }; const handleViewTasks = plan => { navigate(`/inventory/execution?planId=${plan.planId}`); }; const handleRoomsChange = roomIds => { setSelectedRooms(roomIds || []); if (!roomIds || roomIds.length === 0) { setFilteredRacks(racks); } else { const filtered = racks.filter( rack => roomIds.includes(rack.roomId) || (rack.Room && roomIds.includes(rack.Room.roomId)) ); setFilteredRacks(filtered); } }; const getStatusTag = status => { const statusMap = { draft: { color: 'default', text: '草稿', icon: }, pending: { color: 'orange', text: '待执行', icon: }, in_progress: { color: 'processing', text: '进行中', icon: }, completed: { color: 'success', text: '已完成', icon: }, cancelled: { color: 'error', text: '已取消', icon: }, }; const config = statusMap[status] || statusMap.draft; return ( {config.text} ); }; const getTypeTag = type => { const typeMap = { full: { color: 'blue', text: '全面盘点' }, partial: { color: 'cyan', text: '局部盘点' }, sample: { color: 'purple', text: '抽样盘点' }, }; const config = typeMap[type] || typeMap.full; return {config.text}; }; const getProgressPercent = plan => { if (!plan.totalDevices || plan.totalDevices === 0) return 0; return Math.round((plan.checkedDevices / plan.totalDevices) * 100); }; const columns = [ { title: '盘点计划', key: 'planInfo', render: (_, record) => (
{record.name}
{record.planId}
), }, { title: '类型', dataIndex: 'type', key: 'type', width: 100, render: type => getTypeTag(type), }, { title: '状态', dataIndex: 'status', key: 'status', width: 120, render: status => getStatusTag(status), }, { title: '盘点进度', key: 'progress', width: 180, render: (_, record) => (
已盘: {record.checkedDevices || 0} / {record.totalDevices || 0} {getProgressPercent(record)}%
), }, { title: '异常设备', key: 'abnormal', width: 100, render: (_, record) => record.abnormalDevices > 0 ? ( {record.abnormalDevices} 异常 ) : ( - ), }, { title: '创建人', dataIndex: ['Creator', 'realName'], key: 'creator', width: 100, render: name => name || '-', }, { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 160, render: date => (date ? dayjs(date).format('YYYY-MM-DD HH:mm') : '-'), }, { title: '操作', key: 'action', width: 200, fixed: 'right', render: (_, record) => ( `共 ${total} 条记录`, onChange: (page, pageSize) => { setPagination(prev => ({ ...prev, current: page, pageSize })); }, }} scroll={{ x: 1200 }} locale={{ emptyText: ( ), }} /> {editingPlan ? '编辑盘点计划' : '新建盘点计划'} } open={modalVisible} closeIcon={} onCancel={() => setModalVisible(false)} footer={null} width={640} centered styles={{ header: { borderBottom: '1px solid #f0f0f0', padding: '16px 24px' }, body: { padding: 24 }, footer: { borderTop: '1px solid #f0f0f0', padding: '12px 24px' }, }} >
); }; export default InventoryManagement;