InvertContrastModal.tsx 6.9 KB

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