import { useCallback, useEffect, useState } from 'react'; import { Button, Input, message, Popconfirm, Result, Select, Space, Table, Tag, Badge } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; import { deviceApi, type DeviceItem } from '../../api/health/devices'; import { DEVICE_TYPE_OPTIONS, DEVICE_TYPE_COLOR, DEVICE_STATUS_OPTIONS } from '../../constants/health'; import { PatientSelect } from './components/PatientSelect'; import { usePermission } from '../../hooks/usePermission'; function formatTime(val?: string | null): string { if (!val) return '-'; return dayjs(val).format('YYYY-MM-DD HH:mm'); } export default function DeviceManage() { const { hasPermission } = usePermission('health.devices.list'); if (!hasPermission) return ; const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [page, setPage] = useState(1); const [filterPatientId, setFilterPatientId] = useState(''); const [filterDeviceType, setFilterDeviceType] = useState(undefined); const [filterStatus, setFilterStatus] = useState(undefined); const fetchDevices = useCallback(async () => { setLoading(true); try { const res = await deviceApi.listDevices({ page, page_size: 20, ...(filterPatientId ? { patient_id: filterPatientId } : {}), ...(filterDeviceType ? { device_type: filterDeviceType } : {}), ...(filterStatus ? { status: filterStatus } : {}), }); setData(res.data); setTotal(res.total); } catch { message.error('加载设备列表失败'); } finally { setLoading(false); } }, [page, filterPatientId, filterDeviceType, filterStatus]); useEffect(() => { fetchDevices(); }, [fetchDevices]); const handleUnbind = async (record: DeviceItem) => { try { await deviceApi.unbindDevice(record.id, record.version); message.success('设备已解绑'); fetchDevices(); } catch { message.error('解绑失败'); } }; const columns: ColumnsType = [ { title: '设备 ID', dataIndex: 'device_id', width: 120, render: (v: string) => v.slice(0, 8), }, { title: '设备型号', dataIndex: 'device_model', width: 160, }, { title: '设备类型', dataIndex: 'device_type', width: 100, render: (v: string) => { const label = DEVICE_TYPE_OPTIONS.find((d) => d.value === v)?.label || v; return {label}; }, }, { title: '状态', dataIndex: 'status', width: 80, render: (v: string) => { const config: Record = { online: { color: 'success', label: '在线' }, offline: { color: 'default', label: '离线' }, paired: { color: 'processing', label: '已配对' }, error: { color: 'error', label: '异常' }, }; const c = config[v]; return c ? : {v || '-'}; }, }, { title: '连接方式', dataIndex: 'connection_type', width: 90, render: (v: string) => { const map: Record = { ble: '蓝牙', gateway: '网关', manual: '手动' }; return {map[v] ?? v ?? '-'}; }, }, { title: '固件版本', dataIndex: 'firmware_version', width: 110, render: (v: string) => v ?? '-', }, { title: '绑定时间', dataIndex: 'bound_at', width: 170, render: (v: string) => formatTime(v), }, { title: '最后同步', dataIndex: 'last_sync_at', width: 170, render: (v: string) => formatTime(v), }, { title: '操作', width: 80, render: (_, record) => ( handleUnbind(record)} okText="确定" cancelText="取消" > ), }, ]; return (

设备管理

setFilterPatientId(val || '')} placeholder="搜索患者" />
rowKey="id" columns={columns} dataSource={data} loading={loading} pagination={{ current: page, total, pageSize: 20, showTotal: (t) => `共 ${t} 条`, onChange: (p) => setPage(p), }} />
); }