ActionPanel.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import React from 'react';
  2. import { Button, Tooltip, Modal, message } from 'antd';
  3. import { useDispatch, useSelector } from 'react-redux';
  4. import {
  5. deleteWorkThunk,
  6. lockWorkInWorklistThunk,
  7. } from '@/states/patient/worklist/slices/workSlice';
  8. import { deleteWorkThunk as deleteWorkThunkFromHistory, lockWorkInhistorylistThunk } from '@/states/patient/worklist/slices/history';
  9. import { switchToSendPanel } from '@/states/patient/worklist/slices/historyPanelSwitchSlice';
  10. import { FormattedMessage } from 'react-intl';
  11. import { AppDispatch, RootState, useAppSelector } from '@/states/store';
  12. import Icon from '@/components/Icon';
  13. import DiagnosticReport from '../DiagnosticReport';
  14. import { Popup } from 'antd-mobile';
  15. import { setVisible } from '@/states/patient/DiagnosticReport/slice';
  16. import EditTaskModal from './EditTaskModal';
  17. import { openEditModal } from '@/states/patient/edit/editFormSlice';
  18. import { showNotImplemented } from '@/utils/notificationHelper';
  19. interface ActionButtonProps {
  20. icon: React.ReactNode;
  21. tooltip: React.ReactNode;
  22. onClick?: () => void;
  23. 'data-testid'?: string;
  24. }
  25. const ActionButton: React.FC<ActionButtonProps> = ({
  26. icon,
  27. tooltip,
  28. onClick,
  29. 'data-testid': dataTestId,
  30. }) => (
  31. <Tooltip title={tooltip}>
  32. <Button icon={icon} onClick={onClick} style={{ width: '2.5rem' }} data-testid={dataTestId} />
  33. </Tooltip>
  34. );
  35. const ActionPanel: React.FC = () => {
  36. const dispatch = useDispatch<AppDispatch>();
  37. const workSelectedIds = useSelector(
  38. (state: RootState) => state.workSelection.selectedIds
  39. );
  40. const historySelectedIds = useSelector(
  41. (state: RootState) => state.historySelection.selectedIds
  42. );
  43. const visible = useSelector(
  44. (state: RootState) => state.diagnosticReport.visible
  45. );
  46. const themeType = useAppSelector((state) => state.theme.themeType);
  47. const currentKey = useSelector(
  48. (state: RootState) => state.BusinessFlow.currentKey
  49. );
  50. const workEntities = useSelector(
  51. (state: RootState) => state.workEntities.data
  52. );
  53. const workEntitiesFromHistory = useSelector(
  54. (state: RootState) => state.historyEntities.data
  55. );
  56. const getSelectedWorkIds = () => {
  57. const selectedIds =
  58. currentKey === 'worklist' ? workSelectedIds : historySelectedIds;
  59. return selectedIds;
  60. };
  61. const getDeleteThunk = (): typeof deleteWorkThunk => {
  62. return currentKey === 'worklist'
  63. ? deleteWorkThunk
  64. : deleteWorkThunkFromHistory;
  65. };
  66. const getLockThunk = () => {
  67. return currentKey === 'worklist'
  68. ? lockWorkInWorklistThunk
  69. : lockWorkInhistorylistThunk;
  70. };
  71. const getSelectedWorks = () => {
  72. const selectedIds = getSelectedWorkIds();
  73. if (currentKey === 'worklist') {
  74. return workEntities.find((w) => selectedIds.includes(w.StudyID));
  75. } else if (currentKey === 'historylist') {
  76. return workEntitiesFromHistory.find((w) =>
  77. selectedIds.includes(w.StudyID)
  78. );
  79. }
  80. };
  81. // 使用 worklist 或 history 的选中项,取决于哪个有值
  82. const selectedIds = getSelectedWorkIds();
  83. const handleDelete = () => {
  84. if (selectedIds.length === 0) {
  85. message.warning('请先选择要删除的项目');
  86. return;
  87. }
  88. //判断是否锁定
  89. const selectedWork = getSelectedWorks();
  90. if (selectedWork?.StudyLock === 'Locked') {
  91. message.warning('锁定状态不可删除');
  92. return;
  93. }
  94. Modal.confirm({
  95. title: '确认删除',
  96. content: `确定要删除选中的 ${selectedIds.length} 个项目吗?此操作不可撤销。`,
  97. okText: '确认删除',
  98. cancelText: '取消',
  99. okButtonProps: {
  100. danger: true,
  101. 'data-testid': 'modal-confirm-delete'
  102. },
  103. cancelButtonProps: {
  104. 'data-testid': 'modal-cancel-delete'
  105. },
  106. centered: true,
  107. onOk: () => {
  108. const delThunk = getDeleteThunk();
  109. dispatch(delThunk(selectedIds));
  110. },
  111. });
  112. };
  113. const handleSend = () => {
  114. dispatch(switchToSendPanel());
  115. };
  116. const handleShowReport = () => {
  117. dispatch(setVisible(true));
  118. };
  119. const getWorksFromWorklistOrHistory = () => {
  120. return currentKey === 'worklist' ? workEntities : workEntitiesFromHistory;
  121. };
  122. const handleEdit = () => {
  123. const selectedIds = getSelectedWorkIds();
  124. if (selectedIds.length === 0) {
  125. message.warning('请先选择要编辑的任务');
  126. return;
  127. }
  128. if (selectedIds.length > 1) {
  129. message.warning('只能编辑一个任务');
  130. return;
  131. }
  132. const works = getWorksFromWorklistOrHistory();
  133. const task = works.find((item) => item.StudyID === selectedIds[0]);
  134. if (task) {
  135. // 通过 dispatch action 传递数据到 slice
  136. dispatch(openEditModal(task));
  137. }
  138. };
  139. const handleLock = () => {
  140. const selectedIds = getSelectedWorkIds();
  141. // 2. 检查是否有选中项
  142. if (selectedIds.length === 0) {
  143. message.warning('请先选择要锁定/解锁的项目');
  144. return;
  145. }
  146. // 3. 获取第一个选中项的锁定状态
  147. const works = getWorksFromWorklistOrHistory();
  148. const selectedItem = works.find((item) => item.StudyID === selectedIds[0]);
  149. if (!selectedItem) return;
  150. // 4. 根据当前状态切换
  151. const newLockState =
  152. selectedItem.StudyLock === 'Locked' ? 'Unlocked' : 'Locked';
  153. const lockThunk = getLockThunk();
  154. // 为每个选中项执行锁定/解锁操作
  155. selectedIds.forEach((studyId) => {
  156. console.log(
  157. `锁定,触发action ,目标 studyid是 ${studyId},新状态是 ${newLockState}`
  158. );
  159. dispatch(lockThunk({ studyId, lock: newLockState }));
  160. });
  161. };
  162. return (
  163. <div className="flex flex-wrap gap-2 w-full">
  164. <ActionButton
  165. icon={
  166. <Icon
  167. module="module-patient"
  168. name="Delete"
  169. userId="base"
  170. theme="default"
  171. size="2x"
  172. state="normal"
  173. />
  174. }
  175. tooltip={
  176. <FormattedMessage
  177. id="actionPanel.deleteTask"
  178. defaultMessage="actionPanel.deleteTask"
  179. />
  180. }
  181. onClick={handleDelete}
  182. data-testid="delete-button"
  183. />
  184. <ActionButton
  185. icon={
  186. <Icon
  187. module="module-patient"
  188. name="EditPatient"
  189. userId="base"
  190. theme="default"
  191. size="2x"
  192. state="normal"
  193. />
  194. }
  195. tooltip={
  196. <FormattedMessage
  197. id="actionPanel.editPatient"
  198. defaultMessage="actionPanel.editPatient"
  199. />
  200. }
  201. onClick={handleEdit}
  202. />
  203. <ActionButton
  204. icon={
  205. <Icon
  206. module="module-patient"
  207. name="Protect"
  208. userId="base"
  209. theme="default"
  210. size="2x"
  211. state="normal"
  212. />
  213. }
  214. tooltip={
  215. <FormattedMessage
  216. id="actionPanel.lockTask"
  217. defaultMessage="actionPanel.lockTask"
  218. />
  219. }
  220. onClick={handleLock}
  221. />
  222. <ActionButton
  223. icon={
  224. <Icon
  225. module="module-patient"
  226. name="RIS"
  227. userId="base"
  228. theme="default"
  229. size="2x"
  230. state="normal"
  231. />
  232. }
  233. tooltip={
  234. <FormattedMessage
  235. id="actionPanel.risSync"
  236. defaultMessage="actionPanel.risSync"
  237. />
  238. }
  239. onClick={() => showNotImplemented('')}
  240. />
  241. <ActionButton
  242. icon={
  243. <Icon
  244. module="module-patient"
  245. name="ReRegister"
  246. userId="base"
  247. theme="default"
  248. size="2x"
  249. state="normal"
  250. />
  251. }
  252. tooltip={
  253. <FormattedMessage
  254. id="actionPanel.reRegister"
  255. defaultMessage="actionPanel.reRegister"
  256. />
  257. }
  258. onClick={() => showNotImplemented('')}
  259. />
  260. <ActionButton
  261. icon={
  262. <Icon
  263. module="module-patient"
  264. name="Sort"
  265. userId="base"
  266. theme="default"
  267. size="2x"
  268. state="normal"
  269. />
  270. }
  271. tooltip={
  272. <FormattedMessage
  273. id="actionPanel.sortList"
  274. defaultMessage="actionPanel.sortList"
  275. />
  276. }
  277. onClick={() => showNotImplemented('')}
  278. />
  279. <ActionButton
  280. icon={
  281. <Icon
  282. module="module-patient"
  283. name="Swap"
  284. userId="base"
  285. theme="default"
  286. size="2x"
  287. state="normal"
  288. />
  289. }
  290. tooltip={
  291. <FormattedMessage
  292. id="actionPanel.imageExchange"
  293. defaultMessage="actionPanel.imageExchange"
  294. />
  295. }
  296. onClick={() => showNotImplemented('')}
  297. />
  298. <ActionButton
  299. icon={
  300. <Icon
  301. module="module-patient"
  302. name="Send"
  303. userId="base"
  304. theme="default"
  305. size="2x"
  306. state="normal"
  307. />
  308. }
  309. tooltip={
  310. <FormattedMessage
  311. id="actionPanel.send"
  312. defaultMessage="actionPanel.send"
  313. />
  314. }
  315. onClick={handleSend}
  316. />
  317. <ActionButton
  318. icon={
  319. <Icon
  320. module="module-patient"
  321. name="Export"
  322. userId="base"
  323. theme="default"
  324. size="2x"
  325. state="normal"
  326. />
  327. }
  328. tooltip={
  329. <FormattedMessage
  330. id="actionPanel.export"
  331. defaultMessage="actionPanel.export"
  332. />
  333. }
  334. onClick={() => showNotImplemented('')}
  335. />
  336. <ActionButton
  337. icon={
  338. <Icon
  339. module="module-patient"
  340. name="Import"
  341. userId="base"
  342. theme="default"
  343. size="2x"
  344. state="normal"
  345. />
  346. }
  347. tooltip={
  348. <FormattedMessage
  349. id="actionPanel.import"
  350. defaultMessage="actionPanel.import"
  351. />
  352. }
  353. onClick={() => showNotImplemented('')}
  354. />
  355. <ActionButton
  356. icon={
  357. <Icon
  358. module="module-patient"
  359. name="report"
  360. userId="base"
  361. theme={themeType}
  362. size="2x"
  363. state="normal"
  364. width={40}
  365. height={40}
  366. />
  367. }
  368. tooltip={
  369. <FormattedMessage
  370. id="actionPanel.showReport"
  371. defaultMessage="actionPanel.showReport"
  372. />
  373. }
  374. onClick={handleShowReport}
  375. />
  376. <Popup
  377. visible={visible}
  378. onMaskClick={() => dispatch(setVisible(false))}
  379. position="right"
  380. bodyStyle={{ width: '100vw', height: '100vh' }}
  381. >
  382. <DiagnosticReport />
  383. </Popup>
  384. <EditTaskModal />
  385. </div>
  386. );
  387. };
  388. export default ActionPanel;