InvertContrastModal.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /**
  2. * InvertContrastModal - 反色对比Modal组件
  3. *
  4. * 完整布局:左侧缩略图列表 + 中间对比区域 + 底部工具栏
  5. */
  6. import React, { useEffect } from 'react';
  7. import { Modal } from 'antd';
  8. import * as cornerstone from '@cornerstonejs/core';
  9. import { useSelector, useDispatch } from 'react-redux';
  10. import { RootState, AppDispatch } from '@/states/store';
  11. import {
  12. selectIsModalOpen,
  13. selectSelectedPositions,
  14. closeInvertContrastModal,
  15. updateSelectedPositions
  16. } from '@/states/view/invertContrastSlice';
  17. import { StackViewerWithErrorBoundary, invertContrast } from './viewers/stack.image.viewer';
  18. import InvertContrastThumbnailList from './InvertContrastThumbnailList';
  19. import InvertContrastToolbar from './InvertContrastToolbar';
  20. const InvertContrastModal: React.FC = () => {
  21. const dispatch = useDispatch<AppDispatch>();
  22. const isOpen = useSelector(selectIsModalOpen);
  23. const selectedPositions = useSelector(selectSelectedPositions);
  24. const renderingEngineId = 'invertContrastEngine'; // 独立的渲染引擎
  25. // 初始化Modal的渲染引擎
  26. useEffect(() => {
  27. if (isOpen) {
  28. try {
  29. if (!cornerstone.getRenderingEngine(renderingEngineId)) {
  30. console.log(`[InvertContrastModal] Creating new rendering engine: ${renderingEngineId}`);
  31. new cornerstone.RenderingEngine(renderingEngineId);
  32. }
  33. } catch (error) {
  34. console.error('[InvertContrastModal] Failed to create rendering engine:', error);
  35. }
  36. }
  37. }, [isOpen, renderingEngineId]);
  38. // 获取左图和右图的URL
  39. const leftImageUrl = selectedPositions[0] || '';
  40. const rightImageUrl = selectedPositions[1] || selectedPositions[0] || ''; // 如果只有一个选择,右图和左图相同但应用反色
  41. const handleClose = () => {
  42. console.log('[InvertContrastModal] Closing modal');
  43. dispatch(closeInvertContrastModal());
  44. };
  45. // 应用反色效果到右侧图像
  46. useEffect(() => {
  47. if (isOpen && leftImageUrl && rightImageUrl) {
  48. console.log('[InvertContrastModal] Modal opened with:', { leftImageUrl, rightImageUrl });
  49. // Modal打开后,等待右侧viewport创建完成,然后应用反色
  50. const timer = setTimeout(() => {
  51. const rightViewportId = 'invert-contrast-right';
  52. try {
  53. console.log(`[InvertContrastModal] Applying invert to ${rightViewportId} using renderingEngine ${renderingEngineId}`);
  54. invertContrast(rightViewportId);
  55. console.log('[InvertContrastModal] Invert applied successfully');
  56. } catch (error) {
  57. console.error('[InvertContrastModal] Failed to apply invert:', error);
  58. console.error('[InvertContrastModal] Error stack:', error.stack);
  59. }
  60. }, 500); // 增加延迟到500ms确保viewport完全加载
  61. return () => clearTimeout(timer);
  62. }
  63. }, [isOpen, leftImageUrl, rightImageUrl]);
  64. // 工具栏事件处理
  65. const handleRotateLeft = () => {
  66. console.log('[InvertContrastModal] Rotate left clicked');
  67. // TODO: 实现旋转逻辑
  68. };
  69. const handleRotateRight = () => {
  70. console.log('[InvertContrastModal] Rotate right clicked');
  71. // TODO: 实现旋转逻辑
  72. };
  73. const handleHorizontalFlip = () => {
  74. console.log('[InvertContrastModal] Horizontal flip clicked');
  75. // TODO: 实现水平翻转逻辑
  76. };
  77. const handleVerticalFlip = () => {
  78. console.log('[InvertContrastModal] Vertical flip clicked');
  79. // TODO: 实现垂直翻转逻辑
  80. };
  81. const handleZoomIn = () => {
  82. console.log('[InvertContrastModal] Zoom in clicked');
  83. // TODO: 实现放大逻辑
  84. };
  85. const handleZoomOut = () => {
  86. console.log('[InvertContrastModal] Zoom out clicked');
  87. // TODO: 实现缩小逻辑
  88. };
  89. const handleReset = () => {
  90. console.log('[InvertContrastModal] Reset clicked');
  91. // TODO: 实现重置逻辑
  92. };
  93. if (!isOpen) {
  94. return null;
  95. }
  96. return (
  97. <Modal
  98. title="反色对比"
  99. open={isOpen}
  100. onCancel={handleClose}
  101. width="95vw"
  102. style={{
  103. top: 20,
  104. maxWidth: '95vw',
  105. }}
  106. bodyStyle={{
  107. height: '85vh',
  108. padding: 0,
  109. display: 'flex',
  110. flexDirection: 'column',
  111. }}
  112. footer={null}
  113. destroyOnClose // 关闭时销毁内容,避免状态残留
  114. maskClosable={false} // 防止点击遮罩关闭
  115. >
  116. {/* 主内容区域 */}
  117. <div style={{
  118. display: 'flex',
  119. flex: 1,
  120. height: 'calc(100% - 50px)', // 减去工具栏高度
  121. }}>
  122. {/* 左侧:缩略图列表 */}
  123. <div style={{
  124. width: '280px',
  125. borderRight: '1px solid #d9d9d9',
  126. backgroundColor: '#fafafa',
  127. }}>
  128. <InvertContrastThumbnailList />
  129. </div>
  130. {/* 右侧:对比区域 */}
  131. <div style={{
  132. flex: 1,
  133. display: 'grid',
  134. gridTemplateColumns: '1fr 1fr',
  135. gap: '8px',
  136. padding: '16px',
  137. backgroundColor: '#000',
  138. }}>
  139. {/* 左侧对比图 */}
  140. <div style={{
  141. border: '1px solid #d9d9d9',
  142. position: 'relative',
  143. display: 'flex',
  144. flexDirection: 'column',
  145. backgroundColor: '#000'
  146. }}>
  147. <div style={{
  148. position: 'absolute',
  149. top: 8,
  150. left: 8,
  151. zIndex: 10,
  152. background: 'rgba(0,0,0,0.7)',
  153. color: '#fff',
  154. padding: '4px 8px',
  155. borderRadius: '4px',
  156. fontSize: '14px'
  157. }}>
  158. {selectedPositions.length > 1 ? '原图' : '对比图'}
  159. </div>
  160. {leftImageUrl && (
  161. <div style={{ flex: 1, overflow: 'hidden' }}>
  162. <StackViewerWithErrorBoundary
  163. imageIndex={0}
  164. imageUrls={[leftImageUrl]}
  165. viewportId="invert-contrast-left"
  166. renderingEngineId={renderingEngineId}
  167. selected={false}
  168. />
  169. </div>
  170. )}
  171. </div>
  172. {/* 右侧反色图 */}
  173. <div style={{
  174. border: '1px solid #d9d9d9',
  175. position: 'relative',
  176. display: 'flex',
  177. flexDirection: 'column',
  178. backgroundColor: '#000'
  179. }}>
  180. <div style={{
  181. position: 'absolute',
  182. top: 8,
  183. left: 8,
  184. zIndex: 10,
  185. background: 'rgba(0,0,0,0.7)',
  186. color: '#fff',
  187. padding: '4px 8px',
  188. borderRadius: '4px',
  189. fontSize: '14px'
  190. }}>
  191. 反色图
  192. </div>
  193. {rightImageUrl && (
  194. <div style={{ flex: 1, overflow: 'hidden' }}>
  195. <StackViewerWithErrorBoundary
  196. imageIndex={0}
  197. imageUrls={[rightImageUrl]}
  198. viewportId="invert-contrast-right"
  199. renderingEngineId={renderingEngineId}
  200. selected={false}
  201. />
  202. </div>
  203. )}
  204. </div>
  205. </div>
  206. </div>
  207. {/* 底部工具栏 */}
  208. <InvertContrastToolbar
  209. onRotateLeft={handleRotateLeft}
  210. onRotateRight={handleRotateRight}
  211. onHorizontalFlip={handleHorizontalFlip}
  212. onVerticalFlip={handleVerticalFlip}
  213. onZoomIn={handleZoomIn}
  214. onZoomOut={handleZoomOut}
  215. onReset={handleReset}
  216. />
  217. </Modal>
  218. );
  219. };
  220. export default InvertContrastModal;