import { useState, useCallback, useEffect } from 'react'; import { Button, Descriptions, Form, Input, message, Modal, Popconfirm, Select, Space, Table, Tag, Tabs, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; import { useParams, useNavigate } from 'react-router-dom'; import { bleGatewayApi, type BleGateway, type GatewayBinding, type CreateBindingReq, GATEWAY_STATUS_LABEL, GATEWAY_STATUS_COLOR, BINDING_STATUS_COLOR, } from '../../api/health/bleGateways'; import { PageContainer } from '../../components/PageContainer'; export default function BleGatewayDetail() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [gateway, setGateway] = useState(null); const [loading, setLoading] = useState(false); // Bindings state const [bindings, setBindings] = useState([]); const [bindingsTotal, setBindingsTotal] = useState(0); const [bindingsPage, setBindingsPage] = useState(1); const [bindingsLoading, setBindingsLoading] = useState(false); // Binding modal const [bindModalOpen, setBindModalOpen] = useState(false); const [bindSubmitting, setBindSubmitting] = useState(false); const [bindForm] = Form.useForm(); const pageSize = 20; const fetchGateway = useCallback(async () => { if (!id) return; setLoading(true); try { const resp = await bleGatewayApi.get(id); setGateway(resp); } catch { message.error('加载网关详情失败'); } finally { setLoading(false); } }, [id]); const fetchBindings = useCallback(async (p: number) => { if (!id) return; setBindingsLoading(true); try { const resp = await bleGatewayApi.listBindings(id, { page: p, page_size: pageSize }); setBindings(resp.data); setBindingsTotal(resp.total); setBindingsPage(p); } catch { message.error('加载绑定列表失败'); } finally { setBindingsLoading(false); } }, [id]); useEffect(() => { fetchGateway(); fetchBindings(1); }, [fetchGateway, fetchBindings]); const handleBindPatient = () => { bindForm.resetFields(); setBindModalOpen(true); }; const handleSubmitBind = async () => { try { const values = await bindForm.validateFields(); setBindSubmitting(true); const req: CreateBindingReq = values; await bleGatewayApi.bindPatient(id!, req); message.success('患者绑定成功'); setBindModalOpen(false); fetchBindings(bindingsPage); fetchGateway(); } catch { // validation } finally { setBindSubmitting(false); } }; const handleUnbind = async (binding: GatewayBinding) => { try { await bleGatewayApi.unbindPatient(id!, binding.id, binding.version); message.success('已解除绑定'); fetchBindings(bindingsPage); fetchGateway(); } catch { message.error('解除绑定失败'); } }; const bindingColumns: ColumnsType = [ { title: '患者 ID', dataIndex: 'patient_id', width: 200, ellipsis: true, }, { title: '外设 MAC', dataIndex: 'peripheral_mac', width: 160, render: (v: string) => v ?? '-', }, { title: '设备类型', dataIndex: 'device_type', width: 120, render: (v: string) => v ?? '-', }, { title: '状态', dataIndex: 'status', width: 100, render: (v: string) => ( {v} ), }, { title: '绑定时间', dataIndex: 'created_at', width: 170, render: (v: string) => v ? dayjs(v).format('YYYY-MM-DD HH:mm:ss') : '-', }, { title: '操作', width: 100, render: (_, record) => ( handleUnbind(record)}> ), }, ]; if (!gateway && !loading) { return ( navigate('/health/ble-gateways')}>
网关不存在或加载失败
); } return ( navigate('/health/ble-gateways')} loading={loading} > {gateway?.gateway_id} {gateway?.name} {GATEWAY_STATUS_LABEL[gateway?.status ?? ''] ?? gateway?.status} {gateway?.firmware_version ?? '-'} {gateway?.ip_address ?? '-'} {gateway?.patient_count ?? 0} {gateway?.last_heartbeat_at ? dayjs(gateway.last_heartbeat_at).format('YYYY-MM-DD HH:mm:ss') : '-'} {gateway?.created_at ? dayjs(gateway.created_at).format('YYYY-MM-DD HH:mm:ss') : '-'} rowKey="id" columns={bindingColumns} dataSource={bindings} loading={bindingsLoading} pagination={{ current: bindingsPage, pageSize, total: bindingsTotal, showTotal: (t) => `共 ${t} 条`, onChange: (p) => fetchBindings(p), }} /> ), }, ]} /> setBindModalOpen(false)} confirmLoading={bindSubmitting} width={480} >