index.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /*
  2. * @Author: your name
  3. * @Date: 2021-11-16 09:12:37
  4. * @LastEditTime: 2025-01-20 10:38:30
  5. * @LastEditors: code4eat awesomedema@gmail.com
  6. * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  7. * @FilePath: /KC-MiddlePlatform/src/pages/index/components/topBar/index.tsx
  8. */
  9. import React, { useEffect, useRef, useState } from 'react';
  10. import './style.less';
  11. import { Input, Modal, Tooltip } from 'antd';
  12. import { LogoutOutlined, SettingOutlined } from '@ant-design/icons';
  13. // import logo from '../../../public/images/kc-logo.png';
  14. import tabCloseIcon from '../../../public/images/tabCloseIcon.png';
  15. import { history, useLocation, useModel } from 'umi';
  16. import { Divider } from 'antd';
  17. import { updateTokenReq } from '@/service/user';
  18. import { logoutHandle } from '@/global';
  19. import { switchOrgReq } from '@/service';
  20. interface TopBarType {
  21. onTabChange?: (data: TopBar.Tab[]) => void; //当tab切换时回调
  22. openedTabs: TopBar.Tab[]; //已打开的列表
  23. currentTab?: TopBar.Tab | undefined; //当前tab
  24. userPannelTabClick?: (tag: 'SETTING' | 'LOGOUT' | 'SETUSERINFO') => void;
  25. onCloseTab?: (data: TopBar.Tab) => void;
  26. onTabClick?: (data: TopBar.Tab) => void;
  27. userData?: { name: string; [key: string]: any };
  28. navData: TopBar.PanelData[];
  29. logo?: string;
  30. topBarTitle?: string;
  31. }
  32. let isClickInside = false;
  33. const TopBar: React.FC<TopBarType> = (props) => {
  34. const { onTabChange, userPannelTabClick, onCloseTab, onTabClick, userData, navData, currentTab, logo = undefined, topBarTitle = '欢迎进入医管平台' } = props;
  35. const [systemTabs, setSystemTabs] = useState<TopBar.Tab[]>([]); //已打开的tab
  36. const [currentSelectedTab, setCurrentSelectedTab] = useState<TopBar.Tab>();
  37. const [ifOpenPannel, setIfOpenPannel] = useState(false);
  38. const [arrowRotate, setArrowRotate] = useState(false);
  39. // const [pageTitle, set_pageTitle] = useState('');
  40. const [currentActivedBlockIndex, set_currentActivedBlockIndex] = useState(0);
  41. const [panelData, set_panelData] = useState<TopBar.PanelData[]>([]);
  42. const [onTabSystemTabs, set_onTabSystemTabs] = useState<TopBar.Tab[]>([]); //tab导航可以放下的数量,剩余通过下拉获取
  43. const [onTabSystemTabs_hide, set_onTabSystemTabs_hide] = useState<TopBar.Tab[]>([]); //下拉掩藏的导航
  44. const { initialState, setInitialState } = useModel('@@initialState');
  45. const [tokenUpdateModalVisible, set_tokenUpdateModalVisible] = useState(false);
  46. const location = useLocation();
  47. const [showMoreTabPannel, set_showMoreTabPannel] = useState(false);
  48. const [password, set_password] = useState<string | undefined>(undefined);
  49. const [showGroupList, set_showGroupList] = useState(false);
  50. const [groupList, set_groupList] = useState<any[]>([]);
  51. const [currentActivedGroup, set_currentActivedGroup] = useState<any>(undefined);
  52. const localSavedTab = localStorage.getItem('currentSelectedTab');
  53. const currentSelectedTabFromLocal = localSavedTab ? JSON.parse(localSavedTab) : {};
  54. const PannelRef = useRef<any>(null);
  55. const GroupListWrapperRef = useRef<any>(null);
  56. const _systemTabClickHandle = (item: TopBar.Tab) => {
  57. //导航栏tab点击
  58. // console.log('_systemTabClickHandle',item);
  59. onTabClick && onTabClick(item);
  60. if (item.type != 1 && item.contentType != 7) {
  61. localStorage.setItem('currentSelectedTab', JSON.stringify(item));
  62. setCurrentSelectedTab(item);
  63. }
  64. };
  65. const _systemListClickHandle = (data: TopBar.Tab, currentActivedBlockIndex: number, index: number, i: number) => {
  66. //导航栏系统菜单列表点击回调
  67. if (currentSelectedTab?.menuId == data.menuId) return;
  68. //临时保存衣打开过的菜单
  69. const t = localStorage.getItem('visitedTabs');
  70. if (t) {
  71. let visitedTabs = JSON.parse(t);
  72. let index = visitedTabs.findIndex((t: TopBar.Tab) => t.menuId == data.menuId);
  73. if (index == -1) {
  74. visitedTabs.push(data);
  75. localStorage.setItem('visitedTabs', JSON.stringify(visitedTabs));
  76. }
  77. } else {
  78. localStorage.setItem('visitedTabs', JSON.stringify([data]));
  79. }
  80. _systemTabClickHandle(data); //触发一次tab点击
  81. // set_pageTitle(panelData[currentActivedBlockIndex].child[index].name);
  82. setInitialState((s: any) => ({ ...s, pageTitle: panelData[currentActivedBlockIndex].child[index].name }));
  83. if (panelData[currentActivedBlockIndex].child[index].child) {
  84. //console.log([...panelData[currentActivedBlockIndex].child[index].child])
  85. setSystemTabs([...panelData[currentActivedBlockIndex].child[index].child]);
  86. }
  87. setIfOpenPannel(false);
  88. };
  89. const _userPannelTabClick = (tag: 'SETTING' | 'LOGOUT') => {
  90. //用户菜单tab点击回调
  91. userPannelTabClick && userPannelTabClick(tag);
  92. };
  93. const closeTabHandle = (item: TopBar.Tab, e: React.MouseEvent) => {
  94. e.stopPropagation();
  95. let _systemTabs = [...systemTabs];
  96. let delIndex = -1;
  97. const filtered: any[] = _systemTabs.filter((t, index) => {
  98. if (t.menuId == item.menuId) {
  99. delIndex = index;
  100. }
  101. return t.menuId != item.menuId;
  102. });
  103. setSystemTabs([...filtered]);
  104. if (delIndex != 0) {
  105. _systemTabClickHandle(_systemTabs[delIndex - 1]); //自动切换为前一个tab
  106. }
  107. onTabChange && onTabChange(filtered);
  108. onCloseTab && onCloseTab(item);
  109. };
  110. const UserPannel = () => {
  111. return (
  112. <div className="userPannel">
  113. <div className="userPannelTab" onClick={() => _userPannelTabClick('SETTING')}>
  114. <SettingOutlined />
  115. <span>设置</span>
  116. </div>
  117. <div className="userPannelTab" onClick={() => _userPannelTabClick('LOGOUT')}>
  118. <LogoutOutlined />
  119. <span>退出</span>
  120. </div>
  121. </div>
  122. );
  123. };
  124. const goChannelIndex = (menuData: any) => {
  125. setIfOpenPannel(false);
  126. onTabClick && onTabClick(menuData);
  127. };
  128. const goToHome = () => {
  129. const go = () => {
  130. history.replace('/index');
  131. setSystemTabs([]); //清空tab导航
  132. onTabChange && onTabChange([]);
  133. setCurrentSelectedTab(undefined);
  134. setInitialState((s: any) => ({ ...s, pageTitle: topBarTitle }));
  135. setIfOpenPannel(false);
  136. console.log('goHome');
  137. localStorage.removeItem('currentSelectedTab');
  138. localStorage.removeItem('selectedKeys');
  139. // localStorage.removeItem('visitedTabs');
  140. localStorage.removeItem('openKeys');
  141. };
  142. const currentSelectedSubHop_json = localStorage.getItem('currentSelectedSubHop');
  143. if (currentSelectedSubHop_json) {
  144. const currentSelectedSubHop = JSON.parse(currentSelectedSubHop_json);
  145. if (currentSelectedSubHop.loadType) {
  146. return false;
  147. } else {
  148. go();
  149. }
  150. } else {
  151. go();
  152. }
  153. };
  154. const goSystemIndex = (name: string) => {
  155. if (panelData[currentActivedBlockIndex]) {
  156. const result: TopBar.TypeBlock[] = panelData[currentActivedBlockIndex].child.filter((t) => t.name == name);
  157. if (result.length > 0) {
  158. _systemTabClickHandle(Object.assign(result[0]));
  159. // history.push(result[0].path)
  160. }
  161. }
  162. };
  163. const openNav = () => {
  164. isClickInside = true;
  165. setIfOpenPannel(!ifOpenPannel);
  166. };
  167. const moreItemClickHandle = (systemData: TopBar.Tab) => {
  168. //点击更多应用时
  169. _systemTabClickHandle(systemData);
  170. const temp = onTabSystemTabs[onTabSystemTabs.length - 1];
  171. const _onTabSystemTabs = [...onTabSystemTabs];
  172. const b = _onTabSystemTabs.filter((a) => (a.systemId ? a.systemId : a.menuId) != (temp.systemId ? temp.systemId : temp.menuId));
  173. set_onTabSystemTabs([...b, systemData]);
  174. set_onTabSystemTabs_hide([...onTabSystemTabs_hide.filter((a) => (a.systemId ? a.systemId : a.menuId) != (systemData.systemId ? systemData.systemId : systemData.menuId)), temp]);
  175. };
  176. const reSetNav = (_panelData: TopBar.PanelData[], cur: TopBar.Tab) => {
  177. if (!(_panelData.length > 0)) return;
  178. if (JSON.stringify(cur) != '{}') {
  179. let blockIndex = 0;
  180. let channelIndex = 0;
  181. const _currentSelectedTabFromLocal = cur;
  182. one: for (let index = 0; index < _panelData.length; index++) {
  183. blockIndex = index;
  184. if (_panelData[index] && _panelData[index].child) {
  185. two: for (let i = 0; i < _panelData[index].child.length; i++) {
  186. channelIndex = i;
  187. if (_panelData && _panelData.length > 0) {
  188. const _systems = _panelData[index].child[i].child;
  189. if (_systems && _systems.length > 0) {
  190. for (let k = 0; k < _systems.length; k++) {
  191. if (_systems[k].menuId == _currentSelectedTabFromLocal.menuId) {
  192. set_currentActivedBlockIndex(blockIndex);
  193. // set_pageTitle(_panelData[blockIndex].child[channelIndex].name); //设置体系标题
  194. setInitialState((s: any) => ({ ...s, pageTitle: _panelData[blockIndex].child[channelIndex].name }));
  195. break one;
  196. }
  197. }
  198. }
  199. }
  200. }
  201. }
  202. }
  203. if (_panelData && _panelData.length > 0 && _panelData[blockIndex].child) {
  204. setSystemTabs(_panelData[blockIndex].child[channelIndex].child); //恢复体系列表
  205. setCurrentSelectedTab(_currentSelectedTabFromLocal);
  206. localStorage.setItem('currentSelectedTab', JSON.stringify(_currentSelectedTabFromLocal));
  207. setInitialState((s) => ({ ...s, currentSelectedSys: _currentSelectedTabFromLocal } as any));
  208. }
  209. // console.log({_currentSelectedTabFromLocal,location});
  210. const { pathname } = location;
  211. if (pathname.indexOf(_currentSelectedTabFromLocal.path) == -1) {
  212. history.push(_currentSelectedTabFromLocal.path);
  213. }
  214. //_systemTabClickHandle(_currentSelectedTabFromLocal); //恢复选中的tab
  215. }
  216. };
  217. const [hideTimer, setHideTimer] = useState<null | any>(null);
  218. const handleTabMoreMouseLeave = () => {
  219. // 设置一个延时器,在一段时间后隐藏morePannel
  220. const timer = setTimeout(() => {
  221. set_showMoreTabPannel(false);
  222. }, 300); // 例如延迟300毫秒
  223. setHideTimer(timer);
  224. };
  225. const handleMorePannelMouseEnter = () => {
  226. // 清除延时器,防止morePannel隐藏
  227. if (hideTimer) {
  228. clearTimeout(hideTimer);
  229. setHideTimer(null);
  230. }
  231. };
  232. const handleMorePannelMouseLeave = () => {
  233. set_showMoreTabPannel(false);
  234. };
  235. const updateToken = async () => {
  236. const account = localStorage.getItem('account') as string;
  237. const hospSign = localStorage.getItem('hospSign');
  238. const data = {
  239. account,
  240. password: password,
  241. hospSign,
  242. };
  243. const resp = await updateTokenReq(data);
  244. if (resp) {
  245. set_password(undefined);
  246. set_tokenUpdateModalVisible(false);
  247. window.location.reload();
  248. }
  249. };
  250. const switchGroupHandle = async (group: any) => {
  251. const resp = await switchOrgReq(group.orgId, group.orgName);
  252. if (resp) {
  253. set_currentActivedGroup(group);
  254. set_showGroupList(false);
  255. const currentPath = window.location.pathname;
  256. window.history.pushState(null, '', '/');
  257. setTimeout(() => {
  258. window.history.pushState(null, '', currentPath);
  259. }, 100);
  260. }
  261. };
  262. useEffect(() => {
  263. if (currentSelectedTabFromLocal) {
  264. reSetNav(navData, currentSelectedTabFromLocal);
  265. }
  266. set_panelData(navData);
  267. }, [navData]);
  268. useEffect(() => {
  269. if (currentTab) reSetNav(panelData, currentTab);
  270. }, [currentTab]);
  271. useEffect(() => {
  272. setInitialState((s: any) => ({ ...s, pageTitle: topBarTitle }));
  273. }, [topBarTitle]);
  274. useEffect(() => {
  275. if (systemTabs.length > 5) {
  276. //set_onTabSystemTabs(tabs);
  277. let _onTabSystemTabs: any[] = [];
  278. let _set_onTabSystemTabs_hide: any[] = [];
  279. systemTabs.forEach((a, index) => {
  280. if (index <= 4) {
  281. _onTabSystemTabs.push(a);
  282. } else {
  283. if (a.systemId == currentSelectedTabFromLocal.systemId) {
  284. const _temp = _onTabSystemTabs[_onTabSystemTabs.length - 1];
  285. _onTabSystemTabs.pop();
  286. _onTabSystemTabs.push(a);
  287. _set_onTabSystemTabs_hide.push(_temp);
  288. } else {
  289. _set_onTabSystemTabs_hide.push(a);
  290. }
  291. }
  292. });
  293. set_onTabSystemTabs(_onTabSystemTabs);
  294. set_onTabSystemTabs_hide(_set_onTabSystemTabs_hide);
  295. } else {
  296. set_onTabSystemTabs(systemTabs);
  297. }
  298. }, [systemTabs]);
  299. useEffect(() => {
  300. if (initialState?.userInfo) {
  301. const { orgInfo = [] } = initialState.userInfo;
  302. if (orgInfo && orgInfo.length > 0) {
  303. const defaultGroup = orgInfo.filter((a: any) => a.defaultFlag);
  304. set_groupList(orgInfo);
  305. set_currentActivedGroup(defaultGroup.length > 0 ? defaultGroup[0] : undefined);
  306. }
  307. } else {
  308. console.error('initialState or userInfo is null/undefined');
  309. }
  310. }, [initialState]);
  311. useEffect(() => {
  312. // _systemTabClickHandle(currentSelectedTabFromLocal); //恢复选中的tab
  313. const handleClickOutside = (event: { target: any }) => {
  314. if (!isClickInside && PannelRef.current && !PannelRef.current.contains(event.target)) {
  315. setIfOpenPannel(false);
  316. }
  317. if (!isClickInside && GroupListWrapperRef.current && !GroupListWrapperRef.current.contains(event.target)) {
  318. set_showGroupList(false);
  319. }
  320. isClickInside = false;
  321. };
  322. document.addEventListener('click', handleClickOutside);
  323. // 事件监听器的函数定义
  324. const handleStorageChange = (e: any) => {
  325. if (e.key === 'tokenExpired') {
  326. set_tokenUpdateModalVisible(true);
  327. }
  328. };
  329. // 添加事件监听器
  330. window.addEventListener('removeLocalItemEvent', handleStorageChange);
  331. // 返回的函数用于在组件卸载时移除事件监听器
  332. return () => {
  333. window.removeEventListener('removeLocalItemEvent', handleStorageChange);
  334. document.removeEventListener('click', handleClickOutside);
  335. };
  336. }, []);
  337. return (
  338. <div className="topBar" onClick={(e) => e.stopPropagation()}>
  339. <Modal className="TokenUpdateModal" open={tokenUpdateModalVisible} width={400} title={false} footer={false} closable={false}>
  340. <div className="content">
  341. <div className="title">登录超时锁定</div>
  342. <div className="form">
  343. <div className="avatar">
  344. <img className="avatarImg" src={require('../../../public/images/initAvatar.png')} alt="" />
  345. <img className="suoding" src={require('../../../public/images/suoding.png')} alt="" />
  346. </div>
  347. <div className="name">{userData?.name}</div>
  348. <Input onChange={(e) => set_password(e.target.value)} value={password} className="input" autoComplete="off" onKeyDown={(e) => e.key === 'Enter' && updateToken()} />
  349. <div className="updateBtn" onClick={() => updateToken()}>
  350. 解锁
  351. </div>
  352. <a onClick={() => logoutHandle()}>退出登录</a>
  353. </div>
  354. </div>
  355. </Modal>
  356. <div className="logoWrap">
  357. {logo && <img className="logo" src={logo} onClick={() => goToHome()} />}
  358. <Divider type="vertical" style={{ background: 'white', height: 16, opacity: 0.29, position: 'relative', top: 1, marginLeft: 16, marginRight: 8 }} />
  359. <div className={ifOpenPannel ? 'menu active' : 'menu'} onClick={() => openNav()}>
  360. <img src={require('../../../public/images/menu.png')} alt="" />
  361. </div>
  362. <span
  363. className="systemTitle"
  364. // onClick={() => goSystemIndex(pageTitle)}
  365. >
  366. {initialState?.pageTitle}
  367. </span>
  368. </div>
  369. <div className="userRelaInfoWrap">
  370. <>
  371. {/**
  372. * 已打开的tab
  373. */}
  374. <div className="tabWrap">
  375. {onTabSystemTabs &&
  376. onTabSystemTabs.map((item, index) => (
  377. <div key={index} className={currentSelectedTab?.menuId == item.menuId ? `tab on` : `tab`} onClick={() => _systemTabClickHandle(item)}>
  378. <div className="tabText">{item.name} </div>
  379. <div className="closeIconWrap">
  380. <img src={tabCloseIcon} onClick={(e) => closeTabHandle(item, e)} />
  381. </div>
  382. </div>
  383. ))}
  384. </div>
  385. {systemTabs.length > 5 && (
  386. <div className={showMoreTabPannel ? 'tabMore active' : 'tabMore'} onMouseEnter={() => set_showMoreTabPannel(true)} onMouseLeave={handleTabMoreMouseLeave}>
  387. {showMoreTabPannel && (
  388. <div className="morePannel" onMouseEnter={handleMorePannelMouseEnter} onMouseLeave={handleMorePannelMouseLeave}>
  389. {onTabSystemTabs_hide.map((item, index) => {
  390. return (
  391. <div key={index} className="moreItem" onClick={() => moreItemClickHandle(item)}>
  392. {/* <Tooltip placement="right" title={item.name}> */}
  393. <span>{item.name}</span>
  394. {/* </Tooltip> */}
  395. </div>
  396. );
  397. })}
  398. </div>
  399. )}
  400. </div>
  401. )}
  402. </>
  403. <div className="notification">
  404. <img className="notificationIcon" src={require('../../../public/images/notificationIcon.png')} />
  405. </div>
  406. <div className="group">
  407. {groupList.length > 0 && (
  408. <Tooltip title={currentActivedGroup?.orgName ?? ''} placement="right">
  409. <div
  410. className="group-switcher"
  411. onClick={() => {
  412. isClickInside = true;
  413. set_showGroupList(true);
  414. }}
  415. >
  416. <img src={require('./images/groupIcon.png')} alt="" />
  417. </div>
  418. </Tooltip>
  419. )}
  420. {showGroupList && (
  421. <div className="group-list-wrapper" ref={GroupListWrapperRef}>
  422. {groupList.map((a, index) => (
  423. <div key={index} className={currentActivedGroup?.orgId === a.orgId ? `group-list actived` : 'group-list'} onClick={() => switchGroupHandle(a)}>
  424. {a.orgName}
  425. </div>
  426. ))}
  427. </div>
  428. )}
  429. </div>
  430. <Tooltip className="topBarTooltip" placement="bottomRight" title={<UserPannel />} color="#fff" onOpenChange={(visible) => setArrowRotate(visible)}>
  431. <div className="user">
  432. <div className="avator">
  433. <img src={userData?.avatarUrl ? userData.avatarUrl : require('../../../public/images/avatar.png')} />
  434. </div>
  435. <div className="info">
  436. <span className="hospName">{localStorage.getItem('hospAbbreviation')}</span>
  437. <span className="name">{userData?.name}</span>
  438. </div>
  439. {/* <img className={arrowRotate ? `arrow on` : `arrow`} src={require('../../../public/images/arrow_white.png')} /> */}
  440. </div>
  441. </Tooltip>
  442. </div>
  443. {ifOpenPannel && (
  444. <div className="panel" ref={PannelRef} onClick={(e) => e.stopPropagation()}>
  445. <div className="left">
  446. {panelData.map((item, index) => {
  447. return (
  448. <div className={currentActivedBlockIndex == index ? `typeBlock active` : `typeBlock`} key={index} onClick={() => set_currentActivedBlockIndex(index)}>
  449. {/* <img className='typeBlockIcon' src={item?.icon} alt="" /> */}
  450. <div className={` typeBlockIcon typeBlockIcon${item.icon}`}></div>
  451. <span className="typeBlockName">{item.name}</span>
  452. </div>
  453. );
  454. })}
  455. </div>
  456. <div className="right">
  457. <div className="panelCloseBtn" onClick={() => setIfOpenPannel(false)}></div>
  458. {panelData.length > 0 &&
  459. panelData[currentActivedBlockIndex] &&
  460. panelData[currentActivedBlockIndex].child &&
  461. panelData[currentActivedBlockIndex].child.map((item, index: number) => {
  462. return (
  463. <div className="row" key={index}>
  464. <img className="channelIcon" src={item.icon ? item.icon : require(`../../../public/images/tongyong_tixi.png`)} alt="" />
  465. <div className="rowDetail">
  466. <div className="channelName" onClick={() => goChannelIndex(item)}>
  467. {item.name}
  468. </div>
  469. <div className="channelList">
  470. {item.child &&
  471. item.child.length > 0 &&
  472. item.child.map((val, i: number) => {
  473. return (
  474. <div
  475. className={currentSelectedTab?.menuId == val.menuId ? 'systemTab on' : 'systemTab'}
  476. key={i}
  477. onClick={() => _systemListClickHandle(val, currentActivedBlockIndex, index, i)}
  478. >
  479. {val.name}
  480. </div>
  481. );
  482. })}
  483. </div>
  484. </div>
  485. </div>
  486. );
  487. })}
  488. </div>
  489. </div>
  490. )}
  491. </div>
  492. );
  493. };
  494. export default TopBar;