HeaderBar.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import React, { useContext, useEffect, useState } from 'react';
  2. import { Link, useNavigate } from 'react-router-dom';
  3. import { UserContext } from '../context/User';
  4. import { useSetTheme, useTheme } from '../context/Theme';
  5. import { useTranslation } from 'react-i18next';
  6. import { API, getLogo, getSystemName, isMobile, showSuccess } from '../helpers';
  7. import '../index.css';
  8. import fireworks from 'react-fireworks';
  9. import {
  10. IconClose,
  11. IconHelpCircle,
  12. IconHome,
  13. IconHomeStroked, IconIndentLeft,
  14. IconComment,
  15. IconKey, IconMenu,
  16. IconNoteMoneyStroked,
  17. IconPriceTag,
  18. IconUser,
  19. IconLanguage
  20. } from '@douyinfe/semi-icons';
  21. import { Avatar, Button, Dropdown, Layout, Nav, Switch } from '@douyinfe/semi-ui';
  22. import { stringToColor } from '../helpers/render';
  23. import Text from '@douyinfe/semi-ui/lib/es/typography/text';
  24. import { StyleContext } from '../context/Style/index.js';
  25. const HeaderBar = () => {
  26. const { t, i18n } = useTranslation();
  27. const [userState, userDispatch] = useContext(UserContext);
  28. const [styleState, styleDispatch] = useContext(StyleContext);
  29. let navigate = useNavigate();
  30. const [currentLang, setCurrentLang] = useState(i18n.language);
  31. const systemName = getSystemName();
  32. const logo = getLogo();
  33. const currentDate = new Date();
  34. // enable fireworks on new year(1.1 and 2.9-2.24)
  35. const isNewYear =
  36. (currentDate.getMonth() === 0 && currentDate.getDate() === 1);
  37. let buttons = [
  38. {
  39. text: t('首页'),
  40. itemKey: 'home',
  41. to: '/',
  42. },
  43. {
  44. text: t('控制台'),
  45. itemKey: 'detail',
  46. to: '/',
  47. },
  48. {
  49. text: t('定价'),
  50. itemKey: 'pricing',
  51. to: '/pricing',
  52. },
  53. {
  54. text: t('关于'),
  55. itemKey: 'about',
  56. to: '/about',
  57. },
  58. ];
  59. async function logout() {
  60. await API.get('/api/user/logout');
  61. showSuccess(t('注销成功!'));
  62. userDispatch({ type: 'logout' });
  63. localStorage.removeItem('user');
  64. navigate('/login');
  65. }
  66. const handleNewYearClick = () => {
  67. fireworks.init('root', {});
  68. fireworks.start();
  69. setTimeout(() => {
  70. fireworks.stop();
  71. setTimeout(() => {
  72. window.location.reload();
  73. }, 10000);
  74. }, 3000);
  75. };
  76. const theme = useTheme();
  77. const setTheme = useSetTheme();
  78. useEffect(() => {
  79. if (theme === 'dark') {
  80. document.body.setAttribute('theme-mode', 'dark');
  81. } else {
  82. document.body.removeAttribute('theme-mode');
  83. }
  84. // 发送当前主题模式给子页面
  85. const iframe = document.querySelector('iframe');
  86. if (iframe) {
  87. iframe.contentWindow.postMessage({ themeMode: theme }, '*');
  88. }
  89. if (isNewYear) {
  90. console.log('Happy New Year!');
  91. }
  92. }, [theme]);
  93. useEffect(() => {
  94. const handleLanguageChanged = (lng) => {
  95. setCurrentLang(lng);
  96. const iframe = document.querySelector('iframe');
  97. if (iframe) {
  98. iframe.contentWindow.postMessage({ lang: lng }, '*');
  99. }
  100. };
  101. i18n.on('languageChanged', handleLanguageChanged);
  102. return () => {
  103. i18n.off('languageChanged', handleLanguageChanged);
  104. };
  105. }, [i18n]);
  106. const handleLanguageChange = (lang) => {
  107. i18n.changeLanguage(lang);
  108. };
  109. return (
  110. <>
  111. <Layout>
  112. <div style={{ width: '100%' }}>
  113. <Nav
  114. className={'topnav'}
  115. mode={'horizontal'}
  116. renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => {
  117. const routerMap = {
  118. about: '/about',
  119. login: '/login',
  120. register: '/register',
  121. pricing: '/pricing',
  122. detail: '/detail',
  123. home: '/',
  124. chat: '/chat',
  125. };
  126. return (
  127. <div onClick={(e) => {
  128. if (props.itemKey === 'home') {
  129. styleDispatch({ type: 'SET_INNER_PADDING', payload: false });
  130. styleDispatch({ type: 'SET_SIDER', payload: false });
  131. } else {
  132. styleDispatch({ type: 'SET_INNER_PADDING', payload: true });
  133. if (!styleState.isMobile) {
  134. styleDispatch({ type: 'SET_SIDER', payload: true });
  135. }
  136. }
  137. }}>
  138. <Link
  139. className="header-bar-text"
  140. style={{ textDecoration: 'none' }}
  141. to={routerMap[props.itemKey]}
  142. >
  143. {itemElement}
  144. </Link>
  145. </div>
  146. );
  147. }}
  148. selectedKeys={[]}
  149. // items={headerButtons}
  150. onSelect={(key) => {}}
  151. header={styleState.isMobile?{
  152. logo: (
  153. <>
  154. {
  155. !styleState.showSider ?
  156. <Button icon={<IconMenu />} theme="light" aria-label={t('展开侧边栏')} onClick={
  157. () => styleDispatch({ type: 'SET_SIDER', payload: true })
  158. } />:
  159. <Button icon={<IconIndentLeft />} theme="light" aria-label={t('闭侧边栏')} onClick={
  160. () => styleDispatch({ type: 'SET_SIDER', payload: false })
  161. } />
  162. }
  163. </>
  164. ),
  165. }:{
  166. logo: (
  167. <img src={logo} alt='logo' />
  168. ),
  169. text: systemName,
  170. }}
  171. items={buttons}
  172. footer={
  173. <>
  174. {isNewYear && (
  175. // happy new year
  176. <Dropdown
  177. position='bottomRight'
  178. render={
  179. <Dropdown.Menu>
  180. <Dropdown.Item onClick={handleNewYearClick}>
  181. Happy New Year!!!
  182. </Dropdown.Item>
  183. </Dropdown.Menu>
  184. }
  185. >
  186. <Nav.Item itemKey={'new-year'} text={'🏮'} />
  187. </Dropdown>
  188. )}
  189. {/* <Nav.Item itemKey={'about'} icon={<IconHelpCircle />} /> */}
  190. <>
  191. <Switch
  192. checkedText='🌞'
  193. size={styleState.isMobile?'default':'large'}
  194. checked={theme === 'dark'}
  195. uncheckedText='🌙'
  196. onChange={(checked) => {
  197. setTheme(checked);
  198. }}
  199. />
  200. </>
  201. <Dropdown
  202. position='bottomRight'
  203. render={
  204. <Dropdown.Menu>
  205. <Dropdown.Item
  206. onClick={() => handleLanguageChange('zh')}
  207. type={currentLang === 'zh' ? 'primary' : 'tertiary'}
  208. >
  209. 中文
  210. </Dropdown.Item>
  211. <Dropdown.Item
  212. onClick={() => handleLanguageChange('en')}
  213. type={currentLang === 'en' ? 'primary' : 'tertiary'}
  214. >
  215. English
  216. </Dropdown.Item>
  217. </Dropdown.Menu>
  218. }
  219. >
  220. <Nav.Item
  221. itemKey={'language'}
  222. icon={<IconLanguage />}
  223. />
  224. </Dropdown>
  225. {userState.user ? (
  226. <>
  227. <Dropdown
  228. position='bottomRight'
  229. render={
  230. <Dropdown.Menu>
  231. <Dropdown.Item onClick={logout}>{t('退出')}</Dropdown.Item>
  232. </Dropdown.Menu>
  233. }
  234. >
  235. <Avatar
  236. size='small'
  237. color={stringToColor(userState.user.username)}
  238. style={{ margin: 4 }}
  239. >
  240. {userState.user.username[0]}
  241. </Avatar>
  242. {styleState.isMobile?null:<Text>{userState.user.username}</Text>}
  243. </Dropdown>
  244. </>
  245. ) : (
  246. <>
  247. <Nav.Item
  248. itemKey={'login'}
  249. text={!styleState.isMobile?t('登录'):null}
  250. icon={<IconUser />}
  251. />
  252. {
  253. !styleState.isMobile && (
  254. <Nav.Item
  255. itemKey={'register'}
  256. text={t('注册')}
  257. icon={<IconKey />}
  258. />
  259. )
  260. }
  261. </>
  262. )}
  263. </>
  264. }
  265. ></Nav>
  266. </div>
  267. </Layout>
  268. </>
  269. );
  270. };
  271. export default HeaderBar;