| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- import React from 'react';
- import { useSelector, useDispatch } from 'react-redux';
- import { Radio } from 'antd';
- import { RootState, AppDispatch } from '../../../states/store';
- import { updateSelectedPositions } from '../../../states/view/invertContrastSlice';
- import { getExposedImageUrl, getViewIconUrl, getDcmImageUrl } from '../../../API/bodyPosition';
- import { ExtendedBodyPosition } from '../../../states/exam/bodyPositionListSlice';
- import { selectSelectedPositions } from '../../../states/view/invertContrastSlice';
- /**
- * InvertContrastThumbnailList - 反色对比缩略图列表组件
- *
- * 功能:显示当前检查的所有体位缩略图,支持单选用于反色对比
- * 选择一个体位:左侧显示原图,右侧显示反色图
- */
- const InvertContrastThumbnailList: React.FC = () => {
- const dispatch = useDispatch<AppDispatch>();
- // 获取所有体位
- const bodyPositions = useSelector(
- (state: RootState) => state.bodyPositionList.bodyPositions
- );
- // 获取当前选中的体位URLs
- const selectedPositions = useSelector(selectSelectedPositions);
- /**
- * 处理体位选择变更 - 单选模式,每次选择替换当前选择
- */
- const handlePositionSelect = (bodyPosition: ExtendedBodyPosition) => {
- const dcmUrl = getDcmImageUrl(bodyPosition.sop_instance_uid);
- // 单选模式:直接替换为当前选择的体位
- dispatch(updateSelectedPositions({ positions: [dcmUrl] }));
- };
- /**
- * 获取体位对应的图像URL
- */
- const getPositionImageUrl = (bodyPosition: ExtendedBodyPosition): string => {
- return bodyPosition.dview.expose_status === 'Exposed'
- ? getExposedImageUrl(bodyPosition.sop_instance_uid)
- : getViewIconUrl(bodyPosition.view_icon_name);
- };
- /**
- * 检查体位是否被选中
- */
- const isPositionSelected = (bodyPosition: ExtendedBodyPosition): boolean => {
- const dcmUrl = getDcmImageUrl(bodyPosition.sop_instance_uid);
- return selectedPositions.includes(dcmUrl);
- };
- return (
- <div
- className="flex flex-col gap-2 overflow-y-auto"
- style={{
- height: '100%',
- padding: '8px',
- backgroundColor: '#f5f5f5',
- borderRadius: '4px'
- }}
- >
- <div className="text-sm font-medium text-gray-700 mb-2 px-2">
- 选择对比体位
- </div>
- {bodyPositions.map((bodyPosition, index) => {
- const imageUrl = getPositionImageUrl(bodyPosition);
- const isSelected = isPositionSelected(bodyPosition);
- return (
- <div
- key={bodyPosition.sop_instance_uid || index}
- className={`relative border rounded-md overflow-hidden transition-all ${
- isSelected ? 'border-blue-500 shadow-md' : 'border-gray-300'
- } cursor-pointer hover:shadow-sm`}
- style={{
- width: '100%',
- aspectRatio: '1',
- backgroundColor: '#fff'
- }}
- onClick={() => handlePositionSelect(bodyPosition)}
- >
- {/* 单选按钮 */}
- <div className="absolute top-1 left-1 z-20">
- <Radio
- checked={isSelected}
- style={{
- backgroundColor: 'rgba(255,255,255,0.9)',
- borderRadius: '2px',
- padding: '2px'
- }}
- />
- </div>
- {/* 体位名称标签 */}
- <div className="absolute bottom-1 left-1 text-xs bg-black bg-opacity-70 text-white px-1 py-0.5 rounded z-10">
- {bodyPosition.view_name}
- </div>
- {/* 缩略图 */}
- <div className={`${isSelected ? 'ring-2 ring-blue-500' : ''} w-full h-full overflow-hidden`}>
- <img
- src={imageUrl}
- alt={bodyPosition.view_name}
- className="w-full h-full object-cover cursor-pointer"
- draggable={false}
- />
- </div>
- {/* 状态标签 */}
- {bodyPosition.dview.expose_status === 'Exposed' && (
- <div className="absolute top-1 right-1 z-10 flex gap-1">
- {bodyPosition.dview.judged_status && (
- <span
- className={`text-xs px-1 py-0.5 rounded text-white ${
- bodyPosition.dview.judged_status === 'Accept'
- ? 'bg-green-500'
- : bodyPosition.dview.judged_status === 'Reject'
- ? 'bg-red-500'
- : 'bg-yellow-500'
- }`}
- >
- {bodyPosition.dview.judged_status === 'Accept' ? '✓' :
- bodyPosition.dview.judged_status === 'Reject' ? '✗' : '?'}
- </span>
- )}
- </div>
- )}
- </div>
- );
- })}
- </div>
- );
- };
- export default InvertContrastThumbnailList;
|