BusinessZone.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. import React, { useState } from 'react';
  2. import { Flex } from 'antd';
  3. import { FormattedMessage } from 'react-intl';
  4. import { useSelector } from 'react-redux';
  5. import {
  6. DataState,
  7. getBtnAvailability,
  8. LocationKey,
  9. } from '@/domain/permissionMap';
  10. import { RootState } from '@/states/store';
  11. import { IconButton } from '@/components/IconButton';
  12. import Icon from '@/components/Icon';
  13. interface BusinessZoneProps {
  14. onMenuClick?: (key: string) => void;
  15. }
  16. function useItems(btnAvailability: Record<string, boolean>, productName: 'DROS' | 'VETDROS') {
  17. const isAnimalProduct = productName === 'VETDROS';
  18. return [
  19. {
  20. key: 'patient_management',
  21. label: (
  22. <FormattedMessage
  23. id={isAnimalProduct ? "animal.patient" : "patient"}
  24. defaultMessage={'语言包中没有定义patient的翻译文本'}
  25. />
  26. ),
  27. icon: 'Registration',
  28. disabled: !btnAvailability['patient_management'],
  29. children: [
  30. {
  31. key: 'register',
  32. disabled: !btnAvailability['register'],
  33. label: (
  34. <FormattedMessage
  35. id="register"
  36. defaultMessage={'语言包中没有定义patient的翻译文本'}
  37. />
  38. ),
  39. icon: 'Registration',
  40. },
  41. {
  42. key: 'worklist',
  43. disabled: !btnAvailability['worklist'],
  44. label: (
  45. <FormattedMessage
  46. id="tasklist"
  47. defaultMessage={'语言包中没有定义patient的翻译文本'}
  48. />
  49. ),
  50. icon: 'Worklist',
  51. },
  52. {
  53. key: 'historylist',
  54. disabled: !btnAvailability['historylist'],
  55. label: (
  56. <FormattedMessage
  57. id="historylist"
  58. defaultMessage={'语言包中没有定义patient的翻译文本'}
  59. />
  60. ),
  61. icon: 'Registration',
  62. },
  63. ...(isAnimalProduct ? [] : [{
  64. key: 'archivelist',
  65. disabled: !btnAvailability['archivelist'],
  66. label: (
  67. <FormattedMessage
  68. id="archivelist"
  69. defaultMessage={'语言包中没有定义patient的翻译文本'}
  70. />
  71. ),
  72. icon: 'Registration',
  73. }]),
  74. ...(isAnimalProduct ? [] : [{
  75. key: 'bin',
  76. disabled: !btnAvailability['bin'],
  77. label: (
  78. <FormattedMessage
  79. id="bin"
  80. defaultMessage={'语言包中没有定义patient的翻译文本'}
  81. />
  82. ),
  83. }]),
  84. {
  85. key: 'outputlist',
  86. disabled: !btnAvailability['outputlist'],
  87. label: (
  88. <FormattedMessage
  89. id="outputlist"
  90. defaultMessage={'语言包中没有定义patient的翻译文本'}
  91. />
  92. ),
  93. icon: 'Registration',
  94. },
  95. ],
  96. },
  97. // {
  98. // key: 'emergency',
  99. // icon: 'Emergency',
  100. // label: '急诊',
  101. // disabled: false,
  102. // },
  103. {
  104. key: 'exam',
  105. disabled:
  106. (console.log(`我要看看exam对应的值:${!btnAvailability['exam']}`),
  107. !btnAvailability['exam']),
  108. label: (
  109. <FormattedMessage
  110. id="exam"
  111. defaultMessage={'语言包中没有定义patient的翻译文本'}
  112. />
  113. ),
  114. icon: 'Exam',
  115. },
  116. {
  117. type: 'divider',
  118. },
  119. {
  120. key: 'process',
  121. disabled:
  122. (console.log(
  123. `我要看看[process]对应的值:${!btnAvailability['process']}`
  124. ),
  125. !btnAvailability['process']),
  126. label: (
  127. <FormattedMessage
  128. id="process"
  129. defaultMessage={'语言包中没有定义patient的翻译文本'}
  130. />
  131. ),
  132. icon: 'Process',
  133. },
  134. {
  135. key: 'print',
  136. disabled: !btnAvailability['print'],
  137. label: (
  138. <FormattedMessage
  139. id="print"
  140. defaultMessage={'语言包中没有定义patient的翻译文本'}
  141. />
  142. ),
  143. icon: 'Output',
  144. },
  145. ];
  146. }
  147. const BusinessZone: React.FC<BusinessZoneProps> = ({ onMenuClick }) => {
  148. // eslint-disable-next-line
  149. const currentKey = useSelector((state: RootState) => state.BusinessFlow.currentKey);
  150. const productName = useSelector((state: RootState) => state.product.productName);
  151. console.log('Current Business Flow Key:', currentKey);
  152. const dataState: DataState = useSelector((state: RootState) => {
  153. if (currentKey === 'worklist') {
  154. return {
  155. hasSelection: state.workSelection.selectedIds.length > 0,
  156. hasExposedImage:
  157. state.bodyPositionList.exposureStatus === 'Half Exposed' ||
  158. state.bodyPositionList.exposureStatus === 'Fully Exposed',
  159. };
  160. } else if (currentKey === 'historylist') {
  161. return {
  162. hasSelection: state.historySelection.selectedIds.length > 0,
  163. hasExposedImage:
  164. state.bodyPositionList.exposureStatus === 'Half Exposed' ||
  165. state.bodyPositionList.exposureStatus === 'Fully Exposed',
  166. };
  167. } else if (currentKey === 'exam') {
  168. return {
  169. // hasSelection: state.historySelection.selectedIds.length > 0,
  170. hasExposedImage:
  171. state.bodyPositionList.exposureStatus === 'Half Exposed' ||
  172. state.bodyPositionList.exposureStatus === 'Fully Exposed',
  173. };
  174. } else {
  175. return {};
  176. }
  177. });
  178. const btnAvailability = getBtnAvailability(
  179. currentKey as LocationKey,
  180. dataState
  181. );
  182. console.log('Button Availability:', btnAvailability);
  183. const items = useItems(btnAvailability, productName);
  184. // const [visibleItems, setVisibleItems] = useState(items);
  185. const [floatingMenuVisible, setFloatingMenuVisible] = useState(true);
  186. // const businessZoneRef = useRef<HTMLDivElement>(null);
  187. // useEffect(() => {
  188. // const handleResize = () => {
  189. // if (businessZoneRef.current) {
  190. // const businessZoneHeight = businessZoneRef.current.offsetHeight;
  191. // const windowHeight = window.innerHeight;
  192. // const systemZoneHeight = 100; // Assuming SystemZone height is fixed
  193. // if (businessZoneHeight + systemZoneHeight > windowHeight) {
  194. // const visibleCount = Math.floor(
  195. // (windowHeight - systemZoneHeight) / 50
  196. // ); // Assuming each button height is 50px
  197. // setVisibleItems(items.slice(0, visibleCount));
  198. // } else {
  199. // setVisibleItems(items);
  200. // }
  201. // }
  202. // };
  203. // window.addEventListener('resize', handleResize);
  204. // handleResize(); // Initial check
  205. // return () => {
  206. // window.removeEventListener('resize', handleResize);
  207. // };
  208. // }, [items]);
  209. const handlePatientManagementClick = () => {
  210. setFloatingMenuVisible(!floatingMenuVisible);
  211. };
  212. const buttonClassNameGenerator = (
  213. itemkey: string,
  214. currentkey: string
  215. ): string => {
  216. const originalName =
  217. 'w-full max-w-full whitespace-nowrap overflow-hidden text-ellipsis min-w-0 truncate';
  218. if (itemkey === currentkey) {
  219. return originalName + ' border border-red-500 ';
  220. }
  221. return originalName;
  222. };
  223. useSelector((state: RootState) => state.permission.triggerPermissionCalc);
  224. return (
  225. // <div
  226. // className="grid grid-rows-[1fr] flex-grow h-0 overflow-y-auto"
  227. // >
  228. <Flex vertical className=" min-h-0 overflow-y-auto flex-grow px-1">
  229. {items.map((item) =>
  230. item.type === 'divider' ? (
  231. <hr
  232. key="divider"
  233. style={{
  234. width: '100%',
  235. borderTop: '1px solid #f0f0f0',
  236. margin: '8px 0',
  237. }}
  238. />
  239. ) : (
  240. <>
  241. <IconButton
  242. data-testid={item.key}
  243. onClick={
  244. item.key === 'patient_management'
  245. ? handlePatientManagementClick
  246. : () => onMenuClick?.(item.key ?? 'error')
  247. }
  248. iconSize={56}
  249. iconPlace="left"
  250. icon={
  251. <Icon
  252. module="module-common"
  253. name={item.icon ?? ''}
  254. userId="user-A"
  255. theme="default"
  256. size="2x"
  257. state="normal"
  258. />
  259. }
  260. style={{
  261. //flex: 1,
  262. minWidth: 0,
  263. overflow: 'hidden', // 超出隐藏
  264. textOverflow: 'ellipsis', // 超出用省略号
  265. whiteSpace: 'nowrap', // 不换行
  266. padding: '0px',
  267. minHeight: '1.5rem',
  268. }}
  269. className={buttonClassNameGenerator(
  270. item.key as string,
  271. currentKey
  272. )}
  273. disabled={item.disabled}
  274. >
  275. <span
  276. style={{
  277. overflow: 'hidden',
  278. textOverflow: 'ellipsis',
  279. whiteSpace: 'nowrap',
  280. display: 'block', // 或 inline-block
  281. flex: 1, // 占据剩余空间并允许收缩
  282. minWidth: 0, // 再次确保可以收缩
  283. }}
  284. >
  285. {item.label}
  286. </span>
  287. </IconButton>
  288. {item.key === 'patient_management' && floatingMenuVisible && (
  289. <Flex vertical className="px-1">
  290. {item.children?.map((child) => (
  291. <IconButton
  292. data-testid={child.key}
  293. iconSize={56}
  294. icon={
  295. <Icon
  296. module="module-common"
  297. name={item.icon ?? ''}
  298. userId="user-A"
  299. theme="default"
  300. size="2x"
  301. state="normal"
  302. />
  303. }
  304. key={child.key}
  305. onClick={() => onMenuClick?.(child.key)}
  306. className={buttonClassNameGenerator(
  307. child.key as string,
  308. currentKey
  309. )}
  310. style={{
  311. //flex: 1,
  312. minWidth: 0,
  313. overflow: 'hidden', // 超出隐藏
  314. textOverflow: 'ellipsis', // 超出用省略号
  315. whiteSpace: 'nowrap', // 不换行
  316. padding: '0px',
  317. }}
  318. disabled={item.disabled}
  319. >
  320. <span
  321. style={{
  322. overflow: 'hidden',
  323. textOverflow: 'ellipsis',
  324. whiteSpace: 'nowrap',
  325. display: 'block', // 或 inline-block
  326. flex: 1, // 占据剩余空间并允许收缩
  327. minWidth: 0, // 再次确保可以收缩
  328. }}
  329. >
  330. {child.label}
  331. </span>
  332. </IconButton>
  333. ))}
  334. </Flex>
  335. )}
  336. </>
  337. )
  338. )}
  339. </Flex>
  340. // </div>
  341. );
  342. };
  343. export default BusinessZone;