WorklistTable.tsx 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import React, { useState } from 'react';
  2. import { Table, TableColumnsType } from 'antd';
  3. import { FormattedMessage } from 'react-intl';
  4. import { Task, Task as DataType } from '@/domain/work';
  5. import type { ResizeCallbackData } from 'react-resizable';
  6. import { Resizable } from 'react-resizable';
  7. import { WorkFilter } from '@/states/patient/worklist/types/workfilter';
  8. const columnsDef = [
  9. {
  10. title: (
  11. <FormattedMessage
  12. id="worklistTable.StudyInstanceUID"
  13. defaultMessage="worklistTable.StudyInstanceUID"
  14. />
  15. ),
  16. dataIndex: 'StudyInstanceUID',
  17. },
  18. {
  19. title: (
  20. <FormattedMessage
  21. id="worklistTable.StudyID"
  22. defaultMessage="worklistTable.StudyID"
  23. />
  24. ),
  25. dataIndex: 'StudyID',
  26. },
  27. {
  28. title: (
  29. <FormattedMessage
  30. id="worklistTable.SpecificCharacterSet"
  31. defaultMessage="worklistTable.SpecificCharacterSet"
  32. />
  33. ),
  34. dataIndex: 'SpecificCharacterSet',
  35. },
  36. {
  37. title: (
  38. <FormattedMessage
  39. id="worklistTable.AccessionNumber"
  40. defaultMessage="worklistTable.AccessionNumber"
  41. />
  42. ),
  43. dataIndex: 'AccessionNumber',
  44. },
  45. {
  46. title: (
  47. <FormattedMessage
  48. id="worklistTable.PatientID"
  49. defaultMessage="worklistTable.PatientID"
  50. />
  51. ),
  52. dataIndex: 'PatientID',
  53. },
  54. {
  55. title: (
  56. <FormattedMessage
  57. id="worklistTable.PatientName"
  58. defaultMessage="worklistTable.PatientName"
  59. />
  60. ),
  61. dataIndex: 'PatientName',
  62. },
  63. {
  64. title: (
  65. <FormattedMessage
  66. id="worklistTable.DisplayPatientName"
  67. defaultMessage="worklistTable.DisplayPatientName"
  68. />
  69. ),
  70. dataIndex: 'DisplayPatientName',
  71. },
  72. {
  73. title: (
  74. <FormattedMessage
  75. id="worklistTable.PatientSize"
  76. defaultMessage="worklistTable.PatientSize"
  77. />
  78. ),
  79. dataIndex: 'PatientSize',
  80. },
  81. {
  82. title: (
  83. <FormattedMessage
  84. id="worklistTable.PatientAge"
  85. defaultMessage="worklistTable.PatientAge"
  86. />
  87. ),
  88. dataIndex: 'PatientAge',
  89. },
  90. {
  91. title: (
  92. <FormattedMessage
  93. id="worklistTable.PatientSex"
  94. defaultMessage="worklistTable.PatientSex"
  95. />
  96. ),
  97. dataIndex: 'PatientSex',
  98. },
  99. {
  100. title: (
  101. <FormattedMessage
  102. id="worklistTable.AdmittingTime"
  103. defaultMessage="worklistTable.AdmittingTime"
  104. />
  105. ),
  106. dataIndex: 'AdmittingTime',
  107. },
  108. {
  109. title: (
  110. <FormattedMessage
  111. id="worklistTable.RegSource"
  112. defaultMessage="worklistTable.RegSource"
  113. />
  114. ),
  115. dataIndex: 'RegSource',
  116. },
  117. {
  118. title: (
  119. <FormattedMessage
  120. id="worklistTable.StudyStatus"
  121. defaultMessage="worklistTable.StudyStatus"
  122. />
  123. ),
  124. dataIndex: 'StudyStatus',
  125. },
  126. {
  127. title: (
  128. <FormattedMessage
  129. id="worklistTable.RequestedProcedureID"
  130. defaultMessage="worklistTable.RequestedProcedureID"
  131. />
  132. ),
  133. dataIndex: 'RequestedProcedureID',
  134. },
  135. {
  136. title: (
  137. <FormattedMessage
  138. id="worklistTable.PerformedProtocolCodeValue"
  139. defaultMessage="worklistTable.PerformedProtocolCodeValue"
  140. />
  141. ),
  142. dataIndex: 'PerformedProtocolCodeValue',
  143. },
  144. {
  145. title: (
  146. <FormattedMessage
  147. id="worklistTable.PerformedProtocolCodeMeaning"
  148. defaultMessage="worklistTable.PerformedProtocolCodeMeaning"
  149. />
  150. ),
  151. dataIndex: 'PerformedProtocolCodeMeaning',
  152. },
  153. {
  154. title: (
  155. <FormattedMessage
  156. id="worklistTable.PerformedProcedureStepID"
  157. defaultMessage="worklistTable.PerformedProcedureStepID"
  158. />
  159. ),
  160. dataIndex: 'PerformedProcedureStepID',
  161. },
  162. {
  163. title: (
  164. <FormattedMessage
  165. id="worklistTable.StudyDescription"
  166. defaultMessage="worklistTable.StudyDescription"
  167. />
  168. ),
  169. dataIndex: 'StudyDescription',
  170. },
  171. {
  172. title: (
  173. <FormattedMessage
  174. id="worklistTable.StudyStartDatetime"
  175. defaultMessage="worklistTable.StudyStartDatetime"
  176. />
  177. ),
  178. dataIndex: 'StudyStartDatetime',
  179. },
  180. {
  181. title: (
  182. <FormattedMessage
  183. id="worklistTable.ScheduledProcedureStepStartDate"
  184. defaultMessage="worklistTable.ScheduledProcedureStepStartDate"
  185. />
  186. ),
  187. dataIndex: 'ScheduledProcedureStepStartDate',
  188. },
  189. {
  190. title: (
  191. <FormattedMessage
  192. id="worklistTable.StudyLock"
  193. defaultMessage="worklistTable.StudyLock"
  194. />
  195. ),
  196. dataIndex: 'StudyLock',
  197. },
  198. {
  199. title: (
  200. <FormattedMessage
  201. id="worklistTable.OperatorID"
  202. defaultMessage="worklistTable.OperatorID"
  203. />
  204. ),
  205. dataIndex: 'OperatorID',
  206. },
  207. {
  208. title: (
  209. <FormattedMessage
  210. id="worklistTable.Modality"
  211. defaultMessage="worklistTable.Modality"
  212. />
  213. ),
  214. dataIndex: 'Modality',
  215. },
  216. {
  217. title: (
  218. <FormattedMessage
  219. id="worklistTable.Views"
  220. defaultMessage="worklistTable.Views"
  221. />
  222. ),
  223. dataIndex: 'Views',
  224. },
  225. {
  226. title: (
  227. <FormattedMessage
  228. id="worklistTable.Thickness"
  229. defaultMessage="worklistTable.Thickness"
  230. />
  231. ),
  232. dataIndex: 'Thickness',
  233. },
  234. {
  235. title: (
  236. <FormattedMessage
  237. id="worklistTable.PatientType"
  238. defaultMessage="worklistTable.PatientType"
  239. />
  240. ),
  241. dataIndex: 'PatientType',
  242. },
  243. {
  244. title: (
  245. <FormattedMessage
  246. id="worklistTable.StudyType"
  247. defaultMessage="worklistTable.StudyType"
  248. />
  249. ),
  250. dataIndex: 'StudyType',
  251. },
  252. {
  253. title: (
  254. <FormattedMessage
  255. id="worklistTable.QRCode"
  256. defaultMessage="worklistTable.QRCode"
  257. />
  258. ),
  259. dataIndex: 'QRCode',
  260. },
  261. {
  262. title: (
  263. <FormattedMessage
  264. id="worklistTable.IsExported"
  265. defaultMessage="worklistTable.IsExported"
  266. />
  267. ),
  268. dataIndex: 'IsExported',
  269. },
  270. {
  271. title: (
  272. <FormattedMessage
  273. id="worklistTable.IsEdited"
  274. defaultMessage="worklistTable.IsEdited"
  275. />
  276. ),
  277. dataIndex: 'IsEdited',
  278. },
  279. {
  280. title: (
  281. <FormattedMessage
  282. id="worklistTable.WorkRef"
  283. defaultMessage="worklistTable.WorkRef"
  284. />
  285. ),
  286. dataIndex: 'WorkRef',
  287. },
  288. {
  289. title: (
  290. <FormattedMessage
  291. id="worklistTable.IsAppended"
  292. defaultMessage="worklistTable.IsAppended"
  293. />
  294. ),
  295. dataIndex: 'IsAppended',
  296. },
  297. {
  298. title: (
  299. <FormattedMessage
  300. id="worklistTable.CreationTime"
  301. defaultMessage="worklistTable.CreationTime"
  302. />
  303. ),
  304. dataIndex: 'CreationTime',
  305. },
  306. {
  307. title: (
  308. <FormattedMessage
  309. id="worklistTable.MappedStatus"
  310. defaultMessage="worklistTable.MappedStatus"
  311. />
  312. ),
  313. dataIndex: 'MappedStatus',
  314. },
  315. {
  316. title: (
  317. <FormattedMessage
  318. id="worklistTable.IsDelete"
  319. defaultMessage="worklistTable.IsDelete"
  320. />
  321. ),
  322. dataIndex: 'IsDelete',
  323. },
  324. ];
  325. interface TitlePropsType {
  326. width: number;
  327. onResize: (
  328. e: React.SyntheticEvent<Element>,
  329. data: ResizeCallbackData
  330. ) => void;
  331. }
  332. const ResizableTitle: React.FC<
  333. Readonly<
  334. React.HTMLAttributes<HTMLTableCellElement | Resizable> & TitlePropsType
  335. >
  336. > = (props) => {
  337. const { onResize, width, ...restProps } = props;
  338. if (!width) {
  339. return <th {...restProps} />;
  340. }
  341. return (
  342. <Resizable
  343. width={width}
  344. height={0}
  345. minConstraints={[50, 0]} // 最小宽度 50px
  346. // maxConstraints={[500, 0]} // 最大宽度 500px
  347. handle={
  348. <span
  349. style={{
  350. position: 'absolute',
  351. right: '-5px', // 或 insetInlineEnd: "-5px"(支持逻辑属性)
  352. bottom: '0',
  353. zIndex: 1,
  354. width: '10px',
  355. height: '100%',
  356. cursor: 'col-resize',
  357. }}
  358. onClick={(e) => e.stopPropagation()}
  359. />
  360. }
  361. onResize={onResize}
  362. draggableOpts={{ enableUserSelectHack: false }}
  363. >
  364. <th {...restProps} />
  365. </Resizable>
  366. );
  367. };
  368. interface WorklistTableProps {
  369. worklistData: Task[];
  370. filters?: WorkFilter;
  371. page?: number;
  372. pageSize?: number;
  373. selectedIds: string[];
  374. handleRowClick: (record: Task) => void;
  375. handleRowDoubleClick: (record: Task) => void;
  376. }
  377. const WorklistTable: React.FC<WorklistTableProps> = ({
  378. worklistData,
  379. // filters,
  380. // page,
  381. // pageSize,
  382. selectedIds,
  383. handleRowClick,
  384. handleRowDoubleClick,
  385. }) => {
  386. const [columns, setColumns] = useState<TableColumnsType<DataType>>(
  387. columnsDef.map((col) => ({
  388. ...col,
  389. width: 150, // 默认宽度
  390. }))
  391. );
  392. const handleResize =
  393. (index: number) =>
  394. (_: React.SyntheticEvent<Element>, { size }: ResizeCallbackData) => {
  395. console.log('Resizing column:', index, size);
  396. const newColumns = [...columns];
  397. newColumns[index] = {
  398. ...newColumns[index],
  399. width: size.width,
  400. };
  401. setColumns(newColumns);
  402. };
  403. const mergedColumns = columns.map<TableColumnsType<DataType>[number]>(
  404. (col, index) => ({
  405. ...col,
  406. onHeaderCell: (column: TableColumnsType<DataType>[number]) => ({
  407. width: column.width,
  408. onResize: handleResize(index) as React.ReactEventHandler<HTMLElement>,
  409. style: { whiteSpace: 'nowrap' },
  410. }),
  411. onCell: () => ({ style: { whiteSpace: 'nowrap' } }),
  412. })
  413. );
  414. return (
  415. <Table<DataType>
  416. bordered
  417. className="px-4"
  418. columns={mergedColumns}
  419. scroll={{ x: 'max-content' }}
  420. components={{ header: { cell: ResizableTitle } }}
  421. dataSource={worklistData}
  422. rowKey="StudyID"
  423. onRow={(record) => ({
  424. onClick: () => handleRowClick(record),
  425. onDoubleClick: () => handleRowDoubleClick(record),
  426. })}
  427. rowHoverable={false}
  428. rowClassName={(record) =>
  429. selectedIds.includes(record.StudyID)
  430. ? 'bg-yellow-500 hover:bg-yellow-800'
  431. : ''
  432. }
  433. />
  434. );
  435. };
  436. export default WorklistTable;