UserArea.jsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import React from 'react';
  16. import { Link } from 'react-router-dom';
  17. import { Avatar, Button, Dropdown, Typography } from '@douyinfe/semi-ui';
  18. import { ChevronDown } from 'lucide-react';
  19. import {
  20. IconExit,
  21. IconUserSetting,
  22. IconCreditCard,
  23. IconKey,
  24. } from '@douyinfe/semi-icons';
  25. import { stringToColor } from '../../../helpers';
  26. import SkeletonWrapper from '../components/SkeletonWrapper';
  27. const UserArea = ({
  28. userState,
  29. isLoading,
  30. isMobile,
  31. isSelfUseMode,
  32. logout,
  33. navigate,
  34. t,
  35. }) => {
  36. if (isLoading) {
  37. return (
  38. <SkeletonWrapper
  39. loading={true}
  40. type='userArea'
  41. width={50}
  42. isMobile={isMobile}
  43. />
  44. );
  45. }
  46. if (userState.user) {
  47. return (
  48. <Dropdown
  49. position='bottomRight'
  50. render={
  51. <Dropdown.Menu className='!bg-semi-color-bg-overlay !border-semi-color-border !shadow-lg !rounded-lg dark:!bg-gray-700 dark:!border-gray-600'>
  52. <Dropdown.Item
  53. onClick={() => {
  54. navigate('/console/personal');
  55. }}
  56. className='!px-3 !py-1.5 !text-sm !text-semi-color-text-0 hover:!bg-semi-color-fill-1 dark:!text-gray-200 dark:hover:!bg-blue-500 dark:hover:!text-white'
  57. >
  58. <div className='flex items-center gap-2'>
  59. <IconUserSetting
  60. size='small'
  61. className='text-gray-500 dark:text-gray-400'
  62. />
  63. <span>{t('个人设置')}</span>
  64. </div>
  65. </Dropdown.Item>
  66. <Dropdown.Item
  67. onClick={() => {
  68. navigate('/console/token');
  69. }}
  70. className='!px-3 !py-1.5 !text-sm !text-semi-color-text-0 hover:!bg-semi-color-fill-1 dark:!text-gray-200 dark:hover:!bg-blue-500 dark:hover:!text-white'
  71. >
  72. <div className='flex items-center gap-2'>
  73. <IconKey
  74. size='small'
  75. className='text-gray-500 dark:text-gray-400'
  76. />
  77. <span>{t('令牌管理')}</span>
  78. </div>
  79. </Dropdown.Item>
  80. <Dropdown.Item
  81. onClick={() => {
  82. navigate('/console/topup');
  83. }}
  84. className='!px-3 !py-1.5 !text-sm !text-semi-color-text-0 hover:!bg-semi-color-fill-1 dark:!text-gray-200 dark:hover:!bg-blue-500 dark:hover:!text-white'
  85. >
  86. <div className='flex items-center gap-2'>
  87. <IconCreditCard
  88. size='small'
  89. className='text-gray-500 dark:text-gray-400'
  90. />
  91. <span>{t('钱包管理')}</span>
  92. </div>
  93. </Dropdown.Item>
  94. <Dropdown.Item
  95. onClick={logout}
  96. className='!px-3 !py-1.5 !text-sm !text-semi-color-text-0 hover:!bg-semi-color-fill-1 dark:!text-gray-200 dark:hover:!bg-red-500 dark:hover:!text-white'
  97. >
  98. <div className='flex items-center gap-2'>
  99. <IconExit
  100. size='small'
  101. className='text-gray-500 dark:text-gray-400'
  102. />
  103. <span>{t('退出')}</span>
  104. </div>
  105. </Dropdown.Item>
  106. </Dropdown.Menu>
  107. }
  108. >
  109. <Button
  110. theme='borderless'
  111. type='tertiary'
  112. className='flex items-center gap-1.5 !p-1 !rounded-full hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-700 !bg-semi-color-fill-0 dark:!bg-semi-color-fill-1 dark:hover:!bg-semi-color-fill-2'
  113. >
  114. <Avatar
  115. size='extra-small'
  116. color={stringToColor(userState.user.username)}
  117. className='mr-1'
  118. >
  119. {userState.user.username[0].toUpperCase()}
  120. </Avatar>
  121. <span className='hidden md:inline'>
  122. <Typography.Text className='!text-xs !font-medium !text-semi-color-text-1 dark:!text-gray-300 mr-1'>
  123. {userState.user.username}
  124. </Typography.Text>
  125. </span>
  126. <ChevronDown
  127. size={14}
  128. className='text-xs text-semi-color-text-2 dark:text-gray-400'
  129. />
  130. </Button>
  131. </Dropdown>
  132. );
  133. } else {
  134. const showRegisterButton = !isSelfUseMode;
  135. const commonSizingAndLayoutClass =
  136. 'flex items-center justify-center !py-[10px] !px-1.5';
  137. const loginButtonSpecificStyling =
  138. '!bg-semi-color-fill-0 dark:!bg-semi-color-fill-1 hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-700 transition-colors';
  139. let loginButtonClasses = `${commonSizingAndLayoutClass} ${loginButtonSpecificStyling}`;
  140. let registerButtonClasses = `${commonSizingAndLayoutClass}`;
  141. const loginButtonTextSpanClass =
  142. '!text-xs !text-semi-color-text-1 dark:!text-gray-300 !p-1.5';
  143. const registerButtonTextSpanClass = '!text-xs !text-white !p-1.5';
  144. if (showRegisterButton) {
  145. if (isMobile) {
  146. loginButtonClasses += ' !rounded-full';
  147. } else {
  148. loginButtonClasses += ' !rounded-l-full !rounded-r-none';
  149. }
  150. registerButtonClasses += ' !rounded-r-full !rounded-l-none';
  151. } else {
  152. loginButtonClasses += ' !rounded-full';
  153. }
  154. return (
  155. <div className='flex items-center'>
  156. <Link to='/login' className='flex'>
  157. <Button
  158. theme='borderless'
  159. type='tertiary'
  160. className={loginButtonClasses}
  161. >
  162. <span className={loginButtonTextSpanClass}>{t('登录')}</span>
  163. </Button>
  164. </Link>
  165. {showRegisterButton && (
  166. <div className='hidden md:block'>
  167. <Link to='/register' className='flex -ml-px'>
  168. <Button
  169. theme='solid'
  170. type='primary'
  171. className={registerButtonClasses}
  172. >
  173. <span className={registerButtonTextSpanClass}>{t('注册')}</span>
  174. </Button>
  175. </Link>
  176. </div>
  177. )}
  178. </div>
  179. );
  180. }
  181. };
  182. export default UserArea;