ActionPanel.tsx 12 KB

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