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),
}}
/>
);
}