Sider.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { ChatContext } from '@/app/chat-context';
  2. import { DarkSvg, SunnySvg } from '@/components/icons';
  3. import UserBar from '@/new-components/layout/UserBar';
  4. import { STORAGE_LANG_KEY, STORAGE_THEME_KEY } from '@/utils/constants/index';
  5. import Icon, { GlobalOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
  6. import { Layout, Popover } from 'antd';
  7. import moment from 'moment';
  8. import Image from 'next/image';
  9. import Link from 'next/link';
  10. import React, { ReactNode, useCallback, useContext, useMemo, useState } from 'react';
  11. import { useTranslation } from 'react-i18next';
  12. interface SettingItem {
  13. key: string;
  14. name: string;
  15. icon: ReactNode;
  16. onClick?: () => void;
  17. placement?: 'top' | 'topLeft';
  18. }
  19. const Sider: React.FC = () => {
  20. const { mode, setMode } = useContext(ChatContext);
  21. const { t, i18n } = useTranslation();
  22. const [collapsed, setCollapsed] = useState<boolean>(false);
  23. // 切换主题
  24. const handleToggleTheme = useCallback(() => {
  25. const theme = mode === 'light' ? 'dark' : 'light';
  26. setMode(theme);
  27. localStorage.setItem(STORAGE_THEME_KEY, theme);
  28. }, [mode, setMode]);
  29. // 切换语言
  30. const handleChangeLang = useCallback(() => {
  31. const language = i18n.language === 'en' ? 'zh' : 'en';
  32. i18n.changeLanguage(language);
  33. if (language === 'zh') {
  34. moment.locale('zh-cn');
  35. }
  36. if (language === 'en') {
  37. moment.locale('en');
  38. }
  39. localStorage.setItem(STORAGE_LANG_KEY, language);
  40. }, [i18n]);
  41. // 展开或收起
  42. const handleToggleMenu = useCallback(() => {
  43. setCollapsed(!collapsed);
  44. }, [collapsed]);
  45. const settings: SettingItem[] = useMemo(() => {
  46. return [
  47. {
  48. key: 'theme',
  49. name: t('Theme'),
  50. icon: mode === 'dark' ? <Icon component={DarkSvg} /> : <Icon component={SunnySvg} />,
  51. onClick: handleToggleTheme,
  52. },
  53. {
  54. key: 'language',
  55. name: t('language'),
  56. icon: <GlobalOutlined />,
  57. onClick: handleChangeLang,
  58. },
  59. {
  60. key: 'fold',
  61. name: t(collapsed ? 'Show_Sidebar' : 'Close_Sidebar'),
  62. icon: collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />,
  63. onClick: handleToggleMenu,
  64. },
  65. ];
  66. }, [collapsed, handleChangeLang, handleToggleMenu, handleToggleTheme, mode, t]);
  67. return (
  68. <Layout.Sider
  69. theme={mode}
  70. width={240}
  71. collapsedWidth={80}
  72. collapsible={true}
  73. collapsed={collapsed}
  74. trigger={null}
  75. className='flex flex-1 flex-col h-full justify-between bg-bar dark:bg-[#232734] px-4 pt-4'
  76. >
  77. {collapsed ? (
  78. <></>
  79. ) : (
  80. <>
  81. <Link href='/' className='flex items-center justify-center p-2 pb-4'>
  82. <Image src='/logo_zh_latest.png' alt='DB-GPT' width={180} height={40} />
  83. </Link>
  84. <div></div>
  85. <div className='flex flex-col'>
  86. <UserBar />
  87. <div className='flex items-start justify-between border-t border-dashed border-gray-200 dark:border-gray-700'>
  88. {settings.map(item => (
  89. <Popover key={item.key} content={item.name}>
  90. <div
  91. className='flex-1 flex items-center justify-center cursor-pointer text-xl'
  92. onClick={item.onClick}
  93. >
  94. {item.icon}
  95. </div>
  96. </Popover>
  97. ))}
  98. </div>
  99. </div>
  100. </>
  101. )}
  102. </Layout.Sider>
  103. );
  104. };
  105. export default Sider;