import React, { useState, useEffect, useRef } from 'react'; import { getPortraitUrl, hasPortrait } from '@/API/patient/portraitActions'; import { CloseOutlined, LoadingOutlined } from '@ant-design/icons'; import { Spin } from 'antd'; import { Task } from '@/domain/work'; /** * 患者照片浮动显示组件 Props */ interface PatientPortraitFloatProps { /** 患者数据,直接从 handleRowClick 传入 */ patient: Task | null; /** 关闭回调 */ onClose?: () => void; } /** * 患者照片浮动显示组件 * * 功能特性: * 1. 浮动在页面右上角(可拖拽调整位置) * 2. 显示后3秒自动关闭,带动态倒计时 * 3. 提供手动关闭按钮 * 4. 只在有患者且有照片时显示 * 5. 支持加载状态和错误处理 */ const PatientPortraitFloat: React.FC = ({ patient, onClose }) => { const [isVisible, setIsVisible] = useState(false); const [imageLoading, setImageLoading] = useState(false); const [imageError, setImageError] = useState(false); const [imageUrl, setImageUrl] = useState(''); const [countdown, setCountdown] = useState(3); // 倒计时状态 // 拖拽相关状态 const [position, setPosition] = useState({ x: 20, y: 20 }); // 初始位置:右上角 const [isDragging, setIsDragging] = useState(false); const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); const floatRef = useRef(null); // 定时器引用 const countdownIntervalRef = useRef(null); // 关闭组件 const handleClose = () => { setIsVisible(false); if (countdownIntervalRef.current) { clearInterval(countdownIntervalRef.current); } onClose?.(); // 调用父组件的关闭回调 }; // 监听患者变化 useEffect(() => { if (patient && hasPortrait(patient.portrait_status)) { // 有照片,显示组件 setIsVisible(true); setImageLoading(true); setImageError(false); setCountdown(3); // 重置倒计时 const url = getPortraitUrl(patient.portrait_file || ''); console.log(`得到的照片路径是: ${url}`); setImageUrl(url); // 清除旧的定时器 if (countdownIntervalRef.current) { clearInterval(countdownIntervalRef.current); } // 启动倒计时定时器,每秒更新一次 countdownIntervalRef.current = setInterval(() => { setCountdown((prev) => { if (prev <= 1) { handleClose(); return 0; } return prev - 1; }); }, 1000); } else { // 无照片或未选中,隐藏组件 setIsVisible(false); setImageUrl(''); if (countdownIntervalRef.current) { clearInterval(countdownIntervalRef.current); } } return () => { if (countdownIntervalRef.current) { clearInterval(countdownIntervalRef.current); } }; }, [patient, onClose]); // 图片加载完成 const handleImageLoad = () => { setImageLoading(false); setImageError(false); }; // 图片加载失败 const handleImageError = () => { setImageLoading(false); setImageError(true); }; // 拖拽开始 const handleMouseDown = (e: React.MouseEvent) => { // 如果点击的是关闭按钮,不触发拖拽 if ((e.target as HTMLElement).closest('.close-btn')) { return; } setIsDragging(true); setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y, }); e.preventDefault(); }; // 拖拽中 useEffect(() => { const handleMouseMove = (e: MouseEvent) => { if (isDragging) { const newX = e.clientX - dragStart.x; const newY = e.clientY - dragStart.y; // 限制在窗口范围内 const maxX = window.innerWidth - (floatRef.current?.offsetWidth || 300); const maxY = window.innerHeight - (floatRef.current?.offsetHeight || 300); setPosition({ x: Math.max(0, Math.min(newX, maxX)), y: Math.max(0, Math.min(newY, maxY)), }); } }; const handleMouseUp = () => { setIsDragging(false); }; if (isDragging) { document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); } return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging, dragStart]); if (!isVisible) return null; return (
{/* 标题栏 */}
患者照片 {patient && ( {patient.PatientName} )} {countdown}秒
{/* 照片区域 */}
{imageLoading && (
} />
)} {imageError && !imageLoading && (
照片加载失败
)} {imageUrl && !imageError && ( 患者照片 )}
); }; export default PatientPortraitFloat;