import React, { useRef, useState, useEffect } from 'react'; import { useAccess } from '@umijs/max'; import { Button, message, Modal, Tag, Row, Col, Card, Switch, Statistic, Badge, Dropdown, Space } from 'antd'; import { ActionType, ProColumns, ProTable, ModalForm, ProFormText, ProFormSelect } from '@ant-design/pro-components'; import { PlusOutlined, DeleteOutlined, FormOutlined, QrcodeOutlined, SwapOutlined, ApartmentOutlined, ScanOutlined, EllipsisOutlined } from '@ant-design/icons'; import { getDeviceList, addDevice, updateDevice, deleteDevice, assignDevice, toggleDeviceStatus, getGateStatuses, toggleGate, getGateEntryLogs, simulateGateScan, } from '@/services/jobportal/outdoorFairDetail'; import { getBoothList } from '@/services/jobportal/outdoorFairDetail'; interface Props { fairId: number; } const deviceTypeMap: Record = { mobile_terminal: '移动终端', qr_sticker: '展位二维码', }; const DeviceTab: React.FC = ({ fairId }) => { const access = useAccess(); const mobileActionRef = useRef(); const qrActionRef = useRef(); const gateLogActionRef = useRef(); const [deviceModalOpen, setDeviceModalOpen] = useState(false); const [editingDevice, setEditingDevice] = useState(null); const [defaultType, setDefaultType] = useState('mobile_terminal'); const [assignModalOpen, setAssignModalOpen] = useState(false); const [assigningDevice, setAssigningDevice] = useState(null); const [gateStatuses, setGateStatuses] = useState([]); const [scanModalOpen, setScanModalOpen] = useState(false); const [scanGate, setScanGate] = useState(null); const fetchGateStatuses = async () => { const res = await getGateStatuses(fairId); setGateStatuses(res.rows || []); }; useEffect(() => { fetchGateStatuses(); }, [fairId]); const handleToggleGate = async (id: number) => { const res = await toggleGate(id); if (res.code === 200) { message.success(res.msg); fetchGateStatuses(); } }; const deviceColumns = (type: string): ProColumns[] => [ { title: '设备名称', dataIndex: 'deviceName', width: 120, ellipsis: true }, { title: '设备编号', dataIndex: 'deviceCode', width: 180, hideInSearch: true }, { title: '分配展位', dataIndex: 'assignedBoothNumber', width: 100, hideInSearch: true, render: (_, r) => r.assignedBoothNumber || '--' }, { title: '分配企业', dataIndex: 'assignedCompanyName', width: 180, ellipsis: true, hideInSearch: true, render: (_, r) => r.assignedCompanyName || '--' }, { title: '状态', dataIndex: 'status', width: 80, hideInSearch: true, render: (_, r) => , }, { title: '最后活跃', dataIndex: 'lastActiveTime', width: 170, hideInSearch: true, render: (_, r) => r.lastActiveTime || '--' }, { title: '操作', valueType: 'option', width: 120, render: (_, record) => { const isQrType = type === 'qr_sticker'; const menuItems = [ { key: 'toggle', icon: record.status === 'online' ? : , label: record.status === 'online' ? '禁用' : '启用', onClick: async () => { const res = await toggleDeviceStatus(record.id); if (res.code === 200) { message.success(res.msg); type === 'mobile_terminal' ? mobileActionRef.current?.reload() : qrActionRef.current?.reload(); } }, }, { key: 'assign', icon: , label: '分配展位', onClick: () => { setAssigningDevice(record); setAssignModalOpen(true); }, }, ...(isQrType ? [{ key: 'qrcode', icon: , label: '重新生成二维码', onClick: () => message.success(`已为「${record.deviceName}」重新生成二维码`), }] : []), { key: 'edit', icon: , label: '编辑', onClick: () => { setEditingDevice(record); setDefaultType(record.type); setDeviceModalOpen(true); }, }, { type: 'divider' as const }, { key: 'delete', icon: , label: '删除', danger: true, onClick: () => { Modal.confirm({ title: '确认删除', content: `确定删除「${record.deviceName}」吗?`, onOk: async () => { const res = await deleteDevice(record.id); if (res.code === 200) { message.success('删除成功'); type === 'mobile_terminal' ? mobileActionRef.current?.reload() : qrActionRef.current?.reload(); } }, }); }, }, ]; return ( ); }, }, ]; return (
} onClick={() => { setEditingDevice(null); setDefaultType('mobile_terminal'); setDeviceModalOpen(true); }} >新增 } bodyStyle={{ padding: 0 }} > { const res = await getDeviceList(fairId, { current: params.current, pageSize: params.pageSize, type: 'mobile_terminal' }); return { data: res.rows, total: res.total, success: true }; }} /> } onClick={() => { setEditingDevice(null); setDefaultType('qr_sticker'); setDeviceModalOpen(true); }} >新增 } bodyStyle={{ padding: 0 }} > { const res = await getDeviceList(fairId, { current: params.current, pageSize: params.pageSize, type: 'qr_sticker' }); return { data: res.rows, total: res.total, success: true }; }} /> {/* 闸机管理 */} {gateStatuses.map((gate: any) => ( {gate.gateName} handleToggleGate(gate.id)} checkedChildren="开" unCheckedChildren="关" /> {gate.isOpen ? '已开启' : '已关闭'} ))} r.scanMethod === 'qr' ? '二维码' : '身份证' }, ]} request={async (params) => { const res = await getGateEntryLogs(fairId, { current: params.current, pageSize: params.pageSize }); return { data: res.rows, total: res.total, success: true }; }} /> {/* 新增/编辑设备 */} setDeviceModalOpen(false) }} initialValues={editingDevice || { type: defaultType }} onFinish={async (values) => { const res = editingDevice ? await updateDevice({ id: editingDevice.id, ...values }) : await addDevice({ fairId, ...values }); if (res.code === 200) { message.success(res.msg); setDeviceModalOpen(false); mobileActionRef.current?.reload(); qrActionRef.current?.reload(); return true; } message.error(res.msg); return false; }} > {/* 分配展位 */} setAssignModalOpen(false) }} onFinish={async (values) => { if (!assigningDevice) return; const booth = (await getBoothList(fairId, { current: 1, pageSize: 100 })).rows.find((b: any) => b.id === values.boothId); const res = await assignDevice(assigningDevice.id, values.boothId, booth?.boothNumber || '', booth?.companyName || ''); if (res.code === 200) { message.success(res.msg); setAssignModalOpen(false); mobileActionRef.current?.reload(); qrActionRef.current?.reload(); return true; } message.error(res.msg); return false; }} > { const res = await getBoothList(fairId); return res.rows.map((b: any) => ({ label: `${b.boothNumber} (${b.companyName || '未分配'})`, value: b.id })); }} placeholder="请搜索并选择展位" /> {/* 闸机扫码 */} setScanModalOpen(false) }} onFinish={async (values) => { if (!scanGate) return; const res = await simulateGateScan({ fairId, gateName: scanGate.gateName, ...values }); if (res.code === 200) { message.success(res.msg); setScanModalOpen(false); gateLogActionRef.current?.reload(); fetchGateStatuses(); return true; } message.error(res.msg); return false; }} >
); }; export default DeviceTab;