import { useCallback, useEffect, useState } from 'react' import toast from 'react-hot-toast' import { Loader2, LogOut, MonitorSmartphone, RefreshCw, X } from 'lucide-react' import { devicesAPI, type DeviceInfo } from '../api/devices' import type { User } from '../types' // AdminUserDevicesDialog 让管理员代管某个用户的登录设备。 // // 使用场景:用户忘记密码/账号疑似外借/需要清掉旧电视端会话时,管理员不必 // 让用户自己操作,也不必进数据库。 type AdminUserDevicesDialogProps = { user: User | null isOpen: boolean onClose: () => void } export function AdminUserDevicesDialog({ user, isOpen, onClose }: AdminUserDevicesDialogProps) { const [devices, setDevices] = useState([]) const [loading, setLoading] = useState(false) const [busyId, setBusyId] = useState(null) const [kickingAll, setKickingAll] = useState(false) const load = useCallback(async (userId: string) => { setLoading(true) try { const rows = await devicesAPI.listForUser(userId) setDevices(rows) } catch { toast.error('加载设备列表失败') setDevices([]) } finally { setLoading(false) } }, []) useEffect(() => { if (!isOpen || !user) return setDevices([]) void load(user.id) }, [isOpen, user, load]) if (!isOpen || !user) return null const kick = async (device: DeviceInfo) => { const id = device.device_id || device.id setBusyId(id) try { await devicesAPI.kickForUser(user.id, id) toast.success('设备已下线') await load(user.id) } catch { toast.error('踢下线失败') } finally { setBusyId(null) } } const kickAll = async () => { setKickingAll(true) try { await devicesAPI.kickAllForUser(user.id) toast.success(`已下线 ${user.username} 的全部设备`) await load(user.id) } catch { toast.error('操作失败') } finally { setKickingAll(false) } } return (
e.stopPropagation()} >

登录设备管理

用户:{user.username} 共 {devices.length} 台

{loading ? (
正在加载设备…
) : devices.length === 0 ? (
该用户暂无设备记录
) : (
    {devices.map((device) => (
  • {device.device_name || device.client || '未知设备'} {device.online && ( 在线 )} {device.playing && ( 播放中 )} {device.kicked && ( 已下线 )} {device.warnings > 0 && ( 警告 {device.warnings} )}
    {device.client && {device.client}} {device.last_ip && {device.last_ip}} {device.last_seen_at && 最近活跃 {formatTime(device.last_seen_at)}}
  • ))}
)}
) } function formatTime(value: string): string { const date = new Date(value) if (Number.isNaN(date.getTime())) return value return date.toLocaleString() }