HistoryList.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import React, { useState, useEffect } from 'react';
  2. import { Row, Col, Button, Drawer } from 'antd';
  3. import { SettingOutlined } from '@ant-design/icons';
  4. import { FormattedMessage } from 'react-intl';
  5. import { useSelector, useDispatch } from 'react-redux';
  6. import {
  7. fetchWorkThunk,
  8. historySelectionSlice,
  9. historyPaginationSlice,
  10. historyFiltersSlice,
  11. } from '../../states/patient/worklist/slices/history';
  12. import { updateThumbnailsFromHistorySelection } from '../../states/patient/worklist/slices/thumbnailListSlice';
  13. import WorklistTable from './components/WorklistTable';
  14. import OperationPanel from './components/OperationPanel';
  15. import SendPanel from '../../pages/output/SendPanel';
  16. import ThumbnailList from './components/ThumbnailList';
  17. import GenericPagination from '../../components/GenericPagination';
  18. import PatientPortraitFloat from './components/PatientPortraitFloat';
  19. import { RootState, AppDispatch } from '../../states/store';
  20. import { Task } from '@/domain/work';
  21. import worklistToExam from '../../domain/patient/worklistToExam';
  22. import { ColumnConfig, columnConfigService } from '@/config/tableColumns';
  23. import { useMultiSelection } from '@/hooks/useMultiSelection';
  24. import useEffectiveBreakpoint from '../../hooks/useEffectiveBreakpoint';
  25. const HistorylistPage: React.FC = () => {
  26. const screens = useEffectiveBreakpoint();
  27. const [drawerVisible, setDrawerVisible] = useState(false);
  28. const [selectedPatientForPortrait, setSelectedPatientForPortrait] = useState<Task | null>(null); // 照片显示用的选中患者
  29. const dispatch: AppDispatch = useDispatch();
  30. const filters = useSelector((state: RootState) => state.historyFilters);
  31. const page = useSelector((state: RootState) => state.historyPagination.page);
  32. const pageSize = useSelector(
  33. (state: RootState) => state.historyPagination.pageSize
  34. );
  35. const selectedIds = useSelector(
  36. (state: RootState) => state.historySelection.selectedIds
  37. );
  38. const historylistData = useSelector(
  39. (state: RootState) => state.historyEntities.data
  40. );
  41. const currentPanel = useSelector(
  42. (state: RootState) => state.historyPanelSwitch.currentPanel
  43. );
  44. const productName = useSelector(
  45. (state: RootState) => state.product.productName
  46. );
  47. const [columnConfig, setColumnConfig] = useState<ColumnConfig[]>([]);
  48. // 获取和应用列配置
  49. useEffect(() => {
  50. columnConfigService
  51. .getColumnConfig('history')
  52. .then((config) => {
  53. setColumnConfig(config.columns);
  54. console.log(`成功为history设置列的配置信息`);
  55. })
  56. .catch((error) => {
  57. console.error('Failed to load history column config:', error);
  58. // 失败时使用空配置,表格会显示所有列
  59. setColumnConfig([]);
  60. });
  61. }, []);
  62. // 同步分页状态到过滤器
  63. useEffect(() => {
  64. console.log('[historylist] Syncing pagination to filters:', {
  65. page,
  66. pageSize,
  67. });
  68. dispatch(
  69. historyFiltersSlice.actions.setFilters({
  70. page,
  71. page_size: pageSize,
  72. })
  73. );
  74. }, [dispatch, page, pageSize]);
  75. useEffect(() => {
  76. console.log(
  77. 'Fetching historylist data with filters:',
  78. filters,
  79. 'page:',
  80. page,
  81. 'pageSize:',
  82. pageSize
  83. );
  84. dispatch(fetchWorkThunk({ page, pageSize, filters }));
  85. }, [dispatch, filters, page, pageSize]);
  86. // 使用多选 Hook
  87. const { handleRowClick } = useMultiSelection({
  88. selectedIds,
  89. onSelectionChange: (newIds) => {
  90. console.log('Selected IDs changed:', newIds);
  91. // 如果只有一个选中项,设置选中患者用于显示照片
  92. if (newIds.length === 1) {
  93. const selectedRecord = historylistData.find(item => item.StudyID === newIds[0]);
  94. if (selectedRecord) {
  95. setSelectedPatientForPortrait(selectedRecord);
  96. }
  97. } else {
  98. // 多选时清空患者照片
  99. setSelectedPatientForPortrait(null);
  100. }
  101. // 更新 Redux 状态(用于其他功能)
  102. dispatch(historySelectionSlice.actions.setSelectedIds(newIds));
  103. dispatch(updateThumbnailsFromHistorySelection(newIds));
  104. },
  105. enableMultiSelect: true,
  106. });
  107. // 处理行点击事件(兼容现有功能)
  108. const handleRowClickInternal = (record: Task, event?: React.MouseEvent) => {
  109. handleRowClick(record, event || {} as React.MouseEvent);
  110. };
  111. const handleRowDoubleClick = (record: Task) => {
  112. console.log(
  113. '[WorklistTable] Row double-clicked:',
  114. JSON.stringify(record, null, 2)
  115. );
  116. worklistToExam(record);
  117. };
  118. return (
  119. <div className="h-full">
  120. {/* 患者照片浮动组件 */}
  121. <PatientPortraitFloat
  122. patient={selectedPatientForPortrait}
  123. onClose={() => setSelectedPatientForPortrait(null)}
  124. />
  125. {screens.xs ? (
  126. <>
  127. <div className="flex-1 overflow-auto">
  128. <WorklistTable
  129. productName={productName}
  130. columnConfig={columnConfig}
  131. worklistData={historylistData}
  132. filters={filters}
  133. page={page}
  134. pageSize={pageSize}
  135. selectedIds={selectedIds}
  136. handleRowClick={handleRowClickInternal}
  137. handleRowDoubleClick={handleRowDoubleClick}
  138. />
  139. </div>
  140. <GenericPagination
  141. paginationSelector={(state) => state.historyPagination}
  142. entitiesSelector={(state) => state.historyEntities}
  143. paginationActions={historyPaginationSlice.actions}
  144. className="border-t"
  145. />
  146. <Button
  147. type="primary"
  148. shape="circle"
  149. icon={<SettingOutlined />}
  150. className="fixed bottom-6 right-6 z-50"
  151. onClick={() => setDrawerVisible(true)}
  152. />
  153. <Drawer
  154. title={
  155. <FormattedMessage
  156. id="worklist.operationPanel"
  157. defaultMessage="worklist.operationPanel"
  158. />
  159. }
  160. placement="left"
  161. onClose={() => setDrawerVisible(false)}
  162. open={drawerVisible}
  163. width={300}
  164. >
  165. {currentPanel === 'OperationPanel' ? (
  166. <OperationPanel />
  167. ) : (
  168. <SendPanel />
  169. )}
  170. </Drawer>
  171. </>
  172. ) : (
  173. <Row className="h-full">
  174. <Col
  175. span={screens.lg ? 18 : screens.md ? 20 : 24}
  176. className="h-full flex flex-col"
  177. >
  178. <div className="flex-1 flex flex-col">
  179. <div className="flex-1 overflow-auto">
  180. <WorklistTable
  181. productName={productName}
  182. columnConfig={columnConfig}
  183. worklistData={historylistData}
  184. filters={filters}
  185. page={page}
  186. pageSize={pageSize}
  187. selectedIds={selectedIds}
  188. handleRowClick={handleRowClickInternal}
  189. handleRowDoubleClick={handleRowDoubleClick}
  190. />
  191. </div>
  192. <GenericPagination
  193. paginationSelector={(state) => state.historyPagination}
  194. entitiesSelector={(state) => state.historyEntities}
  195. paginationActions={historyPaginationSlice.actions}
  196. className="border-t"
  197. />
  198. </div>
  199. <div className="h-60 border-t border-gray-200">
  200. <ThumbnailList />
  201. </div>
  202. </Col>
  203. <Col
  204. span={screens.lg ? 6 : screens.md ? 4 : 0}
  205. className="h-full overflow-auto"
  206. >
  207. {currentPanel === 'OperationPanel' ? (
  208. <OperationPanel />
  209. ) : (
  210. <SendPanel />
  211. )}
  212. </Col>
  213. </Row>
  214. )}
  215. </div>
  216. );
  217. };
  218. export default HistorylistPage;